diff --git a/.gitignore b/.gitignore index 27c96cf74..c0fb87220 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ !.gitignore !.gitattributes -/.idea -/.svn +*.DS_Store +*.idea +*.svn +*.git /runtime /nbproject +!composer.json /composer.lock -/public/upload /static/upload \ No newline at end of file diff --git a/.htaccess b/.htaccess index 2cfa33ed1..cbc786893 100644 --- a/.htaccess +++ b/.htaccess @@ -1,6 +1,7 @@ Options +FollowSymlinks -Multiviews RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] diff --git a/README.md b/README.md index 9b6fd5224..2af29deab 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,59 @@ -Think.Admin ---- +ThinkAdmin for PHP +-- +## 大道至简 · 悟在天成 -`Think.Admin`是一个基于`Thinkphp5`开发的后台管理系统,集成后台系统常用功能。 +* ThinkAdmin 是一个基于 Thinkphp 5.1.x 开发的后台管理系统,集成后台系统常用功能。 +* 项目安装及二次开发请参考 ThinkPHP 官方文档及下面的服务环境说明,数据库 sql 文件存放于项目根目录下。 +>* 注意:项目测试请另行搭建环境并创建数据库(数据库配置 config/database.php), 切勿直接使用测试环境数据! +>* 如果系统提示“测试系统禁止操作等字样”,可以删除项目演示路由配置(route/demo.php)或清空里面的路由记录。 +>* 当前版本使用 ThinkPHP 5.1.x 版本,对PHP版本要求不低于php5.6,具体请查阅ThinkPHP官方文档。 -项目安装请参考`ThinkPHP`官方文档及下面的服务环境说明,数据库`sql`文件存放于项目根目录下。 -注意:项目测试请另行搭建环境并创建数据库(数据库配置`application/database.php`), 切勿直接使用测试环境数据! +Documentation +-- +认真看看文档可能会对你的开发有所帮助哦! -`Think.Admin`及`微信开发`技术交流QQ群 -[![QQ群](http://pub.idqqimg.com/wpa/images/group.png "QQ群")](http://shang.qq.com/wpa/qunwpa?idkey=ae25cf789dafbef62e50a980ffc31242f150bc61a61164458216dd98c411832a) +文档地址:[ThinkAdmin 开发文档](https://www.kancloud.cn/zoujingli/thinkadmin/content) -**`Think.Admin`开发手册 ( 撰写中 )** : http://doc.think.ctolog.com -`Think.Admin`已集成模块 ---- +PHP开发技术交流(QQ群 513350915) + +[![PHP微信开发群 (SDK)](http://pub.idqqimg.com/wpa/images/group.png)](http://shang.qq.com/wpa/qunwpa?idkey=ae25cf789dafbef62e50a980ffc31242f150bc61a61164458216dd98c411832a) + + +Repositorie +-- + ThinkAdmin 为开源项目,允许把它用于任何地方,不受任何约束,欢迎 fork 项目。 +* Gitee 托管地址:https://gitee.com/zoujingli/Think.Admin +* GitHub 托管地址:https://github.com/zoujingli/ThinkAdmin + +对于新版本的微信模块使用的是授权模式,需要用到 ThinkService 项目。 +* Gitee 托管地址:https://gitee.com/zoujingli/ThinkService +* GitHub 托管地址:https://github.com/zoujingli/ThinkService + +其安装与 ThinkAdmin 相似,这里就不多说了。具体可以参见微信开放平台官网 +https://open.weixin.qq.com ,ThinkService 后台具体可以配置对应参数。 + +ThinkAdmin 与 ThinkService 对接是通过 WebService 通信的,因此运行环境需要安装 Soap 模块支持。 + + +Module +-- * 简易`RBAC`权限管理(用户、权限、节点、菜单控制) * 自建秒传文件上载组件(本地存储、七牛云存储,阿里云OSS存储) * 基站数据服务组件(唯一随机序号、表单更新) * `Http`服务组件(原生`CURL`封装,兼容PHP多版本) -* 微信公众号服务组件(基于[wechat-php-sdk](https://github.com/zoujingli/wechat-php-sdk),微信网页授权获取用户信息、已关注粉丝管理、自定义菜单管理等等) -* 微信商户支付服务组件(基于[wechat-php-sdk](https://github.com/zoujingli/wechat-php-sdk),支持JSAPI支付、扫码模式一支付、扫码模式二支付) -* 测试公众号名称:思过崖思过 (大家可以关注它来进行简单的测试) +* 微信公众号服务组件(基于[WeChatDeveloper](https://github.com/zoujingli/WeChatDeveloper),微信网页授权获取用户信息、已关注粉丝管理、自定义菜单管理等等) +* 微信商户支付服务组件(基于[WeChatDeveloper](https://github.com/zoujingli/WeChatDeveloper),支持JSAPI支付、扫码模式一支付、扫码模式二支付) * 更多组件开发中... -服务器环境 +Environment --- -* `PHP`版本不低于`PHP5.4`,推荐使用`PHP7`以达到最优效果 -* 项目运行需支持`PATHINFO`,项目不支持`ThinkPHP`的`URL`兼容模式运行(源于如何优雅的展示) -* `Apache`:已在项目根目录加入`.htaccess`文件,只需开启`rewrite`模块 +>1. PHP 版本不低于 PHP5.6,推荐使用 PHP7 以达到最优效果; +>2. 需开启 PATHINFO,不再支持 ThinkPHP 的 URL 兼容模式运行(源于如何优雅的展示)。 + +* Apache ```xml @@ -40,13 +65,13 @@ Think.Admin ``` -* `Nginx`:配置参考下面的`demo`代码 +* Nginx ``` server { listen 80; server_name wealth.demo.cuci.cc; - root /home/wwwroot/Think.Admin; + root /home/wwwroot/ThinkAdmin; index index.php index.html index.htm; add_header X-Powered-Host $hostname; @@ -87,3 +112,13 @@ server { } } ``` + +Copyright +-- +* ThinkAdmin 基于`MIT`协议发布,任何人可以用在任何地方,不受约束 +* ThinkAdmin 部分代码来自互联网,若有异议,可以联系作者进行删除 + + +Sponsor +-- +![赞助](http://zoujingli.oschina.io/static/pay.png) \ No newline at end of file diff --git a/_composer.cmd b/_composer.cmd deleted file mode 100644 index f0c7ac58a..000000000 --- a/_composer.cmd +++ /dev/null @@ -1,13 +0,0 @@ -:: Composer װ½ű -@echo off -title Composer Plugs Install And Optimize -echo. -echo ========= 1. Ѱװ ========= -rmdir /s/q vendor thinkphp runtime -echo. -echo ========= 2. زװ ========= -composer update --profile --prefer-dist --optimize-autoloader -echo. -echo ========= 3. ѹ ========= -composer dump-autoload --optimize -exit \ No newline at end of file diff --git a/admin_v3.sql b/admin_v3.sql new file mode 100644 index 000000000..1789db95c --- /dev/null +++ b/admin_v3.sql @@ -0,0 +1,947 @@ +/* +Navicat MySQL Data Transfer + +Source Server : server.cuci.cc +Source Server Version : 50558 +Source Host : server.cuci.cc:3306 +Source Database : admin_v3 + +Target Server Type : MYSQL +Target Server Version : 50558 +File Encoding : 65001 + +Date: 2018-05-04 11:40:19 +*/ + +SET FOREIGN_KEY_CHECKS=0; + +-- ---------------------------- +-- Table structure for store_express +-- ---------------------------- +DROP TABLE IF EXISTS `store_express`; +CREATE TABLE `store_express` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `express_title` varchar(50) DEFAULT '' COMMENT '快递公司名称', + `express_code` varchar(50) DEFAULT '' COMMENT '快递公司代码', + `express_desc` text COMMENT '快递公司描述', + `status` tinyint(1) unsigned DEFAULT '1' COMMENT '状态(0.无效,1.有效)', + `sort` bigint(20) unsigned DEFAULT '0' COMMENT '排序', + `is_deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=95 DEFAULT CHARSET=utf8 COMMENT='商城快递配置'; + +-- ---------------------------- +-- Records of store_express +-- ---------------------------- +INSERT INTO `store_express` VALUES ('5', 'AAE全球专递', 'aae', '顺丰快递公司', '0', '0', '0', '2017-09-12 11:53:40'); +INSERT INTO `store_express` VALUES ('6', '安捷快递', 'anjie', '', '0', '0', '0', '2017-09-13 15:27:26'); +INSERT INTO `store_express` VALUES ('7', '安信达快递', 'anxindakuaixi', null, '0', '0', '0', '2017-09-13 16:05:19'); +INSERT INTO `store_express` VALUES ('8', '彪记快递', 'biaojikuaidi', null, '0', '0', '0', '2017-09-13 16:05:26'); +INSERT INTO `store_express` VALUES ('9', 'BHT', 'bht', '', '0', '0', '0', '2017-09-13 16:05:37'); +INSERT INTO `store_express` VALUES ('10', '百福东方国际物流', 'baifudongfang', null, '0', '0', '0', '2017-09-13 16:05:41'); +INSERT INTO `store_express` VALUES ('11', '中国东方(COE)', 'coe', null, '0', '0', '0', '2017-09-13 16:05:48'); +INSERT INTO `store_express` VALUES ('12', '长宇物流', 'changyuwuliu', null, '0', '0', '0', '2017-09-13 16:05:58'); +INSERT INTO `store_express` VALUES ('13', '大田物流', 'datianwuliu', null, '0', '0', '0', '2017-09-13 16:06:06'); +INSERT INTO `store_express` VALUES ('14', '德邦物流', 'debangwuliu', null, '1', '1', '0', '2017-09-13 16:06:14'); +INSERT INTO `store_express` VALUES ('15', 'DHL', 'dhl', null, '0', '0', '0', '2017-09-13 16:06:24'); +INSERT INTO `store_express` VALUES ('16', 'DPEX', 'dpex', null, '0', '0', '0', '2017-09-13 16:06:29'); +INSERT INTO `store_express` VALUES ('17', 'd速快递', 'dsukuaidi', null, '0', '0', '0', '2017-09-13 16:06:34'); +INSERT INTO `store_express` VALUES ('18', '递四方', 'disifang', null, '0', '0', '0', '2017-09-13 16:06:40'); +INSERT INTO `store_express` VALUES ('19', 'EMS快递', 'ems', '', '1', '0', '0', '2017-09-13 16:06:47'); +INSERT INTO `store_express` VALUES ('20', 'FEDEX(国外)', 'fedex', null, '0', '0', '0', '2017-09-13 16:06:56'); +INSERT INTO `store_express` VALUES ('21', '飞康达物流', 'feikangda', null, '0', '0', '0', '2017-09-13 16:07:03'); +INSERT INTO `store_express` VALUES ('22', '凤凰快递', 'fenghuangkuaidi', null, '0', '0', '0', '2017-09-13 16:07:10'); +INSERT INTO `store_express` VALUES ('23', '飞快达', 'feikuaida', null, '0', '0', '0', '2017-09-13 16:07:16'); +INSERT INTO `store_express` VALUES ('24', '国通快递', 'guotongkuaidi', null, '0', '0', '0', '2017-09-13 16:07:27'); +INSERT INTO `store_express` VALUES ('25', '港中能达物流', 'ganzhongnengda', null, '0', '0', '0', '2017-09-13 16:07:33'); +INSERT INTO `store_express` VALUES ('26', '广东邮政物流', 'guangdongyouzhengwuliu', null, '0', '0', '0', '2017-09-13 16:08:22'); +INSERT INTO `store_express` VALUES ('27', '共速达', 'gongsuda', null, '0', '0', '0', '2017-09-13 16:08:48'); +INSERT INTO `store_express` VALUES ('28', '汇通快运', 'huitongkuaidi', null, '0', '0', '0', '2017-09-13 16:08:56'); +INSERT INTO `store_express` VALUES ('29', '恒路物流', 'hengluwuliu', null, '0', '0', '0', '2017-09-13 16:09:02'); +INSERT INTO `store_express` VALUES ('30', '华夏龙物流', 'huaxialongwuliu', null, '0', '0', '0', '2017-09-13 16:09:12'); +INSERT INTO `store_express` VALUES ('31', '海红', 'haihongwangsong', null, '0', '0', '0', '2017-09-13 16:09:20'); +INSERT INTO `store_express` VALUES ('32', '海外环球', 'haiwaihuanqiu', null, '0', '0', '0', '2017-09-13 16:09:27'); +INSERT INTO `store_express` VALUES ('33', '佳怡物流', 'jiayiwuliu', null, '0', '0', '0', '2017-09-13 16:09:35'); +INSERT INTO `store_express` VALUES ('34', '京广速递', 'jinguangsudikuaijian', null, '0', '0', '0', '2017-09-13 16:09:42'); +INSERT INTO `store_express` VALUES ('35', '急先达', 'jixianda', null, '0', '0', '0', '2017-09-13 16:09:49'); +INSERT INTO `store_express` VALUES ('36', '佳吉物流', 'jjwl', null, '0', '0', '0', '2017-09-13 16:10:01'); +INSERT INTO `store_express` VALUES ('37', '加运美物流', 'jymwl', null, '0', '0', '0', '2017-09-13 16:10:13'); +INSERT INTO `store_express` VALUES ('38', '金大物流', 'jindawuliu', null, '0', '0', '0', '2017-09-13 16:10:22'); +INSERT INTO `store_express` VALUES ('39', '嘉里大通', 'jialidatong', null, '0', '0', '0', '2017-09-13 16:10:33'); +INSERT INTO `store_express` VALUES ('40', '晋越快递', 'jykd', null, '0', '0', '0', '2017-09-13 16:10:40'); +INSERT INTO `store_express` VALUES ('41', '快捷速递', 'kuaijiesudi', null, '0', '0', '0', '2017-09-13 16:10:49'); +INSERT INTO `store_express` VALUES ('42', '联邦快递(国内)', 'lianb', null, '0', '0', '0', '2017-09-13 16:10:58'); +INSERT INTO `store_express` VALUES ('43', '联昊通物流', 'lianhaowuliu', null, '0', '0', '0', '2017-09-13 16:11:07'); +INSERT INTO `store_express` VALUES ('44', '龙邦物流', 'longbanwuliu', null, '0', '0', '0', '2017-09-13 16:11:15'); +INSERT INTO `store_express` VALUES ('45', '立即送', 'lijisong', null, '0', '0', '0', '2017-09-13 16:11:25'); +INSERT INTO `store_express` VALUES ('46', '乐捷递', 'lejiedi', null, '0', '0', '0', '2017-09-13 16:11:36'); +INSERT INTO `store_express` VALUES ('47', '民航快递', 'minghangkuaidi', null, '0', '0', '0', '2017-09-13 16:11:45'); +INSERT INTO `store_express` VALUES ('48', '美国快递', 'meiguokuaidi', null, '0', '0', '0', '2017-09-13 16:11:53'); +INSERT INTO `store_express` VALUES ('49', '门对门', 'menduimen', null, '0', '0', '0', '2017-09-13 16:12:01'); +INSERT INTO `store_express` VALUES ('50', 'OCS', 'ocs', null, '0', '0', '0', '2017-09-13 16:12:10'); +INSERT INTO `store_express` VALUES ('51', '配思货运', 'peisihuoyunkuaidi', null, '0', '0', '0', '2017-09-13 16:12:18'); +INSERT INTO `store_express` VALUES ('52', '全晨快递', 'quanchenkuaidi', null, '0', '0', '0', '2017-09-13 16:12:26'); +INSERT INTO `store_express` VALUES ('53', '全峰快递', 'quanfengkuaidi', null, '0', '0', '0', '2017-09-13 16:12:34'); +INSERT INTO `store_express` VALUES ('54', '全际通物流', 'quanjitong', null, '0', '0', '0', '2017-09-13 16:12:41'); +INSERT INTO `store_express` VALUES ('55', '全日通快递', 'quanritongkuaidi', null, '0', '0', '0', '2017-09-13 16:12:49'); +INSERT INTO `store_express` VALUES ('56', '全一快递', 'quanyikuaidi', null, '0', '0', '0', '2017-09-13 16:12:56'); +INSERT INTO `store_express` VALUES ('57', '如风达', 'rufengda', null, '0', '0', '0', '2017-09-13 16:13:03'); +INSERT INTO `store_express` VALUES ('58', '三态速递', 'santaisudi', null, '0', '0', '0', '2017-09-13 16:13:15'); +INSERT INTO `store_express` VALUES ('59', '盛辉物流', 'shenghuiwuliu', null, '0', '0', '0', '2017-09-13 16:13:22'); +INSERT INTO `store_express` VALUES ('60', '申通', 'shentong', null, '0', '0', '0', '2017-09-13 16:13:34'); +INSERT INTO `store_express` VALUES ('61', '顺丰', 'shunfeng', '', '1', '0', '0', '2017-09-13 16:13:41'); +INSERT INTO `store_express` VALUES ('62', '速尔物流', 'sue', null, '0', '0', '0', '2017-09-13 16:13:48'); +INSERT INTO `store_express` VALUES ('63', '盛丰物流', 'shengfeng', null, '0', '0', '0', '2017-09-13 16:13:55'); +INSERT INTO `store_express` VALUES ('64', '赛澳递', 'saiaodi', null, '0', '0', '0', '2017-09-13 16:14:02'); +INSERT INTO `store_express` VALUES ('65', '天地华宇', 'tiandihuayu', null, '0', '0', '0', '2017-09-13 16:14:11'); +INSERT INTO `store_express` VALUES ('66', '天天快递', 'tiantian', null, '0', '0', '0', '2017-09-13 16:14:19'); +INSERT INTO `store_express` VALUES ('67', 'TNT', 'tnt', null, '0', '0', '0', '2017-09-13 16:14:26'); +INSERT INTO `store_express` VALUES ('68', 'UPS', 'ups', null, '0', '0', '0', '2017-09-13 16:14:29'); +INSERT INTO `store_express` VALUES ('69', '万家物流', 'wanjiawuliu', null, '0', '0', '0', '2017-09-13 16:14:37'); +INSERT INTO `store_express` VALUES ('70', '文捷航空速递', 'wenjiesudi', null, '0', '0', '0', '2017-09-13 16:14:46'); +INSERT INTO `store_express` VALUES ('71', '伍圆', 'wuyuan', null, '0', '0', '0', '2017-09-13 16:14:52'); +INSERT INTO `store_express` VALUES ('72', '万象物流', 'wxwl', null, '0', '0', '0', '2017-09-13 16:15:00'); +INSERT INTO `store_express` VALUES ('73', '新邦物流', 'xinbangwuliu', null, '0', '0', '0', '2017-09-13 16:15:06'); +INSERT INTO `store_express` VALUES ('74', '信丰物流', 'xinfengwuliu', null, '0', '0', '0', '2017-09-13 16:15:15'); +INSERT INTO `store_express` VALUES ('75', '亚风速递', 'yafengsudi', null, '0', '0', '0', '2017-09-13 16:15:23'); +INSERT INTO `store_express` VALUES ('76', '一邦速递', 'yibangwuliu', null, '0', '0', '0', '2017-09-13 16:15:30'); +INSERT INTO `store_express` VALUES ('77', '优速物流', 'youshuwuliu', null, '0', '0', '0', '2017-09-13 16:15:36'); +INSERT INTO `store_express` VALUES ('78', '邮政包裹挂号信', 'youzhengguonei', null, '0', '3', '0', '2017-09-13 16:15:44'); +INSERT INTO `store_express` VALUES ('79', '邮政国际包裹挂号信', 'youzhengguoji', null, '0', '2', '0', '2017-09-13 16:15:51'); +INSERT INTO `store_express` VALUES ('80', '远成物流', 'yuanchengwuliu', null, '0', '0', '0', '2017-09-13 16:15:57'); +INSERT INTO `store_express` VALUES ('81', '圆通速递', 'yuantong', null, '1', '1', '0', '2017-09-13 16:16:03'); +INSERT INTO `store_express` VALUES ('82', '源伟丰快递', 'yuanweifeng', null, '0', '0', '0', '2017-09-13 16:16:10'); +INSERT INTO `store_express` VALUES ('83', '元智捷诚快递', 'yuanzhijiecheng', null, '0', '0', '0', '2017-09-13 16:16:17'); +INSERT INTO `store_express` VALUES ('84', '韵达快运', 'yunda', null, '0', '0', '0', '2017-09-13 16:16:24'); +INSERT INTO `store_express` VALUES ('85', '运通快递', 'yuntongkuaidi', null, '0', '0', '0', '2017-09-13 16:16:33'); +INSERT INTO `store_express` VALUES ('86', '越丰物流', 'yuefengwuliu', null, '0', '0', '0', '2017-09-13 16:16:40'); +INSERT INTO `store_express` VALUES ('87', '源安达', 'yad', null, '0', '0', '0', '2017-09-13 16:16:47'); +INSERT INTO `store_express` VALUES ('88', '银捷速递', 'yinjiesudi', null, '0', '0', '0', '2017-09-13 16:16:56'); +INSERT INTO `store_express` VALUES ('89', '宅急送', 'zhaijisong', null, '0', '0', '0', '2017-09-13 16:17:03'); +INSERT INTO `store_express` VALUES ('90', '中铁快运', 'zhongtiekuaiyun', null, '0', '0', '0', '2017-09-13 16:17:10'); +INSERT INTO `store_express` VALUES ('91', '中通速递', 'zhongtong', '', '0', '0', '0', '2017-09-13 16:17:16'); +INSERT INTO `store_express` VALUES ('92', '中邮物流', 'zhongyouwuliu', null, '0', '0', '0', '2017-09-13 16:17:27'); +INSERT INTO `store_express` VALUES ('93', '忠信达', 'zhongxinda', null, '0', '0', '0', '2017-09-13 16:17:34'); +INSERT INTO `store_express` VALUES ('94', '芝麻开门', 'zhimakaimen', null, '0', '0', '0', '2017-09-13 16:17:41'); + +-- ---------------------------- +-- Table structure for store_goods +-- ---------------------------- +DROP TABLE IF EXISTS `store_goods`; +CREATE TABLE `store_goods` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `brand_id` bigint(20) unsigned DEFAULT '0' COMMENT '品牌ID', + `cate_id` bigint(20) unsigned DEFAULT '0' COMMENT '商品分类id', + `unit_id` bigint(20) DEFAULT NULL COMMENT '商品单位ID', + `spec_id` bigint(20) unsigned DEFAULT '0' COMMENT '规格ID', + `tags_id` varchar(255) DEFAULT '' COMMENT '商品标签ID', + `is_code` bigint(1) DEFAULT '1' COMMENT '是否有码商品', + `goods_title` varchar(255) DEFAULT '' COMMENT '商品标签', + `goods_content` text COMMENT '商品内容', + `goods_logo` varchar(255) DEFAULT '' COMMENT '商品LOGO', + `goods_image` text COMMENT '商品图片地址', + `goods_video` varchar(500) DEFAULT '' COMMENT '商品视频URL', + `goods_desc` varchar(500) DEFAULT '' COMMENT '商品描述', + `package_stock` bigint(20) unsigned DEFAULT '0' COMMENT '总库存数量', + `package_sale` bigint(20) unsigned DEFAULT '0' COMMENT '已销售数量', + `favorite_num` bigint(20) unsigned DEFAULT '0' COMMENT '收藏次数', + `sort` bigint(20) unsigned DEFAULT '0' COMMENT '数据排序', + `status` bigint(1) unsigned DEFAULT '1' COMMENT '商品状态(1有效,0无效)', + `is_deleted` bigint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城商品主表'; + +-- ---------------------------- +-- Records of store_goods +-- ---------------------------- + +-- ---------------------------- +-- Table structure for store_goods_brand +-- ---------------------------- +DROP TABLE IF EXISTS `store_goods_brand`; +CREATE TABLE `store_goods_brand` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `brand_logo` varchar(1024) DEFAULT '' COMMENT '品牌logo', + `brand_cover` varchar(1024) DEFAULT '' COMMENT '品牌封面', + `brand_title` varchar(255) DEFAULT '' COMMENT '商品品牌名称', + `brand_desc` text COMMENT '商品品牌描述', + `brand_detail` text COMMENT '品牌图文信息', + `sort` int(11) unsigned DEFAULT '0' COMMENT '商品分类排序', + `status` tinyint(1) unsigned DEFAULT '1' COMMENT '商品状态(1有效,0无效)', + `is_deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品品牌'; + +-- ---------------------------- +-- Records of store_goods_brand +-- ---------------------------- + +-- ---------------------------- +-- Table structure for store_goods_cate +-- ---------------------------- +DROP TABLE IF EXISTS `store_goods_cate`; +CREATE TABLE `store_goods_cate` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `pid` bigint(20) unsigned DEFAULT '0' COMMENT '上级分类编号', + `brand_id` bigint(20) DEFAULT '0' COMMENT '品牌ID', + `cate_title` varchar(255) DEFAULT '' COMMENT '商品分类名称', + `cate_desc` text COMMENT '商品分类', + `sort` bigint(20) unsigned DEFAULT '0' COMMENT '商品分类排序', + `status` bigint(1) unsigned DEFAULT '1' COMMENT '商品状态(1有效,0无效)', + `is_deleted` bigint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城商品分类'; + +-- ---------------------------- +-- Records of store_goods_cate +-- ---------------------------- + +-- ---------------------------- +-- Table structure for store_goods_list +-- ---------------------------- +DROP TABLE IF EXISTS `store_goods_list`; +CREATE TABLE `store_goods_list` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `goods_id` bigint(20) unsigned DEFAULT '0' COMMENT '商品ID', + `goods_spec` varchar(255) DEFAULT '' COMMENT '商品规格名称', + `goods_number` bigint(20) unsigned DEFAULT '0' COMMENT '商品礼品-商品数量', + `market_price` decimal(20,2) unsigned DEFAULT '0.00' COMMENT '销售价格', + `selling_price` decimal(20,2) unsigned DEFAULT '0.00' COMMENT '商品价格', + `goods_stock` bigint(20) unsigned DEFAULT '0' COMMENT '商品库存统计', + `goods_sale` bigint(20) unsigned DEFAULT '0' COMMENT '已销售数量', + `status` bigint(1) unsigned DEFAULT '1' COMMENT '商品状态(1有效,0无效)', + `is_deleted` bigint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城商品列表'; + +-- ---------------------------- +-- Records of store_goods_list +-- ---------------------------- + +-- ---------------------------- +-- Table structure for store_goods_spec +-- ---------------------------- +DROP TABLE IF EXISTS `store_goods_spec`; +CREATE TABLE `store_goods_spec` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `mch_id` bigint(20) unsigned DEFAULT '0' COMMENT '商户ID', + `spec_title` varchar(255) DEFAULT '' COMMENT '商品规格名称', + `spec_param` varchar(255) DEFAULT '' COMMENT '商品规格参数', + `spec_desc` varchar(255) DEFAULT '' COMMENT '商品规格描述', + `sort` bigint(20) unsigned DEFAULT '0' COMMENT '商品规格排序', + `status` bigint(1) unsigned DEFAULT '1' COMMENT '商品状态(1有效,0无效)', + `is_deleted` bigint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `index_store_goods_spec_mch_id` (`mch_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城商品规格'; + +-- ---------------------------- +-- Records of store_goods_spec +-- ---------------------------- + +-- ---------------------------- +-- Table structure for store_member +-- ---------------------------- +DROP TABLE IF EXISTS `store_member`; +CREATE TABLE `store_member` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `openid` varchar(100) DEFAULT '' COMMENT '会员微信OPENID', + `phone` varchar(16) DEFAULT '' COMMENT '会员手机号', + `password` varchar(64) NOT NULL DEFAULT '' COMMENT '登录密码', + `nickname` varchar(20) DEFAULT '' COMMENT '会员昵称', + `headimg` varchar(500) DEFAULT '' COMMENT '会员头像', + `sex` char(2) DEFAULT '' COMMENT '性别', + `level` tinyint(2) unsigned DEFAULT '1' COMMENT '会员级别', + `remark` varchar(500) DEFAULT '' COMMENT '会员个性签名', + `status` tinyint(1) unsigned DEFAULT '1' COMMENT '会员状态(1有效,0无效)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `index_store_member_openid` (`openid`) USING BTREE, + KEY `index_store_member_phone` (`phone`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城会员信息'; + +-- ---------------------------- +-- Records of store_member +-- ---------------------------- + +-- ---------------------------- +-- Table structure for store_member_address +-- ---------------------------- +DROP TABLE IF EXISTS `store_member_address`; +CREATE TABLE `store_member_address` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `mid` bigint(20) unsigned DEFAULT '0' COMMENT '会员ID', + `username` varchar(20) DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(16) DEFAULT '' COMMENT '收货手机号', + `province` varchar(50) DEFAULT '' COMMENT '收货地址省份', + `city` varchar(50) DEFAULT '' COMMENT '收货地址城市', + `area` varchar(255) DEFAULT '' COMMENT '收货地址区域', + `address` varchar(255) DEFAULT '' COMMENT '收货详细地址', + `status` tinyint(1) unsigned DEFAULT '1' COMMENT '状态(0无效, 1新快递)', + `is_default` tinyint(1) unsigned DEFAULT '1' COMMENT '默认收货地址', + `is_deleted` bigint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城会员收货地址'; + +-- ---------------------------- +-- Records of store_member_address +-- ---------------------------- + +-- ---------------------------- +-- Table structure for store_order +-- ---------------------------- +DROP TABLE IF EXISTS `store_order`; +CREATE TABLE `store_order` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `type` tinyint(1) unsigned DEFAULT '1' COMMENT '订单类型(1普通订单,2积分订单)', + `mid` bigint(20) unsigned DEFAULT '0' COMMENT '会员ID', + `order_no` char(10) DEFAULT '' COMMENT '订单号', + `freight_price` decimal(20,2) unsigned DEFAULT '0.00' COMMENT '快递费', + `goods_price` decimal(20,2) unsigned DEFAULT '0.00' COMMENT '商品总金额(不含快递费)', + `real_price` decimal(20,2) unsigned DEFAULT '0.00' COMMENT '真实支付金额(商品金额+快递金额)', + `is_pay` tinyint(1) unsigned DEFAULT '0' COMMENT '订单支付状态(0.未支付,1.已支付)', + `pay_type` varchar(255) DEFAULT '' COMMENT '支付方式 (wechat微信支付, alipay支付宝支付)', + `pay_no` varchar(100) DEFAULT '' COMMENT '商户交易号', + `pay_price` decimal(20,2) unsigned DEFAULT '0.00' COMMENT '实际支付金额', + `pay_at` datetime DEFAULT NULL COMMENT '支付时间', + `desc` text COMMENT '订单描述', + `status` bigint(1) unsigned DEFAULT '1' COMMENT '订单状态(0.无效,1.新订单,2.待发货,3.已发货,4.已收货,5.已完成,6.已退货及退款)', + `is_deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `index_store_order_order_no` (`order_no`) USING BTREE, + KEY `index_store_order_status` (`status`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城订单主表'; + +-- ---------------------------- +-- Records of store_order +-- ---------------------------- + +-- ---------------------------- +-- Table structure for store_order_express +-- ---------------------------- +DROP TABLE IF EXISTS `store_order_express`; +CREATE TABLE `store_order_express` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `mid` bigint(20) unsigned DEFAULT '0' COMMENT '会员ID', + `type` tinyint(1) unsigned DEFAULT '0' COMMENT '快递类型(0.订单快递,1.退货快递)', + `order_no` char(10) DEFAULT '' COMMENT '订单编号', + `company_title` varchar(50) DEFAULT '' COMMENT '物流公司名称', + `company_code` varchar(50) DEFAULT '' COMMENT '物流公司编码', + `username` varchar(20) DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(16) DEFAULT '' COMMENT '收货手机号', + `province` varchar(50) DEFAULT NULL COMMENT '收货地址省份', + `city` varchar(50) DEFAULT '' COMMENT '收货地址城市', + `area` varchar(255) DEFAULT '' COMMENT '收货地址区域', + `address` varchar(255) DEFAULT '' COMMENT '收货详细地址', + `send_no` varchar(50) DEFAULT '' COMMENT '实际物流公司单号', + `send_company_title` varchar(50) DEFAULT '' COMMENT '实际发货快递公司', + `send_company_code` varchar(50) DEFAULT '' COMMENT '实际发货快递代码', + `send_username` varchar(255) DEFAULT '' COMMENT '寄件人名称', + `send_phone` varchar(16) DEFAULT '' COMMENT '寄件人手机号', + `send_province` varchar(50) DEFAULT '' COMMENT '寄件人地址省份', + `send_city` varchar(50) DEFAULT '' COMMENT '寄件人地址城市', + `send_area` varchar(255) DEFAULT '' COMMENT '寄件人地址区域', + `send_address` varchar(255) DEFAULT '' COMMENT '寄件人详细地址', + `send_at` datetime DEFAULT NULL COMMENT '快递发货时间', + `desc` text COMMENT '发货描述', + `status` tinyint(1) unsigned DEFAULT '1' COMMENT '状态(0.无效,1.新快递,2.已签收,3.签收失败)', + `is_deleted` bigint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城订单快递'; + +-- ---------------------------- +-- Records of store_order_express +-- ---------------------------- + +-- ---------------------------- +-- Table structure for store_order_goods +-- ---------------------------- +DROP TABLE IF EXISTS `store_order_goods`; +CREATE TABLE `store_order_goods` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `mid` bigint(20) unsigned DEFAULT '0' COMMENT '会员ID', + `order_no` char(10) DEFAULT '' COMMENT '订单编号', + `goods_id` bigint(20) DEFAULT '0' COMMENT '商品ID', + `goods_title` varchar(255) DEFAULT '' COMMENT '商品标签', + `goods_spec` varchar(255) DEFAULT '' COMMENT '商品规格', + `goods_logo` varchar(255) DEFAULT '' COMMENT '商品LOGO', + `goods_image` text COMMENT '商品图片地址', + `market_price` decimal(20,2) unsigned DEFAULT '0.00' COMMENT '市场价格', + `selling_price` decimal(20,2) unsigned DEFAULT '0.00' COMMENT '商品销售价格', + `price_field` varchar(20) DEFAULT 'selling_price' COMMENT '计价字段', + `number` bigint(20) unsigned DEFAULT '0' COMMENT '订单商品数量', + `status` bigint(1) unsigned DEFAULT '1' COMMENT '商品状态(1有效,0无效)', + `is_deleted` bigint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `index_store_order_list_order_no` (`order_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城订单关联商品'; + +-- ---------------------------- +-- Records of store_order_goods +-- ---------------------------- + +-- ---------------------------- +-- Table structure for system_auth +-- ---------------------------- +DROP TABLE IF EXISTS `system_auth`; +CREATE TABLE `system_auth` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(20) NOT NULL COMMENT '权限名称', + `status` tinyint(1) unsigned DEFAULT '1' COMMENT '状态(1:禁用,2:启用)', + `sort` smallint(6) unsigned DEFAULT '0' COMMENT '排序权重', + `desc` varchar(255) DEFAULT NULL COMMENT '备注说明', + `create_by` bigint(11) unsigned DEFAULT '0' COMMENT '创建人', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `index_system_auth_title` (`title`) USING BTREE, + KEY `index_system_auth_status` (`status`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统权限表'; + +-- ---------------------------- +-- Records of system_auth +-- ---------------------------- + +-- ---------------------------- +-- Table structure for system_auth_node +-- ---------------------------- +DROP TABLE IF EXISTS `system_auth_node`; +CREATE TABLE `system_auth_node` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `auth` bigint(20) unsigned DEFAULT NULL COMMENT '角色ID', + `node` varchar(200) DEFAULT NULL COMMENT '节点路径', + PRIMARY KEY (`id`), + KEY `index_system_auth_auth` (`auth`) USING BTREE, + KEY `index_system_auth_node` (`node`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统角色与节点绑定'; + +-- ---------------------------- +-- Records of system_auth_node +-- ---------------------------- + +-- ---------------------------- +-- Table structure for system_config +-- ---------------------------- +DROP TABLE IF EXISTS `system_config`; +CREATE TABLE `system_config` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(100) DEFAULT NULL COMMENT '配置编码', + `value` varchar(500) DEFAULT NULL COMMENT '配置值', + PRIMARY KEY (`id`), + KEY `index_system_config_name` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8 COMMENT='系统参数配置'; + +-- ---------------------------- +-- Records of system_config +-- ---------------------------- +INSERT INTO `system_config` VALUES ('1', 'app_name', 'ThinkAdmin'); +INSERT INTO `system_config` VALUES ('2', 'site_name', 'ThinkAdmin'); +INSERT INTO `system_config` VALUES ('3', 'app_version', '3.0 dev'); +INSERT INTO `system_config` VALUES ('4', 'site_copy', '©版权所有 2014-2018 楚才科技'); +INSERT INTO `system_config` VALUES ('5', 'browser_icon', 'http://localhost/ThinkAdmin/static/upload/f47b8fe06e38ae99/08e8398da45583b9.png'); +INSERT INTO `system_config` VALUES ('6', 'tongji_baidu_key', ''); +INSERT INTO `system_config` VALUES ('7', 'miitbeian', '粤ICP备16006642号-2'); +INSERT INTO `system_config` VALUES ('8', 'storage_type', 'local'); +INSERT INTO `system_config` VALUES ('9', 'storage_local_exts', 'png,jpg,rar,doc,icon,mp4'); +INSERT INTO `system_config` VALUES ('10', 'storage_qiniu_bucket', ''); +INSERT INTO `system_config` VALUES ('11', 'storage_qiniu_domain', ''); +INSERT INTO `system_config` VALUES ('12', 'storage_qiniu_access_key', ''); +INSERT INTO `system_config` VALUES ('13', 'storage_qiniu_secret_key', ''); +INSERT INTO `system_config` VALUES ('14', 'storage_oss_bucket', 'cuci'); +INSERT INTO `system_config` VALUES ('15', 'storage_oss_endpoint', 'oss-cn-beijing.aliyuncs.com'); +INSERT INTO `system_config` VALUES ('16', 'storage_oss_domain', 'cuci.oss-cn-beijing.aliyuncs.com'); +INSERT INTO `system_config` VALUES ('17', 'storage_oss_keyid', '用你自己的吧'); +INSERT INTO `system_config` VALUES ('18', 'storage_oss_secret', '用你自己的吧'); +INSERT INTO `system_config` VALUES ('34', 'wechat_appid', 'wx60a43dd8161666d4'); +INSERT INTO `system_config` VALUES ('35', 'wechat_appkey', '9890a0d7c91801a609d151099e95b61a'); +INSERT INTO `system_config` VALUES ('36', 'storage_oss_is_https', 'http'); +INSERT INTO `system_config` VALUES ('37', 'wechat_type', 'thr'); +INSERT INTO `system_config` VALUES ('38', 'wechat_token', 'test'); +INSERT INTO `system_config` VALUES ('39', 'wechat_appsecret', 'a041bec98ed015d52b99acea5c6a16ef'); +INSERT INTO `system_config` VALUES ('40', 'wechat_encodingaeskey', 'BJIUzE0gqlWy0GxfPp4J1oPTBmOrNDIGPNav1YFH5Z5'); +INSERT INTO `system_config` VALUES ('41', 'wechat_thr_appid', 'wx60a43dd8161666d4'); +INSERT INTO `system_config` VALUES ('42', 'wechat_thr_appkey', '05db2aa335382c66ab56d69b1a9ad0ee'); + +-- ---------------------------- +-- Table structure for system_log +-- ---------------------------- +DROP TABLE IF EXISTS `system_log`; +CREATE TABLE `system_log` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `ip` char(15) NOT NULL DEFAULT '' COMMENT '操作者IP地址', + `node` char(200) NOT NULL DEFAULT '' COMMENT '当前操作节点', + `username` varchar(32) NOT NULL DEFAULT '' COMMENT '操作人用户名', + `action` varchar(200) NOT NULL DEFAULT '' COMMENT '操作行为', + `content` text NOT NULL COMMENT '操作内容描述', + `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统操作日志表'; + +-- ---------------------------- +-- Records of system_log +-- ---------------------------- + +-- ---------------------------- +-- Table structure for system_menu +-- ---------------------------- +DROP TABLE IF EXISTS `system_menu`; +CREATE TABLE `system_menu` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `pid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '父id', + `title` varchar(100) NOT NULL DEFAULT '' COMMENT '名称', + `node` varchar(200) NOT NULL DEFAULT '' COMMENT '节点代码', + `icon` varchar(100) NOT NULL DEFAULT '' COMMENT '菜单图标', + `url` varchar(400) NOT NULL DEFAULT '' COMMENT '链接', + `params` varchar(500) DEFAULT '' COMMENT '链接参数', + `target` varchar(20) NOT NULL DEFAULT '_self' COMMENT '链接打开方式', + `sort` int(11) unsigned DEFAULT '0' COMMENT '菜单排序', + `status` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '状态(0:禁用,1:启用)', + `create_by` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '创建人', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `index_system_menu_node` (`node`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8 COMMENT='系统菜单表'; + +-- ---------------------------- +-- Records of system_menu +-- ---------------------------- +INSERT INTO `system_menu` VALUES ('1', '0', '系统设置', '', '', '#', '', '_self', '9000', '1', '10000', '2018-01-19 15:27:00'); +INSERT INTO `system_menu` VALUES ('2', '10', '后台菜单', '', 'fa fa-leaf', 'admin/menu/index', '', '_self', '10', '1', '10000', '2018-01-19 15:27:17'); +INSERT INTO `system_menu` VALUES ('3', '10', '系统参数', '', 'fa fa-modx', 'admin/config/index', '', '_self', '20', '1', '10000', '2018-01-19 15:27:57'); +INSERT INTO `system_menu` VALUES ('4', '11', '访问授权', '', 'fa fa-group', 'admin/auth/index', '', '_self', '20', '1', '10000', '2018-01-22 11:13:02'); +INSERT INTO `system_menu` VALUES ('5', '11', '用户管理', '', 'fa fa-user', 'admin/user/index', '', '_self', '10', '1', '0', '2018-01-23 12:15:12'); +INSERT INTO `system_menu` VALUES ('6', '11', '访问节点', '', 'fa fa-fort-awesome', 'admin/node/index', '', '_self', '30', '1', '0', '2018-01-23 12:36:54'); +INSERT INTO `system_menu` VALUES ('7', '0', '后台首页', '', '', 'admin/index/main', '', '_self', '1000', '1', '0', '2018-01-23 13:42:30'); +INSERT INTO `system_menu` VALUES ('8', '16', '系统日志', '', 'fa fa-code', 'admin/log/index', '', '_self', '10', '1', '0', '2018-01-24 13:52:58'); +INSERT INTO `system_menu` VALUES ('9', '10', '文件存储', '', 'fa fa-stop-circle', 'admin/config/file', '', '_self', '30', '1', '0', '2018-01-25 10:54:28'); +INSERT INTO `system_menu` VALUES ('10', '1', '系统管理', '', '', '#', '', '_self', '200', '1', '0', '2018-01-25 18:14:28'); +INSERT INTO `system_menu` VALUES ('11', '1', '访问权限', '', '', '#', '', '_self', '300', '1', '0', '2018-01-25 18:15:08'); +INSERT INTO `system_menu` VALUES ('16', '1', '日志管理', '', '', '#', '', '_self', '400', '1', '0', '2018-02-10 16:31:15'); +INSERT INTO `system_menu` VALUES ('17', '0', '微信管理', '', '', '#', '', '_self', '8000', '1', '0', '2018-03-06 14:42:49'); +INSERT INTO `system_menu` VALUES ('18', '17', '公众号配置', '', '', '#', '', '_self', '0', '1', '0', '2018-03-06 14:43:05'); +INSERT INTO `system_menu` VALUES ('19', '18', '微信授权绑定', '', 'fa fa-cog', 'wechat/config/index', '', '_self', '0', '1', '0', '2018-03-06 14:43:26'); +INSERT INTO `system_menu` VALUES ('20', '18', '关注默认回复', '', 'fa fa-comment-o', 'wechat/keys/subscribe', '', '_self', '0', '1', '0', '2018-03-06 14:44:45'); +INSERT INTO `system_menu` VALUES ('21', '18', '无反馈默认回复', '', 'fa fa-commenting', 'wechat/keys/defaults', '', '_self', '0', '1', '0', '2018-03-06 14:45:55'); +INSERT INTO `system_menu` VALUES ('22', '18', '微信关键字管理', '', 'fa fa-hashtag', 'wechat/keys/index', '', '_self', '0', '1', '0', '2018-03-06 14:46:23'); +INSERT INTO `system_menu` VALUES ('23', '17', '微信服务定制', '', '', '#', '', '_self', '0', '1', '0', '2018-03-06 14:47:11'); +INSERT INTO `system_menu` VALUES ('24', '23', '微信菜单管理', '', 'fa fa-gg-circle', 'wechat/menu/index', '', '_self', '0', '1', '0', '2018-03-06 14:47:39'); +INSERT INTO `system_menu` VALUES ('25', '23', '微信图文管理', '', 'fa fa-map-o', 'wechat/news/index', '', '_self', '0', '1', '0', '2018-03-06 14:48:14'); +INSERT INTO `system_menu` VALUES ('26', '17', '微信粉丝管理', '', 'fa fa-user', '#', '', '_self', '0', '1', '0', '2018-03-06 14:48:33'); +INSERT INTO `system_menu` VALUES ('27', '26', '微信粉丝列表', '', 'fa fa-users', 'wechat/fans/index', '', '_self', '20', '1', '0', '2018-03-06 14:49:04'); +INSERT INTO `system_menu` VALUES ('28', '26', '微信黑名单管理', '', 'fa fa-user-times', 'wechat/fans_block/index', '', '_self', '30', '1', '0', '2018-03-06 14:49:22'); +INSERT INTO `system_menu` VALUES ('29', '26', '微信标签管理', '', 'fa fa-tags', 'wechat/tags/index', '', '_self', '10', '1', '0', '2018-03-06 14:49:39'); +INSERT INTO `system_menu` VALUES ('32', '0', '商城管理', '', '', '#', '', '_self', '2000', '1', '0', '2018-03-20 16:46:07'); +INSERT INTO `system_menu` VALUES ('33', '32', '商品管理', '', '', '#', '', '_self', '0', '1', '0', '2018-03-20 16:46:21'); +INSERT INTO `system_menu` VALUES ('34', '33', '产品管理', '', 'fa fa-modx', 'store/goods/index', '', '_self', '0', '1', '0', '2018-03-20 16:46:45'); +INSERT INTO `system_menu` VALUES ('35', '33', '规格管理', '', 'fa fa-hashtag', 'store/goods_spec/index', '', '_self', '0', '1', '0', '2018-03-20 16:47:08'); +INSERT INTO `system_menu` VALUES ('36', '33', '分类管理', '', 'fa fa-product-hunt', 'store/goods_cate/index', '', '_self', '0', '1', '0', '2018-03-20 16:47:50'); +INSERT INTO `system_menu` VALUES ('37', '33', '品牌管理', '', 'fa fa-scribd', 'store/goods_brand/index', '', '_self', '0', '1', '0', '2018-03-20 16:48:05'); +INSERT INTO `system_menu` VALUES ('38', '32', '订单管理', '', '', '#', '', '_self', '0', '1', '0', '2018-04-21 15:07:36'); +INSERT INTO `system_menu` VALUES ('39', '38', '订单列表', '', 'fa fa-adjust', 'store/order/index', '', '_self', '0', '1', '0', '2018-04-21 15:07:54'); +INSERT INTO `system_menu` VALUES ('40', '32', '商城配置', '', '', '#', '', '_self', '0', '1', '0', '2018-04-21 15:08:17'); +INSERT INTO `system_menu` VALUES ('41', '40', '参数管理', '', '', '#', '', '_self', '0', '0', '0', '2018-04-21 15:08:25'); +INSERT INTO `system_menu` VALUES ('42', '40', '快递公司', '', 'fa fa-mixcloud', 'store/express/index', '', '_self', '0', '1', '0', '2018-04-21 15:08:50'); + +-- ---------------------------- +-- Table structure for system_node +-- ---------------------------- +DROP TABLE IF EXISTS `system_node`; +CREATE TABLE `system_node` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `node` varchar(100) DEFAULT NULL COMMENT '节点代码', + `title` varchar(500) DEFAULT NULL COMMENT '节点标题', + `is_menu` tinyint(1) unsigned DEFAULT '0' COMMENT '是否可设置为菜单', + `is_auth` tinyint(1) unsigned DEFAULT '1' COMMENT '是否启动RBAC权限控制', + `is_login` tinyint(1) unsigned DEFAULT '1' COMMENT '是否启动登录控制', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `index_system_node_node` (`node`) +) ENGINE=InnoDB AUTO_INCREMENT=145 DEFAULT CHARSET=utf8 COMMENT='系统节点表'; + +-- ---------------------------- +-- Records of system_node +-- ---------------------------- +INSERT INTO `system_node` VALUES ('13', 'admin', '系统设置', '0', '1', '1', '2018-05-04 11:02:34'); +INSERT INTO `system_node` VALUES ('14', 'admin/auth', '权限管理', '0', '1', '1', '2018-05-04 11:06:55'); +INSERT INTO `system_node` VALUES ('15', 'admin/auth/index', '权限列表', '1', '1', '1', '2018-05-04 11:06:56'); +INSERT INTO `system_node` VALUES ('16', 'admin/auth/apply', '权限配置', '0', '1', '1', '2018-05-04 11:06:56'); +INSERT INTO `system_node` VALUES ('17', 'admin/auth/add', '添加权限', '0', '1', '1', '2018-05-04 11:06:56'); +INSERT INTO `system_node` VALUES ('18', 'admin/auth/edit', '编辑权限', '0', '1', '1', '2018-05-04 11:06:56'); +INSERT INTO `system_node` VALUES ('19', 'admin/auth/forbid', '禁用权限', '0', '1', '1', '2018-05-04 11:06:56'); +INSERT INTO `system_node` VALUES ('20', 'admin/auth/resume', '启用权限', '0', '1', '1', '2018-05-04 11:06:56'); +INSERT INTO `system_node` VALUES ('21', 'admin/auth/del', '删除权限', '0', '1', '1', '2018-05-04 11:06:56'); +INSERT INTO `system_node` VALUES ('22', 'admin/config', '系统配置', '0', '1', '1', '2018-05-04 11:08:18'); +INSERT INTO `system_node` VALUES ('23', 'admin/config/index', '系统参数', '1', '1', '1', '2018-05-04 11:08:25'); +INSERT INTO `system_node` VALUES ('24', 'admin/config/file', '文件存储', '1', '1', '1', '2018-05-04 11:08:27'); +INSERT INTO `system_node` VALUES ('25', 'admin/log', '日志管理', '0', '1', '1', '2018-05-04 11:08:43'); +INSERT INTO `system_node` VALUES ('26', 'admin/log/index', '日志管理', '1', '1', '1', '2018-05-04 11:08:43'); +INSERT INTO `system_node` VALUES ('28', 'admin/log/del', '日志删除', '0', '1', '1', '2018-05-04 11:08:43'); +INSERT INTO `system_node` VALUES ('29', 'admin/menu', '系统菜单', '0', '1', '1', '2018-05-04 11:09:54'); +INSERT INTO `system_node` VALUES ('30', 'admin/menu/index', '菜单列表', '1', '1', '1', '2018-05-04 11:09:54'); +INSERT INTO `system_node` VALUES ('31', 'admin/menu/add', '添加菜单', '0', '1', '1', '2018-05-04 11:09:55'); +INSERT INTO `system_node` VALUES ('32', 'admin/menu/edit', '编辑菜单', '0', '1', '1', '2018-05-04 11:09:55'); +INSERT INTO `system_node` VALUES ('33', 'admin/menu/del', '删除菜单', '0', '1', '1', '2018-05-04 11:09:55'); +INSERT INTO `system_node` VALUES ('34', 'admin/menu/forbid', '禁用菜单', '0', '1', '1', '2018-05-04 11:09:55'); +INSERT INTO `system_node` VALUES ('35', 'admin/menu/resume', '启用菜单', '0', '1', '1', '2018-05-04 11:09:55'); +INSERT INTO `system_node` VALUES ('36', 'admin/node', '节点管理', '0', '1', '1', '2018-05-04 11:10:20'); +INSERT INTO `system_node` VALUES ('37', 'admin/node/index', '节点列表', '1', '1', '1', '2018-05-04 11:10:20'); +INSERT INTO `system_node` VALUES ('38', 'admin/node/clear', '清理节点', '0', '1', '1', '2018-05-04 11:10:21'); +INSERT INTO `system_node` VALUES ('39', 'admin/node/save', '更新节点', '0', '1', '1', '2018-05-04 11:10:21'); +INSERT INTO `system_node` VALUES ('40', 'admin/user', '系统用户', '0', '1', '1', '2018-05-04 11:10:43'); +INSERT INTO `system_node` VALUES ('41', 'admin/user/index', '用户列表', '1', '1', '1', '2018-05-04 11:10:43'); +INSERT INTO `system_node` VALUES ('42', 'admin/user/auth', '用户授权', '0', '1', '1', '2018-05-04 11:10:43'); +INSERT INTO `system_node` VALUES ('43', 'admin/user/add', '添加用户', '0', '1', '1', '2018-05-04 11:10:43'); +INSERT INTO `system_node` VALUES ('44', 'admin/user/edit', '编辑用户', '0', '1', '1', '2018-05-04 11:10:43'); +INSERT INTO `system_node` VALUES ('45', 'admin/user/pass', '修改密码', '0', '1', '1', '2018-05-04 11:10:43'); +INSERT INTO `system_node` VALUES ('46', 'admin/user/del', '删除用户', '0', '1', '1', '2018-05-04 11:10:43'); +INSERT INTO `system_node` VALUES ('47', 'admin/user/forbid', '禁用启用', '0', '1', '1', '2018-05-04 11:10:43'); +INSERT INTO `system_node` VALUES ('48', 'admin/user/resume', '启用用户', '0', '1', '1', '2018-05-04 11:10:44'); +INSERT INTO `system_node` VALUES ('49', 'store', '商城管理', '0', '1', '1', '2018-05-04 11:11:28'); +INSERT INTO `system_node` VALUES ('50', 'store/express', '快递公司管理', '0', '1', '1', '2018-05-04 11:11:39'); +INSERT INTO `system_node` VALUES ('51', 'store/express/index', '快递公司列表', '1', '1', '1', '2018-05-04 11:11:39'); +INSERT INTO `system_node` VALUES ('52', 'store/express/add', '添加快递公司', '0', '1', '1', '2018-05-04 11:11:39'); +INSERT INTO `system_node` VALUES ('53', 'store/express/edit', '编辑快递公司', '0', '1', '1', '2018-05-04 11:11:39'); +INSERT INTO `system_node` VALUES ('54', 'store/express/del', '删除快递公司', '0', '1', '1', '2018-05-04 11:11:39'); +INSERT INTO `system_node` VALUES ('55', 'store/express/forbid', '禁用快递公司', '0', '1', '1', '2018-05-04 11:11:39'); +INSERT INTO `system_node` VALUES ('56', 'store/express/resume', '启用快递公司', '0', '1', '1', '2018-05-04 11:11:40'); +INSERT INTO `system_node` VALUES ('57', 'store/order', '订单管理', '0', '1', '1', '2018-05-04 11:12:14'); +INSERT INTO `system_node` VALUES ('58', 'store/order/index', '订单列表', '1', '1', '1', '2018-05-04 11:12:17'); +INSERT INTO `system_node` VALUES ('59', 'store/order/address', '修改地址', '0', '1', '1', '2018-05-04 11:12:19'); +INSERT INTO `system_node` VALUES ('76', 'wechat', '微信管理', '0', '1', '1', '2018-05-04 11:14:59'); +INSERT INTO `system_node` VALUES ('78', 'wechat/config', '微信对接管理', '0', '1', '1', '2018-05-04 11:16:20'); +INSERT INTO `system_node` VALUES ('79', 'wechat/config/index', '微信对接配置', '1', '1', '1', '2018-05-04 11:16:23'); +INSERT INTO `system_node` VALUES ('80', 'wechat/fans', '微信粉丝管理', '0', '1', '1', '2018-05-04 11:16:31'); +INSERT INTO `system_node` VALUES ('81', 'wechat/fans/index', '微信粉丝列表', '1', '1', '1', '2018-05-04 11:16:32'); +INSERT INTO `system_node` VALUES ('82', 'wechat/fans/backadd', '微信粉丝拉黑', '0', '1', '1', '2018-05-04 11:16:32'); +INSERT INTO `system_node` VALUES ('83', 'wechat/fans/tagset', '设置粉丝标签', '0', '1', '1', '2018-05-04 11:16:32'); +INSERT INTO `system_node` VALUES ('84', 'wechat/fans/tagadd', '添加粉丝标签', '0', '1', '1', '2018-05-04 11:16:32'); +INSERT INTO `system_node` VALUES ('85', 'wechat/fans/tagdel', '删除粉丝标签', '0', '1', '1', '2018-05-04 11:16:32'); +INSERT INTO `system_node` VALUES ('86', 'wechat/fans/sync', '同步粉丝列表', '0', '1', '1', '2018-05-04 11:16:32'); +INSERT INTO `system_node` VALUES ('87', 'wechat/fans_block', '粉丝黑名单管理', '0', '1', '1', '2018-05-04 11:17:25'); +INSERT INTO `system_node` VALUES ('88', 'wechat/fans_block/index', '粉丝黑名单列表', '1', '1', '1', '2018-05-04 11:17:50'); +INSERT INTO `system_node` VALUES ('89', 'wechat/fans_block/backdel', '移除粉丝黑名单', '0', '1', '1', '2018-05-04 11:17:51'); +INSERT INTO `system_node` VALUES ('90', 'wechat/keys', '微信关键字', '0', '1', '1', '2018-05-04 11:18:09'); +INSERT INTO `system_node` VALUES ('91', 'wechat/keys/index', '关键字列表', '1', '1', '1', '2018-05-04 11:18:09'); +INSERT INTO `system_node` VALUES ('92', 'wechat/keys/add', '添加关键字', '0', '1', '1', '2018-05-04 11:18:09'); +INSERT INTO `system_node` VALUES ('93', 'wechat/keys/edit', '编辑关键字', '0', '1', '1', '2018-05-04 11:18:09'); +INSERT INTO `system_node` VALUES ('94', 'wechat/keys/del', '删除关键字', '0', '1', '1', '2018-05-04 11:18:09'); +INSERT INTO `system_node` VALUES ('95', 'wechat/keys/forbid', '禁用关键字', '0', '1', '1', '2018-05-04 11:18:09'); +INSERT INTO `system_node` VALUES ('96', 'wechat/keys/resume', '启用关键字', '0', '1', '1', '2018-05-04 11:18:09'); +INSERT INTO `system_node` VALUES ('97', 'wechat/keys/subscribe', '关注回复', '1', '1', '1', '2018-05-04 11:18:09'); +INSERT INTO `system_node` VALUES ('98', 'wechat/keys/defaults', '默认回复', '1', '1', '1', '2018-05-04 11:18:09'); +INSERT INTO `system_node` VALUES ('99', 'wechat/menu', '微信菜单管理', '0', '1', '1', '2018-05-04 11:18:57'); +INSERT INTO `system_node` VALUES ('100', 'wechat/menu/index', '微信菜单展示', '1', '1', '1', '2018-05-04 11:19:10'); +INSERT INTO `system_node` VALUES ('101', 'wechat/menu/edit', '编辑微信菜单', '0', '1', '1', '2018-05-04 11:19:22'); +INSERT INTO `system_node` VALUES ('102', 'wechat/menu/cancel', '取消微信菜单', '0', '1', '1', '2018-05-04 11:19:26'); +INSERT INTO `system_node` VALUES ('103', 'wechat/news/index', '微信图文列表', '1', '1', '1', '2018-05-04 11:19:28'); +INSERT INTO `system_node` VALUES ('104', 'wechat/news/select', '微信图文选择', '0', '1', '1', '2018-05-04 11:19:28'); +INSERT INTO `system_node` VALUES ('105', 'wechat/news/image', '微信图片选择', '0', '1', '1', '2018-05-04 11:19:28'); +INSERT INTO `system_node` VALUES ('106', 'wechat/news/add', '添加微信图文', '0', '1', '1', '2018-05-04 11:19:28'); +INSERT INTO `system_node` VALUES ('107', 'wechat/news/edit', '编辑微信图文', '0', '1', '1', '2018-05-04 11:19:28'); +INSERT INTO `system_node` VALUES ('108', 'wechat/news/del', '删除微信图文', '0', '1', '1', '2018-05-04 11:19:28'); +INSERT INTO `system_node` VALUES ('109', 'wechat/news/push', '推送微信图文', '0', '1', '1', '2018-05-04 11:19:28'); +INSERT INTO `system_node` VALUES ('110', 'wechat/news', '微信图文管理', '0', '1', '1', '2018-05-04 11:19:35'); +INSERT INTO `system_node` VALUES ('111', 'wechat/tags', '微信粉丝标签管理', '0', '1', '1', '2018-05-04 11:20:28'); +INSERT INTO `system_node` VALUES ('112', 'wechat/tags/index', '粉丝标签列表', '1', '1', '1', '2018-05-04 11:20:28'); +INSERT INTO `system_node` VALUES ('113', 'wechat/tags/add', '添加粉丝标签', '0', '1', '1', '2018-05-04 11:20:28'); +INSERT INTO `system_node` VALUES ('114', 'wechat/tags/edit', '编辑粉丝标签', '0', '1', '1', '2018-05-04 11:20:29'); +INSERT INTO `system_node` VALUES ('115', 'wechat/tags/del', '删除粉丝标签', '0', '1', '1', '2018-05-04 11:20:29'); +INSERT INTO `system_node` VALUES ('116', 'wechat/tags/sync', '同步粉丝标签', '0', '1', '1', '2018-05-04 11:20:29'); +INSERT INTO `system_node` VALUES ('117', 'store/goods', '商品管理', '0', '1', '1', '2018-05-04 11:29:55'); +INSERT INTO `system_node` VALUES ('118', 'store/goods/index', '商品列表', '1', '1', '1', '2018-05-04 11:29:56'); +INSERT INTO `system_node` VALUES ('119', 'store/goods/add', '添加商品', '0', '1', '1', '2018-05-04 11:29:56'); +INSERT INTO `system_node` VALUES ('120', 'store/goods/edit', '编辑商品', '0', '1', '1', '2018-05-04 11:29:56'); +INSERT INTO `system_node` VALUES ('121', 'store/goods/del', '删除商品', '0', '1', '1', '2018-05-04 11:29:56'); +INSERT INTO `system_node` VALUES ('122', 'store/goods/forbid', '下架商品', '0', '1', '1', '2018-05-04 11:29:56'); +INSERT INTO `system_node` VALUES ('123', 'store/goods/resume', '上架商品', '0', '1', '1', '2018-05-04 11:29:57'); +INSERT INTO `system_node` VALUES ('124', 'store/goods_brand', '商品品牌管理', '0', '1', '1', '2018-05-04 11:30:44'); +INSERT INTO `system_node` VALUES ('125', 'store/goods_brand/index', '商品品牌列表', '1', '1', '1', '2018-05-04 11:30:52'); +INSERT INTO `system_node` VALUES ('126', 'store/goods_brand/add', '添加商品品牌', '0', '1', '1', '2018-05-04 11:30:55'); +INSERT INTO `system_node` VALUES ('127', 'store/goods_brand/edit', '编辑商品品牌', '0', '1', '1', '2018-05-04 11:30:56'); +INSERT INTO `system_node` VALUES ('128', 'store/goods_brand/del', '删除商品品牌', '0', '1', '1', '2018-05-04 11:30:56'); +INSERT INTO `system_node` VALUES ('129', 'store/goods_brand/forbid', '禁用商品品牌', '0', '1', '1', '2018-05-04 11:30:56'); +INSERT INTO `system_node` VALUES ('130', 'store/goods_brand/resume', '启用商品品牌', '0', '1', '1', '2018-05-04 11:30:56'); +INSERT INTO `system_node` VALUES ('131', 'store/goods_cate', '商品分类管理', '0', '1', '1', '2018-05-04 11:31:19'); +INSERT INTO `system_node` VALUES ('132', 'store/goods_cate/index', '商品分类列表', '1', '1', '1', '2018-05-04 11:31:23'); +INSERT INTO `system_node` VALUES ('133', 'store/goods_cate/add', '添加商品分类', '0', '1', '1', '2018-05-04 11:31:23'); +INSERT INTO `system_node` VALUES ('134', 'store/goods_cate/edit', '编辑商品分类', '0', '1', '1', '2018-05-04 11:31:23'); +INSERT INTO `system_node` VALUES ('135', 'store/goods_cate/del', '删除商品分类', '0', '1', '1', '2018-05-04 11:31:24'); +INSERT INTO `system_node` VALUES ('136', 'store/goods_cate/forbid', '禁用商品分类', '0', '1', '1', '2018-05-04 11:31:24'); +INSERT INTO `system_node` VALUES ('137', 'store/goods_cate/resume', '启用商品分类', '0', '1', '1', '2018-05-04 11:31:24'); +INSERT INTO `system_node` VALUES ('138', 'store/goods_spec', '商品规格管理', '0', '1', '1', '2018-05-04 11:31:47'); +INSERT INTO `system_node` VALUES ('139', 'store/goods_spec/index', '商品规格列表', '1', '1', '1', '2018-05-04 11:31:47'); +INSERT INTO `system_node` VALUES ('140', 'store/goods_spec/add', '添加商品规格', '0', '1', '1', '2018-05-04 11:31:47'); +INSERT INTO `system_node` VALUES ('141', 'store/goods_spec/edit', '编辑商品规格', '0', '1', '1', '2018-05-04 11:31:48'); +INSERT INTO `system_node` VALUES ('142', 'store/goods_spec/del', '删除商品规格', '0', '1', '1', '2018-05-04 11:31:48'); +INSERT INTO `system_node` VALUES ('143', 'store/goods_spec/forbid', '禁用商品规格', '0', '1', '1', '2018-05-04 11:31:48'); +INSERT INTO `system_node` VALUES ('144', 'store/goods_spec/resume', '启用商品规格', '0', '1', '1', '2018-05-04 11:31:48'); + +-- ---------------------------- +-- Table structure for system_sequence +-- ---------------------------- +DROP TABLE IF EXISTS `system_sequence`; +CREATE TABLE `system_sequence` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `type` varchar(20) DEFAULT NULL COMMENT '序号类型', + `sequence` char(50) NOT NULL COMMENT '序号值', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `index_system_sequence_unique` (`type`,`sequence`) USING BTREE, + KEY `index_system_sequence_type` (`type`), + KEY `index_system_sequence_number` (`sequence`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统序号表'; + +-- ---------------------------- +-- Records of system_sequence +-- ---------------------------- + +-- ---------------------------- +-- Table structure for system_user +-- ---------------------------- +DROP TABLE IF EXISTS `system_user`; +CREATE TABLE `system_user` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(50) NOT NULL DEFAULT '' COMMENT '用户登录名', + `password` char(32) NOT NULL DEFAULT '' COMMENT '用户登录密码', + `qq` varchar(16) DEFAULT NULL COMMENT '联系QQ', + `mail` varchar(32) DEFAULT NULL COMMENT '联系邮箱', + `phone` varchar(16) DEFAULT NULL COMMENT '联系手机号', + `desc` varchar(255) DEFAULT '' COMMENT '备注说明', + `login_num` bigint(20) unsigned DEFAULT '0' COMMENT '登录次数', + `login_at` datetime DEFAULT NULL, + `status` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '状态(0:禁用,1:启用)', + `authorize` varchar(255) DEFAULT NULL, + `is_deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '删除状态(1:删除,0:未删)', + `create_by` bigint(20) unsigned DEFAULT NULL COMMENT '创建人', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `index_system_user_username` (`username`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='系统用户表'; + +-- ---------------------------- +-- Records of system_user +-- ---------------------------- +INSERT INTO `system_user` VALUES ('10000', 'admin', '21232f297a57a5a743894a0e4a801fc3', '22222222', '', '', '', '22973', '2018-03-26 17:06:48', '1', '2,4', '0', null, '2015-11-13 15:14:22'); + +-- ---------------------------- +-- Table structure for wechat_fans +-- ---------------------------- +DROP TABLE IF EXISTS `wechat_fans`; +CREATE TABLE `wechat_fans` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `appid` char(50) DEFAULT '' COMMENT '公众号Appid', + `unionid` char(100) DEFAULT '' COMMENT 'unionid', + `openid` char(100) DEFAULT '' COMMENT '用户的标识,对当前公众号唯一', + `spread_openid` char(100) DEFAULT '' COMMENT '推荐人OPENID', + `spread_at` datetime DEFAULT NULL COMMENT '推荐时间', + `tagid_list` varchar(100) DEFAULT '' COMMENT '标签id', + `is_black` tinyint(1) unsigned DEFAULT '0' COMMENT '是否为黑名单用户', + `subscribe` tinyint(1) unsigned DEFAULT '0' COMMENT '用户是否关注该公众号(0:未关注, 1:已关注)', + `nickname` varchar(200) DEFAULT '' COMMENT '用户的昵称', + `sex` tinyint(1) unsigned DEFAULT NULL COMMENT '用户的性别,值为1时是男性,值为2时是女性,值为0时是未知', + `country` varchar(50) DEFAULT '' COMMENT '用户所在国家', + `province` varchar(50) DEFAULT '' COMMENT '用户所在省份', + `city` varchar(50) DEFAULT '' COMMENT '用户所在城市', + `language` varchar(50) DEFAULT '' COMMENT '用户的语言,简体中文为zh_CN', + `headimgurl` varchar(500) DEFAULT '' COMMENT '用户头像', + `subscribe_time` bigint(20) unsigned DEFAULT '0' COMMENT '用户关注时间', + `subscribe_at` datetime DEFAULT NULL COMMENT '关注时间', + `remark` varchar(50) DEFAULT '' COMMENT '备注', + `expires_in` bigint(20) unsigned DEFAULT '0' COMMENT '有效时间', + `refresh_token` varchar(200) DEFAULT '' COMMENT '刷新token', + `access_token` varchar(200) DEFAULT '' COMMENT '访问token', + `subscribe_scene` varchar(200) DEFAULT '' COMMENT '扫码关注场景', + `qr_scene` varchar(100) DEFAULT '' COMMENT '二维码场景值', + `qr_scene_str` varchar(200) DEFAULT '' COMMENT '二维码场景内容', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `index_wechat_fans_spread_openid` (`spread_openid`) USING BTREE, + KEY `index_wechat_fans_openid` (`openid`) USING BTREE, + KEY `index_wechat_fans_unionid` (`unionid`), + KEY `index_wechat_fans_is_back` (`is_black`), + KEY `index_wechat_fans_subscribe` (`subscribe`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信粉丝'; + +-- ---------------------------- +-- Records of wechat_fans +-- ---------------------------- + +-- ---------------------------- +-- Table structure for wechat_fans_tags +-- ---------------------------- +DROP TABLE IF EXISTS `wechat_fans_tags`; +CREATE TABLE `wechat_fans_tags` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '标签ID', + `appid` char(50) DEFAULT NULL COMMENT '公众号APPID', + `name` varchar(35) DEFAULT NULL COMMENT '标签名称', + `count` int(11) unsigned DEFAULT NULL COMMENT '总数', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期', + KEY `index_wechat_fans_tags_id` (`id`) USING BTREE, + KEY `index_wechat_fans_tags_appid` (`appid`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信会员标签'; + +-- ---------------------------- +-- Records of wechat_fans_tags +-- ---------------------------- + +-- ---------------------------- +-- Table structure for wechat_keys +-- ---------------------------- +DROP TABLE IF EXISTS `wechat_keys`; +CREATE TABLE `wechat_keys` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `appid` char(100) DEFAULT '' COMMENT '公众号APPID', + `type` varchar(20) DEFAULT '' COMMENT '类型,text 文件消息,image 图片消息,news 图文消息', + `keys` varchar(100) DEFAULT NULL COMMENT '关键字', + `content` text COMMENT '文本内容', + `image_url` varchar(255) DEFAULT '' COMMENT '图片链接', + `voice_url` varchar(255) DEFAULT '' COMMENT '语音链接', + `music_title` varchar(100) DEFAULT '' COMMENT '音乐标题', + `music_url` varchar(255) DEFAULT '' COMMENT '音乐链接', + `music_image` varchar(255) DEFAULT '' COMMENT '音乐缩略图链接', + `music_desc` varchar(255) DEFAULT '' COMMENT '音乐描述', + `video_title` varchar(100) DEFAULT '' COMMENT '视频标题', + `video_url` varchar(255) DEFAULT '' COMMENT '视频URL', + `video_desc` varchar(255) DEFAULT '' COMMENT '视频描述', + `news_id` bigint(20) unsigned DEFAULT NULL COMMENT '图文ID', + `sort` bigint(20) unsigned DEFAULT '0' COMMENT '排序字段', + `status` tinyint(1) unsigned DEFAULT '1' COMMENT '0 禁用,1 启用', + `create_by` bigint(20) unsigned DEFAULT NULL COMMENT '创建人', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `index_wechat_keys_appid` (`appid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信关键字'; + +-- ---------------------------- +-- Records of wechat_keys +-- ---------------------------- + +-- ---------------------------- +-- Table structure for wechat_menu +-- ---------------------------- +DROP TABLE IF EXISTS `wechat_menu`; +CREATE TABLE `wechat_menu` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `index` bigint(20) DEFAULT NULL, + `pindex` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '父id', + `type` varchar(24) NOT NULL DEFAULT '' COMMENT '菜单类型 null主菜单 link链接 keys关键字', + `name` varchar(256) NOT NULL DEFAULT '' COMMENT '菜单名称', + `content` text NOT NULL COMMENT '文字内容', + `sort` bigint(20) unsigned DEFAULT '0' COMMENT '排序', + `status` tinyint(1) unsigned DEFAULT '1' COMMENT '状态(0禁用1启用)', + `create_by` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '创建人', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `index_wechat_menu_pindex` (`pindex`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信菜单配置'; + +-- ---------------------------- +-- Records of wechat_menu +-- ---------------------------- + +-- ---------------------------- +-- Table structure for wechat_news +-- ---------------------------- +DROP TABLE IF EXISTS `wechat_news`; +CREATE TABLE `wechat_news` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `media_id` varchar(100) DEFAULT '' COMMENT '永久素材MediaID', + `local_url` varchar(300) DEFAULT '' COMMENT '永久素材显示URL', + `article_id` varchar(60) DEFAULT '' COMMENT '关联图文ID,用,号做分割', + `is_deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '是否删除', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `create_by` bigint(20) DEFAULT NULL COMMENT '创建人', + PRIMARY KEY (`id`), + KEY `index_wechat_news_artcle_id` (`article_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信图文表'; + +-- ---------------------------- +-- Records of wechat_news +-- ---------------------------- + +-- ---------------------------- +-- Table structure for wechat_news_article +-- ---------------------------- +DROP TABLE IF EXISTS `wechat_news_article`; +CREATE TABLE `wechat_news_article` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(50) DEFAULT '' COMMENT '素材标题', + `local_url` varchar(300) DEFAULT '' COMMENT '永久素材显示URL', + `show_cover_pic` tinyint(4) unsigned DEFAULT '0' COMMENT '是否显示封面 0不显示,1 显示', + `author` varchar(20) DEFAULT '' COMMENT '作者', + `digest` varchar(300) DEFAULT '' COMMENT '摘要内容', + `content` longtext COMMENT '图文内容', + `content_source_url` varchar(200) DEFAULT '' COMMENT '图文消息原文地址', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `create_by` bigint(20) DEFAULT NULL COMMENT '创建人', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信素材表'; + +-- ---------------------------- +-- Records of wechat_news_article +-- ---------------------------- + +-- ---------------------------- +-- Table structure for wechat_news_image +-- ---------------------------- +DROP TABLE IF EXISTS `wechat_news_image`; +CREATE TABLE `wechat_news_image` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `md5` varchar(32) DEFAULT '' COMMENT '文件md5', + `local_url` varchar(300) DEFAULT '' COMMENT '本地文件链接', + `media_url` varchar(300) DEFAULT '' COMMENT '远程图片链接', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `index_wechat_news_image_md5` (`md5`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信服务器图片'; + +-- ---------------------------- +-- Records of wechat_news_image +-- ---------------------------- + +-- ---------------------------- +-- Table structure for wechat_news_media +-- ---------------------------- +DROP TABLE IF EXISTS `wechat_news_media`; +CREATE TABLE `wechat_news_media` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `appid` varchar(100) DEFAULT '' COMMENT '公众号ID', + `md5` varchar(32) DEFAULT '' COMMENT '文件md5', + `type` varchar(20) DEFAULT '' COMMENT '媒体类型', + `media_id` varchar(100) DEFAULT '' COMMENT '永久素材MediaID', + `local_url` varchar(300) DEFAULT '' COMMENT '本地文件链接', + `media_url` varchar(300) DEFAULT '' COMMENT '远程图片链接', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信素材表'; + + +-- ---------------------------- +-- Table structure for store_goods_stock +-- ---------------------------- +DROP TABLE IF EXISTS `store_goods_stock`; +CREATE TABLE `store_goods_stock` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `goods_id` bigint(20) unsigned DEFAULT '0' COMMENT '商品ID', + `goods_spec` varchar(255) DEFAULT '' COMMENT '商品属性', + `goods_stock` bigint(20) unsigned DEFAULT '0' COMMENT '商品库存', + `stock_desc` varchar(255) DEFAULT '' COMMENT '商品库存描述', + `sort` bigint(20) unsigned DEFAULT '0' COMMENT '数据排序', + `status` bigint(1) unsigned DEFAULT '1' COMMENT '商品状态(1有效,0无效)', + `is_deleted` bigint(1) unsigned DEFAULT '0' COMMENT '删除状态(1删除,0未删除)', + `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8 COMMENT='商城商品库存'; + +-- ---------------------------- +-- Records of wechat_news_media +-- ---------------------------- diff --git a/application/.htaccess b/application/.htaccess new file mode 100644 index 000000000..3418e55a6 --- /dev/null +++ b/application/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/application/admin/controller/Auth.php b/application/admin/controller/Auth.php index 59c992922..b9beb8adb 100644 --- a/application/admin/controller/Auth.php +++ b/application/admin/controller/Auth.php @@ -1,7 +1,7 @@ * @date 2017/02/15 18:13 */ -class Auth extends BasicAdmin { +class Auth extends BasicAdmin +{ /** * 默认数据模型 @@ -37,44 +38,67 @@ class Auth extends BasicAdmin { /** * 权限列表 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function index() { + public function index() + { $this->title = '系统权限管理'; return parent::_list($this->table); } /** * 权限授权 - * @return string|array + * @return string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function apply() { + public function apply() + { + $this->title = '节点授权'; $auth_id = $this->request->get('id', '0'); - switch (strtolower($this->request->get('action', '0'))) { - case 'getnode': - $nodes = NodeService::get(); - $checked = Db::name('SystemAuthNode')->where('auth', $auth_id)->column('node'); - foreach ($nodes as $key => &$node) { - $node['checked'] = in_array($node['node'], $checked); - if (empty($node['is_auth']) && substr_count($node['node'], '/') > 1) { - unset($nodes[$key]); - } - } - $this->success('获取节点成功!', '', $this->_filterNodes($this->_filterNodes(ToolsService::arr2tree($nodes, 'node', 'pnode', '_sub_')))); - break; - case 'save': - $data = []; - $post = $this->request->post(); - foreach (isset($post['nodes']) ? $post['nodes'] : [] as $node) { - $data[] = ['auth' => $auth_id, 'node' => $node]; - } - Db::name('SystemAuthNode')->where('auth', $auth_id)->delete(); - Db::name('SystemAuthNode')->insertAll($data); - $this->success('节点授权更新成功!', ''); - break; - default : - $this->assign('title', '节点授权'); - return $this->_form($this->table, 'apply'); + $method = '_apply_' . strtolower($this->request->get('action', '0')); + if (method_exists($this, $method)) { + return $this->$method($auth_id); } + return $this->_form($this->table, 'apply'); + } + + /** + * 读取授权节点 + * @param string $auth + */ + protected function _apply_getnode($auth) + { + $nodes = NodeService::get(); + $checked = Db::name('SystemAuthNode')->where(['auth' => $auth])->column('node'); + foreach ($nodes as &$node) { + $node['checked'] = in_array($node['node'], $checked); + } + $all = $this->_apply_filter(ToolsService::arr2tree($nodes, 'node', 'pnode', '_sub_')); + $this->success('获取节点成功!', '', $all); + } + + /** + * 保存授权节点 + * @param string $auth + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + protected function _apply_save($auth) + { + list($data, $post) = [[], $this->request->post()]; + foreach (isset($post['nodes']) ? $post['nodes'] : [] as $node) { + $data[] = ['auth' => $auth, 'node' => $node]; + } + Db::name('SystemAuthNode')->where(['auth' => $auth])->delete(); + Db::name('SystemAuthNode')->insertAll($data); + $this->success('节点授权更新成功!', ''); } /** @@ -83,12 +107,11 @@ class Auth extends BasicAdmin { * @param int $level * @return array */ - protected function _filterNodes($nodes, $level = 1) { - foreach ($nodes as $key => &$node) { + protected function _apply_filter($nodes, $level = 1) + { + foreach ($nodes as $key => $node) { if (!empty($node['_sub_']) && is_array($node['_sub_'])) { - $node['_sub_'] = $this->_filterNodes($node['_sub_'], $level + 1); - } elseif ($level < 3) { - unset($nodes[$key]); + $node[$key]['_sub_'] = $this->_apply_filter($node['_sub_'], $level + 1); } } return $nodes; @@ -96,22 +119,37 @@ class Auth extends BasicAdmin { /** * 权限添加 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function add() { + public function add() + { return $this->_form($this->table, 'form'); } /** * 权限编辑 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function edit() { + public function edit() + { return $this->_form($this->table, 'form'); } /** * 权限禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function forbid() { + public function forbid() + { if (DataService::update($this->table)) { $this->success("权限禁用成功!", ''); } @@ -120,8 +158,11 @@ class Auth extends BasicAdmin { /** * 权限恢复 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function resume() { + public function resume() + { if (DataService::update($this->table)) { $this->success("权限启用成功!", ''); } @@ -130,11 +171,14 @@ class Auth extends BasicAdmin { /** * 权限删除 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function del() { + public function del() + { if (DataService::update($this->table)) { - $id = $this->request->post('id'); - Db::name('SystemAuthNode')->where('auth', $id)->delete(); + $where = ['auth' => $this->request->post('id')]; + Db::name('SystemAuthNode')->where($where)->delete(); $this->success("权限删除成功!", ''); } $this->error("权限删除失败,请稍候再试!"); diff --git a/application/admin/controller/Config.php b/application/admin/controller/Config.php index 6e0634aa5..2d4022100 100644 --- a/application/admin/controller/Config.php +++ b/application/admin/controller/Config.php @@ -1,7 +1,7 @@ * @date 2017/02/15 18:05 */ -class Config extends BasicAdmin { +class Config extends BasicAdmin +{ /** * 当前默认数据模型 @@ -36,32 +38,36 @@ class Config extends BasicAdmin { * 当前页面标题 * @var string */ - public $title = '网站参数配置'; + public $title = '系统参数配置'; /** * 显示系统常规配置 + * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function index() { - if (!$this->request->isPost()) { - $this->assign('title', $this->title); - return view(); + public function index() + { + if ($this->request->isGet()) { + return $this->fetch('', ['title' => $this->title]); } - foreach ($this->request->post() as $key => $vo) { - sysconf($key, $vo); + if ($this->request->isPost()) { + foreach ($this->request->post() as $key => $vo) { + sysconf($key, $vo); + } + LogService::write('系统管理', '系统参数配置成功'); + $this->success('系统参数配置成功!', ''); } - LogService::write('系统管理', '修改系统配置参数成功'); - $this->success('数据修改成功!', ''); } /** * 文件存储配置 + * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function file() { - $this->assign('alert', [ - 'type' => 'success', - 'title' => '操作提示', - 'content' => '文件引擎参数影响全局文件上传功能,请勿随意修改!' - ]); + public function file() + { $this->title = '文件存储配置'; return $this->index(); } diff --git a/application/admin/controller/Index.php b/application/admin/controller/Index.php index 65dff1480..9658264b3 100644 --- a/application/admin/controller/Index.php +++ b/application/admin/controller/Index.php @@ -1,7 +1,7 @@ * @date 2017/02/15 10:41 */ -class Index extends BasicAdmin { +class Index extends BasicAdmin +{ /** * 后台框架布局 - * @return View + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function index() { + public function index() + { NodeService::applyAuthNode(); - $list = Db::name('SystemMenu')->where('status', '1')->order('sort asc,id asc')->select(); - $menus = $this->_filterMenu(ToolsService::arr2tree($list)); - $this->assign('title', '系统管理'); - $this->assign('menus', $menus); - return view(); + $list = (array)Db::name('SystemMenu')->where(['status' => '1'])->order('sort asc,id asc')->select(); + $menus = $this->buildMenuData(ToolsService::arr2tree($list), NodeService::get(), !!session('user')); + if (empty($menus) && !session('user.id')) { + $this->redirect('@admin/login'); + } + return $this->fetch('', ['title' => '系统管理', 'menus' => $menus]); } /** * 后台主菜单权限过滤 - * @param array $menus + * @param array $menus 当前菜单列表 + * @param array $nodes 系统权限节点数据 + * @param bool $isLogin 是否已经登录 * @return array */ - private function _filterMenu($menus) { + private function buildMenuData($menus, $nodes, $isLogin) + { foreach ($menus as $key => &$menu) { - if (!empty($menu['sub'])) { - $menu['sub'] = $this->_filterMenu($menu['sub']); - } + !empty($menu['sub']) && $menu['sub'] = $this->buildMenuData($menu['sub'], $nodes, $isLogin); if (!empty($menu['sub'])) { $menu['url'] = '#'; - } elseif (stripos($menu['url'], 'http') === 0) { + } elseif (preg_match('/^https?\:/i', $menu['url'])) { continue; - } elseif ($menu['url'] !== '#' && auth(join('/', array_slice(explode('/', $menu['url']), 0, 3)))) { - $menu['url'] = url($menu['url']); + } elseif ($menu['url'] !== '#') { + $node = join('/', array_slice(explode('/', preg_replace('/[\W]/', '/', $menu['url'])), 0, 3)); + $menu['url'] = url($menu['url']) . (empty($menu['params']) ? '' : "?{$menu['params']}"); + if (isset($nodes[$node]) && $nodes[$node]['is_login'] && empty($isLogin)) { + unset($menus[$key]); + } elseif (isset($nodes[$node]) && $nodes[$node]['is_auth'] && $isLogin && !auth($node)) { + unset($menus[$key]); + } } else { unset($menus[$key]); } @@ -68,66 +80,64 @@ class Index extends BasicAdmin { /** * 主机信息显示 - * @return View + * @return string */ - public function main() { + public function main() + { $_version = Db::query('select version() as ver'); - $version = array_pop($_version); - $this->assign('mysql_ver', $version['ver']); - if (session('user.username') === 'admin' && session('user.password') === '21232f297a57a5a743894a0e4a801fc3') { - $url = url('admin/index/pass') . '?id=' . session('user.id'); - $alert = [ - 'type' => 'danger', - 'title' => '安全提示', - 'content' => "超级管理员默认密码未修改,建议马上修改!" - ]; - $this->assign('alert', $alert); - $this->assign('title', '后台首页'); - } - return view(); + return $this->fetch('', [ + 'title' => '后台首页', + 'think_ver' => App::VERSION, + 'mysql_ver' => array_pop($_version)['ver'], + ]); } /** * 修改密码 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException */ - public function pass() { - if (in_array('10000', explode(',', $this->request->post('id')))) { - $this->error('系统超级账号禁止操作!'); - } + public function pass() + { if (intval($this->request->request('id')) !== intval(session('user.id'))) { - $this->error('访问异常!'); + $this->error('只能修改当前用户的密码!'); } if ($this->request->isGet()) { $this->assign('verify', true); return $this->_form('SystemUser', 'user/pass'); - } else { - $data = $this->request->post(); - if ($data['password'] !== $data['repassword']) { - $this->error('两次输入的密码不一致,请重新输入!'); - } - $user = Db::name('SystemUser')->where('id', session('user.id'))->find(); - if (md5($data['oldpassword']) !== $user['password']) { - $this->error('旧密码验证失败,请重新输入!'); - } - if (DataService::save('SystemUser', ['id' => session('user.id'), 'password' => md5($data['password'])])) { - $this->success('密码修改成功,下次请使用新密码登录!', ''); - } else { - $this->error('密码修改失败,请稍候再试!'); - } } + $data = $this->request->post(); + if ($data['password'] !== $data['repassword']) { + $this->error('两次输入的密码不一致,请重新输入!'); + } + $user = Db::name('SystemUser')->where('id', session('user.id'))->find(); + if (md5($data['oldpassword']) !== $user['password']) { + $this->error('旧密码验证失败,请重新输入!'); + } + if (DataService::save('SystemUser', ['id' => session('user.id'), 'password' => md5($data['password'])])) { + $this->success('密码修改成功,下次请使用新密码登录!', ''); + } + $this->error('密码修改失败,请稍候再试!'); } /** * 修改资料 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function info() { - if (in_array('10000', explode(',', $this->request->post('id')))) { - $this->error('系统超级账号禁止操作!'); - } + public function info() + { if (intval($this->request->request('id')) === intval(session('user.id'))) { return $this->_form('SystemUser', 'user/form'); } - $this->error('访问异常!'); + $this->error('只能修改当前用户的资料!'); } } diff --git a/application/admin/controller/Log.php b/application/admin/controller/Log.php index 197239ecd..d9ae070dd 100644 --- a/application/admin/controller/Log.php +++ b/application/admin/controller/Log.php @@ -1,7 +1,7 @@ * @date 2017/02/15 18:12 */ -class Log extends BasicAdmin { +class Log extends BasicAdmin +{ /** * 指定当前数据表 @@ -35,41 +36,56 @@ class Log extends BasicAdmin { /** * 日志列表 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function index() { - $this->title = '系统操作日志'; - $this->assign('actions', Db::name($this->table)->group('action')->column('action')); + public function index() + { + // 日志行为类别 + $actions = Db::name($this->table)->group('action')->column('action'); + $this->assign('actions', $actions); + // 日志数据库对象 + list($this->title, $get) = ['系统操作日志', $this->request->get()]; $db = Db::name($this->table)->order('id desc'); - $get = $this->request->get(); foreach (['action', 'content', 'username'] as $key) { - if (isset($get[$key]) && $get[$key] !== '') { - $db->where($key, 'like', "%{$get[$key]}%"); - } + (isset($get[$key]) && $get[$key] !== '') && $db->whereLike($key, "%{$get[$key]}%"); + } + if (isset($get['create_at']) && $get['create_at'] !== '') { + list($start, $end) = explode(' - ', $get['create_at']); + $db->whereBetween('create_at', ["{$start} 00:00:00", "{$end} 23:59:59"]); } return parent::_list($db); } /** * 列表数据处理 - * @param $data + * @param array $data + * @throws \Exception */ - protected function _index_data_filter(&$data) { + protected function _index_data_filter(&$data) + { $ip = new \Ip2Region(); foreach ($data as &$vo) { $result = $ip->btreeSearch($vo['ip']); $vo['isp'] = isset($result['region']) ? $result['region'] : ''; - $vo['isp'] = str_replace(['|0|0|0|0', '|'], ['', ' '], $vo['isp']); + $vo['isp'] = str_replace(['内网IP', '0', '|'], '', $vo['isp']); } } /** * 日志删除操作 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function del() { + public function del() + { if (DataService::update($this->table)) { - $this->success("日志删除成功!", ''); + $this->success("日志删除成功!", ''); } - $this->error("日志删除失败,请稍候再试!"); + $this->error("日志删除失败, 请稍候再试!"); } } diff --git a/application/admin/controller/Login.php b/application/admin/controller/Login.php index 5c1a16a6f..6e61de34d 100644 --- a/application/admin/controller/Login.php +++ b/application/admin/controller/Login.php @@ -1,7 +1,7 @@ * @date 2017/02/10 13:59 */ -class Login extends BasicAdmin { - - /** - * 默认检查用户登录状态 - * @var bool - */ - public $checkLogin = false; - - /** - * 默认检查节点访问权限 - * @var bool - */ - public $checkAuth = false; +class Login extends BasicAdmin +{ /** * 控制器基础方法 */ - public function _initialize() { - if (session('user') && $this->request->action() !== 'out') { + public function initialize() + { + if (session('user.id') && $this->request->action() !== 'out') { $this->redirect('@admin'); } } @@ -52,34 +44,56 @@ class Login extends BasicAdmin { /** * 用户登录 * @return string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException */ - public function index() { + public function index() + { if ($this->request->isGet()) { - $this->assign('title', '用户登录'); - return $this->fetch(); - } else { - $username = $this->request->post('username', '', 'trim'); - $password = $this->request->post('password', '', 'trim'); - (empty($username) || strlen($username) < 4) && $this->error('登录账号长度不能少于4位有效字符!'); - (empty($password) || strlen($password) < 4) && $this->error('登录密码长度不能少于4位有效字符!'); - $user = Db::name('SystemUser')->where('username', $username)->find(); - empty($user) && $this->error('登录账号不存在,请重新输入!'); - ($user['password'] !== md5($password)) && $this->error('登录密码与账号不匹配,请重新输入!'); - Db::name('SystemUser')->where('id', $user['id'])->update(['login_at' => ['exp', 'now()'], 'login_num' => ['exp', 'login_num+1']]); - session('user', $user); - !empty($user['authorize']) && NodeService::applyAuthNode(); - LogService::write('系统管理', '用户登录系统成功'); - $this->success('登录成功,正在进入系统...', '@admin'); + return $this->fetch('', ['title' => '用户登录']); } + // 输入数据效验 + $validate = Validate::make([ + 'username' => 'require|min:4', + 'password' => 'require|min:4', + ], [ + 'username.require' => '登录账号不能为空!', + 'username.min' => '登录账号长度不能少于4位有效字符!', + 'password.require' => '登录密码不能为空!', + 'password.min' => '登录密码长度不能少于4位有效字符!', + ]); + $data = [ + 'username' => $this->request->post('username', ''), + 'password' => $this->request->post('password', ''), + ]; + $validate->check($data) || $this->error($validate->getError()); + // 用户信息验证 + $user = Db::name('SystemUser')->where(['username' => $data['username'], 'is_deleted' => '0'])->find(); + empty($user) && $this->error('登录账号不存在,请重新输入!'); + empty($user['status']) && $this->error('账号已经被禁用,请联系管理员!'); + $user['password'] !== md5($data['password']) && $this->error('登录密码错误,请重新输入!'); + // 更新登录信息 + Db::name('SystemUser')->where(['id' => $user['id']])->update([ + 'login_at' => Db::raw('now()'), + 'login_num' => Db::raw('login_num+1'), + ]); + session('user', $user); + !empty($user['authorize']) && NodeService::applyAuthNode(); + LogService::write('系统管理', '用户登录系统成功'); + $this->success('登录成功,正在进入系统...', '@admin'); } /** * 退出登录 */ - public function out() { - LogService::write('系统管理', '用户退出系统成功'); - session('user', null); - session_destroy(); + public function out() + { + session('user') && LogService::write('系统管理', '用户退出系统成功'); + !empty($_SESSION) && $_SESSION = []; + [session_unset(), session_destroy()]; $this->success('退出登录成功!', '@admin/login'); } diff --git a/application/admin/controller/Menu.php b/application/admin/controller/Menu.php index 74b69e7df..69fa9e63a 100644 --- a/application/admin/controller/Menu.php +++ b/application/admin/controller/Menu.php @@ -1,7 +1,7 @@ * @date 2017/02/15 */ -class Menu extends BasicAdmin { +class Menu extends BasicAdmin +{ /** * 绑定操作模型 @@ -37,9 +38,15 @@ class Menu extends BasicAdmin { /** * 菜单列表 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function index() { - $this->title = '系统菜单管理'; + public function index() + { + $this->title = '后台菜单管理'; $db = Db::name($this->table)->order('sort asc,id asc'); return parent::_list($db, false); } @@ -48,9 +55,12 @@ class Menu extends BasicAdmin { * 列表数据处理 * @param array $data */ - protected function _index_data_filter(&$data) { + protected function _index_data_filter(&$data) + { foreach ($data as &$vo) { - ($vo['url'] !== '#') && ($vo['url'] = url($vo['url'])); + if ($vo['url'] !== '#') { + $vo['url'] = url($vo['url']) . (empty($vo['params']) ? '' : "?{$vo['params']}"); + } $vo['ids'] = join(',', ToolsService::getArrSubIds($data, $vo['id'])); } $data = ToolsService::arr2table($data); @@ -58,26 +68,42 @@ class Menu extends BasicAdmin { /** * 添加菜单 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function add() { + public function add() + { return $this->_form($this->table, 'form'); } /** * 编辑菜单 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function edit() { + public function edit() + { return $this->_form($this->table, 'form'); } /** * 表单数据前缀方法 * @param array $vo + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - protected function _form_filter(&$vo) { + protected function _form_filter(&$vo) + { if ($this->request->isGet()) { // 上级菜单处理 - $_menus = Db::name($this->table)->where('status', '1')->order('sort desc,id desc')->select(); + $_menus = Db::name($this->table)->where(['status' => '1'])->order('sort asc,id asc')->select(); $_menus[] = ['title' => '顶级菜单', 'id' => '0', 'pid' => '-1']; $menus = ToolsService::arr2table($_menus); foreach ($menus as $key => &$menu) { @@ -89,49 +115,62 @@ class Menu extends BasicAdmin { $current_path = "-{$vo['pid']}-{$vo['id']}"; if ($vo['pid'] !== '' && (stripos("{$menu['path']}-", "{$current_path}-") !== false || $menu['path'] === $current_path)) { unset($menus[$key]); + continue; } } } // 读取系统功能节点 $nodes = NodeService::get(); - foreach ($nodes as $key => $_vo) { - if (empty($_vo['is_menu'])) { + foreach ($nodes as $key => $node) { + if (empty($node['is_menu'])) { unset($nodes[$key]); } } - $this->assign('nodes', array_column($nodes, 'node')); - $this->assign('menus', $menus); + // 设置上级菜单 + if (!isset($vo['pid']) && $this->request->get('pid', '0')) { + $vo['pid'] = $this->request->get('pid', '0'); + } + $this->assign(['nodes' => array_column($nodes, 'node'), 'menus' => $menus]); } } /** * 删除菜单 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function del() { + public function del() + { if (DataService::update($this->table)) { - $this->success("菜单删除成功!", ''); + $this->success("菜单删除成功!", ''); } - $this->error("菜单删除失败,请稍候再试!"); + $this->error("菜单删除失败, 请稍候再试!"); } /** * 菜单禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function forbid() { + public function forbid() + { if (DataService::update($this->table)) { - $this->success("菜单禁用成功!", ''); + $this->success("菜单禁用成功!", ''); } - $this->error("菜单禁用失败,请稍候再试!"); + $this->error("菜单禁用失败, 请稍候再试!"); } /** * 菜单禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function resume() { + public function resume() + { if (DataService::update($this->table)) { - $this->success("菜单启用成功!", ''); + $this->success("菜单启用成功!", ''); } - $this->error("菜单启用失败,请稍候再试!"); + $this->error("菜单启用失败, 请稍候再试!"); } } diff --git a/application/admin/controller/Node.php b/application/admin/controller/Node.php index 7eb633067..8e3d6e6bb 100644 --- a/application/admin/controller/Node.php +++ b/application/admin/controller/Node.php @@ -1,7 +1,7 @@ * @date 2017/02/15 18:13 */ -class Node extends BasicAdmin { +class Node extends BasicAdmin +{ /** * 指定当前默认模型 @@ -36,34 +38,55 @@ class Node extends BasicAdmin { /** * 显示节点列表 + * @return string */ - public function index() { - $this->assign('alert', [ - 'type' => 'danger', - 'title' => '安全警告', - 'content' => '结构为系统自动生成,状态数据请勿随意修改!' - ]); - $this->assign('title', '系统节点管理'); - $this->assign('nodes', ToolsService::arr2table(NodeService::get(), 'node', 'pnode')); - return view(); + public function index() + { + $nodes = ToolsService::arr2table(NodeService::get(), 'node', 'pnode'); + $groups = []; + foreach ($nodes as $node) { + $pnode = explode('/', $node['node'])[0]; + if ($node['node'] === $pnode) { + $groups[$pnode]['node'] = $node; + } + $groups[$pnode]['list'][] = $node; + } + return $this->fetch('', ['title' => '系统节点管理', 'nodes' => $nodes, 'groups' => $groups]); + } + + /** + * 清理无效的节点记录 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function clear() + { + $nodes = array_keys(NodeService::get()); + if (false !== Db::name($this->table)->whereNotIn('node', $nodes)->delete()) { + $this->success('清理无效节点记录成功!', ''); + } + $this->error('清理无效记录失败,请稍候再试!'); } /** * 保存节点变更 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function save() { + public function save() + { if ($this->request->isPost()) { - $post = $this->request->post(); - if (isset($post['name']) && isset($post['value'])) { - $nameattr = explode('.', $post['name']); - $field = array_shift($nameattr); - $data = ['node' => join(',', $nameattr), $field => $post['value']]; - DataService::save($this->table, $data, 'node'); - $this->success('参数保存成功!', ''); + list($post, $data) = [$this->request->post(), []]; + foreach ($post['list'] as $vo) { + if (!empty($vo['node'])) { + $data['node'] = $vo['node']; + $data[$vo['name']] = $vo['value']; + } } - } else { - $this->error('访问异常,请重新进入...'); + !empty($data) && DataService::save($this->table, $data, 'node'); + $this->success('参数保存成功!', ''); } + $this->error('访问异常,请重新进入...'); } } diff --git a/application/admin/controller/Plugs.php b/application/admin/controller/Plugs.php index 37b0c08b1..9a894d9b8 100644 --- a/application/admin/controller/Plugs.php +++ b/application/admin/controller/Plugs.php @@ -1,7 +1,7 @@ * @date 2017/02/21 */ -class Plugs extends BasicAdmin { - - /** - * 默认检查用户登录状态 - * @var bool - */ - public $checkLogin = false; - - /** - * 默认检查节点访问权限 - * @var bool - */ - public $checkAuth = false; +class Plugs extends BasicAdmin +{ /** * 文件上传 - * @return View + * @return mixed + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function upfile() { - $types = $this->request->get('type', 'jpg,png'); - $mode = $this->request->get('mode', 'one'); - $this->assign('mode', $mode); - $this->assign('types', $types); - if (!in_array(($uptype = $this->request->get('uptype')), ['local', 'qiniu'])) { + public function upfile() + { + $uptype = $this->request->get('uptype'); + if (!in_array($uptype, ['local', 'qiniu', 'oss'])) { $uptype = sysconf('storage_type'); } - $this->assign('uptype', $uptype); + $mode = $this->request->get('mode', 'one'); + $types = $this->request->get('type', 'jpg,png'); $this->assign('mimes', FileService::getFileMine($types)); $this->assign('field', $this->request->get('field', 'file')); - return view(); + return $this->fetch('', ['mode' => $mode, 'types' => $types, 'uptype' => $uptype]); } /** * 通用文件上传 - * @return string + * @return \think\response\Json + * @throws \think\Exception + * @throws \think\exception\PDOException + * @throws \OSS\Core\OssException */ - public function upload() { - if ($this->request->isPost()) { - $md5s = str_split($this->request->post('md5'), 16); - if (($info = $this->request->file('file')->move('static' . DS . 'upload' . DS . $md5s[0], $md5s[1], true))) { - $filename = join('/', $md5s) . '.' . $info->getExtension(); - $site_url = FileService::getFileUrl($filename, 'local'); - if ($site_url) { - return json(['data' => ['site_url' => $site_url], 'code' => 'SUCCESS']); - } + public function upload() + { + $file = $this->request->file('file'); + if (!$file->checkExt(strtolower(sysconf('storage_local_exts')))) { + return json(['code' => 'ERROR', 'msg' => '文件上传类型受限']); + } + $names = str_split($this->request->post('md5'), 16); + $ext = strtolower(pathinfo($file->getInfo('name'), 4)); + $ext = $ext ? $ext : 'tmp'; + $filename = "{$names[0]}/{$names[1]}.{$ext}"; + // 文件上传Token验证 + if ($this->request->post('token') !== md5($filename . session_id())) { + return json(['code' => 'ERROR', 'msg' => '文件上传验证失败']); + } + // 文件上传处理 + if (($info = $file->move("static/upload/{$names[0]}", "{$names[1]}.{$ext}", true))) { + if (($site_url = FileService::getFileUrl($filename, 'local'))) { + return json(['data' => ['site_url' => $site_url], 'code' => 'SUCCESS', 'msg' => '文件上传成功']); } } - return json(['code' => 'ERROR']); + return json(['code' => 'ERROR', 'msg' => '文件上传失败']); } /** * 文件状态检查 + * @throws \OSS\Core\OssException + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function upstate() { + public function upstate() + { $post = $this->request->post(); - $filename = join('/', str_split($post['md5'], 16)) . '.' . pathinfo($post['filename'], PATHINFO_EXTENSION); + $ext = strtolower(pathinfo($post['filename'], 4)); + $filename = join('/', str_split($post['md5'], 16)) . '.' . ($ext ? $ext : 'tmp'); // 检查文件是否已上传 if (($site_url = FileService::getFileUrl($filename))) { - $this->result(['site_url' => $site_url], 'IS_FOUND'); + return json(['data' => ['site_url' => $site_url], 'code' => "IS_FOUND"]); } // 需要上传文件,生成上传配置参数 - $config = ['uptype' => $post['uptype'], 'file_url' => $filename]; + $data = ['uptype' => $post['uptype'], 'file_url' => $filename]; switch (strtolower($post['uptype'])) { - case 'qiniu': - $config['server'] = FileService::getUploadQiniuUrl(true); - $config['token'] = $this->_getQiniuToken($filename); - break; case 'local': - $config['server'] = FileService::getUploadLocalUrl(); + $data['token'] = md5($filename . session_id()); + $data['server'] = FileService::getUploadLocalUrl(); + break; + case 'qiniu': + $data['token'] = $this->_getQiniuToken($filename); + $data['server'] = FileService::getUploadQiniuUrl(true); break; case 'oss': $time = time() + 3600; $policyText = [ 'expiration' => date('Y-m-d', $time) . 'T' . date('H:i:s', $time) . '.000Z', - 'conditions' => [ - ['content-length-range', 0, 1048576000] - ] + 'conditions' => [['content-length-range', 0, 1048576000]], ]; - $config['policy'] = base64_encode(json_encode($policyText)); - $config['server'] = FileService::getUploadOssUrl(); - $config['site_url'] = FileService::getBaseUriOss() . $filename; - $config['signature'] = base64_encode(hash_hmac('sha1', $config['policy'], sysconf('storage_oss_secret'), true)); - $config['OSSAccessKeyId'] = sysconf('storage_oss_keyid'); + $data['server'] = FileService::getUploadOssUrl(); + $data['policy'] = base64_encode(json_encode($policyText)); + $data['site_url'] = FileService::getBaseUriOss() . $filename; + $data['signature'] = base64_encode(hash_hmac('sha1', $data['policy'], sysconf('storage_oss_secret'), true)); + $data['OSSAccessKeyId'] = sysconf('storage_oss_keyid'); + break; } - $this->result($config, 'NOT_FOUND'); + return json(['data' => $data, 'code' => "NOT_FOUND"]); } /** * 生成七牛文件上传Token * @param string $key * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - protected function _getQiniuToken($key) { + protected function _getQiniuToken($key) + { + $baseUrl = FileService::getBaseUriQiniu(); + $bucket = sysconf('storage_qiniu_bucket'); $accessKey = sysconf('storage_qiniu_access_key'); $secretKey = sysconf('storage_qiniu_secret_key'); - $bucket = sysconf('storage_qiniu_bucket'); - $host = sysconf('storage_qiniu_domain'); - $protocol = sysconf('storage_qiniu_is_https') ? 'https' : 'http'; $params = [ - "scope" => "{$bucket}:{$key}", - "deadline" => 3600 + time(), - "returnBody" => "{\"data\":{\"site_url\":\"{$protocol}://{$host}/$(key)\",\"file_url\":\"$(key)\"}, \"code\": \"SUCCESS\"}", + "scope" => "{$bucket}:{$key}", "deadline" => 3600 + time(), + "returnBody" => "{\"data\":{\"site_url\":\"{$baseUrl}/$(key)\",\"file_url\":\"$(key)\"}, \"code\": \"SUCCESS\"}", ]; $data = str_replace(['+', '/'], ['-', '_'], base64_encode(json_encode($params))); return $accessKey . ':' . str_replace(['+', '/'], ['-', '_'], base64_encode(hash_hmac('sha1', $data, $secretKey, true))) . ':' . $data; } /** - * 字体图标 + * 字体图标选择器 + * @return \think\response\View */ - public function icon() { - $this->assign('field', $this->request->get('field', 'icon')); - return view(); + public function icon() + { + $field = $this->request->get('field', 'icon'); + return $this->fetch('', ['field' => $field]); } } diff --git a/application/admin/controller/User.php b/application/admin/controller/User.php index a03f839d6..2510c4094 100644 --- a/application/admin/controller/User.php +++ b/application/admin/controller/User.php @@ -1,7 +1,7 @@ * @date 2017/02/15 18:12 */ -class User extends BasicAdmin { +class User extends BasicAdmin +{ /** * 指定当前数据表 @@ -35,62 +36,86 @@ class User extends BasicAdmin { /** * 用户列表 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function index() { - // 设置页面标题 + public function index() + { $this->title = '系统用户管理'; - // 获取到所有GET参数 - $get = $this->request->get(); - // 实例Query对象 - $db = Db::name($this->table)->where('is_deleted', '0'); - // 应用搜索条件 - foreach (['username', 'phone'] as $key) { - if (isset($get[$key]) && $get[$key] !== '') { - $db->where($key, 'like', "%{$get[$key]}%"); - } + list($get, $db) = [$this->request->get(), Db::name($this->table)]; + foreach (['username', 'phone', 'mail'] as $key) { + (isset($get[$key]) && $get[$key] !== '') && $db->whereLike($key, "%{$get[$key]}%"); } - // 实例化并显示 - return parent::_list($db); + if (isset($get['date']) && $get['date'] !== '') { + list($start, $end) = explode(' - ', $get['date']); + $db->whereBetween('login_at', ["{$start} 00:00:00", "{$end} 23:59:59"]); + } + return parent::_list($db->where(['is_deleted' => '0'])); } /** * 授权管理 * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function auth() { + public function auth() + { return $this->_form($this->table, 'auth'); } /** * 用户添加 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function add() { + public function add() + { return $this->_form($this->table, 'form'); } /** * 用户编辑 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function edit() { + public function edit() + { return $this->_form($this->table, 'form'); } /** * 用户密码修改 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException */ - public function pass() { - if (in_array('10000', explode(',', $this->request->post('id')))) { - $this->error('系统超级账号禁止操作!'); - } + public function pass() + { if ($this->request->isGet()) { $this->assign('verify', false); return $this->_form($this->table, 'pass'); } - $data = $this->request->post(); - if ($data['password'] !== $data['repassword']) { + $post = $this->request->post(); + if ($post['password'] !== $post['repassword']) { $this->error('两次输入的密码不一致!'); } - if (DataService::save($this->table, ['id' => $data['id'], 'password' => md5($data['password'])], 'id')) { + $data = ['id' => $post['id'], 'password' => md5($post['password'])]; + if (DataService::save($this->table, $data, 'id')) { $this->success('密码修改成功,下次请使用新密码登录!', ''); } $this->error('密码修改失败,请稍候再试!'); @@ -99,27 +124,36 @@ class User extends BasicAdmin { /** * 表单数据默认处理 * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function _form_filter(&$data) { + public function _form_filter(&$data) + { if ($this->request->isPost()) { if (isset($data['authorize']) && is_array($data['authorize'])) { $data['authorize'] = join(',', $data['authorize']); + } else { + $data['authorize'] = ''; } if (isset($data['id'])) { unset($data['username']); - } elseif (Db::name($this->table)->where('username', $data['username'])->find()) { + } elseif (Db::name($this->table)->where(['username' => $data['username']])->count() > 0) { $this->error('用户账号已经存在,请使用其它账号!'); } } else { $data['authorize'] = explode(',', isset($data['authorize']) ? $data['authorize'] : ''); - $this->assign('authorizes', Db::name('SystemAuth')->select()); + $this->assign('authorizes', Db::name('SystemAuth')->where(['status' => '1'])->select()); } } /** * 删除用户 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function del() { + public function del() + { if (in_array('10000', explode(',', $this->request->post('id')))) { $this->error('系统超级账号禁止删除!'); } @@ -131,8 +165,11 @@ class User extends BasicAdmin { /** * 用户禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function forbid() { + public function forbid() + { if (in_array('10000', explode(',', $this->request->post('id')))) { $this->error('系统超级账号禁止操作!'); } @@ -144,8 +181,11 @@ class User extends BasicAdmin { /** * 用户禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function resume() { + public function resume() + { if (DataService::update($this->table)) { $this->success("用户启用成功!", ''); } diff --git a/application/admin/middleware/Auth.php b/application/admin/middleware/Auth.php new file mode 100644 index 000000000..8dd0a3d1f --- /dev/null +++ b/application/admin/middleware/Auth.php @@ -0,0 +1,71 @@ +module(), $request->controller(), $request->action()]; + $access = $this->buildAuth($node = NodeService::parseNodeStr("{$module}/{$controller}/{$action}")); + // 登录状态检查 + if (!empty($access['is_login']) && !session('user')) { + $msg = ['code' => 0, 'msg' => '抱歉,您还没有登录获取访问权限!', 'url' => url('@admin/login')]; + return $request->isAjax() ? json($msg) : redirect($msg['url']); + } + // 访问权限检查 + if (!empty($access['is_auth']) && !auth($node)) { + return json(['code' => 0, 'msg' => '抱歉,您没有访问该模块的权限!']); + } + // 模板常量声明 + app('view')->init(config('template.'))->assign(['classuri' => NodeService::parseNodeStr("{$module}/{$controller}")]); + return $next($request); + } + + /** + * 根据节点获取对应权限配置 + * @param string $node 权限节点 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function buildAuth($node) + { + $info = Db::name('SystemNode')->cache(true, 30)->where(['node' => $node])->find(); + return [ + 'is_menu' => intval(!empty($info['is_menu'])), + 'is_auth' => intval(!empty($info['is_auth'])), + 'is_login' => empty($info['is_auth']) ? intval(!empty($info['is_login'])) : 1, + ]; + } +} diff --git a/application/admin/view/auth.index.html b/application/admin/view/auth.index.html deleted file mode 100644 index 561e473f5..000000000 --- a/application/admin/view/auth.index.html +++ /dev/null @@ -1,78 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="button"} -
- - -
-{/block} - -{block name="content"} -
- - - - - - - - - - - - - - {foreach $list as $key=>$vo} - - - - - - - - - {/foreach} - -
- - - - 权限名称权限描述状态操作
- - - - {$vo.title}{$vo.desc|default="没有写描述哦!"} - {if $vo.status eq 0} - 已禁用 - {elseif $vo.status eq 1} - 使用中 - {/if} - - - {if auth("$classuri/edit")} - | - 编辑 - {/if} - - {if auth("$classuri/apply")} - | - 授权 - {/if} - - {if $vo.status eq 1 and auth("$classuri/forbid")} - | - 禁用 - {elseif auth("$classuri/resume")} - | - 启用 - {/if} - - {if auth("$classuri/del")} - | - 删除 - {/if} - -
- {if isset($page)}

{$page}

{/if} -
-{/block} \ No newline at end of file diff --git a/application/admin/view/auth.apply.html b/application/admin/view/auth/apply.html similarity index 67% rename from application/admin/view/auth.apply.html rename to application/admin/view/auth/apply.html index 30ae678ab..122881af6 100644 --- a/application/admin/view/auth.apply.html +++ b/application/admin/view/auth/apply.html @@ -1,36 +1,22 @@ -{extend name='extra@admin/content'} +{extend name='admin@public/content'} {block name="content"} - - - -
-
+{/block} +{block name="script"} + + +{/block} +{block name="style"} + {/block} \ No newline at end of file diff --git a/application/admin/view/auth.form.html b/application/admin/view/auth/form.html similarity index 87% rename from application/admin/view/auth.form.html rename to application/admin/view/auth/form.html index ec94c8856..cc134f0fe 100644 --- a/application/admin/view/auth.form.html +++ b/application/admin/view/auth/form.html @@ -1,4 +1,4 @@ -
+
@@ -17,7 +17,7 @@
- {if isset($vo['id'])}{/if} + {if isset($vo['id'])}{/if}
diff --git a/application/admin/view/auth/index.html b/application/admin/view/auth/index.html new file mode 100644 index 000000000..1b787cd0b --- /dev/null +++ b/application/admin/view/auth/index.html @@ -0,0 +1,83 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + + +{/block} + +{block name="content"} + + +

没 有 记 录 哦!

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 名称描述状态添加时间
+ + + + {$vo.title}{$vo.desc|default="没有写描述哦!"} + {if $vo.status eq 0}已禁用{elseif $vo.status eq 1}使用中{/if} + {$vo.create_at|format_datetime} + + {if auth("$classuri/edit")} + | + 编辑 + {/if} + + {if auth("$classuri/apply")} + | + 授权 + {/if} + + {if $vo.status eq 1 and auth("$classuri/forbid")} + | + 禁用 + {elseif auth("$classuri/resume")} + | + 启用 + {/if} + + {if auth("$classuri/del")} + | + 删除 + {/if} + +
+ {if isset($page)}

{$page|raw}

{/if} + + +{/block} \ No newline at end of file diff --git a/application/admin/view/config.file.html b/application/admin/view/config.file.html deleted file mode 100644 index 52d89ad3a..000000000 --- a/application/admin/view/config.file.html +++ /dev/null @@ -1,200 +0,0 @@ -{extend name="extra@admin/content"} - -{block name="content"} -
- -
- -
- -
- 若还没有七牛云帐号,请点击 - 免费申请10G存储空间, - 申请成功后添加公开bucket空间 -
-
- 若还没有AliOSS存储账号, 请点击 创建AliOSS存储空间, - 目前仅支持公开空间URL访问, 另外还需要配置AliOSS跨域策略 -
-
-
- -
- -
- -
- -

七牛云存储空间所在区域,需要严格对应储存所在区域才能上传文件

-
-
- -
- -
- -

七牛云资源访问协议(HTTP 或 HTTPS),HTTPS 需要配置证书才能使用

-
-
- - -
- -
- -

填写七牛云存储空间名称,如:static

-
-
- -
- -
- -

填写七牛云存储访问域名,如:static.ctolog.cc

-
-
- -
- -
- -

可以在 [ 七牛云 > 个人中心 ] 设置并获取到访问密钥

-
-
- - -
- -
- -

可以在 [ 七牛云 > 个人中心 ] 设置并获取到安全密钥

-
-
- -
- -
- -

AliOSS资源访问协议(HTTP 或 HTTPS),HTTPS 需要配置证书才能使用

-
-
- -
- -
- -

填写OSS存储空间名称,如:static

-
-
- -
- -
- -

填写OSS存储外部访问域名,如:static.ctolog.cc

-
-
- -
- -
- -

可以在 [ 阿里云 > 个人中心 ] 设置并获取到访问密钥

-
-
- - -
- -
- -

可以在 [ 阿里云 > 个人中心 ] 设置并获取到安全密钥

-
-
- -
- -
-
- -
-
- -
-{/block} - -{block name="script"} - -{/block} \ No newline at end of file diff --git a/application/admin/view/config.index.html b/application/admin/view/config.index.html deleted file mode 100644 index 0141514b0..000000000 --- a/application/admin/view/config.index.html +++ /dev/null @@ -1,68 +0,0 @@ -{extend name="extra@admin/content"} - -{block name="content"} -
- -
- -
- -

网站名称,显示在浏览器标签上

-
-
- -
- -
- -

程序的版权信息设置,在后台登录页面显示

-
-
- -
- -
- -

当前程序名称,在后台主标题上显示

-
-
- - -
- -
- -

当前程序版本号,在后台主标题上标显示

-
-
- -
- -
- -

百度统计应用ID,可以在百度网站统计申请并获取

-
-
- -
- -
- - - 上传图片 -

建议上传ICO图标的尺寸为128x128px,此图标用于网站标题前,ICON在线制作。

-
-
- -
- -
-
- -
-
- -
- -{/block} \ No newline at end of file diff --git a/application/admin/view/config/file.html b/application/admin/view/config/file.html new file mode 100644 index 000000000..5e6fb44d5 --- /dev/null +++ b/application/admin/view/config/file.html @@ -0,0 +1,228 @@ +{extend name="admin@public/content"} + +{block name="content"} +
+ +
+ +
+ {foreach ['local'=>'本地服务器存储','qiniu'=>'七牛云存储','oss'=>'阿里云OSS存储'] as $k=>$v} + + {/foreach} +
+ 文件存储在本地服务器,请确保服务器的 ./static/upload/ 目录有写入权限 +
+
+ 若还没有七牛云帐号,可免费申请10G存储,申请成功后添加公开bucket。 +
+
+ 若还没有OSS存储账号, 可创建阿里云OSS存储,需要配置OSS公开访问及跨域策略。 +
+
+
+ +
+ +
+ +
+ +

设置系统允许上传文件的后缀,多个以英文逗号隔开。如:png,jpg,rar,doc

+
+
+ +
+ +
+ {foreach ['华东','华北','华南','北美'] as $area} + + {/foreach} +

七牛云存储空间所在区域,需要严格对应储存所在区域才能上传文件。

+
+
+ +
+ +
+ + + +

七牛云存储访问协议(http、https、auto),其中 https 需要配置证书才能使用,auto 为相对协议自动根据域名切换http与https。

+
+
+ +
+ +
+ +

填写七牛云存储空间名称,如:static

+
+
+ +
+ +
+ +

填写七牛云存储访问域名,如:static.ctolog.cc

+
+
+ +
+ +
+ +

可以在 [ 七牛云 > 个人中心 ] 设置并获取到访问密钥。

+
+
+ +
+ +
+ +

可以在 [ 七牛云 > 个人中心 ] 设置并获取到安全密钥。

+
+
+ +
+ +
+ + + +

阿里云对象存储访问协议(http、https、auto),其中 https 需要配置证书才能使用,auto 为相对协议自动根据域名切换http与https。

+
+
+ +
+ +
+ +

填写OSS存储空间名称,如:think-admin-oss

+
+
+ +
+ +
+ +

填写OSS数据中心访问域名,如:oss-cn-shenzhen.aliyuncs.com

+
+
+ +
+ +
+ +

填写OSS存储外部访问域名,如:think-admin-oss.oss-cn-shenzhen.aliyuncs.com

+
+
+ +
+ +
+ +

可以在 [ 阿里云 > 个人中心 ] 设置并获取到访问密钥。

+
+
+ +
+ +
+ +

可以在 [ 阿里云 > 个人中心 ] 设置并获取到安全密钥。

+
+
+ + +
+ +
+
+ +
+
+ +
+{/block} + +{block name="script"} + +{/block} \ No newline at end of file diff --git a/application/admin/view/config/index.html b/application/admin/view/config/index.html new file mode 100644 index 000000000..6b6cf751f --- /dev/null +++ b/application/admin/view/config/index.html @@ -0,0 +1,82 @@ +{extend name="admin@public/content"} + +{block name="content"} +
+ +
+ +
+ +

当前程序名称,在后台主标题上显示

+
+
+ +
+ +
+ +

当前程序版本号,在后台主标题上标显示

+
+
+ +
+ +
+ +
+ +

网站名称,显示在浏览器标签上

+
+
+ +
+ +
+ +

程序的版权信息设置,在后台登录页面显示

+
+
+ +
+ +
+ +
+ + + 上传图片 +

建议上传ICO图标的尺寸为128x128px,此图标用于网站标题前,ICON在线制作

+
+
+ +
+ +
+ +

网站备案号,可以在备案管理中心查询获取

+
+
+ +
+ +
+
+ +
+
+ +
+ +{/block} \ No newline at end of file diff --git a/application/admin/view/index.index.html b/application/admin/view/index.index.html deleted file mode 100644 index d8f8064fa..000000000 --- a/application/admin/view/index.index.html +++ /dev/null @@ -1,9 +0,0 @@ -{extend name="extra@admin/main"} - -{block name='body'} -{include file="extra@admin/main/top"} -
- {include file="extra@admin/main/left"} -
-
-{/block} \ No newline at end of file diff --git a/application/admin/view/index.main.html b/application/admin/view/index.main.html deleted file mode 100644 index e702533f8..000000000 --- a/application/admin/view/index.main.html +++ /dev/null @@ -1,122 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="content"} -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
系统信息
Think.Admin 版本{:sysconf('app_version')}
ThinkPHP 版本{$Think.const.THINK_VERSION}
服务器操作系统{:php_uname('s')}
WEB运行环境{:php_sapi_name()}
MySQL数据库版本{$mysql_ver}
运行PHP版本{:phpversion()}
上传大小限制{:ini_get('upload_max_filesize')}
POST大小限制{:ini_get('post_max_size')}
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
产品团队
产品名称Think.Admin 管理框架
产品研发团队广州楚才信息科技有限公司
产品DEMO体验 - - https://think.ctolog.com - -
官方QQ群 - - PHP微信开发群 (SDK) - -
BUG反馈 - - https://github.com/zoujingli/Think.Admin/issues - -
项目地址 - - https://github.com/zoujingli/Think.Admin - -
公司官网 - - http://www.cuci.cc - -
公司地址 - 广东省 广州市 海珠区 世港国际公寓E1栋 -
-
-
-{/block} \ No newline at end of file diff --git a/application/admin/view/index/index.html b/application/admin/view/index/index.html new file mode 100644 index 000000000..f0922dac4 --- /dev/null +++ b/application/admin/view/index/index.html @@ -0,0 +1,83 @@ +{extend name="admin@public/main"} + +{block name='body'} + + + + + + +
+ {foreach $menus as $oneMenu} + + + + {/foreach} +
+ + + +
+ +{/block} \ No newline at end of file diff --git a/application/admin/view/index/main.html b/application/admin/view/index/main.html new file mode 100644 index 000000000..d5e62ca14 --- /dev/null +++ b/application/admin/view/index/main.html @@ -0,0 +1,83 @@ +{extend name='admin@public/content'} + +{block name="content"} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
系统信息产品团队
ThinkAdmin 版本{:sysconf('app_version')}产品名称ThinkAdmin 管理框架
ThinkPHP 版本{$think_ver}产品名称ThinkAdmin 管理框架
服务器操作系统{:php_uname('s')}产品DEMO体验 + http://demo.thinkadmin.top +
WEB运行环境{:php_sapi_name()}官方QQ群 + + + +
MySQL数据库版本{$mysql_ver}BUG反馈 + + https://github.com/zoujingli/ThinkAdmin/issues + +
运行PHP版本{$Think.PHP_VERSION}项目地址 + + https://github.com/zoujingli/ThinkAdmin + +
上传大小限制{:ini_get('upload_max_filesize')}公司官网http://www.cuci.cc
POST大小限制{:ini_get('post_max_size')}公司地址广州市天河区东圃一横路东泷商贸中心C座316
+{/block} \ No newline at end of file diff --git a/application/admin/view/log.index.html b/application/admin/view/log.index.html deleted file mode 100644 index 9b48798a5..000000000 --- a/application/admin/view/log.index.html +++ /dev/null @@ -1,89 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="button"} -
- {if auth("$classuri/del")} - - {/if} -
-{/block} - -{block name="content"} - - - - - -
- - - - - - - - - - - - - - - {foreach $list as $key=>$vo} - - - - - - - - - - {/foreach} - -
- - 操作者节点行为操作内容操作位置操作时间
- - {$vo.username}{$vo.node}{$vo.action}{$vo.content}{$vo.isp|default=$vo.ip}{$vo.create_at}
- {if isset($page)}

{$page}

{/if} -
-{/block} \ No newline at end of file diff --git a/application/admin/view/log/index.html b/application/admin/view/log/index.html new file mode 100644 index 000000000..7c3afffe9 --- /dev/null +++ b/application/admin/view/log/index.html @@ -0,0 +1,100 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + +{/block} + +{block name="content"} + + + + + + +
+ +

没 有 记 录 哦!

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + 操作账号权限节点操作行为操作内容操作位置操作时间
+ + {$vo.username}{$vo.node}{$vo.action}{$vo.content}{$vo.isp|default=$vo.ip}{$vo.create_at|format_datetime}
+ {if isset($page)}

{$page|raw}

{/if} + +
+{/block} \ No newline at end of file diff --git a/application/admin/view/log/sms.html b/application/admin/view/log/sms.html new file mode 100644 index 000000000..6d99c46d3 --- /dev/null +++ b/application/admin/view/log/sms.html @@ -0,0 +1,76 @@ +{extend name='admin@public/content'} + +{block name="content"} + + + + + +
+ + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + {/foreach} + +
+ + 手机号短信内容返回结果发送时间
+ + {$vo.phone}{$vo.content}{$vo.result}{$vo.create_at|format_datetime}
+ {if isset($page)}

{$page}

{/if} + +
+{/block} \ No newline at end of file diff --git a/application/admin/view/login.index.html b/application/admin/view/login.index.html deleted file mode 100644 index 19e787b76..000000000 --- a/application/admin/view/login.index.html +++ /dev/null @@ -1,102 +0,0 @@ -{extend name="extra@admin/main"} - -{block name="style"} - -{/block} - -{block name="body"} - -{/block} - -{block name="script"} - -{/block} \ No newline at end of file diff --git a/application/admin/view/login/index.html b/application/admin/view/login/index.html new file mode 100644 index 000000000..bf83d4606 --- /dev/null +++ b/application/admin/view/login/index.html @@ -0,0 +1,82 @@ +{extend name="admin@public/main"} + +{block name="style"} + +{/block} + +{block name="body"} + +{/block} + +{block name="script"} + +{/block} \ No newline at end of file diff --git a/application/admin/view/menu.form.html b/application/admin/view/menu.form.html deleted file mode 100644 index 11c918b56..000000000 --- a/application/admin/view/menu.form.html +++ /dev/null @@ -1,58 +0,0 @@ -
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
- - - - -
- -
- -
- {if isset($vo['id'])}{/if} - - -
- - - -
diff --git a/application/admin/view/menu.index.html b/application/admin/view/menu.index.html deleted file mode 100644 index d980b13a9..000000000 --- a/application/admin/view/menu.index.html +++ /dev/null @@ -1,73 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="button"} -
- - -
-{/block} - -{block name="content"} - -
- - - - - - - - - - - - - - - {foreach $list as $key=>$vo} - - - - - - - - - - {/foreach} - -
- - - - 菜单名称菜单链接状态操作
- - - - - - {$vo.spl}{$vo.title}{$vo.url} - {if $vo.status eq 0} - 已禁用 - {elseif $vo.status eq 1} - 使用中 - {/if} - - {if auth("$classuri/edit")} - | - 编辑 - {/if} - {if $vo.status eq 1 and auth("$classuri/forbid")} - | - 禁用 - {elseif auth("$classuri/resume")} - | - 启用 - {/if} - {if auth("$classuri/del")} - | - 删除 - {/if} -
-
-{/block} \ No newline at end of file diff --git a/application/admin/view/menu/form.html b/application/admin/view/menu/form.html new file mode 100644 index 000000000..ad5d7b0f8 --- /dev/null +++ b/application/admin/view/menu/form.html @@ -0,0 +1,74 @@ + diff --git a/application/admin/view/menu/index.html b/application/admin/view/menu/index.html new file mode 100644 index 000000000..3b8d03255 --- /dev/null +++ b/application/admin/view/menu/index.html @@ -0,0 +1,87 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + +{/block} + +{block name="content"} +
+ +

没 有 记 录 哦!

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + {$vo.spl|raw}{$vo.title}{$vo.url} + {if $vo.status eq 0}已禁用{elseif $vo.status eq 1}使用中{/if} + + + {if auth("$classuri/add")} + | + + 添加下级 + + 添加下级 + + {/if} + + {if auth("$classuri/edit")} + | + 编辑 + {/if} + + {if $vo.status eq 1 and auth("$classuri/forbid")} + | + 禁用 + {elseif auth("$classuri/resume")} + | + 启用 + {/if} + + {if auth("$classuri/del")} + | + 删除 + {/if} + +
+ +
+{/block} \ No newline at end of file diff --git a/application/admin/view/node.index.html b/application/admin/view/node.index.html deleted file mode 100644 index 335a5092e..000000000 --- a/application/admin/view/node.index.html +++ /dev/null @@ -1,69 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="content"} - - - - - - - - - - - {foreach $nodes as $key=>$vo} - - - - - - - {/foreach} - -
系统节点结构 
- {$vo.spl}{$vo.node} - {if auth("$classuri/save")} -   - {/if} - - {if auth("$classuri/save")} - -      - - {/if} -
-{if auth("$classuri/save")} - -{/if} -{/block} \ No newline at end of file diff --git a/application/admin/view/node/index.html b/application/admin/view/node/index.html new file mode 100644 index 000000000..97e96dd86 --- /dev/null +++ b/application/admin/view/node/index.html @@ -0,0 +1,225 @@ +{extend name='admin@public/content'} + +{block name='button'} + + + +{/block} + +{block name="content"} + + + +
+
    + +
  • {$group.node.title|default='未配置名称'|raw}({$key})
  • + +
+
+ {foreach $groups as $k=>$group} +
+ + +

没 有 记 录 哦!

+ + + + + + + + + +
+ {$vo.spl|raw} {$vo.node} + {if auth("$classuri/save")} {/if} + + {if auth("$classuri/save") and $vo.spt eq 1} + +      + +      + + {/if} + {if auth("$classuri/save") and $vo.spt eq 2} +   ├─  + +      ├─  + +      ├─  + + {/if} +
+
+ {/foreach} +
+
+ + + + +{/block} \ No newline at end of file diff --git a/application/admin/view/plugs.icon.html b/application/admin/view/plugs.icon.html deleted file mode 100644 index a0b9127ce..000000000 --- a/application/admin/view/plugs.icon.html +++ /dev/null @@ -1,1931 +0,0 @@ - - - - {'app_name'|sysconf} {'app_version'|sysconf} - - - - - - - - - -
-
-
-
    -
  • fa fa-bluetooth
  • -
  • fa fa-bluetooth-b
  • -
  • fa fa-codiepie
  • -
  • fa fa-credit-card-alt
  • -
  • fa fa-edge
  • -
  • fa fa-fort-awesome
  • -
  • fa fa-hashtag
  • -
  • fa fa-mixcloud
  • -
  • fa fa-modx
  • -
  • fa fa-pause-circle
  • -
  • fa fa-pause-circle-o
  • -
  • fa fa-percent
  • -
  • fa fa-product-hunt
  • -
  • fa fa-reddit-alien
  • -
  • fa fa-scribd
  • -
  • fa fa-shopping-bag
  • -
  • fa fa-shopping-basket
  • -
  • fa fa-stop-circle
  • -
  • fa fa-stop-circle-o
  • -
  • fa fa-usb
  • -
  • fa fa-adjust
  • -
  • fa fa-anchor
  • -
  • fa fa-archive
  • -
  • fa fa-area-chart
  • -
  • fa fa-arrows
  • -
  • fa fa-arrows-h
  • -
  • fa fa-arrows-v
  • -
  • fa fa-asterisk
  • -
  • fa fa-at
  • -
  • fa fa-automobile
  • -
  • fa fa-balance-scale
  • -
  • fa fa-ban
  • -
  • fa fa-bank
  • -
  • fa fa-bar-chart
  • -
  • fa fa-bar-chart-o
  • -
  • fa fa-barcode
  • -
  • fa fa-bars
  • -
  • fa fa-battery-0
  • -
  • fa fa-battery-1
  • -
  • fa fa-battery-2
  • -
  • fa fa-battery-3
  • -
  • fa fa-battery-4
  • -
  • fa fa-battery-empty
  • -
  • fa fa-battery-full
  • -
  • fa fa-battery-half
  • -
  • fa fa-battery-quarter
  • -
  • fa fa-battery-three-quarters
  • -
  • fa fa-bed
  • -
  • fa fa-beer
  • -
  • fa fa-bell
  • -
  • fa fa-bell-o
  • -
  • fa fa-bell-slash
  • -
  • fa fa-bell-slash-o
  • -
  • fa fa-bicycle
  • -
  • fa fa-binoculars
  • -
  • fa fa-birthday-cake
  • -
  • fa fa-bluetooth
  • -
  • fa fa-bluetooth-b
  • -
  • fa fa-bolt
  • -
  • fa fa-bomb
  • -
  • fa fa-book
  • -
  • fa fa-bookmark
  • -
  • fa fa-bookmark-o
  • -
  • fa fa-briefcase
  • -
  • fa fa-bug
  • -
  • fa fa-building
  • -
  • fa fa-building-o
  • -
  • fa fa-bullhorn
  • -
  • fa fa-bullseye
  • -
  • fa fa-bus
  • -
  • fa fa-cab
  • -
  • fa fa-calculator
  • -
  • fa fa-calendar
  • -
  • fa fa-calendar-check-o
  • -
  • fa fa-calendar-minus-o
  • -
  • fa fa-calendar-o
  • -
  • fa fa-calendar-plus-o
  • -
  • fa fa-calendar-times-o
  • -
  • fa fa-camera
  • -
  • fa fa-camera-retro
  • -
  • fa fa-car
  • -
  • fa fa-caret-square-o-down
  • -
  • fa fa-caret-square-o-left
  • -
  • fa fa-caret-square-o-right
  • -
  • fa fa-caret-square-o-up
  • -
  • fa fa-cart-arrow-down
  • -
  • fa fa-cart-plus
  • -
  • fa fa-cc
  • -
  • fa fa-certificate
  • -
  • fa fa-check
  • -
  • fa fa-check-circle
  • -
  • fa fa-check-circle-o
  • -
  • fa fa-check-square
  • -
  • fa fa-check-square-o
  • -
  • fa fa-child
  • -
  • fa fa-circle
  • -
  • fa fa-circle-o
  • -
  • fa fa-circle-o-notch
  • -
  • fa fa-circle-thin
  • -
  • fa fa-clock-o
  • -
  • fa fa-clone
  • -
  • fa fa-close
  • -
  • fa fa-cloud
  • -
  • fa fa-cloud-download
  • -
  • fa fa-cloud-upload
  • -
  • fa fa-code
  • -
  • fa fa-code-fork
  • -
  • fa fa-coffee
  • -
  • fa fa-cog
  • -
  • fa fa-cogs
  • -
  • fa fa-comment
  • -
  • fa fa-comment-o
  • -
  • fa fa-commenting
  • -
  • fa fa-commenting-o
  • -
  • fa fa-comments
  • -
  • fa fa-comments-o
  • -
  • fa fa-compass
  • -
  • fa fa-copyright
  • -
  • fa fa-creative-commons
  • -
  • fa fa-credit-card
  • -
  • fa fa-credit-card-alt
  • -
  • fa fa-crop
  • -
  • fa fa-crosshairs
  • -
  • fa fa-cube
  • -
  • fa fa-cubes
  • -
  • fa fa-cutlery
  • -
  • fa fa-dashboard
  • -
  • fa fa-database
  • -
  • fa fa-desktop
  • -
  • fa fa-diamond
  • -
  • fa fa-dot-circle-o
  • -
  • fa fa-download
  • -
  • fa fa-edit
  • -
  • fa fa-ellipsis-h
  • -
  • fa fa-ellipsis-v
  • -
  • fa fa-envelope
  • -
  • fa fa-envelope-o
  • -
  • fa fa-envelope-square
  • -
  • fa fa-eraser
  • -
  • fa fa-exchange
  • -
  • fa fa-exclamation
  • -
  • fa fa-exclamation-circle
  • -
  • fa fa-exclamation-triangle
  • -
  • fa fa-external-link
  • -
  • fa fa-external-link-square
  • -
  • fa fa-eye
  • -
  • fa fa-eye-slash
  • -
  • fa fa-eyedropper
  • -
  • fa fa-fax
  • -
  • fa fa-feed
  • -
  • fa fa-female
  • -
  • fa fa-fighter-jet
  • -
  • fa fa-file-archive-o
  • -
  • fa fa-file-audio-o
  • -
  • fa fa-file-code-o
  • -
  • fa fa-file-excel-o
  • -
  • fa fa-file-image-o
  • -
  • fa fa-file-movie-o
  • -
  • fa fa-file-pdf-o
  • -
  • fa fa-file-photo-o
  • -
  • fa fa-file-picture-o
  • -
  • fa fa-file-powerpoint-o
  • -
  • fa fa-file-sound-o
  • -
  • fa fa-file-video-o
  • -
  • fa fa-file-word-o
  • -
  • fa fa-file-zip-o
  • -
  • fa fa-film
  • -
  • fa fa-filter
  • -
  • fa fa-fire
  • -
  • fa fa-fire-extinguisher
  • -
  • fa fa-flag
  • -
  • fa fa-flag-checkered
  • -
  • fa fa-flag-o
  • -
  • fa fa-flash
  • -
  • fa fa-flask
  • -
  • fa fa-folder
  • -
  • fa fa-folder-o
  • -
  • fa fa-folder-open
  • -
  • fa fa-folder-open-o
  • -
  • fa fa-frown-o
  • -
  • fa fa-futbol-o
  • -
  • fa fa-gamepad
  • -
  • fa fa-gavel
  • -
  • fa fa-gear
  • -
  • fa fa-gears
  • -
  • fa fa-gift
  • -
  • fa fa-glass
  • -
  • fa fa-globe
  • -
  • fa fa-graduation-cap
  • -
  • fa fa-group
  • -
  • fa fa-hand-grab-o
  • -
  • fa fa-hand-lizard-o
  • -
  • fa fa-hand-paper-o
  • -
  • fa fa-hand-peace-o
  • -
  • fa fa-hand-pointer-o
  • -
  • fa fa-hand-rock-o
  • -
  • fa fa-hand-scissors-o
  • -
  • fa fa-hand-spock-o
  • -
  • fa fa-hand-stop-o
  • -
  • fa fa-hashtag
  • -
  • fa fa-hdd-o
  • -
  • fa fa-headphones
  • -
  • fa fa-heart
  • -
  • fa fa-heart-o
  • -
  • fa fa-heartbeat
  • -
  • fa fa-history
  • -
  • fa fa-home
  • -
  • fa fa-hotel
  • -
  • fa fa-hourglass
  • -
  • fa fa-hourglass-1
  • -
  • fa fa-hourglass-2
  • -
  • fa fa-hourglass-3
  • -
  • fa fa-hourglass-end
  • -
  • fa fa-hourglass-half
  • -
  • fa fa-hourglass-o
  • -
  • fa fa-hourglass-start
  • -
  • fa fa-i-cursor
  • -
  • fa fa-image
  • -
  • fa fa-inbox
  • -
  • fa fa-industry
  • -
  • fa fa-info
  • -
  • fa fa-info-circle
  • -
  • fa fa-institution
  • -
  • fa fa-key
  • -
  • fa fa-keyboard-o
  • -
  • fa fa-language
  • -
  • fa fa-laptop
  • -
  • fa fa-leaf
  • -
  • fa fa-legal
  • -
  • fa fa-lemon-o
  • -
  • fa fa-level-down
  • -
  • fa fa-level-up
  • -
  • fa fa-life-bouy
  • -
  • fa fa-life-buoy
  • -
  • fa fa-life-ring
  • -
  • fa fa-life-saver
  • -
  • fa fa-lightbulb-o
  • -
  • fa fa-line-chart
  • -
  • fa fa-location-arrow
  • -
  • fa fa-lock
  • -
  • fa fa-magic
  • -
  • fa fa-magnet
  • -
  • fa fa-mail-forward
  • -
  • fa fa-mail-reply
  • -
  • fa fa-mail-reply-all
  • -
  • fa fa-male
  • -
  • fa fa-map
  • -
  • fa fa-map-marker
  • -
  • fa fa-map-o
  • -
  • fa fa-map-pin
  • -
  • fa fa-map-signs
  • -
  • fa fa-meh-o
  • -
  • fa fa-microphone
  • -
  • fa fa-microphone-slash
  • -
  • fa fa-minus
  • -
  • fa fa-minus-circle
  • -
  • fa fa-minus-square
  • -
  • fa fa-minus-square-o
  • -
  • fa fa-mobile
  • -
  • fa fa-mobile-phone
  • -
  • fa fa-money
  • -
  • fa fa-moon-o
  • -
  • fa fa-mortar-board
  • -
  • fa fa-motorcycle
  • -
  • fa fa-mouse-pointer
  • -
  • fa fa-music
  • -
  • fa fa-navicon
  • -
  • fa fa-newspaper-o
  • -
  • fa fa-object-group
  • -
  • fa fa-object-ungroup
  • -
  • fa fa-paint-brush
  • -
  • fa fa-paper-plane
  • -
  • fa fa-paper-plane-o
  • -
  • fa fa-paw
  • -
  • fa fa-pencil
  • -
  • fa fa-pencil-square
  • -
  • fa fa-pencil-square-o
  • -
  • fa fa-percent
  • -
  • fa fa-phone
  • -
  • fa fa-phone-square
  • -
  • fa fa-photo
  • -
  • fa fa-picture-o
  • -
  • fa fa-pie-chart
  • -
  • fa fa-plane
  • -
  • fa fa-plug
  • -
  • fa fa-plus
  • -
  • fa fa-plus-circle
  • -
  • fa fa-plus-square
  • -
  • fa fa-plus-square-o
  • -
  • fa fa-power-off
  • -
  • fa fa-print
  • -
  • fa fa-puzzle-piece
  • -
  • fa fa-qrcode
  • -
  • fa fa-question
  • -
  • fa fa-question-circle
  • -
  • fa fa-quote-left
  • -
  • fa fa-quote-right
  • -
  • fa fa-random
  • -
  • fa fa-recycle
  • -
  • fa fa-refresh
  • -
  • fa fa-registered
  • -
  • fa fa-remove
  • -
  • fa fa-reorder
  • -
  • fa fa-reply
  • -
  • fa fa-reply-all
  • -
  • fa fa-retweet
  • -
  • fa fa-road
  • -
  • fa fa-rocket
  • -
  • fa fa-rss
  • -
  • fa fa-rss-square
  • -
  • fa fa-search
  • -
  • fa fa-search-minus
  • -
  • fa fa-search-plus
  • -
  • fa fa-send
  • -
  • fa fa-send-o
  • -
  • fa fa-server
  • -
  • fa fa-share
  • -
  • fa fa-share-alt
  • -
  • fa fa-share-alt-square
  • -
  • fa fa-share-square
  • -
  • fa fa-share-square-o
  • -
  • fa fa-shield
  • -
  • fa fa-ship
  • -
  • fa fa-shopping-bag
  • -
  • fa fa-shopping-basket
  • -
  • fa fa-shopping-cart
  • -
  • fa fa-sign-in
  • -
  • fa fa-sign-out
  • -
  • fa fa-signal
  • -
  • fa fa-sitemap
  • -
  • fa fa-sliders
  • -
  • fa fa-smile-o
  • -
  • fa fa-soccer-ball-o
  • -
  • fa fa-sort
  • -
  • fa fa-sort-alpha-asc
  • -
  • fa fa-sort-alpha-desc
  • -
  • fa fa-sort-amount-asc
  • -
  • fa fa-sort-amount-desc
  • -
  • fa fa-sort-asc
  • -
  • fa fa-sort-desc
  • -
  • fa fa-sort-down
  • -
  • fa fa-sort-numeric-asc
  • -
  • fa fa-sort-numeric-desc
  • -
  • fa fa-sort-up
  • -
  • fa fa-space-shuttle
  • -
  • fa fa-spoon
  • -
  • fa fa-square
  • -
  • fa fa-square-o
  • -
  • fa fa-star
  • -
  • fa fa-star-half
  • -
  • fa fa-star-half-empty
  • -
  • fa fa-star-half-full
  • -
  • fa fa-star-half-o
  • -
  • fa fa-star-o
  • -
  • fa fa-sticky-note
  • -
  • fa fa-sticky-note-o
  • -
  • fa fa-street-view
  • -
  • fa fa-suitcase
  • -
  • fa fa-sun-o
  • -
  • fa fa-support
  • -
  • fa fa-tablet
  • -
  • fa fa-tachometer
  • -
  • fa fa-tag
  • -
  • fa fa-tags
  • -
  • fa fa-tasks
  • -
  • fa fa-taxi
  • -
  • fa fa-television
  • -
  • fa fa-terminal
  • -
  • fa fa-thumb-tack
  • -
  • fa fa-thumbs-down
  • -
  • fa fa-thumbs-o-down
  • -
  • fa fa-thumbs-o-up
  • -
  • fa fa-thumbs-up
  • -
  • fa fa-ticket
  • -
  • fa fa-times
  • -
  • fa fa-times-circle
  • -
  • fa fa-times-circle-o
  • -
  • fa fa-tint
  • -
  • fa fa-toggle-down
  • -
  • fa fa-toggle-left
  • -
  • fa fa-toggle-off
  • -
  • fa fa-toggle-on
  • -
  • fa fa-toggle-right
  • -
  • fa fa-toggle-up
  • -
  • fa fa-trademark
  • -
  • fa fa-trash
  • -
  • fa fa-trash-o
  • -
  • fa fa-tree
  • -
  • fa fa-trophy
  • -
  • fa fa-truck
  • -
  • fa fa-tty
  • -
  • fa fa-tv
  • -
  • fa fa-umbrella
  • -
  • fa fa-university
  • -
  • fa fa-unlock
  • -
  • fa fa-unlock-alt
  • -
  • fa fa-unsorted
  • -
  • fa fa-upload
  • -
  • fa fa-user
  • -
  • fa fa-user-plus
  • -
  • fa fa-user-secret
  • -
  • fa fa-user-times
  • -
  • fa fa-users
  • -
  • fa fa-video-camera
  • -
  • fa fa-volume-down
  • -
  • fa fa-volume-off
  • -
  • fa fa-volume-up
  • -
  • fa fa-warning
  • -
  • fa fa-wheelchair
  • -
  • fa fa-wifi
  • -
  • fa fa-wrench
  • -
  • fa fa-hand-grab-o
  • -
  • fa fa-hand-lizard-o
  • -
  • fa fa-hand-o-down
  • -
  • fa fa-hand-o-left
  • -
  • fa fa-hand-o-right
  • -
  • fa fa-hand-o-up
  • -
  • fa fa-hand-paper-o
  • -
  • fa fa-hand-peace-o
  • -
  • fa fa-hand-pointer-o
  • -
  • fa fa-hand-rock-o
  • -
  • fa fa-hand-scissors-o
  • -
  • fa fa-hand-spock-o
  • -
  • fa fa-hand-stop-o
  • -
  • fa fa-thumbs-down
  • -
  • fa fa-thumbs-o-down
  • -
  • fa fa-thumbs-o-up
  • -
  • fa fa-thumbs-up
  • -
  • fa fa-ambulance
  • -
  • fa fa-automobile
  • -
  • fa fa-bicycle
  • -
  • fa fa-bus
  • -
  • fa fa-cab
  • -
  • fa fa-car
  • -
  • fa fa-fighter-jet
  • -
  • fa fa-motorcycle
  • -
  • fa fa-plane
  • -
  • fa fa-rocket
  • -
  • fa fa-ship
  • -
  • fa fa-space-shuttle
  • -
  • fa fa-subway
  • -
  • fa fa-taxi
  • -
  • fa fa-train
  • -
  • fa fa-truck
  • -
  • fa fa-wheelchair
  • -
  • fa fa-genderless
  • -
  • fa fa-intersex
  • -
  • fa fa-mars
  • -
  • fa fa-mars-double
  • -
  • fa fa-mars-stroke
  • -
  • fa fa-mars-stroke-h
  • -
  • fa fa-mars-stroke-v
  • -
  • fa fa-mercury
  • -
  • fa fa-neuter
  • -
  • fa fa-transgender
  • -
  • fa fa-transgender-alt
  • -
  • fa fa-venus
  • -
  • fa fa-venus-double
  • -
  • fa fa-venus-mars
  • -
  • fa fa-file
  • -
  • fa fa-file-archive-o
  • -
  • fa fa-file-audio-o
  • -
  • fa fa-file-code-o
  • -
  • fa fa-file-excel-o
  • -
  • fa fa-file-image-o
  • -
  • fa fa-file-movie-o
  • -
  • fa fa-file-o
  • -
  • fa fa-file-pdf-o
  • -
  • fa fa-file-photo-o
  • -
  • fa fa-file-picture-o
  • -
  • fa fa-file-powerpoint-o
  • -
  • fa fa-file-sound-o
  • -
  • fa fa-file-text
  • -
  • fa fa-file-text-o
  • -
  • fa fa-file-video-o
  • -
  • fa fa-file-word-o
  • -
  • fa fa-file-zip-o
  • -
  • fa fa-circle-o-notch
  • -
  • fa fa-cog
  • -
  • fa fa-gear
  • -
  • fa fa-refresh
  • -
  • faner
  • -
  • fa fa-check-square
  • -
  • fa fa-check-square-o
  • -
  • fa fa-circle
  • -
  • fa fa-circle-o
  • -
  • fa fa-dot-circle-o
  • -
  • fa fa-minus-square
  • -
  • fa fa-minus-square-o
  • -
  • fa fa-plus-square
  • -
  • fa fa-plus-square-o
  • -
  • fa fa-square
  • -
  • fa fa-square-o
  • -
  • fa fa-cc-amex
  • -
  • fa fa-cc-diners-club
  • -
  • fa fa-cc-discover
  • -
  • fa fa-cc-jcb
  • -
  • fa fa-cc-mastercard
  • -
  • fa fa-cc-paypal
  • -
  • fa fa-cc-stripe
  • -
  • fa fa-cc-visa
  • -
  • fa fa-credit-card
  • -
  • fa fa-credit-card-alt
  • -
  • fa fa-google-wallet
  • -
  • fa fa-paypal
  • -
  • fa fa-area-chart
  • -
  • fa fa-bar-chart
  • -
  • fa fa-bar-chart-o
  • -
  • fa fa-line-chart
  • -
  • fa fa-pie-chart
  • -
  • fa fa-bitcoin
  • -
  • fa fa-btc
  • -
  • fa fa-cny
  • -
  • fa fa-dollar
  • -
  • fa fa-eur
  • -
  • fa fa-euro
  • -
  • fa fa-gbp
  • -
  • fa fa-gg
  • -
  • fa fa-gg-circle
  • -
  • fa fa-ils
  • -
  • fa fa-inr
  • -
  • fa fa-jpy
  • -
  • fa fa-krw
  • -
  • fa fa-money
  • -
  • fa fa-rmb
  • -
  • fa fa-rouble
  • -
  • fa fa-rub
  • -
  • fa fa-ruble
  • -
  • fa fa-rupee
  • -
  • fa fa-shekel
  • -
  • fa fa-sheqel
  • -
  • fa fa-try
  • -
  • fa fa-turkish-lira
  • -
  • fa fa-usd
  • -
  • fa fa-won
  • -
  • fa fa-yen
  • -
  • fa fa-align-center
  • -
  • fa fa-align-justify
  • -
  • fa fa-align-left
  • -
  • fa fa-align-right
  • -
  • fa fa-bold
  • -
  • fa fa-chain
  • -
  • fa fa-chain-broken
  • -
  • fa fa-clipboard
  • -
  • fa fa-columns
  • -
  • fa fa-copy
  • -
  • fa fa-cut
  • -
  • fa fa-dedent
  • -
  • fa fa-eraser
  • -
  • fa fa-file
  • -
  • fa fa-file-o
  • -
  • fa fa-file-text
  • -
  • fa fa-file-text-o
  • -
  • fa fa-files-o
  • -
  • fa fa-floppy-o
  • -
  • fa fa-font
  • -
  • fa fa-header
  • -
  • fa fa-indent
  • -
  • fa fa-italic
  • -
  • fa fa-link
  • -
  • fa fa-list
  • -
  • fa fa-list-alt
  • -
  • fa fa-list-ol
  • -
  • fa fa-list-ul
  • -
  • fa fa-outdent
  • -
  • fa fa-paperclip
  • -
  • fa fa-paragraph
  • -
  • fa fa-paste
  • -
  • fa fa-repeat
  • -
  • fa fa-rotate-left
  • -
  • fa fa-rotate-right
  • -
  • fa fa-save
  • -
  • fa fa-scissors
  • -
  • fa fa-strikethrough
  • -
  • fa fa-subscript
  • -
  • fa fa-superscript
  • -
  • fa fa-table
  • -
  • fa fa-text-height
  • -
  • fa fa-text-width
  • -
  • fa fa-th
  • -
  • fa fa-th-large
  • -
  • fa fa-th-list
  • -
  • fa fa-underline
  • -
  • fa fa-undo
  • -
  • fa fa-unlink
  • -
  • fa fa-angle-double-down
  • -
  • fa fa-angle-double-left
  • -
  • fa fa-angle-double-right
  • -
  • fa fa-angle-double-up
  • -
  • fa fa-angle-down
  • -
  • fa fa-angle-left
  • -
  • fa fa-angle-right
  • -
  • fa fa-angle-up
  • -
  • fa fa-arrow-circle-down
  • -
  • fa fa-arrow-circle-left
  • -
  • fa fa-arrow-circle-o-down
  • -
  • fa fa-arrow-circle-o-left
  • -
  • fa fa-arrow-circle-o-right
  • -
  • fa fa-arrow-circle-o-up
  • -
  • fa fa-arrow-circle-right
  • -
  • fa fa-arrow-circle-up
  • -
  • fa fa-arrow-down
  • -
  • fa fa-arrow-left
  • -
  • fa fa-arrow-right
  • -
  • fa fa-arrow-up
  • -
  • fa fa-arrows
  • -
  • fa fa-arrows-alt
  • -
  • fa fa-arrows-h
  • -
  • fa fa-arrows-v
  • -
  • fa fa-caret-down
  • -
  • fa fa-caret-left
  • -
  • fa fa-caret-right
  • -
  • fa fa-caret-square-o-down
  • -
  • fa fa-caret-square-o-left
  • -
  • fa fa-caret-square-o-right
  • -
  • fa fa-caret-square-o-up
  • -
  • fa fa-caret-up
  • -
  • fa fa-chevron-circle-down
  • -
  • fa fa-chevron-circle-left
  • -
  • fa fa-chevron-circle-right
  • -
  • fa fa-chevron-circle-up
  • -
  • fa fa-chevron-down
  • -
  • fa fa-chevron-left
  • -
  • fa fa-chevron-right
  • -
  • fa fa-chevron-up
  • -
  • fa fa-exchange
  • -
  • fa fa-hand-o-down
  • -
  • fa fa-hand-o-left
  • -
  • fa fa-hand-o-right
  • -
  • fa fa-hand-o-up
  • -
  • fa fa-long-arrow-down
  • -
  • fa fa-long-arrow-left
  • -
  • fa fa-long-arrow-right
  • -
  • fa fa-long-arrow-up
  • -
  • fa fa-toggle-down
  • -
  • fa fa-toggle-left
  • -
  • fa fa-toggle-right
  • -
  • fa fa-toggle-up
  • -
  • fa fa-arrows-alt
  • -
  • fa fa-backward
  • -
  • fa fa-compress
  • -
  • fa fa-eject
  • -
  • fa fa-expand
  • -
  • fa fa-fast-backward
  • -
  • fa fa-fast-forward
  • -
  • fa fa-forward
  • -
  • fa fa-pause
  • -
  • fa fa-pause-circle
  • -
  • fa fa-pause-circle-o
  • -
  • fa fa-play
  • -
  • fa fa-play-circle
  • -
  • fa fa-play-circle-o
  • -
  • fa fa-random
  • -
  • fa fa-step-backward
  • -
  • fa fa-step-forward
  • -
  • fa fa-stop
  • -
  • fa fa-stop-circle
  • -
  • fa fa-stop-circle-o
  • -
  • fa fa-youtube-play
  • -
  • fa fa-500px
  • -
  • fa fa-adn
  • -
  • fa fa-amazon
  • -
  • fa fa-android
  • -
  • fa fa-angellist
  • -
  • fa fa-apple
  • -
  • fa fa-behance
  • -
  • fa fa-behance-square
  • -
  • fa fa-bitbucket
  • -
  • fa fa-bitbucket-square
  • -
  • fa fa-bitcoin
  • -
  • fa fa-black-tie
  • -
  • fa fa-bluetooth
  • -
  • fa fa-bluetooth-b
  • -
  • fa fa-btc
  • -
  • fa fa-buysellads
  • -
  • fa fa-cc-amex
  • -
  • fa fa-cc-diners-club
  • -
  • fa fa-cc-discover
  • -
  • fa fa-cc-jcb
  • -
  • fa fa-cc-mastercard
  • -
  • fa fa-cc-paypal
  • -
  • fa fa-cc-stripe
  • -
  • fa fa-cc-visa
  • -
  • fa fa-chrome
  • -
  • fa fa-codepen
  • -
  • fa fa-codiepie
  • -
  • fa fa-connectdevelop
  • -
  • fa fa-contao
  • -
  • fa fa-css3
  • -
  • fa fa-dashcube
  • -
  • fa fa-delicious
  • -
  • fa fa-deviantart
  • -
  • fa fa-digg
  • -
  • fa fa-dribbble
  • -
  • fa fa-dropbox
  • -
  • fa fa-drupal
  • -
  • fa fa-edge
  • -
  • fa fa-empire
  • -
  • fa fa-expeditedssl
  • -
  • fa fa-facebook
  • -
  • fa fa-facebook-f
  • -
  • fa fa-facebook-official
  • -
  • fa fa-facebook-square
  • -
  • fa fa-firefox
  • -
  • fa fa-flickr
  • -
  • fa fa-fonticons
  • -
  • fa fa-fort-awesome
  • -
  • fa fa-forumbee
  • -
  • fa fa-foursquare
  • -
  • fa fa-ge
  • -
  • fa fa-get-pocket
  • -
  • fa fa-gg
  • -
  • fa fa-gg-circle
  • -
  • fa fa-git
  • -
  • fa fa-git-square
  • -
  • fa fa-github
  • -
  • fa fa-github-alt
  • -
  • fa fa-github-square
  • -
  • fa fa-gittip
  • -
  • fa fa-google
  • -
  • fa fa-google-plus
  • -
  • fa fa-google-plus-square
  • -
  • fa fa-google-wallet
  • -
  • fa fa-gratipay
  • -
  • fa fa-hacker-news
  • -
  • fa fa-houzz
  • -
  • fa fa-html5
  • -
  • fa fa-instagram
  • -
  • fa fa-internet-explorer
  • -
  • fa fa-ioxhost
  • -
  • fa fa-joomla
  • -
  • fa fa-jsfiddle
  • -
  • fa fa-lastfm
  • -
  • fa fa-lastfm-square
  • -
  • fa fa-leanpub
  • -
  • fa fa-linkedin
  • -
  • fa fa-linkedin-square
  • -
  • fa fa-linux
  • -
  • fa fa-maxcdn
  • -
  • fa fa-meanpath
  • -
  • fa fa-medium
  • -
  • fa fa-mixcloud
  • -
  • fa fa-modx
  • -
  • fa fa-odnoklassniki
  • -
  • fa fa-odnoklassniki-square
  • -
  • fa fa-opencart
  • -
  • fa fa-openid
  • -
  • fa fa-opera
  • -
  • fa fa-optin-monster
  • -
  • fa fa-pagelines
  • -
  • fa fa-paypal
  • -
  • fa fa-pied-piper
  • -
  • fa fa-pied-piper-alt
  • -
  • fa fa-pinterest
  • -
  • fa fa-pinterest-p
  • -
  • fa fa-pinterest-square
  • -
  • fa fa-product-hunt
  • -
  • fa fa-qq
  • -
  • fa fa-ra
  • -
  • fa fa-rebel
  • -
  • fa fa-reddit
  • -
  • fa fa-reddit-alien
  • -
  • fa fa-reddit-square
  • -
  • fa fa-renren
  • -
  • fa fa-safari
  • -
  • fa fa-scribd
  • -
  • fa fa-sellsy
  • -
  • fa fa-share-alt
  • -
  • fa fa-share-alt-square
  • -
  • fa fa-shirtsinbulk
  • -
  • fa fa-simplybuilt
  • -
  • fa fa-skyatlas
  • -
  • fa fa-skype
  • -
  • fa fa-slack
  • -
  • fa fa-slideshare
  • -
  • fa fa-soundcloud
  • -
  • fa fa-spotify
  • -
  • fa fa-stack-exchange
  • -
  • fa fa-stack-overflow
  • -
  • fa fa-steam
  • -
  • fa fa-steam-square
  • -
  • fa fa-stumbleupon
  • -
  • fa fa-stumbleupon-circle
  • -
  • fa fa-tencent-weibo
  • -
  • fa fa-trello
  • -
  • fa fa-tripadvisor
  • -
  • fa fa-tumblr
  • -
  • fa fa-tumblr-square
  • -
  • fa fa-twitch
  • -
  • fa fa-twitter
  • -
  • fa fa-twitter-square
  • -
  • fa fa-usb
  • -
  • fa fa-viacoin
  • -
  • fa fa-vimeo
  • -
  • fa fa-vimeo-square
  • -
  • fa fa-vine
  • -
  • fa fa-vk
  • -
  • fa fa-wechat
  • -
  • fa fa-weibo
  • -
  • fa fa-weixin
  • -
  • fa fa-whatsapp
  • -
  • fa fa-wikipedia-w
  • -
  • fa fa-windows
  • -
  • fa fa-wordpress
  • -
  • fa fa-xing
  • -
  • fa fa-xing-square
  • -
  • fa fa-y-combinator
  • -
  • fa fa-y-combinator-square
  • -
  • fa fa-yahoo
  • -
  • fa fa-yc
  • -
  • fa fa-yc-square
  • -
  • fa fa-yelp
  • -
  • fa fa-youtube
  • -
  • fa fa-youtube-play
  • -
  • fa fa-youtube-square
  • - -
  • fa fa-ambulance
  • -
  • fa fa-h-square
  • -
  • fa fa-heart
  • -
  • fa fa-heart-o
  • -
  • fa fa-heartbeat
  • -
  • fa fa-hospital-o
  • -
  • fa fa-medkit
  • -
  • fa fa-plus-square
  • -
  • fa fa-stethoscope
  • -
  • fa fa-user-md
  • -
  • fa fa-wheelchair
  • -
  • - - glyphicon glyphicon-asterisk -
  • -
  • - - glyphicon glyphicon-plus -
  • -
  • - - glyphicon glyphicon-euro -
  • -
  • - - glyphicon glyphicon-eur -
  • -
  • - - glyphicon glyphicon-minus -
  • -
  • - - glyphicon glyphicon-cloud -
  • -
  • - - glyphicon glyphicon-envelope -
  • -
  • - - glyphicon glyphicon-pencil -
  • -
  • - - glyphicon glyphicon-glass -
  • -
  • - - glyphicon glyphicon-music -
  • -
  • - - glyphicon glyphicon-search -
  • -
  • - - glyphicon glyphicon-heart -
  • -
  • - - glyphicon glyphicon-star -
  • -
  • - - glyphicon glyphicon-star-empty -
  • -
  • - - glyphicon glyphicon-user -
  • -
  • - - glyphicon glyphicon-film -
  • -
  • - - glyphicon glyphicon-th-large -
  • -
  • - - glyphicon glyphicon-th -
  • -
  • - - glyphicon glyphicon-th-list -
  • -
  • - - glyphicon glyphicon-ok -
  • -
  • - - glyphicon glyphicon-remove -
  • -
  • - - glyphicon glyphicon-zoom-in -
  • -
  • - - glyphicon glyphicon-zoom-out -
  • -
  • - - glyphicon glyphicon-off -
  • -
  • - - glyphicon glyphicon-signal -
  • -
  • - - glyphicon glyphicon-cog -
  • -
  • - - glyphicon glyphicon-trash -
  • -
  • - - glyphicon glyphicon-home -
  • -
  • - - glyphicon glyphicon-file -
  • -
  • - - glyphicon glyphicon-time -
  • -
  • - - glyphicon glyphicon-road -
  • -
  • - - glyphicon glyphicon-download-alt -
  • -
  • - - glyphicon glyphicon-download -
  • -
  • - - glyphicon glyphicon-upload -
  • -
  • - - glyphicon glyphicon-inbox -
  • -
  • - - glyphicon glyphicon-play-circle -
  • -
  • - - glyphicon glyphicon-repeat -
  • -
  • - - glyphicon glyphicon-refresh -
  • -
  • - - glyphicon glyphicon-list-alt -
  • -
  • - - glyphicon glyphicon-lock -
  • -
  • - - glyphicon glyphicon-flag -
  • -
  • - - glyphicon glyphicon-headphones -
  • -
  • - - glyphicon glyphicon-volume-off -
  • -
  • - - glyphicon glyphicon-volume-down -
  • -
  • - - glyphicon glyphicon-volume-up -
  • -
  • - - glyphicon glyphicon-qrcode -
  • -
  • - - glyphicon glyphicon-barcode -
  • -
  • - - glyphicon glyphicon-tag -
  • -
  • - - glyphicon glyphicon-tags -
  • -
  • - - glyphicon glyphicon-book -
  • -
  • - - glyphicon glyphicon-bookmark -
  • -
  • - - glyphicon glyphicon-print -
  • -
  • - - glyphicon glyphicon-camera -
  • -
  • - - glyphicon glyphicon-font -
  • -
  • - - glyphicon glyphicon-bold -
  • -
  • - - glyphicon glyphicon-italic -
  • -
  • - - glyphicon glyphicon-text-height -
  • -
  • - - glyphicon glyphicon-text-width -
  • -
  • - - glyphicon glyphicon-align-left -
  • -
  • - - glyphicon glyphicon-align-center -
  • -
  • - - glyphicon glyphicon-align-right -
  • -
  • - - glyphicon glyphicon-align-justify -
  • -
  • - - glyphicon glyphicon-list -
  • -
  • - - glyphicon glyphicon-indent-left -
  • -
  • - - glyphicon glyphicon-indent-right -
  • -
  • - - glyphicon glyphicon-facetime-video -
  • -
  • - - glyphicon glyphicon-picture -
  • -
  • - - glyphicon glyphicon-map-marker -
  • -
  • - - glyphicon glyphicon-adjust -
  • -
  • - - glyphicon glyphicon-tint -
  • -
  • - - glyphicon glyphicon-edit -
  • -
  • - - glyphicon glyphicon-share -
  • -
  • - - glyphicon glyphicon-check -
  • -
  • - - glyphicon glyphicon-move -
  • -
  • - - glyphicon glyphicon-step-backward -
  • -
  • - - glyphicon glyphicon-fast-backward -
  • -
  • - - glyphicon glyphicon-backward -
  • -
  • - - glyphicon glyphicon-play -
  • -
  • - - glyphicon glyphicon-pause -
  • -
  • - - glyphicon glyphicon-stop -
  • -
  • - - glyphicon glyphicon-forward -
  • -
  • - - glyphicon glyphicon-fast-forward -
  • -
  • - - glyphicon glyphicon-step-forward -
  • -
  • - - glyphicon glyphicon-eject -
  • -
  • - - glyphicon glyphicon-chevron-left -
  • -
  • - - glyphicon glyphicon-chevron-right -
  • -
  • - - glyphicon glyphicon-plus-sign -
  • -
  • - - glyphicon glyphicon-minus-sign -
  • -
  • - - glyphicon glyphicon-remove-sign -
  • -
  • - - glyphicon glyphicon-ok-sign -
  • -
  • - - glyphicon glyphicon-question-sign -
  • -
  • - - glyphicon glyphicon-info-sign -
  • -
  • - - glyphicon glyphicon-screenshot -
  • -
  • - - glyphicon glyphicon-remove-circle -
  • -
  • - - glyphicon glyphicon-ok-circle -
  • -
  • - - glyphicon glyphicon-ban-circle -
  • -
  • - - glyphicon glyphicon-arrow-left -
  • -
  • - - glyphicon glyphicon-arrow-right -
  • -
  • - - glyphicon glyphicon-arrow-up -
  • -
  • - - glyphicon glyphicon-arrow-down -
  • -
  • - - glyphicon glyphicon-share-alt -
  • -
  • - - glyphicon glyphicon-resize-full -
  • -
  • - - glyphicon glyphicon-resize-small -
  • -
  • - - glyphicon glyphicon-exclamation-sign -
  • -
  • - - glyphicon glyphicon-gift -
  • -
  • - - glyphicon glyphicon-leaf -
  • -
  • - - glyphicon glyphicon-fire -
  • -
  • - - glyphicon glyphicon-eye-open -
  • -
  • - - glyphicon glyphicon-eye-close -
  • -
  • - - glyphicon glyphicon-warning-sign -
  • -
  • - - glyphicon glyphicon-plane -
  • -
  • - - glyphicon glyphicon-calendar -
  • -
  • - - glyphicon glyphicon-random -
  • -
  • - - glyphicon glyphicon-comment -
  • -
  • - - glyphicon glyphicon-magnet -
  • -
  • - - glyphicon glyphicon-chevron-up -
  • -
  • - - glyphicon glyphicon-chevron-down -
  • -
  • - - glyphicon glyphicon-retweet -
  • -
  • - - glyphicon glyphicon-shopping-cart -
  • -
  • - - glyphicon glyphicon-folder-close -
  • -
  • - - glyphicon glyphicon-folder-open -
  • -
  • - - glyphicon glyphicon-resize-vertical -
  • -
  • - - glyphicon glyphicon-resize-horizontal -
  • -
  • - - glyphicon glyphicon-hdd -
  • -
  • - - glyphicon glyphicon-bullhorn -
  • -
  • - - glyphicon glyphicon-bell -
  • -
  • - - glyphicon glyphicon-certificate -
  • -
  • - - glyphicon glyphicon-thumbs-up -
  • -
  • - - glyphicon glyphicon-thumbs-down -
  • -
  • - - glyphicon glyphicon-hand-right -
  • -
  • - - glyphicon glyphicon-hand-left -
  • -
  • - - glyphicon glyphicon-hand-up -
  • -
  • - - glyphicon glyphicon-hand-down -
  • -
  • - - glyphicon glyphicon-circle-arrow-right -
  • -
  • - - glyphicon glyphicon-circle-arrow-left -
  • -
  • - - glyphicon glyphicon-circle-arrow-up -
  • -
  • - - glyphicon glyphicon-circle-arrow-down -
  • -
  • - - glyphicon glyphicon-globe -
  • -
  • - - glyphicon glyphicon-wrench -
  • -
  • - - glyphicon glyphicon-tasks -
  • -
  • - - glyphicon glyphicon-filter -
  • -
  • - - glyphicon glyphicon-briefcase -
  • -
  • - - glyphicon glyphicon-fullscreen -
  • -
  • - - glyphicon glyphicon-dashboard -
  • -
  • - - glyphicon glyphicon-paperclip -
  • -
  • - - glyphicon glyphicon-heart-empty -
  • -
  • - - glyphicon glyphicon-link -
  • -
  • - - glyphicon glyphicon-phone -
  • -
  • - - glyphicon glyphicon-pushpin -
  • -
  • - - glyphicon glyphicon-usd -
  • -
  • - - glyphicon glyphicon-gbp -
  • -
  • - - glyphicon glyphicon-sort -
  • -
  • - - glyphicon glyphicon-sort-by-alphabet -
  • -
  • - - glyphicon glyphicon-sort-by-alphabet-alt -
  • -
  • - - glyphicon glyphicon-sort-by-order -
  • -
  • - - glyphicon glyphicon-sort-by-order-alt -
  • -
  • - - glyphicon glyphicon-sort-by-attributes -
  • -
  • - - glyphicon glyphicon-sort-by-attributes-alt -
  • -
  • - - glyphicon glyphicon-unchecked -
  • -
  • - - glyphicon glyphicon-expand -
  • -
  • - - glyphicon glyphicon-collapse-down -
  • -
  • - - glyphicon glyphicon-collapse-up -
  • -
  • - - glyphicon glyphicon-log-in -
  • -
  • - - glyphicon glyphicon-flash -
  • -
  • - - glyphicon glyphicon-log-out -
  • -
  • - - glyphicon glyphicon-new-window -
  • -
  • - - glyphicon glyphicon-record -
  • -
  • - - glyphicon glyphicon-save -
  • -
  • - - glyphicon glyphicon-open -
  • -
  • - - glyphicon glyphicon-saved -
  • -
  • - - glyphicon glyphicon-import -
  • -
  • - - glyphicon glyphicon-export -
  • -
  • - - glyphicon glyphicon-send -
  • -
  • - - glyphicon glyphicon-floppy-disk -
  • -
  • - - glyphicon glyphicon-floppy-saved -
  • -
  • - - glyphicon glyphicon-floppy-remove -
  • -
  • - - glyphicon glyphicon-floppy-save -
  • -
  • - - glyphicon glyphicon-floppy-open -
  • -
  • - - glyphicon glyphicon-credit-card -
  • -
  • - - glyphicon glyphicon-transfer -
  • -
  • - - glyphicon glyphicon-cutlery -
  • -
  • - - glyphicon glyphicon-header -
  • -
  • - - glyphicon glyphicon-compressed -
  • -
  • - - glyphicon glyphicon-earphone -
  • -
  • - - glyphicon glyphicon-phone-alt -
  • -
  • - - glyphicon glyphicon-tower -
  • -
  • - - glyphicon glyphicon-stats -
  • -
  • - - glyphicon glyphicon-sd-video -
  • -
  • - - glyphicon glyphicon-hd-video -
  • -
  • - - glyphicon glyphicon-subtitles -
  • -
  • - - glyphicon glyphicon-sound-stereo -
  • -
  • - - glyphicon glyphicon-sound-dolby -
  • -
  • - - glyphicon glyphicon-sound-5-1 -
  • -
  • - - glyphicon glyphicon-sound-6-1 -
  • -
  • - - glyphicon glyphicon-sound-7-1 -
  • -
  • - - glyphicon glyphicon-copyright-mark -
  • -
  • - - glyphicon glyphicon-registration-mark -
  • -
  • - - glyphicon glyphicon-cloud-download -
  • -
  • - - glyphicon glyphicon-cloud-upload -
  • -
  • - - glyphicon glyphicon-tree-conifer -
  • -
  • - - glyphicon glyphicon-tree-deciduous -
  • -
  • - - glyphicon glyphicon-cd -
  • -
  • - - glyphicon glyphicon-save-file -
  • -
  • - - glyphicon glyphicon-open-file -
  • -
  • - - glyphicon glyphicon-level-up -
  • -
  • - - glyphicon glyphicon-copy -
  • -
  • - - glyphicon glyphicon-paste -
  • -
  • - - glyphicon glyphicon-alert -
  • -
  • - - glyphicon glyphicon-equalizer -
  • -
  • - - glyphicon glyphicon-king -
  • -
  • - - glyphicon glyphicon-queen -
  • -
  • - - glyphicon glyphicon-pawn -
  • -
  • - - glyphicon glyphicon-bishop -
  • -
  • - - glyphicon glyphicon-knight -
  • -
  • - - glyphicon glyphicon-baby-formula -
  • -
  • - - glyphicon glyphicon-tent -
  • -
  • - - glyphicon glyphicon-blackboard -
  • -
  • - - glyphicon glyphicon-bed -
  • -
  • - - glyphicon glyphicon-apple -
  • -
  • - - glyphicon glyphicon-erase -
  • -
  • - - glyphicon glyphicon-hourglass -
  • -
  • - - glyphicon glyphicon-lamp -
  • -
  • - - glyphicon glyphicon-duplicate -
  • -
  • - - glyphicon glyphicon-piggy-bank -
  • -
  • - - glyphicon glyphicon-scissors -
  • -
  • - - glyphicon glyphicon-bitcoin -
  • -
  • - - glyphicon glyphicon-btc -
  • -
  • - - glyphicon glyphicon-xbt -
  • -
  • - - glyphicon glyphicon-yen -
  • -
  • - - glyphicon glyphicon-jpy -
  • -
  • - - glyphicon glyphicon-ruble -
  • -
  • - - glyphicon glyphicon-rub -
  • -
  • - - glyphicon glyphicon-scale -
  • -
  • - - glyphicon glyphicon-ice-lolly -
  • -
  • - - glyphicon glyphicon-ice-lolly-tasted -
  • -
  • - - glyphicon glyphicon-education -
  • -
  • - - glyphicon glyphicon-option-horizontal -
  • -
  • - - glyphicon glyphicon-option-vertical -
  • -
  • - - glyphicon glyphicon-menu-hamburger -
  • -
  • - - glyphicon glyphicon-modal-window -
  • -
  • - - glyphicon glyphicon-oil -
  • -
  • - - glyphicon glyphicon-grain -
  • -
  • - - glyphicon glyphicon-sunglasses -
  • -
  • - - glyphicon glyphicon-text-size -
  • -
  • - - glyphicon glyphicon-text-color -
  • -
  • - - glyphicon glyphicon-text-background -
  • -
  • - - glyphicon glyphicon-object-align-top -
  • -
  • - - glyphicon glyphicon-object-align-bottom -
  • -
  • - - glyphicon glyphicon-object-align-horizontal -
  • -
  • - - glyphicon glyphicon-object-align-left -
  • -
  • - - glyphicon glyphicon-object-align-vertical -
  • -
  • - - glyphicon glyphicon-object-align-right -
  • -
  • - - glyphicon glyphicon-triangle-right -
  • -
  • - - glyphicon glyphicon-triangle-left -
  • -
  • - - glyphicon glyphicon-triangle-bottom -
  • -
  • - - glyphicon glyphicon-triangle-top -
  • -
  • - - glyphicon glyphicon-console -
  • -
  • - - glyphicon glyphicon-superscript -
  • -
  • - - glyphicon glyphicon-subscript -
  • -
  • - - glyphicon glyphicon-menu-left -
  • -
  • - - glyphicon glyphicon-menu-right -
  • -
  • - - glyphicon glyphicon-menu-down -
  • -
  • - - glyphicon glyphicon-menu-up -
  • -
-
-
- -
- - diff --git a/application/admin/view/plugs/icon.html b/application/admin/view/plugs/icon.html new file mode 100644 index 000000000..b743fb1bf --- /dev/null +++ b/application/admin/view/plugs/icon.html @@ -0,0 +1,3660 @@ + + + + + {block name="title"}{$title|default=''}{if !empty($title)} · {/if}{:sysconf('site_name')}{/block} + + + + + + + + + + + + + +
    +
  • + +
    layui-icon-rate-half
    +
  • +
  • + +
    layui-icon-rate
    +
  • +
  • + +
    layui-icon-rate-solid
    +
  • +
  • + +
    layui-icon-cellphone
    +
  • +
  • + +
    layui-icon-vercode
    +
  • +
  • + +
    layui-icon-login-wechat
    +
  • +
  • + +
    layui-icon-login-qq
    +
  • +
  • + +
    layui-icon-login-weibo
    +
  • +
  • + +
    layui-icon-password
    +
  • +
  • + +
    layui-icon-username
    +
  • +
  • + +
    layui-icon-refresh-3
    +
  • +
  • + +
    layui-icon-auz
    +
  • +
  • + +
    layui-icon-spread-left
    +
  • +
  • + +
    layui-icon-shrink-right
    +
  • +
  • + +
    layui-icon-snowflake
    +
  • +
  • + +
    layui-icon-tips
    +
  • +
  • + +
    layui-icon-note
    +
  • +
  • + +
    layui-icon-home
    +
  • +
  • + +
    layui-icon-senior
    +
  • +
  • + +
    layui-icon-refresh
    +
  • +
  • + +
    layui-icon-refresh-1
    +
  • +
  • + +
    layui-icon-flag
    +
  • +
  • + +
    layui-icon-theme
    +
  • +
  • + +
    layui-icon-notice
    +
  • +
  • + +
    layui-icon-website
    +
  • +
  • + +
    layui-icon-console
    +
  • +
  • + +
    layui-icon-face-surprised
    +
  • +
  • + +
    layui-icon-set
    +
  • +
  • + +
    layui-icon-template-1
    +
  • +
  • + +
    layui-icon-app
    +
  • +
  • + +
    layui-icon-template
    +
  • +
  • + +
    layui-icon-praise
    +
  • +
  • + +
    layui-icon-tread
    +
  • +
  • + +
    layui-icon-male
    +
  • +
  • + +
    layui-icon-female
    +
  • +
  • + +
    layui-icon-camera
    +
  • +
  • + +
    layui-icon-camera-fill
    +
  • +
  • + +
    layui-icon-more
    +
  • +
  • + +
    layui-icon-more-vertical
    +
  • +
  • + +
    layui-icon-rmb
    +
  • +
  • + +
    layui-icon-dollar
    +
  • +
  • + +
    layui-icon-diamond
    +
  • +
  • + +
    layui-icon-fire
    +
  • +
  • + +
    layui-icon-return
    +
  • +
  • + +
    layui-icon-location
    +
  • +
  • + +
    layui-icon-read
    +
  • +
  • + +
    layui-icon-survey
    +
  • +
  • + +
    layui-icon-face-smile
    +
  • +
  • + +
    layui-icon-face-cry
    +
  • +
  • + +
    layui-icon-cart-simple
    +
  • +
  • + +
    layui-icon-cart
    +
  • +
  • + +
    layui-icon-next
    +
  • +
  • + +
    layui-icon-prev
    +
  • +
  • + +
    layui-icon-upload-drag
    +
  • +
  • + +
    layui-icon-upload
    +
  • +
  • + +
    layui-icon-download-circle
    +
  • +
  • + +
    layui-icon-component
    +
  • +
  • + +
    layui-icon-file-b
    +
  • +
  • + +
    layui-icon-user
    +
  • +
  • + +
    layui-icon-find-fill
    +
  • +
  • + +
    layui-icon-loading
    +
  • +
  • + +
    layui-icon-loading-1
    +
  • +
  • + +
    layui-icon-add-1
    +
  • +
  • + +
    layui-icon-play
    +
  • +
  • + +
    layui-icon-pause
    +
  • +
  • + +
    layui-icon-headset
    +
  • +
  • + +
    layui-icon-video
    +
  • +
  • + +
    layui-icon-voice
    +
  • +
  • + +
    layui-icon-speaker
    +
  • +
  • + +
    layui-icon-fonts-del
    +
  • +
  • + +
    layui-icon-fonts-code
    +
  • +
  • + +
    layui-icon-fonts-html
    +
  • +
  • + +
    layui-icon-fonts-strong
    +
  • +
  • + +
    layui-icon-unlink
    +
  • +
  • + +
    layui-icon-picture
    +
  • +
  • + +
    layui-icon-link
    +
  • +
  • + +
    layui-icon-face-smile-b
    +
  • +
  • + +
    layui-icon-align-left
    +
  • +
  • + +
    layui-icon-align-right
    +
  • +
  • + +
    layui-icon-align-center
    +
  • +
  • + +
    layui-icon-fonts-u
    +
  • +
  • + +
    layui-icon-fonts-i
    +
  • +
  • + +
    layui-icon-tabs
    +
  • +
  • + +
    layui-icon-radio
    +
  • +
  • + +
    layui-icon-circle
    +
  • +
  • + +
    layui-icon-edit
    +
  • +
  • + +
    layui-icon-share
    +
  • +
  • + +
    layui-icon-delete
    +
  • +
  • + +
    layui-icon-form
    +
  • +
  • + +
    layui-icon-cellphone-fine
    +
  • +
  • + +
    layui-icon-dialogue
    +
  • +
  • + +
    layui-icon-fonts-clear
    +
  • +
  • + +
    layui-icon-layer
    +
  • +
  • + +
    layui-icon-date
    +
  • +
  • + +
    layui-icon-water
    +
  • +
  • + +
    layui-icon-code-circle
    +
  • +
  • + +
    layui-icon-carousel
    +
  • +
  • + +
    layui-icon-prev-circle
    +
  • +
  • + +
    layui-icon-layouts
    +
  • +
  • + +
    layui-icon-util
    +
  • +
  • + +
    layui-icon-templeate-1
    +
  • +
  • + +
    layui-icon-upload-circle
    +
  • +
  • + +
    layui-icon-tree
    +
  • +
  • + +
    layui-icon-table
    +
  • +
  • + +
    layui-icon-chart
    +
  • +
  • + +
    layui-icon-chart-screen
    +
  • +
  • + +
    layui-icon-engine
    +
  • +
  • + +
    layui-icon-triangle-d
    +
  • +
  • + +
    layui-icon-triangle-r
    +
  • +
  • + +
    layui-icon-file
    +
  • +
  • + +
    layui-icon-set-sm
    +
  • +
  • + +
    layui-icon-add-circle
    +
  • +
  • + +
    layui-icon-404
    +
  • +
  • + +
    layui-icon-about
    +
  • +
  • + +
    layui-icon-up
    +
  • +
  • + +
    layui-icon-down
    +
  • +
  • + +
    layui-icon-left
    +
  • +
  • + +
    layui-icon-right
    +
  • +
  • + +
    layui-icon-circle-dot
    +
  • +
  • + +
    layui-icon-search
    +
  • +
  • + +
    layui-icon-set-fill
    +
  • +
  • + +
    layui-icon-group
    +
  • +
  • + +
    layui-icon-friends
    +
  • +
  • + +
    layui-icon-reply-fill
    +
  • +
  • + +
    layui-icon-menu-fill
    +
  • +
  • + +
    layui-icon-log
    +
  • +
  • + +
    layui-icon-picture-fine
    +
  • +
  • + +
    layui-icon-face-smile-fine
    +
  • +
  • + +
    layui-icon-list
    +
  • +
  • + +
    layui-icon-release
    +
  • +
  • + +
    layui-icon-ok
    +
  • +
  • + +
    layui-icon-help
    +
  • +
  • + +
    layui-icon-chat
    +
  • +
  • + +
    layui-icon-top
    +
  • +
  • + +
    layui-icon-star
    +
  • +
  • + +
    layui-icon-star-fill
    +
  • +
  • + +
    layui-icon-close-fill
    +
  • +
  • + +
    layui-icon-close
    +
  • +
  • + +
    layui-icon-ok-circle
    +
  • +
  • + +
    layui-icon-add-circle-fine
    +
  • +
+
    +
  • + +
    fa-address-book
    +
  • +
  • + +
    fa-address-book-o
    +
  • +
  • + +
    fa-address-card
    +
  • +
  • + +
    fa-address-card-o
    +
  • +
  • + +
    fa-adjust
    +
  • +
  • + +
    fa-american-sign-language-interpreting
    +
  • +
  • + +
    fa-anchor
    +
  • +
  • + +
    fa-archive
    +
  • +
  • + +
    fa-area-chart
    +
  • +
  • + +
    fa-arrows
    +
  • +
  • + +
    fa-arrows-h
    +
  • +
  • + +
    fa-arrows-v
    +
  • +
  • + +
    fa-asl-interpreting
    +
  • +
  • + +
    fa-assistive-listening-systems
    +
  • +
  • + +
    fa-asterisk
    +
  • +
  • + +
    fa-at
    +
  • +
  • + +
    fa-audio-description
    +
  • +
  • + +
    fa-automobile
    +
  • +
  • + +
    fa-balance-scale
    +
  • +
  • + +
    fa-ban
    +
  • +
  • + +
    fa-bank
    +
  • +
  • + +
    fa-bar-chart
    +
  • +
  • + +
    fa-bar-chart-o
    +
  • +
  • + +
    fa-barcode
    +
  • +
  • + +
    fa-bars
    +
  • +
  • + +
    fa-bath
    +
  • +
  • + +
    fa-bathtub
    +
  • +
  • + +
    fa-battery
    +
  • +
  • + +
    fa-battery-0
    +
  • +
  • + +
    fa-battery-1
    +
  • +
  • + +
    fa-battery-2
    +
  • +
  • + +
    fa-battery-3
    +
  • +
  • + +
    fa-battery-4
    +
  • +
  • + +
    fa-battery-empty
    +
  • +
  • + +
    fa-battery-full
    +
  • +
  • + +
    fa-battery-half
    +
  • +
  • + +
    fa-battery-quarter
    +
  • +
  • + +
    fa-battery-three-quarters
    +
  • +
  • + +
    fa-bed
    +
  • +
  • + +
    fa-beer
    +
  • +
  • + +
    fa-bell
    +
  • +
  • + +
    fa-bell-o
    +
  • +
  • + +
    fa-bell-slash
    +
  • +
  • + +
    fa-bell-slash-o
    +
  • +
  • + +
    fa-bicycle
    +
  • +
  • + +
    fa-binoculars
    +
  • +
  • + +
    fa-birthday-cake
    +
  • +
  • + +
    fa-blind
    +
  • +
  • + +
    fa-bluetooth
    +
  • +
  • + +
    fa-bluetooth-b
    +
  • +
  • + +
    fa-bolt
    +
  • +
  • + +
    fa-bomb
    +
  • +
  • + +
    fa-book
    +
  • +
  • + +
    fa-bookmark
    +
  • +
  • + +
    fa-bookmark-o
    +
  • +
  • + +
    fa-braille
    +
  • +
  • + +
    fa-briefcase
    +
  • +
  • + +
    fa-bug
    +
  • +
  • + +
    fa-building
    +
  • +
  • + +
    fa-building-o
    +
  • +
  • + +
    fa-bullhorn
    +
  • +
  • + +
    fa-bullseye
    +
  • +
  • + +
    fa-bus
    +
  • +
  • + +
    fa-cab
    +
  • +
  • + +
    fa-calculator
    +
  • +
  • + +
    fa-calendar
    +
  • +
  • + +
    fa-calendar-check-o
    +
  • +
  • + +
    fa-calendar-minus-o
    +
  • +
  • + +
    fa-calendar-o
    +
  • +
  • + +
    fa-calendar-plus-o
    +
  • +
  • + +
    fa-calendar-times-o
    +
  • +
  • + +
    fa-camera
    +
  • +
  • + +
    fa-camera-retro
    +
  • +
  • + +
    fa-car
    +
  • +
  • + +
    fa-caret-square-o-down
    +
  • +
  • + +
    fa-caret-square-o-left
    +
  • +
  • + +
    fa-caret-square-o-right
    +
  • +
  • + +
    fa-caret-square-o-up
    +
  • +
  • + +
    fa-cart-arrow-down
    +
  • +
  • + +
    fa-cart-plus
    +
  • +
  • + +
    fa-cc
    +
  • +
  • + +
    fa-certificate
    +
  • +
  • + +
    fa-check
    +
  • +
  • + +
    fa-check-circle
    +
  • +
  • + +
    fa-check-circle-o
    +
  • +
  • + +
    fa-check-square
    +
  • +
  • + +
    fa-check-square-o
    +
  • +
  • + +
    fa-child
    +
  • +
  • + +
    fa-circle
    +
  • +
  • + +
    fa-circle-o
    +
  • +
  • + +
    fa-circle-o-notch
    +
  • +
  • + +
    fa-circle-thin
    +
  • +
  • + +
    fa-clock-o
    +
  • +
  • + +
    fa-clone
    +
  • +
  • + +
    fa-close
    +
  • +
  • + +
    fa-cloud
    +
  • +
  • + +
    fa-cloud-download
    +
  • +
  • + +
    fa-cloud-upload
    +
  • +
  • + +
    fa-code
    +
  • +
  • + +
    fa-code-fork
    +
  • +
  • + +
    fa-coffee
    +
  • +
  • + +
    fa-cog
    +
  • +
  • + +
    fa-cogs
    +
  • +
  • + +
    fa-comment
    +
  • +
  • + +
    fa-comment-o
    +
  • +
  • + +
    fa-commenting
    +
  • +
  • + +
    fa-commenting-o
    +
  • +
  • + +
    fa-comments
    +
  • +
  • + +
    fa-comments-o
    +
  • +
  • + +
    fa-compass
    +
  • +
  • + +
    fa-copyright
    +
  • +
  • + +
    fa-creative-commons
    +
  • +
  • + +
    fa-credit-card
    +
  • +
  • +
    fa-credit-card-alt
    +
  • +
  • +
    fa-crop
    +
  • +
  • +
    fa-crosshairs
    +
  • +
  • +
    fa-cube
    +
  • +
  • +
    fa-cubes
    +
  • +
  • +
    fa-cutlery
    +
  • +
  • +
    fa-dashboard
    +
  • +
  • +
    fa-database
    +
  • +
  • +
    fa-deaf
    +
  • +
  • +
    fa-deafness
    +
  • +
  • +
    fa-desktop
    +
  • +
  • +
    fa-diamond
    +
  • +
  • +
    fa-dot-circle-o
    +
  • +
  • +
    fa-download
    +
  • +
  • +
    fa-drivers-license
    +
  • +
  • +
    fa-drivers-license-o
    +
  • +
  • +
    fa-edit
    +
  • +
  • +
    fa-ellipsis-h
    +
  • +
  • +
    fa-ellipsis-v
    +
  • +
  • +
    fa-envelope
    +
  • +
  • +
    fa-envelope-o
    +
  • +
  • +
    fa-envelope-open
    +
  • +
  • +
    fa-envelope-open-o
    +
  • +
  • +
    fa-envelope-square
    +
  • +
  • +
    fa-eraser
    +
  • +
  • +
    fa-exchange
    +
  • +
  • +
    fa-exclamation
    +
  • +
  • +
    fa-exclamation-circle
    +
  • +
  • +
    fa-exclamation-triangle
    +
  • +
  • +
    fa-external-link
    +
  • +
  • +
    fa-external-link-square
    +
  • +
  • +
    fa-eye
    +
  • +
  • +
    fa-eye-slash
    +
  • +
  • +
    fa-eyedropper
    +
  • +
  • +
    fa-fax
    +
  • +
  • +
    fa-feed
    +
  • +
  • +
    fa-female
    +
  • +
  • +
    fa-fighter-jet
    +
  • +
  • +
    fa-file-archive-o
    +
  • +
  • +
    fa-file-audio-o
    +
  • +
  • +
    fa-file-code-o
    +
  • +
  • +
    fa-file-excel-o
    +
  • +
  • +
    fa-file-image-o
    +
  • +
  • +
    fa-file-movie-o
    +
  • +
  • +
    fa-file-pdf-o
    +
  • +
  • +
    fa-file-photo-o
    +
  • +
  • +
    fa-file-picture-o
    +
  • +
  • +
    fa-file-powerpoint-o
    +
  • +
  • +
    fa-file-sound-o
    +
  • +
  • +
    fa-file-video-o
    +
  • +
  • +
    fa-file-word-o
    +
  • +
  • +
    fa-file-zip-o
    +
  • +
  • +
    fa-film
    +
  • +
  • +
    fa-filter
    +
  • +
  • +
    fa-fire
    +
  • +
  • +
    fa-fire-extinguisher
    +
  • +
  • +
    fa-flag
    +
  • +
  • +
    fa-flag-checkered
    +
  • +
  • +
    fa-flag-o
    +
  • +
  • +
    fa-flash
    +
  • +
  • +
    fa-flask
    +
  • +
  • +
    fa-folder
    +
  • +
  • +
    fa-folder-o
    +
  • +
  • +
    fa-folder-open
    +
  • +
  • +
    fa-folder-open-o
    +
  • +
  • +
    fa-frown-o
    +
  • +
  • +
    fa-futbol-o
    +
  • +
  • +
    fa-gamepad
    +
  • +
  • +
    fa-gavel
    +
  • +
  • +
    fa-gear
    +
  • +
  • +
    fa-gears
    +
  • +
  • +
    fa-gift
    +
  • +
  • +
    fa-glass
    +
  • +
  • +
    fa-globe
    +
  • +
  • +
    fa-graduation-cap
    +
  • +
  • +
    fa-group
    +
  • +
  • +
    fa-hand-grab-o
    +
  • +
  • +
    fa-hand-lizard-o
    +
  • +
  • +
    fa-hand-paper-o
    +
  • +
  • +
    fa-hand-peace-o
    +
  • +
  • +
    fa-hand-pointer-o
    +
  • +
  • +
    fa-hand-rock-o
    +
  • +
  • +
    fa-hand-scissors-o
    +
  • +
  • +
    fa-hand-spock-o
    +
  • +
  • +
    fa-hand-stop-o
    +
  • +
  • +
    fa-handshake-o
    +
  • +
  • +
    fa-hard-of-hearing
    +
  • +
  • +
    fa-hashtag
    +
  • +
  • +
    fa-hdd-o
    +
  • +
  • +
    fa-headphones
    +
  • +
  • +
    fa-heart
    +
  • +
  • +
    fa-heart-o
    +
  • +
  • +
    fa-heartbeat
    +
  • +
  • +
    fa-history
    +
  • +
  • +
    fa-home
    +
  • +
  • +
    fa-hotel
    +
  • +
  • +
    fa-hourglass
    +
  • +
  • +
    fa-hourglass-1
    +
  • +
  • +
    fa-hourglass-2
    +
  • +
  • +
    fa-hourglass-3
    +
  • +
  • +
    fa-hourglass-end
    +
  • +
  • +
    fa-hourglass-half
    +
  • +
  • +
    fa-hourglass-o
    +
  • +
  • +
    fa-hourglass-start
    +
  • +
  • +
    fa-i-cursor
    +
  • +
  • +
    fa-id-badge
    +
  • +
  • +
    fa-id-card
    +
  • +
  • +
    fa-id-card-o
    +
  • +
  • +
    fa-image
    +
  • +
  • +
    fa-inbox
    +
  • +
  • +
    fa-industry
    +
  • +
  • +
    fa-info
    +
  • +
  • +
    fa-info-circle
    +
  • +
  • +
    fa-institution
    +
  • +
  • +
    fa-key
    +
  • +
  • +
    fa-keyboard-o
    +
  • +
  • +
    fa-language
    +
  • +
  • +
    fa-laptop
    +
  • +
  • +
    fa-leaf
    +
  • +
  • +
    fa-legal
    +
  • +
  • +
    fa-lemon-o
    +
  • +
  • +
    fa-level-down
    +
  • +
  • +
    fa-level-up
    +
  • +
  • +
    fa-life-bouy
    +
  • +
  • +
    fa-life-buoy
    +
  • +
  • +
    fa-life-ring
    +
  • +
  • +
    fa-life-saver
    +
  • +
  • +
    fa-lightbulb-o
    +
  • +
  • +
    fa-line-chart
    +
  • +
  • +
    fa-location-arrow
    +
  • +
  • +
    fa-lock
    +
  • +
  • +
    fa-low-vision
    +
  • +
  • +
    fa-magic
    +
  • +
  • +
    fa-magnet
    +
  • +
  • +
    fa-mail-forward
    +
  • +
  • +
    fa-mail-reply
    +
  • +
  • +
    fa-mail-reply-all
    +
  • +
  • +
    fa-male
    +
  • +
  • +
    fa-map
    +
  • +
  • +
    fa-map-marker
    +
  • +
  • +
    fa-map-o
    +
  • +
  • +
    fa-map-pin
    +
  • +
  • +
    fa-map-signs
    +
  • +
  • +
    fa-meh-o
    +
  • +
  • +
    fa-microchip
    +
  • +
  • +
    fa-microphone
    +
  • +
  • +
    fa-microphone-slash
    +
  • +
  • +
    fa-minus
    +
  • +
  • +
    fa-minus-circle
    +
  • +
  • +
    fa-minus-square
    +
  • +
  • +
    fa-minus-square-o
    +
  • +
  • +
    fa-mobile
    +
  • +
  • +
    fa-mobile-phone
    +
  • +
  • +
    fa-money
    +
  • +
  • +
    fa-moon-o
    +
  • +
  • +
    fa-mortar-board
    +
  • +
  • +
    fa-motorcycle
    +
  • +
  • +
    fa-mouse-pointer
    +
  • +
  • +
    fa-music
    +
  • +
  • +
    fa-navicon
    +
  • +
  • +
    fa-newspaper-o
    +
  • +
  • +
    fa-object-group
    +
  • +
  • +
    fa-object-ungroup
    +
  • +
  • +
    fa-paint-brush
    +
  • +
  • +
    fa-paper-plane
    +
  • +
  • +
    fa-paper-plane-o
    +
  • +
  • +
    fa-paw
    +
  • +
  • +
    fa-pencil
    +
  • +
  • +
    fa-pencil-square
    +
  • +
  • +
    fa-pencil-square-o
    +
  • +
  • +
    fa-percent
    +
  • +
  • +
    fa-phone
    +
  • +
  • +
    fa-phone-square
    +
  • +
  • +
    fa-photo
    +
  • +
  • +
    fa-picture-o
    +
  • +
  • +
    fa-pie-chart
    +
  • +
  • +
    fa-plane
    +
  • +
  • +
    fa-plug
    +
  • +
  • +
    fa-plus
    +
  • +
  • +
    fa-plus-circle
    +
  • +
  • +
    fa-plus-square
    +
  • +
  • +
    fa-plus-square-o
    +
  • +
  • +
    fa-podcast
    +
  • +
  • +
    fa-power-off
    +
  • +
  • +
    fa-print
    +
  • +
  • +
    fa-puzzle-piece
    +
  • +
  • +
    fa-qrcode
    +
  • +
  • +
    fa-question
    +
  • +
  • +
    fa-question-circle
    +
  • +
  • +
    fa-question-circle-o
    +
  • +
  • +
    fa-quote-left
    +
  • +
  • +
    fa-quote-right
    +
  • +
  • +
    fa-random
    +
  • +
  • +
    fa-recycle
    +
  • +
  • +
    fa-refresh
    +
  • +
  • +
    fa-registered
    +
  • +
  • +
    fa-remove
    +
  • +
  • +
    fa-reorder
    +
  • +
  • +
    fa-reply
    +
  • +
  • +
    fa-reply-all
    +
  • +
  • +
    fa-retweet
    +
  • +
  • +
    fa-road
    +
  • +
  • +
    fa-rocket
    +
  • +
  • +
    fa-rss
    +
  • +
  • +
    fa-rss-square
    +
  • +
  • +
    fa-s15
    +
  • +
  • +
    fa-search
    +
  • +
  • +
    fa-search-minus
    +
  • +
  • +
    fa-search-plus
    +
  • +
  • +
    fa-send
    +
  • +
  • +
    fa-send-o
    +
  • +
  • +
    fa-server
    +
  • +
  • +
    fa-share
    +
  • +
  • +
    fa-share-alt
    +
  • +
  • +
    fa-share-alt-square
    +
  • +
  • +
    fa-share-square
    +
  • +
  • +
    fa-share-square-o
    +
  • +
  • +
    fa-shield
    +
  • +
  • +
    fa-ship
    +
  • +
  • +
    fa-shopping-bag
    +
  • +
  • +
    fa-shopping-basket
    +
  • +
  • +
    fa-shopping-cart
    +
  • +
  • +
    fa-shower
    +
  • +
  • +
    fa-sign-in
    +
  • +
  • +
    fa-sign-language
    +
  • +
  • +
    fa-sign-out
    +
  • +
  • +
    fa-signal
    +
  • +
  • +
    fa-signing
    +
  • +
  • +
    fa-sitemap
    +
  • +
  • +
    fa-sliders
    +
  • +
  • +
    fa-smile-o
    +
  • +
  • +
    fa-snowflake-o
    +
  • +
  • +
    fa-soccer-ball-o
    +
  • +
  • +
    fa-sort
    +
  • +
  • +
    fa-sort-alpha-asc
    +
  • +
  • +
    fa-sort-alpha-desc
    +
  • +
  • +
    fa-sort-amount-asc
    +
  • +
  • +
    fa-sort-amount-desc
    +
  • +
  • +
    fa-sort-asc
    +
  • +
  • +
    fa-sort-desc
    +
  • +
  • +
    fa-sort-down
    +
  • +
  • +
    fa-sort-numeric-asc
    +
  • +
  • +
    fa-sort-numeric-desc
    +
  • +
  • +
    fa-sort-up
    +
  • +
  • +
    fa-space-shuttle
    +
  • +
  • +
    fa-spinner
    +
  • +
  • +
    fa-spoon
    +
  • +
  • +
    fa-square
    +
  • +
  • +
    fa-square-o
    +
  • +
  • +
    fa-star
    +
  • +
  • +
    fa-star-half
    +
  • +
  • +
    fa-star-half-empty
    +
  • +
  • +
    fa-star-half-full
    +
  • +
  • +
    fa-star-half-o
    +
  • +
  • +
    fa-star-o
    +
  • +
  • +
    fa-sticky-note
    +
  • +
  • +
    fa-sticky-note-o
    +
  • +
  • +
    fa-street-view
    +
  • +
  • +
    fa-suitcase
    +
  • +
  • +
    fa-sun-o
    +
  • +
  • +
    fa-support
    +
  • +
  • +
    fa-tablet
    +
  • +
  • +
    fa-tachometer
    +
  • +
  • +
    fa-tag
    +
  • +
  • +
    fa-tags
    +
  • +
  • +
    fa-tasks
    +
  • +
  • +
    fa-taxi
    +
  • +
  • +
    fa-television
    +
  • +
  • +
    fa-terminal
    +
  • +
  • +
    fa-thermometer
    +
  • +
  • +
    fa-thermometer-0
    +
  • +
  • +
    fa-thermometer-1
    +
  • +
  • +
    fa-thermometer-2
    +
  • +
  • +
    fa-thermometer-3
    +
  • +
  • +
    fa-thermometer-4
    +
  • +
  • +
    fa-thermometer-empty
    +
  • +
  • +
    fa-thermometer-full
    +
  • +
  • +
    fa-thermometer-half
    +
  • +
  • +
    fa-thermometer-quarter
    +
  • +
  • +
    fa-thermometer-three-quarters
    +
  • +
  • +
    fa-thumb-tack
    +
  • +
  • +
    fa-thumbs-down
    +
  • +
  • +
    fa-thumbs-o-down
    +
  • +
  • +
    fa-thumbs-o-up
    +
  • +
  • +
    fa-thumbs-up
    +
  • +
  • +
    fa-ticket
    +
  • +
  • +
    fa-times
    +
  • +
  • +
    fa-times-circle
    +
  • +
  • +
    fa-times-circle-o
    +
  • +
  • +
    fa-times-rectangle
    +
  • +
  • +
    fa-times-rectangle-o
    +
  • +
  • +
    fa-tint
    +
  • +
  • +
    fa-toggle-down
    +
  • +
  • +
    fa-toggle-left
    +
  • +
  • +
    fa-toggle-off
    +
  • +
  • +
    fa-toggle-on
    +
  • +
  • +
    fa-toggle-right
    +
  • +
  • +
    fa-toggle-up
    +
  • +
  • +
    fa-trademark
    +
  • +
  • +
    fa-trash
    +
  • +
  • +
    fa-trash-o
    +
  • +
  • +
    fa-tree
    +
  • +
  • +
    fa-trophy
    +
  • +
  • +
    fa-truck
    +
  • +
  • +
    fa-tty
    +
  • +
  • +
    fa-tv
    +
  • +
  • +
    fa-umbrella
    +
  • +
  • +
    fa-universal-access
    +
  • +
  • +
    fa-university
    +
  • +
  • +
    fa-unlock
    +
  • +
  • +
    fa-unlock-alt
    +
  • +
  • +
    fa-unsorted
    +
  • +
  • +
    fa-upload
    +
  • +
  • +
    fa-user
    +
  • +
  • +
    fa-user-circle
    +
  • +
  • +
    fa-user-circle-o
    +
  • +
  • +
    fa-user-o
    +
  • +
  • +
    fa-user-plus
    +
  • +
  • +
    fa-user-secret
    +
  • +
  • +
    fa-user-times
    +
  • +
  • +
    fa-users
    +
  • +
  • +
    fa-vcard
    +
  • +
  • +
    fa-vcard-o
    +
  • +
  • +
    fa-video-camera
    +
  • +
  • +
    fa-volume-control-phone
    +
  • +
  • +
    fa-volume-down
    +
  • +
  • +
    fa-volume-off
    +
  • +
  • +
    fa-volume-up
    +
  • +
  • +
    fa-warning
    +
  • +
  • +
    fa-wheelchair
    +
  • +
  • +
    fa-wheelchair-alt
    +
  • +
  • +
    fa-wifi
    +
  • +
  • +
    fa-window-close
    +
  • +
  • +
    fa-window-close-o
    +
  • +
  • +
    fa-window-maximize
    +
  • +
  • +
    fa-window-minimize
    +
  • +
  • +
    fa-window-restore
    +
  • +
  • +
    fa-wrench
    +
  • +
  • +
    fa-american-sign-language-interpreting
    +
  • +
  • +
    fa-asl-interpreting
    +
  • +
  • +
    fa-assistive-listening-systems
    +
  • +
  • +
    fa-audio-description
    +
  • +
  • +
    fa-blind
    +
  • +
  • +
    fa-braille
    +
  • +
  • +
    fa-cc
    +
  • +
  • +
    fa-deaf
    +
  • +
  • +
    fa-deafness
    +
  • +
  • +
    fa-hard-of-hearing
    +
  • +
  • +
    fa-low-vision
    +
  • +
  • +
    fa-question-circle-o
    +
  • +
  • +
    fa-sign-language
    +
  • +
  • +
    fa-signing
    +
  • +
  • +
    fa-tty
    +
  • +
  • +
    fa-universal-access
    +
  • +
  • +
    fa-volume-control-phone
    +
  • +
  • +
    fa-wheelchair
    +
  • +
  • +
    fa-wheelchair-alt
    +
  • +
  • +
    fa-hand-grab-o
    +
  • +
  • +
    fa-hand-lizard-o
    +
  • +
  • +
    fa-hand-o-down
    +
  • +
  • +
    fa-hand-o-left
    +
  • +
  • +
    fa-hand-o-right
    +
  • +
  • +
    fa-hand-o-up
    +
  • +
  • +
    fa-hand-paper-o
    +
  • +
  • +
    fa-hand-peace-o
    +
  • +
  • +
    fa-hand-pointer-o
    +
  • +
  • +
    fa-hand-rock-o
    +
  • +
  • +
    fa-hand-scissors-o
    +
  • +
  • +
    fa-hand-spock-o
    +
  • +
  • +
    fa-hand-stop-o
    +
  • +
  • +
    fa-thumbs-down
    +
  • +
  • +
    fa-thumbs-o-down
    +
  • +
  • +
    fa-thumbs-o-up
    +
  • +
  • +
    fa-thumbs-up
    +
  • +
  • +
    fa-ambulance
    +
  • +
  • +
    fa-automobile
    +
  • +
  • +
    fa-bicycle
    +
  • +
  • +
    fa-bus
    +
  • +
  • +
    fa-cab
    +
  • +
  • +
    fa-car
    +
  • +
  • +
    fa-fighter-jet
    +
  • +
  • +
    fa-motorcycle
    +
  • +
  • +
    fa-plane
    +
  • +
  • +
    fa-rocket
    +
  • +
  • +
    fa-ship
    +
  • +
  • +
    fa-space-shuttle
    +
  • +
  • +
    fa-subway
    +
  • +
  • +
    fa-taxi
    +
  • +
  • +
    fa-train
    +
  • +
  • +
    fa-truck
    +
  • +
  • +
    fa-wheelchair
    +
  • +
  • +
    fa-wheelchair-alt
    +
  • +
  • +
    fa-genderless
    +
  • +
  • +
    fa-intersex
    +
  • +
  • +
    fa-mars
    +
  • +
  • +
    fa-mars-double
    +
  • +
  • +
    fa-mars-stroke
    +
  • +
  • +
    fa-mars-stroke-h
    +
  • +
  • +
    fa-mars-stroke-v
    +
  • +
  • +
    fa-mercury
    +
  • +
  • +
    fa-neuter
    +
  • +
  • +
    fa-transgender
    +
  • +
  • +
    fa-transgender-alt
    +
  • +
  • +
    fa-venus
    +
  • +
  • +
    fa-venus-double
    +
  • +
  • +
    fa-venus-mars
    +
  • +
  • +
    fa-file
    +
  • +
  • +
    fa-file-archive-o
    +
  • +
  • +
    fa-file-audio-o
    +
  • +
  • +
    fa-file-code-o
    +
  • +
  • +
    fa-file-excel-o
    +
  • +
  • +
    fa-file-image-o
    +
  • +
  • +
    fa-file-movie-o
    +
  • +
  • +
    fa-file-o
    +
  • +
  • +
    fa-file-pdf-o
    +
  • +
  • +
    fa-file-photo-o
    +
  • +
  • +
    fa-file-picture-o
    +
  • +
  • +
    fa-file-powerpoint-o
    +
  • +
  • +
    fa-file-sound-o
    +
  • +
  • +
    fa-file-text
    +
  • +
  • +
    fa-file-text-o
    +
  • +
  • +
    fa-file-video-o
    +
  • +
  • +
    fa-file-word-o
    +
  • +
  • +
    fa-file-zip-o
    +
  • +
  • +
    fa-circle-o-notch
    +
  • +
  • +
    fa-cog
    +
  • +
  • +
    fa-gear
    +
  • +
  • +
    fa-refresh
    +
  • +
  • +
    fa-spinner
    +
  • +
  • +
    fa-check-square
    +
  • +
  • +
    fa-check-square-o
    +
  • +
  • +
    fa-circle
    +
  • +
  • +
    fa-circle-o
    +
  • +
  • +
    fa-dot-circle-o
    +
  • +
  • +
    fa-minus-square
    +
  • +
  • +
    fa-minus-square-o
    +
  • +
  • +
    fa-plus-square
    +
  • +
  • +
    fa-plus-square-o
    +
  • +
  • +
    fa-square
    +
  • +
  • +
    fa-square-o
    +
  • +
  • +
    fa-cc-amex
    +
  • +
  • +
    fa-cc-diners-club
    +
  • +
  • +
    fa-cc-discover
    +
  • +
  • +
    fa-cc-jcb
    +
  • +
  • +
    fa-cc-mastercard
    +
  • +
  • +
    fa-cc-paypal
    +
  • +
  • +
    fa-cc-stripe
    +
  • +
  • +
    fa-cc-visa
    +
  • +
  • +
    fa-credit-card
    +
  • +
  • +
    fa-credit-card-alt
    +
  • +
  • +
    fa-google-wallet
    +
  • +
  • +
    fa-paypal
    +
  • +
  • +
    fa-area-chart
    +
  • +
  • +
    fa-bar-chart
    +
  • +
  • +
    fa-bar-chart-o
    +
  • +
  • +
    fa-line-chart
    +
  • +
  • +
    fa-pie-chart
    +
  • +
  • +
    fa-bitcoin
    +
  • +
  • +
    fa-btc
    +
  • +
  • +
    fa-cny
    +
  • +
  • +
    fa-dollar
    +
  • +
  • +
    fa-eur
    +
  • +
  • +
    fa-euro
    +
  • +
  • +
    fa-gbp
    +
  • +
  • +
    fa-gg
    +
  • +
  • +
    fa-gg-circle
    +
  • +
  • +
    fa-ils
    +
  • +
  • +
    fa-inr
    +
  • +
  • +
    fa-jpy
    +
  • +
  • +
    fa-krw
    +
  • +
  • +
    fa-money
    +
  • +
  • +
    fa-rmb
    +
  • +
  • +
    fa-rouble
    +
  • +
  • +
    fa-rub
    +
  • +
  • +
    fa-ruble
    +
  • +
  • +
    fa-rupee
    +
  • +
  • +
    fa-shekel
    +
  • +
  • +
    fa-sheqel
    +
  • +
  • +
    fa-try
    +
  • +
  • +
    fa-turkish-lira
    +
  • +
  • +
    fa-usd
    +
  • +
  • +
    fa-won
    +
  • +
  • +
    fa-yen
    +
  • +
  • +
    fa-align-center
    +
  • +
  • +
    fa-align-justify
    +
  • +
  • +
    fa-align-left
    +
  • +
  • +
    fa-align-right
    +
  • +
  • +
    fa-bold
    +
  • +
  • +
    fa-chain
    +
  • +
  • +
    fa-chain-broken
    +
  • +
  • +
    fa-clipboard
    +
  • +
  • +
    fa-columns
    +
  • +
  • +
    fa-copy
    +
  • +
  • +
    fa-cut
    +
  • +
  • +
    fa-dedent
    +
  • +
  • +
    fa-eraser
    +
  • +
  • +
    fa-file
    +
  • +
  • +
    fa-file-o
    +
  • +
  • +
    fa-file-text
    +
  • +
  • +
    fa-file-text-o
    +
  • +
  • +
    fa-files-o
    +
  • +
  • +
    fa-floppy-o
    +
  • +
  • +
    fa-font
    +
  • +
  • +
    fa-header
    +
  • +
  • +
    fa-indent
    +
  • +
  • +
    fa-italic
    +
  • +
  • +
    fa-link
    +
  • +
  • +
    fa-list
    +
  • +
  • +
    fa-list-alt
    +
  • +
  • +
    fa-list-ol
    +
  • +
  • +
    fa-list-ul
    +
  • +
  • +
    fa-outdent
    +
  • +
  • +
    fa-paperclip
    +
  • +
  • +
    fa-paragraph
    +
  • +
  • +
    fa-paste
    +
  • +
  • +
    fa-repeat
    +
  • +
  • +
    fa-rotate-left
    +
  • +
  • +
    fa-rotate-right
    +
  • +
  • +
    fa-save
    +
  • +
  • +
    fa-scissors
    +
  • +
  • +
    fa-strikethrough
    +
  • +
  • +
    fa-subscript
    +
  • +
  • +
    fa-superscript
    +
  • +
  • +
    fa-table
    +
  • +
  • +
    fa-text-height
    +
  • +
  • +
    fa-text-width
    +
  • +
  • +
    fa-th
    +
  • +
  • +
    fa-th-large
    +
  • +
  • +
    fa-th-list
    +
  • +
  • +
    fa-underline
    +
  • +
  • +
    fa-undo
    +
  • +
  • +
    fa-unlink
    +
  • +
  • +
    fa-angle-double-down
    +
  • +
  • +
    fa-angle-double-left
    +
  • +
  • +
    fa-angle-double-right
    +
  • +
  • +
    fa-angle-double-up
    +
  • +
  • +
    fa-angle-down
    +
  • +
  • +
    fa-angle-left
    +
  • +
  • +
    fa-angle-right
    +
  • +
  • +
    fa-angle-up
    +
  • +
  • +
    fa-arrow-circle-down
    +
  • +
  • +
    fa-arrow-circle-left
    +
  • +
  • +
    fa-arrow-circle-o-down
    +
  • +
  • +
    fa-arrow-circle-o-left
    +
  • +
  • +
    fa-arrow-circle-o-right
    +
  • +
  • +
    fa-arrow-circle-o-up
    +
  • +
  • +
    fa-arrow-circle-right
    +
  • +
  • +
    fa-arrow-circle-up
    +
  • +
  • +
    fa-arrow-down
    +
  • +
  • +
    fa-arrow-left
    +
  • +
  • +
    fa-arrow-right
    +
  • +
  • +
    fa-arrow-up
    +
  • +
  • +
    fa-arrows
    +
  • +
  • +
    fa-arrows-alt
    +
  • +
  • +
    fa-arrows-h
    +
  • +
  • +
    fa-arrows-v
    +
  • +
  • +
    fa-caret-down
    +
  • +
  • +
    fa-caret-left
    +
  • +
  • +
    fa-caret-right
    +
  • +
  • +
    fa-caret-square-o-down
    +
  • +
  • +
    fa-caret-square-o-left
    +
  • +
  • +
    fa-caret-square-o-right
    +
  • +
  • +
    fa-caret-square-o-up
    +
  • +
  • +
    fa-caret-up
    +
  • +
  • +
    fa-chevron-circle-down
    +
  • +
  • +
    fa-chevron-circle-left
    +
  • +
  • +
    fa-chevron-circle-right
    +
  • +
  • +
    fa-chevron-circle-up
    +
  • +
  • +
    fa-chevron-down
    +
  • +
  • +
    fa-chevron-left
    +
  • +
  • +
    fa-chevron-right
    +
  • +
  • +
    fa-chevron-up
    +
  • +
  • +
    fa-exchange
    +
  • +
  • +
    fa-hand-o-down
    +
  • +
  • +
    fa-hand-o-left
    +
  • +
  • +
    fa-hand-o-right
    +
  • +
  • +
    fa-hand-o-up
    +
  • +
  • +
    fa-long-arrow-down
    +
  • +
  • +
    fa-long-arrow-left
    +
  • +
  • +
    fa-long-arrow-right
    +
  • +
  • +
    fa-long-arrow-up
    +
  • +
  • +
    fa-toggle-down
    +
  • +
  • +
    fa-toggle-left
    +
  • +
  • +
    fa-toggle-right
    +
  • +
  • +
    fa-toggle-up
    +
  • +
  • +
    fa-arrows-alt
    +
  • +
  • +
    fa-backward
    +
  • +
  • +
    fa-compress
    +
  • +
  • +
    fa-eject
    +
  • +
  • +
    fa-expand
    +
  • +
  • +
    fa-fast-backward
    +
  • +
  • +
    fa-fast-forward
    +
  • +
  • +
    fa-forward
    +
  • +
  • +
    fa-pause
    +
  • +
  • +
    fa-pause-circle
    +
  • +
  • +
    fa-pause-circle-o
    +
  • +
  • +
    fa-play
    +
  • +
  • +
    fa-play-circle
    +
  • +
  • +
    fa-play-circle-o
    +
  • +
  • +
    fa-random
    +
  • +
  • +
    fa-step-backward
    +
  • +
  • +
    fa-step-forward
    +
  • +
  • +
    fa-stop
    +
  • +
  • +
    fa-stop-circle
    +
  • +
  • +
    fa-stop-circle-o
    +
  • +
  • +
    fa-youtube-play
    +
  • +
  • +
    fa-500px
    +
  • +
  • +
    fa-adn
    +
  • +
  • +
    fa-amazon
    +
  • +
  • +
    fa-android
    +
  • +
  • +
    fa-angellist
    +
  • +
  • +
    fa-apple
    +
  • +
  • +
    fa-bandcamp
    +
  • +
  • +
    fa-behance
    +
  • +
  • +
    fa-behance-square
    +
  • +
  • +
    fa-bitbucket
    +
  • +
  • +
    fa-bitbucket-square
    +
  • +
  • +
    fa-bitcoin
    +
  • +
  • +
    fa-black-tie
    +
  • +
  • +
    fa-bluetooth
    +
  • +
  • +
    fa-bluetooth-b
    +
  • +
  • +
    fa-btc
    +
  • +
  • +
    fa-buysellads
    +
  • +
  • +
    fa-cc-amex
    +
  • +
  • +
    fa-cc-diners-club
    +
  • +
  • +
    fa-cc-discover
    +
  • +
  • +
    fa-cc-jcb
    +
  • +
  • +
    fa-cc-mastercard
    +
  • +
  • +
    fa-cc-paypal
    +
  • +
  • +
    fa-cc-stripe
    +
  • +
  • +
    fa-cc-visa
    +
  • +
  • +
    fa-chrome
    +
  • +
  • +
    fa-codepen
    +
  • +
  • +
    fa-codiepie
    +
  • +
  • +
    fa-connectdevelop
    +
  • +
  • +
    fa-contao
    +
  • +
  • +
    fa-css3
    +
  • +
  • +
    fa-dashcube
    +
  • +
  • +
    fa-delicious
    +
  • +
  • +
    fa-deviantart
    +
  • +
  • +
    fa-digg
    +
  • +
  • +
    fa-dribbble
    +
  • +
  • +
    fa-dropbox
    +
  • +
  • +
    fa-drupal
    +
  • +
  • +
    fa-edge
    +
  • +
  • +
    fa-eercast
    +
  • +
  • +
    fa-empire
    +
  • +
  • +
    fa-envira
    +
  • +
  • +
    fa-etsy
    +
  • +
  • +
    fa-expeditedssl
    +
  • +
  • +
    fa-fa
    +
  • +
  • +
    fa-facebook
    +
  • +
  • +
    fa-facebook-f
    +
  • +
  • +
    fa-facebook-official
    +
  • +
  • +
    fa-facebook-square
    +
  • +
  • +
    fa-firefox
    +
  • +
  • +
    fa-first-order
    +
  • +
  • +
    fa-flickr
    +
  • +
  • +
    fa-font-awesome
    +
  • +
  • +
    fa-fonticons
    +
  • +
  • +
    fa-fort-awesome
    +
  • +
  • +
    fa-forumbee
    +
  • +
  • +
    fa-foursquare
    +
  • +
  • +
    fa-free-code-camp
    +
  • +
  • +
    fa-ge
    +
  • +
  • +
    fa-get-pocket
    +
  • +
  • +
    fa-gg
    +
  • +
  • +
    fa-gg-circle
    +
  • +
  • +
    fa-git
    +
  • +
  • +
    fa-git-square
    +
  • +
  • +
    fa-github
    +
  • +
  • +
    fa-github-alt
    +
  • +
  • +
    fa-github-square
    +
  • +
  • +
    fa-gitlab
    +
  • +
  • +
    fa-gittip
    +
  • +
  • +
    fa-glide
    +
  • +
  • +
    fa-glide-g
    +
  • +
  • +
    fa-google
    +
  • +
  • +
    fa-google-plus
    +
  • +
  • +
    fa-google-plus-circle
    +
  • +
  • +
    fa-google-plus-official
    +
  • +
  • +
    fa-google-plus-square
    +
  • +
  • +
    fa-google-wallet
    +
  • +
  • +
    fa-gratipay
    +
  • +
  • +
    fa-grav
    +
  • +
  • +
    fa-hacker-news
    +
  • +
  • +
    fa-houzz
    +
  • +
  • +
    fa-html5
    +
  • +
  • +
    fa-imdb
    +
  • +
  • +
    fa-instagram
    +
  • +
  • +
    fa-internet-explorer
    +
  • +
  • +
    fa-ioxhost
    +
  • +
  • +
    fa-joomla
    +
  • +
  • +
    fa-jsfiddle
    +
  • +
  • +
    fa-lastfm
    +
  • +
  • +
    fa-lastfm-square
    +
  • +
  • +
    fa-leanpub
    +
  • +
  • +
    fa-linkedin
    +
  • +
  • + +
    fa-linkedin-square
    +
  • +
  • + +
    fa-linode
    +
  • +
  • + +
    fa-linux
    +
  • +
  • + +
    fa-maxcdn
    +
  • +
  • + +
    fa-meanpath
    +
  • +
  • + +
    fa-medium
    +
  • +
  • + +
    fa-meetup
    +
  • +
  • + +
    fa-mixcloud
    +
  • +
  • + +
    fa-modx
    +
  • +
  • + +
    fa-odnoklassniki
    +
  • +
  • + +
    fa-odnoklassniki-square
    +
  • +
  • + +
    fa-opencart
    +
  • +
  • + +
    fa-openid
    +
  • +
  • + +
    fa-opera
    +
  • +
  • + +
    fa-optin-monster
    +
  • +
  • + +
    fa-pagelines
    +
  • +
  • + +
    fa-paypal
    +
  • +
  • + +
    fa-pied-piper
    +
  • +
  • + +
    fa-pied-piper-alt
    +
  • +
  • + +
    fa-pied-piper-pp
    +
  • +
  • + +
    fa-pinterest
    +
  • +
  • + +
    fa-pinterest-p
    +
  • +
  • + +
    fa-pinterest-square
    +
  • +
  • + +
    fa-product-hunt
    +
  • +
  • + +
    fa-qq
    +
  • +
  • + +
    fa-quora
    +
  • +
  • + +
    fa-ra
    +
  • +
  • + +
    fa-ravelry
    +
  • +
  • + +
    fa-rebel
    +
  • +
  • + +
    fa-reddit
    +
  • +
  • + +
    fa-reddit-alien
    +
  • +
  • + +
    fa-reddit-square
    +
  • +
  • + +
    fa-renren
    +
  • +
  • + +
    fa-resistance
    +
  • +
  • + +
    fa-safari
    +
  • +
  • + +
    fa-scribd
    +
  • +
  • + +
    fa-sellsy
    +
  • +
  • + +
    fa-share-alt
    +
  • +
  • + +
    fa-share-alt-square
    +
  • +
  • + +
    fa-shirtsinbulk
    +
  • +
  • + +
    fa-simplybuilt
    +
  • +
  • + +
    fa-skyatlas
    +
  • +
  • + +
    fa-skype
    +
  • +
  • + +
    fa-slack
    +
  • +
  • + +
    fa-slideshare
    +
  • +
  • + +
    fa-snapchat
    +
  • +
  • + +
    fa-snapchat-ghost
    +
  • +
  • + +
    fa-snapchat-square
    +
  • +
  • + +
    fa-soundcloud
    +
  • +
  • + +
    fa-spotify
    +
  • +
  • + +
    fa-stack-exchange
    +
  • +
  • + +
    fa-stack-overflow
    +
  • +
  • + +
    fa-steam
    +
  • +
  • + +
    fa-steam-square
    +
  • +
  • + +
    fa-stumbleupon
    +
  • +
  • + +
    fa-stumbleupon-circle
    +
  • +
  • + +
    fa-superpowers
    +
  • +
  • + +
    fa-telegram
    +
  • +
  • + +
    fa-tencent-weibo
    +
  • +
  • + +
    fa-themeisle
    +
  • +
  • + +
    fa-trello
    +
  • +
  • + +
    fa-tripadvisor
    +
  • +
  • + +
    fa-tumblr
    +
  • +
  • + +
    fa-tumblr-square
    +
  • +
  • + +
    fa-twitch
    +
  • +
  • + +
    fa-twitter
    +
  • +
  • + +
    fa-twitter-square
    +
  • +
  • + +
    fa-usb
    +
  • +
  • + +
    fa-viacoin
    +
  • +
  • + +
    fa-viadeo
    +
  • +
  • + +
    fa-viadeo-square
    +
  • +
  • + +
    fa-vimeo
    +
  • +
  • + +
    fa-vimeo-square
    +
  • +
  • + +
    fa-vine
    +
  • +
  • + +
    fa-vk
    +
  • +
  • + +
    fa-wechat
    +
  • +
  • + +
    fa-weibo
    +
  • +
  • + +
    fa-weixin
    +
  • +
  • + +
    fa-whatsapp
    +
  • +
  • + +
    fa-wikipedia-w
    +
  • +
  • + +
    fa-windows
    +
  • +
  • + +
    fa-wordpress
    +
  • +
  • + +
    fa-wpbeginner
    +
  • +
  • + +
    fa-wpexplorer
    +
  • +
  • + +
    fa-wpforms
    +
  • +
  • + +
    fa-xing
    +
  • +
  • + +
    fa-xing-square
    +
  • +
  • + +
    fa-y-combinator
    +
  • +
  • + +
    fa-y-combinator-square
    +
  • +
  • + +
    fa-yahoo
    +
  • +
  • + +
    fa-yc
    +
  • +
  • + +
    fa-yc-square
    +
  • +
  • + +
    fa-yelp
    +
  • +
  • + +
    fa-yoast
    +
  • +
  • + +
    fa-youtube
    +
  • +
  • + +
    fa-youtube-play
    +
  • +
  • + +
    fa-youtube-square
    +
  • +
  • + +
    fa-ambulance
    +
  • +
  • + +
    fa-h-square
    +
  • +
  • + +
    fa-heart
    +
  • +
  • + +
    fa-heart-o
    +
  • +
  • + +
    fa-heartbeat
    +
  • +
  • + +
    fa-hospital-o
    +
  • +
  • + +
    fa-medkit
    +
  • +
  • + +
    fa-plus-square
    +
  • +
  • + +
    fa-stethoscope
    +
  • +
  • + +
    fa-user-md
    +
  • +
  • + +
    fa-wheelchair
    +
  • +
  • + +
    fa-wheelchair-alt
    +
  • +
+ + + + + + diff --git a/application/admin/view/plugs.upfile.html b/application/admin/view/plugs/upfile.html similarity index 76% rename from application/admin/view/plugs.upfile.html rename to application/admin/view/plugs/upfile.html index 4e8f207d4..0f816b963 100644 --- a/application/admin/view/plugs.upfile.html +++ b/application/admin/view/plugs/upfile.html @@ -6,11 +6,11 @@ - - - - - + + + + +
@@ -43,40 +43,38 @@ function uploaded(ret, file) { var url = ret.url || ret.site_url; $('#' + file.id).attr('data-md5', file.md5).attr('data-src', url); - /* {if $mode === 'one'} */ + /*{if $mode === 'one'}*/ top.$('[name="{$field}"]').map(function () { top.$(this).attr('data-srcs', ret.url).attr('data-md5', file.md5).val(url).trigger('change'); }); - //top.$.msg.tips('文件上传成功!'); - var index = top.layer.getFrameIndex(window.name); - top.layer.close(index); - /* {/if} {$mode}*/ + top.layer.close(top.layer.getFrameIndex(window.name)); + /*{/if}*/ } - function confirmSelected() { - var srcs = new Array(), md5s = new Array(); - $('[data-md5] .success').map(function () { - var $li = $(this).parents('[data-md5]'); - md5s.push($li.attr('data-md5')); - srcs.push($li.attr('data-src')); - }); - if (srcs.length < 1) { - return top.$.msg.tips('还没有选择文件,请勾选需要使用的文件!'); - } - top.$('[name="{$field}"]').map(function () { - top.$(this).attr('data-srcs', srcs.join('|')).attr('data-md5', md5s.join('|')).val(srcs.join('|')).trigger('change'); - }); - var index = top.layer.getFrameIndex(window.name); - top.layer.close(index); - } + var isSuccessState = false; function completed() { var btnHTML = '完成上传'; - $('.uploadBtn').on('click', function () { - if (this.innerHTML === btnHTML) { - confirmSelected.call(this); + $('.uploadBtn').html(btnHTML).on('click', successSelected); + + function successSelected() { + if (!isSuccessState && this.innerHTML === btnHTML) { + isSuccessState = true; + var srcs = [], md5s = []; + $('[data-md5] .success').map(function () { + var $li = $(this).parents('[data-md5]'); + md5s.push($li.attr('data-md5')); + srcs.push($li.attr('data-src')); + }); + if (srcs.length < 1) { + return top.$.msg.tips('还没有选择文件,请勾选需要使用的文件!'); + } + top.$('[name="{$field}"]').map(function () { + top.$(this).attr('data-srcs', srcs.join('|')).attr('data-md5', md5s.join('|')).val(srcs.join('|')).trigger('change'); + }); + top.layer.close(top.layer.getFrameIndex(window.name)); } - }).html(btnHTML); + } } // 当domReady的时候开始初始化 @@ -121,7 +119,7 @@ flashVersion = (function () { var version; try { - version = navigator.plugins[ 'Shockwave Flash' ]; + version = navigator.plugins['Shockwave Flash']; version = version.description; } catch (ex) { try { @@ -131,12 +129,12 @@ } } version = version.match(/\d+/g); - return parseFloat(version[ 0 ] + '.' + version[ 1 ], 10); + return parseFloat(version[0] + '.' + version[1], 10); })(), supportTransition = (function () { - var s = document.createElement('p').style, r = 'transition' in s || 'WebkitTransition' in s || 'MozTransition' in s || 'msTransition' in s || 'OTransition' in s; - s = null; - return r; + var s = document.createElement('p').style, + r = 'transition' in s || 'WebkitTransition' in s || 'MozTransition' in s || 'msTransition' in s || 'OTransition' in s; + return (s = null), r; })(), uploader; if (!WebUploader.Uploader.support('flash') && WebUploader.browser.ie) { @@ -146,10 +144,10 @@ window['expressinstallcallback'] = function (state) { switch (state) { case 'Download.Cancelled': - alert('您取消了更新!') + alert('您取消了更新!'); break; case 'Download.Failed': - alert('安装失败') + alert('安装失败'); break; default: alert('安装已成功,请刷新!'); @@ -157,7 +155,7 @@ } delete window['expressinstallcallback']; }; - var swf = '__PUBLIC__/static/plugs/uploader/expressInstall.swf'; + var swf = '__STATIC__/plugs/uploader/expressInstall.swf'; var html = '' + '

' + file.name + '

' + - '

' + - '

' + - ''), - $btns = $('
' + - '删除' + - '向右旋转' + - '向左旋转
').appendTo($li), - $prgress = $li.find('p.progress span'), - $wrap = $li.find('p.imgWrap'), - $info = $('

'), - showError = function (code) { - var text = ''; - switch (code) { - case 'exceed_size': - text = '文件大小超出'; - break; - case 'interrupt': - text = '上传暂停'; - break; - default: - text = '上传失败,请重试'; - break; - } - $info.text(text).appendTo($li); - }; + var $li = $('
  • ' + file.name + '

  • '), + $btns = $('
    删除向右旋转向左旋转
    ').appendTo($li), + $prgress = $li.find('p.progress span'), $wrap = $li.find('p.imgWrap'), $info = $('

    '); + var showError = function (code) { + var text = ''; + switch (code) { + case 'exceed_size': + text = '文件大小超出'; + break; + case 'interrupt': + text = '上传暂停'; + break; + case 'http': + case 'server': + case 'abort': + text = '上传失败,请重试'; + break; + default: + text = code; + break; + } + $info.text(text).appendTo($li); + }; if (file.getStatus() === 'invalid') { showError(file.statusText); } else { // @todo lazyload $wrap.text('预览中'); uploader.makeThumb(file, function (error, src) { - var img; if (error) { - $wrap.text('不能预览'); - return; + return $wrap.text('不能预览'); } + var img; if (isSupportBase64) { img = $(''); $wrap.empty().append(img); } else { - $.ajax('{"plugs/file/preview"|url}', {method: 'POST', data: src, dataType: 'json'}).done(function (response) { + $.ajax('{"plugs/file/preview"|url}', {method: 'post', data: src, dataType: 'json'}).done(function (response) { if (response.result) { img = $(''); $wrap.empty().append(img); @@ -362,50 +345,41 @@ }); } }, thumbnailWidth, thumbnailHeight); - percentages[ file.id ] = [file.size, 0]; + percentages[file.id] = [file.size, 0]; file.rotation = 0; $upload.html('开始上传'); } - + // 文件上传状态变化 file.on('statuschange', function (cur, prev) { if (prev === 'progress') { $prgress.hide().width(0); } else if (prev === 'queued') { - $li.off('mouseenter mouseleave'); - $btns.remove(); + $li.off('mouseenter mouseleave'), $btns.remove(); } - // 成功 if (cur === 'error' || cur === 'invalid') { showError(file.statusText); - percentages[ file.id ][ 1 ] = 1; + percentages[file.id][1] = 1; } else if (cur === 'interrupt') { showError('interrupt'); } else if (cur === 'queued') { - percentages[ file.id ][ 1 ] = 0; + percentages[file.id][1] = 0; } else if (cur === 'progress') { - $info.remove(); - $prgress.css('display', 'block'); + $info.remove(), $prgress.css('display', 'block'); } else if (cur === 'complete') { $li.append(''); } - $li.removeClass('state-' + prev).addClass('state-' + cur); }); - $li.on('mouseenter', function () { $btns.stop().animate({height: 30}); - }); - - $li.on('mouseleave', function () { + }).on('mouseleave', function () { $btns.stop().animate({height: 0}); }); - $btns.on('click', 'span', function () { - var index = $(this).index(), deg; - switch (index) { + switch ($(this).index()) { case 0: - uploader.removeFile(file); - return; + removeFile(file); + return uploader.removeFile(file); case 1: file.rotation += 90; break; @@ -414,7 +388,7 @@ break; } if (supportTransition) { - deg = 'rotate(' + file.rotation + 'deg)'; + var deg = 'rotate(' + file.rotation + 'deg)'; $wrap.css({'-webkit-transform': deg, '-mos-transform': deg, '-o-transform': deg, 'transform': deg}); } else { $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')'); @@ -425,17 +399,16 @@ // 负责view的销毁 function removeFile(file) { - var $li = $('#' + file.id); - delete percentages[ file.id ]; + delete percentages[file.id]; updateTotalProgress(); - $li.off().find('.file-panel').off().end().remove(); + $('#' + file.id).off().find('.file-panel').off().end().remove(); } function updateTotalProgress() { var loaded = 0, total = 0, spans = $progress.children(), percent; $.each(percentages, function (k, v) { - total += v[ 0 ]; - loaded += v[ 0 ] * v[ 1 ]; + total += v[0]; + loaded += v[0] * v[1]; }); percent = total ? loaded / total : 0; spans.eq(0).text(Math.round(percent * 100) + '%'); @@ -463,7 +436,7 @@ } function setState(val) { - var file, stats; + var stats; if (val === state) { return; } @@ -519,7 +492,7 @@ uploader.onUploadProgress = function (file, percentage) { var $li = $('#' + file.id), $percent = $li.find('.progress span'); $percent.css('width', percentage * 100 + '%'); - percentages[ file.id ][ 1 ] = percentage; + percentages[file.id][1] = percentage; updateTotalProgress(); }; @@ -530,35 +503,29 @@ $placeHolder.addClass('element-invisible'); $statusBar.show(); } - addFile(file); - setState('ready'); - updateTotalProgress(); + addFile(file), setState('ready'), updateTotalProgress(); }; uploader.onfieldequeued = function (file) { fileCount--; fileSize -= file.size; !fileCount && setState('pedding'); - removeFile(file); - updateTotalProgress(); + removeFile(file), updateTotalProgress(); }; uploader.on('all', function (type) { switch (type) { case 'uploadFinished': - setState('confirm'); - break; + return setState('confirm'); case 'startUpload': - setState('uploading'); - break; + return setState('uploading'); case 'stopUpload': - setState('paused'); - break; + return setState('paused'); } }); uploader.onError = function (code) { - //alert('Eroor: ' + code); + // 000alert('Error: ' + code); }; $upload.on('click', function () { diff --git a/application/admin/view/public/content.html b/application/admin/view/public/content.html new file mode 100644 index 000000000..ba530561d --- /dev/null +++ b/application/admin/view/public/content.html @@ -0,0 +1,13 @@ + +{block name="style"}{/block} +
    + +
    +
    {$title}
    +
    {block name="button"}{/block}
    +
    + +
    {block name='content'}{/block}
    +
    +{block name='script'}{/block} + \ No newline at end of file diff --git a/application/admin/view/public/main.html b/application/admin/view/public/main.html new file mode 100644 index 000000000..e24ce4287 --- /dev/null +++ b/application/admin/view/public/main.html @@ -0,0 +1,26 @@ + + + + + + {block name="title"}{$title|default=''}{if !empty($title)} · {/if}{:sysconf('site_name')}{/block} + + + + + + + + {block name="style"}{/block} + + + + + + + {block name="body"}{/block} + + + {block name="script"}{/block} + + \ No newline at end of file diff --git a/application/admin/view/user.index.html b/application/admin/view/user.index.html deleted file mode 100644 index 9df0772d0..000000000 --- a/application/admin/view/user.index.html +++ /dev/null @@ -1,110 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="button"} -
    - - -
    -{/block} - -{block name="content"} - - - - - -
    - - - - - - - - - - - - - - - - {foreach $list as $key=>$vo} - - - - - - - - - - - {/foreach} - -
    - - 用户账号手机号电子邮箱登录次数最后登录状态操作
    - - {$vo.username}{$vo.phone|default="还没有设置手机号"}{$vo.mail|default="还没有设置邮箱"}{$vo.login_num|default="从未登录"}{$vo.login_at|default="从未登录"} - {if $vo.status eq 0} - 已禁用 - {elseif $vo.status eq 1} - 使用中 - {/if} - - {if auth("$classuri/edit")} - | - 编辑 - {/if} - {if auth("$classuri/auth")} - | - 授权 - {/if} - {if auth("$classuri/pass")} - | - 密码 - {/if} - {if $vo.status eq 1 and auth("$classuri/forbid")} - | - 禁用 - {elseif auth("$classuri/resume")} - | - 启用 - {/if} - {if auth("$classuri/del")} - | - 删除 - {/if} -
    - {if isset($page)}

    {$page}

    {/if} -
    -{/block} \ No newline at end of file diff --git a/application/admin/view/user.pass.html b/application/admin/view/user.pass.html deleted file mode 100644 index cf2f3ac5d..000000000 --- a/application/admin/view/user.pass.html +++ /dev/null @@ -1,45 +0,0 @@ -
    - -
    - -
    - {if $vo and $vo.username} - - {else} - - {/if} -
    -
    - - {if $verify} -
    - -
    - -
    -
    - {/if} - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - {if isset($vo['id'])}{/if} - - -
    - -
    diff --git a/application/admin/view/user.auth.html b/application/admin/view/user/auth.html similarity index 63% rename from application/admin/view/user.auth.html rename to application/admin/view/user/auth.html index ff4dd9bfa..3410ac33a 100644 --- a/application/admin/view/user.auth.html +++ b/application/admin/view/user/auth.html @@ -1,35 +1,39 @@ -
    - -
    - -
    - {if $vo and $vo.username} - - {else} - - {/if} -
    -
    - -
    - -
    - {foreach $authorizes as $authorize} - {if in_array($authorize['id'],$vo['authorize'])} - - {else} - - {/if} - {/foreach} -
    -
    - -
    - -
    - {if isset($vo['id'])}{/if} - - -
    - -
    + diff --git a/application/admin/view/user.form.html b/application/admin/view/user/form.html similarity index 56% rename from application/admin/view/user.form.html rename to application/admin/view/user/form.html index fac60bf99..0d7438b03 100644 --- a/application/admin/view/user.form.html +++ b/application/admin/view/user/form.html @@ -1,60 +1,61 @@ -
    - -
    - -
    - {if $vo and isset($vo.username)} - - {else} - - {/if} -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - - {if isset($authorizes)} -
    - -
    - {foreach $authorizes as $authorize} - {if in_array($authorize['id'],$vo['authorize'])} - - {else} - - {/if} - {/foreach} -
    -
    - {/if} - -
    - -
    - -
    -
    - - -
    - -
    - {if isset($vo['id'])}{/if} - - -
    - - -
    + diff --git a/application/admin/view/user/index.html b/application/admin/view/user/index.html new file mode 100644 index 000000000..ecdc488fb --- /dev/null +++ b/application/admin/view/user/index.html @@ -0,0 +1,136 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + + +{/block} + +{block name="content"} + + + + + + +
    + +

    没 有 记 录 哦!

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + 用户名手机号电子邮箱登录次数最后登录状态
    + + + {$vo.username} + + {$vo.phone|default="还没有设置手机号"|raw} + + {$vo.mail|default="还没有设置邮箱"|raw} + {$vo.login_num|default=0} + {$vo.login_at|format_datetime|default="从未登录"|raw} + + {if $vo.status eq 0}已禁用{elseif $vo.status eq 1}使用中{/if} + + + {if auth("$classuri/edit")} + | + 编辑 + {/if} + + {if auth("$classuri/auth")} + | + 授权 + {/if} + + {if auth("$classuri/pass")} + | + 密码 + {/if} + + {if $vo.status eq 1 and auth("$classuri/forbid")} + | + 禁用 + {elseif auth("$classuri/resume")} + | + 启用 + {/if} + + {if auth("$classuri/del")} + | + 删除 + {/if} + +
    + {if isset($page)}

    {$page|raw}

    {/if} + +
    +{/block} \ No newline at end of file diff --git a/application/admin/view/user/pass.html b/application/admin/view/user/pass.html new file mode 100644 index 000000000..da3b95b4e --- /dev/null +++ b/application/admin/view/user/pass.html @@ -0,0 +1,45 @@ + diff --git a/application/common.php b/application/common.php index d6e57edab..94ce94fae 100644 --- a/application/common.php +++ b/application/common.php @@ -1,7 +1,7 @@ sysconf('wechat_token'), - 'appid' => sysconf('wechat_appid'), - 'appsecret' => sysconf('wechat_appsecret'), - 'encodingaeskey' => sysconf('wechat_encodingaeskey'), - 'mch_id' => sysconf('wechat_mch_id'), - 'partnerkey' => sysconf('wechat_partnerkey'), - 'ssl_cer' => sysconf('wechat_cert_cert'), - 'ssl_key' => sysconf('wechat_cert_key'), - 'cachepath' => CACHE_PATH . 'wxpay' . DS, - ]; - $wechat[$index] = Loader::get($type, $config); - } - return $wechat[$index]; -} - -/** - * 安全URL编码 - * @param array|string $data - * @return string - */ -function encode($data) { - return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode(serialize($data))); -} - -/** - * 安全URL解码 - * @param string $string - * @return string - */ -function decode($string) { - $data = str_replace(['-', '_'], ['+', '/'], $string); - $mod4 = strlen($data) % 4; - !!$mod4 && $data .= substr('====', $mod4); - return unserialize(base64_decode($data)); +function p($data, $force = false, $file = null) +{ + is_null($file) && $file = env('runtime_path') . date('Ymd') . '.txt'; + $str = (is_string($data) ? $data : (is_array($data) || is_object($data)) ? print_r($data, true) : var_export($data, true)) . PHP_EOL; + $force ? file_put_contents($file, $str) : file_put_contents($file, $str, FILE_APPEND); } /** @@ -81,46 +34,77 @@ function decode($string) { * @param string $node * @return bool */ -function auth($node) { +function auth($node) +{ return NodeService::checkAuthNode($node); } /** * 设备或配置系统参数 * @param string $name 参数名称 - * @param bool $value 默认是false为获取值,否则为更新 + * @param bool $value 默认是null为获取值,否则为更新 * @return string|bool + * @throws \think\Exception + * @throws \think\exception\PDOException */ -function sysconf($name, $value = false) { +function sysconf($name, $value = null) +{ static $config = []; - if ($value !== false) { - $config = []; - $data = ['name' => $name, 'value' => $value]; + if ($value !== null) { + list($config, $data) = [[], ['name' => $name, 'value' => $value]]; return DataService::save('SystemConfig', $data, 'name'); } if (empty($config)) { - foreach (Db::name('SystemConfig')->select() as $vo) { - $config[$vo['name']] = $vo['value']; - } + $config = Db::name('SystemConfig')->column('name,value'); } return isset($config[$name]) ? $config[$name] : ''; } /** - * array_column 函数兼容 + * 日期格式标准输出 + * @param string $datetime 输入日期 + * @param string $format 输出格式 + * @return false|string */ -if (!function_exists("array_column")) { +function format_datetime($datetime, $format = 'Y年m月d日 H:i:s') +{ + return date($format, strtotime($datetime)); +} - function array_column(array &$rows, $column_key, $index_key = null) { - $data = []; - foreach ($rows as $row) { - if (empty($index_key)) { - $data[] = $row[$column_key]; - } else { - $data[$row[$index_key]] = $row[$column_key]; - } - } - return $data; +/** + * UTF8字符串加密 + * @param string $string + * @return string + */ +function encode($string) +{ + list($chars, $length) = ['', strlen($string = iconv('utf-8', 'gbk', $string))]; + for ($i = 0; $i < $length; $i++) { + $chars .= str_pad(base_convert(ord($string[$i]), 10, 36), 2, 0, 0); } + return $chars; +} -} \ No newline at end of file +/** + * UTF8字符串解密 + * @param string $string + * @return string + */ +function decode($string) +{ + $chars = ''; + foreach (str_split($string, 2) as $char) { + $chars .= chr(intval(base_convert($char, 36, 10))); + } + return iconv('gbk', 'utf-8', $chars); +} + +/** + * 下载远程文件到本地 + * @param string $url 远程图片地址 + * @return string + */ +function local_image($url) +{ + return \service\FileService::download($url)['url']; +} diff --git a/application/config.php b/application/config.php deleted file mode 100644 index 4a5b631be..000000000 --- a/application/config.php +++ /dev/null @@ -1,224 +0,0 @@ - 'app', - // 应用调试模式 - 'app_debug' => true, - // 应用Trace - 'app_trace' => false, - // 应用模式状态 - 'app_status' => '', - // 是否支持多模块 - 'app_multi_module' => true, - // 入口自动绑定模块 - 'auto_bind_module' => false, - // 注册的根命名空间 - 'root_namespace' => [], - // 扩展函数文件 - 'extra_file_list' => [THINK_PATH . 'helper' . EXT], - // 默认输出类型 - 'default_return_type' => 'html', - // 默认AJAX 数据返回格式,可选json xml ... - 'default_ajax_return' => 'json', - // 默认JSONP格式返回的处理方法 - 'default_jsonp_handler' => 'jsonpReturn', - // 默认JSONP处理方法 - 'var_jsonp_handler' => 'callback', - // 默认时区 - 'default_timezone' => 'PRC', - // 是否开启多语言 - 'lang_switch_on' => false, - // 默认全局过滤方法 用逗号分隔多个 - 'default_filter' => '', - // 默认语言 - 'default_lang' => 'zh-cn', - // 应用类库后缀 - 'class_suffix' => false, - // 控制器类后缀 - 'controller_suffix' => false, - // +---------------------------------------------------------------------- - // | 模块设置 - // +---------------------------------------------------------------------- - // 默认模块名 - 'default_module' => 'index', - // 禁止访问模块 - 'deny_module_list' => ['common'], - // 默认控制器名 - 'default_controller' => 'Index', - // 默认操作名 - 'default_action' => 'index', - // 默认验证器 - 'default_validate' => '', - // 默认的空控制器名 - 'empty_controller' => 'Error', - // 操作方法后缀 - 'action_suffix' => '', - // 自动搜索控制器 - 'controller_auto_search' => false, - // +---------------------------------------------------------------------- - // | URL设置 - // +---------------------------------------------------------------------- - // PATHINFO变量名 用于兼容模式 - 'var_pathinfo' => 's', - // 兼容PATH_INFO获取 - 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], - // pathinfo分隔符 - 'pathinfo_depr' => '/', - // URL伪静态后缀 - 'url_html_suffix' => 'html', - // URL普通方式参数 用于自动生成 - 'url_common_param' => false, - // URL参数方式 0 按名称成对解析 1 按顺序解析 - 'url_param_type' => 0, - // 是否开启路由 - 'url_route_on' => true, - // 路由使用完整匹配 - 'route_complete_match' => false, - // 路由配置文件(支持配置多个) - 'route_config_file' => ['route'], - // 是否强制使用路由 - 'url_route_must' => false, - // 域名部署 - 'url_domain_deploy' => false, - // 域名根,如thinkphp.cn - 'url_domain_root' => '', - // 是否自动转换URL中的控制器和操作名 - 'url_convert' => true, - // 默认的访问控制器层 - 'url_controller_layer' => 'controller', - // 表单请求类型伪装变量 - 'var_method' => '_method', - // 表单ajax伪装变量 - 'var_ajax' => '_ajax', - // 表单pjax伪装变量 - 'var_pjax' => '_pjax', - // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 - 'request_cache' => false, - // 请求缓存有效期 - 'request_cache_expire' => null, - // +---------------------------------------------------------------------- - // | 模板设置 - // +---------------------------------------------------------------------- - 'template' => [ - // 模板引擎类型 支持 php think 支持扩展 - 'type' => 'Think', - // 模板路径 - 'view_path' => '', - // 模板后缀 - 'view_suffix' => 'html', - // 模板文件名分隔符 - 'view_depr' => '.', - // 模板引擎普通标签开始标记 - 'tpl_begin' => '{', - // 模板引擎普通标签结束标记 - 'tpl_end' => '}', - // 标签库标签开始标记 - 'taglib_begin' => '{', - // 标签库标签结束标记 - 'taglib_end' => '}', - ], - // 视图输出字符串内容替换 - 'view_replace_str' => [], - // 默认跳转页面对应的模板文件 - 'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', - 'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', - // +---------------------------------------------------------------------- - // | 异常及错误设置 - // +---------------------------------------------------------------------- - // 异常页面的模板文件 - 'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl', - // 错误显示信息,非调试模式有效 - 'error_message' => '页面错误!请稍后再试~', - // 显示错误信息 - 'show_error_msg' => false, - // 异常处理handle类 留空使用 \think\exception\Handle - 'exception_handle' => '', - // +---------------------------------------------------------------------- - // | 日志设置 - // +---------------------------------------------------------------------- - 'log' => [ - // 日志记录方式,内置 file socket 支持扩展 - 'type' => 'File', - // 日志保存目录 - 'path' => LOG_PATH, - // 日志记录级别 log,error,info,sql,notice,alert,debug - 'level' => ['error', 'log', 'info', 'sql', 'notice', 'alert', 'debug'], - // error和sql日志单独记录 - 'apart_level' => ['error', 'sql'], - ], - // +---------------------------------------------------------------------- - // | Trace设置 开启 app_trace 后 有效 - // +---------------------------------------------------------------------- - 'trace' => [ - // 内置Html Console 支持扩展 - 'type' => 'Html', - ], - // +---------------------------------------------------------------------- - // | 缓存设置 - // +---------------------------------------------------------------------- - 'cache' => [ - // 驱动方式 - 'type' => 'File', - // 缓存保存目录 - 'path' => CACHE_PATH, - // 缓存前缀 - 'prefix' => '', - // 缓存有效期 0表示永久缓存 - 'expire' => 0, - ], - // +---------------------------------------------------------------------- - // | 会话设置 - // +---------------------------------------------------------------------- - 'session' => [ - 'id' => '', - // SESSION_ID的提交变量,解决flash上传跨域 - 'var_session_id' => '', - // SESSION 前缀 - 'prefix' => 'think', - // 驱动方式 支持redis memcache memcached - 'type' => '', - // 是否自动开启 SESSION - 'auto_start' => true, - ], - // +---------------------------------------------------------------------- - // | Cookie设置 - // +---------------------------------------------------------------------- - 'cookie' => [ - // cookie 名称前缀 - 'prefix' => '', - // cookie 保存时间 - 'expire' => 0, - // cookie 保存路径 - 'path' => '/', - // cookie 有效域名 - 'domain' => '', - // cookie 启用安全传输 - 'secure' => false, - // httponly设置 - 'httponly' => '', - // 是否使用 setcookie - 'setcookie' => true, - ], - //分页配置 - 'paginate' => [ - 'type' => 'bootstrap', - 'var_page' => 'page', - 'list_rows' => 15, - ], -]; diff --git a/application/database.php b/application/database.php deleted file mode 100644 index 597199d28..000000000 --- a/application/database.php +++ /dev/null @@ -1,60 +0,0 @@ - 'mysql', - // 服务器地址 - 'hostname' => '127.0.0.1', - // 数据库名 - 'database' => 'think.admin', - // 用户名 - 'username' => 'think.admin', - // 密码 - 'password' => '', - // 端口 - 'hostport' => '3306', - // 连接dsn - 'dsn' => '', - // 数据库连接参数 - 'params' => [], - // 数据库编码默认采用utf8 - 'charset' => 'utf8', - // 数据库表前缀 - 'prefix' => '', - // 数据库调试模式 - 'debug' => true, - // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, - // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, - // 读写分离后 主服务器数量 - 'master_num' => 1, - // 指定从服务器序号 - 'slave_no' => '', - // 是否严格检查字段是否存在 - 'fields_strict' => true, - // 数据集返回类型 - 'resultset_type' => 'array', - // 自动写入时间戳字段 - 'auto_timestamp' => false, - // 时间字段取出后的默认时间格式 - 'datetime_format' => 'Y-m-d H:i:s', - // 是否需要进行SQL性能分析 - 'sql_explain' => false, - // Builder类 - 'builder' => '', - // Query类 - 'query' => '\\think\\db\\Query', -]; diff --git a/application/extra/mines.php b/application/extra/mines.php deleted file mode 100644 index bb58d36da..000000000 --- a/application/extra/mines.php +++ /dev/null @@ -1,161 +0,0 @@ - ['application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'], - 'cpt' => 'application/mac-compactpro', - 'csv' => ['text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel', 'text/plain'], - 'bin' => ['application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'], - 'dms' => 'application/octet-stream', - 'lha' => 'application/octet-stream', - 'lzh' => 'application/octet-stream', - 'exe' => ['application/octet-stream', 'application/x-msdownload'], - 'class' => 'application/octet-stream', - 'psd' => ['application/x-photoshop', 'image/vnd.adobe.photoshop'], - 'so' => 'application/octet-stream', - 'sea' => 'application/octet-stream', - 'dll' => 'application/octet-stream', - 'oda' => 'application/oda', - 'pdf' => ['application/pdf', 'application/force-download', 'application/x-download', 'binary/octet-stream'], - 'ai' => ['application/pdf', 'application/postscript'], - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'mif' => 'application/vnd.mif', - 'xls' => ['application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/excel', 'application/download', 'application/vnd.ms-office', 'application/msword'], - 'ppt' => ['application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office', 'application/msword'], - 'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/x-zip', 'application/zip'], - 'wbxml' => 'application/wbxml', - 'wmlc' => 'application/wmlc', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'dvi' => 'application/x-dvi', - 'gtar' => 'application/x-gtar', - 'gz' => 'application/x-gzip', - 'gzip' => 'application/x-gzip', - 'php' => ['application/x-httpd-php', 'application/php', 'application/x-php', 'text/php', 'text/x-php', 'application/x-httpd-php-source'], - 'php4' => 'application/x-httpd-php', - 'php3' => 'application/x-httpd-php', - 'phtml' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'js' => ['application/x-javascript', 'text/plain'], - 'swf' => 'application/x-shockwave-flash', - 'sit' => 'application/x-stuffit', - 'tar' => 'application/x-tar', - 'tgz' => ['application/x-tar', 'application/x-gzip-compressed'], - 'z' => 'application/x-compress', - 'xhtml' => 'application/xhtml+xml', - 'xht' => 'application/xhtml+xml', - 'zip' => ['application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'], - 'rar' => ['application/x-rar', 'application/rar', 'application/x-rar-compressed'], - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mpga' => 'audio/mpeg', - 'mp2' => 'audio/mpeg', - 'mp3' => ['audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'], - 'aif' => ['audio/x-aiff', 'audio/aiff'], - 'aiff' => ['audio/x-aiff', 'audio/aiff'], - 'aifc' => 'audio/x-aiff', - 'ram' => 'audio/x-pn-realaudio', - 'rm' => 'audio/x-pn-realaudio', - 'rpm' => 'audio/x-pn-realaudio-plugin', - 'ra' => 'audio/x-realaudio', - 'rv' => 'video/vnd.rn-realvideo', - 'wav' => ['audio/x-wav', 'audio/wave', 'audio/wav'], - 'bmp' => ['image/bmp', 'image/x-bmp', 'image/x-bitmap', 'image/x-xbitmap', 'image/x-win-bitmap', 'image/x-windows-bmp', 'image/ms-bmp', 'image/x-ms-bmp', 'application/bmp', 'application/x-bmp', 'application/x-win-bitmap'], - 'gif' => 'image/gif', - 'jpeg' => ['image/jpeg', 'image/pjpeg'], - 'jpg' => ['image/jpeg', 'image/pjpeg'], - 'jpe' => ['image/jpeg', 'image/pjpeg'], - 'png' => ['image/png', 'image/x-png'], - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'css' => ['text/css', 'text/plain'], - 'html' => ['text/html', 'text/plain'], - 'htm' => ['text/html', 'text/plain'], - 'shtml' => ['text/html', 'text/plain'], - 'txt' => 'text/plain', - 'text' => 'text/plain', - 'log' => ['text/plain', 'text/x-log'], - 'rtx' => 'text/richtext', - 'rtf' => 'text/rtf', - 'xml' => ['application/xml', 'text/xml', 'text/plain'], - 'xsl' => ['application/xml', 'text/xsl', 'text/xml'], - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - 'avi' => ['video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'], - 'movie' => 'video/x-sgi-movie', - 'doc' => ['application/msword', 'application/vnd.ms-office'], - 'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword', 'application/x-zip'], - 'dot' => ['application/msword', 'application/vnd.ms-office'], - 'dotx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword'], - 'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword', 'application/x-zip'], - 'word' => ['application/msword', 'application/octet-stream'], - 'xl' => 'application/excel', - 'eml' => 'message/rfc822', - 'json' => ['application/json', 'text/json'], - 'pem' => ['application/x-x509-user-cert', 'application/x-x509-ca-cert', 'application/x-pem-file', 'application/pkix-cert'], - 'crt' => ['application/x-x509-user-cert', 'application/x-x509-ca-cert', 'application/pkix-cert'], - 'p10' => ['application/x-pkcs10', 'application/pkcs10'], - 'p12' => 'application/x-pkcs12', - 'p7a' => 'application/x-pkcs7-signature', - 'p7c' => ['application/pkcs7-mime', 'application/x-pkcs7-mime'], - 'p7m' => ['application/pkcs7-mime', 'application/x-pkcs7-mime'], - 'p7r' => 'application/x-pkcs7-certreqresp', - 'p7s' => 'application/pkcs7-signature', - 'crl' => ['application/pkix-crl', 'application/pkcs-crl'], - 'der' => 'application/x-x509-ca-cert', - 'kdb' => 'application/octet-stream', - 'pgp' => 'application/pgp', - 'gpg' => 'application/gpg-keys', - 'sst' => 'application/octet-stream', - 'csr' => 'application/octet-stream', - 'rsa' => 'application/x-pkcs7', - 'cer' => ['application/pkix-cert', 'application/x-x509-ca-cert'], - '3g2' => 'video/3gpp2', - '3gp' => ['video/3gp', 'video/3gpp'], - 'mp4' => 'video/mp4', - 'm4a' => 'audio/x-m4a', - 'f4v' => 'video/mp4', - 'webm' => 'video/webm', - 'webp' => 'image/webp', - 'aac' => 'audio/x-acc', - 'm4u' => 'application/vnd.mpegurl', - 'm3u' => 'text/plain', - 'xspf' => 'application/xspf+xml', - 'vlc' => 'application/videolan', - 'wmv' => ['video/x-ms-wmv', 'video/x-ms-asf'], - 'au' => 'audio/x-au', - 'ac3' => 'audio/ac3', - 'flac' => 'audio/x-flac', - 'ogg' => 'audio/ogg', - 'kmz' => ['application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'], - 'kml' => ['application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml'], - 'ics' => 'text/calendar', - 'ical' => 'text/calendar', - 'zsh' => 'text/x-scriptzsh', - '7zip' => ['application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'], - 'cdr' => ['application/cdr', 'application/coreldraw', 'application/x-cdr', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'], - 'wma' => ['audio/x-ms-wma', 'video/x-ms-asf'], - 'jar' => ['application/java-archive', 'application/x-java-application', 'application/x-jar', 'application/x-compressed'], - 'svg' => ['image/svg+xml', 'application/xml', 'text/xml'], - 'vcf' => 'text/x-vcard', - 'srt' => ['text/srt', 'text/plain'], - 'vtt' => ['text/vtt', 'text/plain'], - 'ico' => ['image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon'], -]; diff --git a/application/extra/view/admin.content.html b/application/extra/view/admin.content.html deleted file mode 100644 index 305b07d45..000000000 --- a/application/extra/view/admin.content.html +++ /dev/null @@ -1,22 +0,0 @@ -
    - {block name="style"}{/block} - {if isset($title)} -
    -
    {$title}
    - {block name="button"}{/block} -
    - {/if} -
    - {if isset($alert)} - - {/if} - {block name="content"}{/block} -
    - {block name='script'}{/block} -
    \ No newline at end of file diff --git a/application/extra/view/admin.main.html b/application/extra/view/admin.main.html deleted file mode 100644 index b9b5ea87f..000000000 --- a/application/extra/view/admin.main.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - {block name="title"}{$title|default=''} {if !empty($title)}-{/if} {:sysconf('site_name')}{/block} - - - - {block name="style"}{/block} - - - - - {block name="bodyTag"} - - {block name="body"}{/block} - {block name="script"}{/block} - - {/block} - \ No newline at end of file diff --git a/application/extra/view/admin.main.left.html b/application/extra/view/admin.main.left.html deleted file mode 100644 index 386d36b57..000000000 --- a/application/extra/view/admin.main.left.html +++ /dev/null @@ -1,52 +0,0 @@ -
    - -
    \ No newline at end of file diff --git a/application/extra/view/admin.main.top.html b/application/extra/view/admin.main.top.html deleted file mode 100644 index 87259a604..000000000 --- a/application/extra/view/admin.main.top.html +++ /dev/null @@ -1,65 +0,0 @@ -
    -
    -
    - - {volist name='menus' id='pmenu'} - {empty name='pmenu.sub'} - - {notempty name='$pmenu.icon'}{/notempty} {$pmenu.title} - - {else} - - {notempty name='$pmenu.icon'}{/notempty} {$pmenu.title} - - {/empty} - {/volist} - -
    - - - - - -
    -
    -
    -
    \ No newline at end of file diff --git a/application/index/controller/Index.php b/application/index/controller/Index.php index 579ae6110..ca70a9d5b 100644 --- a/application/index/controller/Index.php +++ b/application/index/controller/Index.php @@ -1,7 +1,7 @@ - * @date 2017/04/05 10:38 */ -class Index extends Controller { +class Index extends Controller +{ - /** - * 网站入口 - */ - public function index() { - $this->redirect('@admin'); + public function index() + { + $this->redirect('@admin/login'); } - } diff --git a/application/index/controller/Wap.php b/application/index/controller/Wap.php deleted file mode 100644 index 188a76b78..000000000 --- a/application/index/controller/Wap.php +++ /dev/null @@ -1,99 +0,0 @@ -oAuth(false)); // 仅获取用户openid - dump($this->oAuth()); // 获取用户详情信息 - dump($this->fansinfo); // 打 - } - - /** - * 微信二维码支付DEMO - * @return \think\response\Json|\think\response\View - */ - public function payqrc() { - switch ($this->request->get('action')) { - case 'payqrc': - $pay = &load_wechat('pay'); - $order_no = session('pay-test-order-no'); - if (empty($order_no)) { - $order_no = DataService::createSequence(10, 'wechat-pay-test'); - session('pay-test-order-no', $order_no); - } - if (PayService::isPay($order_no)) { - return json(['code' => 2, 'order_no' => $order_no]); - } - $url = PayService::createWechatPayQrc($pay, $order_no, 1, '微信扫码支付测试!'); - if ($url !== false) { - return json(['code' => 1, 'url' => $url, 'order_no' => $order_no]); - } - $this->error("生成支付二维码失败,{$pay->errMsg}[{$pay->errCode}]"); - break; - case 'reset': - session('pay-test-order-no', null); - break; - default: - return view(); - } - } - - /** - * 微信JSAPI支付DEMO - * @return \think\response\Json|\think\response\View - */ - public function payjs() { - $this->openid = $this->oAuth(false); - switch ($this->request->get('action')) { - case 'options': - $order_no = session('pay-test-order-no'); - if (empty($order_no)) { - $order_no = DataService::createSequence(10, 'wechat-pay-test'); - session('pay-test-order-no', $order_no); - } - if (PayService::isPay($order_no)) { - return json(['code' => 2, 'order_no' => $order_no]); - } - $pay = &load_wechat('pay'); - $options = PayService::createWechatPayJsPicker($pay, $this->openid, $order_no, 1, 'JSAPI支付测试'); - if ($options === false) { - $options = ['code' => 3, 'msg' => "创建支付失败,{$pay->errMsg}[$pay->errCode]"]; - } - $options['order_no'] = $order_no; - return json($options); - case 'reset': - session('pay-test-order-no', null); - break; - default: - return view(); - } - } - -} diff --git a/application/index/view/wap.payjs.html b/application/index/view/wap.payjs.html deleted file mode 100644 index 93d771f4d..000000000 --- a/application/index/view/wap.payjs.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - JSAPI支付DEMO - - - - - - -
    -

    JSAPI支付DEMO

    -
    -

    金额: 0.01元

    -
    -

    -
    -
    -
    - -
    -
    - 扫码支付测试 -
    - - - - - - - \ No newline at end of file diff --git a/application/index/view/wap.payqrc.html b/application/index/view/wap.payqrc.html deleted file mode 100644 index 9cdad91c2..000000000 --- a/application/index/view/wap.payqrc.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - 扫码支付DEMO - - - - - - -
    -

    扫码支付DEMO

    -
    -

    -

    请使用其它手机微信扫描二维码。

    -
    -

    金额: 0.01元

    -
    -

    -
    - - JSAPI支付测试 -
    - - - - - - \ No newline at end of file diff --git a/application/extra/queue.php b/application/middleware.php similarity index 80% rename from application/extra/queue.php rename to application/middleware.php index fcda45c1e..1dabca938 100644 --- a/application/extra/queue.php +++ b/application/middleware.php @@ -1,7 +1,7 @@ 'Sync' -]; + // 系统权限访问管理 + \app\admin\middleware\Auth::class, +]; \ No newline at end of file diff --git a/application/route.php b/application/route.php deleted file mode 100644 index a79592e8a..000000000 --- a/application/route.php +++ /dev/null @@ -1,51 +0,0 @@ - function() { - return json(['code' => 0, 'msg' => '测试环境禁修改系统配置操作!']); - }, - 'admin/config/file' => function() { - return json(['code' => 0, 'msg' => '测试环境禁修改文件配置操作!']); - }, - 'admin/menu/add' => function() { - return json(['code' => 0, 'msg' => '测试环境禁添加菜单操作!']); - }, - 'admin/menu/edit' => function() { - return json(['code' => 0, 'msg' => '测试环境禁编辑菜单操作!']); - }, - 'admin/menu/forbid' => function() { - return json(['code' => 0, 'msg' => '测试环境禁止禁用菜单操作!']); - }, - 'admin/menu/del' => function() { - return json(['code' => 0, 'msg' => '测试环境禁止删除菜单操作!']); - }, - 'wechat/config/index' => function() { - return json(['code' => 0, 'msg' => '测试环境禁止修改微信配置操作!']); - }, - 'wechat/config/pay' => function() { - return json(['code' => 0, 'msg' => '测试环境禁止修改微信支付操作!']); - }, - 'wechat/menu/edit' => function() { - return json(['code' => 0, 'msg' => '测试环境禁止修改微信菜单操作!']); - } -]); -think\Route::get([ - 'wechat/menu/cancel' => function() { - return json(['code' => 0, 'msg' => '测试环境禁止删除微信菜单操作!']); - } -]); - -return []; diff --git a/application/store/controller/Express.php b/application/store/controller/Express.php new file mode 100644 index 000000000..f4b16b38d --- /dev/null +++ b/application/store/controller/Express.php @@ -0,0 +1,126 @@ + + * @date 2017/03/27 14:43 + */ +class Express extends BasicAdmin +{ + + /** + * 定义当前操作表名 + * @var string + */ + public $table = 'StoreExpress'; + + /** + * 快递公司列表 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function index() + { + $this->title = '快递公司管理'; + list($get, $db) = [$this->request->get(), Db::name($this->table)]; + foreach (['express_title', 'express_code'] as $field) { + (isset($get[$field]) && $get[$field] !== '') && $db->whereLike($field, "%{$get[$field]}%"); + } + if (isset($get['date']) && $get['date'] !== '') { + list($start, $end) = explode(' - ', $get['date']); + $db->whereBetween('create_at', ["{$start} 00:00:00", "{$end} 23:59:59"]); + } + return parent::_list($db->where(['is_deleted' => '0'])->order('status desc,sort asc,id desc')); + } + + /** + * 添加快递公司 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function add() + { + $this->title = '添加快递公司'; + return $this->_form($this->table, 'form'); + } + + /** + * 编辑快递公司 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function edit() + { + $this->title = '编辑快递公司'; + return $this->_form($this->table, 'form'); + } + + /** + * 删除快递公司 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function del() + { + if (DataService::update($this->table)) { + $this->success("快递公司删除成功!", ''); + } + $this->error("快递公司删除失败,请稍候再试!"); + } + + /** + * 快递公司禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function forbid() + { + if (DataService::update($this->table)) { + $this->success("快递公司禁用成功!", ''); + } + $this->error("快递公司禁用失败,请稍候再试!"); + } + + /** + * 快递公司禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function resume() + { + if (DataService::update($this->table)) { + $this->success("快递公司启用成功!", ''); + } + $this->error("快递公司启用失败,请稍候再试!"); + } + +} diff --git a/application/store/controller/Goods.php b/application/store/controller/Goods.php new file mode 100644 index 000000000..fb8d9b7c4 --- /dev/null +++ b/application/store/controller/Goods.php @@ -0,0 +1,304 @@ + + * @date 2017/03/27 14:43 + */ +class Goods extends BasicAdmin +{ + + /** + * 定义当前操作表名 + * @var string + */ + public $table = 'StoreGoods'; + + /** + * 普通商品 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function index() + { + $this->title = '商品管理'; + $get = $this->request->get(); + $db = Db::name($this->table)->where(['is_deleted' => '0']); + if (isset($get['tags_id']) && $get['tags_id'] !== '') { + $db->whereLike('tags_id', "%,{$get['tags_id']},%"); + } + if (isset($get['goods_title']) && $get['goods_title'] !== '') { + $db->whereLike('goods_title', "%{$get['goods_title']}%"); + } + foreach (['cate_id', 'brand_id'] as $field) { + (isset($get[$field]) && $get[$field] !== '') && $db->where($field, $get[$field]); + } + if (isset($get['create_at']) && $get['create_at'] !== '') { + list($start, $end) = explode(' - ', $get['create_at']); + $db->whereBetween('create_at', ["{$start} 00:00:00", "{$end} 23:59:59"]); + } + return parent::_list($db->order('status desc,sort asc,id desc')); + } + + /** + * 商城数据处理 + * @param array $data + */ + protected function _data_filter(&$data) + { + $result = GoodsService::buildGoodsList($data); + $this->assign([ + 'brands' => $result['brand'], + 'cates' => ToolsService::arr2table($result['cate']), + ]); + } + + /** + * 添加商品 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception + */ + public function add() + { + if ($this->request->isGet()) { + $this->title = '添加商品'; + $this->_form_assign(); + return $this->_form($this->table, 'form'); + } + try { + $data = $this->_form_build_data(); + Db::transaction(function () use ($data) { + $goodsID = Db::name($this->table)->insertGetId($data['main']); + foreach ($data['list'] as &$vo) { + $vo['goods_id'] = $goodsID; + } + Db::name('StoreGoodsList')->insertAll($data['list']); + }); + } catch (HttpResponseException $exception) { + return $exception->getResponse(); + } catch (\Exception $e) { + $this->error('商品添加失败,请稍候再试!'); + } + list($base, $spm, $url) = [url('@admin'), $this->request->get('spm'), url('store/goods/index')]; + $this->success('添加商品成功!', "{$base}#{$url}?spm={$spm}"); + } + + /** + * 编辑商品 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function edit() + { + if (!$this->request->isPost()) { + $goods_id = $this->request->get('id'); + $goods = Db::name($this->table)->where(['id' => $goods_id, 'is_deleted' => '0'])->find(); + empty($goods) && $this->error('需要编辑的商品不存在!'); + $goods['list'] = Db::name('StoreGoodsList')->where(['goods_id' => $goods_id, 'is_deleted' => '0'])->select(); + $this->_form_assign(); + return $this->fetch('form', ['vo' => $goods, 'title' => '编辑商品']); + } + try { + $data = $this->_form_build_data(); + $goods_id = $this->request->post('id'); + $goods = Db::name($this->table)->where(['id' => $goods_id, 'is_deleted' => '0'])->find(); + empty($goods) && $this->error('商品编辑失败,请稍候再试!'); + foreach ($data['list'] as &$vo) { + $vo['goods_id'] = $goods_id; + } + Db::transaction(function () use ($data, $goods_id, $goods) { + // 更新商品主表 + $where = ['id' => $goods_id, 'is_deleted' => '0']; + Db::name('StoreGoods')->where($where)->update(array_merge($goods, $data['main'])); + // 更新商品详细 + Db::name('StoreGoodsList')->where(['goods_id' => $goods_id])->delete(); + Db::name('StoreGoodsList')->insertAll($data['list']); + }); + } catch (HttpResponseException $exception) { + return $exception->getResponse(); + } catch (\Exception $e) { + $this->error('商品编辑失败,请稍候再试!' . $e->getMessage()); + } + list($base, $spm, $url) = [url('@admin'), $this->request->get('spm'), url('store/goods/index')]; + $this->success('商品编辑成功!', "{$base}#{$url}?spm={$spm}"); + } + + /** + * 表单数据处理 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + protected function _form_assign() + { + list($where, $order) = [['status' => '1', 'is_deleted' => '0'], 'sort asc,id desc']; + $specs = (array)Db::name('StoreGoodsSpec')->where($where)->order($order)->select(); + $brands = (array)Db::name('StoreGoodsBrand')->where($where)->order($order)->select(); + $cates = (array)Db::name('StoreGoodsCate')->where($where)->order($order)->select(); + // 所有的商品信息 + $where = ['is_deleted' => '0', 'status' => '1']; + $goodsListField = 'goods_id,goods_spec,goods_stock,goods_sale'; + $goods = Db::name('StoreGoods')->field('id,goods_title')->where($where)->select(); + $list = Db::name('StoreGoodsList')->field($goodsListField)->where($where)->select(); + foreach ($goods as $k => $g) { + $goods[$k]['list'] = []; + foreach ($list as $v) { + ($g['id'] === $v['goods_id']) && $goods[$k]['list'][] = $v; + } + } + array_unshift($specs, ['spec_title' => ' - 不使用规格模板 -', 'spec_param' => '[]', 'id' => '0']); + $this->assign([ + 'specs' => $specs, + 'cates' => ToolsService::arr2table($cates), + 'brands' => $brands, + 'all' => $goods, + ]); + } + + /** + * 读取POST表单数据 + * @return array + */ + protected function _form_build_data() + { + list($main, $list, $post, $verify) = [[], [], $this->request->post(), false]; + empty($post['goods_logo']) && $this->error('商品LOGO不能为空,请上传后再提交数据!'); + // 商品主数据组装 + $main['cate_id'] = $this->request->post('cate_id', '0'); + $main['spec_id'] = $this->request->post('spec_id', '0'); + $main['brand_id'] = $this->request->post('brand_id', '0'); + $main['goods_logo'] = $this->request->post('goods_logo', ''); + $main['goods_title'] = $this->request->post('goods_title', ''); + $main['goods_video'] = $this->request->post('goods_video', ''); + $main['goods_image'] = $this->request->post('goods_image', ''); + $main['goods_desc'] = $this->request->post('goods_desc', '', null); + $main['goods_content'] = $this->request->post('goods_content', ''); + $main['tags_id'] = ',' . join(',', isset($post['tags_id']) ? $post['tags_id'] : []) . ','; + // 商品从数据组装 + if (!empty($post['goods_spec'])) { + foreach ($post['goods_spec'] as $key => $value) { + $goods = []; + $goods['goods_spec'] = $value; + $goods['market_price'] = $post['market_price'][$key]; + $goods['selling_price'] = $post['selling_price'][$key]; + $goods['status'] = intval(!empty($post['spec_status'][$key])); + !empty($goods['status']) && $verify = true; + $list[] = $goods; + } + } else { + $this->error('没有商品规格或套餐信息哦!'); + } + !$verify && $this->error('没有设置有效的商品规格!'); + return ['main' => $main, 'list' => $list]; + } + + /** + * 商品库存信息更新 + * @return string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function stock() + { + if (!$this->request->post()) { + $goods_id = $this->request->get('id'); + $goods = Db::name('StoreGoods')->where(['id' => $goods_id, 'is_deleted' => '0'])->find(); + empty($goods) && $this->error('该商品无法操作入库操作!'); + $where = ['goods_id' => $goods_id, 'status' => '1', 'is_deleted' => '0']; + $goods['list'] = Db::name('StoreGoodsList')->where($where)->select(); + return $this->fetch('', ['vo' => $goods]); + } + // 入库保存 + $goods_id = $this->request->post('id'); + list($post, $data) = [$this->request->post(), []]; + foreach ($post['spec'] as $key => $spec) { + if ($post['stock'][$key] > 0) { + $data[] = [ + 'goods_stock' => $post['stock'][$key], + 'stock_desc' => $this->request->post('desc'), + 'goods_spec' => $spec, 'goods_id' => $goods_id, + ]; + } + } + empty($data) && $this->error('无需入库的数据哦!'); + if (Db::name('StoreGoodsStock')->insertAll($data) !== false) { + GoodsService::syncGoodsStock($goods_id); + $this->success('商品入库成功!', ''); + } + $this->error('商品入库失败,请稍候再试!'); + } + + /** + * 删除商品 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function del() + { + if (DataService::update($this->table)) { + $this->success("商品删除成功!", ''); + } + $this->error("商品删除失败,请稍候再试!"); + } + + /** + * 商品禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function forbid() + { + if (DataService::update($this->table)) { + $this->success("商品下架成功!", ''); + } + $this->error("商品下架失败,请稍候再试!"); + } + + /** + * 商品禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function resume() + { + if (DataService::update($this->table)) { + $this->success("商品上架成功!", ''); + } + $this->error("商品上架失败,请稍候再试!"); + } + +} diff --git a/application/store/controller/GoodsBrand.php b/application/store/controller/GoodsBrand.php new file mode 100644 index 000000000..54e39a05e --- /dev/null +++ b/application/store/controller/GoodsBrand.php @@ -0,0 +1,151 @@ + + * @date 2017/03/27 14:43 + */ +class GoodsBrand extends BasicAdmin +{ + + /** + * 定义当前操作表名 + * @var string + */ + public $table = 'StoreGoodsBrand'; + + /** + * 品牌列表 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function index() + { + $this->title = '品牌管理'; + $get = $this->request->get(); + $db = Db::name($this->table)->where(['is_deleted' => '0']); + if (isset($get['brand_title']) && $get['brand_title'] !== '') { + $db->whereLike('brand_title', "%{$get['brand_title']}%"); + } + if (isset($get['create_at']) && $get['create_at'] !== '') { + list($start, $end) = explode(' - ', $get['create_at']); + $db->whereBetween('create_at', ["{$start} 00:00:00", "{$end} 23:59:59"]); + } + return parent::_list($db->order('sort asc,id desc')); + } + + /** + * 添加品牌 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception + */ + public function add() + { + $this->title = '添加品牌'; + return $this->_form($this->table, 'form'); + } + + /** + * 编辑品牌 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception + */ + public function edit() + { + $this->title = '编辑品牌'; + return $this->_form($this->table, 'form'); + } + + /** + * 表单提交数据处理 + * @param array $data + */ + protected function _form_filter($data) + { + if ($this->request->isPost()) { + empty($data['brand_logo']) && $this->error('请上传品牌Logo图片'); + empty($data['brand_cover']) && $this->error('请上传品牌封面图片'); + } + } + + /** + * 添加成功回跳处理 + * @param bool $result + */ + protected function _form_result($result) + { + if ($result !== false) { + list($base, $spm, $url) = [url('@admin'), $this->request->get('spm'), url('store/goods_brand/index')]; + $this->success('数据保存成功!', "{$base}#{$url}?spm={$spm}"); + } + } + + /** + * 删除品牌 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function del() + { + if (DataService::update($this->table)) { + $this->success("品牌删除成功!", ''); + } + $this->error("品牌删除失败,请稍候再试!"); + } + + /** + * 品牌禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function forbid() + { + if (DataService::update($this->table)) { + $this->success("品牌禁用成功!", ''); + } + $this->error("品牌禁用失败,请稍候再试!"); + } + + /** + * 品牌签禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function resume() + { + if (DataService::update($this->table)) { + $this->success("品牌启用成功!", ''); + } + $this->error("品牌启用失败,请稍候再试!"); + } + +} diff --git a/application/store/controller/GoodsCate.php b/application/store/controller/GoodsCate.php new file mode 100644 index 000000000..e168d4f87 --- /dev/null +++ b/application/store/controller/GoodsCate.php @@ -0,0 +1,157 @@ + + * @date 2017/03/27 14:43 + */ +class GoodsCate extends BasicAdmin +{ + + /** + * 定义当前操作表名 + * @var string + */ + public $table = 'StoreGoodsCate'; + + /** + * 商品分类列表 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function index() + { + $this->title = '商品分类'; + $db = Db::name($this->table)->where(['is_deleted' => '0']); + return parent::_list($db->order('sort asc,id asc'), false); + } + + /** + * 列表数据处理 + * @param array $data + */ + protected function _index_data_filter(&$data) + { + foreach ($data as &$vo) { + $vo['ids'] = join(',', ToolsService::getArrSubIds($data, $vo['id'])); + } + $data = ToolsService::arr2table($data); + } + + /** + * 添加菜单 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function add() + { + return $this->_form($this->table, 'form'); + } + + /** + * 编辑菜单 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function edit() + { + return $this->_form($this->table, 'form'); + } + + /** + * 表单数据前缀方法 + * @param array $vo + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + protected function _form_filter(&$vo) + { + if ($this->request->isGet()) { + // 读取上级分类 + $where = ['status' => '1', 'is_deleted' => '0']; + $_cates = (array)Db::name($this->table)->where($where)->order('sort desc,id desc')->select(); + array_unshift($_cates, ['id' => 0, 'pid' => -1, 'cate_title' => '--- 顶级分类 ---']); + $cates = ToolsService::arr2table($_cates); + foreach ($cates as $key => &$cate) { + if (isset($vo['pid'])) { + $path = "-{$vo['pid']}-{$vo['id']}"; + if ($vo['pid'] !== '' && (stripos("{$cate['path']}-", "{$path}-") !== false || $cate['path'] === $path)) { + unset($cates[$key]); + } + } + } + $this->assign('cates', $cates); + } + } + + /** + * 删除商品分类 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function del() + { + if (DataService::update($this->table)) { + $this->success("商品分类删除成功!", ''); + } + $this->error("商品分类删除失败,请稍候再试!"); + } + + /** + * 商品分类禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function forbid() + { + if (DataService::update($this->table)) { + $this->success("商品分类禁用成功!", ''); + } + $this->error("商品分类禁用失败,请稍候再试!"); + } + + /** + * 商品分类禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function resume() + { + if (DataService::update($this->table)) { + $this->success("商品分类启用成功!", ''); + } + $this->error("商品分类启用失败,请稍候再试!"); + } + +} diff --git a/application/store/controller/GoodsSpec.php b/application/store/controller/GoodsSpec.php new file mode 100644 index 000000000..73d999e9f --- /dev/null +++ b/application/store/controller/GoodsSpec.php @@ -0,0 +1,157 @@ + + * @date 2017/03/27 14:43 + */ +class GoodsSpec extends BasicAdmin +{ + + /** + * 定义当前操作表名 + * @var string + */ + public $table = 'StoreGoodsSpec'; + + /** + * 商品列表 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function index() + { + $this->title = '规格管理(请勿随意修改或删除)'; + $get = $this->request->get(); + $db = Db::name($this->table)->where(['is_deleted' => '0']); + if (isset($get['spec_title']) && $get['spec_title'] !== '') { + $db->whereLike('spec_title', "%{$get['spec_title']}%"); + } + if (isset($get['date']) && $get['date'] !== '') { + list($start, $end) = explode(' - ', $get['date']); + $db->whereBetween('create_at', ["{$start} 00:00:00", "{$end} 23:59:59"]); + } + return parent::_list($db->order('sort asc,id desc')); + } + + /** + * 列表数据处理 + * @param array $data + */ + protected function _index_data_filter(&$data) + { + foreach ($data as &$vo) { + $vo['spec_param'] = json_decode($vo['spec_param'], true); + $vo['spec_param'] = is_array($vo['spec_param']) ? $vo['spec_param'] : []; + } + } + + /** + * 添加商品 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception + */ + public function add() + { + $this->title = '添加规格'; + return $this->_form($this->table, 'form'); + } + + /** + * 编辑商品 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception + */ + public function edit() + { + $this->title = '编辑规格'; + return $this->_form($this->table, 'form'); + } + + /** + * 表单数据处理 + * @param array $vo + */ + protected function _form_filter(&$vo) + { + if ($this->request->isPost()) { + $param = json_decode($this->request->post('spec_param', '[]', null), true); + foreach ($param as &$v) { + $count = 1; + while ($count) { + $v['value'] = trim(str_replace([' ', '_', ',', ',', ';', ';'], ' ', $v['value'], $count)); + } + } + $vo['spec_param'] = json_encode($param, JSON_UNESCAPED_UNICODE); + } + } + + /** + * 删除商品规格 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function del() + { + if (DataService::update($this->table)) { + $this->success("商品规格删除成功!", ''); + } + $this->error("商品规格删除失败,请稍候再试!"); + } + + /** + * 商品规格禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function forbid() + { + if (DataService::update($this->table)) { + $this->success("商品规格禁用成功!", ''); + } + $this->error("商品规格禁用失败,请稍候再试!"); + } + + /** + * 商品规格禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function resume() + { + if (DataService::update($this->table)) { + $this->success("商品规格启用成功!", ''); + } + $this->error("商品规格启用失败,请稍候再试!"); + } + +} diff --git a/application/store/controller/Order.php b/application/store/controller/Order.php new file mode 100644 index 000000000..c484bace3 --- /dev/null +++ b/application/store/controller/Order.php @@ -0,0 +1,157 @@ + + * @date 2017/03/27 14:43 + */ +class Order extends BasicAdmin +{ + + /** + * 定义当前操作表名 + * @var string + */ + public $table = 'StoreOrder'; + + /** + * 订单列表 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function index() + { + $this->title = '订单管理'; + $db = Db::name($this->table); + $get = $this->request->get(); + // 会员信息查询过滤 + $memberWhere = []; + foreach (['phone', 'nickname'] as $field) { + if (isset($get[$field]) && $get[$field] !== '') { + $memberWhere[] = [$field, 'like', "%{$get[$field]}%"]; + } + } + if (!empty($memberWhere)) { + $memberWhere['status'] = '1'; + $sql = Db::name('Member')->field('id')->where($memberWhere)->buildSql(true); + $db->where("mid in {$sql}"); + } + // =============== 商品信息查询过滤 =============== + $goodsWhere = []; + foreach (['goods_title'] as $field) { + if (isset($get[$field]) && $get[$field] !== '') { + $goodsWhere[] = [$field, 'like', "%{$get[$field]}%"]; + } + } + if (!empty($goodsWhere)) { + $sql = Db::name('StoreOrderList')->field('order_no')->where($goodsWhere)->buildSql(true); + $db->where("order_no in {$sql}"); + } + // =============== 收货地址过滤 =============== + $expressWhere = []; + if (isset($get['express_title']) && $get['express_title'] !== '') { + $expressWhere[] = ['send_company_title|company_title', 'like', "%{$get['express_title']}%"]; + } + foreach (['send_no', 'username', 'phone', 'province', 'city', 'area', 'address'] as $field) { + if (isset($get[$field]) && $get[$field] !== '') { + $expressWhere[] = [$field, 'like', "%{$get[$field]}%"]; + } + } + if (isset($get['send_status']) && $get['send_status'] !== '') { + $expressWhere[] = empty($get['send_status']) ? ['send_no', 'eq', ''] : ['send_no', 'neq', '']; + } + if (!empty($expressWhere)) { + $sql = Db::name('StoreOrderExpress')->field('order_no')->where($expressWhere)->buildSql(true); + $db->where("order_no in {$sql}"); + } + // =============== 主订单过滤 =============== + foreach (['order_no', 'desc'] as $field) { + (isset($get[$field]) && $get[$field] !== '') && $db->whereLike($field, "%{$get[$field]}%"); + } + (isset($get['status']) && $get['status'] !== '') && $db->where('status', $get['status']); + // 订单是否包邮状态检索 + if (isset($get['express_zero']) && $get['express_zero'] !== '') { + empty($get['express_zero']) ? $db->where('freight_price', '>', '0') : $db->where('freight_price', '0'); + } + // 订单时间过滤 + foreach (['create_at', 'pay_at'] as $field) { + if (isset($get[$field]) && $get[$field] !== '') { + list($start, $end) = explode(' - ', $get[$field]); + $db->whereBetween($field, ["{$start} 00:00:00", "{$end} 23:59:59"]); + } + } + return parent::_list($db); + } + + /** + * 订单列表数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + protected function _data_filter(&$data) + { + OrderService::buildOrderList($data); + } + + /** + * 订单地址修改 + * @return string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception + */ + public function address() + { + $order_no = $this->request->get('order_no'); + if ($this->request->isGet()) { + $order = Db::name('StoreOrder')->where(['order_no' => $order_no])->find(); + empty($order) && $this->error('该订单无法进行地址修改,订单数据不存在!'); + $orderExpress = Db::name('StoreOrderExpress')->where(['order_no' => $order_no])->find(); + empty($orderExpress) && $this->error('该订单无法进行地址修改!'); + return $this->fetch('', $orderExpress); + } + $data = [ + 'order_no' => $order_no, + 'username' => $this->request->post('express_username'), + 'phone' => $this->request->post('express_phone'), + 'province' => $this->request->post('form_express_province'), + 'city' => $this->request->post('form_express_city'), + 'area' => $this->request->post('form_express_area'), + 'address' => $this->request->post('express_address'), + 'desc' => $this->request->post('express_desc'), + ]; + if (DataService::save('StoreOrderExpress', $data, 'order_no')) { + $this->success('收货地址修改成功!', ''); + } + $this->error('收货地址修改失败,请稍候再试!'); + } + + +} diff --git a/application/store/controller/wechat/Demo.php b/application/store/controller/wechat/Demo.php new file mode 100644 index 000000000..f11270a04 --- /dev/null +++ b/application/store/controller/wechat/Demo.php @@ -0,0 +1,204 @@ +createParamsForRuleQrc('8888888'); + return $this->createQrc($result); + } + + /** + * 微信扫码支付模式一通知处理 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function scanOneNotify() + { + $wechat = new Pay(config('wechat.')); + $notify = $wechat->getNotify(); + p('======= 来自扫码支付1的数据 ======'); + p($notify); + // 产品ID @todo 你的业务,并实现下面的统一下单操作 + $product_id = $notify['product_id']; + // 微信统一下单处理 + $options = [ + 'body' => '测试商品,产品ID:' . $product_id, + 'out_trade_no' => time(), + 'total_fee' => '1', + 'trade_type' => 'NATIVE', + 'notify_url' => url('@wx-demo-notify', '', true, true), + 'spbill_create_ip' => request()->ip(), + ]; + $order = $wechat->createOrder($options); + p('======= 来自扫码支付1统一下单结果 ======'); + p($order); + // 回复XML文本 + $result = [ + 'return_code' => 'SUCCESS', + 'return_msg' => '处理成功', + 'appid' => $notify['appid'], + 'mch_id' => $notify['mch_id'], + 'nonce_str' => \WeChat\Contracts\Tools::createNoncestr(), + 'prepay_id' => $order['prepay_id'], + 'result_code' => 'SUCCESS', + ]; + $result['sign'] = $wechat->getPaySign($result); + p('======= 来自扫码支付1返回的结果 ======'); + p($result); + return \WeChat\Contracts\Tools::arr2xml($result); + } + + /** + * 扫码支付模式二测试二维码 + * @return \think\Response + * @throws \Endroid\QrCode\Exceptions\ImageFunctionFailedException + * @throws \Endroid\QrCode\Exceptions\ImageFunctionUnknownException + * @throws \Endroid\QrCode\Exceptions\ImageTypeInvalidException + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function scanQrc() + { + + $wechat = new Pay(config('wechat.')); + $options = [ + 'body' => '测试商品', + 'out_trade_no' => time(), + 'total_fee' => '1', + 'trade_type' => 'NATIVE', + 'notify_url' => url('@wx-demo-notify', '', true, true), + 'spbill_create_ip' => request()->ip(), + ]; + // 生成预支付码 + $result = $wechat->createOrder($options); + return $this->createQrc($result['code_url']); + } + + + /** + * 公众号JSAPI支付二维码 + * @return \think\Response + * @throws \Endroid\QrCode\Exceptions\ImageFunctionFailedException + * @throws \Endroid\QrCode\Exceptions\ImageFunctionUnknownException + * @throws \Endroid\QrCode\Exceptions\ImageTypeInvalidException + */ + public function jsapiQrc() + { + $url = url('@wx-demo-jsapi', '', true, true); + return $this->createQrc($url); + } + + /** + * 公众号JSAPI支付测试 + * @link wx-demo-jsapi + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function jsapi() + { + $wechat = new Pay(config('wechat.')); + $openid = WechatService::webOauth(request()->url(true), 0)['openid']; + $options = [ + 'body' => '测试商品', + 'out_trade_no' => time(), + 'total_fee' => '1', + 'openid' => $openid, + 'trade_type' => 'JSAPI', + 'notify_url' => url('@wx-demo-notify', '', true, true), + 'spbill_create_ip' => request()->ip(), + ]; + // 生成预支付码 + $result = $wechat->createOrder($options); + // 创建JSAPI参数签名 + $options = $wechat->createParamsForJsApi($result['prepay_id']); + $optionJSON = json_encode($options, JSON_UNESCAPED_UNICODE); + // JSSDK 签名配置 + $configJSON = json_encode(WechatService::webJsSDK(), JSON_UNESCAPED_UNICODE); + + echo '
    ';
    +        echo "当前用户OPENID: {$openid}";
    +        echo "\n--- 创建预支付码 ---\n";
    +        var_export($result);
    +        echo '
    '; + + echo '
    ';
    +        echo "\n\n--- JSAPI 及 H5 参数 ---\n";
    +        var_export($options);
    +        echo '
    '; + echo ""; + echo " + + "; + } + + /** + * 支付通知接收处理 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + */ + public function notify() + { + $wechat = new Pay(config('wechat.')); + p($wechat->getNotify()); + return 'SUCCESS'; + } + + /** + * 显示二维码 + * @param string $url + * @return \think\Response + * @throws \Endroid\QrCode\Exceptions\ImageFunctionFailedException + * @throws \Endroid\QrCode\Exceptions\ImageFunctionUnknownException + * @throws \Endroid\QrCode\Exceptions\ImageTypeInvalidException + */ + protected function createQrc($url) + { + $qrCode = new QrCode(); + $qrCode->setText($url)->setSize(300)->setPadding(20)->setImageType(QrCode::IMAGE_TYPE_PNG); + return \think\facade\Response::header('Content-Type', 'image/png')->data($qrCode->get()); + } + +} \ No newline at end of file diff --git a/application/index/controller/Test.php b/application/store/controller/wechat/Index.php similarity index 65% rename from application/index/controller/Test.php rename to application/store/controller/wechat/Index.php index 473c76c35..d2be107f6 100644 --- a/application/index/controller/Test.php +++ b/application/store/controller/wechat/Index.php @@ -3,24 +3,28 @@ // +---------------------------------------------------------------------- // | Think.Admin // +---------------------------------------------------------------------- -// | 版权所有 2016~2017 广州楚才信息科技有限公司 [ http://www.cuci.cc ] +// | 版权所有 2014~2017 广州楚才信息科技有限公司 [ http://www.cuci.cc ] // +---------------------------------------------------------------------- // | 官方网站: http://think.ctolog.com // +---------------------------------------------------------------------- -// | 开源协议 ( http://www.apache.org/licenses/LICENSE-2.0 ) +// | 开源协议 ( https://mit-license.org ) // +---------------------------------------------------------------------- // | github开源项目:https://github.com/zoujingli/Think.Admin // +---------------------------------------------------------------------- -namespace app\index\controller; +namespace app\store\controller\wechat; -use controller\BasicApi; -use service\FileService; -class Test extends BasicApi { - - public function index() { - dump(FileService::oss('fassdfsa', 'fsadfasdf')); +class Index +{ + public function index() + { + return '微信商城---开发中...'; } -} + public function pay() + { + return 'fsdfasfsa'; + } + +} \ No newline at end of file diff --git a/application/store/init.php b/application/store/init.php new file mode 100644 index 000000000..d8876c888 --- /dev/null +++ b/application/store/init.php @@ -0,0 +1,34 @@ +', function (Request $request) { + $params = explode('-', $request->path()); + array_shift($params); + $controller = array_shift($params) ?: config('app.default_controller'); + $action = array_shift($params) ?: config('app.default_action'); + return App::action("store/wechat.{$controller}/{$action}", $params); +}); + +// 微信菜单链接配置 +$GLOBALS['WechatMenuLink'][] = ['link' => '@wx', 'title' => '微信商城首页']; +$GLOBALS['WechatMenuLink'][] = ['link' => '@wx-demo-jsapi', 'title' => 'JSAPI支付测试']; + +// @todo 模块处理机制将写在下面(包括模块初始化及升级) +// @todo 模块权限处理,使用全局数组 +// @todo 模板菜单处理,默认放到全局数组中,然后在菜单中可以快速编辑(还要考虑下) \ No newline at end of file diff --git a/application/store/service/GoodsService.php b/application/store/service/GoodsService.php new file mode 100644 index 000000000..8873f2f7f --- /dev/null +++ b/application/store/service/GoodsService.php @@ -0,0 +1,130 @@ + '1', 'is_deleted' => '0']; + $cateList = Db::name('StoreGoodsCate')->where($cateWhere)->order('sort asc,id desc')->column($cateField); + // 商品品牌处理 + $brandWhere = ['status' => '1', 'is_deleted' => '0']; + $brandField = 'id,brand_logo,brand_cover,brand_title,brand_desc,brand_detail'; + $brandList = Db::name('StoreGoodsBrand')->where($brandWhere)->order('sort asc,id desc')->column($brandField); + // 无商品列表时 + if (empty($goodsList)) { + return ['list' => $goodsList, 'cate' => $cateList, 'brand' => $brandList]; + } + // 读取商品详情列表 + $specWhere = [['status', 'eq', '1'], ['is_deleted', 'eq', '0'], ['goods_id', 'in', array_column($goodsList, 'id')]]; + $specField = 'id,goods_id,goods_spec,goods_number,market_price,selling_price,goods_stock,goods_sale'; + $specList = Db::name('StoreGoodsList')->where($specWhere)->column($specField); + foreach ($specList as $key => $spec) { + foreach ($goodsList as $goods) { + if ($goods['id'] === $spec['goods_id']) { + $specList[$key]['goods_title'] = $goods['goods_title']; + } + } + if ($spec['goods_spec'] === 'default:default') { + $specList[$key]['goods_spec_alias'] = '默认规格'; + } else { + $specList[$key]['goods_spec_alias'] = str_replace([':', ','], [': ', ', '], $spec['goods_spec']); + } + } + // 商品数据组装 + foreach ($goodsList as $key => $vo) { + // 商品内容处理 + $goodsList[$key]['goods_content'] = $vo['goods_content']; + // 商品品牌处理 + $goodsList[$key]['brand'] = isset($brandList[$vo['brand_id']]) ? $brandList[$vo['brand_id']] : []; + // 商品分类关联 + $goodsList[$key]['cate'] = []; + if (isset($cateList[$vo['cate_id']])) { + $goodsList[$key]['cate'][] = ($tcate = $cateList[$vo['cate_id']]); + while (isset($tcate['pid']) && $tcate['pid'] > 0 && isset($cateList[$tcate['pid']])) { + $goodsList[$key]['cate'][] = ($tcate = $cateList[$tcate['pid']]); + } + $goodsList[$key]['cate'] = array_reverse($goodsList[$key]['cate']); + } + // 商品详细列表关联 + $goodsList[$key]['spec'] = []; + foreach ($specList as $spec) { + if ($vo['id'] === $spec['goods_id']) { + $goodsList[$key]['spec'][] = $spec; + } + } + } + return ['list' => $goodsList, 'cate' => $cateList, 'brand' => $brandList]; + } + + /** + * 同步更新商品库存及售出 + * @param int $goods_id 商品ID + * @return array + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public static function syncGoodsStock($goods_id) + { + // 检查商品是否需要更新库存 + $map = ['id' => $goods_id, 'is_deleted' => '0']; + if (!($goods = Db::name('StoreGoods')->where($map)->find())) { + return ['code' => 0, 'msg' => '指定商品信息无法同步库存!']; + } + // 统计入库信息 + $stockField = 'goods_id,goods_spec,ifnull(sum(goods_stock), 0) goods_stock'; + $stockWhere = ['status' => '1', 'is_deleted' => '0', 'goods_id' => $goods_id]; + $stockList = (array)Db::name('StoreGoodsStock')->field($stockField)->where($stockWhere)->group('goods_id,goods_spec')->select(); + // 统计销售信息 + $saleField = 'goods_id,goods_spec,ifnull(sum(number), 0) goods_sale'; + $saleWhere = ['status' => '1', 'is_deleted' => '0', 'goods_id' => $goods_id]; + $saleList = (array)Db::name('StoreOrderGoods')->field($saleField)->where($saleWhere)->group('goods_id,goods_spec')->select(); + // 库存置零 + list($where, $total_sale) = [['goods_id' => $goods_id], 0]; + Db::name('StoreGoodsList')->where($where)->update(['goods_stock' => 0, 'goods_sale' => 0]); + // 更新商品库存 + foreach ($stockList as $stock) { + $where = ['goods_id' => $goods_id, 'goods_spec' => $stock['goods_spec']]; + Db::name('StoreGoodsList')->where($where)->update(['goods_stock' => $stock['goods_stock']]); + } + // 更新商品销量 + foreach ($saleList as $sale) { + $total_sale += intval($sale['goods_sale']); + $where = ['goods_id' => $goods_id, 'goods_spec' => $sale['goods_spec']]; + Db::name('StoreGoodsList')->where($where)->update(['goods_sale' => $sale['goods_sale']]); + } + return ['code' => 1, 'msg' => '同步商品库存成功!']; + } + +} \ No newline at end of file diff --git a/application/store/service/MemberService.php b/application/store/service/MemberService.php new file mode 100644 index 000000000..92c6c77d3 --- /dev/null +++ b/application/store/service/MemberService.php @@ -0,0 +1,37 @@ +where(['id' => $mid])->find())) { + return ['code' => 0, 'msg' => '会员数据处理异常,请刷新重试!']; + } + // 订单数据生成 + list($order_no, $orderList) = [DataService::createSequence(10, 'ORDER'), []]; + $order = ['mid' => $mid, 'order_no' => $order_no, 'real_price' => 0, 'goods_price' => 0, 'desc' => $orderDesc, 'type' => $orderType, 'from' => $from]; + foreach (explode(';', trim($params, ',;@')) as $param) { + list($goods_id, $goods_spec, $number) = explode('@', "{$param}@@"); + $item = ['mid' => $mid, 'type' => $orderType, 'order_no' => $order_no, 'goods_id' => $goods_id, 'goods_spec' => $goods_spec, 'goods_number' => $number]; + $goodsResult = self::buildOrderData($item, $order, $orderList, 'selling_price'); + if (empty($goodsResult['code'])) { + return $goodsResult; + } + } + // 生成快递信息 + $expressResult = self::buildExpressData($order, $addressId, $expressId); + if (empty($expressResult['code'])) { + return $expressResult; + } + try { + // 写入订单信息 + Db::transaction(function () use ($order, $orderList, $expressResult) { + Db::name('StoreOrder')->insert($order); // 主订单信息 + Db::name('StoreOrderGoods')->insertAll($orderList); // 订单关联的商品信息 + Db::name('storeOrderExpress')->insert($expressResult['data']); // 快递信息 + }); + // 同步商品库存列表 + foreach (array_unique(array_column($orderList, 'goods_id')) as $stock_goods_id) { + GoodsService::syncGoodsStock($stock_goods_id); + } + } catch (\Exception $e) { + return ['code' => 0, 'msg' => '商城订单创建失败,请稍候再试!' . $e->getLine() . $e->getFile() . $e->getMessage()]; + } + return ['code' => 1, 'msg' => '商城订单创建成功!', 'order_no' => $order_no]; + } + + /** + * 生成订单快递数据 + * @param array $order 订单主表记录 + * @param int $address_id 会员地址ID + * @param int $express_id 快递信息ID + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public static function buildExpressData(&$order, $address_id, $express_id) + { + // 收货地址处理 + $addressWhere = ['mid' => $order['mid'], 'id' => $address_id, 'status' => '1', 'is_deleted' => '0']; + $addressField = 'username express_username,phone express_phone,province express_province,city express_city,area express_area,address express_address'; + if (!($address = Db::name('StoreMemberAddress')->field($addressField)->where($addressWhere)->find())) { + return ['code' => 0, 'msg' => '收货地址数据异常!']; + } + // 物流信息查询 + $expressField = 'express_title,express_code'; + $expressWhere = ['id' => $express_id, 'status' => '1', 'is_deleted' => '0']; + if (!($express = Db::name('StoreExpress')->field($expressField)->where($expressWhere)->find())) { + return ['code' => 0, 'msg' => '快递公司数据异常!']; + } + // @todo 运费计算处理 + // $order['freight_price'] = '0.00'; + // $order['real_price'] += floatval($order['freight_price']); + $extend = ['mid' => $order['mid'], 'order_no' => $order['order_no'], 'type' => $order['type']]; + return ['code' => 1, 'data' => array_merge($address, $express, $extend), 'msg' => '生成快递信息成功!']; + } + + /** + * 订单数据生成 + * @param array $item 订单单项参数 + * (mid,type,order_no,goods_id,goods_spec,goods_number) + * @param array $order 订单主表 + * @param array $orderList 订单详细表 + * @param string $price_field 实际计算单价字段 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private static function buildOrderData($item, &$order, &$orderList, $price_field = 'selling_price') + { + list($mid, $type, $order_no, $goods_id, $goods_spec, $number) = [ + $item['mid'], $item['type'], $item['order_no'], $item['goods_id'], $item['goods_spec'], $item['goods_number'], + ]; + // 商品主体信息 + $goodsField = 'goods_title,goods_logo,goods_image'; + $goodsWhere = ['id' => $goods_id, 'status' => '1', 'is_deleted' => '0']; + if (!($goods = Db::name('StoreGoods')->field($goodsField)->where($goodsWhere)->find())) { + return ['code' => 0, 'msg' => "无效的商品信息!", 'data' => "{$goods_id}, {$goods_spec}, {$number}"]; + } + // 商品规格信息 + $specField = 'goods_id,goods_spec,market_price,selling_price,goods_stock,goods_sale'; + $specWhere = ['status' => '1', 'is_deleted' => '0', 'goods_id' => $goods_id, 'goods_spec' => $goods_spec]; + if (!($goodsSpec = Db::name('StoreGoodsList')->field($specField)->where($specWhere)->find())) { + return ['code' => 0, 'msg' => '无效的商品规格信息!', 'data' => "{$goods_id}, {$goods_spec}, {$number}"]; + } + // 商品库存检查 + if ($goodsSpec['goods_stock'] - $goodsSpec['goods_sale'] < $number) { + return ['code' => 0, 'msg' => '商品库存不足,请更换其它商品!', 'data' => "{$goods_id}, {$goods_spec}, {$number}"]; + } + // 订单价格处理 + $goodsSpec['price_field'] = $price_field; + $orderList[] = array_merge($goods, $goodsSpec, ['mid' => $mid, 'number' => $number, 'order_no' => $order_no, 'type' => $type]); + $order['goods_price'] += floatval($goodsSpec[$price_field]) * $number; + $order['real_price'] += floatval($goodsSpec[$price_field]) * $number; + return ['code' => 1, 'msg' => '商品添加到订单成功!']; + } + + /** + * 订单主表数据处理 + * @param array $list + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public static function buildOrderList(&$list) + { + $mids = array_unique(array_column($list, 'mid')); + $orderNos = array_unique(array_column($list, 'order_no')); + $memberList = Db::name("StoreMember")->whereIn('id', $mids)->select(); + $goodsList = Db::name('StoreOrderGoods')->whereIn('order_no', $orderNos)->select(); + $expressList = Db::name('StoreOrderExpress')->whereIn('order_no', $orderNos)->select(); + foreach ($list as $key => $vo) { + list($list[$key]['member'], $list[$key]['goods'], $list[$key]['express']) = [[], [], []]; + foreach ($memberList as $member) { + $member['nickname'] = ToolsService::emojiDecode($member['nickname']); + ($vo['mid'] === $member['id']) && $list[$key]['member'] = $member; + } + foreach ($expressList as $express) { + ($vo['order_no'] === $express['order_no']) && $list[$key]['express'] = $express; + } + foreach ($goodsList as $goods) { + if ($goods['goods_spec'] === 'default:default') { + $goods['goods_spec_alias'] = '默认规格'; + } else { + $goods['goods_spec_alias'] = str_replace([':', ','], [':', ','], $goods['goods_spec']); + } + ($vo['order_no'] === $goods['order_no']) && $list[$key]['goods'][] = $goods; + } + } + return $list; + } + +} \ No newline at end of file diff --git a/application/store/view/express/form.html b/application/store/view/express/form.html new file mode 100644 index 000000000..d93c6f72d --- /dev/null +++ b/application/store/view/express/form.html @@ -0,0 +1,36 @@ +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + {if isset($vo['id'])}{/if} + + +
    + + +
    diff --git a/application/store/view/express/index.html b/application/store/view/express/index.html new file mode 100644 index 000000000..fbefaa67f --- /dev/null +++ b/application/store/view/express/index.html @@ -0,0 +1,117 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + + + + +{/block} + +{block name="content"} + + + + + + + +
    + + {if empty($list)} +

    没 有 记 录 哦!

    + {else} + + + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + + + {/foreach} + +
    + + + + 快递公司快递代码添加时间快递状态操作
    + + + + {$vo.express_title}{$vo.express_code}{$vo.create_at|format_datetime} + {if $vo.status eq 0}已禁用{elseif $vo.status eq 1}使用中{/if} + + + + | + 编辑 + + + + | + 禁用 + + | + 启用 + + + {if auth("$classuri/del")} + | + 删除 + {/if} + +
    + {if isset($page)}

    {$page|raw}

    {/if} + {/if} +
    +{/block} \ No newline at end of file diff --git a/application/store/view/goods/form.html b/application/store/view/goods/form.html new file mode 100644 index 000000000..f961a670b --- /dev/null +++ b/application/store/view/goods/form.html @@ -0,0 +1,357 @@ +{extend name='admin@public/content'} + +{block name="content"} +
    + + +
    + +
    + +
    +
    + + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + {if !empty($tags)} +
    + +
    +
    + {foreach $tags as $tag} + + {/foreach} +
    +
    +
    + {/if} + +
    + +
    + + + + + + + + + + + + + +
    商品LOGO商品图片
    + + + +
    +
    +
    + + +
    + +
    + + + + + + + + + + +
    可选规格规格内容
    + +
    + + + + + + + + + + + + + + + + + +
    商品规格市场价格销售价格规格状态
    + + + + + + + + + + + +
    +
    +
    + + +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    +
    + {if !empty($vo.id)}{/if} + + +
    +
    + +
    + + +{/block} + +{block name="style"} + +{/block} \ No newline at end of file diff --git a/application/store/view/goods/index.html b/application/store/view/goods/index.html new file mode 100644 index 000000000..1fe371eb8 --- /dev/null +++ b/application/store/view/goods/index.html @@ -0,0 +1,194 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + + + + + + + + + + + + +{/block} + +{block name="content"} + + + + + +
    + +

    没 有 记 录 哦!

    + + + + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + + + {/foreach} + +
    + + + + 品牌分类 + + + + + + + +
    商品信息售价 ( 标价 ) / 库存 ( 剩余, 已售 )
    +
    添加时间 / 状态
    + + + + + 品牌:{$vo.brand.brand_title|default='未配置品牌'|raw}
    + 分类:{if empty($vo.cate)}未配置分类{else} + {foreach $vo.cate as $k=>$cate}{$cate.cate_title} + {if $k+1 < count($vo.cate)}{/if} + {/foreach} + {/if} +
    + + + + + {foreach $vo.spec as $spec} + + + + + {/foreach} +
    + [{$spec.goods_id}] {$spec.goods_title|default=''|raw} + {$spec.goods_spec_alias|raw} + + 售 {$spec.selling_price} ( 市 {$spec.market_price} ) + 存 {$spec.goods_stock} ( 剩 {$spec.goods_stock-$spec.goods_sale}, 售 {$spec.goods_sale} ) +
    +
    + {$vo.create_at|format_datetime|str_replace=' ','
    ',###|raw} + {if $vo.status eq '0'}已下架{elseif $vo.status eq '1'}销售中{/if} +
    + + + | + 编辑 + + + + | + 入库 + + + + | + 下架 + + | + 上架 + + + + | + 删除 + + +
    + {if isset($page)}

    {$page|raw}

    {/if} + +
    + +{/block} \ No newline at end of file diff --git a/application/store/view/goods/stock.html b/application/store/view/goods/stock.html new file mode 100644 index 000000000..9be9cd20f --- /dev/null +++ b/application/store/view/goods/stock.html @@ -0,0 +1,52 @@ + diff --git a/application/store/view/goods_brand/form.html b/application/store/view/goods_brand/form.html new file mode 100644 index 000000000..6e6a6a783 --- /dev/null +++ b/application/store/view/goods_brand/form.html @@ -0,0 +1,82 @@ +{extend name='admin@public/content'} + +{block name="content"} +
    + +
    + +
    + +
    +
    + +
    + +
    + + + + + + + + + + + + + +
    品牌LOGO品牌图片
    + + + + + +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    +
    + {if !empty($vo.id)}{/if} + + +
    +
    + + + + + +
    +{/block} \ No newline at end of file diff --git a/application/store/view/goods_brand/index.html b/application/store/view/goods_brand/index.html new file mode 100644 index 000000000..e89d176cd --- /dev/null +++ b/application/store/view/goods_brand/index.html @@ -0,0 +1,108 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + + + + +{/block} + +{block name="content"} + + + + + + + +
    + {if empty($list)} +

    没 有 记 录 哦!

    + {else} + + + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + + {/foreach} + +
    + + + + 商品品牌添加时间品牌状态
    + + + + {$vo.brand_title}{$vo.create_at|format_datetime} + {if $vo.status eq 0}已禁用{elseif $vo.status eq 1}使用中{/if} + + + {if auth("$classuri/edit")} + | + 编辑 + {/if} + + {if $vo.status eq 1 and auth("$classuri/forbid")} + | + 禁用 + {elseif auth("$classuri/resume")} + | + 启用 + {/if} + + {if auth("$classuri/del")} + | + 删除 + {/if} + +
    + {if isset($page)}

    {$page|raw}

    {/if} + {/if} +
    +{/block} \ No newline at end of file diff --git a/application/store/view/goods_cate/form.html b/application/store/view/goods_cate/form.html new file mode 100644 index 000000000..0309d9f3d --- /dev/null +++ b/application/store/view/goods_cate/form.html @@ -0,0 +1,39 @@ + diff --git a/application/store/view/goods_cate/index.html b/application/store/view/goods_cate/index.html new file mode 100644 index 000000000..1f5539fbe --- /dev/null +++ b/application/store/view/goods_cate/index.html @@ -0,0 +1,83 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + +{/block} + +{block name="content"} +
    + +

    没 有 记 录 哦!

    + + + + + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + + + {/foreach} + +
    + + + + 商品分类分类描述添加时间分类状态
    + + + + + {$vo.spl|raw} {$vo.cate_title} + + {$vo.cate_desc|default='未设置分类描述'|raw} + + {$vo.create_at|format_datetime} + + {if $vo.status eq 0}已禁用{elseif $vo.status eq 1}使用中{/if} + + + {if auth("$classuri/edit")} + | + 编辑 + {/if} + + {if $vo.status eq 1 and auth("$classuri/forbid")} + | + 禁用 + {elseif auth("$classuri/resume")} + | + 启用 + {/if} + + {if auth("$classuri/del")} + | + 删除 + {/if} + +
    + {if isset($page)}

    {$page|raw}

    {/if} + +
    +{/block} \ No newline at end of file diff --git a/application/store/view/goods_spec/form.html b/application/store/view/goods_spec/form.html new file mode 100644 index 000000000..9e68819e4 --- /dev/null +++ b/application/store/view/goods_spec/form.html @@ -0,0 +1,200 @@ +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    + + + + + + + + +
    + + + + + 删除 + 删除 + 上移 + 上移 + 下移 + 下移 +
    +
    + + + + +
    + +
    +

    设置多个内容值时请使用空格键或英文逗号隔开。

    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + {if isset($vo['id'])}{/if} + + +
    + + + + +
    diff --git a/application/store/view/goods_spec/index.html b/application/store/view/goods_spec/index.html new file mode 100644 index 000000000..fe870cf96 --- /dev/null +++ b/application/store/view/goods_spec/index.html @@ -0,0 +1,116 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + + + + +{/block} + +{block name="content"} + + + + + + + +
    + {if empty($list)} +

    没 有 记 录 哦!

    + {else} + + + + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + + + {/foreach} + +
    + + + + 规格分组规格内容添加时间标签状态
    + + + + {$vo.spec_title} + {foreach $vo.spec_param as $param} +

    {$param.name} : {$param.value}

    + {/foreach} +
    + {$vo.create_at|format_datetime|raw} + + {if $vo.status eq 0}已禁用{elseif $vo.status eq 1}使用中{/if} + + + + | + 编辑 + + + + | + 禁用 + + | + 启用 + + + + | + 删除 + + +
    + {if isset($page)}

    {$page|raw}

    {/if} + {/if} +
    +{/block} \ No newline at end of file diff --git a/application/store/view/order/address.html b/application/store/view/order/address.html new file mode 100644 index 000000000..a62c38248 --- /dev/null +++ b/application/store/view/order/address.html @@ -0,0 +1,60 @@ +
    + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + {if isset($order_no)}{/if} + + +
    + + +
    diff --git a/application/store/view/order/index.html b/application/store/view/order/index.html new file mode 100644 index 000000000..cb27cb965 --- /dev/null +++ b/application/store/view/order/index.html @@ -0,0 +1,287 @@ +{extend name='admin@public/content'} + +{block name="content"} + + + + + +
    + {if empty($list)} +

    没 有 记 录 哦!

    + {else} + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + {/foreach} + +
    + + 会员信息订单信息 + + + + + + + + +
    商品信息 + 价格 数量 +
    +
    发货信息
    + + + 会员昵称:{$vo.member.nickname|default='未设置会员昵称'|raw}
    + 会员手机:{$vo.member.phone|default='未设置会员手机'|raw} +
    + 订单单号:{$vo.order_no} {if $vo.type eq 3}套餐 {/if} + {switch name='vo.status'} + {case value='0'}已取消{/case} + {case value='1'}待付款{/case} + {case value='2'}待发货{/case} + {case value='3'}已发货{/case} + {case value='4'}已完成{/case} + {case value='5'}已退单{/case} + {case value='6'}退款审核中({if($vo.refund_type == 1)}退款申请{/if}{if($vo.refund_type == 2)}退货申请{/if}) + 审核{/case} + {case value='7'}退款处理中{/case} + {/switch} +
    +
    + + + + + {foreach $vo.goods as $goods} + + + + + {/foreach} +
    [{$goods.goods_id}] {$goods.goods_title}({$goods.goods_spec_alias}) + + {if in_array($vo.type,['1','3'])} + ¥ {$goods[$goods['price_field']]} + {else} + {$goods.selling_integral} 积分 + {/if} + + {$goods.number} + +
    +
    + {if empty($vo.express)} + 无发货信息 + {else} + 快递公司:{$vo.express.real_company_title|default=$vo.express.company_title} + {if !empty($vo.express.real_company_title)} + + {if $vo.express.real_company_title neq $vo.express.company_title} + {$vo.express.company_title} + {/if} + {/if} +
    + 快递单号:{$vo.express.real_express_no|default='未发货'|raw} + + 查看 + + 快速发货 + +
    + 收货信息:{$vo.express.username} {$vo.express.phone}
    + 收货地址:{$vo.express.province}{$vo.express.city}{$vo.express.area}{$vo.express.address} + {if auth('store/order/address')}修改{/if} + {/if} + {if !empty($vo.express.real_express_at)}
    发货时间:{$vo.express.real_express_at|format_datetime}{/if} +
    + {if isset($page)}

    {$page|raw}

    {/if} + {/if} +
    + +{/block} diff --git a/application/wechat/controller/Api.php b/application/wechat/controller/Api.php deleted file mode 100644 index f81b6c780..000000000 --- a/application/wechat/controller/Api.php +++ /dev/null @@ -1,254 +0,0 @@ - - */ -class Api extends Controller { - - /** - * 微信openid - * @var string - */ - protected $openid; - - /** - * 微信消息对象 - * @var WechatReceive - */ - protected $wechat; - - /** - * 微信消息接口 - * @return string - */ - public function index() { - // 实例接口对象 - $this->wechat = &load_wechat('Receive'); - // 验证接口请求 - if ($this->wechat->valid() === false) { - $msg = "{$this->wechat->errMsg}[{$this->wechat->errCode}]"; - Log::error($msg); - return $msg; - } - // 获取消息来源用户OPENID - $this->openid = $this->wechat->getRev()->getRevFrom(); - // 获取并同步粉丝信息到数据库 - $this->_updateFansInfo(true); - // 分别执行对应类型的操作 - switch ($this->wechat->getRev()->getRevType()) { - case WechatReceive::MSGTYPE_TEXT: - return $this->_keys("WechatKeys#keys#" . $this->wechat->getRevContent()); - case WechatReceive::MSGTYPE_EVENT: - return $this->_event(); - case WechatReceive::MSGTYPE_IMAGE: - return $this->_image(); - case WechatReceive::MSGTYPE_LOCATION: - return $this->_location(); - default: - return 'success'; - } - } - - /** - * 关键字处理 - * @param string $keys - * @param bool $isForce - * @return string - */ - private function _keys($keys, $isForce = false) { - list($table, $field, $value) = explode('#', $keys . '##'); - if (is_array($info = Db::name($table)->where($field, $value)->find()) && isset($info['type'])) { - // 数据状态检查 - if (array_key_exists('status', $info) && empty($info['status'])) { - return 'success'; - } - switch ($info['type']) { - case 'customservice': // 多客服 - $this->wechat->sendCustomMessage(['touser' => $this->openid, 'msgtype' => 'text', 'text' => ['content' => $info['content']]]); - return $this->wechat->transfer_customer_service()->reply(false, true); - case 'keys': // 关键字 - if (empty($info['content']) && empty($info['name'])) { - return 'success'; - } - return $this->_keys('wechat_keys#keys#' . (empty($info['content']) ? $info['name'] : $info['content'])); - case 'text': // 文本消息 - if (empty($info['content']) && empty($info['name'])) { - return 'success'; - } - return $this->wechat->text($info['content'])->reply(false, true); - case 'news': // 图文消息 - if (empty($info['news_id'])) { - return 'success'; - } - return $this->_news($info['news_id']); - case 'music': // 音频消息 - if (empty($info['music_url']) || empty($info['music_title']) || empty($info['music_desc'])) { - return 'success'; - } - $media_id = empty($info['music_image']) ? '' : WechatService::uploadForeverMedia($info['music_image'], 'image'); - if (empty($media_id)) { - return 'success'; - } - return $this->wechat->music($info['music_title'], $info['music_desc'], $info['music_url'], $info['music_url'], $media_id)->reply(false, true); - case 'voice': // 语音消息 - if (empty($info['voice_url'])) { - return 'success'; - } - $media_id = WechatService::uploadForeverMedia($info['voice_url'], 'voice'); - if (empty($media_id)) { - return 'success'; - } - return $this->wechat->voice($media_id)->reply(false, true); - case 'image': // 图文消息 - if (empty($info['image_url'])) { - return 'success'; - } - $media_id = WechatService::uploadForeverMedia($info['image_url'], 'image'); - if (empty($media_id)) { - return 'success'; - } - return $this->wechat->image($media_id)->reply(false, true); - case 'video': // 视频消息 - if (empty($info['video_url']) || empty($info['video_desc']) || empty($info['video_title'])) { - return 'success'; - } - $data = ['title' => $info['video_title'], 'introduction' => $info['video_desc']]; - $media_id = WechatService::uploadForeverMedia($info['video_url'], 'video', true, $data); - return $this->wechat->video($media_id, $info['video_title'], $info['video_desc'])->reply(false, true); - } - } - if ($isForce) { - return 'success'; - } - return $this->_keys('wechat_keys#keys#default', true); - } - - /** - * 回复图文 - * @param int $news_id - * @return bool|string - */ - protected function _news($news_id = 0) { - if (is_array($newsinfo = WechatService::getNewsById($news_id)) && !empty($newsinfo['articles'])) { - $newsdata = array(); - foreach ($newsinfo['articles'] as $vo) { - $newsdata[] = [ - 'Title' => $vo['title'], - 'Description' => $vo['digest'], - 'PicUrl' => $vo['local_url'], - 'Url' => url("@wechat/review", '', true, true) . "?content={$vo['id']}&type=article", - ]; - } - return $this->wechat->news($newsdata)->reply(false, true); - } - return 'success'; - } - - /** - * 事件处理 - */ - protected function _event() { - $event = $this->wechat->getRevEvent(); - switch (strtolower($event['event'])) { - case 'subscribe': // 粉丝关注事件 - $this->_updateFansInfo(true); - $this->_spread($event['key']); - return $this->_keys('wechat_keys#keys#subscribe', true); - case 'unsubscribe':// 粉丝取消关注 - $this->_updateFansInfo(false); - return 'success'; - case 'click': // 点击菜单事件 - return $this->_keys($event['key']); - case 'scancode_push': - case 'scancode_waitmsg': // 扫码推事件 - $scanInfo = $this->wechat->getRev()->getRevScanInfo(); - if (isset($scanInfo['ScanResult'])) { - return $this->_keys($scanInfo['ScanResult']); - } - return 'success'; - case 'scan': - if (!empty($event['key'])) { - return $this->_spread($event['key']); - } - return 'success'; - } - return 'success'; - } - - /** - * 推荐好友扫码关注 - * @param string $event - * @return mixed - */ - private function _spread($event) { - $key = preg_replace('|^.*?(\d+).*?$|', '$1', "{$event}"); - // 检测推荐是否有效 - $fans = Db::name('WechatFans')->where('id', $key)->find(); - if (!is_array($fans) || !isset($fans['openid']) || $fans['openid'] === $this->openid) { - return false; - } - // 标识推荐关系 - $data = ['spread_by' => $fans['openid'], 'spread_at' => date('Y-m-d H:i:s')]; - Db::name('WechatFans')->where("openid='{$this->openid}' and (spread_openid is null or spread_openid='')")->setField($data); - // @todo 推荐成功的奖励 - } - - /** - * 位置事情回复 - * @return string - */ - private function _location() { - return 'success'; - } - - /** - * 图片事件处理 - */ - private function _image() { - return 'success'; - } - - /** - * 同步粉丝状态 - * @param bool $subscribe 关注状态 - */ - protected function _updateFansInfo($subscribe = true) { - if ($subscribe) { - $fans = WechatService::getFansInfo($this->openid); - if (empty($fans) || empty($fans['subscribe'])) { - $wechat = &load_wechat('User'); - $userInfo = $wechat->getUserInfo($this->openid); - $userInfo['subscribe'] = intval($subscribe); - WechatService::setFansInfo($userInfo, $wechat->appid); - } - } else { - $data = ['subscribe' => '0', 'appid' => $this->wechat->appid, 'openid' => $this->openid]; - DataService::save('wechat_fans', $data, ['appid', 'openid']); - } - } - -} diff --git a/application/wechat/controller/Config.php b/application/wechat/controller/Config.php index 0b6d6bdf6..dd32ba797 100644 --- a/application/wechat/controller/Config.php +++ b/application/wechat/controller/Config.php @@ -1,7 +1,7 @@ * @date 2017/03/27 14:43 */ -class Config extends BasicAdmin { +class Config extends BasicAdmin +{ /** * 定义当前操作表名 @@ -37,88 +37,56 @@ class Config extends BasicAdmin { /** * 微信基础参数配置 - * @return View + * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function index() { + public function index() + { + $thrNotifyUrl = url('@wechat/api.push', '', true, true); if ($this->request->isGet()) { - $this->assign('title', '微信接口配置'); - return view(); - } - foreach ($this->request->post() as $key => $vo) { - sysconf($key, $vo); - } - LogService::write('微信管理', '修改微信接口参数成功'); - $this->success('数据修改成功!', ''); - } - - /** - * 微信商户参数配置 - * @return View - */ - public function pay() { - if ($this->request->isGet()) { - switch ($this->request->get('action')) { - // 生成测试支付二维码 - case 'payqrc': - $pay = &load_wechat('pay'); - // 生成订单号 - $order_no = session('pay-test-order-no'); - if (empty($order_no)) { - $order_no = DataService::createSequence(10, 'wechat-pay-test'); - session('pay-test-order-no', $order_no); - } - // 该订单号已经支付 - if (PayService::isPay($order_no)) { - return json(['code' => 2, 'order_no' => $order_no]); - } - // 订单号未支付,生成支付二维码URL - $url = PayService::createWechatPayQrc($pay, $order_no, 1, '微信扫码支付测试!'); - if ($url !== false) { - return json(['code' => 1, 'url' => $url, 'order_no' => $order_no]); - } - // 生成支付二维码URL失败 - $this->error("生成支付二维码失败,{$pay->errMsg}[{$pay->errCode}]"); - break; - // 微信支付退款操作 - case 'refund': - $order_no = session('pay-test-order-no'); - if (empty($order_no)) { - $this->error('测试订单号不存在,请重新开始支付测试!'); - } - if (!PayService::isPay($order_no)) { - $this->error('测试订单未支付或未收到微信支付通过!'); - } - $pay = &load_wechat('pay'); - if (!file_exists($pay->ssl_cer) || !file_exists($pay->ssl_key)) { - $this->error('微信支付双向证书异常,无法完成退款操作!'); - } - $refund_no = DataService::createSequence(10, 'wechat-pay-test'); - if (false !== PayService::putWechatRefund($pay, $order_no, 1, $refund_no)) { - session('pay-test-order-no', null); - $this->success('测试退款操作成功,请查看微信通知!', ''); - } - $this->error("操作退款失败,{$pay->errMsg}[{$pay->errCode}]"); - break; - // 显示支付配置界面 - default: - $this->assign('title', '微信支付配置'); - return view(); + $code = encode(url('@admin', '', true, true) . '#' . $this->request->url()); + $data = [ + 'title' => '微信接口配置', + 'appid' => $this->request->get('appid', sysconf('wechat_thr_appid')), + 'appkey' => $this->request->get('appkey', sysconf('wechat_thr_appkey')), + 'authurl' => config('wechat.service_url') . "/wechat/api.push/auth/{$code}.html", + ]; + if ($this->request->get('appid', false)) { + sysconf('wechat_thr_appid', $data['appid']); + sysconf('wechat_thr_appkey', $data['appkey']); + sysconf('wechat_type', 'thr'); + WechatService::config()->setApiNotifyUri($thrNotifyUrl); } - } - $data = $this->request->post(); - foreach ($data as $key => $vo) { - if (in_array($key, ['wechat_cert_key_md5', 'wechat_cert_cert_md5']) && !empty($vo)) { - $filename = ROOT_PATH . 'static/upload/' . join('/', str_split($vo, 16)) . '.pem'; - !file_exists($filename) && $this->error('支付双向证书上传失败,请重新上传!'); - $data[str_replace('_md5', '', $key)] = $filename; + try { + $data['wechat'] = WechatService::config()->getConfig(); + } catch (Exception $e) { + $data['wechat'] = []; } + return $this->fetch('', $data); } - unset($data['wechat_cert_key_md5'], $data['wechat_cert_cert_md5']); - foreach ($data as $key => $vo) { - DataService::save($this->table, ['name' => $key, 'value' => $vo], 'name'); + try { + // 接口对接类型 + sysconf('wechat_type', $this->request->post('wechat_type')); + // 直接参数对应 + sysconf('wechat_token', $this->request->post('wechat_token')); + sysconf('wechat_appid', $this->request->post('wechat_appid')); + sysconf('wechat_appsecret', $this->request->post('wechat_appsecret')); + sysconf('wechat_encodingaeskey', $this->request->post('wechat_encodingaeskey')); + // 第三方平台配置 + sysconf('wechat_thr_appid', $this->request->post('wechat_thr_appid')); + sysconf('wechat_thr_appkey', $this->request->post('wechat_thr_appkey')); + // 第三方平台时设置远程平台通知接口 + if ($this->request->post('wechat_type') === 'thr') { + if (!WechatService::config()->setApiNotifyUri($thrNotifyUrl)) { + $this->error('远程服务端接口更新失败,请稍候再试!'); + } + } + LogService::write('微信管理', '修改微信接口参数成功'); + } catch (\Exception $e) { + $this->error('微信授权保存成功, 但授权验证失败 !
    ' . $e->getMessage()); } - LogService::write('微信管理', '修改微信支付参数成功'); - $this->success('数据修改成功!', ''); + $this->success('微信授权数据修改成功!', url('@admin') . "#" . url('@wechat/config/index')); } } diff --git a/application/wechat/controller/Fans.php b/application/wechat/controller/Fans.php index c5d871e0b..2066496f9 100644 --- a/application/wechat/controller/Fans.php +++ b/application/wechat/controller/Fans.php @@ -1,7 +1,7 @@ * @date 2017/03/27 14:43 */ -class Fans extends BasicAdmin { +class Fans extends BasicAdmin +{ /** * 定义当前默认数据表 @@ -38,32 +41,39 @@ class Fans extends BasicAdmin { /** * 显示粉丝列表 * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function index() { + public function index() + { $this->title = '微信粉丝管理'; - $db = Db::name($this->table)->where('is_back', '0')->order('subscribe_time desc'); $get = $this->request->get(); - !empty($get['sex']) && $db->where('sex', $get['sex']); + $db = Db::name($this->table)->where(['is_black' => '0']); + (isset($get['sex']) && $get['sex'] !== '') && $db->where('sex', $get['sex']); foreach (['nickname', 'country', 'province', 'city'] as $key) { - if (isset($get[$key]) && $get[$key] !== '') { - $db->where($key, 'like', "%{$get[$key]}%"); - } + (isset($get[$key]) && $get[$key] !== '') && $db->whereLike($key, "%{$get[$key]}%"); } if (isset($get['tag']) && $get['tag'] !== '') { $db->where("concat(',',tagid_list,',') like :tag", ['tag' => "%,{$get['tag']},%"]); } - return parent::_list($db); + if (isset($get['create_at']) && $get['create_at'] !== '') { + list($start, $end) = explode(' - ', $get['create_at']); + $db->whereBetween('subscribe_at', ["{$start} 00:00:00", "{$end} 23:59:59"]); + } + return parent::_list($db->order('subscribe_time desc')); } /** * 列表数据处理 - * @param type $list + * @param array $list */ - protected function _data_filter(&$list) { + protected function _data_filter(&$list) + { $tags = Db::name('WechatFansTags')->column('id,name'); foreach ($list as &$vo) { - $vo['nickname'] = ToolsService::emojiDecode($vo['nickname']); - $vo['tags_list'] = []; + list($vo['tags_list'], $vo['nickname']) = [[], ToolsService::emojiDecode($vo['nickname'])]; foreach (explode(',', $vo['tagid_list']) as $tag) { if ($tag !== '' && isset($tags[$tag])) { $vo['tags_list'][$tag] = $tags[$tag]; @@ -75,108 +85,89 @@ class Fans extends BasicAdmin { $this->assign('tags', $tags); } - /** - * 黑名单列表 - */ - public function back() { - $this->title = '微信粉丝黑名单管理'; - $db = Db::name($this->table)->where('is_back', '1')->order('subscribe_time desc'); - $get = $this->request->get(); - !empty($get['sex']) && $db->where('sex', $get['sex']); - foreach (['nickname', 'country', 'province', 'city'] as $key) { - if (isset($get[$key]) && $get[$key] !== '') { - $db->where($key, 'like', "%{$get[$key]}%"); - } - } - if (isset($get['tag']) && $get['tag'] !== '') { - $db->where("concat(',',tagid_list,',') like :tag", ['tag' => "%,{$get['tag']},%"]); - } - return parent::_list($db); - } - /** * 设置黑名单 */ - public function backadd() { - $wechat = & load_wechat('User'); - $openids = $this->_getActionOpenids(); - if (false !== $wechat->addBacklist($openids)) { - Db::name($this->table)->where('openid', 'in', $openids)->setField('is_back', '1'); - $this->success("已成功将 " . count($openids) . " 名粉丝移到黑名单!", ''); + public function backadd() + { + try { + $openids = $this->_getActionOpenids(); + WechatService::WeChatUser()->batchBlackList($openids); + Db::name($this->table)->whereIn('openid', $openids)->setField('is_black', '1'); + } catch (\Exception $e) { + $this->error("设置黑名单失败,请稍候再试!"); } - $this->error("设备黑名单失败,请稍候再试!{$wechat->errMsg}[{$wechat->errCode}]"); + $this->success('设置黑名单成功!', ''); } /** * 标签选择 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function tagset() { + public function tagset() + { $tags = $this->request->post('tags', ''); $fans_id = $this->request->post('fans_id', ''); - $fans = Db::name('WechatFans')->where('id', $fans_id)->find(); + $fans = Db::name('WechatFans')->where(['id' => $fans_id])->find(); empty($fans) && $this->error('需要操作的数据不存在!'); - $wechat = & load_wechat('User'); - foreach (explode(',', $fans['tagid_list']) as $tagid) { - is_numeric($tagid) && $wechat->batchDeleteUserTag($tagid, [$fans['openid']]); + try { + $wechat = WechatService::WeChatTags(); + foreach (explode(',', $fans['tagid_list']) as $tagid) { + is_numeric($tagid) && $wechat->batchUntagging([$fans['openid']], $tagid); + } + foreach (explode(',', $tags) as $tagid) { + is_numeric($tagid) && $wechat->batchTagging([$fans['openid']], $tagid); + } + Db::name('WechatFans')->where(['id' => $fans_id])->setField('tagid_list', $tags); + } catch (\Exception $e) { + $this->error('粉丝标签设置失败, 请稍候再试!'); } - foreach (explode(',', $tags) as $tagid) { - is_numeric($tagid) && $wechat->batchAddUserTag($tagid, [$fans['openid']]); - } - if (false !== Db::name('WechatFans')->where('id', $fans_id)->setField('tagid_list', $tags)) { - $this->success('粉丝标签成功!', ''); - } - $this->error('粉丝标签设置失败, 请稍候再试!'); - } - - /** - * 取消黑名 - */ - public function backdel() { - $wechat = & load_wechat('User'); - $openids = $this->_getActionOpenids(); - if (false !== $wechat->delBacklist($openids)) { - Db::name($this->table)->where('openid', 'in', $openids)->setField('is_back', '0'); - $this->success("已成功将 " . count($openids) . " 名粉丝从黑名单中移除!", ''); - } - $this->error("设备黑名单失败,请稍候再试!{$wechat->errMsg}[{$wechat->errCode}]"); + $this->success('粉丝标签成功!', ''); } /** * 给粉丝增加标签 */ - public function tagadd() { + public function tagadd() + { $tagid = $this->request->post('tag_id', 0); empty($tagid) && $this->error('没有可能操作的标签ID'); - $openids = $this->_getActionOpenids(); - $wechat = & load_wechat('User'); - if (false !== $wechat->batchAddUserTag($tagid, $openids)) { - $this->success('设置粉丝标签成功!', ''); + try { + $openids = $this->_getActionOpenids(); + WechatService::WeChatTags()->batchTagging($openids, $tagid); + } catch (\Exception $e) { + $this->error("设置粉丝标签失败, 请稍候再试! " . $e->getMessage()); } - $this->error("设置粉丝标签失败, 请稍候再试! {$wechat->errMsg}[{$wechat->errCode}]"); + $this->success('设置粉丝标签成功!', ''); } /** * 移除粉丝标签 */ - public function tagdel() { + public function tagdel() + { $tagid = $this->request->post('tag_id', 0); empty($tagid) && $this->error('没有可能操作的标签ID'); - $openids = $this->_getActionOpenids(); - $wechat = & load_wechat('User'); - if (false !== $wechat->batchDeleteUserTag($tagid, $openids)) { - $this->success('删除粉丝标签成功!', ''); + try { + $openids = $this->_getActionOpenids(); + WechatService::WeChatTags()->batchUntagging($openids, $tagid); + } catch (\Exception $e) { + $this->error("删除粉丝标签失败, 请稍候再试! "); } - $this->error("删除粉丝标签失败, 请稍候再试! {$wechat->errMsg}[{$wechat->errCode}]"); + $this->success('删除粉丝标签成功!', ''); } /** * 获取当前操作用户openid数组 * @return array */ - private function _getActionOpenids() { + private function _getActionOpenids() + { $ids = $this->request->post('id', ''); empty($ids) && $this->error('没有需要操作的数据!'); - $openids = Db::name($this->table)->where('id', 'in', explode(',', $ids))->column('openid'); + $openids = Db::name($this->table)->whereIn('id', explode(',', $ids))->column('openid'); empty($openids) && $this->error('没有需要操作的数据!'); return $openids; } @@ -184,14 +175,16 @@ class Fans extends BasicAdmin { /** * 同步粉丝列表 */ - public function sync() { - Db::name($this->table)->where('1=1')->delete(); - if (WechatService::syncAllFans('')) { - WechatService::syncBlackFans(''); + public function sync() + { + try { + Db::name($this->table)->where('1=1')->delete(); + [FansService::sync(), TagsService::sync()]; LogService::write('微信管理', '同步全部微信粉丝成功'); - $this->success('同步获取所有粉丝成功!', ''); + } catch (\Exception $e) { + $this->error('同步粉丝记录失败,请稍候再试!' . $e->getMessage()); } - $this->error('同步获取粉丝失败,请稍候再试!'); + $this->success('同步获取所有粉丝成功!', ''); } } diff --git a/application/wechat/controller/FansBlock.php b/application/wechat/controller/FansBlock.php new file mode 100644 index 000000000..ed2570ded --- /dev/null +++ b/application/wechat/controller/FansBlock.php @@ -0,0 +1,113 @@ + + * @date 2017/03/27 14:43 + */ +class FansBlock extends BasicAdmin +{ + + /** + * 定义当前默认数据表 + * @var string + */ + public $table = 'WechatFans'; + + /** + * 黑名单列表 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception + */ + public function index() + { + $this->title = '微信黑名单管理'; + $get = $this->request->get(); + $db = Db::name($this->table)->where(['is_black' => '1']); + (isset($get['sex']) && $get['sex'] !== '') && $db->where('sex', $get['sex']); + foreach (['nickname', 'country', 'province', 'city'] as $key) { + (isset($get[$key]) && $get[$key] !== '') && $db->whereLike($key, "%{$get[$key]}%"); + } + if (isset($get['tag']) && $get['tag'] !== '') { + $db->where("concat(',',tagid_list,',') like :tag", ['tag' => "%,{$get['tag']},%"]); + } + if (isset($get['create_at']) && $get['create_at'] !== '') { + list($start, $end) = explode(' - ', $get['create_at']); + $db->whereBetween('subscribe_at', ["{$start} 00:00:00", "{$end} 23:59:59"]); + } + return parent::_list($db->order('subscribe_time desc')); + } + + /** + * 列表数据处理 + * @param array $list + */ + protected function _data_filter(&$list) + { + $tags = Db::name('WechatFansTags')->column('id,name'); + foreach ($list as &$vo) { + list($vo['tags_list'], $vo['nickname']) = [[], ToolsService::emojiDecode($vo['nickname'])]; + foreach (explode(',', $vo['tagid_list']) as $tag) { + if ($tag !== '' && isset($tags[$tag])) { + $vo['tags_list'][$tag] = $tags[$tag]; + } elseif ($tag !== '') { + $vo['tags_list'][$tag] = $tag; + } + } + } + $this->assign('tags', $tags); + } + + /** + * 取消黑名 + */ + public function backdel() + { + $openids = $this->_getActionOpenids(); + try { + WechatService::WeChatUser()->batchUnblackList($openids); + Db::name($this->table)->whereIn('openid', $openids)->setField('is_black', '0'); + } catch (\Exception $e) { + $this->error("设备黑名单失败,请稍候再试!" . $e->getMessage()); + } + $this->success("已成功将 " . count($openids) . " 名粉丝从黑名单中移除!", ''); + } + + /** + * 获取当前操作用户openid数组 + * @return array + */ + private function _getActionOpenids() + { + $ids = $this->request->post('id', ''); + empty($ids) && $this->error('没有需要操作的数据!'); + $openids = Db::name($this->table)->whereIn('id', explode(',', $ids))->column('openid'); + empty($openids) && $this->error('没有需要操作的数据!'); + return $openids; + } + +} diff --git a/application/wechat/controller/Keys.php b/application/wechat/controller/Keys.php index 049229fd7..abea453fe 100644 --- a/application/wechat/controller/Keys.php +++ b/application/wechat/controller/Keys.php @@ -1,6 +1,7 @@ * @date 2017/03/27 14:43 */ -class Keys extends BasicAdmin { +class Keys extends BasicAdmin +{ /** * 指定当前数据表 @@ -34,29 +37,58 @@ class Keys extends BasicAdmin { /** * 显示关键字列表 + * @return array|string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function index() { - $this->assign('title', '微信关键字'); - $db = Db::name($this->table)->where('keys', 'not in', ['subscribe', 'default']); - return $this->_list($db); + public function index() + { + // 关键字二维码显示 + if ($this->request->get('action') === 'qrc') { + $wechat = WechatService::WeChatQrcode(); + $result = $wechat->create($this->request->get('keys', '')); + $this->redirect($wechat->url($result['ticket'])); + } + // 显示关键字列表 + $this->title = '微信关键字管理'; + $db = Db::name($this->table)->whereNotIn('keys', ['subscribe', 'default']); + return $this->_list($db->order('sort asc,id desc')); } /** * 列表数据处理 * @param array $data */ - protected function _index_data_filter(&$data) { - $types = ['keys' => '关键字', 'image' => '图片', 'news' => '图文', 'music' => '音乐', 'text' => '文字', 'video' => '视频', 'voice' => '语音']; - foreach ($data as &$vo) { - $vo['type'] = isset($types[$vo['type']]) ? $types[$vo['type']] : $vo['type']; + protected function _index_data_filter(&$data) + { + try { + $types = [ + 'keys' => '关键字', 'image' => '图片', 'news' => '图文', + 'music' => '音乐', 'text' => '文字', 'video' => '视频', 'voice' => '语音', + ]; + foreach ($data as &$vo) { + $vo['qrc'] = url('@wechat/keys/index') . "?action=qrc&keys={$vo['keys']}"; + $vo['type'] = isset($types[$vo['type']]) ? $types[$vo['type']] : $vo['type']; + } + } catch (\Exception $e) { + $this->error($e->getMessage()); } } /** * 添加关键字 * @return string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function add() { + public function add() + { $this->title = '添加关键字规则'; return $this->_form($this->table, 'form'); } @@ -64,40 +96,38 @@ class Keys extends BasicAdmin { /** * 编辑关键字 * @return string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function edit() { + public function edit() + { $this->title = '编辑关键字规则'; return $this->_form($this->table, 'form'); } - /** - * 表单处理 - * @param $data - */ - protected function _form_filter($data) { - if ($this->request->isPost() && isset($data['keys'])) { - $db = Db::name($this->table)->where('keys', $data['keys']); - !empty($data['id']) && $db->where('id', 'neq', $data['id']); - $db->count() > 0 && $this->error('关键字已经存在,请使用其它关键字!'); - } - } - /** * 删除关键字 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function del() { + public function del() + { if (DataService::update($this->table)) { $this->success("关键字删除成功!", ''); } $this->error("关键字删除失败,请稍候再试!"); } - /** * 关键字禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function forbid() { + public function forbid() + { if (DataService::update($this->table)) { $this->success("关键字禁用成功!", ''); } @@ -106,8 +136,11 @@ class Keys extends BasicAdmin { /** * 关键字禁用 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function resume() { + public function resume() + { if (DataService::update($this->table)) { $this->success("关键字启用成功!", ''); } @@ -116,41 +149,60 @@ class Keys extends BasicAdmin { /** * 关注默认回复 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function subscribe() { + public function subscribe() + { $this->assign('title', '编辑默认回复'); - return $this->_form($this->table, 'form'); - } - - /** - * 关注默认回复表单处理 - * @param $data - */ - protected function _subscribe_form_filter(&$data) { - if ($this->request->isGet()) { - $data = Db::name($this->table)->where('keys', 'subscribe')->find(); - } - $data['keys'] = 'subscribe'; + return $this->_form($this->table, 'form', 'keys', [], ['keys' => 'subscribe']); } /** * 无配置默认回复 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function defaults() { + public function defaults() + { $this->assign('title', '编辑无配置默认回复'); - return $this->_form($this->table, 'form'); + return $this->_form($this->table, 'form', 'keys', [], ['keys' => 'default']); } - /** - * 无配置默认回复表单处理 - * @param $data + * 添加数据处理 + * @param array $data */ - protected function _defaults_form_filter(&$data) { - if ($this->request->isGet()) { - $data = Db::name($this->table)->where('keys', 'default')->find(); + protected function _form_filter(array &$data) + { + if ($this->request->isPost() && isset($data['keys'])) { + $db = Db::name($this->table)->where('keys', $data['keys']); + !empty($data['id']) && $db->where('id', 'neq', $data['id']); + $db->count() > 0 && $this->error('关键字已经存在,请使用其它关键字!'); } - $data['keys'] = 'default'; } + + /** + * 编辑结果处理 + * @param $result + */ + protected function _form_result($result) + { + if ($result !== false) { + list($url, $keys) = ['', $this->request->post('keys')]; + if (!in_array($keys, ['subscribe', 'default'])) { + $url = url('@admin') . '#' . url('wechat/keys/index') . '?spm=' . $this->request->get('spm'); + } + $this->success('恭喜, 关键字保存成功!', $url); + } + $this->error('关键字保存失败, 请稍候再试!'); + } + } diff --git a/application/wechat/controller/Menu.php b/application/wechat/controller/Menu.php index 359884836..34865d45a 100644 --- a/application/wechat/controller/Menu.php +++ b/application/wechat/controller/Menu.php @@ -1,7 +1,7 @@ * @date 2017/03/27 14:43 */ -class Menu extends BasicAdmin { +class Menu extends BasicAdmin +{ /** * 指定当前页面标题 @@ -44,7 +46,7 @@ class Menu extends BasicAdmin { * 微信菜单的类型 * @var array */ - protected $menu_type = array( + protected $menuType = [ 'view' => '跳转URL', 'click' => '点击推事件', 'scancode_push' => '扫码推事件', @@ -53,12 +55,18 @@ class Menu extends BasicAdmin { 'pic_photo_or_album' => '弹出拍照或者相册发图', 'pic_weixin' => '弹出微信相册发图器', 'location_select' => '弹出地理位置选择器', - ); + ]; /** - * 显示列表操作 + * 显示菜单列表 + * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function index() { + public function index() + { return parent::_list(Db::name($this->table), false, true); } @@ -66,78 +74,91 @@ class Menu extends BasicAdmin { * 列表数据处理 * @param array $data */ - protected function _index_data_filter(&$data) { + protected function _index_data_filter(&$data) + { $data = ToolsService::arr2tree($data, 'index', 'pindex'); } /** * 微信菜单编辑 */ - public function edit() { + public function edit() + { if ($this->request->isPost()) { $post = $this->request->post(); !isset($post['data']) && $this->error('访问出错,请稍候再试!'); - $data = $post['data']; - if (empty($data)) { - Db::name($this->table)->where('1=1')->delete(); - load_wechat('Menu')->deleteMenu(); + // 删除菜单 + if (empty($post['data'])) { + try { + Db::name($this->table)->where('1=1')->delete(); + WechatService::WeChatMenu()->delete(); + } catch (\Exception $e) { + $this->error('删除取消微信菜单失败,请稍候再试!' . $e->getMessage()); + } $this->success('删除并取消微信菜单成功!', ''); } - foreach ($data as &$vo) { - if (isset($vo['content'])) { - $vo['content'] = str_replace('"', "'", $vo['content']); + // 数据过滤处理 + try { + foreach ($post['data'] as &$vo) { + isset($vo['content']) && ($vo['content'] = str_replace('"', "'", $vo['content'])); } + Db::transaction(function () use ($post) { + Db::name($this->table)->where('1=1')->delete(); + Db::name($this->table)->insertAll($post['data']); + }); + $this->_push(); + } catch (\Exception $e) { + $this->error('微信菜单发布失败,请稍候再试!' . $e->getMessage()); } - if (Db::name($this->table)->where('1=1')->delete() !== false && Db::name($this->table)->insertAll($data) !== false) { - $result = $this->_push(); - if ($result['status']) { - LogService::write('微信管理', '发布微信菜单成功'); - $this->success('保存发布菜单成功!', ''); - } - $this->error('菜单发布失败,' . $result['errmsg']); - } - $this->error('保存发布菜单失败!'); + LogService::write('微信管理', '发布微信菜单成功'); + $this->success('保存发布菜单成功!', ''); } } /** * 取消菜单 */ - public function cancel() { - $wehcat = &load_wechat('Menu'); - if (false !== $wehcat->deleteMenu()) { - $this->success('菜单取消成功,重新关注可立即生效!', ''); + public function cancel() + { + try { + WechatService::WeChatMenu()->delete(); + } catch (\Exception $e) { + $this->error('菜单取消失败'); } - $this->error('菜单取消失败,' . $wehcat->errMsg); + $this->success('菜单取消成功,重新关注可立即生效!', ''); } /** * 菜单推送 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - protected function _push() { - $result = Db::name($this->table) - ->field('id,index,pindex,name,type,content') - ->where('status', '1') - ->order('sort ASC,id ASC') - ->select(); + private function _push() + { + list($map, $field) = [['status' => '1'], 'id,index,pindex,name,type,content']; + $result = (array)Db::name($this->table)->field($field)->where($map)->order('sort ASC,id ASC')->select(); foreach ($result as &$row) { empty($row['content']) && $row['content'] = uniqid(); - switch ($row['type']) { - case 'view': - $row['url'] = preg_match('#^(\w+:)?//#i', $row['content']) ? $row['content'] : url($row['content'], '', true, true); - break; - case 'event': - if (isset($this->menu_type[$row['content']])) { - $row['type'] = $row['content']; - $row['key'] = "wechat_menu#id#{$row['id']}"; - } - break; - case 'media_id': - $row['media_id'] = $row['content']; - break; - default : - (!in_array($row['type'], $this->menu_type)) && $row['type'] = 'click'; - $row['key'] = "wechat_menu#id#{$row['id']}"; + if ($row['type'] === 'miniprogram') { + list($row['appid'], $row['url'], $row['pagepath']) = explode(',', "{$row['content']},,"); + } elseif ($row['type'] === 'view') { + if (preg_match('#^(\w+:)?//#', $row['content'])) { + $row['url'] = $row['content']; + } else { + $row['url'] = url($row['content'], '', true, true); + } + } elseif ($row['type'] === 'event') { + if (isset($this->menuType[$row['content']])) { + list($row['type'], $row['key']) = [$row['content'], "wechat_menu#id#{$row['id']}"]; + } + } elseif ($row['type'] === 'media_id') { + $row['media_id'] = $row['content']; + } else { + $row['key'] = "wechat_menu#id#{$row['id']}"; + !in_array($row['type'], $this->menuType) && $row['type'] = 'click'; } unset($row['content']); } @@ -153,11 +174,7 @@ class Menu extends BasicAdmin { } unset($menu['type']); } - $wechat = &load_wechat('Menu'); - if (false !== $wechat->createMenu(['button' => $menus])) { - return array('status' => true, 'errmsg' => ''); - } - return array('status' => false, 'errmsg' => $wechat->errMsg); + WechatService::WeChatMenu()->create(['button' => $menus]); } } diff --git a/application/wechat/controller/News.php b/application/wechat/controller/News.php index 588f655d6..8eccff790 100644 --- a/application/wechat/controller/News.php +++ b/application/wechat/controller/News.php @@ -1,7 +1,7 @@ * @date 2017/03/27 14:43 */ -class News extends BasicAdmin { +class News extends BasicAdmin +{ /** * 设置默认操作表 @@ -39,65 +41,93 @@ class News extends BasicAdmin { /** * 图文列表 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function index() { - $this->assign('title', '图文列表'); - $db = Db::name($this->table)->where('is_deleted', '0')->order('id desc'); - return parent::_list($db); + public function index() + { + $this->title = '微信图文列表'; + $db = Db::name($this->table)->where(['is_deleted' => '0']); + return parent::_list($db->order('id desc')); + } + + /** + * 图文列表数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + protected function _index_data_filter(&$data) + { + foreach ($data as &$vo) { + $vo = MediaService::getNewsById($vo['id']); + } } /** * 图文选择器 * @return string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function select() { + public function select() + { return $this->index(); } + /** + * 图文列表数据处理 + * @param array $data + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + protected function _select_data_filter(&$data) + { + foreach ($data as &$vo) { + $vo = MediaService::getNewsById($vo['id']); + } + } + /** * 媒体资源显示 * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function image() { + public function image() + { $_GET['rows'] = 18; $this->assign('field', $this->request->get('field', 'local_url')); return $this->_list(Db::name('WechatNewsMedia')->where('type', 'image')); } - /** - * 图文列表数据处理 - * @param $data - */ - protected function _index_data_filter(&$data) { - foreach ($data as &$vo) { - $vo = WechatService::getNewsById($vo['id']); - } - } - - /** - * 图文列表数据处理 - * @param $data - */ - protected function _select_data_filter(&$data) { - foreach ($data as &$vo) { - $vo = WechatService::getNewsById($vo['id']); - } - } - /** * 添加图文 - * @return View + * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function add() { + public function add() + { if ($this->request->isGet()) { - return view('form', ['title' => '新建图文']); + return $this->fetch('form', ['title' => '新建图文']); } if ($this->request->isPost()) { $data = $this->request->post(); if (($ids = $this->_apply_news_article($data['data'])) && !empty($ids)) { $post = ['article_id' => $ids, 'create_by' => session('user.id')]; if (DataService::save($this->table, $post, 'id') !== false) { - $this->success('图文添加成功!', ''); + $url = url('@admin') . '#' . url('@wechat/news/index') . '?spm=' . $this->request->get('spm'); + $this->success('图文添加成功!', $url); } } $this->error('图文添加失败,请稍候再试!'); @@ -106,20 +136,27 @@ class News extends BasicAdmin { /** * 编辑图文 - * @return View + * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function edit() { + public function edit() + { $id = $this->request->get('id', ''); if ($this->request->isGet()) { empty($id) && $this->error('参数错误,请稍候再试!'); - return view('form', ['title' => '编辑图文', 'vo' => WechatService::getNewsById($id)]); + if ($this->request->get('output') === 'json') { + ToolsService::success('获取数据成功', MediaService::getNewsById($id)); + } + return $this->fetch('form', ['title' => '编辑图文']); } $data = $this->request->post(); $ids = $this->_apply_news_article($data['data']); if (!empty($ids)) { $post = ['id' => $id, 'article_id' => $ids, 'create_by' => session('user.id')]; if (false !== DataService::save('wechat_news', $post, 'id')) { - $this->success('图文更新成功!', ''); + $url = url('@admin') . '#' . url('@wechat/news/index') . '?spm=' . $this->request->get('spm'); + $this->success('图文更新成功!', $url); } } $this->error('图文更新失败,请稍候再试!'); @@ -130,19 +167,24 @@ class News extends BasicAdmin { * @param array $data * @param array $ids * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - protected function _apply_news_article($data, $ids = []) { + protected function _apply_news_article($data, $ids = []) + { foreach ($data as &$vo) { $vo['create_by'] = session('user.id'); $vo['create_at'] = date('Y-m-d H:i:s'); - $vo['digest'] = empty($vo['digest']) ? mb_substr(strip_tags(str_replace(["\s", ' '], '', $vo['content'])), 0, 120) : $vo['digest']; + if (empty($vo['digest'])) { + $vo['digest'] = mb_substr(strip_tags(str_replace(["\s", ' '], '', $vo['content'])), 0, 120); + } if (empty($vo['id'])) { $result = $id = Db::name('WechatNewsArticle')->insertGetId($vo); } else { $id = intval($vo['id']); $result = Db::name('WechatNewsArticle')->where('id', $id)->update($vo); } - if ($result !== FALSE) { + if ($result !== false) { $ids[] = $id; } } @@ -151,8 +193,11 @@ class News extends BasicAdmin { /** * 删除用户 + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function del() { + public function del() + { if (DataService::update($this->table)) { $this->success("图文删除成功!", ''); } @@ -161,32 +206,40 @@ class News extends BasicAdmin { /** * 推荐图文 - * @return array|void + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException */ - public function push() { + public function push() + { # 获取将要推送的粉丝列表 switch (strtolower($this->request->get('action', ''))) { case 'getuser': if ('' === ($params = $this->request->post('group', ''))) { return ['code' => 'SUCCESS', 'data' => []]; } - $ids = explode(',', $params); - $db = Db::name('WechatFans'); + list($ids, $db) = [explode(',', $params), Db::name('WechatFans')]; !in_array('0', $ids) && $db->where("concat(',',tagid_list,',') REGEXP '," . join(',|,', $ids) . ",'"); - return ['code' => "SUCCESS", 'data' => $db->where('subscribe', '1')->limit(200)->column('nickname')]; + $list = $db->where(['subscribe' => '1'])->limit(200)->column('nickname'); + foreach ($list as &$vo) { + $vo = ToolsService::emojiDecode($vo); + } + return ['code' => "SUCCESS", 'data' => $list]; default : $news_id = $this->request->get('id', ''); // 显示及图文 - $newsinfo = WechatService::getNewsById($news_id); + $newsinfo = MediaService::getNewsById($news_id); // Get 请求,显示选择器界面 if ($this->request->isGet()) { - $fans_tags = Db::name('WechatFansTags')->select(); - array_unshift($fans_tags, [ - 'id' => 0, - 'name' => '全部', - 'count' => Db::name('WechatFans')->where('subscribe', '1')->count(), - ]); - return view('push', ['vo' => $newsinfo, 'fans_tags' => $fans_tags]); + $fans_tags = (array)Db::name('WechatFansTags')->select(); + $count = Db::name('WechatFans')->where(['subscribe' => '1'])->count(); + array_unshift($fans_tags, ['id' => 0, 'name' => '全部', 'count' => $count]); + return $this->fetch('push', ['vo' => $newsinfo, 'fans_tags' => $fans_tags]); } // Post 请求,执行图文推送操作 $post = $this->request->post(); @@ -204,38 +257,42 @@ class News extends BasicAdmin { $data['filter'] = ['is_to_all' => false, 'tag_id' => join(',', $post['fans_tags'])]; $data['mpnews'] = ['media_id' => $newsinfo['media_id']]; } - $wechat = &load_wechat('Receive'); - if (FALSE !== $wechat->sendGroupMassMessage($data)) { + if (WechatService::WeChatCustom()->massSendAll($data)) { LogService::write('微信管理', "图文[{$news_id}]推送成功"); $this->success('微信图文推送成功!', ''); } - $this->error("微信图文推送失败,{$wechat->errMsg} [{$wechat->errCode}]"); + $this->error("微信图文推送失败"); } } /** * 上传永久图文 - * @param type $newsinfo - * @return boolean + * @param array $news + * @return bool + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException */ - private function _uploadWechatNews(&$newsinfo) { - foreach ($newsinfo['articles'] as &$article) { - $article['thumb_media_id'] = WechatService::uploadForeverMedia($article['local_url']); + private function _uploadWechatNews(&$news) + { + foreach ($news['articles'] as &$article) { + $article['thumb_media_id'] = MediaService::uploadForeverMedia($article['local_url']); $article['content'] = preg_replace_callback("//i", function ($matches) { - $src = WechatService::uploadImage($matches[2]); + $src = MediaService::uploadImage($matches[2]); return ""; - }, htmlspecialchars_decode($article['content'])); + }, $article['content']); } - $wechat = & load_wechat('media'); + $wechat = WechatService::WeChatMedia(); // 如果已经上传过,先删除之前的历史记录 - !empty($newsinfo['media_id']) && $wechat->delForeverMedia($newsinfo['media_id']); + !empty($news['media_id']) && $wechat->delMaterial($news['media_id']); // 上传图文到微信服务器 - $result = $wechat->uploadForeverArticles(['articles' => $newsinfo['articles']]); + $result = $wechat->addNews(['articles' => $news['articles']]); if (isset($result['media_id'])) { - $newsinfo['media_id'] = $result['media_id']; - return Db::name('WechatNews')->where('id', $newsinfo['id'])->setField('media_id', $result['media_id']); + $news['media_id'] = $result['media_id']; + return Db::name('WechatNews')->where(['id' => $news['id']])->update(['media_id' => $result['media_id']]); } - Log::error("上传永久图文失败, {$wechat->errMsg}[{$wechat->errCode}]"); + Log::error("上传永久图文失败"); return false; } diff --git a/application/wechat/controller/Notify.php b/application/wechat/controller/Notify.php deleted file mode 100644 index e98ddab42..000000000 --- a/application/wechat/controller/Notify.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @date 2017/04/05 14:02 - */ -class Notify extends Controller { - - public function index() { - // 实例支付接口 - $pay = &load_wechat('Pay'); - - // 获取支付通知 - $notifyInfo = $pay->getNotify(); - - // 支付通知数据获取失败 - if ($notifyInfo === FALSE) { - // 接口失败的处理 - Log::error("微信支付通知消息验证失败,{$pay->errCode}[{$pay->errCode}]"); - return $pay->errMsg; - } else { - //支付通知数据获取成功 - if ($notifyInfo['result_code'] == 'SUCCESS' && $notifyInfo['return_code'] == 'SUCCESS') { - // 记录支付通知数据 - if(!Db::name('WechatPayNotify')->insert($notifyInfo)){ - $pay->replyXml(['return_code' => 'ERROR', 'return_msg' => '系统记录微信通知时发生异常!']); - } - $prepayMap = ['out_trade_no' => $notifyInfo['out_trade_no']]; - $prepayData = Db::name('WechatPayPrepayid')->where($prepayMap)->find(); - if (empty($prepayData)) { - $pay->replyXml(['return_code' => 'ERROR', 'return_msg' => '系统中未发现对应的预支付记录!']); - } - $prepayUpdateData = ['transaction_id' => $notifyInfo['transaction_id'], 'is_pay' => 1, 'pay_at' => date('Y-m-d H:i:s')]; - if (false === Db::name('WechatPayPrepayid')->where($prepayMap)->update($prepayUpdateData)) { - $pay->replyXml(['return_code' => 'ERROR', 'return_msg' => '更新系统预支付记录失败!']); - } - // 支付状态完全成功,可以更新订单的支付状态了 - // @todo 这里去完成你的订单状态修改操作 - // 回复xml,replyXml方法是终态方法 - $pay->replyXml(['return_code' => 'SUCCESS', 'return_msg' => '系统业务处理成功!']); - } - } - } - -} diff --git a/application/wechat/controller/Review.php b/application/wechat/controller/Review.php index 3c85a5ecc..f84c20024 100644 --- a/application/wechat/controller/Review.php +++ b/application/wechat/controller/Review.php @@ -1,7 +1,7 @@ request->get(); - $content = str_replace("\n", "
    ", $this->request->get('content', '')); // 内容 + public function index() + { + $content = str_replace("\n", "
    ", $this->request->get('content', '', 'urldecode')); // 内容 $type = $this->request->get('type', 'text'); // 类型 - $this->assign('type', $type); // 图文处理 if ($type === 'news' && is_numeric($content) && !empty($content)) { - $news = WechatService::getNewsById($content); + $news = MediaService::getNewsById($content); $this->assign('articles', $news['articles']); } // 文章预览 if ($type === 'article' && is_numeric($content) && !empty($content)) { $article = Db::name('WechatNewsArticle')->where('id', $content)->find(); + if (!empty($article['content_source_url'])) { + $this->redirect($article['content_source_url']); + } $this->assign('vo', $article); } - $this->assign($get); + $this->assign('type', $type); $this->assign('content', $content); + $this->assign($this->request->get()); // 渲染模板并显示 - return view(); - } - - /** - * 微信图片显示 - */ - public function img() { - $url = $this->request->get('url', ''); - $filename = FileService::getFileName($url, 'jpg', 'tmp/'); - if (false === ($img = FileService::getFileUrl($filename))) { - $info = FileService::save($filename, file_get_contents($url)); - $img = (is_array($info) && isset($info['url'])) ? $info['url'] : $url; - } - $this->redirect($img); + return $this->fetch(); } } diff --git a/application/wechat/controller/Tags.php b/application/wechat/controller/Tags.php index 0d392fe4a..30468a3a3 100644 --- a/application/wechat/controller/Tags.php +++ b/application/wechat/controller/Tags.php @@ -1,7 +1,7 @@ - * @date 2017/03/27 14:43 */ -class Tags extends BasicAdmin { +class Tags extends BasicAdmin +{ /** * 定义当前默认数据表 @@ -38,69 +38,118 @@ class Tags extends BasicAdmin { /** * 显示粉丝标签列表 * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - public function index() { + public function index() + { $this->title = '微信粉丝标签管理'; - $get = $this->request->get(); - $db = Db::name($this->table)->order('id asc'); + list($get, $db) = [$this->request->get(), Db::name($this->table)]; foreach (['name'] as $key) { - if (isset($get[$key]) && $get[$key] !== '') { - $db->where($key, 'like', "%{$get[$key]}%"); - } + (isset($get[$key]) && $get[$key] !== '') && $db->whereLike($key, "%{$get[$key]}%"); } - return parent::_list($db); + return parent::_list($db->order('id asc')); } /** * 添加粉丝标签 + * @return array|string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException */ - public function add() { + public function add() + { if ($this->request->isGet()) { return parent::_form($this->table, 'form', 'id'); } $name = $this->request->post('name', ''); empty($name) && $this->error('粉丝标签名不能为空!'); - (Db::name($this->table)->where('name', $name)->count() > 0) && $this->error('粉丝标签标签名已经存在, 请使用其它标签名!'); - $wechat = & load_wechat('User'); + if (Db::name($this->table)->where('name', $name)->count() > 0) { + $this->error('粉丝标签标签名已经存在, 请使用其它标签名!'); + } + $wechat = WechatService::WeChatTags(); if (false === ($result = $wechat->createTags($name)) && isset($result['tag'])) { - $this->error("添加粉丝标签失败. {$wechat->errMsg}[{$wechat->errCode}]"); + $this->error("添加粉丝标签失败. "); } $result['tag']['count'] = 0; - DataService::save($this->table, $result['tag'], 'id') && $this->success('添加粉丝标签成功!', ''); + if (DataService::save($this->table, $result['tag'], 'id')) { + $this->success('添加粉丝标签成功!', ''); + } $this->error('粉丝标签添加失败, 请稍候再试!'); } /** * 编辑粉丝标签 + * @return array|string + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public function edit() { + public function edit() + { // 显示编辑界面 if ($this->request->isGet()) { return parent::_form($this->table, 'form', 'id'); } // 接收提交的数据 - list($name, $id) = [$this->request->post('name', ''), $this->request->post('id', '0')]; - $info = Db::name($this->table)->where('name', $name)->find(); + $id = $this->request->post('id', '0'); + $name = $this->request->post('name', ''); + $info = Db::name($this->table)->where(['name' => $name])->find(); if (!empty($info)) { if (intval($info['id']) === intval($id)) { $this->error('粉丝标签名没有改变, 无需修改!'); } $this->error('标签已经存在, 使用其它名称再试!'); } - $wechat = &load_wechat('User'); - $data = ['id' => $id, 'name' => $name]; - if (false !== $wechat->updateTag($id, $name) && false !== DataService::save($this->table, $data, 'id')) { - $this->success('编辑标签成功!', ''); + try { + WechatService::WeChatTags()->updateTags($id, $name); + DataService::save($this->table, ['id' => $id, 'name' => $name], 'id'); + } catch (\Exception $e) { + $this->error('编辑标签失败, 请稍后再试!' . $e->getMessage()); } - $this->error('编辑标签失败, 请稍后再试!' . $wechat->errMsg); + $this->success('编辑标签成功!', ''); + } + + + /** + * 删除粉丝标签 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function del() + { + $wechat = WechatService::WeChatTags(); + foreach (explode(',', $this->request->post('id', '')) as $id) { + if ($wechat->deleteTags($id)) { + Db::name('WechatFansTags')->where(['id' => $id])->delete(); + } else { + $this->error('移除粉丝标签失败,请稍候再试!'); + } + } + $this->success('移除粉丝标签成功!', ''); } /** * 同步粉丝标签列表 + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public function sync() { + public function sync() + { Db::name($this->table)->where('1=1')->delete(); - if (WechatService::syncFansTags()) { + if (TagsService::sync()) { LogService::write('微信管理', '同步全部微信粉丝标签成功'); $this->success('同步获取所有粉丝标签成功!', ''); } diff --git a/application/wechat/controller/api/Js.php b/application/wechat/controller/api/Js.php new file mode 100644 index 000000000..033fb4bc9 --- /dev/null +++ b/application/wechat/controller/api/Js.php @@ -0,0 +1,50 @@ +server('HTTP_REFERER', $result->url(true), null); + $wechat = WechatService::webOauth($url, $result->get('mode', 1), false); + $assign = [ + 'jssdk' => WechatService::webJsSDK($url), + 'openid' => $wechat['openid'], 'fansinfo' => $wechat['fansinfo'], + ]; + return Response::create(env('APP_PATH') . 'wechat/view/api/script/index.js', 'view', 200, [ + 'Content-Type' => 'application/x-javascript', + 'Cache-Control' => 'no-cache', 'Pragma' => 'no-cache', 'Expires' => '0', + ])->assign($assign); + } +} \ No newline at end of file diff --git a/application/wechat/controller/api/Push.php b/application/wechat/controller/api/Push.php new file mode 100644 index 000000000..5185cfff8 --- /dev/null +++ b/application/wechat/controller/api/Push.php @@ -0,0 +1,329 @@ + + */ +class Push +{ + + /** + * 当前公众号APPID + * @var string + */ + protected $appid; + + /** + * 当前微信用户openid + * @var string + */ + protected $openid; + + /** + * 当前微信消息对象 + * @var array + */ + protected $receive; + + /** + * 微信消息接口(来自ThinkService授权的消息推送) + * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function index() + { + $request = app('request'); + $this->appid = $request->post('appid', '', null); + $this->openid = $request->post('openid', '', null); + $this->receive = unserialize($request->post('receive', '', null)); + if (empty($this->appid) || empty($this->openid) || empty($this->receive)) { + throw new Exception('微信API实例缺失必要参数[appid,openid,receive].'); + } + return $this->init(); + } + + /** + * 微信消息接口(来自在公众号官方的消息推送) + * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function notify() + { + $wechat = WechatService::WeChatReceive(); + $this->openid = $wechat->getOpenid(); + $this->receive = $wechat->getReceive(); + $this->appid = WechatService::getAppid(); + return $this->init(); + } + + /** + * 初始化接口 + * @return string + * @throws Exception + * @throws \think\exception\PDOException + */ + private function init() + { + if ($this->appid !== WechatService::getAppid()) { + throw new Exception('微信API实例APPID验证失败.'); + } + // text,event,image,location + if (method_exists($this, ($method = $this->receive['MsgType']))) { + if (is_string(($result = $this->$method()))) { + return $result; + } + } + return 'success'; + } + + /** + * 文件消息处理 + * @return bool + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + protected function text() + { + return $this->keys("wechat_keys#keys#{$this->receive['Content']}"); + } + + /** + * 事件消息处理 + * @return bool|string + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + protected function event() + { + switch (strtolower($this->receive['Event'])) { + case 'subscribe': + $this->updateFansinfo(true); + if (isset($this->receive['EventKey']) && is_string($this->receive['EventKey'])) { + if (($key = preg_replace('/^qrscene_/i', '', $this->receive['EventKey']))) { + [$this->updateSpread($key), $this->keys("wechat_keys#keys#{$key}")]; + } + } + return $this->keys('wechat_keys#keys#subscribe', true); + case 'unsubscribe': + return $this->updateFansinfo(false); + case 'click': + return $this->keys($this->receive['EventKey']); + case 'scancode_push': + case 'scancode_waitmsg': + if (isset($this->receive['ScanCodeInfo'])) { + $this->receive['ScanCodeInfo'] = (array)$this->receive['ScanCodeInfo']; + if (!empty($this->receive['ScanCodeInfo']['ScanResult'])) { + return $this->keys("wechat_keys#keys#{$this->receive['ScanCodeInfo']['ScanResult']}"); + } + } + return false; + case 'scan': + if (!empty($this->receive['EventKey'])) { + return $this->keys("wechat_keys#keys#{$this->receive['EventKey']}"); + } + return false; + } + return false; + } + + /** + * 关键字处理 + * @param string $rule 关键字规则 + * @param bool $isLastReply 强制结束 + * @return bool|string + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + protected function keys($rule, $isLastReply = false) + { + list($table, $field, $value) = explode('#', $rule . '##'); + $info = Db::name($table)->where($field, $value)->find(); + if (empty($info['type']) || (array_key_exists('status', $info) && empty($info['status']))) { + // 切换默认回复 + return $isLastReply ? false : $this->keys('wechat_keys#keys#default', true); + } + switch ($info['type']) { + case 'customservice': + return $this->sendMessage('customservice', ['content' => $info['content']]); + case 'keys': + $content = empty($info['content']) ? $info['name'] : $info['content']; + return $this->keys("wechat_keys#keys#{$content}"); + case 'text': + return $this->sendMessage('text', ['content' => $info['content']]); + case 'news': + list($news, $data) = [MediaService::getNewsById($info['news_id']), []]; + if (empty($news['articles'])) { + return false; + } + foreach ($news['articles'] as $vo) { + $url = url("@wechat/review", '', true, true) . "?content={$vo['id']}&type=article"; + $data[] = ['url' => $url, 'title' => $vo['title'], 'picurl' => $vo['local_url'], 'description' => $vo['digest']]; + } + return $this->sendMessage('news', ['articles' => $data]); + case 'music': + if (empty($info['music_url']) || empty($info['music_title']) || empty($info['music_desc'])) { + return false; + } + $media_id = empty($info['music_image']) ? '' : MediaService::uploadForeverMedia($info['music_image'], 'image'); + $data = ['title' => $info['music_title'], 'description' => $info['music_desc'], 'musicurl' => $info['music_url'], 'hqmusicurl' => $info['music_url'], 'thumb_media_id' => $media_id]; + return $this->sendMessage('music', $data); + case 'voice': + if (empty($info['voice_url']) || !($media_id = MediaService::uploadForeverMedia($info['voice_url'], 'voice'))) { + return false; + } + return $this->sendMessage('voice', ['media_id' => $media_id]); + case 'image': + if (empty($info['image_url']) || !($media_id = MediaService::uploadForeverMedia($info['image_url'], 'image'))) { + return false; + } + return $this->sendMessage('image', ['media_id' => $media_id]); + case 'video': + if (empty($info['video_url']) || empty($info['video_desc']) || empty($info['video_title'])) { + return false; + } + $videoData = ['title' => $info['video_title'], 'introduction' => $info['video_desc']]; + if (!($media_id = MediaService::uploadForeverMedia($info['video_url'], 'video', $videoData))) { + return false; + } + $data = ['media_id' => $media_id, 'title' => $info['video_title'], 'description' => $info['video_desc']]; + return $this->sendMessage('video', $data); + default: + return false; + } + } + + /** + * 发送消息到公众号 + * @param string $type 消息类型(text|image|voice|video|music|news|mpnews|wxcard) + * @param array $data 消息内容 + * @return array|bool + * @throws \WeChat\Exceptions\InvalidDecryptException + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + protected function sendMessage($type, $data) + { + $msgData = ['touser' => $this->openid, 'msgtype' => $type, "{$type}" => $data]; + switch (strtolower(sysconf('wechat_type'))) { + case 'api': // 参数对接,直接回复XML来实现消息回复 + $wechat = WechatService::WeChatReceive(); + switch (strtolower($type)) { + case 'text': + return $wechat->text($data['content'])->reply([], true); + case 'image': + return $wechat->image($data['media_id'])->reply([], true); + case 'video': + return $wechat->video($data['media_id'], $data['title'], $data['description'])->reply([], true); + case 'voice': + return $wechat->voice($data['media_id'])->reply([], true); + case 'music': + return $wechat->music($data['title'], $data['description'], $data['musicurl'], $data['hqmusicurl'], $data['thumb_media_id'])->reply([], true); + case 'news': + $articles = []; + foreach ($data['articles'] as $article) { + $articles[] = ['Url' => $article['url'], 'Title' => $article['title'], 'PicUrl' => $article['picurl'], 'Description' => $article['description']]; + } + return $wechat->news($articles)->reply([], true); + case 'customservice': + WechatService::WeChatCustom()->send(['touser' => $this->openid, 'msgtype' => 'text', "text" => $data['content']]); + return $wechat->transferCustomerService()->reply([], true); + default: + return 'success'; + } + case 'thr': // 第三方平台,使用客服消息来实现 + return WechatService::WeChatCustom()->send($msgData); + default: + return 'success'; + } + } + + /** + * 更新推荐二维码关系 + * @param string $openid + * @return bool + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + protected function updateSpread($openid) + { + // 检测推荐是否有效 + $fans = Db::name('WechatFans')->where('openid', $openid)->find(); + if (empty($fans['openid']) || $fans['openid'] === $this->openid) { + return false; + } + // 标识推荐关系 + $data = ['spread_openid' => $fans['openid'], 'spread_at' => date('Y-m-d H:i:s')]; + $where = "openid='{$this->openid}' and (spread_openid is null or spread_openid='')"; + return Db::name('WechatFans')->where($where)->update($data) !== false; + } + + /** + * 同步粉丝状态 + * @param bool $subscribe 关注状态 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + protected function updateFansinfo($subscribe = true) + { + if ($subscribe) { + $userInfo = WechatService::WeChatUser()->getUserInfo($this->openid); + $userInfo['subscribe'] = intval($subscribe); + FansService::set($userInfo); + } else { + $fans = ['subscribe' => '0', 'openid' => $this->openid, 'appid' => $this->appid]; + DataService::save('WechatFans', $fans, 'openid', [['appid', 'eq', $this->appid]]); + } + } + +} diff --git a/application/wechat/controller/api/Tools.php b/application/wechat/controller/api/Tools.php new file mode 100644 index 000000000..8fb1db1c0 --- /dev/null +++ b/application/wechat/controller/api/Tools.php @@ -0,0 +1,95 @@ +request->url(true), 1); + return $this->fetch('', ['fans' => $fans]); + } + + /** + * 显示网页授权二维码 + * @return \think\Response + * @throws \Endroid\QrCode\Exceptions\ImageFunctionFailedException + * @throws \Endroid\QrCode\Exceptions\ImageFunctionUnknownException + * @throws \Endroid\QrCode\Exceptions\ImageTypeInvalidException + */ + public function oauth_qrc() + { + $url = url('@wechat/api.tools/oauth', '', true, true); + return $this->createQrc($url); + } + + /** + * JSSDK测试 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function jssdk() + { + return $this->fetch('', ['options' => WechatService::webJsSDK()]); + } + + /** + * 显示网页授权二维码 + * @return \think\Response + * @throws \Endroid\QrCode\Exceptions\ImageFunctionFailedException + * @throws \Endroid\QrCode\Exceptions\ImageFunctionUnknownException + * @throws \Endroid\QrCode\Exceptions\ImageTypeInvalidException + */ + public function jssdk_qrc() + { + $url = url('@wechat/api.tools/jssdk', '', true, true); + return $this->createQrc($url); + } + + /** + * 创建二维码响应对应 + * @param string $url 二维码内容 + * @return \think\Response + * @throws \Endroid\QrCode\Exceptions\ImageFunctionFailedException + * @throws \Endroid\QrCode\Exceptions\ImageFunctionUnknownException + * @throws \Endroid\QrCode\Exceptions\ImageTypeInvalidException + */ + protected function createQrc($url) + { + $qrCode = new QrCode(); + $qrCode->setText($url)->setSize(300)->setPadding(20)->setImageType(QrCode::IMAGE_TYPE_PNG); + return \think\facade\Response::header('Content-Type', 'image/png')->data($qrCode->get()); + } + +} \ No newline at end of file diff --git a/application/wechat/service/FansService.php b/application/wechat/service/FansService.php new file mode 100644 index 000000000..e5273cec9 --- /dev/null +++ b/application/wechat/service/FansService.php @@ -0,0 +1,123 @@ + $openid, 'appid' => WechatService::getAppid()]; + $user = Db::name('WechatFans')->where($map)->find(); + foreach (['country', 'province', 'city', 'nickname', 'remark'] as $k) { + isset($user[$k]) && $user[$k] = ToolsService::emojiDecode($user[$k]); + } + return $user; + } + + /** + * 同步所有粉丝记录 + * @param string $next_openid + * @return bool + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public static function sync($next_openid = '') + { + $wechat = WechatService::WeChatUser(); + $result = $wechat->getUserList($next_openid); + if (empty($result['data']['openid'])) { + return false; + } + foreach (array_chunk($result['data']['openid'], 100) as $openids) { + foreach ($wechat->getBatchUserInfo($openids)['user_info_list'] as $user) { + if (false === self::set($user)) { + return false; + } + if ($result['next_openid'] === $user['openid']) { + unset($result['next_openid']); + } + } + } + return empty($result['next_openid']) ? true : self::sync($result['next_openid']); + } + + /** + * 同步获取黑名单信息 + * @param string $next_openid + * @return bool + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public static function syncBlack($next_openid = '') + { + $wechat = WechatService::WeChatUser(); + $result = $wechat->getBlackList($next_openid); + foreach (array_chunk($result['data']['openid'], 100) as $openids) { + $info = $wechat->getBatchUserInfo($openids); + foreach ($info as $user) { + $user['is_black'] = '1'; + if (self::set($user) && $result['next_openid'] === $user['openid']) { + unset($result['next_openid']); + } + } + } + return empty($result['next_openid']) ? true : self::syncBlack($result['next_openid']); + } + +} \ No newline at end of file diff --git a/application/wechat/service/MediaService.php b/application/wechat/service/MediaService.php new file mode 100644 index 000000000..c662c1fb8 --- /dev/null +++ b/application/wechat/service/MediaService.php @@ -0,0 +1,128 @@ +where(['id' => $id])->where($where)->find(); + $article_ids = explode(',', $data['article_id']); + $articles = Db::name('WechatNewsArticle')->whereIn('id', $article_ids)->select(); + $data['articles'] = []; + foreach ($article_ids as $article_id) { + foreach ($articles as $article) { + if (intval($article['id']) === intval($article_id)) { + unset($article['create_by'], $article['create_at']); + $data['articles'][] = $article; + } + } + } + return $data; + } + + /** + * 上传图片到微信服务器 + * @param string $local_url 图文地址 + * @return string + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public static function uploadImage($local_url) + { + $map = ['md5' => md5($local_url)]; + if (($media_url = Db::name('WechatNewsImage')->where($map)->value('media_url'))) { + return $media_url; + } + $info = WechatService::WeChatMedia()->uploadImg(self::getServerPath($local_url)); + if (strtolower(sysconf('wechat_type')) === 'thr') { + WechatService::wechat()->rmFile($local_url); + } + $data = ['local_url' => $local_url, 'media_url' => $info['url'], 'md5' => $map['md5']]; + DataService::save('WechatNewsImage', $data, 'md5'); + return $info['url']; + } + + /** + * 上传图片永久素材,返回素材media_id + * @param string $local_url 文件URL地址 + * @param string $type 文件类型 + * @param array $video_info 视频信息 + * @return string|null + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public static function uploadForeverMedia($local_url, $type = 'image', $video_info = []) + { + $map = ['md5' => md5($local_url), 'appid' => WechatService::getAppid()]; + if (($media_id = Db::name('WechatNewsMedia')->where($map)->value('media_id'))) { + return $media_id; + } + $result = WechatService::WeChatMedia()->addMaterial(self::getServerPath($local_url), $type, $video_info); + if (strtolower(sysconf('wechat_type')) === 'thr') { + WechatService::wechat()->rmFile($local_url); + } + $data = ['md5' => $map['md5'], 'type' => $type, 'appid' => $map['appid'], 'media_id' => $result['media_id'], 'local_url' => $local_url]; + isset($result['url']) && $data['media_url'] = $result['url']; + DataService::save('WechatNewsMedia', $data, 'md5', ['appid' => $map['appid'], 'type' => $type]); + return $data['media_id']; + } + + /** + * 文件位置处理 + * @param string $local + * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + protected static function getServerPath($local) + { + switch (strtolower(sysconf('wechat_type'))) { + case 'api': + if (file_exists($local)) { + return $local; + } + return FileService::download($local)['file']; + case 'thr': + return WechatService::wechat()->upFile(base64_encode(file_get_contents($local)), $local)['file']; + default: + return $local; + } + } + +} \ No newline at end of file diff --git a/application/wechat/service/TagsService.php b/application/wechat/service/TagsService.php new file mode 100644 index 000000000..d2aa784ba --- /dev/null +++ b/application/wechat/service/TagsService.php @@ -0,0 +1,69 @@ +getUserTagId($openid); + if (!is_array($tagsid)) { + return false; + } + $data = ['openid' => $openid, 'tagid_list' => join(',', $tagsid)]; + return DataService::save('WechatFans', $data, 'openid', ['appid' => sysconf('wechat_appid')]); + } + + /** + * 从微信服务器获取所有标签 + * @return bool + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public static function sync() + { + $appid = WechatService::getAppid(); + $result = WechatService::WeChatTags()->getTags(); + Db::name('WechatFansTags')->where(['appid' => $appid])->delete(); + foreach (array_chunk($result['tags'], 100) as $list) { + foreach ($list as &$vo) { + $vo['appid'] = $appid; + } + Db::name('WechatFansTags')->insertAll($list); + } + return true; + } + +} \ No newline at end of file diff --git a/application/wechat/view/api/script/index.js b/application/wechat/view/api/script/index.js new file mode 100644 index 000000000..5fb7b054f --- /dev/null +++ b/application/wechat/view/api/script/index.js @@ -0,0 +1,10 @@ +if (typeof wx === 'object') { + wx.openid = '{$fansinfo.openid|default=""}'; + wx.unionid = '{$fansinfo.unionid|default=""}'; + wx.fansinfo = eval('({$fansinfo|default=[]|json_encode=###,256|raw})'); + wx.config(eval('({$jssdk|default=[]|json_encode=###,256|raw})')); + wx.ready(function () { + wx.hideOptionMenu(); + wx.hideAllNonBaseMenuItem(); + }); +} diff --git a/application/wechat/view/api/tools/jssdk.html b/application/wechat/view/api/tools/jssdk.html new file mode 100644 index 000000000..4c592a2aa --- /dev/null +++ b/application/wechat/view/api/tools/jssdk.html @@ -0,0 +1,42 @@ + + + + JSSDK 功能测试 + + + + + + + + + + + +
    +

    JSSDK 功能测试

    +
    + + + + + + + diff --git a/application/wechat/view/api/tools/oauth.html b/application/wechat/view/api/tools/oauth.html new file mode 100644 index 000000000..96df8f291 --- /dev/null +++ b/application/wechat/view/api/tools/oauth.html @@ -0,0 +1,66 @@ + + + + + 微信网页授权测试 + + + + + + + + + + +
    +
    +
    +

    操作失败

    +

    通过网页授权获取用户资料失败,请检查权限再试!

    +
    + +
    + +
    + +

    {$fans.fansinfo.nickname}

    +
    +
    用户标识
    +
    +
    +
    OPENID
    +
    {$fans.fansinfo.openid}
    +
    +
    +
    UNIONID
    +
    {$fans.fansinfo.unionid|default='未获取到'}
    +
    +
    +
    详细资料
    +
    +
    +
    性别
    +
    {:[1=>'男',2=>'女'][$fans.fansinfo.sex]??'未知'}
    +
    +
    +
    系统语言
    +
    {$fans.fansinfo.language}
    +
    +
    +
    所在区域
    +
    {$fans.fansinfo.country}{$fans.fansinfo.province}{$fans.fansinfo.city}
    +
    + {if isset($fans.fansinfo.privilege.0)} +
    +
    设备网络
    +
    {$fans.fansinfo.privilege.0|default='未获取到网络信息'}
    +
    + {/if} +
    + + \ No newline at end of file diff --git a/application/wechat/view/config.index.html b/application/wechat/view/config.index.html deleted file mode 100644 index 2c5e6d85a..000000000 --- a/application/wechat/view/config.index.html +++ /dev/null @@ -1,72 +0,0 @@ -{extend name="extra@admin/content"} - -{block name="content"} -
    - -
    - -
    - -

    - 请复制此URL地址填写在公众号平台 [ 开发 >> 基本配置 ] 中 [ URL ( 服务器地址 ) ] -
    注意:URL主域名必需备案,微信服务接口只支持 80 端口 ( http ) 和 443 端口 ( https ) -

    -
    -
    - -
    - -
    - -
    - -

    - 公众号应用ID是所有接口必要参数,可以在公众号平台 [ 开发 >> 基本配置 ] 页面获取。 -

    -
    -
    - - -
    - -
    - -

    - 公众号应用密钥是所有接口必要参数,可以在公众号平台 [ 开发 >> 基本配置 ] 页面授权后获取。 -

    -
    -
    - - -
    - -
    - -

    - 公众号平台与系统对接认证Token,请优先填写此参数并保存,然后再在微信公众号平台操作对接。 -

    -
    -
    - - -
    - -
    - -

    - 公众号平台接口设置为加密模式,消息加密密钥必需填写并保持与公众号平台一致。 -

    -
    -
    - -
    - -
    -
    - -
    -
    - -
    - -{/block} diff --git a/application/wechat/view/config.pay.html b/application/wechat/view/config.pay.html deleted file mode 100644 index 40746bb83..000000000 --- a/application/wechat/view/config.pay.html +++ /dev/null @@ -1,132 +0,0 @@ -{extend name="extra@admin/content"} - -{block name="style"} - -{/block} - -{block name="content"} -
    - -
    - -
    - -

    - 注意:商户ID必需与微信接口配置公众号APPID对应,否则无法使用支付功能! -

    -
    -
    - -
    - -
    - -

    - 微信支付商户密钥需要在商户平台配置,必需填写密钥之后才能正常使用微信支付功能。 -

    -
    -
    - -
    - -
    -
    - - - - -
    -

    - 企业打款、企业红包、订单退款等操作需要使用双向证书,可在微信商户平台下载证书! -

    -
    -
    - -
    - -
    -
    - - -
    -
    - -
    -{/block} - -{block name="script"} - -{/block} diff --git a/application/wechat/view/config/index.html b/application/wechat/view/config/index.html new file mode 100644 index 000000000..8c616db8c --- /dev/null +++ b/application/wechat/view/config/index.html @@ -0,0 +1,168 @@ +{extend name="admin@public/content"} + +{block name="content"} +
    + +
    + +
    + {php} + $wechat_type=sysconf('wechat_type')?sysconf('wechat_type'):'api'; + $wechat_type=request()->get('appkey')?'thr':$wechat_type; + {/php} + {foreach ['api'=>'普通接口参数','thr'=>'第三方授权对接'] as $k=>$v} + + {/foreach} +

    如果使用第三方授权对接,需要 ThinkService 项目的支持。

    +
    +
    + +
    + +
    +
    WxTest
    功能测试
    +
    +
    + +

    网页授权

    +
    +
    + +

    JSSDK签名

    +
    +
    + +

    JSAPI支付

    +
    +
    + +

    扫码支付①

    +
    +
    + +

    扫码支付②

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

    公众号平台与系统对接认证Token,请优先填写此参数并保存,然后再在微信公众号平台操作对接。

    +
    +
    +
    + +
    + +

    公众号应用ID是所有接口必要参数,可以在公众号平台 [ 开发 > 基本配置 ] 页面获取。

    +
    +
    +
    + +
    + +

    公众号应用密钥是所有接口必要参数,可以在公众号平台 [ 开发 > 基本配置 ] 页面授权后获取。

    +
    +
    +
    + +
    + +

    公众号平台接口设置为加密模式,消息加密密钥必需填写并保持与公众号平台一致。

    +
    +
    +
    + +
    + +

    公众号服务平台接口通知URL, 公众号消息接收与回复等。

    +
    +
    +
    +
    + + +
    + +
    +
    QRCode
    公众号二维码
    +
    +
    + +
    +
    +

    微信昵称:{$wechat.nick_name}

    +

    微信类型:{if $wechat.service_type eq 2}服务号{elseif $wechat.service_type eq 3}小程序{else}订阅号{/if} / + {$wechat.verify_type_info == -1 ? '未认证' : '已认证'}

    +

    注册公司:{$wechat.principal_name}

    +

    授权绑定:{$wechat.create_at|format_datetime}

    +
    +
    +
    +
    + +
    + +
    + +

    点击连接将跳转到微信第三方平台进行公众号授权。

    +
    +
    +
    +
    + +
    + +

    公众号 appid 通过微信第三方授权自动获取. 若没有值请进行微信第三方授权。

    +
    +
    +
    + +
    + +

    公众号服务平台接口密钥, 通过微信第三方授权自动获取, 若没有值请进行微信第三方授权。

    +
    +
    +
    + +
    + +

    公众号服务平台接口通知URL, 公众号消息接收与回复等。

    +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    + + + +{/block} diff --git a/application/wechat/view/fans.back.html b/application/wechat/view/fans.back.html deleted file mode 100644 index 78999c3c4..000000000 --- a/application/wechat/view/fans.back.html +++ /dev/null @@ -1,204 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="button"} - -
    - -
    - -
    - -
    - -{/block} - -{block name="content"} - - - - - -
    - - - - - - - - - - - - - - {foreach $list as $key=>$vo} - - - - - - - - - {/foreach} - {if empty($list)} - - {/if} - -
    - - 用户昵称性别标签区域关注时间
    - - - - {$vo.nickname|default="未设置微信昵称"} - {$vo.sex==1?'男':($vo.sex==2?'女':'未知')} - - + - - - {if empty($vo.tags_list)} - 尚未设置标签 - {else} - {foreach $vo.tags_list as $k=>$tag} - - {$tag} - - {/foreach} - {/if} - - {$vo.country|default='未设置区域信息'}{$vo.province}{$vo.city}{$vo.subscribe_at}
    没 有 记 录 了 哦 !
    - {if isset($page)}

    {$page}

    {/if} -
    - -
    -
    -
    - {foreach $tags as $key=>$tag} -
    - -
    - {/foreach} -
    -
    -
    - - -
    -
    -
    -{/block} - -{block name="script"} -{if auth("$classuri/tagset")} - -{/if} -{/block} diff --git a/application/wechat/view/fans.index.html b/application/wechat/view/fans.index.html deleted file mode 100644 index 8068fde83..000000000 --- a/application/wechat/view/fans.index.html +++ /dev/null @@ -1,204 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="button"} - -
    - -
    - -
    - -
    - -{/block} - -{block name="content"} - - - - - -
    - - - - - - - - - - - - - - {foreach $list as $key=>$vo} - - - - - - - - - {/foreach} - {if empty($list)} - - {/if} - -
    - - 用户昵称性别标签区域关注时间
    - - - - {$vo.nickname|default="未设置微信昵称"} - {$vo.sex==1?'男':($vo.sex==2?'女':'未知')} - - + - - - {if empty($vo.tags_list)} - 尚未设置标签 - {else} - {foreach $vo.tags_list as $k=>$tag} - - {$tag} - - {/foreach} - {/if} - - {$vo.country|default='未设置区域信息'}{$vo.province}{$vo.city}{$vo.subscribe_at}
    没 有 记 录 了 哦 !
    - {if isset($page)}

    {$page}

    {/if} -
    - -
    -
    -
    - {foreach $tags as $key=>$tag} -
    - -
    - {/foreach} -
    -
    -
    - - -
    -
    -
    -{/block} - -{block name="script"} -{if auth("$classuri/tagset")} - -{/if} -{/block} diff --git a/application/wechat/view/fans/index.html b/application/wechat/view/fans/index.html new file mode 100644 index 000000000..1b0c3a64b --- /dev/null +++ b/application/wechat/view/fans/index.html @@ -0,0 +1,84 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + +{/block} + +{block name="content"} + +{include file='wechat@fans/search_inc'} + + +
    + +

    没 有 记 录 哦!

    + + + + + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + + + {/foreach} + +
    + + 用户昵称性别标签区域关注时间
    + + + + {$vo.nickname|default='未设置微信昵称'} + + {$vo.sex==1?'男':($vo.sex==2?'女':'未知')} + + {if auth("$classuri/tagset")} + + + {/if} + {if empty($vo.tags_list)} + 尚未设置标签 + {else} + {foreach $vo.tags_list as $k=>$tag}{$tag}{/foreach} + {/if} + + {$vo.country|default='未设置区域信息'|raw}{$vo.province}{$vo.city} + {$vo.subscribe_at|format_datetime} + {if auth("$classuri/backdel")}拉黑{/if} +
    + {if isset($page)}

    {$page|raw}

    {/if} + +
    +{/block} + +{block name="script"} + + + + +{if auth("$classuri/tagset")}{include file='wechat@fans/tags_inc'}{/if} +{/block} diff --git a/application/wechat/view/fans/search_inc.html b/application/wechat/view/fans/search_inc.html new file mode 100644 index 000000000..a6d3afc7f --- /dev/null +++ b/application/wechat/view/fans/search_inc.html @@ -0,0 +1,74 @@ + \ No newline at end of file diff --git a/application/wechat/view/fans/tags_inc.html b/application/wechat/view/fans/tags_inc.html new file mode 100644 index 000000000..f40a297f1 --- /dev/null +++ b/application/wechat/view/fans/tags_inc.html @@ -0,0 +1,82 @@ + + +
    +
    +
    + {foreach $tags as $key=>$tag} +
    + +
    + {/foreach} +
    +
    +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/application/wechat/view/fans_block/index.html b/application/wechat/view/fans_block/index.html new file mode 100644 index 000000000..86b8359bd --- /dev/null +++ b/application/wechat/view/fans_block/index.html @@ -0,0 +1,84 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + +{/block} + +{block name="content"} + +{include file='wechat@fans/search_inc'} + + +
    + +

    没 有 记 录 哦!

    + + + + + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + + + {/foreach} + +
    + + 用户昵称性别用户标签所在区域关注时间操作
    + + + + {$vo.nickname|default='未设置微信昵称'} + + {$vo.sex==1?'男':($vo.sex==2?'女':'未知')} + + {if auth("$classuri/tagset")} + + + {/if} + {if empty($vo.tags_list)} + 尚未设置标签 + {else} + {foreach $vo.tags_list as $k=>$tag}{$tag}{/foreach} + {/if} + + {$vo.country|default='未设置区域信息'|raw}{$vo.province}{$vo.city} + {$vo.subscribe_at|format_datetime} + {if auth("$classuri/backdel")}移出黑名单{/if} +
    + {if isset($page)}

    {$page|raw}

    {/if} + +
    +{/block} + +{block name="script"} + + + +{if auth("$classuri/tagset")}{include file='wechat@fans/tags_inc'}{/if} +{/block} + diff --git a/application/wechat/view/keys.form.html b/application/wechat/view/keys.form.html deleted file mode 100644 index 8d33ec24b..000000000 --- a/application/wechat/view/keys.form.html +++ /dev/null @@ -1,296 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="style"} - -{/block} - -{block name="content"} - - -
    -
    公众号
    -
    - -
    -
    - - -
    -
    -
    -
    - {$title} -
    - {if !isset($vo.keys) or ($vo.keys neq 'default' and $vo.keys neq 'subscribe')} -
    - -
    - -
    -
    - {else} -
    - -
    -
    - {if !isset($vo.status) or $vo.status neq 0} - - - {else} - - - {/if} -
    -
    -
    - {/if} - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - 选择图文 - -
    -
    - -
    - -
    - -

    文件最大2Mb,支持bmp/png/jpeg/jpg/gif格式

    - - 上传图片 -
    -
    - -
    - -
    -
    - - -
    -

    文件最大2Mb,播放长度不超过60s,mp3/wma/wav/amr格式

    -
    -
    - -
    - -
    - -
    -
    -
    - -
    -
    - - -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    - - -

    文件最大64KB,只支持JPG格式

    - - 上传图片 -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    - - -
    -

    文件最大10MB,只支持MP4格式

    -
    -
    - -
    - -
    - -
    -
    - -
    -
    - - {if !isset($vo.keys) || !in_array($vo.keys,['default','subscribe'])} - - {/if} -
    - - {if isset($vo['id'])}{/if} -
    -
    -
    -
    -
    -{/block} - -{block name="script"} - -{/block} \ No newline at end of file diff --git a/application/wechat/view/keys.index.html b/application/wechat/view/keys.index.html deleted file mode 100644 index 6345563ff..000000000 --- a/application/wechat/view/keys.index.html +++ /dev/null @@ -1,188 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="button"} -
    - - -
    -{/block} - -{block name='content'} -
    - - - - - - - - - - - - - - - - {foreach $list as $key=>$vo} - - - - - - - - - - - {/foreach} - -
    - - - - 关键字回复类型回复内容更新时间状态操作
    - - - - {$vo.keys}{$vo.type} - {if $vo.type eq '音乐'} - 预览 - - {elseif $vo.type eq '文字'} - 预览 - - {elseif $vo.type eq '图片'} - 预览 - - {elseif $vo.type eq '图文'} - 预览 - - {elseif $vo.type eq '视频'} - 预览 - - {else} - {$vo.content} - {/if} - {$vo.create_at} - {if $vo.status eq 0} - 已禁用 - {elseif $vo.status eq 1} - 使用中 - {/if} - - - {if auth("$classuri/edit")} - | - 编辑 - {/if} - - {if $vo.status eq 1 and auth("$classuri/forbid")} - | - 禁用 - {elseif auth("$classuri/resume")} - | - 启用 - {/if} - - {if auth("$classuri/del")} - | - 删除 - {/if} - -
    - {if isset($page)}

    {$page}

    {/if} -
    - -{/block} - -{block name="script"} - -{/block} \ No newline at end of file diff --git a/application/wechat/view/keys/form.html b/application/wechat/view/keys/form.html new file mode 100644 index 000000000..b45e7b8b3 --- /dev/null +++ b/application/wechat/view/keys/form.html @@ -0,0 +1,263 @@ +{extend name='admin@public/content'} + +{block name="style"} + +{/block} + +{block name="content"} + + +
    +
    公众号
    +
    + +
    +
    + + +
    +
    +
    +
    + {$title} + +
    + +
    + +
    +
    + + + +
    + +
    +
    + {foreach ['1'=>'启用','0'=>'禁用'] as $k=>$v} + + {/foreach} +
    +
    +
    +
    + +
    + {foreach ['text'=>'文字','news'=>'图文','image'=>'图片','music'=>'音乐','video'=>'视频'] as $k=>$v} + + {/foreach} +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + 选择图文 + +
    +
    + +
    + +
    + +

    文件最大2Mb,支持bmp/png/jpeg/jpg/gif格式

    + + 上传图片 +
    +
    + +
    + +
    +
    + + +
    +

    文件最大2Mb,播放长度不超过60s,mp3/wma/wav/amr格式

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

    文件最大64KB,只支持JPG格式

    + + 上传图片 +
    +
    + +
    + +
    + +
    +
    +
    + +
    +
    + + +
    +

    文件最大10MB,只支持MP4格式

    +
    +
    +
    + +
    + +
    +
    + +
    +
    + + + + +
    + {if isset($vo['id'])}{/if} + +
    +
    +
    +
    +{/block} + +{block name="script"} + +{/block} \ No newline at end of file diff --git a/application/wechat/view/keys/index.html b/application/wechat/view/keys/index.html new file mode 100644 index 000000000..e68afae9b --- /dev/null +++ b/application/wechat/view/keys/index.html @@ -0,0 +1,187 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + + + + +{/block} + +{block name='content'} +
    + +

    没 有 记 录 哦!

    + + + + + + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + + + + {/foreach} + +
    + + + + 关键字类型预览添加时间状态
    + + + + + {if !empty($vo.qrc)}{/if} + {$vo.keys} + {$vo.type} + {if $vo.type eq '音乐'} + 预览 + {elseif $vo.type eq '文字'} + 预览 + {elseif $vo.type eq '图片'} + 预览 + {elseif $vo.type eq '图文'} + 预览 + {elseif $vo.type eq '视频'} + 预览 + {else} + {$vo.content} + {/if} + {$vo.create_at|format_datetime} + {if $vo.status eq 0}已禁用{elseif $vo.status eq 1}使用中{/if} + + + {if auth("$classuri/edit")} + | + 编辑 + {/if} + + {if $vo.status eq 1 and auth("$classuri/forbid")} + | + 禁用 + {elseif auth("$classuri/resume")} + | + 启用 + {/if} + + {if auth("$classuri/del")} + | + 删除 + {/if} + +
    + {if isset($page)}

    {$page|raw}

    {/if} + +
    + +{/block} + +{block name="script"} + +{/block} \ No newline at end of file diff --git a/application/wechat/view/menu.index.html b/application/wechat/view/menu.index.html deleted file mode 100644 index 19c0591d8..000000000 --- a/application/wechat/view/menu.index.html +++ /dev/null @@ -1,311 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="style"} - -{/block} - -{block name='content'} -
    -
    公众号
    -
    - -
    -
    - -
    - - -
    -
    - {if auth("$classuri/edit")} - - {/if} - {if auth("$classuri/cancel")} - - {/if} -
    -{/block} - -{block name="script"} - -{/block} \ No newline at end of file diff --git a/application/wechat/view/menu/index.html b/application/wechat/view/menu/index.html new file mode 100644 index 000000000..0d077e224 --- /dev/null +++ b/application/wechat/view/menu/index.html @@ -0,0 +1,362 @@ +{extend name='admin@public/content'} + +{block name='content'} +
    +
    公众号
    +
    + +
    +
    + +
    + + + + + +
    + + +{/block} + +{block name="script"} + +{/block} + +{block name="style"} + +{/block} diff --git a/application/wechat/view/news.form.html b/application/wechat/view/news.form.html deleted file mode 100644 index 40098a8e7..000000000 --- a/application/wechat/view/news.form.html +++ /dev/null @@ -1,279 +0,0 @@ -{extend name='extra@admin/content'} - -{block name='content'} - - -
    -
    图文列表
    -
    - {if empty($vo['articles']) eq false} - {foreach $vo.articles as $key=>$value} -
    - - {$value.title} -
    -
    - {/foreach} - {else} -
    - - -
    -
    - {/if} - -
    -
    - - - -
    -
    图文内容编辑
    -
    -
    - -
    -
    -
    - 标题 - - -
    -
    -
    - -
    -
    -
    - 作者 - -
    -
    -
    - -
    -
    -
    - -
    -
    -
    - -
    -
    -
    -
    - 上传图片 - 选择图片 -

    - -
    -
    -

    封面大图片建议尺寸:900像素 * 500像素

    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - - -
    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - - -{/block} - - -{block name='script'} - -{/block} - -{block name="style"} - -{/block} \ No newline at end of file diff --git a/application/wechat/view/news.image.html b/application/wechat/view/news.image.html deleted file mode 100644 index 6c44c64cb..000000000 --- a/application/wechat/view/news.image.html +++ /dev/null @@ -1,28 +0,0 @@ -{extend name='extra@admin/main'} - -{block name="body"} - -
    - {foreach $list as $key=>$vo} - - {/foreach} -
    -
    - -{if isset($page)}{$page}{/if} - -{/block} diff --git a/application/wechat/view/news.index.html b/application/wechat/view/news.index.html deleted file mode 100644 index 8360680a6..000000000 --- a/application/wechat/view/news.index.html +++ /dev/null @@ -1,88 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="button"} -
    - -
    -{/block} - -{block name='content'} - -
    - {foreach $list as $vo} -
    - - {foreach $vo.articles as $k => $v} - {if $k < 1} -
    - {if $v.title}

    {$v.title}

    {/if} -
    -
    - {else} -
    -
    {$v.title}
    -
    -
    -
    - {/if} - {/foreach} -
    - {/foreach} -
    -
    -{if isset($page)}

    {$page}

    {/if} - -{/block} - -{block name='script'} - -{/block} - -{block name="style"} - -{/block} \ No newline at end of file diff --git a/application/wechat/view/news.push.html b/application/wechat/view/news.push.html deleted file mode 100644 index 8d633c184..000000000 --- a/application/wechat/view/news.push.html +++ /dev/null @@ -1,112 +0,0 @@ -
    - -
    -
    微信图文
    -
    - {foreach $vo.articles as $key=>$value} -
    -
    - {$value.title} -
    -
    - {/foreach} -
    -
    - -
    -
    指定粉丝标签推送 全选
    -
    - - {foreach $fans_tags as $tag} - - {/foreach} - - {literal} - - {/literal} - - -
    -
    -
    -
    - -
    - - -
    - -
    - - \ No newline at end of file diff --git a/application/wechat/view/news.select.html b/application/wechat/view/news.select.html deleted file mode 100644 index bf13760a5..000000000 --- a/application/wechat/view/news.select.html +++ /dev/null @@ -1,62 +0,0 @@ -{extend name='extra@admin/main'} - -{block name="body"} -
    - {foreach $list as $key=>$vo} -
    - {foreach $vo.articles as $key=>$value} -
    -
    - image -
    - {$value.title} -
    - {/foreach} -
    - {/foreach} -
    - -{if isset($page)}

    {$page}

    {/if} - -{/block} - -{block name='style'} - -{/block} - -{block name="script"} - -{/block} \ No newline at end of file diff --git a/application/wechat/view/news/form.html b/application/wechat/view/news/form.html new file mode 100644 index 000000000..ced4f6e57 --- /dev/null +++ b/application/wechat/view/news/form.html @@ -0,0 +1,361 @@ +{extend name='admin@public/content'} + +{block name='content'} +
    + +
    +
    图文列表
    +
    +
    +
    + + + + +
    +
    +
    +
    + + + + +
    +
    + + + +
    +
    + + +
    +
    图文内容编辑
    +
    +
    + +
    +
    +
    + 文章标题 + +
    +
    +
    + +
    +
    +
    + 文章作者 + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + + +
    + +
    +
    +

    封面大图片建议尺寸:900像素 * 500像素

    +
    +
    +
    + +
    +
    +
    + +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +{/block} + + +{block name='script'} + +{/block} + +{block name="style"} + +{/block} \ No newline at end of file diff --git a/application/wechat/view/news/image.html b/application/wechat/view/news/image.html new file mode 100644 index 000000000..fc4a13b33 --- /dev/null +++ b/application/wechat/view/news/image.html @@ -0,0 +1,42 @@ +{extend name='admin@public/main'} + +{block name='style'} + +{/block} + +{block name="body"} +
    + {foreach $list as $key=>$vo} + + {/foreach} +
    +
    +
    +
    + {if isset($page)}{$page|raw}{/if} +
    +{/block} + +{block name="script"} + +{/block} diff --git a/application/wechat/view/news/index.html b/application/wechat/view/news/index.html new file mode 100644 index 000000000..869fe7208 --- /dev/null +++ b/application/wechat/view/news/index.html @@ -0,0 +1,168 @@ +{extend name='admin@public/content'} + +{block name="button"} + +{/block} + +{block name='content'} + +
    + {foreach $list as $vo} +
    + + {foreach $vo.articles as $k => $v} + {if $k < 1} +
    + {if $v.title}

    {$v.title}

    {/if} +
    +
    + {else} +
    +
    {$v.title}
    +
    +
    +
    + {/if} + {/foreach} +
    + {/foreach} +
    + {if empty($list)} +

    没 有 记 录 哦!

    + {/if} +
    +{if isset($page)}

    {$page|raw}

    {/if} + +{/block} + +{block name='script'} + +{/block} + +{block name="style"} + +{/block} \ No newline at end of file diff --git a/application/wechat/view/news/push.html b/application/wechat/view/news/push.html new file mode 100644 index 000000000..67fba17b0 --- /dev/null +++ b/application/wechat/view/news/push.html @@ -0,0 +1,192 @@ +
    + +
    +
    微信图文
    +
    + {foreach $vo.articles as $key=>$value} +
    +
    + {$value.title} +
    +
    + {/foreach} +
    +
    + +
    +
    指定粉丝标签推送 全选
    +
    + + {foreach $fans_tags as $tag} + + {/foreach} + + {literal} + + {/literal} +
    +
    +
    +
    + +
    + + +
    + +
    + + + + \ No newline at end of file diff --git a/application/wechat/view/news/select.html b/application/wechat/view/news/select.html new file mode 100644 index 000000000..ebb2a4543 --- /dev/null +++ b/application/wechat/view/news/select.html @@ -0,0 +1,80 @@ +{extend name='admin@public/main'} + +{block name='style'} + +{/block} + +{block name="body"} +
    + {foreach $list as $vo} +
    + {foreach $vo.articles as $k => $v} + {if $k < 1} +
    + {if $v.title}

    {$v.title}

    {/if} +
    +
    + {else} +
    +
    {$v.title}
    +
    +
    +
    + {/if} + {/foreach} +
    + {/foreach} +
    + {if empty($list)}

    没 有 记 录 哦!

    {/if} +
    +
    +
    {if isset($page)}{$page|raw}{/if}
    +{/block} + + +{block name="script"} + +{/block} \ No newline at end of file diff --git a/application/wechat/view/review.index.html b/application/wechat/view/review/index.html similarity index 91% rename from application/wechat/view/review.index.html rename to application/wechat/view/review/index.html index 8011984ae..bc2f45c5d 100644 --- a/application/wechat/view/review.index.html +++ b/application/wechat/view/review/index.html @@ -1,178 +1,177 @@ - - - - - - - - - - - - - - - {if ($type eq 'text') or ($type eq 'image') or ($type eq 'music')} -
    -
    {:date('H:i')}
    -
    -
    - -
    -
    - {if $type eq 'text'} -
    -
    -
    - {$content|default=''} -
    - {elseif $type eq 'image'} -
    -
    -
    - -
    - {elseif $type eq 'music'} -
    -
    -
    - - - - - - - - -
    - {$title|default=''} - -
    -
    - -
    -
    - {$desc|default=''}           -
    -
    - {/if} -
    -
    -
    - {elseif $type eq 'article'} -
    -
    -
    {$vo.title|default=''}
    -
    -
    - {:date('Y-m-d',strtotime($vo['create_at']))} - {$vo.author|default=''} -
    -
    - {if $vo.show_cover_pic eq 1} -
    - {/if} -
    {$vo.content|default=''}
    - {if $vo.content_source_url} - - {/if} -
    -
    - {elseif $type eq 'video'} -
    -
    {:date('H:i')}
    -
    -
    -
    -
    -
    - {$title|default=''} -
    -
    {:date('m月d日')}
    -
    - -
    - -
    -
    -
    -
    -
    - - {elseif $type eq 'news'} -
    -
    {:date('H:i')}
    -
    -
    -
    - {if !empty($articles)} - {foreach $articles as $key=>$vo} - {if count($articles) gt 1} - {if $key < 1} -
    -
    -
    - {$vo.title|default=''} -
    -
    - {else} - - - - - -
    {$vo.title}
    - {/if} - {else} -
    -
    - {$vo.title|default=''} -
    -
    - {:date('m月d日')} -
    -
    -
    - {:str_replace([' ',"\n"],'',strip_tags($vo.digest))} ... -
    -
    - - {/if} - {/foreach} - {/if} -
    -
    -
    -
    - - {/if} - + + + + + + + + + + + + + + + {if ($type eq 'text') or ($type eq 'image') or ($type eq 'music')} +
    +
    {:date('H:i')}
    +
    +
    + +
    +
    + {if $type eq 'text'} +
    +
    +
    + {$content|default=''|raw|htmlspecialchars_decode} +
    + {elseif $type eq 'image'} +
    +
    +
    + +
    + {elseif $type eq 'music'} +
    +
    +
    + + + + + + + + +
    + {$title|default=''} + +
    +
    + +
    +
    + {$desc|default=''}           +
    +
    + {/if} +
    +
    +
    + {elseif $type eq 'article'} +
    +
    +
    {$vo.title|default=''}
    +
    +
    + {:date('Y-m-d',strtotime($vo['create_at']))} + {$vo.author|default=''} +
    +
    + {if $vo.show_cover_pic eq 1} +
    + {/if} +
    {$vo.content|default=''|raw}
    + {if $vo.content_source_url} + + {/if} +
    +
    + {elseif $type eq 'video'} +
    +
    {:date('H:i')}
    +
    +
    +
    +
    +
    + {$title|default=''} +
    +
    {:date('m月d日')}
    +
    + +
    + +
    +
    +
    +
    +
    + + {elseif $type eq 'news'} +
    +
    {:date('H:i')}
    +
    +
    +
    + {if !empty($articles)} + {foreach $articles as $key=>$vo} + {if count($articles) gt 1} + {if $key < 1} +
    +
    +
    + {$vo.title|default=''} +
    +
    + {else} + + + + + +
    {$vo.title}
    + {/if} + {else} +
    +
    + {$vo.title|default=''} +
    +
    + {:date('m月d日')} +
    +
    +
    + {:str_replace([' ',"\n"],'',strip_tags($vo.digest))} ... +
    +
    + + {/if} + {/foreach} + {/if} +
    +
    +
    +
    + + {/if} + \ No newline at end of file diff --git a/application/wechat/view/tags.index.html b/application/wechat/view/tags.index.html deleted file mode 100644 index ddf1026fd..000000000 --- a/application/wechat/view/tags.index.html +++ /dev/null @@ -1,89 +0,0 @@ -{extend name='extra@admin/content'} - -{block name="button"} -
    - - -
    -{/block} - -{block name="content"} - - - - - -
    - - - - - - - - - - - - - - {foreach $list as $key=>$vo} - - - - - - - - - {/foreach} - {if empty($list)} - - {/if} - -
    - - ID标签类型粉丝数操作
    - - {$vo.id|default='0'}{$vo.name|default=''}{$vo.id < 100 ? "系统标签" : "自定义标签"}{$vo.count|default=''} - - {if auth("$classuri/edit")} - {if $vo.id >= 100} - | - 编辑 - {else} - 编辑 - {/if} - {/if} - - {if auth("$classuri/del")} - | - {if $vo.id >= 100} - 删除 - {else} - 删除 - {/if} - {/if} - -
    没 有 记 录 了 哦 !
    - {if isset($page)}

    {$page}

    {/if} -
    -{/block} \ No newline at end of file diff --git a/application/wechat/view/tags.form.html b/application/wechat/view/tags/form.html similarity index 68% rename from application/wechat/view/tags.form.html rename to application/wechat/view/tags/form.html index 441699758..13c2bded3 100644 --- a/application/wechat/view/tags.form.html +++ b/application/wechat/view/tags/form.html @@ -1,22 +1,18 @@ -
    - -
    - -
    - -
    -
    - -
    - -
    - - {if isset($vo['id'])}{/if} - - - - - -
    - -
    + diff --git a/application/wechat/view/tags/index.html b/application/wechat/view/tags/index.html new file mode 100644 index 000000000..de6748978 --- /dev/null +++ b/application/wechat/view/tags/index.html @@ -0,0 +1,89 @@ +{extend name='admin@public/content'} + +{block name="button"} + + + + + + + + + + + + + +{/block} + +{block name="content"} + + + + +
    + {if empty($list)} +

    没 有 记 录 哦!

    + {else} + + + + + + + + + + + + + + {foreach $list as $key=>$vo} + + + + + + + + + {/foreach} + +
    + + ID标签名称标签类型粉丝数操作
    + + {$vo.id|default='0'}{$vo.name|default=''}{$vo.id < 100 ? "系统标签" : "自定义标签"}{$vo.count|default=''} + + {if auth("$classuri/edit")} + | + + 编辑 + + 编辑 + + {/if} + + {if auth("$classuri/del")} + | + + 删除 + + 删除 + + {/if} +
    + {if isset($page)}

    {$page|raw}

    {/if} + {/if} +
    +{/block} \ No newline at end of file diff --git a/build.cmd b/build.cmd new file mode 100644 index 000000000..c0d05a3e7 --- /dev/null +++ b/build.cmd @@ -0,0 +1,6 @@ +@echo off +@title Composer Plugs Update and Optimize +@rmdir /s/q vendor thinkphp +composer update --profile --prefer-dist --optimize-autoloader +composer dump-autoload --optimize +exit \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 000000000..53ab9a613 --- /dev/null +++ b/build.sh @@ -0,0 +1,37 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +LANG=en_US.UTF-8 + +export PATH + +echo " ++---------------------------------------------------------------------- +| ThinkAdmin environmental preparation tools ++---------------------------------------------------------------------- +| GtiHub : https://github.com/zoujingli/ThinkAdmin ++---------------------------------------------------------------------- +| document : https://www.kancloud.cn/zoujingli/thinkadmin/323614 ++---------------------------------------------------------------------- +" + +hasComposer=`command -v composer` + +echo -e "\033[34mConfirm the existence of the command....\033[0m" + +if [ ! -f "${hasComposer}" ]; then +echo -e "\033[31mComposer Not Found! Initialization cannot continue. \033[0m" +exit +fi + +echo -e "\033[34mClean up the running environment....\033[0m" +rm -rf ./vendor +rm -rf ./thinkphp +rm -rf ./composer.lock + +echo -e "\033[34mComposer install....\033[0m" +composer install --profile --prefer-dist --optimize-autoloader + +echo -e "\033[34mMake Autoload....\033[0m" +composer dump-autoload --optimize + +echo -e "\033[31mEnvironmental preparation success!\033[0m" \ No newline at end of file diff --git a/composer.json b/composer.json index d79dbe52c..7ef3a1898 100644 --- a/composer.json +++ b/composer.json @@ -1,36 +1,29 @@ -{ - "name": "topthink/think", - "description": "the new thinkphp framework", - "type": "project", - "keywords": [ - "framework", - "thinkphp", - "ORM" - ], - "homepage": "http://thinkphp.cn/", - "license": "Apache-2.0", - "authors": [ - { - "name": "liu21st", - "email": "liu21st@gmail.com" - } - ], - "require": { - "php": ">=5.4.0", - "qiniu/php-sdk": "^7.0", - "zoujingli/wechat-php-sdk": "dev-master", - "zoujingli/ip2region": "^1.0", - "topthink/framework": "^5.0", - "topthink/think-captcha": "^1.0", - "topthink/think-mongo": "^1.1", - "topthink/think-queue": "^1.0", - "endroid/qrcode": "^1.9", - "aliyuncs/oss-sdk-php": "^2.2" - }, - "extra": { - "think-path": "thinkphp" - }, - "config": { - "preferred-install": "dist" - } -} +{ + "type": "project", + "name": "zoujingli/thinkadmin", + "license": "MIT", + "homepage": "http://demo.thinkadmin.top", + "description": "ThinkAdmin Developer CMF", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com" + } + ], + "require": { + "php": ">=5.6.0", + "qiniu/php-sdk": "^7.2", + "endroid/qr-code": "^1.9", + "topthink/framework": "^5.1", + "zoujingli/ip2region": "^1.0", + "aliyuncs/oss-sdk-php": "^2.2", + "topthink/think-captcha": "^2.0", + "zoujingli/weopen-developer": "^1.1" + }, + "repositories": { + "packagist": { + "type": "composer", + "url": "https://packagist.laravel-china.org" + } + } +} diff --git a/application/tags.php b/config/app.php similarity index 58% rename from application/tags.php rename to config/app.php index e8f398496..c23ab0a8a 100644 --- a/application/tags.php +++ b/config/app.php @@ -1,7 +1,7 @@ [], - // 应用开始 - 'app_begin' => [], - // 模块初始化 - 'module_init' => [], - // 操作开始执行 - 'action_begin' => ['hook\\AccessAuth'], - // 视图内容过滤 - 'view_filter' => ['hook\\FilterView'], - // 日志写入 - 'log_write' => [], - // 应用结束 - 'app_end' => [], + // 应用调试模式 + 'app_debug' => true, + // 应用Trace调试 + 'app_trace' => false, + // URL参数方式 0 按名称成对解析 1 按顺序解析 + 'url_param_type' => 1, ]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 000000000..69782ed10 --- /dev/null +++ b/config/database.php @@ -0,0 +1,30 @@ + true, + // 数据库类型 + 'type' => 'mysql', + // 服务器地址 + 'hostname' => '127.0.0.1', + // 数据库名 + 'database' => 'admin_v3', + // 用户名 + 'username' => 'admin_v3', + // 密码 + 'password' => 'FbYBHcWKr2', + // 端口 + 'hostport' => '3306', +]; diff --git a/config/log.php b/config/log.php new file mode 100644 index 000000000..79dc06d57 --- /dev/null +++ b/config/log.php @@ -0,0 +1,19 @@ + 'file', + 'max_files' => '200', + 'apart_level' => ['error', 'sql'], +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 000000000..56b722b28 --- /dev/null +++ b/config/session.php @@ -0,0 +1,26 @@ + 'fw', + 'path' => $_path_, + 'name' => $_name_, + 'var_session_id' => $_name_, +]; \ No newline at end of file diff --git a/config/template.php b/config/template.php new file mode 100644 index 000000000..95db27061 --- /dev/null +++ b/config/template.php @@ -0,0 +1,25 @@ +root(); +$uriRoot = rtrim(preg_match('/\.php$/', $appRoot) ? dirname($appRoot) : $appRoot, '\\/'); + +return [ + // 定义模板替换字符串 + 'tpl_replace_string' => [ + '__APP__' => $appRoot, + '__ROOT__' => $uriRoot, + '__STATIC__' => $uriRoot . "/static", + ], +]; \ No newline at end of file diff --git a/config/wechat.php b/config/wechat.php new file mode 100644 index 000000000..099b03c7c --- /dev/null +++ b/config/wechat.php @@ -0,0 +1,21 @@ + 'https://service.thinkadmin.top', + // 下面参数用作微信支付 + 'appid' => 'wx60a43dd8161666d4', + 'mch_id' => '1332187001', + 'mch_key' => 'A82DC5BD1F3359081049C568D8502BC5', +]; \ No newline at end of file diff --git a/extend/controller/BasicAdmin.php b/extend/controller/BasicAdmin.php index 65ec789ea..66b015daa 100644 --- a/extend/controller/BasicAdmin.php +++ b/extend/controller/BasicAdmin.php @@ -1,7 +1,7 @@ table) : (is_string($dbQuery) ? Db::name($dbQuery) : $dbQuery); $pk = empty($pkField) ? ($db->getPk() ? $db->getPk() : 'id') : $pkField; $pkValue = $this->request->request($pk, isset($where[$pk]) ? $where[$pk] : (isset($extendData[$pk]) ? $extendData[$pk] : null)); // 非POST请求, 获取数据并显示表单页面 if (!$this->request->isPost()) { - $vo = ($pkValue !== null) ? array_merge((array) $db->where($pk, $pkValue)->where($where)->find(), $extendData) : $extendData; - if (false !== $this->_callback('_form_filter', $vo)) { + $vo = ($pkValue !== null) ? array_merge((array)$db->where($pk, $pkValue)->where($where)->find(), $extendData) : $extendData; + if (false !== $this->_callback('_form_filter', $vo, [])) { empty($this->title) || $this->assign('title', $this->title); return $this->fetch($tplFile, ['vo' => $vo]); } @@ -74,10 +68,13 @@ class BasicAdmin extends Controller { } // POST请求, 数据自动存库 $data = array_merge($this->request->post(), $extendData); - if (false !== $this->_callback('_form_filter', $data)) { + if (false !== $this->_callback('_form_filter', $data, [])) { $result = DataService::save($db, $data, $pk, $where); - if (false !== $this->_callback('_form_result', $result)) { - $result !== false ? $this->success('恭喜, 数据保存成功!', '') : $this->error('数据保存失败, 请稍候再试!'); + if (false !== $this->_callback('_form_result', $result, $data)) { + if ($result !== false) { + $this->success('恭喜, 数据保存成功!', ''); + } + $this->error('数据保存失败, 请稍候再试!'); } } } @@ -88,37 +85,55 @@ class BasicAdmin extends Controller { * @param bool $isPage 是启用分页 * @param bool $isDisplay 是否直接输出显示 * @param bool $total 总记录数 + * @param array $result 结果集 * @return array|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\Exception */ - protected function _list($dbQuery = null, $isPage = true, $isDisplay = true, $total = false) { + protected function _list($dbQuery = null, $isPage = true, $isDisplay = true, $total = false, $result = []) + { $db = is_null($dbQuery) ? Db::name($this->table) : (is_string($dbQuery) ? Db::name($dbQuery) : $dbQuery); // 列表排序默认处理 if ($this->request->isPost() && $this->request->post('action') === 'resort') { - $data = $this->request->post(); - unset($data['action']); - foreach ($data as $key => &$value) { - if (false === $db->where('id', intval(ltrim($key, '_')))->setField('sort', $value)) { - $this->error('列表排序失败, 请稍候再试'); + foreach ($this->request->post() as $key => $value) { + if (preg_match('/^_\d{1,}$/', $key) && preg_match('/^\d{1,}$/', $value)) { + list($where, $update) = [['id' => trim($key, '_')], ['sort' => $value]]; + if (false === Db::table($db->getTable())->where($where)->update($update)) { + $this->error('列表排序失败, 请稍候再试'); + } } } $this->success('列表排序成功, 正在刷新列表', ''); } // 列表数据查询与显示 if (null === $db->getOptions('order')) { - $fields = $db->getTableFields(['table' => $db->getTable()]); - in_array('sort', $fields) && $db->order('sort asc'); + in_array('sort', $db->getTableFields($db->getTable())) && $db->order('sort asc'); } - $result = array(); if ($isPage) { - $rowPage = intval($this->request->get('rows', cookie('rows'))); - cookie('rows', $rowPage >= 10 ? $rowPage : 20); - $page = $db->paginate($rowPage, $total, ['query' => $this->request->get()]); - $result['list'] = $page->all(); - $result['page'] = preg_replace(['|href="(.*?)"|', '|pagination|'], ['data-open="$1" href="javascript:void(0);"', 'pagination pull-right'], $page->render()); + $rows = intval($this->request->get('rows', cookie('page-rows'))); + cookie('page-rows', $rows = $rows >= 10 ? $rows : 20); + // 分页数据处理 + $query = $this->request->get(); + $page = $db->paginate($rows, $total, ['query' => $query]); + if (($totalNum = $page->total()) > 0) { + list($rowHTML, $curPage, $maxNum) = [[], $page->currentPage(), $page->lastPage()]; + foreach ([10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200] as $num) { + list($query['rows'], $query['page']) = [$num, '1']; + $url = url('@admin') . '#' . $this->request->baseUrl() . '?' . urldecode(http_build_query($query)); + $rowHTML[] = ""; + } + list($pattern, $replacement) = [['|href="(.*?)"|', '|pagination|'], ['data-open="$1"', 'pagination pull-right']]; + $html = "共 {$totalNum} 条记录,每页显示 条,共 {$maxNum} 页当前显示第 {$curPage} 页。"; + list($result['total'], $result['list'], $result['page']) = [$totalNum, $page->all(), $html . preg_replace($pattern, $replacement, $page->render())]; + } else { + list($result['total'], $result['list'], $result['page']) = [$totalNum, $page->all(), $page->render()]; + } } else { $result['list'] = $db->select(); } - if (false !== $this->_callback('_data_filter', $result['list']) && $isDisplay) { + if (false !== $this->_callback('_data_filter', $result['list'], []) && $isDisplay) { !empty($this->title) && $this->assign('title', $this->title); return $this->fetch('', $result); } @@ -128,12 +143,14 @@ class BasicAdmin extends Controller { /** * 当前对象回调成员方法 * @param string $method - * @param array|bool $data + * @param array|bool $data1 + * @param array|bool $data2 * @return bool */ - protected function _callback($method, &$data) { + protected function _callback($method, &$data1, $data2) + { foreach ([$method, "_" . $this->request->action() . "{$method}"] as $_method) { - if (method_exists($this, $_method) && false === $this->$_method($data)) { + if (method_exists($this, $_method) && false === $this->$_method($data1, $data2)) { return false; } } diff --git a/extend/controller/BasicApi.php b/extend/controller/BasicApi.php index 4712b2add..c46ec1b26 100644 --- a/extend/controller/BasicApi.php +++ b/extend/controller/BasicApi.php @@ -1,121 +1,65 @@ -request = is_null($request) ? Request::instance() : $request; - // 安全方法请求过滤 - if (in_array(strtolower($this->request->action()), ['response', 'setcache', 'getcache', 'delcache', '_empty'])) { - exit($this->response('禁止访问接口安全方法!', 'ACCESS_NOT_ALLOWED')->send()); - } - // 访问 Token 检测处理 - $this->token = $this->request->param('token', $this->request->header('token', false)); - if (empty($this->token) && !method_exists($this, $this->request->action())) { - exit($this->response('访问TOKEN失效,请重新授权!', 'ACCESS_TOKEN_FAILD')->send()); - } - } - - /** - * 输出返回数据 - * @param string $msg 提示消息内容 - * @param string $code 业务状态码 - * @param mixed $data 要返回的数据 - * @param string $type 返回类型 JSON XML - * @return Response - */ - public function response($msg, $code = 'SUCCESS', $data = [], $type = 'json') { - $result = ['msg' => $msg, 'code' => $code, 'data' => $data, 'token' => $this->token, 'dataType' => strtolower($type)]; - return Response::create($result, $type)->header(ToolsService::corsRequestHander())->code(200); - } - - /** - * 写入缓存 - * @param string $name 缓存标识 - * @param mixed $value 存储数据 - * @param int|null $expire 有效时间 0为永久 - * @return bool - */ - public function setCache($name, $value, $expire = null) { - return Cache::set("{$this->token}_{$name}", $value, $expire); - } - - /** - * 读取缓存 - * @param string $name 缓存标识 - * @param mixed $default 默认值 - * @return mixed - */ - public function getCache($name, $default = false) { - return Cache::get("{$this->token}_{$name}", $default); - } - - /** - * 删除缓存 - * @param string $name 缓存标识 - * @return bool - */ - public function delCache($name) { - return Cache::rm("{$this->token}_{$name}"); - } - - /** - * API接口调度 - * @return Response - */ - public function _empty() { - list($module, $controller, $action, $method) = explode('/', $this->request->path() . '///'); - if (!empty($module) && !empty($controller) && !empty($action) && !empty($method)) { - $action = ucfirst($action); - $Api = config('app_namespace') . "\\{$module}\\{$controller}\\{$action}Api"; - if (method_exists($Api, $method)) { - return $Api::$method($this); - } - return $this->response('访问的接口不存在!', 'API_NOT_FOUND'); - } - return $this->response('不符合标准的接口!', 'API_ERROR'); - } - -} +request = app('request'); + } + + /** + * 返回成功的操作 + * @param mixed $msg 消息内容 + * @param array $data 返回数据 + * @param integer $code 返回代码 + */ + protected function success($msg, $data = [], $code = 1) + { + ToolsService::success($msg, $data, $code); + } + + /** + * 返回失败的请求 + * @param mixed $msg 消息内容 + * @param array $data 返回数据 + * @param integer $code 返回代码 + */ + protected function error($msg, $data = [], $code = 0) + { + ToolsService::error($msg, $data, $code); + } + +} \ No newline at end of file diff --git a/extend/controller/BasicWechat.php b/extend/controller/BasicWechat.php index 552daec66..1e81becd9 100644 --- a/extend/controller/BasicWechat.php +++ b/extend/controller/BasicWechat.php @@ -1,7 +1,7 @@ url = $this->request->url(true); - // 网页授权,并获粉丝信息 - $this->assign('jsSign', load_wechat('script')->getJsSign($this->url)); - // 检查启用网页授权 - $this->checkAuth && $this->oAuth(); + protected function initMember() + { + $openid = $this->getOpenid(); + $this->member = Db::name('StoreMember')->where(['openid' => $openid])->find(); + if (empty($this->member)) { + MemberService::create(['openid' => $openid]); + $this->member = Db::name('StoreMember')->where(['openid' => $openid])->find(); + } + return $this->member; } /** - * 微信网页授权 - * @param bool $fullMode 获取完整 - * @return string + * 获取粉丝用户OPENID + * @return bool|string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - protected function oAuth($fullMode = true) { - // 本地开发调试用户 openid - if (in_array($this->request->host(), ['127.0.0.1', 'localhost'])) { - session('openid', 'oBWB3wWVNujb-PJlmPmxC5CBTNF0'); - } - // 检查缓存中 openid 信息是否完整 - if (($this->openid = session('openid'))) { - if (!$fullMode) { - return $this->openid; - } - $this->fansinfo = WechatService::getFansInfo($this->openid); - if (!empty($this->fansinfo) && $this->fansinfo['expires_in'] > time()) { - $this->assign('fansinfo', $this->fansinfo); - return $this->openid; - } - } - // 发起微信网页授权 - $wxoauth_url = $this->url; - if (!($redirect_url = $this->request->get('redirectcode', false, 'decode'))) { - $split = stripos($this->url, '?') === false ? '?' : '&'; - $wxoauth_url = "{$this->url}{$split}redirectcode=" . encode($this->url); - } - // 微信网页授权处理 - $wechat = &load_wechat('Oauth'); - if (!$this->request->get('code', false)) { - $this->redirect($wechat->getOauthRedirect($wxoauth_url, 'webOauth', 'snsapi_base')); - } - if (FALSE === ($result = $wechat->getOauthAccessToken()) || empty($result['openid'])) { - Log::error("微信网页授权失败, {$wechat->errMsg}[{$wechat->errCode}]"); - $this->error("微信网页授权失败, {$wechat->errMsg}[{$wechat->errCode}]"); - } - session('openid', $this->openid = $result['openid']); - empty($fullMode) && $this->redirect($redirect_url); - // 微信粉丝信息处理 - $this->fansinfo = WechatService::getFansInfo($this->openid); - if (empty($this->fansinfo['expires_in']) || intval($this->fansinfo['expires_in']) < time()) { - /* 使用普通授权, 获取用户资料; 未关注时重新使用高级授权 */ - if ($result['scope'] === 'snsapi_base') : - $user = load_wechat('User')->getUserInfo($this->openid); - empty($user['subscribe']) && $this->redirect($wechat->getOauthRedirect($wxoauth_url, 'webOauth', 'snsapi_userinfo')); - /* 使用高级授权, 获取完整用户资料 */ - elseif ($result['scope'] === 'snsapi_userinfo') : - $user = $wechat->getOauthUserinfo($result['access_token'], $this->openid); - endif; - /* 授权结果处理, 更新粉丝信息 */ - if ((empty($user) || !array_key_exists('nickname', $user))) : - Log::error("微信网页授权获取用户信息失败, {$wechat->errMsg}[{$wechat->errCode}]"); - $this->error("微信网页授权获取用户信息失败, {$wechat->errMsg}[{$wechat->errCode}]"); - endif; - $user['expires_in'] = $result['expires_in'] + time() - 100; - $user['refresh_token'] = $result['refresh_token']; - $user['access_token'] = $result['access_token']; - WechatService::setFansInfo($user, $wechat->appid) or $this->error('微信网页授权用户保存失败!'); - } - $this->redirect($redirect_url); + protected function getOpenid() + { + $url = $this->request->url(true); + return WechatService::webOauth($url, 0)['openid']; + } + + /** + * 获取微信粉丝信息 + * @return bool|array + * @throws \Exception + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + protected function getFansinfo() + { + $url = $this->request->url(true); + return WechatService::webOauth($url, 1)['fansinfo']; } } diff --git a/extend/hook/AccessAuth.php b/extend/hook/AccessAuth.php deleted file mode 100644 index 957be9282..000000000 --- a/extend/hook/AccessAuth.php +++ /dev/null @@ -1,63 +0,0 @@ - - * @date 2017/05/12 11:59 - */ -class AccessAuth { - - /** - * 当前请求对象 - * @var Request - */ - protected $request; - - /** - * 行为入口 - * @param $params - */ - public function run(&$params) { - $this->request = Request::instance(); - list($module, $controller, $action) = [$this->request->module(), $this->request->controller(), $this->request->action()]; - $vars = get_class_vars(config('app_namespace') . "\\{$module}\\controller\\{$controller}"); - // 用户登录状态检查 - if ((!empty($vars['checkAuth']) || !empty($vars['checkLogin'])) && !session('user')) { - if ($this->request->isAjax()) { - $result = ['code' => 0, 'msg' => '抱歉, 您还没有登录获取访问权限!', 'data' => '', 'url' => '@admin/login', 'wait' => 3]; - throw new HttpResponseException(json($result)); - } - throw new HttpResponseException(redirect('@admin/login')); - } - // 访问权限节点检查 - if (!empty($vars['checkLogin']) && !auth("{$module}/{$controller}/{$action}")) { - $result = ['code' => 0, 'msg' => '抱歉, 您没有访问该模块的权限!', 'data' => '', 'url' => '', 'wait' => 3]; - throw new HttpResponseException(json($result)); - } - // 权限正常, 默认赋值 - $view = View::instance(Config::get('template'), Config::get('view_replace_str')); - $view->assign('classuri', strtolower("{$module}/{$controller}")); - } - -} diff --git a/extend/hook/FilterView.php b/extend/hook/FilterView.php deleted file mode 100644 index 6134732d3..000000000 --- a/extend/hook/FilterView.php +++ /dev/null @@ -1,71 +0,0 @@ - - * @date 2017/04/25 11:59 - */ -class FilterView { - - /** - * 当前请求对象 - * @var Request - */ - protected $request; - - /** - * 行为入口 - * @param $params - */ - public function run(&$params) { - $this->request = Request::instance(); - $appRoot = $this->request->root(true); - $replace = [ - '__APP__' => $appRoot, - '__SELF__' => $this->request->url(true), - '__PUBLIC__' => strpos($appRoot, EXT) ? ltrim(dirname($appRoot), DS) : $appRoot, - ]; - $params = str_replace(array_keys($replace), array_values($replace), $params); - !IS_CLI && $this->baidu($params); - } - - /** - * 百度统计实现代码 - * @param $params - */ - public function baidu(&$params) { - if (($key = sysconf('tongji_baidu_key'))) { - $script = << -SCRIPT; - $params = preg_replace('||i', "{$script}\n ", $params); - } - } - -} diff --git a/extend/service/AlismsService.php b/extend/service/AlismsService.php new file mode 100644 index 000000000..5e0391428 --- /dev/null +++ b/extend/service/AlismsService.php @@ -0,0 +1,177 @@ + "cn-hangzhou", + "Version" => "2017-05-25", + ]; + + /** + * 短信发送记录查询 + * @param string $PhoneNumber 短信接收号码 + * @param string $SendDate 短信发送日期,格式Ymd,支持近30天记录查询 + * @param integer $PageSize 分页大小 + * @param integer $CurrentPage 当前页码 + * @param null|string $BizId 设置发送短信流水号(可选) + * @return bool|array + */ + public static function query($PhoneNumber, $SendDate, $PageSize = 10, $CurrentPage = 1, $BizId = null) + { + $params = []; + $params["SendDate"] = $SendDate; + $params["PageSize"] = $PageSize; + $params["CurrentPage"] = $CurrentPage; + $params["PhoneNumber"] = $PhoneNumber; + $params['Action'] = 'QuerySendDetails'; + is_null($BizId) || $params["BizId"] = $BizId; + return self::request("dysmsapi.aliyuncs.com", array_merge($params, self::$sdkVersion), true); + } + + /** + * 批量发送短信 + * @param array $PhoneNumbers 待发送手机号 + * @param string $TemplateCode 短信模板Code + * @param array $SignNames 短信签名 + * @param array $TemplateParams 模板中的变量 + * @param array $SmsUpExtendCodes 上行短信扩展码 + * @return bool|array + */ + public static function batchSend(array $PhoneNumbers, $TemplateCode, array $SignNames, array $TemplateParams, $SmsUpExtendCodes = []) + { + $params = []; + $params["Action"] = 'SendBatchSms'; + $params["TemplateCode"] = $TemplateCode; + !empty($SmsUpExtendCodes) && $params["SmsUpExtendCodeJson"] = json_encode($SmsUpExtendCodes); + $params["TemplateParamJson"] = json_encode($TemplateParams, JSON_UNESCAPED_UNICODE); + $params["SignNameJson"] = json_encode($SignNames, JSON_UNESCAPED_UNICODE); + $params["PhoneNumberJson"] = json_encode($PhoneNumbers, JSON_UNESCAPED_UNICODE); + if (!empty($params["SmsUpExtendCodeJson"]) && is_array($params["SmsUpExtendCodeJson"])) { + $params["SmsUpExtendCodeJson"] = json_encode($params["SmsUpExtendCodeJson"], JSON_UNESCAPED_UNICODE); + } + return self::request("dysmsapi.aliyuncs.com", array_merge($params, self::$sdkVersion), true); + } + + /** + * 发送短信 + * @param string $PhoneNumbers 短信接收号码 + * @param string $TemplateCode 短信模板Code + * @param string $SignName 短信签名 + * @param array $TemplateParam 设置模板参数 + * @param null|string $OutId 设置发送短信流水号(可选) + * @param null|string $SmsUpExtendCode 上行短信扩展码(可选) + * @return bool|array + */ + public static function send($PhoneNumbers, $TemplateCode, $SignName, array $TemplateParam, $OutId = null, $SmsUpExtendCode = null) + { + $params = []; + $params['Action'] = 'SendSms'; + $params["SignName"] = $SignName; + $params["TemplateCode"] = $TemplateCode; + $params["PhoneNumbers"] = $PhoneNumbers; + $params['TemplateParam'] = $TemplateParam; + is_null($OutId) || $params['OutId'] = $OutId; + is_null($SmsUpExtendCode) || $params['SmsUpExtendCode'] = $SmsUpExtendCode; + if (!empty($params["TemplateParam"]) && is_array($params["TemplateParam"])) { + $params["TemplateParam"] = json_encode($params["TemplateParam"], JSON_UNESCAPED_UNICODE); + } + return self::request("dysmsapi.aliyuncs.com", array_merge($params, self::$sdkVersion), true); + } + + /** + * 生成签名并发起请求 + * @param $domain string API接口所在域名 + * @param $params array API具体参数 + * @param $security boolean 使用https + * @return bool|array 返回API接口调用结果,当发生错误时返回false + */ + public static function request($domain, $params, $security = false) + { + $apiParams = array_merge([ + "SignatureMethod" => "HMAC-SHA1", + "SignatureNonce" => uniqid(mt_rand(0, 0xffff), true), + "SignatureVersion" => "1.0", + "AccessKeyId" => config('aliyun.SmsAppid'), + "Timestamp" => gmdate("Y-m-d\TH:i:s\Z"), + "Format" => "JSON", + ], $params); + ksort($apiParams); + $sortedQueryStringTmp = ""; + foreach ($apiParams as $key => $value) { + $sortedQueryStringTmp .= "&" . self::encode($key) . "=" . self::encode($value); + } + $stringToSign = "GET&%2F&" . self::encode(substr($sortedQueryStringTmp, 1)); + $sign = base64_encode(hash_hmac("sha1", $stringToSign, config('aliyun.SmsAppkey') . "&", true)); + $signature = self::encode($sign); + $url = ($security ? 'https' : 'http') . "://{$domain}/?Signature={$signature}{$sortedQueryStringTmp}"; + try { + return json_decode(self::fetchContent($url), true); + } catch (\Exception $e) { + return false; + } + } + + /** + * 数据编码处理 + * @param string $str + * @return null|string + */ + private static function encode($str) + { + $res = urlencode($str); + $res = preg_replace("/\+/", "%20", $res); + $res = preg_replace("/\*/", "%2A", $res); + $res = preg_replace("/%7E/", "~", $res); + return $res; + } + + /** + * 网络请求 + * @param string $url 请求URL + * @return mixed + */ + private static function fetchContent($url) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-sdk-client" => "php/2.0.0"]); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $rtn = curl_exec($ch); + if ($rtn === false) { + trigger_error("[CURL_" . curl_errno($ch) . "]: " . curl_error($ch), E_USER_ERROR); + } + curl_close($ch); + return $rtn; + } +} \ No newline at end of file diff --git a/extend/service/DataService.php b/extend/service/DataService.php index ad207e578..7009aec4d 100644 --- a/extend/service/DataService.php +++ b/extend/service/DataService.php @@ -1,7 +1,7 @@ * @date 2017/03/22 15:32 */ -class DataService { +class DataService +{ /** * 删除指定序号 * @param string $sequence * @param string $type * @return bool + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function deleteSequence($sequence, $type = 'SYSTEM') { + public static function deleteSequence($sequence, $type = 'SYSTEM') + { $data = ['sequence' => $sequence, 'type' => strtoupper($type)]; return Db::name('SystemSequence')->where($data)->delete(); } @@ -42,16 +47,16 @@ class DataService { * @param string $type 序号顾类型 * @return string */ - public static function createSequence($length = 10, $type = 'SYSTEM') { + public static function createSequence($length = 10, $type = 'SYSTEM') + { $times = 0; while ($times++ < 10) { - $sequence = ''; - $i = 0; + list($i, $sequence) = [0, '']; while ($i++ < $length) { $sequence .= ($i <= 1 ? rand(1, 9) : rand(0, 9)); } $data = ['sequence' => $sequence, 'type' => strtoupper($type)]; - if (Db::name('SystemSequence')->where($data)->count() < 1 && Db::name('SystemSequence')->insert($data)) { + if (Db::name('SystemSequence')->where($data)->count() < 1 && Db::name('SystemSequence')->insert($data) !== false) { return $sequence; } } @@ -60,68 +65,48 @@ class DataService { /** * 数据增量保存 - * @param \think\db\Query|string $dbQuery 数据查询对象 + * @param Query|string $dbQuery 数据查询对象 * @param array $data 需要保存或更新的数据 - * @param string $upkey 条件主键限制 + * @param string $key 条件主键限制 * @param array $where 其它的where条件 * @return bool + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function save($dbQuery, $data, $upkey = 'id', $where = []) { + public static function save($dbQuery, $data, $key = 'id', $where = []) + { $db = is_string($dbQuery) ? Db::name($dbQuery) : $dbQuery; - $fields = $db->getTableFields(['table' => $db->getTable()]); - $_data = []; - foreach ($data as $k => $v) { - in_array($k, $fields) && ($_data[$k] = $v); + list($table, $map) = [$db->getTable(), [$key => isset($data[$key]) ? $data[$key] : '']]; + if (Db::table($table)->where($where)->where($map)->count() > 0) { + return Db::table($table)->strict(false)->where($where)->where($map)->update($data) !== false; } - if (self::_apply_save_where($db, $data, $upkey, $where)->count() > 0) { - return self::_apply_save_where($db, $data, $upkey, $where)->update($_data) !== FALSE; - } - return self::_apply_save_where($db, $data, $upkey, $where)->insert($_data) !== FALSE; - } - - /** - * 应用 where 条件 - * @param \think\db\Query|string $db 数据查询对象 - * @param array $data 需要保存或更新的数据 - * @param string $upkey 条件主键限制 - * @param array $where 其它的where条件 - * @return \think\db\Query - */ - protected static function _apply_save_where(&$db, $data, $upkey, $where) { - foreach (is_string($upkey) ? explode(',', $upkey) : $upkey as $v) { - if (is_string($v) && array_key_exists($v, $data)) { - $db->where($v, $data[$v]); - } elseif (is_string($v)) { - $db->where("{$v} IS NULL"); - } - } - return $db->where($where); + return Db::table($table)->strict(false)->insert($data) !== false; } /** * 更新数据表内容 - * @param \think\db\Query|string $dbQuery 数据查询对象 + * @param Query|string $dbQuery 数据查询对象 * @param array $where 额外查询条件 * @return bool|null + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function update(&$dbQuery, $where = []) { + public static function update(&$dbQuery, $where = []) + { + $request = app('request'); $db = is_string($dbQuery) ? Db::name($dbQuery) : $dbQuery; - $ids = explode(',', input("post.id", '')); - $field = input('post.field', ''); - $value = input('post.value', ''); - $pk = $db->getPk(['table' => $db->getTable()]); - $db->where(empty($pk) ? 'id' : $pk, 'in', $ids); - !empty($where) && $db->where($where); - // 删除模式 + list($pk, $table, $map) = [$db->getPk(), $db->getTable(), []]; + list($field, $value) = [$request->post('field', ''), $request->post('value', '')]; + $map[] = [empty($pk) ? 'id' : $pk, 'in', explode(',', $request->post('id', ''))]; + // 删除模式,如果存在 is_deleted 字段使用软删除 if ($field === 'delete') { - $fields = $db->getTableFields(['table' => $db->getTable()]); - if (in_array('is_deleted', $fields)) { - return false !== $db->update(['is_deleted' => 1]); + if (method_exists($db, 'getTableFields') && in_array('is_deleted', $db->getTableFields())) { + return Db::table($table)->where($where)->where($map)->update(['is_deleted' => '1']) !== false; } - return false !== $db->delete(); + return Db::table($table)->where($where)->where($map)->delete() !== false; } - // 更新模式 - return false !== $db->update([$field => $value]); + // 更新模式,更新指定字段内容 + return Db::table($table)->where($where)->where($map)->update([$field => $value]) !== false; } } diff --git a/extend/service/ExpressService.php b/extend/service/ExpressService.php deleted file mode 100644 index ac327ad52..000000000 --- a/extend/service/ExpressService.php +++ /dev/null @@ -1,75 +0,0 @@ - - * @date 2017/03/15 15:17 - */ -class ExpressService { - - const APPID = '1232779'; - const APPKEY = 'ac45f461-8c1a-4518-87b1-bb8e835a2f9d'; - const APIURI = 'http://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx'; - - /** - * @brief 获取物流轨迹线路 - * @param $ShipperCode string 物流公司代号 - * @param $LogisticCode string 物流单号 - * @return string array 轨迹数据 - */ - public static function line($ShipperCode, $LogisticCode) { - $sendData = json_encode(array('ShipperCode' => $ShipperCode, 'LogisticCode' => $LogisticCode), JSON_UNESCAPED_UNICODE); - $data = array( - 'RequestData' => $sendData, - 'EBusinessID' => self::APPID, - 'RequestType' => '1002', - 'DataType' => 2, - 'DataSign' => base64_encode(md5($sendData . self::APPKEY)), - ); - $result = HttpService::post(self::APIURI, $data); - !($resultJson = json_decode($result, true)) && die(var_export($result)); - return self::response($resultJson); - } - - /** - * 处理返回数据统一数据格式 - * @param $result 结果处理 - * @return array 通用的结果集 array('result' => 'success或者fail','data' => array( array('time' => '时间','info' => '地点'),......),'reason' => '失败原因') - */ - public static function response($result) { - $status = "fail"; - $data = array(); - $message = "此单号无跟踪记录"; - if (isset($result['Message'])) { - $message = $result['Message']; - } else if (isset($result['Reason'])) { - $message = $result['Reason']; - } - if (isset($result['Traces']) && $result['Traces']) { - foreach ($result['Traces'] as $key => $val) { - $data[$key]['time'] = $val['AcceptTime']; - $data[$key]['info'] = $val['AcceptStation']; - } - $status = "success"; - $message = '此订单号有' . count($data) . '条跟踪记录'; - } - return array('result' => $status, 'data' => $data, 'message' => $message); - } - -} diff --git a/extend/service/FileService.php b/extend/service/FileService.php index 8181e3245..893d4d8c2 100644 --- a/extend/service/FileService.php +++ b/extend/service/FileService.php @@ -1,7 +1,7 @@ * @date 2017/03/15 15:17 */ -class FileService { +class FileService +{ /** * 根据文件后缀获取文件MINE - * @param array $exts 文件后缀 + * @param array $ext 文件后缀 * @param array $mine 文件后缀MINE信息 * @return string */ - public static function getFileMine($exts, $mine = []) { - $mines = Config::get('mines'); - foreach (is_string($exts) ? explode(',', $exts) : $exts as $_e) { - if (isset($mines[strtolower($_e)])) { - $_exinfo = $mines[strtolower($_e)]; - $mine[] = is_array($_exinfo) ? join(',', $_exinfo) : $_exinfo; - } + public static function getFileMine($ext, $mine = []) + { + $mines = self::getMines(); + foreach (is_string($ext) ? explode(',', $ext) : $ext as $e) { + $mine[] = isset($mines[strtolower($e)]) ? $mines[strtolower($e)] : 'application/octet-stream'; } - return join(',', $mine); + return join(',', array_unique($mine)); + } + + /** + * 获取所有文件扩展的mine + * @return mixed + */ + public static function getMines() + { + $mines = cache('all_ext_mine'); + if (empty($mines)) { + $content = file_get_contents('http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types'); + preg_match_all('#^([^\s]{2,}?)\s+(.+?)$#ism', $content, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + foreach (explode(" ", $match[2]) as $ext) { + $mines[$ext] = $match[1]; + } + } + cache('all_ext_mine', $mines); + } + return $mines; } /** * 获取文件当前URL地址 - * @param string $filename - * @param string|null $storage + * @param string $filename 文件HASH名称 + * @param string|null $storage 文件存储引擎 * @return bool|string + * @throws OssException + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function getFileUrl($filename, $storage = null) { + public static function getFileUrl($filename, $storage = null) + { if (self::hasFile($filename, $storage) === false) { return false; } @@ -67,14 +89,15 @@ class FileService { case 'oss': return self::getBaseUriOss() . $filename; } - return false; + throw new \think\Exception('未设置存储方式,无法获取到文件对应URL地址'); } /** - * 根据配置获取到七牛云文件上传目标地址 + * 根据配置获取到本地上传的目标地址 * @return string */ - public static function getUploadLocalUrl() { + public static function getUploadLocalUrl() + { return url('@admin/plugs/upload'); } @@ -82,82 +105,114 @@ class FileService { * 根据配置获取到七牛云文件上传目标地址 * @param bool $isClient * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function getUploadQiniuUrl($isClient = true) { + public static function getUploadQiniuUrl($isClient = true) + { $region = sysconf('storage_qiniu_region'); $isHttps = !!sysconf('storage_qiniu_is_https'); switch ($region) { case '华东': if ($isHttps) { - return $isClient ? 'https://upload.qbox.me' : 'https://up.qbox.me'; + return $isClient ? 'https://upload.qiniup.com' : 'https://upload.qiniup.com'; } - return $isClient ? 'http://upload.qiniu.com' : 'http://up.qiniu.com'; + return $isClient ? 'http://upload.qiniup.com' : 'http://upload.qiniup.com'; case '华北': if ($isHttps) { - return $isClient ? 'https://upload-z1.qbox.me' : 'https://up-z1.qbox.me'; + return $isClient ? 'https://upload-z1.qiniup.com' : 'https://up-z1.qiniup.com'; } - return $isClient ? 'http://upload-z1.qiniu.com' : 'http://up-z1.qiniu.com'; + return $isClient ? 'http://upload-z1.qiniup.com' : 'http://up-z1.qiniup.com'; case '北美': if ($isHttps) { - return $isClient ? 'https://upload-na0.qbox.me' : 'https://up-na0.qbox.me'; + return $isClient ? 'https://upload-na0.qiniup.com' : 'https://up-na0.qiniup.com'; } - return $isClient ? 'http://upload-na0.qiniu.com' : 'http://up-na0.qiniu.com'; + return $isClient ? 'http://upload-na0.qiniup.com' : 'http://up-na0.qiniup.com'; case '华南': - default: if ($isHttps) { - return $isClient ? 'https://upload-z2.qbox.me' : 'https://up-z2.qbox.me'; + return $isClient ? 'https://upload-z2.qiniup.com' : 'https://up-z2.qiniup.com'; } - return $isClient ? 'http://upload-z2.qiniu.com' : 'http://up-z2.qiniu.com'; + return $isClient ? 'http://upload-z2.qiniup.com' : 'http://up-z2.qiniup.com'; + default: + throw new \think\Exception('未配置七牛云存储区域'); } } /** * 获取AliOSS上传地址 * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function getUploadOssUrl() { - return (request()->isSsl() ? 'https' : 'http') . '://' . sysconf('storage_oss_domain'); + public static function getUploadOssUrl() + { + $protocol = request()->isSsl() ? 'https' : 'http'; + return "{$protocol}://" . sysconf('storage_oss_domain'); } /** * 获取服务器URL前缀 * @return string */ - public static function getBaseUriLocal() { - $request = request(); - $base = $request->root(); - $root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base; - if ('' != $root) { - $root = '/' . ltrim($root, '/'); - } - return ($request->isSsl() ? 'https' : 'http') . '://' . $request->host() . "{$root}/static/upload/"; + public static function getBaseUriLocal() + { + $appRoot = request()->root(true); // 去掉参数 true 将获得相对地址 + $uriRoot = preg_match('/\.php$/', $appRoot) ? dirname($appRoot) : $appRoot; + $uriRoot = in_array($uriRoot, ['/', '\\']) ? '' : $uriRoot; + return "{$uriRoot}/static/upload/"; } /** * 获取七牛云URL前缀 * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function getBaseUriQiniu() { - return (sysconf('storage_qiniu_is_https') ? 'https' : 'http') . '://' . sysconf('storage_qiniu_domain') . '/'; + public static function getBaseUriQiniu() + { + switch (strtolower(sysconf('storage_qiniu_is_https'))) { + case 'https': + return 'https://' . sysconf('storage_qiniu_domain') . '/'; + case 'http': + return 'http://' . sysconf('storage_qiniu_domain') . '/'; + case 'auto': + return '//' . sysconf('storage_qiniu_domain') . '/'; + default: + throw new \think\Exception('未设置七牛云文件地址协议'); + } } /** - * 获取AliOss URL前缀 + * 获取阿里云对象存储URL前缀 * @return string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function getBaseUriOss() { - return (sysconf('storage_oss_is_https') ? 'https' : 'http') . '://' . sysconf('storage_oss_domain') . '/'; + public static function getBaseUriOss() + { + switch (strtolower(sysconf('storage_oss_is_https'))) { + case 'https': + return 'https://' . sysconf('storage_oss_domain') . '/'; + case 'http': + return 'http://' . sysconf('storage_oss_domain') . '/'; + case 'auto': + return '//' . sysconf('storage_oss_domain') . '/'; + default: + throw new \think\Exception('未设置阿里云文件地址协议'); + } } /** * 获取文件相对名称 - * @param 文件标识 $source - * @param 文件后缀 $ext - * @param 文件前缀 $pre + * @param string $local_url 文件标识 + * @param string $ext 文件后缀 + * @param string $pre 文件前缀(若有值需要以/结尾) * @return string */ - public static function getFileName($source, $ext = '', $pre = '') { - return $pre . join('/', str_split(md5($source), 16)) . '.' . $ext; + public static function getFileName($local_url, $ext = '', $pre = '') + { + empty($ext) && $ext = strtolower(pathinfo($local_url, 4)); + return $pre . join('/', str_split(md5($local_url), 16)) . '.' . ($ext ? $ext : 'tmp'); } /** @@ -165,11 +220,15 @@ class FileService { * @param string $filename * @param string|null $storage * @return bool + * @throws OssException + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function hasFile($filename, $storage = null) { + public static function hasFile($filename, $storage = null) + { switch (empty($storage) ? sysconf('storage_type') : $storage) { case 'local': - return file_exists(ROOT_PATH . 'static/upload/' . $filename); + return file_exists(env('root_path') . 'static/upload/' . $filename); case 'qiniu': $auth = new Auth(sysconf('storage_qiniu_access_key'), sysconf('storage_qiniu_secret_key')); $bucketMgr = new BucketManager($auth); @@ -187,23 +246,25 @@ class FileService { * @param string $filename * @param string|null $storage * @return string|null + * @throws \think\Exception + * @throws \think\exception\PDOException + * @throws OssException */ - public static function readFile($filename, $storage = null) { + public static function readFile($filename, $storage = null) + { switch (empty($storage) ? sysconf('storage_type') : $storage) { case 'local': - $filepath = ROOT_PATH . 'static/upload/' . $filename; - if (file_exists($filepath)) { - return file_get_contents($filepath); - } + $file = env('root_path') . 'static/upload/' . $filename; + return file_exists($file) ? file_get_contents($file) : ''; case 'qiniu': $auth = new Auth(sysconf('storage_qiniu_access_key'), sysconf('storage_qiniu_secret_key')); return file_get_contents($auth->privateDownloadUrl(self::getBaseUriQiniu() . $filename)); case 'oss': $ossClient = new OssClient(sysconf('storage_oss_keyid'), sysconf('storage_oss_secret'), self::getBaseUriOss(), true); return $ossClient->getObject(sysconf('storage_oss_bucket'), $filename); + default: + throw new \think\Exception('未配置读取文件的存储方法'); } - Log::error("通过{$storage}读取文件{$filename}的不存在!"); - return null; } /** @@ -212,8 +273,11 @@ class FileService { * @param string $content * @param string|null $file_storage * @return array|false + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function save($filename, $content, $file_storage = null) { + public static function save($filename, $content, $file_storage = null) + { $type = empty($file_storage) ? sysconf('storage_type') : $file_storage; if (!method_exists(__CLASS__, $type)) { Log::error("保存存储失败,调用{$type}存储引擎不存在!"); @@ -228,16 +292,17 @@ class FileService { * @param string $content * @return array|null */ - public static function local($filename, $content) { + public static function local($filename, $content) + { try { - $filepath = ROOT_PATH . 'static/upload/' . $filename; - !file_exists(dirname($filepath)) && mkdir(dirname($filepath), '0755', true); - if (file_put_contents($filepath, $content)) { + $realfile = env('root_path') . 'static/upload/' . $filename; + !file_exists(dirname($realfile)) && mkdir(dirname($realfile), 0755, true); + if (file_put_contents($realfile, $content)) { $url = pathinfo(request()->baseFile(true), PATHINFO_DIRNAME) . '/static/upload/' . $filename; - return ['file' => $filepath, 'hash' => md5_file($filepath), 'key' => "static/upload/{$filename}", 'url' => $url]; + return ['file' => $realfile, 'hash' => md5_file($realfile), 'key' => "static/upload/{$filename}", 'url' => $url]; } } catch (Exception $err) { - Log::error('本地文件存储失败, ' . var_export($err, true)); + Log::error('本地文件存储失败, ' . $err->getMessage()); } return null; } @@ -247,14 +312,17 @@ class FileService { * @param string $filename * @param string $content * @return array|null + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function qiniu($filename, $content) { + public static function qiniu($filename, $content) + { $auth = new Auth(sysconf('storage_qiniu_access_key'), sysconf('storage_qiniu_secret_key')); $token = $auth->uploadToken(sysconf('storage_qiniu_bucket')); $uploadMgr = new UploadManager(); list($result, $err) = $uploadMgr->put($token, $filename, $content); if ($err !== null) { - Log::error('七牛云文件上传失败, ' . var_export($err, true)); + Log::error('七牛云文件上传失败, ' . $err->getMessage()); return null; } $result['file'] = $filename; @@ -267,36 +335,49 @@ class FileService { * @param string $filename * @param string $content * @return array|null + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function oss($filename, $content) { + public static function oss($filename, $content) + { try { - $ossClient = new OssClient(sysconf('storage_oss_keyid'), sysconf('storage_oss_secret'), self::getBaseUriOss(), true); + $endpoint = 'http://' . sysconf('storage_oss_domain'); + $ossClient = new OssClient(sysconf('storage_oss_keyid'), sysconf('storage_oss_secret'), $endpoint, true); $result = $ossClient->putObject(sysconf('storage_oss_bucket'), $filename, $content); - return ['file' => $filename, 'hash' => $result['content-md5'], 'key' => $filename, 'url' => $result['oss-request-url']]; + $baseUrl = explode('://', $result['oss-request-url'])[1]; + if (strtolower(sysconf('storage_oss_is_https')) === 'http') { + $site_url = "http://{$baseUrl}"; + } elseif (strtolower(sysconf('storage_oss_is_https')) === 'https') { + $site_url = "https://{$baseUrl}"; + } else { + $site_url = "//{$baseUrl}"; + } + return ['file' => $filename, 'hash' => $result['content-md5'], 'key' => $filename, 'url' => $site_url]; } catch (OssException $err) { - Log::error('阿里云OSS文件上传失败, ' . var_export($err, true)); - return null; + Log::error('阿里云OSS文件上传失败, ' . $err->getMessage()); } + return null; } /** * 下载文件到本地 * @param string $url 文件URL地址 * @param bool $isForce 是否强制重新下载文件 - * @return array|null; + * @return array */ - public static function download($url, $isForce = false) { + public static function download($url, $isForce = false) + { try { - $filename = self::getFileName($url, strtolower(pathinfo($url, 4)), 'download/'); + $filename = self::getFileName($url, '', 'download/'); if (false === $isForce && ($siteUrl = self::getFileUrl($filename, 'local'))) { - $realfile = ROOT_PATH . 'static/upload/' . $filename; + $realfile = env('root_path') . 'static/upload/' . $filename; return ['file' => $realfile, 'hash' => md5_file($realfile), 'key' => "static/upload/{$filename}", 'url' => $siteUrl]; } return self::local($filename, file_get_contents($url)); } catch (\Exception $e) { - Log::error("FileService 文件下载失败 [ {$url} ] . {$e->getMessage()}"); - return false; + Log::error("FileService 文件下载失败 [ {$url} ] " . $e->getMessage()); } + return ['url' => $url]; } } diff --git a/extend/service/HttpService.php b/extend/service/HttpService.php index 51f87d086..2eccd61d0 100644 --- a/extend/service/HttpService.php +++ b/extend/service/HttpService.php @@ -1,7 +1,7 @@ * @date 2017/03/22 15:32 */ -class HttpService { +class HttpService +{ /** - * HTTP GET 请求 - * @param string $url 请求的URL地址 - * @param array $data GET参数 - * @param int $second 设置超时时间(默认30秒) - * @param array $header 请求Header信息 + * 以get模拟网络请求 + * @param string $url HTTP请求URL地址 + * @param array $query GET请求参数 + * @param array $options CURL参数 * @return bool|string */ - public static function get($url, $data = array(), $second = 30, $header = []) { - if (!empty($data)) { - $url .= (stripos($url, '?') === false ? '?' : '&'); - $url .= (is_array($data) ? http_build_query($data) : $data); - } - $curl = curl_init(); - curl_setopt($curl, CURLOPT_TIMEOUT, $second); - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - !empty($header) && curl_setopt($curl, CURLOPT_HTTPHEADER, $header); - self::_setSsl($curl, $url); - $content = curl_exec($curl); - $status = curl_getinfo($curl); - curl_close($curl); - return (intval($status["http_code"]) === 200) ? $content : false; + public static function get($url, $query = [], $options = []) + { + $options['query'] = $query; + return HttpService::request('get', $url, $options); } /** - * POST 请求(支持文件上传) + * 以get模拟网络请求 * @param string $url HTTP请求URL地址 - * @param array|string $data POST提交的数据 - * @param int $second 请求超时时间 - * @param array $header 请求Header信息 + * @param array $data POST请求数据 + * @param array $options CURL参数 * @return bool|string */ - static public function post($url, $data = [], $second = 30, $header = []) { - self::_setUploadFile($data); + public static function post($url, $data = [], $options = []) + { + $options['data'] = $data; + return HttpService::request('post', $url, $options); + } + + /** + * CURL模拟网络请求 + * @param string $method 请求方法 + * @param string $url 请求方法 + * @param array $options 请求参数[header,data,ssl_cer,ssl_key] + * @return bool|string + */ + public static function request($method, $url, $options = []) + { $curl = curl_init(); - curl_setopt($curl, CURLOPT_TIMEOUT, $second); + // GET参数设置 + if (!empty($options['query'])) { + $url .= stripos($url, '?') !== false ? '&' : '?' . http_build_query($options['query']); + } + // POST数据设置 + if (strtolower($method) === 'post') { + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDS, self::build($options['data'])); + } + // 请求超时设置 + $options['timeout'] = isset($options['timeout']) ? $options['timeout'] : 60; + curl_setopt($curl, CURLOPT_TIMEOUT, $options['timeout']); + // CURL头信息设置 + if (!empty($options['header'])) { + curl_setopt($curl, CURLOPT_HTTPHEADER, $options['header']); + } + // 证书文件设置 + if (!empty($options['ssl_cer']) && file_exists($options['ssl_cer'])) { + curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM'); + curl_setopt($curl, CURLOPT_SSLCERT, $options['ssl_cer']); + } + if (!empty($options['ssl_key']) && file_exists($options['ssl_key'])) { + curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM'); + curl_setopt($curl, CURLOPT_SSLKEY, $options['ssl_key']); + } curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_POST, true); - curl_setopt($curl, CURLOPT_POSTFIELDS, $data); - !empty($header) && curl_setopt($curl, CURLOPT_HTTPHEADER, $header); - self::_setSsl($curl, $url); - $content = curl_exec($curl); - $status = curl_getinfo($curl); - curl_close($curl); + list($content, $status) = [curl_exec($curl), curl_getinfo($curl), curl_close($curl)]; return (intval($status["http_code"]) === 200) ? $content : false; } /** - * 设置SSL参数 - * @param $curl - * @param string $url - */ - private static function _setSsl(&$curl, $url) { - if (stripos($url, "https") === 0) { - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($curl, CURLOPT_SSLVERSION, 1); - } - } - - /** - * 设置POST文件上传兼容 + * POST数据过滤处理 * @param array $data - * @return string + * @param bool $needBuild + * @return array */ - private static function _setUploadFile(&$data) { + private static function build($data, $needBuild = true) + { if (!is_array($data)) { - return null; + return $data; } - foreach ($data as &$value) { - if (!(is_string($value) && strlen($value) > 0 && $value[0] === '@')) { - continue; + foreach ($data as $key => $value) { + if (is_string($value) && class_exists('CURLFile', false) && stripos($value, '@') === 0) { + if (($filename = realpath(trim($value, '@'))) && file_exists($filename)) { + list($needBuild, $data[$key]) = [false, new \CURLFile($filename)]; + } } - $filename = realpath(trim($value, '@')); - $filemime = FileService::getFileMine(strtolower(pathinfo($filename, PATHINFO_EXTENSION))); - $value = class_exists('CURLFile', false) ? new CURLFile($filename, $filemime) : "{$value};type={$filemime}"; } + return $needBuild ? http_build_query($data) : $data; } } diff --git a/extend/service/LogService.php b/extend/service/LogService.php index a69e5cfe2..700c1303c 100644 --- a/extend/service/LogService.php +++ b/extend/service/LogService.php @@ -1,7 +1,7 @@ * @date 2017/03/24 13:25 */ -class LogService { +class LogService +{ /** * 获取数据操作对象 - * @return \think\db\Query + * @return Query */ - protected static function db() { + protected static function db() + { return Db::name('SystemLog'); } @@ -40,10 +42,17 @@ class LogService { * @param string $content * @return bool */ - public static function write($action = '行为', $content = "内容描述") { - $request = Request::instance(); + public static function write($action = '行为', $content = "内容描述") + { + $request = app('request'); $node = strtolower(join('/', [$request->module(), $request->controller(), $request->action()])); - $data = ['ip' => $request->ip(), 'node' => $node, 'username' => session('user.username') . '', 'action' => $action, 'content' => $content]; + $data = [ + 'ip' => $request->ip(), + 'node' => $node, + 'action' => $action, + 'content' => $content, + 'username' => session('user.username') . '', + ]; return self::db()->insert($data) !== false; } diff --git a/extend/service/NodeService.php b/extend/service/NodeService.php index 48c01c43a..d208f3b6a 100644 --- a/extend/service/NodeService.php +++ b/extend/service/NodeService.php @@ -1,7 +1,7 @@ * @date 2017/05/08 11:28 */ -class NodeService { +class NodeService +{ /** * 应用用户权限节点 * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException */ - public static function applyAuthNode() { + public static function applyAuthNode() + { cache('need_access_node', null); if (($userid = session('user.id'))) { - session('user', Db::name('SystemUser')->where('id', $userid)->find()); + session('user', Db::name('SystemUser')->where(['id' => $userid])->find()); } if (($authorize = session('user.authorize'))) { - $authorizeids = Db::name('SystemAuth')->where('id', 'in', explode(',', $authorize))->where('status', '1')->column('id'); + $where = ['status' => '1']; + $authorizeids = Db::name('SystemAuth')->whereIn('id', explode(',', $authorize))->where($where)->column('id'); if (empty($authorizeids)) { return session('user.nodes', []); } - $nodes = Db::name('SystemAuthNode')->where('auth', 'in', $authorizeids)->column('node'); + $nodes = Db::name('SystemAuthNode')->whereIn('auth', $authorizeids)->column('node'); return session('user.nodes', $nodes); } return false; @@ -49,10 +55,11 @@ class NodeService { * 获取授权节点 * @return array */ - public static function getAuthNode() { + public static function getAuthNode() + { $nodes = cache('need_access_node'); if (empty($nodes)) { - $nodes = Db::name('SystemNode')->where('is_auth', '1')->column('node'); + $nodes = Db::name('SystemNode')->where(['is_auth' => '1'])->column('node'); cache('need_access_node', $nodes); } return $nodes; @@ -63,87 +70,102 @@ class NodeService { * @param string $node 节点 * @return bool */ - public static function checkAuthNode($node) { + public static function checkAuthNode($node) + { list($module, $controller, $action) = explode('/', str_replace(['?', '=', '&'], '/', $node . '///')); - $auth_node = strtolower(trim("{$module}/{$controller}/{$action}", '/')); + $currentNode = self::parseNodeStr("{$module}/{$controller}") . strtolower("/{$action}"); if (session('user.username') === 'admin' || stripos($node, 'admin/index') === 0) { return true; } - if (!in_array($auth_node, self::getAuthNode())) { + if (!in_array($currentNode, self::getAuthNode())) { return true; } - return in_array($auth_node, (array) session('user.nodes')); + return in_array($currentNode, (array)session('user.nodes')); } /** * 获取系统代码节点 + * @param array $nodes * @return array */ - public static function get() { - $alias = []; - foreach (Db::name('SystemNode')->select() as $vo) { - $alias["{$vo['node']}"] = $vo; - } - $nodes = []; - $ignore = [ - 'index', - 'wechat/api', 'wechat/notify', 'wechat/review', - 'admin/plugs', 'admin/login', 'admin/index', - ]; - foreach (self::getNodeTree(APP_PATH) as $thr) { + public static function get($nodes = []) + { + $alias = Db::name('SystemNode')->column('node,is_menu,is_auth,is_login,title'); + $ignore = ['index', 'wechat/review', 'admin/plugs', 'admin/login', 'admin/index']; + foreach (self::getNodeTree(env('app_path')) as $thr) { foreach ($ignore as $str) { if (stripos($thr, $str) === 0) { continue 2; } } $tmp = explode('/', $thr); - $one = $tmp[0]; - $two = "{$tmp[0]}/{$tmp[1]}"; - $nodes[$one] = array_merge(isset($alias[$one]) ? $alias[$one] : ['node' => $one, 'title' => '', 'is_menu' => 0, 'is_auth' => 0], ['pnode' => '']); - $nodes[$two] = array_merge(isset($alias[$two]) ? $alias[$two] : ['node' => $two, 'title' => '', 'is_menu' => 0, 'is_auth' => 0], ['pnode' => $one]); - $nodes[$thr] = array_merge(isset($alias[$thr]) ? $alias[$thr] : ['node' => $thr, 'title' => '', 'is_menu' => 0, 'is_auth' => 0], ['pnode' => $two]); + list($one, $two) = ["{$tmp[0]}", "{$tmp[0]}/{$tmp[1]}"]; + $nodes[$one] = array_merge(isset($alias[$one]) ? $alias[$one] : ['node' => $one, 'title' => '', 'is_menu' => 0, 'is_auth' => 0, 'is_login' => 0], ['pnode' => '']); + $nodes[$two] = array_merge(isset($alias[$two]) ? $alias[$two] : ['node' => $two, 'title' => '', 'is_menu' => 0, 'is_auth' => 0, 'is_login' => 0], ['pnode' => $one]); + $nodes[$thr] = array_merge(isset($alias[$thr]) ? $alias[$thr] : ['node' => $thr, 'title' => '', 'is_menu' => 0, 'is_auth' => 0, 'is_login' => 0], ['pnode' => $two]); + } + foreach ($nodes as &$node) { + list($node['is_auth'], $node['is_menu'], $node['is_login']) = [intval($node['is_auth']), intval($node['is_menu']), empty($node['is_auth']) ? intval($node['is_login']) : 1]; } return $nodes; } /** * 获取节点列表 - * @param string $path 路径 + * @param string $dirPath 路径 * @param array $nodes 额外数据 * @return array */ - public static function getNodeTree($path, $nodes = []) { - foreach (self::_getFilePaths($path) as $vo) { - if (!preg_match('|/(\w+)/controller/(\w+)|', str_replace(DS, '/', $vo), $matches) || count($matches) !== 3) { + public static function getNodeTree($dirPath, $nodes = []) + { + foreach (self::scanDirFile($dirPath) as $filename) { + $matches = []; + if (!preg_match('|/(\w+)/controller/(\w+)|', str_replace(DIRECTORY_SEPARATOR, '/', $filename), $matches) || count($matches) !== 3) { continue; } - $className = config('app_namespace') . str_replace('/', '\\', $matches[0]); - if (!class_exists($className)) { - continue; - } - foreach (get_class_methods($className) as $actionName) { - if ($actionName[0] !== '_') { - $nodes[] = strtolower("{$matches[1]}/{$matches[2]}/{$actionName}"); + $className = env('app_namespace') . str_replace('/', '\\', $matches[0]); + if (!class_exists($className)) continue; + foreach (get_class_methods($className) as $funcName) { + if (strpos($funcName, '_') !== 0 && $funcName !== 'initialize' && $funcName !== 'registerMiddleware') { + $nodes[] = self::parseNodeStr("{$matches[1]}/{$matches[2]}") . '/' . strtolower($funcName); } } } return $nodes; } + /** + * 驼峰转下划线规则 + * @param string $node + * @return string + */ + public static function parseNodeStr($node) + { + $tmp = []; + foreach (explode('/', $node) as $name) { + $tmp[] = strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); + } + return trim(join('/', $tmp), '/'); + } + /** * 获取所有PHP文件 - * @param string $path 目录 + * @param string $dirPath 目录 * @param array $data 额外数据 - * @param string $ext 文件后缀 + * @param string $ext 有文件后缀 * @return array */ - private static function _getFilePaths($path, $data = [], $ext = 'php') { - foreach (scandir($path) as $dir) { - if ($dir[0] === '.') { + private static function scanDirFile($dirPath, $data = [], $ext = 'php') + { + foreach (scandir($dirPath) as $dir) { + if (strpos($dir, '.') === 0) { continue; } - if (($tmp = realpath($path . DS . $dir)) && (is_dir($tmp) || pathinfo($tmp, PATHINFO_EXTENSION) === $ext)) { - is_dir($tmp) ? $data = array_merge($data, self::_getFilePaths($tmp)) : $data[] = $tmp; + $tmpPath = realpath($dirPath . DIRECTORY_SEPARATOR . $dir); + if (is_dir($tmpPath)) { + $data = array_merge($data, self::scanDirFile($tmpPath)); + } elseif (pathinfo($tmpPath, 4) === $ext) { + $data[] = $tmpPath; } } return $data; diff --git a/extend/service/PayService.php b/extend/service/PayService.php deleted file mode 100644 index 991c2d234..000000000 --- a/extend/service/PayService.php +++ /dev/null @@ -1,141 +0,0 @@ - - * @date 2016/10/25 14:49 - */ -class PayService { - - /** - * 查询订单是否已经支付 - * @param string $order_no - * @return bool - */ - public static function isPay($order_no) { - $map = ['order_no' => $order_no, 'is_pay' => '1']; - return Db::name('WechatPayPrepayid')->where($map)->count() > 0; - } - - /** - * 创建微信二维码支付(扫码支付模式二) - * @param WechatPay $pay 支付SDK - * @param string $order_no 系统订单号 - * @param int $fee 支付金额 - * @param string $title 订单标题 - * @param string $from 订单来源 - * @return false|string - */ - public static function createWechatPayQrc(WechatPay $pay, $order_no, $fee, $title, $from = 'wechat') { - $prepayid = self::createWechatPrepayid($pay, null, $order_no, $fee, $title, 'NATIVE', $from); - if ($prepayid === false) { - return false; - } - $filename = FileService::getFileName($prepayid, 'png', 'qrc/'); - if (!FileService::hasFile($filename, 'local')) { - $qrCode = new QrCode($prepayid); - if (null === FileService::save($filename, $qrCode->get(), 'local')) { - return false; - } - } - return FileService::getFileUrl($filename, 'local'); - } - - /** - * 创建微信JSAPI支付签名包 - * @param WechatPay $pay 支付SDK - * @param string $openid 微信用户openid - * @param string $order_no 系统订单号 - * @param int $fee 支付金额 - * @param string $title 订单标题 - * @return bool|array - */ - public static function createWechatPayJsPicker(WechatPay $pay, $openid, $order_no, $fee, $title) { - if (($prepayid = self::createWechatPrepayid($pay, $openid, $order_no, $fee, $title, 'JSAPI')) === false) { - return false; - } - return $pay->createMchPay($prepayid); - } - - /** - * 微信退款操作 - * @param WechatPay $pay 支付SDK - * @param string $order_no 系统订单号 - * @param int $fee 退款金额 - * @param string|null $refund_no 退款订单号 - * @return bool - */ - public static function putWechatRefund(WechatPay $pay, $order_no, $fee = 0, $refund_no = NULL, $refund_account = '') { - $map = array('order_no' => $order_no, 'is_pay' => '1', 'appid' => $pay->appid); - $notify = Db::name('WechatPayPrepayid')->where($map)->find(); - if (empty($notify)) { - Log::error("内部订单号{$order_no}验证退款失败"); - return false; - } - if (false !== $pay->refund($notify['out_trade_no'], $notify['transaction_id'], is_null($refund_no) ? "T{$order_no}" : $refund_no, $notify['fee'], empty($fee) ? $notify['fee'] : $fee, '', $refund_account)) { - $data = ['out_trade_no' => $notify['out_trade_no'], 'is_refund' => "1", 'refund_at' => date('Y-m-d H:i:s'), 'expires_in' => time() + 7000]; - if (DataService::save('wechat_pay_prepayid', $data, 'out_trade_no')) { - return true; - } - Log::error("内部订单号{$order_no}退款成功,系统更新异常"); - return false; - } - Log::error("内部订单号{$order_no}退款失败,{$pay->errMsg}"); - return false; - } - - /** - * 创建微信预支付码 - * @param WechatPay $pay 支付SDK - * @param string $openid 支付者Openid - * @param string $order_no 实际订单号 - * @param int $fee 实际订单支付费用 - * @param string $title 订单标题 - * @param string $trade_type 付款方式 - * @param string $from 订单来源 - * @return bool|string - */ - public static function createWechatPrepayid(WechatPay $pay, $openid, $order_no, $fee, $title, $trade_type = 'JSAPI', $from = 'wechat') { - $map = ['order_no' => $order_no, 'is_pay' => '1', 'expires_in' => time(), 'appid' => $pay->appid, 'trade_type' => $trade_type]; - $where = 'appid=:appid and order_no=:order_no and (is_pay=:is_pay or expires_in>:expires_in) and trade_type=:trade_type'; - $prepayinfo = Db::name('WechatPayPrepayid')->where($where, $map)->find(); - if (empty($prepayinfo) || empty($prepayinfo['prepayid'])) { - $out_trade_no = DataService::createSequence(18, 'WXPAY-OUTER-NO'); - if (!($prepayid = $pay->getPrepayId($openid, $title, $out_trade_no, $fee, url("@wechat/notify", '', true, true), $trade_type))) { - Log::error("内部订单号{$order_no}生成预支付失败,{$pay->errMsg}"); - return false; - } - $data = ['prepayid' => $prepayid, 'order_no' => $order_no, 'out_trade_no' => $out_trade_no, 'fee' => $fee, 'trade_type' => $trade_type]; - $data['from'] = $from; - $data['appid'] = $pay->appid; - $data['expires_in'] = time() + 5400; //微信预支付码有效时间1.5小时(最长为2小时) - if (Db::name('WechatPayPrepayid')->insert($data) > 0) { - Log::notice("内部订单号{$order_no}生成预支付成功,{$prepayid}"); - return $prepayid; - } - } - return $prepayinfo['prepayid']; - } - -} diff --git a/extend/service/SoapService.php b/extend/service/SoapService.php new file mode 100644 index 000000000..de6c7fbc5 --- /dev/null +++ b/extend/service/SoapService.php @@ -0,0 +1,65 @@ +soap = new \SoapClient($wsdl, $params); + } + + /** + * @param string $name SOAP调用方法名 + * @param array|string $arguments SOAP调用参数 + * @return array|string|bool + * @throws \think\Exception + */ + public function __call($name, $arguments) + { + try { + return $this->soap->__soapCall($name, $arguments); + } catch (\Exception $e) { + Log::error("Soap Error. Call {$name} Method --- " . $e->getMessage()); + throw new Exception($e->getMessage(), $e->getCode()); + } + } + +} diff --git a/extend/service/ToolsService.php b/extend/service/ToolsService.php index 5322854fc..357f91c84 100644 --- a/extend/service/ToolsService.php +++ b/extend/service/ToolsService.php @@ -1,7 +1,7 @@ * @date 2016/10/25 14:49 */ -class ToolsService { +class ToolsService +{ /** * Cors Options 授权处理 */ - public static function corsOptionsHandler() { + public static function corsOptionsHandler() + { + if (PHP_SESSION_ACTIVE !== session_status()) Session::init(config('session.')); + try { + $token = request()->header('token', input('token', '')); + list($name, $value) = explode('=', decode($token) . '='); + if (!empty($value) && session_name() === $name) session_id($value); + } catch (\Exception $e) { + } if (request()->isOptions()) { header('Access-Control-Allow-Origin:*'); - header('Access-Control-Allow-Headers:Accept,Referer,Host,Keep-Alive,User-Agent,X-Requested-With,Cache-Control,Content-Type,Cookie,token'); header('Access-Control-Allow-Credentials:true'); header('Access-Control-Allow-Methods:GET,POST,OPTIONS'); + header("Access-Control-Allow-Headers:Accept,Referer,Host,Keep-Alive,User-Agent,X-Requested-With,Cache-Control,Cookie,token"); + header('Content-Type:text/plain charset=utf-8'); header('Access-Control-Max-Age:1728000'); - header('Content-Type:text/plain charset=UTF-8'); - header('Content-Length: 0', true); - header('status: 204'); header('HTTP/1.0 204 No Content'); + header('Content-Length:0'); + header('status:204'); + exit; } } @@ -44,25 +58,49 @@ class ToolsService { * Cors Request Header信息 * @return array */ - public static function corsRequestHander() { + public static function corsRequestHander() + { return [ - 'Access-Control-Allow-Origin' => '*', - 'Access-Control-Allow-Credentials' => true, + 'Access-Control-Allow-Origin' => request()->header('origin', '*'), 'Access-Control-Allow-Methods' => 'GET,POST,OPTIONS', - 'X-Support' => 'service@cuci.cc', - 'X-Servers' => 'Guangzhou Cuci Technology Co. Ltd', + 'Access-Control-Allow-Credentials' => "true", ]; } + /** + * 返回成功的操作 + * @param mixed $msg 消息内容 + * @param array $data 返回数据 + * @param integer $code 返回代码 + */ + public static function success($msg, $data = [], $code = 1) + { + $result = ['code' => $code, 'msg' => $msg, 'data' => $data, 'token' => encode(session_name() . '=' . session_id())]; + throw new HttpResponseException(Response::create($result, 'json', 200, self::corsRequestHander())); + } + + /** + * 返回失败的请求 + * @param mixed $msg 消息内容 + * @param array $data 返回数据 + * @param integer $code 返回代码 + */ + public static function error($msg, $data = [], $code = 0) + { + $result = ['code' => $code, 'msg' => $msg, 'data' => $data, 'token' => encode(session_name() . '=' . session_id())]; + throw new HttpResponseException(Response::create($result, 'json', 200, self::corsRequestHander())); + } + /** * Emoji原形转换为String * @param string $content * @return string */ - public static function emojiEncode($content) { - return json_decode(preg_replace_callback("/(\\\u[ed][0-9a-f]{3})/i", function($str) { - return addslashes($str[0]); - }, json_encode($content))); + public static function emojiEncode($content) + { + return json_decode(preg_replace_callback("/(\\\u[ed][0-9a-f]{3})/i", function ($str) { + return addslashes($str[0]); + }, json_encode($content))); } /** @@ -70,10 +108,11 @@ class ToolsService { * @param string $content * @return string */ - public static function emojiDecode($content) { - return json_decode(preg_replace_callback('/\\\\\\\\/i', function() { - return '\\'; - }, json_encode($content))); + public static function emojiDecode($content) + { + return json_decode(preg_replace_callback('/\\\\\\\\/i', function () { + return '\\'; + }, json_encode($content))); } /** @@ -84,18 +123,13 @@ class ToolsService { * @param string $son 定义子数据Key * @return array */ - public static function arr2tree($list, $id = 'id', $pid = 'pid', $son = 'sub') { - $tree = $map = array(); - foreach ($list as $item) { - $map[$item[$id]] = $item; - } - foreach ($list as $item) { - if (isset($item[$pid]) && isset($map[$item[$pid]])) { - $map[$item[$pid]][$son][] = &$map[$item[$id]]; - } else { - $tree[] = &$map[$item[$id]]; - } - } + public static function arr2tree($list, $id = 'id', $pid = 'pid', $son = 'sub') + { + list($tree, $map) = [[], []]; + foreach ($list as $item) $map[$item[$id]] = $item; + foreach ($list as $item) if (isset($item[$pid]) && isset($map[$item[$pid]])) { + $map[$item[$pid]][$son][] = &$map[$item[$id]]; + } else $tree[] = &$map[$item[$id]]; unset($map); return $tree; } @@ -106,24 +140,21 @@ class ToolsService { * @param string $id ID Key * @param string $pid 父ID Key * @param string $path + * @param string $ppath * @return array */ - public static function arr2table($list, $id = 'id', $pid = 'pid', $path = 'path', $ppath = '') { - $_array_tree = self::arr2tree($list, $id, $pid); - $tree = array(); - foreach ($_array_tree as $_tree) { - $_tree[$path] = $ppath . '-' . $_tree[$id]; - $_tree['spl'] = str_repeat("   ├  ", substr_count($ppath, '-')); - if (!isset($_tree['sub'])) { - $_tree['sub'] = array(); - } - $sub = $_tree['sub']; - unset($_tree['sub']); - $tree[] = $_tree; - if (!empty($sub)) { - $sub_array = self::arr2table($sub, $id, $pid, $path, $_tree[$path]); - $tree = array_merge($tree, (Array) $sub_array); - } + public static function arr2table(array $list, $id = 'id', $pid = 'pid', $path = 'path', $ppath = '') + { + $tree = []; + foreach (self::arr2tree($list, $id, $pid) as $attr) { + $attr[$path] = "{$ppath}-{$attr[$id]}"; + $attr['sub'] = isset($attr['sub']) ? $attr['sub'] : []; + $attr['spt'] = substr_count($ppath, '-'); + $attr['spl'] = str_repeat("   ├  ", $attr['spt']); + $sub = $attr['sub']; + unset($attr['sub']); + $tree[] = $attr; + if (!empty($sub)) $tree = array_merge($tree, self::arr2table($sub, $id, $pid, $path, $attr[$path])); } return $tree; } @@ -136,14 +167,56 @@ class ToolsService { * @param string $pkey 父Key * @return array */ - public static function getArrSubIds($list, $id = 0, $key = 'id', $pkey = 'pid') { - $ids = array(intval($id)); - foreach ($list as $vo) { - if (intval($vo[$pkey]) > 0 && intval($vo[$pkey]) == intval($id)) { - $ids = array_merge($ids, self::getArrSubIds($list, intval($vo[$key]), $key, $pkey)); - } + public static function getArrSubIds($list, $id = 0, $key = 'id', $pkey = 'pid') + { + $ids = [intval($id)]; + foreach ($list as $vo) if (intval($vo[$pkey]) > 0 && intval($vo[$pkey]) === intval($id)) { + $ids = array_merge($ids, self::getArrSubIds($list, intval($vo[$key]), $key, $pkey)); } return $ids; } + /** + * 写入CSV文件头部 + * @param string $filename 导出文件 + * @param array $headers CSV 头部(一级数组) + */ + public static function setCsvHeader($filename, array $headers) + { + header('Content-Type: application/octet-stream'); + header("Content-Disposition: attachment; filename=" . iconv('utf-8', 'gbk//TRANSLIT', $filename)); + echo @iconv('utf-8', 'gbk//TRANSLIT', "\"" . implode('","', $headers) . "\"\n"); + } + + /** + * 写入CSV文件内容 + * @param array $list 数据列表(二维数组或多维数组) + * @param array $rules 数据规则(一维数组) + */ + public static function setCsvBody(array $list, array $rules) + { + foreach ($list as $data) { + $rows = []; + foreach ($rules as $rule) { + $item = self::parseKeyDot($data, $rule); + $rows[] = $item === $data ? '' : $item; + } + echo @iconv('utf-8', 'gbk//TRANSLIT', "\"" . implode('","', $rows) . "\"\n"); + flush(); + } + } + + /** + * 根据数组key查询(可带点规则) + * @param array $data 数据 + * @param string $rule 规则,如: order.order_no + * @return mixed + */ + private static function parseKeyDot(array $data, $rule) + { + list($temp, $attr) = [$data, explode('.', trim($rule, '.'))]; + while ($key = array_shift($attr)) $temp = isset($temp[$key]) ? $temp[$key] : $temp; + return (is_string($temp) || is_numeric($temp)) ? str_replace('"', '""', "\t{$temp}") : ''; + } + } diff --git a/extend/service/WechatService.php b/extend/service/WechatService.php index 35a91555a..a74aac45c 100644 --- a/extend/service/WechatService.php +++ b/extend/service/WechatService.php @@ -1,7 +1,7 @@ * @date 2017/03/22 15:32 + * + * ----- WeChat ----- + * @method \WeChat\Card WeChatCard() static 微信卡券管理 + * @method \WeChat\Custom WeChatCustom() static 微信客服消息 + * @method \WeChat\Limit WeChatLimit() static 接口调用频次限制 + * @method \WeChat\Media WeChatMedia() static 微信素材管理 + * @method \WeChat\Menu WeChatMenu() static 微信菜单管理 + * @method \WeChat\Oauth WeChatOauth() static 微信网页授权 + * @method \WeChat\Pay WeChatPay() static 微信支付商户 + * @method \WeChat\Product WeChatProduct() static 微信商店管理 + * @method \WeChat\Qrcode WeChatQrcode() static 微信二维码管理 + * @method \WeChat\Receive WeChatReceive() static 微信推送管理 + * @method \WeChat\Scan WeChatScan() static 微信扫一扫接入管理 + * @method \WeChat\Script WeChatScript() static 微信前端支持 + * @method \WeChat\Shake WeChatShake() static 微信揺一揺周边 + * @method \WeChat\Tags WeChatTags() static 微信用户标签管理 + * @method \WeChat\Template WeChatTemplate() static 微信模板消息 + * @method \WeChat\User WeChatUser() static 微信粉丝管理 + * @method \WeChat\Wifi WeChatWifi() static 微信门店WIFI管理 + * + * ----- WeMini ----- + * @method \WeMini\Account WeMiniAccount() static 小程序账号管理 + * @method \WeMini\Basic WeMiniBasic() static 小程序基础信息设置 + * @method \WeMini\Code WeMiniCode() static 小程序代码管理 + * @method \WeMini\Domain WeMiniDomain() static 小程序域名管理 + * @method \WeMini\Tester WeMiniTester() static 小程序成员管理 + * @method \WeMini\User WeMiniUser() static 小程序帐号管理 + * -------------------- + * @method \WeMini\Crypt WeMiniCrypt() static 小程序数据加密处理 + * @method \WeMini\Plugs WeMiniPlugs() static 小程序插件管理 + * @method \WeMini\Poi WeMiniPoi() static 小程序地址管理 + * @method \WeMini\Qrcode WeMiniQrcode() static 小程序二维码管理 + * @method \WeMini\Template WeMiniTemplate() static 小程序模板消息支持 + * @method \WeMini\Total WeMiniTotal() static 小程序数据接口 + * + * ----- WePay ----- + * @method \WePay\Bill WePayBill() static 微信商户账单及评论 + * @method \WePay\Order WePayOrder() static 微信商户订单 + * @method \WePay\Refund WePayRefund() static 微信商户退款 + * @method \WePay\Coupon WePayCoupon() static 微信商户代金券 + * @method \WePay\Redpack WePayRedpack() static 微信红包支持 + * @method \WePay\Transfers WePayTransfers() static 微信商户打款到零钱 + * @method \WePay\TransfersBank WePayTransfersBank() static 微信商户打款到银行卡 + * + * ----- WeOpen ----- + * @method \WeOpen\Login login() static 第三方微信登录 + * @method \WeOpen\Service service() static 第三方服务 + * + * ----- ThinkService ----- + * @method mixed wechat() static 第三方微信工具 + * @method mixed config() static 第三方配置工具 */ -class WechatService { +class WechatService +{ /** - * 通过图文ID读取图文信息 - * @param int $id 本地图文ID - * @param array $where 额外的查询条件 + * 接口类型模式 + * @var string + */ + private static $type = 'WeChat'; + + /** + * 获取微信实例ID + * @param string $name 实例对象名称 + * @param string $type 接口实例类型 + * @return SoapService|string + * @throws Exception + * @throws \think\exception\PDOException + */ + public static function instance($name, $type = null) + { + if (!in_array($type, ['WeChat', 'WeMini'])) { + $type = self::$type; + } + switch (strtolower(sysconf('wechat_type'))) { + case 'api': + $config = [ + 'token' => sysconf('wechat_token'), + 'appid' => sysconf('wechat_appid'), + 'appsecret' => sysconf('wechat_appsecret'), + 'encodingaeskey' => sysconf('wechat_encodingaeskey'), + 'mch_id' => sysconf('wechat_mch_id'), + 'mch_key' => sysconf('wechat_partnerkey'), + 'ssl_cer' => sysconf('wechat_cert_cert'), + 'ssl_key' => sysconf('wechat_cert_key'), + 'cachepath' => env('cache_path') . 'wechat' . DIRECTORY_SEPARATOR, + ]; + $class = "\\{$type}\\" . ucfirst(strtolower($name)); + if (class_exists($class)) { + return new $class($config); + } + throw new Exception("Class '{$class}' not found"); + case 'thr': + list($appid, $appkey) = [sysconf('wechat_thr_appid'), sysconf('wechat_thr_appkey')]; + $token = strtolower("{$name}-{$appid}-{$appkey}-{$type}"); + $location = config('wechat.service_url') . "/wechat/api.client/soap/{$token}.html"; + $params = ['uri' => strtolower($name), 'location' => $location, 'trace' => true]; + return new SoapService(null, $params); + default: + throw new Exception('请在后台配置微信对接授权模式!'); + } + } + + /** + * 获取微信网页JSSDK + * @param null|string $url JS签名地址 * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function getNewsById($id, $where = []) { - $data = Db::name('WechatNews')->where('id', $id)->where($where)->find(); - $article_ids = explode(',', $data['article_id']); - $articles = Db::name('WechatNewsArticle')->where('id', 'in', $article_ids)->select(); - $data['articles'] = array(); - foreach ($article_ids as $article_id) { - foreach ($articles as $article) { - if (intval($article['id']) === intval($article_id)) { - unset($article['create_by'], $article['create_at']); - $data['articles'][] = $article; + public static function webJsSDK($url = null) + { + $signUrl = is_null($url) ? app('request')->url(true) : $url; + switch (strtolower(sysconf('wechat_type'))) { + case 'api': + return WechatService::WeChatScript()->getJsSign($signUrl); + case 'thr': + return WechatService::wechat()->jsSign($signUrl); + default: + throw new Exception('请在后台配置微信对接授权模式!'); + } + } + + /** + * 初始化进入授权 + * @param string $url 授权页面URL地址 + * @param int $fullMode 授权公众号模式 + * @param bool $isRedirect 是否进行跳转 + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public static function webOauth($url, $fullMode = 0, $isRedirect = true) + { + $appid = self::getAppid(); + list($openid, $fansinfo) = [session("{$appid}_openid"), session("{$appid}_fansinfo")]; + if ((empty($fullMode) && !empty($openid)) || (!empty($fullMode) && !empty($fansinfo))) { + empty($fansinfo) || FansService::set($fansinfo); + return ['openid' => $openid, 'fansinfo' => $fansinfo]; + } + switch (strtolower(sysconf('wechat_type'))) { + case 'api': + $wechat = self::WeChatOauth(); + if (request()->get('state') !== $appid) { + $snsapi = empty($fullMode) ? 'snsapi_base' : 'snsapi_userinfo'; + $param = (strpos($url, '?') !== false ? '&' : '?') . 'rcode=' . encode($url); + $OauthUrl = $wechat->getOauthRedirect($url . $param, $appid, $snsapi); + $isRedirect && redirect($OauthUrl, [], 301)->send(); + exit("window.location.href='{$OauthUrl}'"); } - } - } - unset($articles); - return $data; - } - - /** - * 上传图片到微信服务器 - * @param string $local_url - * @return string|null - */ - public static function uploadImage($local_url) { - # 检测文件上否已经上传过了 - if (($img = Db::name('WechatNewsImage')->where('md5', md5($local_url))->find()) && isset($img['media_url'])) { - return $img['media_url']; - } - # 下载临时文件到本地 - $filename = 'wechat/image/' . join('/', str_split(md5($local_url), 16)) . '.' . strtolower(pathinfo($local_url, 4)); - $result = FileService::local($filename, file_get_contents($local_url)); - if ($result && isset($result['file'])) { - # 上传图片到微信服务器 - $wechat = &load_wechat('media'); - $mediainfo = $wechat->uploadImg(['media' => "@{$result['file']}"]); - if (!empty($mediainfo)) { - $data = ['local_url' => $local_url, 'media_url' => $mediainfo['url'], 'md5' => md5($local_url)]; - Db::name('WechatNewsImage')->insert($data); - return $mediainfo['url']; - } - } - Log::error("图片上传失败,请稍后再试!{$wechat->errMsg}[{$wechat->errCode}]"); - return null; - } - - /** - * 上传图片永久素材 - * @param string $local_url 文件URL地址 - * @param string $type 文件类型 - * @param bool $is_video 是否为视频文件 - * @param array $video_info 视频信息 - * @return string|null - */ - public static function uploadForeverMedia($local_url = '', $type = 'image', $is_video = false, $video_info = array()) { - # 检测文件上否已经上传过了 - $wechat = &load_wechat('media'); - # 检查文件URL是否已经上传为永久素材 - $map = ['md5' => ($md5 = md5($local_url)), 'appid' => $wechat->appid]; - if (($img = Db::name('WechatNewsMedia')->where($map)->find()) && isset($img['media_id'])) { - return $img['media_id']; - } - # 下载临时文件到本地 - $filename = 'wechat/image/' . join('/', str_split(md5($local_url), 16)) . '.' . strtolower(pathinfo($local_url, 4)); - $upload = FileService::local($filename, file_get_contents($local_url)); - if (!empty($upload) && isset($upload['file']) && file_exists($upload['file'])) { - # 上传图片到微信服务器 - if (false !== ($result = $wechat->uploadForeverMedia(array('media' => "@{$upload['file']}"), $type, $is_video, $video_info))) { - $data = ['md5' => $md5, 'type' => $type, 'appid' => $wechat->appid, 'media_id' => $result['media_id'], 'local_url' => $local_url]; - isset($result['url']) && $data['media_url'] = $result['url']; - Db::name('WechatNewsMedia')->insert($data); - return $data['media_id']; - } - } - Log::error("素材上传失败,请稍后再试!{$wechat->errMsg}[{$wechat->errCode}]"); - return null; - } - - /** - * 从微信服务器获取所有标签 - * @return bool - */ - public static function syncFansTags() { - $wechat = &load_wechat("User"); - if (($result = $wechat->getTags()) !== false) { - $tags = $result['tags']; - foreach ($tags as &$tag) { - $tag['appid'] = $wechat->appid; - } - Db::name('WechatFansTags')->where('appid', $wechat->appid)->delete(); - foreach (array_chunk($tags, 100) as $list) { - Db::name('WechatFansTags')->insertAll($list); - } - } - return true; - } - - /** - * 同步粉丝的标签 - * @param string $openid - * @return bool - */ - public static function syncFansTagsByOpenid($openid) { - $wechat = &load_wechat('User'); - $tagsid = $wechat->getUserTags($openid); - if ($tagsid === false || !is_array($tagsid)) { - return false; - } - $data = ['appid' => $wechat->appid, 'openid' => $openid, 'tagid_list' => join(',', $tagsid)]; - return DataService::save('wechat_fans', $data, 'openid', ['appid' => $wechat->appid]); - } - - /** - * 保存/更新粉丝信息 - * @param array $userInfo - * @param string $appid - * @return bool - */ - public static function setFansInfo($userInfo, $appid = '') { - if (!empty($userInfo['subscribe_time'])) { - $userInfo['subscribe_at'] = date('Y-m-d H:i:s', $userInfo['subscribe_time']); - } - if (!empty($userInfo['tagid_list']) && is_array($userInfo['tagid_list'])) { - $userInfo['tagid_list'] = join(',', $userInfo['tagid_list']); - } - $userInfo['appid'] = $appid; - $userInfo['nickname'] = ToolsService::emojiEncode($userInfo['nickname']); - return DataService::save('WechatFans', $userInfo, 'openid'); - } - - /** - * 读取粉丝信息 - * @param string $openid 微信用户openid - * @param string $appid 公众号appid - * @return array|false - */ - public static function getFansInfo($openid, $appid = null) { - $map = ['openid' => $openid]; - is_string($appid) && $map['appid'] = $appid; - if (($fans = Db::name('WechatFans')->where($map)->find()) && isset($fans['nickname'])) { - $fans['nickname'] = ToolsService::emojiDecode($fans['nickname']); - } - return $fans; - } - - /** - * 同步获取粉丝列表 - * @param string $next_openid - * @return bool - */ - public static function syncAllFans($next_openid = '') { - $wechat = &load_wechat('User'); - if (false === ($result = $wechat->getUserList($next_openid)) || empty($result['data']['openid'])) { - Log::error("获取粉丝列表失败, {$wechat->errMsg} [{$wechat->errCode}]"); - return false; - } - foreach (array_chunk($result['data']['openid'], 100) as $openids) { - if (false === ($info = $wechat->getUserBatchInfo($openids)) || !is_array($info)) { - Log::error("获取用户信息失败, {$wechat->errMsg} [{$wechat->errCode}]"); - return false; - } - foreach ($info as $userInfo) { - if (false === self::setFansInfo($userInfo, $wechat->appid)) { - Log::error('更新粉丝信息更新失败!'); - return false; + $token = $wechat->getOauthAccessToken(); + if (isset($token['openid'])) { + session("{$appid}_openid", $openid = $token['openid']); + if (empty($fullMode) && request()->get('rcode')) { + redirect(decode(request()->get('rcode')), [], 301)->send(); + } + session("{$appid}_fansinfo", $fansinfo = $wechat->getUserInfo($token['access_token'], $openid)); + empty($fansinfo) || FansService::set($fansinfo); } - if ($result['next_openid'] === $userInfo['openid']) { - unset($result['next_openid']); + redirect(decode(request()->get('rcode')), [], 301)->send(); + break; + case 'thr': + $service = self::wechat(); + $result = $service->oauth(session_id(), $url, $fullMode); + session("{$appid}_openid", $openid = $result['openid']); + session("{$appid}_fansinfo", $fansinfo = $result['fans']); + if ((empty($fullMode) && !empty($openid)) || (!empty($fullMode) && !empty($fansinfo))) { + empty($fansinfo) || FansService::set($fansinfo); + return ['openid' => $openid, 'fansinfo' => $fansinfo]; } - } + if ($isRedirect && !empty($result['url'])) { + redirect($result['url'], [], 301)->send(); + } + exit("window.location.href='{$result['url']}'"); + default: + throw new Exception('请在后台配置微信对接授权模式!'); + } - return !empty($result['next_openid']) ? self::syncAllFans($result['next_openid']) : true; } /** - * 同步获取黑名单信息 - * @param string $next_openid - * @return bool + * 获取当前公众号的AppId + * @return bool|string + * @throws \think\Exception + * @throws \think\exception\PDOException */ - public static function syncBlackFans($next_openid = '') { - $wechat = &load_wechat('User'); - $result = $wechat->getBacklist($next_openid); - if ($result === false || (empty($result['data']['openid']))) { - if (empty($result['total'])) { - return true; - } - Log::error("获取粉丝黑名单列表失败,{$wechat->errMsg} [{$wechat->errCode}]"); - return false; + public static function getAppid() + { + switch (strtolower(sysconf('wechat_type'))) { + case 'api': + return sysconf('wechat_appid'); + case 'thr': + return sysconf('wechat_thr_appid'); + default: + throw new Exception('请在后台配置微信对接授权模式!'); } - foreach ($result['data']['openid'] as $openid) { - if (false === ($userInfo = $wechat->getUserInfo($openid))) { - Log::error("获取用户[{$openid}]信息失败,$wechat->errMsg"); - return false; - } - $userInfo['is_back'] = '1'; - if (false === self::setFansInfo($userInfo)) { - Log::error('更新粉丝信息更新失败!'); - return false; - } - if ($result['next_openid'] === $openid) { - unset($result['next_openid']); - } + } + + /** + * 魔术静态方法实现对象 + * @param string $name 方法类名 + * @param array $arguments 调用参数 + * @return SoapService + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public static function __callStatic($name, $arguments) + { + if (substr($name, 0, 6) === 'WeMini') { + self::$type = 'WeMini'; + $name = substr($name, 6); + } elseif (substr($name, 0, 6) === 'WeChat') { + self::$type = 'WeChat'; + $name = substr($name, 6); + } elseif (substr($name, 0, 5) === 'WePay') { + self::$type = 'WePay'; + $name = substr($name, 5); } - return !empty($result['next_openid']) ? self::syncBlackFans($result['next_openid']) : true; + return self::instance($name); } } diff --git a/index.php b/index.php index 2636da582..735a45133 100644 --- a/index.php +++ b/index.php @@ -1,7 +1,7 @@ run()->send(); diff --git a/route/demo.php b/route/demo.php new file mode 100644 index 000000000..75b4df739 --- /dev/null +++ b/route/demo.php @@ -0,0 +1,59 @@ + 0, 'msg' => '测试环境禁修改用户密码!']); +}); +Route::post('admin/index/pass', function () { + return json(['code' => 0, 'msg' => '测试环境禁修改用户密码!']); +}); +Route::post('admin/config/index', function () { + return json(['code' => 0, 'msg' => '测试环境禁修改系统配置操作!']); +}); +Route::post('admin/config/file', function () { + return json(['code' => 0, 'msg' => '测试环境禁修改文件配置操作!']); +}); +Route::post('admin/menu/index', function () { + return json(['code' => 0, 'msg' => '测试环境禁排序菜单操作!']); +}); +Route::post('admin/menu/add', function () { + return json(['code' => 0, 'msg' => '测试环境禁添加菜单操作!']); +}); +Route::post('admin/menu/edit', function () { + return json(['code' => 0, 'msg' => '测试环境禁编辑菜单操作!']); +}); +Route::post('admin/menu/forbid', function () { + return json(['code' => 0, 'msg' => '测试环境禁止禁用菜单操作!']); +}); +Route::post('admin/menu/del', function () { + return json(['code' => 0, 'msg' => '测试环境禁止删除菜单操作!']); +}); +Route::post('wechat/config/index', function () { + return json(['code' => 0, 'msg' => '测试环境禁止修改微信配置操作!']); +}); +Route::post('admin/node/save', function () { + return json(['code' => 0, 'msg' => '测试环境禁止修改节点数据操作!']); +}); +Route::post('wechat/menu/edit', function () { + return json(['code' => 0, 'msg' => '测试环境禁止修改微信菜单操作!']); +}); +Route::get('wechat/menu/cancel', function () { + return json(['code' => 0, 'msg' => '测试环境禁止删除微信菜单操作!']); +}); diff --git a/route/route.php b/route/route.php new file mode 100644 index 000000000..1abdd7ac2 --- /dev/null +++ b/route/route.php @@ -0,0 +1,27 @@ + + */ +foreach (scandir(env('app_path')) as $dir) { + if ($dir[0] !== '.') { + $filename = realpath(env('app_path') . "{$dir}/init.php"); + $filename && file_exists($filename) && include($filename); + } +} + +return []; diff --git a/static/admin.js b/static/admin.js new file mode 100644 index 000000000..a49a691f4 --- /dev/null +++ b/static/admin.js @@ -0,0 +1,667 @@ +// +---------------------------------------------------------------------- +// | ThinkAdmin +// +---------------------------------------------------------------------- +// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ] +// +---------------------------------------------------------------------- +// | 官方网站: http://think.ctolog.com +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// +---------------------------------------------------------------------- +// | github开源项目:https://github.com/zoujingli/ThinkAdmin +// +---------------------------------------------------------------------- + +/** + * jQuery后台初始化 + * @version 1.0 + * @date 2018/02/03 + * @author Anyon + */ +if (typeof layui !== 'undefined') { + var form = layui.form, + layer = layui.layer, + laydate = layui.laydate; + if (typeof jQuery === 'undefined') { + var $ = jQuery = layui.$; + } +} + +$(function () { + + // 当前页面Bogy对象 + var $body = $('body'); + + // jQuery placeholder, fix for IE6,7,8,9 + var JPlaceHolder = new function () { + this.init = function () { + if (!('placeholder' in document.createElement('input'))) { + $(':input[placeholder]').map(function () { + var self = $(this), txt = self.attr('placeholder'); + self.wrap($('
    ').css({zoom: '1', margin: 'none', border: 'none', padding: 'none', background: 'none', position: 'relative'})); + var pos = self.position(), h = self.outerHeight(true), paddingleft = self.css('padding-left'); + var holder = $('').text(txt).css({position: 'absolute', left: pos.left, top: pos.top, height: h, lineHeight: h + 'px', paddingLeft: paddingleft, color: '#aaa', zIndex: '999'}).appendTo(self.parent()); + self.on('focusin focusout change keyup', function () { + self.val() ? holder.hide() : holder.show(); + }); + holder.click(function () { + self.get(0).focus(); + }); + self.val() && holder.hide(); + }); + } + }; + this.init(); + }; + + /*! 消息组件实例 */ + $.msg = new function () { + var self = this; + this.shade = [0.02, '#000']; + this.dialogIndexs = []; + // 关闭消息框 + this.close = function (index) { + return layer.close(index); + }; + // 弹出警告消息框 + this.alert = function (msg, callback) { + var index = layer.alert(msg, {end: callback, scrollbar: false}); + return this.dialogIndexs.push(index), index; + }; + // 确认对话框 + this.confirm = function (msg, ok, no) { + var index = layer.confirm(msg, {title: '操作确认', btn: ['确认', '取消']}, function () { + typeof ok === 'function' && ok.call(this); + }, function () { + typeof no === 'function' && no.call(this); + self.close(index); + }); + return index; + }; + // 显示成功类型的消息 + this.success = function (msg, time, callback) { + var index = layer.msg(msg, {icon: 1, shade: this.shade, scrollbar: false, end: callback, time: (time || 2) * 1000, shadeClose: true}); + return this.dialogIndexs.push(index), index; + }; + // 显示失败类型的消息 + this.error = function (msg, time, callback) { + var index = layer.msg(msg, {icon: 2, shade: this.shade, scrollbar: false, time: (time || 3) * 1000, end: callback, shadeClose: true}); + return this.dialogIndexs.push(index), index; + }; + // 状态消息提示 + this.tips = function (msg, time, callback) { + var index = layer.msg(msg, {time: (time || 3) * 1000, shade: this.shade, end: callback, shadeClose: true}); + return this.dialogIndexs.push(index), index; + }; + // 显示正在加载中的提示 + this.loading = function (msg, callback) { + var index = msg ? layer.msg(msg, {icon: 16, scrollbar: false, shade: this.shade, time: 0, end: callback}) : layer.load(2, {time: 0, scrollbar: false, shade: this.shade, end: callback}); + return this.dialogIndexs.push(index), index; + }; + // 自动处理显示Think返回的Json数据 + this.auto = function (data, time) { + return (parseInt(data.code) === 1) ? self.success(data.msg, time, function () { + !!data.url ? (window.location.href = data.url) : $.form.reload(); + for (var i in self.dialogIndexs) { + layer.close(self.dialogIndexs[i]); + } + self.dialogIndexs = []; + }) : self.error(data.msg, 3, function () { + !!data.url && (window.location.href = data.url); + }); + }; + }; + + /*! 表单自动化组件 */ + $.form = new function () { + var self = this; + // 默认异常提示消息 + this.errMsg = '{status}服务器繁忙,请稍候再试!'; + // 内容区域动态加载后初始化 + this.reInit = function ($container) { + $.vali.listen(this), JPlaceHolder.init(); + $container.find('[required]').parent().prevAll('label').addClass('label-required'); + }; + // 在内容区显示视图 + this.show = function (html) { + var $container = $('.framework-body').html(html); + reinit.call(this), setTimeout(reinit, 500), setTimeout(reinit, 1000); + + function reinit() { + $.form.reInit($container); + } + }; + // 以hash打开网页 + this.href = function (url, obj) { + if (url !== '#') { + window.location.href = '#' + $.menu.parseUri(url, obj); + } else if (obj && obj.getAttribute('data-menu-node')) { + var node = obj.getAttribute('data-menu-node'); + $('[data-menu-node^="' + node + '-"][data-open!="#"]:first').trigger('click'); + } + }; + // 刷新当前页面 + this.reload = function () { + window.onhashchange.call(this); + }; + // 异步加载的数据 + this.load = function (url, data, type, callback, loading, tips, time) { + var self = this, dialogIndex = 0; + (loading !== false) && (dialogIndex = $.msg.loading(tips)); + (typeof Pace === 'object') && Pace.restart(); + $.ajax({ + type: type || 'GET', url: $.menu.parseUri(url), data: data || {}, + statusCode: { + 404: function () { + $.msg.close(dialogIndex); + $.msg.tips(self.errMsg.replace('{status}', 'E404 - ')); + }, + 500: function () { + $.msg.close(dialogIndex); + $.msg.tips(self.errMsg.replace('{status}', 'E500 - ')); + } + }, + error: function (XMLHttpRequest, textStatus, errorThrown) { + $.msg.close(dialogIndex); + $.msg.tips(self.errMsg.replace('{status}', 'E' + textStatus + ' - ')); + }, + success: function (res) { + $.msg.close(dialogIndex); + if (typeof callback === 'function' && callback.call(self, res) === false) { + return false; + } + if (typeof (res) === 'object') { + return $.msg.auto(res, time || res.wait || undefined); + } + self.show(res); + } + }); + }; + // 加载HTML到目标位置 + this.open = function (url, data, callback, loading, tips) { + this.load(url, data, 'get', function (res) { + if (typeof (res) === 'object') { + return $.msg.auto(res); + } + self.show(res); + }, loading, tips); + }; + // 打开一个iframe窗口 + this.iframe = function (url, title) { + return layer.open({title: title || '窗口', type: 2, area: ['800px', '550px'], fix: true, maxmin: false, content: url}); + }; + // 加载HTML到弹出层 + this.modal = function (url, data, title, callback, loading, tips) { + this.load(url, data, 'GET', function (res) { + if (typeof (res) === 'object') { + return $.msg.auto(res); + } + var layerIndex = layer.open({ + type: 1, btn: false, area: "800px", content: res, title: title || '', success: function (dom, index) { + $(dom).find('[data-close]').off('click').on('click', function () { + if ($(this).attr('data-confirm')) { + var confirmIndex = $.msg.confirm($(this).attr('data-confirm'), function () { + layer.close(confirmIndex), layer.close(index); + }); + return false; + } + layer.close(index); + }); + $.form.reInit($(dom)); + } + }); + $.msg.dialogIndexs.push(layerIndex); + return (typeof callback === 'function') && callback.call(this); + }, loading, tips); + }; + }; + + /*! 后台菜单辅助插件 */ + $.menu = new function () { + // 计算URL地址中有效的URI + this.getUri = function (uri) { + uri = uri || window.location.href; + uri = (uri.indexOf(window.location.host) > -1 ? uri.split(window.location.host)[1] : uri).split('?')[0]; + return (uri.indexOf('#') !== -1 ? uri.split('#')[1] : uri); + }; + // 通过URI查询最有可能的菜单NODE + this.queryNode = function (url) { + var node = location.href.replace(/.*spm=([\d\-m]+).*/ig, '$1'); + if (!/^m\-/.test(node)) { + var $menu = $('[data-menu-node][data-open*="' + url.replace(/\.html$/ig, '') + '"]'); + return $menu.size() ? $menu.get(0).getAttribute('data-menu-node') : ''; + } + return node; + }; + // URL转URI + this.parseUri = function (uri, obj) { + var params = {}; + if (uri.indexOf('?') > -1) { + var serach = uri.split('?')[1].split('&'); + for (var i in serach) { + if (serach[i].indexOf('=') > -1) { + var arr = serach[i].split('='); + try { + params[arr[0]] = window.decodeURIComponent(window.decodeURIComponent(arr[1].replace(/%2B/ig, ' '))); + } catch (e) { + console.log([e, uri, serach, arr]); + } + } + } + } + uri = this.getUri(uri); + params.spm = obj && obj.getAttribute('data-menu-node') || this.queryNode(uri); + delete params[""]; + var query = '?' + $.param(params); + return uri + (query !== '?' ? query : ''); + }; + // 后台菜单动作初始化 + this.listen = function () { + var self = this; + // 左则二级菜单展示 + $('[data-submenu-layout]>a').on('click', function () { + $(this).parent().toggleClass('open'); + self.syncOpenStatus(1); + }); + // 同步二级菜单展示状态 + this.syncOpenStatus = function (mode) { + $('[data-submenu-layout]').map(function () { + var node = $(this).attr('data-submenu-layout'); + if (mode === 1) { + var type = (this.className || '').indexOf('open') > -1 ? 2 : 1; + layui.data('menu', {key: node, value: type}); + } else { + var type = layui.data('menu')[node] || 2; + (type === 2) && $(this).addClass('open'); + } + }); + }; + window.onhashchange = function () { + var hash = window.location.hash || ''; + if (hash.length < 1) { + return $('[data-menu-node][data-open!="#"]:first').trigger('click'); + } + $.form.load(hash); + self.syncOpenStatus(2); + // 菜单选择切换 + var node = self.queryNode(self.getUri()); + if (/^m\-/.test(node)) { + var $all = $('a[data-menu-node]'), tmp = node.split('-'), tmpNode = tmp.shift(); + while (tmp.length > 0) { + tmpNode = tmpNode + '-' + tmp.shift(); + $all = $all.not($('a[data-menu-node="' + tmpNode + '"]').addClass('active')); + } + $all.removeClass('active'); + // 菜单模式切换 + if (node.split('-').length > 2) { + var _tmp = node.split('-'), _node = _tmp.shift() + '-' + _tmp.shift(); + $('[data-menu-layout]').not($('[data-menu-layout="' + _node + '"]').removeClass('hide')).addClass('hide'); + $('[data-menu-node="' + node + '"]').parent('div').parent('div').addClass('open'); + $('body.framework').removeClass('mini'); + } else { + $('body.framework').addClass('mini'); + } + self.syncOpenStatus(1); + } + }; + // URI初始化动作 + window.onhashchange.call(this); + }; + }; + + // 注册对象到Jq + $.vali = function (form, callback, options) { + return (new function () { + var self = this; + // 表单元素 + this.tags = 'input,textarea,select'; + // 检测元素事件 + this.checkEvent = {change: true, blur: true, keyup: false}; + // 去除字符串两头的空格 + this.trim = function (str) { + return str.replace(/(^\s*)|(\s*$)/g, ''); + }; + // 标签元素是否可见 + this.isVisible = function (ele) { + return $(ele).is(':visible'); + }; + // 检测属性是否有定义 + this.hasProp = function (ele, prop) { + if (typeof prop !== "string") { + return false; + } + var attrProp = ele.getAttribute(prop); + return (typeof attrProp !== 'undefined' && attrProp !== null && attrProp !== false); + }; + // 判断表单元素是否为空 + this.isEmpty = function (ele, value) { + var trimValue = this.trim(ele.value); + value = value || ele.getAttribute('placeholder'); + return (trimValue === "" || trimValue === value); + }; + // 正则验证表单元素 + this.isRegex = function (ele, regex, params) { + var inputValue = ele.value, dealValue = this.trim(inputValue); + regex = regex || ele.getAttribute('pattern'); + if (dealValue === "" || !regex) { + return true; + } + if (dealValue !== inputValue) { + (ele.tagName.toLowerCase() !== "textarea") ? (ele.value = dealValue) : (ele.innerHTML = dealValue); + } + return new RegExp(regex, params || 'i').test(dealValue); + }; + // 检侧所的表单元素 + this.isAllpass = function (elements, options) { + if (!elements) { + return true; + } + var allpass = true, self = this, params = options || {}; + if (elements.size && elements.size() === 1 && elements.get(0).tagName.toLowerCase() === "form") { + elements = $(elements).find(self.tags); + } else if (elements.tagName && elements.tagName.toLowerCase() === "form") { + elements = $(elements).find(self.tags); + } + elements.each(function () { + if (self.checkInput(this, params) === false) { + return $(this).focus(), (allpass = false); + } + }); + return allpass; + }; + // 验证标志 + this.remind = function (input) { + return this.isVisible(input) ? this.showError(input, input.getAttribute('title') || '') : false; + }; + // 检测表单单元 + this.checkInput = function (input) { + var type = (input.getAttribute("type") + "").replace(/\W+$/, "").toLowerCase(); + var tag = input.tagName.toLowerCase(), isRequired = this.hasProp(input, "required"); + if (this.hasProp(input, 'data-auto-none') || input.disabled || type === 'submit' || type === 'reset' || type === 'file' || type === 'image' || !this.isVisible(input)) { + return; + } + var allpass = true; + if (type === "radio" && isRequired) { + var radiopass = false, eleRadios = input.name ? $("input[type='radio'][name='" + input.name + "']") : $(input); + eleRadios.each(function () { + (radiopass === false && $(this).is("[checked]")) && (radiopass = true); + }); + if (radiopass === false) { + allpass = this.remind(eleRadios.get(0), type, tag); + } else { + this.hideError(input); + } + } else if (type === "checkbox" && isRequired && !$(input).is("[checked]")) { + allpass = this.remind(input, type, tag); + } else if (tag === "select" && isRequired && !input.value) { + allpass = this.remind(input, type, tag); + } else if ((isRequired && this.isEmpty(input)) || !(allpass = this.isRegex(input))) { + allpass ? this.remind(input, type, "empty") : this.remind(input, type, tag); + allpass = false; + } else { + this.hideError(input); + } + return allpass; + }; + // 错误消息显示 + this.showError = function (ele, content) { + $(ele).addClass('validate-error'), this.insertError(ele); + $($(ele).data('input-info')).addClass('fadeInRight animated').css({width: 'auto'}).html(content); + }; + // 错误消息消除 + this.hideError = function (ele) { + $(ele).removeClass('validate-error'), this.insertError(ele); + $($(ele).data('input-info')).removeClass('fadeInRight').css({width: '30px'}).html(''); + }; + // 错误消息标签插入 + this.insertError = function (ele) { + var $html = $(''); + $html.css({top: $(ele).position().top + 'px', paddingBottom: $(ele).css('paddingBottom'), lineHeight: $(ele).css('height')}); + $(ele).data('input-info') || $(ele).data('input-info', $html.insertAfter(ele)); + }; + // 表单验证入口 + this.check = function (form, callback, options) { + var params = $.extend({}, options || {}); + $(form).attr("novalidate", "novalidate"); + $(form).find(self.tags).map(function () { + for (var i in self.checkEvent) { + if (self.checkEvent[i] === true) { + $(this).off(i, func).on(i, func); + } + } + + function func() { + self.checkInput(this); + } + }); + $(form).bind("submit", function (event) { + if (self.isAllpass($(this).find(self.tags), params) && typeof callback === 'function') { + if (typeof CKEDITOR === 'object' && typeof CKEDITOR.instances === 'object') { + for (var instance in CKEDITOR.instances) { + CKEDITOR.instances[instance].updateElement(); + } + } + callback.call(this, $(form).serialize()); + } + return event.preventDefault(), false; + }); + return $(form).data('validate', this); + }; + }).check(form, callback, options); + }; + + // 自动监听规则内表单 + $.vali.listen = function () { + $('form[data-auto]').map(function () { + if ($(this).attr('data-listen') !== 'true') { + var callbackname = $(this).attr('data-callback'); + $(this).attr('data-listen', 'true').vali(function (data) { + var method = this.getAttribute('method') || 'POST'; + var tips = this.getAttribute('data-tips') || undefined; + var url = this.getAttribute('action') || window.location.href; + var callback = window[callbackname || '_default_callback'] || undefined; + var time = this.getAttribute('data-time') || undefined; + $.form.load(url, data, method, callback, true, tips, time); + }); + $(this).find('[data-form-loaded]').map(function () { + $(this).html(this.getAttribute('data-form-loaded') || this.innerHTML); + $(this).removeAttr('data-form-loaded').removeClass('layui-disabled'); + }); + } + }); + }; + + // 注册对象到JqFn + $.fn.vali = function (callback, options) { + return $.vali(this, callback, options); + }; + + // 上传单个图片 + $.fn.uploadOneImage = function () { + var name = $(this).attr('name') || 'image'; + var type = $(this).data('type') || 'png,jpg'; + var $tpl = $(''); + $(this).attr('name', name).after($tpl).on('change', function () { + !!this.value && $tpl.css('backgroundImage', 'url(' + this.value + ')'); + }).trigger('change'); + }; + + // 上传多个图片 + $.fn.uploadMultipleImage = function () { + var type = $(this).data('type') || 'png,jpg'; + var name = $(this).attr('name') || 'umt-image'; + var $tpl = $(''); + $(this).attr('name', name).after($tpl).on('change', function () { + var input = this, values = [], srcs = this.value.split('|'); + $(this).prevAll('.uploadimage').map(function () { + values.push($(this).attr('data-tips-image')); + }), $(this).prevAll('.uploadimage').remove(), values.reverse(); + for (var i in srcs) { + srcs[i] && values.push(srcs[i]); + } + this.value = values.join('|'); + for (var i in values) { + var tpl = ''; + var $tpl = $(tpl).attr('data-tips-image', values[i]).css('backgroundImage', 'url(' + values[i] + ')'); + $tpl.data('input', input).data('srcs', values).data('index', i); + $tpl.on('click', 'a', function (e) { + e.stopPropagation(); + var $cur = $(this).parent(); + var dialogIndex = $.msg.confirm('确定要移除这张图片吗?', function () { + var data = $cur.data('srcs'), tmp = []; + for (var i in data) { + i !== $cur.data('index') && tmp.push(data[i]); + } + $cur.data('input').value = tmp.join('|'); + $cur.remove(), $.msg.close(dialogIndex); + }); + }); + $(this).before($tpl); + } + }).trigger('change'); + }; + + /*! 注册 data-load 事件行为 */ + $body.on('click', '[data-load]', function () { + var url = $(this).attr('data-load'), tips = $(this).attr('data-tips'); + if ($(this).attr('data-confirm')) { + return $.msg.confirm($(this).attr('data-confirm'), _goLoad); + } + return _goLoad.call(this); + + function _goLoad() { + $.form.load(url, {}, 'get', null, true, tips); + } + }); + + /*! 注册 data-serach 表单搜索行为 */ + $body.on('submit', 'form.form-search', function () { + var url = $(this).attr('action').replace(/\&?page\=\d+/g, ''), split = url.indexOf('?') === -1 ? '?' : '&'; + if ((this.method || 'get').toLowerCase() === 'get') { + return window.location.href = '#' + $.menu.parseUri(url + split + $(this).serialize()); + } + $.form.load(url, this, 'post'); + }); + + /*! 注册 data-modal 事件行为 */ + $body.on('click', '[data-modal]', function () { + return $.form.modal($(this).attr('data-modal'), 'open_type=modal', $(this).attr('data-title') || '编辑'); + }); + + /*! 注册 data-open 事件行为 */ + $body.on('click', '[data-open]', function () { + $.form.href($(this).attr('data-open'), this); + }); + + /*! 注册 data-reload 事件行为 */ + $body.on('click', '[data-reload]', function () { + $.form.reload(); + }); + + /*! 注册 data-check 事件行为 */ + $body.on('click', '[data-check-target]', function () { + var checked = !!this.checked; + $($(this).attr('data-check-target')).map(function () { + this.checked = checked; + }); + }); + + /*! 注册 data-update 事件行为 */ + $body.on('click', '[data-update]', function () { + var id = $(this).attr('data-update') || (function () { + var data = []; + return $($(this).attr('data-list-target') || 'input.list-check-box').map(function () { + (this.checked) && data.push(this.value); + }), data.join(','); + }).call(this); + if (id.length < 1) { + return $.msg.tips('请选择需要操作的数据!'); + } + var action = $(this).attr('data-action') || $(this).parents('[data-location]').attr('data-location'); + var value = $(this).attr('data-value') || 0, field = $(this).attr('data-field') || 'status'; + $.msg.confirm('确定要操作这些数据吗?', function () { + $.form.load(action, {field: field, value: value, id: id}, 'post'); + }); + }); + + /*! 注册 data-href 事件行为 */ + $body.on('click', '[data-href]', function () { + var href = $(this).attr('data-href'); + (href && href.indexOf('#') !== 0) && (window.location.href = href); + }); + + /*! 注册 data-page-href 事件行为 */ + $body.on('click', 'a[data-page-href]', function () { + window.location.href = '#' + $.menu.parseUri(this.href, this); + }); + + /*! 注册 data-file 事件行为 */ + $body.on('click', '[data-file]', function () { + var method = $(this).attr('data-file') === 'one' ? 'one' : 'mtl'; + var type = $(this).attr('data-type') || 'jpg,png', field = $(this).attr('data-field') || 'file'; + var title = $(this).attr('data-title') || '文件上传', uptype = $(this).attr('data-uptype') || ''; + var url = window.ROOT_URL + '/index.php/admin/plugs/upfile.html?mode=' + method + '&uptype=' + uptype + '&type=' + type + '&field=' + field; + $.form.iframe(url, title || '文件管理'); + }); + + /*! 注册 data-iframe 事件行为 */ + $body.on('click', '[data-iframe]', function () { + $.form.iframe($(this).attr('data-iframe'), $(this).attr('data-title') || '窗口'); + }); + + /*! 注册 data-icon 事件行为 */ + $body.on('click', '[data-icon]', function () { + var field = $(this).attr('data-icon') || $(this).attr('data-field') || 'icon'; + var url = window.ROOT_URL + '/index.php/admin/plugs/icon.html?field=' + field; + $.form.iframe(url, '图标选择'); + }); + + /*! 注册 data-tips-image 事件行为 */ + $body.on('click', '[data-tips-image]', function () { + var img = new Image(), index = $.msg.loading(); + var imgWidth = this.getAttribute('data-width') || '480px'; + img.onload = function () { + var $content = $(img).appendTo('body').css({background: '#fff', width: imgWidth, height: 'auto'}); + layer.open({ + type: 1, area: imgWidth, title: false, closeBtn: 1, + skin: 'layui-layer-nobg', shadeClose: true, content: $content, + end: function () { + $(img).remove(); + }, + success: function () { + $.msg.close(index); + } + }); + }; + img.onerror = function (e) { + $.msg.close(index); + }; + img.src = this.getAttribute('data-tips-image') || this.src; + }); + + /*! 注册 data-tips-text 事件行为 */ + $body.on('mouseenter', '[data-tips-text]', function () { + var text = $(this).attr('data-tips-text'), placement = $(this).attr('data-tips-placement') || 'auto'; + $(this).tooltip({title: text, placement: placement}).tooltip('show'); + }); + + /*! 注册 data-phone-view 事件行为 */ + $body.on('click', '[data-phone-view]', function () { + var $container = $('
    公众号
    ').appendTo('body'); + $container.find('iframe').attr('src', this.getAttribute('data-phone-view') || this.href); + layer.style(layer.open({ + type: 1, scrollbar: !1, area: ['335px', '600px'], title: !1, + closeBtn: 1, skin: 'layui-layer-nobg', shadeClose: !1, content: $container, + end: function () { + $container.remove(); + } + }), {boxShadow: 'none'}); + }); + + /*! 分页事件切换处理 */ + $body.on('change', '.pagination-trigger select', function () { + window.location.href = this.options[this.selectedIndex].getAttribute('data-url'); + }); + + /*! 初始化 */ + $.menu.listen(); + $.vali.listen(); +}); \ No newline at end of file diff --git a/static/admin/app.js b/static/admin/app.js deleted file mode 100644 index c6d53ba29..000000000 --- a/static/admin/app.js +++ /dev/null @@ -1,78 +0,0 @@ -// +---------------------------------------------------------------------- -// | Think.Admin -// +---------------------------------------------------------------------- -// | 版权所有 2014~2017 广州楚才信息科技有限公司 [ http://www.cuci.cc ] -// +---------------------------------------------------------------------- -// | 官方网站: http://think.ctolog.com -// +---------------------------------------------------------------------- -// | 开源协议 ( https://mit-license.org ) -// +---------------------------------------------------------------------- -// | github开源项目:https://github.com/zoujingli/Think.Admin -// +---------------------------------------------------------------------- - -// 当前资源URL目录 -var baseUrl = (function () { - var scripts = document.scripts, src = scripts[scripts.length - 1].src; - return src.substring(0, src.lastIndexOf("/") + 1); -})(); - -// RequireJs 配置参数 -require.config({ - baseUrl: baseUrl, - waitSeconds: 0, - map: {'*': {css: baseUrl + '../plugs/require/require.css.js'}}, - paths: { - // 自定义插件(源码自创建或已修改源码) - 'admin.plugs': ['plugs'], - 'admin.listen': ['listen'], - 'layui': ['../plugs/layui/layui'], - 'ueditor': ['../plugs/ueditor/ueditor'], - 'template': ['../plugs/template/template'], - 'pcasunzips': ['../plugs/jquery/pcasunzips'], - 'laydate': ['../plugs/layui/laydate/laydate'], - // 开源插件(未修改源码) - 'pace': ['../plugs/jquery/pace.min'], - 'json': ['../plugs/jquery/json2.min'], - 'print': ['../plugs/jquery/jquery.PrintArea'], - 'base64': ['../plugs/jquery/base64.min'], - 'jquery': ['../plugs/jquery/jquery.min'], - 'websocket': ['../plugs/socket/websocket'], - 'bootstrap': ['../plugs/bootstrap/js/bootstrap.min'], - 'jquery.ztree': ['../plugs/ztree/jquery.ztree.all.min'], - 'bootstrap.typeahead': ['../plugs/bootstrap/js/bootstrap3-typeahead.min'], - 'zeroclipboard': ['../plugs/ueditor/third-party/zeroclipboard/ZeroClipboard.min'], - 'jquery.cookies': ['../plugs/jquery/jquery.cookie'], - 'jquery.masonry': ['../plugs/jquery/masonry.min'], - - }, - shim: { - 'layui': {deps: ['jquery']}, - 'laydate': {deps: ['jquery']}, - 'bootstrap': {deps: ['jquery']}, - 'pcasunzips': {deps: ['jquery']}, - 'jquery.cookies': {deps: ['jquery']}, - 'jquery.masonry': {deps: ['jquery']}, - 'admin.plugs': {deps: ['jquery', 'layui']}, - 'bootstrap.typeahead': {deps: ['jquery', 'bootstrap']}, - 'websocket': {deps: [baseUrl + '../plugs/socket/swfobject.min.js']}, - 'admin.listen': {deps: ['jquery', 'jquery.cookies', 'admin.plugs']}, - 'jquery.ztree': {deps: ['jquery', 'css!' + baseUrl + '../plugs/ztree/zTreeStyle/zTreeStyle.css']}, - }, - deps: ['css!' + baseUrl + '../plugs/awesome/css/font-awesome.min.css'], - // 开启debug模式,不缓存资源 - urlArgs: "ver=" + (new Date()).getTime() -}); - -window.WEB_SOCKET_SWF_LOCATION = baseUrl + "../plugs/socket/WebSocketMain.swf"; -window.UEDITOR_HOME_URL = (window.ROOT_URL ? window.ROOT_URL + '/static/' : baseUrl) + 'plugs/ueditor/'; -window.LAYDATE_PATH = baseUrl + '../plugs/layui/laydate/'; - -// UI框架初始化 -require(['pace', 'jquery', 'layui', 'bootstrap', 'jquery.cookies'], function () { - layui.config({dir: baseUrl + '../plugs/layui/'}); - layui.use(['layer', 'form'], function () { - window.layer = layui.layer; - window.form = layui.form(); - require(['admin.listen']); - }); -}); diff --git a/static/admin/listen.js b/static/admin/listen.js deleted file mode 100644 index b0f84e3fb..000000000 --- a/static/admin/listen.js +++ /dev/null @@ -1,158 +0,0 @@ -// +---------------------------------------------------------------------- -// | Think.Admin -// +---------------------------------------------------------------------- -// | 版权所有 2014~2017 广州楚才信息科技有限公司 [ http://www.cuci.cc ] -// +---------------------------------------------------------------------- -// | 官方网站: http://think.ctolog.com -// +---------------------------------------------------------------------- -// | 开源协议 ( https://mit-license.org ) -// +---------------------------------------------------------------------- -// | github开源项目:https://github.com/zoujingli/Think.Admin -// +---------------------------------------------------------------------- - -define(['jquery', 'admin.plugs'], function () { - - /*! 定义当前body对象 */ - this.$body = $('body'); - - /*! 注册 data-load 事件行为 */ - this.$body.on('click', '[data-load]', function () { - var url = $(this).attr('data-load'), tips = $(this).attr('data-tips'); - function _goLoad() { - $.form.load(url, {}, 'GET', null, true, tips); - } - if ($(this).attr('data-confirm')) { - return $.msg.confirm($(this).attr('data-confirm'), _goLoad); - } - return _goLoad.call(this); - }); - - /*! 注册 data-serach 表单搜索行为 */ - this.$body.on('submit', 'form.form-search', function () { - var url = $(this).attr('action'); - var split = url.indexOf('?') === -1 ? '?' : '&'; - if ((this.method || 'get').toLowerCase() === 'get') { - window.location.href = '#' + $.menu.parseUri(url + split + $(this).serialize()); - } else { - $.form.load(url, this, 'post'); - } - }); - - /*! 注册 data-modal 事件行为 */ - this.$body.on('click', '[data-modal]', function () { - return $.form.modal($(this).attr('data-modal'), 'open_type=modal', $(this).attr('data-title') || '编辑'); - }); - - /*! 注册 data-open 事件行为 */ - this.$body.on('click', '[data-open]', function () { - $.form.href($(this).attr('data-open'), this); - }); - - /*! 注册 data-reload 事件行为 */ - this.$body.on('click', '[data-reload]', function () { - $.form.reload(); - }); - - /*! 注册 data-check 事件行为 */ - this.$body.on('click', '[data-check-target]', function () { - var checked = !!this.checked; - $($(this).attr('data-check-target')).map(function () { - this.checked = checked; - }); - }); - - /*! 注册 data-update 事件行为 */ - this.$body.on('click', '[data-update]', function () { - var id = $(this).attr('data-update') || (function () { - var data = []; - return $($(this).attr('data-list-target') || 'input.list-check-box').map(function () { - (this.checked) && data.push(this.value); - }), data.join(','); - }).call(this); - if (id.length < 1) { - return $.msg.tips('请选择需要操作的数据!'); - } - var action = $(this).attr('data-action') || $(this).parents('[data-location]').attr('data-location'); - var value = $(this).attr('data-value') || 0, field = $(this).attr('data-field') || 'status'; - $.msg.confirm('确定要操作这些数据吗?', function () { - $.form.load(action, {field: field, value: value, id: id}, 'POST'); - }); - }); - - /*! 注册 data-href 事件行为 */ - this.$body.on('click', '[data-href]', function () { - var href = $(this).attr('data-href'); - if (href && href.indexOf('#') !== 0) { - window.location.href = href; - } - }); - - /*! 注册 data-page-href 事件行为 */ - this.$body.on('click', 'a[data-page-href]', function () { - window.location.href = '#' + $.menu.parseUri(this.href, this); - }); - - /*! 注册 data-file 事件行为 */ - this.$body.on('click', '[data-file]', function () { - var type = $(this).attr('data-type') || 'jpg,png'; - var field = $(this).attr('data-field') || 'file'; - var method = $(this).attr('data-file') === 'one' ? 'one' : 'mtl'; - var title = $(this).attr('data-title') || '文件上传'; - var uptype = $(this).attr('data-uptype') || ''; - var url = window.ROOT_URL + '/index.php/admin/plugs/upfile/mode/' + method + '.html?uptype=' + uptype + '&type=' + type + '&field=' + field; - $.form.iframe(url, title || '文件管理'); - }); - - /*! 注册 data-iframe 事件行为 */ - this.$body.on('click', '[data-iframe]', function () { - $.form.iframe($(this).attr('data-iframe'), $(this).attr('data-title') || '窗口'); - }); - - /*! 注册 data-icon 事件行为 */ - this.$body.on('click', '[data-icon]', function () { - var field = $(this).attr('data-icon') || $(this).attr('data-field') || 'icon'; - var url = window.ROOT_URL + '/index.php/admin/plugs/icon.html?field=' + field; - $.form.iframe(url, '图标选择'); - }); - - /*! 注册 data-tips-image 事件行为 */ - this.$body.on('click', '[data-tips-image]', function () { - var src = this.getAttribute('data-tips-image') || this.src, img = new Image(); - var imgWidth = this.getAttribute('data-width') || '480px'; - img.onload = function () { - layer.open({ - type: 1, area: imgWidth, title: false, closeBtn: 1, skin: 'layui-layer-nobg', shadeClose: true, - content: $(img).appendTo('body').css({background: '#fff', width: imgWidth, height: 'auto'}), - end: function () { - $(img).remove(); - } - }); - }; - img.src = src; - }); - - /*! 注册 data-tips-text 事件行为 */ - this.$body.on('mouseenter', '[data-tips-text]', function () { - var text = $(this).attr('data-tips-text'); - var placement = $(this).attr('data-tips-placement') || 'auto'; - $(this).tooltip({title: text, placement: placement}).tooltip('show'); - }); - - /*! 注册 data-phone-view 事件行为 */ - this.$body.on('click', '[data-phone-view]', function () { - var $container = $('
    公众号
    ').appendTo('body'); - $container.find('iframe').attr('src', this.getAttribute('data-phone-view') || this.href); - layer.style(layer.open({type: 1, scrollbar: !1, area: ['330px', '600px'], title: !1, closeBtn: 1, skin: 'layui-layer-nobg', shadeClose: !!1, - content: $container, - end: function () { - $container.remove(); - } - }), {boxShadow: 'none'}); - }); - - /*! 后台菜单控制初始化 */ - $.menu.listen(); - - /*! 表单监听初始化 */ - $.validate.listen(this); -}); \ No newline at end of file diff --git a/static/admin/plugs.js b/static/admin/plugs.js deleted file mode 100644 index 7c4770ada..000000000 --- a/static/admin/plugs.js +++ /dev/null @@ -1,804 +0,0 @@ - -// +---------------------------------------------------------------------- -// | Think.Admin -// +---------------------------------------------------------------------- -// | 版权所有 2014~2017 广州楚才信息科技有限公司 [ http://www.cuci.cc ] -// +---------------------------------------------------------------------- -// | 官方网站: http://think.ctolog.com -// +---------------------------------------------------------------------- -// | 开源协议 ( https://mit-license.org ) -// +---------------------------------------------------------------------- -// | github开源项目:https://github.com/zoujingli/Think.Admin -// +---------------------------------------------------------------------- - -define(['jquery'], function () { - - /*! - * jQuery placeholder, fix for IE6,7,8,9 - */ - var JPlaceHolder = { - _check: function () { - return 'placeholder' in document.createElement('input'); - }, - init: function () { - !this._check() && this.fix(); - }, - fix: function () { - $(':input[placeholder]').map(function () { - var self = $(this), txt = self.attr('placeholder'); - self.wrap($('
    ').css({zoom: '1', margin: 'none', border: 'none', padding: 'none', background: 'none', position: 'relative'})); - var pos = self.position(), h = self.outerHeight(true), paddingleft = self.css('padding-left'); - var holder = $('').text(txt).css({position: 'absolute', left: pos.left, top: pos.top, height: h, lineHeight: h + 'px', paddingLeft: paddingleft, color: '#aaa'}).appendTo(self.parent()); - self.on('focusin focusout change keyup', function () { - self.val() ? holder.hide() : holder.show(); - }); - holder.click(function () { - self.get(0).focus(); - }); - self.val() && holder.hide(); - }); - } - }; - JPlaceHolder.init(); - - /** - * 定义消息处理构造方法 - */ - function msg() { - this.version = '2.0'; - this.shade = [0.02, '#000']; - this.closeIndexs = {}; - } - - /** - * 关闭消息框 - */ - msg.prototype.close = function () { - if (!this.closeIndexs['_' + this.index]) { - this.closeIndexs['_' + this.index] = true; - return layer.close(this.index); - } - }; - - /** - * 弹出警告消息框 - * @param msg 消息内容 - * @param callback 回调函数 - * @return {*|undefined} - */ - msg.prototype.alert = function (msg, callback) { - this.close(); - return this.index = layer.alert(msg, {end: callback, scrollbar: false}); - }; - - /** - * 确认对话框 - * @param msg 提示消息内容 - * @param ok 确认的回调函数 - * @param no 取消的回调函数 - * @return {undefined|*} - */ - msg.prototype.confirm = function (msg, ok, no) { - var self = this; - return this.index = layer.confirm(msg, {btn: ['确认', '取消']}, function () { - typeof ok === 'function' && ok.call(this); - self.close(); - }, function () { - typeof no === 'function' && ok.call(this); - self.close(); - }); - }; - - /** - * 显示成功类型的消息 - * @param msg 消息内容 - * @param time 延迟关闭时间 - * @param callback 回调函数 - * @return {common_L11._msg|*} - */ - msg.prototype.success = function (msg, time, callback) { - this.close(); - return this.index = layer.msg(msg, { - icon: 1, - shade: this.shade, - scrollbar: false, - end: callback, - time: (time || 2) * 1000, - shadeClose: true - }); - }; - - /** - * 显示失败类型的消息 - * @param msg 消息内容 - * @param time 延迟关闭时间 - * @param callback 回调函数 - * @return {common_L11._msg|*} - */ - msg.prototype.error = function (msg, time, callback) { - this.close(); - return this.index = layer.msg(msg, { - icon: 2, - shade: this.shade, - scrollbar: false, - time: (time || 3) * 1000, - end: callback, - shadeClose: true - }); - }; - - /** - * 状态消息提示 - * @param msg 消息内容 - * @param time 显示时间s - * @param callback 回调函数 - * @return {common_L11._msg|*} - */ - msg.prototype.tips = function (msg, time, callback) { - this.close(); - return this.index = layer.msg(msg, { - time: (time || 3) * 1000, - shade: this.shade, - end: callback, - shadeClose: true - }); - }; - - /** - * 显示正在加载中的提示 - * @param msg 提示内容 - * @param callback 回调方法 - * @return {common_L11._msg|*} - */ - msg.prototype.loading = function (msg, callback) { - this.close(); - return this.index = msg - ? layer.msg(msg, {icon: 16, scrollbar: false, shade: this.shade, time: 0, end: callback}) - : layer.load(2, {time: 0, scrollbar: false, shade: this.shade, end: callback}); - }; - - /** - * 自动处理显示Think返回的Json数据 - * @param data JSON数据对象 - * @param time 延迟关闭时间 - * @return {common_L11._msg|*} - */ - msg.prototype.auto = function (data, time) { - var self = this; - if (parseInt(data.code) === 1) { - return self.success(data.msg, time, function () { - !!data.url ? (window.location.href = data.url) : $.form.reload(); - if (self.autoSuccessCloseIndexs && self.autoSuccessCloseIndexs.length > 0) { - for (var i in self.autoSuccessCloseIndexs) { - layer.close(self.autoSuccessCloseIndexs[i]); - } - self.autoSuccessCloseIndexs = []; - } - }); - } - self.error(data.msg, 3, function () { - !!data.url && (window.location.href = data.url); - }); - }; - - /** - * auto处理成功的自动关闭 - * @param index - */ - msg.prototype.addAutoSuccessCloseIndex = function (index) { - this.autoSuccessCloseIndexs = this.autoSuccessCloseIndexs || []; - this.autoSuccessCloseIndexs.push(index); - }; - - /** - * 将消息对象挂载到Jq - */ - $.msg = new msg(); - - - /** - * 表单构造函数 - * @private - */ - function _form() { - this.version = '2.0'; - this.errMsg = '{status}服务器繁忙,请稍候再试!'; - } - - /** - * 异步加载的数据 - * @param url 请求的地址 - * @param data 额外请求数据 - * @param type 提交的类型 GET|POST - * @param callback 成功后的回调方法 - * @param loading 是否显示加载层 - * @param tips 提示消息 - * @param time 消息提示时间 - */ - _form.prototype.load = function (url, data, type, callback, loading, tips, time) { - var self = this, dialogIndex = 0; - (loading !== false) && (dialogIndex = $.msg.loading(tips)); - (typeof Pace === 'object') && Pace.restart(); - $.ajax({ - type: type || 'GET', - url: $.menu.parseUri(url), - data: data || {}, - statusCode: { - 404: function () { - $.msg.tips(self.errMsg.replace('{status}', 'E404 - ')); - }, - 500: function () { - $.msg.tips(self.errMsg.replace('{status}', 'E500 - ')); - } - }, - error: function (XMLHttpRequest, textStatus, errorThrown) { - $.msg.tips(self.errMsg.replace('{status}', 'E' + textStatus + ' - ')); - }, - success: function (res) { - $.msg.close(dialogIndex); - if (typeof callback === 'function' && callback.call(self, res) === false) { - return false; - } - if (typeof (res) === 'object') { - return $.msg.auto(res, time); - } - self.show(res); - } - }); - }; - - /** - * 动态HTML事件重载 - * @param $container - */ - _form.prototype.reInit = function ($container) { - $.validate.listen.call(this), JPlaceHolder.init(); - $container.find('[required]').parent().prevAll('label').addClass('label-required'); - }; - - /** - * 加载HTML到目标位置 - * @param url 目标URL地址 - * @param data URL参数 - * @param callback 回调函数 - * @param loading 是否显示加载 - * @param tips 提示消息 - */ - _form.prototype.open = function (url, data, callback, loading, tips) { - this.load(url, data, 'GET', function (res) { - if (typeof (res) === 'object') { - return $.msg.auto(res); - } - var $container = $('.layer-main-container').html(res); - reinit.call(this), setTimeout(reinit, 500), setTimeout(reinit, 1000); - return (typeof callback === 'function') && callback.call(this); - function reinit() { - $.form.reInit($container); - } - }, loading, tips); - }; - - /** - * 打开一个内置HTML页面 - * @param url - * @param obj - */ - _form.prototype.href = function (url, obj) { - window.location.href = '#' + $.menu.parseUri(url, obj); - }; - - /** - * 加载HTML到弹出层 - * @param url - * @param data - * @param title - * @param callback - * @param loading - * @param tips - */ - _form.prototype.modal = function (url, data, title, callback, loading, tips) { - this.load(url, data, 'GET', function (res) { - if (typeof (res) === 'object') { - return $.msg.auto(res); - } - layer.open({ - type: 1, - btn: false, - area: "800px", - content: res, - title: title || '', - success: function (dom, index) { - // 此窗口完成时需要自动关闭 - $.msg.addAutoSuccessCloseIndex(index); - var $container = $(dom); - /* 处理样式及返回按钮事件 */ - $container.find('[data-close]').off('click').on('click', function () { - if ($(this).attr('data-confirm')) { - $.msg.confirm($(this).attr('data-confirm'), function () { - layer.close(index); - }); - } else { - layer.close(index); - } - }); - /* 事件重载 */ - $.form.reInit($container); - } - }); - return (typeof callback === 'function') && callback.call(this); - }, loading, tips); - }; - - /** - * 显示HTML到中主内容区 - * @param html - */ - _form.prototype.show = function (html) { - var $container = $('.layer-main-container').html(html); - reinit.call(this), setTimeout(reinit, 500), setTimeout(reinit, 1000); - function reinit() { - $.form.reInit($container); - } - }; - - /** - * 打开一个iframe窗口 - * @param url iframe打开的URL地址 - * @param title 窗口标题 - */ - _form.prototype.iframe = function (url, title) { - return layer.open({title: title || '窗口', type: 2, area: ['800px', '530px'], fix: true, maxmin: false, content: url}); - }; - - /** - * 关闭FORM框 - * @return {*|jQuery} - */ - _form.prototype.close = function () { - return $(this._modal).modal('hide'); - }; - - /** - * 刷新当前页面 - */ - _form.prototype.reload = function () { - window.onhashchange.call(this); - }; - - /*!表单实例挂载*/ - $.form = new _form(); - - /** - * 定义模块函数 - * @returns {validate_L1.validate} - */ - var validate = function () { - // 表单元素 - this.inputTag = 'input,textarea,select'; - // 检测元素事件 - this.checkEvent = {change: true, blur: true, keyup: false}; - }; - - /** - *获取表单元素的类型 - */ - validate.prototype.getElementType = function (ele) { - return (ele.getAttribute("type") + "").replace(/\W+$/, "").toLowerCase(); - }; - - /** - *去除字符串两头的空格 - */ - validate.prototype.trim = function (str) { - return str.replace(/(^\s*)|(\s*$)/g, ''); - }; - - - /** - * 标签元素是否可见 - * @returns {Boolean} - */ - validate.prototype.isVisible = function (ele) { - return $(ele).is(':visible'); - }; - - /** - * 检测属性是否有定义 - * @param {type} ele - * @param {type} prop - * @param {type} undefined - * @returns {Boolean} - */ - validate.prototype.hasProp = function (ele, prop, undefined) { - if (typeof prop !== "string") { - return false; - } - var attrProp = ele.getAttribute(prop); - return (attrProp !== undefined && attrProp !== null && attrProp !== false) - }; - - /** - * 判断表单元素是否为空 - * @param {type} ele - * @param {type} value - * @returns {Boolean} - */ - validate.prototype.isEmpty = function (ele, value) { - value = value || ele.getAttribute('placeholder'); - var trimValue = this.trim(ele.value); - return (trimValue === "" || trimValue === value); - }; - - /** - * 正则验证表单元素 - * @param {type} ele - * @param {type} regex - * @param {type} params - * @returns {Boolean} - */ - validate.prototype.isRegex = function (ele, regex, params) { - // 原始值和处理值 - var inputValue = ele.value, dealValue = inputValue; - var self = this, type = this.getElementType(ele); - if (type !== "password") { // 密码不trim前后空格 - dealValue = this.trim(inputValue); - if (dealValue !== inputValue) { - if (ele.tagName.toLowerCase() !== "textarea") { - ele.value = dealValue; - } else { - ele.innerHTML = dealValue; - } - } - } - // 获取正则表达式,pattern属性获取优先,然后通过type类型匹配。注意,不处理为空的情况 - regex = regex || ele.getAttribute('pattern'); - if (dealValue === "" || !regex) { - return true; - } - // multiple多数据的处理 - var isMultiple = this.hasProp(ele, 'multiple'), newRegExp = new RegExp(regex, params || 'i'); - // number类型下multiple是无效的 - if (isMultiple && !/^number|range$/i.test(type)) { - var isAllPass = true; - var dealValues = dealValue.split(","); - for (var i in dealValues) { - var partValue = self.trim(dealValues[i]); - if (isAllPass && !newRegExp.test(partValue)) { - isAllPass = false; - } - } - return isAllPass; - } else { - return newRegExp.test(dealValue); - } - }; - - /** - * 检侧所的表单元素 - */ - validate.prototype.isAllpass = function (elements, options) { - if (!elements) { - return true; - } - var allpass = true, self = this, params = options || {}; - if (elements.size && elements.size() === 1 && elements.get(0).tagName.toLowerCase() === "form") { - elements = $(elements).find(self.inputTag); - } else if (elements.tagName && elements.tagName.toLowerCase() === "form") { - elements = $(elements).find(self.inputTag); - } - elements.each(function () { - if (self.checkInput(this, params) === false) { - return $(this).focus(), (allpass = false); - } - }); - return allpass; - }; - - /** - * 验证标志 - */ - validate.prototype.remind = function (input, type, tag) { - var text = ''; - // 如果元素完全显示 - if (this.isVisible(input)) { - if (type === "radio" || type === "checkbox") { - this.errorPlacement(input, this.getErrMsg(input)); - } else if (tag === "select" || tag === "empty") { - // 下拉值为空或文本框文本域等为空 - this.errorPlacement(input, (tag === "empty" && text) ? "您尚未输入" + text : this.getErrMsg(input)); - } else if (/^range|number$/i.test(type) && Number(input.value)) { - // 整数值与数值的特殊提示 - this.errorPlacement(input, "值无效"); - } else { - // 文本框文本域格式不准确 - var finalText = this.getErrMsg(input); - if (text) { - finalText = "您输入的" + text + "格式不准确"; - } - this.errorPlacement(input, finalText); - } - } - return false; - }; - - /** - * 检测表单单元 - */ - validate.prototype.checkInput = function (input, options) { - var type = this.getElementType(input); - var tag = input.tagName.toLowerCase(); - var isRequired = this.hasProp(input, "required"); - var isNone = this.hasProp(input, 'data-auto-none'); - //无需要验证 - if (isNone || input.disabled || type === 'submit' || type === 'reset' || type === 'file' || type === 'image' || !this.isVisible(input)) { - return; - } - var allpass = true; - // 需要验证的有 - if (type === "radio" && isRequired) { - var eleRadios = input.name ? $("input[type='radio'][name='" + input.name + "']") : $(input); - var radiopass = false; - eleRadios.each(function () { - if (radiopass === false && $(this).is("[checked]")) { - radiopass = true; - } - }); - if (radiopass === false) { - allpass = this.remind(eleRadios.get(0), type, tag); - } else { - this.successPlacement(input); - } - } else if (type === "checkbox" && isRequired && !$(input).is("[checked]")) { - allpass = this.remind(input, type, tag); - } else if (tag === "select" && isRequired && !input.value) { - allpass = this.remind(input, type, tag); - } else if ((isRequired && this.isEmpty(input)) || !(allpass = this.isRegex(input))) { - allpass ? this.remind(input, type, "empty") : this.remind(input, type, tag); - allpass = false; - } else { - this.successPlacement(input); - } - return allpass; - }; - - /** - *获取错误提示的内容 - */ - validate.prototype.getErrMsg = function (ele) { - return ele.getAttribute('title') || ''; - }; - - /** - * 错误消息显示 - * @param {type} ele - * @param {type} content - * @param {type} options - * @returns {undefined} - */ - validate.prototype.errorPlacement = function (ele, content) { - $(ele).addClass('validate-error'), this.insertErrorEle(ele); - $($(ele).data('input-info')).addClass('fadeInRight animated').css({width: 'auto'}).html(content); - }; - - /** - * 错误消息消除 - */ - validate.prototype.successPlacement = function (ele) { - $(ele).removeClass('validate-error'), this.insertErrorEle(ele); - $($(ele).data('input-info')).removeClass('fadeInRight').css({width: '30px'}).html(''); - }; - - /** - * 错误消息标签插入 - * @param {type} ele - * @returns {undefined} - */ - validate.prototype.insertErrorEle = function (ele) { - var $html = $(''); - $html.css({top: $(ele).position().top + 'px', paddingTop: $(ele).css('paddingTop'), paddingBottom: $(ele).css('paddingBottom'), lineHeight: $(ele).css('lineHeight')}); - $(ele).data('input-info') || $(ele).data('input-info', $html.insertAfter(ele)); - }; - - /** - * 表单验证入口 - * @param {type} callback - * @param {type} options - * @returns {$|Function|Zepto} - */ - validate.prototype.check = function (form, callback, options) { - var params = $.extend({}, options || {}), self = this; - // 去除HTML默认验证 - $(form).attr("novalidate", "novalidate"); - // 表单元素动态监听 - $(form).find(self.inputTag).map(function () { - var func = function () { - self.checkInput(this); - }; - for (var i in self.checkEvent) { - if (self.checkEvent[i] === true) { - $(this).off(i, func).on(i, func); - } - } - }); - // 表单提交事情监听 - $(form).bind("submit", function (event) { - if (self.isAllpass($(this).find(self.inputTag), params) && typeof callback === 'function') { - callback.call(this, $(form).serialize()); - } - return event.preventDefault(), false; - }); - // 表单对象绑定 - return $(form).data('validate', this); - }; - - /** - * 注册对象到JqFn - * @param callback - * @param options - * @return {$|Function|Zepto} - */ - $.fn.validate = function (callback, options) { - return (new validate()).check(this, callback, options); - }; - - /** - * 注册对象到Jq - * @param form - * @param callback - * @param options - * @return {$|Function|Zepto} - */ - $.validate = function (form, callback, options) { - return (new validate()).check(form, callback, options); - }; - - /*! 自动监听规则内表单 */ - $.validate.listen = function () { - $('form[data-auto]').map(function () { - if ($(this).attr('data-listen') !== 'true') { - // 表单监听初始化 - var callback = $(this).attr('data-callback'); - $(this).attr('data-listen', 'true').validate(function (data) { - $.form.load(this.getAttribute('action') || window.location.href, - data, - this.getAttribute('method') || 'POST', - window[callback || '_default_callback'] || undefined, - true, - this.getAttribute('data-tips') || undefined, - this.getAttribute('data-time') || undefined); - }); - // 表单监听初始完成后的处理 - $(this).find('[data-form-loaded]').map(function () { - $(this).html(this.getAttribute('data-form-loaded') || this.innerHTML); - $(this).removeAttr('data-form-loaded').removeClass('layui-disabled'); - }); - } - }); - }; - - /** - * 后台菜单辅助插件 - * @returns {plugsL#14.menu} - */ - var menu = function () { - this.version = '1.0'; - }; - - /*! 计算URL地址中有效的URI */ - menu.prototype.getUri = function (uri) { - uri = uri || window.location.href; - uri = (uri.indexOf(window.location.host) !== -1 ? uri.split(window.location.host)[1] : uri).split('?')[0]; - return (uri.indexOf('#') !== -1 ? uri.split('#')[1] : uri); - }; - - /*! 通过URI查询最有可能的菜单NODE */ - menu.prototype.queryNode = function (url) { - var $menu = $('[data-menu-node][data-open*="_URL_"]'.replace('_URL_', url.replace(/\.html$/ig, ''))); - if ($menu.size()) { - return $menu.get(0).getAttribute('data-menu-node'); - } - return /^m\-/.test(node = location.href.replace(/.*spm=([\d\-m]+).*/ig, '$1')) ? node : ''; - }; - - /*! URL转URI */ - menu.prototype.parseUri = function (uri, obj) { - var params = {}; - if (uri.indexOf('?') !== -1) { - var queryParams = uri.split('?')[1].split('&'); - for (var i in queryParams) { - if (queryParams[i].indexOf('=') !== -1) { - var hash = queryParams[i].split('='); - try { - params[hash[0]] = window.decodeURIComponent(window.decodeURIComponent(hash[1].replace(/%2B/ig, ' '))); - } catch (e) { - console.log([e, uri, queryParams, hash]); - } - } - } - } - uri = this.getUri(uri); - params.spm = obj && obj.getAttribute('data-menu-node') || this.queryNode(uri); - if (!params.token) { - var token = window.location.href.replace(/.*token=(\w+).*/ig, '$1'); - (/^\w{16}$/.test(token)) && (params.token = token); - } - delete params[""]; - var query = '?' + $.param(params); - return uri + (query !== '?' ? query : ''); - }; - - /*! 后台菜单动作初始化 */ - menu.prototype.listen = function () { - /*! 左侧菜单状态切换 */ - $('ul.sidebar-trans .nav-item a').on('click', function () { - $(this).parents('.sidebar-nav.main-nav').addClass('open').find('ul.sidebar-trans').show(); - $('.sidebar-trans .nav-item').not($(this).parent().addClass('active')).removeClass('active'); - }); - $('body').on('click', '.framework-sidebar-full .sidebar-title', function () { - var $trans = $(this).next('ul.sidebar-trans'), node = $trans.attr('data-menu-node') || false; - node && $.cookie(node, $(this).parent().toggleClass('open').hasClass('open') ? 2 : 1); - $(this).parent().hasClass('open') ? $trans.show() : $trans.hide(); - }); - $('ul.sidebar-trans').map(function () { - var node = this.getAttribute('data-menu-node') || false; - node && (parseInt($.cookie(node) || 2) === 2) && $(this).show().parent().addClass('open'); - }); - - /*! Mini 菜单模式 Tips 显示*/ - $('body').on('mouseenter mouseleave', '.framework-sidebar-mini .sidebar-trans .nav-item,.framework-sidebar-mini .sidebar-title', function (e) { - $(this).tooltip({ - template: '', - title: $(this).text(), placement: 'right', container: 'body' - }).tooltip('show'), (e.type === 'mouseleave') && $(this).tooltip('destroy'); - }); - - /*! 切换左侧菜单 */ - var $menutarget = $('[data-menu-target]').on('click', function () { - $menutarget.not($(this).addClass('active')).removeClass('active'); - var menuNode = $(this).attr('data-menu-target'); - var $leftmenu = $('[data-menu-box=' + menuNode + ']').removeClass('hide'); - $("[data-menu-box]").not($leftmenu).addClass('hide'); - $leftmenu.find('[data-open]:first').trigger('click') - }); - - /*! 左侧菜单样式切换 */ - var $targetmenu = $('.sidebar-fold').on('click', function () { - var $body = $('.framework-body').toggleClass('framework-sidebar-mini framework-sidebar-full'); - $.cookie('menu-style', $body.hasClass('framework-sidebar-mini') ? 'mini' : 'full'); - }); - ($.cookie('menu-style') !== 'mini') && $targetmenu.trigger('click'); - - /*! URI路由处理 */ - window.onhashchange = function () { - var hash = (window.location.hash || '').substring(1), node = hash.replace(/.*spm=([\d\-m]+).*/ig, "$1"); - if (!/^m\-[\d\-]+$/.test(node)) { - node = $.menu.queryNode($.menu.getUri()) || ''; - } - if (hash.length < 1 || node.length < 1) { - return $('.topbar-home-link:first').trigger('click'); - } - /* 顶部菜单选中处理 */ - var parentNode = [node.split('-')[0], node.split('-')[1]].join('-'); - $('[data-menu-target]').not($('[data-menu-target="' + parentNode + '"]').addClass('active')).removeClass('active'); - /* 左则菜单处理 */ - var $menu = $('[data-menu-node="' + node + '"]').eq(0); - if ($menu.size() > 0) { - $('.framework-container').addClass('framework-sidebar-full'); - var $li = $menu.parent('li').addClass('active'); - $li.parents('.framework-sidebar').find('li.active').not($li).removeClass('active'); - $menu.parents('.sidebar-trans').removeClass('hide').show(); - $menu.parents('.main-nav').addClass('open'); - $menu.parents('[data-menu-box]').removeClass('hide').siblings('[data-menu-box]').addClass('hide'); - } else { - $('.framework-container').removeClass('framework-sidebar-full'); - } - $.form.open(hash); - }; - // URI初始化动作 - window.onhashchange.call(this); - }; - - /*! 实例并加载到jQ对象上 */ - $.menu = new menu(); - -}); \ No newline at end of file diff --git a/static/app.js b/static/app.js new file mode 100644 index 000000000..7295bc5b3 --- /dev/null +++ b/static/app.js @@ -0,0 +1,73 @@ +// +---------------------------------------------------------------------- +// | ThinkAdmin +// +---------------------------------------------------------------------- +// | 版权所有 2014~2017 广州楚才信息科技有限公司 [ http://www.cuci.cc ] +// +---------------------------------------------------------------------- +// | 官方网站: http://think.ctolog.com +// +---------------------------------------------------------------------- +// | 开源协议 ( https://mit-license.org ) +// +---------------------------------------------------------------------- +// | github开源项目:https://github.com/zoujingli/ThinkAdmin +// +---------------------------------------------------------------------- + +// 当前资源URL目录 +var baseRoot = (function () { + var scripts = document.scripts, src = scripts[scripts.length - 1].src; + return src.substring(0, src.lastIndexOf("/") + 1); +})(); + +// 配置参数 +require.config({ + waitSeconds: 60, + baseUrl: baseRoot, + map: {'*': {css: baseRoot + 'plugs/require/require.css.js'}}, + paths: { + 'template': ['plugs/template/template'], + 'pcasunzips': ['plugs/jquery/pcasunzips'], + // openSource + 'json': ['plugs/jquery/json2.min'], + 'layui': ['plugs/layui/layui'], + 'base64': ['plugs/jquery/base64.min'], + 'angular': ['plugs/angular/angular.min'], + 'ckeditor': ['plugs/ckeditor/ckeditor'], + 'websocket': ['plugs/socket/websocket'], + 'clipboard': ['plugs/clipboard/clipboard.min'], + // jQuery + 'jquery.ztree': ['plugs/ztree/jquery.ztree.all.min'], + 'jquery.masonry': ['plugs/jquery/masonry.min'], + 'jquery.cookies': ['plugs/jquery/jquery.cookie'], + // bootstrap + 'bootstrap': ['plugs/bootstrap/js/bootstrap.min'], + 'bootstrap.typeahead': ['plugs/bootstrap/js/bootstrap3-typeahead.min'], + 'bootstrap.multiselect': ['plugs/bootstrap-multiselect/bootstrap-multiselect'], + // distpicker + 'distpicker': ['plugs/distpicker/distpicker'], + }, + shim: { + // open-source + 'websocket': {deps: [baseRoot + 'plugs/socket/swfobject.min.js']}, + // jquery + 'jquery.ztree': {deps: ['css!' + baseRoot + 'plugs/ztree/zTreeStyle/zTreeStyle.css']}, + // bootstrap + 'bootstrap.typeahead': {deps: ['bootstrap']}, + 'bootstrap.multiselect': {deps: ['bootstrap', 'css!' + baseRoot + 'plugs/bootstrap-multiselect/bootstrap-multiselect.css']}, + 'distpicker': {deps: [baseRoot + 'plugs/distpicker/distpicker.data.js']}, + }, + deps: ['json', 'bootstrap'], + // 开启debug模式,不缓存资源 + // urlArgs: "ver=" + (new Date()).getTime() +}); + +// 注册jquery到require模块 +define('jquery', function () { + return layui.$; +}); + +// UI框架初始化 +PageLayout.call(this); + +// UI框架布局函数 +function PageLayout(callback, custom) { + window.WEB_SOCKET_SWF_LOCATION = baseRoot + "plugs/socket/WebSocketMain.swf"; + require(custom || [], callback || false); +} \ No newline at end of file diff --git a/static/mobile/lib/city-picker.js b/static/mobile/lib/city-picker.js new file mode 100644 index 000000000..129755d20 --- /dev/null +++ b/static/mobile/lib/city-picker.js @@ -0,0 +1,16713 @@ +// jshint ignore: start ++function($){ + +$.rawCitiesData = [ + { + "name":"北京", + "code":"110000", + "sub": [ + { + "name": "北京市", + "code": "110000", + "sub":[ + { + "name":"东城区", + "code":"110101" + }, + { + "name":"西城区", + "code":"110102" + }, + { + "name":"朝阳区", + "code":"110105" + }, + { + "name":"丰台区", + "code":"110106" + }, + { + "name":"石景山区", + "code":"110107" + }, + { + "name":"海淀区", + "code":"110108" + }, + { + "name":"门头沟区", + "code":"110109" + }, + { + "name":"房山区", + "code":"110111" + }, + { + "name":"通州区", + "code":"110112" + }, + { + "name":"顺义区", + "code":"110113" + }, + { + "name":"昌平区", + "code":"110114" + }, + { + "name":"大兴区", + "code":"110115" + }, + { + "name":"怀柔区", + "code":"110116" + }, + { + "name":"平谷区", + "code":"110117" + }, + { + "name":"密云县", + "code":"110228" + }, + { + "name":"延庆县", + "code":"110229" + } + ] + } + ] + }, + { + "name":"天津", + "code":"120000", + "sub": [ + { + "name": "天津市", + "code": "120000", + "sub":[ + { + "name":"和平区", + "code":"120101" + }, + { + "name":"河东区", + "code":"120102" + }, + { + "name":"河西区", + "code":"120103" + }, + { + "name":"南开区", + "code":"120104" + }, + { + "name":"河北区", + "code":"120105" + }, + { + "name":"红桥区", + "code":"120106" + }, + { + "name":"东丽区", + "code":"120110" + }, + { + "name":"西青区", + "code":"120111" + }, + { + "name":"津南区", + "code":"120112" + }, + { + "name":"北辰区", + "code":"120113" + }, + { + "name":"武清区", + "code":"120114" + }, + { + "name":"宝坻区", + "code":"120115" + }, + { + "name":"滨海新区", + "code":"120116" + }, + { + "name":"宁河县", + "code":"120221" + }, + { + "name":"静海县", + "code":"120223" + }, + { + "name":"蓟县", + "code":"120225" + } + ] + } + ] + }, + { + "name":"河北省", + "code":"130000", + "sub":[ + { + "name":"石家庄市", + "code":"130100", + "sub":[ + { + "name":"市辖区", + "code":"130101" + }, + { + "name":"长安区", + "code":"130102" + }, + { + "name":"桥西区", + "code":"130104" + }, + { + "name":"新华区", + "code":"130105" + }, + { + "name":"井陉矿区", + "code":"130107" + }, + { + "name":"裕华区", + "code":"130108" + }, + { + "name":"藁城区", + "code":"130109" + }, + { + "name":"鹿泉区", + "code":"130110" + }, + { + "name":"栾城区", + "code":"130111" + }, + { + "name":"井陉县", + "code":"130121" + }, + { + "name":"正定县", + "code":"130123" + }, + { + "name":"行唐县", + "code":"130125" + }, + { + "name":"灵寿县", + "code":"130126" + }, + { + "name":"高邑县", + "code":"130127" + }, + { + "name":"深泽县", + "code":"130128" + }, + { + "name":"赞皇县", + "code":"130129" + }, + { + "name":"无极县", + "code":"130130" + }, + { + "name":"平山县", + "code":"130131" + }, + { + "name":"元氏县", + "code":"130132" + }, + { + "name":"赵县", + "code":"130133" + }, + { + "name":"辛集市", + "code":"130181" + }, + { + "name":"晋州市", + "code":"130183" + }, + { + "name":"新乐市", + "code":"130184" + } + ] + }, + { + "name":"唐山市", + "code":"130200", + "sub":[ + { + "name":"市辖区", + "code":"130201" + }, + { + "name":"路南区", + "code":"130202" + }, + { + "name":"路北区", + "code":"130203" + }, + { + "name":"古冶区", + "code":"130204" + }, + { + "name":"开平区", + "code":"130205" + }, + { + "name":"丰南区", + "code":"130207" + }, + { + "name":"丰润区", + "code":"130208" + }, + { + "name":"曹妃甸区", + "code":"130209" + }, + { + "name":"滦县", + "code":"130223" + }, + { + "name":"滦南县", + "code":"130224" + }, + { + "name":"乐亭县", + "code":"130225" + }, + { + "name":"迁西县", + "code":"130227" + }, + { + "name":"玉田县", + "code":"130229" + }, + { + "name":"遵化市", + "code":"130281" + }, + { + "name":"迁安市", + "code":"130283" + } + ] + }, + { + "name":"秦皇岛市", + "code":"130300", + "sub":[ + { + "name":"市辖区", + "code":"130301" + }, + { + "name":"海港区", + "code":"130302" + }, + { + "name":"山海关区", + "code":"130303" + }, + { + "name":"北戴河区", + "code":"130304" + }, + { + "name":"青龙满族自治县", + "code":"130321" + }, + { + "name":"昌黎县", + "code":"130322" + }, + { + "name":"抚宁县", + "code":"130323" + }, + { + "name":"卢龙县", + "code":"130324" + } + ] + }, + { + "name":"邯郸市", + "code":"130400", + "sub":[ + { + "name":"市辖区", + "code":"130401" + }, + { + "name":"邯山区", + "code":"130402" + }, + { + "name":"丛台区", + "code":"130403" + }, + { + "name":"复兴区", + "code":"130404" + }, + { + "name":"峰峰矿区", + "code":"130406" + }, + { + "name":"邯郸县", + "code":"130421" + }, + { + "name":"临漳县", + "code":"130423" + }, + { + "name":"成安县", + "code":"130424" + }, + { + "name":"大名县", + "code":"130425" + }, + { + "name":"涉县", + "code":"130426" + }, + { + "name":"磁县", + "code":"130427" + }, + { + "name":"肥乡县", + "code":"130428" + }, + { + "name":"永年县", + "code":"130429" + }, + { + "name":"邱县", + "code":"130430" + }, + { + "name":"鸡泽县", + "code":"130431" + }, + { + "name":"广平县", + "code":"130432" + }, + { + "name":"馆陶县", + "code":"130433" + }, + { + "name":"魏县", + "code":"130434" + }, + { + "name":"曲周县", + "code":"130435" + }, + { + "name":"武安市", + "code":"130481" + } + ] + }, + { + "name":"邢台市", + "code":"130500", + "sub":[ + { + "name":"市辖区", + "code":"130501" + }, + { + "name":"桥东区", + "code":"130502" + }, + { + "name":"桥西区", + "code":"130503" + }, + { + "name":"邢台县", + "code":"130521" + }, + { + "name":"临城县", + "code":"130522" + }, + { + "name":"内丘县", + "code":"130523" + }, + { + "name":"柏乡县", + "code":"130524" + }, + { + "name":"隆尧县", + "code":"130525" + }, + { + "name":"任县", + "code":"130526" + }, + { + "name":"南和县", + "code":"130527" + }, + { + "name":"宁晋县", + "code":"130528" + }, + { + "name":"巨鹿县", + "code":"130529" + }, + { + "name":"新河县", + "code":"130530" + }, + { + "name":"广宗县", + "code":"130531" + }, + { + "name":"平乡县", + "code":"130532" + }, + { + "name":"威县", + "code":"130533" + }, + { + "name":"清河县", + "code":"130534" + }, + { + "name":"临西县", + "code":"130535" + }, + { + "name":"南宫市", + "code":"130581" + }, + { + "name":"沙河市", + "code":"130582" + } + ] + }, + { + "name":"保定市", + "code":"130600", + "sub":[ + { + "name":"市辖区", + "code":"130601" + }, + { + "name":"新市区", + "code":"130602" + }, + { + "name":"北市区", + "code":"130603" + }, + { + "name":"南市区", + "code":"130604" + }, + { + "name":"满城县", + "code":"130621" + }, + { + "name":"清苑县", + "code":"130622" + }, + { + "name":"涞水县", + "code":"130623" + }, + { + "name":"阜平县", + "code":"130624" + }, + { + "name":"徐水县", + "code":"130625" + }, + { + "name":"定兴县", + "code":"130626" + }, + { + "name":"唐县", + "code":"130627" + }, + { + "name":"高阳县", + "code":"130628" + }, + { + "name":"容城县", + "code":"130629" + }, + { + "name":"涞源县", + "code":"130630" + }, + { + "name":"望都县", + "code":"130631" + }, + { + "name":"安新县", + "code":"130632" + }, + { + "name":"易县", + "code":"130633" + }, + { + "name":"曲阳县", + "code":"130634" + }, + { + "name":"蠡县", + "code":"130635" + }, + { + "name":"顺平县", + "code":"130636" + }, + { + "name":"博野县", + "code":"130637" + }, + { + "name":"雄县", + "code":"130638" + }, + { + "name":"涿州市", + "code":"130681" + }, + { + "name":"定州市", + "code":"130682" + }, + { + "name":"安国市", + "code":"130683" + }, + { + "name":"高碑店市", + "code":"130684" + } + ] + }, + { + "name":"张家口市", + "code":"130700", + "sub":[ + { + "name":"市辖区", + "code":"130701" + }, + { + "name":"桥东区", + "code":"130702" + }, + { + "name":"桥西区", + "code":"130703" + }, + { + "name":"宣化区", + "code":"130705" + }, + { + "name":"下花园区", + "code":"130706" + }, + { + "name":"宣化县", + "code":"130721" + }, + { + "name":"张北县", + "code":"130722" + }, + { + "name":"康保县", + "code":"130723" + }, + { + "name":"沽源县", + "code":"130724" + }, + { + "name":"尚义县", + "code":"130725" + }, + { + "name":"蔚县", + "code":"130726" + }, + { + "name":"阳原县", + "code":"130727" + }, + { + "name":"怀安县", + "code":"130728" + }, + { + "name":"万全县", + "code":"130729" + }, + { + "name":"怀来县", + "code":"130730" + }, + { + "name":"涿鹿县", + "code":"130731" + }, + { + "name":"赤城县", + "code":"130732" + }, + { + "name":"崇礼县", + "code":"130733" + } + ] + }, + { + "name":"承德市", + "code":"130800", + "sub":[ + { + "name":"市辖区", + "code":"130801" + }, + { + "name":"双桥区", + "code":"130802" + }, + { + "name":"双滦区", + "code":"130803" + }, + { + "name":"鹰手营子矿区", + "code":"130804" + }, + { + "name":"承德县", + "code":"130821" + }, + { + "name":"兴隆县", + "code":"130822" + }, + { + "name":"平泉县", + "code":"130823" + }, + { + "name":"滦平县", + "code":"130824" + }, + { + "name":"隆化县", + "code":"130825" + }, + { + "name":"丰宁满族自治县", + "code":"130826" + }, + { + "name":"宽城满族自治县", + "code":"130827" + }, + { + "name":"围场满族蒙古族自治县", + "code":"130828" + } + ] + }, + { + "name":"沧州市", + "code":"130900", + "sub":[ + { + "name":"市辖区", + "code":"130901" + }, + { + "name":"新华区", + "code":"130902" + }, + { + "name":"运河区", + "code":"130903" + }, + { + "name":"沧县", + "code":"130921" + }, + { + "name":"青县", + "code":"130922" + }, + { + "name":"东光县", + "code":"130923" + }, + { + "name":"海兴县", + "code":"130924" + }, + { + "name":"盐山县", + "code":"130925" + }, + { + "name":"肃宁县", + "code":"130926" + }, + { + "name":"南皮县", + "code":"130927" + }, + { + "name":"吴桥县", + "code":"130928" + }, + { + "name":"献县", + "code":"130929" + }, + { + "name":"孟村回族自治县", + "code":"130930" + }, + { + "name":"泊头市", + "code":"130981" + }, + { + "name":"任丘市", + "code":"130982" + }, + { + "name":"黄骅市", + "code":"130983" + }, + { + "name":"河间市", + "code":"130984" + } + ] + }, + { + "name":"廊坊市", + "code":"131000", + "sub":[ + { + "name":"市辖区", + "code":"131001" + }, + { + "name":"安次区", + "code":"131002" + }, + { + "name":"广阳区", + "code":"131003" + }, + { + "name":"固安县", + "code":"131022" + }, + { + "name":"永清县", + "code":"131023" + }, + { + "name":"香河县", + "code":"131024" + }, + { + "name":"大城县", + "code":"131025" + }, + { + "name":"文安县", + "code":"131026" + }, + { + "name":"大厂回族自治县", + "code":"131028" + }, + { + "name":"霸州市", + "code":"131081" + }, + { + "name":"三河市", + "code":"131082" + } + ] + }, + { + "name":"衡水市", + "code":"131100", + "sub":[ + { + "name":"市辖区", + "code":"131101" + }, + { + "name":"桃城区", + "code":"131102" + }, + { + "name":"枣强县", + "code":"131121" + }, + { + "name":"武邑县", + "code":"131122" + }, + { + "name":"武强县", + "code":"131123" + }, + { + "name":"饶阳县", + "code":"131124" + }, + { + "name":"安平县", + "code":"131125" + }, + { + "name":"故城县", + "code":"131126" + }, + { + "name":"景县", + "code":"131127" + }, + { + "name":"阜城县", + "code":"131128" + }, + { + "name":"冀州市", + "code":"131181" + }, + { + "name":"深州市", + "code":"131182" + } + ] + } + ] + }, + { + "name":"山西省", + "code":"140000", + "sub":[ + { + "name":"太原市", + "code":"140100", + "sub":[ + { + "name":"市辖区", + "code":"140101" + }, + { + "name":"小店区", + "code":"140105" + }, + { + "name":"迎泽区", + "code":"140106" + }, + { + "name":"杏花岭区", + "code":"140107" + }, + { + "name":"尖草坪区", + "code":"140108" + }, + { + "name":"万柏林区", + "code":"140109" + }, + { + "name":"晋源区", + "code":"140110" + }, + { + "name":"清徐县", + "code":"140121" + }, + { + "name":"阳曲县", + "code":"140122" + }, + { + "name":"娄烦县", + "code":"140123" + }, + { + "name":"古交市", + "code":"140181" + } + ] + }, + { + "name":"大同市", + "code":"140200", + "sub":[ + { + "name":"市辖区", + "code":"140201" + }, + { + "name":"城区", + "code":"140202" + }, + { + "name":"矿区", + "code":"140203" + }, + { + "name":"南郊区", + "code":"140211" + }, + { + "name":"新荣区", + "code":"140212" + }, + { + "name":"阳高县", + "code":"140221" + }, + { + "name":"天镇县", + "code":"140222" + }, + { + "name":"广灵县", + "code":"140223" + }, + { + "name":"灵丘县", + "code":"140224" + }, + { + "name":"浑源县", + "code":"140225" + }, + { + "name":"左云县", + "code":"140226" + }, + { + "name":"大同县", + "code":"140227" + } + ] + }, + { + "name":"阳泉市", + "code":"140300", + "sub":[ + { + "name":"市辖区", + "code":"140301" + }, + { + "name":"城区", + "code":"140302" + }, + { + "name":"矿区", + "code":"140303" + }, + { + "name":"郊区", + "code":"140311" + }, + { + "name":"平定县", + "code":"140321" + }, + { + "name":"盂县", + "code":"140322" + } + ] + }, + { + "name":"长治市", + "code":"140400", + "sub":[ + { + "name":"市辖区", + "code":"140401" + }, + { + "name":"城区", + "code":"140402" + }, + { + "name":"郊区", + "code":"140411" + }, + { + "name":"长治县", + "code":"140421" + }, + { + "name":"襄垣县", + "code":"140423" + }, + { + "name":"屯留县", + "code":"140424" + }, + { + "name":"平顺县", + "code":"140425" + }, + { + "name":"黎城县", + "code":"140426" + }, + { + "name":"壶关县", + "code":"140427" + }, + { + "name":"长子县", + "code":"140428" + }, + { + "name":"武乡县", + "code":"140429" + }, + { + "name":"沁县", + "code":"140430" + }, + { + "name":"沁源县", + "code":"140431" + }, + { + "name":"潞城市", + "code":"140481" + } + ] + }, + { + "name":"晋城市", + "code":"140500", + "sub":[ + { + "name":"市辖区", + "code":"140501" + }, + { + "name":"城区", + "code":"140502" + }, + { + "name":"沁水县", + "code":"140521" + }, + { + "name":"阳城县", + "code":"140522" + }, + { + "name":"陵川县", + "code":"140524" + }, + { + "name":"泽州县", + "code":"140525" + }, + { + "name":"高平市", + "code":"140581" + } + ] + }, + { + "name":"朔州市", + "code":"140600", + "sub":[ + { + "name":"市辖区", + "code":"140601" + }, + { + "name":"朔城区", + "code":"140602" + }, + { + "name":"平鲁区", + "code":"140603" + }, + { + "name":"山阴县", + "code":"140621" + }, + { + "name":"应县", + "code":"140622" + }, + { + "name":"右玉县", + "code":"140623" + }, + { + "name":"怀仁县", + "code":"140624" + } + ] + }, + { + "name":"晋中市", + "code":"140700", + "sub":[ + { + "name":"市辖区", + "code":"140701" + }, + { + "name":"榆次区", + "code":"140702" + }, + { + "name":"榆社县", + "code":"140721" + }, + { + "name":"左权县", + "code":"140722" + }, + { + "name":"和顺县", + "code":"140723" + }, + { + "name":"昔阳县", + "code":"140724" + }, + { + "name":"寿阳县", + "code":"140725" + }, + { + "name":"太谷县", + "code":"140726" + }, + { + "name":"祁县", + "code":"140727" + }, + { + "name":"平遥县", + "code":"140728" + }, + { + "name":"灵石县", + "code":"140729" + }, + { + "name":"介休市", + "code":"140781" + } + ] + }, + { + "name":"运城市", + "code":"140800", + "sub":[ + { + "name":"市辖区", + "code":"140801" + }, + { + "name":"盐湖区", + "code":"140802" + }, + { + "name":"临猗县", + "code":"140821" + }, + { + "name":"万荣县", + "code":"140822" + }, + { + "name":"闻喜县", + "code":"140823" + }, + { + "name":"稷山县", + "code":"140824" + }, + { + "name":"新绛县", + "code":"140825" + }, + { + "name":"绛县", + "code":"140826" + }, + { + "name":"垣曲县", + "code":"140827" + }, + { + "name":"夏县", + "code":"140828" + }, + { + "name":"平陆县", + "code":"140829" + }, + { + "name":"芮城县", + "code":"140830" + }, + { + "name":"永济市", + "code":"140881" + }, + { + "name":"河津市", + "code":"140882" + } + ] + }, + { + "name":"忻州市", + "code":"140900", + "sub":[ + { + "name":"市辖区", + "code":"140901" + }, + { + "name":"忻府区", + "code":"140902" + }, + { + "name":"定襄县", + "code":"140921" + }, + { + "name":"五台县", + "code":"140922" + }, + { + "name":"代县", + "code":"140923" + }, + { + "name":"繁峙县", + "code":"140924" + }, + { + "name":"宁武县", + "code":"140925" + }, + { + "name":"静乐县", + "code":"140926" + }, + { + "name":"神池县", + "code":"140927" + }, + { + "name":"五寨县", + "code":"140928" + }, + { + "name":"岢岚县", + "code":"140929" + }, + { + "name":"河曲县", + "code":"140930" + }, + { + "name":"保德县", + "code":"140931" + }, + { + "name":"偏关县", + "code":"140932" + }, + { + "name":"原平市", + "code":"140981" + } + ] + }, + { + "name":"临汾市", + "code":"141000", + "sub":[ + { + "name":"市辖区", + "code":"141001" + }, + { + "name":"尧都区", + "code":"141002" + }, + { + "name":"曲沃县", + "code":"141021" + }, + { + "name":"翼城县", + "code":"141022" + }, + { + "name":"襄汾县", + "code":"141023" + }, + { + "name":"洪洞县", + "code":"141024" + }, + { + "name":"古县", + "code":"141025" + }, + { + "name":"安泽县", + "code":"141026" + }, + { + "name":"浮山县", + "code":"141027" + }, + { + "name":"吉县", + "code":"141028" + }, + { + "name":"乡宁县", + "code":"141029" + }, + { + "name":"大宁县", + "code":"141030" + }, + { + "name":"隰县", + "code":"141031" + }, + { + "name":"永和县", + "code":"141032" + }, + { + "name":"蒲县", + "code":"141033" + }, + { + "name":"汾西县", + "code":"141034" + }, + { + "name":"侯马市", + "code":"141081" + }, + { + "name":"霍州市", + "code":"141082" + } + ] + }, + { + "name":"吕梁市", + "code":"141100", + "sub":[ + { + "name":"市辖区", + "code":"141101" + }, + { + "name":"离石区", + "code":"141102" + }, + { + "name":"文水县", + "code":"141121" + }, + { + "name":"交城县", + "code":"141122" + }, + { + "name":"兴县", + "code":"141123" + }, + { + "name":"临县", + "code":"141124" + }, + { + "name":"柳林县", + "code":"141125" + }, + { + "name":"石楼县", + "code":"141126" + }, + { + "name":"岚县", + "code":"141127" + }, + { + "name":"方山县", + "code":"141128" + }, + { + "name":"中阳县", + "code":"141129" + }, + { + "name":"交口县", + "code":"141130" + }, + { + "name":"孝义市", + "code":"141181" + }, + { + "name":"汾阳市", + "code":"141182" + } + ] + } + ] + }, + { + "name":"内蒙古自治区", + "code":"150000", + "sub":[ + { + "name":"呼和浩特市", + "code":"150100", + "sub":[ + { + "name":"市辖区", + "code":"150101" + }, + { + "name":"新城区", + "code":"150102" + }, + { + "name":"回民区", + "code":"150103" + }, + { + "name":"玉泉区", + "code":"150104" + }, + { + "name":"赛罕区", + "code":"150105" + }, + { + "name":"土默特左旗", + "code":"150121" + }, + { + "name":"托克托县", + "code":"150122" + }, + { + "name":"和林格尔县", + "code":"150123" + }, + { + "name":"清水河县", + "code":"150124" + }, + { + "name":"武川县", + "code":"150125" + } + ] + }, + { + "name":"包头市", + "code":"150200", + "sub":[ + { + "name":"市辖区", + "code":"150201" + }, + { + "name":"东河区", + "code":"150202" + }, + { + "name":"昆都仑区", + "code":"150203" + }, + { + "name":"青山区", + "code":"150204" + }, + { + "name":"石拐区", + "code":"150205" + }, + { + "name":"白云鄂博矿区", + "code":"150206" + }, + { + "name":"九原区", + "code":"150207" + }, + { + "name":"土默特右旗", + "code":"150221" + }, + { + "name":"固阳县", + "code":"150222" + }, + { + "name":"达尔罕茂明安联合旗", + "code":"150223" + } + ] + }, + { + "name":"乌海市", + "code":"150300", + "sub":[ + { + "name":"市辖区", + "code":"150301" + }, + { + "name":"海勃湾区", + "code":"150302" + }, + { + "name":"海南区", + "code":"150303" + }, + { + "name":"乌达区", + "code":"150304" + } + ] + }, + { + "name":"赤峰市", + "code":"150400", + "sub":[ + { + "name":"市辖区", + "code":"150401" + }, + { + "name":"红山区", + "code":"150402" + }, + { + "name":"元宝山区", + "code":"150403" + }, + { + "name":"松山区", + "code":"150404" + }, + { + "name":"阿鲁科尔沁旗", + "code":"150421" + }, + { + "name":"巴林左旗", + "code":"150422" + }, + { + "name":"巴林右旗", + "code":"150423" + }, + { + "name":"林西县", + "code":"150424" + }, + { + "name":"克什克腾旗", + "code":"150425" + }, + { + "name":"翁牛特旗", + "code":"150426" + }, + { + "name":"喀喇沁旗", + "code":"150428" + }, + { + "name":"宁城县", + "code":"150429" + }, + { + "name":"敖汉旗", + "code":"150430" + } + ] + }, + { + "name":"通辽市", + "code":"150500", + "sub":[ + { + "name":"市辖区", + "code":"150501" + }, + { + "name":"科尔沁区", + "code":"150502" + }, + { + "name":"科尔沁左翼中旗", + "code":"150521" + }, + { + "name":"科尔沁左翼后旗", + "code":"150522" + }, + { + "name":"开鲁县", + "code":"150523" + }, + { + "name":"库伦旗", + "code":"150524" + }, + { + "name":"奈曼旗", + "code":"150525" + }, + { + "name":"扎鲁特旗", + "code":"150526" + }, + { + "name":"霍林郭勒市", + "code":"150581" + } + ] + }, + { + "name":"鄂尔多斯市", + "code":"150600", + "sub":[ + { + "name":"市辖区", + "code":"150601" + }, + { + "name":"东胜区", + "code":"150602" + }, + { + "name":"达拉特旗", + "code":"150621" + }, + { + "name":"准格尔旗", + "code":"150622" + }, + { + "name":"鄂托克前旗", + "code":"150623" + }, + { + "name":"鄂托克旗", + "code":"150624" + }, + { + "name":"杭锦旗", + "code":"150625" + }, + { + "name":"乌审旗", + "code":"150626" + }, + { + "name":"伊金霍洛旗", + "code":"150627" + } + ] + }, + { + "name":"呼伦贝尔市", + "code":"150700", + "sub":[ + { + "name":"市辖区", + "code":"150701" + }, + { + "name":"海拉尔区", + "code":"150702" + }, + { + "name":"扎赉诺尔区", + "code":"150703" + }, + { + "name":"阿荣旗", + "code":"150721" + }, + { + "name":"莫力达瓦达斡尔族自治旗", + "code":"150722" + }, + { + "name":"鄂伦春自治旗", + "code":"150723" + }, + { + "name":"鄂温克族自治旗", + "code":"150724" + }, + { + "name":"陈巴尔虎旗", + "code":"150725" + }, + { + "name":"新巴尔虎左旗", + "code":"150726" + }, + { + "name":"新巴尔虎右旗", + "code":"150727" + }, + { + "name":"满洲里市", + "code":"150781" + }, + { + "name":"牙克石市", + "code":"150782" + }, + { + "name":"扎兰屯市", + "code":"150783" + }, + { + "name":"额尔古纳市", + "code":"150784" + }, + { + "name":"根河市", + "code":"150785" + } + ] + }, + { + "name":"巴彦淖尔市", + "code":"150800", + "sub":[ + { + "name":"市辖区", + "code":"150801" + }, + { + "name":"临河区", + "code":"150802" + }, + { + "name":"五原县", + "code":"150821" + }, + { + "name":"磴口县", + "code":"150822" + }, + { + "name":"乌拉特前旗", + "code":"150823" + }, + { + "name":"乌拉特中旗", + "code":"150824" + }, + { + "name":"乌拉特后旗", + "code":"150825" + }, + { + "name":"杭锦后旗", + "code":"150826" + } + ] + }, + { + "name":"乌兰察布市", + "code":"150900", + "sub":[ + { + "name":"市辖区", + "code":"150901" + }, + { + "name":"集宁区", + "code":"150902" + }, + { + "name":"卓资县", + "code":"150921" + }, + { + "name":"化德县", + "code":"150922" + }, + { + "name":"商都县", + "code":"150923" + }, + { + "name":"兴和县", + "code":"150924" + }, + { + "name":"凉城县", + "code":"150925" + }, + { + "name":"察哈尔右翼前旗", + "code":"150926" + }, + { + "name":"察哈尔右翼中旗", + "code":"150927" + }, + { + "name":"察哈尔右翼后旗", + "code":"150928" + }, + { + "name":"四子王旗", + "code":"150929" + }, + { + "name":"丰镇市", + "code":"150981" + } + ] + }, + { + "name":"兴安盟", + "code":"152200", + "sub":[ + { + "name":"乌兰浩特市", + "code":"152201" + }, + { + "name":"阿尔山市", + "code":"152202" + }, + { + "name":"科尔沁右翼前旗", + "code":"152221" + }, + { + "name":"科尔沁右翼中旗", + "code":"152222" + }, + { + "name":"扎赉特旗", + "code":"152223" + }, + { + "name":"突泉县", + "code":"152224" + } + ] + }, + { + "name":"锡林郭勒盟", + "code":"152500", + "sub":[ + { + "name":"二连浩特市", + "code":"152501" + }, + { + "name":"锡林浩特市", + "code":"152502" + }, + { + "name":"阿巴嘎旗", + "code":"152522" + }, + { + "name":"苏尼特左旗", + "code":"152523" + }, + { + "name":"苏尼特右旗", + "code":"152524" + }, + { + "name":"东乌珠穆沁旗", + "code":"152525" + }, + { + "name":"西乌珠穆沁旗", + "code":"152526" + }, + { + "name":"太仆寺旗", + "code":"152527" + }, + { + "name":"镶黄旗", + "code":"152528" + }, + { + "name":"正镶白旗", + "code":"152529" + }, + { + "name":"正蓝旗", + "code":"152530" + }, + { + "name":"多伦县", + "code":"152531" + } + ] + }, + { + "name":"阿拉善盟", + "code":"152900", + "sub":[ + { + "name":"阿拉善左旗", + "code":"152921" + }, + { + "name":"阿拉善右旗", + "code":"152922" + }, + { + "name":"额济纳旗", + "code":"152923" + } + ] + } + ] + }, + { + "name":"辽宁省", + "code":"210000", + "sub":[ + { + "name":"沈阳市", + "code":"210100", + "sub":[ + { + "name":"市辖区", + "code":"210101" + }, + { + "name":"和平区", + "code":"210102" + }, + { + "name":"沈河区", + "code":"210103" + }, + { + "name":"大东区", + "code":"210104" + }, + { + "name":"皇姑区", + "code":"210105" + }, + { + "name":"铁西区", + "code":"210106" + }, + { + "name":"苏家屯区", + "code":"210111" + }, + { + "name":"浑南区", + "code":"210112" + }, + { + "name":"沈北新区", + "code":"210113" + }, + { + "name":"于洪区", + "code":"210114" + }, + { + "name":"辽中县", + "code":"210122" + }, + { + "name":"康平县", + "code":"210123" + }, + { + "name":"法库县", + "code":"210124" + }, + { + "name":"新民市", + "code":"210181" + } + ] + }, + { + "name":"大连市", + "code":"210200", + "sub":[ + { + "name":"市辖区", + "code":"210201" + }, + { + "name":"中山区", + "code":"210202" + }, + { + "name":"西岗区", + "code":"210203" + }, + { + "name":"沙河口区", + "code":"210204" + }, + { + "name":"甘井子区", + "code":"210211" + }, + { + "name":"旅顺口区", + "code":"210212" + }, + { + "name":"金州区", + "code":"210213" + }, + { + "name":"长海县", + "code":"210224" + }, + { + "name":"瓦房店市", + "code":"210281" + }, + { + "name":"普兰店市", + "code":"210282" + }, + { + "name":"庄河市", + "code":"210283" + } + ] + }, + { + "name":"鞍山市", + "code":"210300", + "sub":[ + { + "name":"市辖区", + "code":"210301" + }, + { + "name":"铁东区", + "code":"210302" + }, + { + "name":"铁西区", + "code":"210303" + }, + { + "name":"立山区", + "code":"210304" + }, + { + "name":"千山区", + "code":"210311" + }, + { + "name":"台安县", + "code":"210321" + }, + { + "name":"岫岩满族自治县", + "code":"210323" + }, + { + "name":"海城市", + "code":"210381" + } + ] + }, + { + "name":"抚顺市", + "code":"210400", + "sub":[ + { + "name":"市辖区", + "code":"210401" + }, + { + "name":"新抚区", + "code":"210402" + }, + { + "name":"东洲区", + "code":"210403" + }, + { + "name":"望花区", + "code":"210404" + }, + { + "name":"顺城区", + "code":"210411" + }, + { + "name":"抚顺县", + "code":"210421" + }, + { + "name":"新宾满族自治县", + "code":"210422" + }, + { + "name":"清原满族自治县", + "code":"210423" + } + ] + }, + { + "name":"本溪市", + "code":"210500", + "sub":[ + { + "name":"市辖区", + "code":"210501" + }, + { + "name":"平山区", + "code":"210502" + }, + { + "name":"溪湖区", + "code":"210503" + }, + { + "name":"明山区", + "code":"210504" + }, + { + "name":"南芬区", + "code":"210505" + }, + { + "name":"本溪满族自治县", + "code":"210521" + }, + { + "name":"桓仁满族自治县", + "code":"210522" + } + ] + }, + { + "name":"丹东市", + "code":"210600", + "sub":[ + { + "name":"市辖区", + "code":"210601" + }, + { + "name":"元宝区", + "code":"210602" + }, + { + "name":"振兴区", + "code":"210603" + }, + { + "name":"振安区", + "code":"210604" + }, + { + "name":"宽甸满族自治县", + "code":"210624" + }, + { + "name":"东港市", + "code":"210681" + }, + { + "name":"凤城市", + "code":"210682" + } + ] + }, + { + "name":"锦州市", + "code":"210700", + "sub":[ + { + "name":"市辖区", + "code":"210701" + }, + { + "name":"古塔区", + "code":"210702" + }, + { + "name":"凌河区", + "code":"210703" + }, + { + "name":"太和区", + "code":"210711" + }, + { + "name":"黑山县", + "code":"210726" + }, + { + "name":"义县", + "code":"210727" + }, + { + "name":"凌海市", + "code":"210781" + }, + { + "name":"北镇市", + "code":"210782" + } + ] + }, + { + "name":"营口市", + "code":"210800", + "sub":[ + { + "name":"市辖区", + "code":"210801" + }, + { + "name":"站前区", + "code":"210802" + }, + { + "name":"西市区", + "code":"210803" + }, + { + "name":"鲅鱼圈区", + "code":"210804" + }, + { + "name":"老边区", + "code":"210811" + }, + { + "name":"盖州市", + "code":"210881" + }, + { + "name":"大石桥市", + "code":"210882" + } + ] + }, + { + "name":"阜新市", + "code":"210900", + "sub":[ + { + "name":"市辖区", + "code":"210901" + }, + { + "name":"海州区", + "code":"210902" + }, + { + "name":"新邱区", + "code":"210903" + }, + { + "name":"太平区", + "code":"210904" + }, + { + "name":"清河门区", + "code":"210905" + }, + { + "name":"细河区", + "code":"210911" + }, + { + "name":"阜新蒙古族自治县", + "code":"210921" + }, + { + "name":"彰武县", + "code":"210922" + } + ] + }, + { + "name":"辽阳市", + "code":"211000", + "sub":[ + { + "name":"市辖区", + "code":"211001" + }, + { + "name":"白塔区", + "code":"211002" + }, + { + "name":"文圣区", + "code":"211003" + }, + { + "name":"宏伟区", + "code":"211004" + }, + { + "name":"弓长岭区", + "code":"211005" + }, + { + "name":"太子河区", + "code":"211011" + }, + { + "name":"辽阳县", + "code":"211021" + }, + { + "name":"灯塔市", + "code":"211081" + } + ] + }, + { + "name":"盘锦市", + "code":"211100", + "sub":[ + { + "name":"市辖区", + "code":"211101" + }, + { + "name":"双台子区", + "code":"211102" + }, + { + "name":"兴隆台区", + "code":"211103" + }, + { + "name":"大洼县", + "code":"211121" + }, + { + "name":"盘山县", + "code":"211122" + } + ] + }, + { + "name":"铁岭市", + "code":"211200", + "sub":[ + { + "name":"市辖区", + "code":"211201" + }, + { + "name":"银州区", + "code":"211202" + }, + { + "name":"清河区", + "code":"211204" + }, + { + "name":"铁岭县", + "code":"211221" + }, + { + "name":"西丰县", + "code":"211223" + }, + { + "name":"昌图县", + "code":"211224" + }, + { + "name":"调兵山市", + "code":"211281" + }, + { + "name":"开原市", + "code":"211282" + } + ] + }, + { + "name":"朝阳市", + "code":"211300", + "sub":[ + { + "name":"市辖区", + "code":"211301" + }, + { + "name":"双塔区", + "code":"211302" + }, + { + "name":"龙城区", + "code":"211303" + }, + { + "name":"朝阳县", + "code":"211321" + }, + { + "name":"建平县", + "code":"211322" + }, + { + "name":"喀喇沁左翼蒙古族自治县", + "code":"211324" + }, + { + "name":"北票市", + "code":"211381" + }, + { + "name":"凌源市", + "code":"211382" + } + ] + }, + { + "name":"葫芦岛市", + "code":"211400", + "sub":[ + { + "name":"市辖区", + "code":"211401" + }, + { + "name":"连山区", + "code":"211402" + }, + { + "name":"龙港区", + "code":"211403" + }, + { + "name":"南票区", + "code":"211404" + }, + { + "name":"绥中县", + "code":"211421" + }, + { + "name":"建昌县", + "code":"211422" + }, + { + "name":"兴城市", + "code":"211481" + } + ] + } + ] + }, + { + "name":"吉林省", + "code":"220000", + "sub":[ + { + "name":"长春市", + "code":"220100", + "sub":[ + { + "name":"市辖区", + "code":"220101" + }, + { + "name":"南关区", + "code":"220102" + }, + { + "name":"宽城区", + "code":"220103" + }, + { + "name":"朝阳区", + "code":"220104" + }, + { + "name":"二道区", + "code":"220105" + }, + { + "name":"绿园区", + "code":"220106" + }, + { + "name":"双阳区", + "code":"220112" + }, + { + "name":"九台区", + "code":"220113" + }, + { + "name":"农安县", + "code":"220122" + }, + { + "name":"榆树市", + "code":"220182" + }, + { + "name":"德惠市", + "code":"220183" + } + ] + }, + { + "name":"吉林市", + "code":"220200", + "sub":[ + { + "name":"市辖区", + "code":"220201" + }, + { + "name":"昌邑区", + "code":"220202" + }, + { + "name":"龙潭区", + "code":"220203" + }, + { + "name":"船营区", + "code":"220204" + }, + { + "name":"丰满区", + "code":"220211" + }, + { + "name":"永吉县", + "code":"220221" + }, + { + "name":"蛟河市", + "code":"220281" + }, + { + "name":"桦甸市", + "code":"220282" + }, + { + "name":"舒兰市", + "code":"220283" + }, + { + "name":"磐石市", + "code":"220284" + } + ] + }, + { + "name":"四平市", + "code":"220300", + "sub":[ + { + "name":"市辖区", + "code":"220301" + }, + { + "name":"铁西区", + "code":"220302" + }, + { + "name":"铁东区", + "code":"220303" + }, + { + "name":"梨树县", + "code":"220322" + }, + { + "name":"伊通满族自治县", + "code":"220323" + }, + { + "name":"公主岭市", + "code":"220381" + }, + { + "name":"双辽市", + "code":"220382" + } + ] + }, + { + "name":"辽源市", + "code":"220400", + "sub":[ + { + "name":"市辖区", + "code":"220401" + }, + { + "name":"龙山区", + "code":"220402" + }, + { + "name":"西安区", + "code":"220403" + }, + { + "name":"东丰县", + "code":"220421" + }, + { + "name":"东辽县", + "code":"220422" + } + ] + }, + { + "name":"通化市", + "code":"220500", + "sub":[ + { + "name":"市辖区", + "code":"220501" + }, + { + "name":"东昌区", + "code":"220502" + }, + { + "name":"二道江区", + "code":"220503" + }, + { + "name":"通化县", + "code":"220521" + }, + { + "name":"辉南县", + "code":"220523" + }, + { + "name":"柳河县", + "code":"220524" + }, + { + "name":"梅河口市", + "code":"220581" + }, + { + "name":"集安市", + "code":"220582" + } + ] + }, + { + "name":"白山市", + "code":"220600", + "sub":[ + { + "name":"市辖区", + "code":"220601" + }, + { + "name":"浑江区", + "code":"220602" + }, + { + "name":"江源区", + "code":"220605" + }, + { + "name":"抚松县", + "code":"220621" + }, + { + "name":"靖宇县", + "code":"220622" + }, + { + "name":"长白朝鲜族自治县", + "code":"220623" + }, + { + "name":"临江市", + "code":"220681" + } + ] + }, + { + "name":"松原市", + "code":"220700", + "sub":[ + { + "name":"市辖区", + "code":"220701" + }, + { + "name":"宁江区", + "code":"220702" + }, + { + "name":"前郭尔罗斯蒙古族自治县", + "code":"220721" + }, + { + "name":"长岭县", + "code":"220722" + }, + { + "name":"乾安县", + "code":"220723" + }, + { + "name":"扶余市", + "code":"220781" + } + ] + }, + { + "name":"白城市", + "code":"220800", + "sub":[ + { + "name":"市辖区", + "code":"220801" + }, + { + "name":"洮北区", + "code":"220802" + }, + { + "name":"镇赉县", + "code":"220821" + }, + { + "name":"通榆县", + "code":"220822" + }, + { + "name":"洮南市", + "code":"220881" + }, + { + "name":"大安市", + "code":"220882" + } + ] + }, + { + "name":"延边朝鲜族自治州", + "code":"222400", + "sub":[ + { + "name":"延吉市", + "code":"222401" + }, + { + "name":"图们市", + "code":"222402" + }, + { + "name":"敦化市", + "code":"222403" + }, + { + "name":"珲春市", + "code":"222404" + }, + { + "name":"龙井市", + "code":"222405" + }, + { + "name":"和龙市", + "code":"222406" + }, + { + "name":"汪清县", + "code":"222424" + }, + { + "name":"安图县", + "code":"222426" + } + ] + } + ] + }, + { + "name":"黑龙江省", + "code":"230000", + "sub":[ + { + "name":"哈尔滨市", + "code":"230100", + "sub":[ + { + "name":"市辖区", + "code":"230101" + }, + { + "name":"道里区", + "code":"230102" + }, + { + "name":"南岗区", + "code":"230103" + }, + { + "name":"道外区", + "code":"230104" + }, + { + "name":"平房区", + "code":"230108" + }, + { + "name":"松北区", + "code":"230109" + }, + { + "name":"香坊区", + "code":"230110" + }, + { + "name":"呼兰区", + "code":"230111" + }, + { + "name":"阿城区", + "code":"230112" + }, + { + "name":"双城区", + "code":"230113" + }, + { + "name":"依兰县", + "code":"230123" + }, + { + "name":"方正县", + "code":"230124" + }, + { + "name":"宾县", + "code":"230125" + }, + { + "name":"巴彦县", + "code":"230126" + }, + { + "name":"木兰县", + "code":"230127" + }, + { + "name":"通河县", + "code":"230128" + }, + { + "name":"延寿县", + "code":"230129" + }, + { + "name":"尚志市", + "code":"230183" + }, + { + "name":"五常市", + "code":"230184" + } + ] + }, + { + "name":"齐齐哈尔市", + "code":"230200", + "sub":[ + { + "name":"市辖区", + "code":"230201" + }, + { + "name":"龙沙区", + "code":"230202" + }, + { + "name":"建华区", + "code":"230203" + }, + { + "name":"铁锋区", + "code":"230204" + }, + { + "name":"昂昂溪区", + "code":"230205" + }, + { + "name":"富拉尔基区", + "code":"230206" + }, + { + "name":"碾子山区", + "code":"230207" + }, + { + "name":"梅里斯达斡尔族区", + "code":"230208" + }, + { + "name":"龙江县", + "code":"230221" + }, + { + "name":"依安县", + "code":"230223" + }, + { + "name":"泰来县", + "code":"230224" + }, + { + "name":"甘南县", + "code":"230225" + }, + { + "name":"富裕县", + "code":"230227" + }, + { + "name":"克山县", + "code":"230229" + }, + { + "name":"克东县", + "code":"230230" + }, + { + "name":"拜泉县", + "code":"230231" + }, + { + "name":"讷河市", + "code":"230281" + } + ] + }, + { + "name":"鸡西市", + "code":"230300", + "sub":[ + { + "name":"市辖区", + "code":"230301" + }, + { + "name":"鸡冠区", + "code":"230302" + }, + { + "name":"恒山区", + "code":"230303" + }, + { + "name":"滴道区", + "code":"230304" + }, + { + "name":"梨树区", + "code":"230305" + }, + { + "name":"城子河区", + "code":"230306" + }, + { + "name":"麻山区", + "code":"230307" + }, + { + "name":"鸡东县", + "code":"230321" + }, + { + "name":"虎林市", + "code":"230381" + }, + { + "name":"密山市", + "code":"230382" + } + ] + }, + { + "name":"鹤岗市", + "code":"230400", + "sub":[ + { + "name":"市辖区", + "code":"230401" + }, + { + "name":"向阳区", + "code":"230402" + }, + { + "name":"工农区", + "code":"230403" + }, + { + "name":"南山区", + "code":"230404" + }, + { + "name":"兴安区", + "code":"230405" + }, + { + "name":"东山区", + "code":"230406" + }, + { + "name":"兴山区", + "code":"230407" + }, + { + "name":"萝北县", + "code":"230421" + }, + { + "name":"绥滨县", + "code":"230422" + } + ] + }, + { + "name":"双鸭山市", + "code":"230500", + "sub":[ + { + "name":"市辖区", + "code":"230501" + }, + { + "name":"尖山区", + "code":"230502" + }, + { + "name":"岭东区", + "code":"230503" + }, + { + "name":"四方台区", + "code":"230505" + }, + { + "name":"宝山区", + "code":"230506" + }, + { + "name":"集贤县", + "code":"230521" + }, + { + "name":"友谊县", + "code":"230522" + }, + { + "name":"宝清县", + "code":"230523" + }, + { + "name":"饶河县", + "code":"230524" + } + ] + }, + { + "name":"大庆市", + "code":"230600", + "sub":[ + { + "name":"市辖区", + "code":"230601" + }, + { + "name":"萨尔图区", + "code":"230602" + }, + { + "name":"龙凤区", + "code":"230603" + }, + { + "name":"让胡路区", + "code":"230604" + }, + { + "name":"红岗区", + "code":"230605" + }, + { + "name":"大同区", + "code":"230606" + }, + { + "name":"肇州县", + "code":"230621" + }, + { + "name":"肇源县", + "code":"230622" + }, + { + "name":"林甸县", + "code":"230623" + }, + { + "name":"杜尔伯特蒙古族自治县", + "code":"230624" + } + ] + }, + { + "name":"伊春市", + "code":"230700", + "sub":[ + { + "name":"市辖区", + "code":"230701" + }, + { + "name":"伊春区", + "code":"230702" + }, + { + "name":"南岔区", + "code":"230703" + }, + { + "name":"友好区", + "code":"230704" + }, + { + "name":"西林区", + "code":"230705" + }, + { + "name":"翠峦区", + "code":"230706" + }, + { + "name":"新青区", + "code":"230707" + }, + { + "name":"美溪区", + "code":"230708" + }, + { + "name":"金山屯区", + "code":"230709" + }, + { + "name":"五营区", + "code":"230710" + }, + { + "name":"乌马河区", + "code":"230711" + }, + { + "name":"汤旺河区", + "code":"230712" + }, + { + "name":"带岭区", + "code":"230713" + }, + { + "name":"乌伊岭区", + "code":"230714" + }, + { + "name":"红星区", + "code":"230715" + }, + { + "name":"上甘岭区", + "code":"230716" + }, + { + "name":"嘉荫县", + "code":"230722" + }, + { + "name":"铁力市", + "code":"230781" + } + ] + }, + { + "name":"佳木斯市", + "code":"230800", + "sub":[ + { + "name":"市辖区", + "code":"230801" + }, + { + "name":"向阳区", + "code":"230803" + }, + { + "name":"前进区", + "code":"230804" + }, + { + "name":"东风区", + "code":"230805" + }, + { + "name":"郊区", + "code":"230811" + }, + { + "name":"桦南县", + "code":"230822" + }, + { + "name":"桦川县", + "code":"230826" + }, + { + "name":"汤原县", + "code":"230828" + }, + { + "name":"抚远县", + "code":"230833" + }, + { + "name":"同江市", + "code":"230881" + }, + { + "name":"富锦市", + "code":"230882" + } + ] + }, + { + "name":"七台河市", + "code":"230900", + "sub":[ + { + "name":"市辖区", + "code":"230901" + }, + { + "name":"新兴区", + "code":"230902" + }, + { + "name":"桃山区", + "code":"230903" + }, + { + "name":"茄子河区", + "code":"230904" + }, + { + "name":"勃利县", + "code":"230921" + } + ] + }, + { + "name":"牡丹江市", + "code":"231000", + "sub":[ + { + "name":"市辖区", + "code":"231001" + }, + { + "name":"东安区", + "code":"231002" + }, + { + "name":"阳明区", + "code":"231003" + }, + { + "name":"爱民区", + "code":"231004" + }, + { + "name":"西安区", + "code":"231005" + }, + { + "name":"东宁县", + "code":"231024" + }, + { + "name":"林口县", + "code":"231025" + }, + { + "name":"绥芬河市", + "code":"231081" + }, + { + "name":"海林市", + "code":"231083" + }, + { + "name":"宁安市", + "code":"231084" + }, + { + "name":"穆棱市", + "code":"231085" + } + ] + }, + { + "name":"黑河市", + "code":"231100", + "sub":[ + { + "name":"市辖区", + "code":"231101" + }, + { + "name":"爱辉区", + "code":"231102" + }, + { + "name":"嫩江县", + "code":"231121" + }, + { + "name":"逊克县", + "code":"231123" + }, + { + "name":"孙吴县", + "code":"231124" + }, + { + "name":"北安市", + "code":"231181" + }, + { + "name":"五大连池市", + "code":"231182" + } + ] + }, + { + "name":"绥化市", + "code":"231200", + "sub":[ + { + "name":"市辖区", + "code":"231201" + }, + { + "name":"北林区", + "code":"231202" + }, + { + "name":"望奎县", + "code":"231221" + }, + { + "name":"兰西县", + "code":"231222" + }, + { + "name":"青冈县", + "code":"231223" + }, + { + "name":"庆安县", + "code":"231224" + }, + { + "name":"明水县", + "code":"231225" + }, + { + "name":"绥棱县", + "code":"231226" + }, + { + "name":"安达市", + "code":"231281" + }, + { + "name":"肇东市", + "code":"231282" + }, + { + "name":"海伦市", + "code":"231283" + } + ] + }, + { + "name":"大兴安岭地区", + "code":"232700", + "sub":[ + { + "name":"呼玛县", + "code":"232721" + }, + { + "name":"塔河县", + "code":"232722" + }, + { + "name":"漠河县", + "code":"232723" + } + ] + } + ] + }, + { + "name":"上海", + "code":"310000", + "sub": [ + { + "name":"上海市", + "code": "310000", + "sub":[ + { + "name":"黄浦区", + "code":"310101" + }, + { + "name":"徐汇区", + "code":"310104" + }, + { + "name":"长宁区", + "code":"310105" + }, + { + "name":"静安区", + "code":"310106" + }, + { + "name":"普陀区", + "code":"310107" + }, + { + "name":"闸北区", + "code":"310108" + }, + { + "name":"虹口区", + "code":"310109" + }, + { + "name":"杨浦区", + "code":"310110" + }, + { + "name":"闵行区", + "code":"310112" + }, + { + "name":"宝山区", + "code":"310113" + }, + { + "name":"嘉定区", + "code":"310114" + }, + { + "name":"浦东新区", + "code":"310115" + }, + { + "name":"金山区", + "code":"310116" + }, + { + "name":"松江区", + "code":"310117" + }, + { + "name":"青浦区", + "code":"310118" + }, + { + "name":"奉贤区", + "code":"310120" + }, + { + "name":"崇明县", + "code":"310230" + } + ] + } + ] + }, + { + "name":"江苏省", + "code":"320000", + "sub":[ + { + "name":"南京市", + "code":"320100", + "sub":[ + { + "name":"市辖区", + "code":"320101" + }, + { + "name":"玄武区", + "code":"320102" + }, + { + "name":"秦淮区", + "code":"320104" + }, + { + "name":"建邺区", + "code":"320105" + }, + { + "name":"鼓楼区", + "code":"320106" + }, + { + "name":"浦口区", + "code":"320111" + }, + { + "name":"栖霞区", + "code":"320113" + }, + { + "name":"雨花台区", + "code":"320114" + }, + { + "name":"江宁区", + "code":"320115" + }, + { + "name":"六合区", + "code":"320116" + }, + { + "name":"溧水区", + "code":"320117" + }, + { + "name":"高淳区", + "code":"320118" + } + ] + }, + { + "name":"无锡市", + "code":"320200", + "sub":[ + { + "name":"市辖区", + "code":"320201" + }, + { + "name":"崇安区", + "code":"320202" + }, + { + "name":"南长区", + "code":"320203" + }, + { + "name":"北塘区", + "code":"320204" + }, + { + "name":"锡山区", + "code":"320205" + }, + { + "name":"惠山区", + "code":"320206" + }, + { + "name":"滨湖区", + "code":"320211" + }, + { + "name":"江阴市", + "code":"320281" + }, + { + "name":"宜兴市", + "code":"320282" + } + ] + }, + { + "name":"徐州市", + "code":"320300", + "sub":[ + { + "name":"市辖区", + "code":"320301" + }, + { + "name":"鼓楼区", + "code":"320302" + }, + { + "name":"云龙区", + "code":"320303" + }, + { + "name":"贾汪区", + "code":"320305" + }, + { + "name":"泉山区", + "code":"320311" + }, + { + "name":"铜山区", + "code":"320312" + }, + { + "name":"丰县", + "code":"320321" + }, + { + "name":"沛县", + "code":"320322" + }, + { + "name":"睢宁县", + "code":"320324" + }, + { + "name":"新沂市", + "code":"320381" + }, + { + "name":"邳州市", + "code":"320382" + } + ] + }, + { + "name":"常州市", + "code":"320400", + "sub":[ + { + "name":"市辖区", + "code":"320401" + }, + { + "name":"天宁区", + "code":"320402" + }, + { + "name":"钟楼区", + "code":"320404" + }, + { + "name":"戚墅堰区", + "code":"320405" + }, + { + "name":"新北区", + "code":"320411" + }, + { + "name":"武进区", + "code":"320412" + }, + { + "name":"溧阳市", + "code":"320481" + }, + { + "name":"金坛市", + "code":"320482" + } + ] + }, + { + "name":"苏州市", + "code":"320500", + "sub":[ + { + "name":"市辖区", + "code":"320501" + }, + { + "name":"虎丘区", + "code":"320505" + }, + { + "name":"吴中区", + "code":"320506" + }, + { + "name":"相城区", + "code":"320507" + }, + { + "name":"姑苏区", + "code":"320508" + }, + { + "name":"吴江区", + "code":"320509" + }, + { + "name":"常熟市", + "code":"320581" + }, + { + "name":"张家港市", + "code":"320582" + }, + { + "name":"昆山市", + "code":"320583" + }, + { + "name":"太仓市", + "code":"320585" + } + ] + }, + { + "name":"南通市", + "code":"320600", + "sub":[ + { + "name":"市辖区", + "code":"320601" + }, + { + "name":"崇川区", + "code":"320602" + }, + { + "name":"港闸区", + "code":"320611" + }, + { + "name":"通州区", + "code":"320612" + }, + { + "name":"海安县", + "code":"320621" + }, + { + "name":"如东县", + "code":"320623" + }, + { + "name":"启东市", + "code":"320681" + }, + { + "name":"如皋市", + "code":"320682" + }, + { + "name":"海门市", + "code":"320684" + } + ] + }, + { + "name":"连云港市", + "code":"320700", + "sub":[ + { + "name":"市辖区", + "code":"320701" + }, + { + "name":"连云区", + "code":"320703" + }, + { + "name":"海州区", + "code":"320706" + }, + { + "name":"赣榆区", + "code":"320707" + }, + { + "name":"东海县", + "code":"320722" + }, + { + "name":"灌云县", + "code":"320723" + }, + { + "name":"灌南县", + "code":"320724" + } + ] + }, + { + "name":"淮安市", + "code":"320800", + "sub":[ + { + "name":"市辖区", + "code":"320801" + }, + { + "name":"清河区", + "code":"320802" + }, + { + "name":"淮安区", + "code":"320803" + }, + { + "name":"淮阴区", + "code":"320804" + }, + { + "name":"清浦区", + "code":"320811" + }, + { + "name":"涟水县", + "code":"320826" + }, + { + "name":"洪泽县", + "code":"320829" + }, + { + "name":"盱眙县", + "code":"320830" + }, + { + "name":"金湖县", + "code":"320831" + } + ] + }, + { + "name":"盐城市", + "code":"320900", + "sub":[ + { + "name":"市辖区", + "code":"320901" + }, + { + "name":"亭湖区", + "code":"320902" + }, + { + "name":"盐都区", + "code":"320903" + }, + { + "name":"响水县", + "code":"320921" + }, + { + "name":"滨海县", + "code":"320922" + }, + { + "name":"阜宁县", + "code":"320923" + }, + { + "name":"射阳县", + "code":"320924" + }, + { + "name":"建湖县", + "code":"320925" + }, + { + "name":"东台市", + "code":"320981" + }, + { + "name":"大丰市", + "code":"320982" + } + ] + }, + { + "name":"扬州市", + "code":"321000", + "sub":[ + { + "name":"市辖区", + "code":"321001" + }, + { + "name":"广陵区", + "code":"321002" + }, + { + "name":"邗江区", + "code":"321003" + }, + { + "name":"江都区", + "code":"321012" + }, + { + "name":"宝应县", + "code":"321023" + }, + { + "name":"仪征市", + "code":"321081" + }, + { + "name":"高邮市", + "code":"321084" + } + ] + }, + { + "name":"镇江市", + "code":"321100", + "sub":[ + { + "name":"市辖区", + "code":"321101" + }, + { + "name":"京口区", + "code":"321102" + }, + { + "name":"润州区", + "code":"321111" + }, + { + "name":"丹徒区", + "code":"321112" + }, + { + "name":"丹阳市", + "code":"321181" + }, + { + "name":"扬中市", + "code":"321182" + }, + { + "name":"句容市", + "code":"321183" + } + ] + }, + { + "name":"泰州市", + "code":"321200", + "sub":[ + { + "name":"市辖区", + "code":"321201" + }, + { + "name":"海陵区", + "code":"321202" + }, + { + "name":"高港区", + "code":"321203" + }, + { + "name":"姜堰区", + "code":"321204" + }, + { + "name":"兴化市", + "code":"321281" + }, + { + "name":"靖江市", + "code":"321282" + }, + { + "name":"泰兴市", + "code":"321283" + } + ] + }, + { + "name":"宿迁市", + "code":"321300", + "sub":[ + { + "name":"市辖区", + "code":"321301" + }, + { + "name":"宿城区", + "code":"321302" + }, + { + "name":"宿豫区", + "code":"321311" + }, + { + "name":"沭阳县", + "code":"321322" + }, + { + "name":"泗阳县", + "code":"321323" + }, + { + "name":"泗洪县", + "code":"321324" + } + ] + } + ] + }, + { + "name":"浙江省", + "code":"330000", + "sub":[ + { + "name":"杭州市", + "code":"330100", + "sub":[ + { + "name":"市辖区", + "code":"330101" + }, + { + "name":"上城区", + "code":"330102" + }, + { + "name":"下城区", + "code":"330103" + }, + { + "name":"江干区", + "code":"330104" + }, + { + "name":"拱墅区", + "code":"330105" + }, + { + "name":"西湖区", + "code":"330106" + }, + { + "name":"滨江区", + "code":"330108" + }, + { + "name":"萧山区", + "code":"330109" + }, + { + "name":"余杭区", + "code":"330110" + }, + { + "name":"富阳区", + "code":"330111" + }, + { + "name":"桐庐县", + "code":"330122" + }, + { + "name":"淳安县", + "code":"330127" + }, + { + "name":"建德市", + "code":"330182" + }, + { + "name":"临安市", + "code":"330185" + } + ] + }, + { + "name":"宁波市", + "code":"330200", + "sub":[ + { + "name":"市辖区", + "code":"330201" + }, + { + "name":"海曙区", + "code":"330203" + }, + { + "name":"江东区", + "code":"330204" + }, + { + "name":"江北区", + "code":"330205" + }, + { + "name":"北仑区", + "code":"330206" + }, + { + "name":"镇海区", + "code":"330211" + }, + { + "name":"鄞州区", + "code":"330212" + }, + { + "name":"象山县", + "code":"330225" + }, + { + "name":"宁海县", + "code":"330226" + }, + { + "name":"余姚市", + "code":"330281" + }, + { + "name":"慈溪市", + "code":"330282" + }, + { + "name":"奉化市", + "code":"330283" + } + ] + }, + { + "name":"温州市", + "code":"330300", + "sub":[ + { + "name":"市辖区", + "code":"330301" + }, + { + "name":"鹿城区", + "code":"330302" + }, + { + "name":"龙湾区", + "code":"330303" + }, + { + "name":"瓯海区", + "code":"330304" + }, + { + "name":"洞头县", + "code":"330322" + }, + { + "name":"永嘉县", + "code":"330324" + }, + { + "name":"平阳县", + "code":"330326" + }, + { + "name":"苍南县", + "code":"330327" + }, + { + "name":"文成县", + "code":"330328" + }, + { + "name":"泰顺县", + "code":"330329" + }, + { + "name":"瑞安市", + "code":"330381" + }, + { + "name":"乐清市", + "code":"330382" + } + ] + }, + { + "name":"嘉兴市", + "code":"330400", + "sub":[ + { + "name":"市辖区", + "code":"330401" + }, + { + "name":"南湖区", + "code":"330402" + }, + { + "name":"秀洲区", + "code":"330411" + }, + { + "name":"嘉善县", + "code":"330421" + }, + { + "name":"海盐县", + "code":"330424" + }, + { + "name":"海宁市", + "code":"330481" + }, + { + "name":"平湖市", + "code":"330482" + }, + { + "name":"桐乡市", + "code":"330483" + } + ] + }, + { + "name":"湖州市", + "code":"330500", + "sub":[ + { + "name":"市辖区", + "code":"330501" + }, + { + "name":"吴兴区", + "code":"330502" + }, + { + "name":"南浔区", + "code":"330503" + }, + { + "name":"德清县", + "code":"330521" + }, + { + "name":"长兴县", + "code":"330522" + }, + { + "name":"安吉县", + "code":"330523" + } + ] + }, + { + "name":"绍兴市", + "code":"330600", + "sub":[ + { + "name":"市辖区", + "code":"330601" + }, + { + "name":"越城区", + "code":"330602" + }, + { + "name":"柯桥区", + "code":"330603" + }, + { + "name":"上虞区", + "code":"330604" + }, + { + "name":"新昌县", + "code":"330624" + }, + { + "name":"诸暨市", + "code":"330681" + }, + { + "name":"嵊州市", + "code":"330683" + } + ] + }, + { + "name":"金华市", + "code":"330700", + "sub":[ + { + "name":"市辖区", + "code":"330701" + }, + { + "name":"婺城区", + "code":"330702" + }, + { + "name":"金东区", + "code":"330703" + }, + { + "name":"武义县", + "code":"330723" + }, + { + "name":"浦江县", + "code":"330726" + }, + { + "name":"磐安县", + "code":"330727" + }, + { + "name":"兰溪市", + "code":"330781" + }, + { + "name":"义乌市", + "code":"330782" + }, + { + "name":"东阳市", + "code":"330783" + }, + { + "name":"永康市", + "code":"330784" + } + ] + }, + { + "name":"衢州市", + "code":"330800", + "sub":[ + { + "name":"市辖区", + "code":"330801" + }, + { + "name":"柯城区", + "code":"330802" + }, + { + "name":"衢江区", + "code":"330803" + }, + { + "name":"常山县", + "code":"330822" + }, + { + "name":"开化县", + "code":"330824" + }, + { + "name":"龙游县", + "code":"330825" + }, + { + "name":"江山市", + "code":"330881" + } + ] + }, + { + "name":"舟山市", + "code":"330900", + "sub":[ + { + "name":"市辖区", + "code":"330901" + }, + { + "name":"定海区", + "code":"330902" + }, + { + "name":"普陀区", + "code":"330903" + }, + { + "name":"岱山县", + "code":"330921" + }, + { + "name":"嵊泗县", + "code":"330922" + } + ] + }, + { + "name":"台州市", + "code":"331000", + "sub":[ + { + "name":"市辖区", + "code":"331001" + }, + { + "name":"椒江区", + "code":"331002" + }, + { + "name":"黄岩区", + "code":"331003" + }, + { + "name":"路桥区", + "code":"331004" + }, + { + "name":"玉环县", + "code":"331021" + }, + { + "name":"三门县", + "code":"331022" + }, + { + "name":"天台县", + "code":"331023" + }, + { + "name":"仙居县", + "code":"331024" + }, + { + "name":"温岭市", + "code":"331081" + }, + { + "name":"临海市", + "code":"331082" + } + ] + }, + { + "name":"丽水市", + "code":"331100", + "sub":[ + { + "name":"市辖区", + "code":"331101" + }, + { + "name":"莲都区", + "code":"331102" + }, + { + "name":"青田县", + "code":"331121" + }, + { + "name":"缙云县", + "code":"331122" + }, + { + "name":"遂昌县", + "code":"331123" + }, + { + "name":"松阳县", + "code":"331124" + }, + { + "name":"云和县", + "code":"331125" + }, + { + "name":"庆元县", + "code":"331126" + }, + { + "name":"景宁畲族自治县", + "code":"331127" + }, + { + "name":"龙泉市", + "code":"331181" + } + ] + } + ] + }, + { + "name":"安徽省", + "code":"340000", + "sub":[ + { + "name":"合肥市", + "code":"340100", + "sub":[ + { + "name":"市辖区", + "code":"340101" + }, + { + "name":"瑶海区", + "code":"340102" + }, + { + "name":"庐阳区", + "code":"340103" + }, + { + "name":"蜀山区", + "code":"340104" + }, + { + "name":"包河区", + "code":"340111" + }, + { + "name":"长丰县", + "code":"340121" + }, + { + "name":"肥东县", + "code":"340122" + }, + { + "name":"肥西县", + "code":"340123" + }, + { + "name":"庐江县", + "code":"340124" + }, + { + "name":"巢湖市", + "code":"340181" + } + ] + }, + { + "name":"芜湖市", + "code":"340200", + "sub":[ + { + "name":"市辖区", + "code":"340201" + }, + { + "name":"镜湖区", + "code":"340202" + }, + { + "name":"弋江区", + "code":"340203" + }, + { + "name":"鸠江区", + "code":"340207" + }, + { + "name":"三山区", + "code":"340208" + }, + { + "name":"芜湖县", + "code":"340221" + }, + { + "name":"繁昌县", + "code":"340222" + }, + { + "name":"南陵县", + "code":"340223" + }, + { + "name":"无为县", + "code":"340225" + } + ] + }, + { + "name":"蚌埠市", + "code":"340300", + "sub":[ + { + "name":"市辖区", + "code":"340301" + }, + { + "name":"龙子湖区", + "code":"340302" + }, + { + "name":"蚌山区", + "code":"340303" + }, + { + "name":"禹会区", + "code":"340304" + }, + { + "name":"淮上区", + "code":"340311" + }, + { + "name":"怀远县", + "code":"340321" + }, + { + "name":"五河县", + "code":"340322" + }, + { + "name":"固镇县", + "code":"340323" + } + ] + }, + { + "name":"淮南市", + "code":"340400", + "sub":[ + { + "name":"市辖区", + "code":"340401" + }, + { + "name":"大通区", + "code":"340402" + }, + { + "name":"田家庵区", + "code":"340403" + }, + { + "name":"谢家集区", + "code":"340404" + }, + { + "name":"八公山区", + "code":"340405" + }, + { + "name":"潘集区", + "code":"340406" + }, + { + "name":"凤台县", + "code":"340421" + } + ] + }, + { + "name":"马鞍山市", + "code":"340500", + "sub":[ + { + "name":"市辖区", + "code":"340501" + }, + { + "name":"花山区", + "code":"340503" + }, + { + "name":"雨山区", + "code":"340504" + }, + { + "name":"博望区", + "code":"340506" + }, + { + "name":"当涂县", + "code":"340521" + }, + { + "name":"含山县", + "code":"340522" + }, + { + "name":"和县", + "code":"340523" + } + ] + }, + { + "name":"淮北市", + "code":"340600", + "sub":[ + { + "name":"市辖区", + "code":"340601" + }, + { + "name":"杜集区", + "code":"340602" + }, + { + "name":"相山区", + "code":"340603" + }, + { + "name":"烈山区", + "code":"340604" + }, + { + "name":"濉溪县", + "code":"340621" + } + ] + }, + { + "name":"铜陵市", + "code":"340700", + "sub":[ + { + "name":"市辖区", + "code":"340701" + }, + { + "name":"铜官山区", + "code":"340702" + }, + { + "name":"狮子山区", + "code":"340703" + }, + { + "name":"郊区", + "code":"340711" + }, + { + "name":"铜陵县", + "code":"340721" + } + ] + }, + { + "name":"安庆市", + "code":"340800", + "sub":[ + { + "name":"市辖区", + "code":"340801" + }, + { + "name":"迎江区", + "code":"340802" + }, + { + "name":"大观区", + "code":"340803" + }, + { + "name":"宜秀区", + "code":"340811" + }, + { + "name":"怀宁县", + "code":"340822" + }, + { + "name":"枞阳县", + "code":"340823" + }, + { + "name":"潜山县", + "code":"340824" + }, + { + "name":"太湖县", + "code":"340825" + }, + { + "name":"宿松县", + "code":"340826" + }, + { + "name":"望江县", + "code":"340827" + }, + { + "name":"岳西县", + "code":"340828" + }, + { + "name":"桐城市", + "code":"340881" + } + ] + }, + { + "name":"黄山市", + "code":"341000", + "sub":[ + { + "name":"市辖区", + "code":"341001" + }, + { + "name":"屯溪区", + "code":"341002" + }, + { + "name":"黄山区", + "code":"341003" + }, + { + "name":"徽州区", + "code":"341004" + }, + { + "name":"歙县", + "code":"341021" + }, + { + "name":"休宁县", + "code":"341022" + }, + { + "name":"黟县", + "code":"341023" + }, + { + "name":"祁门县", + "code":"341024" + } + ] + }, + { + "name":"滁州市", + "code":"341100", + "sub":[ + { + "name":"市辖区", + "code":"341101" + }, + { + "name":"琅琊区", + "code":"341102" + }, + { + "name":"南谯区", + "code":"341103" + }, + { + "name":"来安县", + "code":"341122" + }, + { + "name":"全椒县", + "code":"341124" + }, + { + "name":"定远县", + "code":"341125" + }, + { + "name":"凤阳县", + "code":"341126" + }, + { + "name":"天长市", + "code":"341181" + }, + { + "name":"明光市", + "code":"341182" + } + ] + }, + { + "name":"阜阳市", + "code":"341200", + "sub":[ + { + "name":"市辖区", + "code":"341201" + }, + { + "name":"颍州区", + "code":"341202" + }, + { + "name":"颍东区", + "code":"341203" + }, + { + "name":"颍泉区", + "code":"341204" + }, + { + "name":"临泉县", + "code":"341221" + }, + { + "name":"太和县", + "code":"341222" + }, + { + "name":"阜南县", + "code":"341225" + }, + { + "name":"颍上县", + "code":"341226" + }, + { + "name":"界首市", + "code":"341282" + } + ] + }, + { + "name":"宿州市", + "code":"341300", + "sub":[ + { + "name":"市辖区", + "code":"341301" + }, + { + "name":"埇桥区", + "code":"341302" + }, + { + "name":"砀山县", + "code":"341321" + }, + { + "name":"萧县", + "code":"341322" + }, + { + "name":"灵璧县", + "code":"341323" + }, + { + "name":"泗县", + "code":"341324" + } + ] + }, + { + "name":"六安市", + "code":"341500", + "sub":[ + { + "name":"市辖区", + "code":"341501" + }, + { + "name":"金安区", + "code":"341502" + }, + { + "name":"裕安区", + "code":"341503" + }, + { + "name":"寿县", + "code":"341521" + }, + { + "name":"霍邱县", + "code":"341522" + }, + { + "name":"舒城县", + "code":"341523" + }, + { + "name":"金寨县", + "code":"341524" + }, + { + "name":"霍山县", + "code":"341525" + } + ] + }, + { + "name":"亳州市", + "code":"341600", + "sub":[ + { + "name":"市辖区", + "code":"341601" + }, + { + "name":"谯城区", + "code":"341602" + }, + { + "name":"涡阳县", + "code":"341621" + }, + { + "name":"蒙城县", + "code":"341622" + }, + { + "name":"利辛县", + "code":"341623" + } + ] + }, + { + "name":"池州市", + "code":"341700", + "sub":[ + { + "name":"市辖区", + "code":"341701" + }, + { + "name":"贵池区", + "code":"341702" + }, + { + "name":"东至县", + "code":"341721" + }, + { + "name":"石台县", + "code":"341722" + }, + { + "name":"青阳县", + "code":"341723" + } + ] + }, + { + "name":"宣城市", + "code":"341800", + "sub":[ + { + "name":"市辖区", + "code":"341801" + }, + { + "name":"宣州区", + "code":"341802" + }, + { + "name":"郎溪县", + "code":"341821" + }, + { + "name":"广德县", + "code":"341822" + }, + { + "name":"泾县", + "code":"341823" + }, + { + "name":"绩溪县", + "code":"341824" + }, + { + "name":"旌德县", + "code":"341825" + }, + { + "name":"宁国市", + "code":"341881" + } + ] + } + ] + }, + { + "name":"福建省", + "code":"350000", + "sub":[ + { + "name":"福州市", + "code":"350100", + "sub":[ + { + "name":"市辖区", + "code":"350101" + }, + { + "name":"鼓楼区", + "code":"350102" + }, + { + "name":"台江区", + "code":"350103" + }, + { + "name":"仓山区", + "code":"350104" + }, + { + "name":"马尾区", + "code":"350105" + }, + { + "name":"晋安区", + "code":"350111" + }, + { + "name":"闽侯县", + "code":"350121" + }, + { + "name":"连江县", + "code":"350122" + }, + { + "name":"罗源县", + "code":"350123" + }, + { + "name":"闽清县", + "code":"350124" + }, + { + "name":"永泰县", + "code":"350125" + }, + { + "name":"平潭县", + "code":"350128" + }, + { + "name":"福清市", + "code":"350181" + }, + { + "name":"长乐市", + "code":"350182" + } + ] + }, + { + "name":"厦门市", + "code":"350200", + "sub":[ + { + "name":"市辖区", + "code":"350201" + }, + { + "name":"思明区", + "code":"350203" + }, + { + "name":"海沧区", + "code":"350205" + }, + { + "name":"湖里区", + "code":"350206" + }, + { + "name":"集美区", + "code":"350211" + }, + { + "name":"同安区", + "code":"350212" + }, + { + "name":"翔安区", + "code":"350213" + } + ] + }, + { + "name":"莆田市", + "code":"350300", + "sub":[ + { + "name":"市辖区", + "code":"350301" + }, + { + "name":"城厢区", + "code":"350302" + }, + { + "name":"涵江区", + "code":"350303" + }, + { + "name":"荔城区", + "code":"350304" + }, + { + "name":"秀屿区", + "code":"350305" + }, + { + "name":"仙游县", + "code":"350322" + } + ] + }, + { + "name":"三明市", + "code":"350400", + "sub":[ + { + "name":"市辖区", + "code":"350401" + }, + { + "name":"梅列区", + "code":"350402" + }, + { + "name":"三元区", + "code":"350403" + }, + { + "name":"明溪县", + "code":"350421" + }, + { + "name":"清流县", + "code":"350423" + }, + { + "name":"宁化县", + "code":"350424" + }, + { + "name":"大田县", + "code":"350425" + }, + { + "name":"尤溪县", + "code":"350426" + }, + { + "name":"沙县", + "code":"350427" + }, + { + "name":"将乐县", + "code":"350428" + }, + { + "name":"泰宁县", + "code":"350429" + }, + { + "name":"建宁县", + "code":"350430" + }, + { + "name":"永安市", + "code":"350481" + } + ] + }, + { + "name":"泉州市", + "code":"350500", + "sub":[ + { + "name":"市辖区", + "code":"350501" + }, + { + "name":"鲤城区", + "code":"350502" + }, + { + "name":"丰泽区", + "code":"350503" + }, + { + "name":"洛江区", + "code":"350504" + }, + { + "name":"泉港区", + "code":"350505" + }, + { + "name":"惠安县", + "code":"350521" + }, + { + "name":"安溪县", + "code":"350524" + }, + { + "name":"永春县", + "code":"350525" + }, + { + "name":"德化县", + "code":"350526" + }, + { + "name":"金门县", + "code":"350527" + }, + { + "name":"石狮市", + "code":"350581" + }, + { + "name":"晋江市", + "code":"350582" + }, + { + "name":"南安市", + "code":"350583" + } + ] + }, + { + "name":"漳州市", + "code":"350600", + "sub":[ + { + "name":"市辖区", + "code":"350601" + }, + { + "name":"芗城区", + "code":"350602" + }, + { + "name":"龙文区", + "code":"350603" + }, + { + "name":"云霄县", + "code":"350622" + }, + { + "name":"漳浦县", + "code":"350623" + }, + { + "name":"诏安县", + "code":"350624" + }, + { + "name":"长泰县", + "code":"350625" + }, + { + "name":"东山县", + "code":"350626" + }, + { + "name":"南靖县", + "code":"350627" + }, + { + "name":"平和县", + "code":"350628" + }, + { + "name":"华安县", + "code":"350629" + }, + { + "name":"龙海市", + "code":"350681" + } + ] + }, + { + "name":"南平市", + "code":"350700", + "sub":[ + { + "name":"市辖区", + "code":"350701" + }, + { + "name":"延平区", + "code":"350702" + }, + { + "name":"建阳区", + "code":"350703" + }, + { + "name":"顺昌县", + "code":"350721" + }, + { + "name":"浦城县", + "code":"350722" + }, + { + "name":"光泽县", + "code":"350723" + }, + { + "name":"松溪县", + "code":"350724" + }, + { + "name":"政和县", + "code":"350725" + }, + { + "name":"邵武市", + "code":"350781" + }, + { + "name":"武夷山市", + "code":"350782" + }, + { + "name":"建瓯市", + "code":"350783" + } + ] + }, + { + "name":"龙岩市", + "code":"350800", + "sub":[ + { + "name":"市辖区", + "code":"350801" + }, + { + "name":"新罗区", + "code":"350802" + }, + { + "name":"永定区", + "code":"350803" + }, + { + "name":"长汀县", + "code":"350821" + }, + { + "name":"上杭县", + "code":"350823" + }, + { + "name":"武平县", + "code":"350824" + }, + { + "name":"连城县", + "code":"350825" + }, + { + "name":"漳平市", + "code":"350881" + } + ] + }, + { + "name":"宁德市", + "code":"350900", + "sub":[ + { + "name":"市辖区", + "code":"350901" + }, + { + "name":"蕉城区", + "code":"350902" + }, + { + "name":"霞浦县", + "code":"350921" + }, + { + "name":"古田县", + "code":"350922" + }, + { + "name":"屏南县", + "code":"350923" + }, + { + "name":"寿宁县", + "code":"350924" + }, + { + "name":"周宁县", + "code":"350925" + }, + { + "name":"柘荣县", + "code":"350926" + }, + { + "name":"福安市", + "code":"350981" + }, + { + "name":"福鼎市", + "code":"350982" + } + ] + } + ] + }, + { + "name":"江西省", + "code":"360000", + "sub":[ + { + "name":"南昌市", + "code":"360100", + "sub":[ + { + "name":"市辖区", + "code":"360101" + }, + { + "name":"东湖区", + "code":"360102" + }, + { + "name":"西湖区", + "code":"360103" + }, + { + "name":"青云谱区", + "code":"360104" + }, + { + "name":"湾里区", + "code":"360105" + }, + { + "name":"青山湖区", + "code":"360111" + }, + { + "name":"南昌县", + "code":"360121" + }, + { + "name":"新建县", + "code":"360122" + }, + { + "name":"安义县", + "code":"360123" + }, + { + "name":"进贤县", + "code":"360124" + } + ] + }, + { + "name":"景德镇市", + "code":"360200", + "sub":[ + { + "name":"市辖区", + "code":"360201" + }, + { + "name":"昌江区", + "code":"360202" + }, + { + "name":"珠山区", + "code":"360203" + }, + { + "name":"浮梁县", + "code":"360222" + }, + { + "name":"乐平市", + "code":"360281" + } + ] + }, + { + "name":"萍乡市", + "code":"360300", + "sub":[ + { + "name":"市辖区", + "code":"360301" + }, + { + "name":"安源区", + "code":"360302" + }, + { + "name":"湘东区", + "code":"360313" + }, + { + "name":"莲花县", + "code":"360321" + }, + { + "name":"上栗县", + "code":"360322" + }, + { + "name":"芦溪县", + "code":"360323" + } + ] + }, + { + "name":"九江市", + "code":"360400", + "sub":[ + { + "name":"市辖区", + "code":"360401" + }, + { + "name":"庐山区", + "code":"360402" + }, + { + "name":"浔阳区", + "code":"360403" + }, + { + "name":"九江县", + "code":"360421" + }, + { + "name":"武宁县", + "code":"360423" + }, + { + "name":"修水县", + "code":"360424" + }, + { + "name":"永修县", + "code":"360425" + }, + { + "name":"德安县", + "code":"360426" + }, + { + "name":"星子县", + "code":"360427" + }, + { + "name":"都昌县", + "code":"360428" + }, + { + "name":"湖口县", + "code":"360429" + }, + { + "name":"彭泽县", + "code":"360430" + }, + { + "name":"瑞昌市", + "code":"360481" + }, + { + "name":"共青城市", + "code":"360482" + } + ] + }, + { + "name":"新余市", + "code":"360500", + "sub":[ + { + "name":"市辖区", + "code":"360501" + }, + { + "name":"渝水区", + "code":"360502" + }, + { + "name":"分宜县", + "code":"360521" + } + ] + }, + { + "name":"鹰潭市", + "code":"360600", + "sub":[ + { + "name":"市辖区", + "code":"360601" + }, + { + "name":"月湖区", + "code":"360602" + }, + { + "name":"余江县", + "code":"360622" + }, + { + "name":"贵溪市", + "code":"360681" + } + ] + }, + { + "name":"赣州市", + "code":"360700", + "sub":[ + { + "name":"市辖区", + "code":"360701" + }, + { + "name":"章贡区", + "code":"360702" + }, + { + "name":"南康区", + "code":"360703" + }, + { + "name":"赣县", + "code":"360721" + }, + { + "name":"信丰县", + "code":"360722" + }, + { + "name":"大余县", + "code":"360723" + }, + { + "name":"上犹县", + "code":"360724" + }, + { + "name":"崇义县", + "code":"360725" + }, + { + "name":"安远县", + "code":"360726" + }, + { + "name":"龙南县", + "code":"360727" + }, + { + "name":"定南县", + "code":"360728" + }, + { + "name":"全南县", + "code":"360729" + }, + { + "name":"宁都县", + "code":"360730" + }, + { + "name":"于都县", + "code":"360731" + }, + { + "name":"兴国县", + "code":"360732" + }, + { + "name":"会昌县", + "code":"360733" + }, + { + "name":"寻乌县", + "code":"360734" + }, + { + "name":"石城县", + "code":"360735" + }, + { + "name":"瑞金市", + "code":"360781" + } + ] + }, + { + "name":"吉安市", + "code":"360800", + "sub":[ + { + "name":"市辖区", + "code":"360801" + }, + { + "name":"吉州区", + "code":"360802" + }, + { + "name":"青原区", + "code":"360803" + }, + { + "name":"吉安县", + "code":"360821" + }, + { + "name":"吉水县", + "code":"360822" + }, + { + "name":"峡江县", + "code":"360823" + }, + { + "name":"新干县", + "code":"360824" + }, + { + "name":"永丰县", + "code":"360825" + }, + { + "name":"泰和县", + "code":"360826" + }, + { + "name":"遂川县", + "code":"360827" + }, + { + "name":"万安县", + "code":"360828" + }, + { + "name":"安福县", + "code":"360829" + }, + { + "name":"永新县", + "code":"360830" + }, + { + "name":"井冈山市", + "code":"360881" + } + ] + }, + { + "name":"宜春市", + "code":"360900", + "sub":[ + { + "name":"市辖区", + "code":"360901" + }, + { + "name":"袁州区", + "code":"360902" + }, + { + "name":"奉新县", + "code":"360921" + }, + { + "name":"万载县", + "code":"360922" + }, + { + "name":"上高县", + "code":"360923" + }, + { + "name":"宜丰县", + "code":"360924" + }, + { + "name":"靖安县", + "code":"360925" + }, + { + "name":"铜鼓县", + "code":"360926" + }, + { + "name":"丰城市", + "code":"360981" + }, + { + "name":"樟树市", + "code":"360982" + }, + { + "name":"高安市", + "code":"360983" + } + ] + }, + { + "name":"抚州市", + "code":"361000", + "sub":[ + { + "name":"市辖区", + "code":"361001" + }, + { + "name":"临川区", + "code":"361002" + }, + { + "name":"南城县", + "code":"361021" + }, + { + "name":"黎川县", + "code":"361022" + }, + { + "name":"南丰县", + "code":"361023" + }, + { + "name":"崇仁县", + "code":"361024" + }, + { + "name":"乐安县", + "code":"361025" + }, + { + "name":"宜黄县", + "code":"361026" + }, + { + "name":"金溪县", + "code":"361027" + }, + { + "name":"资溪县", + "code":"361028" + }, + { + "name":"东乡县", + "code":"361029" + }, + { + "name":"广昌县", + "code":"361030" + } + ] + }, + { + "name":"上饶市", + "code":"361100", + "sub":[ + { + "name":"市辖区", + "code":"361101" + }, + { + "name":"信州区", + "code":"361102" + }, + { + "name":"上饶县", + "code":"361121" + }, + { + "name":"广丰县", + "code":"361122" + }, + { + "name":"玉山县", + "code":"361123" + }, + { + "name":"铅山县", + "code":"361124" + }, + { + "name":"横峰县", + "code":"361125" + }, + { + "name":"弋阳县", + "code":"361126" + }, + { + "name":"余干县", + "code":"361127" + }, + { + "name":"鄱阳县", + "code":"361128" + }, + { + "name":"万年县", + "code":"361129" + }, + { + "name":"婺源县", + "code":"361130" + }, + { + "name":"德兴市", + "code":"361181" + } + ] + } + ] + }, + { + "name":"山东省", + "code":"370000", + "sub":[ + { + "name":"济南市", + "code":"370100", + "sub":[ + { + "name":"市辖区", + "code":"370101" + }, + { + "name":"历下区", + "code":"370102" + }, + { + "name":"市中区", + "code":"370103" + }, + { + "name":"槐荫区", + "code":"370104" + }, + { + "name":"天桥区", + "code":"370105" + }, + { + "name":"历城区", + "code":"370112" + }, + { + "name":"长清区", + "code":"370113" + }, + { + "name":"平阴县", + "code":"370124" + }, + { + "name":"济阳县", + "code":"370125" + }, + { + "name":"商河县", + "code":"370126" + }, + { + "name":"章丘市", + "code":"370181" + } + ] + }, + { + "name":"青岛市", + "code":"370200", + "sub":[ + { + "name":"市辖区", + "code":"370201" + }, + { + "name":"市南区", + "code":"370202" + }, + { + "name":"市北区", + "code":"370203" + }, + { + "name":"黄岛区", + "code":"370211" + }, + { + "name":"崂山区", + "code":"370212" + }, + { + "name":"李沧区", + "code":"370213" + }, + { + "name":"城阳区", + "code":"370214" + }, + { + "name":"胶州市", + "code":"370281" + }, + { + "name":"即墨市", + "code":"370282" + }, + { + "name":"平度市", + "code":"370283" + }, + { + "name":"莱西市", + "code":"370285" + } + ] + }, + { + "name":"淄博市", + "code":"370300", + "sub":[ + { + "name":"市辖区", + "code":"370301" + }, + { + "name":"淄川区", + "code":"370302" + }, + { + "name":"张店区", + "code":"370303" + }, + { + "name":"博山区", + "code":"370304" + }, + { + "name":"临淄区", + "code":"370305" + }, + { + "name":"周村区", + "code":"370306" + }, + { + "name":"桓台县", + "code":"370321" + }, + { + "name":"高青县", + "code":"370322" + }, + { + "name":"沂源县", + "code":"370323" + } + ] + }, + { + "name":"枣庄市", + "code":"370400", + "sub":[ + { + "name":"市辖区", + "code":"370401" + }, + { + "name":"市中区", + "code":"370402" + }, + { + "name":"薛城区", + "code":"370403" + }, + { + "name":"峄城区", + "code":"370404" + }, + { + "name":"台儿庄区", + "code":"370405" + }, + { + "name":"山亭区", + "code":"370406" + }, + { + "name":"滕州市", + "code":"370481" + } + ] + }, + { + "name":"东营市", + "code":"370500", + "sub":[ + { + "name":"市辖区", + "code":"370501" + }, + { + "name":"东营区", + "code":"370502" + }, + { + "name":"河口区", + "code":"370503" + }, + { + "name":"垦利县", + "code":"370521" + }, + { + "name":"利津县", + "code":"370522" + }, + { + "name":"广饶县", + "code":"370523" + } + ] + }, + { + "name":"烟台市", + "code":"370600", + "sub":[ + { + "name":"市辖区", + "code":"370601" + }, + { + "name":"芝罘区", + "code":"370602" + }, + { + "name":"福山区", + "code":"370611" + }, + { + "name":"牟平区", + "code":"370612" + }, + { + "name":"莱山区", + "code":"370613" + }, + { + "name":"长岛县", + "code":"370634" + }, + { + "name":"龙口市", + "code":"370681" + }, + { + "name":"莱阳市", + "code":"370682" + }, + { + "name":"莱州市", + "code":"370683" + }, + { + "name":"蓬莱市", + "code":"370684" + }, + { + "name":"招远市", + "code":"370685" + }, + { + "name":"栖霞市", + "code":"370686" + }, + { + "name":"海阳市", + "code":"370687" + } + ] + }, + { + "name":"潍坊市", + "code":"370700", + "sub":[ + { + "name":"市辖区", + "code":"370701" + }, + { + "name":"潍城区", + "code":"370702" + }, + { + "name":"寒亭区", + "code":"370703" + }, + { + "name":"坊子区", + "code":"370704" + }, + { + "name":"奎文区", + "code":"370705" + }, + { + "name":"临朐县", + "code":"370724" + }, + { + "name":"昌乐县", + "code":"370725" + }, + { + "name":"青州市", + "code":"370781" + }, + { + "name":"诸城市", + "code":"370782" + }, + { + "name":"寿光市", + "code":"370783" + }, + { + "name":"安丘市", + "code":"370784" + }, + { + "name":"高密市", + "code":"370785" + }, + { + "name":"昌邑市", + "code":"370786" + } + ] + }, + { + "name":"济宁市", + "code":"370800", + "sub":[ + { + "name":"市辖区", + "code":"370801" + }, + { + "name":"任城区", + "code":"370811" + }, + { + "name":"兖州区", + "code":"370812" + }, + { + "name":"微山县", + "code":"370826" + }, + { + "name":"鱼台县", + "code":"370827" + }, + { + "name":"金乡县", + "code":"370828" + }, + { + "name":"嘉祥县", + "code":"370829" + }, + { + "name":"汶上县", + "code":"370830" + }, + { + "name":"泗水县", + "code":"370831" + }, + { + "name":"梁山县", + "code":"370832" + }, + { + "name":"曲阜市", + "code":"370881" + }, + { + "name":"邹城市", + "code":"370883" + } + ] + }, + { + "name":"泰安市", + "code":"370900", + "sub":[ + { + "name":"市辖区", + "code":"370901" + }, + { + "name":"泰山区", + "code":"370902" + }, + { + "name":"岱岳区", + "code":"370911" + }, + { + "name":"宁阳县", + "code":"370921" + }, + { + "name":"东平县", + "code":"370923" + }, + { + "name":"新泰市", + "code":"370982" + }, + { + "name":"肥城市", + "code":"370983" + } + ] + }, + { + "name":"威海市", + "code":"371000", + "sub":[ + { + "name":"市辖区", + "code":"371001" + }, + { + "name":"环翠区", + "code":"371002" + }, + { + "name":"文登市", + "code":"371081" + }, + { + "name":"荣成市", + "code":"371082" + }, + { + "name":"乳山市", + "code":"371083" + } + ] + }, + { + "name":"日照市", + "code":"371100", + "sub":[ + { + "name":"市辖区", + "code":"371101" + }, + { + "name":"东港区", + "code":"371102" + }, + { + "name":"岚山区", + "code":"371103" + }, + { + "name":"五莲县", + "code":"371121" + }, + { + "name":"莒县", + "code":"371122" + } + ] + }, + { + "name":"莱芜市", + "code":"371200", + "sub":[ + { + "name":"市辖区", + "code":"371201" + }, + { + "name":"莱城区", + "code":"371202" + }, + { + "name":"钢城区", + "code":"371203" + } + ] + }, + { + "name":"临沂市", + "code":"371300", + "sub":[ + { + "name":"市辖区", + "code":"371301" + }, + { + "name":"兰山区", + "code":"371302" + }, + { + "name":"罗庄区", + "code":"371311" + }, + { + "name":"河东区", + "code":"371312" + }, + { + "name":"沂南县", + "code":"371321" + }, + { + "name":"郯城县", + "code":"371322" + }, + { + "name":"沂水县", + "code":"371323" + }, + { + "name":"兰陵县", + "code":"371324" + }, + { + "name":"费县", + "code":"371325" + }, + { + "name":"平邑县", + "code":"371326" + }, + { + "name":"莒南县", + "code":"371327" + }, + { + "name":"蒙阴县", + "code":"371328" + }, + { + "name":"临沭县", + "code":"371329" + } + ] + }, + { + "name":"德州市", + "code":"371400", + "sub":[ + { + "name":"市辖区", + "code":"371401" + }, + { + "name":"德城区", + "code":"371402" + }, + { + "name":"陵城区", + "code":"371403" + }, + { + "name":"宁津县", + "code":"371422" + }, + { + "name":"庆云县", + "code":"371423" + }, + { + "name":"临邑县", + "code":"371424" + }, + { + "name":"齐河县", + "code":"371425" + }, + { + "name":"平原县", + "code":"371426" + }, + { + "name":"夏津县", + "code":"371427" + }, + { + "name":"武城县", + "code":"371428" + }, + { + "name":"乐陵市", + "code":"371481" + }, + { + "name":"禹城市", + "code":"371482" + } + ] + }, + { + "name":"聊城市", + "code":"371500", + "sub":[ + { + "name":"市辖区", + "code":"371501" + }, + { + "name":"东昌府区", + "code":"371502" + }, + { + "name":"阳谷县", + "code":"371521" + }, + { + "name":"莘县", + "code":"371522" + }, + { + "name":"茌平县", + "code":"371523" + }, + { + "name":"东阿县", + "code":"371524" + }, + { + "name":"冠县", + "code":"371525" + }, + { + "name":"高唐县", + "code":"371526" + }, + { + "name":"临清市", + "code":"371581" + } + ] + }, + { + "name":"滨州市", + "code":"371600", + "sub":[ + { + "name":"市辖区", + "code":"371601" + }, + { + "name":"滨城区", + "code":"371602" + }, + { + "name":"沾化区", + "code":"371603" + }, + { + "name":"惠民县", + "code":"371621" + }, + { + "name":"阳信县", + "code":"371622" + }, + { + "name":"无棣县", + "code":"371623" + }, + { + "name":"博兴县", + "code":"371625" + }, + { + "name":"邹平县", + "code":"371626" + } + ] + }, + { + "name":"菏泽市", + "code":"371700", + "sub":[ + { + "name":"市辖区", + "code":"371701" + }, + { + "name":"牡丹区", + "code":"371702" + }, + { + "name":"曹县", + "code":"371721" + }, + { + "name":"单县", + "code":"371722" + }, + { + "name":"成武县", + "code":"371723" + }, + { + "name":"巨野县", + "code":"371724" + }, + { + "name":"郓城县", + "code":"371725" + }, + { + "name":"鄄城县", + "code":"371726" + }, + { + "name":"定陶县", + "code":"371727" + }, + { + "name":"东明县", + "code":"371728" + } + ] + } + ] + }, + { + "name":"河南省", + "code":"410000", + "sub":[ + { + "name":"郑州市", + "code":"410100", + "sub":[ + { + "name":"市辖区", + "code":"410101" + }, + { + "name":"中原区", + "code":"410102" + }, + { + "name":"二七区", + "code":"410103" + }, + { + "name":"管城回族区", + "code":"410104" + }, + { + "name":"金水区", + "code":"410105" + }, + { + "name":"上街区", + "code":"410106" + }, + { + "name":"惠济区", + "code":"410108" + }, + { + "name":"中牟县", + "code":"410122" + }, + { + "name":"巩义市", + "code":"410181" + }, + { + "name":"荥阳市", + "code":"410182" + }, + { + "name":"新密市", + "code":"410183" + }, + { + "name":"新郑市", + "code":"410184" + }, + { + "name":"登封市", + "code":"410185" + } + ] + }, + { + "name":"开封市", + "code":"410200", + "sub":[ + { + "name":"市辖区", + "code":"410201" + }, + { + "name":"龙亭区", + "code":"410202" + }, + { + "name":"顺河回族区", + "code":"410203" + }, + { + "name":"鼓楼区", + "code":"410204" + }, + { + "name":"禹王台区", + "code":"410205" + }, + { + "name":"祥符区", + "code":"410212" + }, + { + "name":"杞县", + "code":"410221" + }, + { + "name":"通许县", + "code":"410222" + }, + { + "name":"尉氏县", + "code":"410223" + }, + { + "name":"兰考县", + "code":"410225" + } + ] + }, + { + "name":"洛阳市", + "code":"410300", + "sub":[ + { + "name":"市辖区", + "code":"410301" + }, + { + "name":"老城区", + "code":"410302" + }, + { + "name":"西工区", + "code":"410303" + }, + { + "name":"瀍河回族区", + "code":"410304" + }, + { + "name":"涧西区", + "code":"410305" + }, + { + "name":"吉利区", + "code":"410306" + }, + { + "name":"洛龙区", + "code":"410311" + }, + { + "name":"孟津县", + "code":"410322" + }, + { + "name":"新安县", + "code":"410323" + }, + { + "name":"栾川县", + "code":"410324" + }, + { + "name":"嵩县", + "code":"410325" + }, + { + "name":"汝阳县", + "code":"410326" + }, + { + "name":"宜阳县", + "code":"410327" + }, + { + "name":"洛宁县", + "code":"410328" + }, + { + "name":"伊川县", + "code":"410329" + }, + { + "name":"偃师市", + "code":"410381" + } + ] + }, + { + "name":"平顶山市", + "code":"410400", + "sub":[ + { + "name":"市辖区", + "code":"410401" + }, + { + "name":"新华区", + "code":"410402" + }, + { + "name":"卫东区", + "code":"410403" + }, + { + "name":"石龙区", + "code":"410404" + }, + { + "name":"湛河区", + "code":"410411" + }, + { + "name":"宝丰县", + "code":"410421" + }, + { + "name":"叶县", + "code":"410422" + }, + { + "name":"鲁山县", + "code":"410423" + }, + { + "name":"郏县", + "code":"410425" + }, + { + "name":"舞钢市", + "code":"410481" + }, + { + "name":"汝州市", + "code":"410482" + } + ] + }, + { + "name":"安阳市", + "code":"410500", + "sub":[ + { + "name":"市辖区", + "code":"410501" + }, + { + "name":"文峰区", + "code":"410502" + }, + { + "name":"北关区", + "code":"410503" + }, + { + "name":"殷都区", + "code":"410505" + }, + { + "name":"龙安区", + "code":"410506" + }, + { + "name":"安阳县", + "code":"410522" + }, + { + "name":"汤阴县", + "code":"410523" + }, + { + "name":"滑县", + "code":"410526" + }, + { + "name":"内黄县", + "code":"410527" + }, + { + "name":"林州市", + "code":"410581" + } + ] + }, + { + "name":"鹤壁市", + "code":"410600", + "sub":[ + { + "name":"市辖区", + "code":"410601" + }, + { + "name":"鹤山区", + "code":"410602" + }, + { + "name":"山城区", + "code":"410603" + }, + { + "name":"淇滨区", + "code":"410611" + }, + { + "name":"浚县", + "code":"410621" + }, + { + "name":"淇县", + "code":"410622" + } + ] + }, + { + "name":"新乡市", + "code":"410700", + "sub":[ + { + "name":"市辖区", + "code":"410701" + }, + { + "name":"红旗区", + "code":"410702" + }, + { + "name":"卫滨区", + "code":"410703" + }, + { + "name":"凤泉区", + "code":"410704" + }, + { + "name":"牧野区", + "code":"410711" + }, + { + "name":"新乡县", + "code":"410721" + }, + { + "name":"获嘉县", + "code":"410724" + }, + { + "name":"原阳县", + "code":"410725" + }, + { + "name":"延津县", + "code":"410726" + }, + { + "name":"封丘县", + "code":"410727" + }, + { + "name":"长垣县", + "code":"410728" + }, + { + "name":"卫辉市", + "code":"410781" + }, + { + "name":"辉县市", + "code":"410782" + } + ] + }, + { + "name":"焦作市", + "code":"410800", + "sub":[ + { + "name":"市辖区", + "code":"410801" + }, + { + "name":"解放区", + "code":"410802" + }, + { + "name":"中站区", + "code":"410803" + }, + { + "name":"马村区", + "code":"410804" + }, + { + "name":"山阳区", + "code":"410811" + }, + { + "name":"修武县", + "code":"410821" + }, + { + "name":"博爱县", + "code":"410822" + }, + { + "name":"武陟县", + "code":"410823" + }, + { + "name":"温县", + "code":"410825" + }, + { + "name":"沁阳市", + "code":"410882" + }, + { + "name":"孟州市", + "code":"410883" + } + ] + }, + { + "name":"濮阳市", + "code":"410900", + "sub":[ + { + "name":"市辖区", + "code":"410901" + }, + { + "name":"华龙区", + "code":"410902" + }, + { + "name":"清丰县", + "code":"410922" + }, + { + "name":"南乐县", + "code":"410923" + }, + { + "name":"范县", + "code":"410926" + }, + { + "name":"台前县", + "code":"410927" + }, + { + "name":"濮阳县", + "code":"410928" + } + ] + }, + { + "name":"许昌市", + "code":"411000", + "sub":[ + { + "name":"市辖区", + "code":"411001" + }, + { + "name":"魏都区", + "code":"411002" + }, + { + "name":"许昌县", + "code":"411023" + }, + { + "name":"鄢陵县", + "code":"411024" + }, + { + "name":"襄城县", + "code":"411025" + }, + { + "name":"禹州市", + "code":"411081" + }, + { + "name":"长葛市", + "code":"411082" + } + ] + }, + { + "name":"漯河市", + "code":"411100", + "sub":[ + { + "name":"市辖区", + "code":"411101" + }, + { + "name":"源汇区", + "code":"411102" + }, + { + "name":"郾城区", + "code":"411103" + }, + { + "name":"召陵区", + "code":"411104" + }, + { + "name":"舞阳县", + "code":"411121" + }, + { + "name":"临颍县", + "code":"411122" + } + ] + }, + { + "name":"三门峡市", + "code":"411200", + "sub":[ + { + "name":"市辖区", + "code":"411201" + }, + { + "name":"湖滨区", + "code":"411202" + }, + { + "name":"渑池县", + "code":"411221" + }, + { + "name":"陕县", + "code":"411222" + }, + { + "name":"卢氏县", + "code":"411224" + }, + { + "name":"义马市", + "code":"411281" + }, + { + "name":"灵宝市", + "code":"411282" + } + ] + }, + { + "name":"南阳市", + "code":"411300", + "sub":[ + { + "name":"市辖区", + "code":"411301" + }, + { + "name":"宛城区", + "code":"411302" + }, + { + "name":"卧龙区", + "code":"411303" + }, + { + "name":"南召县", + "code":"411321" + }, + { + "name":"方城县", + "code":"411322" + }, + { + "name":"西峡县", + "code":"411323" + }, + { + "name":"镇平县", + "code":"411324" + }, + { + "name":"内乡县", + "code":"411325" + }, + { + "name":"淅川县", + "code":"411326" + }, + { + "name":"社旗县", + "code":"411327" + }, + { + "name":"唐河县", + "code":"411328" + }, + { + "name":"新野县", + "code":"411329" + }, + { + "name":"桐柏县", + "code":"411330" + }, + { + "name":"邓州市", + "code":"411381" + } + ] + }, + { + "name":"商丘市", + "code":"411400", + "sub":[ + { + "name":"市辖区", + "code":"411401" + }, + { + "name":"梁园区", + "code":"411402" + }, + { + "name":"睢阳区", + "code":"411403" + }, + { + "name":"民权县", + "code":"411421" + }, + { + "name":"睢县", + "code":"411422" + }, + { + "name":"宁陵县", + "code":"411423" + }, + { + "name":"柘城县", + "code":"411424" + }, + { + "name":"虞城县", + "code":"411425" + }, + { + "name":"夏邑县", + "code":"411426" + }, + { + "name":"永城市", + "code":"411481" + } + ] + }, + { + "name":"信阳市", + "code":"411500", + "sub":[ + { + "name":"市辖区", + "code":"411501" + }, + { + "name":"浉河区", + "code":"411502" + }, + { + "name":"平桥区", + "code":"411503" + }, + { + "name":"罗山县", + "code":"411521" + }, + { + "name":"光山县", + "code":"411522" + }, + { + "name":"新县", + "code":"411523" + }, + { + "name":"商城县", + "code":"411524" + }, + { + "name":"固始县", + "code":"411525" + }, + { + "name":"潢川县", + "code":"411526" + }, + { + "name":"淮滨县", + "code":"411527" + }, + { + "name":"息县", + "code":"411528" + } + ] + }, + { + "name":"周口市", + "code":"411600", + "sub":[ + { + "name":"市辖区", + "code":"411601" + }, + { + "name":"川汇区", + "code":"411602" + }, + { + "name":"扶沟县", + "code":"411621" + }, + { + "name":"西华县", + "code":"411622" + }, + { + "name":"商水县", + "code":"411623" + }, + { + "name":"沈丘县", + "code":"411624" + }, + { + "name":"郸城县", + "code":"411625" + }, + { + "name":"淮阳县", + "code":"411626" + }, + { + "name":"太康县", + "code":"411627" + }, + { + "name":"鹿邑县", + "code":"411628" + }, + { + "name":"项城市", + "code":"411681" + } + ] + }, + { + "name":"驻马店市", + "code":"411700", + "sub":[ + { + "name":"市辖区", + "code":"411701" + }, + { + "name":"驿城区", + "code":"411702" + }, + { + "name":"西平县", + "code":"411721" + }, + { + "name":"上蔡县", + "code":"411722" + }, + { + "name":"平舆县", + "code":"411723" + }, + { + "name":"正阳县", + "code":"411724" + }, + { + "name":"确山县", + "code":"411725" + }, + { + "name":"泌阳县", + "code":"411726" + }, + { + "name":"汝南县", + "code":"411727" + }, + { + "name":"遂平县", + "code":"411728" + }, + { + "name":"新蔡县", + "code":"411729" + } + ] + }, + { + "name":"济源市", + "code":"419001" + } + ] + }, + { + "name":"湖北省", + "code":"420000", + "sub":[ + { + "name":"武汉市", + "code":"420100", + "sub":[ + { + "name":"市辖区", + "code":"420101" + }, + { + "name":"江岸区", + "code":"420102" + }, + { + "name":"江汉区", + "code":"420103" + }, + { + "name":"硚口区", + "code":"420104" + }, + { + "name":"汉阳区", + "code":"420105" + }, + { + "name":"武昌区", + "code":"420106" + }, + { + "name":"青山区", + "code":"420107" + }, + { + "name":"洪山区", + "code":"420111" + }, + { + "name":"东西湖区", + "code":"420112" + }, + { + "name":"汉南区", + "code":"420113" + }, + { + "name":"蔡甸区", + "code":"420114" + }, + { + "name":"江夏区", + "code":"420115" + }, + { + "name":"黄陂区", + "code":"420116" + }, + { + "name":"新洲区", + "code":"420117" + } + ] + }, + { + "name":"黄石市", + "code":"420200", + "sub":[ + { + "name":"市辖区", + "code":"420201" + }, + { + "name":"黄石港区", + "code":"420202" + }, + { + "name":"西塞山区", + "code":"420203" + }, + { + "name":"下陆区", + "code":"420204" + }, + { + "name":"铁山区", + "code":"420205" + }, + { + "name":"阳新县", + "code":"420222" + }, + { + "name":"大冶市", + "code":"420281" + } + ] + }, + { + "name":"十堰市", + "code":"420300", + "sub":[ + { + "name":"市辖区", + "code":"420301" + }, + { + "name":"茅箭区", + "code":"420302" + }, + { + "name":"张湾区", + "code":"420303" + }, + { + "name":"郧阳区", + "code":"420304" + }, + { + "name":"郧西县", + "code":"420322" + }, + { + "name":"竹山县", + "code":"420323" + }, + { + "name":"竹溪县", + "code":"420324" + }, + { + "name":"房县", + "code":"420325" + }, + { + "name":"丹江口市", + "code":"420381" + } + ] + }, + { + "name":"宜昌市", + "code":"420500", + "sub":[ + { + "name":"市辖区", + "code":"420501" + }, + { + "name":"西陵区", + "code":"420502" + }, + { + "name":"伍家岗区", + "code":"420503" + }, + { + "name":"点军区", + "code":"420504" + }, + { + "name":"猇亭区", + "code":"420505" + }, + { + "name":"夷陵区", + "code":"420506" + }, + { + "name":"远安县", + "code":"420525" + }, + { + "name":"兴山县", + "code":"420526" + }, + { + "name":"秭归县", + "code":"420527" + }, + { + "name":"长阳土家族自治县", + "code":"420528" + }, + { + "name":"五峰土家族自治县", + "code":"420529" + }, + { + "name":"宜都市", + "code":"420581" + }, + { + "name":"当阳市", + "code":"420582" + }, + { + "name":"枝江市", + "code":"420583" + } + ] + }, + { + "name":"襄阳市", + "code":"420600", + "sub":[ + { + "name":"市辖区", + "code":"420601" + }, + { + "name":"襄城区", + "code":"420602" + }, + { + "name":"樊城区", + "code":"420606" + }, + { + "name":"襄州区", + "code":"420607" + }, + { + "name":"南漳县", + "code":"420624" + }, + { + "name":"谷城县", + "code":"420625" + }, + { + "name":"保康县", + "code":"420626" + }, + { + "name":"老河口市", + "code":"420682" + }, + { + "name":"枣阳市", + "code":"420683" + }, + { + "name":"宜城市", + "code":"420684" + } + ] + }, + { + "name":"鄂州市", + "code":"420700", + "sub":[ + { + "name":"市辖区", + "code":"420701" + }, + { + "name":"梁子湖区", + "code":"420702" + }, + { + "name":"华容区", + "code":"420703" + }, + { + "name":"鄂城区", + "code":"420704" + } + ] + }, + { + "name":"荆门市", + "code":"420800", + "sub":[ + { + "name":"市辖区", + "code":"420801" + }, + { + "name":"东宝区", + "code":"420802" + }, + { + "name":"掇刀区", + "code":"420804" + }, + { + "name":"京山县", + "code":"420821" + }, + { + "name":"沙洋县", + "code":"420822" + }, + { + "name":"钟祥市", + "code":"420881" + } + ] + }, + { + "name":"孝感市", + "code":"420900", + "sub":[ + { + "name":"市辖区", + "code":"420901" + }, + { + "name":"孝南区", + "code":"420902" + }, + { + "name":"孝昌县", + "code":"420921" + }, + { + "name":"大悟县", + "code":"420922" + }, + { + "name":"云梦县", + "code":"420923" + }, + { + "name":"应城市", + "code":"420981" + }, + { + "name":"安陆市", + "code":"420982" + }, + { + "name":"汉川市", + "code":"420984" + } + ] + }, + { + "name":"荆州市", + "code":"421000", + "sub":[ + { + "name":"市辖区", + "code":"421001" + }, + { + "name":"沙市区", + "code":"421002" + }, + { + "name":"荆州区", + "code":"421003" + }, + { + "name":"公安县", + "code":"421022" + }, + { + "name":"监利县", + "code":"421023" + }, + { + "name":"江陵县", + "code":"421024" + }, + { + "name":"石首市", + "code":"421081" + }, + { + "name":"洪湖市", + "code":"421083" + }, + { + "name":"松滋市", + "code":"421087" + } + ] + }, + { + "name":"黄冈市", + "code":"421100", + "sub":[ + { + "name":"市辖区", + "code":"421101" + }, + { + "name":"黄州区", + "code":"421102" + }, + { + "name":"团风县", + "code":"421121" + }, + { + "name":"红安县", + "code":"421122" + }, + { + "name":"罗田县", + "code":"421123" + }, + { + "name":"英山县", + "code":"421124" + }, + { + "name":"浠水县", + "code":"421125" + }, + { + "name":"蕲春县", + "code":"421126" + }, + { + "name":"黄梅县", + "code":"421127" + }, + { + "name":"麻城市", + "code":"421181" + }, + { + "name":"武穴市", + "code":"421182" + } + ] + }, + { + "name":"咸宁市", + "code":"421200", + "sub":[ + { + "name":"市辖区", + "code":"421201" + }, + { + "name":"咸安区", + "code":"421202" + }, + { + "name":"嘉鱼县", + "code":"421221" + }, + { + "name":"通城县", + "code":"421222" + }, + { + "name":"崇阳县", + "code":"421223" + }, + { + "name":"通山县", + "code":"421224" + }, + { + "name":"赤壁市", + "code":"421281" + } + ] + }, + { + "name":"随州市", + "code":"421300", + "sub":[ + { + "name":"市辖区", + "code":"421301" + }, + { + "name":"曾都区", + "code":"421303" + }, + { + "name":"随县", + "code":"421321" + }, + { + "name":"广水市", + "code":"421381" + } + ] + }, + { + "name":"恩施土家族苗族自治州", + "code":"422800", + "sub":[ + { + "name":"恩施市", + "code":"422801" + }, + { + "name":"利川市", + "code":"422802" + }, + { + "name":"建始县", + "code":"422822" + }, + { + "name":"巴东县", + "code":"422823" + }, + { + "name":"宣恩县", + "code":"422825" + }, + { + "name":"咸丰县", + "code":"422826" + }, + { + "name":"来凤县", + "code":"422827" + }, + { + "name":"鹤峰县", + "code":"422828" + } + ] + }, + { + "name":"仙桃市", + "code":"429004" + }, + { + "name":"潜江市", + "code":"429005" + }, + { + "name":"天门市", + "code":"429006" + }, + { + "name":"神农架林区", + "code":"429021" + } + ] + }, + { + "name":"湖南省", + "code":"430000", + "sub":[ + { + "name":"长沙市", + "code":"430100", + "sub":[ + { + "name":"市辖区", + "code":"430101" + }, + { + "name":"芙蓉区", + "code":"430102" + }, + { + "name":"天心区", + "code":"430103" + }, + { + "name":"岳麓区", + "code":"430104" + }, + { + "name":"开福区", + "code":"430105" + }, + { + "name":"雨花区", + "code":"430111" + }, + { + "name":"望城区", + "code":"430112" + }, + { + "name":"长沙县", + "code":"430121" + }, + { + "name":"宁乡县", + "code":"430124" + }, + { + "name":"浏阳市", + "code":"430181" + } + ] + }, + { + "name":"株洲市", + "code":"430200", + "sub":[ + { + "name":"市辖区", + "code":"430201" + }, + { + "name":"荷塘区", + "code":"430202" + }, + { + "name":"芦淞区", + "code":"430203" + }, + { + "name":"石峰区", + "code":"430204" + }, + { + "name":"天元区", + "code":"430211" + }, + { + "name":"株洲县", + "code":"430221" + }, + { + "name":"攸县", + "code":"430223" + }, + { + "name":"茶陵县", + "code":"430224" + }, + { + "name":"炎陵县", + "code":"430225" + }, + { + "name":"醴陵市", + "code":"430281" + } + ] + }, + { + "name":"湘潭市", + "code":"430300", + "sub":[ + { + "name":"市辖区", + "code":"430301" + }, + { + "name":"雨湖区", + "code":"430302" + }, + { + "name":"岳塘区", + "code":"430304" + }, + { + "name":"湘潭县", + "code":"430321" + }, + { + "name":"湘乡市", + "code":"430381" + }, + { + "name":"韶山市", + "code":"430382" + } + ] + }, + { + "name":"衡阳市", + "code":"430400", + "sub":[ + { + "name":"市辖区", + "code":"430401" + }, + { + "name":"珠晖区", + "code":"430405" + }, + { + "name":"雁峰区", + "code":"430406" + }, + { + "name":"石鼓区", + "code":"430407" + }, + { + "name":"蒸湘区", + "code":"430408" + }, + { + "name":"南岳区", + "code":"430412" + }, + { + "name":"衡阳县", + "code":"430421" + }, + { + "name":"衡南县", + "code":"430422" + }, + { + "name":"衡山县", + "code":"430423" + }, + { + "name":"衡东县", + "code":"430424" + }, + { + "name":"祁东县", + "code":"430426" + }, + { + "name":"耒阳市", + "code":"430481" + }, + { + "name":"常宁市", + "code":"430482" + } + ] + }, + { + "name":"邵阳市", + "code":"430500", + "sub":[ + { + "name":"市辖区", + "code":"430501" + }, + { + "name":"双清区", + "code":"430502" + }, + { + "name":"大祥区", + "code":"430503" + }, + { + "name":"北塔区", + "code":"430511" + }, + { + "name":"邵东县", + "code":"430521" + }, + { + "name":"新邵县", + "code":"430522" + }, + { + "name":"邵阳县", + "code":"430523" + }, + { + "name":"隆回县", + "code":"430524" + }, + { + "name":"洞口县", + "code":"430525" + }, + { + "name":"绥宁县", + "code":"430527" + }, + { + "name":"新宁县", + "code":"430528" + }, + { + "name":"城步苗族自治县", + "code":"430529" + }, + { + "name":"武冈市", + "code":"430581" + } + ] + }, + { + "name":"岳阳市", + "code":"430600", + "sub":[ + { + "name":"市辖区", + "code":"430601" + }, + { + "name":"岳阳楼区", + "code":"430602" + }, + { + "name":"云溪区", + "code":"430603" + }, + { + "name":"君山区", + "code":"430611" + }, + { + "name":"岳阳县", + "code":"430621" + }, + { + "name":"华容县", + "code":"430623" + }, + { + "name":"湘阴县", + "code":"430624" + }, + { + "name":"平江县", + "code":"430626" + }, + { + "name":"汨罗市", + "code":"430681" + }, + { + "name":"临湘市", + "code":"430682" + } + ] + }, + { + "name":"常德市", + "code":"430700", + "sub":[ + { + "name":"市辖区", + "code":"430701" + }, + { + "name":"武陵区", + "code":"430702" + }, + { + "name":"鼎城区", + "code":"430703" + }, + { + "name":"安乡县", + "code":"430721" + }, + { + "name":"汉寿县", + "code":"430722" + }, + { + "name":"澧县", + "code":"430723" + }, + { + "name":"临澧县", + "code":"430724" + }, + { + "name":"桃源县", + "code":"430725" + }, + { + "name":"石门县", + "code":"430726" + }, + { + "name":"津市市", + "code":"430781" + } + ] + }, + { + "name":"张家界市", + "code":"430800", + "sub":[ + { + "name":"市辖区", + "code":"430801" + }, + { + "name":"永定区", + "code":"430802" + }, + { + "name":"武陵源区", + "code":"430811" + }, + { + "name":"慈利县", + "code":"430821" + }, + { + "name":"桑植县", + "code":"430822" + } + ] + }, + { + "name":"益阳市", + "code":"430900", + "sub":[ + { + "name":"市辖区", + "code":"430901" + }, + { + "name":"资阳区", + "code":"430902" + }, + { + "name":"赫山区", + "code":"430903" + }, + { + "name":"南县", + "code":"430921" + }, + { + "name":"桃江县", + "code":"430922" + }, + { + "name":"安化县", + "code":"430923" + }, + { + "name":"沅江市", + "code":"430981" + } + ] + }, + { + "name":"郴州市", + "code":"431000", + "sub":[ + { + "name":"市辖区", + "code":"431001" + }, + { + "name":"北湖区", + "code":"431002" + }, + { + "name":"苏仙区", + "code":"431003" + }, + { + "name":"桂阳县", + "code":"431021" + }, + { + "name":"宜章县", + "code":"431022" + }, + { + "name":"永兴县", + "code":"431023" + }, + { + "name":"嘉禾县", + "code":"431024" + }, + { + "name":"临武县", + "code":"431025" + }, + { + "name":"汝城县", + "code":"431026" + }, + { + "name":"桂东县", + "code":"431027" + }, + { + "name":"安仁县", + "code":"431028" + }, + { + "name":"资兴市", + "code":"431081" + } + ] + }, + { + "name":"永州市", + "code":"431100", + "sub":[ + { + "name":"市辖区", + "code":"431101" + }, + { + "name":"零陵区", + "code":"431102" + }, + { + "name":"冷水滩区", + "code":"431103" + }, + { + "name":"祁阳县", + "code":"431121" + }, + { + "name":"东安县", + "code":"431122" + }, + { + "name":"双牌县", + "code":"431123" + }, + { + "name":"道县", + "code":"431124" + }, + { + "name":"江永县", + "code":"431125" + }, + { + "name":"宁远县", + "code":"431126" + }, + { + "name":"蓝山县", + "code":"431127" + }, + { + "name":"新田县", + "code":"431128" + }, + { + "name":"江华瑶族自治县", + "code":"431129" + } + ] + }, + { + "name":"怀化市", + "code":"431200", + "sub":[ + { + "name":"市辖区", + "code":"431201" + }, + { + "name":"鹤城区", + "code":"431202" + }, + { + "name":"中方县", + "code":"431221" + }, + { + "name":"沅陵县", + "code":"431222" + }, + { + "name":"辰溪县", + "code":"431223" + }, + { + "name":"溆浦县", + "code":"431224" + }, + { + "name":"会同县", + "code":"431225" + }, + { + "name":"麻阳苗族自治县", + "code":"431226" + }, + { + "name":"新晃侗族自治县", + "code":"431227" + }, + { + "name":"芷江侗族自治县", + "code":"431228" + }, + { + "name":"靖州苗族侗族自治县", + "code":"431229" + }, + { + "name":"通道侗族自治县", + "code":"431230" + }, + { + "name":"洪江市", + "code":"431281" + } + ] + }, + { + "name":"娄底市", + "code":"431300", + "sub":[ + { + "name":"市辖区", + "code":"431301" + }, + { + "name":"娄星区", + "code":"431302" + }, + { + "name":"双峰县", + "code":"431321" + }, + { + "name":"新化县", + "code":"431322" + }, + { + "name":"冷水江市", + "code":"431381" + }, + { + "name":"涟源市", + "code":"431382" + } + ] + }, + { + "name":"湘西土家族苗族自治州", + "code":"433100", + "sub":[ + { + "name":"吉首市", + "code":"433101" + }, + { + "name":"泸溪县", + "code":"433122" + }, + { + "name":"凤凰县", + "code":"433123" + }, + { + "name":"花垣县", + "code":"433124" + }, + { + "name":"保靖县", + "code":"433125" + }, + { + "name":"古丈县", + "code":"433126" + }, + { + "name":"永顺县", + "code":"433127" + }, + { + "name":"龙山县", + "code":"433130" + } + ] + } + ] + }, + { + "name":"广东省", + "code":"440000", + "sub":[ + { + "name":"广州市", + "code":"440100", + "sub":[ + { + "name":"市辖区", + "code":"440101" + }, + { + "name":"荔湾区", + "code":"440103" + }, + { + "name":"越秀区", + "code":"440104" + }, + { + "name":"海珠区", + "code":"440105" + }, + { + "name":"天河区", + "code":"440106" + }, + { + "name":"白云区", + "code":"440111" + }, + { + "name":"黄埔区", + "code":"440112" + }, + { + "name":"番禺区", + "code":"440113" + }, + { + "name":"花都区", + "code":"440114" + }, + { + "name":"南沙区", + "code":"440115" + }, + { + "name":"从化区", + "code":"440117" + }, + { + "name":"增城区", + "code":"440118" + } + ] + }, + { + "name":"韶关市", + "code":"440200", + "sub":[ + { + "name":"市辖区", + "code":"440201" + }, + { + "name":"武江区", + "code":"440203" + }, + { + "name":"浈江区", + "code":"440204" + }, + { + "name":"曲江区", + "code":"440205" + }, + { + "name":"始兴县", + "code":"440222" + }, + { + "name":"仁化县", + "code":"440224" + }, + { + "name":"翁源县", + "code":"440229" + }, + { + "name":"乳源瑶族自治县", + "code":"440232" + }, + { + "name":"新丰县", + "code":"440233" + }, + { + "name":"乐昌市", + "code":"440281" + }, + { + "name":"南雄市", + "code":"440282" + } + ] + }, + { + "name":"深圳市", + "code":"440300", + "sub":[ + { + "name":"市辖区", + "code":"440301" + }, + { + "name":"罗湖区", + "code":"440303" + }, + { + "name":"福田区", + "code":"440304" + }, + { + "name":"南山区", + "code":"440305" + }, + { + "name":"宝安区", + "code":"440306" + }, + { + "name":"龙岗区", + "code":"440307" + }, + { + "name":"盐田区", + "code":"440308" + } + ] + }, + { + "name":"珠海市", + "code":"440400", + "sub":[ + { + "name":"市辖区", + "code":"440401" + }, + { + "name":"香洲区", + "code":"440402" + }, + { + "name":"斗门区", + "code":"440403" + }, + { + "name":"金湾区", + "code":"440404" + } + ] + }, + { + "name":"汕头市", + "code":"440500", + "sub":[ + { + "name":"市辖区", + "code":"440501" + }, + { + "name":"龙湖区", + "code":"440507" + }, + { + "name":"金平区", + "code":"440511" + }, + { + "name":"濠江区", + "code":"440512" + }, + { + "name":"潮阳区", + "code":"440513" + }, + { + "name":"潮南区", + "code":"440514" + }, + { + "name":"澄海区", + "code":"440515" + }, + { + "name":"南澳县", + "code":"440523" + } + ] + }, + { + "name":"佛山市", + "code":"440600", + "sub":[ + { + "name":"市辖区", + "code":"440601" + }, + { + "name":"禅城区", + "code":"440604" + }, + { + "name":"南海区", + "code":"440605" + }, + { + "name":"顺德区", + "code":"440606" + }, + { + "name":"三水区", + "code":"440607" + }, + { + "name":"高明区", + "code":"440608" + } + ] + }, + { + "name":"江门市", + "code":"440700", + "sub":[ + { + "name":"市辖区", + "code":"440701" + }, + { + "name":"蓬江区", + "code":"440703" + }, + { + "name":"江海区", + "code":"440704" + }, + { + "name":"新会区", + "code":"440705" + }, + { + "name":"台山市", + "code":"440781" + }, + { + "name":"开平市", + "code":"440783" + }, + { + "name":"鹤山市", + "code":"440784" + }, + { + "name":"恩平市", + "code":"440785" + } + ] + }, + { + "name":"湛江市", + "code":"440800", + "sub":[ + { + "name":"市辖区", + "code":"440801" + }, + { + "name":"赤坎区", + "code":"440802" + }, + { + "name":"霞山区", + "code":"440803" + }, + { + "name":"坡头区", + "code":"440804" + }, + { + "name":"麻章区", + "code":"440811" + }, + { + "name":"遂溪县", + "code":"440823" + }, + { + "name":"徐闻县", + "code":"440825" + }, + { + "name":"廉江市", + "code":"440881" + }, + { + "name":"雷州市", + "code":"440882" + }, + { + "name":"吴川市", + "code":"440883" + } + ] + }, + { + "name":"茂名市", + "code":"440900", + "sub":[ + { + "name":"市辖区", + "code":"440901" + }, + { + "name":"茂南区", + "code":"440902" + }, + { + "name":"电白区", + "code":"440904" + }, + { + "name":"高州市", + "code":"440981" + }, + { + "name":"化州市", + "code":"440982" + }, + { + "name":"信宜市", + "code":"440983" + } + ] + }, + { + "name":"肇庆市", + "code":"441200", + "sub":[ + { + "name":"市辖区", + "code":"441201" + }, + { + "name":"端州区", + "code":"441202" + }, + { + "name":"鼎湖区", + "code":"441203" + }, + { + "name":"广宁县", + "code":"441223" + }, + { + "name":"怀集县", + "code":"441224" + }, + { + "name":"封开县", + "code":"441225" + }, + { + "name":"德庆县", + "code":"441226" + }, + { + "name":"高要市", + "code":"441283" + }, + { + "name":"四会市", + "code":"441284" + } + ] + }, + { + "name":"惠州市", + "code":"441300", + "sub":[ + { + "name":"市辖区", + "code":"441301" + }, + { + "name":"惠城区", + "code":"441302" + }, + { + "name":"惠阳区", + "code":"441303" + }, + { + "name":"博罗县", + "code":"441322" + }, + { + "name":"惠东县", + "code":"441323" + }, + { + "name":"龙门县", + "code":"441324" + } + ] + }, + { + "name":"梅州市", + "code":"441400", + "sub":[ + { + "name":"市辖区", + "code":"441401" + }, + { + "name":"梅江区", + "code":"441402" + }, + { + "name":"梅县区", + "code":"441403" + }, + { + "name":"大埔县", + "code":"441422" + }, + { + "name":"丰顺县", + "code":"441423" + }, + { + "name":"五华县", + "code":"441424" + }, + { + "name":"平远县", + "code":"441426" + }, + { + "name":"蕉岭县", + "code":"441427" + }, + { + "name":"兴宁市", + "code":"441481" + } + ] + }, + { + "name":"汕尾市", + "code":"441500", + "sub":[ + { + "name":"市辖区", + "code":"441501" + }, + { + "name":"城区", + "code":"441502" + }, + { + "name":"海丰县", + "code":"441521" + }, + { + "name":"陆河县", + "code":"441523" + }, + { + "name":"陆丰市", + "code":"441581" + } + ] + }, + { + "name":"河源市", + "code":"441600", + "sub":[ + { + "name":"市辖区", + "code":"441601" + }, + { + "name":"源城区", + "code":"441602" + }, + { + "name":"紫金县", + "code":"441621" + }, + { + "name":"龙川县", + "code":"441622" + }, + { + "name":"连平县", + "code":"441623" + }, + { + "name":"和平县", + "code":"441624" + }, + { + "name":"东源县", + "code":"441625" + } + ] + }, + { + "name":"阳江市", + "code":"441700", + "sub":[ + { + "name":"市辖区", + "code":"441701" + }, + { + "name":"江城区", + "code":"441702" + }, + { + "name":"阳东区", + "code":"441704" + }, + { + "name":"阳西县", + "code":"441721" + }, + { + "name":"阳春市", + "code":"441781" + } + ] + }, + { + "name":"清远市", + "code":"441800", + "sub":[ + { + "name":"市辖区", + "code":"441801" + }, + { + "name":"清城区", + "code":"441802" + }, + { + "name":"清新区", + "code":"441803" + }, + { + "name":"佛冈县", + "code":"441821" + }, + { + "name":"阳山县", + "code":"441823" + }, + { + "name":"连山壮族瑶族自治县", + "code":"441825" + }, + { + "name":"连南瑶族自治县", + "code":"441826" + }, + { + "name":"英德市", + "code":"441881" + }, + { + "name":"连州市", + "code":"441882" + } + ] + }, + { + "name":"东莞市", + "code":"441900", + "sub":[ + + ] + }, + { + "name":"中山市", + "code":"442000", + "sub":[ + + ] + }, + { + "name":"潮州市", + "code":"445100", + "sub":[ + { + "name":"市辖区", + "code":"445101" + }, + { + "name":"湘桥区", + "code":"445102" + }, + { + "name":"潮安区", + "code":"445103" + }, + { + "name":"饶平县", + "code":"445122" + } + ] + }, + { + "name":"揭阳市", + "code":"445200", + "sub":[ + { + "name":"市辖区", + "code":"445201" + }, + { + "name":"榕城区", + "code":"445202" + }, + { + "name":"揭东区", + "code":"445203" + }, + { + "name":"揭西县", + "code":"445222" + }, + { + "name":"惠来县", + "code":"445224" + }, + { + "name":"普宁市", + "code":"445281" + } + ] + }, + { + "name":"云浮市", + "code":"445300", + "sub":[ + { + "name":"市辖区", + "code":"445301" + }, + { + "name":"云城区", + "code":"445302" + }, + { + "name":"云安区", + "code":"445303" + }, + { + "name":"新兴县", + "code":"445321" + }, + { + "name":"郁南县", + "code":"445322" + }, + { + "name":"罗定市", + "code":"445381" + } + ] + } + ] + }, + { + "name":"广西壮族自治区", + "code":"450000", + "sub":[ + { + "name":"南宁市", + "code":"450100", + "sub":[ + { + "name":"市辖区", + "code":"450101" + }, + { + "name":"兴宁区", + "code":"450102" + }, + { + "name":"青秀区", + "code":"450103" + }, + { + "name":"江南区", + "code":"450105" + }, + { + "name":"西乡塘区", + "code":"450107" + }, + { + "name":"良庆区", + "code":"450108" + }, + { + "name":"邕宁区", + "code":"450109" + }, + { + "name":"武鸣县", + "code":"450122" + }, + { + "name":"隆安县", + "code":"450123" + }, + { + "name":"马山县", + "code":"450124" + }, + { + "name":"上林县", + "code":"450125" + }, + { + "name":"宾阳县", + "code":"450126" + }, + { + "name":"横县", + "code":"450127" + } + ] + }, + { + "name":"柳州市", + "code":"450200", + "sub":[ + { + "name":"市辖区", + "code":"450201" + }, + { + "name":"城中区", + "code":"450202" + }, + { + "name":"鱼峰区", + "code":"450203" + }, + { + "name":"柳南区", + "code":"450204" + }, + { + "name":"柳北区", + "code":"450205" + }, + { + "name":"柳江县", + "code":"450221" + }, + { + "name":"柳城县", + "code":"450222" + }, + { + "name":"鹿寨县", + "code":"450223" + }, + { + "name":"融安县", + "code":"450224" + }, + { + "name":"融水苗族自治县", + "code":"450225" + }, + { + "name":"三江侗族自治县", + "code":"450226" + } + ] + }, + { + "name":"桂林市", + "code":"450300", + "sub":[ + { + "name":"市辖区", + "code":"450301" + }, + { + "name":"秀峰区", + "code":"450302" + }, + { + "name":"叠彩区", + "code":"450303" + }, + { + "name":"象山区", + "code":"450304" + }, + { + "name":"七星区", + "code":"450305" + }, + { + "name":"雁山区", + "code":"450311" + }, + { + "name":"临桂区", + "code":"450312" + }, + { + "name":"阳朔县", + "code":"450321" + }, + { + "name":"灵川县", + "code":"450323" + }, + { + "name":"全州县", + "code":"450324" + }, + { + "name":"兴安县", + "code":"450325" + }, + { + "name":"永福县", + "code":"450326" + }, + { + "name":"灌阳县", + "code":"450327" + }, + { + "name":"龙胜各族自治县", + "code":"450328" + }, + { + "name":"资源县", + "code":"450329" + }, + { + "name":"平乐县", + "code":"450330" + }, + { + "name":"荔浦县", + "code":"450331" + }, + { + "name":"恭城瑶族自治县", + "code":"450332" + } + ] + }, + { + "name":"梧州市", + "code":"450400", + "sub":[ + { + "name":"市辖区", + "code":"450401" + }, + { + "name":"万秀区", + "code":"450403" + }, + { + "name":"长洲区", + "code":"450405" + }, + { + "name":"龙圩区", + "code":"450406" + }, + { + "name":"苍梧县", + "code":"450421" + }, + { + "name":"藤县", + "code":"450422" + }, + { + "name":"蒙山县", + "code":"450423" + }, + { + "name":"岑溪市", + "code":"450481" + } + ] + }, + { + "name":"北海市", + "code":"450500", + "sub":[ + { + "name":"市辖区", + "code":"450501" + }, + { + "name":"海城区", + "code":"450502" + }, + { + "name":"银海区", + "code":"450503" + }, + { + "name":"铁山港区", + "code":"450512" + }, + { + "name":"合浦县", + "code":"450521" + } + ] + }, + { + "name":"防城港市", + "code":"450600", + "sub":[ + { + "name":"市辖区", + "code":"450601" + }, + { + "name":"港口区", + "code":"450602" + }, + { + "name":"防城区", + "code":"450603" + }, + { + "name":"上思县", + "code":"450621" + }, + { + "name":"东兴市", + "code":"450681" + } + ] + }, + { + "name":"钦州市", + "code":"450700", + "sub":[ + { + "name":"市辖区", + "code":"450701" + }, + { + "name":"钦南区", + "code":"450702" + }, + { + "name":"钦北区", + "code":"450703" + }, + { + "name":"灵山县", + "code":"450721" + }, + { + "name":"浦北县", + "code":"450722" + } + ] + }, + { + "name":"贵港市", + "code":"450800", + "sub":[ + { + "name":"市辖区", + "code":"450801" + }, + { + "name":"港北区", + "code":"450802" + }, + { + "name":"港南区", + "code":"450803" + }, + { + "name":"覃塘区", + "code":"450804" + }, + { + "name":"平南县", + "code":"450821" + }, + { + "name":"桂平市", + "code":"450881" + } + ] + }, + { + "name":"玉林市", + "code":"450900", + "sub":[ + { + "name":"市辖区", + "code":"450901" + }, + { + "name":"玉州区", + "code":"450902" + }, + { + "name":"福绵区", + "code":"450903" + }, + { + "name":"容县", + "code":"450921" + }, + { + "name":"陆川县", + "code":"450922" + }, + { + "name":"博白县", + "code":"450923" + }, + { + "name":"兴业县", + "code":"450924" + }, + { + "name":"北流市", + "code":"450981" + } + ] + }, + { + "name":"百色市", + "code":"451000", + "sub":[ + { + "name":"市辖区", + "code":"451001" + }, + { + "name":"右江区", + "code":"451002" + }, + { + "name":"田阳县", + "code":"451021" + }, + { + "name":"田东县", + "code":"451022" + }, + { + "name":"平果县", + "code":"451023" + }, + { + "name":"德保县", + "code":"451024" + }, + { + "name":"靖西县", + "code":"451025" + }, + { + "name":"那坡县", + "code":"451026" + }, + { + "name":"凌云县", + "code":"451027" + }, + { + "name":"乐业县", + "code":"451028" + }, + { + "name":"田林县", + "code":"451029" + }, + { + "name":"西林县", + "code":"451030" + }, + { + "name":"隆林各族自治县", + "code":"451031" + } + ] + }, + { + "name":"贺州市", + "code":"451100", + "sub":[ + { + "name":"市辖区", + "code":"451101" + }, + { + "name":"八步区", + "code":"451102" + }, + { + "name":"平桂管理区", + "code":"451119" + }, + { + "name":"昭平县", + "code":"451121" + }, + { + "name":"钟山县", + "code":"451122" + }, + { + "name":"富川瑶族自治县", + "code":"451123" + } + ] + }, + { + "name":"河池市", + "code":"451200", + "sub":[ + { + "name":"市辖区", + "code":"451201" + }, + { + "name":"金城江区", + "code":"451202" + }, + { + "name":"南丹县", + "code":"451221" + }, + { + "name":"天峨县", + "code":"451222" + }, + { + "name":"凤山县", + "code":"451223" + }, + { + "name":"东兰县", + "code":"451224" + }, + { + "name":"罗城仫佬族自治县", + "code":"451225" + }, + { + "name":"环江毛南族自治县", + "code":"451226" + }, + { + "name":"巴马瑶族自治县", + "code":"451227" + }, + { + "name":"都安瑶族自治县", + "code":"451228" + }, + { + "name":"大化瑶族自治县", + "code":"451229" + }, + { + "name":"宜州市", + "code":"451281" + } + ] + }, + { + "name":"来宾市", + "code":"451300", + "sub":[ + { + "name":"市辖区", + "code":"451301" + }, + { + "name":"兴宾区", + "code":"451302" + }, + { + "name":"忻城县", + "code":"451321" + }, + { + "name":"象州县", + "code":"451322" + }, + { + "name":"武宣县", + "code":"451323" + }, + { + "name":"金秀瑶族自治县", + "code":"451324" + }, + { + "name":"合山市", + "code":"451381" + } + ] + }, + { + "name":"崇左市", + "code":"451400", + "sub":[ + { + "name":"市辖区", + "code":"451401" + }, + { + "name":"江州区", + "code":"451402" + }, + { + "name":"扶绥县", + "code":"451421" + }, + { + "name":"宁明县", + "code":"451422" + }, + { + "name":"龙州县", + "code":"451423" + }, + { + "name":"大新县", + "code":"451424" + }, + { + "name":"天等县", + "code":"451425" + }, + { + "name":"凭祥市", + "code":"451481" + } + ] + } + ] + }, + { + "name":"海南省", + "code":"460000", + "sub":[ + { + "name":"海口市", + "code":"460100", + "sub":[ + { + "name":"市辖区", + "code":"460101" + }, + { + "name":"秀英区", + "code":"460105" + }, + { + "name":"龙华区", + "code":"460106" + }, + { + "name":"琼山区", + "code":"460107" + }, + { + "name":"美兰区", + "code":"460108" + } + ] + }, + { + "name":"三亚市", + "code":"460200", + "sub":[ + { + "name":"市辖区", + "code":"460201" + }, + { + "name":"海棠区", + "code":"460202" + }, + { + "name":"吉阳区", + "code":"460203" + }, + { + "name":"天涯区", + "code":"460204" + }, + { + "name":"崖州区", + "code":"460205" + } + ] + }, + { + "name":"三沙市", + "code":"460300", + "sub":[ + { + "name":"西沙群岛", + "code":"460321" + }, + { + "name":"南沙群岛", + "code":"460322" + }, + { + "name":"中沙群岛的岛礁及其海域", + "code":"460323" + } + ] + }, + { + "name":"五指山市", + "code":"469001" + }, + { + "name":"琼海市", + "code":"469002" + }, + { + "name":"儋州市", + "code":"469003" + }, + { + "name":"文昌市", + "code":"469005" + }, + { + "name":"万宁市", + "code":"469006" + }, + { + "name":"东方市", + "code":"469007" + }, + { + "name":"定安县", + "code":"469021" + }, + { + "name":"屯昌县", + "code":"469022" + }, + { + "name":"澄迈县", + "code":"469023" + }, + { + "name":"临高县", + "code":"469024" + }, + { + "name":"白沙黎族自治县", + "code":"469025" + }, + { + "name":"昌江黎族自治县", + "code":"469026" + }, + { + "name":"乐东黎族自治县", + "code":"469027" + }, + { + "name":"陵水黎族自治县", + "code":"469028" + }, + { + "name":"保亭黎族苗族自治县", + "code":"469029" + }, + { + "name":"琼中黎族苗族自治县", + "code":"469030" + } + ] + }, + { + "name":"重庆", + "code":"500000", + "sub": [ + { + "name": "重庆市", + "code": "500000", + "sub":[ + { + "name":"万州区", + "code":"500101" + }, + { + "name":"涪陵区", + "code":"500102" + }, + { + "name":"渝中区", + "code":"500103" + }, + { + "name":"大渡口区", + "code":"500104" + }, + { + "name":"江北区", + "code":"500105" + }, + { + "name":"沙坪坝区", + "code":"500106" + }, + { + "name":"九龙坡区", + "code":"500107" + }, + { + "name":"南岸区", + "code":"500108" + }, + { + "name":"北碚区", + "code":"500109" + }, + { + "name":"綦江区", + "code":"500110" + }, + { + "name":"大足区", + "code":"500111" + }, + { + "name":"渝北区", + "code":"500112" + }, + { + "name":"巴南区", + "code":"500113" + }, + { + "name":"黔江区", + "code":"500114" + }, + { + "name":"长寿区", + "code":"500115" + }, + { + "name":"江津区", + "code":"500116" + }, + { + "name":"合川区", + "code":"500117" + }, + { + "name":"永川区", + "code":"500118" + }, + { + "name":"南川区", + "code":"500119" + }, + { + "name":"璧山区", + "code":"500120" + }, + { + "name":"铜梁区", + "code":"500151" + }, + { + "name":"潼南县", + "code":"500223" + }, + { + "name":"荣昌县", + "code":"500226" + }, + { + "name":"梁平县", + "code":"500228" + }, + { + "name":"城口县", + "code":"500229" + }, + { + "name":"丰都县", + "code":"500230" + }, + { + "name":"垫江县", + "code":"500231" + }, + { + "name":"武隆县", + "code":"500232" + }, + { + "name":"忠县", + "code":"500233" + }, + { + "name":"开县", + "code":"500234" + }, + { + "name":"云阳县", + "code":"500235" + }, + { + "name":"奉节县", + "code":"500236" + }, + { + "name":"巫山县", + "code":"500237" + }, + { + "name":"巫溪县", + "code":"500238" + }, + { + "name":"石柱土家族自治县", + "code":"500240" + }, + { + "name":"秀山土家族苗族自治县", + "code":"500241" + }, + { + "name":"酉阳土家族苗族自治县", + "code":"500242" + }, + { + "name":"彭水苗族土家族自治县", + "code":"500243" + } + ] + } + ] + }, + { + "name":"四川省", + "code":"510000", + "sub":[ + { + "name":"成都市", + "code":"510100", + "sub":[ + { + "name":"市辖区", + "code":"510101" + }, + { + "name":"锦江区", + "code":"510104" + }, + { + "name":"青羊区", + "code":"510105" + }, + { + "name":"金牛区", + "code":"510106" + }, + { + "name":"武侯区", + "code":"510107" + }, + { + "name":"成华区", + "code":"510108" + }, + { + "name":"龙泉驿区", + "code":"510112" + }, + { + "name":"青白江区", + "code":"510113" + }, + { + "name":"新都区", + "code":"510114" + }, + { + "name":"温江区", + "code":"510115" + }, + { + "name":"金堂县", + "code":"510121" + }, + { + "name":"双流县", + "code":"510122" + }, + { + "name":"郫县", + "code":"510124" + }, + { + "name":"大邑县", + "code":"510129" + }, + { + "name":"蒲江县", + "code":"510131" + }, + { + "name":"新津县", + "code":"510132" + }, + { + "name":"都江堰市", + "code":"510181" + }, + { + "name":"彭州市", + "code":"510182" + }, + { + "name":"邛崃市", + "code":"510183" + }, + { + "name":"崇州市", + "code":"510184" + } + ] + }, + { + "name":"自贡市", + "code":"510300", + "sub":[ + { + "name":"市辖区", + "code":"510301" + }, + { + "name":"自流井区", + "code":"510302" + }, + { + "name":"贡井区", + "code":"510303" + }, + { + "name":"大安区", + "code":"510304" + }, + { + "name":"沿滩区", + "code":"510311" + }, + { + "name":"荣县", + "code":"510321" + }, + { + "name":"富顺县", + "code":"510322" + } + ] + }, + { + "name":"攀枝花市", + "code":"510400", + "sub":[ + { + "name":"市辖区", + "code":"510401" + }, + { + "name":"东区", + "code":"510402" + }, + { + "name":"西区", + "code":"510403" + }, + { + "name":"仁和区", + "code":"510411" + }, + { + "name":"米易县", + "code":"510421" + }, + { + "name":"盐边县", + "code":"510422" + } + ] + }, + { + "name":"泸州市", + "code":"510500", + "sub":[ + { + "name":"市辖区", + "code":"510501" + }, + { + "name":"江阳区", + "code":"510502" + }, + { + "name":"纳溪区", + "code":"510503" + }, + { + "name":"龙马潭区", + "code":"510504" + }, + { + "name":"泸县", + "code":"510521" + }, + { + "name":"合江县", + "code":"510522" + }, + { + "name":"叙永县", + "code":"510524" + }, + { + "name":"古蔺县", + "code":"510525" + } + ] + }, + { + "name":"德阳市", + "code":"510600", + "sub":[ + { + "name":"市辖区", + "code":"510601" + }, + { + "name":"旌阳区", + "code":"510603" + }, + { + "name":"中江县", + "code":"510623" + }, + { + "name":"罗江县", + "code":"510626" + }, + { + "name":"广汉市", + "code":"510681" + }, + { + "name":"什邡市", + "code":"510682" + }, + { + "name":"绵竹市", + "code":"510683" + } + ] + }, + { + "name":"绵阳市", + "code":"510700", + "sub":[ + { + "name":"市辖区", + "code":"510701" + }, + { + "name":"涪城区", + "code":"510703" + }, + { + "name":"游仙区", + "code":"510704" + }, + { + "name":"三台县", + "code":"510722" + }, + { + "name":"盐亭县", + "code":"510723" + }, + { + "name":"安县", + "code":"510724" + }, + { + "name":"梓潼县", + "code":"510725" + }, + { + "name":"北川羌族自治县", + "code":"510726" + }, + { + "name":"平武县", + "code":"510727" + }, + { + "name":"江油市", + "code":"510781" + } + ] + }, + { + "name":"广元市", + "code":"510800", + "sub":[ + { + "name":"市辖区", + "code":"510801" + }, + { + "name":"利州区", + "code":"510802" + }, + { + "name":"昭化区", + "code":"510811" + }, + { + "name":"朝天区", + "code":"510812" + }, + { + "name":"旺苍县", + "code":"510821" + }, + { + "name":"青川县", + "code":"510822" + }, + { + "name":"剑阁县", + "code":"510823" + }, + { + "name":"苍溪县", + "code":"510824" + } + ] + }, + { + "name":"遂宁市", + "code":"510900", + "sub":[ + { + "name":"市辖区", + "code":"510901" + }, + { + "name":"船山区", + "code":"510903" + }, + { + "name":"安居区", + "code":"510904" + }, + { + "name":"蓬溪县", + "code":"510921" + }, + { + "name":"射洪县", + "code":"510922" + }, + { + "name":"大英县", + "code":"510923" + } + ] + }, + { + "name":"内江市", + "code":"511000", + "sub":[ + { + "name":"市辖区", + "code":"511001" + }, + { + "name":"市中区", + "code":"511002" + }, + { + "name":"东兴区", + "code":"511011" + }, + { + "name":"威远县", + "code":"511024" + }, + { + "name":"资中县", + "code":"511025" + }, + { + "name":"隆昌县", + "code":"511028" + } + ] + }, + { + "name":"乐山市", + "code":"511100", + "sub":[ + { + "name":"市辖区", + "code":"511101" + }, + { + "name":"市中区", + "code":"511102" + }, + { + "name":"沙湾区", + "code":"511111" + }, + { + "name":"五通桥区", + "code":"511112" + }, + { + "name":"金口河区", + "code":"511113" + }, + { + "name":"犍为县", + "code":"511123" + }, + { + "name":"井研县", + "code":"511124" + }, + { + "name":"夹江县", + "code":"511126" + }, + { + "name":"沐川县", + "code":"511129" + }, + { + "name":"峨边彝族自治县", + "code":"511132" + }, + { + "name":"马边彝族自治县", + "code":"511133" + }, + { + "name":"峨眉山市", + "code":"511181" + } + ] + }, + { + "name":"南充市", + "code":"511300", + "sub":[ + { + "name":"市辖区", + "code":"511301" + }, + { + "name":"顺庆区", + "code":"511302" + }, + { + "name":"高坪区", + "code":"511303" + }, + { + "name":"嘉陵区", + "code":"511304" + }, + { + "name":"南部县", + "code":"511321" + }, + { + "name":"营山县", + "code":"511322" + }, + { + "name":"蓬安县", + "code":"511323" + }, + { + "name":"仪陇县", + "code":"511324" + }, + { + "name":"西充县", + "code":"511325" + }, + { + "name":"阆中市", + "code":"511381" + } + ] + }, + { + "name":"眉山市", + "code":"511400", + "sub":[ + { + "name":"市辖区", + "code":"511401" + }, + { + "name":"东坡区", + "code":"511402" + }, + { + "name":"彭山区", + "code":"511403" + }, + { + "name":"仁寿县", + "code":"511421" + }, + { + "name":"洪雅县", + "code":"511423" + }, + { + "name":"丹棱县", + "code":"511424" + }, + { + "name":"青神县", + "code":"511425" + } + ] + }, + { + "name":"宜宾市", + "code":"511500", + "sub":[ + { + "name":"市辖区", + "code":"511501" + }, + { + "name":"翠屏区", + "code":"511502" + }, + { + "name":"南溪区", + "code":"511503" + }, + { + "name":"宜宾县", + "code":"511521" + }, + { + "name":"江安县", + "code":"511523" + }, + { + "name":"长宁县", + "code":"511524" + }, + { + "name":"高县", + "code":"511525" + }, + { + "name":"珙县", + "code":"511526" + }, + { + "name":"筠连县", + "code":"511527" + }, + { + "name":"兴文县", + "code":"511528" + }, + { + "name":"屏山县", + "code":"511529" + } + ] + }, + { + "name":"广安市", + "code":"511600", + "sub":[ + { + "name":"市辖区", + "code":"511601" + }, + { + "name":"广安区", + "code":"511602" + }, + { + "name":"前锋区", + "code":"511603" + }, + { + "name":"岳池县", + "code":"511621" + }, + { + "name":"武胜县", + "code":"511622" + }, + { + "name":"邻水县", + "code":"511623" + }, + { + "name":"华蓥市", + "code":"511681" + } + ] + }, + { + "name":"达州市", + "code":"511700", + "sub":[ + { + "name":"市辖区", + "code":"511701" + }, + { + "name":"通川区", + "code":"511702" + }, + { + "name":"达川区", + "code":"511703" + }, + { + "name":"宣汉县", + "code":"511722" + }, + { + "name":"开江县", + "code":"511723" + }, + { + "name":"大竹县", + "code":"511724" + }, + { + "name":"渠县", + "code":"511725" + }, + { + "name":"万源市", + "code":"511781" + } + ] + }, + { + "name":"雅安市", + "code":"511800", + "sub":[ + { + "name":"市辖区", + "code":"511801" + }, + { + "name":"雨城区", + "code":"511802" + }, + { + "name":"名山区", + "code":"511803" + }, + { + "name":"荥经县", + "code":"511822" + }, + { + "name":"汉源县", + "code":"511823" + }, + { + "name":"石棉县", + "code":"511824" + }, + { + "name":"天全县", + "code":"511825" + }, + { + "name":"芦山县", + "code":"511826" + }, + { + "name":"宝兴县", + "code":"511827" + } + ] + }, + { + "name":"巴中市", + "code":"511900", + "sub":[ + { + "name":"市辖区", + "code":"511901" + }, + { + "name":"巴州区", + "code":"511902" + }, + { + "name":"恩阳区", + "code":"511903" + }, + { + "name":"通江县", + "code":"511921" + }, + { + "name":"南江县", + "code":"511922" + }, + { + "name":"平昌县", + "code":"511923" + } + ] + }, + { + "name":"资阳市", + "code":"512000", + "sub":[ + { + "name":"市辖区", + "code":"512001" + }, + { + "name":"雁江区", + "code":"512002" + }, + { + "name":"安岳县", + "code":"512021" + }, + { + "name":"乐至县", + "code":"512022" + }, + { + "name":"简阳市", + "code":"512081" + } + ] + }, + { + "name":"阿坝藏族羌族自治州", + "code":"513200", + "sub":[ + { + "name":"汶川县", + "code":"513221" + }, + { + "name":"理县", + "code":"513222" + }, + { + "name":"茂县", + "code":"513223" + }, + { + "name":"松潘县", + "code":"513224" + }, + { + "name":"九寨沟县", + "code":"513225" + }, + { + "name":"金川县", + "code":"513226" + }, + { + "name":"小金县", + "code":"513227" + }, + { + "name":"黑水县", + "code":"513228" + }, + { + "name":"马尔康县", + "code":"513229" + }, + { + "name":"壤塘县", + "code":"513230" + }, + { + "name":"阿坝县", + "code":"513231" + }, + { + "name":"若尔盖县", + "code":"513232" + }, + { + "name":"红原县", + "code":"513233" + } + ] + }, + { + "name":"甘孜藏族自治州", + "code":"513300", + "sub":[ + { + "name":"康定县", + "code":"513321" + }, + { + "name":"泸定县", + "code":"513322" + }, + { + "name":"丹巴县", + "code":"513323" + }, + { + "name":"九龙县", + "code":"513324" + }, + { + "name":"雅江县", + "code":"513325" + }, + { + "name":"道孚县", + "code":"513326" + }, + { + "name":"炉霍县", + "code":"513327" + }, + { + "name":"甘孜县", + "code":"513328" + }, + { + "name":"新龙县", + "code":"513329" + }, + { + "name":"德格县", + "code":"513330" + }, + { + "name":"白玉县", + "code":"513331" + }, + { + "name":"石渠县", + "code":"513332" + }, + { + "name":"色达县", + "code":"513333" + }, + { + "name":"理塘县", + "code":"513334" + }, + { + "name":"巴塘县", + "code":"513335" + }, + { + "name":"乡城县", + "code":"513336" + }, + { + "name":"稻城县", + "code":"513337" + }, + { + "name":"得荣县", + "code":"513338" + } + ] + }, + { + "name":"凉山彝族自治州", + "code":"513400", + "sub":[ + { + "name":"西昌市", + "code":"513401" + }, + { + "name":"木里藏族自治县", + "code":"513422" + }, + { + "name":"盐源县", + "code":"513423" + }, + { + "name":"德昌县", + "code":"513424" + }, + { + "name":"会理县", + "code":"513425" + }, + { + "name":"会东县", + "code":"513426" + }, + { + "name":"宁南县", + "code":"513427" + }, + { + "name":"普格县", + "code":"513428" + }, + { + "name":"布拖县", + "code":"513429" + }, + { + "name":"金阳县", + "code":"513430" + }, + { + "name":"昭觉县", + "code":"513431" + }, + { + "name":"喜德县", + "code":"513432" + }, + { + "name":"冕宁县", + "code":"513433" + }, + { + "name":"越西县", + "code":"513434" + }, + { + "name":"甘洛县", + "code":"513435" + }, + { + "name":"美姑县", + "code":"513436" + }, + { + "name":"雷波县", + "code":"513437" + } + ] + } + ] + }, + { + "name":"贵州省", + "code":"520000", + "sub":[ + { + "name":"贵阳市", + "code":"520100", + "sub":[ + { + "name":"市辖区", + "code":"520101" + }, + { + "name":"南明区", + "code":"520102" + }, + { + "name":"云岩区", + "code":"520103" + }, + { + "name":"花溪区", + "code":"520111" + }, + { + "name":"乌当区", + "code":"520112" + }, + { + "name":"白云区", + "code":"520113" + }, + { + "name":"观山湖区", + "code":"520115" + }, + { + "name":"开阳县", + "code":"520121" + }, + { + "name":"息烽县", + "code":"520122" + }, + { + "name":"修文县", + "code":"520123" + }, + { + "name":"清镇市", + "code":"520181" + } + ] + }, + { + "name":"六盘水市", + "code":"520200", + "sub":[ + { + "name":"钟山区", + "code":"520201" + }, + { + "name":"六枝特区", + "code":"520203" + }, + { + "name":"水城县", + "code":"520221" + }, + { + "name":"盘县", + "code":"520222" + } + ] + }, + { + "name":"遵义市", + "code":"520300", + "sub":[ + { + "name":"市辖区", + "code":"520301" + }, + { + "name":"红花岗区", + "code":"520302" + }, + { + "name":"汇川区", + "code":"520303" + }, + { + "name":"遵义县", + "code":"520321" + }, + { + "name":"桐梓县", + "code":"520322" + }, + { + "name":"绥阳县", + "code":"520323" + }, + { + "name":"正安县", + "code":"520324" + }, + { + "name":"道真仡佬族苗族自治县", + "code":"520325" + }, + { + "name":"务川仡佬族苗族自治县", + "code":"520326" + }, + { + "name":"凤冈县", + "code":"520327" + }, + { + "name":"湄潭县", + "code":"520328" + }, + { + "name":"余庆县", + "code":"520329" + }, + { + "name":"习水县", + "code":"520330" + }, + { + "name":"赤水市", + "code":"520381" + }, + { + "name":"仁怀市", + "code":"520382" + } + ] + }, + { + "name":"安顺市", + "code":"520400", + "sub":[ + { + "name":"市辖区", + "code":"520401" + }, + { + "name":"西秀区", + "code":"520402" + }, + { + "name":"平坝区", + "code":"520403" + }, + { + "name":"普定县", + "code":"520422" + }, + { + "name":"镇宁布依族苗族自治县", + "code":"520423" + }, + { + "name":"关岭布依族苗族自治县", + "code":"520424" + }, + { + "name":"紫云苗族布依族自治县", + "code":"520425" + } + ] + }, + { + "name":"毕节市", + "code":"520500", + "sub":[ + { + "name":"市辖区", + "code":"520501" + }, + { + "name":"七星关区", + "code":"520502" + }, + { + "name":"大方县", + "code":"520521" + }, + { + "name":"黔西县", + "code":"520522" + }, + { + "name":"金沙县", + "code":"520523" + }, + { + "name":"织金县", + "code":"520524" + }, + { + "name":"纳雍县", + "code":"520525" + }, + { + "name":"威宁彝族回族苗族自治县", + "code":"520526" + }, + { + "name":"赫章县", + "code":"520527" + } + ] + }, + { + "name":"铜仁市", + "code":"520600", + "sub":[ + { + "name":"市辖区", + "code":"520601" + }, + { + "name":"碧江区", + "code":"520602" + }, + { + "name":"万山区", + "code":"520603" + }, + { + "name":"江口县", + "code":"520621" + }, + { + "name":"玉屏侗族自治县", + "code":"520622" + }, + { + "name":"石阡县", + "code":"520623" + }, + { + "name":"思南县", + "code":"520624" + }, + { + "name":"印江土家族苗族自治县", + "code":"520625" + }, + { + "name":"德江县", + "code":"520626" + }, + { + "name":"沿河土家族自治县", + "code":"520627" + }, + { + "name":"松桃苗族自治县", + "code":"520628" + } + ] + }, + { + "name":"黔西南布依族苗族自治州", + "code":"522300", + "sub":[ + { + "name":"兴义市", + "code":"522301" + }, + { + "name":"兴仁县", + "code":"522322" + }, + { + "name":"普安县", + "code":"522323" + }, + { + "name":"晴隆县", + "code":"522324" + }, + { + "name":"贞丰县", + "code":"522325" + }, + { + "name":"望谟县", + "code":"522326" + }, + { + "name":"册亨县", + "code":"522327" + }, + { + "name":"安龙县", + "code":"522328" + } + ] + }, + { + "name":"黔东南苗族侗族自治州", + "code":"522600", + "sub":[ + { + "name":"凯里市", + "code":"522601" + }, + { + "name":"黄平县", + "code":"522622" + }, + { + "name":"施秉县", + "code":"522623" + }, + { + "name":"三穗县", + "code":"522624" + }, + { + "name":"镇远县", + "code":"522625" + }, + { + "name":"岑巩县", + "code":"522626" + }, + { + "name":"天柱县", + "code":"522627" + }, + { + "name":"锦屏县", + "code":"522628" + }, + { + "name":"剑河县", + "code":"522629" + }, + { + "name":"台江县", + "code":"522630" + }, + { + "name":"黎平县", + "code":"522631" + }, + { + "name":"榕江县", + "code":"522632" + }, + { + "name":"从江县", + "code":"522633" + }, + { + "name":"雷山县", + "code":"522634" + }, + { + "name":"麻江县", + "code":"522635" + }, + { + "name":"丹寨县", + "code":"522636" + } + ] + }, + { + "name":"黔南布依族苗族自治州", + "code":"522700", + "sub":[ + { + "name":"都匀市", + "code":"522701" + }, + { + "name":"福泉市", + "code":"522702" + }, + { + "name":"荔波县", + "code":"522722" + }, + { + "name":"贵定县", + "code":"522723" + }, + { + "name":"瓮安县", + "code":"522725" + }, + { + "name":"独山县", + "code":"522726" + }, + { + "name":"平塘县", + "code":"522727" + }, + { + "name":"罗甸县", + "code":"522728" + }, + { + "name":"长顺县", + "code":"522729" + }, + { + "name":"龙里县", + "code":"522730" + }, + { + "name":"惠水县", + "code":"522731" + }, + { + "name":"三都水族自治县", + "code":"522732" + } + ] + } + ] + }, + { + "name":"云南省", + "code":"530000", + "sub":[ + { + "name":"昆明市", + "code":"530100", + "sub":[ + { + "name":"市辖区", + "code":"530101" + }, + { + "name":"五华区", + "code":"530102" + }, + { + "name":"盘龙区", + "code":"530103" + }, + { + "name":"官渡区", + "code":"530111" + }, + { + "name":"西山区", + "code":"530112" + }, + { + "name":"东川区", + "code":"530113" + }, + { + "name":"呈贡区", + "code":"530114" + }, + { + "name":"晋宁县", + "code":"530122" + }, + { + "name":"富民县", + "code":"530124" + }, + { + "name":"宜良县", + "code":"530125" + }, + { + "name":"石林彝族自治县", + "code":"530126" + }, + { + "name":"嵩明县", + "code":"530127" + }, + { + "name":"禄劝彝族苗族自治县", + "code":"530128" + }, + { + "name":"寻甸回族彝族自治县", + "code":"530129" + }, + { + "name":"安宁市", + "code":"530181" + } + ] + }, + { + "name":"曲靖市", + "code":"530300", + "sub":[ + { + "name":"市辖区", + "code":"530301" + }, + { + "name":"麒麟区", + "code":"530302" + }, + { + "name":"马龙县", + "code":"530321" + }, + { + "name":"陆良县", + "code":"530322" + }, + { + "name":"师宗县", + "code":"530323" + }, + { + "name":"罗平县", + "code":"530324" + }, + { + "name":"富源县", + "code":"530325" + }, + { + "name":"会泽县", + "code":"530326" + }, + { + "name":"沾益县", + "code":"530328" + }, + { + "name":"宣威市", + "code":"530381" + } + ] + }, + { + "name":"玉溪市", + "code":"530400", + "sub":[ + { + "name":"市辖区", + "code":"530401" + }, + { + "name":"红塔区", + "code":"530402" + }, + { + "name":"江川县", + "code":"530421" + }, + { + "name":"澄江县", + "code":"530422" + }, + { + "name":"通海县", + "code":"530423" + }, + { + "name":"华宁县", + "code":"530424" + }, + { + "name":"易门县", + "code":"530425" + }, + { + "name":"峨山彝族自治县", + "code":"530426" + }, + { + "name":"新平彝族傣族自治县", + "code":"530427" + }, + { + "name":"元江哈尼族彝族傣族自治县", + "code":"530428" + } + ] + }, + { + "name":"保山市", + "code":"530500", + "sub":[ + { + "name":"市辖区", + "code":"530501" + }, + { + "name":"隆阳区", + "code":"530502" + }, + { + "name":"施甸县", + "code":"530521" + }, + { + "name":"腾冲县", + "code":"530522" + }, + { + "name":"龙陵县", + "code":"530523" + }, + { + "name":"昌宁县", + "code":"530524" + } + ] + }, + { + "name":"昭通市", + "code":"530600", + "sub":[ + { + "name":"市辖区", + "code":"530601" + }, + { + "name":"昭阳区", + "code":"530602" + }, + { + "name":"鲁甸县", + "code":"530621" + }, + { + "name":"巧家县", + "code":"530622" + }, + { + "name":"盐津县", + "code":"530623" + }, + { + "name":"大关县", + "code":"530624" + }, + { + "name":"永善县", + "code":"530625" + }, + { + "name":"绥江县", + "code":"530626" + }, + { + "name":"镇雄县", + "code":"530627" + }, + { + "name":"彝良县", + "code":"530628" + }, + { + "name":"威信县", + "code":"530629" + }, + { + "name":"水富县", + "code":"530630" + } + ] + }, + { + "name":"丽江市", + "code":"530700", + "sub":[ + { + "name":"市辖区", + "code":"530701" + }, + { + "name":"古城区", + "code":"530702" + }, + { + "name":"玉龙纳西族自治县", + "code":"530721" + }, + { + "name":"永胜县", + "code":"530722" + }, + { + "name":"华坪县", + "code":"530723" + }, + { + "name":"宁蒗彝族自治县", + "code":"530724" + } + ] + }, + { + "name":"普洱市", + "code":"530800", + "sub":[ + { + "name":"市辖区", + "code":"530801" + }, + { + "name":"思茅区", + "code":"530802" + }, + { + "name":"宁洱哈尼族彝族自治县", + "code":"530821" + }, + { + "name":"墨江哈尼族自治县", + "code":"530822" + }, + { + "name":"景东彝族自治县", + "code":"530823" + }, + { + "name":"景谷傣族彝族自治县", + "code":"530824" + }, + { + "name":"镇沅彝族哈尼族拉祜族自治县", + "code":"530825" + }, + { + "name":"江城哈尼族彝族自治县", + "code":"530826" + }, + { + "name":"孟连傣族拉祜族佤族自治县", + "code":"530827" + }, + { + "name":"澜沧拉祜族自治县", + "code":"530828" + }, + { + "name":"西盟佤族自治县", + "code":"530829" + } + ] + }, + { + "name":"临沧市", + "code":"530900", + "sub":[ + { + "name":"市辖区", + "code":"530901" + }, + { + "name":"临翔区", + "code":"530902" + }, + { + "name":"凤庆县", + "code":"530921" + }, + { + "name":"云县", + "code":"530922" + }, + { + "name":"永德县", + "code":"530923" + }, + { + "name":"镇康县", + "code":"530924" + }, + { + "name":"双江拉祜族佤族布朗族傣族自治县", + "code":"530925" + }, + { + "name":"耿马傣族佤族自治县", + "code":"530926" + }, + { + "name":"沧源佤族自治县", + "code":"530927" + } + ] + }, + { + "name":"楚雄彝族自治州", + "code":"532300", + "sub":[ + { + "name":"楚雄市", + "code":"532301" + }, + { + "name":"双柏县", + "code":"532322" + }, + { + "name":"牟定县", + "code":"532323" + }, + { + "name":"南华县", + "code":"532324" + }, + { + "name":"姚安县", + "code":"532325" + }, + { + "name":"大姚县", + "code":"532326" + }, + { + "name":"永仁县", + "code":"532327" + }, + { + "name":"元谋县", + "code":"532328" + }, + { + "name":"武定县", + "code":"532329" + }, + { + "name":"禄丰县", + "code":"532331" + } + ] + }, + { + "name":"红河哈尼族彝族自治州", + "code":"532500", + "sub":[ + { + "name":"个旧市", + "code":"532501" + }, + { + "name":"开远市", + "code":"532502" + }, + { + "name":"蒙自市", + "code":"532503" + }, + { + "name":"弥勒市", + "code":"532504" + }, + { + "name":"屏边苗族自治县", + "code":"532523" + }, + { + "name":"建水县", + "code":"532524" + }, + { + "name":"石屏县", + "code":"532525" + }, + { + "name":"泸西县", + "code":"532527" + }, + { + "name":"元阳县", + "code":"532528" + }, + { + "name":"红河县", + "code":"532529" + }, + { + "name":"金平苗族瑶族傣族自治县", + "code":"532530" + }, + { + "name":"绿春县", + "code":"532531" + }, + { + "name":"河口瑶族自治县", + "code":"532532" + } + ] + }, + { + "name":"文山壮族苗族自治州", + "code":"532600", + "sub":[ + { + "name":"文山市", + "code":"532601" + }, + { + "name":"砚山县", + "code":"532622" + }, + { + "name":"西畴县", + "code":"532623" + }, + { + "name":"麻栗坡县", + "code":"532624" + }, + { + "name":"马关县", + "code":"532625" + }, + { + "name":"丘北县", + "code":"532626" + }, + { + "name":"广南县", + "code":"532627" + }, + { + "name":"富宁县", + "code":"532628" + } + ] + }, + { + "name":"西双版纳傣族自治州", + "code":"532800", + "sub":[ + { + "name":"景洪市", + "code":"532801" + }, + { + "name":"勐海县", + "code":"532822" + }, + { + "name":"勐腊县", + "code":"532823" + } + ] + }, + { + "name":"大理白族自治州", + "code":"532900", + "sub":[ + { + "name":"大理市", + "code":"532901" + }, + { + "name":"漾濞彝族自治县", + "code":"532922" + }, + { + "name":"祥云县", + "code":"532923" + }, + { + "name":"宾川县", + "code":"532924" + }, + { + "name":"弥渡县", + "code":"532925" + }, + { + "name":"南涧彝族自治县", + "code":"532926" + }, + { + "name":"巍山彝族回族自治县", + "code":"532927" + }, + { + "name":"永平县", + "code":"532928" + }, + { + "name":"云龙县", + "code":"532929" + }, + { + "name":"洱源县", + "code":"532930" + }, + { + "name":"剑川县", + "code":"532931" + }, + { + "name":"鹤庆县", + "code":"532932" + } + ] + }, + { + "name":"德宏傣族景颇族自治州", + "code":"533100", + "sub":[ + { + "name":"瑞丽市", + "code":"533102" + }, + { + "name":"芒市", + "code":"533103" + }, + { + "name":"梁河县", + "code":"533122" + }, + { + "name":"盈江县", + "code":"533123" + }, + { + "name":"陇川县", + "code":"533124" + } + ] + }, + { + "name":"怒江傈僳族自治州", + "code":"533300", + "sub":[ + { + "name":"泸水县", + "code":"533321" + }, + { + "name":"福贡县", + "code":"533323" + }, + { + "name":"贡山独龙族怒族自治县", + "code":"533324" + }, + { + "name":"兰坪白族普米族自治县", + "code":"533325" + } + ] + }, + { + "name":"迪庆藏族自治州", + "code":"533400", + "sub":[ + { + "name":"香格里拉市", + "code":"533401" + }, + { + "name":"德钦县", + "code":"533422" + }, + { + "name":"维西傈僳族自治县", + "code":"533423" + } + ] + } + ] + }, + { + "name":"西藏自治区", + "code":"540000", + "sub":[ + { + "name":"拉萨市", + "code":"540100", + "sub":[ + { + "name":"市辖区", + "code":"540101" + }, + { + "name":"城关区", + "code":"540102" + }, + { + "name":"林周县", + "code":"540121" + }, + { + "name":"当雄县", + "code":"540122" + }, + { + "name":"尼木县", + "code":"540123" + }, + { + "name":"曲水县", + "code":"540124" + }, + { + "name":"堆龙德庆县", + "code":"540125" + }, + { + "name":"达孜县", + "code":"540126" + }, + { + "name":"墨竹工卡县", + "code":"540127" + } + ] + }, + { + "name":"日喀则市", + "code":"540200", + "sub":[ + { + "name":"市辖区", + "code":"540201" + }, + { + "name":"桑珠孜区", + "code":"540202" + }, + { + "name":"南木林县", + "code":"540221" + }, + { + "name":"江孜县", + "code":"540222" + }, + { + "name":"定日县", + "code":"540223" + }, + { + "name":"萨迦县", + "code":"540224" + }, + { + "name":"拉孜县", + "code":"540225" + }, + { + "name":"昂仁县", + "code":"540226" + }, + { + "name":"谢通门县", + "code":"540227" + }, + { + "name":"白朗县", + "code":"540228" + }, + { + "name":"仁布县", + "code":"540229" + }, + { + "name":"康马县", + "code":"540230" + }, + { + "name":"定结县", + "code":"540231" + }, + { + "name":"仲巴县", + "code":"540232" + }, + { + "name":"亚东县", + "code":"540233" + }, + { + "name":"吉隆县", + "code":"540234" + }, + { + "name":"聂拉木县", + "code":"540235" + }, + { + "name":"萨嘎县", + "code":"540236" + }, + { + "name":"岗巴县", + "code":"540237" + } + ] + }, + { + "name":"昌都市", + "code":"540300", + "sub":[ + { + "name":"市辖区", + "code":"540301" + }, + { + "name":"卡若区", + "code":"540302" + }, + { + "name":"江达县", + "code":"540321" + }, + { + "name":"贡觉县", + "code":"540322" + }, + { + "name":"类乌齐县", + "code":"540323" + }, + { + "name":"丁青县", + "code":"540324" + }, + { + "name":"察雅县", + "code":"540325" + }, + { + "name":"八宿县", + "code":"540326" + }, + { + "name":"左贡县", + "code":"540327" + }, + { + "name":"芒康县", + "code":"540328" + }, + { + "name":"洛隆县", + "code":"540329" + }, + { + "name":"边坝县", + "code":"540330" + } + ] + }, + { + "name":"山南地区", + "code":"542200", + "sub":[ + { + "name":"乃东县", + "code":"542221" + }, + { + "name":"扎囊县", + "code":"542222" + }, + { + "name":"贡嘎县", + "code":"542223" + }, + { + "name":"桑日县", + "code":"542224" + }, + { + "name":"琼结县", + "code":"542225" + }, + { + "name":"曲松县", + "code":"542226" + }, + { + "name":"措美县", + "code":"542227" + }, + { + "name":"洛扎县", + "code":"542228" + }, + { + "name":"加查县", + "code":"542229" + }, + { + "name":"隆子县", + "code":"542231" + }, + { + "name":"错那县", + "code":"542232" + }, + { + "name":"浪卡子县", + "code":"542233" + } + ] + }, + { + "name":"那曲地区", + "code":"542400", + "sub":[ + { + "name":"那曲县", + "code":"542421" + }, + { + "name":"嘉黎县", + "code":"542422" + }, + { + "name":"比如县", + "code":"542423" + }, + { + "name":"聂荣县", + "code":"542424" + }, + { + "name":"安多县", + "code":"542425" + }, + { + "name":"申扎县", + "code":"542426" + }, + { + "name":"索县", + "code":"542427" + }, + { + "name":"班戈县", + "code":"542428" + }, + { + "name":"巴青县", + "code":"542429" + }, + { + "name":"尼玛县", + "code":"542430" + }, + { + "name":"双湖县", + "code":"542431" + } + ] + }, + { + "name":"阿里地区", + "code":"542500", + "sub":[ + { + "name":"普兰县", + "code":"542521" + }, + { + "name":"札达县", + "code":"542522" + }, + { + "name":"噶尔县", + "code":"542523" + }, + { + "name":"日土县", + "code":"542524" + }, + { + "name":"革吉县", + "code":"542525" + }, + { + "name":"改则县", + "code":"542526" + }, + { + "name":"措勤县", + "code":"542527" + } + ] + }, + { + "name":"林芝地区", + "code":"542600", + "sub":[ + { + "name":"林芝县", + "code":"542621" + }, + { + "name":"工布江达县", + "code":"542622" + }, + { + "name":"米林县", + "code":"542623" + }, + { + "name":"墨脱县", + "code":"542624" + }, + { + "name":"波密县", + "code":"542625" + }, + { + "name":"察隅县", + "code":"542626" + }, + { + "name":"朗县", + "code":"542627" + } + ] + } + ] + }, + { + "name":"陕西省", + "code":"610000", + "sub":[ + { + "name":"西安市", + "code":"610100", + "sub":[ + { + "name":"市辖区", + "code":"610101" + }, + { + "name":"新城区", + "code":"610102" + }, + { + "name":"碑林区", + "code":"610103" + }, + { + "name":"莲湖区", + "code":"610104" + }, + { + "name":"灞桥区", + "code":"610111" + }, + { + "name":"未央区", + "code":"610112" + }, + { + "name":"雁塔区", + "code":"610113" + }, + { + "name":"阎良区", + "code":"610114" + }, + { + "name":"临潼区", + "code":"610115" + }, + { + "name":"长安区", + "code":"610116" + }, + { + "name":"高陵区", + "code":"610117" + }, + { + "name":"蓝田县", + "code":"610122" + }, + { + "name":"周至县", + "code":"610124" + }, + { + "name":"户县", + "code":"610125" + } + ] + }, + { + "name":"铜川市", + "code":"610200", + "sub":[ + { + "name":"市辖区", + "code":"610201" + }, + { + "name":"王益区", + "code":"610202" + }, + { + "name":"印台区", + "code":"610203" + }, + { + "name":"耀州区", + "code":"610204" + }, + { + "name":"宜君县", + "code":"610222" + } + ] + }, + { + "name":"宝鸡市", + "code":"610300", + "sub":[ + { + "name":"市辖区", + "code":"610301" + }, + { + "name":"渭滨区", + "code":"610302" + }, + { + "name":"金台区", + "code":"610303" + }, + { + "name":"陈仓区", + "code":"610304" + }, + { + "name":"凤翔县", + "code":"610322" + }, + { + "name":"岐山县", + "code":"610323" + }, + { + "name":"扶风县", + "code":"610324" + }, + { + "name":"眉县", + "code":"610326" + }, + { + "name":"陇县", + "code":"610327" + }, + { + "name":"千阳县", + "code":"610328" + }, + { + "name":"麟游县", + "code":"610329" + }, + { + "name":"凤县", + "code":"610330" + }, + { + "name":"太白县", + "code":"610331" + } + ] + }, + { + "name":"咸阳市", + "code":"610400", + "sub":[ + { + "name":"市辖区", + "code":"610401" + }, + { + "name":"秦都区", + "code":"610402" + }, + { + "name":"杨陵区", + "code":"610403" + }, + { + "name":"渭城区", + "code":"610404" + }, + { + "name":"三原县", + "code":"610422" + }, + { + "name":"泾阳县", + "code":"610423" + }, + { + "name":"乾县", + "code":"610424" + }, + { + "name":"礼泉县", + "code":"610425" + }, + { + "name":"永寿县", + "code":"610426" + }, + { + "name":"彬县", + "code":"610427" + }, + { + "name":"长武县", + "code":"610428" + }, + { + "name":"旬邑县", + "code":"610429" + }, + { + "name":"淳化县", + "code":"610430" + }, + { + "name":"武功县", + "code":"610431" + }, + { + "name":"兴平市", + "code":"610481" + } + ] + }, + { + "name":"渭南市", + "code":"610500", + "sub":[ + { + "name":"市辖区", + "code":"610501" + }, + { + "name":"临渭区", + "code":"610502" + }, + { + "name":"华县", + "code":"610521" + }, + { + "name":"潼关县", + "code":"610522" + }, + { + "name":"大荔县", + "code":"610523" + }, + { + "name":"合阳县", + "code":"610524" + }, + { + "name":"澄城县", + "code":"610525" + }, + { + "name":"蒲城县", + "code":"610526" + }, + { + "name":"白水县", + "code":"610527" + }, + { + "name":"富平县", + "code":"610528" + }, + { + "name":"韩城市", + "code":"610581" + }, + { + "name":"华阴市", + "code":"610582" + } + ] + }, + { + "name":"延安市", + "code":"610600", + "sub":[ + { + "name":"市辖区", + "code":"610601" + }, + { + "name":"宝塔区", + "code":"610602" + }, + { + "name":"延长县", + "code":"610621" + }, + { + "name":"延川县", + "code":"610622" + }, + { + "name":"子长县", + "code":"610623" + }, + { + "name":"安塞县", + "code":"610624" + }, + { + "name":"志丹县", + "code":"610625" + }, + { + "name":"吴起县", + "code":"610626" + }, + { + "name":"甘泉县", + "code":"610627" + }, + { + "name":"富县", + "code":"610628" + }, + { + "name":"洛川县", + "code":"610629" + }, + { + "name":"宜川县", + "code":"610630" + }, + { + "name":"黄龙县", + "code":"610631" + }, + { + "name":"黄陵县", + "code":"610632" + } + ] + }, + { + "name":"汉中市", + "code":"610700", + "sub":[ + { + "name":"市辖区", + "code":"610701" + }, + { + "name":"汉台区", + "code":"610702" + }, + { + "name":"南郑县", + "code":"610721" + }, + { + "name":"城固县", + "code":"610722" + }, + { + "name":"洋县", + "code":"610723" + }, + { + "name":"西乡县", + "code":"610724" + }, + { + "name":"勉县", + "code":"610725" + }, + { + "name":"宁强县", + "code":"610726" + }, + { + "name":"略阳县", + "code":"610727" + }, + { + "name":"镇巴县", + "code":"610728" + }, + { + "name":"留坝县", + "code":"610729" + }, + { + "name":"佛坪县", + "code":"610730" + } + ] + }, + { + "name":"榆林市", + "code":"610800", + "sub":[ + { + "name":"市辖区", + "code":"610801" + }, + { + "name":"榆阳区", + "code":"610802" + }, + { + "name":"神木县", + "code":"610821" + }, + { + "name":"府谷县", + "code":"610822" + }, + { + "name":"横山县", + "code":"610823" + }, + { + "name":"靖边县", + "code":"610824" + }, + { + "name":"定边县", + "code":"610825" + }, + { + "name":"绥德县", + "code":"610826" + }, + { + "name":"米脂县", + "code":"610827" + }, + { + "name":"佳县", + "code":"610828" + }, + { + "name":"吴堡县", + "code":"610829" + }, + { + "name":"清涧县", + "code":"610830" + }, + { + "name":"子洲县", + "code":"610831" + } + ] + }, + { + "name":"安康市", + "code":"610900", + "sub":[ + { + "name":"市辖区", + "code":"610901" + }, + { + "name":"汉阴县", + "code":"610921" + }, + { + "name":"石泉县", + "code":"610922" + }, + { + "name":"宁陕县", + "code":"610923" + }, + { + "name":"紫阳县", + "code":"610924" + }, + { + "name":"岚皋县", + "code":"610925" + }, + { + "name":"平利县", + "code":"610926" + }, + { + "name":"镇坪县", + "code":"610927" + }, + { + "name":"旬阳县", + "code":"610928" + }, + { + "name":"白河县", + "code":"610929" + } + ] + }, + { + "name":"商洛市", + "code":"611000", + "sub":[ + { + "name":"市辖区", + "code":"611001" + }, + { + "name":"商州区", + "code":"611002" + }, + { + "name":"洛南县", + "code":"611021" + }, + { + "name":"丹凤县", + "code":"611022" + }, + { + "name":"商南县", + "code":"611023" + }, + { + "name":"山阳县", + "code":"611024" + }, + { + "name":"镇安县", + "code":"611025" + }, + { + "name":"柞水县", + "code":"611026" + } + ] + } + ] + }, + { + "name":"甘肃省", + "code":"620000", + "sub":[ + { + "name":"兰州市", + "code":"620100", + "sub":[ + { + "name":"市辖区", + "code":"620101" + }, + { + "name":"城关区", + "code":"620102" + }, + { + "name":"七里河区", + "code":"620103" + }, + { + "name":"西固区", + "code":"620104" + }, + { + "name":"安宁区", + "code":"620105" + }, + { + "name":"红古区", + "code":"620111" + }, + { + "name":"永登县", + "code":"620121" + }, + { + "name":"皋兰县", + "code":"620122" + }, + { + "name":"榆中县", + "code":"620123" + } + ] + }, + { + "name":"嘉峪关市", + "code":"620200", + "sub":[ + { + "name":"市辖区", + "code":"620201" + } + ] + }, + { + "name":"金昌市", + "code":"620300", + "sub":[ + { + "name":"市辖区", + "code":"620301" + }, + { + "name":"金川区", + "code":"620302" + }, + { + "name":"永昌县", + "code":"620321" + } + ] + }, + { + "name":"白银市", + "code":"620400", + "sub":[ + { + "name":"市辖区", + "code":"620401" + }, + { + "name":"白银区", + "code":"620402" + }, + { + "name":"平川区", + "code":"620403" + }, + { + "name":"靖远县", + "code":"620421" + }, + { + "name":"会宁县", + "code":"620422" + }, + { + "name":"景泰县", + "code":"620423" + } + ] + }, + { + "name":"天水市", + "code":"620500", + "sub":[ + { + "name":"市辖区", + "code":"620501" + }, + { + "name":"秦州区", + "code":"620502" + }, + { + "name":"麦积区", + "code":"620503" + }, + { + "name":"清水县", + "code":"620521" + }, + { + "name":"秦安县", + "code":"620522" + }, + { + "name":"甘谷县", + "code":"620523" + }, + { + "name":"武山县", + "code":"620524" + }, + { + "name":"张家川回族自治县", + "code":"620525" + } + ] + }, + { + "name":"武威市", + "code":"620600", + "sub":[ + { + "name":"市辖区", + "code":"620601" + }, + { + "name":"凉州区", + "code":"620602" + }, + { + "name":"民勤县", + "code":"620621" + }, + { + "name":"古浪县", + "code":"620622" + }, + { + "name":"天祝藏族自治县", + "code":"620623" + } + ] + }, + { + "name":"张掖市", + "code":"620700", + "sub":[ + { + "name":"市辖区", + "code":"620701" + }, + { + "name":"甘州区", + "code":"620702" + }, + { + "name":"肃南裕固族自治县", + "code":"620721" + }, + { + "name":"民乐县", + "code":"620722" + }, + { + "name":"临泽县", + "code":"620723" + }, + { + "name":"高台县", + "code":"620724" + }, + { + "name":"山丹县", + "code":"620725" + } + ] + }, + { + "name":"平凉市", + "code":"620800", + "sub":[ + { + "name":"市辖区", + "code":"620801" + }, + { + "name":"崆峒区", + "code":"620802" + }, + { + "name":"泾川县", + "code":"620821" + }, + { + "name":"灵台县", + "code":"620822" + }, + { + "name":"崇信县", + "code":"620823" + }, + { + "name":"华亭县", + "code":"620824" + }, + { + "name":"庄浪县", + "code":"620825" + }, + { + "name":"静宁县", + "code":"620826" + } + ] + }, + { + "name":"酒泉市", + "code":"620900", + "sub":[ + { + "name":"市辖区", + "code":"620901" + }, + { + "name":"肃州区", + "code":"620902" + }, + { + "name":"金塔县", + "code":"620921" + }, + { + "name":"瓜州县", + "code":"620922" + }, + { + "name":"肃北蒙古族自治县", + "code":"620923" + }, + { + "name":"阿克塞哈萨克族自治县", + "code":"620924" + }, + { + "name":"玉门市", + "code":"620981" + }, + { + "name":"敦煌市", + "code":"620982" + } + ] + }, + { + "name":"庆阳市", + "code":"621000", + "sub":[ + { + "name":"市辖区", + "code":"621001" + }, + { + "name":"西峰区", + "code":"621002" + }, + { + "name":"庆城县", + "code":"621021" + }, + { + "name":"环县", + "code":"621022" + }, + { + "name":"华池县", + "code":"621023" + }, + { + "name":"合水县", + "code":"621024" + }, + { + "name":"正宁县", + "code":"621025" + }, + { + "name":"宁县", + "code":"621026" + }, + { + "name":"镇原县", + "code":"621027" + } + ] + }, + { + "name":"定西市", + "code":"621100", + "sub":[ + { + "name":"市辖区", + "code":"621101" + }, + { + "name":"安定区", + "code":"621102" + }, + { + "name":"通渭县", + "code":"621121" + }, + { + "name":"陇西县", + "code":"621122" + }, + { + "name":"渭源县", + "code":"621123" + }, + { + "name":"临洮县", + "code":"621124" + }, + { + "name":"漳县", + "code":"621125" + }, + { + "name":"岷县", + "code":"621126" + } + ] + }, + { + "name":"陇南市", + "code":"621200", + "sub":[ + { + "name":"市辖区", + "code":"621201" + }, + { + "name":"武都区", + "code":"621202" + }, + { + "name":"成县", + "code":"621221" + }, + { + "name":"文县", + "code":"621222" + }, + { + "name":"宕昌县", + "code":"621223" + }, + { + "name":"康县", + "code":"621224" + }, + { + "name":"西和县", + "code":"621225" + }, + { + "name":"礼县", + "code":"621226" + }, + { + "name":"徽县", + "code":"621227" + }, + { + "name":"两当县", + "code":"621228" + } + ] + }, + { + "name":"临夏回族自治州", + "code":"622900", + "sub":[ + { + "name":"临夏市", + "code":"622901" + }, + { + "name":"临夏县", + "code":"622921" + }, + { + "name":"康乐县", + "code":"622922" + }, + { + "name":"永靖县", + "code":"622923" + }, + { + "name":"广河县", + "code":"622924" + }, + { + "name":"和政县", + "code":"622925" + }, + { + "name":"东乡族自治县", + "code":"622926" + }, + { + "name":"积石山保安族东乡族撒拉族自治县", + "code":"622927" + } + ] + }, + { + "name":"甘南藏族自治州", + "code":"623000", + "sub":[ + { + "name":"合作市", + "code":"623001" + }, + { + "name":"临潭县", + "code":"623021" + }, + { + "name":"卓尼县", + "code":"623022" + }, + { + "name":"舟曲县", + "code":"623023" + }, + { + "name":"迭部县", + "code":"623024" + }, + { + "name":"玛曲县", + "code":"623025" + }, + { + "name":"碌曲县", + "code":"623026" + }, + { + "name":"夏河县", + "code":"623027" + } + ] + } + ] + }, + { + "name":"青海省", + "code":"630000", + "sub":[ + { + "name":"西宁市", + "code":"630100", + "sub":[ + { + "name":"市辖区", + "code":"630101" + }, + { + "name":"城东区", + "code":"630102" + }, + { + "name":"城中区", + "code":"630103" + }, + { + "name":"城西区", + "code":"630104" + }, + { + "name":"城北区", + "code":"630105" + }, + { + "name":"大通回族土族自治县", + "code":"630121" + }, + { + "name":"湟中县", + "code":"630122" + }, + { + "name":"湟源县", + "code":"630123" + } + ] + }, + { + "name":"海东市", + "code":"630200", + "sub":[ + { + "name":"市辖区", + "code":"630201" + }, + { + "name":"乐都区", + "code":"630202" + }, + { + "name":"平安县", + "code":"630221" + }, + { + "name":"民和回族土族自治县", + "code":"630222" + }, + { + "name":"互助土族自治县", + "code":"630223" + }, + { + "name":"化隆回族自治县", + "code":"630224" + }, + { + "name":"循化撒拉族自治县", + "code":"630225" + } + ] + }, + { + "name":"海北藏族自治州", + "code":"632200", + "sub":[ + { + "name":"门源回族自治县", + "code":"632221" + }, + { + "name":"祁连县", + "code":"632222" + }, + { + "name":"海晏县", + "code":"632223" + }, + { + "name":"刚察县", + "code":"632224" + } + ] + }, + { + "name":"黄南藏族自治州", + "code":"632300", + "sub":[ + { + "name":"同仁县", + "code":"632321" + }, + { + "name":"尖扎县", + "code":"632322" + }, + { + "name":"泽库县", + "code":"632323" + }, + { + "name":"河南蒙古族自治县", + "code":"632324" + } + ] + }, + { + "name":"海南藏族自治州", + "code":"632500", + "sub":[ + { + "name":"共和县", + "code":"632521" + }, + { + "name":"同德县", + "code":"632522" + }, + { + "name":"贵德县", + "code":"632523" + }, + { + "name":"兴海县", + "code":"632524" + }, + { + "name":"贵南县", + "code":"632525" + } + ] + }, + { + "name":"果洛藏族自治州", + "code":"632600", + "sub":[ + { + "name":"玛沁县", + "code":"632621" + }, + { + "name":"班玛县", + "code":"632622" + }, + { + "name":"甘德县", + "code":"632623" + }, + { + "name":"达日县", + "code":"632624" + }, + { + "name":"久治县", + "code":"632625" + }, + { + "name":"玛多县", + "code":"632626" + } + ] + }, + { + "name":"玉树藏族自治州", + "code":"632700", + "sub":[ + { + "name":"玉树市", + "code":"632701" + }, + { + "name":"杂多县", + "code":"632722" + }, + { + "name":"称多县", + "code":"632723" + }, + { + "name":"治多县", + "code":"632724" + }, + { + "name":"囊谦县", + "code":"632725" + }, + { + "name":"曲麻莱县", + "code":"632726" + } + ] + }, + { + "name":"海西蒙古族藏族自治州", + "code":"632800", + "sub":[ + { + "name":"格尔木市", + "code":"632801" + }, + { + "name":"德令哈市", + "code":"632802" + }, + { + "name":"乌兰县", + "code":"632821" + }, + { + "name":"都兰县", + "code":"632822" + }, + { + "name":"天峻县", + "code":"632823" + } + ] + } + ] + }, + { + "name":"宁夏回族自治区", + "code":"640000", + "sub":[ + { + "name":"银川市", + "code":"640100", + "sub":[ + { + "name":"市辖区", + "code":"640101" + }, + { + "name":"兴庆区", + "code":"640104" + }, + { + "name":"西夏区", + "code":"640105" + }, + { + "name":"金凤区", + "code":"640106" + }, + { + "name":"永宁县", + "code":"640121" + }, + { + "name":"贺兰县", + "code":"640122" + }, + { + "name":"灵武市", + "code":"640181" + } + ] + }, + { + "name":"石嘴山市", + "code":"640200", + "sub":[ + { + "name":"市辖区", + "code":"640201" + }, + { + "name":"大武口区", + "code":"640202" + }, + { + "name":"惠农区", + "code":"640205" + }, + { + "name":"平罗县", + "code":"640221" + } + ] + }, + { + "name":"吴忠市", + "code":"640300", + "sub":[ + { + "name":"市辖区", + "code":"640301" + }, + { + "name":"利通区", + "code":"640302" + }, + { + "name":"红寺堡区", + "code":"640303" + }, + { + "name":"盐池县", + "code":"640323" + }, + { + "name":"同心县", + "code":"640324" + }, + { + "name":"青铜峡市", + "code":"640381" + } + ] + }, + { + "name":"固原市", + "code":"640400", + "sub":[ + { + "name":"市辖区", + "code":"640401" + }, + { + "name":"原州区", + "code":"640402" + }, + { + "name":"西吉县", + "code":"640422" + }, + { + "name":"隆德县", + "code":"640423" + }, + { + "name":"泾源县", + "code":"640424" + }, + { + "name":"彭阳县", + "code":"640425" + } + ] + }, + { + "name":"中卫市", + "code":"640500", + "sub":[ + { + "name":"市辖区", + "code":"640501" + }, + { + "name":"沙坡头区", + "code":"640502" + }, + { + "name":"中宁县", + "code":"640521" + }, + { + "name":"海原县", + "code":"640522" + } + ] + } + ] + }, + { + "name":"新疆维吾尔自治区", + "code":"650000", + "sub":[ + { + "name":"乌鲁木齐市", + "code":"650100", + "sub":[ + { + "name":"市辖区", + "code":"650101" + }, + { + "name":"天山区", + "code":"650102" + }, + { + "name":"沙依巴克区", + "code":"650103" + }, + { + "name":"新市区", + "code":"650104" + }, + { + "name":"水磨沟区", + "code":"650105" + }, + { + "name":"头屯河区", + "code":"650106" + }, + { + "name":"达坂城区", + "code":"650107" + }, + { + "name":"米东区", + "code":"650109" + }, + { + "name":"乌鲁木齐县", + "code":"650121" + } + ] + }, + { + "name":"克拉玛依市", + "code":"650200", + "sub":[ + { + "name":"市辖区", + "code":"650201" + }, + { + "name":"独山子区", + "code":"650202" + }, + { + "name":"克拉玛依区", + "code":"650203" + }, + { + "name":"白碱滩区", + "code":"650204" + }, + { + "name":"乌尔禾区", + "code":"650205" + } + ] + }, + { + "name":"吐鲁番地区", + "code":"652100", + "sub":[ + { + "name":"吐鲁番市", + "code":"652101" + }, + { + "name":"鄯善县", + "code":"652122" + }, + { + "name":"托克逊县", + "code":"652123" + } + ] + }, + { + "name":"哈密地区", + "code":"652200", + "sub":[ + { + "name":"哈密市", + "code":"652201" + }, + { + "name":"巴里坤哈萨克自治县", + "code":"652222" + }, + { + "name":"伊吾县", + "code":"652223" + } + ] + }, + { + "name":"昌吉回族自治州", + "code":"652300", + "sub":[ + { + "name":"昌吉市", + "code":"652301" + }, + { + "name":"阜康市", + "code":"652302" + }, + { + "name":"呼图壁县", + "code":"652323" + }, + { + "name":"玛纳斯县", + "code":"652324" + }, + { + "name":"奇台县", + "code":"652325" + }, + { + "name":"吉木萨尔县", + "code":"652327" + }, + { + "name":"木垒哈萨克自治县", + "code":"652328" + } + ] + }, + { + "name":"博尔塔拉蒙古自治州", + "code":"652700", + "sub":[ + { + "name":"博乐市", + "code":"652701" + }, + { + "name":"阿拉山口市", + "code":"652702" + }, + { + "name":"精河县", + "code":"652722" + }, + { + "name":"温泉县", + "code":"652723" + } + ] + }, + { + "name":"巴音郭楞蒙古自治州", + "code":"652800", + "sub":[ + { + "name":"库尔勒市", + "code":"652801" + }, + { + "name":"轮台县", + "code":"652822" + }, + { + "name":"尉犁县", + "code":"652823" + }, + { + "name":"若羌县", + "code":"652824" + }, + { + "name":"且末县", + "code":"652825" + }, + { + "name":"焉耆回族自治县", + "code":"652826" + }, + { + "name":"和静县", + "code":"652827" + }, + { + "name":"和硕县", + "code":"652828" + }, + { + "name":"博湖县", + "code":"652829" + } + ] + }, + { + "name":"阿克苏地区", + "code":"652900", + "sub":[ + { + "name":"阿克苏市", + "code":"652901" + }, + { + "name":"温宿县", + "code":"652922" + }, + { + "name":"库车县", + "code":"652923" + }, + { + "name":"沙雅县", + "code":"652924" + }, + { + "name":"新和县", + "code":"652925" + }, + { + "name":"拜城县", + "code":"652926" + }, + { + "name":"乌什县", + "code":"652927" + }, + { + "name":"阿瓦提县", + "code":"652928" + }, + { + "name":"柯坪县", + "code":"652929" + } + ] + }, + { + "name":"克孜勒苏柯尔克孜自治州", + "code":"653000", + "sub":[ + { + "name":"阿图什市", + "code":"653001" + }, + { + "name":"阿克陶县", + "code":"653022" + }, + { + "name":"阿合奇县", + "code":"653023" + }, + { + "name":"乌恰县", + "code":"653024" + } + ] + }, + { + "name":"喀什地区", + "code":"653100", + "sub":[ + { + "name":"喀什市", + "code":"653101" + }, + { + "name":"疏附县", + "code":"653121" + }, + { + "name":"疏勒县", + "code":"653122" + }, + { + "name":"英吉沙县", + "code":"653123" + }, + { + "name":"泽普县", + "code":"653124" + }, + { + "name":"莎车县", + "code":"653125" + }, + { + "name":"叶城县", + "code":"653126" + }, + { + "name":"麦盖提县", + "code":"653127" + }, + { + "name":"岳普湖县", + "code":"653128" + }, + { + "name":"伽师县", + "code":"653129" + }, + { + "name":"巴楚县", + "code":"653130" + }, + { + "name":"塔什库尔干塔吉克自治县", + "code":"653131" + } + ] + }, + { + "name":"和田地区", + "code":"653200", + "sub":[ + { + "name":"和田市", + "code":"653201" + }, + { + "name":"和田县", + "code":"653221" + }, + { + "name":"墨玉县", + "code":"653222" + }, + { + "name":"皮山县", + "code":"653223" + }, + { + "name":"洛浦县", + "code":"653224" + }, + { + "name":"策勒县", + "code":"653225" + }, + { + "name":"于田县", + "code":"653226" + }, + { + "name":"民丰县", + "code":"653227" + } + ] + }, + { + "name":"伊犁哈萨克自治州", + "code":"654000", + "sub":[ + { + "name":"伊宁市", + "code":"654002" + }, + { + "name":"奎屯市", + "code":"654003" + }, + { + "name":"霍尔果斯市", + "code":"654004" + }, + { + "name":"伊宁县", + "code":"654021" + }, + { + "name":"察布查尔锡伯自治县", + "code":"654022" + }, + { + "name":"霍城县", + "code":"654023" + }, + { + "name":"巩留县", + "code":"654024" + }, + { + "name":"新源县", + "code":"654025" + }, + { + "name":"昭苏县", + "code":"654026" + }, + { + "name":"特克斯县", + "code":"654027" + }, + { + "name":"尼勒克县", + "code":"654028" + }, + { + "name":"塔城地区", + "code":"654200" + }, + { + "name":"塔城市", + "code":"654201" + }, + { + "name":"乌苏市", + "code":"654202" + }, + { + "name":"额敏县", + "code":"654221" + }, + { + "name":"沙湾县", + "code":"654223" + }, + { + "name":"托里县", + "code":"654224" + }, + { + "name":"裕民县", + "code":"654225" + }, + { + "name":"和布克赛尔蒙古自治县", + "code":"654226" + }, + { + "name":"阿勒泰地区", + "code":"654300" + }, + { + "name":"阿勒泰市", + "code":"654301" + }, + { + "name":"布尔津县", + "code":"654321" + }, + { + "name":"富蕴县", + "code":"654322" + }, + { + "name":"福海县", + "code":"654323" + }, + { + "name":"哈巴河县", + "code":"654324" + }, + { + "name":"青河县", + "code":"654325" + }, + { + "name":"吉木乃县", + "code":"654326" + } + ] + }, + { + "name":"自治区直辖县级行政区划", + "code":"659000", + "sub":[ + { + "name":"石河子市", + "code":"659001" + }, + { + "name":"阿拉尔市", + "code":"659002" + }, + { + "name":"图木舒克市", + "code":"659003" + }, + { + "name":"五家渠市", + "code":"659004" + }, + { + "name":"北屯市", + "code":"659005" + }, + { + "name":"铁门关市", + "code":"659006" + }, + { + "name":"双河市", + "code":"659007" + } + ] + } + ] + }, + { + "name":"台湾省", + "code":"710000", + "sub":[ + { + "name":"台北市", + "code":"710100", + "sub":[ + { + "name":"松山区", + "code":"710101" + }, + { + "name":"信义区", + "code":"710102" + }, + { + "name":"大安区", + "code":"710103" + }, + { + "name":"中山区", + "code":"710104" + }, + { + "name":"中正区", + "code":"710105" + }, + { + "name":"大同区", + "code":"710106" + }, + { + "name":"万华区", + "code":"710107" + }, + { + "name":"文山区", + "code":"710108" + }, + { + "name":"南港区", + "code":"710109" + }, + { + "name":"内湖区", + "code":"710110" + }, + { + "name":"士林区", + "code":"710111" + }, + { + "name":"北投区", + "code":"710112" + } + ] + }, + { + "name":"高雄市", + "code":"710200", + "sub":[ + { + "name":"盐埕区", + "code":"710201" + }, + { + "name":"鼓山区", + "code":"710202" + }, + { + "name":"左营区", + "code":"710203" + }, + { + "name":"楠梓区", + "code":"710204" + }, + { + "name":"三民区", + "code":"710205" + }, + { + "name":"新兴区", + "code":"710206" + }, + { + "name":"前金区", + "code":"710207" + }, + { + "name":"苓雅区", + "code":"710208" + }, + { + "name":"前镇区", + "code":"710209" + }, + { + "name":"旗津区", + "code":"710210" + }, + { + "name":"小港区", + "code":"710211" + }, + { + "name":"凤山区", + "code":"710212" + }, + { + "name":"林园区", + "code":"710213" + }, + { + "name":"大寮区", + "code":"710214" + }, + { + "name":"大树区", + "code":"710215" + }, + { + "name":"大社区", + "code":"710216" + }, + { + "name":"仁武区", + "code":"710217" + }, + { + "name":"鸟松区", + "code":"710218" + }, + { + "name":"冈山区", + "code":"710219" + }, + { + "name":"桥头区", + "code":"710220" + }, + { + "name":"燕巢区", + "code":"710221" + }, + { + "name":"田寮区", + "code":"710222" + }, + { + "name":"阿莲区", + "code":"710223" + }, + { + "name":"路竹区", + "code":"710224" + }, + { + "name":"湖内区", + "code":"710225" + }, + { + "name":"茄萣区", + "code":"710226" + }, + { + "name":"永安区", + "code":"710227" + }, + { + "name":"弥陀区", + "code":"710228" + }, + { + "name":"梓官区", + "code":"710229" + }, + { + "name":"旗山区", + "code":"710230" + }, + { + "name":"美浓区", + "code":"710231" + }, + { + "name":"六龟区", + "code":"710232" + }, + { + "name":"甲仙区", + "code":"710233" + }, + { + "name":"杉林区", + "code":"710234" + }, + { + "name":"内门区", + "code":"710235" + }, + { + "name":"茂林区", + "code":"710236" + }, + { + "name":"桃源区", + "code":"710237" + }, + { + "name":"那玛夏区", + "code":"710238" + } + ] + }, + { + "name":"基隆市", + "code":"710300", + "sub":[ + { + "name":"中正区", + "code":"710301" + }, + { + "name":"七堵区", + "code":"710302" + }, + { + "name":"暖暖区", + "code":"710303" + }, + { + "name":"仁爱区", + "code":"710304" + }, + { + "name":"中山区", + "code":"710305" + }, + { + "name":"安乐区", + "code":"710306" + }, + { + "name":"信义区", + "code":"710307" + } + ] + }, + { + "name":"台中市", + "code":"710400", + "sub":[ + { + "name":"中区", + "code":"710401" + }, + { + "name":"东区", + "code":"710402" + }, + { + "name":"南区", + "code":"710403" + }, + { + "name":"西区", + "code":"710404" + }, + { + "name":"北区", + "code":"710405" + }, + { + "name":"西屯区", + "code":"710406" + }, + { + "name":"南屯区", + "code":"710407" + }, + { + "name":"北屯区", + "code":"710408" + }, + { + "name":"丰原区", + "code":"710409" + }, + { + "name":"东势区", + "code":"710410" + }, + { + "name":"大甲区", + "code":"710411" + }, + { + "name":"清水区", + "code":"710412" + }, + { + "name":"沙鹿区", + "code":"710413" + }, + { + "name":"梧栖区", + "code":"710414" + }, + { + "name":"后里区", + "code":"710415" + }, + { + "name":"神冈区", + "code":"710416" + }, + { + "name":"潭子区", + "code":"710417" + }, + { + "name":"大雅区", + "code":"710418" + }, + { + "name":"新社区", + "code":"710419" + }, + { + "name":"石冈区", + "code":"710420" + }, + { + "name":"外埔区", + "code":"710421" + }, + { + "name":"大安区", + "code":"710422" + }, + { + "name":"乌日区", + "code":"710423" + }, + { + "name":"大肚区", + "code":"710424" + }, + { + "name":"龙井区", + "code":"710425" + }, + { + "name":"雾峰区", + "code":"710426" + }, + { + "name":"太平区", + "code":"710427" + }, + { + "name":"大里区", + "code":"710428" + }, + { + "name":"和平区", + "code":"710429" + } + ] + }, + { + "name":"台南市", + "code":"710500", + "sub":[ + { + "name":"东区", + "code":"710501" + }, + { + "name":"南区", + "code":"710502" + }, + { + "name":"北区", + "code":"710504" + }, + { + "name":"安南区", + "code":"710506" + }, + { + "name":"安平区", + "code":"710507" + }, + { + "name":"中西区", + "code":"710508" + }, + { + "name":"新营区", + "code":"710509" + }, + { + "name":"盐水区", + "code":"710510" + }, + { + "name":"白河区", + "code":"710511" + }, + { + "name":"柳营区", + "code":"710512" + }, + { + "name":"后壁区", + "code":"710513" + }, + { + "name":"东山区", + "code":"710514" + }, + { + "name":"麻豆区", + "code":"710515" + }, + { + "name":"下营区", + "code":"710516" + }, + { + "name":"六甲区", + "code":"710517" + }, + { + "name":"官田区", + "code":"710518" + }, + { + "name":"大内区", + "code":"710519" + }, + { + "name":"佳里区", + "code":"710520" + }, + { + "name":"学甲区", + "code":"710521" + }, + { + "name":"西港区", + "code":"710522" + }, + { + "name":"七股区", + "code":"710523" + }, + { + "name":"将军区", + "code":"710524" + }, + { + "name":"北门区", + "code":"710525" + }, + { + "name":"新化区", + "code":"710526" + }, + { + "name":"善化区", + "code":"710527" + }, + { + "name":"新市区", + "code":"710528" + }, + { + "name":"安定区", + "code":"710529" + }, + { + "name":"山上区", + "code":"710530" + }, + { + "name":"玉井区", + "code":"710531" + }, + { + "name":"楠西区", + "code":"710532" + }, + { + "name":"南化区", + "code":"710533" + }, + { + "name":"左镇区", + "code":"710534" + }, + { + "name":"仁德区", + "code":"710535" + }, + { + "name":"归仁区", + "code":"710536" + }, + { + "name":"关庙区", + "code":"710537" + }, + { + "name":"龙崎区", + "code":"710538" + }, + { + "name":"永康区", + "code":"710539" + } + ] + }, + { + "name":"新竹市", + "code":"710600", + "sub":[ + { + "name":"东区", + "code":"710601" + }, + { + "name":"北区", + "code":"710602" + }, + { + "name":"香山区", + "code":"710603" + } + ] + }, + { + "name":"嘉义市", + "code":"710700", + "sub":[ + { + "name":"东区", + "code":"710701" + }, + { + "name":"西区", + "code":"710702" + } + ] + }, + { + "name":"新北市", + "code":"710800", + "sub":[ + { + "name":"板桥区", + "code":"710801" + }, + { + "name":"三重区", + "code":"710802" + }, + { + "name":"中和区", + "code":"710803" + }, + { + "name":"永和区", + "code":"710804" + }, + { + "name":"新庄区", + "code":"710805" + }, + { + "name":"新店区", + "code":"710806" + }, + { + "name":"树林区", + "code":"710807" + }, + { + "name":"莺歌区", + "code":"710808" + }, + { + "name":"三峡区", + "code":"710809" + }, + { + "name":"淡水区", + "code":"710810" + }, + { + "name":"汐止区", + "code":"710811" + }, + { + "name":"瑞芳区", + "code":"710812" + }, + { + "name":"土城区", + "code":"710813" + }, + { + "name":"芦洲区", + "code":"710814" + }, + { + "name":"五股区", + "code":"710815" + }, + { + "name":"泰山区", + "code":"710816" + }, + { + "name":"林口区", + "code":"710817" + }, + { + "name":"深坑区", + "code":"710818" + }, + { + "name":"石碇区", + "code":"710819" + }, + { + "name":"坪林区", + "code":"710820" + }, + { + "name":"三芝区", + "code":"710821" + }, + { + "name":"石门区", + "code":"710822" + }, + { + "name":"八里区", + "code":"710823" + }, + { + "name":"平溪区", + "code":"710824" + }, + { + "name":"双溪区", + "code":"710825" + }, + { + "name":"贡寮区", + "code":"710826" + }, + { + "name":"金山区", + "code":"710827" + }, + { + "name":"万里区", + "code":"710828" + }, + { + "name":"乌来区", + "code":"710829" + } + ] + }, + { + "name":"宜兰县", + "code":"712200", + "sub":[ + { + "name":"宜兰市", + "code":"712201" + }, + { + "name":"罗东镇", + "code":"712221" + }, + { + "name":"苏澳镇", + "code":"712222" + }, + { + "name":"头城镇", + "code":"712223" + }, + { + "name":"礁溪乡", + "code":"712224" + }, + { + "name":"壮围乡", + "code":"712225" + }, + { + "name":"员山乡", + "code":"712226" + }, + { + "name":"冬山乡", + "code":"712227" + }, + { + "name":"五结乡", + "code":"712228" + }, + { + "name":"三星乡", + "code":"712229" + }, + { + "name":"大同乡", + "code":"712230" + }, + { + "name":"南澳乡", + "code":"712231" + } + ] + }, + { + "name":"桃园县", + "code":"712300", + "sub":[ + { + "name":"桃园市", + "code":"712301" + }, + { + "name":"中坜市", + "code":"712302" + }, + { + "name":"平镇市", + "code":"712303" + }, + { + "name":"八德市", + "code":"712304" + }, + { + "name":"杨梅市", + "code":"712305" + }, + { + "name":"大溪镇", + "code":"712321" + }, + { + "name":"芦竹乡", + "code":"712323" + }, + { + "name":"大园乡", + "code":"712324" + }, + { + "name":"龟山乡", + "code":"712325" + }, + { + "name":"龙潭乡", + "code":"712327" + }, + { + "name":"新屋乡", + "code":"712329" + }, + { + "name":"观音乡", + "code":"712330" + }, + { + "name":"复兴乡", + "code":"712331" + } + ] + }, + { + "name":"新竹县", + "code":"712400", + "sub":[ + { + "name":"竹北市", + "code":"712401" + }, + { + "name":"竹东镇", + "code":"712421" + }, + { + "name":"新埔镇", + "code":"712422" + }, + { + "name":"关西镇", + "code":"712423" + }, + { + "name":"湖口乡", + "code":"712424" + }, + { + "name":"新丰乡", + "code":"712425" + }, + { + "name":"芎林乡", + "code":"712426" + }, + { + "name":"橫山乡", + "code":"712427" + }, + { + "name":"北埔乡", + "code":"712428" + }, + { + "name":"宝山乡", + "code":"712429" + }, + { + "name":"峨眉乡", + "code":"712430" + }, + { + "name":"尖石乡", + "code":"712431" + }, + { + "name":"五峰乡", + "code":"712432" + } + ] + }, + { + "name":"苗栗县", + "code":"712500", + "sub":[ + { + "name":"苗栗市", + "code":"712501" + }, + { + "name":"苑里镇", + "code":"712521" + }, + { + "name":"通霄镇", + "code":"712522" + }, + { + "name":"竹南镇", + "code":"712523" + }, + { + "name":"头份镇", + "code":"712524" + }, + { + "name":"后龙镇", + "code":"712525" + }, + { + "name":"卓兰镇", + "code":"712526" + }, + { + "name":"大湖乡", + "code":"712527" + }, + { + "name":"公馆乡", + "code":"712528" + }, + { + "name":"铜锣乡", + "code":"712529" + }, + { + "name":"南庄乡", + "code":"712530" + }, + { + "name":"头屋乡", + "code":"712531" + }, + { + "name":"三义乡", + "code":"712532" + }, + { + "name":"西湖乡", + "code":"712533" + }, + { + "name":"造桥乡", + "code":"712534" + }, + { + "name":"三湾乡", + "code":"712535" + }, + { + "name":"狮潭乡", + "code":"712536" + }, + { + "name":"泰安乡", + "code":"712537" + } + ] + }, + { + "name":"彰化县", + "code":"712700", + "sub":[ + { + "name":"彰化市", + "code":"712701" + }, + { + "name":"鹿港镇", + "code":"712721" + }, + { + "name":"和美镇", + "code":"712722" + }, + { + "name":"线西乡", + "code":"712723" + }, + { + "name":"伸港乡", + "code":"712724" + }, + { + "name":"福兴乡", + "code":"712725" + }, + { + "name":"秀水乡", + "code":"712726" + }, + { + "name":"花坛乡", + "code":"712727" + }, + { + "name":"芬园乡", + "code":"712728" + }, + { + "name":"员林镇", + "code":"712729" + }, + { + "name":"溪湖镇", + "code":"712730" + }, + { + "name":"田中镇", + "code":"712731" + }, + { + "name":"大村乡", + "code":"712732" + }, + { + "name":"埔盐乡", + "code":"712733" + }, + { + "name":"埔心乡", + "code":"712734" + }, + { + "name":"永靖乡", + "code":"712735" + }, + { + "name":"社头乡", + "code":"712736" + }, + { + "name":"二水乡", + "code":"712737" + }, + { + "name":"北斗镇", + "code":"712738" + }, + { + "name":"二林镇", + "code":"712739" + }, + { + "name":"田尾乡", + "code":"712740" + }, + { + "name":"埤头乡", + "code":"712741" + }, + { + "name":"芳苑乡", + "code":"712742" + }, + { + "name":"大城乡", + "code":"712743" + }, + { + "name":"竹塘乡", + "code":"712744" + }, + { + "name":"溪州乡", + "code":"712745" + } + ] + }, + { + "name":"南投县", + "code":"712800", + "sub":[ + { + "name":"南投市", + "code":"712801" + }, + { + "name":"埔里镇", + "code":"712821" + }, + { + "name":"草屯镇", + "code":"712822" + }, + { + "name":"竹山镇", + "code":"712823" + }, + { + "name":"集集镇", + "code":"712824" + }, + { + "name":"名间乡", + "code":"712825" + }, + { + "name":"鹿谷乡", + "code":"712826" + }, + { + "name":"中寮乡", + "code":"712827" + }, + { + "name":"鱼池乡", + "code":"712828" + }, + { + "name":"国姓乡", + "code":"712829" + }, + { + "name":"水里乡", + "code":"712830" + }, + { + "name":"信义乡", + "code":"712831" + }, + { + "name":"仁爱乡", + "code":"712832" + } + ] + }, + { + "name":"云林县", + "code":"712900", + "sub":[ + { + "name":"斗六市", + "code":"712901" + }, + { + "name":"斗南镇", + "code":"712921" + }, + { + "name":"虎尾镇", + "code":"712922" + }, + { + "name":"西螺镇", + "code":"712923" + }, + { + "name":"土库镇", + "code":"712924" + }, + { + "name":"北港镇", + "code":"712925" + }, + { + "name":"古坑乡", + "code":"712926" + }, + { + "name":"大埤乡", + "code":"712927" + }, + { + "name":"莿桐乡", + "code":"712928" + }, + { + "name":"林内乡", + "code":"712929" + }, + { + "name":"二仑乡", + "code":"712930" + }, + { + "name":"仑背乡", + "code":"712931" + }, + { + "name":"麦寮乡", + "code":"712932" + }, + { + "name":"东势乡", + "code":"712933" + }, + { + "name":"褒忠乡", + "code":"712934" + }, + { + "name":"台西乡", + "code":"712935" + }, + { + "name":"元长乡", + "code":"712936" + }, + { + "name":"四湖乡", + "code":"712937" + }, + { + "name":"口湖乡", + "code":"712938" + }, + { + "name":"水林乡", + "code":"712939" + } + ] + }, + { + "name":"嘉义县", + "code":"713000", + "sub":[ + { + "name":"太保市", + "code":"713001" + }, + { + "name":"朴子市", + "code":"713002" + }, + { + "name":"布袋镇", + "code":"713023" + }, + { + "name":"大林镇", + "code":"713024" + }, + { + "name":"民雄乡", + "code":"713025" + }, + { + "name":"溪口乡", + "code":"713026" + }, + { + "name":"新港乡", + "code":"713027" + }, + { + "name":"六脚乡", + "code":"713028" + }, + { + "name":"东石乡", + "code":"713029" + }, + { + "name":"义竹乡", + "code":"713030" + }, + { + "name":"鹿草乡", + "code":"713031" + }, + { + "name":"水上乡", + "code":"713032" + }, + { + "name":"中埔乡", + "code":"713033" + }, + { + "name":"竹崎乡", + "code":"713034" + }, + { + "name":"梅山乡", + "code":"713035" + }, + { + "name":"番路乡", + "code":"713036" + }, + { + "name":"大埔乡", + "code":"713037" + }, + { + "name":"阿里山乡", + "code":"713038" + } + ] + }, + { + "name":"屏东县", + "code":"713300", + "sub":[ + { + "name":"屏东市", + "code":"713301" + }, + { + "name":"潮州镇", + "code":"713321" + }, + { + "name":"东港镇", + "code":"713322" + }, + { + "name":"恒春镇", + "code":"713323" + }, + { + "name":"万丹乡", + "code":"713324" + }, + { + "name":"长治乡", + "code":"713325" + }, + { + "name":"麟洛乡", + "code":"713326" + }, + { + "name":"九如乡", + "code":"713327" + }, + { + "name":"里港乡", + "code":"713328" + }, + { + "name":"盐埔乡", + "code":"713329" + }, + { + "name":"高树乡", + "code":"713330" + }, + { + "name":"万峦乡", + "code":"713331" + }, + { + "name":"内埔乡", + "code":"713332" + }, + { + "name":"竹田乡", + "code":"713333" + }, + { + "name":"新埤乡", + "code":"713334" + }, + { + "name":"枋寮乡", + "code":"713335" + }, + { + "name":"新园乡", + "code":"713336" + }, + { + "name":"崁顶乡", + "code":"713337" + }, + { + "name":"林边乡", + "code":"713338" + }, + { + "name":"南州乡", + "code":"713339" + }, + { + "name":"佳冬乡", + "code":"713340" + }, + { + "name":"琉球乡", + "code":"713341" + }, + { + "name":"车城乡", + "code":"713342" + }, + { + "name":"满州乡", + "code":"713343" + }, + { + "name":"枋山乡", + "code":"713344" + }, + { + "name":"三地门乡", + "code":"713345" + }, + { + "name":"雾台乡", + "code":"713346" + }, + { + "name":"玛家乡", + "code":"713347" + }, + { + "name":"泰武乡", + "code":"713348" + }, + { + "name":"来义乡", + "code":"713349" + }, + { + "name":"春日乡", + "code":"713350" + }, + { + "name":"狮子乡", + "code":"713351" + }, + { + "name":"牡丹乡", + "code":"713352" + } + ] + }, + { + "name":"台东县", + "code":"713400", + "sub":[ + { + "name":"台东市", + "code":"713401" + }, + { + "name":"成功镇", + "code":"713421" + }, + { + "name":"关山镇", + "code":"713422" + }, + { + "name":"卑南乡", + "code":"713423" + }, + { + "name":"鹿野乡", + "code":"713424" + }, + { + "name":"池上乡", + "code":"713425" + }, + { + "name":"东河乡", + "code":"713426" + }, + { + "name":"长滨乡", + "code":"713427" + }, + { + "name":"太麻里乡", + "code":"713428" + }, + { + "name":"大武乡", + "code":"713429" + }, + { + "name":"绿岛乡", + "code":"713430" + }, + { + "name":"海端乡", + "code":"713431" + }, + { + "name":"延平乡", + "code":"713432" + }, + { + "name":"金峰乡", + "code":"713433" + }, + { + "name":"达仁乡", + "code":"713434" + }, + { + "name":"兰屿乡", + "code":"713435" + } + ] + }, + { + "name":"花莲县", + "code":"713500", + "sub":[ + { + "name":"花莲市", + "code":"713501" + }, + { + "name":"凤林镇", + "code":"713521" + }, + { + "name":"玉里镇", + "code":"713522" + }, + { + "name":"新城乡", + "code":"713523" + }, + { + "name":"吉安乡", + "code":"713524" + }, + { + "name":"寿丰乡", + "code":"713525" + }, + { + "name":"光复乡", + "code":"713526" + }, + { + "name":"丰滨乡", + "code":"713527" + }, + { + "name":"瑞穗乡", + "code":"713528" + }, + { + "name":"富里乡", + "code":"713529" + }, + { + "name":"秀林乡", + "code":"713530" + }, + { + "name":"万荣乡", + "code":"713531" + }, + { + "name":"卓溪乡", + "code":"713532" + } + ] + }, + { + "name":"澎湖县", + "code":"713600", + "sub":[ + { + "name":"马公市", + "code":"713601" + }, + { + "name":"湖西乡", + "code":"713621" + }, + { + "name":"白沙乡", + "code":"713622" + }, + { + "name":"西屿乡", + "code":"713623" + }, + { + "name":"望安乡", + "code":"713624" + }, + { + "name":"七美乡", + "code":"713625" + } + ] + } + ] + }, + { + "name":"香港特别行政区", + "code":"810000", + "sub":[ + { + "name":"香港岛", + "code":"810100", + "sub":[ + { + "name":"中西区", + "code":"810101" + }, + { + "name":"湾仔区", + "code":"810102" + }, + { + "name":"东区", + "code":"810103" + }, + { + "name":"南区", + "code":"810104" + } + ] + }, + { + "name":"九龙", + "code":"810200", + "sub":[ + { + "name":"油尖旺区", + "code":"810201" + }, + { + "name":"深水埗区", + "code":"810202" + }, + { + "name":"九龙城区", + "code":"810203" + }, + { + "name":"黄大仙区", + "code":"810204" + }, + { + "name":"观塘区", + "code":"810205" + } + ] + }, + { + "name":"新界", + "code":"810300", + "sub":[ + { + "name":"荃湾区", + "code":"810301" + }, + { + "name":"屯门区", + "code":"810302" + }, + { + "name":"元朗区", + "code":"810303" + }, + { + "name":"北区", + "code":"810304" + }, + { + "name":"大埔区", + "code":"810305" + }, + { + "name":"西贡区", + "code":"810306" + }, + { + "name":"沙田区", + "code":"810307" + }, + { + "name":"葵青区", + "code":"810308" + }, + { + "name":"离岛区", + "code":"810309" + } + ] + } + ] + }, + { + "name":"澳门特别行政区", + "code":"820000", + "sub":[ + { + "name":"澳门半岛", + "code":"820100", + "sub":[ + { + "name":"花地玛堂区", + "code":"820101" + }, + { + "name":"圣安多尼堂区", + "code":"820102" + }, + { + "name":"大堂区", + "code":"820103" + }, + { + "name":"望德堂区", + "code":"820104" + }, + { + "name":"风顺堂区", + "code":"820105" + } + ] + }, + { + "name":"氹仔岛", + "code":"820200", + "sub":[ + { + "name":"嘉模堂区", + "code":"820201" + } + ] + }, + { + "name":"路环岛", + "code":"820300", + "sub":[ + { + "name":"圣方济各堂区", + "code":"820301" + } + ] + } + ] + } + +]; +}($); +// jshint ignore: end + +/* global $:true */ +/* jshint unused:false*/ + ++ function($) { + "use strict"; + + var defaults; + var raw = $.rawCitiesData; + + var format = function(data) { + var result = []; + for(var i=0;i= 0; + + /** + * Android requires exceptions. + * + * @type boolean + */ + var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone; + + + /** + * iOS requires exceptions. + * + * @type boolean + */ + var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone; + + + /** + * iOS 4 requires an exception for select elements. + * + * @type boolean + */ + var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent); + + + /** + * iOS 6.0-7.* requires the target element to be manually derived + * + * @type boolean + */ + var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent); + + /** + * BlackBerry requires exceptions. + * + * @type boolean + */ + var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0; + + /** + * Determine whether a given element requires a native click. + * + * @param {EventTarget|Element} target Target DOM element + * @returns {boolean} Returns true if the element needs a native click + */ + FastClick.prototype.needsClick = function(target) { + switch (target.nodeName.toLowerCase()) { + + // Don't send a synthetic click to disabled inputs (issue #62) + case 'button': + case 'select': + case 'textarea': + if (target.disabled) { + return true; + } + + break; + case 'input': + + // File inputs need real clicks on iOS 6 due to a browser bug (issue #68) + if ((deviceIsIOS && target.type === 'file') || target.disabled) { + return true; + } + + break; + case 'label': + case 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames + case 'video': + return true; + } + + return (/\bneedsclick\b/).test(target.className); + }; + + + /** + * Determine whether a given element requires a call to focus to simulate click into element. + * + * @param {EventTarget|Element} target Target DOM element + * @returns {boolean} Returns true if the element requires a call to focus to simulate native click. + */ + FastClick.prototype.needsFocus = function(target) { + switch (target.nodeName.toLowerCase()) { + case 'textarea': + return true; + case 'select': + return !deviceIsAndroid; + case 'input': + switch (target.type) { + case 'button': + case 'checkbox': + case 'file': + case 'image': + case 'radio': + case 'submit': + return false; + } + + // No point in attempting to focus disabled inputs + return !target.disabled && !target.readOnly; + default: + return (/\bneedsfocus\b/).test(target.className); + } + }; + + + /** + * Send a click event to the specified element. + * + * @param {EventTarget|Element} targetElement + * @param {Event} event + */ + FastClick.prototype.sendClick = function(targetElement, event) { + var clickEvent, touch; + + // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24) + if (document.activeElement && document.activeElement !== targetElement) { + document.activeElement.blur(); + } + + touch = event.changedTouches[0]; + + // Synthesise a click event, with an extra attribute so it can be tracked + clickEvent = document.createEvent('MouseEvents'); + clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null); + clickEvent.forwardedTouchEvent = true; + targetElement.dispatchEvent(clickEvent); + }; + + FastClick.prototype.determineEventType = function(targetElement) { + + //Issue #159: Android Chrome Select Box does not open with a synthetic click event + if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') { + return 'mousedown'; + } + + return 'click'; + }; + + + /** + * @param {EventTarget|Element} targetElement + */ + FastClick.prototype.focus = function(targetElement) { + var length; + + // Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724. + if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') { + length = targetElement.value.length; + targetElement.setSelectionRange(length, length); + } else { + targetElement.focus(); + } + }; + + + /** + * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it. + * + * @param {EventTarget|Element} targetElement + */ + FastClick.prototype.updateScrollParent = function(targetElement) { + var scrollParent, parentElement; + + scrollParent = targetElement.fastClickScrollParent; + + // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the + // target element was moved to another parent. + if (!scrollParent || !scrollParent.contains(targetElement)) { + parentElement = targetElement; + do { + if (parentElement.scrollHeight > parentElement.offsetHeight) { + scrollParent = parentElement; + targetElement.fastClickScrollParent = parentElement; + break; + } + + parentElement = parentElement.parentElement; + } while (parentElement); + } + + // Always update the scroll top tracker if possible. + if (scrollParent) { + scrollParent.fastClickLastScrollTop = scrollParent.scrollTop; + } + }; + + + /** + * @param {EventTarget} targetElement + * @returns {Element|EventTarget} + */ + FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) { + + // On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node. + if (eventTarget.nodeType === Node.TEXT_NODE) { + return eventTarget.parentNode; + } + + return eventTarget; + }; + + + /** + * On touch start, record the position and scroll offset. + * + * @param {Event} event + * @returns {boolean} + */ + FastClick.prototype.onTouchStart = function(event) { + var targetElement, touch, selection; + + // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111). + if (event.targetTouches.length > 1) { + return true; + } + + targetElement = this.getTargetElementFromEventTarget(event.target); + touch = event.targetTouches[0]; + + if (deviceIsIOS) { + + // Only trusted events will deselect text on iOS (issue #49) + selection = window.getSelection(); + if (selection.rangeCount && !selection.isCollapsed) { + return true; + } + + if (!deviceIsIOS4) { + + // Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23): + // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched + // with the same identifier as the touch event that previously triggered the click that triggered the alert. + // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an + // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform. + // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string, + // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long, + // random integers, it's safe to to continue if the identifier is 0 here. + if (touch.identifier && touch.identifier === this.lastTouchIdentifier) { + event.preventDefault(); + return false; + } + + this.lastTouchIdentifier = touch.identifier; + + // If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and: + // 1) the user does a fling scroll on the scrollable layer + // 2) the user stops the fling scroll with another tap + // then the event.target of the last 'touchend' event will be the element that was under the user's finger + // when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check + // is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42). + this.updateScrollParent(targetElement); + } + } + + this.trackingClick = true; + this.trackingClickStart = event.timeStamp; + this.targetElement = targetElement; + + this.touchStartX = touch.pageX; + this.touchStartY = touch.pageY; + + // Prevent phantom clicks on fast double-tap (issue #36) + if ((event.timeStamp - this.lastClickTime) < this.tapDelay) { + event.preventDefault(); + } + + return true; + }; + + + /** + * Based on a touchmove event object, check whether the touch has moved past a boundary since it started. + * + * @param {Event} event + * @returns {boolean} + */ + FastClick.prototype.touchHasMoved = function(event) { + var touch = event.changedTouches[0], boundary = this.touchBoundary; + + if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) { + return true; + } + + return false; + }; + + + /** + * Update the last position. + * + * @param {Event} event + * @returns {boolean} + */ + FastClick.prototype.onTouchMove = function(event) { + if (!this.trackingClick) { + return true; + } + + // If the touch has moved, cancel the click tracking + if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) { + this.trackingClick = false; + this.targetElement = null; + } + + return true; + }; + + + /** + * Attempt to find the labelled control for the given label element. + * + * @param {EventTarget|HTMLLabelElement} labelElement + * @returns {Element|null} + */ + FastClick.prototype.findControl = function(labelElement) { + + // Fast path for newer browsers supporting the HTML5 control attribute + if (labelElement.control !== undefined) { + return labelElement.control; + } + + // All browsers under test that support touch events also support the HTML5 htmlFor attribute + if (labelElement.htmlFor) { + return document.getElementById(labelElement.htmlFor); + } + + // If no for attribute exists, attempt to retrieve the first labellable descendant element + // the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label + return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea'); + }; + + + /** + * On touch end, determine whether to send a click event at once. + * + * @param {Event} event + * @returns {boolean} + */ + FastClick.prototype.onTouchEnd = function(event) { + var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement; + + if (!this.trackingClick) { + return true; + } + + // Prevent phantom clicks on fast double-tap (issue #36) + if ((event.timeStamp - this.lastClickTime) < this.tapDelay) { + this.cancelNextClick = true; + return true; + } + + if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) { + return true; + } + + // Reset to prevent wrong click cancel on input (issue #156). + this.cancelNextClick = false; + + this.lastClickTime = event.timeStamp; + + trackingClickStart = this.trackingClickStart; + this.trackingClick = false; + this.trackingClickStart = 0; + + // On some iOS devices, the targetElement supplied with the event is invalid if the layer + // is performing a transition or scroll, and has to be re-detected manually. Note that + // for this to function correctly, it must be called *after* the event target is checked! + // See issue #57; also filed as rdar://13048589 . + if (deviceIsIOSWithBadTarget) { + touch = event.changedTouches[0]; + + // In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null + targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement; + targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent; + } + + targetTagName = targetElement.tagName.toLowerCase(); + if (targetTagName === 'label') { + forElement = this.findControl(targetElement); + if (forElement) { + this.focus(targetElement); + if (deviceIsAndroid) { + return false; + } + + targetElement = forElement; + } + } else if (this.needsFocus(targetElement)) { + + // Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through. + // Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37). + if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) { + this.targetElement = null; + return false; + } + + this.focus(targetElement); + this.sendClick(targetElement, event); + + // Select elements need the event to go through on iOS 4, otherwise the selector menu won't open. + // Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others) + if (!deviceIsIOS || targetTagName !== 'select') { + this.targetElement = null; + event.preventDefault(); + } + + return false; + } + + if (deviceIsIOS && !deviceIsIOS4) { + + // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled + // and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42). + scrollParent = targetElement.fastClickScrollParent; + if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) { + return true; + } + } + + // Prevent the actual click from going though - unless the target node is marked as requiring + // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted. + if (!this.needsClick(targetElement)) { + event.preventDefault(); + this.sendClick(targetElement, event); + } + + return false; + }; + + + /** + * On touch cancel, stop tracking the click. + * + * @returns {void} + */ + FastClick.prototype.onTouchCancel = function() { + this.trackingClick = false; + this.targetElement = null; + }; + + + /** + * Determine mouse events which should be permitted. + * + * @param {Event} event + * @returns {boolean} + */ + FastClick.prototype.onMouse = function(event) { + + // If a target element was never set (because a touch event was never fired) allow the event + if (!this.targetElement) { + return true; + } + + if (event.forwardedTouchEvent) { + return true; + } + + // Programmatically generated events targeting a specific element should be permitted + if (!event.cancelable) { + return true; + } + + // Derive and check the target element to see whether the mouse event needs to be permitted; + // unless explicitly enabled, prevent non-touch click events from triggering actions, + // to prevent ghost/doubleclicks. + if (!this.needsClick(this.targetElement) || this.cancelNextClick) { + + // Prevent any user-added listeners declared on FastClick element from being fired. + if (event.stopImmediatePropagation) { + event.stopImmediatePropagation(); + } else { + + // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2) + event.propagationStopped = true; + } + + // Cancel the event + event.stopPropagation(); + event.preventDefault(); + + return false; + } + + // If the mouse event is permitted, return true for the action to go through. + return true; + }; + + + /** + * On actual clicks, determine whether this is a touch-generated click, a click action occurring + * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or + * an actual click which should be permitted. + * + * @param {Event} event + * @returns {boolean} + */ + FastClick.prototype.onClick = function(event) { + var permitted; + + // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early. + if (this.trackingClick) { + this.targetElement = null; + this.trackingClick = false; + return true; + } + + // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target. + if (event.target.type === 'submit' && event.detail === 0) { + return true; + } + + permitted = this.onMouse(event); + + // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through. + if (!permitted) { + this.targetElement = null; + } + + // If clicks are permitted, return true for the action to go through. + return permitted; + }; + + + /** + * Remove all FastClick's event listeners. + * + * @returns {void} + */ + FastClick.prototype.destroy = function() { + var layer = this.layer; + + if (deviceIsAndroid) { + layer.removeEventListener('mouseover', this.onMouse, true); + layer.removeEventListener('mousedown', this.onMouse, true); + layer.removeEventListener('mouseup', this.onMouse, true); + } + + layer.removeEventListener('click', this.onClick, true); + layer.removeEventListener('touchstart', this.onTouchStart, false); + layer.removeEventListener('touchmove', this.onTouchMove, false); + layer.removeEventListener('touchend', this.onTouchEnd, false); + layer.removeEventListener('touchcancel', this.onTouchCancel, false); + }; + + + /** + * Check whether FastClick is needed. + * + * @param {Element} layer The layer to listen on + */ + FastClick.notNeeded = function(layer) { + var metaViewport; + var chromeVersion; + var blackberryVersion; + var firefoxVersion; + + // Devices that don't support touch don't need FastClick + if (typeof window.ontouchstart === 'undefined') { + return true; + } + + // Chrome version - zero for other browsers + chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1]; + + if (chromeVersion) { + + if (deviceIsAndroid) { + metaViewport = document.querySelector('meta[name=viewport]'); + + if (metaViewport) { + // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89) + if (metaViewport.content.indexOf('user-scalable=no') !== -1) { + return true; + } + // Chrome 32 and above with width=device-width or less don't need FastClick + if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) { + return true; + } + } + + // Chrome desktop doesn't need FastClick (issue #15) + } else { + return true; + } + } + + if (deviceIsBlackBerry10) { + blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/); + + // BlackBerry 10.3+ does not require Fastclick library. + // https://github.com/ftlabs/fastclick/issues/251 + if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) { + metaViewport = document.querySelector('meta[name=viewport]'); + + if (metaViewport) { + // user-scalable=no eliminates click delay. + if (metaViewport.content.indexOf('user-scalable=no') !== -1) { + return true; + } + // width=device-width (or less than device-width) eliminates click delay. + if (document.documentElement.scrollWidth <= window.outerWidth) { + return true; + } + } + } + } + + // IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97) + if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') { + return true; + } + + // Firefox version - zero for other browsers + firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1]; + + if (firefoxVersion >= 27) { + // Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896 + + metaViewport = document.querySelector('meta[name=viewport]'); + if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) { + return true; + } + } + + // IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version + // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx + if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') { + return true; + } + + return false; + }; + + + /** + * Factory method for creating a FastClick object + * + * @param {Element} layer The layer to listen on + * @param {Object} [options={}] The options to override the defaults + */ + FastClick.attach = function(layer, options) { + return new FastClick(layer, options); + }; + + if("addEventListener" in document){document.addEventListener("DOMContentLoaded",function(){FastClick.attach(document.body)},false)} + + if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) { + + // AMD. Register as an anonymous module. + define(function() { + return FastClick; + }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = FastClick.attach; + module.exports.FastClick = FastClick; + } else { + window.FastClick = FastClick; + } +}()); diff --git a/static/mobile/lib/fastclick.min.js b/static/mobile/lib/fastclick.min.js new file mode 100644 index 000000000..04e2a7fbe --- /dev/null +++ b/static/mobile/lib/fastclick.min.js @@ -0,0 +1,3 @@ +/* fastclick v1.0.6 */ +!function(){"use strict";function t(e,o){function i(t,e){return function(){return t.apply(e,arguments)}}var r;if(o=o||{},this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.touchBoundary=o.touchBoundary||10,this.layer=e,this.tapDelay=o.tapDelay||200,this.tapTimeout=o.tapTimeout||700,!t.notNeeded(e)){for(var a=["onMouse","onClick","onTouchStart","onTouchMove","onTouchEnd","onTouchCancel"],c=this,s=0,u=a.length;u>s;s++)c[a[s]]=i(c[a[s]],c);n&&(e.addEventListener("mouseover",this.onMouse,!0),e.addEventListener("mousedown",this.onMouse,!0),e.addEventListener("mouseup",this.onMouse,!0)),e.addEventListener("click",this.onClick,!0),e.addEventListener("touchstart",this.onTouchStart,!1),e.addEventListener("touchmove",this.onTouchMove,!1),e.addEventListener("touchend",this.onTouchEnd,!1),e.addEventListener("touchcancel",this.onTouchCancel,!1),Event.prototype.stopImmediatePropagation||(e.removeEventListener=function(t,n,o){var i=Node.prototype.removeEventListener;"click"===t?i.call(e,t,n.hijacked||n,o):i.call(e,t,n,o)},e.addEventListener=function(t,n,o){var i=Node.prototype.addEventListener;"click"===t?i.call(e,t,n.hijacked||(n.hijacked=function(t){t.propagationStopped||n(t)}),o):i.call(e,t,n,o)}),"function"==typeof e.onclick&&(r=e.onclick,e.addEventListener("click",function(t){r(t)},!1),e.onclick=null)}}var e=navigator.userAgent.indexOf("Windows Phone")>=0,n=navigator.userAgent.indexOf("Android")>0&&!e,o=/iP(ad|hone|od)/.test(navigator.userAgent)&&!e,i=o&&/OS 4_\d(_\d)?/.test(navigator.userAgent),r=o&&/OS [6-7]_\d/.test(navigator.userAgent),a=navigator.userAgent.indexOf("BB10")>0;t.prototype.needsClick=function(t){switch(t.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(t.disabled)return!0;break;case"input":if(o&&"file"===t.type||t.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(t.className)},t.prototype.needsFocus=function(t){switch(t.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!n;case"input":switch(t.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!t.disabled&&!t.readOnly;default:return/\bneedsfocus\b/.test(t.className)}},t.prototype.sendClick=function(t,e){var n,o;document.activeElement&&document.activeElement!==t&&document.activeElement.blur(),o=e.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(t),!0,!0,window,1,o.screenX,o.screenY,o.clientX,o.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,t.dispatchEvent(n)},t.prototype.determineEventType=function(t){return n&&"select"===t.tagName.toLowerCase()?"mousedown":"click"},t.prototype.focus=function(t){var e;o&&t.setSelectionRange&&0!==t.type.indexOf("date")&&"time"!==t.type&&"month"!==t.type?(e=t.value.length,t.setSelectionRange(e,e)):t.focus()},t.prototype.updateScrollParent=function(t){var e,n;if(e=t.fastClickScrollParent,!e||!e.contains(t)){n=t;do{if(n.scrollHeight>n.offsetHeight){e=n,t.fastClickScrollParent=n;break}n=n.parentElement}while(n)}e&&(e.fastClickLastScrollTop=e.scrollTop)},t.prototype.getTargetElementFromEventTarget=function(t){return t.nodeType===Node.TEXT_NODE?t.parentNode:t},t.prototype.onTouchStart=function(t){var e,n,r;if(t.targetTouches.length>1)return!0;if(e=this.getTargetElementFromEventTarget(t.target),n=t.targetTouches[0],o){if(r=window.getSelection(),r.rangeCount&&!r.isCollapsed)return!0;if(!i){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return t.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(e)}}return this.trackingClick=!0,this.trackingClickStart=t.timeStamp,this.targetElement=e,this.touchStartX=n.pageX,this.touchStartY=n.pageY,t.timeStamp-this.lastClickTimen||Math.abs(e.pageY-this.touchStartY)>n?!0:!1},t.prototype.onTouchMove=function(t){return this.trackingClick?((this.targetElement!==this.getTargetElementFromEventTarget(t.target)||this.touchHasMoved(t))&&(this.trackingClick=!1,this.targetElement=null),!0):!0},t.prototype.findControl=function(t){return void 0!==t.control?t.control:t.htmlFor?document.getElementById(t.htmlFor):t.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},t.prototype.onTouchEnd=function(t){var e,a,c,s,u,l=this.targetElement;if(!this.trackingClick)return!0;if(t.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=t.timeStamp,a=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,r&&(u=t.changedTouches[0],l=document.elementFromPoint(u.pageX-window.pageXOffset,u.pageY-window.pageYOffset)||l,l.fastClickScrollParent=this.targetElement.fastClickScrollParent),c=l.tagName.toLowerCase(),"label"===c){if(e=this.findControl(l)){if(this.focus(l),n)return!1;l=e}}else if(this.needsFocus(l))return t.timeStamp-a>100||o&&window.top!==window&&"input"===c?(this.targetElement=null,!1):(this.focus(l),this.sendClick(l,t),o&&"select"===c||(this.targetElement=null,t.preventDefault()),!1);return o&&!i&&(s=l.fastClickScrollParent,s&&s.fastClickLastScrollTop!==s.scrollTop)?!0:(this.needsClick(l)||(t.preventDefault(),this.sendClick(l,t)),!1)},t.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},t.prototype.onMouse=function(t){return this.targetElement?t.forwardedTouchEvent?!0:t.cancelable&&(!this.needsClick(this.targetElement)||this.cancelNextClick)?(t.stopImmediatePropagation?t.stopImmediatePropagation():t.propagationStopped=!0,t.stopPropagation(),t.preventDefault(),!1):!0:!0},t.prototype.onClick=function(t){var e;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===t.target.type&&0===t.detail?!0:(e=this.onMouse(t),e||(this.targetElement=null),e)},t.prototype.destroy=function(){var t=this.layer;n&&(t.removeEventListener("mouseover",this.onMouse,!0),t.removeEventListener("mousedown",this.onMouse,!0),t.removeEventListener("mouseup",this.onMouse,!0)),t.removeEventListener("click",this.onClick,!0),t.removeEventListener("touchstart",this.onTouchStart,!1),t.removeEventListener("touchmove",this.onTouchMove,!1),t.removeEventListener("touchend",this.onTouchEnd,!1),t.removeEventListener("touchcancel",this.onTouchCancel,!1)},t.notNeeded=function(t){var e,o,i,r;if("undefined"==typeof window.ontouchstart)return!0;if(o=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!n)return!0;if(e=document.querySelector("meta[name=viewport]")){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(o>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(a&&(i=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),i[1]>=10&&i[2]>=3&&(e=document.querySelector("meta[name=viewport]")))){if(-1!==e.content.indexOf("user-scalable=no"))return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===t.style.msTouchAction||"manipulation"===t.style.touchAction?!0:(r=+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1],r>=27&&(e=document.querySelector("meta[name=viewport]"),e&&(-1!==e.content.indexOf("user-scalable=no")||document.documentElement.scrollWidth<=window.outerWidth))?!0:"none"===t.style.touchAction||"manipulation"===t.style.touchAction?!0:!1)},t.attach=function(e,n){return new t(e,n)},"function"==typeof define&&"object"==typeof define.amd&&define.amd?define(function(){return t}):"undefined"!=typeof module&&module.exports?(module.exports=t.attach,module.exports.FastClick=t):window.FastClick=t}(); +if("addEventListener" in document){document.addEventListener("DOMContentLoaded",function(){m.attach(document.body)},false)} \ No newline at end of file diff --git a/static/mobile/lib/jquery-weui.js b/static/mobile/lib/jquery-weui.js new file mode 100644 index 000000000..3ba54c312 --- /dev/null +++ b/static/mobile/lib/jquery-weui.js @@ -0,0 +1,6441 @@ +/** +* jQuery WeUI V1.2.0 +* By 言川 +* http://lihongxun945.github.io/jquery-weui/ + */ +/* global $:true */ +/* global WebKitCSSMatrix:true */ + +(function($) { + "use strict"; + + $.fn.transitionEnd = function(callback) { + var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'], + i, dom = this; + + function fireCallBack(e) { + /*jshint validthis:true */ + if (e.target !== this) return; + callback.call(this, e); + for (i = 0; i < events.length; i++) { + dom.off(events[i], fireCallBack); + } + } + if (callback) { + for (i = 0; i < events.length; i++) { + dom.on(events[i], fireCallBack); + } + } + return this; + }; + + $.support = (function() { + var support = { + touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) + }; + return support; + })(); + + $.touchEvents = { + start: $.support.touch ? 'touchstart' : 'mousedown', + move: $.support.touch ? 'touchmove' : 'mousemove', + end: $.support.touch ? 'touchend' : 'mouseup' + }; + + $.getTouchPosition = function(e) { + e = e.originalEvent || e; //jquery wrap the originevent + if(e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend') { + return { + x: e.targetTouches[0].pageX, + y: e.targetTouches[0].pageY + }; + } else { + return { + x: e.pageX, + y: e.pageY + }; + } + }; + + $.fn.scrollHeight = function() { + return this[0].scrollHeight; + }; + + $.fn.transform = function(transform) { + for (var i = 0; i < this.length; i++) { + var elStyle = this[i].style; + elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform; + } + return this; + }; + $.fn.transition = function(duration) { + if (typeof duration !== 'string') { + duration = duration + 'ms'; + } + for (var i = 0; i < this.length; i++) { + var elStyle = this[i].style; + elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration; + } + return this; + }; + + $.getTranslate = function (el, axis) { + var matrix, curTransform, curStyle, transformMatrix; + + // automatic axis detection + if (typeof axis === 'undefined') { + axis = 'x'; + } + + curStyle = window.getComputedStyle(el, null); + if (window.WebKitCSSMatrix) { + // Some old versions of Webkit choke when 'none' is passed; pass + // empty string instead in this case + transformMatrix = new WebKitCSSMatrix(curStyle.webkitTransform === 'none' ? '' : curStyle.webkitTransform); + } + else { + transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,'); + matrix = transformMatrix.toString().split(','); + } + + if (axis === 'x') { + //Latest Chrome and webkits Fix + if (window.WebKitCSSMatrix) + curTransform = transformMatrix.m41; + //Crazy IE10 Matrix + else if (matrix.length === 16) + curTransform = parseFloat(matrix[12]); + //Normal Browsers + else + curTransform = parseFloat(matrix[4]); + } + if (axis === 'y') { + //Latest Chrome and webkits Fix + if (window.WebKitCSSMatrix) + curTransform = transformMatrix.m42; + //Crazy IE10 Matrix + else if (matrix.length === 16) + curTransform = parseFloat(matrix[13]); + //Normal Browsers + else + curTransform = parseFloat(matrix[5]); + } + + return curTransform || 0; + }; + $.requestAnimationFrame = function (callback) { + if (window.requestAnimationFrame) return window.requestAnimationFrame(callback); + else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback); + else if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame(callback); + else { + return window.setTimeout(callback, 1000 / 60); + } + }; + + $.cancelAnimationFrame = function (id) { + if (window.cancelAnimationFrame) return window.cancelAnimationFrame(id); + else if (window.webkitCancelAnimationFrame) return window.webkitCancelAnimationFrame(id); + else if (window.mozCancelAnimationFrame) return window.mozCancelAnimationFrame(id); + else { + return window.clearTimeout(id); + } + }; + + $.fn.join = function(arg) { + return this.toArray().join(arg); + } +})($); + +/*=========================== + Template7 Template engine + ===========================*/ +/* global $:true */ +/* jshint unused:false */ +/* jshint forin:false */ ++function ($) { + "use strict"; + $.Template7 = $.t7 = (function () { + function isArray(arr) { + return Object.prototype.toString.apply(arr) === '[object Array]'; + } + function isObject(obj) { + return obj instanceof Object; + } + function isFunction(func) { + return typeof func === 'function'; + } + var cache = {}; + function helperToSlices(string) { + var helperParts = string.replace(/[{}#}]/g, '').split(' '); + var slices = []; + var shiftIndex, i, j; + for (i = 0; i < helperParts.length; i++) { + var part = helperParts[i]; + if (i === 0) slices.push(part); + else { + if (part.indexOf('"') === 0) { + // Plain String + if (part.match(/"/g).length === 2) { + // One word string + slices.push(part); + } + else { + // Find closed Index + shiftIndex = 0; + for (j = i + 1; j < helperParts.length; j++) { + part += ' ' + helperParts[j]; + if (helperParts[j].indexOf('"') >= 0) { + shiftIndex = j; + slices.push(part); + break; + } + } + if (shiftIndex) i = shiftIndex; + } + } + else { + if (part.indexOf('=') > 0) { + // Hash + var hashParts = part.split('='); + var hashName = hashParts[0]; + var hashContent = hashParts[1]; + if (hashContent.match(/"/g).length !== 2) { + shiftIndex = 0; + for (j = i + 1; j < helperParts.length; j++) { + hashContent += ' ' + helperParts[j]; + if (helperParts[j].indexOf('"') >= 0) { + shiftIndex = j; + break; + } + } + if (shiftIndex) i = shiftIndex; + } + var hash = [hashName, hashContent.replace(/"/g,'')]; + slices.push(hash); + } + else { + // Plain variable + slices.push(part); + } + } + } + } + return slices; + } + function stringToBlocks(string) { + var blocks = [], i, j, k; + if (!string) return []; + var _blocks = string.split(/({{[^{^}]*}})/); + for (i = 0; i < _blocks.length; i++) { + var block = _blocks[i]; + if (block === '') continue; + if (block.indexOf('{{') < 0) { + blocks.push({ + type: 'plain', + content: block + }); + } + else { + if (block.indexOf('{/') >= 0) { + continue; + } + if (block.indexOf('{#') < 0 && block.indexOf(' ') < 0 && block.indexOf('else') < 0) { + // Simple variable + blocks.push({ + type: 'variable', + contextName: block.replace(/[{}]/g, '') + }); + continue; + } + // Helpers + var helperSlices = helperToSlices(block); + var helperName = helperSlices[0]; + var helperContext = []; + var helperHash = {}; + for (j = 1; j < helperSlices.length; j++) { + var slice = helperSlices[j]; + if (isArray(slice)) { + // Hash + helperHash[slice[0]] = slice[1] === 'false' ? false : slice[1]; + } + else { + helperContext.push(slice); + } + } + + if (block.indexOf('{#') >= 0) { + // Condition/Helper + var helperStartIndex = i; + var helperContent = ''; + var elseContent = ''; + var toSkip = 0; + var shiftIndex; + var foundClosed = false, foundElse = false, foundClosedElse = false, depth = 0; + for (j = i + 1; j < _blocks.length; j++) { + if (_blocks[j].indexOf('{{#') >= 0) { + depth ++; + } + if (_blocks[j].indexOf('{{/') >= 0) { + depth --; + } + if (_blocks[j].indexOf('{{#' + helperName) >= 0) { + helperContent += _blocks[j]; + if (foundElse) elseContent += _blocks[j]; + toSkip ++; + } + else if (_blocks[j].indexOf('{{/' + helperName) >= 0) { + if (toSkip > 0) { + toSkip--; + helperContent += _blocks[j]; + if (foundElse) elseContent += _blocks[j]; + } + else { + shiftIndex = j; + foundClosed = true; + break; + } + } + else if (_blocks[j].indexOf('else') >= 0 && depth === 0) { + foundElse = true; + } + else { + if (!foundElse) helperContent += _blocks[j]; + if (foundElse) elseContent += _blocks[j]; + } + + } + if (foundClosed) { + if (shiftIndex) i = shiftIndex; + blocks.push({ + type: 'helper', + helperName: helperName, + contextName: helperContext, + content: helperContent, + inverseContent: elseContent, + hash: helperHash + }); + } + } + else if (block.indexOf(' ') > 0) { + blocks.push({ + type: 'helper', + helperName: helperName, + contextName: helperContext, + hash: helperHash + }); + } + } + } + return blocks; + } + var Template7 = function (template) { + var t = this; + t.template = template; + + function getCompileFn(block, depth) { + if (block.content) return compile(block.content, depth); + else return function () {return ''; }; + } + function getCompileInverse(block, depth) { + if (block.inverseContent) return compile(block.inverseContent, depth); + else return function () {return ''; }; + } + function getCompileVar(name, ctx) { + var variable, parts, levelsUp = 0, initialCtx = ctx; + if (name.indexOf('../') === 0) { + levelsUp = name.split('../').length - 1; + var newDepth = ctx.split('_')[1] - levelsUp; + ctx = 'ctx_' + (newDepth >= 1 ? newDepth : 1); + parts = name.split('../')[levelsUp].split('.'); + } + else if (name.indexOf('@global') === 0) { + ctx = '$.Template7.global'; + parts = name.split('@global.')[1].split('.'); + } + else if (name.indexOf('@root') === 0) { + ctx = 'ctx_1'; + parts = name.split('@root.')[1].split('.'); + } + else { + parts = name.split('.'); + } + variable = ctx; + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + if (part.indexOf('@') === 0) { + if (i > 0) { + variable += '[(data && data.' + part.replace('@', '') + ')]'; + } + else { + variable = '(data && data.' + name.replace('@', '') + ')'; + } + } + else { + if (isFinite(part)) { + variable += '[' + part + ']'; + } + else { + if (part.indexOf('this') === 0) { + variable = part.replace('this', ctx); + } + else { + variable += '.' + part; + } + } + } + } + + return variable; + } + function getCompiledArguments(contextArray, ctx) { + var arr = []; + for (var i = 0; i < contextArray.length; i++) { + if (contextArray[i].indexOf('"') === 0) arr.push(contextArray[i]); + else { + arr.push(getCompileVar(contextArray[i], ctx)); + } + } + return arr.join(', '); + } + function compile(template, depth) { + depth = depth || 1; + template = template || t.template; + if (typeof template !== 'string') { + throw new Error('Template7: Template must be a string'); + } + var blocks = stringToBlocks(template); + if (blocks.length === 0) { + return function () { return ''; }; + } + var ctx = 'ctx_' + depth; + var resultString = '(function (' + ctx + ', data) {\n'; + if (depth === 1) { + resultString += 'function isArray(arr){return Object.prototype.toString.apply(arr) === \'[object Array]\';}\n'; + resultString += 'function isFunction(func){return (typeof func === \'function\');}\n'; + resultString += 'function c(val, ctx) {if (typeof val !== "undefined") {if (isFunction(val)) {return val.call(ctx);} else return val;} else return "";}\n'; + } + resultString += 'var r = \'\';\n'; + var i, j, context; + for (i = 0; i < blocks.length; i++) { + var block = blocks[i]; + // Plain block + if (block.type === 'plain') { + resultString += 'r +=\'' + (block.content).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/'/g, '\\' + '\'') + '\';'; + continue; + } + var variable, compiledArguments; + // Variable block + if (block.type === 'variable') { + variable = getCompileVar(block.contextName, ctx); + resultString += 'r += c(' + variable + ', ' + ctx + ');'; + } + // Helpers block + if (block.type === 'helper') { + if (block.helperName in t.helpers) { + compiledArguments = getCompiledArguments(block.contextName, ctx); + resultString += 'r += ($.Template7.helpers.' + block.helperName + ').call(' + ctx + ', ' + (compiledArguments && (compiledArguments + ', ')) +'{hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});'; + } + else { + if (block.contextName.length > 0) { + throw new Error('Template7: Missing helper: "' + block.helperName + '"'); + } + else { + variable = getCompileVar(block.helperName, ctx); + resultString += 'if (' + variable + ') {'; + resultString += 'if (isArray(' + variable + ')) {'; + resultString += 'r += ($.Template7.helpers.each).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});'; + resultString += '}else {'; + resultString += 'r += ($.Template7.helpers.with).call(' + ctx + ', ' + variable + ', {hash:' + JSON.stringify(block.hash) + ', data: data || {}, fn: ' + getCompileFn(block, depth+1) + ', inverse: ' + getCompileInverse(block, depth+1) + ', root: ctx_1});'; + resultString += '}}'; + } + } + } + } + resultString += '\nreturn r;})'; + return eval.call(window, resultString); + } + t.compile = function (template) { + if (!t.compiled) { + t.compiled = compile(template); + } + return t.compiled; + }; + }; + Template7.prototype = { + options: {}, + helpers: { + 'if': function (context, options) { + if (isFunction(context)) { context = context.call(this); } + if (context) { + return options.fn(this, options.data); + } + else { + return options.inverse(this, options.data); + } + }, + 'unless': function (context, options) { + if (isFunction(context)) { context = context.call(this); } + if (!context) { + return options.fn(this, options.data); + } + else { + return options.inverse(this, options.data); + } + }, + 'each': function (context, options) { + var ret = '', i = 0; + if (isFunction(context)) { context = context.call(this); } + if (isArray(context)) { + if (options.hash.reverse) { + context = context.reverse(); + } + for (i = 0; i < context.length; i++) { + ret += options.fn(context[i], {first: i === 0, last: i === context.length - 1, index: i}); + } + if (options.hash.reverse) { + context = context.reverse(); + } + } + else { + for (var key in context) { + i++; + ret += options.fn(context[key], {key: key}); + } + } + if (i > 0) return ret; + else return options.inverse(this); + }, + 'with': function (context, options) { + if (isFunction(context)) { context = context.call(this); } + return options.fn(context); + }, + 'join': function (context, options) { + if (isFunction(context)) { context = context.call(this); } + return context.join(options.hash.delimiter || options.hash.delimeter); + }, + 'js': function (expression, options) { + var func; + if (expression.indexOf('return')>=0) { + func = '(function(){'+expression+'})'; + } + else { + func = '(function(){return ('+expression+')})'; + } + return eval.call(this, func).call(this); + }, + 'js_compare': function (expression, options) { + var func; + if (expression.indexOf('return')>=0) { + func = '(function(){'+expression+'})'; + } + else { + func = '(function(){return ('+expression+')})'; + } + var condition = eval.call(this, func).call(this); + if (condition) { + return options.fn(this, options.data); + } + else { + return options.inverse(this, options.data); + } + } + } + }; + var t7 = function (template, data) { + if (arguments.length === 2) { + var instance = new Template7(template); + var rendered = instance.compile()(data); + instance = null; + return (rendered); + } + else return new Template7(template); + }; + t7.registerHelper = function (name, fn) { + Template7.prototype.helpers[name] = fn; + }; + t7.unregisterHelper = function (name) { + Template7.prototype.helpers[name] = undefined; + delete Template7.prototype.helpers[name]; + }; + + t7.compile = function (template, options) { + var instance = new Template7(template, options); + return instance.compile(); + }; + + t7.options = Template7.prototype.options; + t7.helpers = Template7.prototype.helpers; + return t7; + })(); +}($); + +/*! Hammer.JS - v2.0.8 - 2016-04-23 + * http://hammerjs.github.io/ + * + * Copyright (c) 2016 Jorik Tangelder; + * Licensed under the MIT license */ +(function(window, document, exportName, undefined) { + 'use strict'; + +var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; +var TEST_ELEMENT = document.createElement('div'); + +var TYPE_FUNCTION = 'function'; + +var round = Math.round; +var abs = Math.abs; +var now = Date.now; + +/** + * set a timeout with a given scope + * @param {Function} fn + * @param {Number} timeout + * @param {Object} context + * @returns {number} + */ +function setTimeoutContext(fn, timeout, context) { + return setTimeout(bindFn(fn, context), timeout); +} + +/** + * if the argument is an array, we want to execute the fn on each entry + * if it aint an array we don't want to do a thing. + * this is used by all the methods that accept a single and array argument. + * @param {*|Array} arg + * @param {String} fn + * @param {Object} [context] + * @returns {Boolean} + */ +function invokeArrayArg(arg, fn, context) { + if (Array.isArray(arg)) { + each(arg, context[fn], context); + return true; + } + return false; +} + +/** + * walk objects and arrays + * @param {Object} obj + * @param {Function} iterator + * @param {Object} context + */ +function each(obj, iterator, context) { + var i; + + if (!obj) { + return; + } + + if (obj.forEach) { + obj.forEach(iterator, context); + } else if (obj.length !== undefined) { + i = 0; + while (i < obj.length) { + iterator.call(context, obj[i], i, obj); + i++; + } + } else { + for (i in obj) { + obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); + } + } +} + +/** + * wrap a method with a deprecation warning and stack trace + * @param {Function} method + * @param {String} name + * @param {String} message + * @returns {Function} A new function wrapping the supplied method. + */ +function deprecate(method, name, message) { + var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; + return function() { + var e = new Error('get-stack-trace'); + var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '') + .replace(/^\s+at\s+/gm, '') + .replace(/^Object.\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; + + var log = window.console && (window.console.warn || window.console.log); + if (log) { + log.call(window.console, deprecationMessage, stack); + } + return method.apply(this, arguments); + }; +} + +/** + * extend object. + * means that properties in dest will be overwritten by the ones in src. + * @param {Object} target + * @param {...Object} objects_to_assign + * @returns {Object} target + */ +var assign; +if (typeof Object.assign !== 'function') { + assign = function assign(target) { + if (target === undefined || target === null) { + throw new TypeError('Cannot convert undefined or null to object'); + } + + var output = Object(target); + for (var index = 1; index < arguments.length; index++) { + var source = arguments[index]; + if (source !== undefined && source !== null) { + for (var nextKey in source) { + if (source.hasOwnProperty(nextKey)) { + output[nextKey] = source[nextKey]; + } + } + } + } + return output; + }; +} else { + assign = Object.assign; +} + +/** + * extend object. + * means that properties in dest will be overwritten by the ones in src. + * @param {Object} dest + * @param {Object} src + * @param {Boolean} [merge=false] + * @returns {Object} dest + */ +var extend = deprecate(function extend(dest, src, merge) { + var keys = Object.keys(src); + var i = 0; + while (i < keys.length) { + if (!merge || (merge && dest[keys[i]] === undefined)) { + dest[keys[i]] = src[keys[i]]; + } + i++; + } + return dest; +}, 'extend', 'Use `assign`.'); + +/** + * merge the values from src in the dest. + * means that properties that exist in dest will not be overwritten by src + * @param {Object} dest + * @param {Object} src + * @returns {Object} dest + */ +var merge = deprecate(function merge(dest, src) { + return extend(dest, src, true); +}, 'merge', 'Use `assign`.'); + +/** + * simple class inheritance + * @param {Function} child + * @param {Function} base + * @param {Object} [properties] + */ +function inherit(child, base, properties) { + var baseP = base.prototype, + childP; + + childP = child.prototype = Object.create(baseP); + childP.constructor = child; + childP._super = baseP; + + if (properties) { + assign(childP, properties); + } +} + +/** + * simple function bind + * @param {Function} fn + * @param {Object} context + * @returns {Function} + */ +function bindFn(fn, context) { + return function boundFn() { + return fn.apply(context, arguments); + }; +} + +/** + * let a boolean value also be a function that must return a boolean + * this first item in args will be used as the context + * @param {Boolean|Function} val + * @param {Array} [args] + * @returns {Boolean} + */ +function boolOrFn(val, args) { + if (typeof val == TYPE_FUNCTION) { + return val.apply(args ? args[0] || undefined : undefined, args); + } + return val; +} + +/** + * use the val2 when val1 is undefined + * @param {*} val1 + * @param {*} val2 + * @returns {*} + */ +function ifUndefined(val1, val2) { + return (val1 === undefined) ? val2 : val1; +} + +/** + * addEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler + */ +function addEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.addEventListener(type, handler, false); + }); +} + +/** + * removeEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler + */ +function removeEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.removeEventListener(type, handler, false); + }); +} + +/** + * find if a node is in the given parent + * @method hasParent + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @return {Boolean} found + */ +function hasParent(node, parent) { + while (node) { + if (node == parent) { + return true; + } + node = node.parentNode; + } + return false; +} + +/** + * small indexOf wrapper + * @param {String} str + * @param {String} find + * @returns {Boolean} found + */ +function inStr(str, find) { + return str.indexOf(find) > -1; +} + +/** + * split string on whitespace + * @param {String} str + * @returns {Array} words + */ +function splitStr(str) { + return str.trim().split(/\s+/g); +} + +/** + * find if a array contains the object using indexOf or a simple polyFill + * @param {Array} src + * @param {String} find + * @param {String} [findByKey] + * @return {Boolean|Number} false when not found, or the index + */ +function inArray(src, find, findByKey) { + if (src.indexOf && !findByKey) { + return src.indexOf(find); + } else { + var i = 0; + while (i < src.length) { + if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { + return i; + } + i++; + } + return -1; + } +} + +/** + * convert array-like objects to real arrays + * @param {Object} obj + * @returns {Array} + */ +function toArray(obj) { + return Array.prototype.slice.call(obj, 0); +} + +/** + * unique array with objects based on a key (like 'id') or just by the array's value + * @param {Array} src [{id:1},{id:2},{id:1}] + * @param {String} [key] + * @param {Boolean} [sort=False] + * @returns {Array} [{id:1},{id:2}] + */ +function uniqueArray(src, key, sort) { + var results = []; + var values = []; + var i = 0; + + while (i < src.length) { + var val = key ? src[i][key] : src[i]; + if (inArray(values, val) < 0) { + results.push(src[i]); + } + values[i] = val; + i++; + } + + if (sort) { + if (!key) { + results = results.sort(); + } else { + results = results.sort(function sortUniqueArray(a, b) { + return a[key] > b[key]; + }); + } + } + + return results; +} + +/** + * get the prefixed property + * @param {Object} obj + * @param {String} property + * @returns {String|Undefined} prefixed + */ +function prefixed(obj, property) { + var prefix, prop; + var camelProp = property[0].toUpperCase() + property.slice(1); + + var i = 0; + while (i < VENDOR_PREFIXES.length) { + prefix = VENDOR_PREFIXES[i]; + prop = (prefix) ? prefix + camelProp : property; + + if (prop in obj) { + return prop; + } + i++; + } + return undefined; +} + +/** + * get a unique id + * @returns {number} uniqueId + */ +var _uniqueId = 1; +function uniqueId() { + return _uniqueId++; +} + +/** + * get the window object of an element + * @param {HTMLElement} element + * @returns {DocumentView|Window} + */ +function getWindowForElement(element) { + var doc = element.ownerDocument || element; + return (doc.defaultView || doc.parentWindow || window); +} + +var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; + +var SUPPORT_TOUCH = ('ontouchstart' in window); +var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; +var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); + +var INPUT_TYPE_TOUCH = 'touch'; +var INPUT_TYPE_PEN = 'pen'; +var INPUT_TYPE_MOUSE = 'mouse'; +var INPUT_TYPE_KINECT = 'kinect'; + +var COMPUTE_INTERVAL = 25; + +var INPUT_START = 1; +var INPUT_MOVE = 2; +var INPUT_END = 4; +var INPUT_CANCEL = 8; + +var DIRECTION_NONE = 1; +var DIRECTION_LEFT = 2; +var DIRECTION_RIGHT = 4; +var DIRECTION_UP = 8; +var DIRECTION_DOWN = 16; + +var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; +var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; +var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; + +var PROPS_XY = ['x', 'y']; +var PROPS_CLIENT_XY = ['clientX', 'clientY']; + +/** + * create new input type manager + * @param {Manager} manager + * @param {Function} callback + * @returns {Input} + * @constructor + */ +function Input(manager, callback) { + var self = this; + this.manager = manager; + this.callback = callback; + this.element = manager.element; + this.target = manager.options.inputTarget; + + // smaller wrapper around the handler, for the scope and the enabled state of the manager, + // so when disabled the input events are completely bypassed. + this.domHandler = function(ev) { + if (boolOrFn(manager.options.enable, [manager])) { + self.handler(ev); + } + }; + + this.init(); + +} + +Input.prototype = { + /** + * should handle the inputEvent data and trigger the callback + * @virtual + */ + handler: function() { }, + + /** + * bind the events + */ + init: function() { + this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + }, + + /** + * unbind the events + */ + destroy: function() { + this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + } +}; + +/** + * create new input type manager + * called by the Manager constructor + * @param {Hammer} manager + * @returns {Input} + */ +function createInputInstance(manager) { + var Type; + var inputClass = manager.options.inputClass; + + if (inputClass) { + Type = inputClass; + } else if (SUPPORT_POINTER_EVENTS) { + Type = PointerEventInput; + } else if (SUPPORT_ONLY_TOUCH) { + Type = TouchInput; + } else if (!SUPPORT_TOUCH) { + Type = MouseInput; + } else { + Type = TouchMouseInput; + } + return new (Type)(manager, inputHandler); +} + +/** + * handle input events + * @param {Manager} manager + * @param {String} eventType + * @param {Object} input + */ +function inputHandler(manager, eventType, input) { + var pointersLen = input.pointers.length; + var changedPointersLen = input.changedPointers.length; + var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); + var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); + + input.isFirst = !!isFirst; + input.isFinal = !!isFinal; + + if (isFirst) { + manager.session = {}; + } + + // source event is the normalized value of the domEvents + // like 'touchstart, mouseup, pointerdown' + input.eventType = eventType; + + // compute scale, rotation etc + computeInputData(manager, input); + + // emit secret event + manager.emit('hammer.input', input); + + manager.recognize(input); + manager.session.prevInput = input; +} + +/** + * extend the data with some usable properties like scale, rotate, velocity etc + * @param {Object} manager + * @param {Object} input + */ +function computeInputData(manager, input) { + var session = manager.session; + var pointers = input.pointers; + var pointersLength = pointers.length; + + // store the first input to calculate the distance and direction + if (!session.firstInput) { + session.firstInput = simpleCloneInputData(input); + } + + // to compute scale and rotation we need to store the multiple touches + if (pointersLength > 1 && !session.firstMultiple) { + session.firstMultiple = simpleCloneInputData(input); + } else if (pointersLength === 1) { + session.firstMultiple = false; + } + + var firstInput = session.firstInput; + var firstMultiple = session.firstMultiple; + var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; + + var center = input.center = getCenter(pointers); + input.timeStamp = now(); + input.deltaTime = input.timeStamp - firstInput.timeStamp; + + input.angle = getAngle(offsetCenter, center); + input.distance = getDistance(offsetCenter, center); + + computeDeltaXY(session, input); + input.offsetDirection = getDirection(input.deltaX, input.deltaY); + + var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); + input.overallVelocityX = overallVelocity.x; + input.overallVelocityY = overallVelocity.y; + input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y; + + input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; + input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; + + input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length > + session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers); + + computeIntervalInputData(session, input); + + // find the correct target + var target = manager.element; + if (hasParent(input.srcEvent.target, target)) { + target = input.srcEvent.target; + } + input.target = target; +} + +function computeDeltaXY(session, input) { + var center = input.center; + var offset = session.offsetDelta || {}; + var prevDelta = session.prevDelta || {}; + var prevInput = session.prevInput || {}; + + if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { + prevDelta = session.prevDelta = { + x: prevInput.deltaX || 0, + y: prevInput.deltaY || 0 + }; + + offset = session.offsetDelta = { + x: center.x, + y: center.y + }; + } + + input.deltaX = prevDelta.x + (center.x - offset.x); + input.deltaY = prevDelta.y + (center.y - offset.y); +} + +/** + * velocity is calculated every x ms + * @param {Object} session + * @param {Object} input + */ +function computeIntervalInputData(session, input) { + var last = session.lastInterval || input, + deltaTime = input.timeStamp - last.timeStamp, + velocity, velocityX, velocityY, direction; + + if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { + var deltaX = input.deltaX - last.deltaX; + var deltaY = input.deltaY - last.deltaY; + + var v = getVelocity(deltaTime, deltaX, deltaY); + velocityX = v.x; + velocityY = v.y; + velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; + direction = getDirection(deltaX, deltaY); + + session.lastInterval = input; + } else { + // use latest velocity info if it doesn't overtake a minimum period + velocity = last.velocity; + velocityX = last.velocityX; + velocityY = last.velocityY; + direction = last.direction; + } + + input.velocity = velocity; + input.velocityX = velocityX; + input.velocityY = velocityY; + input.direction = direction; +} + +/** + * create a simple clone from the input used for storage of firstInput and firstMultiple + * @param {Object} input + * @returns {Object} clonedInputData + */ +function simpleCloneInputData(input) { + // make a simple copy of the pointers because we will get a reference if we don't + // we only need clientXY for the calculations + var pointers = []; + var i = 0; + while (i < input.pointers.length) { + pointers[i] = { + clientX: round(input.pointers[i].clientX), + clientY: round(input.pointers[i].clientY) + }; + i++; + } + + return { + timeStamp: now(), + pointers: pointers, + center: getCenter(pointers), + deltaX: input.deltaX, + deltaY: input.deltaY + }; +} + +/** + * get the center of all the pointers + * @param {Array} pointers + * @return {Object} center contains `x` and `y` properties + */ +function getCenter(pointers) { + var pointersLength = pointers.length; + + // no need to loop when only one touch + if (pointersLength === 1) { + return { + x: round(pointers[0].clientX), + y: round(pointers[0].clientY) + }; + } + + var x = 0, y = 0, i = 0; + while (i < pointersLength) { + x += pointers[i].clientX; + y += pointers[i].clientY; + i++; + } + + return { + x: round(x / pointersLength), + y: round(y / pointersLength) + }; +} + +/** + * calculate the velocity between two points. unit is in px per ms. + * @param {Number} deltaTime + * @param {Number} x + * @param {Number} y + * @return {Object} velocity `x` and `y` + */ +function getVelocity(deltaTime, x, y) { + return { + x: x / deltaTime || 0, + y: y / deltaTime || 0 + }; +} + +/** + * get the direction between two points + * @param {Number} x + * @param {Number} y + * @return {Number} direction + */ +function getDirection(x, y) { + if (x === y) { + return DIRECTION_NONE; + } + + if (abs(x) >= abs(y)) { + return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; +} + +/** + * calculate the absolute distance between two points + * @param {Object} p1 {x, y} + * @param {Object} p2 {x, y} + * @param {Array} [props] containing x and y keys + * @return {Number} distance + */ +function getDistance(p1, p2, props) { + if (!props) { + props = PROPS_XY; + } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; + + return Math.sqrt((x * x) + (y * y)); +} + +/** + * calculate the angle between two coordinates + * @param {Object} p1 + * @param {Object} p2 + * @param {Array} [props] containing x and y keys + * @return {Number} angle + */ +function getAngle(p1, p2, props) { + if (!props) { + props = PROPS_XY; + } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; + return Math.atan2(y, x) * 180 / Math.PI; +} + +/** + * calculate the rotation degrees between two pointersets + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} rotation + */ +function getRotation(start, end) { + return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); +} + +/** + * calculate the scale factor between two pointersets + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} scale + */ +function getScale(start, end) { + return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); +} + +var MOUSE_INPUT_MAP = { + mousedown: INPUT_START, + mousemove: INPUT_MOVE, + mouseup: INPUT_END +}; + +var MOUSE_ELEMENT_EVENTS = 'mousedown'; +var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; + +/** + * Mouse events input + * @constructor + * @extends Input + */ +function MouseInput() { + this.evEl = MOUSE_ELEMENT_EVENTS; + this.evWin = MOUSE_WINDOW_EVENTS; + + this.pressed = false; // mousedown state + + Input.apply(this, arguments); +} + +inherit(MouseInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function MEhandler(ev) { + var eventType = MOUSE_INPUT_MAP[ev.type]; + + // on start we want to have the left mouse button down + if (eventType & INPUT_START && ev.button === 0) { + this.pressed = true; + } + + if (eventType & INPUT_MOVE && ev.which !== 1) { + eventType = INPUT_END; + } + + // mouse must be down + if (!this.pressed) { + return; + } + + if (eventType & INPUT_END) { + this.pressed = false; + } + + this.callback(this.manager, eventType, { + pointers: [ev], + changedPointers: [ev], + pointerType: INPUT_TYPE_MOUSE, + srcEvent: ev + }); + } +}); + +var POINTER_INPUT_MAP = { + pointerdown: INPUT_START, + pointermove: INPUT_MOVE, + pointerup: INPUT_END, + pointercancel: INPUT_CANCEL, + pointerout: INPUT_CANCEL +}; + +// in IE10 the pointer types is defined as an enum +var IE10_POINTER_TYPE_ENUM = { + 2: INPUT_TYPE_TOUCH, + 3: INPUT_TYPE_PEN, + 4: INPUT_TYPE_MOUSE, + 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 +}; + +var POINTER_ELEMENT_EVENTS = 'pointerdown'; +var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; + +// IE10 has prefixed support, and case-sensitive +if (window.MSPointerEvent && !window.PointerEvent) { + POINTER_ELEMENT_EVENTS = 'MSPointerDown'; + POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; +} + +/** + * Pointer events input + * @constructor + * @extends Input + */ +function PointerEventInput() { + this.evEl = POINTER_ELEMENT_EVENTS; + this.evWin = POINTER_WINDOW_EVENTS; + + Input.apply(this, arguments); + + this.store = (this.manager.session.pointerEvents = []); +} + +inherit(PointerEventInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function PEhandler(ev) { + var store = this.store; + var removePointer = false; + + var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); + var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; + var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; + + var isTouch = (pointerType == INPUT_TYPE_TOUCH); + + // get index of the event in the store + var storeIndex = inArray(store, ev.pointerId, 'pointerId'); + + // start and mouse must be down + if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { + if (storeIndex < 0) { + store.push(ev); + storeIndex = store.length - 1; + } + } else if (eventType & (INPUT_END | INPUT_CANCEL)) { + removePointer = true; + } + + // it not found, so the pointer hasn't been down (so it's probably a hover) + if (storeIndex < 0) { + return; + } + + // update the event in the store + store[storeIndex] = ev; + + this.callback(this.manager, eventType, { + pointers: store, + changedPointers: [ev], + pointerType: pointerType, + srcEvent: ev + }); + + if (removePointer) { + // remove from the store + store.splice(storeIndex, 1); + } + } +}); + +var SINGLE_TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL +}; + +var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; +var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; + +/** + * Touch events input + * @constructor + * @extends Input + */ +function SingleTouchInput() { + this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; + this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; + this.started = false; + + Input.apply(this, arguments); +} + +inherit(SingleTouchInput, Input, { + handler: function TEhandler(ev) { + var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; + + // should we handle the touch events? + if (type === INPUT_START) { + this.started = true; + } + + if (!this.started) { + return; + } + + var touches = normalizeSingleTouches.call(this, ev, type); + + // when done, reset the started state + if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { + this.started = false; + } + + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); + } +}); + +/** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ +function normalizeSingleTouches(ev, type) { + var all = toArray(ev.touches); + var changed = toArray(ev.changedTouches); + + if (type & (INPUT_END | INPUT_CANCEL)) { + all = uniqueArray(all.concat(changed), 'identifier', true); + } + + return [all, changed]; +} + +var TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL +}; + +var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; + +/** + * Multi-user touch events input + * @constructor + * @extends Input + */ +function TouchInput() { + this.evTarget = TOUCH_TARGET_EVENTS; + this.targetIds = {}; + + Input.apply(this, arguments); +} + +inherit(TouchInput, Input, { + handler: function MTEhandler(ev) { + var type = TOUCH_INPUT_MAP[ev.type]; + var touches = getTouches.call(this, ev, type); + if (!touches) { + return; + } + + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); + } +}); + +/** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ +function getTouches(ev, type) { + var allTouches = toArray(ev.touches); + var targetIds = this.targetIds; + + // when there is only one touch, the process can be simplified + if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { + targetIds[allTouches[0].identifier] = true; + return [allTouches, allTouches]; + } + + var i, + targetTouches, + changedTouches = toArray(ev.changedTouches), + changedTargetTouches = [], + target = this.target; + + // get target touches from touches + targetTouches = allTouches.filter(function(touch) { + return hasParent(touch.target, target); + }); + + // collect touches + if (type === INPUT_START) { + i = 0; + while (i < targetTouches.length) { + targetIds[targetTouches[i].identifier] = true; + i++; + } + } + + // filter changed touches to only contain touches that exist in the collected target ids + i = 0; + while (i < changedTouches.length) { + if (targetIds[changedTouches[i].identifier]) { + changedTargetTouches.push(changedTouches[i]); + } + + // cleanup removed touches + if (type & (INPUT_END | INPUT_CANCEL)) { + delete targetIds[changedTouches[i].identifier]; + } + i++; + } + + if (!changedTargetTouches.length) { + return; + } + + return [ + // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' + uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), + changedTargetTouches + ]; +} + +/** + * Combined touch and mouse input + * + * Touch has a higher priority then mouse, and while touching no mouse events are allowed. + * This because touch devices also emit mouse events while doing a touch. + * + * @constructor + * @extends Input + */ + +var DEDUP_TIMEOUT = 2500; +var DEDUP_DISTANCE = 25; + +function TouchMouseInput() { + Input.apply(this, arguments); + + var handler = bindFn(this.handler, this); + this.touch = new TouchInput(this.manager, handler); + this.mouse = new MouseInput(this.manager, handler); + + this.primaryTouch = null; + this.lastTouches = []; +} + +inherit(TouchMouseInput, Input, { + /** + * handle mouse and touch events + * @param {Hammer} manager + * @param {String} inputEvent + * @param {Object} inputData + */ + handler: function TMEhandler(manager, inputEvent, inputData) { + var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), + isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); + + if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { + return; + } + + // when we're in a touch event, record touches to de-dupe synthetic mouse event + if (isTouch) { + recordTouches.call(this, inputEvent, inputData); + } else if (isMouse && isSyntheticEvent.call(this, inputData)) { + return; + } + + this.callback(manager, inputEvent, inputData); + }, + + /** + * remove the event listeners + */ + destroy: function destroy() { + this.touch.destroy(); + this.mouse.destroy(); + } +}); + +function recordTouches(eventType, eventData) { + if (eventType & INPUT_START) { + this.primaryTouch = eventData.changedPointers[0].identifier; + setLastTouch.call(this, eventData); + } else if (eventType & (INPUT_END | INPUT_CANCEL)) { + setLastTouch.call(this, eventData); + } +} + +function setLastTouch(eventData) { + var touch = eventData.changedPointers[0]; + + if (touch.identifier === this.primaryTouch) { + var lastTouch = {x: touch.clientX, y: touch.clientY}; + this.lastTouches.push(lastTouch); + var lts = this.lastTouches; + var removeLastTouch = function() { + var i = lts.indexOf(lastTouch); + if (i > -1) { + lts.splice(i, 1); + } + }; + setTimeout(removeLastTouch, DEDUP_TIMEOUT); + } +} + +function isSyntheticEvent(eventData) { + var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY; + for (var i = 0; i < this.lastTouches.length; i++) { + var t = this.lastTouches[i]; + var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); + if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { + return true; + } + } + return false; +} + +var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); +var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; + +// magical touchAction value +var TOUCH_ACTION_COMPUTE = 'compute'; +var TOUCH_ACTION_AUTO = 'auto'; +var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented +var TOUCH_ACTION_NONE = 'none'; +var TOUCH_ACTION_PAN_X = 'pan-x'; +var TOUCH_ACTION_PAN_Y = 'pan-y'; +var TOUCH_ACTION_MAP = getTouchActionProps(); + +/** + * Touch Action + * sets the touchAction property or uses the js alternative + * @param {Manager} manager + * @param {String} value + * @constructor + */ +function TouchAction(manager, value) { + this.manager = manager; + this.set(value); +} + +TouchAction.prototype = { + /** + * set the touchAction value on the element or enable the polyfill + * @param {String} value + */ + set: function(value) { + // find out the touch-action by the event handlers + if (value == TOUCH_ACTION_COMPUTE) { + value = this.compute(); + } + + if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { + this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; + } + this.actions = value.toLowerCase().trim(); + }, + + /** + * just re-set the touchAction value + */ + update: function() { + this.set(this.manager.options.touchAction); + }, + + /** + * compute the value for the touchAction property based on the recognizer's settings + * @returns {String} value + */ + compute: function() { + var actions = []; + each(this.manager.recognizers, function(recognizer) { + if (boolOrFn(recognizer.options.enable, [recognizer])) { + actions = actions.concat(recognizer.getTouchAction()); + } + }); + return cleanTouchActions(actions.join(' ')); + }, + + /** + * this method is called on each input cycle and provides the preventing of the browser behavior + * @param {Object} input + */ + preventDefaults: function(input) { + var srcEvent = input.srcEvent; + var direction = input.offsetDirection; + + // if the touch action did prevented once this session + if (this.manager.session.prevented) { + srcEvent.preventDefault(); + return; + } + + var actions = this.actions; + var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; + + if (hasNone) { + //do not prevent defaults if this is a tap gesture + + var isTapPointer = input.pointers.length === 1; + var isTapMovement = input.distance < 2; + var isTapTouchTime = input.deltaTime < 250; + + if (isTapPointer && isTapMovement && isTapTouchTime) { + return; + } + } + + if (hasPanX && hasPanY) { + // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent + return; + } + + if (hasNone || + (hasPanY && direction & DIRECTION_HORIZONTAL) || + (hasPanX && direction & DIRECTION_VERTICAL)) { + return this.preventSrc(srcEvent); + } + }, + + /** + * call preventDefault to prevent the browser's default behavior (scrolling in most cases) + * @param {Object} srcEvent + */ + preventSrc: function(srcEvent) { + this.manager.session.prevented = true; + srcEvent.preventDefault(); + } +}; + +/** + * when the touchActions are collected they are not a valid value, so we need to clean things up. * + * @param {String} actions + * @returns {*} + */ +function cleanTouchActions(actions) { + // none + if (inStr(actions, TOUCH_ACTION_NONE)) { + return TOUCH_ACTION_NONE; + } + + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); + + // if both pan-x and pan-y are set (different recognizers + // for different directions, e.g. horizontal pan but vertical swipe?) + // we need none (as otherwise with pan-x pan-y combined none of these + // recognizers will work, since the browser would handle all panning + if (hasPanX && hasPanY) { + return TOUCH_ACTION_NONE; + } + + // pan-x OR pan-y + if (hasPanX || hasPanY) { + return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; + } + + // manipulation + if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { + return TOUCH_ACTION_MANIPULATION; + } + + return TOUCH_ACTION_AUTO; +} + +function getTouchActionProps() { + if (!NATIVE_TOUCH_ACTION) { + return false; + } + var touchMap = {}; + var cssSupports = window.CSS && window.CSS.supports; + ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) { + + // If css.supports is not supported but there is native touch-action assume it supports + // all values. This is the case for IE 10 and 11. + touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true; + }); + return touchMap; +} + +/** + * Recognizer flow explained; * + * All recognizers have the initial state of POSSIBLE when a input session starts. + * The definition of a input session is from the first input until the last input, with all it's movement in it. * + * Example session for mouse-input: mousedown -> mousemove -> mouseup + * + * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed + * which determines with state it should be. + * + * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to + * POSSIBLE to give it another change on the next cycle. + * + * Possible + * | + * +-----+---------------+ + * | | + * +-----+-----+ | + * | | | + * Failed Cancelled | + * +-------+------+ + * | | + * Recognized Began + * | + * Changed + * | + * Ended/Recognized + */ +var STATE_POSSIBLE = 1; +var STATE_BEGAN = 2; +var STATE_CHANGED = 4; +var STATE_ENDED = 8; +var STATE_RECOGNIZED = STATE_ENDED; +var STATE_CANCELLED = 16; +var STATE_FAILED = 32; + +/** + * Recognizer + * Every recognizer needs to extend from this class. + * @constructor + * @param {Object} options + */ +function Recognizer(options) { + this.options = assign({}, this.defaults, options || {}); + + this.id = uniqueId(); + + this.manager = null; + + // default is enable true + this.options.enable = ifUndefined(this.options.enable, true); + + this.state = STATE_POSSIBLE; + + this.simultaneous = {}; + this.requireFail = []; +} + +Recognizer.prototype = { + /** + * @virtual + * @type {Object} + */ + defaults: {}, + + /** + * set options + * @param {Object} options + * @return {Recognizer} + */ + set: function(options) { + assign(this.options, options); + + // also update the touchAction, in case something changed about the directions/enabled state + this.manager && this.manager.touchAction.update(); + return this; + }, + + /** + * recognize simultaneous with an other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + recognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { + return this; + } + + var simultaneous = this.simultaneous; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (!simultaneous[otherRecognizer.id]) { + simultaneous[otherRecognizer.id] = otherRecognizer; + otherRecognizer.recognizeWith(this); + } + return this; + }, + + /** + * drop the simultaneous link. it doesnt remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRecognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { + return this; + } + + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + delete this.simultaneous[otherRecognizer.id]; + return this; + }, + + /** + * recognizer can only run when an other is failing + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + requireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { + return this; + } + + var requireFail = this.requireFail; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (inArray(requireFail, otherRecognizer) === -1) { + requireFail.push(otherRecognizer); + otherRecognizer.requireFailure(this); + } + return this; + }, + + /** + * drop the requireFailure link. it does not remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRequireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { + return this; + } + + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + var index = inArray(this.requireFail, otherRecognizer); + if (index > -1) { + this.requireFail.splice(index, 1); + } + return this; + }, + + /** + * has require failures boolean + * @returns {boolean} + */ + hasRequireFailures: function() { + return this.requireFail.length > 0; + }, + + /** + * if the recognizer can recognize simultaneous with an other recognizer + * @param {Recognizer} otherRecognizer + * @returns {Boolean} + */ + canRecognizeWith: function(otherRecognizer) { + return !!this.simultaneous[otherRecognizer.id]; + }, + + /** + * You should use `tryEmit` instead of `emit` directly to check + * that all the needed recognizers has failed before emitting. + * @param {Object} input + */ + emit: function(input) { + var self = this; + var state = this.state; + + function emit(event) { + self.manager.emit(event, input); + } + + // 'panstart' and 'panmove' + if (state < STATE_ENDED) { + emit(self.options.event + stateStr(state)); + } + + emit(self.options.event); // simple 'eventName' events + + if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) + emit(input.additionalEvent); + } + + // panend and pancancel + if (state >= STATE_ENDED) { + emit(self.options.event + stateStr(state)); + } + }, + + /** + * Check that all the require failure recognizers has failed, + * if true, it emits a gesture event, + * otherwise, setup the state to FAILED. + * @param {Object} input + */ + tryEmit: function(input) { + if (this.canEmit()) { + return this.emit(input); + } + // it's failing anyway + this.state = STATE_FAILED; + }, + + /** + * can we emit? + * @returns {boolean} + */ + canEmit: function() { + var i = 0; + while (i < this.requireFail.length) { + if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { + return false; + } + i++; + } + return true; + }, + + /** + * update the recognizer + * @param {Object} inputData + */ + recognize: function(inputData) { + // make a new copy of the inputData + // so we can change the inputData without messing up the other recognizers + var inputDataClone = assign({}, inputData); + + // is is enabled and allow recognizing? + if (!boolOrFn(this.options.enable, [this, inputDataClone])) { + this.reset(); + this.state = STATE_FAILED; + return; + } + + // reset when we've reached the end + if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { + this.state = STATE_POSSIBLE; + } + + this.state = this.process(inputDataClone); + + // the recognizer has recognized a gesture + // so trigger an event + if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { + this.tryEmit(inputDataClone); + } + }, + + /** + * return the state of the recognizer + * the actual recognizing happens in this method + * @virtual + * @param {Object} inputData + * @returns {Const} STATE + */ + process: function(inputData) { }, // jshint ignore:line + + /** + * return the preferred touch-action + * @virtual + * @returns {Array} + */ + getTouchAction: function() { }, + + /** + * called when the gesture isn't allowed to recognize + * like when another is being recognized or it is disabled + * @virtual + */ + reset: function() { } +}; + +/** + * get a usable string, used as event postfix + * @param {Const} state + * @returns {String} state + */ +function stateStr(state) { + if (state & STATE_CANCELLED) { + return 'cancel'; + } else if (state & STATE_ENDED) { + return 'end'; + } else if (state & STATE_CHANGED) { + return 'move'; + } else if (state & STATE_BEGAN) { + return 'start'; + } + return ''; +} + +/** + * direction cons to string + * @param {Const} direction + * @returns {String} + */ +function directionStr(direction) { + if (direction == DIRECTION_DOWN) { + return 'down'; + } else if (direction == DIRECTION_UP) { + return 'up'; + } else if (direction == DIRECTION_LEFT) { + return 'left'; + } else if (direction == DIRECTION_RIGHT) { + return 'right'; + } + return ''; +} + +/** + * get a recognizer by name if it is bound to a manager + * @param {Recognizer|String} otherRecognizer + * @param {Recognizer} recognizer + * @returns {Recognizer} + */ +function getRecognizerByNameIfManager(otherRecognizer, recognizer) { + var manager = recognizer.manager; + if (manager) { + return manager.get(otherRecognizer); + } + return otherRecognizer; +} + +/** + * This recognizer is just used as a base for the simple attribute recognizers. + * @constructor + * @extends Recognizer + */ +function AttrRecognizer() { + Recognizer.apply(this, arguments); +} + +inherit(AttrRecognizer, Recognizer, { + /** + * @namespace + * @memberof AttrRecognizer + */ + defaults: { + /** + * @type {Number} + * @default 1 + */ + pointers: 1 + }, + + /** + * Used to check if it the recognizer receives valid input, like input.distance > 10. + * @memberof AttrRecognizer + * @param {Object} input + * @returns {Boolean} recognized + */ + attrTest: function(input) { + var optionPointers = this.options.pointers; + return optionPointers === 0 || input.pointers.length === optionPointers; + }, + + /** + * Process the input and return the state for the recognizer + * @memberof AttrRecognizer + * @param {Object} input + * @returns {*} State + */ + process: function(input) { + var state = this.state; + var eventType = input.eventType; + + var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); + var isValid = this.attrTest(input); + + // on cancel input and we've recognized before, return STATE_CANCELLED + if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { + return state | STATE_CANCELLED; + } else if (isRecognized || isValid) { + if (eventType & INPUT_END) { + return state | STATE_ENDED; + } else if (!(state & STATE_BEGAN)) { + return STATE_BEGAN; + } + return state | STATE_CHANGED; + } + return STATE_FAILED; + } +}); + +/** + * Pan + * Recognized when the pointer is down and moved in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ +function PanRecognizer() { + AttrRecognizer.apply(this, arguments); + + this.pX = null; + this.pY = null; +} + +inherit(PanRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PanRecognizer + */ + defaults: { + event: 'pan', + threshold: 10, + pointers: 1, + direction: DIRECTION_ALL + }, + + getTouchAction: function() { + var direction = this.options.direction; + var actions = []; + if (direction & DIRECTION_HORIZONTAL) { + actions.push(TOUCH_ACTION_PAN_Y); + } + if (direction & DIRECTION_VERTICAL) { + actions.push(TOUCH_ACTION_PAN_X); + } + return actions; + }, + + directionTest: function(input) { + var options = this.options; + var hasMoved = true; + var distance = input.distance; + var direction = input.direction; + var x = input.deltaX; + var y = input.deltaY; + + // lock to axis? + if (!(direction & options.direction)) { + if (options.direction & DIRECTION_HORIZONTAL) { + direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; + hasMoved = x != this.pX; + distance = Math.abs(input.deltaX); + } else { + direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; + hasMoved = y != this.pY; + distance = Math.abs(input.deltaY); + } + } + input.direction = direction; + return hasMoved && distance > options.threshold && direction & options.direction; + }, + + attrTest: function(input) { + return AttrRecognizer.prototype.attrTest.call(this, input) && + (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); + }, + + emit: function(input) { + + this.pX = input.deltaX; + this.pY = input.deltaY; + + var direction = directionStr(input.direction); + + if (direction) { + input.additionalEvent = this.options.event + direction; + } + this._super.emit.call(this, input); + } +}); + +/** + * Pinch + * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). + * @constructor + * @extends AttrRecognizer + */ +function PinchRecognizer() { + AttrRecognizer.apply(this, arguments); +} + +inherit(PinchRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'pinch', + threshold: 0, + pointers: 2 + }, + + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, + + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); + }, + + emit: function(input) { + if (input.scale !== 1) { + var inOut = input.scale < 1 ? 'in' : 'out'; + input.additionalEvent = this.options.event + inOut; + } + this._super.emit.call(this, input); + } +}); + +/** + * Press + * Recognized when the pointer is down for x ms without any movement. + * @constructor + * @extends Recognizer + */ +function PressRecognizer() { + Recognizer.apply(this, arguments); + + this._timer = null; + this._input = null; +} + +inherit(PressRecognizer, Recognizer, { + /** + * @namespace + * @memberof PressRecognizer + */ + defaults: { + event: 'press', + pointers: 1, + time: 251, // minimal time of the pointer to be pressed + threshold: 9 // a minimal movement is ok, but keep it low + }, + + getTouchAction: function() { + return [TOUCH_ACTION_AUTO]; + }, + + process: function(input) { + var options = this.options; + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTime = input.deltaTime > options.time; + + this._input = input; + + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { + this.reset(); + } else if (input.eventType & INPUT_START) { + this.reset(); + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.time, this); + } else if (input.eventType & INPUT_END) { + return STATE_RECOGNIZED; + } + return STATE_FAILED; + }, + + reset: function() { + clearTimeout(this._timer); + }, + + emit: function(input) { + if (this.state !== STATE_RECOGNIZED) { + return; + } + + if (input && (input.eventType & INPUT_END)) { + this.manager.emit(this.options.event + 'up', input); + } else { + this._input.timeStamp = now(); + this.manager.emit(this.options.event, this._input); + } + } +}); + +/** + * Rotate + * Recognized when two or more pointer are moving in a circular motion. + * @constructor + * @extends AttrRecognizer + */ +function RotateRecognizer() { + AttrRecognizer.apply(this, arguments); +} + +inherit(RotateRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof RotateRecognizer + */ + defaults: { + event: 'rotate', + threshold: 0, + pointers: 2 + }, + + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, + + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); + } +}); + +/** + * Swipe + * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ +function SwipeRecognizer() { + AttrRecognizer.apply(this, arguments); +} + +inherit(SwipeRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof SwipeRecognizer + */ + defaults: { + event: 'swipe', + threshold: 10, + velocity: 0.3, + direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, + pointers: 1 + }, + + getTouchAction: function() { + return PanRecognizer.prototype.getTouchAction.call(this); + }, + + attrTest: function(input) { + var direction = this.options.direction; + var velocity; + + if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { + velocity = input.overallVelocity; + } else if (direction & DIRECTION_HORIZONTAL) { + velocity = input.overallVelocityX; + } else if (direction & DIRECTION_VERTICAL) { + velocity = input.overallVelocityY; + } + + return this._super.attrTest.call(this, input) && + direction & input.offsetDirection && + input.distance > this.options.threshold && + input.maxPointers == this.options.pointers && + abs(velocity) > this.options.velocity && input.eventType & INPUT_END; + }, + + emit: function(input) { + var direction = directionStr(input.offsetDirection); + if (direction) { + this.manager.emit(this.options.event + direction, input); + } + + this.manager.emit(this.options.event, input); + } +}); + +/** + * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur + * between the given interval and position. The delay option can be used to recognize multi-taps without firing + * a single tap. + * + * The eventData from the emitted event contains the property `tapCount`, which contains the amount of + * multi-taps being recognized. + * @constructor + * @extends Recognizer + */ +function TapRecognizer() { + Recognizer.apply(this, arguments); + + // previous time and center, + // used for tap counting + this.pTime = false; + this.pCenter = false; + + this._timer = null; + this._input = null; + this.count = 0; +} + +inherit(TapRecognizer, Recognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'tap', + pointers: 1, + taps: 1, + interval: 300, // max time between the multi-tap taps + time: 250, // max time of the pointer to be down (like finger on the screen) + threshold: 9, // a minimal movement is ok, but keep it low + posThreshold: 10 // a multi-tap can be a bit off the initial position + }, + + getTouchAction: function() { + return [TOUCH_ACTION_MANIPULATION]; + }, + + process: function(input) { + var options = this.options; + + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTouchTime = input.deltaTime < options.time; + + this.reset(); + + if ((input.eventType & INPUT_START) && (this.count === 0)) { + return this.failTimeout(); + } + + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (validMovement && validTouchTime && validPointers) { + if (input.eventType != INPUT_END) { + return this.failTimeout(); + } + + var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; + var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; + + this.pTime = input.timeStamp; + this.pCenter = input.center; + + if (!validMultiTap || !validInterval) { + this.count = 1; + } else { + this.count += 1; + } + + this._input = input; + + // if tap count matches we have recognized it, + // else it has began recognizing... + var tapCount = this.count % options.taps; + if (tapCount === 0) { + // no failing requirements, immediately trigger the tap event + // or wait as long as the multitap interval to trigger + if (!this.hasRequireFailures()) { + return STATE_RECOGNIZED; + } else { + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.interval, this); + return STATE_BEGAN; + } + } + } + return STATE_FAILED; + }, + + failTimeout: function() { + this._timer = setTimeoutContext(function() { + this.state = STATE_FAILED; + }, this.options.interval, this); + return STATE_FAILED; + }, + + reset: function() { + clearTimeout(this._timer); + }, + + emit: function() { + if (this.state == STATE_RECOGNIZED) { + this._input.tapCount = this.count; + this.manager.emit(this.options.event, this._input); + } + } +}); + +/** + * Simple way to create a manager with a default set of recognizers. + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ +function Hammer(element, options) { + options = options || {}; + options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); + return new Manager(element, options); +} + +/** + * @const {string} + */ +Hammer.VERSION = '2.0.8'; + +/** + * default settings + * @namespace + */ +Hammer.defaults = { + /** + * set if DOM events are being triggered. + * But this is slower and unused by simple implementations, so disabled by default. + * @type {Boolean} + * @default false + */ + domEvents: false, + + /** + * The value for the touchAction property/fallback. + * When set to `compute` it will magically set the correct value based on the added recognizers. + * @type {String} + * @default compute + */ + touchAction: TOUCH_ACTION_COMPUTE, + + /** + * @type {Boolean} + * @default true + */ + enable: true, + + /** + * EXPERIMENTAL FEATURE -- can be removed/changed + * Change the parent input target element. + * If Null, then it is being set the to main element. + * @type {Null|EventTarget} + * @default null + */ + inputTarget: null, + + /** + * force an input class + * @type {Null|Function} + * @default null + */ + inputClass: null, + + /** + * Default recognizer setup when calling `Hammer()` + * When creating a new Manager these will be skipped. + * @type {Array} + */ + preset: [ + // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] + [RotateRecognizer, {enable: false}], + [PinchRecognizer, {enable: false}, ['rotate']], + [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}], + [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']], + [TapRecognizer], + [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']], + [PressRecognizer] + ], + + /** + * Some CSS properties can be used to improve the working of Hammer. + * Add them to this method and they will be set when creating a new Manager. + * @namespace + */ + cssProps: { + /** + * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userSelect: 'none', + + /** + * Disable the Windows Phone grippers when pressing an element. + * @type {String} + * @default 'none' + */ + touchSelect: 'none', + + /** + * Disables the default callout shown when you touch and hold a touch target. + * On iOS, when you touch and hold a touch target such as a link, Safari displays + * a callout containing information about the link. This property allows you to disable that callout. + * @type {String} + * @default 'none' + */ + touchCallout: 'none', + + /** + * Specifies whether zooming is enabled. Used by IE10> + * @type {String} + * @default 'none' + */ + contentZooming: 'none', + + /** + * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userDrag: 'none', + + /** + * Overrides the highlight color shown when the user taps a link or a JavaScript + * clickable element in iOS. This property obeys the alpha value, if specified. + * @type {String} + * @default 'rgba(0,0,0,0)' + */ + tapHighlightColor: 'rgba(0,0,0,0)' + } +}; + +var STOP = 1; +var FORCED_STOP = 2; + +/** + * Manager + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ +function Manager(element, options) { + this.options = assign({}, Hammer.defaults, options || {}); + + this.options.inputTarget = this.options.inputTarget || element; + + this.handlers = {}; + this.session = {}; + this.recognizers = []; + this.oldCssProps = {}; + + this.element = element; + this.input = createInputInstance(this); + this.touchAction = new TouchAction(this, this.options.touchAction); + + toggleCssProps(this, true); + + each(this.options.recognizers, function(item) { + var recognizer = this.add(new (item[0])(item[1])); + item[2] && recognizer.recognizeWith(item[2]); + item[3] && recognizer.requireFailure(item[3]); + }, this); +} + +Manager.prototype = { + /** + * set options + * @param {Object} options + * @returns {Manager} + */ + set: function(options) { + assign(this.options, options); + + // Options that need a little more setup + if (options.touchAction) { + this.touchAction.update(); + } + if (options.inputTarget) { + // Clean up existing event listeners and reinitialize + this.input.destroy(); + this.input.target = options.inputTarget; + this.input.init(); + } + return this; + }, + + /** + * stop recognizing for this session. + * This session will be discarded, when a new [input]start event is fired. + * When forced, the recognizer cycle is stopped immediately. + * @param {Boolean} [force] + */ + stop: function(force) { + this.session.stopped = force ? FORCED_STOP : STOP; + }, + + /** + * run the recognizers! + * called by the inputHandler function on every movement of the pointers (touches) + * it walks through all the recognizers and tries to detect the gesture that is being made + * @param {Object} inputData + */ + recognize: function(inputData) { + var session = this.session; + if (session.stopped) { + return; + } + + // run the touch-action polyfill + this.touchAction.preventDefaults(inputData); + + var recognizer; + var recognizers = this.recognizers; + + // this holds the recognizer that is being recognized. + // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED + // if no recognizer is detecting a thing, it is set to `null` + var curRecognizer = session.curRecognizer; + + // reset when the last recognizer is recognized + // or when we're in a new session + if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { + curRecognizer = session.curRecognizer = null; + } + + var i = 0; + while (i < recognizers.length) { + recognizer = recognizers[i]; + + // find out if we are allowed try to recognize the input for this one. + // 1. allow if the session is NOT forced stopped (see the .stop() method) + // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one + // that is being recognized. + // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. + // this can be setup with the `recognizeWith()` method on the recognizer. + if (session.stopped !== FORCED_STOP && ( // 1 + !curRecognizer || recognizer == curRecognizer || // 2 + recognizer.canRecognizeWith(curRecognizer))) { // 3 + recognizer.recognize(inputData); + } else { + recognizer.reset(); + } + + // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the + // current active recognizer. but only if we don't already have an active recognizer + if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { + curRecognizer = session.curRecognizer = recognizer; + } + i++; + } + }, + + /** + * get a recognizer by its event name. + * @param {Recognizer|String} recognizer + * @returns {Recognizer|Null} + */ + get: function(recognizer) { + if (recognizer instanceof Recognizer) { + return recognizer; + } + + var recognizers = this.recognizers; + for (var i = 0; i < recognizers.length; i++) { + if (recognizers[i].options.event == recognizer) { + return recognizers[i]; + } + } + return null; + }, + + /** + * add a recognizer to the manager + * existing recognizers with the same event name will be removed + * @param {Recognizer} recognizer + * @returns {Recognizer|Manager} + */ + add: function(recognizer) { + if (invokeArrayArg(recognizer, 'add', this)) { + return this; + } + + // remove existing + var existing = this.get(recognizer.options.event); + if (existing) { + this.remove(existing); + } + + this.recognizers.push(recognizer); + recognizer.manager = this; + + this.touchAction.update(); + return recognizer; + }, + + /** + * remove a recognizer by name or instance + * @param {Recognizer|String} recognizer + * @returns {Manager} + */ + remove: function(recognizer) { + if (invokeArrayArg(recognizer, 'remove', this)) { + return this; + } + + recognizer = this.get(recognizer); + + // let's make sure this recognizer exists + if (recognizer) { + var recognizers = this.recognizers; + var index = inArray(recognizers, recognizer); + + if (index !== -1) { + recognizers.splice(index, 1); + this.touchAction.update(); + } + } + + return this; + }, + + /** + * bind event + * @param {String} events + * @param {Function} handler + * @returns {EventEmitter} this + */ + on: function(events, handler) { + if (events === undefined) { + return; + } + if (handler === undefined) { + return; + } + + var handlers = this.handlers; + each(splitStr(events), function(event) { + handlers[event] = handlers[event] || []; + handlers[event].push(handler); + }); + return this; + }, + + /** + * unbind event, leave emit blank to remove all handlers + * @param {String} events + * @param {Function} [handler] + * @returns {EventEmitter} this + */ + off: function(events, handler) { + if (events === undefined) { + return; + } + + var handlers = this.handlers; + each(splitStr(events), function(event) { + if (!handler) { + delete handlers[event]; + } else { + handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); + } + }); + return this; + }, + + /** + * emit event to the listeners + * @param {String} event + * @param {Object} data + */ + emit: function(event, data) { + // we also want to trigger dom events + if (this.options.domEvents) { + triggerDomEvent(event, data); + } + + // no handlers, so skip it all + var handlers = this.handlers[event] && this.handlers[event].slice(); + if (!handlers || !handlers.length) { + return; + } + + data.type = event; + data.preventDefault = function() { + data.srcEvent.preventDefault(); + }; + + var i = 0; + while (i < handlers.length) { + handlers[i](data); + i++; + } + }, + + /** + * destroy the manager and unbinds all events + * it doesn't unbind dom events, that is the user own responsibility + */ + destroy: function() { + this.element && toggleCssProps(this, false); + + this.handlers = {}; + this.session = {}; + this.input.destroy(); + this.element = null; + } +}; + +/** + * add/remove the css properties as defined in manager.options.cssProps + * @param {Manager} manager + * @param {Boolean} add + */ +function toggleCssProps(manager, add) { + var element = manager.element; + if (!element.style) { + return; + } + var prop; + each(manager.options.cssProps, function(value, name) { + prop = prefixed(element.style, name); + if (add) { + manager.oldCssProps[prop] = element.style[prop]; + element.style[prop] = value; + } else { + element.style[prop] = manager.oldCssProps[prop] || ''; + } + }); + if (!add) { + manager.oldCssProps = {}; + } +} + +/** + * trigger dom event + * @param {String} event + * @param {Object} data + */ +function triggerDomEvent(event, data) { + var gestureEvent = document.createEvent('Event'); + gestureEvent.initEvent(event, true, true); + gestureEvent.gesture = data; + data.target.dispatchEvent(gestureEvent); +} + +assign(Hammer, { + INPUT_START: INPUT_START, + INPUT_MOVE: INPUT_MOVE, + INPUT_END: INPUT_END, + INPUT_CANCEL: INPUT_CANCEL, + + STATE_POSSIBLE: STATE_POSSIBLE, + STATE_BEGAN: STATE_BEGAN, + STATE_CHANGED: STATE_CHANGED, + STATE_ENDED: STATE_ENDED, + STATE_RECOGNIZED: STATE_RECOGNIZED, + STATE_CANCELLED: STATE_CANCELLED, + STATE_FAILED: STATE_FAILED, + + DIRECTION_NONE: DIRECTION_NONE, + DIRECTION_LEFT: DIRECTION_LEFT, + DIRECTION_RIGHT: DIRECTION_RIGHT, + DIRECTION_UP: DIRECTION_UP, + DIRECTION_DOWN: DIRECTION_DOWN, + DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, + DIRECTION_VERTICAL: DIRECTION_VERTICAL, + DIRECTION_ALL: DIRECTION_ALL, + + Manager: Manager, + Input: Input, + TouchAction: TouchAction, + + TouchInput: TouchInput, + MouseInput: MouseInput, + PointerEventInput: PointerEventInput, + TouchMouseInput: TouchMouseInput, + SingleTouchInput: SingleTouchInput, + + Recognizer: Recognizer, + AttrRecognizer: AttrRecognizer, + Tap: TapRecognizer, + Pan: PanRecognizer, + Swipe: SwipeRecognizer, + Pinch: PinchRecognizer, + Rotate: RotateRecognizer, + Press: PressRecognizer, + + on: addEventListeners, + off: removeEventListeners, + each: each, + merge: merge, + extend: extend, + assign: assign, + inherit: inherit, + bindFn: bindFn, + prefixed: prefixed +}); + +// this prevents errors when Hammer is loaded in the presence of an AMD +// style loader but by script tag, not by the loader. +var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line +freeGlobal.Hammer = Hammer; + +if (typeof define === 'function' && define.amd) { + define(function() { + return Hammer; + }); +} else if (typeof module != 'undefined' && module.exports) { + module.exports = Hammer; +} else { + window[exportName] = Hammer; +} + +})(window, document, 'Hammer'); + ++ function($) { + "use strict"; + + var defaults; + + $.modal = function(params, onOpen) { + params = $.extend({}, defaults, params); + + + var buttons = params.buttons; + + var buttonsHtml = buttons.map(function(d, i) { + return '' + d.text + ''; + }).join(""); + + var tpl = '
    ' + + '
    ' + params.title + '
    ' + + ( params.text ? '
    '+params.text+'
    ' : '')+ + '
    ' + buttonsHtml + '
    ' + + '
    '; + + var dialog = $.openModal(tpl, onOpen); + + dialog.find(".weui-dialog__btn").each(function(i, e) { + var el = $(e); + el.click(function() { + //先关闭对话框,再调用回调函数 + if(params.autoClose) $.closeModal(); + + if(buttons[i].onClick) { + buttons[i].onClick.call(dialog); + } + }); + }); + + return dialog; + }; + + $.openModal = function(tpl, onOpen) { + var mask = $("
    ").appendTo(document.body); + mask.show(); + + var dialog = $(tpl).appendTo(document.body); + + if (onOpen) { + dialog.transitionEnd(function () { + onOpen.call(dialog); + }); + } + + dialog.show(); + mask.addClass("weui-mask--visible"); + dialog.addClass("weui-dialog--visible"); + + + return dialog; + } + + $.closeModal = function() { + $(".weui-mask--visible").removeClass("weui-mask--visible").transitionEnd(function() { + $(this).remove(); + }); + $(".weui-dialog--visible").removeClass("weui-dialog--visible").transitionEnd(function() { + $(this).remove(); + }); + }; + + $.alert = function(text, title, onOK) { + var config; + if (typeof text === 'object') { + config = text; + } else { + if (typeof title === 'function') { + onOK = arguments[1]; + title = undefined; + } + + config = { + text: text, + title: title, + onOK: onOK + } + } + return $.modal({ + text: config.text, + title: config.title, + buttons: [{ + text: defaults.buttonOK, + className: "primary", + onClick: config.onOK + }] + }); + } + + $.confirm = function(text, title, onOK, onCancel) { + var config; + if (typeof text === 'object') { + config = text + } else { + if (typeof title === 'function') { + onCancel = arguments[2]; + onOK = arguments[1]; + title = undefined; + } + + config = { + text: text, + title: title, + onOK: onOK, + onCancel: onCancel + } + } + return $.modal({ + text: config.text, + title: config.title, + buttons: [ + { + text: defaults.buttonCancel, + className: "default", + onClick: config.onCancel + }, + { + text: defaults.buttonOK, + className: "primary", + onClick: config.onOK + }] + }); + }; + + //如果参数过多,建议通过 config 对象进行配置,而不是传入多个参数。 + $.prompt = function(text, title, onOK, onCancel, input) { + var config; + if (typeof text === 'object') { + config = text; + } else { + if (typeof title === 'function') { + input = arguments[3]; + onCancel = arguments[2]; + onOK = arguments[1]; + title = undefined; + } + config = { + text: text, + title: title, + input: input, + onOK: onOK, + onCancel: onCancel, + empty: false //allow empty + } + } + + var modal = $.modal({ + text: '

    '+(config.text || '')+'

    ', + title: config.title, + autoClose: false, + buttons: [ + { + text: defaults.buttonCancel, + className: "default", + onClick: function () { + $.closeModal(); + config.onCancel && config.onCancel.call(modal); + } + }, + { + text: defaults.buttonOK, + className: "primary", + onClick: function() { + var input = $("#weui-prompt-input").val(); + if (!config.empty && (input === "" || input === null)) { + modal.find('.weui-prompt-input').focus()[0].select(); + return false; + } + $.closeModal(); + config.onOK && config.onOK.call(modal, input); + } + }] + }, function () { + this.find('.weui-prompt-input').focus()[0].select(); + }); + + return modal; + }; + + //如果参数过多,建议通过 config 对象进行配置,而不是传入多个参数。 + $.login = function(text, title, onOK, onCancel, username, password) { + var config; + if (typeof text === 'object') { + config = text; + } else { + if (typeof title === 'function') { + password = arguments[4]; + username = arguments[3]; + onCancel = arguments[2]; + onOK = arguments[1]; + title = undefined; + } + config = { + text: text, + title: title, + username: username, + password: password, + onOK: onOK, + onCancel: onCancel + } + } + + var modal = $.modal({ + text: '

    '+(config.text || '')+'

    ' + + '' + + '', + title: config.title, + autoClose: false, + buttons: [ + { + text: defaults.buttonCancel, + className: "default", + onClick: function () { + $.closeModal(); + config.onCancel && config.onCancel.call(modal); + } + }, { + text: defaults.buttonOK, + className: "primary", + onClick: function() { + var username = $("#weui-prompt-username").val(); + var password = $("#weui-prompt-password").val(); + if (!config.empty && (username === "" || username === null)) { + modal.find('#weui-prompt-username').focus()[0].select(); + return false; + } + if (!config.empty && (password === "" || password === null)) { + modal.find('#weui-prompt-password').focus()[0].select(); + return false; + } + $.closeModal(); + config.onOK && config.onOK.call(modal, username, password); + } + }] + }, function () { + this.find('#weui-prompt-username').focus()[0].select(); + }); + + return modal; + }; + + defaults = $.modal.prototype.defaults = { + title: "提示", + text: undefined, + buttonOK: "确定", + buttonCancel: "取消", + buttons: [{ + text: "确定", + className: "primary" + }], + autoClose: true //点击按钮自动关闭对话框,如果你不希望点击按钮就关闭对话框,可以把这个设置为false + }; + +}($); + ++ function($) { + "use strict"; + + var defaults; + + var show = function(html, className) { + className = className || ""; + var mask = $("
    ").appendTo(document.body); + + var tpl = '
    ' + html + '
    '; + var dialog = $(tpl).appendTo(document.body); + + dialog.addClass("weui-toast--visible"); + dialog.show(); + }; + + var hide = function(callback) { + $(".weui-mask_transparent").remove(); + $(".weui-toast--visible").removeClass("weui-toast--visible").transitionEnd(function() { + var $this = $(this); + $this.remove(); + callback && callback($this); + }); + } + + $.toast = function(text, style, callback) { + if(typeof style === "function") { + callback = style; + } + var className, iconClassName = 'weui-icon-success-no-circle'; + var duration = toastDefaults.duration; + if(style == "cancel") { + className = "weui-toast_cancel"; + iconClassName = 'weui-icon-cancel' + } else if(style == "forbidden") { + className = "weui-toast--forbidden"; + iconClassName = 'weui-icon-warn' + } else if(style == "text") { + className = "weui-toast--text"; + } else if(typeof style === typeof 1) { + duration = style + } + show('

    ' + (text || "已经完成") + '

    ', className); + + setTimeout(function() { + hide(callback); + }, duration); + } + + $.showLoading = function(text) { + var html = '
    '; + html += ''; + html += '
    '; + html += '

    ' + (text || "数据加载中") + '

    '; + show(html, 'weui_loading_toast'); + } + + $.hideLoading = function() { + hide(); + } + + var toastDefaults = $.toast.prototype.defaults = { + duration: 2500 + } + +}($); + ++ function($) { + "use strict"; + + var defaults; + + var show = function(params) { + + var mask = $("
    ").appendTo(document.body); + + var actions = params.actions || []; + + var actionsHtml = actions.map(function(d, i) { + return '
    ' + d.text + '
    '; + }).join(""); + + var titleHtml = ""; + + if (params.title) { + titleHtml = '

    ' + params.title + '

    '; + } + + var tpl = '
    '+ + titleHtml + + '
    '+ + actionsHtml + + '
    '+ + '
    '+ + '
    取消
    '+ + '
    '+ + '
    '; + var dialog = $(tpl).appendTo(document.body); + + dialog.find(".weui-actionsheet__menu .weui-actionsheet__cell, .weui-actionsheet__action .weui-actionsheet__cell").each(function(i, e) { + $(e).click(function() { + $.closeActions(); + params.onClose && params.onClose(); + if(actions[i] && actions[i].onClick) { + actions[i].onClick(); + } + }) + }); + + mask.show(); + dialog.show(); + mask.addClass("weui-mask--visible"); + dialog.addClass("weui-actionsheet_toggle"); + }; + + var hide = function() { + $(".weui-mask").removeClass("weui-mask--visible").transitionEnd(function() { + $(this).remove(); + }); + $(".weui-actionsheet").removeClass("weui-actionsheet_toggle").transitionEnd(function() { + $(this).remove(); + }); + } + + $.actions = function(params) { + params = $.extend({}, defaults, params); + show(params); + } + + $.closeActions = function() { + hide(); + } + + $(document).on("click", ".weui-actions_mask", function() { + $.closeActions(); + }); + + var defaults = $.actions.prototype.defaults = { + title: undefined, + onClose: undefined, + /*actions: [{ + text: "菜单", + className: "color-danger", + onClick: function() { + console.log(1); + } + },{ + text: "菜单2", + className: "color-success", + onClick: function() { + console.log(2); + } + }]*/ + } + +}($); + +/* =============================================================================== +************ Pull to refreh ************ +=============================================================================== */ +/* global $:true */ + ++function ($) { + "use strict"; + + var PTR = function(el, opt) { + if (typeof opt === typeof function () {}) { + opt = { + onRefresh: opt + } + } + if (typeof opt === typeof 'a') { + opt = undefined + } + this.opt = $.extend(PTR.defaults, opt || {}); + this.container = $(el); + this.attachEvents(); + } + + PTR.defaults = { + distance: 50, + onRefresh: undefined, + onPull: undefined + } + + PTR.prototype.touchStart = function(e) { + if(this.container.hasClass("refreshing")) return; + var p = $.getTouchPosition(e); + this.start = p; + this.diffX = this.diffY = 0; + }; + + PTR.prototype.touchMove= function(e) { + if(this.container.hasClass("refreshing")) return; + if(!this.start) return false; + if(this.container.scrollTop() > 0) return; + var p = $.getTouchPosition(e); + this.diffX = p.x - this.start.x; + this.diffY = p.y - this.start.y; + if (Math.abs(this.diffX) > Math.abs(this.diffY)) return true; // 说明是左右方向的拖动 + if(this.diffY < 0) return; + this.container.addClass("touching"); + e.preventDefault(); + e.stopPropagation(); + this.diffY = Math.pow(this.diffY, 0.75); + this.container.css("transform", "translate3d(0, "+this.diffY+"px, 0)"); + this.triggerPull(this.diffY) + }; + PTR.prototype.touchEnd = function() { + this.start = false; + if(this.diffY <= 0 || this.container.hasClass("refreshing")) return; + this.container.removeClass("touching"); + this.container.removeClass("pull-down pull-up"); + this.container.css("transform", ""); + if(Math.abs(this.diffY) <= this.opt.distance) { + } else { + this.triggerPullToRefresh(); + } + }; + + PTR.prototype.triggerPullToRefresh = function() { + this.triggerPull(this.opt.distance) + this.container.removeClass('pull-up').addClass("refreshing"); + if (this.opt.onRefresh) { + this.opt.onRefresh.call(this) + } + this.container.trigger("pull-to-refresh"); + } + + PTR.prototype.triggerPull = function(diffY) { + + if(diffY < this.opt.distance) { + this.container.removeClass("pull-up").addClass("pull-down"); + } else { + this.container.removeClass("pull-down").addClass("pull-up"); + } + + if (this.opt.onPull) { + this.opt.onPull.call(this, Math.floor(diffY / this.opt.distance * 100)) + } + this.container.trigger("pull"); + } + + PTR.prototype.pullToRefreshDone = function() { + this.container.removeClass("refreshing"); + } + + PTR.prototype.attachEvents = function() { + var el = this.container; + el.addClass("weui-pull-to-refresh"); + el.on($.touchEvents.start, $.proxy(this.touchStart, this)); + el.on($.touchEvents.move, $.proxy(this.touchMove, this)); + el.on($.touchEvents.end, $.proxy(this.touchEnd, this)); + }; + + var pullToRefreshDone = function(el) { + $(el).removeClass("refreshing"); + } + + $.fn.pullToRefresh = function(opt) { + return this.each(function() { + var $this = $(this) + var ptr = $this.data('ptr') + if (!ptr) $this.data('ptr', ptr = new PTR(this, opt)) + if (typeof opt === typeof 'a') { + ptr[opt].call(ptr) + } + }); + } + + $.fn.pullToRefreshDone = function() { + return this.each(function() { + pullToRefreshDone(this); + }); + } + +}($); + +/* =============================================================================== +************ Infinite ************ +=============================================================================== */ +/* global $:true */ ++function ($) { + "use strict"; + + // fix https://github.com/lihongxun945/jquery-weui/issues/442 + // chrome will always return 0, when use document.body.scrollTop + // https://stackoverflow.com/questions/43717316/google-chrome-document-body-scrolltop-always-returns-0 + var getOffset = function (container) { + var tagName = container[0].tagName.toUpperCase() + var scrollTop + if (tagName === 'BODY' || tagName === 'HTML') { + scrollTop = container.scrollTop() || $(window).scrollTop() + } else { + scrollTop = container.scrollTop() + } + var offset = container.scrollHeight() - ($(window).height() + scrollTop) + console.log(offset) + return offset + } + + var Infinite = function(el, distance) { + this.container = $(el); + this.container.data("infinite", this); + this.distance = distance || 50; + this.attachEvents(); + } + + Infinite.prototype.scroll = function() { + var container = this.container; + this._check(); + } + + Infinite.prototype.attachEvents = function(off) { + var el = this.container; + var scrollContainer = (el[0].tagName.toUpperCase() === "BODY" ? $(document) : el); + scrollContainer[off ? "off" : "on"]("scroll", $.proxy(this.scroll, this)); + }; + Infinite.prototype.detachEvents = function(off) { + this.attachEvents(true); + } + Infinite.prototype._check = function() { + var offset = getOffset(this.container); + if(Math.abs(offset) <= this.distance) { + this.container.trigger("infinite"); + } + } + + var infinite = function(el) { + attachEvents(el); + } + + $.fn.infinite = function(distance) { + return this.each(function() { + new Infinite(this, distance); + }); + } + $.fn.destroyInfinite = function() { + return this.each(function() { + var infinite = $(this).data("infinite"); + if(infinite && infinite.detachEvents) infinite.detachEvents(); + }); + } + +}($); + +/* global $:true */ ++function ($) { + "use strict"; + + var ITEM_ON = "weui-bar__item--on"; + + var showTab = function(a) { + var $a = $(a); + if($a.hasClass(ITEM_ON)) return; + var href = $a.attr("href"); + + if(!/^#/.test(href)) return ; + + $a.parent().find("."+ITEM_ON).removeClass(ITEM_ON); + $a.addClass(ITEM_ON); + + var bd = $a.parents(".weui-tab").find(".weui-tab__bd"); + + bd.find(".weui-tab__bd-item--active").removeClass("weui-tab__bd-item--active"); + + $(href).addClass("weui-tab__bd-item--active"); + } + + $.showTab = showTab; + + $(document).on("click", ".weui-navbar__item, .weui-tabbar__item", function(e) { + var $a = $(e.currentTarget); + var href = $a.attr("href"); + if($a.hasClass(ITEM_ON)) return; + if(!/^#/.test(href)) return; + + e.preventDefault(); + + showTab($a); + }); + +}($); + +/* global $:true */ ++ function($) { + "use strict"; + + $(document).on("click touchstart", ".weui-search-bar__label", function(e) { + $(e.target).parents(".weui-search-bar").addClass("weui-search-bar_focusing").find('input').focus(); + }) + /* + .on("blur", ".weui-search-bar__input", function(e) { + var $input = $(e.target); + if(!$input.val()) $input.parents(".weui-search-bar").removeClass("weui-search-bar_focusing"); + }) + */ + .on("click", ".weui-search-bar__cancel-btn", function(e) { + var $input = $(e.target).parents(".weui-search-bar").removeClass("weui-search-bar_focusing").find(".weui-search-bar__input").val("").blur(); + }) + .on("click", ".weui-icon-clear", function(e) { + var $input = $(e.target).parents(".weui-search-bar").find(".weui-search-bar__input").val("").focus(); + }); + +}($); + +/*=========================== +Device/OS Detection +===========================*/ +/* global $:true */ +;(function ($) { + "use strict"; + var device = {}; + var ua = navigator.userAgent; + + var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); + var ipad = ua.match(/(iPad).*OS\s([\d_]+)/); + var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/); + var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/); + + device.ios = device.android = device.iphone = device.ipad = device.androidChrome = false; + + // Android + if (android) { + device.os = 'android'; + device.osVersion = android[2]; + device.android = true; + device.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0; + } + if (ipad || iphone || ipod) { + device.os = 'ios'; + device.ios = true; + } + // iOS + if (iphone && !ipod) { + device.osVersion = iphone[2].replace(/_/g, '.'); + device.iphone = true; + } + if (ipad) { + device.osVersion = ipad[2].replace(/_/g, '.'); + device.ipad = true; + } + if (ipod) { + device.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null; + device.iphone = true; + } + // iOS 8+ changed UA + if (device.ios && device.osVersion && ua.indexOf('Version/') >= 0) { + if (device.osVersion.split('.')[0] === '10') { + device.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0]; + } + } + + // Webview + device.webView = (iphone || ipad || ipod) && ua.match(/.*AppleWebKit(?!.*Safari)/i); + + // Minimal UI + if (device.os && device.os === 'ios') { + var osVersionArr = device.osVersion.split('.'); + device.minimalUi = !device.webView && + (ipod || iphone) && + (osVersionArr[0] * 1 === 7 ? osVersionArr[1] * 1 >= 1 : osVersionArr[0] * 1 > 7) && + $('meta[name="viewport"]').length > 0 && $('meta[name="viewport"]').attr('content').indexOf('minimal-ui') >= 0; + } + + // Check for status bar and fullscreen app mode + var windowWidth = $(window).width(); + var windowHeight = $(window).height(); + device.statusBar = false; + if (device.webView && (windowWidth * windowHeight === screen.width * screen.height)) { + device.statusBar = true; + } + else { + device.statusBar = false; + } + + // Classes + var classNames = []; + + // Pixel Ratio + device.pixelRatio = window.devicePixelRatio || 1; + classNames.push('pixel-ratio-' + Math.floor(device.pixelRatio)); + if (device.pixelRatio >= 2) { + classNames.push('retina'); + } + + // OS classes + if (device.os) { + classNames.push(device.os, device.os + '-' + device.osVersion.split('.')[0], device.os + '-' + device.osVersion.replace(/\./g, '-')); + if (device.os === 'ios') { + var major = parseInt(device.osVersion.split('.')[0], 10); + for (var i = major - 1; i >= 6; i--) { + classNames.push('ios-gt-' + i); + } + } + + } + // Status bar classes + if (device.statusBar) { + classNames.push('with-statusbar-overlay'); + } + else { + $('html').removeClass('with-statusbar-overlay'); + } + + // Add html classes + if (classNames.length > 0) $('html').addClass(classNames.join(' ')); + + $.device = device; +})($); + +/*====================================================== +************ Picker ************ +======================================================*/ +/* global $:true */ +/* jshint unused:false */ +/* jshint multistr:true */ ++ function($) { + "use strict"; + var Picker = function (params) { + var p = this; + var defaults = { + updateValuesOnMomentum: false, + updateValuesOnTouchmove: true, + rotateEffect: false, + momentumRatio: 7, + freeMode: false, + // Common settings + scrollToInput: true, + inputReadOnly: true, + toolbar: true, + toolbarCloseText: '完成', + title: '请选择', + toolbarTemplate: '
    \ +
    \ + {{closeText}}\ +

    {{title}}

    \ +
    \ +
    ', + }; + params = params || {}; + for (var def in defaults) { + if (typeof params[def] === 'undefined') { + params[def] = defaults[def]; + } + } + p.params = params; + p.cols = []; + p.initialized = false; + + // Inline flag + p.inline = p.params.container ? true : false; + + // 3D Transforms origin bug, only on safari + var originBug = $.device.ios || (navigator.userAgent.toLowerCase().indexOf('safari') >= 0 && navigator.userAgent.toLowerCase().indexOf('chrome') < 0) && !$.device.android; + + // Should be converted to popover + function isPopover() { + var toPopover = false; + if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover; + if (!p.inline && p.params.input) { + if (p.params.onlyInPopover) toPopover = true; + else { + if ($.device.ios) { + toPopover = $.device.ipad ? true : false; + } + else { + if ($(window).width() >= 768) toPopover = true; + } + } + } + return toPopover; + } + function inPopover() { + if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true; + else return false; + } + + // Value + p.setValue = function (arrValues, transition) { + var valueIndex = 0; + for (var i = 0; i < p.cols.length; i++) { + if (p.cols[i] && !p.cols[i].divider) { + p.cols[i].setValue(arrValues[valueIndex], transition); + valueIndex++; + } + } + }; + p.updateValue = function () { + var newValue = []; + var newDisplayValue = []; + for (var i = 0; i < p.cols.length; i++) { + if (!p.cols[i].divider) { + newValue.push(p.cols[i].value); + newDisplayValue.push(p.cols[i].displayValue); + } + } + if (newValue.indexOf(undefined) >= 0) { + return; + } + p.value = newValue; + p.displayValue = newDisplayValue; + if (p.params.onChange) { + p.params.onChange(p, p.value, p.displayValue); + } + if (p.input && p.input.length > 0) { + $(p.input).val(p.params.formatValue ? p.params.formatValue(p, p.value, p.displayValue) : p.value.join(' ')); + $(p.input).trigger('change'); + } + }; + + // Columns Handlers + p.initPickerCol = function (colElement, updateItems) { + var colContainer = $(colElement); + var colIndex = colContainer.index(); + var col = p.cols[colIndex]; + if (col.divider) return; + col.container = colContainer; + col.wrapper = col.container.find('.picker-items-col-wrapper'); + col.items = col.wrapper.find('.picker-item'); + + var i, j; + var wrapperHeight, itemHeight, itemsHeight, minTranslate, maxTranslate; + col.replaceValues = function (values, displayValues) { + col.destroyEvents(); + col.values = values; + col.displayValues = displayValues; + var newItemsHTML = p.columnHTML(col, true); + col.wrapper.html(newItemsHTML); + col.items = col.wrapper.find('.picker-item'); + col.calcSize(); + col.setValue(col.values[0] || '', 0, true); + col.initEvents(); + }; + col.calcSize = function () { + if (!col.values.length) return; + if (p.params.rotateEffect) { + col.container.removeClass('picker-items-col-absolute'); + if (!col.width) col.container.css({width:''}); + } + var colWidth, colHeight; + colWidth = 0; + colHeight = col.container[0].offsetHeight; + wrapperHeight = col.wrapper[0].offsetHeight; + itemHeight = col.items[0].offsetHeight; + itemsHeight = itemHeight * col.items.length; + minTranslate = colHeight / 2 - itemsHeight + itemHeight / 2; + maxTranslate = colHeight / 2 - itemHeight / 2; + if (col.width) { + colWidth = col.width; + if (parseInt(colWidth, 10) === colWidth) colWidth = colWidth + 'px'; + col.container.css({width: colWidth}); + } + if (p.params.rotateEffect) { + if (!col.width) { + col.items.each(function () { + var item = $(this); + item.css({width:'auto'}); + colWidth = Math.max(colWidth, item[0].offsetWidth); + item.css({width:''}); + }); + col.container.css({width: (colWidth + 2) + 'px'}); + } + col.container.addClass('picker-items-col-absolute'); + } + }; + col.calcSize(); + + col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)').transition(0); + + + var activeIndex = 0; + var animationFrameId; + + // Set Value Function + col.setValue = function (newValue, transition, valueCallbacks) { + if (typeof transition === 'undefined') transition = ''; + var newActiveIndex = col.wrapper.find('.picker-item[data-picker-value="' + newValue + '"]').index(); + if(typeof newActiveIndex === 'undefined' || newActiveIndex === -1) { + col.value = col.displayValue = newValue; + return; + } + var newTranslate = -newActiveIndex * itemHeight + maxTranslate; + // Update wrapper + col.wrapper.transition(transition); + col.wrapper.transform('translate3d(0,' + (newTranslate) + 'px,0)'); + + // Watch items + if (p.params.updateValuesOnMomentum && col.activeIndex && col.activeIndex !== newActiveIndex ) { + $.cancelAnimationFrame(animationFrameId); + col.wrapper.transitionEnd(function(){ + $.cancelAnimationFrame(animationFrameId); + }); + updateDuringScroll(); + } + + // Update items + col.updateItems(newActiveIndex, newTranslate, transition, valueCallbacks); + }; + + col.updateItems = function (activeIndex, translate, transition, valueCallbacks) { + if (typeof translate === 'undefined') { + translate = $.getTranslate(col.wrapper[0], 'y'); + } + if(typeof activeIndex === 'undefined') activeIndex = -Math.round((translate - maxTranslate)/itemHeight); + if (activeIndex < 0) activeIndex = 0; + if (activeIndex >= col.items.length) activeIndex = col.items.length - 1; + var previousActiveIndex = col.activeIndex; + col.activeIndex = activeIndex; + /* + col.wrapper.find('.picker-selected, .picker-after-selected, .picker-before-selected').removeClass('picker-selected picker-after-selected picker-before-selected'); + + col.items.transition(transition); + var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform(''); + var prevItems = selectedItem.prevAll().addClass('picker-before-selected'); + var nextItems = selectedItem.nextAll().addClass('picker-after-selected'); + */ + //去掉 .picker-after-selected, .picker-before-selected 以提高性能 + col.wrapper.find('.picker-selected').removeClass('picker-selected'); + if (p.params.rotateEffect) { + col.items.transition(transition); + } + var selectedItem = col.items.eq(activeIndex).addClass('picker-selected').transform(''); + + if (valueCallbacks || typeof valueCallbacks === 'undefined') { + // Update values + col.value = selectedItem.attr('data-picker-value'); + col.displayValue = col.displayValues ? col.displayValues[activeIndex] : col.value; + // On change callback + if (previousActiveIndex !== activeIndex) { + if (col.onChange) { + col.onChange(p, col.value, col.displayValue); + } + p.updateValue(); + } + } + + // Set 3D rotate effect + if (!p.params.rotateEffect) { + return; + } + var percentage = (translate - (Math.floor((translate - maxTranslate)/itemHeight) * itemHeight + maxTranslate)) / itemHeight; + + col.items.each(function () { + var item = $(this); + var itemOffsetTop = item.index() * itemHeight; + var translateOffset = maxTranslate - translate; + var itemOffset = itemOffsetTop - translateOffset; + var percentage = itemOffset / itemHeight; + + var itemsFit = Math.ceil(col.height / itemHeight / 2) + 1; + + var angle = (-18*percentage); + if (angle > 180) angle = 180; + if (angle < -180) angle = -180; + // Far class + if (Math.abs(percentage) > itemsFit) item.addClass('picker-item-far'); + else item.removeClass('picker-item-far'); + // Set transform + item.transform('translate3d(0, ' + (-translate + maxTranslate) + 'px, ' + (originBug ? -110 : 0) + 'px) rotateX(' + angle + 'deg)'); + }); + }; + + function updateDuringScroll() { + animationFrameId = $.requestAnimationFrame(function () { + col.updateItems(undefined, undefined, 0); + updateDuringScroll(); + }); + } + + // Update items on init + if (updateItems) col.updateItems(0, maxTranslate, 0); + + var allowItemClick = true; + var isTouched, isMoved, touchStartY, touchCurrentY, touchStartTime, touchEndTime, startTranslate, returnTo, currentTranslate, prevTranslate, velocityTranslate, velocityTime; + function handleTouchStart (e) { + if (isMoved || isTouched) return; + e.preventDefault(); + isTouched = true; + var position = $.getTouchPosition(e); + touchStartY = touchCurrentY = position.y; + touchStartTime = (new Date()).getTime(); + + allowItemClick = true; + startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y'); + } + function handleTouchMove (e) { + if (!isTouched) return; + e.preventDefault(); + allowItemClick = false; + var position = $.getTouchPosition(e); + touchCurrentY = position.y; + if (!isMoved) { + // First move + $.cancelAnimationFrame(animationFrameId); + isMoved = true; + startTranslate = currentTranslate = $.getTranslate(col.wrapper[0], 'y'); + col.wrapper.transition(0); + } + e.preventDefault(); + + var diff = touchCurrentY - touchStartY; + currentTranslate = startTranslate + diff; + returnTo = undefined; + + // Normalize translate + if (currentTranslate < minTranslate) { + currentTranslate = minTranslate - Math.pow(minTranslate - currentTranslate, 0.8); + returnTo = 'min'; + } + if (currentTranslate > maxTranslate) { + currentTranslate = maxTranslate + Math.pow(currentTranslate - maxTranslate, 0.8); + returnTo = 'max'; + } + // Transform wrapper + col.wrapper.transform('translate3d(0,' + currentTranslate + 'px,0)'); + + // Update items + col.updateItems(undefined, currentTranslate, 0, p.params.updateValuesOnTouchmove); + + // Calc velocity + velocityTranslate = currentTranslate - prevTranslate || currentTranslate; + velocityTime = (new Date()).getTime(); + prevTranslate = currentTranslate; + } + function handleTouchEnd (e) { + if (!isTouched || !isMoved) { + isTouched = isMoved = false; + return; + } + isTouched = isMoved = false; + col.wrapper.transition(''); + if (returnTo) { + if (returnTo === 'min') { + col.wrapper.transform('translate3d(0,' + minTranslate + 'px,0)'); + } + else col.wrapper.transform('translate3d(0,' + maxTranslate + 'px,0)'); + } + touchEndTime = new Date().getTime(); + var velocity, newTranslate; + if (touchEndTime - touchStartTime > 300) { + newTranslate = currentTranslate; + } + else { + velocity = Math.abs(velocityTranslate / (touchEndTime - velocityTime)); + newTranslate = currentTranslate + velocityTranslate * p.params.momentumRatio; + } + + newTranslate = Math.max(Math.min(newTranslate, maxTranslate), minTranslate); + + // Active Index + var activeIndex = -Math.floor((newTranslate - maxTranslate)/itemHeight); + + // Normalize translate + if (!p.params.freeMode) newTranslate = -activeIndex * itemHeight + maxTranslate; + + // Transform wrapper + col.wrapper.transform('translate3d(0,' + (parseInt(newTranslate,10)) + 'px,0)'); + + // Update items + col.updateItems(activeIndex, newTranslate, '', true); + + // Watch items + if (p.params.updateValuesOnMomentum) { + updateDuringScroll(); + col.wrapper.transitionEnd(function(){ + $.cancelAnimationFrame(animationFrameId); + }); + } + + // Allow click + setTimeout(function () { + allowItemClick = true; + }, 100); + } + + function handleClick(e) { + if (!allowItemClick) return; + $.cancelAnimationFrame(animationFrameId); + /*jshint validthis:true */ + var value = $(this).attr('data-picker-value'); + col.setValue(value); + } + + col.initEvents = function (detach) { + var method = detach ? 'off' : 'on'; + col.container[method]($.touchEvents.start, handleTouchStart); + col.container[method]($.touchEvents.move, handleTouchMove); + col.container[method]($.touchEvents.end, handleTouchEnd); + col.items[method]('click', handleClick); + }; + col.destroyEvents = function () { + col.initEvents(true); + }; + + col.container[0].f7DestroyPickerCol = function () { + col.destroyEvents(); + }; + + col.initEvents(); + + }; + p.destroyPickerCol = function (colContainer) { + colContainer = $(colContainer); + if ('f7DestroyPickerCol' in colContainer[0]) colContainer[0].f7DestroyPickerCol(); + }; + // Resize cols + function resizeCols() { + if (!p.opened) return; + for (var i = 0; i < p.cols.length; i++) { + if (!p.cols[i].divider) { + p.cols[i].calcSize(); + p.cols[i].setValue(p.cols[i].value, 0, false); + } + } + } + $(window).on('resize', resizeCols); + + // HTML Layout + p.columnHTML = function (col, onlyItems) { + var columnItemsHTML = ''; + var columnHTML = ''; + if (col.divider) { + columnHTML += '
    ' + col.content + '
    '; + } + else { + for (var j = 0; j < col.values.length; j++) { + columnItemsHTML += '
    ' + (col.displayValues ? col.displayValues[j] : col.values[j]) + '
    '; + } + columnHTML += '
    ' + columnItemsHTML + '
    '; + } + return onlyItems ? columnItemsHTML : columnHTML; + }; + p.layout = function () { + var pickerHTML = ''; + var pickerClass = ''; + var i; + p.cols = []; + var colsHTML = ''; + for (i = 0; i < p.params.cols.length; i++) { + var col = p.params.cols[i]; + colsHTML += p.columnHTML(p.params.cols[i]); + p.cols.push(col); + } + pickerClass = 'weui-picker-modal picker-columns ' + (p.params.cssClass || '') + (p.params.rotateEffect ? ' picker-3d' : '') + (p.params.cols.length === 1 ? ' picker-columns-single' : ''); + pickerHTML = + '
    ' + + (p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText).replace(/{{title}}/g, p.params.title) : '') + + '
    ' + + colsHTML + + '
    ' + + '
    ' + + '
    '; + + p.pickerHTML = pickerHTML; + }; + + // Input Events + function openOnInput(e) { + e.preventDefault(); + if (p.opened) return; + p.open(); + if (p.params.scrollToInput && !isPopover()) { + var pageContent = p.input.parents('.content'); + if (pageContent.length === 0) return; + + var paddingTop = parseInt(pageContent.css('padding-top'), 10), + paddingBottom = parseInt(pageContent.css('padding-bottom'), 10), + pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(), + pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(), + newPaddingBottom; + var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight; + if (inputTop > pageHeight) { + var scrollTop = pageContent.scrollTop() + inputTop - pageHeight; + if (scrollTop + pageHeight > pageScrollHeight) { + newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom; + if (pageHeight === pageScrollHeight) { + newPaddingBottom = p.container.height(); + } + pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'}); + } + pageContent.scrollTop(scrollTop, 300); + } + } + } + function closeOnHTMLClick(e) { + if (inPopover()) return; + if (p.input && p.input.length > 0) { + if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close(); + } + else { + if ($(e.target).parents('.weui-picker-modal').length === 0) p.close(); + } + } + + if (p.params.input) { + p.input = $(p.params.input); + if (p.input.length > 0) { + if (p.params.inputReadOnly) p.input.prop('readOnly', true); + if (!p.inline) { + p.input.on('click', openOnInput); + } + if (p.params.inputReadOnly) { + p.input.on('focus mousedown', function (e) { + e.preventDefault(); + }); + } + } + + } + + if (!p.inline) $('html').on('click', closeOnHTMLClick); + + // Open + function onPickerClose() { + p.opened = false; + if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''}); + if (p.params.onClose) p.params.onClose(p); + + // Destroy events + p.container.find('.picker-items-col').each(function () { + p.destroyPickerCol(this); + }); + } + + p.opened = false; + p.open = function () { + var toPopover = isPopover(); + + if (!p.opened) { + + // Layout + p.layout(); + + // Append + if (toPopover) { + p.pickerHTML = '
    ' + p.pickerHTML + '
    '; + p.popover = $.popover(p.pickerHTML, p.params.input, true); + p.container = $(p.popover).find('.weui-picker-modal'); + $(p.popover).on('close', function () { + onPickerClose(); + }); + } + else if (p.inline) { + p.container = $(p.pickerHTML); + p.container.addClass('picker-modal-inline'); + $(p.params.container).append(p.container); + } + else { + p.container = $($.openPicker(p.pickerHTML)); + $(p.container) + .on('close', function () { + onPickerClose(); + }); + } + + // Store picker instance + p.container[0].f7Picker = p; + + // Init Events + p.container.find('.picker-items-col').each(function () { + var updateItems = true; + if ((!p.initialized && p.params.value) || (p.initialized && p.value)) updateItems = false; + p.initPickerCol(this, updateItems); + }); + + // Set value + if (!p.initialized) { + if (p.params.value) { + p.setValue(p.params.value, 0); + } + } + else { + if (p.value) p.setValue(p.value, 0); + } + } + + // Set flag + p.opened = true; + p.initialized = true; + + if (p.params.onOpen) p.params.onOpen(p); + }; + + // Close + p.close = function (force) { + if (!p.opened || p.inline) return; + if (inPopover()) { + $.closePicker(p.popover); + return; + } + else { + $.closePicker(p.container); + return; + } + }; + + // Destroy + p.destroy = function () { + p.close(); + if (p.params.input && p.input.length > 0) { + p.input.off('click focus', openOnInput); + $(p.input).data('picker', null); + } + $('html').off('click', closeOnHTMLClick); + $(window).off('resize', resizeCols); + }; + + if (p.inline) { + p.open(); + } + + return p; + }; + + $(document).on("click", ".close-picker", function() { + var pickerToClose = $('.weui-picker-modal.weui-picker-modal-visible'); + if (pickerToClose.length > 0) { + $.closePicker(pickerToClose); + } + }); + + //修复picker会滚动页面的bug + $(document).on($.touchEvents.move, ".picker-modal-inner", function(e) { + e.preventDefault(); + }); + + + $.openPicker = function(tpl, className, callback) { + + if(typeof className === "function") { + callback = className; + className = undefined; + } + + $.closePicker(); + + var container = $("
    ").appendTo(document.body); + container.show(); + + container.addClass("weui-picker-container-visible"); + + //关于布局的问题,如果直接放在body上,则做动画的时候会撑开body高度而导致滚动条变化。 + var dialog = $(tpl).appendTo(container); + + dialog.width(); //通过取一次CSS值,强制浏览器不能把上下两行代码合并执行,因为合并之后会导致无法出现动画。 + + dialog.addClass("weui-picker-modal-visible"); + + callback && container.on("close", callback); + + return dialog; + } + + $.updatePicker = function(tpl) { + var container = $(".weui-picker-container-visible"); + if(!container[0]) return false; + + container.html(""); + + var dialog = $(tpl).appendTo(container); + + dialog.addClass("weui-picker-modal-visible"); + + return dialog; + } + + $.closePicker = function(container, callback) { + if(typeof container === "function") callback = container; + $(".weui-picker-modal-visible").removeClass("weui-picker-modal-visible").transitionEnd(function() { + $(this).parent().remove(); + callback && callback(); + }).trigger("close"); + }; + + $.fn.picker = function(params) { + var args = arguments; + return this.each(function() { + if(!this) return; + var $this = $(this); + + var picker = $this.data("picker"); + if(!picker) { + params = $.extend({ input: this }, params || {}) // https://github.com/lihongxun945/jquery-weui/issues/432 + var inputValue = $this.val(); + if(params.value === undefined && inputValue !== "") { + params.value = (params.cols && params.cols.length > 1) ? inputValue.split(" ") : [inputValue]; + } + var p = $.extend({input: this}, params); + picker = new Picker(p); + $this.data("picker", picker); + } + if(typeof params === typeof "a") { + picker[params].apply(picker, Array.prototype.slice.call(args, 1)); + } + }); + }; +}($); + +/* global $:true */ ++ function($) { + "use strict"; + + var defaults; + + var selects = []; + + var Select = function(input, config) { + + var self = this; + this.config = config; + + //init empty data + this.data = { + values: '', + titles: '', + origins: [], + length: 0 + }; + + this.$input = $(input); + this.$input.prop("readOnly", true); + + this.initConfig(); + + config = this.config; + + this.$input.click($.proxy(this.open, this)); + selects.push(this) + } + + Select.prototype.initConfig = function() { + this.config = $.extend({}, defaults, this.config); + + var config = this.config; + + if(!config.items || !config.items.length) return; + + config.items = config.items.map(function(d, i) { + if(typeof d == typeof "a") { + return { + title: d, + value: d + }; + } + + return d; + }); + + + this.tpl = $.t7.compile("
    " + config.toolbarTemplate + (config.multi ? config.checkboxTemplate : config.radioTemplate) + "
    "); + + if(config.input !== undefined) this.$input.val(config.input); + + this.parseInitValue(); + + this._init = true; + } + + Select.prototype.updateInputValue = function(values, titles) { + var v, t; + if(this.config.multi) { + v = values.join(this.config.split); + t = titles.join(this.config.split); + } else { + v = values[0]; + t = titles[0]; + } + + //caculate origin data + var origins = []; + + this.config.items.forEach(function(d) { + values.each(function(i, dd) { + if(d.value == dd) origins.push(d); + }); + }); + + this.$input.val(t).data("values", v); + this.$input.attr("value", t).attr("data-values", v); + + var data = { + values: v, + titles: t, + valuesArray: values, + titlesArray: titles, + origins: origins, + length: origins.length + }; + this.data = data; + this.$input.trigger("change", data); + this.config.onChange && this.config.onChange.call(this, data); + } + + Select.prototype.parseInitValue = function() { + var value = this.$input.val(); + var items = this.config.items; + + //如果input为空,只有在第一次初始化的时候才保留默认选择。因为后来就是用户自己取消了全部选择,不能再为他选中默认值。 + if( !this._init && (value === undefined || value == null || value === "")) return; + + var titles = this.config.multi ? value.split(this.config.split) : [value]; + for(var i=0;i this.config.max) { + $.toast("最多只能选择"+this.config.max+"个", "text"); + return false + } + } + } + $.closePicker(function() { + self.onClose(); + callback && callback(); + }); + + return true + } + + Select.prototype.onClose = function() { + this._open = false; + if(this.config.onClose) this.config.onClose(this); + } + + Select.prototype.getHTML = function(callback) { + var config = this.config; + return this.tpl({ + items: config.items, + title: config.title, + closeText: config.closeText + }) + } + + + $.fn.select = function(params, args) { + + return this.each(function() { + var $this = $(this); + if(!$this.data("weui-select")) $this.data("weui-select", new Select(this, params)); + + var select = $this.data("weui-select"); + + if(typeof params === typeof "a") select[params].call(select, args); + + return select; + }); + } + + defaults = $.fn.select.prototype.defaults = { + items: [], + input: undefined, //输入框的初始值 + title: "请选择", + multi: false, + closeText: "确定", + autoClose: true, //是否选择完成后自动关闭,只有单选模式下才有效 + onChange: undefined, //function + beforeClose: undefined, // function 关闭之前,如果返回false则阻止关闭 + onClose: undefined, //function + onOpen: undefined, //function + split: ",", //多选模式下的分隔符 + min: undefined, //多选模式下可用,最少选择数 + max: undefined, //单选模式下可用,最多选择数 + toolbarTemplate: '
    \ +
    \ + {{closeText}}\ +

    {{title}}

    \ +
    \ +
    ', + radioTemplate: + '
    \ + {{#items}}\ + \ + {{/items}}\ +
    ', + checkboxTemplate: + '
    \ + {{#items}}\ + \ + {{/items}}\ +
    ', + } + +}($); + +/*====================================================== +************ Calendar ************ +======================================================*/ +/* global $:true */ +/*jshint unused: false*/ ++function ($) { + "use strict"; + var rtl = false; + var defaults; + var isSameDate = function (a, b) { + var a = new Date(a), + b = new Date(b); + return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate() + } + var Calendar = function (params) { + var p = this; + params = params || {}; + for (var def in defaults) { + if (typeof params[def] === 'undefined') { + params[def] = defaults[def]; + } + } + p.params = params; + p.initialized = false; + + // Inline flag + p.inline = p.params.container ? true : false; + + // Is horizontal + p.isH = p.params.direction === 'horizontal'; + + // RTL inverter + var inverter = p.isH ? (rtl ? -1 : 1) : 1; + + // Animating flag + p.animating = false; + + // Should be converted to popover + function isPopover() { + var toPopover = false; + if (!p.params.convertToPopover && !p.params.onlyInPopover) return toPopover; + if (!p.inline && p.params.input) { + if (p.params.onlyInPopover) toPopover = true; + else { + if ($.device.ios) { + toPopover = $.device.ipad ? true : false; + } + else { + if ($(window).width() >= 768) toPopover = true; + } + } + } + return toPopover; + } + function inPopover() { + if (p.opened && p.container && p.container.length > 0 && p.container.parents('.popover').length > 0) return true; + else return false; + } + + // Format date + function formatDate(date) { + date = new Date(date); + var year = date.getFullYear(); + var month = date.getMonth(); + var month1 = month + 1; + var day = date.getDate(); + var weekDay = date.getDay(); + return p.params.dateFormat + .replace(/yyyy/g, year) + .replace(/yy/g, (year + '').substring(2)) + .replace(/mm/g, month1 < 10 ? '0' + month1 : month1) + .replace(/m/g, month1) + .replace(/MM/g, p.params.monthNames[month]) + .replace(/M/g, p.params.monthNamesShort[month]) + .replace(/dd/g, day < 10 ? '0' + day : day) + .replace(/d/g, day) + .replace(/DD/g, p.params.dayNames[weekDay]) + .replace(/D/g, p.params.dayNamesShort[weekDay]); + } + + + // Value + p.addValue = function (value) { + if (p.params.multiple) { + if (!p.value) p.value = []; + var inValuesIndex; + for (var i = 0; i < p.value.length; i++) { + if (isSameDate(value, p.value[i])) { + inValuesIndex = i; + } + } + if (typeof inValuesIndex === 'undefined') { + p.value.push(value); + } + else { + p.value.splice(inValuesIndex, 1); + } + p.updateValue(); + } + else { + p.value = [value]; + p.updateValue(); + } + }; + p.setValue = function (arrValues) { + var date = new Date(arrValues[0]); + p.setYearMonth(date.getFullYear(), date.getMonth()); + p.addValue(+ date); + }; + p.updateValue = function () { + p.wrapper.find('.picker-calendar-day-selected').removeClass('picker-calendar-day-selected'); + var i, inputValue; + for (i = 0; i < p.value.length; i++) { + var valueDate = new Date(p.value[i]); + p.wrapper.find('.picker-calendar-day[data-date="' + valueDate.getFullYear() + '-' + valueDate.getMonth() + '-' + valueDate.getDate() + '"]').addClass('picker-calendar-day-selected'); + } + if (p.params.onChange) { + p.params.onChange(p, p.value.map(formatDate), p.value.map(function (d) { + return + new Date(typeof d === typeof 'a' ? d.split(/\D/).filter(function (a) { return !!a; }).join("-") : d); + })); + } + if (p.input && p.input.length > 0) { + if (p.params.formatValue) inputValue = p.params.formatValue(p, p.value); + else { + inputValue = []; + for (i = 0; i < p.value.length; i++) { + inputValue.push(formatDate(p.value[i])); + } + inputValue = inputValue.join(', '); + } + $(p.input).val(inputValue); + $(p.input).trigger('change'); + } + }; + + // Columns Handlers + p.initCalendarEvents = function () { + var col; + var allowItemClick = true; + var isTouched, isMoved, touchStartX, touchStartY, touchCurrentX, touchCurrentY, touchStartTime, touchEndTime, startTranslate, currentTranslate, wrapperWidth, wrapperHeight, percentage, touchesDiff, isScrolling; + function handleTouchStart (e) { + if (isMoved || isTouched) return; + // e.preventDefault(); + isTouched = true; + var position = $.getTouchPosition(e); + touchStartX = touchCurrentY = position.x; + touchStartY = touchCurrentY = position.y; + touchStartTime = (new Date()).getTime(); + percentage = 0; + allowItemClick = true; + isScrolling = undefined; + startTranslate = currentTranslate = p.monthsTranslate; + } + function handleTouchMove (e) { + if (!isTouched) return; + var position = $.getTouchPosition(e); + touchCurrentX = position.x; + touchCurrentY = position.y; + if (typeof isScrolling === 'undefined') { + isScrolling = !!(isScrolling || Math.abs(touchCurrentY - touchStartY) > Math.abs(touchCurrentX - touchStartX)); + } + if (p.isH && isScrolling) { + isTouched = false; + return; + } + e.preventDefault(); + if (p.animating) { + isTouched = false; + return; + } + allowItemClick = false; + if (!isMoved) { + // First move + isMoved = true; + wrapperWidth = p.wrapper[0].offsetWidth; + wrapperHeight = p.wrapper[0].offsetHeight; + p.wrapper.transition(0); + } + e.preventDefault(); + + touchesDiff = p.isH ? touchCurrentX - touchStartX : touchCurrentY - touchStartY; + percentage = touchesDiff/(p.isH ? wrapperWidth : wrapperHeight); + currentTranslate = (p.monthsTranslate * inverter + percentage) * 100; + + // Transform wrapper + p.wrapper.transform('translate3d(' + (p.isH ? currentTranslate : 0) + '%, ' + (p.isH ? 0 : currentTranslate) + '%, 0)'); + + } + function handleTouchEnd (e) { + if (!isTouched || !isMoved) { + isTouched = isMoved = false; + return; + } + isTouched = isMoved = false; + + touchEndTime = new Date().getTime(); + if (touchEndTime - touchStartTime < 300) { + if (Math.abs(touchesDiff) < 10) { + p.resetMonth(); + } + else if (touchesDiff >= 10) { + if (rtl) p.nextMonth(); + else p.prevMonth(); + } + else { + if (rtl) p.prevMonth(); + else p.nextMonth(); + } + } + else { + if (percentage <= -0.5) { + if (rtl) p.prevMonth(); + else p.nextMonth(); + } + else if (percentage >= 0.5) { + if (rtl) p.nextMonth(); + else p.prevMonth(); + } + else { + p.resetMonth(); + } + } + + // Allow click + setTimeout(function () { + allowItemClick = true; + }, 100); + } + + function handleDayClick(e) { + if (!allowItemClick) return; + var day = $(e.target).parents('.picker-calendar-day'); + if (day.length === 0 && $(e.target).hasClass('picker-calendar-day')) { + day = $(e.target); + } + if (day.length === 0) return; + // if (day.hasClass('picker-calendar-day-selected') && !p.params.multiple) return; + if (day.hasClass('picker-calendar-day-disabled')) return; + if (day.hasClass('picker-calendar-day-next')) p.nextMonth(); + if (day.hasClass('picker-calendar-day-prev')) p.prevMonth(); + var dateYear = day.attr('data-year'); + var dateMonth = day.attr('data-month'); + var dateDay = day.attr('data-day'); + if (p.params.onDayClick) { + p.params.onDayClick(p, day[0], dateYear, dateMonth, dateDay); + } + p.addValue(new Date(dateYear, dateMonth, dateDay).getTime()); + if (p.params.closeOnSelect && !p.params.multiple) p.close(); + } + + p.container.find('.picker-calendar-prev-month').on('click', p.prevMonth); + p.container.find('.picker-calendar-next-month').on('click', p.nextMonth); + p.container.find('.picker-calendar-prev-year').on('click', p.prevYear); + p.container.find('.picker-calendar-next-year').on('click', p.nextYear); + p.wrapper.on('click', handleDayClick); + if (p.params.touchMove) { + p.wrapper.on($.touchEvents.start, handleTouchStart); + p.wrapper.on($.touchEvents.move, handleTouchMove); + p.wrapper.on($.touchEvents.end, handleTouchEnd); + } + + p.container[0].f7DestroyCalendarEvents = function () { + p.container.find('.picker-calendar-prev-month').off('click', p.prevMonth); + p.container.find('.picker-calendar-next-month').off('click', p.nextMonth); + p.container.find('.picker-calendar-prev-year').off('click', p.prevYear); + p.container.find('.picker-calendar-next-year').off('click', p.nextYear); + p.wrapper.off('click', handleDayClick); + if (p.params.touchMove) { + p.wrapper.off($.touchEvents.start, handleTouchStart); + p.wrapper.off($.touchEvents.move, handleTouchMove); + p.wrapper.off($.touchEvents.end, handleTouchEnd); + } + }; + + + }; + p.destroyCalendarEvents = function (colContainer) { + if ('f7DestroyCalendarEvents' in p.container[0]) p.container[0].f7DestroyCalendarEvents(); + }; + + // Calendar Methods + p.daysInMonth = function (date) { + var d = new Date(date); + return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate(); + }; + p.monthHTML = function (date, offset) { + date = new Date(date); + var year = date.getFullYear(), + month = date.getMonth(), + day = date.getDate(); + if (offset === 'next') { + if (month === 11) date = new Date(year + 1, 0); + else date = new Date(year, month + 1, 1); + } + if (offset === 'prev') { + if (month === 0) date = new Date(year - 1, 11); + else date = new Date(year, month - 1, 1); + } + if (offset === 'next' || offset === 'prev') { + month = date.getMonth(); + year = date.getFullYear(); + } + var daysInPrevMonth = p.daysInMonth(new Date(date.getFullYear(), date.getMonth()).getTime() - 10 * 24 * 60 * 60 * 1000), + daysInMonth = p.daysInMonth(date), + firstDayOfMonthIndex = new Date(date.getFullYear(), date.getMonth()).getDay(); + if (firstDayOfMonthIndex === 0) firstDayOfMonthIndex = 7; + + var dayDate, currentValues = [], i, j, + rows = 6, cols = 7, + monthHTML = '', + dayIndex = 0 + (p.params.firstDay - 1), + today = new Date().setHours(0,0,0,0), + minDate = p.params.minDate ? new Date(p.params.minDate).getTime() : null, + maxDate = p.params.maxDate ? new Date(p.params.maxDate).getTime() : null; + + if (p.value && p.value.length) { + for (i = 0; i < p.value.length; i++) { + currentValues.push(new Date(p.value[i]).setHours(0,0,0,0)); + } + } + + for (i = 1; i <= rows; i++) { + var rowHTML = ''; + var row = i; + for (j = 1; j <= cols; j++) { + var col = j; + dayIndex ++; + var dayNumber = dayIndex - firstDayOfMonthIndex; + var addClass = ''; + if (dayNumber < 0) { + dayNumber = daysInPrevMonth + dayNumber + 1; + addClass += ' picker-calendar-day-prev'; + dayDate = new Date(month - 1 < 0 ? year - 1 : year, month - 1 < 0 ? 11 : month - 1, dayNumber).getTime(); + } + else { + dayNumber = dayNumber + 1; + if (dayNumber > daysInMonth) { + dayNumber = dayNumber - daysInMonth; + addClass += ' picker-calendar-day-next'; + dayDate = new Date(month + 1 > 11 ? year + 1 : year, month + 1 > 11 ? 0 : month + 1, dayNumber).getTime(); + } + else { + dayDate = new Date(year, month, dayNumber).getTime(); + } + } + // Today + if (dayDate === today) addClass += ' picker-calendar-day-today'; + // Selected + if (currentValues.indexOf(dayDate) >= 0) addClass += ' picker-calendar-day-selected'; + // Weekend + if (p.params.weekendDays.indexOf(col - 1) >= 0) { + addClass += ' picker-calendar-day-weekend'; + } + // Disabled + if ((minDate && dayDate < minDate) || (maxDate && dayDate > maxDate)) { + addClass += ' picker-calendar-day-disabled'; + } + + dayDate = new Date(dayDate); + var dayYear = dayDate.getFullYear(); + var dayMonth = dayDate.getMonth(); + rowHTML += '
    '+dayNumber+'
    '; + } + monthHTML += '
    ' + rowHTML + '
    '; + } + monthHTML = '
    ' + monthHTML + '
    '; + return monthHTML; + }; + p.animating = false; + p.updateCurrentMonthYear = function (dir) { + if (typeof dir === 'undefined') { + p.currentMonth = parseInt(p.months.eq(1).attr('data-month'), 10); + p.currentYear = parseInt(p.months.eq(1).attr('data-year'), 10); + } + else { + p.currentMonth = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-month'), 10); + p.currentYear = parseInt(p.months.eq(dir === 'next' ? (p.months.length - 1) : 0).attr('data-year'), 10); + } + p.container.find('.current-month-value').text(p.params.monthNames[p.currentMonth]); + p.container.find('.current-year-value').text(p.currentYear); + + }; + p.onMonthChangeStart = function (dir) { + p.updateCurrentMonthYear(dir); + p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next'); + var currentIndex = dir === 'next' ? p.months.length - 1 : 0; + + p.months.eq(currentIndex).addClass('picker-calendar-month-current'); + p.months.eq(dir === 'next' ? currentIndex - 1 : currentIndex + 1).addClass(dir === 'next' ? 'picker-calendar-month-prev' : 'picker-calendar-month-next'); + + if (p.params.onMonthYearChangeStart) { + p.params.onMonthYearChangeStart(p, p.currentYear, p.currentMonth); + } + }; + p.onMonthChangeEnd = function (dir, rebuildBoth) { + p.animating = false; + var nextMonthHTML, prevMonthHTML, newMonthHTML; + p.wrapper.find('.picker-calendar-month:not(.picker-calendar-month-prev):not(.picker-calendar-month-current):not(.picker-calendar-month-next)').remove(); + + if (typeof dir === 'undefined') { + dir = 'next'; + rebuildBoth = true; + } + if (!rebuildBoth) { + newMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), dir); + } + else { + p.wrapper.find('.picker-calendar-month-next, .picker-calendar-month-prev').remove(); + prevMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'prev'); + nextMonthHTML = p.monthHTML(new Date(p.currentYear, p.currentMonth), 'next'); + } + if (dir === 'next' || rebuildBoth) { + p.wrapper.append(newMonthHTML || nextMonthHTML); + } + if (dir === 'prev' || rebuildBoth) { + p.wrapper.prepend(newMonthHTML || prevMonthHTML); + } + p.months = p.wrapper.find('.picker-calendar-month'); + p.setMonthsTranslate(p.monthsTranslate); + if (p.params.onMonthAdd) { + p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]); + } + if (p.params.onMonthYearChangeEnd) { + p.params.onMonthYearChangeEnd(p, p.currentYear, p.currentMonth); + } + }; + p.setMonthsTranslate = function (translate) { + translate = translate || p.monthsTranslate || 0; + if (typeof p.monthsTranslate === 'undefined') p.monthsTranslate = translate; + p.months.removeClass('picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next'); + var prevMonthTranslate = -(translate + 1) * 100 * inverter; + var currentMonthTranslate = -translate * 100 * inverter; + var nextMonthTranslate = -(translate - 1) * 100 * inverter; + p.months.eq(0).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev'); + p.months.eq(1).transform('translate3d(' + (p.isH ? currentMonthTranslate : 0) + '%, ' + (p.isH ? 0 : currentMonthTranslate) + '%, 0)').addClass('picker-calendar-month-current'); + p.months.eq(2).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next'); + }; + p.nextMonth = function (transition) { + if (typeof transition === 'undefined' || typeof transition === 'object') { + transition = ''; + if (!p.params.animate) transition = 0; + } + var nextMonth = parseInt(p.months.eq(p.months.length - 1).attr('data-month'), 10); + var nextYear = parseInt(p.months.eq(p.months.length - 1).attr('data-year'), 10); + var nextDate = new Date(nextYear, nextMonth); + var nextDateTime = nextDate.getTime(); + var transitionEndCallback = p.animating ? false : true; + if (p.params.maxDate) { + if (nextDateTime > new Date(p.params.maxDate).getTime()) { + return p.resetMonth(); + } + } + p.monthsTranslate --; + if (nextMonth === p.currentMonth) { + var nextMonthTranslate = -(p.monthsTranslate) * 100 * inverter; + var nextMonthHTML = $(p.monthHTML(nextDateTime, 'next')).transform('translate3d(' + (p.isH ? nextMonthTranslate : 0) + '%, ' + (p.isH ? 0 : nextMonthTranslate) + '%, 0)').addClass('picker-calendar-month-next'); + p.wrapper.append(nextMonthHTML[0]); + p.months = p.wrapper.find('.picker-calendar-month'); + if (p.params.onMonthAdd) { + p.params.onMonthAdd(p, p.months.eq(p.months.length - 1)[0]); + } + } + p.animating = true; + p.onMonthChangeStart('next'); + var translate = (p.monthsTranslate * 100) * inverter; + + p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)'); + if (transitionEndCallback) { + p.wrapper.transitionEnd(function () { + p.onMonthChangeEnd('next'); + }); + } + if (!p.params.animate) { + p.onMonthChangeEnd('next'); + } + }; + p.prevMonth = function (transition) { + if (typeof transition === 'undefined' || typeof transition === 'object') { + transition = ''; + if (!p.params.animate) transition = 0; + } + var prevMonth = parseInt(p.months.eq(0).attr('data-month'), 10); + var prevYear = parseInt(p.months.eq(0).attr('data-year'), 10); + var prevDate = new Date(prevYear, prevMonth + 1, -1); + var prevDateTime = prevDate.getTime(); + var transitionEndCallback = p.animating ? false : true; + if (p.params.minDate) { + if (prevDateTime < new Date(p.params.minDate).getTime()) { + return p.resetMonth(); + } + } + p.monthsTranslate ++; + if (prevMonth === p.currentMonth) { + var prevMonthTranslate = -(p.monthsTranslate) * 100 * inverter; + var prevMonthHTML = $(p.monthHTML(prevDateTime, 'prev')).transform('translate3d(' + (p.isH ? prevMonthTranslate : 0) + '%, ' + (p.isH ? 0 : prevMonthTranslate) + '%, 0)').addClass('picker-calendar-month-prev'); + p.wrapper.prepend(prevMonthHTML[0]); + p.months = p.wrapper.find('.picker-calendar-month'); + if (p.params.onMonthAdd) { + p.params.onMonthAdd(p, p.months.eq(0)[0]); + } + } + p.animating = true; + p.onMonthChangeStart('prev'); + var translate = (p.monthsTranslate * 100) * inverter; + p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)'); + if (transitionEndCallback) { + p.wrapper.transitionEnd(function () { + p.onMonthChangeEnd('prev'); + }); + } + if (!p.params.animate) { + p.onMonthChangeEnd('prev'); + } + }; + p.resetMonth = function (transition) { + if (typeof transition === 'undefined') transition = ''; + var translate = (p.monthsTranslate * 100) * inverter; + p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? translate : 0) + '%, ' + (p.isH ? 0 : translate) + '%, 0)'); + }; + p.setYearMonth = function (year, month, transition) { + if (typeof year === 'undefined') year = p.currentYear; + if (typeof month === 'undefined') month = p.currentMonth; + if (typeof transition === 'undefined' || typeof transition === 'object') { + transition = ''; + if (!p.params.animate) transition = 0; + } + var targetDate; + if (year < p.currentYear) { + targetDate = new Date(year, month + 1, -1).getTime(); + } + else { + targetDate = new Date(year, month).getTime(); + } + if (p.params.maxDate && targetDate > new Date(p.params.maxDate).getTime()) { + return false; + } + if (p.params.minDate && targetDate < new Date(p.params.minDate).getTime()) { + return false; + } + var currentDate = new Date(p.currentYear, p.currentMonth).getTime(); + var dir = targetDate > currentDate ? 'next' : 'prev'; + var newMonthHTML = p.monthHTML(new Date(year, month)); + p.monthsTranslate = p.monthsTranslate || 0; + var prevTranslate = p.monthsTranslate; + var monthTranslate, wrapperTranslate; + var transitionEndCallback = p.animating ? false : true; + if (targetDate > currentDate) { + // To next + p.monthsTranslate --; + if (!p.animating) p.months.eq(p.months.length - 1).remove(); + p.wrapper.append(newMonthHTML); + p.months = p.wrapper.find('.picker-calendar-month'); + monthTranslate = -(prevTranslate - 1) * 100 * inverter; + p.months.eq(p.months.length - 1).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-next'); + } + else { + // To prev + p.monthsTranslate ++; + if (!p.animating) p.months.eq(0).remove(); + p.wrapper.prepend(newMonthHTML); + p.months = p.wrapper.find('.picker-calendar-month'); + monthTranslate = -(prevTranslate + 1) * 100 * inverter; + p.months.eq(0).transform('translate3d(' + (p.isH ? monthTranslate : 0) + '%, ' + (p.isH ? 0 : monthTranslate) + '%, 0)').addClass('picker-calendar-month-prev'); + } + if (p.params.onMonthAdd) { + p.params.onMonthAdd(p, dir === 'next' ? p.months.eq(p.months.length - 1)[0] : p.months.eq(0)[0]); + } + p.animating = true; + p.onMonthChangeStart(dir); + wrapperTranslate = (p.monthsTranslate * 100) * inverter; + p.wrapper.transition(transition).transform('translate3d(' + (p.isH ? wrapperTranslate : 0) + '%, ' + (p.isH ? 0 : wrapperTranslate) + '%, 0)'); + if (transitionEndCallback) { + p.wrapper.transitionEnd(function () { + p.onMonthChangeEnd(dir, true); + }); + } + if (!p.params.animate) { + p.onMonthChangeEnd(dir); + } + }; + p.nextYear = function () { + p.setYearMonth(p.currentYear + 1); + }; + p.prevYear = function () { + p.setYearMonth(p.currentYear - 1); + }; + + + // HTML Layout + p.layout = function () { + var pickerHTML = ''; + var pickerClass = ''; + var i; + + var layoutDate = p.value && p.value.length ? p.value[0] : new Date().setHours(0,0,0,0); + var prevMonthHTML = p.monthHTML(layoutDate, 'prev'); + var currentMonthHTML = p.monthHTML(layoutDate); + var nextMonthHTML = p.monthHTML(layoutDate, 'next'); + var monthsHTML = '
    ' + (prevMonthHTML + currentMonthHTML + nextMonthHTML) + '
    '; + // Week days header + var weekHeaderHTML = ''; + if (p.params.weekHeader) { + for (i = 0; i < 7; i++) { + var weekDayIndex = (i + p.params.firstDay > 6) ? (i - 7 + p.params.firstDay) : (i + p.params.firstDay); + var dayName = p.params.dayNamesShort[weekDayIndex]; + weekHeaderHTML += '
    ' + dayName + '
    '; + + } + weekHeaderHTML = '
    ' + weekHeaderHTML + '
    '; + } + pickerClass = 'weui-picker-calendar ' + (p.params.cssClass || ''); + if(!p.inline) pickerClass = 'weui-picker-modal ' + pickerClass; + var toolbarHTML = p.params.toolbar ? p.params.toolbarTemplate.replace(/{{closeText}}/g, p.params.toolbarCloseText) : ''; + if (p.params.toolbar) { + toolbarHTML = p.params.toolbarTemplate + .replace(/{{closeText}}/g, p.params.toolbarCloseText) + .replace(/{{monthPicker}}/g, (p.params.monthPicker ? p.params.monthPickerTemplate : '')) + .replace(/{{yearPicker}}/g, (p.params.yearPicker ? p.params.yearPickerTemplate : '')); + } + + pickerHTML = + '
    ' + + toolbarHTML + + '
    ' + + weekHeaderHTML + + monthsHTML + + '
    ' + + '
    '; + + + p.pickerHTML = pickerHTML; + }; + + // Input Events + function openOnInput(e) { + e.preventDefault(); + if (p.opened) return; + p.open(); + if (p.params.scrollToInput && !isPopover()) { + var pageContent = p.input.parents('.page-content'); + if (pageContent.length === 0) return; + + var paddingTop = parseInt(pageContent.css('padding-top'), 10), + paddingBottom = parseInt(pageContent.css('padding-bottom'), 10), + pageHeight = pageContent[0].offsetHeight - paddingTop - p.container.height(), + pageScrollHeight = pageContent[0].scrollHeight - paddingTop - p.container.height(), + newPaddingBottom; + + var inputTop = p.input.offset().top - paddingTop + p.input[0].offsetHeight; + if (inputTop > pageHeight) { + var scrollTop = pageContent.scrollTop() + inputTop - pageHeight; + if (scrollTop + pageHeight > pageScrollHeight) { + newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom; + if (pageHeight === pageScrollHeight) { + newPaddingBottom = p.container.height(); + } + pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'}); + } + pageContent.scrollTop(scrollTop, 300); + } + } + } + function closeOnHTMLClick(e) { + if (inPopover()) return; + if (p.input && p.input.length > 0) { + if (e.target !== p.input[0] && $(e.target).parents('.weui-picker-modal').length === 0) p.close(); + } + else { + if ($(e.target).parents('.weui-picker-modal').length === 0) p.close(); + } + } + + if (p.params.input) { + p.input = $(p.params.input); + if (p.input.length > 0) { + if (p.params.inputReadOnly) p.input.prop('readOnly', true); + if (!p.inline) { + p.input.on('click', openOnInput); + } + if (p.params.inputReadOnly) { + p.input.on('focus mousedown', function (e) { + e.preventDefault(); + }); + } + } + + } + + //iphone 上无法正确触发 click,会导致点击外面无法关闭 + if (!p.inline) $(document).on('click touchend', closeOnHTMLClick); + + // Open + function onPickerClose() { + p.opened = false; + if (p.input && p.input.length > 0) p.input.parents('.page-content').css({'padding-bottom': ''}); + if (p.params.onClose) p.params.onClose(p); + + // Destroy events + p.destroyCalendarEvents(); + } + + p.opened = false; + p.open = function () { + var toPopover = isPopover() && false; + var updateValue = false; + if (!p.opened) { + // Set date value + if (!p.value) { + if (p.params.value) { + p.value = p.params.value; + updateValue = true; + } + } + + // Layout + p.layout(); + + // Append + if (toPopover) { + p.pickerHTML = '
    ' + p.pickerHTML + '
    '; + p.popover = $.popover(p.pickerHTML, p.params.input, true); + p.container = $(p.popover).find('.weui-picker-modal'); + $(p.popover).on('close', function () { + onPickerClose(); + }); + } + else if (p.inline) { + p.container = $(p.pickerHTML); + p.container.addClass('picker-modal-inline'); + $(p.params.container).append(p.container); + } + else { + p.container = $($.openPicker(p.pickerHTML)); + $(p.container) + .on('close', function () { + onPickerClose(); + }); + } + + // Store calendar instance + p.container[0].f7Calendar = p; + p.wrapper = p.container.find('.picker-calendar-months-wrapper'); + + // Months + p.months = p.wrapper.find('.picker-calendar-month'); + + // Update current month and year + p.updateCurrentMonthYear(); + + // Set initial translate + p.monthsTranslate = 0; + p.setMonthsTranslate(); + + // Init events + p.initCalendarEvents(); + + // Update input value + if (updateValue) p.updateValue(); + + } + + // Set flag + p.opened = true; + p.initialized = true; + if (p.params.onMonthAdd) { + p.months.each(function () { + p.params.onMonthAdd(p, this); + }); + } + if (p.params.onOpen) p.params.onOpen(p); + }; + + // Close + p.close = function () { + if (!p.opened || p.inline) return; + p.animating = false; //有可能还有动画没做完,因此animating设置还没改。 + if (inPopover()) { + $.closePicker(p.popover); + return; + } + else { + $.closePicker(p.container); + return; + } + }; + + // Destroy + p.destroy = function () { + p.close(); + if (p.params.input && p.input.length > 0) { + p.input.off('click focus', openOnInput); + p.input.data("calendar", null); + } + $('html').off('click', closeOnHTMLClick); + }; + + if (p.inline) { + p.open(); + } + + return p; + }; + + var format = function(d) { + return d < 10 ? "0"+d : d; + } + + + $.fn.calendar = function (params, args) { + params = params || {}; + return this.each(function() { + var $this = $(this); + if(!$this[0]) return; + var p = {}; + if($this[0].tagName.toUpperCase() === "INPUT") { + p.input = $this; + } else { + p.container = $this; + } + + var calendar = $this.data("calendar"); + + if(!calendar) { + if(typeof params === typeof "a") { + } else { + if(!params.value && $this.val()) params.value = [$this.val()]; + //默认显示今天 + if(!params.value) { + var today = new Date(); + params.value = [today.getFullYear() + "/" + format(today.getMonth() + 1) + "/" + format(today.getDate())]; + } + calendar = $this.data("calendar", new Calendar($.extend(p, params))); + } + } + + if(typeof params === typeof "a") { + calendar[params].call(calendar, args); + } + }); + }; + + defaults = $.fn.calendar.prototype.defaults = { + value: undefined, // 通过JS赋值,注意是数组 + monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + dayNames: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], + dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], + firstDay: 1, // First day of the week, Monday + weekendDays: [0, 6], // Sunday and Saturday + multiple: false, + dateFormat: 'yyyy/mm/dd', + direction: 'horizontal', // or 'vertical' + minDate: null, + maxDate: null, + touchMove: true, + animate: true, + closeOnSelect: true, + monthPicker: true, + monthPickerTemplate: + '
    ' + + '' + + '
    ' + + '' + + '
    ', + yearPicker: true, + yearPickerTemplate: + '
    ' + + '' + + '' + + '' + + '
    ', + weekHeader: true, + // Common settings + scrollToInput: true, + inputReadOnly: true, + convertToPopover: true, + onlyInPopover: false, + toolbar: true, + toolbarCloseText: 'Done', + toolbarTemplate: + '
    ' + + '
    ' + + '{{yearPicker}}' + + '{{monthPicker}}' + + // '{{closeText}}' + + '
    ' + + '
    ', + /* Callbacks + onMonthAdd + onChange + onOpen + onClose + onDayClick + onMonthYearChangeStart + onMonthYearChangeEnd + */ + }; + +}($); + +/* global $:true */ +/* jshint unused:false*/ + ++ function($) { + "use strict"; + + + var defaults; + + var formatNumber = function (n) { + return n < 10 ? "0" + n : n; + } + + var Datetime = function(input, params) { + this.input = $(input); + this.params = params || {}; + + this.initMonthes = params.monthes + + this.initYears = params.years + + var p = $.extend({}, params, this.getConfig()); + $(this.input).picker(p); + } + + Datetime.prototype = { + getDays : function(max) { + var days = []; + for(var i=1; i<= (max||31);i++) { + days.push(i < 10 ? "0"+i : i); + } + return days; + }, + + getDaysByMonthAndYear : function(month, year) { + var int_d = new Date(year, parseInt(month)+1-1, 1); + var d = new Date(int_d - 1); + return this.getDays(d.getDate()); + }, + getConfig: function() { + var today = new Date(), + params = this.params, + self = this, + lastValidValues; + + var config = { + rotateEffect: false, //为了性能 + cssClass: 'datetime-picker', + + value: [today.getFullYear(), formatNumber(today.getMonth()+1), formatNumber(today.getDate()), formatNumber(today.getHours()), (formatNumber(today.getMinutes()))], + + onChange: function (picker, values, displayValues) { + var cols = picker.cols; + var days = self.getDaysByMonthAndYear(values[1], values[0]); + var currentValue = values[2]; + if(currentValue > days.length) currentValue = days.length; + picker.cols[4].setValue(currentValue); + + //check min and max + var current = new Date(values[0]+'-'+values[1]+'-'+values[2]); + var valid = true; + if(params.min) { + var min = new Date(typeof params.min === "function" ? params.min() : params.min); + + if(current < +min) { + picker.setValue(lastValidValues); + valid = false; + } + } + if(params.max) { + var max = new Date(typeof params.max === "function" ? params.max() : params.max); + if(current > +max) { + picker.setValue(lastValidValues); + valid = false; + } + } + + valid && (lastValidValues = values); + + if (self.params.onChange) { + self.params.onChange.apply(this, arguments); + } + }, + + formatValue: function (p, values, displayValues) { + return self.params.format(p, values, displayValues); + }, + + cols: [ + { + values: this.initYears + }, + { + divider: true, // 这是一个分隔符 + content: params.yearSplit + }, + { + values: this.initMonthes + }, + { + divider: true, // 这是一个分隔符 + content: params.monthSplit + }, + { + values: (function () { + var dates = []; + for (var i=1; i<=31; i++) dates.push(formatNumber(i)); + return dates; + })() + }, + + ] + } + + if (params.dateSplit) { + config.cols.push({ + divider: true, + content: params.dateSplit + }) + } + + config.cols.push({ + divider: true, + content: params.datetimeSplit + }) + + var times = self.params.times(); + if (times && times.length) { + config.cols = config.cols.concat(times); + } + + var inputValue = this.input.val(); + if(inputValue) config.value = params.parse(inputValue); + if(this.params.value) { + this.input.val(this.params.value); + config.value = params.parse(this.params.value); + } + + return config; + } + } + + $.fn.datetimePicker = function(params) { + params = $.extend({}, defaults, params); + return this.each(function() { + if(!this) return; + var $this = $(this); + var datetime = $this.data("datetime"); + if(!datetime) $this.data("datetime", new Datetime(this, params)); + return datetime; + }); + }; + + defaults = $.fn.datetimePicker.prototype.defaults = { + input: undefined, // 默认值 + min: undefined, // YYYY-MM-DD 最大最小值只比较年月日,不比较时分秒 + max: undefined, // YYYY-MM-DD + yearSplit: '-', + monthSplit: '-', + dateSplit: '', // 默认为空 + datetimeSplit: ' ', // 日期和时间之间的分隔符,不可为空 + monthes: ('01 02 03 04 05 06 07 08 09 10 11 12').split(' '), + years: (function () { + var arr = []; + for (var i = 1950; i <= 2030; i++) { arr.push(i); } + return arr; + })(), + times: function () { + return [ // 自定义的时间 + { + values: (function () { + var hours = []; + for (var i=0; i<24; i++) hours.push(formatNumber(i)); + return hours; + })() + }, + { + divider: true, // 这是一个分隔符 + content: ':' + }, + { + values: (function () { + var minutes = []; + for (var i=0; i<60; i++) minutes.push(formatNumber(i)); + return minutes; + })() + } + ]; + }, + format: function (p, values) { // 数组转换成字符串 + return p.cols.map(function (col) { + return col.value || col.content; + }).join(''); + }, + parse: function (str) { + // 把字符串转换成数组,用来解析初始值 + // 如果你的定制的初始值格式无法被这个默认函数解析,请自定义这个函数。比如你的时间是 '子时' 那么默认情况这个'时'会被当做分隔符而导致错误,所以你需要自己定义parse函数 + // 默认兼容的分隔符 + var t = str.split(this.datetimeSplit); + return t[0].split(/\D/).concat(t[1].split(/:|时|分|秒/)).filter(function (d) { + return !!d; + }) + } + } + +}($); + +/*====================================================== +************ Picker ************ +======================================================*/ +/* global $:true */ + ++ function($) { + "use strict"; + + + //Popup 和 picker 之类的不要共用一个弹出方法,因为这样会导致 在 popup 中再弹出 picker 的时候会有问题。 + + $.openPopup = function(popup, className) { + + $.closePopup(); + + popup = $(popup); + popup.show(); + popup.width(); + popup.addClass("weui-popup__container--visible"); + var modal = popup.find(".weui-popup__modal"); + modal.width(); + modal.transitionEnd(function() { + modal.trigger("open"); + }); + } + + + $.closePopup = function(container, remove) { + container = $(container || ".weui-popup__container--visible"); + container.find('.weui-popup__modal').transitionEnd(function() { + var $this = $(this); + $this.trigger("close"); + container.hide(); + remove && container.remove(); + }) + container.removeClass("weui-popup__container--visible") + }; + + + $(document).on("click", ".close-popup, .weui-popup__overlay", function() { + $.closePopup(); + }) + .on("click", ".open-popup", function() { + $($(this).data("target")).popup(); + }) + .on("click", ".weui-popup__container", function(e) { + if($(e.target).hasClass("weui-popup__container")) $.closePopup(); + }) + + $.fn.popup = function() { + return this.each(function() { + $.openPopup(this); + }); + }; + +}($); + +/* =============================================================================== +************ Notification ************ +=============================================================================== */ +/* global $:true */ ++function ($) { + "use strict"; + + var noti, defaults, timeout, start, diffX, diffY; + + var touchStart = function(e) { + var p = $.getTouchPosition(e); + start = p; + diffX = diffY = 0; + noti.addClass("touching"); + }; + var touchMove = function(e) { + if(!start) return false; + e.preventDefault(); + e.stopPropagation(); + var p = $.getTouchPosition(e); + diffX = p.x - start.x; + diffY = p.y - start.y; + if(diffY > 0) { + diffY = Math.sqrt(diffY); + } + + noti.css("transform", "translate3d(0, "+diffY+"px, 0)"); + }; + var touchEnd = function() { + noti.removeClass("touching"); + noti.attr("style", ""); + if(diffY < 0 && (Math.abs(diffY) > noti.height()*0.38)) { + $.closeNotification(); + } + + if(Math.abs(diffX) <= 1 && Math.abs(diffY) <= 1) { + noti.trigger("noti-click"); + } + + start = false; + }; + + var attachEvents = function(el) { + el.on($.touchEvents.start, touchStart); + el.on($.touchEvents.move, touchMove); + el.on($.touchEvents.end, touchEnd); + }; + + $.notification = $.noti = function(params) { + params = $.extend({}, defaults, params); + noti = $(".weui-notification"); + if(!noti[0]) { // create a new notification + noti = $('
    ').appendTo(document.body); + attachEvents(noti); + } + + noti.off("noti-click"); //the click event is not correct sometime: it will trigger when user is draging. + if(params.onClick) noti.on("noti-click", function() { + params.onClick(params.data); + }); + + noti.html($.t7.compile(params.tpl)(params)); + + noti.show(); + + noti.addClass("weui-notification--in"); + noti.data("params", params); + + var startTimeout = function() { + if(timeout) { + clearTimeout(timeout); + timeout = null; + } + + timeout = setTimeout(function() { + if(noti.hasClass("weui-notification--touching")) { + startTimeout(); + } else { + $.closeNotification(); + } + }, params.time); + }; + + startTimeout(); + + }; + + $.closeNotification = function() { + timeout && clearTimeout(timeout); + timeout = null; + var noti = $(".weui-notification").removeClass("weui-notification--in").transitionEnd(function() { + $(this).remove(); + }); + + if(noti[0]) { + var params = $(".weui-notification").data("params"); + if(params && params.onClose) { + params.onClose(params.data); + } + } + }; + + defaults = $.noti.prototype.defaults = { + title: undefined, + text: undefined, + media: undefined, + time: 4000, + onClick: undefined, + onClose: undefined, + data: undefined, + tpl: '
    ' + + '{{#if media}}
    {{media}}
    {{/if}}' + + '
    ' + + '{{#if title}}
    {{title}}
    {{/if}}' + + '{{#if text}}
    {{text}}
    {{/if}}' + + '
    ' + + '
    ' + + '
    ' + }; + +}($); + ++ function($) { + "use strict"; + + var timeout; + + $.toptip = function(text, duration, type) { + if(!text) return; + if(typeof duration === typeof "a") { + type = duration; + duration = undefined; + } + duration = duration || 3000; + var className = type ? 'bg-' + type : 'bg-danger'; + var $t = $('.weui-toptips').remove(); + $t = $('
    ').appendTo(document.body); + $t.html(text); + $t[0].className = 'weui-toptips ' + className + + clearTimeout(timeout); + + if(!$t.hasClass('weui-toptips_visible')) { + $t.show().width(); + $t.addClass('weui-toptips_visible'); + } + + timeout = setTimeout(function() { + $t.removeClass('weui-toptips_visible').transitionEnd(function() { + $t.remove(); + }); + }, duration); + } +}($); + +/* global $:true */ ++ function($) { + "use strict"; + var Slider = function (container, arg) { + this.container = $(container); + this.handler = this.container.find('.weui-slider__handler') + this.track = this.container.find('.weui-slider__track') + this.value = this.container.find('.weui-slider-box__value') + this.bind() + if (typeof arg === 'function') { + this.callback = arg + } + } + + Slider.prototype.bind = function () { + this.container + .on($.touchEvents.start, $.proxy(this.touchStart, this)) + .on($.touchEvents.end, $.proxy(this.touchEnd, this)); + $(document.body).on($.touchEvents.move, $.proxy(this.touchMove, this)) // move even outside container + } + + Slider.prototype.touchStart = function (e) { + e.preventDefault() + this.start = $.getTouchPosition(e) + this.width = this.container.find('.weui-slider__inner').width() + this.left = parseInt(this.container.find('.weui-slider__handler').css('left')) + this.touching = true + } + + Slider.prototype.touchMove = function (e) { + if (!this.touching) return true + var p = $.getTouchPosition(e) + var distance = p.x - this.start.x + var left = distance + this.left + var per = parseInt(left / this.width * 100) + if (per < 0) per = 0 + if (per > 100) per = 100 + this.handler.css('left', per + '%') + this.track.css('width', per + '%') + this.value.text(per) + this.callback && this.callback.call(this, per) + this.container.trigger('change', per) + } + + Slider.prototype.touchEnd = function (e) { + this.touching = false + } + + $.fn.slider = function (arg) { + this.each(function () { + var $this = $(this) + var slider = $this.data('slider') + if (slider) return slider; + else $this.data('slider', new Slider(this, arg)) + }) + } +}($); + +/* =============================================================================== +************ Swipeout ************ +=============================================================================== */ +/* global $:true */ + ++function ($) { + "use strict"; + + var cache = []; + var TOUCHING = 'swipeout-touching' + + var Swipeout = function(el) { + this.container = $(el); + this.mover = this.container.find('>.weui-cell__bd') + this.attachEvents(); + cache.push(this) + } + + Swipeout.prototype.touchStart = function(e) { + var p = $.getTouchPosition(e); + this.container.addClass(TOUCHING); + this.start = p; + this.startX = 0; + this.startTime = + new Date; + var transform = this.mover.css('transform').match(/-?[\d\.]+/g) + if (transform && transform.length) this.startX = parseInt(transform[4]) + this.diffX = this.diffY = 0; + this._closeOthers() + this.limit = this.container.find('>.weui-cell__ft').width() || 68; // 因为有的时候初始化的时候元素是隐藏的(比如在对话框内),所以在touchstart的时候计算宽度而不是初始化的时候 + }; + + Swipeout.prototype.touchMove= function(e) { + if(!this.start) return true; + var p = $.getTouchPosition(e); + this.diffX = p.x - this.start.x; + this.diffY = p.y - this.start.y; + if (Math.abs(this.diffX) < Math.abs(this.diffY)) { // 说明是上下方向在拖动 + this.close() + this.start = false + return true; + } + e.preventDefault(); + e.stopPropagation(); + var x = this.diffX + this.startX + if (x > 0) x = 0; + if (Math.abs(x) > this.limit) x = - (Math.pow(-(x+this.limit), .7) + this.limit) + this.mover.css("transform", "translate3d("+x+"px, 0, 0)"); + }; + Swipeout.prototype.touchEnd = function() { + if (!this.start) return true; + this.start = false; + var x = this.diffX + this.startX + var t = new Date - this.startTime; + if (this.diffX < -5 && t < 200) { // 向左快速滑动,则打开 + this.open() + } else if (this.diffX >= 0 && t < 200) { // 向右快速滑动,或者单击,则关闭 + this.close() + } else if (x > 0 || -x <= this.limit / 2) { + this.close() + } else { + this.open() + } + }; + + + Swipeout.prototype.close = function() { + this.container.removeClass(TOUCHING); + this.mover.css("transform", "translate3d(0, 0, 0)"); + this.container.trigger('swipeout-close'); + } + + Swipeout.prototype.open = function() { + this.container.removeClass(TOUCHING); + this._closeOthers() + this.mover.css("transform", "translate3d(" + (-this.limit) + "px, 0, 0)"); + this.container.trigger('swipeout-open'); + } + + Swipeout.prototype.attachEvents = function() { + var el = this.mover; + el.on($.touchEvents.start, $.proxy(this.touchStart, this)); + el.on($.touchEvents.move, $.proxy(this.touchMove, this)); + el.on($.touchEvents.end, $.proxy(this.touchEnd, this)); + } + Swipeout.prototype._closeOthers = function() { + //close others + var self = this + cache.forEach(function (s) { + if (s !== self) s.close() + }) + } + + var swipeout = function(el) { + return new Swipeout(el); + }; + + $.fn.swipeout = function (arg) { + return this.each(function() { + var $this = $(this) + var s = $this.data('swipeout') || swipeout(this); + $this.data('swipeout', s); + + if (typeof arg === typeof 'a') { + s[arg]() + } + }); + } + + $('.weui-cell_swiped').swipeout() // auto init +}($); diff --git a/static/mobile/lib/jquery-weui.min.js b/static/mobile/lib/jquery-weui.min.js new file mode 100644 index 000000000..c29373749 --- /dev/null +++ b/static/mobile/lib/jquery-weui.min.js @@ -0,0 +1,13 @@ +/** +* jQuery WeUI V1.2.0 +* By 言川 +* http://lihongxun945.github.io/jquery-weui/ + */ +!function(t){"use strict";t.fn.transitionEnd=function(t){function e(r){if(r.target===this)for(t.call(this,r),n=0;n=0){e=i,r.push(o);break}e&&(n=e)}else if(o.indexOf("=")>0){var s=o.split("="),c=s[0],l=s[1];if(2!==l.match(/"/g).length){for(e=0,i=n+1;i=0){e=i;break}e&&(n=e)}var u=[c,l.replace(/"/g,"")];r.push(u)}else r.push(o)}return r}function i(e){var i,a,r=[];if(!e)return[];var o=e.split(/({{[^{^}]*}})/);for(i=0;i=0)continue;if(s.indexOf("{#")<0&&s.indexOf(" ")<0&&s.indexOf("else")<0){r.push({type:"variable",contextName:s.replace(/[{}]/g,"")});continue}var c=n(s),l=c[0],u=[],p={};for(a=1;a=0){var d,f="",m="",v=0,g=!1,w=!1,y=0;for(a=i+1;a=0&&y++,o[a].indexOf("{{/")>=0&&y--,o[a].indexOf("{{#"+l)>=0)f+=o[a],w&&(m+=o[a]),v++;else if(o[a].indexOf("{{/"+l)>=0){if(!(v>0)){d=a,g=!0;break}v--,f+=o[a],w&&(m+=o[a])}else o[a].indexOf("else")>=0&&0===y?w=!0:(w||(f+=o[a]),w&&(m+=o[a]));g&&(d&&(i=d),r.push({type:"helper",helperName:l,contextName:u,content:f,inverseContent:m,hash:p}))}else s.indexOf(" ")>0&&r.push({type:"helper",helperName:l,contextName:u,hash:p})}}return r}var a=function(t){function e(t,e){return t.content?o(t.content,e):function(){return""}}function n(t,e){return t.inverseContent?o(t.inverseContent,e):function(){return""}}function a(t,e){var n,i,a=0;if(0===t.indexOf("../")){a=t.split("../").length-1;var r=e.split("_")[1]-a;e="ctx_"+(r>=1?r:1),i=t.split("../")[a].split(".")}else 0===t.indexOf("@global")?(e="$.Template7.global",i=t.split("@global.")[1].split(".")):0===t.indexOf("@root")?(e="ctx_1",i=t.split("@root.")[1].split(".")):i=t.split(".");n=e;for(var o=0;o0?n+="[(data && data."+s.replace("@","")+")]":n="(data && data."+t.replace("@","")+")":isFinite(s)?n+="["+s+"]":0===s.indexOf("this")?n=s.replace("this",e):n+="."+s}return n}function r(t,e){for(var n=[],i=0;i0)throw new Error('Template7: Missing helper: "'+h.helperName+'"');d=a(h.helperName,l),u+="if ("+d+") {",u+="if (isArray("+d+")) {",u+="r += ($.Template7.helpers.each).call("+l+", "+d+", {hash:"+JSON.stringify(h.hash)+", data: data || {}, fn: "+e(h,o+1)+", inverse: "+n(h,o+1)+", root: ctx_1});",u+="}else {",u+="r += ($.Template7.helpers.with).call("+l+", "+d+", {hash:"+JSON.stringify(h.hash)+", data: data || {}, fn: "+e(h,o+1)+", inverse: "+n(h,o+1)+", root: ctx_1});",u+="}}"}}else u+="r +='"+h.content.replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/'/g,"\\'")+"';"}return u+="\nreturn r;})",eval.call(window,u)}var s=this;s.template=t,s.compile=function(t){return s.compiled||(s.compiled=o(t)),s.compiled}};a.prototype={options:{},helpers:{"if":function(t,n){return e(t)&&(t=t.call(this)),t?n.fn(this,n.data):n.inverse(this,n.data)},unless:function(t,n){return e(t)&&(t=t.call(this)),t?n.inverse(this,n.data):n.fn(this,n.data)},each:function(n,i){var a="",r=0;if(e(n)&&(n=n.call(this)),t(n)){for(i.hash.reverse&&(n=n.reverse()),r=0;r0?a:i.inverse(this)},"with":function(t,n){return e(t)&&(t=t.call(this)),n.fn(t)},join:function(t,n){return e(t)&&(t=t.call(this)),t.join(n.hash.delimiter||n.hash.delimeter)},js:function(t,e){var n;return n=t.indexOf("return")>=0?"(function(){"+t+"})":"(function(){return ("+t+")})",eval.call(this,n).call(this)},js_compare:function(t,e){var n;n=t.indexOf("return")>=0?"(function(){"+t+"})":"(function(){return ("+t+")})";var i=eval.call(this,n).call(this);return i?e.fn(this,e.data):e.inverse(this,e.data)}}};var r=function(t,e){if(2===arguments.length){var n=new a(t),i=n.compile()(e);return n=null,i}return new a(t)};return r.registerHelper=function(t,e){a.prototype.helpers[t]=e},r.unregisterHelper=function(t){a.prototype.helpers[t]=void 0,delete a.prototype.helpers[t]},r.compile=function(t,e){var n=new a(t,e);return n.compile()},r.options=a.prototype.options,r.helpers=a.prototype.helpers,r}()}($),/*! Hammer.JS - v2.0.8 - 2016-04-23 + * http://hammerjs.github.io/ + * + * Copyright (c) 2016 Jorik Tangelder; + * Licensed under the MIT license */ +function(t,e,n,i){"use strict";function a(t,e,n){return setTimeout(l(t,n),e)}function r(t,e,n){return Array.isArray(t)?(o(t,n[e],n),!0):!1}function o(t,e,n){var a;if(t)if(t.forEach)t.forEach(e,n);else if(t.length!==i)for(a=0;a\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",r=t.console&&(t.console.warn||t.console.log);return r&&r.call(t.console,a,i),e.apply(this,arguments)}}function c(t,e,n){var i,a=e.prototype;i=t.prototype=Object.create(a),i.constructor=t,i._super=a,n&&pt(i,n)}function l(t,e){return function(){return t.apply(e,arguments)}}function u(t,e){return typeof t==ft?t.apply(e?e[0]||i:i,e):t}function p(t,e){return t===i?e:t}function h(t,e,n){o(v(e),function(e){t.addEventListener(e,n,!1)})}function d(t,e,n){o(v(e),function(e){t.removeEventListener(e,n,!1)})}function f(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1}function m(t,e){return t.indexOf(e)>-1}function v(t){return t.trim().split(/\s+/g)}function g(t,e,n){if(t.indexOf&&!n)return t.indexOf(e);for(var i=0;in[e]}):i.sort()),i}function T(t,e){for(var n,a,r=e[0].toUpperCase()+e.slice(1),o=0;o1&&!n.firstMultiple?n.firstMultiple=P(e):1===a&&(n.firstMultiple=!1);var r=n.firstInput,o=n.firstMultiple,s=o?o.center:r.center,c=e.center=O(i);e.timeStamp=gt(),e.deltaTime=e.timeStamp-r.timeStamp,e.angle=S(s,c),e.distance=H(s,c),E(n,e),e.offsetDirection=I(e.deltaX,e.deltaY);var l=A(e.deltaTime,e.deltaX,e.deltaY);e.overallVelocityX=l.x,e.overallVelocityY=l.y,e.overallVelocity=vt(l.x)>vt(l.y)?l.x:l.y,e.scale=o?V(o.pointers,i):1,e.rotation=o?Y(o.pointers,i):0,e.maxPointers=n.prevInput?e.pointers.length>n.prevInput.maxPointers?e.pointers.length:n.prevInput.maxPointers:e.pointers.length,D(n,e);var u=t.element;f(e.srcEvent.target,u)&&(u=e.srcEvent.target),e.target=u}function E(t,e){var n=e.center,i=t.offsetDelta||{},a=t.prevDelta||{},r=t.prevInput||{};e.eventType!==Ot&&r.eventType!==It||(a=t.prevDelta={x:r.deltaX||0,y:r.deltaY||0},i=t.offsetDelta={x:n.x,y:n.y}),e.deltaX=a.x+(n.x-i.x),e.deltaY=a.y+(n.y-i.y)}function D(t,e){var n,a,r,o,s=t.lastInterval||e,c=e.timeStamp-s.timeStamp;if(e.eventType!=Ht&&(c>Pt||s.velocity===i)){var l=e.deltaX-s.deltaX,u=e.deltaY-s.deltaY,p=A(c,l,u);a=p.x,r=p.y,n=vt(p.x)>vt(p.y)?p.x:p.y,o=I(l,u),t.lastInterval=e}else n=s.velocity,a=s.velocityX,r=s.velocityY,o=s.direction;e.velocity=n,e.velocityX=a,e.velocityY=r,e.direction=o}function P(t){for(var e=[],n=0;na;)n+=t[a].clientX,i+=t[a].clientY,a++;return{x:mt(n/e),y:mt(i/e)}}function A(t,e,n){return{x:e/t||0,y:n/t||0}}function I(t,e){return t===e?St:vt(t)>=vt(e)?0>t?Yt:Vt:0>e?Nt:Ft}function H(t,e,n){n||(n=Lt);var i=e[n[0]]-t[n[0]],a=e[n[1]]-t[n[1]];return Math.sqrt(i*i+a*a)}function S(t,e,n){n||(n=Lt);var i=e[n[0]]-t[n[0]],a=e[n[1]]-t[n[1]];return 180*Math.atan2(a,i)/Math.PI}function Y(t,e){return S(e[1],e[0],jt)+S(t[1],t[0],jt)}function V(t,e){return H(e[0],e[1],jt)/H(t[0],t[1],jt)}function N(){this.evEl=$t,this.evWin=Wt,this.pressed=!1,C.apply(this,arguments)}function F(){this.evEl=Bt,this.evWin=Gt,C.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function R(){this.evTarget=Zt,this.evWin=Qt,this.started=!1,C.apply(this,arguments)}function q(t,e){var n=w(t.touches),i=w(t.changedTouches);return e&(It|Ht)&&(n=y(n.concat(i),"identifier",!0)),[n,i]}function z(){this.evTarget=ee,this.targetIds={},C.apply(this,arguments)}function L(t,e){var n=w(t.touches),i=this.targetIds;if(e&(Ot|At)&&1===n.length)return i[n[0].identifier]=!0,[n,n];var a,r,o=w(t.changedTouches),s=[],c=this.target;if(r=n.filter(function(t){return f(t.target,c)}),e===Ot)for(a=0;a-1&&i.splice(t,1)};setTimeout(a,ne)}}function W(t){for(var e=t.srcEvent.clientX,n=t.srcEvent.clientY,i=0;i=r&&ie>=o)return!0}return!1}function K(t,e){this.manager=t,this.set(e)}function U(t){if(m(t,le))return le;var e=m(t,ue),n=m(t,pe);return e&&n?le:e||n?e?ue:pe:m(t,ce)?ce:se}function B(){if(!re)return!1;var e={},n=t.CSS&&t.CSS.supports;return["auto","manipulation","pan-y","pan-x","pan-x pan-y","none"].forEach(function(i){e[i]=n?t.CSS.supports("touch-action",i):!0}),e}function G(t){this.options=pt({},this.defaults,t||{}),this.id=k(),this.manager=null,this.options.enable=p(this.options.enable,!0),this.state=de,this.simultaneous={},this.requireFail=[]}function J(t){return t&we?"cancel":t&ve?"end":t&me?"move":t&fe?"start":""}function Z(t){return t==Ft?"down":t==Nt?"up":t==Yt?"left":t==Vt?"right":""}function Q(t,e){var n=e.manager;return n?n.get(t):t}function tt(){G.apply(this,arguments)}function et(){tt.apply(this,arguments),this.pX=null,this.pY=null}function nt(){tt.apply(this,arguments)}function it(){G.apply(this,arguments),this._timer=null,this._input=null}function at(){tt.apply(this,arguments)}function rt(){tt.apply(this,arguments)}function ot(){G.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function st(t,e){return e=e||{},e.recognizers=p(e.recognizers,st.defaults.preset),new ct(t,e)}function ct(t,e){this.options=pt({},st.defaults,e||{}),this.options.inputTarget=this.options.inputTarget||t,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=t,this.input=b(this),this.touchAction=new K(this,this.options.touchAction),lt(this,!0),o(this.options.recognizers,function(t){var e=this.add(new t[0](t[1]));t[2]&&e.recognizeWith(t[2]),t[3]&&e.requireFailure(t[3])},this)}function lt(t,e){var n=t.element;if(n.style){var i;o(t.options.cssProps,function(a,r){i=T(n.style,r),e?(t.oldCssProps[i]=n.style[i],n.style[i]=a):n.style[i]=t.oldCssProps[i]||""}),e||(t.oldCssProps={})}}function ut(t,n){var i=e.createEvent("Event");i.initEvent(t,!0,!0),i.gesture=n,n.target.dispatchEvent(i)}var pt,ht=["","webkit","Moz","MS","ms","o"],dt=e.createElement("div"),ft="function",mt=Math.round,vt=Math.abs,gt=Date.now;pt="function"!=typeof Object.assign?function(t){if(t===i||null===t)throw new TypeError("Cannot convert undefined or null to object");for(var e=Object(t),n=1;ns&&(e.push(t),s=e.length-1):a&(It|Ht)&&(n=!0),0>s||(e[s]=t,this.callback(this.manager,a,{pointers:e,changedPointers:[t],pointerType:r,srcEvent:t}),n&&e.splice(s,1))}});var Jt={touchstart:Ot,touchmove:At,touchend:It,touchcancel:Ht},Zt="touchstart",Qt="touchstart touchmove touchend touchcancel";c(R,C,{handler:function(t){var e=Jt[t.type];if(e===Ot&&(this.started=!0),this.started){var n=q.call(this,t,e);e&(It|Ht)&&n[0].length-n[1].length===0&&(this.started=!1),this.callback(this.manager,e,{pointers:n[0],changedPointers:n[1],pointerType:Mt,srcEvent:t})}}});var te={touchstart:Ot,touchmove:At,touchend:It,touchcancel:Ht},ee="touchstart touchmove touchend touchcancel";c(z,C,{handler:function(t){var e=te[t.type],n=L.call(this,t,e);n&&this.callback(this.manager,e,{pointers:n[0],changedPointers:n[1],pointerType:Mt,srcEvent:t})}});var ne=2500,ie=25;c(j,C,{handler:function(t,e,n){var i=n.pointerType==Mt,a=n.pointerType==Et;if(!(a&&n.sourceCapabilities&&n.sourceCapabilities.firesTouchEvents)){if(i)X.call(this,e,n);else if(a&&W.call(this,n))return;this.callback(t,e,n)}},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var ae=T(dt.style,"touchAction"),re=ae!==i,oe="compute",se="auto",ce="manipulation",le="none",ue="pan-x",pe="pan-y",he=B();K.prototype={set:function(t){t==oe&&(t=this.compute()),re&&this.manager.element.style&&he[t]&&(this.manager.element.style[ae]=t),this.actions=t.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var t=[];return o(this.manager.recognizers,function(e){u(e.options.enable,[e])&&(t=t.concat(e.getTouchAction()))}),U(t.join(" "))},preventDefaults:function(t){var e=t.srcEvent,n=t.offsetDirection;if(this.manager.session.prevented)return void e.preventDefault();var i=this.actions,a=m(i,le)&&!he[le],r=m(i,pe)&&!he[pe],o=m(i,ue)&&!he[ue];if(a){var s=1===t.pointers.length,c=t.distance<2,l=t.deltaTime<250;if(s&&c&&l)return}return o&&r?void 0:a||r&&n&Rt||o&&n&qt?this.preventSrc(e):void 0},preventSrc:function(t){this.manager.session.prevented=!0,t.preventDefault()}};var de=1,fe=2,me=4,ve=8,ge=ve,we=16,ye=32;G.prototype={defaults:{},set:function(t){return pt(this.options,t),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(t){if(r(t,"recognizeWith",this))return this;var e=this.simultaneous;return t=Q(t,this),e[t.id]||(e[t.id]=t,t.recognizeWith(this)),this},dropRecognizeWith:function(t){return r(t,"dropRecognizeWith",this)?this:(t=Q(t,this),delete this.simultaneous[t.id],this)},requireFailure:function(t){if(r(t,"requireFailure",this))return this;var e=this.requireFail;return t=Q(t,this),-1===g(e,t)&&(e.push(t),t.requireFailure(this)),this},dropRequireFailure:function(t){if(r(t,"dropRequireFailure",this))return this;t=Q(t,this);var e=g(this.requireFail,t);return e>-1&&this.requireFail.splice(e,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(t){return!!this.simultaneous[t.id]},emit:function(t){function e(e){n.manager.emit(e,t)}var n=this,i=this.state;ve>i&&e(n.options.event+J(i)),e(n.options.event),t.additionalEvent&&e(t.additionalEvent),i>=ve&&e(n.options.event+J(i))},tryEmit:function(t){return this.canEmit()?this.emit(t):void(this.state=ye)},canEmit:function(){for(var t=0;tr?Yt:Vt,n=r!=this.pX,i=Math.abs(t.deltaX)):(a=0===o?St:0>o?Nt:Ft,n=o!=this.pY,i=Math.abs(t.deltaY))),t.direction=a,n&&i>e.threshold&&a&e.direction},attrTest:function(t){return tt.prototype.attrTest.call(this,t)&&(this.state&fe||!(this.state&fe)&&this.directionTest(t))},emit:function(t){this.pX=t.deltaX,this.pY=t.deltaY;var e=Z(t.direction);e&&(t.additionalEvent=this.options.event+e),this._super.emit.call(this,t)}}),c(nt,tt,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[le]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.scale-1)>this.options.threshold||this.state&fe)},emit:function(t){if(1!==t.scale){var e=t.scale<1?"in":"out";t.additionalEvent=this.options.event+e}this._super.emit.call(this,t)}}),c(it,G,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[se]},process:function(t){var e=this.options,n=t.pointers.length===e.pointers,i=t.distancee.time;if(this._input=t,!i||!n||t.eventType&(It|Ht)&&!r)this.reset();else if(t.eventType&Ot)this.reset(),this._timer=a(function(){this.state=ge,this.tryEmit()},e.time,this);else if(t.eventType&It)return ge;return ye},reset:function(){clearTimeout(this._timer)},emit:function(t){this.state===ge&&(t&&t.eventType&It?this.manager.emit(this.options.event+"up",t):(this._input.timeStamp=gt(),this.manager.emit(this.options.event,this._input)))}}),c(at,tt,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[le]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.rotation)>this.options.threshold||this.state&fe)}}),c(rt,tt,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Rt|qt,pointers:1},getTouchAction:function(){return et.prototype.getTouchAction.call(this)},attrTest:function(t){var e,n=this.options.direction;return n&(Rt|qt)?e=t.overallVelocity:n&Rt?e=t.overallVelocityX:n&qt&&(e=t.overallVelocityY),this._super.attrTest.call(this,t)&&n&t.offsetDirection&&t.distance>this.options.threshold&&t.maxPointers==this.options.pointers&&vt(e)>this.options.velocity&&t.eventType&It},emit:function(t){var e=Z(t.offsetDirection);e&&this.manager.emit(this.options.event+e,t),this.manager.emit(this.options.event,t)}}),c(ot,G,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[ce]},process:function(t){var e=this.options,n=t.pointers.length===e.pointers,i=t.distance'+t.text+""}).join(""),o='
    '+n.title+"
    "+(n.text?'
    '+n.text+"
    ":"")+'
    '+r+"
    ",s=t.openModal(o,i);return s.find(".weui-dialog__btn").each(function(e,i){var r=t(i);r.click(function(){n.autoClose&&t.closeModal(),a[e].onClick&&a[e].onClick.call(s)})}),s},t.openModal=function(e,n){var i=t("
    ").appendTo(document.body);i.show();var a=t(e).appendTo(document.body);return n&&a.transitionEnd(function(){n.call(a)}),a.show(),i.addClass("weui-mask--visible"),a.addClass("weui-dialog--visible"),a},t.closeModal=function(){t(".weui-mask--visible").removeClass("weui-mask--visible").transitionEnd(function(){t(this).remove()}),t(".weui-dialog--visible").removeClass("weui-dialog--visible").transitionEnd(function(){t(this).remove()})},t.alert=function(n,i,a){var r;return"object"==typeof n?r=n:("function"==typeof i&&(a=arguments[1],i=void 0),r={text:n,title:i,onOK:a}),t.modal({text:r.text,title:r.title,buttons:[{text:e.buttonOK,className:"primary",onClick:r.onOK}]})},t.confirm=function(n,i,a,r){var o;return"object"==typeof n?o=n:("function"==typeof i&&(r=arguments[2],a=arguments[1],i=void 0),o={text:n,title:i,onOK:a,onCancel:r}),t.modal({text:o.text,title:o.title,buttons:[{text:e.buttonCancel,className:"default",onClick:o.onCancel},{text:e.buttonOK,className:"primary",onClick:o.onOK}]})},t.prompt=function(n,i,a,r,o){var s;"object"==typeof n?s=n:("function"==typeof i&&(o=arguments[3],r=arguments[2],a=arguments[1],i=void 0),s={text:n,title:i,input:o,onOK:a,onCancel:r,empty:!1});var c=t.modal({text:'

    '+(s.text||"")+'

    ',title:s.title,autoClose:!1,buttons:[{text:e.buttonCancel,className:"default",onClick:function(){t.closeModal(),s.onCancel&&s.onCancel.call(c)}},{text:e.buttonOK,className:"primary",onClick:function(){var e=t("#weui-prompt-input").val();return s.empty||""!==e&&null!==e?(t.closeModal(),void(s.onOK&&s.onOK.call(c,e))):(c.find(".weui-prompt-input").focus()[0].select(),!1)}}]},function(){this.find(".weui-prompt-input").focus()[0].select()});return c},t.login=function(n,i,a,r,o,s){var c;"object"==typeof n?c=n:("function"==typeof i&&(s=arguments[4],o=arguments[3],r=arguments[2],a=arguments[1],i=void 0),c={text:n,title:i,username:o,password:s,onOK:a,onCancel:r});var l=t.modal({text:'

    '+(c.text||"")+'

    ',title:c.title,autoClose:!1,buttons:[{text:e.buttonCancel,className:"default",onClick:function(){t.closeModal(),c.onCancel&&c.onCancel.call(l)}},{text:e.buttonOK,className:"primary",onClick:function(){var e=t("#weui-prompt-username").val(),n=t("#weui-prompt-password").val();return c.empty||""!==e&&null!==e?c.empty||""!==n&&null!==n?(t.closeModal(),void(c.onOK&&c.onOK.call(l,e,n))):(l.find("#weui-prompt-password").focus()[0].select(),!1):(l.find("#weui-prompt-username").focus()[0].select(),!1)}}]},function(){this.find("#weui-prompt-username").focus()[0].select()});return l},e=t.modal.prototype.defaults={title:"提示",text:void 0,buttonOK:"确定",buttonCancel:"取消",buttons:[{text:"确定",className:"primary"}],autoClose:!0}}($),+function(t){"use strict";var e=function(e,n){n=n||"";var i=(t("
    ").appendTo(document.body),'
    '+e+"
    "),a=t(i).appendTo(document.body);a.addClass("weui-toast--visible"),a.show()},n=function(e){t(".weui-mask_transparent").remove(),t(".weui-toast--visible").removeClass("weui-toast--visible").transitionEnd(function(){var n=t(this);n.remove(),e&&e(n)})};t.toast=function(t,a,r){"function"==typeof a&&(r=a);var o,s="weui-icon-success-no-circle",c=i.duration;"cancel"==a?(o="weui-toast_cancel",s="weui-icon-cancel"):"forbidden"==a?(o="weui-toast--forbidden",s="weui-icon-warn"):"text"==a?o="weui-toast--text":"number"==typeof a&&(c=a),e('

    '+(t||"已经完成")+"

    ",o),setTimeout(function(){n(r)},c)},t.showLoading=function(t){var n='
    ';n+='',n+="
    ",n+='

    '+(t||"数据加载中")+"

    ",e(n,"weui_loading_toast")},t.hideLoading=function(){n()};var i=t.toast.prototype.defaults={duration:2500}}($),+function(t){"use strict";var e,n=function(e){var n=t("
    ").appendTo(document.body),i=e.actions||[],a=i.map(function(t,e){return'
    '+t.text+"
    "}).join(""),r="";e.title&&(r='

    '+e.title+"

    ");var o='
    '+r+'
    '+a+'
    取消
    ',s=t(o).appendTo(document.body);s.find(".weui-actionsheet__menu .weui-actionsheet__cell, .weui-actionsheet__action .weui-actionsheet__cell").each(function(n,a){t(a).click(function(){t.closeActions(),e.onClose&&e.onClose(),i[n]&&i[n].onClick&&i[n].onClick()})}),n.show(),s.show(),n.addClass("weui-mask--visible"),s.addClass("weui-actionsheet_toggle")},i=function(){t(".weui-mask").removeClass("weui-mask--visible").transitionEnd(function(){t(this).remove()}),t(".weui-actionsheet").removeClass("weui-actionsheet_toggle").transitionEnd(function(){t(this).remove()})};t.actions=function(i){i=t.extend({},e,i),n(i)},t.closeActions=function(){i()},t(document).on("click",".weui-actions_mask",function(){t.closeActions()});var e=t.actions.prototype.defaults={title:void 0,onClose:void 0}}($),+function(t){"use strict";var e=function(n,i){"function"==typeof i&&(i={onRefresh:i}),"string"==typeof i&&(i=void 0),this.opt=t.extend(e.defaults,i||{}),this.container=t(n),this.attachEvents()};e.defaults={distance:50,onRefresh:void 0,onPull:void 0},e.prototype.touchStart=function(e){if(!this.container.hasClass("refreshing")){var n=t.getTouchPosition(e);this.start=n,this.diffX=this.diffY=0}},e.prototype.touchMove=function(e){if(!this.container.hasClass("refreshing")){if(!this.start)return!1;if(!(this.container.scrollTop()>0)){var n=t.getTouchPosition(e);return this.diffX=n.x-this.start.x,this.diffY=n.y-this.start.y,Math.abs(this.diffX)>Math.abs(this.diffY)?!0:void(this.diffY<0||(this.container.addClass("touching"),e.preventDefault(),e.stopPropagation(),this.diffY=Math.pow(this.diffY,.75),this.container.css("transform","translate3d(0, "+this.diffY+"px, 0)"),this.triggerPull(this.diffY)))}}},e.prototype.touchEnd=function(){this.start=!1,this.diffY<=0||this.container.hasClass("refreshing")||(this.container.removeClass("touching"),this.container.removeClass("pull-down pull-up"),this.container.css("transform",""),Math.abs(this.diffY)<=this.opt.distance||this.triggerPullToRefresh())},e.prototype.triggerPullToRefresh=function(){this.triggerPull(this.opt.distance),this.container.removeClass("pull-up").addClass("refreshing"),this.opt.onRefresh&&this.opt.onRefresh.call(this),this.container.trigger("pull-to-refresh")},e.prototype.triggerPull=function(t){t=0),(a||o||r)&&(e.os="ios",e.ios=!0),o&&!r&&(e.osVersion=o[2].replace(/_/g,"."),e.iphone=!0),a&&(e.osVersion=a[2].replace(/_/g,"."),e.ipad=!0),r&&(e.osVersion=r[3]?r[3].replace(/_/g,"."):null,e.iphone=!0),e.ios&&e.osVersion&&n.indexOf("Version/")>=0&&"10"===e.osVersion.split(".")[0]&&(e.osVersion=n.toLowerCase().split("version/")[1].split(" ")[0]),e.webView=(o||a||r)&&n.match(/.*AppleWebKit(?!.*Safari)/i), +e.os&&"ios"===e.os){var s=e.osVersion.split(".");e.minimalUi=!e.webView&&(r||o)&&(1*s[0]===7?1*s[1]>=1:1*s[0]>7)&&t('meta[name="viewport"]').length>0&&t('meta[name="viewport"]').attr("content").indexOf("minimal-ui")>=0}var c=t(window).width(),l=t(window).height();e.statusBar=!1,e.webView&&c*l===screen.width*screen.height?e.statusBar=!0:e.statusBar=!1;var u=[];if(e.pixelRatio=window.devicePixelRatio||1,u.push("pixel-ratio-"+Math.floor(e.pixelRatio)),e.pixelRatio>=2&&u.push("retina"),e.os&&(u.push(e.os,e.os+"-"+e.osVersion.split(".")[0],e.os+"-"+e.osVersion.replace(/\./g,"-")),"ios"===e.os))for(var p=parseInt(e.osVersion.split(".")[0],10),h=p-1;h>=6;h--)u.push("ios-gt-"+h);e.statusBar?u.push("with-statusbar-overlay"):t("html").removeClass("with-statusbar-overlay"),u.length>0&&t("html").addClass(u.join(" ")),t.device=e}($),+function(t){"use strict";var e=function(e){function n(){var e=!1;return c.params.convertToPopover||c.params.onlyInPopover?(!c.inline&&c.params.input&&(c.params.onlyInPopover?e=!0:t.device.ios?e=!!t.device.ipad:t(window).width()>=768&&(e=!0)),e):e}function i(){return!!(c.opened&&c.container&&c.container.length>0&&c.container.parents(".popover").length>0)}function a(){if(c.opened)for(var t=0;to){var u=e.scrollTop()+l-o;u+o>s&&(i=u+o-s+r,o===s&&(i=c.container.height()),e.css({"padding-bottom":i+"px"})),e.scrollTop(u,300)}}}function o(e){i()||(c.input&&c.input.length>0?e.target!==c.input[0]&&0===t(e.target).parents(".weui-picker-modal").length&&c.close():0===t(e.target).parents(".weui-picker-modal").length&&c.close())}function s(){c.opened=!1,c.input&&c.input.length>0&&c.input.parents(".page-content").css({"padding-bottom":""}),c.params.onClose&&c.params.onClose(c),c.container.find(".picker-items-col").each(function(){c.destroyPickerCol(this)})}var c=this,l={updateValuesOnMomentum:!1,updateValuesOnTouchmove:!0,rotateEffect:!1,momentumRatio:7,freeMode:!1,scrollToInput:!0,inputReadOnly:!0,toolbar:!0,toolbarCloseText:"完成",title:"请选择",toolbarTemplate:'
    {{closeText}}

    {{title}}

    '};e=e||{};for(var u in l)"undefined"==typeof e[u]&&(e[u]=l[u]);c.params=e,c.cols=[],c.initialized=!1,c.inline=!!c.params.container;var p=t.device.ios||navigator.userAgent.toLowerCase().indexOf("safari")>=0&&navigator.userAgent.toLowerCase().indexOf("chrome")<0&&!t.device.android;return c.setValue=function(t,e){for(var n=0,i=0;i=0||(c.value=e,c.displayValue=n,c.params.onChange&&c.params.onChange(c,c.value,c.displayValue),c.input&&c.input.length>0&&(t(c.input).val(c.params.formatValue?c.params.formatValue(c,c.value,c.displayValue):c.value.join(" ")),t(c.input).trigger("change")))},c.initPickerCol=function(e,n){function i(){w=t.requestAnimationFrame(function(){h.updateItems(void 0,void 0,0),i()})}function a(e){if(!T&&!y){e.preventDefault(),y=!0;var n=t.getTouchPosition(e);k=x=n.y,C=(new Date).getTime(),A=!0,M=E=t.getTranslate(h.wrapper[0],"y")}}function r(e){if(y){e.preventDefault(),A=!1;var n=t.getTouchPosition(e);x=n.y,T||(t.cancelAnimationFrame(w),T=!0,M=E=t.getTranslate(h.wrapper[0],"y"),h.wrapper.transition(0)),e.preventDefault();var i=x-k;E=M+i,_=void 0,v>E&&(E=v-Math.pow(v-E,.8),_="min"),E>g&&(E=g+Math.pow(E-g,.8),_="max"),h.wrapper.transform("translate3d(0,"+E+"px,0)"),h.updateItems(void 0,E,0,c.params.updateValuesOnTouchmove),P=E-D||E,O=(new Date).getTime(),D=E}}function o(e){if(!y||!T)return void(y=T=!1);y=T=!1,h.wrapper.transition(""),_&&("min"===_?h.wrapper.transform("translate3d(0,"+v+"px,0)"):h.wrapper.transform("translate3d(0,"+g+"px,0)")),b=(new Date).getTime();var n,a;b-C>300?a=E:(n=Math.abs(P/(b-O)),a=E+P*c.params.momentumRatio),a=Math.max(Math.min(a,g),v);var r=-Math.floor((a-g)/f);c.params.freeMode||(a=-r*f+g),h.wrapper.transform("translate3d(0,"+parseInt(a,10)+"px,0)"),h.updateItems(r,a,"",!0),c.params.updateValuesOnMomentum&&(i(),h.wrapper.transitionEnd(function(){t.cancelAnimationFrame(w)})),setTimeout(function(){A=!0},100)}function s(e){if(A){t.cancelAnimationFrame(w);var n=t(this).attr("data-picker-value");h.setValue(n)}}var l=t(e),u=l.index(),h=c.cols[u];if(!h.divider){h.container=l,h.wrapper=h.container.find(".picker-items-col-wrapper"),h.items=h.wrapper.find(".picker-item");var d,f,m,v,g;h.replaceValues=function(t,e){h.destroyEvents(),h.values=t,h.displayValues=e;var n=c.columnHTML(h,!0);h.wrapper.html(n),h.items=h.wrapper.find(".picker-item"),h.calcSize(),h.setValue(h.values[0]||"",0,!0),h.initEvents()},h.calcSize=function(){if(h.values.length){c.params.rotateEffect&&(h.container.removeClass("picker-items-col-absolute"),h.width||h.container.css({width:""}));var e,n;e=0,n=h.container[0].offsetHeight,d=h.wrapper[0].offsetHeight,f=h.items[0].offsetHeight,m=f*h.items.length,v=n/2-m+f/2,g=n/2-f/2,h.width&&(e=h.width,parseInt(e,10)===e&&(e+="px"),h.container.css({width:e})),c.params.rotateEffect&&(h.width||(h.items.each(function(){var n=t(this);n.css({width:"auto"}),e=Math.max(e,n[0].offsetWidth),n.css({width:""})}),h.container.css({width:e+2+"px"})),h.container.addClass("picker-items-col-absolute"))}},h.calcSize(),h.wrapper.transform("translate3d(0,"+g+"px,0)").transition(0);var w;h.setValue=function(e,n,a){"undefined"==typeof n&&(n="");var r=h.wrapper.find('.picker-item[data-picker-value="'+e+'"]').index();if("undefined"==typeof r||-1===r)return void(h.value=h.displayValue=e);var o=-r*f+g;h.wrapper.transition(n),h.wrapper.transform("translate3d(0,"+o+"px,0)"),c.params.updateValuesOnMomentum&&h.activeIndex&&h.activeIndex!==r&&(t.cancelAnimationFrame(w),h.wrapper.transitionEnd(function(){t.cancelAnimationFrame(w)}),i()),h.updateItems(r,o,n,a)},h.updateItems=function(e,n,i,a){"undefined"==typeof n&&(n=t.getTranslate(h.wrapper[0],"y")),"undefined"==typeof e&&(e=-Math.round((n-g)/f)),0>e&&(e=0),e>=h.items.length&&(e=h.items.length-1);var r=h.activeIndex;h.activeIndex=e,h.wrapper.find(".picker-selected").removeClass("picker-selected"),c.params.rotateEffect&&h.items.transition(i);var o=h.items.eq(e).addClass("picker-selected").transform("");if((a||"undefined"==typeof a)&&(h.value=o.attr("data-picker-value"),h.displayValue=h.displayValues?h.displayValues[e]:h.value,r!==e&&(h.onChange&&h.onChange(c,h.value,h.displayValue),c.updateValue())),c.params.rotateEffect){(n-(Math.floor((n-g)/f)*f+g))/f;h.items.each(function(){var e=t(this),i=e.index()*f,a=g-n,r=i-a,o=r/f,s=Math.ceil(h.height/f/2)+1,c=-18*o;c>180&&(c=180),-180>c&&(c=-180),Math.abs(o)>s?e.addClass("picker-item-far"):e.removeClass("picker-item-far"),e.transform("translate3d(0, "+(-n+g)+"px, "+(p?-110:0)+"px) rotateX("+c+"deg)")})}},n&&h.updateItems(0,g,0);var y,T,k,x,C,b,M,_,E,D,P,O,A=!0;h.initEvents=function(e){var n=e?"off":"on";h.container[n](t.touchEvents.start,a),h.container[n](t.touchEvents.move,r),h.container[n](t.touchEvents.end,o),h.items[n]("click",s)},h.destroyEvents=function(){h.initEvents(!0)},h.container[0].f7DestroyPickerCol=function(){h.destroyEvents()},h.initEvents()}},c.destroyPickerCol=function(e){e=t(e),"f7DestroyPickerCol"in e[0]&&e[0].f7DestroyPickerCol()},t(window).on("resize",a),c.columnHTML=function(t,e){var n="",i="";if(t.divider)i+='
    '+t.content+"
    ";else{for(var a=0;a'+(t.displayValues?t.displayValues[a]:t.values[a])+"";i+='
    '+n+"
    "}return e?n:i},c.layout=function(){var t,e="",n="";c.cols=[];var i="";for(t=0;t'+(c.params.toolbar?c.params.toolbarTemplate.replace(/{{closeText}}/g,c.params.toolbarCloseText).replace(/{{title}}/g,c.params.title):"")+'
    '+i+'
    ',c.pickerHTML=e},c.params.input&&(c.input=t(c.params.input),c.input.length>0&&(c.params.inputReadOnly&&c.input.prop("readOnly",!0),c.inline||c.input.on("click",r),c.params.inputReadOnly&&c.input.on("focus mousedown",function(t){t.preventDefault()}))),c.inline||t("html").on("click",o),c.opened=!1,c.open=function(){var e=n();c.opened||(c.layout(),e?(c.pickerHTML='
    '+c.pickerHTML+"
    ",c.popover=t.popover(c.pickerHTML,c.params.input,!0),c.container=t(c.popover).find(".weui-picker-modal"),t(c.popover).on("close",function(){s()})):c.inline?(c.container=t(c.pickerHTML),c.container.addClass("picker-modal-inline"),t(c.params.container).append(c.container)):(c.container=t(t.openPicker(c.pickerHTML)),t(c.container).on("close",function(){s()})),c.container[0].f7Picker=c,c.container.find(".picker-items-col").each(function(){var t=!0;(!c.initialized&&c.params.value||c.initialized&&c.value)&&(t=!1),c.initPickerCol(this,t)}),c.initialized?c.value&&c.setValue(c.value,0):c.params.value&&c.setValue(c.params.value,0)),c.opened=!0,c.initialized=!0,c.params.onOpen&&c.params.onOpen(c)},c.close=function(e){return c.opened&&!c.inline?i()?void t.closePicker(c.popover):void t.closePicker(c.container):void 0},c.destroy=function(){c.close(),c.params.input&&c.input.length>0&&(c.input.off("click focus",r),t(c.input).data("picker",null)),t("html").off("click",o),t(window).off("resize",a)},c.inline&&c.open(),c};t(document).on("click",".close-picker",function(){var e=t(".weui-picker-modal.weui-picker-modal-visible");e.length>0&&t.closePicker(e)}),t(document).on(t.touchEvents.move,".picker-modal-inner",function(t){t.preventDefault()}),t.openPicker=function(e,n,i){"function"==typeof n&&(i=n,n=void 0),t.closePicker();var a=t("
    ").appendTo(document.body);a.show(),a.addClass("weui-picker-container-visible");var r=t(e).appendTo(a);return r.width(),r.addClass("weui-picker-modal-visible"),i&&a.on("close",i),r},t.updatePicker=function(e){var n=t(".weui-picker-container-visible");if(!n[0])return!1;n.html("");var i=t(e).appendTo(n);return i.addClass("weui-picker-modal-visible"),i},t.closePicker=function(e,n){"function"==typeof e&&(n=e),t(".weui-picker-modal-visible").removeClass("weui-picker-modal-visible").transitionEnd(function(){t(this).parent().remove(),n&&n()}).trigger("close")},t.fn.picker=function(n){var i=arguments;return this.each(function(){if(this){var a=t(this),r=a.data("picker");if(!r){n=t.extend({input:this},n||{});var o=a.val();void 0===n.value&&""!==o&&(n.value=n.cols&&n.cols.length>1?o.split(" "):[o]);var s=t.extend({input:this},n);r=new e(s),a.data("picker",r)}"string"==typeof n&&r[n].apply(r,Array.prototype.slice.call(i,1))}})}}($),+function(t){"use strict";var e,n=[],i=function(e,i){this.config=i,this.data={values:"",titles:"",origins:[],length:0},this.$input=t(e),this.$input.prop("readOnly",!0),this.initConfig(),i=this.config,this.$input.click(t.proxy(this.open,this)),n.push(this)};i.prototype.initConfig=function(){this.config=t.extend({},e,this.config);var n=this.config;n.items&&n.items.length&&(n.items=n.items.map(function(t,e){return"string"==typeof t?{title:t,value:t}:t}),this.tpl=t.t7.compile("
    "+n.toolbarTemplate+(n.multi?n.checkboxTemplate:n.radioTemplate)+"
    "),void 0!==n.input&&this.$input.val(n.input),this.parseInitValue(),this._init=!0)},i.prototype.updateInputValue=function(t,e){var n,i;this.config.multi?(n=t.join(this.config.split),i=e.join(this.config.split)):(n=t[0],i=e[0]);var a=[];this.config.items.forEach(function(e){t.each(function(t,n){e.value==n&&a.push(e)})}),this.$input.val(i).data("values",n),this.$input.attr("value",i).attr("data-values",n);var r={values:n,titles:i,valuesArray:t,titlesArray:e,origins:a,length:a.length};this.data=r,this.$input.trigger("change",r),this.config.onChange&&this.config.onChange.call(this,r)},i.prototype.parseInitValue=function(){var t=this.$input.val(),e=this.config.items;if(this._init||void 0!==t&&null!=t&&""!==t)for(var n=this.config.multi?t.split(this.config.split):[t],i=0;ithis.config.max)return t.toast("最多只能选择"+this.config.max+"个","text"),!1}}return t.closePicker(function(){i.onClose(),e&&e()}),!0},i.prototype.onClose=function(){this._open=!1,this.config.onClose&&this.config.onClose(this)},i.prototype.getHTML=function(t){var e=this.config;return this.tpl({items:e.items,title:e.title,closeText:e.closeText})},t.fn.select=function(e,n){return this.each(function(){var a=t(this);a.data("weui-select")||a.data("weui-select",new i(this,e));var r=a.data("weui-select");return"string"==typeof e&&r[e].call(r,n),r})},e=t.fn.select.prototype.defaults={items:[],input:void 0,title:"请选择",multi:!1,closeText:"确定",autoClose:!0,onChange:void 0,beforeClose:void 0,onClose:void 0,onOpen:void 0,split:",",min:void 0,max:void 0,toolbarTemplate:'
    {{closeText}}

    {{title}}

    ',radioTemplate:'
    {{#items}} {{/items}}
    ',checkboxTemplate:'
    {{#items}} {{/items}}
    '}}($),+function(t){"use strict";var e,n=!1,i=function(t,e){var t=new Date(t),e=new Date(e);return t.getFullYear()===e.getFullYear()&&t.getMonth()===e.getMonth()&&t.getDate()===e.getDate()},a=function(a){function r(){var e=!1;return p.params.convertToPopover||p.params.onlyInPopover?(!p.inline&&p.params.input&&(p.params.onlyInPopover?e=!0:t.device.ios?e=!!t.device.ipad:t(window).width()>=768&&(e=!0)),e):e}function o(){return!!(p.opened&&p.container&&p.container.length>0&&p.container.parents(".popover").length>0)}function s(t){t=new Date(t);var e=t.getFullYear(),n=t.getMonth(),i=n+1,a=t.getDate(),r=t.getDay();return p.params.dateFormat.replace(/yyyy/g,e).replace(/yy/g,(e+"").substring(2)).replace(/mm/g,10>i?"0"+i:i).replace(/m/g,i).replace(/MM/g,p.params.monthNames[n]).replace(/M/g,p.params.monthNamesShort[n]).replace(/dd/g,10>a?"0"+a:a).replace(/d/g,a).replace(/DD/g,p.params.dayNames[r]).replace(/D/g,p.params.dayNamesShort[r])}function c(t){if(t.preventDefault(),!p.opened&&(p.open(),p.params.scrollToInput&&!r())){var e=p.input.parents(".page-content");if(0===e.length)return;var n,i=parseInt(e.css("padding-top"),10),a=parseInt(e.css("padding-bottom"),10),o=e[0].offsetHeight-i-p.container.height(),s=e[0].scrollHeight-i-p.container.height(),c=p.input.offset().top-i+p.input[0].offsetHeight;if(c>o){var l=e.scrollTop()+c-o;l+o>s&&(n=l+o-s+a,o===s&&(n=p.container.height()),e.css({"padding-bottom":n+"px"})),e.scrollTop(l,300)}}}function l(e){o()||(p.input&&p.input.length>0?e.target!==p.input[0]&&0===t(e.target).parents(".weui-picker-modal").length&&p.close():0===t(e.target).parents(".weui-picker-modal").length&&p.close())}function u(){p.opened=!1,p.input&&p.input.length>0&&p.input.parents(".page-content").css({"padding-bottom":""}),p.params.onClose&&p.params.onClose(p),p.destroyCalendarEvents()}var p=this;a=a||{};for(var h in e)"undefined"==typeof a[h]&&(a[h]=e[h]);p.params=a,p.initialized=!1,p.inline=!!p.params.container,p.isH="horizontal"===p.params.direction;var d=p.isH&&n?-1:1;return p.animating=!1,p.addValue=function(t){if(p.params.multiple){p.value||(p.value=[]);for(var e,n=0;n0){if(p.params.formatValue)n=p.params.formatValue(p,p.value);else{for(n=[],e=0;eMath.abs(u-c))),p.isH&&x)return void(o=!1);if(e.preventDefault(),p.animating)return void(o=!1);C=!1,s||(s=!0,w=p.wrapper[0].offsetWidth,y=p.wrapper[0].offsetHeight,p.wrapper.transition(0)),e.preventDefault(),k=p.isH?u-c:h-l,T=k/(p.isH?w:y),g=100*(p.monthsTranslate*d+T),p.wrapper.transform("translate3d("+(p.isH?g:0)+"%, "+(p.isH?0:g)+"%, 0)")}}function a(t){return o&&s?(o=s=!1,m=(new Date).getTime(),300>m-f?Math.abs(k)<10?p.resetMonth():k>=10?n?p.nextMonth():p.prevMonth():n?p.prevMonth():p.nextMonth():-.5>=T?n?p.prevMonth():p.nextMonth():T>=.5?n?p.nextMonth():p.prevMonth():p.resetMonth(),void setTimeout(function(){C=!0},100)):void(o=s=!1)}function r(e){if(C){var n=t(e.target).parents(".picker-calendar-day");if(0===n.length&&t(e.target).hasClass("picker-calendar-day")&&(n=t(e.target)),0!==n.length&&!n.hasClass("picker-calendar-day-disabled")){n.hasClass("picker-calendar-day-next")&&p.nextMonth(),n.hasClass("picker-calendar-day-prev")&&p.prevMonth();var i=n.attr("data-year"),a=n.attr("data-month"),r=n.attr("data-day");p.params.onDayClick&&p.params.onDayClick(p,n[0],i,a,r),p.addValue(new Date(i,a,r).getTime()),p.params.closeOnSelect&&!p.params.multiple&&p.close()}}}var o,s,c,l,u,h,f,m,v,g,w,y,T,k,x,C=!0;p.container.find(".picker-calendar-prev-month").on("click",p.prevMonth),p.container.find(".picker-calendar-next-month").on("click",p.nextMonth),p.container.find(".picker-calendar-prev-year").on("click",p.prevYear),p.container.find(".picker-calendar-next-year").on("click",p.nextYear),p.wrapper.on("click",r),p.params.touchMove&&(p.wrapper.on(t.touchEvents.start,e),p.wrapper.on(t.touchEvents.move,i),p.wrapper.on(t.touchEvents.end,a)),p.container[0].f7DestroyCalendarEvents=function(){p.container.find(".picker-calendar-prev-month").off("click",p.prevMonth),p.container.find(".picker-calendar-next-month").off("click",p.nextMonth),p.container.find(".picker-calendar-prev-year").off("click",p.prevYear),p.container.find(".picker-calendar-next-year").off("click",p.nextYear),p.wrapper.off("click",r),p.params.touchMove&&(p.wrapper.off(t.touchEvents.start,e),p.wrapper.off(t.touchEvents.move,i),p.wrapper.off(t.touchEvents.end,a))}},p.destroyCalendarEvents=function(t){"f7DestroyCalendarEvents"in p.container[0]&&p.container[0].f7DestroyCalendarEvents()},p.daysInMonth=function(t){var e=new Date(t);return new Date(e.getFullYear(),e.getMonth()+1,0).getDate()},p.monthHTML=function(t,e){t=new Date(t);var n=t.getFullYear(),i=t.getMonth();t.getDate();"next"===e&&(t=11===i?new Date(n+1,0):new Date(n,i+1,1)),"prev"===e&&(t=0===i?new Date(n-1,11):new Date(n,i-1,1)),"next"!==e&&"prev"!==e||(i=t.getMonth(),n=t.getFullYear());var a=p.daysInMonth(new Date(t.getFullYear(),t.getMonth()).getTime()-864e6),r=p.daysInMonth(t),o=new Date(t.getFullYear(),t.getMonth()).getDay();0===o&&(o=7);var s,c,l,u=[],h=6,d=7,f="",m=0+(p.params.firstDay-1),v=(new Date).setHours(0,0,0,0),g=p.params.minDate?new Date(p.params.minDate).getTime():null,w=p.params.maxDate?new Date(p.params.maxDate).getTime():null;if(p.value&&p.value.length)for(c=0;c=c;c++){var y="";for(l=1;d>=l;l++){var T=l;m++;var k=m-o,x="";0>k?(k=a+k+1,x+=" picker-calendar-day-prev",s=new Date(0>i-1?n-1:n,0>i-1?11:i-1,k).getTime()):(k+=1,k>r?(k-=r,x+=" picker-calendar-day-next",s=new Date(i+1>11?n+1:n,i+1>11?0:i+1,k).getTime()):s=new Date(n,i,k).getTime()),s===v&&(x+=" picker-calendar-day-today"),u.indexOf(s)>=0&&(x+=" picker-calendar-day-selected"),p.params.weekendDays.indexOf(T-1)>=0&&(x+=" picker-calendar-day-weekend"),(g&&g>s||w&&s>w)&&(x+=" picker-calendar-day-disabled"),s=new Date(s);var C=s.getFullYear(),b=s.getMonth();y+='
    '+k+"
    "}f+='
    '+y+"
    "}return f='
    '+f+"
    "},p.animating=!1,p.updateCurrentMonthYear=function(t){"undefined"==typeof t?(p.currentMonth=parseInt(p.months.eq(1).attr("data-month"),10),p.currentYear=parseInt(p.months.eq(1).attr("data-year"),10)):(p.currentMonth=parseInt(p.months.eq("next"===t?p.months.length-1:0).attr("data-month"),10),p.currentYear=parseInt(p.months.eq("next"===t?p.months.length-1:0).attr("data-year"),10)),p.container.find(".current-month-value").text(p.params.monthNames[p.currentMonth]),p.container.find(".current-year-value").text(p.currentYear)},p.onMonthChangeStart=function(t){p.updateCurrentMonthYear(t),p.months.removeClass("picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next");var e="next"===t?p.months.length-1:0;p.months.eq(e).addClass("picker-calendar-month-current"),p.months.eq("next"===t?e-1:e+1).addClass("next"===t?"picker-calendar-month-prev":"picker-calendar-month-next"),p.params.onMonthYearChangeStart&&p.params.onMonthYearChangeStart(p,p.currentYear,p.currentMonth)},p.onMonthChangeEnd=function(t,e){p.animating=!1;var n,i,a;p.wrapper.find(".picker-calendar-month:not(.picker-calendar-month-prev):not(.picker-calendar-month-current):not(.picker-calendar-month-next)").remove(),"undefined"==typeof t&&(t="next",e=!0),e?(p.wrapper.find(".picker-calendar-month-next, .picker-calendar-month-prev").remove(),i=p.monthHTML(new Date(p.currentYear,p.currentMonth),"prev"),n=p.monthHTML(new Date(p.currentYear,p.currentMonth),"next")):a=p.monthHTML(new Date(p.currentYear,p.currentMonth),t),("next"===t||e)&&p.wrapper.append(a||n),("prev"===t||e)&&p.wrapper.prepend(a||i),p.months=p.wrapper.find(".picker-calendar-month"),p.setMonthsTranslate(p.monthsTranslate),p.params.onMonthAdd&&p.params.onMonthAdd(p,"next"===t?p.months.eq(p.months.length-1)[0]:p.months.eq(0)[0]),p.params.onMonthYearChangeEnd&&p.params.onMonthYearChangeEnd(p,p.currentYear,p.currentMonth)},p.setMonthsTranslate=function(t){t=t||p.monthsTranslate||0,"undefined"==typeof p.monthsTranslate&&(p.monthsTranslate=t),p.months.removeClass("picker-calendar-month-current picker-calendar-month-prev picker-calendar-month-next");var e=100*-(t+1)*d,n=100*-t*d,i=100*-(t-1)*d;p.months.eq(0).transform("translate3d("+(p.isH?e:0)+"%, "+(p.isH?0:e)+"%, 0)").addClass("picker-calendar-month-prev"),p.months.eq(1).transform("translate3d("+(p.isH?n:0)+"%, "+(p.isH?0:n)+"%, 0)").addClass("picker-calendar-month-current"),p.months.eq(2).transform("translate3d("+(p.isH?i:0)+"%, "+(p.isH?0:i)+"%, 0)").addClass("picker-calendar-month-next")},p.nextMonth=function(e){"undefined"!=typeof e&&"object"!=typeof e||(e="",p.params.animate||(e=0));var n=parseInt(p.months.eq(p.months.length-1).attr("data-month"),10),i=parseInt(p.months.eq(p.months.length-1).attr("data-year"),10),a=new Date(i,n),r=a.getTime(),o=!p.animating;if(p.params.maxDate&&r>new Date(p.params.maxDate).getTime())return p.resetMonth();if(p.monthsTranslate--,n===p.currentMonth){var s=100*-p.monthsTranslate*d,c=t(p.monthHTML(r,"next")).transform("translate3d("+(p.isH?s:0)+"%, "+(p.isH?0:s)+"%, 0)").addClass("picker-calendar-month-next");p.wrapper.append(c[0]),p.months=p.wrapper.find(".picker-calendar-month"),p.params.onMonthAdd&&p.params.onMonthAdd(p,p.months.eq(p.months.length-1)[0])}p.animating=!0,p.onMonthChangeStart("next");var l=100*p.monthsTranslate*d;p.wrapper.transition(e).transform("translate3d("+(p.isH?l:0)+"%, "+(p.isH?0:l)+"%, 0)"),o&&p.wrapper.transitionEnd(function(){p.onMonthChangeEnd("next")}),p.params.animate||p.onMonthChangeEnd("next")},p.prevMonth=function(e){"undefined"!=typeof e&&"object"!=typeof e||(e="",p.params.animate||(e=0));var n=parseInt(p.months.eq(0).attr("data-month"),10),i=parseInt(p.months.eq(0).attr("data-year"),10),a=new Date(i,n+1,-1),r=a.getTime(),o=!p.animating;if(p.params.minDate&&rnew Date(p.params.maxDate).getTime())return!1;if(p.params.minDate&&ia?"next":"prev",o=p.monthHTML(new Date(t,e));p.monthsTranslate=p.monthsTranslate||0;var s,c,l=p.monthsTranslate,u=!p.animating;i>a?(p.monthsTranslate--,p.animating||p.months.eq(p.months.length-1).remove(),p.wrapper.append(o),p.months=p.wrapper.find(".picker-calendar-month"),s=100*-(l-1)*d,p.months.eq(p.months.length-1).transform("translate3d("+(p.isH?s:0)+"%, "+(p.isH?0:s)+"%, 0)").addClass("picker-calendar-month-next")):(p.monthsTranslate++,p.animating||p.months.eq(0).remove(),p.wrapper.prepend(o),p.months=p.wrapper.find(".picker-calendar-month"),s=100*-(l+1)*d,p.months.eq(0).transform("translate3d("+(p.isH?s:0)+"%, "+(p.isH?0:s)+"%, 0)").addClass("picker-calendar-month-prev")),p.params.onMonthAdd&&p.params.onMonthAdd(p,"next"===r?p.months.eq(p.months.length-1)[0]:p.months.eq(0)[0]),p.animating=!0,p.onMonthChangeStart(r),c=100*p.monthsTranslate*d,p.wrapper.transition(n).transform("translate3d("+(p.isH?c:0)+"%, "+(p.isH?0:c)+"%, 0)"),u&&p.wrapper.transitionEnd(function(){p.onMonthChangeEnd(r,!0)}),p.params.animate||p.onMonthChangeEnd(r)},p.nextYear=function(){p.setYearMonth(p.currentYear+1)},p.prevYear=function(){p.setYearMonth(p.currentYear-1)},p.layout=function(){var t,e="",n="",i=p.value&&p.value.length?p.value[0]:(new Date).setHours(0,0,0,0),a=p.monthHTML(i,"prev"),r=p.monthHTML(i),o=p.monthHTML(i,"next"),s='
    '+(a+r+o)+"
    ",c="";if(p.params.weekHeader){for(t=0;7>t;t++){var l=t+p.params.firstDay>6?t-7+p.params.firstDay:t+p.params.firstDay,u=p.params.dayNamesShort[l];c+='
    '+u+"
    "}c='
    '+c+"
    "}n="weui-picker-calendar "+(p.params.cssClass||""),p.inline||(n="weui-picker-modal "+n);var h=p.params.toolbar?p.params.toolbarTemplate.replace(/{{closeText}}/g,p.params.toolbarCloseText):"";p.params.toolbar&&(h=p.params.toolbarTemplate.replace(/{{closeText}}/g,p.params.toolbarCloseText).replace(/{{monthPicker}}/g,p.params.monthPicker?p.params.monthPickerTemplate:"").replace(/{{yearPicker}}/g,p.params.yearPicker?p.params.yearPickerTemplate:"")),e='
    '+h+'
    '+c+s+"
    ",p.pickerHTML=e},p.params.input&&(p.input=t(p.params.input),p.input.length>0&&(p.params.inputReadOnly&&p.input.prop("readOnly",!0),p.inline||p.input.on("click",c),p.params.inputReadOnly&&p.input.on("focus mousedown",function(t){t.preventDefault()}))),p.inline||t(document).on("click touchend",l),p.opened=!1,p.open=function(){var e=r()&&!1,n=!1;p.opened||(p.value||p.params.value&&(p.value=p.params.value,n=!0),p.layout(),e?(p.pickerHTML='
    '+p.pickerHTML+"
    ",p.popover=t.popover(p.pickerHTML,p.params.input,!0),p.container=t(p.popover).find(".weui-picker-modal"),t(p.popover).on("close",function(){ +u()})):p.inline?(p.container=t(p.pickerHTML),p.container.addClass("picker-modal-inline"),t(p.params.container).append(p.container)):(p.container=t(t.openPicker(p.pickerHTML)),t(p.container).on("close",function(){u()})),p.container[0].f7Calendar=p,p.wrapper=p.container.find(".picker-calendar-months-wrapper"),p.months=p.wrapper.find(".picker-calendar-month"),p.updateCurrentMonthYear(),p.monthsTranslate=0,p.setMonthsTranslate(),p.initCalendarEvents(),n&&p.updateValue()),p.opened=!0,p.initialized=!0,p.params.onMonthAdd&&p.months.each(function(){p.params.onMonthAdd(p,this)}),p.params.onOpen&&p.params.onOpen(p)},p.close=function(){return p.opened&&!p.inline?(p.animating=!1,o()?void t.closePicker(p.popover):void t.closePicker(p.container)):void 0},p.destroy=function(){p.close(),p.params.input&&p.input.length>0&&(p.input.off("click focus",c),p.input.data("calendar",null)),t("html").off("click",l)},p.inline&&p.open(),p},r=function(t){return 10>t?"0"+t:t};t.fn.calendar=function(e,n){return e=e||{},this.each(function(){var i=t(this);if(i[0]){var o={};"INPUT"===i[0].tagName.toUpperCase()?o.input=i:o.container=i;var s=i.data("calendar");if(!s)if("string"==typeof e);else{if(!e.value&&i.val()&&(e.value=[i.val()]),!e.value){var c=new Date;e.value=[c.getFullYear()+"/"+r(c.getMonth()+1)+"/"+r(c.getDate())]}s=i.data("calendar",new a(t.extend(o,e)))}"string"==typeof e&&s[e].call(s,n)}})},e=t.fn.calendar.prototype.defaults={value:void 0,monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayNames:["周日","周一","周二","周三","周四","周五","周六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],firstDay:1,weekendDays:[0,6],multiple:!1,dateFormat:"yyyy/mm/dd",direction:"horizontal",minDate:null,maxDate:null,touchMove:!0,animate:!0,closeOnSelect:!0,monthPicker:!0,monthPickerTemplate:'
    ',yearPicker:!0,yearPickerTemplate:'
    ',weekHeader:!0,scrollToInput:!0,inputReadOnly:!0,convertToPopover:!0,onlyInPopover:!1,toolbar:!0,toolbarCloseText:"Done",toolbarTemplate:'
    {{yearPicker}}{{monthPicker}}
    '}}($),+function(t){"use strict";var e,n=function(t){return 10>t?"0"+t:t},i=function(e,n){this.input=t(e),this.params=n||{},this.initMonthes=n.monthes,this.initYears=n.years;var i=t.extend({},n,this.getConfig());t(this.input).picker(i)};i.prototype={getDays:function(t){for(var e=[],n=1;(t||31)>=n;n++)e.push(10>n?"0"+n:n);return e},getDaysByMonthAndYear:function(t,e){var n=new Date(e,parseInt(t)+1-1,1),i=new Date(n-1);return this.getDays(i.getDate())},getConfig:function(){var t,e=new Date,i=this.params,a=this,r={rotateEffect:!1,cssClass:"datetime-picker",value:[e.getFullYear(),n(e.getMonth()+1),n(e.getDate()),n(e.getHours()),n(e.getMinutes())],onChange:function(e,n,r){var o=(e.cols,a.getDaysByMonthAndYear(n[1],n[0])),s=n[2];s>o.length&&(s=o.length),e.cols[4].setValue(s);var c=new Date(n[0]+"-"+n[1]+"-"+n[2]),l=!0;if(i.min){var u=new Date("function"==typeof i.min?i.min():i.min);+u>c&&(e.setValue(t),l=!1)}if(i.max){var p=new Date("function"==typeof i.max?i.max():i.max);c>+p&&(e.setValue(t),l=!1)}l&&(t=n),a.params.onChange&&a.params.onChange.apply(this,arguments)},formatValue:function(t,e,n){return a.params.format(t,e,n)},cols:[{values:this.initYears},{divider:!0,content:i.yearSplit},{values:this.initMonthes},{divider:!0,content:i.monthSplit},{values:function(){for(var t=[],e=1;31>=e;e++)t.push(n(e));return t}()}]};i.dateSplit&&r.cols.push({divider:!0,content:i.dateSplit}),r.cols.push({divider:!0,content:i.datetimeSplit});var o=a.params.times();o&&o.length&&(r.cols=r.cols.concat(o));var s=this.input.val();return s&&(r.value=i.parse(s)),this.params.value&&(this.input.val(this.params.value),r.value=i.parse(this.params.value)),r}},t.fn.datetimePicker=function(n){return n=t.extend({},e,n),this.each(function(){if(this){var e=t(this),a=e.data("datetime");return a||e.data("datetime",new i(this,n)),a}})},e=t.fn.datetimePicker.prototype.defaults={input:void 0,min:void 0,max:void 0,yearSplit:"-",monthSplit:"-",dateSplit:"",datetimeSplit:" ",monthes:"01 02 03 04 05 06 07 08 09 10 11 12".split(" "),years:function(){for(var t=[],e=1950;2030>=e;e++)t.push(e);return t}(),times:function(){return[{values:function(){for(var t=[],e=0;24>e;e++)t.push(n(e));return t}()},{divider:!0,content:":"},{values:function(){for(var t=[],e=0;60>e;e++)t.push(n(e));return t}()}]},format:function(t,e){return t.cols.map(function(t){return t.value||t.content}).join("")},parse:function(t){var e=t.split(this.datetimeSplit);return e[0].split(/\D/).concat(e[1].split(/:|时|分|秒/)).filter(function(t){return!!t})}}}($),+function(t){"use strict";t.openPopup=function(e,n){t.closePopup(),e=t(e),e.show(),e.width(),e.addClass("weui-popup__container--visible");var i=e.find(".weui-popup__modal");i.width(),i.transitionEnd(function(){i.trigger("open")})},t.closePopup=function(e,n){e=t(e||".weui-popup__container--visible"),e.find(".weui-popup__modal").transitionEnd(function(){var i=t(this);i.trigger("close"),e.hide(),n&&e.remove()}),e.removeClass("weui-popup__container--visible")},t(document).on("click",".close-popup, .weui-popup__overlay",function(){t.closePopup()}).on("click",".open-popup",function(){t(t(this).data("target")).popup()}).on("click",".weui-popup__container",function(e){t(e.target).hasClass("weui-popup__container")&&t.closePopup()}),t.fn.popup=function(){return this.each(function(){t.openPopup(this)})}}($),+function(t){"use strict";var e,n,i,a,r,o,s=function(n){var i=t.getTouchPosition(n);a=i,r=o=0,e.addClass("touching")},c=function(n){if(!a)return!1;n.preventDefault(),n.stopPropagation();var i=t.getTouchPosition(n);r=i.x-a.x,o=i.y-a.y,o>0&&(o=Math.sqrt(o)),e.css("transform","translate3d(0, "+o+"px, 0)")},l=function(){e.removeClass("touching"),e.attr("style",""),0>o&&Math.abs(o)>.38*e.height()&&t.closeNotification(),Math.abs(r)<=1&&Math.abs(o)<=1&&e.trigger("noti-click"),a=!1},u=function(e){e.on(t.touchEvents.start,s),e.on(t.touchEvents.move,c),e.on(t.touchEvents.end,l)};t.notification=t.noti=function(a){a=t.extend({},n,a),e=t(".weui-notification"),e[0]||(e=t('
    ').appendTo(document.body),u(e)),e.off("noti-click"),a.onClick&&e.on("noti-click",function(){a.onClick(a.data)}),e.html(t.t7.compile(a.tpl)(a)),e.show(),e.addClass("weui-notification--in"),e.data("params",a);var r=function(){i&&(clearTimeout(i),i=null),i=setTimeout(function(){e.hasClass("weui-notification--touching")?r():t.closeNotification()},a.time)};r()},t.closeNotification=function(){i&&clearTimeout(i),i=null;var e=t(".weui-notification").removeClass("weui-notification--in").transitionEnd(function(){t(this).remove()});if(e[0]){var n=t(".weui-notification").data("params");n&&n.onClose&&n.onClose(n.data)}},n=t.noti.prototype.defaults={title:void 0,text:void 0,media:void 0,time:4e3,onClick:void 0,onClose:void 0,data:void 0,tpl:'
    {{#if media}}
    {{media}}
    {{/if}}
    {{#if title}}
    {{title}}
    {{/if}}{{#if text}}
    {{text}}
    {{/if}}
    '}}($),+function(t){"use strict";var e;t.toptip=function(n,i,a){if(n){"string"==typeof i&&(a=i,i=void 0),i=i||3e3;var r=a?"bg-"+a:"bg-danger",o=t(".weui-toptips").remove();o=t('
    ').appendTo(document.body),o.html(n),o[0].className="weui-toptips "+r,clearTimeout(e),o.hasClass("weui-toptips_visible")||(o.show().width(),o.addClass("weui-toptips_visible")),e=setTimeout(function(){o.removeClass("weui-toptips_visible").transitionEnd(function(){o.remove()})},i)}}}($),+function(t){"use strict";var e=function(e,n){this.container=t(e),this.handler=this.container.find(".weui-slider__handler"),this.track=this.container.find(".weui-slider__track"),this.value=this.container.find(".weui-slider-box__value"),this.bind(),"function"==typeof n&&(this.callback=n)};e.prototype.bind=function(){this.container.on(t.touchEvents.start,t.proxy(this.touchStart,this)).on(t.touchEvents.end,t.proxy(this.touchEnd,this)),t(document.body).on(t.touchEvents.move,t.proxy(this.touchMove,this))},e.prototype.touchStart=function(e){e.preventDefault(),this.start=t.getTouchPosition(e),this.width=this.container.find(".weui-slider__inner").width(),this.left=parseInt(this.container.find(".weui-slider__handler").css("left")),this.touching=!0},e.prototype.touchMove=function(e){if(!this.touching)return!0;var n=t.getTouchPosition(e),i=n.x-this.start.x,a=i+this.left,r=parseInt(a/this.width*100);0>r&&(r=0),r>100&&(r=100),this.handler.css("left",r+"%"),this.track.css("width",r+"%"),this.value.text(r),this.callback&&this.callback.call(this,r),this.container.trigger("change",r)},e.prototype.touchEnd=function(t){this.touching=!1},t.fn.slider=function(n){this.each(function(){var i=t(this),a=i.data("slider");return a?a:void i.data("slider",new e(this,n))})}}($),+function(t){"use strict";var e=[],n="swipeout-touching",i=function(n){this.container=t(n),this.mover=this.container.find(">.weui-cell__bd"),this.attachEvents(),e.push(this)};i.prototype.touchStart=function(e){var i=t.getTouchPosition(e);this.container.addClass(n),this.start=i,this.startX=0,this.startTime=+new Date;var a=this.mover.css("transform").match(/-?[\d\.]+/g);a&&a.length&&(this.startX=parseInt(a[4])),this.diffX=this.diffY=0,this._closeOthers(),this.limit=this.container.find(">.weui-cell__ft").width()||68},i.prototype.touchMove=function(e){if(!this.start)return!0;var n=t.getTouchPosition(e);if(this.diffX=n.x-this.start.x,this.diffY=n.y-this.start.y,Math.abs(this.diffX)0&&(i=0),Math.abs(i)>this.limit&&(i=-(Math.pow(-(i+this.limit),.7)+this.limit)),this.mover.css("transform","translate3d("+i+"px, 0, 0)")},i.prototype.touchEnd=function(){if(!this.start)return!0;this.start=!1;var t=this.diffX+this.startX,e=new Date-this.startTime;this.diffX<-5&&200>e?this.open():this.diffX>=0&&200>e?this.close():t>0||-t<=this.limit/2?this.close():this.open()},i.prototype.close=function(){this.container.removeClass(n),this.mover.css("transform","translate3d(0, 0, 0)"),this.container.trigger("swipeout-close")},i.prototype.open=function(){this.container.removeClass(n),this._closeOthers(),this.mover.css("transform","translate3d("+-this.limit+"px, 0, 0)"),this.container.trigger("swipeout-open")},i.prototype.attachEvents=function(){var e=this.mover;e.on(t.touchEvents.start,t.proxy(this.touchStart,this)),e.on(t.touchEvents.move,t.proxy(this.touchMove,this)),e.on(t.touchEvents.end,t.proxy(this.touchEnd,this))},i.prototype._closeOthers=function(){var t=this;e.forEach(function(e){e!==t&&e.close()})};var a=function(t){return new i(t)};t.fn.swipeout=function(e){return this.each(function(){var n=t(this),i=n.data("swipeout")||a(this);n.data("swipeout",i),"string"==typeof e&&i[e]()})},t(".weui-cell_swiped").swipeout()}($); \ No newline at end of file diff --git a/static/mobile/lib/jquery.js b/static/mobile/lib/jquery.js new file mode 100644 index 000000000..9b5206bcc --- /dev/null +++ b/static/mobile/lib/jquery.js @@ -0,0 +1,10364 @@ +/*! + * jQuery JavaScript Library v3.3.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2018-01-20T17:24Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + + + var preservedScriptAttributes = { + type: true, + src: true, + noModule: true + }; + + function DOMEval( code, doc, node ) { + doc = doc || document; + + var i, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + if ( node[ i ] ) { + script[ i ] = node[ i ]; + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.3.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && Array.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.3 + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-08-08 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true && ("form" in elem || "label" in elem); + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + disabledAncestor( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID filter and find + if ( support.getById ) { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( el ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( el ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( el ) { + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( el ) { + return el.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( nodeName( elem, "iframe" ) ) { + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + jQuery.contains( elem.ownerDocument, elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + +var swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE <=9 only + option: [ 1, "" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
    " ], + col: [ 2, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); +var documentElement = document.documentElement; + + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 only +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + div.style.position = "absolute"; + scrollboxSizeVal = div.offsetWidth === 36 || "absolute"; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + + cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style; + +// Return a css property mapped to a potentially vendor prefixed property +function vendorPropName( name ) { + + // Shortcut for names that are not vendor prefixed + if ( name in emptyStyle ) { + return name; + } + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a property mapped along what jQuery.cssProps suggests or to +// a vendor prefixed property. +function finalPropName( name ) { + var ret = jQuery.cssProps[ name ]; + if ( !ret ) { + ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; + } + return ret; +} + +function setPositiveNumber( elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + ) ); + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + val = curCSS( elem, dimension, styles ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox; + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = valueIsBorderBox && + ( support.boxSizingReliable() || val === elem.style[ dimension ] ); + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + if ( val === "auto" || + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) { + + val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ]; + + // offsetWidth/offsetHeight provide border-box values + valueIsBorderBox = true; + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + if ( type === "number" ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra && boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ); + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && support.scrollboxSize() === styles.position ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && + ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || + jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = Date.now(); + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match == null ? null : match; + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + + +jQuery._evalUrl = function( url ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + "throws": true + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " diff --git a/static/plugs/ckeditor/plugins/scayt/CHANGELOG.md b/static/plugs/ckeditor/plugins/scayt/CHANGELOG.md new file mode 100644 index 000000000..05cf2ddc7 --- /dev/null +++ b/static/plugs/ckeditor/plugins/scayt/CHANGELOG.md @@ -0,0 +1,20 @@ +SCAYT plugin for CKEditor 4 Changelog +==================== +### CKEditor 4.5.6 + +New Features: +* CKEditor [language addon](http://ckeditor.com/addon/language) support +* CKEditor [placeholder addon](http://ckeditor.com/addon/placeholder) support +* Drag and Drop support +* *Experimental* GRAYT functionality http://www.webspellchecker.net/samples/scayt-ckeditor-plugin.html#25 + +Fixed issues: +* [#98](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/98) SCAYT Affects Dialog Double Click. Fixed in SCAYT Core. +* [#102](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/102) SCAYT Core performance enhancements +* [#104](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/104) SCAYT's spans leak into the clipboard and after pasting +* [#105](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/105) Javascript error fired in case of multiple instances of CKEditor in one page +* [#107](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/107) SCAYT should not check non-editable parts of content +* [#108](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/108) Latest SCAYT copies id of editor element to the iframe +* SCAYT stops working when CKEditor Undo plug-in not enabled +* Issue with pasting SCAYT markup in CKEditor +* [#32](https://github.com/WebSpellChecker/ckeditor-plugin-wsc/issues/32) SCAYT stops working after pressing Cancel button in WSC dialog diff --git a/static/plugs/ckeditor/plugins/scayt/LICENSE.md b/static/plugs/ckeditor/plugins/scayt/LICENSE.md new file mode 100644 index 000000000..610c80780 --- /dev/null +++ b/static/plugs/ckeditor/plugins/scayt/LICENSE.md @@ -0,0 +1,28 @@ +Software License Agreement +========================== + +**CKEditor SCAYT Plugin** +Copyright © 2012, [CKSource](http://cksource.com) - Frederico Knabben. All rights reserved. + +Licensed under the terms of any of the following licenses at your choice: + +* GNU General Public License Version 2 or later (the "GPL"): + http://www.gnu.org/licenses/gpl.html + +* GNU Lesser General Public License Version 2.1 or later (the "LGPL"): + http://www.gnu.org/licenses/lgpl.html + +* Mozilla Public License Version 1.1 or later (the "MPL"): + http://www.mozilla.org/MPL/MPL-1.1.html + +You are not required to, but if you want to explicitly declare the license you have chosen to be bound to when using, reproducing, modifying and distributing this software, just include a text file titled "legal.txt" in your version of this software, indicating your license choice. + +Sources of Intellectual Property Included in this plugin +-------------------------------------------------------- + +Where not otherwise indicated, all plugin content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, the plugin will incorporate work done by developers outside of CKSource with their express permission. + +Trademarks +---------- + +CKEditor is a trademark of CKSource - Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders. diff --git a/static/plugs/ckeditor/plugins/scayt/README.md b/static/plugs/ckeditor/plugins/scayt/README.md new file mode 100644 index 000000000..1b3de25d3 --- /dev/null +++ b/static/plugs/ckeditor/plugins/scayt/README.md @@ -0,0 +1,25 @@ +CKEditor SCAYT Plugin +===================== + +This plugin brings Spell Check As You Type (SCAYT) into up to CKEditor 4+. + +SCAYT is a "installation-less", using the web-services of [WebSpellChecker.net](http://www.webspellchecker.net/). It's an out of the box solution. + +Installation +------------ + +1. Clone/copy this repository contents in a new "plugins/scayt" folder in your CKEditor installation. +2. Enable the "scayt" plugin in the CKEditor configuration file (config.js): + + config.extraPlugins = 'scayt'; + +That's all. SCAYT will appear on the editor toolbar and will be ready to use. + +License +------- + +Licensed under the terms of any of the following licenses at your choice: [GPL](http://www.gnu.org/licenses/gpl.html), [LGPL](http://www.gnu.org/licenses/lgpl.html) and [MPL](http://www.mozilla.org/MPL/MPL-1.1.html). + +See LICENSE.md for more information. + +Developed in cooperation with [WebSpellChecker.net](http://www.webspellchecker.net/). diff --git a/static/plugs/ckeditor/plugins/scayt/dialogs/dialog.css b/static/plugs/ckeditor/plugins/scayt/dialogs/dialog.css new file mode 100644 index 000000000..427c4b42d --- /dev/null +++ b/static/plugs/ckeditor/plugins/scayt/dialogs/dialog.css @@ -0,0 +1,23 @@ +div.cke_dialog_ui_scaytItemList { + border: 1px solid #c9cccf; +} + +.cke_scaytItemList-child { + position: relative; + padding: 6px 30px 6px 5px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.cke_scaytItemList-child:hover { + background: #ebebeb; +} + +.cke_scaytItemList-child .cke_scaytItemList_remove { + position: absolute; + top: 0; + right: 5px; + width: 26px; + height: 26px; +} diff --git a/static/plugs/ckeditor/plugins/scayt/dialogs/options.js b/static/plugs/ckeditor/plugins/scayt/dialogs/options.js new file mode 100644 index 000000000..5f5b3953e --- /dev/null +++ b/static/plugs/ckeditor/plugins/scayt/dialogs/options.js @@ -0,0 +1,33 @@ +CKEDITOR.dialog.add("scaytDialog",function(c){var d=c.scayt,k='\x3cp\x3e\x3cimg src\x3d"'+d.getLogo()+'" /\x3e\x3c/p\x3e\x3cp\x3e'+d.getLocal("version")+d.getVersion()+'\x3c/p\x3e\x3cp\x3e\x3ca href\x3d"http://scayt.com/user_manual/scayt_plugin_for_ckeditor4_user_manual.pdf" target\x3d"_blank" style\x3d"text-decoration: underline; color: blue;"\x3e'+d.getLocal("btn_userManual")+"\x3c/a\x3e\x3c/p\x3e\x3cp\x3e"+d.getLocal("text_copyrights")+"\x3c/p\x3e",n=CKEDITOR.document,p={isChanged:function(){return null=== +this.newLang||this.currentLang===this.newLang?!1:!0},currentLang:d.getLang(),newLang:null,reset:function(){this.currentLang=d.getLang();this.newLang=null},id:"lang"},k=[{id:"options",label:d.getLocal("tab_options"),onShow:function(){},elements:[{type:"vbox",id:"scaytOptions",children:function(){var b=d.getApplicationConfig(),a=[],c={"ignore-all-caps-words":"label_allCaps","ignore-domain-names":"label_ignoreDomainNames","ignore-words-with-mixed-cases":"label_mixedCase","ignore-words-with-numbers":"label_mixedWithDigits"}, +f;for(f in b)b={type:"checkbox"},b.id=f,b.label=d.getLocal(c[f]),a.push(b);return a}(),onShow:function(){this.getChild();for(var b=c.scayt,a=0;ab[1]?c=1:a[1] div +{ + padding-bottom: 6px !important; +} + +.scayt-lang-list > div input +{ + margin-right: 4px; +} + +#scayt_about_ +{ + margin: 30px auto 0 auto; +} + +#scayt_about_ p +{ + text-align: center; + margin-bottom: 10px; +} + +.cke_dialog_contents_body div[name=dictionaries] .cke_dialog_ui_hbox_last > a.cke_dialog_ui_button +{ + margin-top: 0; +} diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_address.png b/static/plugs/ckeditor/plugins/showblocks/images/block_address.png new file mode 100644 index 000000000..5abdae127 Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_address.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_blockquote.png b/static/plugs/ckeditor/plugins/showblocks/images/block_blockquote.png new file mode 100644 index 000000000..a8f497353 Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_blockquote.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_div.png b/static/plugs/ckeditor/plugins/showblocks/images/block_div.png new file mode 100644 index 000000000..87b3c1714 Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_div.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_h1.png b/static/plugs/ckeditor/plugins/showblocks/images/block_h1.png new file mode 100644 index 000000000..3933325c0 Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_h1.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_h2.png b/static/plugs/ckeditor/plugins/showblocks/images/block_h2.png new file mode 100644 index 000000000..c99894c26 Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_h2.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_h3.png b/static/plugs/ckeditor/plugins/showblocks/images/block_h3.png new file mode 100644 index 000000000..cb73d679e Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_h3.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_h4.png b/static/plugs/ckeditor/plugins/showblocks/images/block_h4.png new file mode 100644 index 000000000..7af6bb498 Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_h4.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_h5.png b/static/plugs/ckeditor/plugins/showblocks/images/block_h5.png new file mode 100644 index 000000000..ce5bec16c Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_h5.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_h6.png b/static/plugs/ckeditor/plugins/showblocks/images/block_h6.png new file mode 100644 index 000000000..e67b98298 Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_h6.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_p.png b/static/plugs/ckeditor/plugins/showblocks/images/block_p.png new file mode 100644 index 000000000..63a582024 Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_p.png differ diff --git a/static/plugs/ckeditor/plugins/showblocks/images/block_pre.png b/static/plugs/ckeditor/plugins/showblocks/images/block_pre.png new file mode 100644 index 000000000..955a8689a Binary files /dev/null and b/static/plugs/ckeditor/plugins/showblocks/images/block_pre.png differ diff --git a/static/plugs/ckeditor/plugins/smiley/dialogs/smiley.js b/static/plugs/ckeditor/plugins/smiley/dialogs/smiley.js new file mode 100644 index 000000000..6f30b31bf --- /dev/null +++ b/static/plugs/ckeditor/plugins/smiley/dialogs/smiley.js @@ -0,0 +1,11 @@ +/* + Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +CKEDITOR.dialog.add("smiley",function(f){for(var e=f.config,a=f.lang.smiley,h=e.smiley_images,g=e.smiley_columns||8,k,m=function(l){var c=l.data.getTarget(),b=c.getName();if("a"==b)c=c.getChild(0);else if("img"!=b)return;var b=c.getAttribute("cke_src"),a=c.getAttribute("title"),c=f.document.createElement("img",{attributes:{src:b,"data-cke-saved-src":b,title:a,alt:a,width:c.$.width,height:c.$.height}});f.insertElement(c);k.hide();l.data.preventDefault()},q=CKEDITOR.tools.addFunction(function(a,c){a= +new CKEDITOR.dom.event(a);c=new CKEDITOR.dom.element(c);var b;b=a.getKeystroke();var d="rtl"==f.lang.dir;switch(b){case 38:if(b=c.getParent().getParent().getPrevious())b=b.getChild([c.getParent().getIndex(),0]),b.focus();a.preventDefault();break;case 40:(b=c.getParent().getParent().getNext())&&(b=b.getChild([c.getParent().getIndex(),0]))&&b.focus();a.preventDefault();break;case 32:m({data:a});a.preventDefault();break;case d?37:39:if(b=c.getParent().getNext())b=b.getChild(0),b.focus(),a.preventDefault(!0); +else if(b=c.getParent().getParent().getNext())(b=b.getChild([0,0]))&&b.focus(),a.preventDefault(!0);break;case d?39:37:if(b=c.getParent().getPrevious())b=b.getChild(0),b.focus(),a.preventDefault(!0);else if(b=c.getParent().getParent().getPrevious())b=b.getLast().getChild(0),b.focus(),a.preventDefault(!0)}}),d=CKEDITOR.tools.getNextId()+"_smiley_emtions_label",d=['\x3cdiv\x3e\x3cspan id\x3d"'+d+'" class\x3d"cke_voice_label"\x3e'+a.options+"\x3c/span\x3e",'\x3ctable role\x3d"listbox" aria-labelledby\x3d"'+ +d+'" style\x3d"width:100%;height:100%;border-collapse:separate;" cellspacing\x3d"2" cellpadding\x3d"2"',CKEDITOR.env.ie&&CKEDITOR.env.quirks?' style\x3d"position:absolute;"':"","\x3e\x3ctbody\x3e"],n=h.length,a=0;an&&(n=f)}return n}function r(a){return function(){var f=this.getValue(),f=!!(CKEDITOR.dialog.validate.integer()(f)&&0q.getSize("width")?"100%":500:0,getValue:u,validate:CKEDITOR.dialog.validate.cssLength(a.lang.common.invalidCssLength.replace("%1", +a.lang.common.width)),onChange:function(){var a=this.getDialog().getContentElement("advanced","advStyles");a&&a.updateStyle("width",this.getValue())},setup:function(a){a=a.getStyle("width");this.setValue(a)},commit:l}]},{type:"hbox",widths:["5em"],children:[{type:"text",id:"txtHeight",requiredContent:"table{height}",controlStyle:"width:5em",label:a.lang.common.height,title:a.lang.common.cssLengthTooltip,"default":"",getValue:u,validate:CKEDITOR.dialog.validate.cssLength(a.lang.common.invalidCssLength.replace("%1", +a.lang.common.height)),onChange:function(){var a=this.getDialog().getContentElement("advanced","advStyles");a&&a.updateStyle("height",this.getValue())},setup:function(a){(a=a.getStyle("height"))&&this.setValue(a)},commit:l}]},{type:"html",html:"\x26nbsp;"},{type:"text",id:"txtCellSpace",requiredContent:"table[cellspacing]",controlStyle:"width:3em",label:a.lang.table.cellSpace,"default":a.filter.check("table[cellspacing]")?1:0,validate:CKEDITOR.dialog.validate.number(a.lang.table.invalidCellSpacing), +setup:function(a){this.setValue(a.getAttribute("cellSpacing")||"")},commit:function(a,d){this.getValue()?d.setAttribute("cellSpacing",this.getValue()):d.removeAttribute("cellSpacing")}},{type:"text",id:"txtCellPad",requiredContent:"table[cellpadding]",controlStyle:"width:3em",label:a.lang.table.cellPad,"default":a.filter.check("table[cellpadding]")?1:0,validate:CKEDITOR.dialog.validate.number(a.lang.table.invalidCellPadding),setup:function(a){this.setValue(a.getAttribute("cellPadding")||"")},commit:function(a, +d){this.getValue()?d.setAttribute("cellPadding",this.getValue()):d.removeAttribute("cellPadding")}}]}]},{type:"html",align:"right",html:""},{type:"vbox",padding:0,children:[{type:"text",id:"txtCaption",requiredContent:"caption",label:a.lang.table.caption,setup:function(a){this.enable();a=a.getElementsByTag("caption");if(0 + + + + + + + +

    + diff --git a/static/plugs/ckeditor/plugins/wsc/dialogs/tmpFrameset.html b/static/plugs/ckeditor/plugins/wsc/dialogs/tmpFrameset.html new file mode 100644 index 000000000..eef6ee2de --- /dev/null +++ b/static/plugs/ckeditor/plugins/wsc/dialogs/tmpFrameset.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + diff --git a/static/plugs/ckeditor/plugins/wsc/dialogs/wsc.css b/static/plugs/ckeditor/plugins/wsc/dialogs/wsc.css new file mode 100644 index 000000000..1056b45f2 --- /dev/null +++ b/static/plugs/ckeditor/plugins/wsc/dialogs/wsc.css @@ -0,0 +1,82 @@ +/* +Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +html, body +{ + background-color: transparent; + margin: 0px; + padding: 0px; +} + +body +{ + padding: 10px; +} + +body, td, input, select, textarea +{ + font-size: 11px; + font-family: 'Microsoft Sans Serif' , Arial, Helvetica, Verdana; +} + +.midtext +{ + padding:0px; + margin:10px; +} + +.midtext p +{ + padding:0px; + margin:10px; +} + +.Button +{ + border: #737357 1px solid; + color: #3b3b1f; + background-color: #c7c78f; +} + +.PopupTabArea +{ + color: #737357; + background-color: #e3e3c7; +} + +.PopupTitleBorder +{ + border-bottom: #d5d59d 1px solid; +} +.PopupTabEmptyArea +{ + padding-left: 10px; + border-bottom: #d5d59d 1px solid; +} + +.PopupTab, .PopupTabSelected +{ + border-right: #d5d59d 1px solid; + border-top: #d5d59d 1px solid; + border-left: #d5d59d 1px solid; + padding: 3px 5px 3px 5px; + color: #737357; +} + +.PopupTab +{ + margin-top: 1px; + border-bottom: #d5d59d 1px solid; + cursor: pointer; +} + +.PopupTabSelected +{ + font-weight: bold; + cursor: default; + padding-top: 4px; + border-bottom: #f1f1e3 1px solid; + background-color: #f1f1e3; +} diff --git a/static/plugs/ckeditor/plugins/wsc/dialogs/wsc.js b/static/plugs/ckeditor/plugins/wsc/dialogs/wsc.js new file mode 100644 index 000000000..0ef46692f --- /dev/null +++ b/static/plugs/ckeditor/plugins/wsc/dialogs/wsc.js @@ -0,0 +1,91 @@ +/* + Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + For licensing, see LICENSE.html or http://ckeditor.com/license +*/ +(function(){function z(a){return a&&a.domId&&a.getInputElement().$?a.getInputElement():a&&a.$?a:!1}function I(a){if(!a)throw"Languages-by-groups list are required for construct selectbox";var c=[],d="",e;for(e in a)for(var g in a[e]){var f=a[e][g];"en_US"==f?d=f:c.push(f)}c.sort();d&&c.unshift(d);return{getCurrentLangGroup:function(c){a:{for(var e in a)for(var d in a[e])if(d.toUpperCase()===c.toUpperCase()){c=e;break a}c=""}return c},setLangList:function(){var c={},e;for(e in a)for(var d in a[e])c[a[e][d]]= +d;return c}()}}var f=function(){var a=function(a,b,e){e=e||{};var g=e.expires;if("number"==typeof g&&g){var f=new Date;f.setTime(f.getTime()+1E3*g);g=e.expires=f}g&&g.toUTCString&&(e.expires=g.toUTCString());b=encodeURIComponent(b);a=a+"\x3d"+b;for(var h in e)b=e[h],a+="; "+h,!0!==b&&(a+="\x3d"+b);document.cookie=a};return{postMessage:{init:function(a){window.addEventListener?window.addEventListener("message",a,!1):window.attachEvent("onmessage",a)},send:function(a){var b=Object.prototype.toString, +e=a.fn||null,g=a.id||"",f=a.target||window,h=a.message||{id:g};a.message&&"[object Object]"==b.call(a.message)&&(a.message.id?a.message.id:a.message.id=g,h=a.message);a=window.JSON.stringify(h,e);f.postMessage(a,"*")},unbindHandler:function(a){window.removeEventListener?window.removeEventListener("message",a,!1):window.detachEvent("onmessage",a)}},hash:{create:function(){},parse:function(){}},cookie:{set:a,get:function(a){return(a=document.cookie.match(new RegExp("(?:^|; )"+a.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, +"\\$1")+"\x3d([^;]*)")))?decodeURIComponent(a[1]):void 0},remove:function(c){a(c,"",{expires:-1})}},misc:{findFocusable:function(a){var b=null;a&&(b=a.find("a[href], area[href], input, select, textarea, button, *[tabindex], *[contenteditable]"));return b},isVisible:function(a){var b;(b=0===a.offsetWidth||0==a.offsetHeight)||(b="none"===(document.defaultView&&document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(a,null).display:a.currentStyle?a.currentStyle.display:a.style.display)); +return!b},hasClass:function(a,b){return!(!a.className||!a.className.match(new RegExp("(\\s|^)"+b+"(\\s|$)")))}}}}(),a=a||{};a.TextAreaNumber=null;a.load=!0;a.cmd={SpellTab:"spell",Thesaurus:"thes",GrammTab:"grammar"};a.dialog=null;a.optionNode=null;a.selectNode=null;a.grammerSuggest=null;a.textNode={};a.iframeMain=null;a.dataTemp="";a.div_overlay=null;a.textNodeInfo={};a.selectNode={};a.selectNodeResponce={};a.langList=null;a.langSelectbox=null;a.banner="";a.show_grammar=null;a.div_overlay_no_check= +null;a.targetFromFrame={};a.onLoadOverlay=null;a.LocalizationComing={};a.OverlayPlace=null;a.sessionid="";a.LocalizationButton={ChangeTo_button:{instance:null,text:"Change to",localizationID:"ChangeTo"},ChangeAll:{instance:null,text:"Change All"},IgnoreWord:{instance:null,text:"Ignore word"},IgnoreAllWords:{instance:null,text:"Ignore all words"},Options:{instance:null,text:"Options",optionsDialog:{instance:null}},AddWord:{instance:null,text:"Add word"},FinishChecking_button:{instance:null,text:"Finish Checking", +localizationID:"FinishChecking"},Option_button:{instance:null,text:"Options",localizationID:"Options"},FinishChecking_button_block:{instance:null,text:"Finish Checking",localizationID:"FinishChecking"}};a.LocalizationLabel={ChangeTo_label:{instance:null,text:"Change to",localizationID:"ChangeTo"},Suggestions:{instance:null,text:"Suggestions"},Categories:{instance:null,text:"Categories"},Synonyms:{instance:null,text:"Synonyms"}};var J=function(b){var c,d,e;for(e in b){if(c=a.dialog.getContentElement(a.dialog._.currentTabId, +e))c=c.getElement();else if(b[e].instance)c=b[e].instance.getElement().getFirst()||b[e].instance.getElement();else continue;d=b[e].localizationID||e;c.setText(a.LocalizationComing[d])}},K=function(b){var c,d,e;for(e in b)c=a.dialog.getContentElement(a.dialog._.currentTabId,e),c||(c=b[e].instance),c.setLabel&&(d=b[e].localizationID||e,c.setLabel(a.LocalizationComing[d]+":"))},t,A;a.framesetHtml=function(b){return"\x3ciframe id\x3d"+a.iframeNumber+"_"+b+' frameborder\x3d"0" allowtransparency\x3d"1" style\x3d"width:100%;border: 1px solid #AEB3B9;overflow: auto;background:#fff; border-radius: 3px;"\x3e\x3c/iframe\x3e'}; +a.setIframe=function(b,c){var d;d=a.framesetHtml(c);var e=a.iframeNumber+"_"+c;b.getElement().setHtml(d);d=document.getElementById(e);d=d.contentWindow?d.contentWindow:d.contentDocument.document?d.contentDocument.document:d.contentDocument;d.document.open();d.document.write('\x3c!DOCTYPE html\x3e\x3chtml\x3e\x3chead\x3e\x3cmeta charset\x3d"UTF-8"\x3e\x3ctitle\x3eiframe\x3c/title\x3e\x3cstyle\x3ehtml,body{margin: 0;height: 100%;font: 13px/1.555 "Trebuchet MS", sans-serif;}a{color: #888;font-weight: bold;text-decoration: none;border-bottom: 1px solid #888;}.main-box {color:#252525;padding: 3px 5px;text-align: justify;}.main-box p{margin: 0 0 14px;}.main-box .cerr{color: #f00000;border-bottom-color: #f00000;}\x3c/style\x3e\x3c/head\x3e\x3cbody\x3e\x3cdiv id\x3d"content" class\x3d"main-box"\x3e\x3c/div\x3e\x3ciframe src\x3d"" frameborder\x3d"0" id\x3d"spelltext" name\x3d"spelltext" style\x3d"display:none; width: 100%" \x3e\x3c/iframe\x3e\x3ciframe src\x3d"" frameborder\x3d"0" id\x3d"loadsuggestfirst" name\x3d"loadsuggestfirst" style\x3d"display:none; width: 100%" \x3e\x3c/iframe\x3e\x3ciframe src\x3d"" frameborder\x3d"0" id\x3d"loadspellsuggestall" name\x3d"loadspellsuggestall" style\x3d"display:none; width: 100%" \x3e\x3c/iframe\x3e\x3ciframe src\x3d"" frameborder\x3d"0" id\x3d"loadOptionsForm" name\x3d"loadOptionsForm" style\x3d"display:none; width: 100%" \x3e\x3c/iframe\x3e\x3cscript\x3e(function(window) {var ManagerPostMessage \x3d function() {var _init \x3d function(handler) {if (document.addEventListener) {window.addEventListener("message", handler, false);} else {window.attachEvent("onmessage", handler);};};var _sendCmd \x3d function(o) {var str,type \x3d Object.prototype.toString,fn \x3d o.fn || null,id \x3d o.id || "",target \x3d o.target || window,message \x3d o.message || { "id": id };if (o.message \x26\x26 type.call(o.message) \x3d\x3d "[object Object]") {(o.message["id"]) ? o.message["id"] : o.message["id"] \x3d id;message \x3d o.message;};str \x3d JSON.stringify(message, fn);target.postMessage(str, "*");};return {init: _init,send: _sendCmd};};var manageMessageTmp \x3d new ManagerPostMessage;var appString \x3d (function(){var spell \x3d parent.CKEDITOR.config.wsc.DefaultParams.scriptPath;var serverUrl \x3d parent.CKEDITOR.config.wsc.DefaultParams.serviceHost;return serverUrl + spell;})();function loadScript(src, callback) {var scriptTag \x3d document.createElement("script");scriptTag.type \x3d "text/javascript";callback ? callback : callback \x3d function() {};if(scriptTag.readyState) {scriptTag.onreadystatechange \x3d function() {if (scriptTag.readyState \x3d\x3d "loaded" ||scriptTag.readyState \x3d\x3d "complete") {scriptTag.onreadystatechange \x3d null;setTimeout(function(){scriptTag.parentNode.removeChild(scriptTag)},1);callback();}};}else{scriptTag.onload \x3d function() {setTimeout(function(){scriptTag.parentNode.removeChild(scriptTag)},1);callback();};};scriptTag.src \x3d src;document.getElementsByTagName("head")[0].appendChild(scriptTag);};window.onload \x3d function(){loadScript(appString, function(){manageMessageTmp.send({"id": "iframeOnload","target": window.parent});});}})(this);\x3c/script\x3e\x3c/body\x3e\x3c/html\x3e'); +d.document.close();a.div_overlay.setEnable()};a.setCurrentIframe=function(b){a.setIframe(a.dialog._.contents[b].Content,b)};a.setHeightBannerFrame=function(){var b=a.dialog.getContentElement("SpellTab","banner").getElement(),c=a.dialog.getContentElement("GrammTab","banner").getElement(),d=a.dialog.getContentElement("Thesaurus","banner").getElement();b.setStyle("height","90px");c.setStyle("height","90px");d.setStyle("height","90px")};a.setHeightFrame=function(){document.getElementById(a.iframeNumber+ +"_"+a.dialog._.currentTabId).style.height="240px"};a.sendData=function(b){var c=b._.currentTabId,d=b._.contents[c].Content,e,g;a.previousTab=c;a.setIframe(d,c);var f=function(h){c=b._.currentTabId;h=h||window.event;h.data.getTarget().is("a")&&c!==a.previousTab&&(a.previousTab=c,d=b._.contents[c].Content,e=a.iframeNumber+"_"+c,a.div_overlay.setEnable(),d.getElement().getChildCount()?E(a.targetFromFrame[e],a.cmd[c]):(a.setIframe(d,c),g=document.getElementById(e),a.targetFromFrame[e]=g.contentWindow))}; +b.parts.tabs.removeListener("click",f);b.parts.tabs.on("click",f)};a.buildSelectLang=function(a){var c=new CKEDITOR.dom.element("div"),d=new CKEDITOR.dom.element("select");a="wscLang"+a;c.addClass("cke_dialog_ui_input_select");c.setAttribute("role","presentation");c.setStyles({height:"auto",position:"absolute",right:"0",top:"-1px",width:"160px","white-space":"normal"});d.setAttribute("id",a);d.addClass("cke_dialog_ui_input_select");d.setStyles({width:"160px"});c.append(d);return c};a.buildOptionLang= +function(b,c){var d=document.getElementById("wscLang"+c),e=document.createDocumentFragment(),g,f,h=[];if(0===d.options.length){for(g in b)h.push([g,b[g]]);h.sort();for(var n=0;nl.width-D&&(d=l.width-D);if(fl.height-r&&(f=l.height-r);m.width=d+D;m.height=f+r;a._.fromResizeEvent=!1;a.resize(d,f);setTimeout(function(){a._.fromResizeEvent=!1;CKEDITOR.dialog.fire("resize",{dialog:a,width:d,height:f},b)},300)}a._.moved||(r=isNaN(c)&&isNaN(e)?0:1,isNaN(c)&&(c=(l.width-m.width)/2),0>c&&(c=0),c>l.width- +m.width&&(c=l.width-m.width),isNaN(e)&&(e=(l.height-m.height)/2),0>e&&(e=0),e>l.height-m.height&&(e=l.height-m.height),a.move(c,e,r))}function d(){b.wsc={};(function(a){var b={separator:"\x3c$\x3e",getDataType:function(a){return"undefined"===typeof a?"undefined":null===a?"null":Object.prototype.toString.call(a).slice(8,-1)},convertDataToString:function(a){return this.getDataType(a).toLowerCase()+this.separator+a},restoreDataFromString:function(a){var b=a,c;a=this.backCompatibility(a);if("string"=== +typeof a)switch(b=a.indexOf(this.separator),c=a.substring(0,b),b=a.substring(b+this.separator.length),c){case "boolean":b="true"===b;break;case "number":b=parseFloat(b);break;case "array":b=""===b?[]:b.split(",");break;case "null":b=null;break;case "undefined":b=void 0}return b},backCompatibility:function(a){var b=a,c;"string"===typeof a&&(c=a.indexOf(this.separator),0>c&&(b=parseFloat(a),isNaN(b)&&("["===a[0]&&"]"===a[a.length-1]?(a=a.replace("[",""),a=a.replace("]",""),b=""===a?[]:a.split(",")): +b="true"===a||"false"===a?"true"===a:a),b=this.convertDataToString(b)));return b}},c={get:function(a){return b.restoreDataFromString(window.localStorage.getItem(a))},set:function(a,c){var d=b.convertDataToString(c);window.localStorage.setItem(a,d)},del:function(a){window.localStorage.removeItem(a)},clear:function(){window.localStorage.clear()}},e={expiration:31622400,get:function(a){return b.restoreDataFromString(this.getCookie(a))},set:function(a,c){var d=b.convertDataToString(c);this.setCookie(a, +d,{expires:this.expiration})},del:function(a){this.deleteCookie(a)},getCookie:function(a){return(a=document.cookie.match(new RegExp("(?:^|; )"+a.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g,"\\$1")+"\x3d([^;]*)")))?decodeURIComponent(a[1]):void 0},setCookie:function(a,b,c){c=c||{};var d=c.expires;if("number"===typeof d&&d){var e=new Date;e.setTime(e.getTime()+1E3*d);d=c.expires=e}d&&d.toUTCString&&(c.expires=d.toUTCString());b=encodeURIComponent(b);a=a+"\x3d"+b;for(var f in c)b=c[f],a+="; "+f,!0!==b&&(a+= +"\x3d"+b);document.cookie=a},deleteCookie:function(a){this.setCookie(a,null,{expires:-1})},clear:function(){for(var a=document.cookie.split(";"),b=0;b .cke_dialog_ui_button:first-child +{ + margin-top: 4px; +} + +div[name=SpellTab] .wsc-spelltab-bottom .cke_dialog_ui_hbox_first .cke_dialog_ui_select > label +{ + margin-left: 0; +} + +div[name=SpellTab] .wsc-spelltab-bottom .cke_dialog_ui_hbox_first .cke_dialog_ui_select div.cke_dialog_ui_input_select +{ + width: 140px !important; +} + +div[name=SpellTab] .wsc-spelltab-bottom .cke_dialog_ui_hbox_first .cke_dialog_ui_select select.cke_dialog_ui_input_select, +div[name=Thesaurus] div.cke_dialog_ui_input_select select.cke_dialog_ui_input_select +{ + margin-top: 1px; +} + +div[name=SpellTab] .wsc-spelltab-bottom .cke_dialog_ui_hbox_first .cke_dialog_ui_select select.cke_dialog_ui_input_select:focus, +div[name=Thesaurus] div.cke_dialog_ui_input_select select.cke_dialog_ui_input_select:focus +{ + margin-top: 0; +} + +div[name=GrammTab] .cke_dialog_ui_vbox tbody > tr:first-child .cke_dialog_ui_button, +div[name=Thesaurus] .cke_dialog_ui_vbox tbody > tr:first-child .cke_dialog_ui_button +{ + margin-top: 4px !important; +} + +div[name=Thesaurus] div.cke_dialog_ui_input_select +{ + width: 180px !important; +} diff --git a/static/plugs/ckeditor/skins/moono-lisa/dialog.css b/static/plugs/ckeditor/skins/moono-lisa/dialog.css new file mode 100644 index 000000000..1b0d22f15 --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/dialog.css @@ -0,0 +1,5 @@ +/* +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +.cke_dialog{visibility:visible}.cke_dialog_body{z-index:1;background:#fff}.cke_dialog strong{font-weight:bold}.cke_dialog_title{font-weight:bold;font-size:12px;cursor:move;position:relative;color:#484848;border-bottom:1px solid #d1d1d1;padding:12px 19px 12px 12px;background:#f8f8f8;letter-spacing:.3px}.cke_dialog_spinner{border-radius:50%;width:12px;height:12px;overflow:hidden;text-indent:-9999em;border:2px solid rgba(102,102,102,0.2);border-left-color:rgba(102,102,102,1);-webkit-animation:dialog_spinner 1s infinite linear;animation:dialog_spinner 1s infinite linear}.cke_browser_ie8 .cke_dialog_spinner,.cke_browser_ie9 .cke_dialog_spinner{background:url(images/spinner.gif) center top no-repeat;width:16px;height:16px;border:0}@-webkit-keyframes dialog_spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes dialog_spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.cke_dialog_contents{background-color:#fff;overflow:auto;padding:15px 10px 5px 10px;margin-top:43px;border-top:1px solid #d1d1d1}.cke_dialog_contents_body{overflow:auto;padding:9px 10px 5px 10px;margin-top:22px}.cke_dialog_footer{text-align:right;position:relative;border-top:1px solid #d1d1d1;background:#f8f8f8}.cke_rtl .cke_dialog_footer{text-align:left}.cke_hc .cke_dialog_footer{outline:0;border-top:1px solid #fff}.cke_dialog .cke_resizer{margin-top:22px}.cke_dialog .cke_resizer_rtl{margin-left:5px}.cke_dialog .cke_resizer_ltr{margin-right:5px}.cke_dialog_tabs{height:33px;display:inline-block;margin:9px 0 0;position:absolute;z-index:2;left:11px}.cke_rtl .cke_dialog_tabs{left:auto;right:11px}a.cke_dialog_tab{height:25px;padding:4px 8px;display:inline-block;cursor:pointer;line-height:26px;outline:0;color:#484848;border:1px solid #d1d1d1;border-radius:3px 3px 0 0;background:#f8f8f8;min-width:90px;text-align:center;margin-left:-1px;letter-spacing:.3px}a.cke_dialog_tab:hover{background-color:#fff}a.cke_dialog_tab:focus{border:2px solid #139ff7;border-bottom-color:#d1d1d1;padding:3px 7px;position:relative;z-index:1}a.cke_dialog_tab_selected{background:#fff;border-bottom-color:#fff;cursor:default;filter:none}a.cke_dialog_tab_selected:hover,a.cke_dialog_tab_selected:focus{border-bottom-color:#fff}.cke_hc a.cke_dialog_tab:hover,.cke_hc a.cke_dialog_tab:focus,.cke_hc a.cke_dialog_tab_selected{border:3px solid;padding:2px 6px}a.cke_dialog_tab_disabled{color:#bababa;cursor:default}.cke_single_page .cke_dialog_tabs{display:none}.cke_single_page .cke_dialog_contents{padding-top:5px;margin-top:0;border-top:0}a.cke_dialog_close_button{background-image:url(images/close.png);background-repeat:no-repeat;background-position:50%;position:absolute;cursor:pointer;text-align:center;height:16px;width:16px;top:11px;z-index:5;opacity:.7;filter:alpha(opacity = 70)}.cke_rtl .cke_dialog_close_button{left:12px}.cke_ltr .cke_dialog_close_button{right:12px}.cke_hc a.cke_dialog_close_button{background-image:none}.cke_hidpi a.cke_dialog_close_button{background-image:url(images/hidpi/close.png);background-size:16px}a.cke_dialog_close_button:hover{opacity:1;filter:alpha(opacity = 100)}a.cke_dialog_close_button span{display:none}.cke_hc a.cke_dialog_close_button span{display:inline;cursor:pointer;font-weight:bold;position:relative;top:3px}div.cke_disabled .cke_dialog_ui_labeled_content div *{background-color:#ddd;cursor:default}.cke_dialog_ui_vbox table,.cke_dialog_ui_hbox table{margin:auto}.cke_dialog_ui_vbox_child{padding:5px 0}.cke_dialog_ui_hbox{width:100%;margin-top:12px}.cke_dialog_ui_hbox_first,.cke_dialog_ui_hbox_child,.cke_dialog_ui_hbox_last{vertical-align:top}.cke_ltr .cke_dialog_ui_hbox_first,.cke_ltr .cke_dialog_ui_hbox_child{padding-right:10px}.cke_rtl .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_ui_hbox_child{padding-left:10px}.cke_ltr .cke_dialog_footer_buttons .cke_dialog_ui_hbox_first,.cke_ltr .cke_dialog_footer_buttons .cke_dialog_ui_hbox_child{padding-right:5px}.cke_rtl .cke_dialog_footer_buttons .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_footer_buttons .cke_dialog_ui_hbox_child{padding-left:5px;padding-right:0}.cke_hc div.cke_dialog_ui_input_text,.cke_hc div.cke_dialog_ui_input_password,.cke_hc div.cke_dialog_ui_input_textarea,.cke_hc div.cke_dialog_ui_input_select,.cke_hc div.cke_dialog_ui_input_file{border:1px solid}textarea.cke_dialog_ui_input_textarea{overflow:auto;resize:none}input.cke_dialog_ui_input_text,input.cke_dialog_ui_input_password,textarea.cke_dialog_ui_input_textarea{background-color:#fff;border:1px solid #bcbcbc;padding:4px 6px;outline:0;width:100%;*width:95%;box-sizing:border-box;border-radius:2px;min-height:28px;margin-left:1px}input.cke_dialog_ui_input_text:hover,input.cke_dialog_ui_input_password:hover,textarea.cke_dialog_ui_input_textarea:hover{border:1px solid #aeb3b9}input.cke_dialog_ui_input_text:focus,input.cke_dialog_ui_input_password:focus,textarea.cke_dialog_ui_input_textarea:focus,select.cke_dialog_ui_input_select:focus{outline:0;border:2px solid #139ff7}input.cke_dialog_ui_input_text:focus{padding-left:5px}textarea.cke_dialog_ui_input_textarea:focus{padding:3px 5px}select.cke_dialog_ui_input_select:focus{margin:0;width:100%!important}input.cke_dialog_ui_checkbox_input,input.cke_dialog_ui_radio_input{margin-left:1px;margin-right:2px}input.cke_dialog_ui_checkbox_input:focus,input.cke_dialog_ui_checkbox_input:active,input.cke_dialog_ui_radio_input:focus,input.cke_dialog_ui_radio_input:active{border:0;outline:2px solid #139ff7}a.cke_dialog_ui_button{display:inline-block;*display:inline;*zoom:1;padding:4px 1px;margin:0;text-align:center;color:#484848;vertical-align:middle;cursor:pointer;border:1px solid #bcbcbc;border-radius:2px;background:#f8f8f8;letter-spacing:.3px;line-height:18px;box-sizing:border-box}.cke_hc a.cke_dialog_ui_button{border-width:3px}span.cke_dialog_ui_button{padding:0 10px;cursor:pointer}a.cke_dialog_ui_button:hover{background:#fff}a.cke_dialog_ui_button:focus,a.cke_dialog_ui_button:active{border:2px solid #139ff7;outline:0;padding:3px 0}.cke_hc a.cke_dialog_ui_button:hover,.cke_hc a.cke_dialog_ui_button:focus,.cke_hc a.cke_dialog_ui_button:active{border:3px solid}.cke_dialog_footer_buttons a.cke_dialog_ui_button span{color:inherit;font-size:12px;font-weight:bold;padding:0 12px}a.cke_dialog_ui_button_ok{color:#fff;background:#09863e;border:1px solid #09863e}.cke_hc a.cke_dialog_ui_button{border:3px solid #bcbcbc}a.cke_dialog_ui_button_ok:hover{background:#53aa78;border-color:#53aa78}a.cke_dialog_ui_button_ok:focus{box-shadow:inset 0 0 0 2px #FFF}a.cke_dialog_ui_button_ok:focus,a.cke_dialog_ui_button_ok:active{border-color:#139ff7}.cke_hc a.cke_dialog_ui_button_ok:hover,.cke_hc a.cke_dialog_ui_button_ok:focus,.cke_hc a.cke_dialog_ui_button_ok:active{border-color:#484848}a.cke_dialog_ui_button_ok.cke_disabled{background:#d1d1d1;border-color:#d1d1d1;cursor:default}a.cke_dialog_ui_button_ok.cke_disabled span{cursor:default}.cke_dialog_footer_buttons{display:inline-table;margin:5px;width:auto;position:relative;vertical-align:middle}div.cke_dialog_ui_input_select{display:table}select.cke_dialog_ui_input_select{height:28px;line-height:28px;background-color:#fff;border:1px solid #bcbcbc;padding:3px 3px 3px 6px;outline:0;border-radius:2px;margin:0 1px;box-sizing:border-box;width:calc(100% - 2px)!important}.cke_dialog_ui_input_file{width:100%;height:25px}.cke_hc .cke_dialog_ui_labeled_content input:focus,.cke_hc .cke_dialog_ui_labeled_content select:focus,.cke_hc .cke_dialog_ui_labeled_content textarea:focus{outline:1px dotted}.cke_dialog_ui_labeled_label{margin-left:1px}.cke_dialog .cke_dark_background{background-color:transparent}.cke_dialog .cke_light_background{background-color:#ebebeb}.cke_dialog .cke_centered{text-align:center}.cke_dialog a.cke_btn_reset{float:right;background:url(images/refresh.png) top left no-repeat;width:16px;height:16px;border:1px none;font-size:1px}.cke_hidpi .cke_dialog a.cke_btn_reset{background-size:16px;background-image:url(images/hidpi/refresh.png)}.cke_rtl .cke_dialog a.cke_btn_reset{float:left}.cke_dialog a.cke_btn_locked,.cke_dialog a.cke_btn_unlocked{float:left;width:16px;height:16px;background-repeat:no-repeat;border:none 1px;font-size:1px}.cke_dialog a.cke_btn_locked,.cke_dialog a.cke_btn_unlocked,.cke_dialog a.cke_btn_reset{margin:2px}.cke_dialog a.cke_btn_locked{background-image:url(images/lock.png)}.cke_dialog a.cke_btn_unlocked{background-image:url(images/lock-open.png)}.cke_rtl .cke_dialog a.cke_btn_locked,.cke_rtl .cke_dialog a.cke_btn_unlocked{float:right}.cke_hidpi .cke_dialog a.cke_btn_unlocked,.cke_hidpi .cke_dialog a.cke_btn_locked{background-size:16px}.cke_hidpi .cke_dialog a.cke_btn_locked{background-image:url(images/hidpi/lock.png)}.cke_hidpi .cke_dialog a.cke_btn_unlocked{background-image:url(images/hidpi/lock-open.png)}.cke_dialog a.cke_btn_locked .cke_icon{display:none}.cke_dialog a.cke_btn_over,.cke_dialog a.cke_btn_locked:hover,.cke_dialog a.cke_btn_locked:focus,.cke_dialog a.cke_btn_locked:active,.cke_dialog a.cke_btn_unlocked:hover,.cke_dialog a.cke_btn_unlocked:focus,.cke_dialog a.cke_btn_unlocked:active,.cke_dialog a.cke_btn_reset:hover,.cke_dialog a.cke_btn_reset:focus,.cke_dialog a.cke_btn_reset:active{cursor:pointer;outline:0;margin:0;border:2px solid #139ff7}.cke_dialog fieldset{border:1px solid #bcbcbc}.cke_dialog fieldset legend{padding:0 6px}.cke_dialog_ui_checkbox,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox{display:inline-block}.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox{padding-top:5px}.cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input,.cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input+label,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input+label{vertical-align:middle}.cke_dialog .ImagePreviewBox{border:1px ridge #bcbcbc;overflow:scroll;height:200px;width:300px;padding:2px;background-color:white}.cke_dialog .ImagePreviewBox table td{white-space:normal}.cke_dialog .ImagePreviewLoader{position:absolute;white-space:normal;overflow:hidden;height:160px;width:230px;margin:2px;padding:2px;opacity:.9;filter:alpha(opacity = 90);background-color:#e4e4e4}.cke_dialog .FlashPreviewBox{white-space:normal;border:1px solid #bcbcbc;overflow:auto;height:160px;width:390px;padding:2px;background-color:white}.cke_dialog .cke_pastetext{width:346px;height:170px}.cke_dialog .cke_pastetext textarea{width:340px;height:170px;resize:none}.cke_dialog iframe.cke_pasteframe{width:346px;height:130px;background-color:white;border:1px solid #aeb3b9;border-radius:3px}.cke_dialog .cke_hand{cursor:pointer}.cke_disabled{color:#a0a0a0}.cke_dialog_body .cke_label{display:none}.cke_dialog_body label{display:inline;cursor:default;letter-spacing:.3px}.cke_dialog_body label+.cke_dialog_ui_labeled_content{margin-top:2px}.cke_dialog_contents_body .cke_dialog_ui_text,.cke_dialog_contents_body .cke_dialog_ui_select,.cke_dialog_contents_body .cke_dialog_ui_hbox_last>a.cke_dialog_ui_button{margin-top:4px}a.cke_smile{overflow:hidden;display:block;text-align:center;padding:.3em 0}a.cke_smile img{vertical-align:middle}a.cke_specialchar{cursor:inherit;display:block;height:1.25em;padding:.2em .3em;text-align:center}a.cke_smile,a.cke_specialchar{border:2px solid transparent}a.cke_smile:hover,a.cke_smile:focus,a.cke_smile:active,a.cke_specialchar:hover,a.cke_specialchar:focus,a.cke_specialchar:active{background:#fff;outline:0}a.cke_smile:hover,a.cke_specialchar:hover{border-color:#888}a.cke_smile:focus,a.cke_smile:active,a.cke_specialchar:focus,a.cke_specialchar:active{border-color:#139ff7}.cke_dialog_contents a.colorChooser{display:block;margin-top:6px;margin-left:10px;width:80px}.cke_rtl .cke_dialog_contents a.colorChooser{margin-right:10px}.cke_iframe_shim{display:block;position:absolute;top:0;left:0;z-index:-1;filter:alpha(opacity = 0);width:100%;height:100%}.cke_dialog_contents_body .cke_accessibility_legend{margin:2px 7px 2px 2px}.cke_dialog_contents_body .cke_accessibility_legend:focus,.cke_dialog_contents_body .cke_accessibility_legend:active{outline:0;border:2px solid #139ff7;margin:0 5px 0 0}.cke_dialog_contents_body input[type=file]:focus,.cke_dialog_contents_body input[type=file]:active{border:2px solid #139ff7}.cke_dialog_find_fieldset{margin-top:10px!important}.cke_dialog_image_ratiolock{margin-top:52px!important}.cke_dialog_forms_select_order label.cke_dialog_ui_labeled_label{margin-left:0}.cke_dialog_forms_select_order div.cke_dialog_ui_input_select{width:100%}.cke_dialog_forms_select_order_txtsize .cke_dialog_ui_hbox_last{padding-top:4px}.cke_dialog_image_url .cke_dialog_ui_hbox_last,.cke_dialog_flash_url .cke_dialog_ui_hbox_last{vertical-align:bottom}a.cke_dialog_ui_button.cke_dialog_image_browse{margin-top:10px}.cke_dialog_contents_body .cke_tpl_list{border:#bcbcbc 1px solid;margin:1px}.cke_dialog_contents_body .cke_tpl_list:focus,.cke_dialog_contents_body .cke_tpl_list:active{outline:0;margin:0;border:2px solid #139ff7}.cke_dialog_contents_body .cke_tpl_list a:focus,.cke_dialog_contents_body .cke_tpl_list a:active{outline:0}.cke_dialog_contents_body .cke_tpl_list a:focus .cke_tpl_item,.cke_dialog_contents_body .cke_tpl_list a:active .cke_tpl_item{border:2px solid #139ff7;padding:6px} \ No newline at end of file diff --git a/static/plugs/ckeditor/skins/moono-lisa/dialog_ie.css b/static/plugs/ckeditor/skins/moono-lisa/dialog_ie.css new file mode 100644 index 000000000..ab9ede663 --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/dialog_ie.css @@ -0,0 +1,5 @@ +/* +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +.cke_dialog{visibility:visible}.cke_dialog_body{z-index:1;background:#fff}.cke_dialog strong{font-weight:bold}.cke_dialog_title{font-weight:bold;font-size:12px;cursor:move;position:relative;color:#484848;border-bottom:1px solid #d1d1d1;padding:12px 19px 12px 12px;background:#f8f8f8;letter-spacing:.3px}.cke_dialog_spinner{border-radius:50%;width:12px;height:12px;overflow:hidden;text-indent:-9999em;border:2px solid rgba(102,102,102,0.2);border-left-color:rgba(102,102,102,1);-webkit-animation:dialog_spinner 1s infinite linear;animation:dialog_spinner 1s infinite linear}.cke_browser_ie8 .cke_dialog_spinner,.cke_browser_ie9 .cke_dialog_spinner{background:url(images/spinner.gif) center top no-repeat;width:16px;height:16px;border:0}@-webkit-keyframes dialog_spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes dialog_spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.cke_dialog_contents{background-color:#fff;overflow:auto;padding:15px 10px 5px 10px;margin-top:43px;border-top:1px solid #d1d1d1}.cke_dialog_contents_body{overflow:auto;padding:9px 10px 5px 10px;margin-top:22px}.cke_dialog_footer{text-align:right;position:relative;border-top:1px solid #d1d1d1;background:#f8f8f8}.cke_rtl .cke_dialog_footer{text-align:left}.cke_hc .cke_dialog_footer{outline:0;border-top:1px solid #fff}.cke_dialog .cke_resizer{margin-top:22px}.cke_dialog .cke_resizer_rtl{margin-left:5px}.cke_dialog .cke_resizer_ltr{margin-right:5px}.cke_dialog_tabs{height:33px;display:inline-block;margin:9px 0 0;position:absolute;z-index:2;left:11px}.cke_rtl .cke_dialog_tabs{left:auto;right:11px}a.cke_dialog_tab{height:25px;padding:4px 8px;display:inline-block;cursor:pointer;line-height:26px;outline:0;color:#484848;border:1px solid #d1d1d1;border-radius:3px 3px 0 0;background:#f8f8f8;min-width:90px;text-align:center;margin-left:-1px;letter-spacing:.3px}a.cke_dialog_tab:hover{background-color:#fff}a.cke_dialog_tab:focus{border:2px solid #139ff7;border-bottom-color:#d1d1d1;padding:3px 7px;position:relative;z-index:1}a.cke_dialog_tab_selected{background:#fff;border-bottom-color:#fff;cursor:default;filter:none}a.cke_dialog_tab_selected:hover,a.cke_dialog_tab_selected:focus{border-bottom-color:#fff}.cke_hc a.cke_dialog_tab:hover,.cke_hc a.cke_dialog_tab:focus,.cke_hc a.cke_dialog_tab_selected{border:3px solid;padding:2px 6px}a.cke_dialog_tab_disabled{color:#bababa;cursor:default}.cke_single_page .cke_dialog_tabs{display:none}.cke_single_page .cke_dialog_contents{padding-top:5px;margin-top:0;border-top:0}a.cke_dialog_close_button{background-image:url(images/close.png);background-repeat:no-repeat;background-position:50%;position:absolute;cursor:pointer;text-align:center;height:16px;width:16px;top:11px;z-index:5;opacity:.7;filter:alpha(opacity = 70)}.cke_rtl .cke_dialog_close_button{left:12px}.cke_ltr .cke_dialog_close_button{right:12px}.cke_hc a.cke_dialog_close_button{background-image:none}.cke_hidpi a.cke_dialog_close_button{background-image:url(images/hidpi/close.png);background-size:16px}a.cke_dialog_close_button:hover{opacity:1;filter:alpha(opacity = 100)}a.cke_dialog_close_button span{display:none}.cke_hc a.cke_dialog_close_button span{display:inline;cursor:pointer;font-weight:bold;position:relative;top:3px}div.cke_disabled .cke_dialog_ui_labeled_content div *{background-color:#ddd;cursor:default}.cke_dialog_ui_vbox table,.cke_dialog_ui_hbox table{margin:auto}.cke_dialog_ui_vbox_child{padding:5px 0}.cke_dialog_ui_hbox{width:100%;margin-top:12px}.cke_dialog_ui_hbox_first,.cke_dialog_ui_hbox_child,.cke_dialog_ui_hbox_last{vertical-align:top}.cke_ltr .cke_dialog_ui_hbox_first,.cke_ltr .cke_dialog_ui_hbox_child{padding-right:10px}.cke_rtl .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_ui_hbox_child{padding-left:10px}.cke_ltr .cke_dialog_footer_buttons .cke_dialog_ui_hbox_first,.cke_ltr .cke_dialog_footer_buttons .cke_dialog_ui_hbox_child{padding-right:5px}.cke_rtl .cke_dialog_footer_buttons .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_footer_buttons .cke_dialog_ui_hbox_child{padding-left:5px;padding-right:0}.cke_hc div.cke_dialog_ui_input_text,.cke_hc div.cke_dialog_ui_input_password,.cke_hc div.cke_dialog_ui_input_textarea,.cke_hc div.cke_dialog_ui_input_select,.cke_hc div.cke_dialog_ui_input_file{border:1px solid}textarea.cke_dialog_ui_input_textarea{overflow:auto;resize:none}input.cke_dialog_ui_input_text,input.cke_dialog_ui_input_password,textarea.cke_dialog_ui_input_textarea{background-color:#fff;border:1px solid #bcbcbc;padding:4px 6px;outline:0;width:100%;*width:95%;box-sizing:border-box;border-radius:2px;min-height:28px;margin-left:1px}input.cke_dialog_ui_input_text:hover,input.cke_dialog_ui_input_password:hover,textarea.cke_dialog_ui_input_textarea:hover{border:1px solid #aeb3b9}input.cke_dialog_ui_input_text:focus,input.cke_dialog_ui_input_password:focus,textarea.cke_dialog_ui_input_textarea:focus,select.cke_dialog_ui_input_select:focus{outline:0;border:2px solid #139ff7}input.cke_dialog_ui_input_text:focus{padding-left:5px}textarea.cke_dialog_ui_input_textarea:focus{padding:3px 5px}select.cke_dialog_ui_input_select:focus{margin:0;width:100%!important}input.cke_dialog_ui_checkbox_input,input.cke_dialog_ui_radio_input{margin-left:1px;margin-right:2px}input.cke_dialog_ui_checkbox_input:focus,input.cke_dialog_ui_checkbox_input:active,input.cke_dialog_ui_radio_input:focus,input.cke_dialog_ui_radio_input:active{border:0;outline:2px solid #139ff7}a.cke_dialog_ui_button{display:inline-block;*display:inline;*zoom:1;padding:4px 1px;margin:0;text-align:center;color:#484848;vertical-align:middle;cursor:pointer;border:1px solid #bcbcbc;border-radius:2px;background:#f8f8f8;letter-spacing:.3px;line-height:18px;box-sizing:border-box}.cke_hc a.cke_dialog_ui_button{border-width:3px}span.cke_dialog_ui_button{padding:0 10px;cursor:pointer}a.cke_dialog_ui_button:hover{background:#fff}a.cke_dialog_ui_button:focus,a.cke_dialog_ui_button:active{border:2px solid #139ff7;outline:0;padding:3px 0}.cke_hc a.cke_dialog_ui_button:hover,.cke_hc a.cke_dialog_ui_button:focus,.cke_hc a.cke_dialog_ui_button:active{border:3px solid}.cke_dialog_footer_buttons a.cke_dialog_ui_button span{color:inherit;font-size:12px;font-weight:bold;padding:0 12px}a.cke_dialog_ui_button_ok{color:#fff;background:#09863e;border:1px solid #09863e}.cke_hc a.cke_dialog_ui_button{border:3px solid #bcbcbc}a.cke_dialog_ui_button_ok:hover{background:#53aa78;border-color:#53aa78}a.cke_dialog_ui_button_ok:focus{box-shadow:inset 0 0 0 2px #FFF}a.cke_dialog_ui_button_ok:focus,a.cke_dialog_ui_button_ok:active{border-color:#139ff7}.cke_hc a.cke_dialog_ui_button_ok:hover,.cke_hc a.cke_dialog_ui_button_ok:focus,.cke_hc a.cke_dialog_ui_button_ok:active{border-color:#484848}a.cke_dialog_ui_button_ok.cke_disabled{background:#d1d1d1;border-color:#d1d1d1;cursor:default}a.cke_dialog_ui_button_ok.cke_disabled span{cursor:default}.cke_dialog_footer_buttons{display:inline-table;margin:5px;width:auto;position:relative;vertical-align:middle}div.cke_dialog_ui_input_select{display:table}select.cke_dialog_ui_input_select{height:28px;line-height:28px;background-color:#fff;border:1px solid #bcbcbc;padding:3px 3px 3px 6px;outline:0;border-radius:2px;margin:0 1px;box-sizing:border-box;width:calc(100% - 2px)!important}.cke_dialog_ui_input_file{width:100%;height:25px}.cke_hc .cke_dialog_ui_labeled_content input:focus,.cke_hc .cke_dialog_ui_labeled_content select:focus,.cke_hc .cke_dialog_ui_labeled_content textarea:focus{outline:1px dotted}.cke_dialog_ui_labeled_label{margin-left:1px}.cke_dialog .cke_dark_background{background-color:transparent}.cke_dialog .cke_light_background{background-color:#ebebeb}.cke_dialog .cke_centered{text-align:center}.cke_dialog a.cke_btn_reset{float:right;background:url(images/refresh.png) top left no-repeat;width:16px;height:16px;border:1px none;font-size:1px}.cke_hidpi .cke_dialog a.cke_btn_reset{background-size:16px;background-image:url(images/hidpi/refresh.png)}.cke_rtl .cke_dialog a.cke_btn_reset{float:left}.cke_dialog a.cke_btn_locked,.cke_dialog a.cke_btn_unlocked{float:left;width:16px;height:16px;background-repeat:no-repeat;border:none 1px;font-size:1px}.cke_dialog a.cke_btn_locked,.cke_dialog a.cke_btn_unlocked,.cke_dialog a.cke_btn_reset{margin:2px}.cke_dialog a.cke_btn_locked{background-image:url(images/lock.png)}.cke_dialog a.cke_btn_unlocked{background-image:url(images/lock-open.png)}.cke_rtl .cke_dialog a.cke_btn_locked,.cke_rtl .cke_dialog a.cke_btn_unlocked{float:right}.cke_hidpi .cke_dialog a.cke_btn_unlocked,.cke_hidpi .cke_dialog a.cke_btn_locked{background-size:16px}.cke_hidpi .cke_dialog a.cke_btn_locked{background-image:url(images/hidpi/lock.png)}.cke_hidpi .cke_dialog a.cke_btn_unlocked{background-image:url(images/hidpi/lock-open.png)}.cke_dialog a.cke_btn_locked .cke_icon{display:none}.cke_dialog a.cke_btn_over,.cke_dialog a.cke_btn_locked:hover,.cke_dialog a.cke_btn_locked:focus,.cke_dialog a.cke_btn_locked:active,.cke_dialog a.cke_btn_unlocked:hover,.cke_dialog a.cke_btn_unlocked:focus,.cke_dialog a.cke_btn_unlocked:active,.cke_dialog a.cke_btn_reset:hover,.cke_dialog a.cke_btn_reset:focus,.cke_dialog a.cke_btn_reset:active{cursor:pointer;outline:0;margin:0;border:2px solid #139ff7}.cke_dialog fieldset{border:1px solid #bcbcbc}.cke_dialog fieldset legend{padding:0 6px}.cke_dialog_ui_checkbox,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox{display:inline-block}.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox{padding-top:5px}.cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input,.cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input+label,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input+label{vertical-align:middle}.cke_dialog .ImagePreviewBox{border:1px ridge #bcbcbc;overflow:scroll;height:200px;width:300px;padding:2px;background-color:white}.cke_dialog .ImagePreviewBox table td{white-space:normal}.cke_dialog .ImagePreviewLoader{position:absolute;white-space:normal;overflow:hidden;height:160px;width:230px;margin:2px;padding:2px;opacity:.9;filter:alpha(opacity = 90);background-color:#e4e4e4}.cke_dialog .FlashPreviewBox{white-space:normal;border:1px solid #bcbcbc;overflow:auto;height:160px;width:390px;padding:2px;background-color:white}.cke_dialog .cke_pastetext{width:346px;height:170px}.cke_dialog .cke_pastetext textarea{width:340px;height:170px;resize:none}.cke_dialog iframe.cke_pasteframe{width:346px;height:130px;background-color:white;border:1px solid #aeb3b9;border-radius:3px}.cke_dialog .cke_hand{cursor:pointer}.cke_disabled{color:#a0a0a0}.cke_dialog_body .cke_label{display:none}.cke_dialog_body label{display:inline;cursor:default;letter-spacing:.3px}.cke_dialog_body label+.cke_dialog_ui_labeled_content{margin-top:2px}.cke_dialog_contents_body .cke_dialog_ui_text,.cke_dialog_contents_body .cke_dialog_ui_select,.cke_dialog_contents_body .cke_dialog_ui_hbox_last>a.cke_dialog_ui_button{margin-top:4px}a.cke_smile{overflow:hidden;display:block;text-align:center;padding:.3em 0}a.cke_smile img{vertical-align:middle}a.cke_specialchar{cursor:inherit;display:block;height:1.25em;padding:.2em .3em;text-align:center}a.cke_smile,a.cke_specialchar{border:2px solid transparent}a.cke_smile:hover,a.cke_smile:focus,a.cke_smile:active,a.cke_specialchar:hover,a.cke_specialchar:focus,a.cke_specialchar:active{background:#fff;outline:0}a.cke_smile:hover,a.cke_specialchar:hover{border-color:#888}a.cke_smile:focus,a.cke_smile:active,a.cke_specialchar:focus,a.cke_specialchar:active{border-color:#139ff7}.cke_dialog_contents a.colorChooser{display:block;margin-top:6px;margin-left:10px;width:80px}.cke_rtl .cke_dialog_contents a.colorChooser{margin-right:10px}.cke_iframe_shim{display:block;position:absolute;top:0;left:0;z-index:-1;filter:alpha(opacity = 0);width:100%;height:100%}.cke_dialog_contents_body .cke_accessibility_legend{margin:2px 7px 2px 2px}.cke_dialog_contents_body .cke_accessibility_legend:focus,.cke_dialog_contents_body .cke_accessibility_legend:active{outline:0;border:2px solid #139ff7;margin:0 5px 0 0}.cke_dialog_contents_body input[type=file]:focus,.cke_dialog_contents_body input[type=file]:active{border:2px solid #139ff7}.cke_dialog_find_fieldset{margin-top:10px!important}.cke_dialog_image_ratiolock{margin-top:52px!important}.cke_dialog_forms_select_order label.cke_dialog_ui_labeled_label{margin-left:0}.cke_dialog_forms_select_order div.cke_dialog_ui_input_select{width:100%}.cke_dialog_forms_select_order_txtsize .cke_dialog_ui_hbox_last{padding-top:4px}.cke_dialog_image_url .cke_dialog_ui_hbox_last,.cke_dialog_flash_url .cke_dialog_ui_hbox_last{vertical-align:bottom}a.cke_dialog_ui_button.cke_dialog_image_browse{margin-top:10px}.cke_dialog_contents_body .cke_tpl_list{border:#bcbcbc 1px solid;margin:1px}.cke_dialog_contents_body .cke_tpl_list:focus,.cke_dialog_contents_body .cke_tpl_list:active{outline:0;margin:0;border:2px solid #139ff7}.cke_dialog_contents_body .cke_tpl_list a:focus,.cke_dialog_contents_body .cke_tpl_list a:active{outline:0}.cke_dialog_contents_body .cke_tpl_list a:focus .cke_tpl_item,.cke_dialog_contents_body .cke_tpl_list a:active .cke_tpl_item{border:2px solid #139ff7;padding:6px}.cke_rtl input.cke_dialog_ui_input_text,.cke_rtl input.cke_dialog_ui_input_password{padding-right:2px}.cke_rtl div.cke_dialog_ui_input_text,.cke_rtl div.cke_dialog_ui_input_password{padding-left:2px}.cke_rtl div.cke_dialog_ui_input_text{padding-right:1px}.cke_rtl .cke_dialog_ui_vbox_child,.cke_rtl .cke_dialog_ui_hbox_child,.cke_rtl .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_ui_hbox_last{padding-right:2px!important}.cke_hc .cke_dialog_title,.cke_hc .cke_dialog_footer,.cke_hc a.cke_dialog_tab,.cke_hc a.cke_dialog_ui_button,.cke_hc a.cke_dialog_ui_button:hover,.cke_hc a.cke_dialog_ui_button_ok,.cke_hc a.cke_dialog_ui_button_ok:hover{filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.cke_hc div.cke_dialog_ui_input_text,.cke_hc div.cke_dialog_ui_input_password,.cke_hc div.cke_dialog_ui_input_textarea,.cke_hc div.cke_dialog_ui_input_select,.cke_hc div.cke_dialog_ui_input_file{border:0} \ No newline at end of file diff --git a/static/plugs/ckeditor/skins/moono-lisa/dialog_ie8.css b/static/plugs/ckeditor/skins/moono-lisa/dialog_ie8.css new file mode 100644 index 000000000..9ca86df8b --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/dialog_ie8.css @@ -0,0 +1,5 @@ +/* +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +.cke_dialog{visibility:visible}.cke_dialog_body{z-index:1;background:#fff}.cke_dialog strong{font-weight:bold}.cke_dialog_title{font-weight:bold;font-size:12px;cursor:move;position:relative;color:#484848;border-bottom:1px solid #d1d1d1;padding:12px 19px 12px 12px;background:#f8f8f8;letter-spacing:.3px}.cke_dialog_spinner{border-radius:50%;width:12px;height:12px;overflow:hidden;text-indent:-9999em;border:2px solid rgba(102,102,102,0.2);border-left-color:rgba(102,102,102,1);-webkit-animation:dialog_spinner 1s infinite linear;animation:dialog_spinner 1s infinite linear}.cke_browser_ie8 .cke_dialog_spinner,.cke_browser_ie9 .cke_dialog_spinner{background:url(images/spinner.gif) center top no-repeat;width:16px;height:16px;border:0}@-webkit-keyframes dialog_spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes dialog_spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.cke_dialog_contents{background-color:#fff;overflow:auto;padding:15px 10px 5px 10px;margin-top:43px;border-top:1px solid #d1d1d1}.cke_dialog_contents_body{overflow:auto;padding:9px 10px 5px 10px;margin-top:22px}.cke_dialog_footer{text-align:right;position:relative;border-top:1px solid #d1d1d1;background:#f8f8f8}.cke_rtl .cke_dialog_footer{text-align:left}.cke_hc .cke_dialog_footer{outline:0;border-top:1px solid #fff}.cke_dialog .cke_resizer{margin-top:22px}.cke_dialog .cke_resizer_rtl{margin-left:5px}.cke_dialog .cke_resizer_ltr{margin-right:5px}.cke_dialog_tabs{height:33px;display:inline-block;margin:9px 0 0;position:absolute;z-index:2;left:11px}.cke_rtl .cke_dialog_tabs{left:auto;right:11px}a.cke_dialog_tab{height:25px;padding:4px 8px;display:inline-block;cursor:pointer;line-height:26px;outline:0;color:#484848;border:1px solid #d1d1d1;border-radius:3px 3px 0 0;background:#f8f8f8;min-width:90px;text-align:center;margin-left:-1px;letter-spacing:.3px}a.cke_dialog_tab:hover{background-color:#fff}a.cke_dialog_tab:focus{border:2px solid #139ff7;border-bottom-color:#d1d1d1;padding:3px 7px;position:relative;z-index:1}a.cke_dialog_tab_selected{background:#fff;border-bottom-color:#fff;cursor:default;filter:none}a.cke_dialog_tab_selected:hover,a.cke_dialog_tab_selected:focus{border-bottom-color:#fff}.cke_hc a.cke_dialog_tab:hover,.cke_hc a.cke_dialog_tab:focus,.cke_hc a.cke_dialog_tab_selected{border:3px solid;padding:2px 6px}a.cke_dialog_tab_disabled{color:#bababa;cursor:default}.cke_single_page .cke_dialog_tabs{display:none}.cke_single_page .cke_dialog_contents{padding-top:5px;margin-top:0;border-top:0}a.cke_dialog_close_button{background-image:url(images/close.png);background-repeat:no-repeat;background-position:50%;position:absolute;cursor:pointer;text-align:center;height:16px;width:16px;top:11px;z-index:5;opacity:.7;filter:alpha(opacity = 70)}.cke_rtl .cke_dialog_close_button{left:12px}.cke_ltr .cke_dialog_close_button{right:12px}.cke_hc a.cke_dialog_close_button{background-image:none}.cke_hidpi a.cke_dialog_close_button{background-image:url(images/hidpi/close.png);background-size:16px}a.cke_dialog_close_button:hover{opacity:1;filter:alpha(opacity = 100)}a.cke_dialog_close_button span{display:none}.cke_hc a.cke_dialog_close_button span{display:inline;cursor:pointer;font-weight:bold;position:relative;top:3px}div.cke_disabled .cke_dialog_ui_labeled_content div *{background-color:#ddd;cursor:default}.cke_dialog_ui_vbox table,.cke_dialog_ui_hbox table{margin:auto}.cke_dialog_ui_vbox_child{padding:5px 0}.cke_dialog_ui_hbox{width:100%;margin-top:12px}.cke_dialog_ui_hbox_first,.cke_dialog_ui_hbox_child,.cke_dialog_ui_hbox_last{vertical-align:top}.cke_ltr .cke_dialog_ui_hbox_first,.cke_ltr .cke_dialog_ui_hbox_child{padding-right:10px}.cke_rtl .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_ui_hbox_child{padding-left:10px}.cke_ltr .cke_dialog_footer_buttons .cke_dialog_ui_hbox_first,.cke_ltr .cke_dialog_footer_buttons .cke_dialog_ui_hbox_child{padding-right:5px}.cke_rtl .cke_dialog_footer_buttons .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_footer_buttons .cke_dialog_ui_hbox_child{padding-left:5px;padding-right:0}.cke_hc div.cke_dialog_ui_input_text,.cke_hc div.cke_dialog_ui_input_password,.cke_hc div.cke_dialog_ui_input_textarea,.cke_hc div.cke_dialog_ui_input_select,.cke_hc div.cke_dialog_ui_input_file{border:1px solid}textarea.cke_dialog_ui_input_textarea{overflow:auto;resize:none}input.cke_dialog_ui_input_text,input.cke_dialog_ui_input_password,textarea.cke_dialog_ui_input_textarea{background-color:#fff;border:1px solid #bcbcbc;padding:4px 6px;outline:0;width:100%;*width:95%;box-sizing:border-box;border-radius:2px;min-height:28px;margin-left:1px}input.cke_dialog_ui_input_text:hover,input.cke_dialog_ui_input_password:hover,textarea.cke_dialog_ui_input_textarea:hover{border:1px solid #aeb3b9}input.cke_dialog_ui_input_text:focus,input.cke_dialog_ui_input_password:focus,textarea.cke_dialog_ui_input_textarea:focus,select.cke_dialog_ui_input_select:focus{outline:0;border:2px solid #139ff7}input.cke_dialog_ui_input_text:focus{padding-left:5px}textarea.cke_dialog_ui_input_textarea:focus{padding:3px 5px}select.cke_dialog_ui_input_select:focus{margin:0;width:100%!important}input.cke_dialog_ui_checkbox_input,input.cke_dialog_ui_radio_input{margin-left:1px;margin-right:2px}input.cke_dialog_ui_checkbox_input:focus,input.cke_dialog_ui_checkbox_input:active,input.cke_dialog_ui_radio_input:focus,input.cke_dialog_ui_radio_input:active{border:0;outline:2px solid #139ff7}a.cke_dialog_ui_button{display:inline-block;*display:inline;*zoom:1;padding:4px 1px;margin:0;text-align:center;color:#484848;vertical-align:middle;cursor:pointer;border:1px solid #bcbcbc;border-radius:2px;background:#f8f8f8;letter-spacing:.3px;line-height:18px;box-sizing:border-box}.cke_hc a.cke_dialog_ui_button{border-width:3px}span.cke_dialog_ui_button{padding:0 10px;cursor:pointer}a.cke_dialog_ui_button:hover{background:#fff}a.cke_dialog_ui_button:focus,a.cke_dialog_ui_button:active{border:2px solid #139ff7;outline:0;padding:3px 0}.cke_hc a.cke_dialog_ui_button:hover,.cke_hc a.cke_dialog_ui_button:focus,.cke_hc a.cke_dialog_ui_button:active{border:3px solid}.cke_dialog_footer_buttons a.cke_dialog_ui_button span{color:inherit;font-size:12px;font-weight:bold;padding:0 12px}a.cke_dialog_ui_button_ok{color:#fff;background:#09863e;border:1px solid #09863e}.cke_hc a.cke_dialog_ui_button{border:3px solid #bcbcbc}a.cke_dialog_ui_button_ok:hover{background:#53aa78;border-color:#53aa78}a.cke_dialog_ui_button_ok:focus{box-shadow:inset 0 0 0 2px #FFF}a.cke_dialog_ui_button_ok:focus,a.cke_dialog_ui_button_ok:active{border-color:#139ff7}.cke_hc a.cke_dialog_ui_button_ok:hover,.cke_hc a.cke_dialog_ui_button_ok:focus,.cke_hc a.cke_dialog_ui_button_ok:active{border-color:#484848}a.cke_dialog_ui_button_ok.cke_disabled{background:#d1d1d1;border-color:#d1d1d1;cursor:default}a.cke_dialog_ui_button_ok.cke_disabled span{cursor:default}.cke_dialog_footer_buttons{display:inline-table;margin:5px;width:auto;position:relative;vertical-align:middle}div.cke_dialog_ui_input_select{display:table}select.cke_dialog_ui_input_select{height:28px;line-height:28px;background-color:#fff;border:1px solid #bcbcbc;padding:3px 3px 3px 6px;outline:0;border-radius:2px;margin:0 1px;box-sizing:border-box;width:calc(100% - 2px)!important}.cke_dialog_ui_input_file{width:100%;height:25px}.cke_hc .cke_dialog_ui_labeled_content input:focus,.cke_hc .cke_dialog_ui_labeled_content select:focus,.cke_hc .cke_dialog_ui_labeled_content textarea:focus{outline:1px dotted}.cke_dialog_ui_labeled_label{margin-left:1px}.cke_dialog .cke_dark_background{background-color:transparent}.cke_dialog .cke_light_background{background-color:#ebebeb}.cke_dialog .cke_centered{text-align:center}.cke_dialog a.cke_btn_reset{float:right;background:url(images/refresh.png) top left no-repeat;width:16px;height:16px;border:1px none;font-size:1px}.cke_hidpi .cke_dialog a.cke_btn_reset{background-size:16px;background-image:url(images/hidpi/refresh.png)}.cke_rtl .cke_dialog a.cke_btn_reset{float:left}.cke_dialog a.cke_btn_locked,.cke_dialog a.cke_btn_unlocked{float:left;width:16px;height:16px;background-repeat:no-repeat;border:none 1px;font-size:1px}.cke_dialog a.cke_btn_locked,.cke_dialog a.cke_btn_unlocked,.cke_dialog a.cke_btn_reset{margin:2px}.cke_dialog a.cke_btn_locked{background-image:url(images/lock.png)}.cke_dialog a.cke_btn_unlocked{background-image:url(images/lock-open.png)}.cke_rtl .cke_dialog a.cke_btn_locked,.cke_rtl .cke_dialog a.cke_btn_unlocked{float:right}.cke_hidpi .cke_dialog a.cke_btn_unlocked,.cke_hidpi .cke_dialog a.cke_btn_locked{background-size:16px}.cke_hidpi .cke_dialog a.cke_btn_locked{background-image:url(images/hidpi/lock.png)}.cke_hidpi .cke_dialog a.cke_btn_unlocked{background-image:url(images/hidpi/lock-open.png)}.cke_dialog a.cke_btn_locked .cke_icon{display:none}.cke_dialog a.cke_btn_over,.cke_dialog a.cke_btn_locked:hover,.cke_dialog a.cke_btn_locked:focus,.cke_dialog a.cke_btn_locked:active,.cke_dialog a.cke_btn_unlocked:hover,.cke_dialog a.cke_btn_unlocked:focus,.cke_dialog a.cke_btn_unlocked:active,.cke_dialog a.cke_btn_reset:hover,.cke_dialog a.cke_btn_reset:focus,.cke_dialog a.cke_btn_reset:active{cursor:pointer;outline:0;margin:0;border:2px solid #139ff7}.cke_dialog fieldset{border:1px solid #bcbcbc}.cke_dialog fieldset legend{padding:0 6px}.cke_dialog_ui_checkbox,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox{display:inline-block}.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox{padding-top:5px}.cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input,.cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input+label,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input+label{vertical-align:middle}.cke_dialog .ImagePreviewBox{border:1px ridge #bcbcbc;overflow:scroll;height:200px;width:300px;padding:2px;background-color:white}.cke_dialog .ImagePreviewBox table td{white-space:normal}.cke_dialog .ImagePreviewLoader{position:absolute;white-space:normal;overflow:hidden;height:160px;width:230px;margin:2px;padding:2px;opacity:.9;filter:alpha(opacity = 90);background-color:#e4e4e4}.cke_dialog .FlashPreviewBox{white-space:normal;border:1px solid #bcbcbc;overflow:auto;height:160px;width:390px;padding:2px;background-color:white}.cke_dialog .cke_pastetext{width:346px;height:170px}.cke_dialog .cke_pastetext textarea{width:340px;height:170px;resize:none}.cke_dialog iframe.cke_pasteframe{width:346px;height:130px;background-color:white;border:1px solid #aeb3b9;border-radius:3px}.cke_dialog .cke_hand{cursor:pointer}.cke_disabled{color:#a0a0a0}.cke_dialog_body .cke_label{display:none}.cke_dialog_body label{display:inline;cursor:default;letter-spacing:.3px}.cke_dialog_body label+.cke_dialog_ui_labeled_content{margin-top:2px}.cke_dialog_contents_body .cke_dialog_ui_text,.cke_dialog_contents_body .cke_dialog_ui_select,.cke_dialog_contents_body .cke_dialog_ui_hbox_last>a.cke_dialog_ui_button{margin-top:4px}a.cke_smile{overflow:hidden;display:block;text-align:center;padding:.3em 0}a.cke_smile img{vertical-align:middle}a.cke_specialchar{cursor:inherit;display:block;height:1.25em;padding:.2em .3em;text-align:center}a.cke_smile,a.cke_specialchar{border:2px solid transparent}a.cke_smile:hover,a.cke_smile:focus,a.cke_smile:active,a.cke_specialchar:hover,a.cke_specialchar:focus,a.cke_specialchar:active{background:#fff;outline:0}a.cke_smile:hover,a.cke_specialchar:hover{border-color:#888}a.cke_smile:focus,a.cke_smile:active,a.cke_specialchar:focus,a.cke_specialchar:active{border-color:#139ff7}.cke_dialog_contents a.colorChooser{display:block;margin-top:6px;margin-left:10px;width:80px}.cke_rtl .cke_dialog_contents a.colorChooser{margin-right:10px}.cke_iframe_shim{display:block;position:absolute;top:0;left:0;z-index:-1;filter:alpha(opacity = 0);width:100%;height:100%}.cke_dialog_contents_body .cke_accessibility_legend{margin:2px 7px 2px 2px}.cke_dialog_contents_body .cke_accessibility_legend:focus,.cke_dialog_contents_body .cke_accessibility_legend:active{outline:0;border:2px solid #139ff7;margin:0 5px 0 0}.cke_dialog_contents_body input[type=file]:focus,.cke_dialog_contents_body input[type=file]:active{border:2px solid #139ff7}.cke_dialog_find_fieldset{margin-top:10px!important}.cke_dialog_image_ratiolock{margin-top:52px!important}.cke_dialog_forms_select_order label.cke_dialog_ui_labeled_label{margin-left:0}.cke_dialog_forms_select_order div.cke_dialog_ui_input_select{width:100%}.cke_dialog_forms_select_order_txtsize .cke_dialog_ui_hbox_last{padding-top:4px}.cke_dialog_image_url .cke_dialog_ui_hbox_last,.cke_dialog_flash_url .cke_dialog_ui_hbox_last{vertical-align:bottom}a.cke_dialog_ui_button.cke_dialog_image_browse{margin-top:10px}.cke_dialog_contents_body .cke_tpl_list{border:#bcbcbc 1px solid;margin:1px}.cke_dialog_contents_body .cke_tpl_list:focus,.cke_dialog_contents_body .cke_tpl_list:active{outline:0;margin:0;border:2px solid #139ff7}.cke_dialog_contents_body .cke_tpl_list a:focus,.cke_dialog_contents_body .cke_tpl_list a:active{outline:0}.cke_dialog_contents_body .cke_tpl_list a:focus .cke_tpl_item,.cke_dialog_contents_body .cke_tpl_list a:active .cke_tpl_item{border:2px solid #139ff7;padding:6px}.cke_rtl input.cke_dialog_ui_input_text,.cke_rtl input.cke_dialog_ui_input_password{padding-right:2px}.cke_rtl div.cke_dialog_ui_input_text,.cke_rtl div.cke_dialog_ui_input_password{padding-left:2px}.cke_rtl div.cke_dialog_ui_input_text{padding-right:1px}.cke_rtl .cke_dialog_ui_vbox_child,.cke_rtl .cke_dialog_ui_hbox_child,.cke_rtl .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_ui_hbox_last{padding-right:2px!important}.cke_hc .cke_dialog_title,.cke_hc .cke_dialog_footer,.cke_hc a.cke_dialog_tab,.cke_hc a.cke_dialog_ui_button,.cke_hc a.cke_dialog_ui_button:hover,.cke_hc a.cke_dialog_ui_button_ok,.cke_hc a.cke_dialog_ui_button_ok:hover{filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.cke_hc div.cke_dialog_ui_input_text,.cke_hc div.cke_dialog_ui_input_password,.cke_hc div.cke_dialog_ui_input_textarea,.cke_hc div.cke_dialog_ui_input_select,.cke_hc div.cke_dialog_ui_input_file{border:0}a.cke_dialog_ui_button{min-height:18px}input.cke_dialog_ui_input_text,input.cke_dialog_ui_input_password,textarea.cke_dialog_ui_input_textarea{min-height:18px}input.cke_dialog_ui_input_text:focus,input.cke_dialog_ui_input_password:focus,textarea.cke_dialog_ui_input_textarea:focus{padding-top:4px;padding-bottom:2px}select.cke_dialog_ui_input_select{width:100%!important}select.cke_dialog_ui_input_select:focus{margin-left:1px;width:100%!important;padding-top:2px;padding-bottom:2px} \ No newline at end of file diff --git a/static/plugs/ckeditor/skins/moono-lisa/dialog_iequirks.css b/static/plugs/ckeditor/skins/moono-lisa/dialog_iequirks.css new file mode 100644 index 000000000..3419770dd --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/dialog_iequirks.css @@ -0,0 +1,5 @@ +/* +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +.cke_dialog{visibility:visible}.cke_dialog_body{z-index:1;background:#fff}.cke_dialog strong{font-weight:bold}.cke_dialog_title{font-weight:bold;font-size:12px;cursor:move;position:relative;color:#484848;border-bottom:1px solid #d1d1d1;padding:12px 19px 12px 12px;background:#f8f8f8;letter-spacing:.3px}.cke_dialog_spinner{border-radius:50%;width:12px;height:12px;overflow:hidden;text-indent:-9999em;border:2px solid rgba(102,102,102,0.2);border-left-color:rgba(102,102,102,1);-webkit-animation:dialog_spinner 1s infinite linear;animation:dialog_spinner 1s infinite linear}.cke_browser_ie8 .cke_dialog_spinner,.cke_browser_ie9 .cke_dialog_spinner{background:url(images/spinner.gif) center top no-repeat;width:16px;height:16px;border:0}@-webkit-keyframes dialog_spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes dialog_spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.cke_dialog_contents{background-color:#fff;overflow:auto;padding:15px 10px 5px 10px;margin-top:43px;border-top:1px solid #d1d1d1}.cke_dialog_contents_body{overflow:auto;padding:9px 10px 5px 10px;margin-top:22px}.cke_dialog_footer{text-align:right;position:relative;border-top:1px solid #d1d1d1;background:#f8f8f8}.cke_rtl .cke_dialog_footer{text-align:left}.cke_hc .cke_dialog_footer{outline:0;border-top:1px solid #fff}.cke_dialog .cke_resizer{margin-top:22px}.cke_dialog .cke_resizer_rtl{margin-left:5px}.cke_dialog .cke_resizer_ltr{margin-right:5px}.cke_dialog_tabs{height:33px;display:inline-block;margin:9px 0 0;position:absolute;z-index:2;left:11px}.cke_rtl .cke_dialog_tabs{left:auto;right:11px}a.cke_dialog_tab{height:25px;padding:4px 8px;display:inline-block;cursor:pointer;line-height:26px;outline:0;color:#484848;border:1px solid #d1d1d1;border-radius:3px 3px 0 0;background:#f8f8f8;min-width:90px;text-align:center;margin-left:-1px;letter-spacing:.3px}a.cke_dialog_tab:hover{background-color:#fff}a.cke_dialog_tab:focus{border:2px solid #139ff7;border-bottom-color:#d1d1d1;padding:3px 7px;position:relative;z-index:1}a.cke_dialog_tab_selected{background:#fff;border-bottom-color:#fff;cursor:default;filter:none}a.cke_dialog_tab_selected:hover,a.cke_dialog_tab_selected:focus{border-bottom-color:#fff}.cke_hc a.cke_dialog_tab:hover,.cke_hc a.cke_dialog_tab:focus,.cke_hc a.cke_dialog_tab_selected{border:3px solid;padding:2px 6px}a.cke_dialog_tab_disabled{color:#bababa;cursor:default}.cke_single_page .cke_dialog_tabs{display:none}.cke_single_page .cke_dialog_contents{padding-top:5px;margin-top:0;border-top:0}a.cke_dialog_close_button{background-image:url(images/close.png);background-repeat:no-repeat;background-position:50%;position:absolute;cursor:pointer;text-align:center;height:16px;width:16px;top:11px;z-index:5;opacity:.7;filter:alpha(opacity = 70)}.cke_rtl .cke_dialog_close_button{left:12px}.cke_ltr .cke_dialog_close_button{right:12px}.cke_hc a.cke_dialog_close_button{background-image:none}.cke_hidpi a.cke_dialog_close_button{background-image:url(images/hidpi/close.png);background-size:16px}a.cke_dialog_close_button:hover{opacity:1;filter:alpha(opacity = 100)}a.cke_dialog_close_button span{display:none}.cke_hc a.cke_dialog_close_button span{display:inline;cursor:pointer;font-weight:bold;position:relative;top:3px}div.cke_disabled .cke_dialog_ui_labeled_content div *{background-color:#ddd;cursor:default}.cke_dialog_ui_vbox table,.cke_dialog_ui_hbox table{margin:auto}.cke_dialog_ui_vbox_child{padding:5px 0}.cke_dialog_ui_hbox{width:100%;margin-top:12px}.cke_dialog_ui_hbox_first,.cke_dialog_ui_hbox_child,.cke_dialog_ui_hbox_last{vertical-align:top}.cke_ltr .cke_dialog_ui_hbox_first,.cke_ltr .cke_dialog_ui_hbox_child{padding-right:10px}.cke_rtl .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_ui_hbox_child{padding-left:10px}.cke_ltr .cke_dialog_footer_buttons .cke_dialog_ui_hbox_first,.cke_ltr .cke_dialog_footer_buttons .cke_dialog_ui_hbox_child{padding-right:5px}.cke_rtl .cke_dialog_footer_buttons .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_footer_buttons .cke_dialog_ui_hbox_child{padding-left:5px;padding-right:0}.cke_hc div.cke_dialog_ui_input_text,.cke_hc div.cke_dialog_ui_input_password,.cke_hc div.cke_dialog_ui_input_textarea,.cke_hc div.cke_dialog_ui_input_select,.cke_hc div.cke_dialog_ui_input_file{border:1px solid}textarea.cke_dialog_ui_input_textarea{overflow:auto;resize:none}input.cke_dialog_ui_input_text,input.cke_dialog_ui_input_password,textarea.cke_dialog_ui_input_textarea{background-color:#fff;border:1px solid #bcbcbc;padding:4px 6px;outline:0;width:100%;*width:95%;box-sizing:border-box;border-radius:2px;min-height:28px;margin-left:1px}input.cke_dialog_ui_input_text:hover,input.cke_dialog_ui_input_password:hover,textarea.cke_dialog_ui_input_textarea:hover{border:1px solid #aeb3b9}input.cke_dialog_ui_input_text:focus,input.cke_dialog_ui_input_password:focus,textarea.cke_dialog_ui_input_textarea:focus,select.cke_dialog_ui_input_select:focus{outline:0;border:2px solid #139ff7}input.cke_dialog_ui_input_text:focus{padding-left:5px}textarea.cke_dialog_ui_input_textarea:focus{padding:3px 5px}select.cke_dialog_ui_input_select:focus{margin:0;width:100%!important}input.cke_dialog_ui_checkbox_input,input.cke_dialog_ui_radio_input{margin-left:1px;margin-right:2px}input.cke_dialog_ui_checkbox_input:focus,input.cke_dialog_ui_checkbox_input:active,input.cke_dialog_ui_radio_input:focus,input.cke_dialog_ui_radio_input:active{border:0;outline:2px solid #139ff7}a.cke_dialog_ui_button{display:inline-block;*display:inline;*zoom:1;padding:4px 1px;margin:0;text-align:center;color:#484848;vertical-align:middle;cursor:pointer;border:1px solid #bcbcbc;border-radius:2px;background:#f8f8f8;letter-spacing:.3px;line-height:18px;box-sizing:border-box}.cke_hc a.cke_dialog_ui_button{border-width:3px}span.cke_dialog_ui_button{padding:0 10px;cursor:pointer}a.cke_dialog_ui_button:hover{background:#fff}a.cke_dialog_ui_button:focus,a.cke_dialog_ui_button:active{border:2px solid #139ff7;outline:0;padding:3px 0}.cke_hc a.cke_dialog_ui_button:hover,.cke_hc a.cke_dialog_ui_button:focus,.cke_hc a.cke_dialog_ui_button:active{border:3px solid}.cke_dialog_footer_buttons a.cke_dialog_ui_button span{color:inherit;font-size:12px;font-weight:bold;padding:0 12px}a.cke_dialog_ui_button_ok{color:#fff;background:#09863e;border:1px solid #09863e}.cke_hc a.cke_dialog_ui_button{border:3px solid #bcbcbc}a.cke_dialog_ui_button_ok:hover{background:#53aa78;border-color:#53aa78}a.cke_dialog_ui_button_ok:focus{box-shadow:inset 0 0 0 2px #FFF}a.cke_dialog_ui_button_ok:focus,a.cke_dialog_ui_button_ok:active{border-color:#139ff7}.cke_hc a.cke_dialog_ui_button_ok:hover,.cke_hc a.cke_dialog_ui_button_ok:focus,.cke_hc a.cke_dialog_ui_button_ok:active{border-color:#484848}a.cke_dialog_ui_button_ok.cke_disabled{background:#d1d1d1;border-color:#d1d1d1;cursor:default}a.cke_dialog_ui_button_ok.cke_disabled span{cursor:default}.cke_dialog_footer_buttons{display:inline-table;margin:5px;width:auto;position:relative;vertical-align:middle}div.cke_dialog_ui_input_select{display:table}select.cke_dialog_ui_input_select{height:28px;line-height:28px;background-color:#fff;border:1px solid #bcbcbc;padding:3px 3px 3px 6px;outline:0;border-radius:2px;margin:0 1px;box-sizing:border-box;width:calc(100% - 2px)!important}.cke_dialog_ui_input_file{width:100%;height:25px}.cke_hc .cke_dialog_ui_labeled_content input:focus,.cke_hc .cke_dialog_ui_labeled_content select:focus,.cke_hc .cke_dialog_ui_labeled_content textarea:focus{outline:1px dotted}.cke_dialog_ui_labeled_label{margin-left:1px}.cke_dialog .cke_dark_background{background-color:transparent}.cke_dialog .cke_light_background{background-color:#ebebeb}.cke_dialog .cke_centered{text-align:center}.cke_dialog a.cke_btn_reset{float:right;background:url(images/refresh.png) top left no-repeat;width:16px;height:16px;border:1px none;font-size:1px}.cke_hidpi .cke_dialog a.cke_btn_reset{background-size:16px;background-image:url(images/hidpi/refresh.png)}.cke_rtl .cke_dialog a.cke_btn_reset{float:left}.cke_dialog a.cke_btn_locked,.cke_dialog a.cke_btn_unlocked{float:left;width:16px;height:16px;background-repeat:no-repeat;border:none 1px;font-size:1px}.cke_dialog a.cke_btn_locked,.cke_dialog a.cke_btn_unlocked,.cke_dialog a.cke_btn_reset{margin:2px}.cke_dialog a.cke_btn_locked{background-image:url(images/lock.png)}.cke_dialog a.cke_btn_unlocked{background-image:url(images/lock-open.png)}.cke_rtl .cke_dialog a.cke_btn_locked,.cke_rtl .cke_dialog a.cke_btn_unlocked{float:right}.cke_hidpi .cke_dialog a.cke_btn_unlocked,.cke_hidpi .cke_dialog a.cke_btn_locked{background-size:16px}.cke_hidpi .cke_dialog a.cke_btn_locked{background-image:url(images/hidpi/lock.png)}.cke_hidpi .cke_dialog a.cke_btn_unlocked{background-image:url(images/hidpi/lock-open.png)}.cke_dialog a.cke_btn_locked .cke_icon{display:none}.cke_dialog a.cke_btn_over,.cke_dialog a.cke_btn_locked:hover,.cke_dialog a.cke_btn_locked:focus,.cke_dialog a.cke_btn_locked:active,.cke_dialog a.cke_btn_unlocked:hover,.cke_dialog a.cke_btn_unlocked:focus,.cke_dialog a.cke_btn_unlocked:active,.cke_dialog a.cke_btn_reset:hover,.cke_dialog a.cke_btn_reset:focus,.cke_dialog a.cke_btn_reset:active{cursor:pointer;outline:0;margin:0;border:2px solid #139ff7}.cke_dialog fieldset{border:1px solid #bcbcbc}.cke_dialog fieldset legend{padding:0 6px}.cke_dialog_ui_checkbox,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox{display:inline-block}.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox{padding-top:5px}.cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input,.cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input+label,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input,.cke_dialog fieldset .cke_dialog_ui_vbox .cke_dialog_ui_checkbox .cke_dialog_ui_checkbox_input+label{vertical-align:middle}.cke_dialog .ImagePreviewBox{border:1px ridge #bcbcbc;overflow:scroll;height:200px;width:300px;padding:2px;background-color:white}.cke_dialog .ImagePreviewBox table td{white-space:normal}.cke_dialog .ImagePreviewLoader{position:absolute;white-space:normal;overflow:hidden;height:160px;width:230px;margin:2px;padding:2px;opacity:.9;filter:alpha(opacity = 90);background-color:#e4e4e4}.cke_dialog .FlashPreviewBox{white-space:normal;border:1px solid #bcbcbc;overflow:auto;height:160px;width:390px;padding:2px;background-color:white}.cke_dialog .cke_pastetext{width:346px;height:170px}.cke_dialog .cke_pastetext textarea{width:340px;height:170px;resize:none}.cke_dialog iframe.cke_pasteframe{width:346px;height:130px;background-color:white;border:1px solid #aeb3b9;border-radius:3px}.cke_dialog .cke_hand{cursor:pointer}.cke_disabled{color:#a0a0a0}.cke_dialog_body .cke_label{display:none}.cke_dialog_body label{display:inline;cursor:default;letter-spacing:.3px}.cke_dialog_body label+.cke_dialog_ui_labeled_content{margin-top:2px}.cke_dialog_contents_body .cke_dialog_ui_text,.cke_dialog_contents_body .cke_dialog_ui_select,.cke_dialog_contents_body .cke_dialog_ui_hbox_last>a.cke_dialog_ui_button{margin-top:4px}a.cke_smile{overflow:hidden;display:block;text-align:center;padding:.3em 0}a.cke_smile img{vertical-align:middle}a.cke_specialchar{cursor:inherit;display:block;height:1.25em;padding:.2em .3em;text-align:center}a.cke_smile,a.cke_specialchar{border:2px solid transparent}a.cke_smile:hover,a.cke_smile:focus,a.cke_smile:active,a.cke_specialchar:hover,a.cke_specialchar:focus,a.cke_specialchar:active{background:#fff;outline:0}a.cke_smile:hover,a.cke_specialchar:hover{border-color:#888}a.cke_smile:focus,a.cke_smile:active,a.cke_specialchar:focus,a.cke_specialchar:active{border-color:#139ff7}.cke_dialog_contents a.colorChooser{display:block;margin-top:6px;margin-left:10px;width:80px}.cke_rtl .cke_dialog_contents a.colorChooser{margin-right:10px}.cke_iframe_shim{display:block;position:absolute;top:0;left:0;z-index:-1;filter:alpha(opacity = 0);width:100%;height:100%}.cke_dialog_contents_body .cke_accessibility_legend{margin:2px 7px 2px 2px}.cke_dialog_contents_body .cke_accessibility_legend:focus,.cke_dialog_contents_body .cke_accessibility_legend:active{outline:0;border:2px solid #139ff7;margin:0 5px 0 0}.cke_dialog_contents_body input[type=file]:focus,.cke_dialog_contents_body input[type=file]:active{border:2px solid #139ff7}.cke_dialog_find_fieldset{margin-top:10px!important}.cke_dialog_image_ratiolock{margin-top:52px!important}.cke_dialog_forms_select_order label.cke_dialog_ui_labeled_label{margin-left:0}.cke_dialog_forms_select_order div.cke_dialog_ui_input_select{width:100%}.cke_dialog_forms_select_order_txtsize .cke_dialog_ui_hbox_last{padding-top:4px}.cke_dialog_image_url .cke_dialog_ui_hbox_last,.cke_dialog_flash_url .cke_dialog_ui_hbox_last{vertical-align:bottom}a.cke_dialog_ui_button.cke_dialog_image_browse{margin-top:10px}.cke_dialog_contents_body .cke_tpl_list{border:#bcbcbc 1px solid;margin:1px}.cke_dialog_contents_body .cke_tpl_list:focus,.cke_dialog_contents_body .cke_tpl_list:active{outline:0;margin:0;border:2px solid #139ff7}.cke_dialog_contents_body .cke_tpl_list a:focus,.cke_dialog_contents_body .cke_tpl_list a:active{outline:0}.cke_dialog_contents_body .cke_tpl_list a:focus .cke_tpl_item,.cke_dialog_contents_body .cke_tpl_list a:active .cke_tpl_item{border:2px solid #139ff7;padding:6px}.cke_rtl input.cke_dialog_ui_input_text,.cke_rtl input.cke_dialog_ui_input_password{padding-right:2px}.cke_rtl div.cke_dialog_ui_input_text,.cke_rtl div.cke_dialog_ui_input_password{padding-left:2px}.cke_rtl div.cke_dialog_ui_input_text{padding-right:1px}.cke_rtl .cke_dialog_ui_vbox_child,.cke_rtl .cke_dialog_ui_hbox_child,.cke_rtl .cke_dialog_ui_hbox_first,.cke_rtl .cke_dialog_ui_hbox_last{padding-right:2px!important}.cke_hc .cke_dialog_title,.cke_hc .cke_dialog_footer,.cke_hc a.cke_dialog_tab,.cke_hc a.cke_dialog_ui_button,.cke_hc a.cke_dialog_ui_button:hover,.cke_hc a.cke_dialog_ui_button_ok,.cke_hc a.cke_dialog_ui_button_ok:hover{filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.cke_hc div.cke_dialog_ui_input_text,.cke_hc div.cke_dialog_ui_input_password,.cke_hc div.cke_dialog_ui_input_textarea,.cke_hc div.cke_dialog_ui_input_select,.cke_hc div.cke_dialog_ui_input_file{border:0}.cke_dialog_footer{filter:""} \ No newline at end of file diff --git a/static/plugs/ckeditor/skins/moono-lisa/editor.css b/static/plugs/ckeditor/skins/moono-lisa/editor.css new file mode 100644 index 000000000..7783c42bb --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/editor.css @@ -0,0 +1,5 @@ +/* +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +.cke_reset{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none}.cke_reset_all,.cke_reset_all *,.cke_reset_all a,.cke_reset_all textarea{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none;border-collapse:collapse;font:normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;color:#000;text-align:left;white-space:nowrap;cursor:auto;float:none}.cke_reset_all .cke_rtl *{text-align:right}.cke_reset_all iframe{vertical-align:inherit}.cke_reset_all textarea{white-space:pre-wrap}.cke_reset_all textarea,.cke_reset_all input[type="text"],.cke_reset_all input[type="password"]{cursor:text}.cke_reset_all textarea[disabled],.cke_reset_all input[type="text"][disabled],.cke_reset_all input[type="password"][disabled]{cursor:default}.cke_reset_all fieldset{padding:10px;border:2px groove #e0dfe3}.cke_reset_all select{box-sizing:border-box}.cke_reset_all table{table-layout:auto}.cke_chrome{display:block;border:1px solid #d1d1d1;padding:0}.cke_inner{display:block;background:#fff;padding:0;-webkit-touch-callout:none}.cke_float{border:0}.cke_float .cke_inner{padding-bottom:0}.cke_top,.cke_contents,.cke_bottom{display:block;overflow:hidden}.cke_top{border-bottom:1px solid #d1d1d1;background:#f8f8f8;padding:6px 8px 2px;white-space:normal}.cke_float .cke_top{border:1px solid #d1d1d1}.cke_bottom{padding:6px 8px 2px;position:relative;border-top:1px solid #d1d1d1;background:#f8f8f8}.cke_browser_ios .cke_contents{overflow-y:auto;-webkit-overflow-scrolling:touch}.cke_resizer{width:0;height:0;overflow:hidden;border-width:10px 10px 0 0;border-color:transparent #bcbcbc transparent transparent;border-style:dashed solid dashed dashed;font-size:0;vertical-align:bottom;margin-top:6px;margin-bottom:2px}.cke_hc .cke_resizer{font-size:15px;width:auto;height:auto;border-width:0}.cke_resizer_ltr{cursor:se-resize;float:right;margin-right:-4px}.cke_resizer_rtl{border-width:10px 0 0 10px;border-color:transparent transparent transparent #bcbcbc;border-style:dashed dashed dashed solid;cursor:sw-resize;float:left;margin-left:-4px;right:auto}.cke_wysiwyg_div{display:block;height:100%;overflow:auto;padding:0 8px;outline-style:none;box-sizing:border-box}.cke_panel{visibility:visible;width:120px;height:100px;overflow:hidden;background-color:#fff;border:1px solid #d1d1d1}.cke_menu_panel{padding:0;margin:0}.cke_combopanel{width:150px;height:170px}.cke_panel_frame{width:100%;height:100%;font-size:12px;overflow:auto;overflow-x:hidden}.cke_panel_container{overflow-y:auto;overflow-x:hidden}.cke_panel_block:focus{outline:0}.cke_panel_list{margin:0;padding:0;list-style-type:none;white-space:nowrap}.cke_panel_listItem{margin:0;padding:0}.cke_panel_listItem a{padding:6px 7px;display:block;color:inherit!important;text-decoration:none;overflow:hidden;text-overflow:ellipsis}.cke_hc .cke_panel_listItem a{border-style:none}.cke_panel_listItem.cke_selected a,.cke_panel_listItem a:hover,.cke_panel_listItem a:focus,.cke_panel_listItem a:active{background-color:#e9e9e9}.cke_panel_listItem a:focus{outline:1px dotted #000}.cke_hc .cke_panel_listItem a:hover,.cke_hc .cke_panel_listItem a:focus,.cke_hc .cke_panel_listItem a:active{border:2px solid;padding:4px 5px}.cke_panel_listItem p,.cke_panel_listItem h1,.cke_panel_listItem h2,.cke_panel_listItem h3,.cke_panel_listItem h4,.cke_panel_listItem h5,.cke_panel_listItem h6,.cke_panel_listItem pre{margin-top:0;margin-bottom:0}.cke_panel_grouptitle{cursor:default;font-size:11px;font-weight:bold;white-space:nowrap;margin:0;padding:6px 6px 7px 6px;color:#484848;border-bottom:1px solid #d1d1d1;background:#f8f8f8}.cke_colorblock{padding:10px;font-size:11px;font-family:'Microsoft Sans Serif',Tahoma,Arial,Verdana,Sans-Serif}.cke_colorblock,.cke_colorblock a{text-decoration:none;color:#000}a.cke_colorbox{padding:2px;float:left;width:20px;height:20px}.cke_rtl a.cke_colorbox{float:right}a:hover.cke_colorbox,a:focus.cke_colorbox,a:active.cke_colorbox{outline:0;padding:0;border:2px solid #139ff7}a:hover.cke_colorbox{border-color:#bcbcbc}span.cke_colorbox{width:20px;height:20px;float:left}.cke_rtl span.cke_colorbox{float:right}a.cke_colorauto,a.cke_colormore{border:#fff 1px solid;padding:3px;display:block;cursor:pointer}a.cke_colorauto{padding:0;border:1px solid transparent;margin-bottom:6px;height:26px;line-height:26px}a.cke_colormore{margin-top:10px;height:20px;line-height:19px}a:hover.cke_colorauto,a:hover.cke_colormore,a:focus.cke_colorauto,a:focus.cke_colormore,a:active.cke_colorauto,a:active.cke_colormore{outline:0;border:#139ff7 1px solid;background-color:#f8f8f8}a:hover.cke_colorauto,a:hover.cke_colormore{border-color:#bcbcbc}.cke_colorauto span.cke_colorbox{width:18px;height:18px;border:1px solid #808080;margin-left:1px;margin-top:3px}.cke_rtl .cke_colorauto span.cke_colorbox{margin-left:0;margin-right:1px}span.cke_colorbox[style*="#ffffff"],span.cke_colorbox[style*="#FFFFFF"],span.cke_colorbox[style="background-color:#fff"],span.cke_colorbox[style="background-color:#FFF"],span.cke_colorbox[style*="rgb(255,255,255)"],span.cke_colorbox[style*="rgb(255, 255, 255)"]{border:1px solid #808080;width:18px;height:18px}.cke_toolbar{float:left}.cke_rtl .cke_toolbar{float:right}.cke_toolgroup{border:0;float:left;margin:1px 2px 6px 0;padding-right:3px}.cke_rtl .cke_toolgroup{float:right;margin:1px 0 6px 2px;padding-left:3px;padding-right:0}.cke_hc .cke_toolgroup{margin-right:5px;margin-bottom:5px}.cke_hc.cke_rtl .cke_toolgroup{margin-right:0;margin-left:5px}a.cke_button{display:inline-block;height:18px;padding:4px 6px;outline:0;cursor:default;float:left;border:0;position:relative}.cke_rtl a.cke_button{float:right}.cke_hc a.cke_button{border:1px solid black;padding:3px 5px;margin:0 3px 5px 0}.cke_hc.cke_rtl a.cke_button{margin:0 0 5px 3px}a.cke_button_on{background:#fff;border:1px #bcbcbc solid;padding:3px 5px}a.cke_button_off:hover,a.cke_button_off:focus,a.cke_button_off:active{background:#e5e5e5;border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_on,.cke_hc a.cke_button_off:hover,.cke_hc a.cke_button_off:focus,.cke_hc a.cke_button_off:active{background:#e5e5e5;border:3px solid #000;padding:1px 3px}a.cke_button_disabled:hover,a.cke_button_disabled:focus,a.cke_button_disabled:active{border:0;padding:4px 6px;background-color:transparent}a.cke_button_disabled:focus{border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_disabled:hover,.cke_hc a.cke_button_disabled:focus,.cke_hc a.cke_button_disabled:active{border:1px solid #acacac;padding:3px 5px;margin:0 3px 5px 0}.cke_hc a.cke_button_disabled:focus{border:3px solid #000;padding:1px 3px}.cke_hc.cke_rtl a.cke_button_disabled:hover,.cke_hc.cke_rtl a.cke_button_disabled:focus,.cke_hc.cke_rtl a.cke_button_disabled:active{margin:0 0 5px 3px}a.cke_button_disabled .cke_button_icon,a.cke_button_disabled .cke_button_arrow{opacity:.3}.cke_hc a.cke_button_disabled{border-color:#acacac}.cke_hc a.cke_button_disabled .cke_button_icon,.cke_hc a.cke_button_disabled .cke_button_label{opacity:.5}.cke_toolgroup a.cke_button:last-child:after,.cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:4px;top:0;right:-3px}.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-right:0;right:auto;border-left:1px solid #bcbcbc;top:0;left:-3px}.cke_hc .cke_toolgroup a.cke_button:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-color:#000;top:0;right:-7px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{top:0;right:auto;left:-7px}.cke_toolgroup a.cke_button:hover:last-child:after,.cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:-4px}.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:auto;left:-4px}.cke_hc .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:-9px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:auto;left:-9px}.cke_toolbar.cke_toolbar_last .cke_toolgroup a.cke_button:last-child:after{content:none;border:0;width:0;height:0}.cke_button_icon{cursor:inherit;background-repeat:no-repeat;margin-top:1px;width:16px;height:16px;float:left;display:inline-block}.cke_rtl .cke_button_icon{float:right}.cke_hc .cke_button_icon{display:none}.cke_button_label{display:none;padding-left:3px;margin-top:1px;line-height:17px;vertical-align:middle;float:left;cursor:default;color:#484848}.cke_rtl .cke_button_label{padding-right:3px;padding-left:0;float:right}.cke_hc .cke_button_label{padding:0;display:inline-block;font-size:12px}.cke_button_arrow{display:inline-block;margin:8px 0 0 1px;width:0;height:0;cursor:default;vertical-align:top;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_rtl .cke_button_arrow{margin-right:5px;margin-left:0}.cke_hc .cke_button_arrow{font-size:10px;margin:3px 0 0 3px;width:auto;border:0}.cke_toolbar_separator{float:left;background-color:#bcbcbc;margin:4px 2px 0 2px;height:18px;width:1px}.cke_rtl .cke_toolbar_separator{float:right}.cke_hc .cke_toolbar_separator{background-color:#000;margin-left:2px;margin-right:5px;margin-bottom:9px}.cke_hc.cke_rtl .cke_toolbar_separator{margin-left:5px;margin-right:2px}.cke_toolbar_break{display:block;clear:left}.cke_rtl .cke_toolbar_break{clear:right}a.cke_toolbox_collapser{width:12px;height:11px;float:right;margin:11px 0 0;font-size:0;cursor:default;text-align:center;border:1px solid #bcbcbc}.cke_rtl .cke_toolbox_collapser{float:left}.cke_toolbox_collapser:hover{background:#e5e5e5}.cke_toolbox_collapser.cke_toolbox_collapser_min{margin:0 2px 4px}.cke_toolbox_collapser .cke_arrow{display:inline-block;height:0;width:0;font-size:0;margin-top:1px;border:3px solid transparent;border-bottom-color:#484848}.cke_toolbox_collapser.cke_toolbox_collapser_min .cke_arrow{margin-top:4px;border-bottom-color:transparent;border-top-color:#484848}.cke_hc .cke_toolbox_collapser .cke_arrow{font-size:8px;width:auto;border:0;margin-top:0}.cke_menuitem span{cursor:default}.cke_menubutton{display:block}.cke_hc .cke_menubutton{padding:2px}.cke_menubutton:hover,.cke_menubutton:focus,.cke_menubutton:active{background-color:#e9e9e9;display:block;outline:1px dotted}.cke_menubutton:hover{outline:0}.cke_hc .cke_menubutton:hover,.cke_hc .cke_menubutton:focus,.cke_hc .cke_menubutton:active{border:2px solid;padding:0}.cke_menubutton_disabled:hover,.cke_menubutton_disabled:focus,.cke_menubutton_disabled:active{background-color:transparent;outline:0}.cke_menubutton_inner{display:table-row}.cke_menubutton_icon,.cke_menubutton_label,.cke_menuarrow{display:table-cell}.cke_menubutton_icon{background-color:#f8f8f8;padding:6px 4px}.cke_hc .cke_menubutton_icon{height:16px;width:0;padding:4px 0}.cke_menubutton:hover .cke_menubutton_icon,.cke_menubutton:focus .cke_menubutton_icon,.cke_menubutton:active .cke_menubutton_icon{background-color:#e9e9e9}.cke_menubutton_disabled:hover .cke_menubutton_icon,.cke_menubutton_disabled:focus .cke_menubutton_icon,.cke_menubutton_disabled:active .cke_menubutton_icon{background-color:#f8f8f8;outline:0}.cke_menuitem .cke_menubutton_on{background-color:#e9e9e9;border:1px solid #dedede;outline:0}.cke_menubutton_on .cke_menubutton_icon{padding-right:3px;background-color:#e9e9e9}.cke_menubutton_label{padding:0 5px;background-color:transparent;width:100%;vertical-align:middle}.cke_menubutton_shortcut{color:#979797}.cke_menubutton_disabled .cke_menubutton_label{opacity:.3;filter:alpha(opacity=30)}.cke_panel_frame .cke_menubutton_label{display:none}.cke_menuseparator{background-color:#d1d1d1;height:1px}.cke_menuarrow{background:transparent url(images/arrow.png) no-repeat 0 10px;padding:0 5px}.cke_rtl .cke_menuarrow{background-position:5px -13px;background-repeat:no-repeat}.cke_hc .cke_menuarrow{background-image:none}.cke_menuarrow span{display:none}.cke_hc .cke_menuarrow span{vertical-align:middle;display:inline}.cke_combo{display:inline-block;float:left;position:relative;margin-bottom:5px}.cke_rtl .cke_combo{float:right}.cke_hc .cke_combo{margin-top:1px;margin-bottom:10px}.cke_combo:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:5px;top:0;right:0}.cke_rtl .cke_combo:after{border-right:0;border-left:1px solid #bcbcbc;right:auto;left:0}.cke_hc .cke_combo:after{border-color:#000}a.cke_combo_button{cursor:default;display:inline-block;float:left;margin:0;padding:1px}.cke_rtl a.cke_combo_button{float:right}.cke_hc a.cke_combo_button{padding:4px}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:hover,.cke_combo_off a.cke_combo_button:focus,.cke_combo_off a.cke_combo_button:active{background:#e5e5e5;border:1px solid #bcbcbc;padding:0 0 0 1px;margin-left:-1px}.cke_combo_off a.cke_combo_button:focus{outline:0}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:active{background:#fff}.cke_rtl .cke_combo_on a.cke_combo_button,.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:0 1px 0 0;margin-left:0;margin-right:-1px}.cke_hc .cke_combo_on a.cke_combo_button,.cke_hc .cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_combo_off a.cke_combo_button:active{border:3px solid #000;padding:1px 1px 1px 2px}.cke_hc.cke_rtl .cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:1px 2px 1px 1px}.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 0 0 3px;margin-left:-3px}.cke_rtl .cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 3px 0 0;margin-left:0;margin-right:-3px}.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 1px 1px 7px;margin-left:-6px}.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 7px 1px 1px;margin-left:0;margin-right:-6px}.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0;margin:0}.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px;margin:0}.cke_toolbar .cke_combo+.cke_toolbar_end,.cke_toolbar .cke_combo+.cke_toolgroup{margin-right:0;margin-left:2px}.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:2px}.cke_hc .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:5px}.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:5px}.cke_toolbar.cke_toolbar_last .cke_combo:nth-last-child(-n+2):after{content:none;border:0;width:0;height:0}.cke_combo_text{line-height:26px;padding-left:10px;text-overflow:ellipsis;overflow:hidden;float:left;cursor:default;color:#484848;width:60px}.cke_rtl .cke_combo_text{float:right;text-align:right;padding-left:0;padding-right:10px}.cke_hc .cke_combo_text{line-height:18px;font-size:12px}.cke_combo_open{cursor:default;display:inline-block;font-size:0;height:19px;line-height:17px;margin:1px 10px 1px;width:5px}.cke_hc .cke_combo_open{height:12px}.cke_combo_arrow{cursor:default;margin:11px 0 0;float:left;height:0;width:0;font-size:0;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_hc .cke_combo_arrow{font-size:10px;width:auto;border:0;margin-top:3px}.cke_combo_label{display:none;float:left;line-height:26px;vertical-align:top;margin-right:5px}.cke_rtl .cke_combo_label{float:right;margin-left:5px;margin-right:0}.cke_combo_disabled .cke_combo_inlinelabel,.cke_combo_disabled .cke_combo_open{opacity:.3}.cke_path{float:left;margin:-2px 0 2px}a.cke_path_item,span.cke_path_empty{display:inline-block;float:left;padding:3px 4px;margin-right:2px;cursor:default;text-decoration:none;outline:0;border:0;color:#484848;font-weight:bold;font-size:11px}.cke_rtl .cke_path,.cke_rtl .cke_path_item,.cke_rtl .cke_path_empty{float:right}a.cke_path_item:hover,a.cke_path_item:focus,a.cke_path_item:active{background-color:#e5e5e5}.cke_hc a.cke_path_item:hover,.cke_hc a.cke_path_item:focus,.cke_hc a.cke_path_item:active{border:2px solid;padding:1px 2px}.cke_button__source_label,.cke_button__sourcedialog_label{display:inline}.cke_combopanel__fontsize{width:135px}textarea.cke_source{font-family:'Courier New',Monospace;font-size:small;background-color:#fff;white-space:pre-wrap;border:0;padding:0;margin:0;display:block}.cke_wysiwyg_frame,.cke_wysiwyg_div{background-color:#fff}.cke_notifications_area{pointer-events:none}.cke_notification{pointer-events:auto;position:relative;margin:10px;width:300px;color:white;text-align:center;opacity:.95;filter:alpha(opacity = 95);-webkit-animation:fadeIn .7s;animation:fadeIn .7s}.cke_notification_message a{color:#12306f}@-webkit-keyframes fadeIn{from{opacity:.4}to{opacity:.95}}@keyframes fadeIn{from{opacity:.4}to{opacity:.95}}.cke_notification_success{background:#72b572;border:1px solid #63a563}.cke_notification_warning{background:#c83939;border:1px solid #902b2b}.cke_notification_info{background:#2e9ad0;border:1px solid #0f74a8}.cke_notification_info span.cke_notification_progress{background-color:#0f74a8;display:block;padding:0;margin:0;height:100%;overflow:hidden;position:absolute;z-index:1}.cke_notification_message{position:relative;margin:4px 23px 3px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;z-index:4;text-overflow:ellipsis;overflow:hidden}.cke_notification_close{background-image:url(images/close.png);background-repeat:no-repeat;background-position:50%;position:absolute;cursor:pointer;text-align:center;height:20px;width:20px;top:1px;right:1px;padding:0;margin:0;z-index:5;opacity:.6;filter:alpha(opacity = 60)}.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_notification_close span{display:none}.cke_notification_warning a.cke_notification_close{opacity:.8;filter:alpha(opacity = 80)}.cke_notification_warning a.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_chrome{visibility:inherit}.cke_voice_label{display:none}legend.cke_voice_label{display:none}.cke_button__about_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -0px !important;}.cke_button__bold_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -24px !important;}.cke_button__italic_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -48px !important;}.cke_button__strike_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -72px !important;}.cke_button__subscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -96px !important;}.cke_button__superscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -120px !important;}.cke_button__underline_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -144px !important;}.cke_button__bidiltr_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -168px !important;}.cke_button__bidirtl_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -192px !important;}.cke_button__blockquote_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -216px !important;}.cke_rtl .cke_button__copy_icon, .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -240px !important;}.cke_ltr .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -264px !important;}.cke_rtl .cke_button__cut_icon, .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -288px !important;}.cke_ltr .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -312px !important;}.cke_rtl .cke_button__paste_icon, .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -336px !important;}.cke_ltr .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -360px !important;}.cke_button__bgcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -384px !important;}.cke_button__textcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -408px !important;}.cke_rtl .cke_button__templates_icon, .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -432px !important;}.cke_ltr .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -456px !important;}.cke_button__copyformatting_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -480px !important;}.cke_button__creatediv_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -504px !important;}.cke_rtl .cke_button__find_icon, .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -528px !important;}.cke_ltr .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -552px !important;}.cke_button__replace_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -576px !important;}.cke_button__flash_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -600px !important;}.cke_button__button_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -624px !important;}.cke_button__checkbox_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -648px !important;}.cke_button__form_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -672px !important;}.cke_button__hiddenfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -696px !important;}.cke_button__imagebutton_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -720px !important;}.cke_button__radio_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -744px !important;}.cke_rtl .cke_button__select_icon, .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -768px !important;}.cke_ltr .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -792px !important;}.cke_rtl .cke_button__textarea_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -816px !important;}.cke_ltr .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -840px !important;}.cke_rtl .cke_button__textfield_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -864px !important;}.cke_ltr .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -888px !important;}.cke_button__horizontalrule_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -912px !important;}.cke_button__iframe_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -936px !important;}.cke_button__image_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -960px !important;}.cke_rtl .cke_button__indent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -984px !important;}.cke_ltr .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1008px !important;}.cke_rtl .cke_button__outdent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1032px !important;}.cke_ltr .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1056px !important;}.cke_button__smiley_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1080px !important;}.cke_button__justifyblock_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1104px !important;}.cke_button__justifycenter_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1128px !important;}.cke_button__justifyleft_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1152px !important;}.cke_button__justifyright_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1176px !important;}.cke_button__language_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1200px !important;}.cke_rtl .cke_button__anchor_icon, .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1224px !important;}.cke_ltr .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1248px !important;}.cke_button__link_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1272px !important;}.cke_button__unlink_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1296px !important;}.cke_rtl .cke_button__bulletedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1320px !important;}.cke_ltr .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1344px !important;}.cke_rtl .cke_button__numberedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1368px !important;}.cke_ltr .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1392px !important;}.cke_button__maximize_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1416px !important;}.cke_rtl .cke_button__newpage_icon, .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1440px !important;}.cke_ltr .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1464px !important;}.cke_rtl .cke_button__pagebreak_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1488px !important;}.cke_ltr .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1512px !important;}.cke_rtl .cke_button__pastetext_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1536px !important;}.cke_ltr .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1560px !important;}.cke_rtl .cke_button__pastefromword_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1584px !important;}.cke_ltr .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1608px !important;}.cke_rtl .cke_button__preview_icon, .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1632px !important;}.cke_ltr .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1656px !important;}.cke_button__print_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1680px !important;}.cke_button__removeformat_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1704px !important;}.cke_button__save_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1728px !important;}.cke_button__selectall_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1752px !important;}.cke_rtl .cke_button__showblocks_icon, .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1776px !important;}.cke_ltr .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1800px !important;}.cke_rtl .cke_button__source_icon, .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1824px !important;}.cke_ltr .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1848px !important;}.cke_button__specialchar_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1872px !important;}.cke_button__scayt_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1896px !important;}.cke_button__table_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1920px !important;}.cke_rtl .cke_button__redo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1944px !important;}.cke_ltr .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1968px !important;}.cke_rtl .cke_button__undo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1992px !important;}.cke_ltr .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2016px !important;}.cke_button__spellchecker_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2040px !important;}.cke_hidpi .cke_button__about_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -0px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bold_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -24px !important;background-size: 16px !important;}.cke_hidpi .cke_button__italic_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -48px !important;background-size: 16px !important;}.cke_hidpi .cke_button__strike_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -72px !important;background-size: 16px !important;}.cke_hidpi .cke_button__subscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -96px !important;background-size: 16px !important;}.cke_hidpi .cke_button__superscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -120px !important;background-size: 16px !important;}.cke_hidpi .cke_button__underline_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -144px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidiltr_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -168px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidirtl_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -192px !important;background-size: 16px !important;}.cke_hidpi .cke_button__blockquote_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -216px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__copy_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -240px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__copy_icon,.cke_ltr.cke_hidpi .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -264px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__cut_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -288px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__cut_icon,.cke_ltr.cke_hidpi .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -312px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__paste_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -336px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__paste_icon,.cke_ltr.cke_hidpi .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -360px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bgcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -384px !important;background-size: 16px !important;}.cke_hidpi .cke_button__textcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -408px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__templates_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -432px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__templates_icon,.cke_ltr.cke_hidpi .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -456px !important;background-size: 16px !important;}.cke_hidpi .cke_button__copyformatting_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -480px !important;background-size: 16px !important;}.cke_hidpi .cke_button__creatediv_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -504px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__find_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -528px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__find_icon,.cke_ltr.cke_hidpi .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -552px !important;background-size: 16px !important;}.cke_hidpi .cke_button__replace_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -576px !important;background-size: 16px !important;}.cke_hidpi .cke_button__flash_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -600px !important;background-size: 16px !important;}.cke_hidpi .cke_button__button_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -624px !important;background-size: 16px !important;}.cke_hidpi .cke_button__checkbox_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -648px !important;background-size: 16px !important;}.cke_hidpi .cke_button__form_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -672px !important;background-size: 16px !important;}.cke_hidpi .cke_button__hiddenfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -696px !important;background-size: 16px !important;}.cke_hidpi .cke_button__imagebutton_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -720px !important;background-size: 16px !important;}.cke_hidpi .cke_button__radio_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -744px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__select_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -768px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__select_icon,.cke_ltr.cke_hidpi .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -792px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textarea_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -816px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textarea_icon,.cke_ltr.cke_hidpi .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -840px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textfield_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -864px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textfield_icon,.cke_ltr.cke_hidpi .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -888px !important;background-size: 16px !important;}.cke_hidpi .cke_button__horizontalrule_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -912px !important;background-size: 16px !important;}.cke_hidpi .cke_button__iframe_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -936px !important;background-size: 16px !important;}.cke_hidpi .cke_button__image_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -960px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__indent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -984px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__indent_icon,.cke_ltr.cke_hidpi .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1008px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__outdent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1032px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__outdent_icon,.cke_ltr.cke_hidpi .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1056px !important;background-size: 16px !important;}.cke_hidpi .cke_button__smiley_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1080px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyblock_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1104px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifycenter_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1128px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyleft_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1152px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyright_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1176px !important;background-size: 16px !important;}.cke_hidpi .cke_button__language_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1200px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__anchor_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1224px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__anchor_icon,.cke_ltr.cke_hidpi .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1248px !important;background-size: 16px !important;}.cke_hidpi .cke_button__link_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1272px !important;background-size: 16px !important;}.cke_hidpi .cke_button__unlink_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1296px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__bulletedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1320px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__bulletedlist_icon,.cke_ltr.cke_hidpi .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1344px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__numberedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1368px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__numberedlist_icon,.cke_ltr.cke_hidpi .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1392px !important;background-size: 16px !important;}.cke_hidpi .cke_button__maximize_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1416px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__newpage_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1440px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__newpage_icon,.cke_ltr.cke_hidpi .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1464px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pagebreak_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1488px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pagebreak_icon,.cke_ltr.cke_hidpi .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1512px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastetext_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1536px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastetext_icon,.cke_ltr.cke_hidpi .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1560px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastefromword_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1584px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastefromword_icon,.cke_ltr.cke_hidpi .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1608px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__preview_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1632px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__preview_icon,.cke_ltr.cke_hidpi .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1656px !important;background-size: 16px !important;}.cke_hidpi .cke_button__print_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1680px !important;background-size: 16px !important;}.cke_hidpi .cke_button__removeformat_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1704px !important;background-size: 16px !important;}.cke_hidpi .cke_button__save_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1728px !important;background-size: 16px !important;}.cke_hidpi .cke_button__selectall_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1752px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__showblocks_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1776px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__showblocks_icon,.cke_ltr.cke_hidpi .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1800px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__source_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1824px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__source_icon,.cke_ltr.cke_hidpi .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1848px !important;background-size: 16px !important;}.cke_hidpi .cke_button__specialchar_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1872px !important;background-size: 16px !important;}.cke_hidpi .cke_button__scayt_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1896px !important;background-size: 16px !important;}.cke_hidpi .cke_button__table_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1920px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__redo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1944px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__redo_icon,.cke_ltr.cke_hidpi .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1968px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__undo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1992px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__undo_icon,.cke_ltr.cke_hidpi .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2016px !important;background-size: 16px !important;}.cke_hidpi .cke_button__spellchecker_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2040px !important;background-size: 16px !important;} \ No newline at end of file diff --git a/static/plugs/ckeditor/skins/moono-lisa/editor_gecko.css b/static/plugs/ckeditor/skins/moono-lisa/editor_gecko.css new file mode 100644 index 000000000..861a27a39 --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/editor_gecko.css @@ -0,0 +1,5 @@ +/* +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +.cke_reset{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none}.cke_reset_all,.cke_reset_all *,.cke_reset_all a,.cke_reset_all textarea{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none;border-collapse:collapse;font:normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;color:#000;text-align:left;white-space:nowrap;cursor:auto;float:none}.cke_reset_all .cke_rtl *{text-align:right}.cke_reset_all iframe{vertical-align:inherit}.cke_reset_all textarea{white-space:pre-wrap}.cke_reset_all textarea,.cke_reset_all input[type="text"],.cke_reset_all input[type="password"]{cursor:text}.cke_reset_all textarea[disabled],.cke_reset_all input[type="text"][disabled],.cke_reset_all input[type="password"][disabled]{cursor:default}.cke_reset_all fieldset{padding:10px;border:2px groove #e0dfe3}.cke_reset_all select{box-sizing:border-box}.cke_reset_all table{table-layout:auto}.cke_chrome{display:block;border:1px solid #d1d1d1;padding:0}.cke_inner{display:block;background:#fff;padding:0;-webkit-touch-callout:none}.cke_float{border:0}.cke_float .cke_inner{padding-bottom:0}.cke_top,.cke_contents,.cke_bottom{display:block;overflow:hidden}.cke_top{border-bottom:1px solid #d1d1d1;background:#f8f8f8;padding:6px 8px 2px;white-space:normal}.cke_float .cke_top{border:1px solid #d1d1d1}.cke_bottom{padding:6px 8px 2px;position:relative;border-top:1px solid #d1d1d1;background:#f8f8f8}.cke_browser_ios .cke_contents{overflow-y:auto;-webkit-overflow-scrolling:touch}.cke_resizer{width:0;height:0;overflow:hidden;border-width:10px 10px 0 0;border-color:transparent #bcbcbc transparent transparent;border-style:dashed solid dashed dashed;font-size:0;vertical-align:bottom;margin-top:6px;margin-bottom:2px}.cke_hc .cke_resizer{font-size:15px;width:auto;height:auto;border-width:0}.cke_resizer_ltr{cursor:se-resize;float:right;margin-right:-4px}.cke_resizer_rtl{border-width:10px 0 0 10px;border-color:transparent transparent transparent #bcbcbc;border-style:dashed dashed dashed solid;cursor:sw-resize;float:left;margin-left:-4px;right:auto}.cke_wysiwyg_div{display:block;height:100%;overflow:auto;padding:0 8px;outline-style:none;box-sizing:border-box}.cke_panel{visibility:visible;width:120px;height:100px;overflow:hidden;background-color:#fff;border:1px solid #d1d1d1}.cke_menu_panel{padding:0;margin:0}.cke_combopanel{width:150px;height:170px}.cke_panel_frame{width:100%;height:100%;font-size:12px;overflow:auto;overflow-x:hidden}.cke_panel_container{overflow-y:auto;overflow-x:hidden}.cke_panel_block:focus{outline:0}.cke_panel_list{margin:0;padding:0;list-style-type:none;white-space:nowrap}.cke_panel_listItem{margin:0;padding:0}.cke_panel_listItem a{padding:6px 7px;display:block;color:inherit!important;text-decoration:none;overflow:hidden;text-overflow:ellipsis}.cke_hc .cke_panel_listItem a{border-style:none}.cke_panel_listItem.cke_selected a,.cke_panel_listItem a:hover,.cke_panel_listItem a:focus,.cke_panel_listItem a:active{background-color:#e9e9e9}.cke_panel_listItem a:focus{outline:1px dotted #000}.cke_hc .cke_panel_listItem a:hover,.cke_hc .cke_panel_listItem a:focus,.cke_hc .cke_panel_listItem a:active{border:2px solid;padding:4px 5px}.cke_panel_listItem p,.cke_panel_listItem h1,.cke_panel_listItem h2,.cke_panel_listItem h3,.cke_panel_listItem h4,.cke_panel_listItem h5,.cke_panel_listItem h6,.cke_panel_listItem pre{margin-top:0;margin-bottom:0}.cke_panel_grouptitle{cursor:default;font-size:11px;font-weight:bold;white-space:nowrap;margin:0;padding:6px 6px 7px 6px;color:#484848;border-bottom:1px solid #d1d1d1;background:#f8f8f8}.cke_colorblock{padding:10px;font-size:11px;font-family:'Microsoft Sans Serif',Tahoma,Arial,Verdana,Sans-Serif}.cke_colorblock,.cke_colorblock a{text-decoration:none;color:#000}a.cke_colorbox{padding:2px;float:left;width:20px;height:20px}.cke_rtl a.cke_colorbox{float:right}a:hover.cke_colorbox,a:focus.cke_colorbox,a:active.cke_colorbox{outline:0;padding:0;border:2px solid #139ff7}a:hover.cke_colorbox{border-color:#bcbcbc}span.cke_colorbox{width:20px;height:20px;float:left}.cke_rtl span.cke_colorbox{float:right}a.cke_colorauto,a.cke_colormore{border:#fff 1px solid;padding:3px;display:block;cursor:pointer}a.cke_colorauto{padding:0;border:1px solid transparent;margin-bottom:6px;height:26px;line-height:26px}a.cke_colormore{margin-top:10px;height:20px;line-height:19px}a:hover.cke_colorauto,a:hover.cke_colormore,a:focus.cke_colorauto,a:focus.cke_colormore,a:active.cke_colorauto,a:active.cke_colormore{outline:0;border:#139ff7 1px solid;background-color:#f8f8f8}a:hover.cke_colorauto,a:hover.cke_colormore{border-color:#bcbcbc}.cke_colorauto span.cke_colorbox{width:18px;height:18px;border:1px solid #808080;margin-left:1px;margin-top:3px}.cke_rtl .cke_colorauto span.cke_colorbox{margin-left:0;margin-right:1px}span.cke_colorbox[style*="#ffffff"],span.cke_colorbox[style*="#FFFFFF"],span.cke_colorbox[style="background-color:#fff"],span.cke_colorbox[style="background-color:#FFF"],span.cke_colorbox[style*="rgb(255,255,255)"],span.cke_colorbox[style*="rgb(255, 255, 255)"]{border:1px solid #808080;width:18px;height:18px}.cke_toolbar{float:left}.cke_rtl .cke_toolbar{float:right}.cke_toolgroup{border:0;float:left;margin:1px 2px 6px 0;padding-right:3px}.cke_rtl .cke_toolgroup{float:right;margin:1px 0 6px 2px;padding-left:3px;padding-right:0}.cke_hc .cke_toolgroup{margin-right:5px;margin-bottom:5px}.cke_hc.cke_rtl .cke_toolgroup{margin-right:0;margin-left:5px}a.cke_button{display:inline-block;height:18px;padding:4px 6px;outline:0;cursor:default;float:left;border:0;position:relative}.cke_rtl a.cke_button{float:right}.cke_hc a.cke_button{border:1px solid black;padding:3px 5px;margin:0 3px 5px 0}.cke_hc.cke_rtl a.cke_button{margin:0 0 5px 3px}a.cke_button_on{background:#fff;border:1px #bcbcbc solid;padding:3px 5px}a.cke_button_off:hover,a.cke_button_off:focus,a.cke_button_off:active{background:#e5e5e5;border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_on,.cke_hc a.cke_button_off:hover,.cke_hc a.cke_button_off:focus,.cke_hc a.cke_button_off:active{background:#e5e5e5;border:3px solid #000;padding:1px 3px}a.cke_button_disabled:hover,a.cke_button_disabled:focus,a.cke_button_disabled:active{border:0;padding:4px 6px;background-color:transparent}a.cke_button_disabled:focus{border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_disabled:hover,.cke_hc a.cke_button_disabled:focus,.cke_hc a.cke_button_disabled:active{border:1px solid #acacac;padding:3px 5px;margin:0 3px 5px 0}.cke_hc a.cke_button_disabled:focus{border:3px solid #000;padding:1px 3px}.cke_hc.cke_rtl a.cke_button_disabled:hover,.cke_hc.cke_rtl a.cke_button_disabled:focus,.cke_hc.cke_rtl a.cke_button_disabled:active{margin:0 0 5px 3px}a.cke_button_disabled .cke_button_icon,a.cke_button_disabled .cke_button_arrow{opacity:.3}.cke_hc a.cke_button_disabled{border-color:#acacac}.cke_hc a.cke_button_disabled .cke_button_icon,.cke_hc a.cke_button_disabled .cke_button_label{opacity:.5}.cke_toolgroup a.cke_button:last-child:after,.cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:4px;top:0;right:-3px}.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-right:0;right:auto;border-left:1px solid #bcbcbc;top:0;left:-3px}.cke_hc .cke_toolgroup a.cke_button:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-color:#000;top:0;right:-7px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{top:0;right:auto;left:-7px}.cke_toolgroup a.cke_button:hover:last-child:after,.cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:-4px}.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:auto;left:-4px}.cke_hc .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:-9px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:auto;left:-9px}.cke_toolbar.cke_toolbar_last .cke_toolgroup a.cke_button:last-child:after{content:none;border:0;width:0;height:0}.cke_button_icon{cursor:inherit;background-repeat:no-repeat;margin-top:1px;width:16px;height:16px;float:left;display:inline-block}.cke_rtl .cke_button_icon{float:right}.cke_hc .cke_button_icon{display:none}.cke_button_label{display:none;padding-left:3px;margin-top:1px;line-height:17px;vertical-align:middle;float:left;cursor:default;color:#484848}.cke_rtl .cke_button_label{padding-right:3px;padding-left:0;float:right}.cke_hc .cke_button_label{padding:0;display:inline-block;font-size:12px}.cke_button_arrow{display:inline-block;margin:8px 0 0 1px;width:0;height:0;cursor:default;vertical-align:top;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_rtl .cke_button_arrow{margin-right:5px;margin-left:0}.cke_hc .cke_button_arrow{font-size:10px;margin:3px 0 0 3px;width:auto;border:0}.cke_toolbar_separator{float:left;background-color:#bcbcbc;margin:4px 2px 0 2px;height:18px;width:1px}.cke_rtl .cke_toolbar_separator{float:right}.cke_hc .cke_toolbar_separator{background-color:#000;margin-left:2px;margin-right:5px;margin-bottom:9px}.cke_hc.cke_rtl .cke_toolbar_separator{margin-left:5px;margin-right:2px}.cke_toolbar_break{display:block;clear:left}.cke_rtl .cke_toolbar_break{clear:right}a.cke_toolbox_collapser{width:12px;height:11px;float:right;margin:11px 0 0;font-size:0;cursor:default;text-align:center;border:1px solid #bcbcbc}.cke_rtl .cke_toolbox_collapser{float:left}.cke_toolbox_collapser:hover{background:#e5e5e5}.cke_toolbox_collapser.cke_toolbox_collapser_min{margin:0 2px 4px}.cke_toolbox_collapser .cke_arrow{display:inline-block;height:0;width:0;font-size:0;margin-top:1px;border:3px solid transparent;border-bottom-color:#484848}.cke_toolbox_collapser.cke_toolbox_collapser_min .cke_arrow{margin-top:4px;border-bottom-color:transparent;border-top-color:#484848}.cke_hc .cke_toolbox_collapser .cke_arrow{font-size:8px;width:auto;border:0;margin-top:0}.cke_menuitem span{cursor:default}.cke_menubutton{display:block}.cke_hc .cke_menubutton{padding:2px}.cke_menubutton:hover,.cke_menubutton:focus,.cke_menubutton:active{background-color:#e9e9e9;display:block;outline:1px dotted}.cke_menubutton:hover{outline:0}.cke_hc .cke_menubutton:hover,.cke_hc .cke_menubutton:focus,.cke_hc .cke_menubutton:active{border:2px solid;padding:0}.cke_menubutton_disabled:hover,.cke_menubutton_disabled:focus,.cke_menubutton_disabled:active{background-color:transparent;outline:0}.cke_menubutton_inner{display:table-row}.cke_menubutton_icon,.cke_menubutton_label,.cke_menuarrow{display:table-cell}.cke_menubutton_icon{background-color:#f8f8f8;padding:6px 4px}.cke_hc .cke_menubutton_icon{height:16px;width:0;padding:4px 0}.cke_menubutton:hover .cke_menubutton_icon,.cke_menubutton:focus .cke_menubutton_icon,.cke_menubutton:active .cke_menubutton_icon{background-color:#e9e9e9}.cke_menubutton_disabled:hover .cke_menubutton_icon,.cke_menubutton_disabled:focus .cke_menubutton_icon,.cke_menubutton_disabled:active .cke_menubutton_icon{background-color:#f8f8f8;outline:0}.cke_menuitem .cke_menubutton_on{background-color:#e9e9e9;border:1px solid #dedede;outline:0}.cke_menubutton_on .cke_menubutton_icon{padding-right:3px;background-color:#e9e9e9}.cke_menubutton_label{padding:0 5px;background-color:transparent;width:100%;vertical-align:middle}.cke_menubutton_shortcut{color:#979797}.cke_menubutton_disabled .cke_menubutton_label{opacity:.3;filter:alpha(opacity=30)}.cke_panel_frame .cke_menubutton_label{display:none}.cke_menuseparator{background-color:#d1d1d1;height:1px}.cke_menuarrow{background:transparent url(images/arrow.png) no-repeat 0 10px;padding:0 5px}.cke_rtl .cke_menuarrow{background-position:5px -13px;background-repeat:no-repeat}.cke_hc .cke_menuarrow{background-image:none}.cke_menuarrow span{display:none}.cke_hc .cke_menuarrow span{vertical-align:middle;display:inline}.cke_combo{display:inline-block;float:left;position:relative;margin-bottom:5px}.cke_rtl .cke_combo{float:right}.cke_hc .cke_combo{margin-top:1px;margin-bottom:10px}.cke_combo:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:5px;top:0;right:0}.cke_rtl .cke_combo:after{border-right:0;border-left:1px solid #bcbcbc;right:auto;left:0}.cke_hc .cke_combo:after{border-color:#000}a.cke_combo_button{cursor:default;display:inline-block;float:left;margin:0;padding:1px}.cke_rtl a.cke_combo_button{float:right}.cke_hc a.cke_combo_button{padding:4px}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:hover,.cke_combo_off a.cke_combo_button:focus,.cke_combo_off a.cke_combo_button:active{background:#e5e5e5;border:1px solid #bcbcbc;padding:0 0 0 1px;margin-left:-1px}.cke_combo_off a.cke_combo_button:focus{outline:0}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:active{background:#fff}.cke_rtl .cke_combo_on a.cke_combo_button,.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:0 1px 0 0;margin-left:0;margin-right:-1px}.cke_hc .cke_combo_on a.cke_combo_button,.cke_hc .cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_combo_off a.cke_combo_button:active{border:3px solid #000;padding:1px 1px 1px 2px}.cke_hc.cke_rtl .cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:1px 2px 1px 1px}.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 0 0 3px;margin-left:-3px}.cke_rtl .cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 3px 0 0;margin-left:0;margin-right:-3px}.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 1px 1px 7px;margin-left:-6px}.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 7px 1px 1px;margin-left:0;margin-right:-6px}.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0;margin:0}.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px;margin:0}.cke_toolbar .cke_combo+.cke_toolbar_end,.cke_toolbar .cke_combo+.cke_toolgroup{margin-right:0;margin-left:2px}.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:2px}.cke_hc .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:5px}.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:5px}.cke_toolbar.cke_toolbar_last .cke_combo:nth-last-child(-n+2):after{content:none;border:0;width:0;height:0}.cke_combo_text{line-height:26px;padding-left:10px;text-overflow:ellipsis;overflow:hidden;float:left;cursor:default;color:#484848;width:60px}.cke_rtl .cke_combo_text{float:right;text-align:right;padding-left:0;padding-right:10px}.cke_hc .cke_combo_text{line-height:18px;font-size:12px}.cke_combo_open{cursor:default;display:inline-block;font-size:0;height:19px;line-height:17px;margin:1px 10px 1px;width:5px}.cke_hc .cke_combo_open{height:12px}.cke_combo_arrow{cursor:default;margin:11px 0 0;float:left;height:0;width:0;font-size:0;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_hc .cke_combo_arrow{font-size:10px;width:auto;border:0;margin-top:3px}.cke_combo_label{display:none;float:left;line-height:26px;vertical-align:top;margin-right:5px}.cke_rtl .cke_combo_label{float:right;margin-left:5px;margin-right:0}.cke_combo_disabled .cke_combo_inlinelabel,.cke_combo_disabled .cke_combo_open{opacity:.3}.cke_path{float:left;margin:-2px 0 2px}a.cke_path_item,span.cke_path_empty{display:inline-block;float:left;padding:3px 4px;margin-right:2px;cursor:default;text-decoration:none;outline:0;border:0;color:#484848;font-weight:bold;font-size:11px}.cke_rtl .cke_path,.cke_rtl .cke_path_item,.cke_rtl .cke_path_empty{float:right}a.cke_path_item:hover,a.cke_path_item:focus,a.cke_path_item:active{background-color:#e5e5e5}.cke_hc a.cke_path_item:hover,.cke_hc a.cke_path_item:focus,.cke_hc a.cke_path_item:active{border:2px solid;padding:1px 2px}.cke_button__source_label,.cke_button__sourcedialog_label{display:inline}.cke_combopanel__fontsize{width:135px}textarea.cke_source{font-family:'Courier New',Monospace;font-size:small;background-color:#fff;white-space:pre-wrap;border:0;padding:0;margin:0;display:block}.cke_wysiwyg_frame,.cke_wysiwyg_div{background-color:#fff}.cke_notifications_area{pointer-events:none}.cke_notification{pointer-events:auto;position:relative;margin:10px;width:300px;color:white;text-align:center;opacity:.95;filter:alpha(opacity = 95);-webkit-animation:fadeIn .7s;animation:fadeIn .7s}.cke_notification_message a{color:#12306f}@-webkit-keyframes fadeIn{from{opacity:.4}to{opacity:.95}}@keyframes fadeIn{from{opacity:.4}to{opacity:.95}}.cke_notification_success{background:#72b572;border:1px solid #63a563}.cke_notification_warning{background:#c83939;border:1px solid #902b2b}.cke_notification_info{background:#2e9ad0;border:1px solid #0f74a8}.cke_notification_info span.cke_notification_progress{background-color:#0f74a8;display:block;padding:0;margin:0;height:100%;overflow:hidden;position:absolute;z-index:1}.cke_notification_message{position:relative;margin:4px 23px 3px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;z-index:4;text-overflow:ellipsis;overflow:hidden}.cke_notification_close{background-image:url(images/close.png);background-repeat:no-repeat;background-position:50%;position:absolute;cursor:pointer;text-align:center;height:20px;width:20px;top:1px;right:1px;padding:0;margin:0;z-index:5;opacity:.6;filter:alpha(opacity = 60)}.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_notification_close span{display:none}.cke_notification_warning a.cke_notification_close{opacity:.8;filter:alpha(opacity = 80)}.cke_notification_warning a.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_chrome{visibility:inherit}.cke_voice_label{display:none}legend.cke_voice_label{display:none}.cke_bottom{padding-bottom:3px}.cke_combo_text{margin-bottom:-1px;margin-top:1px}.cke_button__about_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -0px !important;}.cke_button__bold_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -24px !important;}.cke_button__italic_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -48px !important;}.cke_button__strike_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -72px !important;}.cke_button__subscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -96px !important;}.cke_button__superscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -120px !important;}.cke_button__underline_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -144px !important;}.cke_button__bidiltr_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -168px !important;}.cke_button__bidirtl_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -192px !important;}.cke_button__blockquote_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -216px !important;}.cke_rtl .cke_button__copy_icon, .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -240px !important;}.cke_ltr .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -264px !important;}.cke_rtl .cke_button__cut_icon, .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -288px !important;}.cke_ltr .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -312px !important;}.cke_rtl .cke_button__paste_icon, .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -336px !important;}.cke_ltr .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -360px !important;}.cke_button__bgcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -384px !important;}.cke_button__textcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -408px !important;}.cke_rtl .cke_button__templates_icon, .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -432px !important;}.cke_ltr .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -456px !important;}.cke_button__copyformatting_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -480px !important;}.cke_button__creatediv_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -504px !important;}.cke_rtl .cke_button__find_icon, .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -528px !important;}.cke_ltr .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -552px !important;}.cke_button__replace_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -576px !important;}.cke_button__flash_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -600px !important;}.cke_button__button_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -624px !important;}.cke_button__checkbox_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -648px !important;}.cke_button__form_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -672px !important;}.cke_button__hiddenfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -696px !important;}.cke_button__imagebutton_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -720px !important;}.cke_button__radio_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -744px !important;}.cke_rtl .cke_button__select_icon, .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -768px !important;}.cke_ltr .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -792px !important;}.cke_rtl .cke_button__textarea_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -816px !important;}.cke_ltr .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -840px !important;}.cke_rtl .cke_button__textfield_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -864px !important;}.cke_ltr .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -888px !important;}.cke_button__horizontalrule_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -912px !important;}.cke_button__iframe_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -936px !important;}.cke_button__image_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -960px !important;}.cke_rtl .cke_button__indent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -984px !important;}.cke_ltr .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1008px !important;}.cke_rtl .cke_button__outdent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1032px !important;}.cke_ltr .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1056px !important;}.cke_button__smiley_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1080px !important;}.cke_button__justifyblock_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1104px !important;}.cke_button__justifycenter_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1128px !important;}.cke_button__justifyleft_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1152px !important;}.cke_button__justifyright_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1176px !important;}.cke_button__language_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1200px !important;}.cke_rtl .cke_button__anchor_icon, .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1224px !important;}.cke_ltr .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1248px !important;}.cke_button__link_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1272px !important;}.cke_button__unlink_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1296px !important;}.cke_rtl .cke_button__bulletedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1320px !important;}.cke_ltr .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1344px !important;}.cke_rtl .cke_button__numberedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1368px !important;}.cke_ltr .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1392px !important;}.cke_button__maximize_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1416px !important;}.cke_rtl .cke_button__newpage_icon, .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1440px !important;}.cke_ltr .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1464px !important;}.cke_rtl .cke_button__pagebreak_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1488px !important;}.cke_ltr .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1512px !important;}.cke_rtl .cke_button__pastetext_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1536px !important;}.cke_ltr .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1560px !important;}.cke_rtl .cke_button__pastefromword_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1584px !important;}.cke_ltr .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1608px !important;}.cke_rtl .cke_button__preview_icon, .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1632px !important;}.cke_ltr .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1656px !important;}.cke_button__print_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1680px !important;}.cke_button__removeformat_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1704px !important;}.cke_button__save_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1728px !important;}.cke_button__selectall_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1752px !important;}.cke_rtl .cke_button__showblocks_icon, .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1776px !important;}.cke_ltr .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1800px !important;}.cke_rtl .cke_button__source_icon, .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1824px !important;}.cke_ltr .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1848px !important;}.cke_button__specialchar_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1872px !important;}.cke_button__scayt_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1896px !important;}.cke_button__table_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1920px !important;}.cke_rtl .cke_button__redo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1944px !important;}.cke_ltr .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1968px !important;}.cke_rtl .cke_button__undo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1992px !important;}.cke_ltr .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2016px !important;}.cke_button__spellchecker_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2040px !important;}.cke_hidpi .cke_button__about_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -0px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bold_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -24px !important;background-size: 16px !important;}.cke_hidpi .cke_button__italic_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -48px !important;background-size: 16px !important;}.cke_hidpi .cke_button__strike_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -72px !important;background-size: 16px !important;}.cke_hidpi .cke_button__subscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -96px !important;background-size: 16px !important;}.cke_hidpi .cke_button__superscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -120px !important;background-size: 16px !important;}.cke_hidpi .cke_button__underline_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -144px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidiltr_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -168px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidirtl_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -192px !important;background-size: 16px !important;}.cke_hidpi .cke_button__blockquote_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -216px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__copy_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -240px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__copy_icon,.cke_ltr.cke_hidpi .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -264px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__cut_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -288px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__cut_icon,.cke_ltr.cke_hidpi .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -312px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__paste_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -336px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__paste_icon,.cke_ltr.cke_hidpi .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -360px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bgcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -384px !important;background-size: 16px !important;}.cke_hidpi .cke_button__textcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -408px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__templates_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -432px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__templates_icon,.cke_ltr.cke_hidpi .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -456px !important;background-size: 16px !important;}.cke_hidpi .cke_button__copyformatting_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -480px !important;background-size: 16px !important;}.cke_hidpi .cke_button__creatediv_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -504px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__find_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -528px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__find_icon,.cke_ltr.cke_hidpi .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -552px !important;background-size: 16px !important;}.cke_hidpi .cke_button__replace_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -576px !important;background-size: 16px !important;}.cke_hidpi .cke_button__flash_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -600px !important;background-size: 16px !important;}.cke_hidpi .cke_button__button_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -624px !important;background-size: 16px !important;}.cke_hidpi .cke_button__checkbox_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -648px !important;background-size: 16px !important;}.cke_hidpi .cke_button__form_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -672px !important;background-size: 16px !important;}.cke_hidpi .cke_button__hiddenfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -696px !important;background-size: 16px !important;}.cke_hidpi .cke_button__imagebutton_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -720px !important;background-size: 16px !important;}.cke_hidpi .cke_button__radio_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -744px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__select_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -768px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__select_icon,.cke_ltr.cke_hidpi .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -792px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textarea_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -816px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textarea_icon,.cke_ltr.cke_hidpi .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -840px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textfield_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -864px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textfield_icon,.cke_ltr.cke_hidpi .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -888px !important;background-size: 16px !important;}.cke_hidpi .cke_button__horizontalrule_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -912px !important;background-size: 16px !important;}.cke_hidpi .cke_button__iframe_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -936px !important;background-size: 16px !important;}.cke_hidpi .cke_button__image_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -960px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__indent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -984px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__indent_icon,.cke_ltr.cke_hidpi .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1008px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__outdent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1032px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__outdent_icon,.cke_ltr.cke_hidpi .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1056px !important;background-size: 16px !important;}.cke_hidpi .cke_button__smiley_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1080px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyblock_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1104px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifycenter_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1128px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyleft_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1152px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyright_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1176px !important;background-size: 16px !important;}.cke_hidpi .cke_button__language_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1200px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__anchor_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1224px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__anchor_icon,.cke_ltr.cke_hidpi .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1248px !important;background-size: 16px !important;}.cke_hidpi .cke_button__link_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1272px !important;background-size: 16px !important;}.cke_hidpi .cke_button__unlink_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1296px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__bulletedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1320px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__bulletedlist_icon,.cke_ltr.cke_hidpi .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1344px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__numberedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1368px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__numberedlist_icon,.cke_ltr.cke_hidpi .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1392px !important;background-size: 16px !important;}.cke_hidpi .cke_button__maximize_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1416px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__newpage_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1440px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__newpage_icon,.cke_ltr.cke_hidpi .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1464px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pagebreak_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1488px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pagebreak_icon,.cke_ltr.cke_hidpi .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1512px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastetext_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1536px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastetext_icon,.cke_ltr.cke_hidpi .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1560px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastefromword_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1584px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastefromword_icon,.cke_ltr.cke_hidpi .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1608px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__preview_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1632px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__preview_icon,.cke_ltr.cke_hidpi .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1656px !important;background-size: 16px !important;}.cke_hidpi .cke_button__print_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1680px !important;background-size: 16px !important;}.cke_hidpi .cke_button__removeformat_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1704px !important;background-size: 16px !important;}.cke_hidpi .cke_button__save_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1728px !important;background-size: 16px !important;}.cke_hidpi .cke_button__selectall_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1752px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__showblocks_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1776px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__showblocks_icon,.cke_ltr.cke_hidpi .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1800px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__source_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1824px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__source_icon,.cke_ltr.cke_hidpi .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1848px !important;background-size: 16px !important;}.cke_hidpi .cke_button__specialchar_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1872px !important;background-size: 16px !important;}.cke_hidpi .cke_button__scayt_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1896px !important;background-size: 16px !important;}.cke_hidpi .cke_button__table_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1920px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__redo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1944px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__redo_icon,.cke_ltr.cke_hidpi .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1968px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__undo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1992px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__undo_icon,.cke_ltr.cke_hidpi .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2016px !important;background-size: 16px !important;}.cke_hidpi .cke_button__spellchecker_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2040px !important;background-size: 16px !important;} \ No newline at end of file diff --git a/static/plugs/ckeditor/skins/moono-lisa/editor_ie.css b/static/plugs/ckeditor/skins/moono-lisa/editor_ie.css new file mode 100644 index 000000000..d8b66e82b --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/editor_ie.css @@ -0,0 +1,5 @@ +/* +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +.cke_reset{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none}.cke_reset_all,.cke_reset_all *,.cke_reset_all a,.cke_reset_all textarea{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none;border-collapse:collapse;font:normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;color:#000;text-align:left;white-space:nowrap;cursor:auto;float:none}.cke_reset_all .cke_rtl *{text-align:right}.cke_reset_all iframe{vertical-align:inherit}.cke_reset_all textarea{white-space:pre-wrap}.cke_reset_all textarea,.cke_reset_all input[type="text"],.cke_reset_all input[type="password"]{cursor:text}.cke_reset_all textarea[disabled],.cke_reset_all input[type="text"][disabled],.cke_reset_all input[type="password"][disabled]{cursor:default}.cke_reset_all fieldset{padding:10px;border:2px groove #e0dfe3}.cke_reset_all select{box-sizing:border-box}.cke_reset_all table{table-layout:auto}.cke_chrome{display:block;border:1px solid #d1d1d1;padding:0}.cke_inner{display:block;background:#fff;padding:0;-webkit-touch-callout:none}.cke_float{border:0}.cke_float .cke_inner{padding-bottom:0}.cke_top,.cke_contents,.cke_bottom{display:block;overflow:hidden}.cke_top{border-bottom:1px solid #d1d1d1;background:#f8f8f8;padding:6px 8px 2px;white-space:normal}.cke_float .cke_top{border:1px solid #d1d1d1}.cke_bottom{padding:6px 8px 2px;position:relative;border-top:1px solid #d1d1d1;background:#f8f8f8}.cke_browser_ios .cke_contents{overflow-y:auto;-webkit-overflow-scrolling:touch}.cke_resizer{width:0;height:0;overflow:hidden;border-width:10px 10px 0 0;border-color:transparent #bcbcbc transparent transparent;border-style:dashed solid dashed dashed;font-size:0;vertical-align:bottom;margin-top:6px;margin-bottom:2px}.cke_hc .cke_resizer{font-size:15px;width:auto;height:auto;border-width:0}.cke_resizer_ltr{cursor:se-resize;float:right;margin-right:-4px}.cke_resizer_rtl{border-width:10px 0 0 10px;border-color:transparent transparent transparent #bcbcbc;border-style:dashed dashed dashed solid;cursor:sw-resize;float:left;margin-left:-4px;right:auto}.cke_wysiwyg_div{display:block;height:100%;overflow:auto;padding:0 8px;outline-style:none;box-sizing:border-box}.cke_panel{visibility:visible;width:120px;height:100px;overflow:hidden;background-color:#fff;border:1px solid #d1d1d1}.cke_menu_panel{padding:0;margin:0}.cke_combopanel{width:150px;height:170px}.cke_panel_frame{width:100%;height:100%;font-size:12px;overflow:auto;overflow-x:hidden}.cke_panel_container{overflow-y:auto;overflow-x:hidden}.cke_panel_block:focus{outline:0}.cke_panel_list{margin:0;padding:0;list-style-type:none;white-space:nowrap}.cke_panel_listItem{margin:0;padding:0}.cke_panel_listItem a{padding:6px 7px;display:block;color:inherit!important;text-decoration:none;overflow:hidden;text-overflow:ellipsis}.cke_hc .cke_panel_listItem a{border-style:none}.cke_panel_listItem.cke_selected a,.cke_panel_listItem a:hover,.cke_panel_listItem a:focus,.cke_panel_listItem a:active{background-color:#e9e9e9}.cke_panel_listItem a:focus{outline:1px dotted #000}.cke_hc .cke_panel_listItem a:hover,.cke_hc .cke_panel_listItem a:focus,.cke_hc .cke_panel_listItem a:active{border:2px solid;padding:4px 5px}.cke_panel_listItem p,.cke_panel_listItem h1,.cke_panel_listItem h2,.cke_panel_listItem h3,.cke_panel_listItem h4,.cke_panel_listItem h5,.cke_panel_listItem h6,.cke_panel_listItem pre{margin-top:0;margin-bottom:0}.cke_panel_grouptitle{cursor:default;font-size:11px;font-weight:bold;white-space:nowrap;margin:0;padding:6px 6px 7px 6px;color:#484848;border-bottom:1px solid #d1d1d1;background:#f8f8f8}.cke_colorblock{padding:10px;font-size:11px;font-family:'Microsoft Sans Serif',Tahoma,Arial,Verdana,Sans-Serif}.cke_colorblock,.cke_colorblock a{text-decoration:none;color:#000}a.cke_colorbox{padding:2px;float:left;width:20px;height:20px}.cke_rtl a.cke_colorbox{float:right}a:hover.cke_colorbox,a:focus.cke_colorbox,a:active.cke_colorbox{outline:0;padding:0;border:2px solid #139ff7}a:hover.cke_colorbox{border-color:#bcbcbc}span.cke_colorbox{width:20px;height:20px;float:left}.cke_rtl span.cke_colorbox{float:right}a.cke_colorauto,a.cke_colormore{border:#fff 1px solid;padding:3px;display:block;cursor:pointer}a.cke_colorauto{padding:0;border:1px solid transparent;margin-bottom:6px;height:26px;line-height:26px}a.cke_colormore{margin-top:10px;height:20px;line-height:19px}a:hover.cke_colorauto,a:hover.cke_colormore,a:focus.cke_colorauto,a:focus.cke_colormore,a:active.cke_colorauto,a:active.cke_colormore{outline:0;border:#139ff7 1px solid;background-color:#f8f8f8}a:hover.cke_colorauto,a:hover.cke_colormore{border-color:#bcbcbc}.cke_colorauto span.cke_colorbox{width:18px;height:18px;border:1px solid #808080;margin-left:1px;margin-top:3px}.cke_rtl .cke_colorauto span.cke_colorbox{margin-left:0;margin-right:1px}span.cke_colorbox[style*="#ffffff"],span.cke_colorbox[style*="#FFFFFF"],span.cke_colorbox[style="background-color:#fff"],span.cke_colorbox[style="background-color:#FFF"],span.cke_colorbox[style*="rgb(255,255,255)"],span.cke_colorbox[style*="rgb(255, 255, 255)"]{border:1px solid #808080;width:18px;height:18px}.cke_toolbar{float:left}.cke_rtl .cke_toolbar{float:right}.cke_toolgroup{border:0;float:left;margin:1px 2px 6px 0;padding-right:3px}.cke_rtl .cke_toolgroup{float:right;margin:1px 0 6px 2px;padding-left:3px;padding-right:0}.cke_hc .cke_toolgroup{margin-right:5px;margin-bottom:5px}.cke_hc.cke_rtl .cke_toolgroup{margin-right:0;margin-left:5px}a.cke_button{display:inline-block;height:18px;padding:4px 6px;outline:0;cursor:default;float:left;border:0;position:relative}.cke_rtl a.cke_button{float:right}.cke_hc a.cke_button{border:1px solid black;padding:3px 5px;margin:0 3px 5px 0}.cke_hc.cke_rtl a.cke_button{margin:0 0 5px 3px}a.cke_button_on{background:#fff;border:1px #bcbcbc solid;padding:3px 5px}a.cke_button_off:hover,a.cke_button_off:focus,a.cke_button_off:active{background:#e5e5e5;border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_on,.cke_hc a.cke_button_off:hover,.cke_hc a.cke_button_off:focus,.cke_hc a.cke_button_off:active{background:#e5e5e5;border:3px solid #000;padding:1px 3px}a.cke_button_disabled:hover,a.cke_button_disabled:focus,a.cke_button_disabled:active{border:0;padding:4px 6px;background-color:transparent}a.cke_button_disabled:focus{border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_disabled:hover,.cke_hc a.cke_button_disabled:focus,.cke_hc a.cke_button_disabled:active{border:1px solid #acacac;padding:3px 5px;margin:0 3px 5px 0}.cke_hc a.cke_button_disabled:focus{border:3px solid #000;padding:1px 3px}.cke_hc.cke_rtl a.cke_button_disabled:hover,.cke_hc.cke_rtl a.cke_button_disabled:focus,.cke_hc.cke_rtl a.cke_button_disabled:active{margin:0 0 5px 3px}a.cke_button_disabled .cke_button_icon,a.cke_button_disabled .cke_button_arrow{opacity:.3}.cke_hc a.cke_button_disabled{border-color:#acacac}.cke_hc a.cke_button_disabled .cke_button_icon,.cke_hc a.cke_button_disabled .cke_button_label{opacity:.5}.cke_toolgroup a.cke_button:last-child:after,.cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:4px;top:0;right:-3px}.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-right:0;right:auto;border-left:1px solid #bcbcbc;top:0;left:-3px}.cke_hc .cke_toolgroup a.cke_button:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-color:#000;top:0;right:-7px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{top:0;right:auto;left:-7px}.cke_toolgroup a.cke_button:hover:last-child:after,.cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:-4px}.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:auto;left:-4px}.cke_hc .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:-9px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:auto;left:-9px}.cke_toolbar.cke_toolbar_last .cke_toolgroup a.cke_button:last-child:after{content:none;border:0;width:0;height:0}.cke_button_icon{cursor:inherit;background-repeat:no-repeat;margin-top:1px;width:16px;height:16px;float:left;display:inline-block}.cke_rtl .cke_button_icon{float:right}.cke_hc .cke_button_icon{display:none}.cke_button_label{display:none;padding-left:3px;margin-top:1px;line-height:17px;vertical-align:middle;float:left;cursor:default;color:#484848}.cke_rtl .cke_button_label{padding-right:3px;padding-left:0;float:right}.cke_hc .cke_button_label{padding:0;display:inline-block;font-size:12px}.cke_button_arrow{display:inline-block;margin:8px 0 0 1px;width:0;height:0;cursor:default;vertical-align:top;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_rtl .cke_button_arrow{margin-right:5px;margin-left:0}.cke_hc .cke_button_arrow{font-size:10px;margin:3px 0 0 3px;width:auto;border:0}.cke_toolbar_separator{float:left;background-color:#bcbcbc;margin:4px 2px 0 2px;height:18px;width:1px}.cke_rtl .cke_toolbar_separator{float:right}.cke_hc .cke_toolbar_separator{background-color:#000;margin-left:2px;margin-right:5px;margin-bottom:9px}.cke_hc.cke_rtl .cke_toolbar_separator{margin-left:5px;margin-right:2px}.cke_toolbar_break{display:block;clear:left}.cke_rtl .cke_toolbar_break{clear:right}a.cke_toolbox_collapser{width:12px;height:11px;float:right;margin:11px 0 0;font-size:0;cursor:default;text-align:center;border:1px solid #bcbcbc}.cke_rtl .cke_toolbox_collapser{float:left}.cke_toolbox_collapser:hover{background:#e5e5e5}.cke_toolbox_collapser.cke_toolbox_collapser_min{margin:0 2px 4px}.cke_toolbox_collapser .cke_arrow{display:inline-block;height:0;width:0;font-size:0;margin-top:1px;border:3px solid transparent;border-bottom-color:#484848}.cke_toolbox_collapser.cke_toolbox_collapser_min .cke_arrow{margin-top:4px;border-bottom-color:transparent;border-top-color:#484848}.cke_hc .cke_toolbox_collapser .cke_arrow{font-size:8px;width:auto;border:0;margin-top:0}.cke_menuitem span{cursor:default}.cke_menubutton{display:block}.cke_hc .cke_menubutton{padding:2px}.cke_menubutton:hover,.cke_menubutton:focus,.cke_menubutton:active{background-color:#e9e9e9;display:block;outline:1px dotted}.cke_menubutton:hover{outline:0}.cke_hc .cke_menubutton:hover,.cke_hc .cke_menubutton:focus,.cke_hc .cke_menubutton:active{border:2px solid;padding:0}.cke_menubutton_disabled:hover,.cke_menubutton_disabled:focus,.cke_menubutton_disabled:active{background-color:transparent;outline:0}.cke_menubutton_inner{display:table-row}.cke_menubutton_icon,.cke_menubutton_label,.cke_menuarrow{display:table-cell}.cke_menubutton_icon{background-color:#f8f8f8;padding:6px 4px}.cke_hc .cke_menubutton_icon{height:16px;width:0;padding:4px 0}.cke_menubutton:hover .cke_menubutton_icon,.cke_menubutton:focus .cke_menubutton_icon,.cke_menubutton:active .cke_menubutton_icon{background-color:#e9e9e9}.cke_menubutton_disabled:hover .cke_menubutton_icon,.cke_menubutton_disabled:focus .cke_menubutton_icon,.cke_menubutton_disabled:active .cke_menubutton_icon{background-color:#f8f8f8;outline:0}.cke_menuitem .cke_menubutton_on{background-color:#e9e9e9;border:1px solid #dedede;outline:0}.cke_menubutton_on .cke_menubutton_icon{padding-right:3px;background-color:#e9e9e9}.cke_menubutton_label{padding:0 5px;background-color:transparent;width:100%;vertical-align:middle}.cke_menubutton_shortcut{color:#979797}.cke_menubutton_disabled .cke_menubutton_label{opacity:.3;filter:alpha(opacity=30)}.cke_panel_frame .cke_menubutton_label{display:none}.cke_menuseparator{background-color:#d1d1d1;height:1px}.cke_menuarrow{background:transparent url(images/arrow.png) no-repeat 0 10px;padding:0 5px}.cke_rtl .cke_menuarrow{background-position:5px -13px;background-repeat:no-repeat}.cke_hc .cke_menuarrow{background-image:none}.cke_menuarrow span{display:none}.cke_hc .cke_menuarrow span{vertical-align:middle;display:inline}.cke_combo{display:inline-block;float:left;position:relative;margin-bottom:5px}.cke_rtl .cke_combo{float:right}.cke_hc .cke_combo{margin-top:1px;margin-bottom:10px}.cke_combo:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:5px;top:0;right:0}.cke_rtl .cke_combo:after{border-right:0;border-left:1px solid #bcbcbc;right:auto;left:0}.cke_hc .cke_combo:after{border-color:#000}a.cke_combo_button{cursor:default;display:inline-block;float:left;margin:0;padding:1px}.cke_rtl a.cke_combo_button{float:right}.cke_hc a.cke_combo_button{padding:4px}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:hover,.cke_combo_off a.cke_combo_button:focus,.cke_combo_off a.cke_combo_button:active{background:#e5e5e5;border:1px solid #bcbcbc;padding:0 0 0 1px;margin-left:-1px}.cke_combo_off a.cke_combo_button:focus{outline:0}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:active{background:#fff}.cke_rtl .cke_combo_on a.cke_combo_button,.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:0 1px 0 0;margin-left:0;margin-right:-1px}.cke_hc .cke_combo_on a.cke_combo_button,.cke_hc .cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_combo_off a.cke_combo_button:active{border:3px solid #000;padding:1px 1px 1px 2px}.cke_hc.cke_rtl .cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:1px 2px 1px 1px}.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 0 0 3px;margin-left:-3px}.cke_rtl .cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 3px 0 0;margin-left:0;margin-right:-3px}.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 1px 1px 7px;margin-left:-6px}.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 7px 1px 1px;margin-left:0;margin-right:-6px}.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0;margin:0}.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px;margin:0}.cke_toolbar .cke_combo+.cke_toolbar_end,.cke_toolbar .cke_combo+.cke_toolgroup{margin-right:0;margin-left:2px}.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:2px}.cke_hc .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:5px}.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:5px}.cke_toolbar.cke_toolbar_last .cke_combo:nth-last-child(-n+2):after{content:none;border:0;width:0;height:0}.cke_combo_text{line-height:26px;padding-left:10px;text-overflow:ellipsis;overflow:hidden;float:left;cursor:default;color:#484848;width:60px}.cke_rtl .cke_combo_text{float:right;text-align:right;padding-left:0;padding-right:10px}.cke_hc .cke_combo_text{line-height:18px;font-size:12px}.cke_combo_open{cursor:default;display:inline-block;font-size:0;height:19px;line-height:17px;margin:1px 10px 1px;width:5px}.cke_hc .cke_combo_open{height:12px}.cke_combo_arrow{cursor:default;margin:11px 0 0;float:left;height:0;width:0;font-size:0;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_hc .cke_combo_arrow{font-size:10px;width:auto;border:0;margin-top:3px}.cke_combo_label{display:none;float:left;line-height:26px;vertical-align:top;margin-right:5px}.cke_rtl .cke_combo_label{float:right;margin-left:5px;margin-right:0}.cke_combo_disabled .cke_combo_inlinelabel,.cke_combo_disabled .cke_combo_open{opacity:.3}.cke_path{float:left;margin:-2px 0 2px}a.cke_path_item,span.cke_path_empty{display:inline-block;float:left;padding:3px 4px;margin-right:2px;cursor:default;text-decoration:none;outline:0;border:0;color:#484848;font-weight:bold;font-size:11px}.cke_rtl .cke_path,.cke_rtl .cke_path_item,.cke_rtl .cke_path_empty{float:right}a.cke_path_item:hover,a.cke_path_item:focus,a.cke_path_item:active{background-color:#e5e5e5}.cke_hc a.cke_path_item:hover,.cke_hc a.cke_path_item:focus,.cke_hc a.cke_path_item:active{border:2px solid;padding:1px 2px}.cke_button__source_label,.cke_button__sourcedialog_label{display:inline}.cke_combopanel__fontsize{width:135px}textarea.cke_source{font-family:'Courier New',Monospace;font-size:small;background-color:#fff;white-space:pre-wrap;border:0;padding:0;margin:0;display:block}.cke_wysiwyg_frame,.cke_wysiwyg_div{background-color:#fff}.cke_notifications_area{pointer-events:none}.cke_notification{pointer-events:auto;position:relative;margin:10px;width:300px;color:white;text-align:center;opacity:.95;filter:alpha(opacity = 95);-webkit-animation:fadeIn .7s;animation:fadeIn .7s}.cke_notification_message a{color:#12306f}@-webkit-keyframes fadeIn{from{opacity:.4}to{opacity:.95}}@keyframes fadeIn{from{opacity:.4}to{opacity:.95}}.cke_notification_success{background:#72b572;border:1px solid #63a563}.cke_notification_warning{background:#c83939;border:1px solid #902b2b}.cke_notification_info{background:#2e9ad0;border:1px solid #0f74a8}.cke_notification_info span.cke_notification_progress{background-color:#0f74a8;display:block;padding:0;margin:0;height:100%;overflow:hidden;position:absolute;z-index:1}.cke_notification_message{position:relative;margin:4px 23px 3px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;z-index:4;text-overflow:ellipsis;overflow:hidden}.cke_notification_close{background-image:url(images/close.png);background-repeat:no-repeat;background-position:50%;position:absolute;cursor:pointer;text-align:center;height:20px;width:20px;top:1px;right:1px;padding:0;margin:0;z-index:5;opacity:.6;filter:alpha(opacity = 60)}.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_notification_close span{display:none}.cke_notification_warning a.cke_notification_close{opacity:.8;filter:alpha(opacity = 80)}.cke_notification_warning a.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_chrome{visibility:inherit}.cke_voice_label{display:none}legend.cke_voice_label{display:none}a.cke_button_disabled,a.cke_button_disabled:hover,a.cke_button_disabled:focus,a.cke_button_disabled:active{filter:alpha(opacity = 30)}.cke_button_disabled .cke_button_icon{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#00ffffff,endColorstr=#00ffffff)}.cke_button_off:hover,.cke_button_off:focus,.cke_button_off:active{filter:alpha(opacity = 100)}.cke_combo_disabled .cke_combo_inlinelabel,.cke_combo_disabled .cke_combo_open{filter:alpha(opacity = 30)}.cke_toolbox_collapser{border:1px solid #a6a6a6}.cke_toolbox_collapser .cke_arrow{margin-top:1px}.cke_hc .cke_top,.cke_hc .cke_bottom,.cke_hc .cke_combo_button,.cke_hc a.cke_combo_button:hover,.cke_hc a.cke_combo_button:focus,.cke_hc .cke_toolgroup,.cke_hc .cke_button_on,.cke_hc a.cke_button_off:hover,.cke_hc a.cke_button_off:focus,.cke_hc a.cke_button_off:active,.cke_hc .cke_toolbox_collapser,.cke_hc .cke_toolbox_collapser:hover,.cke_hc .cke_panel_grouptitle{filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.cke_button__about_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -0px !important;}.cke_button__bold_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -24px !important;}.cke_button__italic_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -48px !important;}.cke_button__strike_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -72px !important;}.cke_button__subscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -96px !important;}.cke_button__superscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -120px !important;}.cke_button__underline_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -144px !important;}.cke_button__bidiltr_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -168px !important;}.cke_button__bidirtl_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -192px !important;}.cke_button__blockquote_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -216px !important;}.cke_rtl .cke_button__copy_icon, .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -240px !important;}.cke_ltr .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -264px !important;}.cke_rtl .cke_button__cut_icon, .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -288px !important;}.cke_ltr .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -312px !important;}.cke_rtl .cke_button__paste_icon, .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -336px !important;}.cke_ltr .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -360px !important;}.cke_button__bgcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -384px !important;}.cke_button__textcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -408px !important;}.cke_rtl .cke_button__templates_icon, .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -432px !important;}.cke_ltr .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -456px !important;}.cke_button__copyformatting_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -480px !important;}.cke_button__creatediv_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -504px !important;}.cke_rtl .cke_button__find_icon, .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -528px !important;}.cke_ltr .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -552px !important;}.cke_button__replace_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -576px !important;}.cke_button__flash_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -600px !important;}.cke_button__button_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -624px !important;}.cke_button__checkbox_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -648px !important;}.cke_button__form_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -672px !important;}.cke_button__hiddenfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -696px !important;}.cke_button__imagebutton_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -720px !important;}.cke_button__radio_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -744px !important;}.cke_rtl .cke_button__select_icon, .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -768px !important;}.cke_ltr .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -792px !important;}.cke_rtl .cke_button__textarea_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -816px !important;}.cke_ltr .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -840px !important;}.cke_rtl .cke_button__textfield_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -864px !important;}.cke_ltr .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -888px !important;}.cke_button__horizontalrule_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -912px !important;}.cke_button__iframe_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -936px !important;}.cke_button__image_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -960px !important;}.cke_rtl .cke_button__indent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -984px !important;}.cke_ltr .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1008px !important;}.cke_rtl .cke_button__outdent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1032px !important;}.cke_ltr .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1056px !important;}.cke_button__smiley_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1080px !important;}.cke_button__justifyblock_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1104px !important;}.cke_button__justifycenter_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1128px !important;}.cke_button__justifyleft_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1152px !important;}.cke_button__justifyright_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1176px !important;}.cke_button__language_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1200px !important;}.cke_rtl .cke_button__anchor_icon, .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1224px !important;}.cke_ltr .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1248px !important;}.cke_button__link_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1272px !important;}.cke_button__unlink_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1296px !important;}.cke_rtl .cke_button__bulletedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1320px !important;}.cke_ltr .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1344px !important;}.cke_rtl .cke_button__numberedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1368px !important;}.cke_ltr .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1392px !important;}.cke_button__maximize_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1416px !important;}.cke_rtl .cke_button__newpage_icon, .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1440px !important;}.cke_ltr .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1464px !important;}.cke_rtl .cke_button__pagebreak_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1488px !important;}.cke_ltr .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1512px !important;}.cke_rtl .cke_button__pastetext_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1536px !important;}.cke_ltr .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1560px !important;}.cke_rtl .cke_button__pastefromword_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1584px !important;}.cke_ltr .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1608px !important;}.cke_rtl .cke_button__preview_icon, .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1632px !important;}.cke_ltr .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1656px !important;}.cke_button__print_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1680px !important;}.cke_button__removeformat_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1704px !important;}.cke_button__save_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1728px !important;}.cke_button__selectall_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1752px !important;}.cke_rtl .cke_button__showblocks_icon, .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1776px !important;}.cke_ltr .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1800px !important;}.cke_rtl .cke_button__source_icon, .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1824px !important;}.cke_ltr .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1848px !important;}.cke_button__specialchar_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1872px !important;}.cke_button__scayt_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1896px !important;}.cke_button__table_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1920px !important;}.cke_rtl .cke_button__redo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1944px !important;}.cke_ltr .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1968px !important;}.cke_rtl .cke_button__undo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1992px !important;}.cke_ltr .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2016px !important;}.cke_button__spellchecker_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2040px !important;}.cke_hidpi .cke_button__about_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -0px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bold_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -24px !important;background-size: 16px !important;}.cke_hidpi .cke_button__italic_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -48px !important;background-size: 16px !important;}.cke_hidpi .cke_button__strike_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -72px !important;background-size: 16px !important;}.cke_hidpi .cke_button__subscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -96px !important;background-size: 16px !important;}.cke_hidpi .cke_button__superscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -120px !important;background-size: 16px !important;}.cke_hidpi .cke_button__underline_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -144px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidiltr_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -168px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidirtl_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -192px !important;background-size: 16px !important;}.cke_hidpi .cke_button__blockquote_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -216px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__copy_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -240px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__copy_icon,.cke_ltr.cke_hidpi .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -264px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__cut_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -288px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__cut_icon,.cke_ltr.cke_hidpi .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -312px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__paste_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -336px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__paste_icon,.cke_ltr.cke_hidpi .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -360px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bgcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -384px !important;background-size: 16px !important;}.cke_hidpi .cke_button__textcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -408px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__templates_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -432px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__templates_icon,.cke_ltr.cke_hidpi .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -456px !important;background-size: 16px !important;}.cke_hidpi .cke_button__copyformatting_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -480px !important;background-size: 16px !important;}.cke_hidpi .cke_button__creatediv_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -504px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__find_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -528px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__find_icon,.cke_ltr.cke_hidpi .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -552px !important;background-size: 16px !important;}.cke_hidpi .cke_button__replace_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -576px !important;background-size: 16px !important;}.cke_hidpi .cke_button__flash_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -600px !important;background-size: 16px !important;}.cke_hidpi .cke_button__button_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -624px !important;background-size: 16px !important;}.cke_hidpi .cke_button__checkbox_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -648px !important;background-size: 16px !important;}.cke_hidpi .cke_button__form_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -672px !important;background-size: 16px !important;}.cke_hidpi .cke_button__hiddenfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -696px !important;background-size: 16px !important;}.cke_hidpi .cke_button__imagebutton_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -720px !important;background-size: 16px !important;}.cke_hidpi .cke_button__radio_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -744px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__select_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -768px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__select_icon,.cke_ltr.cke_hidpi .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -792px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textarea_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -816px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textarea_icon,.cke_ltr.cke_hidpi .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -840px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textfield_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -864px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textfield_icon,.cke_ltr.cke_hidpi .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -888px !important;background-size: 16px !important;}.cke_hidpi .cke_button__horizontalrule_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -912px !important;background-size: 16px !important;}.cke_hidpi .cke_button__iframe_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -936px !important;background-size: 16px !important;}.cke_hidpi .cke_button__image_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -960px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__indent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -984px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__indent_icon,.cke_ltr.cke_hidpi .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1008px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__outdent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1032px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__outdent_icon,.cke_ltr.cke_hidpi .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1056px !important;background-size: 16px !important;}.cke_hidpi .cke_button__smiley_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1080px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyblock_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1104px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifycenter_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1128px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyleft_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1152px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyright_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1176px !important;background-size: 16px !important;}.cke_hidpi .cke_button__language_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1200px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__anchor_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1224px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__anchor_icon,.cke_ltr.cke_hidpi .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1248px !important;background-size: 16px !important;}.cke_hidpi .cke_button__link_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1272px !important;background-size: 16px !important;}.cke_hidpi .cke_button__unlink_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1296px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__bulletedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1320px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__bulletedlist_icon,.cke_ltr.cke_hidpi .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1344px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__numberedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1368px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__numberedlist_icon,.cke_ltr.cke_hidpi .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1392px !important;background-size: 16px !important;}.cke_hidpi .cke_button__maximize_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1416px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__newpage_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1440px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__newpage_icon,.cke_ltr.cke_hidpi .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1464px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pagebreak_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1488px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pagebreak_icon,.cke_ltr.cke_hidpi .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1512px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastetext_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1536px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastetext_icon,.cke_ltr.cke_hidpi .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1560px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastefromword_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1584px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastefromword_icon,.cke_ltr.cke_hidpi .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1608px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__preview_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1632px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__preview_icon,.cke_ltr.cke_hidpi .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1656px !important;background-size: 16px !important;}.cke_hidpi .cke_button__print_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1680px !important;background-size: 16px !important;}.cke_hidpi .cke_button__removeformat_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1704px !important;background-size: 16px !important;}.cke_hidpi .cke_button__save_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1728px !important;background-size: 16px !important;}.cke_hidpi .cke_button__selectall_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1752px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__showblocks_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1776px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__showblocks_icon,.cke_ltr.cke_hidpi .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1800px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__source_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1824px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__source_icon,.cke_ltr.cke_hidpi .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1848px !important;background-size: 16px !important;}.cke_hidpi .cke_button__specialchar_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1872px !important;background-size: 16px !important;}.cke_hidpi .cke_button__scayt_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1896px !important;background-size: 16px !important;}.cke_hidpi .cke_button__table_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1920px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__redo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1944px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__redo_icon,.cke_ltr.cke_hidpi .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1968px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__undo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1992px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__undo_icon,.cke_ltr.cke_hidpi .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2016px !important;background-size: 16px !important;}.cke_hidpi .cke_button__spellchecker_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2040px !important;background-size: 16px !important;} \ No newline at end of file diff --git a/static/plugs/ckeditor/skins/moono-lisa/editor_ie8.css b/static/plugs/ckeditor/skins/moono-lisa/editor_ie8.css new file mode 100644 index 000000000..1ca02b0f4 --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/editor_ie8.css @@ -0,0 +1,5 @@ +/* +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +.cke_reset{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none}.cke_reset_all,.cke_reset_all *,.cke_reset_all a,.cke_reset_all textarea{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none;border-collapse:collapse;font:normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;color:#000;text-align:left;white-space:nowrap;cursor:auto;float:none}.cke_reset_all .cke_rtl *{text-align:right}.cke_reset_all iframe{vertical-align:inherit}.cke_reset_all textarea{white-space:pre-wrap}.cke_reset_all textarea,.cke_reset_all input[type="text"],.cke_reset_all input[type="password"]{cursor:text}.cke_reset_all textarea[disabled],.cke_reset_all input[type="text"][disabled],.cke_reset_all input[type="password"][disabled]{cursor:default}.cke_reset_all fieldset{padding:10px;border:2px groove #e0dfe3}.cke_reset_all select{box-sizing:border-box}.cke_reset_all table{table-layout:auto}.cke_chrome{display:block;border:1px solid #d1d1d1;padding:0}.cke_inner{display:block;background:#fff;padding:0;-webkit-touch-callout:none}.cke_float{border:0}.cke_float .cke_inner{padding-bottom:0}.cke_top,.cke_contents,.cke_bottom{display:block;overflow:hidden}.cke_top{border-bottom:1px solid #d1d1d1;background:#f8f8f8;padding:6px 8px 2px;white-space:normal}.cke_float .cke_top{border:1px solid #d1d1d1}.cke_bottom{padding:6px 8px 2px;position:relative;border-top:1px solid #d1d1d1;background:#f8f8f8}.cke_browser_ios .cke_contents{overflow-y:auto;-webkit-overflow-scrolling:touch}.cke_resizer{width:0;height:0;overflow:hidden;border-width:10px 10px 0 0;border-color:transparent #bcbcbc transparent transparent;border-style:dashed solid dashed dashed;font-size:0;vertical-align:bottom;margin-top:6px;margin-bottom:2px}.cke_hc .cke_resizer{font-size:15px;width:auto;height:auto;border-width:0}.cke_resizer_ltr{cursor:se-resize;float:right;margin-right:-4px}.cke_resizer_rtl{border-width:10px 0 0 10px;border-color:transparent transparent transparent #bcbcbc;border-style:dashed dashed dashed solid;cursor:sw-resize;float:left;margin-left:-4px;right:auto}.cke_wysiwyg_div{display:block;height:100%;overflow:auto;padding:0 8px;outline-style:none;box-sizing:border-box}.cke_panel{visibility:visible;width:120px;height:100px;overflow:hidden;background-color:#fff;border:1px solid #d1d1d1}.cke_menu_panel{padding:0;margin:0}.cke_combopanel{width:150px;height:170px}.cke_panel_frame{width:100%;height:100%;font-size:12px;overflow:auto;overflow-x:hidden}.cke_panel_container{overflow-y:auto;overflow-x:hidden}.cke_panel_block:focus{outline:0}.cke_panel_list{margin:0;padding:0;list-style-type:none;white-space:nowrap}.cke_panel_listItem{margin:0;padding:0}.cke_panel_listItem a{padding:6px 7px;display:block;color:inherit!important;text-decoration:none;overflow:hidden;text-overflow:ellipsis}.cke_hc .cke_panel_listItem a{border-style:none}.cke_panel_listItem.cke_selected a,.cke_panel_listItem a:hover,.cke_panel_listItem a:focus,.cke_panel_listItem a:active{background-color:#e9e9e9}.cke_panel_listItem a:focus{outline:1px dotted #000}.cke_hc .cke_panel_listItem a:hover,.cke_hc .cke_panel_listItem a:focus,.cke_hc .cke_panel_listItem a:active{border:2px solid;padding:4px 5px}.cke_panel_listItem p,.cke_panel_listItem h1,.cke_panel_listItem h2,.cke_panel_listItem h3,.cke_panel_listItem h4,.cke_panel_listItem h5,.cke_panel_listItem h6,.cke_panel_listItem pre{margin-top:0;margin-bottom:0}.cke_panel_grouptitle{cursor:default;font-size:11px;font-weight:bold;white-space:nowrap;margin:0;padding:6px 6px 7px 6px;color:#484848;border-bottom:1px solid #d1d1d1;background:#f8f8f8}.cke_colorblock{padding:10px;font-size:11px;font-family:'Microsoft Sans Serif',Tahoma,Arial,Verdana,Sans-Serif}.cke_colorblock,.cke_colorblock a{text-decoration:none;color:#000}a.cke_colorbox{padding:2px;float:left;width:20px;height:20px}.cke_rtl a.cke_colorbox{float:right}a:hover.cke_colorbox,a:focus.cke_colorbox,a:active.cke_colorbox{outline:0;padding:0;border:2px solid #139ff7}a:hover.cke_colorbox{border-color:#bcbcbc}span.cke_colorbox{width:20px;height:20px;float:left}.cke_rtl span.cke_colorbox{float:right}a.cke_colorauto,a.cke_colormore{border:#fff 1px solid;padding:3px;display:block;cursor:pointer}a.cke_colorauto{padding:0;border:1px solid transparent;margin-bottom:6px;height:26px;line-height:26px}a.cke_colormore{margin-top:10px;height:20px;line-height:19px}a:hover.cke_colorauto,a:hover.cke_colormore,a:focus.cke_colorauto,a:focus.cke_colormore,a:active.cke_colorauto,a:active.cke_colormore{outline:0;border:#139ff7 1px solid;background-color:#f8f8f8}a:hover.cke_colorauto,a:hover.cke_colormore{border-color:#bcbcbc}.cke_colorauto span.cke_colorbox{width:18px;height:18px;border:1px solid #808080;margin-left:1px;margin-top:3px}.cke_rtl .cke_colorauto span.cke_colorbox{margin-left:0;margin-right:1px}span.cke_colorbox[style*="#ffffff"],span.cke_colorbox[style*="#FFFFFF"],span.cke_colorbox[style="background-color:#fff"],span.cke_colorbox[style="background-color:#FFF"],span.cke_colorbox[style*="rgb(255,255,255)"],span.cke_colorbox[style*="rgb(255, 255, 255)"]{border:1px solid #808080;width:18px;height:18px}.cke_toolbar{float:left}.cke_rtl .cke_toolbar{float:right}.cke_toolgroup{border:0;float:left;margin:1px 2px 6px 0;padding-right:3px}.cke_rtl .cke_toolgroup{float:right;margin:1px 0 6px 2px;padding-left:3px;padding-right:0}.cke_hc .cke_toolgroup{margin-right:5px;margin-bottom:5px}.cke_hc.cke_rtl .cke_toolgroup{margin-right:0;margin-left:5px}a.cke_button{display:inline-block;height:18px;padding:4px 6px;outline:0;cursor:default;float:left;border:0;position:relative}.cke_rtl a.cke_button{float:right}.cke_hc a.cke_button{border:1px solid black;padding:3px 5px;margin:0 3px 5px 0}.cke_hc.cke_rtl a.cke_button{margin:0 0 5px 3px}a.cke_button_on{background:#fff;border:1px #bcbcbc solid;padding:3px 5px}a.cke_button_off:hover,a.cke_button_off:focus,a.cke_button_off:active{background:#e5e5e5;border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_on,.cke_hc a.cke_button_off:hover,.cke_hc a.cke_button_off:focus,.cke_hc a.cke_button_off:active{background:#e5e5e5;border:3px solid #000;padding:1px 3px}a.cke_button_disabled:hover,a.cke_button_disabled:focus,a.cke_button_disabled:active{border:0;padding:4px 6px;background-color:transparent}a.cke_button_disabled:focus{border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_disabled:hover,.cke_hc a.cke_button_disabled:focus,.cke_hc a.cke_button_disabled:active{border:1px solid #acacac;padding:3px 5px;margin:0 3px 5px 0}.cke_hc a.cke_button_disabled:focus{border:3px solid #000;padding:1px 3px}.cke_hc.cke_rtl a.cke_button_disabled:hover,.cke_hc.cke_rtl a.cke_button_disabled:focus,.cke_hc.cke_rtl a.cke_button_disabled:active{margin:0 0 5px 3px}a.cke_button_disabled .cke_button_icon,a.cke_button_disabled .cke_button_arrow{opacity:.3}.cke_hc a.cke_button_disabled{border-color:#acacac}.cke_hc a.cke_button_disabled .cke_button_icon,.cke_hc a.cke_button_disabled .cke_button_label{opacity:.5}.cke_toolgroup a.cke_button:last-child:after,.cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:4px;top:0;right:-3px}.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-right:0;right:auto;border-left:1px solid #bcbcbc;top:0;left:-3px}.cke_hc .cke_toolgroup a.cke_button:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-color:#000;top:0;right:-7px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{top:0;right:auto;left:-7px}.cke_toolgroup a.cke_button:hover:last-child:after,.cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:-4px}.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:auto;left:-4px}.cke_hc .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:-9px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:auto;left:-9px}.cke_toolbar.cke_toolbar_last .cke_toolgroup a.cke_button:last-child:after{content:none;border:0;width:0;height:0}.cke_button_icon{cursor:inherit;background-repeat:no-repeat;margin-top:1px;width:16px;height:16px;float:left;display:inline-block}.cke_rtl .cke_button_icon{float:right}.cke_hc .cke_button_icon{display:none}.cke_button_label{display:none;padding-left:3px;margin-top:1px;line-height:17px;vertical-align:middle;float:left;cursor:default;color:#484848}.cke_rtl .cke_button_label{padding-right:3px;padding-left:0;float:right}.cke_hc .cke_button_label{padding:0;display:inline-block;font-size:12px}.cke_button_arrow{display:inline-block;margin:8px 0 0 1px;width:0;height:0;cursor:default;vertical-align:top;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_rtl .cke_button_arrow{margin-right:5px;margin-left:0}.cke_hc .cke_button_arrow{font-size:10px;margin:3px 0 0 3px;width:auto;border:0}.cke_toolbar_separator{float:left;background-color:#bcbcbc;margin:4px 2px 0 2px;height:18px;width:1px}.cke_rtl .cke_toolbar_separator{float:right}.cke_hc .cke_toolbar_separator{background-color:#000;margin-left:2px;margin-right:5px;margin-bottom:9px}.cke_hc.cke_rtl .cke_toolbar_separator{margin-left:5px;margin-right:2px}.cke_toolbar_break{display:block;clear:left}.cke_rtl .cke_toolbar_break{clear:right}a.cke_toolbox_collapser{width:12px;height:11px;float:right;margin:11px 0 0;font-size:0;cursor:default;text-align:center;border:1px solid #bcbcbc}.cke_rtl .cke_toolbox_collapser{float:left}.cke_toolbox_collapser:hover{background:#e5e5e5}.cke_toolbox_collapser.cke_toolbox_collapser_min{margin:0 2px 4px}.cke_toolbox_collapser .cke_arrow{display:inline-block;height:0;width:0;font-size:0;margin-top:1px;border:3px solid transparent;border-bottom-color:#484848}.cke_toolbox_collapser.cke_toolbox_collapser_min .cke_arrow{margin-top:4px;border-bottom-color:transparent;border-top-color:#484848}.cke_hc .cke_toolbox_collapser .cke_arrow{font-size:8px;width:auto;border:0;margin-top:0}.cke_menuitem span{cursor:default}.cke_menubutton{display:block}.cke_hc .cke_menubutton{padding:2px}.cke_menubutton:hover,.cke_menubutton:focus,.cke_menubutton:active{background-color:#e9e9e9;display:block;outline:1px dotted}.cke_menubutton:hover{outline:0}.cke_hc .cke_menubutton:hover,.cke_hc .cke_menubutton:focus,.cke_hc .cke_menubutton:active{border:2px solid;padding:0}.cke_menubutton_disabled:hover,.cke_menubutton_disabled:focus,.cke_menubutton_disabled:active{background-color:transparent;outline:0}.cke_menubutton_inner{display:table-row}.cke_menubutton_icon,.cke_menubutton_label,.cke_menuarrow{display:table-cell}.cke_menubutton_icon{background-color:#f8f8f8;padding:6px 4px}.cke_hc .cke_menubutton_icon{height:16px;width:0;padding:4px 0}.cke_menubutton:hover .cke_menubutton_icon,.cke_menubutton:focus .cke_menubutton_icon,.cke_menubutton:active .cke_menubutton_icon{background-color:#e9e9e9}.cke_menubutton_disabled:hover .cke_menubutton_icon,.cke_menubutton_disabled:focus .cke_menubutton_icon,.cke_menubutton_disabled:active .cke_menubutton_icon{background-color:#f8f8f8;outline:0}.cke_menuitem .cke_menubutton_on{background-color:#e9e9e9;border:1px solid #dedede;outline:0}.cke_menubutton_on .cke_menubutton_icon{padding-right:3px;background-color:#e9e9e9}.cke_menubutton_label{padding:0 5px;background-color:transparent;width:100%;vertical-align:middle}.cke_menubutton_shortcut{color:#979797}.cke_menubutton_disabled .cke_menubutton_label{opacity:.3;filter:alpha(opacity=30)}.cke_panel_frame .cke_menubutton_label{display:none}.cke_menuseparator{background-color:#d1d1d1;height:1px}.cke_menuarrow{background:transparent url(images/arrow.png) no-repeat 0 10px;padding:0 5px}.cke_rtl .cke_menuarrow{background-position:5px -13px;background-repeat:no-repeat}.cke_hc .cke_menuarrow{background-image:none}.cke_menuarrow span{display:none}.cke_hc .cke_menuarrow span{vertical-align:middle;display:inline}.cke_combo{display:inline-block;float:left;position:relative;margin-bottom:5px}.cke_rtl .cke_combo{float:right}.cke_hc .cke_combo{margin-top:1px;margin-bottom:10px}.cke_combo:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:5px;top:0;right:0}.cke_rtl .cke_combo:after{border-right:0;border-left:1px solid #bcbcbc;right:auto;left:0}.cke_hc .cke_combo:after{border-color:#000}a.cke_combo_button{cursor:default;display:inline-block;float:left;margin:0;padding:1px}.cke_rtl a.cke_combo_button{float:right}.cke_hc a.cke_combo_button{padding:4px}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:hover,.cke_combo_off a.cke_combo_button:focus,.cke_combo_off a.cke_combo_button:active{background:#e5e5e5;border:1px solid #bcbcbc;padding:0 0 0 1px;margin-left:-1px}.cke_combo_off a.cke_combo_button:focus{outline:0}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:active{background:#fff}.cke_rtl .cke_combo_on a.cke_combo_button,.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:0 1px 0 0;margin-left:0;margin-right:-1px}.cke_hc .cke_combo_on a.cke_combo_button,.cke_hc .cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_combo_off a.cke_combo_button:active{border:3px solid #000;padding:1px 1px 1px 2px}.cke_hc.cke_rtl .cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:1px 2px 1px 1px}.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 0 0 3px;margin-left:-3px}.cke_rtl .cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 3px 0 0;margin-left:0;margin-right:-3px}.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 1px 1px 7px;margin-left:-6px}.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 7px 1px 1px;margin-left:0;margin-right:-6px}.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0;margin:0}.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px;margin:0}.cke_toolbar .cke_combo+.cke_toolbar_end,.cke_toolbar .cke_combo+.cke_toolgroup{margin-right:0;margin-left:2px}.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:2px}.cke_hc .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:5px}.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:5px}.cke_toolbar.cke_toolbar_last .cke_combo:nth-last-child(-n+2):after{content:none;border:0;width:0;height:0}.cke_combo_text{line-height:26px;padding-left:10px;text-overflow:ellipsis;overflow:hidden;float:left;cursor:default;color:#484848;width:60px}.cke_rtl .cke_combo_text{float:right;text-align:right;padding-left:0;padding-right:10px}.cke_hc .cke_combo_text{line-height:18px;font-size:12px}.cke_combo_open{cursor:default;display:inline-block;font-size:0;height:19px;line-height:17px;margin:1px 10px 1px;width:5px}.cke_hc .cke_combo_open{height:12px}.cke_combo_arrow{cursor:default;margin:11px 0 0;float:left;height:0;width:0;font-size:0;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_hc .cke_combo_arrow{font-size:10px;width:auto;border:0;margin-top:3px}.cke_combo_label{display:none;float:left;line-height:26px;vertical-align:top;margin-right:5px}.cke_rtl .cke_combo_label{float:right;margin-left:5px;margin-right:0}.cke_combo_disabled .cke_combo_inlinelabel,.cke_combo_disabled .cke_combo_open{opacity:.3}.cke_path{float:left;margin:-2px 0 2px}a.cke_path_item,span.cke_path_empty{display:inline-block;float:left;padding:3px 4px;margin-right:2px;cursor:default;text-decoration:none;outline:0;border:0;color:#484848;font-weight:bold;font-size:11px}.cke_rtl .cke_path,.cke_rtl .cke_path_item,.cke_rtl .cke_path_empty{float:right}a.cke_path_item:hover,a.cke_path_item:focus,a.cke_path_item:active{background-color:#e5e5e5}.cke_hc a.cke_path_item:hover,.cke_hc a.cke_path_item:focus,.cke_hc a.cke_path_item:active{border:2px solid;padding:1px 2px}.cke_button__source_label,.cke_button__sourcedialog_label{display:inline}.cke_combopanel__fontsize{width:135px}textarea.cke_source{font-family:'Courier New',Monospace;font-size:small;background-color:#fff;white-space:pre-wrap;border:0;padding:0;margin:0;display:block}.cke_wysiwyg_frame,.cke_wysiwyg_div{background-color:#fff}.cke_notifications_area{pointer-events:none}.cke_notification{pointer-events:auto;position:relative;margin:10px;width:300px;color:white;text-align:center;opacity:.95;filter:alpha(opacity = 95);-webkit-animation:fadeIn .7s;animation:fadeIn .7s}.cke_notification_message a{color:#12306f}@-webkit-keyframes fadeIn{from{opacity:.4}to{opacity:.95}}@keyframes fadeIn{from{opacity:.4}to{opacity:.95}}.cke_notification_success{background:#72b572;border:1px solid #63a563}.cke_notification_warning{background:#c83939;border:1px solid #902b2b}.cke_notification_info{background:#2e9ad0;border:1px solid #0f74a8}.cke_notification_info span.cke_notification_progress{background-color:#0f74a8;display:block;padding:0;margin:0;height:100%;overflow:hidden;position:absolute;z-index:1}.cke_notification_message{position:relative;margin:4px 23px 3px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;z-index:4;text-overflow:ellipsis;overflow:hidden}.cke_notification_close{background-image:url(images/close.png);background-repeat:no-repeat;background-position:50%;position:absolute;cursor:pointer;text-align:center;height:20px;width:20px;top:1px;right:1px;padding:0;margin:0;z-index:5;opacity:.6;filter:alpha(opacity = 60)}.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_notification_close span{display:none}.cke_notification_warning a.cke_notification_close{opacity:.8;filter:alpha(opacity = 80)}.cke_notification_warning a.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_chrome{visibility:inherit}.cke_voice_label{display:none}legend.cke_voice_label{display:none}a.cke_button_disabled,a.cke_button_disabled:hover,a.cke_button_disabled:focus,a.cke_button_disabled:active{filter:alpha(opacity = 30)}.cke_button_disabled .cke_button_icon{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#00ffffff,endColorstr=#00ffffff)}.cke_button_off:hover,.cke_button_off:focus,.cke_button_off:active{filter:alpha(opacity = 100)}.cke_combo_disabled .cke_combo_inlinelabel,.cke_combo_disabled .cke_combo_open{filter:alpha(opacity = 30)}.cke_toolbox_collapser{border:1px solid #a6a6a6}.cke_toolbox_collapser .cke_arrow{margin-top:1px}.cke_hc .cke_top,.cke_hc .cke_bottom,.cke_hc .cke_combo_button,.cke_hc a.cke_combo_button:hover,.cke_hc a.cke_combo_button:focus,.cke_hc .cke_toolgroup,.cke_hc .cke_button_on,.cke_hc a.cke_button_off:hover,.cke_hc a.cke_button_off:focus,.cke_hc a.cke_button_off:active,.cke_hc .cke_toolbox_collapser,.cke_hc .cke_toolbox_collapser:hover,.cke_hc .cke_panel_grouptitle{filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.cke_toolbox_collapser .cke_arrow{border-width:4px}.cke_toolbox_collapser.cke_toolbox_collapser_min .cke_arrow{border-width:3px}.cke_toolbox_collapser .cke_arrow{margin-top:0}.cke_toolbar{position:relative}.cke_rtl .cke_toolbar_end{right:auto;left:0}.cke_toolbar_end:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:4px;top:1px;right:2px}.cke_rtl .cke_toolbar_end:after{right:auto;left:2px}.cke_hc .cke_toolbar_end:after{top:2px;right:5px;border-color:#000}.cke_hc.cke_rtl .cke_toolbar_end:after{right:auto;left:5px}.cke_combo+.cke_toolbar_end:after,.cke_toolbar.cke_toolbar_last .cke_toolbar_end:after{content:none;border:0}.cke_combo+.cke_toolgroup+.cke_toolbar_end:after{right:0}.cke_rtl .cke_combo+.cke_toolgroup+.cke_toolbar_end:after{right:auto;left:0}.cke_button__about_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -0px !important;}.cke_button__bold_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -24px !important;}.cke_button__italic_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -48px !important;}.cke_button__strike_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -72px !important;}.cke_button__subscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -96px !important;}.cke_button__superscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -120px !important;}.cke_button__underline_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -144px !important;}.cke_button__bidiltr_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -168px !important;}.cke_button__bidirtl_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -192px !important;}.cke_button__blockquote_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -216px !important;}.cke_rtl .cke_button__copy_icon, .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -240px !important;}.cke_ltr .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -264px !important;}.cke_rtl .cke_button__cut_icon, .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -288px !important;}.cke_ltr .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -312px !important;}.cke_rtl .cke_button__paste_icon, .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -336px !important;}.cke_ltr .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -360px !important;}.cke_button__bgcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -384px !important;}.cke_button__textcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -408px !important;}.cke_rtl .cke_button__templates_icon, .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -432px !important;}.cke_ltr .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -456px !important;}.cke_button__copyformatting_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -480px !important;}.cke_button__creatediv_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -504px !important;}.cke_rtl .cke_button__find_icon, .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -528px !important;}.cke_ltr .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -552px !important;}.cke_button__replace_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -576px !important;}.cke_button__flash_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -600px !important;}.cke_button__button_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -624px !important;}.cke_button__checkbox_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -648px !important;}.cke_button__form_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -672px !important;}.cke_button__hiddenfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -696px !important;}.cke_button__imagebutton_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -720px !important;}.cke_button__radio_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -744px !important;}.cke_rtl .cke_button__select_icon, .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -768px !important;}.cke_ltr .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -792px !important;}.cke_rtl .cke_button__textarea_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -816px !important;}.cke_ltr .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -840px !important;}.cke_rtl .cke_button__textfield_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -864px !important;}.cke_ltr .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -888px !important;}.cke_button__horizontalrule_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -912px !important;}.cke_button__iframe_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -936px !important;}.cke_button__image_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -960px !important;}.cke_rtl .cke_button__indent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -984px !important;}.cke_ltr .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1008px !important;}.cke_rtl .cke_button__outdent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1032px !important;}.cke_ltr .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1056px !important;}.cke_button__smiley_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1080px !important;}.cke_button__justifyblock_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1104px !important;}.cke_button__justifycenter_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1128px !important;}.cke_button__justifyleft_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1152px !important;}.cke_button__justifyright_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1176px !important;}.cke_button__language_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1200px !important;}.cke_rtl .cke_button__anchor_icon, .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1224px !important;}.cke_ltr .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1248px !important;}.cke_button__link_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1272px !important;}.cke_button__unlink_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1296px !important;}.cke_rtl .cke_button__bulletedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1320px !important;}.cke_ltr .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1344px !important;}.cke_rtl .cke_button__numberedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1368px !important;}.cke_ltr .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1392px !important;}.cke_button__maximize_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1416px !important;}.cke_rtl .cke_button__newpage_icon, .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1440px !important;}.cke_ltr .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1464px !important;}.cke_rtl .cke_button__pagebreak_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1488px !important;}.cke_ltr .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1512px !important;}.cke_rtl .cke_button__pastetext_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1536px !important;}.cke_ltr .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1560px !important;}.cke_rtl .cke_button__pastefromword_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1584px !important;}.cke_ltr .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1608px !important;}.cke_rtl .cke_button__preview_icon, .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1632px !important;}.cke_ltr .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1656px !important;}.cke_button__print_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1680px !important;}.cke_button__removeformat_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1704px !important;}.cke_button__save_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1728px !important;}.cke_button__selectall_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1752px !important;}.cke_rtl .cke_button__showblocks_icon, .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1776px !important;}.cke_ltr .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1800px !important;}.cke_rtl .cke_button__source_icon, .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1824px !important;}.cke_ltr .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1848px !important;}.cke_button__specialchar_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1872px !important;}.cke_button__scayt_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1896px !important;}.cke_button__table_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1920px !important;}.cke_rtl .cke_button__redo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1944px !important;}.cke_ltr .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1968px !important;}.cke_rtl .cke_button__undo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1992px !important;}.cke_ltr .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2016px !important;}.cke_button__spellchecker_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2040px !important;}.cke_hidpi .cke_button__about_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -0px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bold_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -24px !important;background-size: 16px !important;}.cke_hidpi .cke_button__italic_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -48px !important;background-size: 16px !important;}.cke_hidpi .cke_button__strike_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -72px !important;background-size: 16px !important;}.cke_hidpi .cke_button__subscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -96px !important;background-size: 16px !important;}.cke_hidpi .cke_button__superscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -120px !important;background-size: 16px !important;}.cke_hidpi .cke_button__underline_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -144px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidiltr_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -168px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidirtl_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -192px !important;background-size: 16px !important;}.cke_hidpi .cke_button__blockquote_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -216px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__copy_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -240px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__copy_icon,.cke_ltr.cke_hidpi .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -264px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__cut_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -288px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__cut_icon,.cke_ltr.cke_hidpi .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -312px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__paste_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -336px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__paste_icon,.cke_ltr.cke_hidpi .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -360px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bgcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -384px !important;background-size: 16px !important;}.cke_hidpi .cke_button__textcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -408px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__templates_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -432px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__templates_icon,.cke_ltr.cke_hidpi .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -456px !important;background-size: 16px !important;}.cke_hidpi .cke_button__copyformatting_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -480px !important;background-size: 16px !important;}.cke_hidpi .cke_button__creatediv_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -504px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__find_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -528px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__find_icon,.cke_ltr.cke_hidpi .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -552px !important;background-size: 16px !important;}.cke_hidpi .cke_button__replace_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -576px !important;background-size: 16px !important;}.cke_hidpi .cke_button__flash_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -600px !important;background-size: 16px !important;}.cke_hidpi .cke_button__button_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -624px !important;background-size: 16px !important;}.cke_hidpi .cke_button__checkbox_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -648px !important;background-size: 16px !important;}.cke_hidpi .cke_button__form_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -672px !important;background-size: 16px !important;}.cke_hidpi .cke_button__hiddenfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -696px !important;background-size: 16px !important;}.cke_hidpi .cke_button__imagebutton_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -720px !important;background-size: 16px !important;}.cke_hidpi .cke_button__radio_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -744px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__select_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -768px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__select_icon,.cke_ltr.cke_hidpi .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -792px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textarea_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -816px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textarea_icon,.cke_ltr.cke_hidpi .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -840px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textfield_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -864px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textfield_icon,.cke_ltr.cke_hidpi .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -888px !important;background-size: 16px !important;}.cke_hidpi .cke_button__horizontalrule_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -912px !important;background-size: 16px !important;}.cke_hidpi .cke_button__iframe_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -936px !important;background-size: 16px !important;}.cke_hidpi .cke_button__image_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -960px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__indent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -984px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__indent_icon,.cke_ltr.cke_hidpi .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1008px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__outdent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1032px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__outdent_icon,.cke_ltr.cke_hidpi .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1056px !important;background-size: 16px !important;}.cke_hidpi .cke_button__smiley_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1080px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyblock_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1104px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifycenter_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1128px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyleft_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1152px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyright_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1176px !important;background-size: 16px !important;}.cke_hidpi .cke_button__language_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1200px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__anchor_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1224px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__anchor_icon,.cke_ltr.cke_hidpi .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1248px !important;background-size: 16px !important;}.cke_hidpi .cke_button__link_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1272px !important;background-size: 16px !important;}.cke_hidpi .cke_button__unlink_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1296px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__bulletedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1320px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__bulletedlist_icon,.cke_ltr.cke_hidpi .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1344px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__numberedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1368px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__numberedlist_icon,.cke_ltr.cke_hidpi .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1392px !important;background-size: 16px !important;}.cke_hidpi .cke_button__maximize_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1416px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__newpage_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1440px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__newpage_icon,.cke_ltr.cke_hidpi .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1464px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pagebreak_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1488px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pagebreak_icon,.cke_ltr.cke_hidpi .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1512px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastetext_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1536px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastetext_icon,.cke_ltr.cke_hidpi .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1560px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastefromword_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1584px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastefromword_icon,.cke_ltr.cke_hidpi .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1608px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__preview_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1632px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__preview_icon,.cke_ltr.cke_hidpi .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1656px !important;background-size: 16px !important;}.cke_hidpi .cke_button__print_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1680px !important;background-size: 16px !important;}.cke_hidpi .cke_button__removeformat_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1704px !important;background-size: 16px !important;}.cke_hidpi .cke_button__save_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1728px !important;background-size: 16px !important;}.cke_hidpi .cke_button__selectall_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1752px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__showblocks_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1776px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__showblocks_icon,.cke_ltr.cke_hidpi .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1800px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__source_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1824px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__source_icon,.cke_ltr.cke_hidpi .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1848px !important;background-size: 16px !important;}.cke_hidpi .cke_button__specialchar_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1872px !important;background-size: 16px !important;}.cke_hidpi .cke_button__scayt_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1896px !important;background-size: 16px !important;}.cke_hidpi .cke_button__table_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1920px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__redo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1944px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__redo_icon,.cke_ltr.cke_hidpi .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1968px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__undo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1992px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__undo_icon,.cke_ltr.cke_hidpi .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2016px !important;background-size: 16px !important;}.cke_hidpi .cke_button__spellchecker_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2040px !important;background-size: 16px !important;} \ No newline at end of file diff --git a/static/plugs/ckeditor/skins/moono-lisa/editor_iequirks.css b/static/plugs/ckeditor/skins/moono-lisa/editor_iequirks.css new file mode 100644 index 000000000..ed8d33688 --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/editor_iequirks.css @@ -0,0 +1,5 @@ +/* +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license +*/ +.cke_reset{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none}.cke_reset_all,.cke_reset_all *,.cke_reset_all a,.cke_reset_all textarea{margin:0;padding:0;border:0;background:transparent;text-decoration:none;width:auto;height:auto;vertical-align:baseline;box-sizing:content-box;position:static;transition:none;border-collapse:collapse;font:normal normal normal 12px Arial,Helvetica,Tahoma,Verdana,Sans-Serif;color:#000;text-align:left;white-space:nowrap;cursor:auto;float:none}.cke_reset_all .cke_rtl *{text-align:right}.cke_reset_all iframe{vertical-align:inherit}.cke_reset_all textarea{white-space:pre-wrap}.cke_reset_all textarea,.cke_reset_all input[type="text"],.cke_reset_all input[type="password"]{cursor:text}.cke_reset_all textarea[disabled],.cke_reset_all input[type="text"][disabled],.cke_reset_all input[type="password"][disabled]{cursor:default}.cke_reset_all fieldset{padding:10px;border:2px groove #e0dfe3}.cke_reset_all select{box-sizing:border-box}.cke_reset_all table{table-layout:auto}.cke_chrome{display:block;border:1px solid #d1d1d1;padding:0}.cke_inner{display:block;background:#fff;padding:0;-webkit-touch-callout:none}.cke_float{border:0}.cke_float .cke_inner{padding-bottom:0}.cke_top,.cke_contents,.cke_bottom{display:block;overflow:hidden}.cke_top{border-bottom:1px solid #d1d1d1;background:#f8f8f8;padding:6px 8px 2px;white-space:normal}.cke_float .cke_top{border:1px solid #d1d1d1}.cke_bottom{padding:6px 8px 2px;position:relative;border-top:1px solid #d1d1d1;background:#f8f8f8}.cke_browser_ios .cke_contents{overflow-y:auto;-webkit-overflow-scrolling:touch}.cke_resizer{width:0;height:0;overflow:hidden;border-width:10px 10px 0 0;border-color:transparent #bcbcbc transparent transparent;border-style:dashed solid dashed dashed;font-size:0;vertical-align:bottom;margin-top:6px;margin-bottom:2px}.cke_hc .cke_resizer{font-size:15px;width:auto;height:auto;border-width:0}.cke_resizer_ltr{cursor:se-resize;float:right;margin-right:-4px}.cke_resizer_rtl{border-width:10px 0 0 10px;border-color:transparent transparent transparent #bcbcbc;border-style:dashed dashed dashed solid;cursor:sw-resize;float:left;margin-left:-4px;right:auto}.cke_wysiwyg_div{display:block;height:100%;overflow:auto;padding:0 8px;outline-style:none;box-sizing:border-box}.cke_panel{visibility:visible;width:120px;height:100px;overflow:hidden;background-color:#fff;border:1px solid #d1d1d1}.cke_menu_panel{padding:0;margin:0}.cke_combopanel{width:150px;height:170px}.cke_panel_frame{width:100%;height:100%;font-size:12px;overflow:auto;overflow-x:hidden}.cke_panel_container{overflow-y:auto;overflow-x:hidden}.cke_panel_block:focus{outline:0}.cke_panel_list{margin:0;padding:0;list-style-type:none;white-space:nowrap}.cke_panel_listItem{margin:0;padding:0}.cke_panel_listItem a{padding:6px 7px;display:block;color:inherit!important;text-decoration:none;overflow:hidden;text-overflow:ellipsis}.cke_hc .cke_panel_listItem a{border-style:none}.cke_panel_listItem.cke_selected a,.cke_panel_listItem a:hover,.cke_panel_listItem a:focus,.cke_panel_listItem a:active{background-color:#e9e9e9}.cke_panel_listItem a:focus{outline:1px dotted #000}.cke_hc .cke_panel_listItem a:hover,.cke_hc .cke_panel_listItem a:focus,.cke_hc .cke_panel_listItem a:active{border:2px solid;padding:4px 5px}.cke_panel_listItem p,.cke_panel_listItem h1,.cke_panel_listItem h2,.cke_panel_listItem h3,.cke_panel_listItem h4,.cke_panel_listItem h5,.cke_panel_listItem h6,.cke_panel_listItem pre{margin-top:0;margin-bottom:0}.cke_panel_grouptitle{cursor:default;font-size:11px;font-weight:bold;white-space:nowrap;margin:0;padding:6px 6px 7px 6px;color:#484848;border-bottom:1px solid #d1d1d1;background:#f8f8f8}.cke_colorblock{padding:10px;font-size:11px;font-family:'Microsoft Sans Serif',Tahoma,Arial,Verdana,Sans-Serif}.cke_colorblock,.cke_colorblock a{text-decoration:none;color:#000}a.cke_colorbox{padding:2px;float:left;width:20px;height:20px}.cke_rtl a.cke_colorbox{float:right}a:hover.cke_colorbox,a:focus.cke_colorbox,a:active.cke_colorbox{outline:0;padding:0;border:2px solid #139ff7}a:hover.cke_colorbox{border-color:#bcbcbc}span.cke_colorbox{width:20px;height:20px;float:left}.cke_rtl span.cke_colorbox{float:right}a.cke_colorauto,a.cke_colormore{border:#fff 1px solid;padding:3px;display:block;cursor:pointer}a.cke_colorauto{padding:0;border:1px solid transparent;margin-bottom:6px;height:26px;line-height:26px}a.cke_colormore{margin-top:10px;height:20px;line-height:19px}a:hover.cke_colorauto,a:hover.cke_colormore,a:focus.cke_colorauto,a:focus.cke_colormore,a:active.cke_colorauto,a:active.cke_colormore{outline:0;border:#139ff7 1px solid;background-color:#f8f8f8}a:hover.cke_colorauto,a:hover.cke_colormore{border-color:#bcbcbc}.cke_colorauto span.cke_colorbox{width:18px;height:18px;border:1px solid #808080;margin-left:1px;margin-top:3px}.cke_rtl .cke_colorauto span.cke_colorbox{margin-left:0;margin-right:1px}span.cke_colorbox[style*="#ffffff"],span.cke_colorbox[style*="#FFFFFF"],span.cke_colorbox[style="background-color:#fff"],span.cke_colorbox[style="background-color:#FFF"],span.cke_colorbox[style*="rgb(255,255,255)"],span.cke_colorbox[style*="rgb(255, 255, 255)"]{border:1px solid #808080;width:18px;height:18px}.cke_toolbar{float:left}.cke_rtl .cke_toolbar{float:right}.cke_toolgroup{border:0;float:left;margin:1px 2px 6px 0;padding-right:3px}.cke_rtl .cke_toolgroup{float:right;margin:1px 0 6px 2px;padding-left:3px;padding-right:0}.cke_hc .cke_toolgroup{margin-right:5px;margin-bottom:5px}.cke_hc.cke_rtl .cke_toolgroup{margin-right:0;margin-left:5px}a.cke_button{display:inline-block;height:18px;padding:4px 6px;outline:0;cursor:default;float:left;border:0;position:relative}.cke_rtl a.cke_button{float:right}.cke_hc a.cke_button{border:1px solid black;padding:3px 5px;margin:0 3px 5px 0}.cke_hc.cke_rtl a.cke_button{margin:0 0 5px 3px}a.cke_button_on{background:#fff;border:1px #bcbcbc solid;padding:3px 5px}a.cke_button_off:hover,a.cke_button_off:focus,a.cke_button_off:active{background:#e5e5e5;border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_on,.cke_hc a.cke_button_off:hover,.cke_hc a.cke_button_off:focus,.cke_hc a.cke_button_off:active{background:#e5e5e5;border:3px solid #000;padding:1px 3px}a.cke_button_disabled:hover,a.cke_button_disabled:focus,a.cke_button_disabled:active{border:0;padding:4px 6px;background-color:transparent}a.cke_button_disabled:focus{border:1px #bcbcbc solid;padding:3px 5px}.cke_hc a.cke_button_disabled:hover,.cke_hc a.cke_button_disabled:focus,.cke_hc a.cke_button_disabled:active{border:1px solid #acacac;padding:3px 5px;margin:0 3px 5px 0}.cke_hc a.cke_button_disabled:focus{border:3px solid #000;padding:1px 3px}.cke_hc.cke_rtl a.cke_button_disabled:hover,.cke_hc.cke_rtl a.cke_button_disabled:focus,.cke_hc.cke_rtl a.cke_button_disabled:active{margin:0 0 5px 3px}a.cke_button_disabled .cke_button_icon,a.cke_button_disabled .cke_button_arrow{opacity:.3}.cke_hc a.cke_button_disabled{border-color:#acacac}.cke_hc a.cke_button_disabled .cke_button_icon,.cke_hc a.cke_button_disabled .cke_button_label{opacity:.5}.cke_toolgroup a.cke_button:last-child:after,.cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:4px;top:0;right:-3px}.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-right:0;right:auto;border-left:1px solid #bcbcbc;top:0;left:-3px}.cke_hc .cke_toolgroup a.cke_button:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{border-color:#000;top:0;right:-7px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_disabled:hover:last-child:after{top:0;right:auto;left:-7px}.cke_toolgroup a.cke_button:hover:last-child:after,.cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:-4px}.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-1px;right:auto;left:-4px}.cke_hc .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:-9px}.cke_hc.cke_rtl .cke_toolgroup a.cke_button:hover:last-child:after,.cke_hc.cke_rtl .cke_toolgroup a.cke_button.cke_button_on:last-child:after{top:-2px;right:auto;left:-9px}.cke_toolbar.cke_toolbar_last .cke_toolgroup a.cke_button:last-child:after{content:none;border:0;width:0;height:0}.cke_button_icon{cursor:inherit;background-repeat:no-repeat;margin-top:1px;width:16px;height:16px;float:left;display:inline-block}.cke_rtl .cke_button_icon{float:right}.cke_hc .cke_button_icon{display:none}.cke_button_label{display:none;padding-left:3px;margin-top:1px;line-height:17px;vertical-align:middle;float:left;cursor:default;color:#484848}.cke_rtl .cke_button_label{padding-right:3px;padding-left:0;float:right}.cke_hc .cke_button_label{padding:0;display:inline-block;font-size:12px}.cke_button_arrow{display:inline-block;margin:8px 0 0 1px;width:0;height:0;cursor:default;vertical-align:top;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_rtl .cke_button_arrow{margin-right:5px;margin-left:0}.cke_hc .cke_button_arrow{font-size:10px;margin:3px 0 0 3px;width:auto;border:0}.cke_toolbar_separator{float:left;background-color:#bcbcbc;margin:4px 2px 0 2px;height:18px;width:1px}.cke_rtl .cke_toolbar_separator{float:right}.cke_hc .cke_toolbar_separator{background-color:#000;margin-left:2px;margin-right:5px;margin-bottom:9px}.cke_hc.cke_rtl .cke_toolbar_separator{margin-left:5px;margin-right:2px}.cke_toolbar_break{display:block;clear:left}.cke_rtl .cke_toolbar_break{clear:right}a.cke_toolbox_collapser{width:12px;height:11px;float:right;margin:11px 0 0;font-size:0;cursor:default;text-align:center;border:1px solid #bcbcbc}.cke_rtl .cke_toolbox_collapser{float:left}.cke_toolbox_collapser:hover{background:#e5e5e5}.cke_toolbox_collapser.cke_toolbox_collapser_min{margin:0 2px 4px}.cke_toolbox_collapser .cke_arrow{display:inline-block;height:0;width:0;font-size:0;margin-top:1px;border:3px solid transparent;border-bottom-color:#484848}.cke_toolbox_collapser.cke_toolbox_collapser_min .cke_arrow{margin-top:4px;border-bottom-color:transparent;border-top-color:#484848}.cke_hc .cke_toolbox_collapser .cke_arrow{font-size:8px;width:auto;border:0;margin-top:0}.cke_menuitem span{cursor:default}.cke_menubutton{display:block}.cke_hc .cke_menubutton{padding:2px}.cke_menubutton:hover,.cke_menubutton:focus,.cke_menubutton:active{background-color:#e9e9e9;display:block;outline:1px dotted}.cke_menubutton:hover{outline:0}.cke_hc .cke_menubutton:hover,.cke_hc .cke_menubutton:focus,.cke_hc .cke_menubutton:active{border:2px solid;padding:0}.cke_menubutton_disabled:hover,.cke_menubutton_disabled:focus,.cke_menubutton_disabled:active{background-color:transparent;outline:0}.cke_menubutton_inner{display:table-row}.cke_menubutton_icon,.cke_menubutton_label,.cke_menuarrow{display:table-cell}.cke_menubutton_icon{background-color:#f8f8f8;padding:6px 4px}.cke_hc .cke_menubutton_icon{height:16px;width:0;padding:4px 0}.cke_menubutton:hover .cke_menubutton_icon,.cke_menubutton:focus .cke_menubutton_icon,.cke_menubutton:active .cke_menubutton_icon{background-color:#e9e9e9}.cke_menubutton_disabled:hover .cke_menubutton_icon,.cke_menubutton_disabled:focus .cke_menubutton_icon,.cke_menubutton_disabled:active .cke_menubutton_icon{background-color:#f8f8f8;outline:0}.cke_menuitem .cke_menubutton_on{background-color:#e9e9e9;border:1px solid #dedede;outline:0}.cke_menubutton_on .cke_menubutton_icon{padding-right:3px;background-color:#e9e9e9}.cke_menubutton_label{padding:0 5px;background-color:transparent;width:100%;vertical-align:middle}.cke_menubutton_shortcut{color:#979797}.cke_menubutton_disabled .cke_menubutton_label{opacity:.3;filter:alpha(opacity=30)}.cke_panel_frame .cke_menubutton_label{display:none}.cke_menuseparator{background-color:#d1d1d1;height:1px}.cke_menuarrow{background:transparent url(images/arrow.png) no-repeat 0 10px;padding:0 5px}.cke_rtl .cke_menuarrow{background-position:5px -13px;background-repeat:no-repeat}.cke_hc .cke_menuarrow{background-image:none}.cke_menuarrow span{display:none}.cke_hc .cke_menuarrow span{vertical-align:middle;display:inline}.cke_combo{display:inline-block;float:left;position:relative;margin-bottom:5px}.cke_rtl .cke_combo{float:right}.cke_hc .cke_combo{margin-top:1px;margin-bottom:10px}.cke_combo:after{content:"";position:absolute;height:18px;width:0;border-right:1px solid #bcbcbc;margin-top:5px;top:0;right:0}.cke_rtl .cke_combo:after{border-right:0;border-left:1px solid #bcbcbc;right:auto;left:0}.cke_hc .cke_combo:after{border-color:#000}a.cke_combo_button{cursor:default;display:inline-block;float:left;margin:0;padding:1px}.cke_rtl a.cke_combo_button{float:right}.cke_hc a.cke_combo_button{padding:4px}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:hover,.cke_combo_off a.cke_combo_button:focus,.cke_combo_off a.cke_combo_button:active{background:#e5e5e5;border:1px solid #bcbcbc;padding:0 0 0 1px;margin-left:-1px}.cke_combo_off a.cke_combo_button:focus{outline:0}.cke_combo_on a.cke_combo_button,.cke_combo_off a.cke_combo_button:active{background:#fff}.cke_rtl .cke_combo_on a.cke_combo_button,.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:0 1px 0 0;margin-left:0;margin-right:-1px}.cke_hc .cke_combo_on a.cke_combo_button,.cke_hc .cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_combo_off a.cke_combo_button:active{border:3px solid #000;padding:1px 1px 1px 2px}.cke_hc.cke_rtl .cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_combo_off a.cke_combo_button:active{padding:1px 2px 1px 1px}.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 0 0 3px;margin-left:-3px}.cke_rtl .cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_rtl .cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0 3px 0 0;margin-left:0;margin-right:-3px}.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 1px 1px 7px;margin-left:-6px}.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc.cke_rtl .cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px 7px 1px 1px;margin-left:0;margin-right:-6px}.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:0;margin:0}.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbox .cke_toolbar:first-child>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_on a.cke_combo_button,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:hover,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:focus,.cke_hc .cke_toolbar_break+.cke_toolbar>.cke_toolbar_start+.cke_combo_off a.cke_combo_button:active{padding:1px;margin:0}.cke_toolbar .cke_combo+.cke_toolbar_end,.cke_toolbar .cke_combo+.cke_toolgroup{margin-right:0;margin-left:2px}.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:2px}.cke_hc .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:5px}.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolbar_end,.cke_hc.cke_rtl .cke_toolbar .cke_combo+.cke_toolgroup{margin-left:0;margin-right:5px}.cke_toolbar.cke_toolbar_last .cke_combo:nth-last-child(-n+2):after{content:none;border:0;width:0;height:0}.cke_combo_text{line-height:26px;padding-left:10px;text-overflow:ellipsis;overflow:hidden;float:left;cursor:default;color:#484848;width:60px}.cke_rtl .cke_combo_text{float:right;text-align:right;padding-left:0;padding-right:10px}.cke_hc .cke_combo_text{line-height:18px;font-size:12px}.cke_combo_open{cursor:default;display:inline-block;font-size:0;height:19px;line-height:17px;margin:1px 10px 1px;width:5px}.cke_hc .cke_combo_open{height:12px}.cke_combo_arrow{cursor:default;margin:11px 0 0;float:left;height:0;width:0;font-size:0;border-left:3px solid transparent;border-right:3px solid transparent;border-top:3px solid #484848}.cke_hc .cke_combo_arrow{font-size:10px;width:auto;border:0;margin-top:3px}.cke_combo_label{display:none;float:left;line-height:26px;vertical-align:top;margin-right:5px}.cke_rtl .cke_combo_label{float:right;margin-left:5px;margin-right:0}.cke_combo_disabled .cke_combo_inlinelabel,.cke_combo_disabled .cke_combo_open{opacity:.3}.cke_path{float:left;margin:-2px 0 2px}a.cke_path_item,span.cke_path_empty{display:inline-block;float:left;padding:3px 4px;margin-right:2px;cursor:default;text-decoration:none;outline:0;border:0;color:#484848;font-weight:bold;font-size:11px}.cke_rtl .cke_path,.cke_rtl .cke_path_item,.cke_rtl .cke_path_empty{float:right}a.cke_path_item:hover,a.cke_path_item:focus,a.cke_path_item:active{background-color:#e5e5e5}.cke_hc a.cke_path_item:hover,.cke_hc a.cke_path_item:focus,.cke_hc a.cke_path_item:active{border:2px solid;padding:1px 2px}.cke_button__source_label,.cke_button__sourcedialog_label{display:inline}.cke_combopanel__fontsize{width:135px}textarea.cke_source{font-family:'Courier New',Monospace;font-size:small;background-color:#fff;white-space:pre-wrap;border:0;padding:0;margin:0;display:block}.cke_wysiwyg_frame,.cke_wysiwyg_div{background-color:#fff}.cke_notifications_area{pointer-events:none}.cke_notification{pointer-events:auto;position:relative;margin:10px;width:300px;color:white;text-align:center;opacity:.95;filter:alpha(opacity = 95);-webkit-animation:fadeIn .7s;animation:fadeIn .7s}.cke_notification_message a{color:#12306f}@-webkit-keyframes fadeIn{from{opacity:.4}to{opacity:.95}}@keyframes fadeIn{from{opacity:.4}to{opacity:.95}}.cke_notification_success{background:#72b572;border:1px solid #63a563}.cke_notification_warning{background:#c83939;border:1px solid #902b2b}.cke_notification_info{background:#2e9ad0;border:1px solid #0f74a8}.cke_notification_info span.cke_notification_progress{background-color:#0f74a8;display:block;padding:0;margin:0;height:100%;overflow:hidden;position:absolute;z-index:1}.cke_notification_message{position:relative;margin:4px 23px 3px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;z-index:4;text-overflow:ellipsis;overflow:hidden}.cke_notification_close{background-image:url(images/close.png);background-repeat:no-repeat;background-position:50%;position:absolute;cursor:pointer;text-align:center;height:20px;width:20px;top:1px;right:1px;padding:0;margin:0;z-index:5;opacity:.6;filter:alpha(opacity = 60)}.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_notification_close span{display:none}.cke_notification_warning a.cke_notification_close{opacity:.8;filter:alpha(opacity = 80)}.cke_notification_warning a.cke_notification_close:hover{opacity:1;filter:alpha(opacity = 100)}.cke_chrome{visibility:inherit}.cke_voice_label{display:none}legend.cke_voice_label{display:none}a.cke_button_disabled,a.cke_button_disabled:hover,a.cke_button_disabled:focus,a.cke_button_disabled:active{filter:alpha(opacity = 30)}.cke_button_disabled .cke_button_icon{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#00ffffff,endColorstr=#00ffffff)}.cke_button_off:hover,.cke_button_off:focus,.cke_button_off:active{filter:alpha(opacity = 100)}.cke_combo_disabled .cke_combo_inlinelabel,.cke_combo_disabled .cke_combo_open{filter:alpha(opacity = 30)}.cke_toolbox_collapser{border:1px solid #a6a6a6}.cke_toolbox_collapser .cke_arrow{margin-top:1px}.cke_hc .cke_top,.cke_hc .cke_bottom,.cke_hc .cke_combo_button,.cke_hc a.cke_combo_button:hover,.cke_hc a.cke_combo_button:focus,.cke_hc .cke_toolgroup,.cke_hc .cke_button_on,.cke_hc a.cke_button_off:hover,.cke_hc a.cke_button_off:focus,.cke_hc a.cke_button_off:active,.cke_hc .cke_toolbox_collapser,.cke_hc .cke_toolbox_collapser:hover,.cke_hc .cke_panel_grouptitle{filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.cke_top,.cke_contents,.cke_bottom{width:100%}.cke_button_arrow{font-size:0}.cke_rtl .cke_toolgroup,.cke_rtl .cke_toolbar_separator,.cke_rtl .cke_button,.cke_rtl .cke_button *,.cke_rtl .cke_combo,.cke_rtl .cke_combo *,.cke_rtl .cke_path_item,.cke_rtl .cke_path_item *,.cke_rtl .cke_path_empty{float:none}.cke_rtl .cke_toolgroup,.cke_rtl .cke_toolbar_separator,.cke_rtl .cke_combo_button,.cke_rtl .cke_combo_button *,.cke_rtl .cke_button,.cke_rtl .cke_button_icon{display:inline-block;vertical-align:top}.cke_rtl .cke_button_icon{float:none}.cke_resizer{width:10px}.cke_source{white-space:normal}.cke_bottom{position:static}.cke_colorbox{font-size:0}.cke_button__about_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -0px !important;}.cke_button__bold_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -24px !important;}.cke_button__italic_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -48px !important;}.cke_button__strike_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -72px !important;}.cke_button__subscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -96px !important;}.cke_button__superscript_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -120px !important;}.cke_button__underline_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -144px !important;}.cke_button__bidiltr_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -168px !important;}.cke_button__bidirtl_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -192px !important;}.cke_button__blockquote_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -216px !important;}.cke_rtl .cke_button__copy_icon, .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -240px !important;}.cke_ltr .cke_button__copy_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -264px !important;}.cke_rtl .cke_button__cut_icon, .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -288px !important;}.cke_ltr .cke_button__cut_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -312px !important;}.cke_rtl .cke_button__paste_icon, .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -336px !important;}.cke_ltr .cke_button__paste_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -360px !important;}.cke_button__bgcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -384px !important;}.cke_button__textcolor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -408px !important;}.cke_rtl .cke_button__templates_icon, .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -432px !important;}.cke_ltr .cke_button__templates_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -456px !important;}.cke_button__copyformatting_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -480px !important;}.cke_button__creatediv_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -504px !important;}.cke_rtl .cke_button__find_icon, .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -528px !important;}.cke_ltr .cke_button__find_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -552px !important;}.cke_button__replace_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -576px !important;}.cke_button__flash_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -600px !important;}.cke_button__button_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -624px !important;}.cke_button__checkbox_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -648px !important;}.cke_button__form_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -672px !important;}.cke_button__hiddenfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -696px !important;}.cke_button__imagebutton_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -720px !important;}.cke_button__radio_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -744px !important;}.cke_rtl .cke_button__select_icon, .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -768px !important;}.cke_ltr .cke_button__select_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -792px !important;}.cke_rtl .cke_button__textarea_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -816px !important;}.cke_ltr .cke_button__textarea_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -840px !important;}.cke_rtl .cke_button__textfield_icon, .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -864px !important;}.cke_ltr .cke_button__textfield_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -888px !important;}.cke_button__horizontalrule_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -912px !important;}.cke_button__iframe_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -936px !important;}.cke_button__image_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -960px !important;}.cke_rtl .cke_button__indent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -984px !important;}.cke_ltr .cke_button__indent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1008px !important;}.cke_rtl .cke_button__outdent_icon, .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1032px !important;}.cke_ltr .cke_button__outdent_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1056px !important;}.cke_button__smiley_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1080px !important;}.cke_button__justifyblock_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1104px !important;}.cke_button__justifycenter_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1128px !important;}.cke_button__justifyleft_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1152px !important;}.cke_button__justifyright_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1176px !important;}.cke_button__language_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1200px !important;}.cke_rtl .cke_button__anchor_icon, .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1224px !important;}.cke_ltr .cke_button__anchor_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1248px !important;}.cke_button__link_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1272px !important;}.cke_button__unlink_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1296px !important;}.cke_rtl .cke_button__bulletedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1320px !important;}.cke_ltr .cke_button__bulletedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1344px !important;}.cke_rtl .cke_button__numberedlist_icon, .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1368px !important;}.cke_ltr .cke_button__numberedlist_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1392px !important;}.cke_button__maximize_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1416px !important;}.cke_rtl .cke_button__newpage_icon, .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1440px !important;}.cke_ltr .cke_button__newpage_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1464px !important;}.cke_rtl .cke_button__pagebreak_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1488px !important;}.cke_ltr .cke_button__pagebreak_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1512px !important;}.cke_rtl .cke_button__pastetext_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1536px !important;}.cke_ltr .cke_button__pastetext_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1560px !important;}.cke_rtl .cke_button__pastefromword_icon, .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1584px !important;}.cke_ltr .cke_button__pastefromword_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1608px !important;}.cke_rtl .cke_button__preview_icon, .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1632px !important;}.cke_ltr .cke_button__preview_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1656px !important;}.cke_button__print_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1680px !important;}.cke_button__removeformat_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1704px !important;}.cke_button__save_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1728px !important;}.cke_button__selectall_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1752px !important;}.cke_rtl .cke_button__showblocks_icon, .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1776px !important;}.cke_ltr .cke_button__showblocks_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1800px !important;}.cke_rtl .cke_button__source_icon, .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1824px !important;}.cke_ltr .cke_button__source_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1848px !important;}.cke_button__specialchar_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1872px !important;}.cke_button__scayt_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1896px !important;}.cke_button__table_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1920px !important;}.cke_rtl .cke_button__redo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1944px !important;}.cke_ltr .cke_button__redo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1968px !important;}.cke_rtl .cke_button__undo_icon, .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -1992px !important;}.cke_ltr .cke_button__undo_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2016px !important;}.cke_button__spellchecker_icon {background: url(icons.png?t=95e5d83) no-repeat 0 -2040px !important;}.cke_hidpi .cke_button__about_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -0px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bold_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -24px !important;background-size: 16px !important;}.cke_hidpi .cke_button__italic_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -48px !important;background-size: 16px !important;}.cke_hidpi .cke_button__strike_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -72px !important;background-size: 16px !important;}.cke_hidpi .cke_button__subscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -96px !important;background-size: 16px !important;}.cke_hidpi .cke_button__superscript_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -120px !important;background-size: 16px !important;}.cke_hidpi .cke_button__underline_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -144px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidiltr_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -168px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bidirtl_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -192px !important;background-size: 16px !important;}.cke_hidpi .cke_button__blockquote_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -216px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__copy_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -240px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__copy_icon,.cke_ltr.cke_hidpi .cke_button__copy_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -264px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__cut_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -288px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__cut_icon,.cke_ltr.cke_hidpi .cke_button__cut_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -312px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__paste_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -336px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__paste_icon,.cke_ltr.cke_hidpi .cke_button__paste_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -360px !important;background-size: 16px !important;}.cke_hidpi .cke_button__bgcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -384px !important;background-size: 16px !important;}.cke_hidpi .cke_button__textcolor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -408px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__templates_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -432px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__templates_icon,.cke_ltr.cke_hidpi .cke_button__templates_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -456px !important;background-size: 16px !important;}.cke_hidpi .cke_button__copyformatting_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -480px !important;background-size: 16px !important;}.cke_hidpi .cke_button__creatediv_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -504px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__find_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -528px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__find_icon,.cke_ltr.cke_hidpi .cke_button__find_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -552px !important;background-size: 16px !important;}.cke_hidpi .cke_button__replace_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -576px !important;background-size: 16px !important;}.cke_hidpi .cke_button__flash_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -600px !important;background-size: 16px !important;}.cke_hidpi .cke_button__button_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -624px !important;background-size: 16px !important;}.cke_hidpi .cke_button__checkbox_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -648px !important;background-size: 16px !important;}.cke_hidpi .cke_button__form_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -672px !important;background-size: 16px !important;}.cke_hidpi .cke_button__hiddenfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -696px !important;background-size: 16px !important;}.cke_hidpi .cke_button__imagebutton_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -720px !important;background-size: 16px !important;}.cke_hidpi .cke_button__radio_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -744px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__select_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -768px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__select_icon,.cke_ltr.cke_hidpi .cke_button__select_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -792px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textarea_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -816px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textarea_icon,.cke_ltr.cke_hidpi .cke_button__textarea_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -840px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__textfield_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -864px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__textfield_icon,.cke_ltr.cke_hidpi .cke_button__textfield_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -888px !important;background-size: 16px !important;}.cke_hidpi .cke_button__horizontalrule_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -912px !important;background-size: 16px !important;}.cke_hidpi .cke_button__iframe_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -936px !important;background-size: 16px !important;}.cke_hidpi .cke_button__image_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -960px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__indent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -984px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__indent_icon,.cke_ltr.cke_hidpi .cke_button__indent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1008px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__outdent_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1032px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__outdent_icon,.cke_ltr.cke_hidpi .cke_button__outdent_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1056px !important;background-size: 16px !important;}.cke_hidpi .cke_button__smiley_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1080px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyblock_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1104px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifycenter_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1128px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyleft_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1152px !important;background-size: 16px !important;}.cke_hidpi .cke_button__justifyright_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1176px !important;background-size: 16px !important;}.cke_hidpi .cke_button__language_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1200px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__anchor_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1224px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__anchor_icon,.cke_ltr.cke_hidpi .cke_button__anchor_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1248px !important;background-size: 16px !important;}.cke_hidpi .cke_button__link_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1272px !important;background-size: 16px !important;}.cke_hidpi .cke_button__unlink_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1296px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__bulletedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1320px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__bulletedlist_icon,.cke_ltr.cke_hidpi .cke_button__bulletedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1344px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__numberedlist_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1368px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__numberedlist_icon,.cke_ltr.cke_hidpi .cke_button__numberedlist_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1392px !important;background-size: 16px !important;}.cke_hidpi .cke_button__maximize_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1416px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__newpage_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1440px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__newpage_icon,.cke_ltr.cke_hidpi .cke_button__newpage_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1464px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pagebreak_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1488px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pagebreak_icon,.cke_ltr.cke_hidpi .cke_button__pagebreak_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1512px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastetext_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1536px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastetext_icon,.cke_ltr.cke_hidpi .cke_button__pastetext_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1560px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__pastefromword_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1584px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__pastefromword_icon,.cke_ltr.cke_hidpi .cke_button__pastefromword_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1608px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__preview_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1632px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__preview_icon,.cke_ltr.cke_hidpi .cke_button__preview_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1656px !important;background-size: 16px !important;}.cke_hidpi .cke_button__print_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1680px !important;background-size: 16px !important;}.cke_hidpi .cke_button__removeformat_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1704px !important;background-size: 16px !important;}.cke_hidpi .cke_button__save_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1728px !important;background-size: 16px !important;}.cke_hidpi .cke_button__selectall_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1752px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__showblocks_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1776px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__showblocks_icon,.cke_ltr.cke_hidpi .cke_button__showblocks_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1800px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__source_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1824px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__source_icon,.cke_ltr.cke_hidpi .cke_button__source_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1848px !important;background-size: 16px !important;}.cke_hidpi .cke_button__specialchar_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1872px !important;background-size: 16px !important;}.cke_hidpi .cke_button__scayt_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1896px !important;background-size: 16px !important;}.cke_hidpi .cke_button__table_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1920px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__redo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1944px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__redo_icon,.cke_ltr.cke_hidpi .cke_button__redo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1968px !important;background-size: 16px !important;}.cke_rtl.cke_hidpi .cke_button__undo_icon, .cke_hidpi .cke_mixed_dir_content .cke_rtl .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -1992px !important;background-size: 16px !important;}.cke_hidpi .cke_ltr .cke_button__undo_icon,.cke_ltr.cke_hidpi .cke_button__undo_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2016px !important;background-size: 16px !important;}.cke_hidpi .cke_button__spellchecker_icon {background: url(icons_hidpi.png?t=95e5d83) no-repeat 0 -2040px !important;background-size: 16px !important;} \ No newline at end of file diff --git a/static/plugs/ckeditor/skins/moono-lisa/icons.png b/static/plugs/ckeditor/skins/moono-lisa/icons.png new file mode 100644 index 000000000..958eedc34 Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/icons.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/icons_hidpi.png b/static/plugs/ckeditor/skins/moono-lisa/icons_hidpi.png new file mode 100644 index 000000000..7b069915f Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/icons_hidpi.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/arrow.png b/static/plugs/ckeditor/skins/moono-lisa/images/arrow.png new file mode 100644 index 000000000..d72b5f3b8 Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/arrow.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/close.png b/static/plugs/ckeditor/skins/moono-lisa/images/close.png new file mode 100644 index 000000000..40caa6ddf Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/close.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/close.png b/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/close.png new file mode 100644 index 000000000..fa00f4fce Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/close.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/lock-open.png b/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/lock-open.png new file mode 100644 index 000000000..c89978907 Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/lock-open.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/lock.png b/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/lock.png new file mode 100644 index 000000000..25ad0f4a3 Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/lock.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/refresh.png b/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/refresh.png new file mode 100644 index 000000000..117a2d4a4 Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/hidpi/refresh.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/lock-open.png b/static/plugs/ckeditor/skins/moono-lisa/images/lock-open.png new file mode 100644 index 000000000..42df5f411 Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/lock-open.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/lock.png b/static/plugs/ckeditor/skins/moono-lisa/images/lock.png new file mode 100644 index 000000000..bde67727d Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/lock.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/refresh.png b/static/plugs/ckeditor/skins/moono-lisa/images/refresh.png new file mode 100644 index 000000000..e363764e3 Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/refresh.png differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/images/spinner.gif b/static/plugs/ckeditor/skins/moono-lisa/images/spinner.gif new file mode 100644 index 000000000..d898d41fa Binary files /dev/null and b/static/plugs/ckeditor/skins/moono-lisa/images/spinner.gif differ diff --git a/static/plugs/ckeditor/skins/moono-lisa/readme.md b/static/plugs/ckeditor/skins/moono-lisa/readme.md new file mode 100644 index 000000000..3073113a4 --- /dev/null +++ b/static/plugs/ckeditor/skins/moono-lisa/readme.md @@ -0,0 +1,46 @@ +"Moono-lisa" Skin +================= + +This skin has been made a **default skin** starting from CKEditor 4.6.0 and is maintained by the core developers. + +For more information about skins, please check the [CKEditor Skin SDK](https://docs.ckeditor.com/ckeditor4/docs/#!/guide/skin_sdk_intro) +documentation. + +Features +------------------- +"Moono-lisa" is a monochromatic skin, which offers a modern, flat and minimalistic look which blends very well in modern design. +It comes with the following features: + +- Chameleon feature with brightness. +- High-contrast compatibility. +- Graphics source provided in SVG. + +Directory Structure +------------------- + +CSS parts: +- **editor.css**: the main CSS file. It's simply loading several other files, for easier maintenance, +- **mainui.css**: the file contains styles of entire editor outline structures, +- **toolbar.css**: the file contains styles of the editor toolbar space (top), +- **richcombo.css**: the file contains styles of the rich combo ui elements on toolbar, +- **panel.css**: the file contains styles of the rich combo drop-down, it's not loaded +until the first panel open up, +- **elementspath.css**: the file contains styles of the editor elements path bar (bottom), +- **menu.css**: the file contains styles of all editor menus including context menu and button drop-down, +it's not loaded until the first menu open up, +- **dialog.css**: the CSS files for the dialog UI, it's not loaded until the first dialog open, +- **reset.css**: the file defines the basis of style resets among all editor UI spaces, +- **preset.css**: the file defines the default styles of some UI elements reflecting the skin preference, +- **editor_XYZ.css** and **dialog_XYZ.css**: browser specific CSS hacks. + +Other parts: +- **skin.js**: the only JavaScript part of the skin that registers the skin, its browser specific files and its icons and defines the Chameleon feature, +- **images/**: contains a fill general used images, +- **dev/**: contains SVG and PNG source of the skin icons. + +License +------- + +Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + +For licensing, see LICENSE.md or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license) diff --git a/static/plugs/ckeditor/styles.js b/static/plugs/ckeditor/styles.js new file mode 100644 index 000000000..1f76c2de8 --- /dev/null +++ b/static/plugs/ckeditor/styles.js @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +// This file contains style definitions that can be used by CKEditor plugins. +// +// The most common use for it is the "stylescombo" plugin which shows the Styles drop-down +// list containing all styles in the editor toolbar. Other plugins, like +// the "div" plugin, use a subset of the styles for their features. +// +// If you do not have plugins that depend on this file in your editor build, you can simply +// ignore it. Otherwise it is strongly recommended to customize this file to match your +// website requirements and design properly. +// +// For more information refer to: https://docs.ckeditor.com/ckeditor4/docs/#!/guide/dev_styles-section-style-rules + +CKEDITOR.stylesSet.add( 'default', [ + /* Block styles */ + + // These styles are already available in the "Format" drop-down list ("format" plugin), + // so they are not needed here by default. You may enable them to avoid + // placing the "Format" combo in the toolbar, maintaining the same features. + /* + { name: 'Paragraph', element: 'p' }, + { name: 'Heading 1', element: 'h1' }, + { name: 'Heading 2', element: 'h2' }, + { name: 'Heading 3', element: 'h3' }, + { name: 'Heading 4', element: 'h4' }, + { name: 'Heading 5', element: 'h5' }, + { name: 'Heading 6', element: 'h6' }, + { name: 'Preformatted Text',element: 'pre' }, + { name: 'Address', element: 'address' }, + */ + + { name: 'Italic Title', element: 'h2', styles: { 'font-style': 'italic' } }, + { name: 'Subtitle', element: 'h3', styles: { 'color': '#aaa', 'font-style': 'italic' } }, + { + name: 'Special Container', + element: 'div', + styles: { + padding: '5px 10px', + background: '#eee', + border: '1px solid #ccc' + } + }, + + /* Inline styles */ + + // These are core styles available as toolbar buttons. You may opt enabling + // some of them in the Styles drop-down list, removing them from the toolbar. + // (This requires the "stylescombo" plugin.) + /* + { name: 'Strong', element: 'strong', overrides: 'b' }, + { name: 'Emphasis', element: 'em' , overrides: 'i' }, + { name: 'Underline', element: 'u' }, + { name: 'Strikethrough', element: 'strike' }, + { name: 'Subscript', element: 'sub' }, + { name: 'Superscript', element: 'sup' }, + */ + + { name: 'Marker', element: 'span', attributes: { 'class': 'marker' } }, + + { name: 'Big', element: 'big' }, + { name: 'Small', element: 'small' }, + { name: 'Typewriter', element: 'tt' }, + + { name: 'Computer Code', element: 'code' }, + { name: 'Keyboard Phrase', element: 'kbd' }, + { name: 'Sample Text', element: 'samp' }, + { name: 'Variable', element: 'var' }, + + { name: 'Deleted Text', element: 'del' }, + { name: 'Inserted Text', element: 'ins' }, + + { name: 'Cited Work', element: 'cite' }, + { name: 'Inline Quotation', element: 'q' }, + + { name: 'Language: RTL', element: 'span', attributes: { 'dir': 'rtl' } }, + { name: 'Language: LTR', element: 'span', attributes: { 'dir': 'ltr' } }, + + /* Object styles */ + + { + name: 'Styled Image (left)', + element: 'img', + attributes: { 'class': 'left' } + }, + + { + name: 'Styled Image (right)', + element: 'img', + attributes: { 'class': 'right' } + }, + + { + name: 'Compact Table', + element: 'table', + attributes: { + cellpadding: '5', + cellspacing: '0', + border: '1', + bordercolor: '#ccc' + }, + styles: { + 'border-collapse': 'collapse' + } + }, + + { name: 'Borderless Table', element: 'table', styles: { 'border-style': 'hidden', 'background-color': '#E6E6FA' } }, + { name: 'Square Bulleted List', element: 'ul', styles: { 'list-style-type': 'square' } }, + + /* Widget styles */ + + { name: 'Clean Image', type: 'widget', widget: 'image', attributes: { 'class': 'image-clean' } }, + { name: 'Grayscale Image', type: 'widget', widget: 'image', attributes: { 'class': 'image-grayscale' } }, + + { name: 'Featured Snippet', type: 'widget', widget: 'codeSnippet', attributes: { 'class': 'code-featured' } }, + + { name: 'Featured Formula', type: 'widget', widget: 'mathjax', attributes: { 'class': 'math-featured' } }, + + { name: '240p', type: 'widget', widget: 'embedSemantic', attributes: { 'class': 'embed-240p' }, group: 'size' }, + { name: '360p', type: 'widget', widget: 'embedSemantic', attributes: { 'class': 'embed-360p' }, group: 'size' }, + { name: '480p', type: 'widget', widget: 'embedSemantic', attributes: { 'class': 'embed-480p' }, group: 'size' }, + { name: '720p', type: 'widget', widget: 'embedSemantic', attributes: { 'class': 'embed-720p' }, group: 'size' }, + { name: '1080p', type: 'widget', widget: 'embedSemantic', attributes: { 'class': 'embed-1080p' }, group: 'size' }, + + // Adding space after the style name is an intended workaround. For now, there + // is no option to create two styles with the same name for different widget types. See https://dev.ckeditor.com/ticket/16664. + { name: '240p ', type: 'widget', widget: 'embed', attributes: { 'class': 'embed-240p' }, group: 'size' }, + { name: '360p ', type: 'widget', widget: 'embed', attributes: { 'class': 'embed-360p' }, group: 'size' }, + { name: '480p ', type: 'widget', widget: 'embed', attributes: { 'class': 'embed-480p' }, group: 'size' }, + { name: '720p ', type: 'widget', widget: 'embed', attributes: { 'class': 'embed-720p' }, group: 'size' }, + { name: '1080p ', type: 'widget', widget: 'embed', attributes: { 'class': 'embed-1080p' }, group: 'size' } + +] ); + diff --git a/static/plugs/clipboard/clipboard.js b/static/plugs/clipboard/clipboard.js new file mode 100644 index 000000000..5e4822acc --- /dev/null +++ b/static/plugs/clipboard/clipboard.js @@ -0,0 +1,939 @@ +/*! + * clipboard.js v2.0.0 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["ClipboardJS"] = factory(); + else + root["ClipboardJS"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 3); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, __webpack_require__(7)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), + __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? + (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(module, require('select')); + } else { + var mod = { + exports: {} + }; + factory(mod, global.select); + global.clipboardAction = mod.exports; + } +})(this, function (module, _select) { + 'use strict'; + + var _select2 = _interopRequireDefault(_select); + + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + var ClipboardAction = function () { + /** + * @param {Object} options + */ + function ClipboardAction(options) { + _classCallCheck(this, ClipboardAction); + + this.resolveOptions(options); + this.initSelection(); + } + + /** + * Defines base properties passed from constructor. + * @param {Object} options + */ + + + _createClass(ClipboardAction, [{ + key: 'resolveOptions', + value: function resolveOptions() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + this.action = options.action; + this.container = options.container; + this.emitter = options.emitter; + this.target = options.target; + this.text = options.text; + this.trigger = options.trigger; + + this.selectedText = ''; + } + }, { + key: 'initSelection', + value: function initSelection() { + if (this.text) { + this.selectFake(); + } else if (this.target) { + this.selectTarget(); + } + } + }, { + key: 'selectFake', + value: function selectFake() { + var _this = this; + + var isRTL = document.documentElement.getAttribute('dir') == 'rtl'; + + this.removeFake(); + + this.fakeHandlerCallback = function () { + return _this.removeFake(); + }; + this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true; + + this.fakeElem = document.createElement('textarea'); + // Prevent zooming on iOS + this.fakeElem.style.fontSize = '12pt'; + // Reset box model + this.fakeElem.style.border = '0'; + this.fakeElem.style.padding = '0'; + this.fakeElem.style.margin = '0'; + // Move element out of screen horizontally + this.fakeElem.style.position = 'absolute'; + this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px'; + // Move element to the same position vertically + var yPosition = window.pageYOffset || document.documentElement.scrollTop; + this.fakeElem.style.top = yPosition + 'px'; + + this.fakeElem.setAttribute('readonly', ''); + this.fakeElem.value = this.text; + + this.container.appendChild(this.fakeElem); + + this.selectedText = (0, _select2.default)(this.fakeElem); + this.copyText(); + } + }, { + key: 'removeFake', + value: function removeFake() { + if (this.fakeHandler) { + this.container.removeEventListener('click', this.fakeHandlerCallback); + this.fakeHandler = null; + this.fakeHandlerCallback = null; + } + + if (this.fakeElem) { + this.container.removeChild(this.fakeElem); + this.fakeElem = null; + } + } + }, { + key: 'selectTarget', + value: function selectTarget() { + this.selectedText = (0, _select2.default)(this.target); + this.copyText(); + } + }, { + key: 'copyText', + value: function copyText() { + var succeeded = void 0; + + try { + succeeded = document.execCommand(this.action); + } catch (err) { + succeeded = false; + } + + this.handleResult(succeeded); + } + }, { + key: 'handleResult', + value: function handleResult(succeeded) { + this.emitter.emit(succeeded ? 'success' : 'error', { + action: this.action, + text: this.selectedText, + trigger: this.trigger, + clearSelection: this.clearSelection.bind(this) + }); + } + }, { + key: 'clearSelection', + value: function clearSelection() { + if (this.trigger) { + this.trigger.focus(); + } + + window.getSelection().removeAllRanges(); + } + }, { + key: 'destroy', + value: function destroy() { + this.removeFake(); + } + }, { + key: 'action', + set: function set() { + var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy'; + + this._action = action; + + if (this._action !== 'copy' && this._action !== 'cut') { + throw new Error('Invalid "action" value, use either "copy" or "cut"'); + } + }, + get: function get() { + return this._action; + } + }, { + key: 'target', + set: function set(target) { + if (target !== undefined) { + if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) { + if (this.action === 'copy' && target.hasAttribute('disabled')) { + throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); + } + + if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) { + throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); + } + + this._target = target; + } else { + throw new Error('Invalid "target" value, use a valid Element'); + } + } + }, + get: function get() { + return this._target; + } + }]); + + return ClipboardAction; + }(); + + module.exports = ClipboardAction; +}); + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +var is = __webpack_require__(6); +var delegate = __webpack_require__(5); + +/** + * Validates all params and calls the right + * listener function based on its target type. + * + * @param {String|HTMLElement|HTMLCollection|NodeList} target + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listen(target, type, callback) { + if (!target && !type && !callback) { + throw new Error('Missing required arguments'); + } + + if (!is.string(type)) { + throw new TypeError('Second argument must be a String'); + } + + if (!is.fn(callback)) { + throw new TypeError('Third argument must be a Function'); + } + + if (is.node(target)) { + return listenNode(target, type, callback); + } + else if (is.nodeList(target)) { + return listenNodeList(target, type, callback); + } + else if (is.string(target)) { + return listenSelector(target, type, callback); + } + else { + throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList'); + } +} + +/** + * Adds an event listener to a HTML element + * and returns a remove listener function. + * + * @param {HTMLElement} node + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listenNode(node, type, callback) { + node.addEventListener(type, callback); + + return { + destroy: function() { + node.removeEventListener(type, callback); + } + } +} + +/** + * Add an event listener to a list of HTML elements + * and returns a remove listener function. + * + * @param {NodeList|HTMLCollection} nodeList + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listenNodeList(nodeList, type, callback) { + Array.prototype.forEach.call(nodeList, function(node) { + node.addEventListener(type, callback); + }); + + return { + destroy: function() { + Array.prototype.forEach.call(nodeList, function(node) { + node.removeEventListener(type, callback); + }); + } + } +} + +/** + * Add an event listener to a selector + * and returns a remove listener function. + * + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @return {Object} + */ +function listenSelector(selector, type, callback) { + return delegate(document.body, selector, type, callback); +} + +module.exports = listen; + + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + +function E () { + // Keep this empty so it's easier to inherit from + // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) +} + +E.prototype = { + on: function (name, callback, ctx) { + var e = this.e || (this.e = {}); + + (e[name] || (e[name] = [])).push({ + fn: callback, + ctx: ctx + }); + + return this; + }, + + once: function (name, callback, ctx) { + var self = this; + function listener () { + self.off(name, listener); + callback.apply(ctx, arguments); + }; + + listener._ = callback + return this.on(name, listener, ctx); + }, + + emit: function (name) { + var data = [].slice.call(arguments, 1); + var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); + var i = 0; + var len = evtArr.length; + + for (i; i < len; i++) { + evtArr[i].fn.apply(evtArr[i].ctx, data); + } + + return this; + }, + + off: function (name, callback) { + var e = this.e || (this.e = {}); + var evts = e[name]; + var liveEvents = []; + + if (evts && callback) { + for (var i = 0, len = evts.length; i < len; i++) { + if (evts[i].fn !== callback && evts[i].fn._ !== callback) + liveEvents.push(evts[i]); + } + } + + // Remove event from queue to prevent memory leak + // Suggested by https://github.com/lazd + // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 + + (liveEvents.length) + ? e[name] = liveEvents + : delete e[name]; + + return this; + } +}; + +module.exports = E; + + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, __webpack_require__(0), __webpack_require__(2), __webpack_require__(1)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), + __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? + (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else if (typeof exports !== "undefined") { + factory(module, require('./clipboard-action'), require('tiny-emitter'), require('good-listener')); + } else { + var mod = { + exports: {} + }; + factory(mod, global.clipboardAction, global.tinyEmitter, global.goodListener); + global.clipboard = mod.exports; + } +})(this, function (module, _clipboardAction, _tinyEmitter, _goodListener) { + 'use strict'; + + var _clipboardAction2 = _interopRequireDefault(_clipboardAction); + + var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter); + + var _goodListener2 = _interopRequireDefault(_goodListener); + + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + function _possibleConstructorReturn(self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; + } + + function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; + } + + var Clipboard = function (_Emitter) { + _inherits(Clipboard, _Emitter); + + /** + * @param {String|HTMLElement|HTMLCollection|NodeList} trigger + * @param {Object} options + */ + function Clipboard(trigger, options) { + _classCallCheck(this, Clipboard); + + var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this)); + + _this.resolveOptions(options); + _this.listenClick(trigger); + return _this; + } + + /** + * Defines if attributes would be resolved using internal setter functions + * or custom functions that were passed in the constructor. + * @param {Object} options + */ + + + _createClass(Clipboard, [{ + key: 'resolveOptions', + value: function resolveOptions() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + this.action = typeof options.action === 'function' ? options.action : this.defaultAction; + this.target = typeof options.target === 'function' ? options.target : this.defaultTarget; + this.text = typeof options.text === 'function' ? options.text : this.defaultText; + this.container = _typeof(options.container) === 'object' ? options.container : document.body; + } + }, { + key: 'listenClick', + value: function listenClick(trigger) { + var _this2 = this; + + this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) { + return _this2.onClick(e); + }); + } + }, { + key: 'onClick', + value: function onClick(e) { + var trigger = e.delegateTarget || e.currentTarget; + + if (this.clipboardAction) { + this.clipboardAction = null; + } + + this.clipboardAction = new _clipboardAction2.default({ + action: this.action(trigger), + target: this.target(trigger), + text: this.text(trigger), + container: this.container, + trigger: trigger, + emitter: this + }); + } + }, { + key: 'defaultAction', + value: function defaultAction(trigger) { + return getAttributeValue('action', trigger); + } + }, { + key: 'defaultTarget', + value: function defaultTarget(trigger) { + var selector = getAttributeValue('target', trigger); + + if (selector) { + return document.querySelector(selector); + } + } + }, { + key: 'defaultText', + value: function defaultText(trigger) { + return getAttributeValue('text', trigger); + } + }, { + key: 'destroy', + value: function destroy() { + this.listener.destroy(); + + if (this.clipboardAction) { + this.clipboardAction.destroy(); + this.clipboardAction = null; + } + } + }], [{ + key: 'isSupported', + value: function isSupported() { + var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut']; + + var actions = typeof action === 'string' ? [action] : action; + var support = !!document.queryCommandSupported; + + actions.forEach(function (action) { + support = support && !!document.queryCommandSupported(action); + }); + + return support; + } + }]); + + return Clipboard; + }(_tinyEmitter2.default); + + /** + * Helper function to retrieve attribute value. + * @param {String} suffix + * @param {Element} element + */ + function getAttributeValue(suffix, element) { + var attribute = 'data-clipboard-' + suffix; + + if (!element.hasAttribute(attribute)) { + return; + } + + return element.getAttribute(attribute); + } + + module.exports = Clipboard; +}); + +/***/ }), +/* 4 */ +/***/ (function(module, exports) { + +var DOCUMENT_NODE_TYPE = 9; + +/** + * A polyfill for Element.matches() + */ +if (typeof Element !== 'undefined' && !Element.prototype.matches) { + var proto = Element.prototype; + + proto.matches = proto.matchesSelector || + proto.mozMatchesSelector || + proto.msMatchesSelector || + proto.oMatchesSelector || + proto.webkitMatchesSelector; +} + +/** + * Finds the closest parent that matches a selector. + * + * @param {Element} element + * @param {String} selector + * @return {Function} + */ +function closest (element, selector) { + while (element && element.nodeType !== DOCUMENT_NODE_TYPE) { + if (typeof element.matches === 'function' && + element.matches(selector)) { + return element; + } + element = element.parentNode; + } +} + +module.exports = closest; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +var closest = __webpack_require__(4); + +/** + * Delegates event to a selector. + * + * @param {Element} element + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @param {Boolean} useCapture + * @return {Object} + */ +function _delegate(element, selector, type, callback, useCapture) { + var listenerFn = listener.apply(this, arguments); + + element.addEventListener(type, listenerFn, useCapture); + + return { + destroy: function() { + element.removeEventListener(type, listenerFn, useCapture); + } + } +} + +/** + * Delegates event to a selector. + * + * @param {Element|String|Array} [elements] + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @param {Boolean} useCapture + * @return {Object} + */ +function delegate(elements, selector, type, callback, useCapture) { + // Handle the regular Element usage + if (typeof elements.addEventListener === 'function') { + return _delegate.apply(null, arguments); + } + + // Handle Element-less usage, it defaults to global delegation + if (typeof type === 'function') { + // Use `document` as the first parameter, then apply arguments + // This is a short way to .unshift `arguments` without running into deoptimizations + return _delegate.bind(null, document).apply(null, arguments); + } + + // Handle Selector-based usage + if (typeof elements === 'string') { + elements = document.querySelectorAll(elements); + } + + // Handle Array-like based usage + return Array.prototype.map.call(elements, function (element) { + return _delegate(element, selector, type, callback, useCapture); + }); +} + +/** + * Finds closest match and invokes callback. + * + * @param {Element} element + * @param {String} selector + * @param {String} type + * @param {Function} callback + * @return {Function} + */ +function listener(element, selector, type, callback) { + return function(e) { + e.delegateTarget = closest(e.target, selector); + + if (e.delegateTarget) { + callback.call(element, e); + } + } +} + +module.exports = delegate; + + +/***/ }), +/* 6 */ +/***/ (function(module, exports) { + +/** + * Check if argument is a HTML element. + * + * @param {Object} value + * @return {Boolean} + */ +exports.node = function(value) { + return value !== undefined + && value instanceof HTMLElement + && value.nodeType === 1; +}; + +/** + * Check if argument is a list of HTML elements. + * + * @param {Object} value + * @return {Boolean} + */ +exports.nodeList = function(value) { + var type = Object.prototype.toString.call(value); + + return value !== undefined + && (type === '[object NodeList]' || type === '[object HTMLCollection]') + && ('length' in value) + && (value.length === 0 || exports.node(value[0])); +}; + +/** + * Check if argument is a string. + * + * @param {Object} value + * @return {Boolean} + */ +exports.string = function(value) { + return typeof value === 'string' + || value instanceof String; +}; + +/** + * Check if argument is a function. + * + * @param {Object} value + * @return {Boolean} + */ +exports.fn = function(value) { + var type = Object.prototype.toString.call(value); + + return type === '[object Function]'; +}; + + +/***/ }), +/* 7 */ +/***/ (function(module, exports) { + +function select(element) { + var selectedText; + + if (element.nodeName === 'SELECT') { + element.focus(); + + selectedText = element.value; + } + else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { + var isReadOnly = element.hasAttribute('readonly'); + + if (!isReadOnly) { + element.setAttribute('readonly', ''); + } + + element.select(); + element.setSelectionRange(0, element.value.length); + + if (!isReadOnly) { + element.removeAttribute('readonly'); + } + + selectedText = element.value; + } + else { + if (element.hasAttribute('contenteditable')) { + element.focus(); + } + + var selection = window.getSelection(); + var range = document.createRange(); + + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + + selectedText = selection.toString(); + } + + return selectedText; +} + +module.exports = select; + + +/***/ }) +/******/ ]); +}); \ No newline at end of file diff --git a/static/plugs/clipboard/clipboard.min.js b/static/plugs/clipboard/clipboard.min.js new file mode 100644 index 000000000..b00ee5153 --- /dev/null +++ b/static/plugs/clipboard/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.0 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e,n){var o,r,i;!function(a,c){r=[t,n(7)],o=c,void 0!==(i="function"==typeof o?o.apply(e,r):o)&&(t.exports=i)}(0,function(t,e){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(t){return t&&t.__esModule?t:{default:t}}(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=a})},function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return r(t,e,n);if(c.nodeList(t))return i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function r(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function i(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return u(document.body,t,e,n)}var c=n(6),u=n(5);t.exports=o},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){r.off(t,o),e.apply(n,arguments)}var r=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;for(o;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,f.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(s.default);t.exports=p})},function(t,e){function n(t,e){for(;t&&t.nodeType!==o;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}var o=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}t.exports=n},function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function r(t,e,n,r,i){return"function"==typeof t.addEventListener?o.apply(null,arguments):"function"==typeof n?o.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return o(t,e,n,r,i)}))}function i(t,e,n,o){return function(n){n.delegateTarget=a(n.target,e),n.delegateTarget&&o.call(t,n)}}var a=n(4);t.exports=r},function(t,e){e.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},e.nodeList=function(t){var n=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in t&&(0===t.length||e.node(t[0]))},e.string=function(t){return"string"==typeof t||t instanceof String},e.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e){function n(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}t.exports=n}])}); \ No newline at end of file diff --git a/static/plugs/distpicker/distpicker.data.js b/static/plugs/distpicker/distpicker.data.js new file mode 100644 index 000000000..faff427ac --- /dev/null +++ b/static/plugs/distpicker/distpicker.data.js @@ -0,0 +1,4528 @@ +/*! + * Distpicker v@VERSION + * https://github.com/fengyuanchen/distpicker + * + * Copyright (c) 2014-@YEAR Fengyuan Chen + * Released under the MIT license + * + * Date: @DATE + */ + +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as anonymous module. + define('ChineseDistricts', [], factory); + } else { + // Browser globals. + factory(); + } +})(function () { + + var ChineseDistricts = { + 86: { + 110000: '北京', + 120000: '天津', + 130000: '河北省', + 140000: '山西省', + 150000: '内蒙古自治区', + 210000: '辽宁省', + 220000: '吉林省', + 230000: '黑龙江省', + 310000: '上海', + 320000: '江苏省', + 330000: '浙江省', + 340000: '安徽省', + 350000: '福建省', + 360000: '江西省', + 370000: '山东省', + 410000: '河南省', + 420000: '湖北省', + 430000: '湖南省', + 440000: '广东省', + 450000: '广西壮族自治区', + 460000: '海南省', + 500000: '重庆', + 510000: '四川省', + 520000: '贵州省', + 530000: '云南省', + 540000: '西藏自治区', + 610000: '陕西省', + 620000: '甘肃省', + 630000: '青海省', + 640000: '宁夏回族自治区', + 650000: '新疆维吾尔自治区', + 710000: '台湾省', + 810000: '香港特别行政区', + 820000: '澳门特别行政区' + }, + 110000: { + 110100: '北京市', + }, + 110100: { + 110101: '东城区', + 110102: '西城区', + 110105: '朝阳区', + 110106: '丰台区', + 110107: '石景山区', + 110108: '海淀区', + 110109: '门头沟区', + 110111: '房山区', + 110112: '通州区', + 110113: '顺义区', + 110114: '昌平区', + 110115: '大兴区', + 110116: '怀柔区', + 110117: '平谷区', + 110118: '密云区', + 110119: '延庆区', + 940100: '其它区' + }, + 120000: { + 120100: '天津市', +// 120200: '天津市郊县', + }, + 120100: { + 120101: '和平区', + 120102: '河东区', + 120103: '河西区', + 120104: '南开区', + 120105: '河北区', + 120106: '红桥区', + 120110: '东丽区', + 120111: '西青区', + 120112: '津南区', + 120113: '北辰区', + 120114: '武清区', + 120115: '宝坻区', + 120116: '滨海新区', + 120117: '宁河区', + 120118: '静海区', + 120225: '蓟县', + 940100: '其它区' + }, + 130000: { + 130100: '石家庄市', + 130200: '唐山市', + 130300: '秦皇岛市', + 130400: '邯郸市', + 130500: '邢台市', + 130600: '保定市', + 130700: '张家口市', + 130800: '承德市', + 130900: '沧州市', + 131000: '廊坊市', + 131100: '衡水市', + }, + 130100: { + 130102: '长安区', + 130104: '桥西区', + 130105: '新华区', + 130107: '井陉矿区', + 130108: '裕华区', + 130109: '藁城区', + 130110: '鹿泉区', + 130111: '栾城区', + 130121: '井陉县', + 130123: '正定县', + 130125: '行唐县', + 130126: '灵寿县', + 130127: '高邑县', + 130128: '深泽县', + 130129: '赞皇县', + 130130: '无极县', + 130131: '平山县', + 130132: '元氏县', + 130133: '赵县', + 130181: '辛集市', + 130183: '晋州市', + 130184: '新乐市', + 940100: '其它区' + }, + 130200: { + 130202: '路南区', + 130203: '路北区', + 130204: '古冶区', + 130205: '开平区', + 130207: '丰南区', + 130208: '丰润区', + 130209: '曹妃甸区', + 130223: '滦县', + 130224: '滦南县', + 130225: '乐亭县', + 130227: '迁西县', + 130229: '玉田县', + 130281: '遵化市', + 130283: '迁安市', + 940100: '其它区' + }, + 130300: { + 130302: '海港区', + 130303: '山海关区', + 130304: '北戴河区', + 130306: '抚宁区', + 130321: '青龙满族自治县', + 130322: '昌黎县', + 130324: '卢龙县', + 940100: '其它区' + }, + 130400: { + 130402: '邯山区', + 130403: '丛台区', + 130404: '复兴区', + 130406: '峰峰矿区', + 130421: '邯郸县', + 130423: '临漳县', + 130424: '成安县', + 130425: '大名县', + 130426: '涉县', + 130427: '磁县', + 130428: '肥乡县', + 130429: '永年县', + 130430: '邱县', + 130431: '鸡泽县', + 130432: '广平县', + 130433: '馆陶县', + 130434: '魏县', + 130435: '曲周县', + 130481: '武安市', + 940100: '其它区' + }, + 130500: { + 130502: '桥东区', + 130503: '桥西区', + 130521: '邢台县', + 130522: '临城县', + 130523: '内丘县', + 130524: '柏乡县', + 130525: '隆尧县', + 130526: '任县', + 130527: '南和县', + 130528: '宁晋县', + 130529: '巨鹿县', + 130530: '新河县', + 130531: '广宗县', + 130532: '平乡县', + 130533: '威县', + 130534: '清河县', + 130535: '临西县', + 130581: '南宫市', + 130582: '沙河市', + 940100: '其它区' + }, + 130600: { + 130602: '竞秀区', + 130606: '莲池区', + 130607: '满城区', + 130608: '清苑区', + 130609: '徐水区', + 130623: '涞水县', + 130624: '阜平县', + 130626: '定兴县', + 130627: '唐县', + 130628: '高阳县', + 130629: '容城县', + 130630: '涞源县', + 130631: '望都县', + 130632: '安新县', + 130633: '易县', + 130634: '曲阳县', + 130635: '蠡县', + 130636: '顺平县', + 130637: '博野县', + 130638: '雄县', + 130681: '涿州市', + 130682: '定州市', + 130683: '安国市', + 130684: '高碑店市', + 940100: '其它区' + }, + 130700: { + 130702: '桥东区', + 130703: '桥西区', + 130705: '宣化区', + 130706: '下花园区', + 130721: '宣化县', + 130722: '张北县', + 130723: '康保县', + 130724: '沽源县', + 130725: '尚义县', + 130726: '蔚县', + 130727: '阳原县', + 130728: '怀安县', + 130729: '万全县', + 130730: '怀来县', + 130731: '涿鹿县', + 130732: '赤城县', + 130733: '崇礼县', + 940100: '其它区' + }, + 130800: { + 130802: '双桥区', + 130803: '双滦区', + 130804: '鹰手营子矿区', + 130821: '承德县', + 130822: '兴隆县', + 130823: '平泉县', + 130824: '滦平县', + 130825: '隆化县', + 130826: '丰宁满族自治县', + 130827: '宽城满族自治县', + 130828: '围场满族蒙古族自治县', + 940100: '其它区' + }, + 130900: { + 130902: '新华区', + 130903: '运河区', + 130921: '沧县', + 130922: '青县', + 130923: '东光县', + 130924: '海兴县', + 130925: '盐山县', + 130926: '肃宁县', + 130927: '南皮县', + 130928: '吴桥县', + 130929: '献县', + 130930: '孟村回族自治县', + 130981: '泊头市', + 130982: '任丘市', + 130983: '黄骅市', + 130984: '河间市', + 940100: '其它区' + }, + 131000: { + 131002: '安次区', + 131003: '广阳区', + 131022: '固安县', + 131023: '永清县', + 131024: '香河县', + 131025: '大城县', + 131026: '文安县', + 131028: '大厂回族自治县', + 131081: '霸州市', + 131082: '三河市', + 940100: '其它区' + }, + 131100: { + 131102: '桃城区', + 131121: '枣强县', + 131122: '武邑县', + 131123: '武强县', + 131124: '饶阳县', + 131125: '安平县', + 131126: '故城县', + 131127: '景县', + 131128: '阜城县', + 131181: '冀州市', + 131182: '深州市', + 940100: '其它区' + }, + 140000: { + 140100: '太原市', + 140200: '大同市', + 140300: '阳泉市', + 140400: '长治市', + 140500: '晋城市', + 140600: '朔州市', + 140700: '晋中市', + 140800: '运城市', + 140900: '忻州市', + 141000: '临汾市', + 141100: '吕梁市', + }, + 140100: { + 140105: '小店区', + 140106: '迎泽区', + 140107: '杏花岭区', + 140108: '尖草坪区', + 140109: '万柏林区', + 140110: '晋源区', + 140121: '清徐县', + 140122: '阳曲县', + 140123: '娄烦县', + 140181: '古交市', + 940100: '其它区' + }, + 140200: { + 140202: '城区', + 140203: '矿区', + 140211: '南郊区', + 140212: '新荣区', + 140221: '阳高县', + 140222: '天镇县', + 140223: '广灵县', + 140224: '灵丘县', + 140225: '浑源县', + 140226: '左云县', + 140227: '大同县', + 940100: '其它区' + }, + 140300: { + 140302: '城区', + 140303: '矿区', + 140311: '郊区', + 140321: '平定县', + 140322: '盂县', + 940100: '其它区' + }, + 140400: { + 140402: '城区', + 140411: '郊区', + 140421: '长治县', + 140423: '襄垣县', + 140424: '屯留县', + 140425: '平顺县', + 140426: '黎城县', + 140427: '壶关县', + 140428: '长子县', + 140429: '武乡县', + 140430: '沁县', + 140431: '沁源县', + 140481: '潞城市', + 940100: '其它区' + }, + 140500: { + 140502: '城区', + 140521: '沁水县', + 140522: '阳城县', + 140524: '陵川县', + 140525: '泽州县', + 140581: '高平市', + 940100: '其它区' + }, + 140600: { + 140602: '朔城区', + 140603: '平鲁区', + 140621: '山阴县', + 140622: '应县', + 140623: '右玉县', + 140624: '怀仁县', + 940100: '其它区' + }, + 140700: { + 140702: '榆次区', + 140721: '榆社县', + 140722: '左权县', + 140723: '和顺县', + 140724: '昔阳县', + 140725: '寿阳县', + 140726: '太谷县', + 140727: '祁县', + 140728: '平遥县', + 140729: '灵石县', + 140781: '介休市', + 940100: '其它区' + }, + 140800: { + 140802: '盐湖区', + 140821: '临猗县', + 140822: '万荣县', + 140823: '闻喜县', + 140824: '稷山县', + 140825: '新绛县', + 140826: '绛县', + 140827: '垣曲县', + 140828: '夏县', + 140829: '平陆县', + 140830: '芮城县', + 140881: '永济市', + 140882: '河津市', + 940100: '其它区' + }, + 140900: { + 140902: '忻府区', + 140921: '定襄县', + 140922: '五台县', + 140923: '代县', + 140924: '繁峙县', + 140925: '宁武县', + 140926: '静乐县', + 140927: '神池县', + 140928: '五寨县', + 140929: '岢岚县', + 140930: '河曲县', + 140931: '保德县', + 140932: '偏关县', + 140981: '原平市', + 940100: '其它区' + }, + 141000: { + 141002: '尧都区', + 141021: '曲沃县', + 141022: '翼城县', + 141023: '襄汾县', + 141024: '洪洞县', + 141025: '古县', + 141026: '安泽县', + 141027: '浮山县', + 141028: '吉县', + 141029: '乡宁县', + 141030: '大宁县', + 141031: '隰县', + 141032: '永和县', + 141033: '蒲县', + 141034: '汾西县', + 141081: '侯马市', + 141082: '霍州市', + 940100: '其它区' + }, + 141100: { + 141102: '离石区', + 141121: '文水县', + 141122: '交城县', + 141123: '兴县', + 141124: '临县', + 141125: '柳林县', + 141126: '石楼县', + 141127: '岚县', + 141128: '方山县', + 141129: '中阳县', + 141130: '交口县', + 141181: '孝义市', + 141182: '汾阳市', + 940100: '其它区' + }, + 150000: { + 150100: '呼和浩特市', + 150200: '包头市', + 150300: '乌海市', + 150400: '赤峰市', + 150500: '通辽市', + 150600: '鄂尔多斯市', + 150700: '呼伦贝尔市', + 150800: '巴彦淖尔市', + 150900: '乌兰察布市', + 152200: '兴安盟', + 152500: '锡林郭勒盟', + 152900: '阿拉善盟', + }, + 150100: { + 150102: '新城区', + 150103: '回民区', + 150104: '玉泉区', + 150105: '赛罕区', + 150121: '土默特左旗', + 150122: '托克托县', + 150123: '和林格尔县', + 150124: '清水河县', + 150125: '武川县', + 940100: '其它区' + }, + 150200: { + 150202: '东河区', + 150203: '昆都仑区', + 150204: '青山区', + 150205: '石拐区', + 150206: '白云鄂博矿区', + 150207: '九原区', + 150221: '土默特右旗', + 150222: '固阳县', + 150223: '达尔罕茂明安联合旗', + 940100: '其它区' + }, + 150300: { + 150302: '海勃湾区', + 150303: '海南区', + 150304: '乌达区', + 940100: '其它区' + }, + 150400: { + 150402: '红山区', + 150403: '元宝山区', + 150404: '松山区', + 150421: '阿鲁科尔沁旗', + 150422: '巴林左旗', + 150423: '巴林右旗', + 150424: '林西县', + 150425: '克什克腾旗', + 150426: '翁牛特旗', + 150428: '喀喇沁旗', + 150429: '宁城县', + 150430: '敖汉旗', + 940100: '其它区' + }, + 150500: { + 150502: '科尔沁区', + 150521: '科尔沁左翼中旗', + 150522: '科尔沁左翼后旗', + 150523: '开鲁县', + 150524: '库伦旗', + 150525: '奈曼旗', + 150526: '扎鲁特旗', + 150581: '霍林郭勒市', + 940100: '其它区' + }, + 150600: { + 150602: '东胜区', + 150621: '达拉特旗', + 150622: '准格尔旗', + 150623: '鄂托克前旗', + 150624: '鄂托克旗', + 150625: '杭锦旗', + 150626: '乌审旗', + 150627: '伊金霍洛旗', + 940100: '其它区' + }, + 150700: { + 150702: '海拉尔区', + 150703: '扎赉诺尔区', + 150721: '阿荣旗', + 150722: '莫力达瓦达斡尔族自治旗', + 150723: '鄂伦春自治旗', + 150724: '鄂温克族自治旗', + 150725: '陈巴尔虎旗', + 150726: '新巴尔虎左旗', + 150727: '新巴尔虎右旗', + 150781: '满洲里市', + 150782: '牙克石市', + 150783: '扎兰屯市', + 150784: '额尔古纳市', + 150785: '根河市', + 940100: '其它区' + }, + 150800: { + 150802: '临河区', + 150821: '五原县', + 150822: '磴口县', + 150823: '乌拉特前旗', + 150824: '乌拉特中旗', + 150825: '乌拉特后旗', + 150826: '杭锦后旗', + 940100: '其它区' + }, + 150900: { + 150902: '集宁区', + 150921: '卓资县', + 150922: '化德县', + 150923: '商都县', + 150924: '兴和县', + 150925: '凉城县', + 150926: '察哈尔右翼前旗', + 150927: '察哈尔右翼中旗', + 150928: '察哈尔右翼后旗', + 150929: '四子王旗', + 150981: '丰镇市', + 940100: '其它区' + }, + 152200: { + 152201: '乌兰浩特市', + 152202: '阿尔山市', + 152221: '科尔沁右翼前旗', + 152222: '科尔沁右翼中旗', + 152223: '扎赉特旗', + 152224: '突泉县', + 940100: '其它区' + }, + 152500: { + 152501: '二连浩特市', + 152502: '锡林浩特市', + 152522: '阿巴嘎旗', + 152523: '苏尼特左旗', + 152524: '苏尼特右旗', + 152525: '东乌珠穆沁旗', + 152526: '西乌珠穆沁旗', + 152527: '太仆寺旗', + 152528: '镶黄旗', + 152529: '正镶白旗', + 152530: '正蓝旗', + 152531: '多伦县', + 940100: '其它区' + }, + 152900: { + 152921: '阿拉善左旗', + 152922: '阿拉善右旗', + 152923: '额济纳旗', + 940100: '其它区' + }, + 210000: { + 210100: '沈阳市', + 210200: '大连市', + 210300: '鞍山市', + 210400: '抚顺市', + 210500: '本溪市', + 210600: '丹东市', + 210700: '锦州市', + 210800: '营口市', + 210900: '阜新市', + 211000: '辽阳市', + 211100: '盘锦市', + 211200: '铁岭市', + 211300: '朝阳市', + 211400: '葫芦岛市', + }, + 210100: { + 210102: '和平区', + 210103: '沈河区', + 210104: '大东区', + 210105: '皇姑区', + 210106: '铁西区', + 210111: '苏家屯区', + 210112: '浑南区', + 210113: '沈北新区', + 210114: '于洪区', + 210122: '辽中县', + 210123: '康平县', + 210124: '法库县', + 210181: '新民市', + 940100: '其它区' + }, + 210200: { + 210202: '中山区', + 210203: '西岗区', + 210204: '沙河口区', + 210211: '甘井子区', + 210212: '旅顺口区', + 210213: '金州区', + 210214: '普兰店区', + 210224: '长海县', + 210281: '瓦房店市', + 210283: '庄河市', + 940100: '其它区' + }, + 210300: { + 210302: '铁东区', + 210303: '铁西区', + 210304: '立山区', + 210311: '千山区', + 210321: '台安县', + 210323: '岫岩满族自治县', + 210381: '海城市', + 940100: '其它区' + }, + 210400: { + 210402: '新抚区', + 210403: '东洲区', + 210404: '望花区', + 210411: '顺城区', + 210421: '抚顺县', + 210422: '新宾满族自治县', + 210423: '清原满族自治县', + 940100: '其它区' + }, + 210500: { + 210502: '平山区', + 210503: '溪湖区', + 210504: '明山区', + 210505: '南芬区', + 210521: '本溪满族自治县', + 210522: '桓仁满族自治县', + 940100: '其它区' + }, + 210600: { + 210602: '元宝区', + 210603: '振兴区', + 210604: '振安区', + 210624: '宽甸满族自治县', + 210681: '东港市', + 210682: '凤城市', + 940100: '其它区' + }, + 210700: { + 210702: '古塔区', + 210703: '凌河区', + 210711: '太和区', + 210726: '黑山县', + 210727: '义县', + 210781: '凌海市', + 210782: '北镇市', + 940100: '其它区' + }, + 210800: { + 210802: '站前区', + 210803: '西市区', + 210804: '鲅鱼圈区', + 210811: '老边区', + 210881: '盖州市', + 210882: '大石桥市', + 940100: '其它区' + }, + 210900: { + 210902: '海州区', + 210903: '新邱区', + 210904: '太平区', + 210905: '清河门区', + 210911: '细河区', + 210921: '阜新蒙古族自治县', + 210922: '彰武县', + 940100: '其它区' + }, + 211000: { + 211002: '白塔区', + 211003: '文圣区', + 211004: '宏伟区', + 211005: '弓长岭区', + 211011: '太子河区', + 211021: '辽阳县', + 211081: '灯塔市', + 940100: '其它区' + }, + 211100: { + 211102: '双台子区', + 211103: '兴隆台区', + 211121: '大洼县', + 211122: '盘山县', + 940100: '其它区' + }, + 211200: { + 211202: '银州区', + 211204: '清河区', + 211221: '铁岭县', + 211223: '西丰县', + 211224: '昌图县', + 211281: '调兵山市', + 211282: '开原市', + 940100: '其它区' + }, + 211300: { + 211302: '双塔区', + 211303: '龙城区', + 211321: '朝阳县', + 211322: '建平县', + 211324: '喀喇沁左翼蒙古族自治县', + 211381: '北票市', + 211382: '凌源市', + 940100: '其它区' + }, + 211400: { + 211402: '连山区', + 211403: '龙港区', + 211404: '南票区', + 211421: '绥中县', + 211422: '建昌县', + 211481: '兴城市', + 940100: '其它区' + }, + 220000: { + 220100: '长春市', + 220200: '吉林市', + 220300: '四平市', + 220400: '辽源市', + 220500: '通化市', + 220600: '白山市', + 220700: '松原市', + 220800: '白城市', + 222400: '延边朝鲜族自治州', + }, + 220100: { + 220102: '南关区', + 220103: '宽城区', + 220104: '朝阳区', + 220105: '二道区', + 220106: '绿园区', + 220112: '双阳区', + 220113: '九台区', + 220122: '农安县', + 220182: '榆树市', + 220183: '德惠市', + 940100: '其它区' + }, + 220200: { + 220202: '昌邑区', + 220203: '龙潭区', + 220204: '船营区', + 220211: '丰满区', + 220221: '永吉县', + 220281: '蛟河市', + 220282: '桦甸市', + 220283: '舒兰市', + 220284: '磐石市', + 940100: '其它区' + }, + 220300: { + 220302: '铁西区', + 220303: '铁东区', + 220322: '梨树县', + 220323: '伊通满族自治县', + 220381: '公主岭市', + 220382: '双辽市', + 940100: '其它区' + }, + 220400: { + 220402: '龙山区', + 220403: '西安区', + 220421: '东丰县', + 220422: '东辽县', + 940100: '其它区' + }, + 220500: { + 220502: '东昌区', + 220503: '二道江区', + 220521: '通化县', + 220523: '辉南县', + 220524: '柳河县', + 220581: '梅河口市', + 220582: '集安市', + 940100: '其它区' + }, + 220600: { + 220602: '浑江区', + 220605: '江源区', + 220621: '抚松县', + 220622: '靖宇县', + 220623: '长白朝鲜族自治县', + 220681: '临江市', + 940100: '其它区' + }, + 220700: { + 220702: '宁江区', + 220721: '前郭尔罗斯蒙古族自治县', + 220722: '长岭县', + 220723: '乾安县', + 220781: '扶余市', + 940100: '其它区' + }, + 220800: { + 220802: '洮北区', + 220821: '镇赉县', + 220822: '通榆县', + 220881: '洮南市', + 220882: '大安市', + 940100: '其它区' + }, + 222400: { + 222401: '延吉市', + 222402: '图们市', + 222403: '敦化市', + 222404: '珲春市', + 222405: '龙井市', + 222406: '和龙市', + 222424: '汪清县', + 222426: '安图县', + 940100: '其它区' + }, + 230000: { + 230100: '哈尔滨市', + 230200: '齐齐哈尔市', + 230300: '鸡西市', + 230400: '鹤岗市', + 230500: '双鸭山市', + 230600: '大庆市', + 230700: '伊春市', + 230800: '佳木斯市', + 230900: '七台河市', + 231000: '牡丹江市', + 231100: '黑河市', + 231200: '绥化市', + 232700: '大兴安岭地区', + }, + 230100: { + 230102: '道里区', + 230103: '南岗区', + 230104: '道外区', + 230108: '平房区', + 230109: '松北区', + 230110: '香坊区', + 230111: '呼兰区', + 230112: '阿城区', + 230113: '双城区', + 230123: '依兰县', + 230124: '方正县', + 230125: '宾县', + 230126: '巴彦县', + 230127: '木兰县', + 230128: '通河县', + 230129: '延寿县', + 230183: '尚志市', + 230184: '五常市', + 940100: '其它区' + }, + 230200: { + 230202: '龙沙区', + 230203: '建华区', + 230204: '铁锋区', + 230205: '昂昂溪区', + 230206: '富拉尔基区', + 230207: '碾子山区', + 230208: '梅里斯达斡尔族区', + 230221: '龙江县', + 230223: '依安县', + 230224: '泰来县', + 230225: '甘南县', + 230227: '富裕县', + 230229: '克山县', + 230230: '克东县', + 230231: '拜泉县', + 230281: '讷河市', + 940100: '其它区' + }, + 230300: { + 230302: '鸡冠区', + 230303: '恒山区', + 230304: '滴道区', + 230305: '梨树区', + 230306: '城子河区', + 230307: '麻山区', + 230321: '鸡东县', + 230381: '虎林市', + 230382: '密山市', + 940100: '其它区' + }, + 230400: { + 230402: '向阳区', + 230403: '工农区', + 230404: '南山区', + 230405: '兴安区', + 230406: '东山区', + 230407: '兴山区', + 230421: '萝北县', + 230422: '绥滨县', + 940100: '其它区' + }, + 230500: { + 230502: '尖山区', + 230503: '岭东区', + 230505: '四方台区', + 230506: '宝山区', + 230521: '集贤县', + 230522: '友谊县', + 230523: '宝清县', + 230524: '饶河县', + 940100: '其它区' + }, + 230600: { + 230602: '萨尔图区', + 230603: '龙凤区', + 230604: '让胡路区', + 230605: '红岗区', + 230606: '大同区', + 230621: '肇州县', + 230622: '肇源县', + 230623: '林甸县', + 230624: '杜尔伯特蒙古族自治县', + 940100: '其它区' + }, + 230700: { + 230702: '伊春区', + 230703: '南岔区', + 230704: '友好区', + 230705: '西林区', + 230706: '翠峦区', + 230707: '新青区', + 230708: '美溪区', + 230709: '金山屯区', + 230710: '五营区', + 230711: '乌马河区', + 230712: '汤旺河区', + 230713: '带岭区', + 230714: '乌伊岭区', + 230715: '红星区', + 230716: '上甘岭区', + 230722: '嘉荫县', + 230781: '铁力市', + 940100: '其它区' + }, + 230800: { + 230803: '向阳区', + 230804: '前进区', + 230805: '东风区', + 230811: '郊区', + 230822: '桦南县', + 230826: '桦川县', + 230828: '汤原县', + 230833: '抚远市', + 230881: '同江市', + 230882: '富锦市', + 940100: '其它区' + }, + 230900: { + 230902: '新兴区', + 230903: '桃山区', + 230904: '茄子河区', + 230921: '勃利县', + 940100: '其它区' + }, + 231000: { + 231002: '东安区', + 231003: '阳明区', + 231004: '爱民区', + 231005: '西安区', + 231025: '林口县', + 231081: '绥芬河市', + 231083: '海林市', + 231084: '宁安市', + 231085: '穆棱市', + 231086: '东宁市', + 940100: '其它区' + }, + 231100: { + 231102: '爱辉区', + 231121: '嫩江县', + 231123: '逊克县', + 231124: '孙吴县', + 231181: '北安市', + 231182: '五大连池市', + 940100: '其它区' + }, + 231200: { + 231202: '北林区', + 231221: '望奎县', + 231222: '兰西县', + 231223: '青冈县', + 231224: '庆安县', + 231225: '明水县', + 231226: '绥棱县', + 231281: '安达市', + 231282: '肇东市', + 231283: '海伦市', + 940100: '其它区' + }, + 232700: { + 232701: '加格达奇区', + 232721: '呼玛县', + 232722: '塔河县', + 232723: '漠河县', + 940100: '其它区' + }, + 310000: { + 310100: '上海市', +// 310200: '上海市郊县', + }, + 310100: { + 310101: '黄浦区', + 310104: '徐汇区', + 310105: '长宁区', + 310106: '静安区', + 310107: '普陀区', + 310109: '虹口区', + 310110: '杨浦区', + 310112: '闵行区', + 310113: '宝山区', + 310114: '嘉定区', + 310115: '浦东新区', + 310116: '金山区', + 310117: '松江区', + 310118: '青浦区', + 310120: '奉贤区', + 310230: '崇明县', + 940100: '其它区' + }, + 320000: { + 320100: '南京市', + 320200: '无锡市', + 320300: '徐州市', + 320400: '常州市', + 320500: '苏州市', + 320600: '南通市', + 320700: '连云港市', + 320800: '淮安市', + 320900: '盐城市', + 321000: '扬州市', + 321100: '镇江市', + 321200: '泰州市', + 321300: '宿迁市', + }, + 320100: { + 320102: '玄武区', + 320104: '秦淮区', + 320105: '建邺区', + 320106: '鼓楼区', + 320111: '浦口区', + 320113: '栖霞区', + 320114: '雨花台区', + 320115: '江宁区', + 320116: '六合区', + 320117: '溧水区', + 320118: '高淳区', + 940100: '其它区' + }, + 320200: { + 320205: '锡山区', + 320206: '惠山区', + 320211: '滨湖区', + 320213: '梁溪区', + 320214: '新吴区', + 320281: '江阴市', + 320282: '宜兴市', + 940100: '其它区' + }, + 320300: { + 320302: '鼓楼区', + 320303: '云龙区', + 320305: '贾汪区', + 320311: '泉山区', + 320312: '铜山区', + 320321: '丰县', + 320322: '沛县', + 320324: '睢宁县', + 320381: '新沂市', + 320382: '邳州市', + 940100: '其它区' + }, + 320400: { + 320402: '天宁区', + 320404: '钟楼区', + 320411: '新北区', + 320412: '武进区', + 320413: '金坛区', + 320481: '溧阳市', + 940100: '其它区' + }, + 320500: { + 320505: '虎丘区', + 320506: '吴中区', + 320507: '相城区', + 320508: '姑苏区', + 320509: '吴江区', + 320581: '常熟市', + 320582: '张家港市', + 320583: '昆山市', + 320585: '太仓市', + 940100: '其它区' + }, + 320600: { + 320602: '崇川区', + 320611: '港闸区', + 320612: '通州区', + 320621: '海安县', + 320623: '如东县', + 320681: '启东市', + 320682: '如皋市', + 320684: '海门市', + 940100: '其它区' + }, + 320700: { + 320703: '连云区', + 320706: '海州区', + 320707: '赣榆区', + 320722: '东海县', + 320723: '灌云县', + 320724: '灌南县', + 940100: '其它区' + }, + 320800: { + 320802: '清河区', + 320803: '淮安区', + 320804: '淮阴区', + 320811: '清浦区', + 320826: '涟水县', + 320829: '洪泽县', + 320830: '盱眙县', + 320831: '金湖县', + 940100: '其它区' + }, + 320900: { + 320902: '亭湖区', + 320903: '盐都区', + 320904: '大丰区', + 320921: '响水县', + 320922: '滨海县', + 320923: '阜宁县', + 320924: '射阳县', + 320925: '建湖县', + 320981: '东台市', + 940100: '其它区' + }, + 321000: { + 321002: '广陵区', + 321003: '邗江区', + 321012: '江都区', + 321023: '宝应县', + 321081: '仪征市', + 321084: '高邮市', + 940100: '其它区' + }, + 321100: { + 321102: '京口区', + 321111: '润州区', + 321112: '丹徒区', + 321181: '丹阳市', + 321182: '扬中市', + 321183: '句容市', + 940100: '其它区' + }, + 321200: { + 321202: '海陵区', + 321203: '高港区', + 321204: '姜堰区', + 321281: '兴化市', + 321282: '靖江市', + 321283: '泰兴市', + 940100: '其它区' + }, + 321300: { + 321302: '宿城区', + 321311: '宿豫区', + 321322: '沭阳县', + 321323: '泗阳县', + 321324: '泗洪县', + 940100: '其它区' + }, + 330000: { + 330100: '杭州市', + 330200: '宁波市', + 330300: '温州市', + 330400: '嘉兴市', + 330500: '湖州市', + 330600: '绍兴市', + 330700: '金华市', + 330800: '衢州市', + 330900: '舟山市', + 331000: '台州市', + 331100: '丽水市', + }, + 330100: { + 330102: '上城区', + 330103: '下城区', + 330104: '江干区', + 330105: '拱墅区', + 330106: '西湖区', + 330108: '滨江区', + 330109: '萧山区', + 330110: '余杭区', + 330111: '富阳区', + 330122: '桐庐县', + 330127: '淳安县', + 330182: '建德市', + 330185: '临安市', + 940100: '其它区' + }, + 330200: { + 330203: '海曙区', + 330204: '江东区', + 330205: '江北区', + 330206: '北仑区', + 330211: '镇海区', + 330212: '鄞州区', + 330225: '象山县', + 330226: '宁海县', + 330281: '余姚市', + 330282: '慈溪市', + 330283: '奉化市', + 940100: '其它区' + }, + 330300: { + 330302: '鹿城区', + 330303: '龙湾区', + 330304: '瓯海区', + 330305: '洞头区', + 330324: '永嘉县', + 330326: '平阳县', + 330327: '苍南县', + 330328: '文成县', + 330329: '泰顺县', + 330381: '瑞安市', + 330382: '乐清市', + 940100: '其它区' + }, + 330400: { + 330402: '南湖区', + 330411: '秀洲区', + 330421: '嘉善县', + 330424: '海盐县', + 330481: '海宁市', + 330482: '平湖市', + 330483: '桐乡市', + 940100: '其它区' + }, + 330500: { + 330502: '吴兴区', + 330503: '南浔区', + 330521: '德清县', + 330522: '长兴县', + 330523: '安吉县', + 940100: '其它区' + }, + 330600: { + 330602: '越城区', + 330603: '柯桥区', + 330604: '上虞区', + 330624: '新昌县', + 330681: '诸暨市', + 330683: '嵊州市', + 940100: '其它区' + }, + 330700: { + 330702: '婺城区', + 330703: '金东区', + 330723: '武义县', + 330726: '浦江县', + 330727: '磐安县', + 330781: '兰溪市', + 330782: '义乌市', + 330783: '东阳市', + 330784: '永康市', + 940100: '其它区' + }, + 330800: { + 330802: '柯城区', + 330803: '衢江区', + 330822: '常山县', + 330824: '开化县', + 330825: '龙游县', + 330881: '江山市', + 940100: '其它区' + }, + 330900: { + 330902: '定海区', + 330903: '普陀区', + 330921: '岱山县', + 330922: '嵊泗县', + 940100: '其它区' + }, + 331000: { + 331002: '椒江区', + 331003: '黄岩区', + 331004: '路桥区', + 331021: '玉环县', + 331022: '三门县', + 331023: '天台县', + 331024: '仙居县', + 331081: '温岭市', + 331082: '临海市', + 940100: '其它区' + }, + 331100: { + 331102: '莲都区', + 331121: '青田县', + 331122: '缙云县', + 331123: '遂昌县', + 331124: '松阳县', + 331125: '云和县', + 331126: '庆元县', + 331127: '景宁畲族自治县', + 331181: '龙泉市', + 940100: '其它区' + }, + 340000: { + 340100: '合肥市', + 340200: '芜湖市', + 340300: '蚌埠市', + 340400: '淮南市', + 340500: '马鞍山市', + 340600: '淮北市', + 340700: '铜陵市', + 340800: '安庆市', + 341000: '黄山市', + 341100: '滁州市', + 341200: '阜阳市', + 341300: '宿州市', + 341500: '六安市', + 341600: '亳州市', + 341700: '池州市', + 341800: '宣城市', + }, + 340100: { + 340102: '瑶海区', + 340103: '庐阳区', + 340104: '蜀山区', + 340111: '包河区', + 340121: '长丰县', + 340122: '肥东县', + 340123: '肥西县', + 340124: '庐江县', + 340181: '巢湖市', + 940100: '其它区' + }, + 340200: { + 340202: '镜湖区', + 340203: '弋江区', + 340207: '鸠江区', + 340208: '三山区', + 340221: '芜湖县', + 340222: '繁昌县', + 340223: '南陵县', + 340225: '无为县', + 940100: '其它区' + }, + 340300: { + 340302: '龙子湖区', + 340303: '蚌山区', + 340304: '禹会区', + 340311: '淮上区', + 340321: '怀远县', + 340322: '五河县', + 340323: '固镇县', + 940100: '其它区' + }, + 340400: { + 340402: '大通区', + 340403: '田家庵区', + 340404: '谢家集区', + 340405: '八公山区', + 340406: '潘集区', + 340421: '凤台县', + 340422: '寿县', + 940100: '其它区' + }, + 340500: { + 340503: '花山区', + 340504: '雨山区', + 340506: '博望区', + 340521: '当涂县', + 340522: '含山县', + 340523: '和县', + 940100: '其它区' + }, + 340600: { + 340602: '杜集区', + 340603: '相山区', + 340604: '烈山区', + 340621: '濉溪县', + 940100: '其它区' + }, + 340700: { + 340705: '铜官区', + 340706: '义安区', + 340711: '郊区', + 340722: '枞阳县', + 940100: '其它区' + }, + 340800: { + 340802: '迎江区', + 340803: '大观区', + 340811: '宜秀区', + 340822: '怀宁县', + 340824: '潜山县', + 340825: '太湖县', + 340826: '宿松县', + 340827: '望江县', + 340828: '岳西县', + 340881: '桐城市', + 940100: '其它区' + }, + 341000: { + 341002: '屯溪区', + 341003: '黄山区', + 341004: '徽州区', + 341021: '歙县', + 341022: '休宁县', + 341023: '黟县', + 341024: '祁门县', + 940100: '其它区' + }, + 341100: { + 341102: '琅琊区', + 341103: '南谯区', + 341122: '来安县', + 341124: '全椒县', + 341125: '定远县', + 341126: '凤阳县', + 341181: '天长市', + 341182: '明光市', + 940100: '其它区' + }, + 341200: { + 341202: '颍州区', + 341203: '颍东区', + 341204: '颍泉区', + 341221: '临泉县', + 341222: '太和县', + 341225: '阜南县', + 341226: '颍上县', + 341282: '界首市', + 940100: '其它区' + }, + 341300: { + 341302: '埇桥区', + 341321: '砀山县', + 341322: '萧县', + 341323: '灵璧县', + 341324: '泗县', + 940100: '其它区' + }, + 341500: { + 341502: '金安区', + 341503: '裕安区', + 341504: '叶集区', + 341522: '霍邱县', + 341523: '舒城县', + 341524: '金寨县', + 341525: '霍山县', + 940100: '其它区' + }, + 341600: { + 341602: '谯城区', + 341621: '涡阳县', + 341622: '蒙城县', + 341623: '利辛县', + 940100: '其它区' + }, + 341700: { + 341702: '贵池区', + 341721: '东至县', + 341722: '石台县', + 341723: '青阳县', + 940100: '其它区' + }, + 341800: { + 341802: '宣州区', + 341821: '郎溪县', + 341822: '广德县', + 341823: '泾县', + 341824: '绩溪县', + 341825: '旌德县', + 341881: '宁国市', + 940100: '其它区' + }, + 350000: { + 350100: '福州市', + 350200: '厦门市', + 350300: '莆田市', + 350400: '三明市', + 350500: '泉州市', + 350600: '漳州市', + 350700: '南平市', + 350800: '龙岩市', + 350900: '宁德市', + }, + 350100: { + 350102: '鼓楼区', + 350103: '台江区', + 350104: '仓山区', + 350105: '马尾区', + 350111: '晋安区', + 350121: '闽侯县', + 350122: '连江县', + 350123: '罗源县', + 350124: '闽清县', + 350125: '永泰县', + 350128: '平潭县', + 350181: '福清市', + 350182: '长乐市', + 940100: '其它区' + }, + 350200: { + 350203: '思明区', + 350205: '海沧区', + 350206: '湖里区', + 350211: '集美区', + 350212: '同安区', + 350213: '翔安区', + 940100: '其它区' + }, + 350300: { + 350302: '城厢区', + 350303: '涵江区', + 350304: '荔城区', + 350305: '秀屿区', + 350322: '仙游县', + 940100: '其它区' + }, + 350400: { + 350402: '梅列区', + 350403: '三元区', + 350421: '明溪县', + 350423: '清流县', + 350424: '宁化县', + 350425: '大田县', + 350426: '尤溪县', + 350427: '沙县', + 350428: '将乐县', + 350429: '泰宁县', + 350430: '建宁县', + 350481: '永安市', + 940100: '其它区' + }, + 350500: { + 350502: '鲤城区', + 350503: '丰泽区', + 350504: '洛江区', + 350505: '泉港区', + 350521: '惠安县', + 350524: '安溪县', + 350525: '永春县', + 350526: '德化县', + 350527: '金门县', + 350581: '石狮市', + 350582: '晋江市', + 350583: '南安市', + 940100: '其它区' + }, + 350600: { + 350602: '芗城区', + 350603: '龙文区', + 350622: '云霄县', + 350623: '漳浦县', + 350624: '诏安县', + 350625: '长泰县', + 350626: '东山县', + 350627: '南靖县', + 350628: '平和县', + 350629: '华安县', + 350681: '龙海市', + 940100: '其它区' + }, + 350700: { + 350702: '延平区', + 350703: '建阳区', + 350721: '顺昌县', + 350722: '浦城县', + 350723: '光泽县', + 350724: '松溪县', + 350725: '政和县', + 350781: '邵武市', + 350782: '武夷山市', + 350783: '建瓯市', + 940100: '其它区' + }, + 350800: { + 350802: '新罗区', + 350803: '永定区', + 350821: '长汀县', + 350823: '上杭县', + 350824: '武平县', + 350825: '连城县', + 350881: '漳平市', + 940100: '其它区' + }, + 350900: { + 350902: '蕉城区', + 350921: '霞浦县', + 350922: '古田县', + 350923: '屏南县', + 350924: '寿宁县', + 350925: '周宁县', + 350926: '柘荣县', + 350981: '福安市', + 350982: '福鼎市', + 940100: '其它区' + }, + 360000: { + 360100: '南昌市', + 360200: '景德镇市', + 360300: '萍乡市', + 360400: '九江市', + 360500: '新余市', + 360600: '鹰潭市', + 360700: '赣州市', + 360800: '吉安市', + 360900: '宜春市', + 361000: '抚州市', + 361100: '上饶市', + }, + 360100: { + 360102: '东湖区', + 360103: '西湖区', + 360104: '青云谱区', + 360105: '湾里区', + 360111: '青山湖区', + 360112: '新建区', + 360121: '南昌县', + 360123: '安义县', + 360124: '进贤县', + 940100: '其它区' + }, + 360200: { + 360202: '昌江区', + 360203: '珠山区', + 360222: '浮梁县', + 360281: '乐平市', + 940100: '其它区' + }, + 360300: { + 360302: '安源区', + 360313: '湘东区', + 360321: '莲花县', + 360322: '上栗县', + 360323: '芦溪县', + 940100: '其它区' + }, + 360400: { + 360402: '庐山区', + 360403: '浔阳区', + 360421: '九江县', + 360423: '武宁县', + 360424: '修水县', + 360425: '永修县', + 360426: '德安县', + 360427: '星子县', + 360428: '都昌县', + 360429: '湖口县', + 360430: '彭泽县', + 360481: '瑞昌市', + 360482: '共青城市', + 940100: '其它区' + }, + 360500: { + 360502: '渝水区', + 360521: '分宜县', + 940100: '其它区' + }, + 360600: { + 360602: '月湖区', + 360622: '余江县', + 360681: '贵溪市', + 940100: '其它区' + }, + 360700: { + 360702: '章贡区', + 360703: '南康区', + 360721: '赣县', + 360722: '信丰县', + 360723: '大余县', + 360724: '上犹县', + 360725: '崇义县', + 360726: '安远县', + 360727: '龙南县', + 360728: '定南县', + 360729: '全南县', + 360730: '宁都县', + 360731: '于都县', + 360732: '兴国县', + 360733: '会昌县', + 360734: '寻乌县', + 360735: '石城县', + 360781: '瑞金市', + 940100: '其它区' + }, + 360800: { + 360802: '吉州区', + 360803: '青原区', + 360821: '吉安县', + 360822: '吉水县', + 360823: '峡江县', + 360824: '新干县', + 360825: '永丰县', + 360826: '泰和县', + 360827: '遂川县', + 360828: '万安县', + 360829: '安福县', + 360830: '永新县', + 360881: '井冈山市', + 940100: '其它区' + }, + 360900: { + 360902: '袁州区', + 360921: '奉新县', + 360922: '万载县', + 360923: '上高县', + 360924: '宜丰县', + 360925: '靖安县', + 360926: '铜鼓县', + 360981: '丰城市', + 360982: '樟树市', + 360983: '高安市', + 940100: '其它区' + }, + 361000: { + 361002: '临川区', + 361021: '南城县', + 361022: '黎川县', + 361023: '南丰县', + 361024: '崇仁县', + 361025: '乐安县', + 361026: '宜黄县', + 361027: '金溪县', + 361028: '资溪县', + 361029: '东乡县', + 361030: '广昌县', + 940100: '其它区' + }, + 361100: { + 361102: '信州区', + 361103: '广丰区', + 361121: '上饶县', + 361123: '玉山县', + 361124: '铅山县', + 361125: '横峰县', + 361126: '弋阳县', + 361127: '余干县', + 361128: '鄱阳县', + 361129: '万年县', + 361130: '婺源县', + 361181: '德兴市', + 940100: '其它区' + }, + 370000: { + 370100: '济南市', + 370200: '青岛市', + 370300: '淄博市', + 370400: '枣庄市', + 370500: '东营市', + 370600: '烟台市', + 370700: '潍坊市', + 370800: '济宁市', + 370900: '泰安市', + 371000: '威海市', + 371100: '日照市', + 371200: '莱芜市', + 371300: '临沂市', + 371400: '德州市', + 371500: '聊城市', + 371600: '滨州市', + 371700: '菏泽市', + }, + 370100: { + 370102: '历下区', + 370103: '市中区', + 370104: '槐荫区', + 370105: '天桥区', + 370112: '历城区', + 370113: '长清区', + 370124: '平阴县', + 370125: '济阳县', + 370126: '商河县', + 370181: '章丘市', + 940100: '其它区' + }, + 370200: { + 370202: '市南区', + 370203: '市北区', + 370211: '黄岛区', + 370212: '崂山区', + 370213: '李沧区', + 370214: '城阳区', + 370281: '胶州市', + 370282: '即墨市', + 370283: '平度市', + 370285: '莱西市', + 940100: '其它区' + }, + 370300: { + 370302: '淄川区', + 370303: '张店区', + 370304: '博山区', + 370305: '临淄区', + 370306: '周村区', + 370321: '桓台县', + 370322: '高青县', + 370323: '沂源县', + 940100: '其它区' + }, + 370400: { + 370402: '市中区', + 370403: '薛城区', + 370404: '峄城区', + 370405: '台儿庄区', + 370406: '山亭区', + 370481: '滕州市', + 940100: '其它区' + }, + 370500: { + 370502: '东营区', + 370503: '河口区', + 370521: '垦利县', + 370522: '利津县', + 370523: '广饶县', + 940100: '其它区' + }, + 370600: { + 370602: '芝罘区', + 370611: '福山区', + 370612: '牟平区', + 370613: '莱山区', + 370634: '长岛县', + 370681: '龙口市', + 370682: '莱阳市', + 370683: '莱州市', + 370684: '蓬莱市', + 370685: '招远市', + 370686: '栖霞市', + 370687: '海阳市', + 940100: '其它区' + }, + 370700: { + 370702: '潍城区', + 370703: '寒亭区', + 370704: '坊子区', + 370705: '奎文区', + 370724: '临朐县', + 370725: '昌乐县', + 370781: '青州市', + 370782: '诸城市', + 370783: '寿光市', + 370784: '安丘市', + 370785: '高密市', + 370786: '昌邑市', + 940100: '其它区' + }, + 370800: { + 370811: '任城区', + 370812: '兖州区', + 370826: '微山县', + 370827: '鱼台县', + 370828: '金乡县', + 370829: '嘉祥县', + 370830: '汶上县', + 370831: '泗水县', + 370832: '梁山县', + 370881: '曲阜市', + 370883: '邹城市', + 940100: '其它区' + }, + 370900: { + 370902: '泰山区', + 370911: '岱岳区', + 370921: '宁阳县', + 370923: '东平县', + 370982: '新泰市', + 370983: '肥城市', + 940100: '其它区' + }, + 371000: { + 371002: '环翠区', + 371003: '文登区', + 371082: '荣成市', + 371083: '乳山市', + 940100: '其它区' + }, + 371100: { + 371102: '东港区', + 371103: '岚山区', + 371121: '五莲县', + 371122: '莒县', + 940100: '其它区' + }, + 371200: { + 371202: '莱城区', + 371203: '钢城区', + 940100: '其它区' + }, + 371300: { + 371302: '兰山区', + 371311: '罗庄区', + 371312: '河东区', + 371321: '沂南县', + 371322: '郯城县', + 371323: '沂水县', + 371324: '兰陵县', + 371325: '费县', + 371326: '平邑县', + 371327: '莒南县', + 371328: '蒙阴县', + 371329: '临沭县', + 940100: '其它区' + }, + 371400: { + 371402: '德城区', + 371403: '陵城区', + 371422: '宁津县', + 371423: '庆云县', + 371424: '临邑县', + 371425: '齐河县', + 371426: '平原县', + 371427: '夏津县', + 371428: '武城县', + 371481: '乐陵市', + 371482: '禹城市', + 940100: '其它区' + }, + 371500: { + 371502: '东昌府区', + 371521: '阳谷县', + 371522: '莘县', + 371523: '茌平县', + 371524: '东阿县', + 371525: '冠县', + 371526: '高唐县', + 371581: '临清市', + 940100: '其它区' + }, + 371600: { + 371602: '滨城区', + 371603: '沾化区', + 371621: '惠民县', + 371622: '阳信县', + 371623: '无棣县', + 371625: '博兴县', + 371626: '邹平县', + 940100: '其它区' + }, + 371700: { + 371702: '牡丹区', + 371721: '曹县', + 371722: '单县', + 371723: '成武县', + 371724: '巨野县', + 371725: '郓城县', + 371726: '鄄城县', + 371727: '定陶县', + 371728: '东明县', + 940100: '其它区' + }, + 410000: { + 410100: '郑州市', + 410200: '开封市', + 410300: '洛阳市', + 410400: '平顶山市', + 410500: '安阳市', + 410600: '鹤壁市', + 410700: '新乡市', + 410800: '焦作市', + 410900: '濮阳市', + 411000: '许昌市', + 411100: '漯河市', + 411200: '三门峡市', + 411300: '南阳市', + 411400: '商丘市', + 411500: '信阳市', + 411600: '周口市', + 411700: '驻马店市', + 419001: '济源市', + }, + 410100: { + 410102: '中原区', + 410103: '二七区', + 410104: '管城回族区', + 410105: '金水区', + 410106: '上街区', + 410108: '惠济区', + 410122: '中牟县', + 410181: '巩义市', + 410182: '荥阳市', + 410183: '新密市', + 410184: '新郑市', + 410185: '登封市', + 940100: '其它区' + }, + 410200: { + 410202: '龙亭区', + 410203: '顺河回族区', + 410204: '鼓楼区', + 410205: '禹王台区', + 410212: '祥符区', + 410221: '杞县', + 410222: '通许县', + 410223: '尉氏县', + 410225: '兰考县', + 940100: '其它区' + }, + 410300: { + 410302: '老城区', + 410303: '西工区', + 410304: '瀍河回族区', + 410305: '涧西区', + 410306: '吉利区', + 410311: '洛龙区', + 410322: '孟津县', + 410323: '新安县', + 410324: '栾川县', + 410325: '嵩县', + 410326: '汝阳县', + 410327: '宜阳县', + 410328: '洛宁县', + 410329: '伊川县', + 410381: '偃师市', + 940100: '其它区' + }, + 410400: { + 410402: '新华区', + 410403: '卫东区', + 410404: '石龙区', + 410411: '湛河区', + 410421: '宝丰县', + 410422: '叶县', + 410423: '鲁山县', + 410425: '郏县', + 410481: '舞钢市', + 410482: '汝州市', + 940100: '其它区' + }, + 410500: { + 410502: '文峰区', + 410503: '北关区', + 410505: '殷都区', + 410506: '龙安区', + 410522: '安阳县', + 410523: '汤阴县', + 410526: '滑县', + 410527: '内黄县', + 410581: '林州市', + 940100: '其它区' + }, + 410600: { + 410602: '鹤山区', + 410603: '山城区', + 410611: '淇滨区', + 410621: '浚县', + 410622: '淇县', + 940100: '其它区' + }, + 410700: { + 410702: '红旗区', + 410703: '卫滨区', + 410704: '凤泉区', + 410711: '牧野区', + 410721: '新乡县', + 410724: '获嘉县', + 410725: '原阳县', + 410726: '延津县', + 410727: '封丘县', + 410728: '长垣县', + 410781: '卫辉市', + 410782: '辉县市', + 940100: '其它区' + }, + 410800: { + 410802: '解放区', + 410803: '中站区', + 410804: '马村区', + 410811: '山阳区', + 410821: '修武县', + 410822: '博爱县', + 410823: '武陟县', + 410825: '温县', + 410882: '沁阳市', + 410883: '孟州市', + 940100: '其它区' + }, + 410900: { + 410902: '华龙区', + 410922: '清丰县', + 410923: '南乐县', + 410926: '范县', + 410927: '台前县', + 410928: '濮阳县', + 940100: '其它区' + }, + 411000: { + 411002: '魏都区', + 411023: '许昌县', + 411024: '鄢陵县', + 411025: '襄城县', + 411081: '禹州市', + 411082: '长葛市', + 940100: '其它区' + }, + 411100: { + 411102: '源汇区', + 411103: '郾城区', + 411104: '召陵区', + 411121: '舞阳县', + 411122: '临颍县', + 940100: '其它区' + }, + 411200: { + 411202: '湖滨区', + 411203: '陕州区', + 411221: '渑池县', + 411224: '卢氏县', + 411281: '义马市', + 411282: '灵宝市', + 940100: '其它区' + }, + 411300: { + 411302: '宛城区', + 411303: '卧龙区', + 411321: '南召县', + 411322: '方城县', + 411323: '西峡县', + 411324: '镇平县', + 411325: '内乡县', + 411326: '淅川县', + 411327: '社旗县', + 411328: '唐河县', + 411329: '新野县', + 411330: '桐柏县', + 411381: '邓州市', + 940100: '其它区' + }, + 411400: { + 411402: '梁园区', + 411403: '睢阳区', + 411421: '民权县', + 411422: '睢县', + 411423: '宁陵县', + 411424: '柘城县', + 411425: '虞城县', + 411426: '夏邑县', + 411481: '永城市', + 940100: '其它区' + }, + 411500: { + 411502: '浉河区', + 411503: '平桥区', + 411521: '罗山县', + 411522: '光山县', + 411523: '新县', + 411524: '商城县', + 411525: '固始县', + 411526: '潢川县', + 411527: '淮滨县', + 411528: '息县', + 940100: '其它区' + }, + 411600: { + 411602: '川汇区', + 411621: '扶沟县', + 411622: '西华县', + 411623: '商水县', + 411624: '沈丘县', + 411625: '郸城县', + 411626: '淮阳县', + 411627: '太康县', + 411628: '鹿邑县', + 411681: '项城市', + 940100: '其它区' + }, + 411700: { + 411702: '驿城区', + 411721: '西平县', + 411722: '上蔡县', + 411723: '平舆县', + 411724: '正阳县', + 411725: '确山县', + 411726: '泌阳县', + 411727: '汝南县', + 411728: '遂平县', + 411729: '新蔡县', + 940100: '其它区' + }, + 420000: { + 420100: '武汉市', + 420200: '黄石市', + 420300: '十堰市', + 420500: '宜昌市', + 420600: '襄阳市', + 420700: '鄂州市', + 420800: '荆门市', + 420900: '孝感市', + 421000: '荆州市', + 421100: '黄冈市', + 421200: '咸宁市', + 421300: '随州市', + 422800: '恩施土家族苗族自治州', + 429004: '仙桃市', + 429005: '潜江市', + 429006: '天门市', + 429021: '神农架林区', + }, + 420100: { + 420102: '江岸区', + 420103: '江汉区', + 420104: '硚口区', + 420105: '汉阳区', + 420106: '武昌区', + 420107: '青山区', + 420111: '洪山区', + 420112: '东西湖区', + 420113: '汉南区', + 420114: '蔡甸区', + 420115: '江夏区', + 420116: '黄陂区', + 420117: '新洲区', + 940100: '其它区' + }, + 420200: { + 420202: '黄石港区', + 420203: '西塞山区', + 420204: '下陆区', + 420205: '铁山区', + 420222: '阳新县', + 420281: '大冶市', + 940100: '其它区' + }, + 420300: { + 420302: '茅箭区', + 420303: '张湾区', + 420304: '郧阳区', + 420322: '郧西县', + 420323: '竹山县', + 420324: '竹溪县', + 420325: '房县', + 420381: '丹江口市', + 940100: '其它区' + }, + 420500: { + 420502: '西陵区', + 420503: '伍家岗区', + 420504: '点军区', + 420505: '猇亭区', + 420506: '夷陵区', + 420525: '远安县', + 420526: '兴山县', + 420527: '秭归县', + 420528: '长阳土家族自治县', + 420529: '五峰土家族自治县', + 420581: '宜都市', + 420582: '当阳市', + 420583: '枝江市', + 940100: '其它区' + }, + 420600: { + 420602: '襄城区', + 420606: '樊城区', + 420607: '襄州区', + 420624: '南漳县', + 420625: '谷城县', + 420626: '保康县', + 420682: '老河口市', + 420683: '枣阳市', + 420684: '宜城市', + 940100: '其它区' + }, + 420700: { + 420702: '梁子湖区', + 420703: '华容区', + 420704: '鄂城区', + 940100: '其它区' + }, + 420800: { + 420802: '东宝区', + 420804: '掇刀区', + 420821: '京山县', + 420822: '沙洋县', + 420881: '钟祥市', + 940100: '其它区' + }, + 420900: { + 420902: '孝南区', + 420921: '孝昌县', + 420922: '大悟县', + 420923: '云梦县', + 420981: '应城市', + 420982: '安陆市', + 420984: '汉川市', + 940100: '其它区' + }, + 421000: { + 421002: '沙市区', + 421003: '荆州区', + 421022: '公安县', + 421023: '监利县', + 421024: '江陵县', + 421081: '石首市', + 421083: '洪湖市', + 421087: '松滋市', + 940100: '其它区' + }, + 421100: { + 421102: '黄州区', + 421121: '团风县', + 421122: '红安县', + 421123: '罗田县', + 421124: '英山县', + 421125: '浠水县', + 421126: '蕲春县', + 421127: '黄梅县', + 421181: '麻城市', + 421182: '武穴市', + 940100: '其它区' + }, + 421200: { + 421202: '咸安区', + 421221: '嘉鱼县', + 421222: '通城县', + 421223: '崇阳县', + 421224: '通山县', + 421281: '赤壁市', + 940100: '其它区' + }, + 421300: { + 421303: '曾都区', + 421321: '随县', + 421381: '广水市', + 940100: '其它区' + }, + 422800: { + 422801: '恩施市', + 422802: '利川市', + 422822: '建始县', + 422823: '巴东县', + 422825: '宣恩县', + 422826: '咸丰县', + 422827: '来凤县', + 422828: '鹤峰县', + 940100: '其它区' + }, + 430000: { + 430100: '长沙市', + 430200: '株洲市', + 430300: '湘潭市', + 430400: '衡阳市', + 430500: '邵阳市', + 430600: '岳阳市', + 430700: '常德市', + 430800: '张家界市', + 430900: '益阳市', + 431000: '郴州市', + 431100: '永州市', + 431200: '怀化市', + 431300: '娄底市', + 433100: '湘西土家族苗族自治州', + }, + 430100: { + 430102: '芙蓉区', + 430103: '天心区', + 430104: '岳麓区', + 430105: '开福区', + 430111: '雨花区', + 430112: '望城区', + 430121: '长沙县', + 430124: '宁乡县', + 430181: '浏阳市', + 940100: '其它区' + }, + 430200: { + 430202: '荷塘区', + 430203: '芦淞区', + 430204: '石峰区', + 430211: '天元区', + 430221: '株洲县', + 430223: '攸县', + 430224: '茶陵县', + 430225: '炎陵县', + 430281: '醴陵市', + 940100: '其它区' + }, + 430300: { + 430302: '雨湖区', + 430304: '岳塘区', + 430321: '湘潭县', + 430381: '湘乡市', + 430382: '韶山市', + 940100: '其它区' + }, + 430400: { + 430405: '珠晖区', + 430406: '雁峰区', + 430407: '石鼓区', + 430408: '蒸湘区', + 430412: '南岳区', + 430421: '衡阳县', + 430422: '衡南县', + 430423: '衡山县', + 430424: '衡东县', + 430426: '祁东县', + 430481: '耒阳市', + 430482: '常宁市', + 940100: '其它区' + }, + 430500: { + 430502: '双清区', + 430503: '大祥区', + 430511: '北塔区', + 430521: '邵东县', + 430522: '新邵县', + 430523: '邵阳县', + 430524: '隆回县', + 430525: '洞口县', + 430527: '绥宁县', + 430528: '新宁县', + 430529: '城步苗族自治县', + 430581: '武冈市', + 940100: '其它区' + }, + 430600: { + 430602: '岳阳楼区', + 430603: '云溪区', + 430611: '君山区', + 430621: '岳阳县', + 430623: '华容县', + 430624: '湘阴县', + 430626: '平江县', + 430681: '汨罗市', + 430682: '临湘市', + 940100: '其它区' + }, + 430700: { + 430702: '武陵区', + 430703: '鼎城区', + 430721: '安乡县', + 430722: '汉寿县', + 430723: '澧县', + 430724: '临澧县', + 430725: '桃源县', + 430726: '石门县', + 430781: '津市市', + 940100: '其它区' + }, + 430800: { + 430802: '永定区', + 430811: '武陵源区', + 430821: '慈利县', + 430822: '桑植县', + 940100: '其它区' + }, + 430900: { + 430902: '资阳区', + 430903: '赫山区', + 430921: '南县', + 430922: '桃江县', + 430923: '安化县', + 430981: '沅江市', + 940100: '其它区' + }, + 431000: { + 431002: '北湖区', + 431003: '苏仙区', + 431021: '桂阳县', + 431022: '宜章县', + 431023: '永兴县', + 431024: '嘉禾县', + 431025: '临武县', + 431026: '汝城县', + 431027: '桂东县', + 431028: '安仁县', + 431081: '资兴市', + 940100: '其它区' + }, + 431100: { + 431102: '零陵区', + 431103: '冷水滩区', + 431121: '祁阳县', + 431122: '东安县', + 431123: '双牌县', + 431124: '道县', + 431125: '江永县', + 431126: '宁远县', + 431127: '蓝山县', + 431128: '新田县', + 431129: '江华瑶族自治县', + 940100: '其它区' + }, + 431200: { + 431202: '鹤城区', + 431221: '中方县', + 431222: '沅陵县', + 431223: '辰溪县', + 431224: '溆浦县', + 431225: '会同县', + 431226: '麻阳苗族自治县', + 431227: '新晃侗族自治县', + 431228: '芷江侗族自治县', + 431229: '靖州苗族侗族自治县', + 431230: '通道侗族自治县', + 431281: '洪江市', + 940100: '其它区' + }, + 431300: { + 431302: '娄星区', + 431321: '双峰县', + 431322: '新化县', + 431381: '冷水江市', + 431382: '涟源市', + 940100: '其它区' + }, + 433100: { + 433101: '吉首市', + 433122: '泸溪县', + 433123: '凤凰县', + 433124: '花垣县', + 433125: '保靖县', + 433126: '古丈县', + 433127: '永顺县', + 433130: '龙山县', + 940100: '其它区' + }, + 440000: { + 440100: '广州市', + 440200: '韶关市', + 440300: '深圳市', + 440400: '珠海市', + 440500: '汕头市', + 440600: '佛山市', + 440700: '江门市', + 440800: '湛江市', + 440900: '茂名市', + 441200: '肇庆市', + 441300: '惠州市', + 441400: '梅州市', + 441500: '汕尾市', + 441600: '河源市', + 441700: '阳江市', + 441800: '清远市', + 441900: '东莞市', + 442000: '中山市', + 445100: '潮州市', + 445200: '揭阳市', + 445300: '云浮市', + }, + 440100: { + 440103: '荔湾区', + 440104: '越秀区', + 440105: '海珠区', + 440106: '天河区', + 440111: '白云区', + 440112: '黄埔区', + 440113: '番禺区', + 440114: '花都区', + 440115: '南沙区', + 440117: '从化市', + 440118: '增城市', + 440119: '萝岗区', + 940100: '其它区' + }, + 440200: { + 440203: '武江区', + 440204: '浈江区', + 440205: '曲江区', + 440222: '始兴县', + 440224: '仁化县', + 440229: '翁源县', + 440232: '乳源瑶族自治县', + 440233: '新丰县', + 440281: '乐昌市', + 440282: '南雄市', + 940100: '其它区' + }, + 440300: { + 440303: '罗湖区', + 440304: '福田区', + 440305: '南山区', + 440306: '宝安区', + 440307: '龙岗区', + 440308: '盐田区', + 940100: '其它区' + }, + 440400: { + 440402: '香洲区', + 440403: '斗门区', + 440404: '金湾区', + 940100: '其它区' + }, + 440500: { + 440507: '龙湖区', + 440511: '金平区', + 440512: '濠江区', + 440513: '潮阳区', + 440514: '潮南区', + 440515: '澄海区', + 440523: '南澳县', + 940100: '其它区' + }, + 440600: { + 440604: '禅城区', + 440605: '南海区', + 440606: '顺德区', + 440607: '三水区', + 440608: '高明区', + 940100: '其它区' + }, + 440700: { + 440703: '蓬江区', + 440704: '江海区', + 440705: '新会区', + 440781: '台山市', + 440783: '开平市', + 440784: '鹤山市', + 440785: '恩平市', + 940100: '其它区' + }, + 440800: { + 440802: '赤坎区', + 440803: '霞山区', + 440804: '坡头区', + 440811: '麻章区', + 440823: '遂溪县', + 440825: '徐闻县', + 440881: '廉江市', + 440882: '雷州市', + 440883: '吴川市', + 940100: '其它区' + }, + 440900: { + 440902: '茂南区', + 440904: '电白区', + 440981: '高州市', + 440982: '化州市', + 440983: '信宜市', + 940100: '其它区' + }, + 441200: { + 441202: '端州区', + 441203: '鼎湖区', + 441204: '高要区', + 441223: '广宁县', + 441224: '怀集县', + 441225: '封开县', + 441226: '德庆县', + 441284: '四会市', + 940100: '其它区' + }, + 441300: { + 441302: '惠城区', + 441303: '惠阳区', + 441322: '博罗县', + 441323: '惠东县', + 441324: '龙门县', + 940100: '其它区' + }, + 441400: { + 441402: '梅江区', + 441403: '梅县区', + 441422: '大埔县', + 441423: '丰顺县', + 441424: '五华县', + 441426: '平远县', + 441427: '蕉岭县', + 441481: '兴宁市', + 940100: '其它区' + }, + 441500: { + 441502: '城区', + 441521: '海丰县', + 441523: '陆河县', + 441581: '陆丰市', + 940100: '其它区' + }, + 441600: { + 441602: '源城区', + 441621: '紫金县', + 441622: '龙川县', + 441623: '连平县', + 441624: '和平县', + 441625: '东源县', + 940100: '其它区' + }, + 441700: { + 441702: '江城区', + 441704: '阳东区', + 441721: '阳西县', + 441781: '阳春市', + 940100: '其它区' + }, + 441800: { + 441802: '清城区', + 441803: '清新区', + 441821: '佛冈县', + 441823: '阳山县', + 441825: '连山壮族瑶族自治县', + 441826: '连南瑶族自治县', + 441881: '英德市', + 441882: '连州市', + 940100: '其它区' + }, + 441900: { + 441901: '莞城街道', + 441902: '南城街道', + 441903: '东城街道', + 441904: '茶山镇', + 441905: '寮步镇', + 441906: '大岭山镇', + 441907: '大朗镇', + 441908: '松山湖', + 441909: '常平镇', + 441910: '横沥镇', + 441911: '东坑镇', + 441912: '石排镇', + 441913: '企石镇', + 441914: '桥头镇', + 441915: '谢岗镇', + 441916: '塘厦镇', + 441917: '樟木头镇', + 441918: '清溪镇', + 441919: '黄江镇', + 441920: '凤岗镇', + 441921: '万江区', + 441922: '高埗镇', + 441923: '石碣镇', + 441924: '石龙镇', + 441925: '麻涌镇', + 441926: '中堂镇', + 441927: '望牛墩镇', + 441928: '洪梅镇', + 441929: '道滘镇', + 441930: '虎门镇', + 441931: '厚街镇', + 441932: '长安镇', + 441933: '沙田镇', + 940100: '其它区' + }, + 442000: { + 442001: '石岐区', + 442002: '东区', + 442003: '西区', + 442004: '南区', + 442005: '火炬开发区', + 442006: '黄圃镇', + 442007: '南头镇', + 442008: '东凤镇', + 442009: '阜沙镇', + 442010: '小榄镇', + 442011: '东升镇', + 442012: '古镇镇', + 442013: '横栏镇', + 442014: '三角镇', + 442015: '民众镇', + 442016: '南朗镇', + 442017: '港口镇', + 442018: '大涌镇', + 442019: '沙溪镇', + 442020: '三乡镇', + 442021: '板芙镇', + 442022: '五桂山', + 442023: '神湾镇', + 442024: '坦洲镇', + 940100: '其它区' + }, + 445100: { + 445102: '湘桥区', + 445103: '潮安区', + 445122: '饶平县', + 940100: '其它区' + }, + 445200: { + 445202: '榕城区', + 445203: '揭东区', + 445222: '揭西县', + 445224: '惠来县', + 445281: '普宁市', + 940100: '其它区' + }, + 445300: { + 445302: '云城区', + 445303: '云安区', + 445321: '新兴县', + 445322: '郁南县', + 445381: '罗定市', + 940100: '其它区' + }, + 450000: { + 450100: '南宁市', + 450200: '柳州市', + 450300: '桂林市', + 450400: '梧州市', + 450500: '北海市', + 450600: '防城港市', + 450700: '钦州市', + 450800: '贵港市', + 450900: '玉林市', + 451000: '百色市', + 451100: '贺州市', + 451200: '河池市', + 451300: '来宾市', + 451400: '崇左市', + }, + 450100: { + 450102: '兴宁区', + 450103: '青秀区', + 450105: '江南区', + 450107: '西乡塘区', + 450108: '良庆区', + 450109: '邕宁区', + 450110: '武鸣区', + 450123: '隆安县', + 450124: '马山县', + 450125: '上林县', + 450126: '宾阳县', + 450127: '横县', + 940100: '其它区' + }, + 450200: { + 450202: '城中区', + 450203: '鱼峰区', + 450204: '柳南区', + 450205: '柳北区', + 450221: '柳江县', + 450222: '柳城县', + 450223: '鹿寨县', + 450224: '融安县', + 450225: '融水苗族自治县', + 450226: '三江侗族自治县', + 940100: '其它区' + }, + 450300: { + 450302: '秀峰区', + 450303: '叠彩区', + 450304: '象山区', + 450305: '七星区', + 450311: '雁山区', + 450312: '临桂区', + 450321: '阳朔县', + 450323: '灵川县', + 450324: '全州县', + 450325: '兴安县', + 450326: '永福县', + 450327: '灌阳县', + 450328: '龙胜各族自治县', + 450329: '资源县', + 450330: '平乐县', + 450331: '荔浦县', + 450332: '恭城瑶族自治县', + 940100: '其它区' + }, + 450400: { + 450403: '万秀区', + 450405: '长洲区', + 450406: '龙圩区', + 450421: '苍梧县', + 450422: '藤县', + 450423: '蒙山县', + 450481: '岑溪市', + 940100: '其它区' + }, + 450500: { + 450502: '海城区', + 450503: '银海区', + 450512: '铁山港区', + 450521: '合浦县', + 940100: '其它区' + }, + 450600: { + 450602: '港口区', + 450603: '防城区', + 450621: '上思县', + 450681: '东兴市', + 940100: '其它区' + }, + 450700: { + 450702: '钦南区', + 450703: '钦北区', + 450721: '灵山县', + 450722: '浦北县', + 940100: '其它区' + }, + 450800: { + 450802: '港北区', + 450803: '港南区', + 450804: '覃塘区', + 450821: '平南县', + 450881: '桂平市', + 940100: '其它区' + }, + 450900: { + 450902: '玉州区', + 450903: '福绵区', + 450921: '容县', + 450922: '陆川县', + 450923: '博白县', + 450924: '兴业县', + 450981: '北流市', + 940100: '其它区' + }, + 451000: { + 451002: '右江区', + 451021: '田阳县', + 451022: '田东县', + 451023: '平果县', + 451024: '德保县', + 451026: '那坡县', + 451027: '凌云县', + 451028: '乐业县', + 451029: '田林县', + 451030: '西林县', + 451031: '隆林各族自治县', + 451081: '靖西市', + 940100: '其它区' + }, + 451100: { + 451102: '八步区', + 451121: '昭平县', + 451122: '钟山县', + 451123: '富川瑶族自治县', + 940100: '其它区' + }, + 451200: { + 451202: '金城江区', + 451221: '南丹县', + 451222: '天峨县', + 451223: '凤山县', + 451224: '东兰县', + 451225: '罗城仫佬族自治县', + 451226: '环江毛南族自治县', + 451227: '巴马瑶族自治县', + 451228: '都安瑶族自治县', + 451229: '大化瑶族自治县', + 451281: '宜州市', + 940100: '其它区' + }, + 451300: { + 451302: '兴宾区', + 451321: '忻城县', + 451322: '象州县', + 451323: '武宣县', + 451324: '金秀瑶族自治县', + 451381: '合山市', + 940100: '其它区' + }, + 451400: { + 451402: '江州区', + 451421: '扶绥县', + 451422: '宁明县', + 451423: '龙州县', + 451424: '大新县', + 451425: '天等县', + 451481: '凭祥市', + 940100: '其它区' + }, + 460000: { + 460100: '海口市', + 460200: '三亚市', + 460300: '三沙市', + 460400: '儋州市', + 469001: '五指山市', + 469002: '琼海市', + 469005: '文昌市', + 469006: '万宁市', + 469007: '东方市', + 469021: '定安县', + 469022: '屯昌县', + 469023: '澄迈县', + 469024: '临高县', + 469025: '白沙黎族自治县', + 469026: '昌江黎族自治县', + 469027: '乐东黎族自治县', + 469028: '陵水黎族自治县', + 469029: '保亭黎族苗族自治县', + 469030: '琼中黎族苗族自治县', + }, + 460100: { + 460105: '秀英区', + 460106: '龙华区', + 460107: '琼山区', + 460108: '美兰区', + 940100: '其它区' + }, + 460200: { + 460200: '三亚湾', + 460202: '海棠区', + 460203: '吉阳区', + 460204: '天涯区', + 460205: '崖州区', + 940100: '其它区' + }, + 460300: { + 460321: '西沙群岛', + 460322: '南沙群岛', + 460323: '中沙群岛的岛礁及其海域', + 940100: '其它区' + }, + 500000: { + 500100: '重庆市', +// 500200: '重庆市郊县', + }, + 500100: { + 500101: '万州区', + 500102: '涪陵区', + 500103: '渝中区', + 500104: '大渡口区', + 500105: '江北区', + 500106: '沙坪坝区', + 500107: '九龙坡区', + 500108: '南岸区', + 500109: '北碚区', + 500110: '綦江区', + 500111: '大足区', + 500112: '渝北区', + 500113: '巴南区', + 500114: '黔江区', + 500115: '长寿区', + 500116: '江津区', + 500117: '合川区', + 500118: '永川区', + 500119: '南川区', + 500120: '璧山区', + 500151: '铜梁区', + 500152: '潼南区', + 500153: '荣昌区', + 500228: '梁平县', + 500229: '城口县', + 500230: '丰都县', + 500231: '垫江县', + 500232: '武隆县', + 500233: '忠县', + 500234: '开县', + 500235: '云阳县', + 500236: '奉节县', + 500237: '巫山县', + 500238: '巫溪县', + 500240: '石柱土家族自治县', + 500241: '秀山土家族苗族自治县', + 500242: '酉阳土家族苗族自治县', + 500243: '彭水苗族土家族自治县', + 940100: '其它区' + }, + 510000: { + 510100: '成都市', + 510300: '自贡市', + 510400: '攀枝花市', + 510500: '泸州市', + 510600: '德阳市', + 510700: '绵阳市', + 510800: '广元市', + 510900: '遂宁市', + 511000: '内江市', + 511100: '乐山市', + 511300: '南充市', + 511400: '眉山市', + 511500: '宜宾市', + 511600: '广安市', + 511700: '达州市', + 511800: '雅安市', + 511900: '巴中市', + 512000: '资阳市', + 513200: '阿坝藏族羌族自治州', + 513300: '甘孜藏族自治州', + 513400: '凉山彝族自治州', + }, + 510100: { + 510104: '锦江区', + 510105: '青羊区', + 510106: '金牛区', + 510107: '武侯区', + 510108: '成华区', + 510112: '龙泉驿区', + 510113: '青白江区', + 510114: '新都区', + 510115: '温江区', + 510116: '双流区', + 510121: '金堂县', + 510124: '郫县', + 510129: '大邑县', + 510131: '蒲江县', + 510132: '新津县', + 510181: '都江堰市', + 510182: '彭州市', + 510183: '邛崃市', + 510184: '崇州市', + 940100: '其它区' + }, + 510300: { + 510302: '自流井区', + 510303: '贡井区', + 510304: '大安区', + 510311: '沿滩区', + 510321: '荣县', + 510322: '富顺县', + 940100: '其它区' + }, + 510400: { + 510402: '东区', + 510403: '西区', + 510411: '仁和区', + 510421: '米易县', + 510422: '盐边县', + 940100: '其它区' + }, + 510500: { + 510502: '江阳区', + 510503: '纳溪区', + 510504: '龙马潭区', + 510521: '泸县', + 510522: '合江县', + 510524: '叙永县', + 510525: '古蔺县', + 940100: '其它区' + }, + 510600: { + 510603: '旌阳区', + 510623: '中江县', + 510626: '罗江县', + 510681: '广汉市', + 510682: '什邡市', + 510683: '绵竹市', + 940100: '其它区' + }, + 510700: { + 510703: '涪城区', + 510704: '游仙区', + 510722: '三台县', + 510723: '盐亭县', + 510724: '安县', + 510725: '梓潼县', + 510726: '北川羌族自治县', + 510727: '平武县', + 510781: '江油市', + 940100: '其它区' + }, + 510800: { + 510802: '利州区', + 510811: '昭化区', + 510812: '朝天区', + 510821: '旺苍县', + 510822: '青川县', + 510823: '剑阁县', + 510824: '苍溪县', + 940100: '其它区' + }, + 510900: { + 510903: '船山区', + 510904: '安居区', + 510921: '蓬溪县', + 510922: '射洪县', + 510923: '大英县', + 940100: '其它区' + }, + 511000: { + 511002: '市中区', + 511011: '东兴区', + 511024: '威远县', + 511025: '资中县', + 511028: '隆昌县', + 940100: '其它区' + }, + 511100: { + 511102: '市中区', + 511111: '沙湾区', + 511112: '五通桥区', + 511113: '金口河区', + 511123: '犍为县', + 511124: '井研县', + 511126: '夹江县', + 511129: '沐川县', + 511132: '峨边彝族自治县', + 511133: '马边彝族自治县', + 511181: '峨眉山市', + 940100: '其它区' + }, + 511300: { + 511302: '顺庆区', + 511303: '高坪区', + 511304: '嘉陵区', + 511321: '南部县', + 511322: '营山县', + 511323: '蓬安县', + 511324: '仪陇县', + 511325: '西充县', + 511381: '阆中市', + 940100: '其它区' + }, + 511400: { + 511402: '东坡区', + 511403: '彭山区', + 511421: '仁寿县', + 511423: '洪雅县', + 511424: '丹棱县', + 511425: '青神县', + 940100: '其它区' + }, + 511500: { + 511502: '翠屏区', + 511503: '南溪区', + 511521: '宜宾县', + 511523: '江安县', + 511524: '长宁县', + 511525: '高县', + 511526: '珙县', + 511527: '筠连县', + 511528: '兴文县', + 511529: '屏山县', + 940100: '其它区' + }, + 511600: { + 511602: '广安区', + 511603: '前锋区', + 511621: '岳池县', + 511622: '武胜县', + 511623: '邻水县', + 511681: '华蓥市', + 940100: '其它区' + }, + 511700: { + 511702: '通川区', + 511703: '达川区', + 511722: '宣汉县', + 511723: '开江县', + 511724: '大竹县', + 511725: '渠县', + 511781: '万源市', + 940100: '其它区' + }, + 511800: { + 511802: '雨城区', + 511803: '名山区', + 511822: '荥经县', + 511823: '汉源县', + 511824: '石棉县', + 511825: '天全县', + 511826: '芦山县', + 511827: '宝兴县', + 940100: '其它区' + }, + 511900: { + 511902: '巴州区', + 511903: '恩阳区', + 511921: '通江县', + 511922: '南江县', + 511923: '平昌县', + 940100: '其它区' + }, + 512000: { + 512002: '雁江区', + 512021: '安岳县', + 512022: '乐至县', + 512081: '简阳市', + 940100: '其它区' + }, + 513200: { + 513201: '马尔康市', + 513221: '汶川县', + 513222: '理县', + 513223: '茂县', + 513224: '松潘县', + 513225: '九寨沟县', + 513226: '金川县', + 513227: '小金县', + 513228: '黑水县', + 513230: '壤塘县', + 513231: '阿坝县', + 513232: '若尔盖县', + 513233: '红原县', + 940100: '其它区' + }, + 513300: { + 513301: '康定市', + 513322: '泸定县', + 513323: '丹巴县', + 513324: '九龙县', + 513325: '雅江县', + 513326: '道孚县', + 513327: '炉霍县', + 513328: '甘孜县', + 513329: '新龙县', + 513330: '德格县', + 513331: '白玉县', + 513332: '石渠县', + 513333: '色达县', + 513334: '理塘县', + 513335: '巴塘县', + 513336: '乡城县', + 513337: '稻城县', + 513338: '得荣县', + 940100: '其它区' + }, + 513400: { + 513401: '西昌市', + 513422: '木里藏族自治县', + 513423: '盐源县', + 513424: '德昌县', + 513425: '会理县', + 513426: '会东县', + 513427: '宁南县', + 513428: '普格县', + 513429: '布拖县', + 513430: '金阳县', + 513431: '昭觉县', + 513432: '喜德县', + 513433: '冕宁县', + 513434: '越西县', + 513435: '甘洛县', + 513436: '美姑县', + 513437: '雷波县', + 940100: '其它区' + }, + 520000: { + 520100: '贵阳市', + 520200: '六盘水市', + 520300: '遵义市', + 520400: '安顺市', + 520500: '毕节市', + 520600: '铜仁市', + 522300: '黔西南布依族苗族自治州', + 522600: '黔东南苗族侗族自治州', + 522700: '黔南布依族苗族自治州', + }, + 520100: { + 520102: '南明区', + 520103: '云岩区', + 520111: '花溪区', + 520112: '乌当区', + 520113: '白云区', + 520115: '观山湖区', + 520121: '开阳县', + 520122: '息烽县', + 520123: '修文县', + 520181: '清镇市', + 940100: '其它区' + }, + 520200: { + 520201: '钟山区', + 520203: '六枝特区', + 520221: '水城县', + 520222: '盘县', + 940100: '其它区' + }, + 520300: { + 520302: '红花岗区', + 520303: '汇川区', + 520321: '遵义县', + 520322: '桐梓县', + 520323: '绥阳县', + 520324: '正安县', + 520325: '道真仡佬族苗族自治县', + 520326: '务川仡佬族苗族自治县', + 520327: '凤冈县', + 520328: '湄潭县', + 520329: '余庆县', + 520330: '习水县', + 520381: '赤水市', + 520382: '仁怀市', + 940100: '其它区' + }, + 520400: { + 520402: '西秀区', + 520403: '平坝区', + 520422: '普定县', + 520423: '镇宁布依族苗族自治县', + 520424: '关岭布依族苗族自治县', + 520425: '紫云苗族布依族自治县', + 940100: '其它区' + }, + 520500: { + 520502: '七星关区', + 520521: '大方县', + 520522: '黔西县', + 520523: '金沙县', + 520524: '织金县', + 520525: '纳雍县', + 520526: '威宁彝族回族苗族自治县', + 520527: '赫章县', + 940100: '其它区' + }, + 520600: { + 520602: '碧江区', + 520603: '万山区', + 520621: '江口县', + 520622: '玉屏侗族自治县', + 520623: '石阡县', + 520624: '思南县', + 520625: '印江土家族苗族自治县', + 520626: '德江县', + 520627: '沿河土家族自治县', + 520628: '松桃苗族自治县', + 940100: '其它区' + }, + 522300: { + 522301: '兴义市', + 522322: '兴仁县', + 522323: '普安县', + 522324: '晴隆县', + 522325: '贞丰县', + 522326: '望谟县', + 522327: '册亨县', + 522328: '安龙县', + 940100: '其它区' + }, + 522600: { + 522601: '凯里市', + 522622: '黄平县', + 522623: '施秉县', + 522624: '三穗县', + 522625: '镇远县', + 522626: '岑巩县', + 522627: '天柱县', + 522628: '锦屏县', + 522629: '剑河县', + 522630: '台江县', + 522631: '黎平县', + 522632: '榕江县', + 522633: '从江县', + 522634: '雷山县', + 522635: '麻江县', + 522636: '丹寨县', + 940100: '其它区' + }, + 522700: { + 522701: '都匀市', + 522702: '福泉市', + 522722: '荔波县', + 522723: '贵定县', + 522725: '瓮安县', + 522726: '独山县', + 522727: '平塘县', + 522728: '罗甸县', + 522729: '长顺县', + 522730: '龙里县', + 522731: '惠水县', + 522732: '三都水族自治县', + 940100: '其它区' + }, + 530000: { + 530100: '昆明市', + 530300: '曲靖市', + 530400: '玉溪市', + 530500: '保山市', + 530600: '昭通市', + 530700: '丽江市', + 530800: '普洱市', + 530900: '临沧市', + 532300: '楚雄彝族自治州', + 532500: '红河哈尼族彝族自治州', + 532600: '文山壮族苗族自治州', + 532800: '西双版纳傣族自治州', + 532900: '大理白族自治州', + 533100: '德宏傣族景颇族自治州', + 533300: '怒江傈僳族自治州', + 533400: '迪庆藏族自治州', + }, + 530100: { + 530102: '五华区', + 530103: '盘龙区', + 530111: '官渡区', + 530112: '西山区', + 530113: '东川区', + 530114: '呈贡区', + 530122: '晋宁县', + 530124: '富民县', + 530125: '宜良县', + 530126: '石林彝族自治县', + 530127: '嵩明县', + 530128: '禄劝彝族苗族自治县', + 530129: '寻甸回族彝族自治县', + 530181: '安宁市', + 940100: '其它区' + }, + 530300: { + 530302: '麒麟区', + 530321: '马龙县', + 530322: '陆良县', + 530323: '师宗县', + 530324: '罗平县', + 530325: '富源县', + 530326: '会泽县', + 530328: '沾益县', + 530381: '宣威市', + 940100: '其它区' + }, + 530400: { + 530402: '红塔区', + 530403: '江川区', + 530422: '澄江县', + 530423: '通海县', + 530424: '华宁县', + 530425: '易门县', + 530426: '峨山彝族自治县', + 530427: '新平彝族傣族自治县', + 530428: '元江哈尼族彝族傣族自治县', + 940100: '其它区' + }, + 530500: { + 530502: '隆阳区', + 530521: '施甸县', + 530523: '龙陵县', + 530524: '昌宁县', + 530581: '腾冲市', + 940100: '其它区' + }, + 530600: { + 530602: '昭阳区', + 530621: '鲁甸县', + 530622: '巧家县', + 530623: '盐津县', + 530624: '大关县', + 530625: '永善县', + 530626: '绥江县', + 530627: '镇雄县', + 530628: '彝良县', + 530629: '威信县', + 530630: '水富县', + 940100: '其它区' + }, + 530700: { + 530702: '古城区', + 530721: '玉龙纳西族自治县', + 530722: '永胜县', + 530723: '华坪县', + 530724: '宁蒗彝族自治县', + 940100: '其它区' + }, + 530800: { + 530802: '思茅区', + 530821: '宁洱哈尼族彝族自治县', + 530822: '墨江哈尼族自治县', + 530823: '景东彝族自治县', + 530824: '景谷傣族彝族自治县', + 530825: '镇沅彝族哈尼族拉祜族自治县', + 530826: '江城哈尼族彝族自治县', + 530827: '孟连傣族拉祜族佤族自治县', + 530828: '澜沧拉祜族自治县', + 530829: '西盟佤族自治县', + 940100: '其它区' + }, + 530900: { + 530902: '临翔区', + 530921: '凤庆县', + 530922: '云县', + 530923: '永德县', + 530924: '镇康县', + 530925: '双江拉祜族佤族布朗族傣族自治县', + 530926: '耿马傣族佤族自治县', + 530927: '沧源佤族自治县', + 940100: '其它区' + }, + 532300: { + 532301: '楚雄市', + 532322: '双柏县', + 532323: '牟定县', + 532324: '南华县', + 532325: '姚安县', + 532326: '大姚县', + 532327: '永仁县', + 532328: '元谋县', + 532329: '武定县', + 532331: '禄丰县', + 940100: '其它区' + }, + 532500: { + 532501: '个旧市', + 532502: '开远市', + 532503: '蒙自市', + 532504: '弥勒市', + 532523: '屏边苗族自治县', + 532524: '建水县', + 532525: '石屏县', + 532527: '泸西县', + 532528: '元阳县', + 532529: '红河县', + 532530: '金平苗族瑶族傣族自治县', + 532531: '绿春县', + 532532: '河口瑶族自治县', + 940100: '其它区' + }, + 532600: { + 532601: '文山市', + 532622: '砚山县', + 532623: '西畴县', + 532624: '麻栗坡县', + 532625: '马关县', + 532626: '丘北县', + 532627: '广南县', + 532628: '富宁县', + 940100: '其它区' + }, + 532800: { + 532801: '景洪市', + 532822: '勐海县', + 532823: '勐腊县', + 940100: '其它区' + }, + 532900: { + 532901: '大理市', + 532922: '漾濞彝族自治县', + 532923: '祥云县', + 532924: '宾川县', + 532925: '弥渡县', + 532926: '南涧彝族自治县', + 532927: '巍山彝族回族自治县', + 532928: '永平县', + 532929: '云龙县', + 532930: '洱源县', + 532931: '剑川县', + 532932: '鹤庆县', + 940100: '其它区' + }, + 533100: { + 533102: '瑞丽市', + 533103: '芒市', + 533122: '梁河县', + 533123: '盈江县', + 533124: '陇川县', + 940100: '其它区' + }, + 533300: { + 533321: '泸水县', + 533323: '福贡县', + 533324: '贡山独龙族怒族自治县', + 533325: '兰坪白族普米族自治县', + 940100: '其它区' + }, + 533400: { + 533401: '香格里拉市', + 533422: '德钦县', + 533423: '维西傈僳族自治县', + 940100: '其它区' + }, + 540000: { + 540100: '拉萨市', + 540200: '日喀则市', + 540300: '昌都市', + 540400: '林芝市', + 542200: '山南地区', + 542400: '那曲地区', + 542500: '阿里地区', + }, + 540100: { + 540102: '城关区', + 540103: '堆龙德庆区', + 540121: '林周县', + 540122: '当雄县', + 540123: '尼木县', + 540124: '曲水县', + 540126: '达孜县', + 540127: '墨竹工卡县', + 940100: '其它区' + }, + 540200: { + 540202: '桑珠孜区', + 540221: '南木林县', + 540222: '江孜县', + 540223: '定日县', + 540224: '萨迦县', + 540225: '拉孜县', + 540226: '昂仁县', + 540227: '谢通门县', + 540228: '白朗县', + 540229: '仁布县', + 540230: '康马县', + 540231: '定结县', + 540232: '仲巴县', + 540233: '亚东县', + 540234: '吉隆县', + 540235: '聂拉木县', + 540236: '萨嘎县', + 540237: '岗巴县', + 940100: '其它区' + }, + 540300: { + 540302: '卡若区', + 540321: '江达县', + 540322: '贡觉县', + 540323: '类乌齐县', + 540324: '丁青县', + 540325: '察雅县', + 540326: '八宿县', + 540327: '左贡县', + 540328: '芒康县', + 540329: '洛隆县', + 540330: '边坝县', + 940100: '其它区' + }, + 540400: { + 540402: '巴宜区', + 540421: '工布江达县', + 540422: '米林县', + 540423: '墨脱县', + 540424: '波密县', + 540425: '察隅县', + 540426: '朗县', + 940100: '其它区' + }, + 542200: { + 542221: '乃东县', + 542222: '扎囊县', + 542223: '贡嘎县', + 542224: '桑日县', + 542225: '琼结县', + 542226: '曲松县', + 542227: '措美县', + 542228: '洛扎县', + 542229: '加查县', + 542231: '隆子县', + 542232: '错那县', + 542233: '浪卡子县', + 940100: '其它区' + }, + 542400: { + 542421: '那曲县', + 542422: '嘉黎县', + 542423: '比如县', + 542424: '聂荣县', + 542425: '安多县', + 542426: '申扎县', + 542427: '索县', + 542428: '班戈县', + 542429: '巴青县', + 542430: '尼玛县', + 542431: '双湖县', + 940100: '其它区' + }, + 542500: { + 542521: '普兰县', + 542522: '札达县', + 542523: '噶尔县', + 542524: '日土县', + 542525: '革吉县', + 542526: '改则县', + 542527: '措勤县', + 940100: '其它区' + }, + 610000: { + 610100: '西安市', + 610200: '铜川市', + 610300: '宝鸡市', + 610400: '咸阳市', + 610500: '渭南市', + 610600: '延安市', + 610700: '汉中市', + 610800: '榆林市', + 610900: '安康市', + 611000: '商洛市', + }, + 610100: { + 610102: '新城区', + 610103: '碑林区', + 610104: '莲湖区', + 610111: '灞桥区', + 610112: '未央区', + 610113: '雁塔区', + 610114: '阎良区', + 610115: '临潼区', + 610116: '长安区', + 610117: '高陵区', + 610122: '蓝田县', + 610124: '周至县', + 610125: '户县', + 940100: '其它区' + }, + 610200: { + 610202: '王益区', + 610203: '印台区', + 610204: '耀州区', + 610222: '宜君县', + 940100: '其它区' + }, + 610300: { + 610302: '渭滨区', + 610303: '金台区', + 610304: '陈仓区', + 610322: '凤翔县', + 610323: '岐山县', + 610324: '扶风县', + 610326: '眉县', + 610327: '陇县', + 610328: '千阳县', + 610329: '麟游县', + 610330: '凤县', + 610331: '太白县', + 940100: '其它区' + }, + 610400: { + 610402: '秦都区', + 610403: '杨陵区', + 610404: '渭城区', + 610422: '三原县', + 610423: '泾阳县', + 610424: '乾县', + 610425: '礼泉县', + 610426: '永寿县', + 610427: '彬县', + 610428: '长武县', + 610429: '旬邑县', + 610430: '淳化县', + 610431: '武功县', + 610481: '兴平市', + 940100: '其它区' + }, + 610500: { + 610502: '临渭区', + 610503: '华州区', + 610522: '潼关县', + 610523: '大荔县', + 610524: '合阳县', + 610525: '澄城县', + 610526: '蒲城县', + 610527: '白水县', + 610528: '富平县', + 610581: '韩城市', + 610582: '华阴市', + 940100: '其它区' + }, + 610600: { + 610602: '宝塔区', + 610621: '延长县', + 610622: '延川县', + 610623: '子长县', + 610624: '安塞县', + 610625: '志丹县', + 610626: '吴起县', + 610627: '甘泉县', + 610628: '富县', + 610629: '洛川县', + 610630: '宜川县', + 610631: '黄龙县', + 610632: '黄陵县', + 940100: '其它区' + }, + 610700: { + 610702: '汉台区', + 610721: '南郑县', + 610722: '城固县', + 610723: '洋县', + 610724: '西乡县', + 610725: '勉县', + 610726: '宁强县', + 610727: '略阳县', + 610728: '镇巴县', + 610729: '留坝县', + 610730: '佛坪县', + 940100: '其它区' + }, + 610800: { + 610802: '榆阳区', + 610803: '横山区', + 610821: '神木县', + 610822: '府谷县', + 610824: '靖边县', + 610825: '定边县', + 610826: '绥德县', + 610827: '米脂县', + 610828: '佳县', + 610829: '吴堡县', + 610830: '清涧县', + 610831: '子洲县', + 940100: '其它区' + }, + 610900: { + 610902: '汉滨区', + 610921: '汉阴县', + 610922: '石泉县', + 610923: '宁陕县', + 610924: '紫阳县', + 610925: '岚皋县', + 610926: '平利县', + 610927: '镇坪县', + 610928: '旬阳县', + 610929: '白河县', + 940100: '其它区' + }, + 611000: { + 611002: '商州区', + 611021: '洛南县', + 611022: '丹凤县', + 611023: '商南县', + 611024: '山阳县', + 611025: '镇安县', + 611026: '柞水县', + 940100: '其它区' + }, + 620000: { + 620100: '兰州市', + 620200: '嘉峪关市', + 620300: '金昌市', + 620400: '白银市', + 620500: '天水市', + 620600: '武威市', + 620700: '张掖市', + 620800: '平凉市', + 620900: '酒泉市', + 621000: '庆阳市', + 621100: '定西市', + 621200: '陇南市', + 622900: '临夏回族自治州', + 623000: '甘南藏族自治州', + }, + 620100: { + 620102: '城关区', + 620103: '七里河区', + 620104: '西固区', + 620105: '安宁区', + 620111: '红古区', + 620121: '永登县', + 620122: '皋兰县', + 620123: '榆中县', + 940100: '其它区' + }, + 620300: { + 620302: '金川区', + 620321: '永昌县', + 940100: '其它区' + }, + 620400: { + 620402: '白银区', + 620403: '平川区', + 620421: '靖远县', + 620422: '会宁县', + 620423: '景泰县', + 940100: '其它区' + }, + 620500: { + 620502: '秦州区', + 620503: '麦积区', + 620521: '清水县', + 620522: '秦安县', + 620523: '甘谷县', + 620524: '武山县', + 620525: '张家川回族自治县', + 940100: '其它区' + }, + 620600: { + 620602: '凉州区', + 620621: '民勤县', + 620622: '古浪县', + 620623: '天祝藏族自治县', + 940100: '其它区' + }, + 620700: { + 620702: '甘州区', + 620721: '肃南裕固族自治县', + 620722: '民乐县', + 620723: '临泽县', + 620724: '高台县', + 620725: '山丹县', + 940100: '其它区' + }, + 620800: { + 620802: '崆峒区', + 620821: '泾川县', + 620822: '灵台县', + 620823: '崇信县', + 620824: '华亭县', + 620825: '庄浪县', + 620826: '静宁县', + 940100: '其它区' + }, + 620900: { + 620902: '肃州区', + 620921: '金塔县', + 620922: '瓜州县', + 620923: '肃北蒙古族自治县', + 620924: '阿克塞哈萨克族自治县', + 620981: '玉门市', + 620982: '敦煌市', + 940100: '其它区' + }, + 621000: { + 621002: '西峰区', + 621021: '庆城县', + 621022: '环县', + 621023: '华池县', + 621024: '合水县', + 621025: '正宁县', + 621026: '宁县', + 621027: '镇原县', + 940100: '其它区' + }, + 621100: { + 621102: '安定区', + 621121: '通渭县', + 621122: '陇西县', + 621123: '渭源县', + 621124: '临洮县', + 621125: '漳县', + 621126: '岷县', + 940100: '其它区' + }, + 621200: { + 621202: '武都区', + 621221: '成县', + 621222: '文县', + 621223: '宕昌县', + 621224: '康县', + 621225: '西和县', + 621226: '礼县', + 621227: '徽县', + 621228: '两当县', + 940100: '其它区' + }, + 622900: { + 622901: '临夏市', + 622921: '临夏县', + 622922: '康乐县', + 622923: '永靖县', + 622924: '广河县', + 622925: '和政县', + 622926: '东乡族自治县', + 622927: '积石山保安族东乡族撒拉族自治县', + 940100: '其它区' + }, + 623000: { + 623001: '合作市', + 623021: '临潭县', + 623022: '卓尼县', + 623023: '舟曲县', + 623024: '迭部县', + 623025: '玛曲县', + 623026: '碌曲县', + 623027: '夏河县', + 940100: '其它区' + }, + 630000: { + 630100: '西宁市', + 630200: '海东市', + 632200: '海北藏族自治州', + 632300: '黄南藏族自治州', + 632500: '海南藏族自治州', + 632600: '果洛藏族自治州', + 632700: '玉树藏族自治州', + 632800: '海西蒙古族藏族自治州', + }, + 630100: { + 630102: '城东区', + 630103: '城中区', + 630104: '城西区', + 630105: '城北区', + 630121: '大通回族土族自治县', + 630122: '湟中县', + 630123: '湟源县', + 940100: '其它区' + }, + 630200: { + 630202: '乐都区', + 630203: '平安区', + 630222: '民和回族土族自治县', + 630223: '互助土族自治县', + 630224: '化隆回族自治县', + 630225: '循化撒拉族自治县', + 940100: '其它区' + }, + 632200: { + 632221: '门源回族自治县', + 632222: '祁连县', + 632223: '海晏县', + 632224: '刚察县', + 940100: '其它区' + }, + 632300: { + 632321: '同仁县', + 632322: '尖扎县', + 632323: '泽库县', + 632324: '河南蒙古族自治县', + 940100: '其它区' + }, + 632500: { + 632521: '共和县', + 632522: '同德县', + 632523: '贵德县', + 632524: '兴海县', + 632525: '贵南县', + 940100: '其它区' + }, + 632600: { + 632621: '玛沁县', + 632622: '班玛县', + 632623: '甘德县', + 632624: '达日县', + 632625: '久治县', + 632626: '玛多县', + 940100: '其它区' + }, + 632700: { + 632701: '玉树市', + 632722: '杂多县', + 632723: '称多县', + 632724: '治多县', + 632725: '囊谦县', + 632726: '曲麻莱县', + 940100: '其它区' + }, + 632800: { + 632801: '格尔木市', + 632802: '德令哈市', + 632821: '乌兰县', + 632822: '都兰县', + 632823: '天峻县', + 632825: '海西蒙古族藏族自治州直辖', + 940100: '其它区' + }, + 640000: { + 640100: '银川市', + 640200: '石嘴山市', + 640300: '吴忠市', + 640400: '固原市', + 640500: '中卫市', + }, + 640100: { + 640104: '兴庆区', + 640105: '西夏区', + 640106: '金凤区', + 640121: '永宁县', + 640122: '贺兰县', + 640181: '灵武市', + 940100: '其它区' + }, + 640200: { + 640202: '大武口区', + 640205: '惠农区', + 640221: '平罗县', + 940100: '其它区' + }, + 640300: { + 640302: '利通区', + 640303: '红寺堡区', + 640323: '盐池县', + 640324: '同心县', + 640381: '青铜峡市', + 940100: '其它区' + }, + 640400: { + 640402: '原州区', + 640422: '西吉县', + 640423: '隆德县', + 640424: '泾源县', + 640425: '彭阳县', + 940100: '其它区' + }, + 640500: { + 640502: '沙坡头区', + 640521: '中宁县', + 640522: '海原县', + 940100: '其它区' + }, + 650000: { + 650100: '乌鲁木齐市', + 650200: '克拉玛依市', + 650400: '吐鲁番市', + 652200: '哈密地区', + 652300: '昌吉回族自治州', + 652700: '博尔塔拉蒙古自治州', + 652800: '巴音郭楞蒙古自治州', + 652900: '阿克苏地区', + 653000: '克孜勒苏柯尔克孜自治州', + 653100: '喀什地区', + 653200: '和田地区', + 654000: '伊犁哈萨克自治州', + 654200: '塔城地区', + 654300: '阿勒泰地区', + 659001: '石河子市', + 659002: '阿拉尔市', + 659003: '图木舒克市', + 659004: '五家渠市', + 659005: '北屯市', + 659006: '铁门关市', + 659007: '双河市', + 659008: '可克达拉市', + }, + 650100: { + 650102: '天山区', + 650103: '沙依巴克区', + 650104: '新市区', + 650105: '水磨沟区', + 650106: '头屯河区', + 650107: '达坂城区', + 650109: '米东区', + 650121: '乌鲁木齐县', + 940100: '其它区' + }, + 650200: { + 650202: '独山子区', + 650203: '克拉玛依区', + 650204: '白碱滩区', + 650205: '乌尔禾区', + 940100: '其它区' + }, + 650400: { + 650402: '高昌区', + 650421: '鄯善县', + 650422: '托克逊县', + 940100: '其它区' + }, + 652200: { + 652201: '哈密市', + 652222: '巴里坤哈萨克自治县', + 652223: '伊吾县', + 940100: '其它区' + }, + 652300: { + 652301: '昌吉市', + 652302: '阜康市', + 652323: '呼图壁县', + 652324: '玛纳斯县', + 652325: '奇台县', + 652327: '吉木萨尔县', + 652328: '木垒哈萨克自治县', + 940100: '其它区' + }, + 652700: { + 652701: '博乐市', + 652702: '阿拉山口市', + 652722: '精河县', + 652723: '温泉县', + 940100: '其它区' + }, + 652800: { + 652801: '库尔勒市', + 652822: '轮台县', + 652823: '尉犁县', + 652824: '若羌县', + 652825: '且末县', + 652826: '焉耆回族自治县', + 652827: '和静县', + 652828: '和硕县', + 652829: '博湖县', + 940100: '其它区' + }, + 652900: { + 652901: '阿克苏市', + 652922: '温宿县', + 652923: '库车县', + 652924: '沙雅县', + 652925: '新和县', + 652926: '拜城县', + 652927: '乌什县', + 652928: '阿瓦提县', + 652929: '柯坪县', + 940100: '其它区' + }, + 653000: { + 653001: '阿图什市', + 653022: '阿克陶县', + 653023: '阿合奇县', + 653024: '乌恰县', + 940100: '其它区' + }, + 653100: { + 653101: '喀什市', + 653121: '疏附县', + 653122: '疏勒县', + 653123: '英吉沙县', + 653124: '泽普县', + 653125: '莎车县', + 653126: '叶城县', + 653127: '麦盖提县', + 653128: '岳普湖县', + 653129: '伽师县', + 653130: '巴楚县', + 653131: '塔什库尔干塔吉克自治县', + 940100: '其它区' + }, + 653200: { + 653201: '和田市', + 653221: '和田县', + 653222: '墨玉县', + 653223: '皮山县', + 653224: '洛浦县', + 653225: '策勒县', + 653226: '于田县', + 653227: '民丰县', + 940100: '其它区' + }, + 654000: { + 654002: '伊宁市', + 654003: '奎屯市', + 654004: '霍尔果斯市', + 654021: '伊宁县', + 654022: '察布查尔锡伯自治县', + 654023: '霍城县', + 654024: '巩留县', + 654025: '新源县', + 654026: '昭苏县', + 654027: '特克斯县', + 654028: '尼勒克县', + 940100: '其它区' + }, + 654200: { + 654201: '塔城市', + 654202: '乌苏市', + 654221: '额敏县', + 654223: '沙湾县', + 654224: '托里县', + 654225: '裕民县', + 654226: '和布克赛尔蒙古自治县', + 940100: '其它区' + }, + 654300: { + 654301: '阿勒泰市', + 654321: '布尔津县', + 654322: '富蕴县', + 654323: '福海县', + 654324: '哈巴河县', + 654325: '青河县', + 654326: '吉木乃县', + 940100: '其它区' + }, + 710000: { + 710100: '台北市', + 710200: '高雄市', + 710300: '基隆市', + 710400: '台中市', + 710500: '台南市', + 710600: '新竹市', + 710700: '嘉义市', + 710800: '县', + }, + 710100: { + 710101: '中正区', + 710102: '大同区', + 710103: '中山区', + 710104: '松山区', + 710105: '大安区', + 710106: '万华区', + 710107: '信义区', + 710108: '士林区', + 710109: '北投区', + 710110: '内湖区', + 710111: '南港区', + 710112: '文山区', + 940100: '其它区' + }, + 710200: { + 710202: '新兴区', + 710203: '前金区', + 710204: '芩雅区', + 710205: '盐埕区', + 710206: '鼓山区', + 710207: '旗津区', + 710208: '前镇区', + 710209: '三民区', + 710210: '左营区', + 710211: '楠梓区', + 710212: '小港区', + 940100: '其它区' + }, + 710300: { + 710301: '仁爱区', + 710302: '信义区', + 710303: '中正区', + 710304: '中山区', + 710305: '安乐区', + 710306: '暖暖区', + 710307: '七堵区', + 940100: '其它区' + }, + 710400: { + 710401: '中区', + 710402: '东区', + 710403: '南区', + 710404: '西区', + 710405: '北区', + 710406: '北屯区', + 710407: '西屯区', + 710408: '南屯区', + 940100: '其它区' + }, + 710500: { + 710501: '中西区', + 710502: '东区', + 710503: '南区', + 710504: '北区', + 710505: '安平区', + 710506: '安南区', + 940100: '其它区' + }, + 710600: { + 710601: '东区', + 710602: '北区', + 710603: '香山区', + 940100: '其它区' + }, + 710700: { + 710701: '东区', + 710702: '西区', + 940100: '其它区' + }, + 710800: { + 710801: '台北县(板桥市)', + 710802: '宜兰县(宜兰市)', + 710803: '新竹县(竹北市)', + 710804: '桃园县(桃园市)', + 710805: '苗栗县(苗栗市)', + 710806: '台中县(丰原市)', + 710807: '彰化县(彰化市)', + 710808: '南投县(南投市)', + 710809: '嘉义县(太保市)', + 710810: '云林县(斗六市)', + 710811: '台南县(新营市)', + 710812: '高雄县(凤山市)', + 710813: '屏东县(屏东市)', + 710814: '台东县(台东市)', + 710815: '花莲县(花莲市)', + 710816: '澎湖县(马公市)', + 940100: '其它区' + }, + 810000: { + 810100: '香港', + }, + 810100: { + 810101: '中西区', + 810102: '湾仔区', + 810103: '东区', + 810104: '南区', + 810105: '油尖旺区', + 810106: '深水埗区', + 810107: '九龙城区', + 810108: '黄大仙区', + 810109: '观塘区', + 810110: '荃湾区', + 810111: '屯门区', + 810112: '元朗区', + 810113: '北区', + 810114: '大埔区', + 810115: '西贡区', + 810116: '沙田区', + 810117: '葵青区', + 810118: '离岛区', + 940100: '其它区' + }, + 820000: { + 820100: '澳门', + }, + 820100: { + 820101: '花地玛堂区', + 820102: '花王堂区', + 820103: '望德堂区', + 820104: '大堂区', + 820105: '风顺堂区', + 820106: '嘉模堂区', + 820107: '路凼填海区', + 820108: '圣方济各堂区', + 940100: '其它区' + } + }; + + if (typeof window !== 'undefined') { + window.ChineseDistricts = ChineseDistricts; + } + + return ChineseDistricts; + +}); diff --git a/static/plugs/distpicker/distpicker.js b/static/plugs/distpicker/distpicker.js new file mode 100644 index 000000000..2e369c1d7 --- /dev/null +++ b/static/plugs/distpicker/distpicker.js @@ -0,0 +1,248 @@ +/*! + * Distpicker v@VERSION + * https://github.com/fengyuanchen/distpicker + * + * Copyright (c) 2014-@YEAR Fengyuan Chen + * Released under the MIT license + * + * Date: @DATE + */ + +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as anonymous module. + define(['jquery', 'ChineseDistricts'], factory); + } else if (typeof exports === 'object') { + // Node / CommonJS + factory(require('jquery'), require('ChineseDistricts')); + } else { + // Browser globals. + factory(jQuery, ChineseDistricts); + } +})(function ($, ChineseDistricts) { + + 'use strict'; + + if (typeof ChineseDistricts === 'undefined') { + throw new Error('The file "distpicker.data.js" must be included first!'); + } + + var NAMESPACE = 'distpicker'; + var EVENT_CHANGE = 'change.' + NAMESPACE; + var PROVINCE = 'province'; + var CIRY = 'city'; + var DISTRICT = 'district'; + + function Distpicker(element, options) { + this.$element = $(element); + this.options = $.extend({}, Distpicker.DEFAULTS, $.isPlainObject(options) && options); + this.placeholders = $.extend({}, Distpicker.DEFAULTS); + this.active = false; + this.init(); + } + + Distpicker.prototype = { + constructor: Distpicker, + + init: function () { + var options = this.options; + var $select = this.$element.find('select'); + var length = $select.length; + var data = {}; + + $select.each(function () { + $.extend(data, $(this).data()); + }); + + $.each([PROVINCE, CIRY, DISTRICT], $.proxy(function (i, type) { + if (data[type]) { + options[type] = data[type]; + this['$' + type] = $select.filter('[data-' + type + ']'); + } else { + this['$' + type] = length > i ? $select.eq(i) : null; + } + }, this)); + + this.bind(); + + // Reset all the selects (after event binding) + this.reset(); + + this.active = true; + }, + + bind: function () { + if (this.$province) { + this.$province.on(EVENT_CHANGE, (this._changeProvince = $.proxy(function () { + this.output(CIRY); + this.output(DISTRICT); + }, this))); + } + + if (this.$city) { + this.$city.on(EVENT_CHANGE, (this._changeCity = $.proxy(function () { + this.output(DISTRICT); + }, this))); + } + }, + + unbind: function () { + if (this.$province) { + this.$province.off(EVENT_CHANGE, this._changeProvince); + } + + if (this.$city) { + this.$city.off(EVENT_CHANGE, this._changeCity); + } + }, + + output: function (type) { + var options = this.options; + var placeholders = this.placeholders; + var $select = this['$' + type]; + var districts = {}; + var data = []; + var code; + var matched; + var value; + + if (!$select || !$select.length) { + return; + } + + value = options[type]; + + code = ( + type === PROVINCE ? 86 : + type === CIRY ? this.$province && this.$province.find(':selected').data('code') : + type === DISTRICT ? this.$city && this.$city.find(':selected').data('code') : code + ); + + districts = $.isNumeric(code) ? ChineseDistricts[code] : null; + + if ($.isPlainObject(districts)) { + $.each(districts, function (code, address) { + var selected = address === value; + + if (selected) { + matched = true; + } + + data.push({ + code: code, + address: address, + selected: selected + }); + }); + } + + if (!matched) { + if (data.length && (options.autoSelect || options.autoselect)) { + data[0].selected = true; + } + + // Save the unmatched value as a placeholder at the first output + if (!this.active && value) { + placeholders[type] = value; + } + } + + // Add placeholder option + if (options.placeholder) { + data.unshift({ + code: '', + address: placeholders[type], + selected: false + }); + } + + $select.html(this.getList(data)); + }, + + getList: function (data) { + var list = []; + + $.each(data, function (i, n) { + list.push( + '' + + (n.address || '') + + '' + ); + }); + + return list.join(''); + }, + + reset: function (deep) { + if (!deep) { + this.output(PROVINCE); + this.output(CIRY); + this.output(DISTRICT); + } else if (this.$province) { + this.$province.find(':first').prop('selected', true).trigger(EVENT_CHANGE); + } + }, + + destroy: function () { + this.unbind(); + this.$element.removeData(NAMESPACE); + } + }; + + Distpicker.DEFAULTS = { + autoSelect: true, + placeholder: false, + province: '请选择省份', + city: '请选择城市', + district: '请选择地区' + }; + + Distpicker.setDefaults = function (options) { + $.extend(Distpicker.DEFAULTS, options); + }; + + // Save the other distpicker + Distpicker.other = $.fn.distpicker; + + // Register as jQuery plugin + $.fn.distpicker = function (option) { + var args = [].slice.call(arguments, 1); + + return this.each(function () { + var $this = $(this); + var data = $this.data(NAMESPACE); + var options; + var fn; + + if (!data) { + if (/destroy/.test(option)) { + return; + } + + options = $.extend({}, $this.data(), $.isPlainObject(option) && option); + $this.data(NAMESPACE, (data = new Distpicker(this, options))); + } + + if (typeof option === 'string' && $.isFunction(fn = data[option])) { + fn.apply(data, args); + } + }); + }; + + $.fn.distpicker.Constructor = Distpicker; + $.fn.distpicker.setDefaults = Distpicker.setDefaults; + + // No conflict + $.fn.distpicker.noConflict = function () { + $.fn.distpicker = Distpicker.other; + return this; + }; + + $(function () { + $('[data-toggle="distpicker"]').distpicker(); + }); +}); diff --git a/static/plugs/jquery/jquery.PrintArea.js b/static/plugs/jquery/jquery.PrintArea.js index 23daa7a21..23ad5d9d7 100644 --- a/static/plugs/jquery/jquery.PrintArea.js +++ b/static/plugs/jquery/jquery.PrintArea.js @@ -26,31 +26,32 @@ * @standard | [string] | strict, loose, (html5) | Only for popup. For html 4.01, strict or loose document standard, or html 5 standard * @extraHead | [string] | ('') | comma separated list of extra elements to be appended to the head tag */ -(function($) { +(function ($) { var counter = 0; - var modes = { iframe : "iframe", popup : "popup" }; - var standards = { strict : "strict", loose : "loose", html5 : "html5" }; - var defaults = { mode : modes.iframe, - standard : standards.html5, - popHt : 500, - popWd : 400, - popX : 200, - popY : 200, - popTitle : '', - popClose : false, - extraCss : '', - extraHead : '', - retainAttr : ["id","class","style"] }; + var modes = {iframe: "iframe", popup: "popup"}; + var standards = {strict: "strict", loose: "loose", html5: "html5"}; + var defaults = { + mode: modes.iframe, + standard: standards.html5, + popHt: 500, + popWd: 400, + popX: 200, + popY: 200, + popTitle: '', + popClose: false, + extraCss: '', + extraHead: '', + retainAttr: ["id", "class", "style"] + }; var settings = {};//global settings - $.fn.printArea = function( options ) - { - $.extend( settings, defaults, options ); + $.fn.printArea = function (options) { + $.extend(settings, defaults, options); counter++; var idPrefix = "printArea_"; - $( "[id^=" + idPrefix + "]" ).remove(); + $("[id^=" + idPrefix + "]").remove(); settings.id = idPrefix + counter; @@ -58,71 +59,78 @@ var PrintAreaWindow = PrintArea.getPrintWindow(); - PrintArea.write( PrintAreaWindow.doc, $printSource ); + PrintArea.write(PrintAreaWindow.doc, $printSource); - setTimeout( function () { PrintArea.print( PrintAreaWindow ); }, 1000 ); + setTimeout(function () { + PrintArea.print(PrintAreaWindow); + }, 1000); }; var PrintArea = { - print : function( PAWindow ) { + print: function (PAWindow) { var paWindow = PAWindow.win; - $(PAWindow.doc).ready(function(){ + $(PAWindow.doc).ready(function () { paWindow.focus(); paWindow.print(); - if ( settings.mode == modes.popup && settings.popClose ) - setTimeout(function() { paWindow.close(); }, 2000); + if (settings.mode == modes.popup && settings.popClose) + setTimeout(function () { + paWindow.close(); + }, 2000); }); }, - write : function ( PADocument, $ele ) { + write: function (PADocument, $ele) { PADocument.open(); - PADocument.write( PrintArea.docType() + "" + PrintArea.getHead() + PrintArea.getBody( $ele ) + "" ); + PADocument.write(PrintArea.docType() + "" + PrintArea.getHead() + PrintArea.getBody($ele) + ""); PADocument.close(); }, - docType : function() { - if ( settings.mode == modes.iframe ) return ""; + docType: function () { + if (settings.mode == modes.iframe) return ""; - if ( settings.standard == standards.html5 ) return ""; + if (settings.standard == standards.html5) return ""; var transitional = settings.standard == standards.loose ? " Transitional" : ""; var dtd = settings.standard == standards.loose ? "loose" : "strict"; - return ''; + return ''; }, - getHead : function() { + getHead: function () { var extraHead = ""; var links = ""; - if ( settings.extraHead ) settings.extraHead.replace( /([^,]+)/g, function(m){ extraHead += m }); + if (settings.extraHead) settings.extraHead.replace(/([^,]+)/g, function (m) { + extraHead += m + }); $(document).find("link") - .filter(function(){ // Requirement: element MUST have rel="stylesheet" to be considered in print document - var relAttr = $(this).attr("rel"); - return ($.type(relAttr) === 'undefined') == false && relAttr.toLowerCase() == 'stylesheet'; - }) - .filter(function(){ // Include if media is undefined, empty, print or all - var mediaAttr = $(this).attr("media"); - return $.type(mediaAttr) === 'undefined' || mediaAttr == "" || mediaAttr.toLowerCase() == 'print' || mediaAttr.toLowerCase() == 'all' - }) - .each(function(){ - links += ''; - }); - if ( settings.extraCss ) settings.extraCss.replace( /([^,\s]+)/g, function(m){ links += '' }); + .filter(function () { // Requirement: element MUST have rel="stylesheet" to be considered in print document + var relAttr = $(this).attr("rel"); + return ($.type(relAttr) === 'undefined') == false && relAttr.toLowerCase() == 'stylesheet'; + }) + .filter(function () { // Include if media is undefined, empty, print or all + var mediaAttr = $(this).attr("media"); + return $.type(mediaAttr) === 'undefined' || mediaAttr == "" || mediaAttr.toLowerCase() == 'print' || mediaAttr.toLowerCase() == 'all' + }) + .each(function () { + links += ''; + }); + if (settings.extraCss) settings.extraCss.replace(/([^,\s]+)/g, function (m) { + links += '' + }); return "" + settings.popTitle + "" + extraHead + links + ""; }, - getBody : function ( elements ) { + getBody: function (elements) { var htm = ""; var attrs = settings.retainAttr; - elements.each(function() { - var ele = PrintArea.getFormData( $(this) ); + elements.each(function () { + var ele = PrintArea.getFormData($(this)); var attributes = "" - for ( var x = 0; x < attrs.length; x++ ) - { - var eleAttr = $(ele).attr( attrs[x] ); - if ( eleAttr ) attributes += (attributes.length > 0 ? " ":"") + attrs[x] + "='" + eleAttr + "'"; + for (var x = 0; x < attrs.length; x++) { + var eleAttr = $(ele).attr(attrs[x]); + if (eleAttr) attributes += (attributes.length > 0 ? " " : "") + attrs[x] + "='" + eleAttr + "'"; } htm += '
    ' + $(ele).html() + '
    '; @@ -130,60 +138,60 @@ return "" + htm + ""; }, - getFormData : function ( ele ) { + getFormData: function (ele) { var copy = ele.clone(); var copiedInputs = $("input,select,textarea", copy); - $("input,select,textarea", ele).each(function( i ){ + $("input,select,textarea", ele).each(function (i) { var typeInput = $(this).attr("type"); if ($.type(typeInput) === 'undefined') typeInput = $(this).is("select") ? "select" : $(this).is("textarea") ? "textarea" : ""; - var copiedInput = copiedInputs.eq( i ); + var copiedInput = copiedInputs.eq(i); - if ( typeInput == "radio" || typeInput == "checkbox" ) copiedInput.attr( "checked", $(this).is(":checked") ); - else if ( typeInput == "text" ) copiedInput.attr( "value", $(this).val() ); - else if ( typeInput == "select" ) - $(this).find( "option" ).each( function( i ) { - if ( $(this).is(":selected") ) $("option", copiedInput).eq( i ).attr( "selected", true ); + if (typeInput == "radio" || typeInput == "checkbox") copiedInput.attr("checked", $(this).is(":checked")); + else if (typeInput == "text") copiedInput.attr("value", $(this).val()); + else if (typeInput == "select") + $(this).find("option").each(function (i) { + if ($(this).is(":selected")) $("option", copiedInput).eq(i).attr("selected", true); }); - else if ( typeInput == "textarea" ) copiedInput.text( $(this).val() ); + else if (typeInput == "textarea") copiedInput.text($(this).val()); }); return copy; }, - getPrintWindow : function () { - switch ( settings.mode ) - { + getPrintWindow: function () { + switch (settings.mode) { case modes.iframe : var f = new PrintArea.Iframe(); - return { win : f.contentWindow || f, doc : f.doc }; + return {win: f.contentWindow || f, doc: f.doc}; case modes.popup : var p = new PrintArea.Popup(); - return { win : p, doc : p.doc }; + return {win: p, doc: p.doc}; } }, - Iframe : function () { + Iframe: function () { var frameId = settings.id; var iframeStyle = 'border:0;position:absolute;width:0px;height:0px;right:0px;top:0px;'; var iframe; - try - { + try { iframe = document.createElement('iframe'); document.body.appendChild(iframe); - $(iframe).attr({ style: iframeStyle, id: frameId, src: "#" + new Date().getTime() }); + $(iframe).attr({style: iframeStyle, id: frameId, src: "#" + new Date().getTime()}); iframe.doc = null; iframe.doc = iframe.contentDocument ? iframe.contentDocument : ( iframe.contentWindow ? iframe.contentWindow.document : iframe.document); } - catch( e ) { throw e + ". iframes may not be supported in this browser."; } + catch (e) { + throw e + ". iframes may not be supported in this browser."; + } - if ( iframe.doc == null ) throw "Cannot find document."; + if (iframe.doc == null) throw "Cannot find document."; return iframe; }, - Popup : function () { + Popup: function () { var windowAttr = "location=yes,statusbar=no,directories=no,menubar=no,titlebar=no,toolbar=no,dependent=no"; windowAttr += ",width=" + settings.popWd + ",height=" + settings.popHt; windowAttr += ",resizable=yes,screenX=" + settings.popX + ",screenY=" + settings.popY + ",personalbar=no,scrollbars=yes"; - var newWin = window.open( "", "_blank", windowAttr ); + var newWin = window.open("", "_blank", windowAttr); newWin.doc = newWin.document; diff --git a/static/plugs/jquery/jquery.cascade.js b/static/plugs/jquery/jquery.cascade.js index 3e8c9ddbf..1bcfb2dcf 100644 --- a/static/plugs/jquery/jquery.cascade.js +++ b/static/plugs/jquery/jquery.cascade.js @@ -18,7 +18,9 @@ this.endDecorate(); this.box(); }, item: function () { - var $box = this.$element, _coord = [], _num = 0, _options = this.options, i = 0, $items = $box.find(this.options.fallsCss), fallsWidth = $items.eq(0).outerWidth() + this.options.margin, boxWidth = $box.outerWidth() + this.options.margin, _autoWidth = 0; + var $box = this.$element, _coord = [], _num = 0, _options = this.options, i = 0, + $items = $box.find(this.options.fallsCss), fallsWidth = $items.eq(0).outerWidth() + this.options.margin, + boxWidth = $box.outerWidth() + this.options.margin, _autoWidth = 0; _num = Math.floor(boxWidth / fallsWidth); _autoWidth = (boxWidth - _num * fallsWidth) / 2; for (; i < _num; i++) { @@ -48,7 +50,8 @@ }, box: function () { this.$element.height(this.getFallsMaxHeight()); }, endDecorate: function () { - var _coord = this.coord, i = 0, _num = this.num, fallsMaxHeight = this.getFallsMaxHeight(), falls = document.createElement('div'), fallsClone, fallsHeight = 0; + var _coord = this.coord, i = 0, _num = this.num, fallsMaxHeight = this.getFallsMaxHeight(), + falls = document.createElement('div'), fallsClone, fallsHeight = 0; falls.className = 'additem'; for (; i < _num; i++) { if (fallsMaxHeight != _coord[i][1]) { diff --git a/static/plugs/jquery/jquery.citys.js b/static/plugs/jquery/jquery.citys.js new file mode 100644 index 000000000..fd59eeba8 --- /dev/null +++ b/static/plugs/jquery/jquery.citys.js @@ -0,0 +1,249 @@ +/** + * jquery.citys.js 1.0 + * http://jquerywidget.com + * Github:https://github.com/mumuy/widget + * -------------- DEMO --------------- + * $('#demo1').citys({valueType:'name',province:'福建',city:'厦门',area:'思明'}); + */ +;(function (factory) { + if (typeof define === "function" && (define.amd || define.cmd) && !jQuery) { + // AMD或CMD + define(["jquery"], factory); + } else if (typeof module === 'object' && module.exports) { + // Node/CommonJS + module.exports = function (root, jQuery) { + if (jQuery === undefined) { + if (typeof window !== 'undefined') { + jQuery = require('jquery'); + } else { + jQuery = require('jquery')(root); + } + } + factory(jQuery); + return jQuery; + }; + } else { + //Browser globals + factory(jQuery); + } +}(function ($) { + $.support.cors = true; + $.fn.citys = function (parameter, getApi) { + if (typeof parameter === 'function') { //重载 + getApi = parameter; + parameter = {}; + } else { + parameter = parameter || {}; + getApi = getApi || function () { + }; + } + var dataUrl = 'http://passer-by.com/data_location/list.json'; + if (window.ROOT_URL) { + dataUrl = window.ROOT_URL + '/index.php/admin/plugs/region.html'; + } + var defaults = { + dataUrl: dataUrl, //数据库地址 + crossDomain: true, //是否开启跨域 + dataType: 'json', //数据库类型:'json'或'jsonp' + provinceField: 'province', //省份字段名 + cityField: 'city', //城市字段名 + areaField: 'area', //地区字段名 + valueType: 'code', //下拉框值的类型,code行政区划代码,name地名 + code: 0, //地区编码 + province: 0, //省份,可以为地区编码或者名称 + city: 0, //城市,可以为地区编码或者名称 + area: 0, //地区,可以为地区编码或者名称 + required: true, //是否必须选一个 + nodata: 'hidden', //当无数据时的表现形式:'hidden'隐藏,'disabled'禁用,为空不做任何处理 + onChange: function () { //地区切换时触发,回调函数传入地区数据 + } + }; + var options = $.extend({}, defaults, parameter); + return this.each(function () { + //对象定义 + var _api = {}; + var $this = $(this); + var $province = $this.find('select[name="' + options.provinceField + '"]'), + $city = $this.find('select[name="' + options.cityField + '"]'), + $area = $this.find('select[name="' + options.areaField + '"]'); + $.ajax({ + url: options.dataUrl, + type: 'GET', + crossDomain: options.crossDomain, + dataType: options.dataType, + jsonpCallback: 'jsonp_location', + success: function (data) { + var province, city, area, hasCity; + if (options.code) { //如果设置地区编码,则忽略单独设置的信息 + var c = options.code - options.code % 1e4; + if (data[c]) { + options.province = c; + } + c = options.code - (options.code % 1e4 ? options.code % 1e2 : options.code); + if (data[c]) { + options.city = c; + } + c = options.code % 1e2 ? options.code : 0; + if (data[c]) { + options.area = c; + } + } + var updateData = function () { + province = {}, city = {}, area = {}; + hasCity = false; //判断是非有地级城市 + for (var code in data) { + if (!(code % 1e4)) { //获取所有的省级行政单位 + province[code] = data[code]; + if (options.required && !options.province) { + if (options.city && !(options.city % 1e4)) { //省未填,并判断为直辖市 + options.province = options.city; + } else { + options.province = code; + } + } else if (isNaN(options.province) && data[code].indexOf(options.province) > -1) { + options.province = code; + } + } else { + var p = code - options.province; + if (options.province && p > 0 && p < 1e4) { //同省的城市或地区 + if (!(code % 100)) { + hasCity = true; + city[code] = data[code]; + if (options.required && !options.city) { + options.city = code; + } else if (isNaN(options.city) && data[code].indexOf(options.city) > -1) { + options.city = code; + } + } else if (p > 8000) { //省直辖县级行政单位 + city[code] = data[code]; + if (options.required && !options.city) { + options.city = code; + } else if (isNaN(options.city) && data[code].indexOf(options.city) > -1) { + options.city = code; + } + } else if (hasCity) { //非直辖市 + var c = code - options.city; + if (options.city && c > 0 && c < 100) { //同个城市的地区 + area[code] = data[code]; + if (options.required && !options.area) { + options.area = code; + } else if (isNaN(options.area) && data[code].indexOf(options.area) > -1) { + options.area = code; + } + } + } else { + area[code] = data[code]; //直辖市 + if (options.required && !options.area) { + options.area = code; + } else if (isNaN(options.area) && data[code].indexOf(options.area) > -1) { + options.area = code; + } + } + } + } + } + }; + var format = { + province: function () { + $province.empty(); + if (!options.required) { + $province.append(''); + } + for (var i in province) { + $province.append(''); + } + if (options.province) { + var value = options.valueType === 'code' ? options.province : province[options.province]; + $province.val(value); + } + this.city(); + }, + city: function () { + $city.empty(); + if (!hasCity) { + $city.css('display', 'none'); + } else { + $city.css('display', ''); + if (!options.required) { + $city.append(''); + } + if (options.nodata === 'disabled') { + $city.prop('disabled', $.isEmptyObject(city)); + } else if (options.nodata === 'hidden') { + $city.css('display', $.isEmptyObject(city) ? 'none' : ''); + } + for (var i in city) { + $city.append(''); + } + if (options.city) { + var value = options.valueType === 'code' ? options.city : city[options.city]; + $city.val(value); + } else if (options.area) { + var value = options.valueType === 'code' ? options.area : city[options.area]; + $city.val(value); + } + } + this.area(); + }, + area: function () { + $area.empty(); + if (!options.required) { + $area.append(''); + } + if (options.nodata === 'disabled') { + $area.prop('disabled', $.isEmptyObject(area)); + } else if (options.nodata === 'hidden') { + $area.css('display', $.isEmptyObject(area) ? 'none' : ''); + } + for (var i in area) { + $area.append(''); + } + if (options.area) { + var value = options.valueType === 'code' ? options.area : area[options.area]; + $area.val(value); + } + } + }; + //获取当前地理信息 + _api.getInfo = function () { + var status = { + direct: !hasCity, + province: data[options.province] || '', + city: data[options.city] || '', + area: data[options.area] || '', + code: options.area || options.city || options.province + }; + return status; + }; + //事件绑定 + $province.on('change', function () { + options.province = $(this).find('option:selected').data('code') || 0; //选中节点的区划代码 + options.city = 0; + options.area = 0; + updateData(); + format.city(); + options.onChange(_api.getInfo()); + }); + $city.on('change', function () { + options.city = $(this).find('option:selected').data('code') || 0; //选中节点的区划代码 + options.area = 0; + updateData(); + format.area(); + options.onChange(_api.getInfo()); + }); + $area.on('change', function () { + options.area = $(this).find('option:selected').data('code') || 0; //选中节点的区划代码 + options.onChange(_api.getInfo()); + }); + //初始化 + updateData(); + format.province(); + if (options.code) { + options.onChange(_api.getInfo()); + } + getApi(_api); + } + }); + }); + }; +})); diff --git a/static/plugs/jquery/jquery.cookie.js b/static/plugs/jquery/jquery.cookie.js index c7f3a59b5..cd7144a56 100644 --- a/static/plugs/jquery/jquery.cookie.js +++ b/static/plugs/jquery/jquery.cookie.js @@ -6,112 +6,113 @@ * Released under the MIT license */ (function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD - define(['jquery'], factory); - } else if (typeof exports === 'object') { - // CommonJS - factory(require('jquery')); - } else { - // Browser globals - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + // AMD + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // CommonJS + factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } }(function ($) { - var pluses = /\+/g; + var pluses = /\+/g; - function encode(s) { - return config.raw ? s : encodeURIComponent(s); - } + function encode(s) { + return config.raw ? s : encodeURIComponent(s); + } - function decode(s) { - return config.raw ? s : decodeURIComponent(s); - } + function decode(s) { + return config.raw ? s : decodeURIComponent(s); + } - function stringifyCookieValue(value) { - return encode(config.json ? JSON.stringify(value) : String(value)); - } + function stringifyCookieValue(value) { + return encode(config.json ? JSON.stringify(value) : String(value)); + } - function parseCookieValue(s) { - if (s.indexOf('"') === 0) { - // This is a quoted cookie as according to RFC2068, unescape... - s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); - } + function parseCookieValue(s) { + if (s.indexOf('"') === 0) { + // This is a quoted cookie as according to RFC2068, unescape... + s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); + } - try { - // Replace server-side written pluses with spaces. - // If we can't decode the cookie, ignore it, it's unusable. - // If we can't parse the cookie, ignore it, it's unusable. - s = decodeURIComponent(s.replace(pluses, ' ')); - return config.json ? JSON.parse(s) : s; - } catch(e) {} - } + try { + // Replace server-side written pluses with spaces. + // If we can't decode the cookie, ignore it, it's unusable. + // If we can't parse the cookie, ignore it, it's unusable. + s = decodeURIComponent(s.replace(pluses, ' ')); + return config.json ? JSON.parse(s) : s; + } catch (e) { + } + } - function read(s, converter) { - var value = config.raw ? s : parseCookieValue(s); - return $.isFunction(converter) ? converter(value) : value; - } + function read(s, converter) { + var value = config.raw ? s : parseCookieValue(s); + return $.isFunction(converter) ? converter(value) : value; + } - var config = $.cookie = function (key, value, options) { + var config = $.cookie = function (key, value, options) { - // Write + // Write - if (value !== undefined && !$.isFunction(value)) { - options = $.extend({}, config.defaults, options); + if (value !== undefined && !$.isFunction(value)) { + options = $.extend({}, config.defaults, options); - if (typeof options.expires === 'number') { - var days = options.expires, t = options.expires = new Date(); - t.setTime(+t + days * 864e+5); - } + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setTime(+t + days * 864e+5); + } - return (document.cookie = [ - encode(key), '=', stringifyCookieValue(value), - options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE - options.path ? '; path=' + options.path : '', - options.domain ? '; domain=' + options.domain : '', - options.secure ? '; secure' : '' - ].join('')); - } + return (document.cookie = [ + encode(key), '=', stringifyCookieValue(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } - // Read + // Read - var result = key ? undefined : {}; + var result = key ? undefined : {}; - // To prevent the for loop in the first place assign an empty array - // in case there are no cookies at all. Also prevents odd result when - // calling $.cookie(). - var cookies = document.cookie ? document.cookie.split('; ') : []; + // To prevent the for loop in the first place assign an empty array + // in case there are no cookies at all. Also prevents odd result when + // calling $.cookie(). + var cookies = document.cookie ? document.cookie.split('; ') : []; - for (var i = 0, l = cookies.length; i < l; i++) { - var parts = cookies[i].split('='); - var name = decode(parts.shift()); - var cookie = parts.join('='); + for (var i = 0, l = cookies.length; i < l; i++) { + var parts = cookies[i].split('='); + var name = decode(parts.shift()); + var cookie = parts.join('='); - if (key && key === name) { - // If second argument (value) is a function it's a converter... - result = read(cookie, value); - break; - } + if (key && key === name) { + // If second argument (value) is a function it's a converter... + result = read(cookie, value); + break; + } - // Prevent storing a cookie that we couldn't decode. - if (!key && (cookie = read(cookie)) !== undefined) { - result[name] = cookie; - } - } + // Prevent storing a cookie that we couldn't decode. + if (!key && (cookie = read(cookie)) !== undefined) { + result[name] = cookie; + } + } - return result; - }; + return result; + }; - config.defaults = {}; + config.defaults = {}; - $.removeCookie = function (key, options) { - if ($.cookie(key) === undefined) { - return false; - } + $.removeCookie = function (key, options) { + if ($.cookie(key) === undefined) { + return false; + } - // Must not alter options, thus extending a fresh object... - $.cookie(key, '', $.extend({}, options, { expires: -1 })); - return !$.cookie(key); - }; + // Must not alter options, thus extending a fresh object... + $.cookie(key, '', $.extend({}, options, {expires: -1})); + return !$.cookie(key); + }; })); diff --git a/static/plugs/jquery/masonry.min.js b/static/plugs/jquery/masonry.min.js index a61c0ded6..d7f84ef16 100644 --- a/static/plugs/jquery/masonry.min.js +++ b/static/plugs/jquery/masonry.min.js @@ -1,9 +1,9 @@ /*! - * Masonry PACKAGED v4.2.0 + * Masonry PACKAGED v4.2.1 * Cascading grid layout library - * http://masonry.desandro.com + * https://masonry.desandro.com * MIT License * by David DeSandro */ -!function(t,e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict";function i(i,r,a){function h(t,e,n){var o,r="$()."+i+'("'+e+'")';return t.each(function(t,h){var u=a.data(h,i);if(!u)return void s(i+" not initialized. Cannot call methods, i.e. "+r);var d=u[e];if(!d||"_"==e.charAt(0))return void s(r+" is not a valid method");var l=d.apply(u,n);o=void 0===o?l:o}),void 0!==o?o:t}function u(t,e){t.each(function(t,n){var o=a.data(n,i);o?(o.option(e),o._init()):(o=new r(n,e),a.data(n,i,o))})}a=a||e||t.jQuery,a&&(r.prototype.option||(r.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=o.call(arguments,1);return h(this,t,e)}return u(this,t),this},n(a))}function n(t){!t||t&&t.bridget||(t.bridget=i)}var o=Array.prototype.slice,r=t.console,s="undefined"==typeof r?function(){}:function(t){r.error(t)};return n(e||t.jQuery),i}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{};return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=0,o=i[n];e=e||[];for(var r=this._onceEvents&&this._onceEvents[t];o;){var s=r&&r[o];s&&(this.off(t,o),delete r[o]),o.apply(this,e),n+=s?0:1,o=i[n]}return this}},t}),function(t,e){"use strict";"function"==typeof define&&define.amd?define("get-size/get-size",[],function(){return e()}):"object"==typeof module&&module.exports?module.exports=e():t.getSize=e()}(window,function(){"use strict";function t(t){var e=parseFloat(t),i=-1==t.indexOf("%")&&!isNaN(e);return i&&e}function e(){}function i(){for(var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},e=0;u>e;e++){var i=h[e];t[i]=0}return t}function n(t){var e=getComputedStyle(t);return e||a("Style returned "+e+". Are you running this code in a hidden iframe on Firefox? See http://bit.ly/getsizebug1"),e}function o(){if(!d){d=!0;var e=document.createElement("div");e.style.width="200px",e.style.padding="1px 2px 3px 4px",e.style.borderStyle="solid",e.style.borderWidth="1px 2px 3px 4px",e.style.boxSizing="border-box";var i=document.body||document.documentElement;i.appendChild(e);var o=n(e);r.isBoxSizeOuter=s=200==t(o.width),i.removeChild(e)}}function r(e){if(o(),"string"==typeof e&&(e=document.querySelector(e)),e&&"object"==typeof e&&e.nodeType){var r=n(e);if("none"==r.display)return i();var a={};a.width=e.offsetWidth,a.height=e.offsetHeight;for(var d=a.isBorderBox="border-box"==r.boxSizing,l=0;u>l;l++){var c=h[l],f=r[c],m=parseFloat(f);a[c]=isNaN(m)?0:m}var p=a.paddingLeft+a.paddingRight,g=a.paddingTop+a.paddingBottom,y=a.marginLeft+a.marginRight,v=a.marginTop+a.marginBottom,_=a.borderLeftWidth+a.borderRightWidth,z=a.borderTopWidth+a.borderBottomWidth,E=d&&s,b=t(r.width);b!==!1&&(a.width=b+(E?0:p+_));var x=t(r.height);return x!==!1&&(a.height=x+(E?0:g+z)),a.innerWidth=a.width-(p+_),a.innerHeight=a.height-(g+z),a.outerWidth=a.width+y,a.outerHeight=a.height+v,a}}var s,a="undefined"==typeof console?e:function(t){console.error(t)},h=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],u=h.length,d=!1;return r}),function(t,e){"use strict";"function"==typeof define&&define.amd?define("desandro-matches-selector/matches-selector",e):"object"==typeof module&&module.exports?module.exports=e():t.matchesSelector=e()}(window,function(){"use strict";var t=function(){var t=window.Element.prototype;if(t.matches)return"matches";if(t.matchesSelector)return"matchesSelector";for(var e=["webkit","moz","ms","o"],i=0;is?"round":"floor";r=Math[a](r),this.cols=Math.max(r,1)},n.getContainerWidth=function(){var t=this._getOption("fitWidth"),i=t?this.element.parentNode:this.element,n=e(i);this.containerWidth=n&&n.innerWidth},n._getItemLayoutPosition=function(t){t.getSize();var e=t.size.outerWidth%this.columnWidth,i=e&&1>e?"round":"ceil",n=Math[i](t.size.outerWidth/this.columnWidth);n=Math.min(n,this.cols);for(var o=this.options.horizontalOrder?"_getHorizontalColPosition":"_getTopColPosition",r=this[o](n,t),s={x:this.columnWidth*r.col,y:r.y},a=r.y+t.size.outerHeight,h=n+r.col,u=r.col;h>u;u++)this.colYs[u]=a;return s},n._getTopColPosition=function(t){var e=this._getTopColGroup(t),i=Math.min.apply(Math,e);return{col:e.indexOf(i),y:i}},n._getTopColGroup=function(t){if(2>t)return this.colYs;for(var e=[],i=this.cols+1-t,n=0;i>n;n++)e[n]=this._getColGroupY(n,t);return e},n._getColGroupY=function(t,e){if(2>e)return this.colYs[t];var i=this.colYs.slice(t,t+e);return Math.max.apply(Math,i)},n._getHorizontalColPosition=function(t,e){var i=this.horizontalColIndex%this.cols,n=t>1&&i+t>this.cols;i=n?0:i;var o=e.size.outerWidth&&e.size.outerHeight;return this.horizontalColIndex=o?i+t:this.horizontalColIndex,{col:i,y:this._getColGroupY(i,t)}},n._manageStamp=function(t){var i=e(t),n=this._getElementOffset(t),o=this._getOption("originLeft"),r=o?n.left:n.right,s=r+i.outerWidth,a=Math.floor(r/this.columnWidth);a=Math.max(0,a);var h=Math.floor(s/this.columnWidth);h-=s%this.columnWidth?0:1,h=Math.min(this.cols-1,h);for(var u=this._getOption("originTop"),d=(u?n.top:n.bottom)+i.outerHeight,l=a;h>=l;l++)this.colYs[l]=Math.max(d,this.colYs[l])},n._getContainerSize=function(){this.maxY=Math.max.apply(Math,this.colYs);var t={height:this.maxY};return this._getOption("fitWidth")&&(t.width=this._getContainerFitWidth()),t},n._getContainerFitWidth=function(){for(var t=0,e=this.cols;--e&&0===this.colYs[e];)t++;return(this.cols-t)*this.columnWidth-this.gutter},n.needsResizeLayout=function(){var t=this.containerWidth;return this.getContainerWidth(),t!=this.containerWidth},i}); \ No newline at end of file +!function(t,e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict";function i(i,r,a){function h(t,e,n){var o,r="$()."+i+'("'+e+'")';return t.each(function(t,h){var u=a.data(h,i);if(!u)return void s(i+" not initialized. Cannot call methods, i.e. "+r);var d=u[e];if(!d||"_"==e.charAt(0))return void s(r+" is not a valid method");var l=d.apply(u,n);o=void 0===o?l:o}),void 0!==o?o:t}function u(t,e){t.each(function(t,n){var o=a.data(n,i);o?(o.option(e),o._init()):(o=new r(n,e),a.data(n,i,o))})}a=a||e||t.jQuery,a&&(r.prototype.option||(r.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=o.call(arguments,1);return h(this,t,e)}return u(this,t),this},n(a))}function n(t){!t||t&&t.bridget||(t.bridget=i)}var o=Array.prototype.slice,r=t.console,s="undefined"==typeof r?function(){}:function(t){r.error(t)};return n(e||t.jQuery),i}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{};return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){i=i.slice(0),e=e||[];for(var n=this._onceEvents&&this._onceEvents[t],o=0;oe;e++){var i=h[e];t[i]=0}return t}function n(t){var e=getComputedStyle(t);return e||a("Style returned "+e+". Are you running this code in a hidden iframe on Firefox? See http://bit.ly/getsizebug1"),e}function o(){if(!d){d=!0;var e=document.createElement("div");e.style.width="200px",e.style.padding="1px 2px 3px 4px",e.style.borderStyle="solid",e.style.borderWidth="1px 2px 3px 4px",e.style.boxSizing="border-box";var i=document.body||document.documentElement;i.appendChild(e);var o=n(e);r.isBoxSizeOuter=s=200==t(o.width),i.removeChild(e)}}function r(e){if(o(),"string"==typeof e&&(e=document.querySelector(e)),e&&"object"==typeof e&&e.nodeType){var r=n(e);if("none"==r.display)return i();var a={};a.width=e.offsetWidth,a.height=e.offsetHeight;for(var d=a.isBorderBox="border-box"==r.boxSizing,l=0;u>l;l++){var c=h[l],f=r[c],m=parseFloat(f);a[c]=isNaN(m)?0:m}var p=a.paddingLeft+a.paddingRight,g=a.paddingTop+a.paddingBottom,y=a.marginLeft+a.marginRight,v=a.marginTop+a.marginBottom,_=a.borderLeftWidth+a.borderRightWidth,z=a.borderTopWidth+a.borderBottomWidth,E=d&&s,b=t(r.width);b!==!1&&(a.width=b+(E?0:p+_));var x=t(r.height);return x!==!1&&(a.height=x+(E?0:g+z)),a.innerWidth=a.width-(p+_),a.innerHeight=a.height-(g+z),a.outerWidth=a.width+y,a.outerHeight=a.height+v,a}}var s,a="undefined"==typeof console?e:function(t){console.error(t)},h=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],u=h.length,d=!1;return r}),function(t,e){"use strict";"function"==typeof define&&define.amd?define("desandro-matches-selector/matches-selector",e):"object"==typeof module&&module.exports?module.exports=e():t.matchesSelector=e()}(window,function(){"use strict";var t=function(){var t=window.Element.prototype;if(t.matches)return"matches";if(t.matchesSelector)return"matchesSelector";for(var e=["webkit","moz","ms","o"],i=0;is?"round":"floor";r=Math[a](r),this.cols=Math.max(r,1)},n.getContainerWidth=function(){var t=this._getOption("fitWidth"),i=t?this.element.parentNode:this.element,n=e(i);this.containerWidth=n&&n.innerWidth},n._getItemLayoutPosition=function(t){t.getSize();var e=t.size.outerWidth%this.columnWidth,i=e&&1>e?"round":"ceil",n=Math[i](t.size.outerWidth/this.columnWidth);n=Math.min(n,this.cols);for(var o=this.options.horizontalOrder?"_getHorizontalColPosition":"_getTopColPosition",r=this[o](n,t),s={x:this.columnWidth*r.col,y:r.y},a=r.y+t.size.outerHeight,h=n+r.col,u=r.col;h>u;u++)this.colYs[u]=a;return s},n._getTopColPosition=function(t){var e=this._getTopColGroup(t),i=Math.min.apply(Math,e);return{col:e.indexOf(i),y:i}},n._getTopColGroup=function(t){if(2>t)return this.colYs;for(var e=[],i=this.cols+1-t,n=0;i>n;n++)e[n]=this._getColGroupY(n,t);return e},n._getColGroupY=function(t,e){if(2>e)return this.colYs[t];var i=this.colYs.slice(t,t+e);return Math.max.apply(Math,i)},n._getHorizontalColPosition=function(t,e){var i=this.horizontalColIndex%this.cols,n=t>1&&i+t>this.cols;i=n?0:i;var o=e.size.outerWidth&&e.size.outerHeight;return this.horizontalColIndex=o?i+t:this.horizontalColIndex,{col:i,y:this._getColGroupY(i,t)}},n._manageStamp=function(t){var i=e(t),n=this._getElementOffset(t),o=this._getOption("originLeft"),r=o?n.left:n.right,s=r+i.outerWidth,a=Math.floor(r/this.columnWidth);a=Math.max(0,a);var h=Math.floor(s/this.columnWidth);h-=s%this.columnWidth?0:1,h=Math.min(this.cols-1,h);for(var u=this._getOption("originTop"),d=(u?n.top:n.bottom)+i.outerHeight,l=a;h>=l;l++)this.colYs[l]=Math.max(d,this.colYs[l])},n._getContainerSize=function(){this.maxY=Math.max.apply(Math,this.colYs);var t={height:this.maxY};return this._getOption("fitWidth")&&(t.width=this._getContainerFitWidth()),t},n._getContainerFitWidth=function(){for(var t=0,e=this.cols;--e&&0===this.colYs[e];)t++;return(this.cols-t)*this.columnWidth-this.gutter},n.needsResizeLayout=function(){var t=this.containerWidth;return this.getContainerWidth(),t!=this.containerWidth},i}); \ No newline at end of file diff --git a/static/plugs/jquery/pcasunzips.js b/static/plugs/jquery/pcasunzips.js index 3b61ff6e0..2683c1c8c 100644 --- a/static/plugs/jquery/pcasunzips.js +++ b/static/plugs/jquery/pcasunzips.js @@ -1,9 +1,9 @@ /* PCAS (Province City Area Selector 省、市、地区联动选择JS封装类) Ver 2.02 完整版 *\ - + 制作时间:2005-12-30 更新时间:2006-01-24 数据修正:2012-01-17(截止2011年10月31日) - + 演示地址:http://www.popub.net/script/pcasunzip.html 下载地址:http://www.popub.net/script/pcasunzip.js 应用说明:页面包含 @@ -18,13 +18,13 @@ new PCAS("Province","City","Area","吉林省","松原市","宁江区") 省、市、地区对象取得的值均为实际值。 注:省、市、地区提示信息选项的值为""(空字符串) - + \*** 程序制作/版权所有:崔永祥(333) E-Mail:zhadan007@21cn.com 网址:http://www.popub.net ***/ SPT = "-省份-"; - SCT = "-城市-"; - SAT = "-地区-"; - ShowT = 0; //提示文字 0:不显示 1:显示 - PCAD = "北京市$市辖区,东城区,西城区,朝阳区,丰台区,石景山区,海淀区,门头沟区,房山区,通州区,顺义区,昌平区,大兴区,怀柔区,平谷区|市辖县,密云县,延庆县#天津市$市辖区,和平区,河东区,河西区,南开区,河北区,红桥区,东丽区,西青区,津南区,北辰区,武清区,宝坻区,滨海新区|市辖县,宁河县,静海县,蓟县#河北省$石家庄市,长安区,桥东区,桥西区,新华区,井陉矿区,裕华区,井陉县,正定县,栾城县,行唐县,灵寿县,高邑县,深泽县,赞皇县,无极县,平山县,元氏县,赵县,辛集市,藁城市,晋州市,新乐市,鹿泉市|唐山市,路南区,路北区,古冶区,开平区,丰南区,丰润区,滦县,滦南县,乐亭县,迁西县,玉田县,唐海县,遵化市,迁安市|秦皇岛市,海港区,山海关区,北戴河区,青龙满族自治县,昌黎县,抚宁县,卢龙县|邯郸市,邯山区,丛台区,复兴区,峰峰矿区,邯郸县,临漳县,成安县,大名县,涉县,磁县,肥乡县,永年县,邱县,鸡泽县,广平县,馆陶县,魏县,曲周县,武安市|邢台市,桥东区,桥西区,邢台县,临城县,内丘县,柏乡县,隆尧县,任县,南和县,宁晋县,巨鹿县,新河县,广宗县,平乡县,威县,清河县,临西县,南宫市,沙河市|保定市,新市区,北市区,南市区,满城县,清苑县,涞水县,阜平县,徐水县,定兴县,唐县,高阳县,容城县,涞源县,望都县,安新县,易县,曲阳县,蠡县,顺平县,博野县,雄县,涿州市,定州市,安国市,高碑店市|张家口市,桥东区,桥西区,宣化区,下花园区,宣化县,张北县,康保县,沽源县,尚义县,蔚县,阳原县,怀安县,万全县,怀来县,涿鹿县,赤城县,崇礼县|承德市,双桥区,双滦区,鹰手营子矿区,承德县,兴隆县,平泉县,滦平县,隆化县,丰宁满族自治县,宽城满族自治县,围场满族蒙古族自治县|沧州市,新华区,运河区,沧县,青县,东光县,海兴县,盐山县,肃宁县,南皮县,吴桥县,献县,孟村回族自治县,泊头市,任丘市,黄骅市,河间市|廊坊市,安次区,广阳区,固安县,永清县,香河县,大城县,文安县,大厂回族自治县,霸州市,三河市|衡水市,桃城区,枣强县,武邑县,武强县,饶阳县,安平县,故城县,景县,阜城县,冀州市,深州市#山西省$太原市,小店区,迎泽区,杏花岭区,尖草坪区,万柏林区,晋源区,清徐县,阳曲县,娄烦县,古交市|大同市,城区,矿区,南郊区,新荣区,阳高县,天镇县,广灵县,灵丘县,浑源县,左云县,大同县|阳泉市,城区,矿区,郊区,平定县,盂县|长治市,城区,郊区,长治县,襄垣县,屯留县,平顺县,黎城县,壶关县,长子县,武乡县,沁县,沁源县,潞城市|晋城市,城区,沁水县,阳城县,陵川县,泽州县,高平市|朔州市,朔城区,平鲁区,山阴县,应县,右玉县,怀仁县|晋中市,榆次区,榆社县,左权县,和顺县,昔阳县,寿阳县,太谷县,祁县,平遥县,灵石县,介休市|运城市,盐湖区,临猗县,万荣县,闻喜县,稷山县,新绛县,绛县,垣曲县,夏县,平陆县,芮城县,永济市,河津市|忻州市,忻府区,定襄县,五台县,代县,繁峙县,宁武县,静乐县,神池县,五寨县,岢岚县,河曲县,保德县,偏关县,原平市|临汾市,尧都区,曲沃县,翼城县,襄汾县,洪洞县,古县,安泽县,浮山县,吉县,乡宁县,大宁县,隰县,永和县,蒲县,汾西县,侯马市,霍州市|吕梁市,离石区,文水县,交城县,兴县,临县,柳林县,石楼县,岚县,方山县,中阳县,交口县,孝义市,汾阳市#内蒙古自治区$呼和浩特市,新城区,回民区,玉泉区,赛罕区,土默特左旗,托克托县,和林格尔县,清水河县,武川县|包头市,东河区,昆都仑区,青山区,石拐区,白云鄂博矿区,九原区,土默特右旗,固阳县,达尔罕茂明安联合旗|乌海市,海勃湾区,海南区,乌达区|赤峰市,红山区,元宝山区,松山区,阿鲁科尔沁旗,巴林左旗,巴林右旗,林西县,克什克腾旗,翁牛特旗,喀喇沁旗,宁城县,敖汉旗|通辽市,科尔沁区,科尔沁左翼中旗,科尔沁左翼后旗,开鲁县,库伦旗,奈曼旗,扎鲁特旗,霍林郭勒市|鄂尔多斯市,东胜区,达拉特旗,准格尔旗,鄂托克前旗,鄂托克旗,杭锦旗,乌审旗,伊金霍洛旗|呼伦贝尔市,海拉尔区,阿荣旗,莫力达瓦达斡尔族自治旗,鄂伦春自治旗,鄂温克族自治旗,陈巴尔虎旗,新巴尔虎左旗,新巴尔虎右旗,满洲里市,牙克石市,扎兰屯市,额尔古纳市,根河市|巴彦淖尔市,临河区,五原县,磴口县,乌拉特前旗,乌拉特中旗,乌拉特后旗,杭锦后旗|乌兰察布市,集宁区,卓资县,化德县,商都县,兴和县,凉城县,察哈尔右翼前旗,察哈尔右翼中旗,察哈尔右翼后旗,四子王旗,丰镇市|兴安盟,乌兰浩特市,阿尔山市,科尔沁右翼前旗,科尔沁右翼中旗,扎赉特旗,突泉县|锡林郭勒盟,二连浩特市,锡林浩特市,阿巴嘎旗,苏尼特左旗,苏尼特右旗,东乌珠穆沁旗,西乌珠穆沁旗,太仆寺旗,镶黄旗,正镶白旗,正蓝旗,多伦县|阿拉善盟,阿拉善左旗,阿拉善右旗,额济纳旗#辽宁省$沈阳市,和平区,沈河区,大东区,皇姑区,铁西区,苏家屯区,东陵区,沈北新区,于洪区,辽中县,康平县,法库县,新民市|大连市,中山区,西岗区,沙河口区,甘井子区,旅顺口区,金州区,长海县,瓦房店市,普兰店市,庄河市|鞍山市,铁东区,铁西区,立山区,千山区,台安县,岫岩满族自治县,海城市|抚顺市,新抚区,东洲区,望花区,顺城区,抚顺县,新宾满族自治县,清原满族自治县|本溪市,平山区,溪湖区,明山区,南芬区,本溪满族自治县,桓仁满族自治县|丹东市,元宝区,振兴区,振安区,宽甸满族自治县,东港市,凤城市|锦州市,古塔区,凌河区,太和区,黑山县,义县,凌海市,北镇市|营口市,站前区,西市区,鲅鱼圈区,老边区,盖州市,大石桥市|阜新市,海州区,新邱区,太平区,清河门区,细河区,阜新蒙古族自治县,彰武县|辽阳市,白塔区,文圣区,宏伟区,弓长岭区,太子河区,辽阳县,灯塔市|盘锦市,双台子区,兴隆台区,大洼县,盘山县|铁岭市,银州区,清河区,铁岭县,西丰县,昌图县,调兵山市,开原市|朝阳市,双塔区,龙城区,朝阳县,建平县,喀喇沁左翼蒙古族自治县,北票市,凌源市|葫芦岛市,连山区,龙港区,南票区,绥中县,建昌县,兴城市#吉林省$长春市,南关区,宽城区,朝阳区,二道区,绿园区,双阳区,农安县,九台市,榆树市,德惠市|吉林市,昌邑区,龙潭区,船营区,丰满区,永吉县,蛟河市,桦甸市,舒兰市,磐石市|四平市,铁西区,铁东区,梨树县,伊通满族自治县,公主岭市,双辽市|辽源市,龙山区,西安区,东丰县,东辽县|通化市,东昌区,二道江区,通化县,辉南县,柳河县,梅河口市,集安市|白山市,八道江区,江源区,抚松县,靖宇县,长白朝鲜族自治县,临江市|松原市,宁江区,前郭尔罗斯蒙古族自治县,长岭县,乾安县,扶余县|白城市,洮北区,镇赉县,通榆县,洮南市,大安市|延边朝鲜族自治州,延吉市,图们市,敦化市,珲春市,龙井市,和龙市,汪清县,安图县#黑龙江省$哈尔滨市,道里区,南岗区,道外区,平房区,松北区,香坊区,呼兰区,阿城区,依兰县,方正县,宾县,巴彦县,木兰县,通河县,延寿县,双城市,尚志市,五常市|齐齐哈尔市,龙沙区,建华区,铁锋区,昂昂溪区,富拉尔基区,碾子山区,梅里斯达斡尔族区,龙江县,依安县,泰来县,甘南县,富裕县,克山县,克东县,拜泉县,讷河市|鸡西市,鸡冠区,恒山区,滴道区,梨树区,城子河区,麻山区,鸡东县,虎林市,密山市|鹤岗市,向阳区,工农区,南山区,兴安区,东山区,兴山区,萝北县,绥滨县|双鸭山市,尖山区,岭东区,四方台区,宝山区,集贤县,友谊县,宝清县,饶河县|大庆市,萨尔图区,龙凤区,让胡路区,红岗区,大同区,肇州县,肇源县,林甸县,杜尔伯特蒙古族自治县|伊春市,伊春区,南岔区,友好区,西林区,翠峦区,新青区,美溪区,金山屯区,五营区,乌马河区,汤旺河区,带岭区,乌伊岭区,红星区,上甘岭区,嘉荫县,铁力市|佳木斯市,向阳区,前进区,东风区,郊区,桦南县,桦川县,汤原县,抚远县,同江市,富锦市|七台河市,新兴区,桃山区,茄子河区,勃利县|牡丹江市,东安区,阳明区,爱民区,西安区,东宁县,林口县,绥芬河市,海林市,宁安市,穆棱市|黑河市,爱辉区,嫩江县,逊克县,孙吴县,北安市,五大连池市|绥化市,北林区,望奎县,兰西县,青冈县,庆安县,明水县,绥棱县,安达市,肇东市,海伦市|大兴安岭地区,呼玛县,塔河县,漠河县#上海市$黄浦区,徐汇区,长宁区,静安区,普陀区,闸北区,虹口区,杨浦区,闵行区,宝山区,嘉定区,浦东新区,金山区,松江区,青浦区,奉贤区|市辖县,崇明县#江苏省$南京市,玄武区,白下区,秦淮区,建邺区,鼓楼区,下关区,浦口区,栖霞区,雨花台区,江宁区,六合区,溧水县,高淳县|无锡市,崇安区,南长区,北塘区,锡山区,惠山区,滨湖区,江阴市,宜兴市|徐州市,鼓楼区,云龙区,贾汪区,泉山区,铜山区,丰县,沛县,睢宁县,新沂市,邳州市|常州市,天宁区,钟楼区,戚墅堰区,新北区,武进区,溧阳市,金坛市|苏州市,沧浪区,平江区,金阊区,虎丘区,吴中区,相城区,常熟市,张家港市,昆山市,吴江市,太仓市|南通市,崇川区,港闸区,通州区,海安县,如东县,启东市,如皋市,海门市|连云港市,连云区,新浦区,海州区,赣榆县,东海县,灌云县,灌南县|淮安市,清河区,楚州区,淮阴区,清浦区,涟水县,洪泽县,盱眙县,金湖县|盐城市,亭湖区,盐都区,响水县,滨海县,阜宁县,射阳县,建湖县,东台市,大丰市|扬州市,广陵区,邗江区,江都区,宝应县,仪征市,高邮市|镇江市,京口区,润州区,丹徒区,丹阳市,扬中市,句容市|泰州市,海陵区,高港区,兴化市,靖江市,泰兴市,姜堰市|宿迁市,宿城区,宿豫区,沭阳县,泗阳县,泗洪县#浙江省$杭州市,上城区,下城区,江干区,拱墅区,西湖区,滨江区,萧山区,余杭区,桐庐县,淳安县,建德市,富阳市,临安市|宁波市,海曙区,江东区,江北区,北仑区,镇海区,鄞州区,象山县,宁海县,余姚市,慈溪市,奉化市|温州市,鹿城区,龙湾区,瓯海区,洞头县,永嘉县,平阳县,苍南县,文成县,泰顺县,瑞安市,乐清市|嘉兴市,南湖区,秀洲区,嘉善县,海盐县,海宁市,平湖市,桐乡市|湖州市,吴兴区,南浔区,德清县,长兴县,安吉县|绍兴市,越城区,绍兴县,新昌县,诸暨市,上虞市,嵊州市|金华市,婺城区,金东区,武义县,浦江县,磐安县,兰溪市,义乌市,东阳市,永康市|衢州市,柯城区,衢江区,常山县,开化县,龙游县,江山市|舟山市,定海区,普陀区,岱山县,嵊泗县|台州市,椒江区,黄岩区,路桥区,玉环县,三门县,天台县,仙居县,温岭市,临海市|丽水市,莲都区,青田县,缙云县,遂昌县,松阳县,云和县,庆元县,景宁畲族自治县,龙泉市#安徽省$合肥市,瑶海区,庐阳区,蜀山区,包河区,长丰县,肥东县,肥西县,庐江县,巢湖市|芜湖市,镜湖区,弋江区,鸠江区,三山区,芜湖县,繁昌县,南陵县,无为县|蚌埠市,龙子湖区,蚌山区,禹会区,淮上区,怀远县,五河县,固镇县|淮南市,大通区,田家庵区,谢家集区,八公山区,潘集区,凤台县|马鞍山市,金家庄区,花山区,雨山区,当涂县,含山县,和县|淮北市,杜集区,相山区,烈山区,濉溪县|铜陵市,铜官山区,狮子山区,郊区,铜陵县|安庆市,迎江区,大观区,宜秀区,怀宁县,枞阳县,潜山县,太湖县,宿松县,望江县,岳西县,桐城市|黄山市,屯溪区,黄山区,徽州区,歙县,休宁县,黟县,祁门县|滁州市,琅琊区,南谯区,来安县,全椒县,定远县,凤阳县,天长市,明光市|阜阳市,颍州区,颍东区,颍泉区,临泉县,太和县,阜南县,颍上县,界首市|宿州市,埇桥区,砀山县,萧县,灵璧县,泗县|六安市,金安区,裕安区,寿县,霍邱县,舒城县,金寨县,霍山县|亳州市,谯城区,涡阳县,蒙城县,利辛县|池州市,贵池区,东至县,石台县,青阳县|宣城市,宣州区,郎溪县,广德县,泾县,绩溪县,旌德县,宁国市#福建省$福州市,鼓楼区,台江区,仓山区,马尾区,晋安区,闽侯县,连江县,罗源县,闽清县,永泰县,平潭县,福清市,长乐市|厦门市,思明区,海沧区,湖里区,集美区,同安区,翔安区|莆田市,城厢区,涵江区,荔城区,秀屿区,仙游县|三明市,梅列区,三元区,明溪县,清流县,宁化县,大田县,尤溪县,沙县,将乐县,泰宁县,建宁县,永安市|泉州市,鲤城区,丰泽区,洛江区,泉港区,惠安县,安溪县,永春县,德化县,金门县,石狮市,晋江市,南安市|漳州市,芗城区,龙文区,云霄县,漳浦县,诏安县,长泰县,东山县,南靖县,平和县,华安县,龙海市|南平市,延平区,顺昌县,浦城县,光泽县,松溪县,政和县,邵武市,武夷山市,建瓯市,建阳市|龙岩市,新罗区,长汀县,永定县,上杭县,武平县,连城县,漳平市|宁德市,蕉城区,霞浦县,古田县,屏南县,寿宁县,周宁县,柘荣县,福安市,福鼎市#江西省$南昌市,东湖区,西湖区,青云谱区,湾里区,青山湖区,南昌县,新建县,安义县,进贤县|景德镇市,昌江区,珠山区,浮梁县,乐平市|萍乡市,安源区,湘东区,莲花县,上栗县,芦溪县|九江市,庐山区,浔阳区,九江县,武宁县,修水县,永修县,德安县,星子县,都昌县,湖口县,彭泽县,瑞昌市,共青城市|新余市,渝水区,分宜县|鹰潭市,月湖区,余江县,贵溪市|赣州市,章贡区,赣县,信丰县,大余县,上犹县,崇义县,安远县,龙南县,定南县,全南县,宁都县,于都县,兴国县,会昌县,寻乌县,石城县,瑞金市,南康市|吉安市,吉州区,青原区,吉安县,吉水县,峡江县,新干县,永丰县,泰和县,遂川县,万安县,安福县,永新县,井冈山市|宜春市,袁州区,奉新县,万载县,上高县,宜丰县,靖安县,铜鼓县,丰城市,樟树市,高安市|抚州市,临川区,南城县,黎川县,南丰县,崇仁县,乐安县,宜黄县,金溪县,资溪县,东乡县,广昌县|上饶市,信州区,上饶县,广丰县,玉山县,铅山县,横峰县,弋阳县,余干县,鄱阳县,万年县,婺源县,德兴市#山东省$济南市,历下区,市中区,槐荫区,天桥区,历城区,长清区,平阴县,济阳县,商河县,章丘市|青岛市,市南区,市北区,四方区,黄岛区,崂山区,李沧区,城阳区,胶州市,即墨市,平度市,胶南市,莱西市|淄博市,淄川区,张店区,博山区,临淄区,周村区,桓台县,高青县,沂源县|枣庄市,市中区,薛城区,峄城区,台儿庄区,山亭区,滕州市|东营市,东营区,河口区,垦利县,利津县,广饶县|烟台市,芝罘区,福山区,牟平区,莱山区,长岛县,龙口市,莱阳市,莱州市,蓬莱市,招远市,栖霞市,海阳市|潍坊市,潍城区,寒亭区,坊子区,奎文区,临朐县,昌乐县,青州市,诸城市,寿光市,安丘市,高密市,昌邑市|济宁市,市中区,任城区,微山县,鱼台县,金乡县,嘉祥县,汶上县,泗水县,梁山县,曲阜市,兖州市,邹城市|泰安市,泰山区,岱岳区,宁阳县,东平县,新泰市,肥城市|威海市,环翠区,文登市,荣成市,乳山市|日照市,东港区,岚山区,五莲县,莒县|莱芜市,莱城区,钢城区|临沂市,兰山区,罗庄区,河东区,沂南县,郯城县,沂水县,苍山县,费县,平邑县,莒南县,蒙阴县,临沭县|德州市,德城区,陵县,宁津县,庆云县,临邑县,齐河县,平原县,夏津县,武城县,乐陵市,禹城市|聊城市,东昌府区,阳谷县,莘县,茌平县,东阿县,冠县,高唐县,临清市|滨州市,滨城区,惠民县,阳信县,无棣县,沾化县,博兴县,邹平县|菏泽市,牡丹区,曹县,单县,成武县,巨野县,郓城县,鄄城县,定陶县,东明县#河南省$郑州市,中原区,二七区,管城回族区,金水区,上街区,惠济区,中牟县,巩义市,荥阳市,新密市,新郑市,登封市|开封市,龙亭区,顺河回族区,鼓楼区,禹王台区,金明区,杞县,通许县,尉氏县,开封县,兰考县|洛阳市,老城区,西工区,瀍河回族区,涧西区,吉利区,洛龙区,孟津县,新安县,栾川县,嵩县,汝阳县,宜阳县,洛宁县,伊川县,偃师市|平顶山市,新华区,卫东区,石龙区,湛河区,宝丰县,叶县,鲁山县,郏县,舞钢市,汝州市|安阳市,文峰区,北关区,殷都区,龙安区,安阳县,汤阴县,滑县,内黄县,林州市|鹤壁市,鹤山区,山城区,淇滨区,浚县,淇县|新乡市,红旗区,卫滨区,凤泉区,牧野区,新乡县,获嘉县,原阳县,延津县,封丘县,长垣县,卫辉市,辉县市|焦作市,解放区,中站区,马村区,山阳区,修武县,博爱县,武陟县,温县,沁阳市,孟州市|濮阳市,华龙区,清丰县,南乐县,范县,台前县,濮阳县|许昌市,魏都区,许昌县,鄢陵县,襄城县,禹州市,长葛市|漯河市,源汇区,郾城区,召陵区,舞阳县,临颍县|三门峡市,湖滨区,渑池县,陕县,卢氏县,义马市,灵宝市|南阳市,宛城区,卧龙区,南召县,方城县,西峡县,镇平县,内乡县,淅川县,社旗县,唐河县,新野县,桐柏县,邓州市|商丘市,梁园区,睢阳区,民权县,睢县,宁陵县,柘城县,虞城县,夏邑县,永城市|信阳市,浉河区,平桥区,罗山县,光山县,新县,商城县,固始县,潢川县,淮滨县,息县|周口市,川汇区,扶沟县,西华县,商水县,沈丘县,郸城县,淮阳县,太康县,鹿邑县,项城市|驻马店市,驿城区,西平县,上蔡县,平舆县,正阳县,确山县,泌阳县,汝南县,遂平县,新蔡县|省直辖县级行政区划,济源市#湖北省$武汉市,江岸区,江汉区,硚口区,汉阳区,武昌区,青山区,洪山区,东西湖区,汉南区,蔡甸区,江夏区,黄陂区,新洲区|黄石市,黄石港区,西塞山区,下陆区,铁山区,阳新县,大冶市|十堰市,茅箭区,张湾区,郧县,郧西县,竹山县,竹溪县,房县,丹江口市|宜昌市,西陵区,伍家岗区,点军区,猇亭区,夷陵区,远安县,兴山县,秭归县,长阳土家族自治县,五峰土家族自治县,宜都市,当阳市,枝江市|襄阳市,襄城区,樊城区,襄州区,南漳县,谷城县,保康县,老河口市,枣阳市,宜城市|鄂州市,梁子湖区,华容区,鄂城区|荆门市,东宝区,掇刀区,京山县,沙洋县,钟祥市|孝感市,孝南区,孝昌县,大悟县,云梦县,应城市,安陆市,汉川市|荆州市,沙市区,荆州区,公安县,监利县,江陵县,石首市,洪湖市,松滋市|黄冈市,黄州区,团风县,红安县,罗田县,英山县,浠水县,蕲春县,黄梅县,麻城市,武穴市|咸宁市,咸安区,嘉鱼县,通城县,崇阳县,通山县,赤壁市|随州市,曾都区,随县,广水市|恩施土家族苗族自治州,恩施市,利川市,建始县,巴东县,宣恩县,咸丰县,来凤县,鹤峰县|省直辖县级行政区划,仙桃市,潜江市,天门市,神农架林区#湖南省$长沙市,芙蓉区,天心区,岳麓区,开福区,雨花区,望城区,长沙县,宁乡县,浏阳市|株洲市,荷塘区,芦淞区,石峰区,天元区,株洲县,攸县,茶陵县,炎陵县,醴陵市|湘潭市,雨湖区,岳塘区,湘潭县,湘乡市,韶山市|衡阳市,珠晖区,雁峰区,石鼓区,蒸湘区,南岳区,衡阳县,衡南县,衡山县,衡东县,祁东县,耒阳市,常宁市|邵阳市,双清区,大祥区,北塔区,邵东县,新邵县,邵阳县,隆回县,洞口县,绥宁县,新宁县,城步苗族自治县,武冈市|岳阳市,岳阳楼区,云溪区,君山区,岳阳县,华容县,湘阴县,平江县,汨罗市,临湘市|常德市,武陵区,鼎城区,安乡县,汉寿县,澧县,临澧县,桃源县,石门县,津市市|张家界市,永定区,武陵源区,慈利县,桑植县|益阳市,资阳区,赫山区,南县,桃江县,安化县,沅江市|郴州市,北湖区,苏仙区,桂阳县,宜章县,永兴县,嘉禾县,临武县,汝城县,桂东县,安仁县,资兴市|永州市,零陵区,冷水滩区,祁阳县,东安县,双牌县,道县,江永县,宁远县,蓝山县,新田县,江华瑶族自治县|怀化市,鹤城区,中方县,沅陵县,辰溪县,溆浦县,会同县,麻阳苗族自治县,新晃侗族自治县,芷江侗族自治县,靖州苗族侗族自治县,通道侗族自治县,洪江市|娄底市,娄星区,双峰县,新化县,冷水江市,涟源市|湘西土家族苗族自治州,吉首市,泸溪县,凤凰县,花垣县,保靖县,古丈县,永顺县,龙山县#广东省$广州市,荔湾区,越秀区,海珠区,天河区,白云区,黄埔区,番禺区,花都区,南沙区,萝岗区,增城市,从化市|韶关市,武江区,浈江区,曲江区,始兴县,仁化县,翁源县,乳源瑶族自治县,新丰县,乐昌市,南雄市|深圳市,罗湖区,福田区,南山区,宝安区,龙岗区,盐田区|珠海市,香洲区,斗门区,金湾区|汕头市,龙湖区,金平区,濠江区,潮阳区,潮南区,澄海区,南澳县|佛山市,禅城区,南海区,顺德区,三水区,高明区|江门市,蓬江区,江海区,新会区,台山市,开平市,鹤山市,恩平市|湛江市,赤坎区,霞山区,坡头区,麻章区,遂溪县,徐闻县,廉江市,雷州市,吴川市|茂名市,茂南区,茂港区,电白县,高州市,化州市,信宜市|肇庆市,端州区,鼎湖区,广宁县,怀集县,封开县,德庆县,高要市,四会市|惠州市,惠城区,惠阳区,博罗县,惠东县,龙门县|梅州市,梅江区,梅县,大埔县,丰顺县,五华县,平远县,蕉岭县,兴宁市|汕尾市,城区,海丰县,陆河县,陆丰市|河源市,源城区,紫金县,龙川县,连平县,和平县,东源县|阳江市,江城区,阳西县,阳东县,阳春市|清远市,清城区,佛冈县,阳山县,连山壮族瑶族自治县,连南瑶族自治县,清新县,英德市,连州市|东莞市|中山市|潮州市,湘桥区,潮安县,饶平县|揭阳市,榕城区,揭东县,揭西县,惠来县,普宁市|云浮市,云城区,新兴县,郁南县,云安县,罗定市#广西壮族自治区$南宁市,兴宁区,青秀区,江南区,西乡塘区,良庆区,邕宁区,武鸣县,隆安县,马山县,上林县,宾阳县,横县|柳州市,城中区,鱼峰区,柳南区,柳北区,柳江县,柳城县,鹿寨县,融安县,融水苗族自治县,三江侗族自治县|桂林市,秀峰区,叠彩区,象山区,七星区,雁山区,阳朔县,临桂县,灵川县,全州县,兴安县,永福县,灌阳县,龙胜各族自治县,资源县,平乐县,荔蒲县,恭城瑶族自治县|梧州市,万秀区,蝶山区,长洲区,苍梧县,藤县,蒙山县,岑溪市|北海市,海城区,银海区,铁山港区,合浦县|防城港市,港口区,防城区,上思县,东兴市|钦州市,钦南区,钦北区,灵山县,浦北县|贵港市,港北区,港南区,覃塘区,平南县,桂平市|玉林市,玉州区,容县,陆川县,博白县,兴业县,北流市|百色市,右江区,田阳县,田东县,平果县,德保县,靖西县,那坡县,凌云县,乐业县,田林县,西林县,隆林各族自治县|贺州市,八步区,昭平县,钟山县,富川瑶族自治县|河池市,金城江区,南丹县,天峨县,凤山县,东兰县,罗城仫佬族自治县,环江毛南族自治县,巴马瑶族自治县,都安瑶族自治县,大化瑶族自治县,宜州市|来宾市,兴宾区,忻城县,象州县,武宣县,金秀瑶族自治县,合山市|崇左市,江洲区,扶绥县,宁明县,龙州县,大新县,天等县,凭祥市#海南省$海口市,秀英区,龙华区,琼山区,美兰区|三亚市,市辖区|省直辖县级行政区划,五指山市,琼海市,儋州市,文昌市,万宁市,东方市,定安县,屯昌县,澄迈县,临高县,白沙黎族自治县,昌江黎族自治县,乐东黎族自治县,陵水黎族自治县,保亭黎族苗族自治县,琼中黎族苗族自治县,西沙群岛,南沙群岛,中沙群岛的岛礁及其海域#重庆市$万州区,涪陵区,渝中区,大渡口区,江北区,沙坪坝区,九龙坡区,南岸区,北碚区,綦江区,大足区,渝北区,巴南区,黔江区,长寿区,江津区,合川区,永川区,南川区|市辖县,潼南县,铜梁县,荣昌县,璧山县,梁平县,城口县,丰都县,垫江县,武隆县,忠县,开县,云阳县,奉节县,巫山县,巫溪县,石柱土家族自治县,秀山土家族苗族自治县,酉阳土家族苗族自治县,彭水苗族土家族自治县#四川省$成都市,锦江区,青羊区,金牛区,武侯区,成华区,龙泉驿区,青白江区,新都区,温江区,金堂县,双流县,郫县,大邑县,蒲江县,新津县,都江堰市,彭州市,邛崃市,崇州市|自贡市,自流井区,贡井区,大安区,沿滩区,荣县,富顺县|攀枝花市,东区,西区,仁和区,米易县,盐边县|泸州市,江阳区,纳溪区,龙马潭区,泸县,合江县,叙永县,古蔺县|德阳市,旌阳区,中江县,罗江县,广汉市,什邡市,绵竹市|绵阳市,涪城区,游仙区,三台县,盐亭县,安县,梓潼县,北川羌族自治县,平武县,江油市|广元市,利州区,元坝区,朝天区,旺苍县,青川县,剑阁县,苍溪县|遂宁市,船山区,安居区,蓬溪县,射洪县,大英县|内江市,市中区,东兴区,威远县,资中县,隆昌县|乐山市,市中区,沙湾区,五通桥区,金口河区,犍为县,井研县,夹江县,沐川县,峨边彝族自治县,马边彝族自治县,峨眉山市|南充市,顺庆区,高坪区,嘉陵区,南部县,营山县,蓬安县,仪陇县,西充县,阆中市|眉山市,东坡区,仁寿县,彭山县,洪雅县,丹棱县,青神县|宜宾市,翠屏区,南溪区,宜宾县,江安县,长宁县,高县,珙县,筠连县,兴文县,屏山县|广安市,广安区,岳池县,武胜县,邻水县,华蓥市|达州市,通川区,达县,宣汉县,开江县,大竹县,渠县,万源市|雅安市,雨城区,名山县,荥经县,汉源县,石棉县,天全县,芦山县,宝兴县|巴中市,巴州区,通江县,南江县,平昌县|资阳市,雁江区,安岳县,乐至县,简阳市|阿坝藏族羌族自治州,汶川县,理县,茂县,松潘县,九寨沟县,金川县,小金县,黑水县,马尔康县,壤塘县,阿坝县,若尔盖县,红原县|甘孜藏族自治州,康定县,泸定县,丹巴县,九龙县,雅江县,道孚县,炉霍县,甘孜县,新龙县,德格县,白玉县,石渠县,色达县,理塘县,巴塘县,乡城县,稻城县,得荣县|凉山彝族自治州,西昌市,木里藏族自治县,盐源县,德昌县,会理县,会东县,宁南县,普格县,布拖县,金阳县,昭觉县,喜德县,冕宁县,越西县,甘洛县,美姑县,雷波县#贵州省$贵阳市,南明区,云岩区,花溪区,乌当区,白云区,小河区,开阳县,息烽县,修文县,清镇市|六盘水市,钟山区,六枝特区,水城县,盘县|遵义市,红花岗区,汇川区,遵义县,桐梓县,绥阳县,正安县,道真仡佬族苗族自治县,务川仡佬族苗族自治县,凤冈县,湄潭县,余庆县,习水县,赤水市,仁怀市|安顺市,西秀区,平坝县,普定县,镇宁布依族苗族自治县,关岭布依族苗族自治县,紫云苗族布依族自治县|毕节市,七星关区,大方县,黔西县,金沙县,织金县,纳雍县,威宁彝族回族苗族自治县,赫章县|铜仁市,碧江区,万山区,江口县,玉屏侗族自治县,石阡县,思南县,印江土家族苗族自治县,德江县,沿河土家族自治县,松桃苗族自治县|黔西南布依族苗族自治州,兴义市,兴仁县,普安县,晴隆县,贞丰县,望谟县,册亨县,安龙县|黔东南苗族侗族自治州,凯里市,黄平县,施秉县,三穗县,镇远县,岑巩县,天柱县,锦屏县,剑河县,台江县,黎平县,榕江县,从江县,雷山县,麻江县,丹寨县|黔南布依族苗族自治州,都匀市,福泉市,荔波县,贵定县,瓮安县,独山县,平塘县,罗甸县,长顺县,龙里县,惠水县,三都水族自治县#云南省$昆明市,五华区,盘龙区,官渡区,西山区,东川区,呈贡区,晋宁县,富民县,宜良县,石林彝族自治县,嵩明县,禄劝彝族苗族自治县,寻甸回族彝族自治县,安宁市|曲靖市,麒麟区,马龙县,陆良县,师宗县,罗平县,富源县,会泽县,沾益县,宣威市|玉溪市,红塔区,江川县,澄江县,通海县,华宁县,易门县,峨山彝族自治县,新平彝族傣族自治县,元江哈尼族彝族傣族自治县|保山市,隆阳区,施甸县,腾冲县,龙陵县,昌宁县|昭通市,昭阳区,鲁甸县,巧家县,盐津县,大关县,永善县,绥江县,镇雄县,彝良县,威信县,水富县|丽江市,古城区,玉龙纳西族自治县,永胜县,华坪县,宁蒗彝族自治县|普洱市,思茅区,宁洱哈尼族彝族自治县,墨江哈尼族自治县,景东彝族自治县,景谷傣族彝族自治县,镇沅彝族哈尼族拉祜族自治县,江城哈尼族彝族自治县,孟连傣族拉祜族佤族自治县,澜沧拉祜族自治县,西盟佤族自治县|临沧市,临翔区,凤庆县,云县,永德县,镇康县,双江拉祜族佤族布朗族傣族自治县,耿马傣族佤族自治县,沧源佤族自治县|楚雄彝族自治州,楚雄市,双柏县,牟定县,南华县,姚安县,大姚县,永仁县,元谋县,武定县,禄丰县|红河哈尼族彝族自治州,个旧市,开远市,蒙自市,屏边苗族自治县,建水县,石屏县,弥勒县,泸西县,元阳县,红河县,金平苗族瑶族傣族自治县,绿春县,河口瑶族自治县|文山壮族苗族自治州,文山市,砚山县,西畴县,麻栗坡县,马关县,丘北县,广南县,富宁县|西双版纳傣族自治州,景洪市,勐海县,勐腊县|大理白族自治州,大理市,漾濞彝族自治县,祥云县,宾川县,弥渡县,南涧彝族自治县,巍山彝族回族自治县,永平县,云龙县,洱源县,剑川县,鹤庆县|德宏傣族景颇族自治州,瑞丽市,芒市,梁河县,盈江县,陇川县|怒江傈僳族自治州,泸水县,福贡县,贡山独龙族怒族自治县,兰坪白族普米族自治县|迪庆藏族自治州,香格里拉县,德钦县,维西傈僳族自治县#西藏自治区$拉萨市,城关区,林周县,当雄县,尼木县,曲水县,堆龙德庆县,达孜县,墨竹工卡县|昌都地区,昌都县,江达县,贡觉县,类乌齐县,丁青县,察雅县,八宿县,左贡县,芒康县,洛隆县,边坝县|山南地区,乃东县,扎囊县,贡嘎县,桑日县,琼结县,曲松县,措美县,洛扎县,加查县,隆子县,错那县,浪卡子县|日喀则地区,日喀则市,南木林县,江孜县,定日县,萨迦县,拉孜县,昂仁县,谢通门县,白朗县,仁布县,康马县,定结县,仲巴县,亚东县,吉隆县,聂拉木县,萨嘎县,岗巴县|那曲地区,那曲县,嘉黎县,比如县,聂荣县,安多县,申扎县,索县,班戈县,巴青县,尼玛县|阿里地区,普兰县,札达县,噶尔县,日土县,革吉县,改则县,措勤县|林芝地区,林芝县,工布江达县,米林县,墨脱县,波密县,察隅县,朗县#陕西省$西安市,新城区,碑林区,莲湖区,灞桥区,未央区,雁塔区,阎良区,临潼区,长安区,蓝田县,周至县,户县,高陵县|铜川市,王益区,印台区,耀州区,宜君县|宝鸡市,渭滨区,金台区,陈仓区,凤翔县,岐山县,扶风县,眉县,陇县,千阳县,麟游县,凤县,太白县|咸阳市,秦都区,杨陵区,渭城区,三原县,泾阳县,乾县,礼泉县,永寿县,彬县,长武县,旬邑县,淳化县,武功县,兴平市|渭南市,临渭区,华县,潼关县,大荔县,合阳县,澄城县,蒲城县,白水县,富平县,韩城市,华阴市|延安市,宝塔区,延长县,延川县,子长县,安塞县,志丹县,吴起县,甘泉县,富县,洛川县,宜川县,黄龙县,黄陵县|汉中市,汉台区,南郑县,城固县,洋县,西乡县,勉县,宁强县,略阳县,镇巴县,留坝县,佛坪县|榆林市,榆阳区,神木县,府谷县,横山县,靖边县,定边县,绥德县,米脂县,佳县,吴堡县,清涧县,子洲县|安康市,汉滨区,汉阴县,石泉县,宁陕县,紫阳县,岚皋县,平利县,镇坪县,旬阳县,白河县|商洛市,商州区,洛南县,丹凤县,商南县,山阳县,镇安县,柞水县#甘肃省$兰州市,城关区,七里河区,西固区,安宁区,红古区,永登县,皋兰县,榆中县|嘉峪关市,市辖区|金昌市,金川区,永昌县|白银市,白银区,平川区,靖远县,会宁县,景泰县|天水市,秦州区,麦积区,清水县,秦安县,甘谷县,武山县,张家川回族自治县|武威市,凉州区,民勤县,古浪县,天祝藏族自治县|张掖市,甘州区,肃南裕固族自治县,民乐县,临泽县,高台县,山丹县|平凉市,崆峒区,泾川县,灵台县,崇信县,华亭县,庄浪县,静宁县|酒泉市,肃州区,金塔县,瓜州县,肃北蒙古族自治县,阿克塞哈萨克族自治县,玉门市,敦煌市|庆阳市,西峰区,庆城县,环县,华池县,合水县,正宁县,宁县,镇原县|定西市,安定区,通渭县,陇西县,渭源县,临洮县,漳县,岷县|陇南市,武都区,成县,文县,宕昌县,康县,西和县,礼县,徽县,两当县|临夏回族自治州,临夏市,临夏县,康乐县,永靖县,广河县,和政县,东乡族自治县,积石山保安族东乡族撒拉族自治县|甘南藏族自治州,合作市,临潭县,卓尼县,舟曲县,迭部县,玛曲县,碌曲县,夏河县#青海省$西宁市,城东区,城中区,城西区,城北区,大通回族土族自治县,湟中县,湟源县|海东地区,平安县,民和回族土族自治县,乐都县,互助土族自治县,化隆回族自治县,循化撒拉族自治县|海北藏族自治州,门源回族自治县,祁连县,海晏县,刚察县|黄南藏族自治州,同仁县,尖扎县,泽库县,河南蒙古族自治县|海南藏族自治州,共和县,同德县,贵德县,兴海县,贵南县|果洛藏族自治州,玛沁县,班玛县,甘德县,达日县,久治县,玛多县|玉树藏族自治州,玉树县,杂多县,称多县,治多县,囊谦县,曲麻莱县|海西蒙古族藏族自治州,格尔木市,德令哈市,乌兰县,都兰县,天峻县#宁夏回族自治区$银川市,兴庆区,西夏区,金凤区,永宁县,贺兰县,灵武市|石嘴山市,大武口区,惠农区,平罗县|吴忠市,利通区,红寺堡区,盐池县,同心县,青铜峡市|固原市,原州区,西吉县,隆德县,泾源县,彭阳县|中卫市,沙坡头区,中宁县,海原县#新疆维吾尔自治区$乌鲁木齐市,天山区,沙依巴克区,新市区,水磨沟区,头屯河区,达坂城区,米东区,乌鲁木齐县|克拉玛依市,独山子区,克拉玛依区,白碱滩区,乌尔禾区|吐鲁番地区,吐鲁番市,鄯善县,托克逊县|哈密地区,哈密市,巴里坤哈萨克自治县,伊吾县|昌吉回族自治州,昌吉市,阜康市,呼图壁县,玛纳斯县,奇台县,吉木萨尔县,木垒哈萨克自治县|博尔塔拉蒙古自治州,博乐市,精河县,温泉县|巴音郭楞蒙古自治州,库尔勒市,轮台县,尉犁县,若羌县,且末县,焉耆回族自治县,和静县,和硕县,博湖县|阿克苏地区,阿克苏市,温宿县,库车县,沙雅县,新和县,拜城县,乌什县,阿瓦提县,柯坪县|克孜勒苏柯尔克孜自治州,阿图什市,阿克陶县,阿合奇县,乌恰县|喀什地区,喀什市,疏附县,疏勒县,英吉沙县,泽普县,莎车县,叶城县,麦盖提县,岳普湖县,伽师县,巴楚县,塔什库尔干塔吉克自治县|和田地区,和田市,和田县,墨玉县,皮山县,洛浦县,策勒县,于田县,民丰县|伊犁哈萨克自治州,伊宁市,奎屯市,伊宁县,察布查尔锡伯自治县,霍城县,巩留县,新源县,昭苏县,特克斯县,尼勒克县|塔城地区,塔城市,乌苏市,额敏县,沙湾县,托里县,裕民县,和布克赛尔蒙古自治县|阿勒泰地区,阿勒泰市,布尔津县,富蕴县,福海县,哈巴河县,青河县,吉木乃县|自治区直辖县级行政区划,石河子市,阿拉尔市,图木舒克市,五家渠市#香港特别行政区$香港,香港特别行政区#澳门特别行政区$澳门,澳门特别行政区#台湾省$台北市,中正区,大同区,中山区,松山区,大安区,万华区,信义区,士林区,北投区,内湖区,南港区,文山区|高雄市,新兴区,前金区,芩雅区,盐埕区,鼓山区,旗津区,前镇区,三民区,左营区,楠梓区,小港区|基隆市,仁爱区,信义区,中正区,中山区,安乐区,暖暖区,七堵区|台中市,中区,东区,南区,西区,北区,北屯区,西屯区,南屯区|台南市,中西区,东区,南区,北区,安平区,安南区|新竹市,东区,北区,香山区|嘉义市,东区,西区|县,台北县(板桥市),宜兰县(宜兰市),新竹县(竹北市),桃园县(桃园市),苗栗县(苗栗市),台中县(丰原市),彰化县(彰化市),南投县(南投市),嘉义县(太保市),云林县(斗六市),台南县(新营市),高雄县(凤山市),屏东县(屏东市),台东县(台东市),花莲县(花莲市),澎湖县(马公市)#其它$亚洲,阿富汗,巴林,孟加拉国,不丹,文莱,缅甸,塞浦路斯,印度,印度尼西亚,伊朗,伊拉克,日本,约旦,朝鲜,科威特,老挝,马尔代夫,黎巴嫩,马来西亚,以色列,蒙古,尼泊尔,阿曼,巴基斯坦,巴勒斯坦,菲律宾,沙特阿拉伯,新加坡,斯里兰卡,叙利亚,泰国,柬埔寨,土耳其,阿联酋,越南,也门,韩国,中国,中国香港,中国澳门,中国台湾|非洲,阿尔及利亚,安哥拉,厄里特里亚,法罗群鸟,加那利群岛(西)(拉斯帕尔马斯),贝宁,博茨瓦纳,布基纳法索,布隆迪,喀麦隆,加那利群岛(西)(圣克鲁斯),佛得角,中非,乍得,科摩罗,刚果,吉布提,埃及,埃塞俄比亚,赤道几内亚,加蓬,冈比亚,加纳,几内亚,南非,几内亚比绍,科特迪瓦,肯尼亚,莱索托,利比里亚,利比亚,马达加斯加,马拉维,马里,毛里塔尼亚,毛里求斯,摩洛哥,莫桑比克,尼日尔,尼日利亚,留尼旺岛,卢旺达,塞内加尔,塞舌尔,塞拉利昂,索马里,苏丹,斯威士兰,坦桑尼亚,圣赤勒拿,多哥,突尼斯,乌干达,扎伊尔,赞比亚,津巴布韦,纳米比亚,迪戈加西亚,桑给巴尔,马约特岛,圣多美和普林西比|欧洲,阿尔巴尼亚,安道尔,奥地利,比利时,保加利亚,捷克,丹麦,芬兰,法国,德国,直布罗陀(英),希腊,匈牙利,冰岛,爱尔兰,意大利,列支敦士登,斯洛伐克,卢森堡,马耳他,摩纳哥,荷兰,挪威,波兰,葡萄牙,马其顿,罗马尼亚,南斯拉夫,圣马力诺,西班牙,瑞典,瑞士,英国,科罗地亚,斯洛文尼亚,梵蒂冈,波斯尼亚和塞哥维那,俄罗斯联邦,亚美尼亚共和国,白俄罗斯共和国,格鲁吉亚共和国,哈萨克斯坦共和国,吉尔吉斯坦共和国,乌兹别克斯坦共和国,塔吉克斯坦共和国,土库曼斯坦共和国,乌克兰,立陶宛,拉脱维亚,爱沙尼亚,摩尔多瓦,阿塞拜疆|美洲,安圭拉岛,安提瓜和巴布达,阿根廷,阿鲁巴岛,阿森松,巴哈马,巴巴多斯,伯利兹,百慕大群岛,玻利维亚,巴西,加拿大,开曼群岛,智利,哥伦比亚,多米尼加联邦,哥斯达黎加,古巴,多米尼加共和国,厄瓜多尔,萨尔瓦多,法属圭亚那,格林纳达,危地马拉,圭亚那,海地,洪都拉斯,牙买加,马提尼克(法),墨西哥,蒙特塞拉特岛,荷属安的列斯群岛,尼加拉瓜,巴拿马,巴拉圭,秘鲁,波多黎哥,圣皮埃尔岛密克隆岛(法),圣克里斯托弗和尼维斯,圣卢西亚,福克兰群岛,维尔京群岛(英),圣文森特岛(英),维尔京群岛(美),苏里南,特立尼达和多巴哥,乌拉圭,美国,委内瑞拉,格陵兰岛,特克斯和凯科斯群岛,瓜多罗普|大洋洲,澳大利亚,科克群岛,斐济,法属波里尼西亚、塔希提,瓦努阿图,关岛,基里巴斯,马里亚纳群岛,中途岛,瑙鲁,新咯里多尼亚群岛,新西兰,巴布亚新几内亚,东萨摩亚,西萨摩亚,所罗门群岛,汤加,对诞岛,威克岛,科科斯岛,夏威夷,诺福克岛,帕劳,纽埃岛,图瓦卢,托克鲁,密克罗尼西亚,马绍尔群岛,瓦里斯加富士那群岛"; +SCT = "-城市-"; +SAT = "-地区-"; +ShowT = 0; //提示文字 0:不显示 1:显示 +PCAD = "北京市$市辖区,东城区,西城区,朝阳区,丰台区,石景山区,海淀区,门头沟区,房山区,通州区,顺义区,昌平区,大兴区,怀柔区,平谷区|市辖县,密云县,延庆县#天津市$市辖区,和平区,河东区,河西区,南开区,河北区,红桥区,东丽区,西青区,津南区,北辰区,武清区,宝坻区,滨海新区|市辖县,宁河县,静海县,蓟县#河北省$石家庄市,长安区,桥东区,桥西区,新华区,井陉矿区,裕华区,井陉县,正定县,栾城县,行唐县,灵寿县,高邑县,深泽县,赞皇县,无极县,平山县,元氏县,赵县,辛集市,藁城市,晋州市,新乐市,鹿泉市|唐山市,路南区,路北区,古冶区,开平区,丰南区,丰润区,滦县,滦南县,乐亭县,迁西县,玉田县,唐海县,遵化市,迁安市|秦皇岛市,海港区,山海关区,北戴河区,青龙满族自治县,昌黎县,抚宁县,卢龙县|邯郸市,邯山区,丛台区,复兴区,峰峰矿区,邯郸县,临漳县,成安县,大名县,涉县,磁县,肥乡县,永年县,邱县,鸡泽县,广平县,馆陶县,魏县,曲周县,武安市|邢台市,桥东区,桥西区,邢台县,临城县,内丘县,柏乡县,隆尧县,任县,南和县,宁晋县,巨鹿县,新河县,广宗县,平乡县,威县,清河县,临西县,南宫市,沙河市|保定市,新市区,北市区,南市区,满城县,清苑县,涞水县,阜平县,徐水县,定兴县,唐县,高阳县,容城县,涞源县,望都县,安新县,易县,曲阳县,蠡县,顺平县,博野县,雄县,涿州市,定州市,安国市,高碑店市|张家口市,桥东区,桥西区,宣化区,下花园区,宣化县,张北县,康保县,沽源县,尚义县,蔚县,阳原县,怀安县,万全县,怀来县,涿鹿县,赤城县,崇礼县|承德市,双桥区,双滦区,鹰手营子矿区,承德县,兴隆县,平泉县,滦平县,隆化县,丰宁满族自治县,宽城满族自治县,围场满族蒙古族自治县|沧州市,新华区,运河区,沧县,青县,东光县,海兴县,盐山县,肃宁县,南皮县,吴桥县,献县,孟村回族自治县,泊头市,任丘市,黄骅市,河间市|廊坊市,安次区,广阳区,固安县,永清县,香河县,大城县,文安县,大厂回族自治县,霸州市,三河市|衡水市,桃城区,枣强县,武邑县,武强县,饶阳县,安平县,故城县,景县,阜城县,冀州市,深州市#山西省$太原市,小店区,迎泽区,杏花岭区,尖草坪区,万柏林区,晋源区,清徐县,阳曲县,娄烦县,古交市|大同市,城区,矿区,南郊区,新荣区,阳高县,天镇县,广灵县,灵丘县,浑源县,左云县,大同县|阳泉市,城区,矿区,郊区,平定县,盂县|长治市,城区,郊区,长治县,襄垣县,屯留县,平顺县,黎城县,壶关县,长子县,武乡县,沁县,沁源县,潞城市|晋城市,城区,沁水县,阳城县,陵川县,泽州县,高平市|朔州市,朔城区,平鲁区,山阴县,应县,右玉县,怀仁县|晋中市,榆次区,榆社县,左权县,和顺县,昔阳县,寿阳县,太谷县,祁县,平遥县,灵石县,介休市|运城市,盐湖区,临猗县,万荣县,闻喜县,稷山县,新绛县,绛县,垣曲县,夏县,平陆县,芮城县,永济市,河津市|忻州市,忻府区,定襄县,五台县,代县,繁峙县,宁武县,静乐县,神池县,五寨县,岢岚县,河曲县,保德县,偏关县,原平市|临汾市,尧都区,曲沃县,翼城县,襄汾县,洪洞县,古县,安泽县,浮山县,吉县,乡宁县,大宁县,隰县,永和县,蒲县,汾西县,侯马市,霍州市|吕梁市,离石区,文水县,交城县,兴县,临县,柳林县,石楼县,岚县,方山县,中阳县,交口县,孝义市,汾阳市#内蒙古自治区$呼和浩特市,新城区,回民区,玉泉区,赛罕区,土默特左旗,托克托县,和林格尔县,清水河县,武川县|包头市,东河区,昆都仑区,青山区,石拐区,白云鄂博矿区,九原区,土默特右旗,固阳县,达尔罕茂明安联合旗|乌海市,海勃湾区,海南区,乌达区|赤峰市,红山区,元宝山区,松山区,阿鲁科尔沁旗,巴林左旗,巴林右旗,林西县,克什克腾旗,翁牛特旗,喀喇沁旗,宁城县,敖汉旗|通辽市,科尔沁区,科尔沁左翼中旗,科尔沁左翼后旗,开鲁县,库伦旗,奈曼旗,扎鲁特旗,霍林郭勒市|鄂尔多斯市,东胜区,达拉特旗,准格尔旗,鄂托克前旗,鄂托克旗,杭锦旗,乌审旗,伊金霍洛旗|呼伦贝尔市,海拉尔区,阿荣旗,莫力达瓦达斡尔族自治旗,鄂伦春自治旗,鄂温克族自治旗,陈巴尔虎旗,新巴尔虎左旗,新巴尔虎右旗,满洲里市,牙克石市,扎兰屯市,额尔古纳市,根河市|巴彦淖尔市,临河区,五原县,磴口县,乌拉特前旗,乌拉特中旗,乌拉特后旗,杭锦后旗|乌兰察布市,集宁区,卓资县,化德县,商都县,兴和县,凉城县,察哈尔右翼前旗,察哈尔右翼中旗,察哈尔右翼后旗,四子王旗,丰镇市|兴安盟,乌兰浩特市,阿尔山市,科尔沁右翼前旗,科尔沁右翼中旗,扎赉特旗,突泉县|锡林郭勒盟,二连浩特市,锡林浩特市,阿巴嘎旗,苏尼特左旗,苏尼特右旗,东乌珠穆沁旗,西乌珠穆沁旗,太仆寺旗,镶黄旗,正镶白旗,正蓝旗,多伦县|阿拉善盟,阿拉善左旗,阿拉善右旗,额济纳旗#辽宁省$沈阳市,和平区,沈河区,大东区,皇姑区,铁西区,苏家屯区,东陵区,沈北新区,于洪区,辽中县,康平县,法库县,新民市|大连市,中山区,西岗区,沙河口区,甘井子区,旅顺口区,金州区,长海县,瓦房店市,普兰店市,庄河市|鞍山市,铁东区,铁西区,立山区,千山区,台安县,岫岩满族自治县,海城市|抚顺市,新抚区,东洲区,望花区,顺城区,抚顺县,新宾满族自治县,清原满族自治县|本溪市,平山区,溪湖区,明山区,南芬区,本溪满族自治县,桓仁满族自治县|丹东市,元宝区,振兴区,振安区,宽甸满族自治县,东港市,凤城市|锦州市,古塔区,凌河区,太和区,黑山县,义县,凌海市,北镇市|营口市,站前区,西市区,鲅鱼圈区,老边区,盖州市,大石桥市|阜新市,海州区,新邱区,太平区,清河门区,细河区,阜新蒙古族自治县,彰武县|辽阳市,白塔区,文圣区,宏伟区,弓长岭区,太子河区,辽阳县,灯塔市|盘锦市,双台子区,兴隆台区,大洼县,盘山县|铁岭市,银州区,清河区,铁岭县,西丰县,昌图县,调兵山市,开原市|朝阳市,双塔区,龙城区,朝阳县,建平县,喀喇沁左翼蒙古族自治县,北票市,凌源市|葫芦岛市,连山区,龙港区,南票区,绥中县,建昌县,兴城市#吉林省$长春市,南关区,宽城区,朝阳区,二道区,绿园区,双阳区,农安县,九台市,榆树市,德惠市|吉林市,昌邑区,龙潭区,船营区,丰满区,永吉县,蛟河市,桦甸市,舒兰市,磐石市|四平市,铁西区,铁东区,梨树县,伊通满族自治县,公主岭市,双辽市|辽源市,龙山区,西安区,东丰县,东辽县|通化市,东昌区,二道江区,通化县,辉南县,柳河县,梅河口市,集安市|白山市,八道江区,江源区,抚松县,靖宇县,长白朝鲜族自治县,临江市|松原市,宁江区,前郭尔罗斯蒙古族自治县,长岭县,乾安县,扶余县|白城市,洮北区,镇赉县,通榆县,洮南市,大安市|延边朝鲜族自治州,延吉市,图们市,敦化市,珲春市,龙井市,和龙市,汪清县,安图县#黑龙江省$哈尔滨市,道里区,南岗区,道外区,平房区,松北区,香坊区,呼兰区,阿城区,依兰县,方正县,宾县,巴彦县,木兰县,通河县,延寿县,双城市,尚志市,五常市|齐齐哈尔市,龙沙区,建华区,铁锋区,昂昂溪区,富拉尔基区,碾子山区,梅里斯达斡尔族区,龙江县,依安县,泰来县,甘南县,富裕县,克山县,克东县,拜泉县,讷河市|鸡西市,鸡冠区,恒山区,滴道区,梨树区,城子河区,麻山区,鸡东县,虎林市,密山市|鹤岗市,向阳区,工农区,南山区,兴安区,东山区,兴山区,萝北县,绥滨县|双鸭山市,尖山区,岭东区,四方台区,宝山区,集贤县,友谊县,宝清县,饶河县|大庆市,萨尔图区,龙凤区,让胡路区,红岗区,大同区,肇州县,肇源县,林甸县,杜尔伯特蒙古族自治县|伊春市,伊春区,南岔区,友好区,西林区,翠峦区,新青区,美溪区,金山屯区,五营区,乌马河区,汤旺河区,带岭区,乌伊岭区,红星区,上甘岭区,嘉荫县,铁力市|佳木斯市,向阳区,前进区,东风区,郊区,桦南县,桦川县,汤原县,抚远县,同江市,富锦市|七台河市,新兴区,桃山区,茄子河区,勃利县|牡丹江市,东安区,阳明区,爱民区,西安区,东宁县,林口县,绥芬河市,海林市,宁安市,穆棱市|黑河市,爱辉区,嫩江县,逊克县,孙吴县,北安市,五大连池市|绥化市,北林区,望奎县,兰西县,青冈县,庆安县,明水县,绥棱县,安达市,肇东市,海伦市|大兴安岭地区,呼玛县,塔河县,漠河县#上海市$黄浦区,徐汇区,长宁区,静安区,普陀区,闸北区,虹口区,杨浦区,闵行区,宝山区,嘉定区,浦东新区,金山区,松江区,青浦区,奉贤区|市辖县,崇明县#江苏省$南京市,玄武区,白下区,秦淮区,建邺区,鼓楼区,下关区,浦口区,栖霞区,雨花台区,江宁区,六合区,溧水县,高淳县|无锡市,崇安区,南长区,北塘区,锡山区,惠山区,滨湖区,江阴市,宜兴市|徐州市,鼓楼区,云龙区,贾汪区,泉山区,铜山区,丰县,沛县,睢宁县,新沂市,邳州市|常州市,天宁区,钟楼区,戚墅堰区,新北区,武进区,溧阳市,金坛市|苏州市,沧浪区,平江区,金阊区,虎丘区,吴中区,相城区,常熟市,张家港市,昆山市,吴江市,太仓市|南通市,崇川区,港闸区,通州区,海安县,如东县,启东市,如皋市,海门市|连云港市,连云区,新浦区,海州区,赣榆县,东海县,灌云县,灌南县|淮安市,清河区,楚州区,淮阴区,清浦区,涟水县,洪泽县,盱眙县,金湖县|盐城市,亭湖区,盐都区,响水县,滨海县,阜宁县,射阳县,建湖县,东台市,大丰市|扬州市,广陵区,邗江区,江都区,宝应县,仪征市,高邮市|镇江市,京口区,润州区,丹徒区,丹阳市,扬中市,句容市|泰州市,海陵区,高港区,兴化市,靖江市,泰兴市,姜堰市|宿迁市,宿城区,宿豫区,沭阳县,泗阳县,泗洪县#浙江省$杭州市,上城区,下城区,江干区,拱墅区,西湖区,滨江区,萧山区,余杭区,桐庐县,淳安县,建德市,富阳市,临安市|宁波市,海曙区,江东区,江北区,北仑区,镇海区,鄞州区,象山县,宁海县,余姚市,慈溪市,奉化市|温州市,鹿城区,龙湾区,瓯海区,洞头县,永嘉县,平阳县,苍南县,文成县,泰顺县,瑞安市,乐清市|嘉兴市,南湖区,秀洲区,嘉善县,海盐县,海宁市,平湖市,桐乡市|湖州市,吴兴区,南浔区,德清县,长兴县,安吉县|绍兴市,越城区,绍兴县,新昌县,诸暨市,上虞市,嵊州市|金华市,婺城区,金东区,武义县,浦江县,磐安县,兰溪市,义乌市,东阳市,永康市|衢州市,柯城区,衢江区,常山县,开化县,龙游县,江山市|舟山市,定海区,普陀区,岱山县,嵊泗县|台州市,椒江区,黄岩区,路桥区,玉环县,三门县,天台县,仙居县,温岭市,临海市|丽水市,莲都区,青田县,缙云县,遂昌县,松阳县,云和县,庆元县,景宁畲族自治县,龙泉市#安徽省$合肥市,瑶海区,庐阳区,蜀山区,包河区,长丰县,肥东县,肥西县,庐江县,巢湖市|芜湖市,镜湖区,弋江区,鸠江区,三山区,芜湖县,繁昌县,南陵县,无为县|蚌埠市,龙子湖区,蚌山区,禹会区,淮上区,怀远县,五河县,固镇县|淮南市,大通区,田家庵区,谢家集区,八公山区,潘集区,凤台县|马鞍山市,金家庄区,花山区,雨山区,当涂县,含山县,和县|淮北市,杜集区,相山区,烈山区,濉溪县|铜陵市,铜官山区,狮子山区,郊区,铜陵县|安庆市,迎江区,大观区,宜秀区,怀宁县,枞阳县,潜山县,太湖县,宿松县,望江县,岳西县,桐城市|黄山市,屯溪区,黄山区,徽州区,歙县,休宁县,黟县,祁门县|滁州市,琅琊区,南谯区,来安县,全椒县,定远县,凤阳县,天长市,明光市|阜阳市,颍州区,颍东区,颍泉区,临泉县,太和县,阜南县,颍上县,界首市|宿州市,埇桥区,砀山县,萧县,灵璧县,泗县|六安市,金安区,裕安区,寿县,霍邱县,舒城县,金寨县,霍山县|亳州市,谯城区,涡阳县,蒙城县,利辛县|池州市,贵池区,东至县,石台县,青阳县|宣城市,宣州区,郎溪县,广德县,泾县,绩溪县,旌德县,宁国市#福建省$福州市,鼓楼区,台江区,仓山区,马尾区,晋安区,闽侯县,连江县,罗源县,闽清县,永泰县,平潭县,福清市,长乐市|厦门市,思明区,海沧区,湖里区,集美区,同安区,翔安区|莆田市,城厢区,涵江区,荔城区,秀屿区,仙游县|三明市,梅列区,三元区,明溪县,清流县,宁化县,大田县,尤溪县,沙县,将乐县,泰宁县,建宁县,永安市|泉州市,鲤城区,丰泽区,洛江区,泉港区,惠安县,安溪县,永春县,德化县,金门县,石狮市,晋江市,南安市|漳州市,芗城区,龙文区,云霄县,漳浦县,诏安县,长泰县,东山县,南靖县,平和县,华安县,龙海市|南平市,延平区,顺昌县,浦城县,光泽县,松溪县,政和县,邵武市,武夷山市,建瓯市,建阳市|龙岩市,新罗区,长汀县,永定县,上杭县,武平县,连城县,漳平市|宁德市,蕉城区,霞浦县,古田县,屏南县,寿宁县,周宁县,柘荣县,福安市,福鼎市#江西省$南昌市,东湖区,西湖区,青云谱区,湾里区,青山湖区,南昌县,新建县,安义县,进贤县|景德镇市,昌江区,珠山区,浮梁县,乐平市|萍乡市,安源区,湘东区,莲花县,上栗县,芦溪县|九江市,庐山区,浔阳区,九江县,武宁县,修水县,永修县,德安县,星子县,都昌县,湖口县,彭泽县,瑞昌市,共青城市|新余市,渝水区,分宜县|鹰潭市,月湖区,余江县,贵溪市|赣州市,章贡区,赣县,信丰县,大余县,上犹县,崇义县,安远县,龙南县,定南县,全南县,宁都县,于都县,兴国县,会昌县,寻乌县,石城县,瑞金市,南康市|吉安市,吉州区,青原区,吉安县,吉水县,峡江县,新干县,永丰县,泰和县,遂川县,万安县,安福县,永新县,井冈山市|宜春市,袁州区,奉新县,万载县,上高县,宜丰县,靖安县,铜鼓县,丰城市,樟树市,高安市|抚州市,临川区,南城县,黎川县,南丰县,崇仁县,乐安县,宜黄县,金溪县,资溪县,东乡县,广昌县|上饶市,信州区,上饶县,广丰县,玉山县,铅山县,横峰县,弋阳县,余干县,鄱阳县,万年县,婺源县,德兴市#山东省$济南市,历下区,市中区,槐荫区,天桥区,历城区,长清区,平阴县,济阳县,商河县,章丘市|青岛市,市南区,市北区,四方区,黄岛区,崂山区,李沧区,城阳区,胶州市,即墨市,平度市,胶南市,莱西市|淄博市,淄川区,张店区,博山区,临淄区,周村区,桓台县,高青县,沂源县|枣庄市,市中区,薛城区,峄城区,台儿庄区,山亭区,滕州市|东营市,东营区,河口区,垦利县,利津县,广饶县|烟台市,芝罘区,福山区,牟平区,莱山区,长岛县,龙口市,莱阳市,莱州市,蓬莱市,招远市,栖霞市,海阳市|潍坊市,潍城区,寒亭区,坊子区,奎文区,临朐县,昌乐县,青州市,诸城市,寿光市,安丘市,高密市,昌邑市|济宁市,市中区,任城区,微山县,鱼台县,金乡县,嘉祥县,汶上县,泗水县,梁山县,曲阜市,兖州市,邹城市|泰安市,泰山区,岱岳区,宁阳县,东平县,新泰市,肥城市|威海市,环翠区,文登市,荣成市,乳山市|日照市,东港区,岚山区,五莲县,莒县|莱芜市,莱城区,钢城区|临沂市,兰山区,罗庄区,河东区,沂南县,郯城县,沂水县,苍山县,费县,平邑县,莒南县,蒙阴县,临沭县|德州市,德城区,陵县,宁津县,庆云县,临邑县,齐河县,平原县,夏津县,武城县,乐陵市,禹城市|聊城市,东昌府区,阳谷县,莘县,茌平县,东阿县,冠县,高唐县,临清市|滨州市,滨城区,惠民县,阳信县,无棣县,沾化县,博兴县,邹平县|菏泽市,牡丹区,曹县,单县,成武县,巨野县,郓城县,鄄城县,定陶县,东明县#河南省$郑州市,中原区,二七区,管城回族区,金水区,上街区,惠济区,中牟县,巩义市,荥阳市,新密市,新郑市,登封市|开封市,龙亭区,顺河回族区,鼓楼区,禹王台区,金明区,杞县,通许县,尉氏县,开封县,兰考县|洛阳市,老城区,西工区,瀍河回族区,涧西区,吉利区,洛龙区,孟津县,新安县,栾川县,嵩县,汝阳县,宜阳县,洛宁县,伊川县,偃师市|平顶山市,新华区,卫东区,石龙区,湛河区,宝丰县,叶县,鲁山县,郏县,舞钢市,汝州市|安阳市,文峰区,北关区,殷都区,龙安区,安阳县,汤阴县,滑县,内黄县,林州市|鹤壁市,鹤山区,山城区,淇滨区,浚县,淇县|新乡市,红旗区,卫滨区,凤泉区,牧野区,新乡县,获嘉县,原阳县,延津县,封丘县,长垣县,卫辉市,辉县市|焦作市,解放区,中站区,马村区,山阳区,修武县,博爱县,武陟县,温县,沁阳市,孟州市|濮阳市,华龙区,清丰县,南乐县,范县,台前县,濮阳县|许昌市,魏都区,许昌县,鄢陵县,襄城县,禹州市,长葛市|漯河市,源汇区,郾城区,召陵区,舞阳县,临颍县|三门峡市,湖滨区,渑池县,陕县,卢氏县,义马市,灵宝市|南阳市,宛城区,卧龙区,南召县,方城县,西峡县,镇平县,内乡县,淅川县,社旗县,唐河县,新野县,桐柏县,邓州市|商丘市,梁园区,睢阳区,民权县,睢县,宁陵县,柘城县,虞城县,夏邑县,永城市|信阳市,浉河区,平桥区,罗山县,光山县,新县,商城县,固始县,潢川县,淮滨县,息县|周口市,川汇区,扶沟县,西华县,商水县,沈丘县,郸城县,淮阳县,太康县,鹿邑县,项城市|驻马店市,驿城区,西平县,上蔡县,平舆县,正阳县,确山县,泌阳县,汝南县,遂平县,新蔡县|省直辖县级行政区划,济源市#湖北省$武汉市,江岸区,江汉区,硚口区,汉阳区,武昌区,青山区,洪山区,东西湖区,汉南区,蔡甸区,江夏区,黄陂区,新洲区|黄石市,黄石港区,西塞山区,下陆区,铁山区,阳新县,大冶市|十堰市,茅箭区,张湾区,郧县,郧西县,竹山县,竹溪县,房县,丹江口市|宜昌市,西陵区,伍家岗区,点军区,猇亭区,夷陵区,远安县,兴山县,秭归县,长阳土家族自治县,五峰土家族自治县,宜都市,当阳市,枝江市|襄阳市,襄城区,樊城区,襄州区,南漳县,谷城县,保康县,老河口市,枣阳市,宜城市|鄂州市,梁子湖区,华容区,鄂城区|荆门市,东宝区,掇刀区,京山县,沙洋县,钟祥市|孝感市,孝南区,孝昌县,大悟县,云梦县,应城市,安陆市,汉川市|荆州市,沙市区,荆州区,公安县,监利县,江陵县,石首市,洪湖市,松滋市|黄冈市,黄州区,团风县,红安县,罗田县,英山县,浠水县,蕲春县,黄梅县,麻城市,武穴市|咸宁市,咸安区,嘉鱼县,通城县,崇阳县,通山县,赤壁市|随州市,曾都区,随县,广水市|恩施土家族苗族自治州,恩施市,利川市,建始县,巴东县,宣恩县,咸丰县,来凤县,鹤峰县|省直辖县级行政区划,仙桃市,潜江市,天门市,神农架林区#湖南省$长沙市,芙蓉区,天心区,岳麓区,开福区,雨花区,望城区,长沙县,宁乡县,浏阳市|株洲市,荷塘区,芦淞区,石峰区,天元区,株洲县,攸县,茶陵县,炎陵县,醴陵市|湘潭市,雨湖区,岳塘区,湘潭县,湘乡市,韶山市|衡阳市,珠晖区,雁峰区,石鼓区,蒸湘区,南岳区,衡阳县,衡南县,衡山县,衡东县,祁东县,耒阳市,常宁市|邵阳市,双清区,大祥区,北塔区,邵东县,新邵县,邵阳县,隆回县,洞口县,绥宁县,新宁县,城步苗族自治县,武冈市|岳阳市,岳阳楼区,云溪区,君山区,岳阳县,华容县,湘阴县,平江县,汨罗市,临湘市|常德市,武陵区,鼎城区,安乡县,汉寿县,澧县,临澧县,桃源县,石门县,津市市|张家界市,永定区,武陵源区,慈利县,桑植县|益阳市,资阳区,赫山区,南县,桃江县,安化县,沅江市|郴州市,北湖区,苏仙区,桂阳县,宜章县,永兴县,嘉禾县,临武县,汝城县,桂东县,安仁县,资兴市|永州市,零陵区,冷水滩区,祁阳县,东安县,双牌县,道县,江永县,宁远县,蓝山县,新田县,江华瑶族自治县|怀化市,鹤城区,中方县,沅陵县,辰溪县,溆浦县,会同县,麻阳苗族自治县,新晃侗族自治县,芷江侗族自治县,靖州苗族侗族自治县,通道侗族自治县,洪江市|娄底市,娄星区,双峰县,新化县,冷水江市,涟源市|湘西土家族苗族自治州,吉首市,泸溪县,凤凰县,花垣县,保靖县,古丈县,永顺县,龙山县#广东省$广州市,荔湾区,越秀区,海珠区,天河区,白云区,黄埔区,番禺区,花都区,南沙区,萝岗区,增城市,从化市|韶关市,武江区,浈江区,曲江区,始兴县,仁化县,翁源县,乳源瑶族自治县,新丰县,乐昌市,南雄市|深圳市,罗湖区,福田区,南山区,宝安区,龙岗区,盐田区|珠海市,香洲区,斗门区,金湾区|汕头市,龙湖区,金平区,濠江区,潮阳区,潮南区,澄海区,南澳县|佛山市,禅城区,南海区,顺德区,三水区,高明区|江门市,蓬江区,江海区,新会区,台山市,开平市,鹤山市,恩平市|湛江市,赤坎区,霞山区,坡头区,麻章区,遂溪县,徐闻县,廉江市,雷州市,吴川市|茂名市,茂南区,茂港区,电白县,高州市,化州市,信宜市|肇庆市,端州区,鼎湖区,广宁县,怀集县,封开县,德庆县,高要市,四会市|惠州市,惠城区,惠阳区,博罗县,惠东县,龙门县|梅州市,梅江区,梅县,大埔县,丰顺县,五华县,平远县,蕉岭县,兴宁市|汕尾市,城区,海丰县,陆河县,陆丰市|河源市,源城区,紫金县,龙川县,连平县,和平县,东源县|阳江市,江城区,阳西县,阳东县,阳春市|清远市,清城区,佛冈县,阳山县,连山壮族瑶族自治县,连南瑶族自治县,清新县,英德市,连州市|东莞市|中山市|潮州市,湘桥区,潮安县,饶平县|揭阳市,榕城区,揭东县,揭西县,惠来县,普宁市|云浮市,云城区,新兴县,郁南县,云安县,罗定市#广西壮族自治区$南宁市,兴宁区,青秀区,江南区,西乡塘区,良庆区,邕宁区,武鸣县,隆安县,马山县,上林县,宾阳县,横县|柳州市,城中区,鱼峰区,柳南区,柳北区,柳江县,柳城县,鹿寨县,融安县,融水苗族自治县,三江侗族自治县|桂林市,秀峰区,叠彩区,象山区,七星区,雁山区,阳朔县,临桂县,灵川县,全州县,兴安县,永福县,灌阳县,龙胜各族自治县,资源县,平乐县,荔蒲县,恭城瑶族自治县|梧州市,万秀区,蝶山区,长洲区,苍梧县,藤县,蒙山县,岑溪市|北海市,海城区,银海区,铁山港区,合浦县|防城港市,港口区,防城区,上思县,东兴市|钦州市,钦南区,钦北区,灵山县,浦北县|贵港市,港北区,港南区,覃塘区,平南县,桂平市|玉林市,玉州区,容县,陆川县,博白县,兴业县,北流市|百色市,右江区,田阳县,田东县,平果县,德保县,靖西县,那坡县,凌云县,乐业县,田林县,西林县,隆林各族自治县|贺州市,八步区,昭平县,钟山县,富川瑶族自治县|河池市,金城江区,南丹县,天峨县,凤山县,东兰县,罗城仫佬族自治县,环江毛南族自治县,巴马瑶族自治县,都安瑶族自治县,大化瑶族自治县,宜州市|来宾市,兴宾区,忻城县,象州县,武宣县,金秀瑶族自治县,合山市|崇左市,江洲区,扶绥县,宁明县,龙州县,大新县,天等县,凭祥市#海南省$海口市,秀英区,龙华区,琼山区,美兰区|三亚市,市辖区|省直辖县级行政区划,五指山市,琼海市,儋州市,文昌市,万宁市,东方市,定安县,屯昌县,澄迈县,临高县,白沙黎族自治县,昌江黎族自治县,乐东黎族自治县,陵水黎族自治县,保亭黎族苗族自治县,琼中黎族苗族自治县,西沙群岛,南沙群岛,中沙群岛的岛礁及其海域#重庆市$万州区,涪陵区,渝中区,大渡口区,江北区,沙坪坝区,九龙坡区,南岸区,北碚区,綦江区,大足区,渝北区,巴南区,黔江区,长寿区,江津区,合川区,永川区,南川区|市辖县,潼南县,铜梁县,荣昌县,璧山县,梁平县,城口县,丰都县,垫江县,武隆县,忠县,开县,云阳县,奉节县,巫山县,巫溪县,石柱土家族自治县,秀山土家族苗族自治县,酉阳土家族苗族自治县,彭水苗族土家族自治县#四川省$成都市,锦江区,青羊区,金牛区,武侯区,成华区,龙泉驿区,青白江区,新都区,温江区,金堂县,双流县,郫县,大邑县,蒲江县,新津县,都江堰市,彭州市,邛崃市,崇州市|自贡市,自流井区,贡井区,大安区,沿滩区,荣县,富顺县|攀枝花市,东区,西区,仁和区,米易县,盐边县|泸州市,江阳区,纳溪区,龙马潭区,泸县,合江县,叙永县,古蔺县|德阳市,旌阳区,中江县,罗江县,广汉市,什邡市,绵竹市|绵阳市,涪城区,游仙区,三台县,盐亭县,安县,梓潼县,北川羌族自治县,平武县,江油市|广元市,利州区,元坝区,朝天区,旺苍县,青川县,剑阁县,苍溪县|遂宁市,船山区,安居区,蓬溪县,射洪县,大英县|内江市,市中区,东兴区,威远县,资中县,隆昌县|乐山市,市中区,沙湾区,五通桥区,金口河区,犍为县,井研县,夹江县,沐川县,峨边彝族自治县,马边彝族自治县,峨眉山市|南充市,顺庆区,高坪区,嘉陵区,南部县,营山县,蓬安县,仪陇县,西充县,阆中市|眉山市,东坡区,仁寿县,彭山县,洪雅县,丹棱县,青神县|宜宾市,翠屏区,南溪区,宜宾县,江安县,长宁县,高县,珙县,筠连县,兴文县,屏山县|广安市,广安区,岳池县,武胜县,邻水县,华蓥市|达州市,通川区,达县,宣汉县,开江县,大竹县,渠县,万源市|雅安市,雨城区,名山县,荥经县,汉源县,石棉县,天全县,芦山县,宝兴县|巴中市,巴州区,通江县,南江县,平昌县|资阳市,雁江区,安岳县,乐至县,简阳市|阿坝藏族羌族自治州,汶川县,理县,茂县,松潘县,九寨沟县,金川县,小金县,黑水县,马尔康县,壤塘县,阿坝县,若尔盖县,红原县|甘孜藏族自治州,康定县,泸定县,丹巴县,九龙县,雅江县,道孚县,炉霍县,甘孜县,新龙县,德格县,白玉县,石渠县,色达县,理塘县,巴塘县,乡城县,稻城县,得荣县|凉山彝族自治州,西昌市,木里藏族自治县,盐源县,德昌县,会理县,会东县,宁南县,普格县,布拖县,金阳县,昭觉县,喜德县,冕宁县,越西县,甘洛县,美姑县,雷波县#贵州省$贵阳市,南明区,云岩区,花溪区,乌当区,白云区,小河区,开阳县,息烽县,修文县,清镇市|六盘水市,钟山区,六枝特区,水城县,盘县|遵义市,红花岗区,汇川区,遵义县,桐梓县,绥阳县,正安县,道真仡佬族苗族自治县,务川仡佬族苗族自治县,凤冈县,湄潭县,余庆县,习水县,赤水市,仁怀市|安顺市,西秀区,平坝县,普定县,镇宁布依族苗族自治县,关岭布依族苗族自治县,紫云苗族布依族自治县|毕节市,七星关区,大方县,黔西县,金沙县,织金县,纳雍县,威宁彝族回族苗族自治县,赫章县|铜仁市,碧江区,万山区,江口县,玉屏侗族自治县,石阡县,思南县,印江土家族苗族自治县,德江县,沿河土家族自治县,松桃苗族自治县|黔西南布依族苗族自治州,兴义市,兴仁县,普安县,晴隆县,贞丰县,望谟县,册亨县,安龙县|黔东南苗族侗族自治州,凯里市,黄平县,施秉县,三穗县,镇远县,岑巩县,天柱县,锦屏县,剑河县,台江县,黎平县,榕江县,从江县,雷山县,麻江县,丹寨县|黔南布依族苗族自治州,都匀市,福泉市,荔波县,贵定县,瓮安县,独山县,平塘县,罗甸县,长顺县,龙里县,惠水县,三都水族自治县#云南省$昆明市,五华区,盘龙区,官渡区,西山区,东川区,呈贡区,晋宁县,富民县,宜良县,石林彝族自治县,嵩明县,禄劝彝族苗族自治县,寻甸回族彝族自治县,安宁市|曲靖市,麒麟区,马龙县,陆良县,师宗县,罗平县,富源县,会泽县,沾益县,宣威市|玉溪市,红塔区,江川县,澄江县,通海县,华宁县,易门县,峨山彝族自治县,新平彝族傣族自治县,元江哈尼族彝族傣族自治县|保山市,隆阳区,施甸县,腾冲县,龙陵县,昌宁县|昭通市,昭阳区,鲁甸县,巧家县,盐津县,大关县,永善县,绥江县,镇雄县,彝良县,威信县,水富县|丽江市,古城区,玉龙纳西族自治县,永胜县,华坪县,宁蒗彝族自治县|普洱市,思茅区,宁洱哈尼族彝族自治县,墨江哈尼族自治县,景东彝族自治县,景谷傣族彝族自治县,镇沅彝族哈尼族拉祜族自治县,江城哈尼族彝族自治县,孟连傣族拉祜族佤族自治县,澜沧拉祜族自治县,西盟佤族自治县|临沧市,临翔区,凤庆县,云县,永德县,镇康县,双江拉祜族佤族布朗族傣族自治县,耿马傣族佤族自治县,沧源佤族自治县|楚雄彝族自治州,楚雄市,双柏县,牟定县,南华县,姚安县,大姚县,永仁县,元谋县,武定县,禄丰县|红河哈尼族彝族自治州,个旧市,开远市,蒙自市,屏边苗族自治县,建水县,石屏县,弥勒县,泸西县,元阳县,红河县,金平苗族瑶族傣族自治县,绿春县,河口瑶族自治县|文山壮族苗族自治州,文山市,砚山县,西畴县,麻栗坡县,马关县,丘北县,广南县,富宁县|西双版纳傣族自治州,景洪市,勐海县,勐腊县|大理白族自治州,大理市,漾濞彝族自治县,祥云县,宾川县,弥渡县,南涧彝族自治县,巍山彝族回族自治县,永平县,云龙县,洱源县,剑川县,鹤庆县|德宏傣族景颇族自治州,瑞丽市,芒市,梁河县,盈江县,陇川县|怒江傈僳族自治州,泸水县,福贡县,贡山独龙族怒族自治县,兰坪白族普米族自治县|迪庆藏族自治州,香格里拉县,德钦县,维西傈僳族自治县#西藏自治区$拉萨市,城关区,林周县,当雄县,尼木县,曲水县,堆龙德庆县,达孜县,墨竹工卡县|昌都地区,昌都县,江达县,贡觉县,类乌齐县,丁青县,察雅县,八宿县,左贡县,芒康县,洛隆县,边坝县|山南地区,乃东县,扎囊县,贡嘎县,桑日县,琼结县,曲松县,措美县,洛扎县,加查县,隆子县,错那县,浪卡子县|日喀则地区,日喀则市,南木林县,江孜县,定日县,萨迦县,拉孜县,昂仁县,谢通门县,白朗县,仁布县,康马县,定结县,仲巴县,亚东县,吉隆县,聂拉木县,萨嘎县,岗巴县|那曲地区,那曲县,嘉黎县,比如县,聂荣县,安多县,申扎县,索县,班戈县,巴青县,尼玛县|阿里地区,普兰县,札达县,噶尔县,日土县,革吉县,改则县,措勤县|林芝地区,林芝县,工布江达县,米林县,墨脱县,波密县,察隅县,朗县#陕西省$西安市,新城区,碑林区,莲湖区,灞桥区,未央区,雁塔区,阎良区,临潼区,长安区,蓝田县,周至县,户县,高陵县|铜川市,王益区,印台区,耀州区,宜君县|宝鸡市,渭滨区,金台区,陈仓区,凤翔县,岐山县,扶风县,眉县,陇县,千阳县,麟游县,凤县,太白县|咸阳市,秦都区,杨陵区,渭城区,三原县,泾阳县,乾县,礼泉县,永寿县,彬县,长武县,旬邑县,淳化县,武功县,兴平市|渭南市,临渭区,华县,潼关县,大荔县,合阳县,澄城县,蒲城县,白水县,富平县,韩城市,华阴市|延安市,宝塔区,延长县,延川县,子长县,安塞县,志丹县,吴起县,甘泉县,富县,洛川县,宜川县,黄龙县,黄陵县|汉中市,汉台区,南郑县,城固县,洋县,西乡县,勉县,宁强县,略阳县,镇巴县,留坝县,佛坪县|榆林市,榆阳区,神木县,府谷县,横山县,靖边县,定边县,绥德县,米脂县,佳县,吴堡县,清涧县,子洲县|安康市,汉滨区,汉阴县,石泉县,宁陕县,紫阳县,岚皋县,平利县,镇坪县,旬阳县,白河县|商洛市,商州区,洛南县,丹凤县,商南县,山阳县,镇安县,柞水县#甘肃省$兰州市,城关区,七里河区,西固区,安宁区,红古区,永登县,皋兰县,榆中县|嘉峪关市,市辖区|金昌市,金川区,永昌县|白银市,白银区,平川区,靖远县,会宁县,景泰县|天水市,秦州区,麦积区,清水县,秦安县,甘谷县,武山县,张家川回族自治县|武威市,凉州区,民勤县,古浪县,天祝藏族自治县|张掖市,甘州区,肃南裕固族自治县,民乐县,临泽县,高台县,山丹县|平凉市,崆峒区,泾川县,灵台县,崇信县,华亭县,庄浪县,静宁县|酒泉市,肃州区,金塔县,瓜州县,肃北蒙古族自治县,阿克塞哈萨克族自治县,玉门市,敦煌市|庆阳市,西峰区,庆城县,环县,华池县,合水县,正宁县,宁县,镇原县|定西市,安定区,通渭县,陇西县,渭源县,临洮县,漳县,岷县|陇南市,武都区,成县,文县,宕昌县,康县,西和县,礼县,徽县,两当县|临夏回族自治州,临夏市,临夏县,康乐县,永靖县,广河县,和政县,东乡族自治县,积石山保安族东乡族撒拉族自治县|甘南藏族自治州,合作市,临潭县,卓尼县,舟曲县,迭部县,玛曲县,碌曲县,夏河县#青海省$西宁市,城东区,城中区,城西区,城北区,大通回族土族自治县,湟中县,湟源县|海东地区,平安县,民和回族土族自治县,乐都县,互助土族自治县,化隆回族自治县,循化撒拉族自治县|海北藏族自治州,门源回族自治县,祁连县,海晏县,刚察县|黄南藏族自治州,同仁县,尖扎县,泽库县,河南蒙古族自治县|海南藏族自治州,共和县,同德县,贵德县,兴海县,贵南县|果洛藏族自治州,玛沁县,班玛县,甘德县,达日县,久治县,玛多县|玉树藏族自治州,玉树县,杂多县,称多县,治多县,囊谦县,曲麻莱县|海西蒙古族藏族自治州,格尔木市,德令哈市,乌兰县,都兰县,天峻县#宁夏回族自治区$银川市,兴庆区,西夏区,金凤区,永宁县,贺兰县,灵武市|石嘴山市,大武口区,惠农区,平罗县|吴忠市,利通区,红寺堡区,盐池县,同心县,青铜峡市|固原市,原州区,西吉县,隆德县,泾源县,彭阳县|中卫市,沙坡头区,中宁县,海原县#新疆维吾尔自治区$乌鲁木齐市,天山区,沙依巴克区,新市区,水磨沟区,头屯河区,达坂城区,米东区,乌鲁木齐县|克拉玛依市,独山子区,克拉玛依区,白碱滩区,乌尔禾区|吐鲁番地区,吐鲁番市,鄯善县,托克逊县|哈密地区,哈密市,巴里坤哈萨克自治县,伊吾县|昌吉回族自治州,昌吉市,阜康市,呼图壁县,玛纳斯县,奇台县,吉木萨尔县,木垒哈萨克自治县|博尔塔拉蒙古自治州,博乐市,精河县,温泉县|巴音郭楞蒙古自治州,库尔勒市,轮台县,尉犁县,若羌县,且末县,焉耆回族自治县,和静县,和硕县,博湖县|阿克苏地区,阿克苏市,温宿县,库车县,沙雅县,新和县,拜城县,乌什县,阿瓦提县,柯坪县|克孜勒苏柯尔克孜自治州,阿图什市,阿克陶县,阿合奇县,乌恰县|喀什地区,喀什市,疏附县,疏勒县,英吉沙县,泽普县,莎车县,叶城县,麦盖提县,岳普湖县,伽师县,巴楚县,塔什库尔干塔吉克自治县|和田地区,和田市,和田县,墨玉县,皮山县,洛浦县,策勒县,于田县,民丰县|伊犁哈萨克自治州,伊宁市,奎屯市,伊宁县,察布查尔锡伯自治县,霍城县,巩留县,新源县,昭苏县,特克斯县,尼勒克县|塔城地区,塔城市,乌苏市,额敏县,沙湾县,托里县,裕民县,和布克赛尔蒙古自治县|阿勒泰地区,阿勒泰市,布尔津县,富蕴县,福海县,哈巴河县,青河县,吉木乃县|自治区直辖县级行政区划,石河子市,阿拉尔市,图木舒克市,五家渠市#香港特别行政区$香港,香港特别行政区#澳门特别行政区$澳门,澳门特别行政区#台湾省$台北市,中正区,大同区,中山区,松山区,大安区,万华区,信义区,士林区,北投区,内湖区,南港区,文山区|高雄市,新兴区,前金区,芩雅区,盐埕区,鼓山区,旗津区,前镇区,三民区,左营区,楠梓区,小港区|基隆市,仁爱区,信义区,中正区,中山区,安乐区,暖暖区,七堵区|台中市,中区,东区,南区,西区,北区,北屯区,西屯区,南屯区|台南市,中西区,东区,南区,北区,安平区,安南区|新竹市,东区,北区,香山区|嘉义市,东区,西区|县,台北县(板桥市),宜兰县(宜兰市),新竹县(竹北市),桃园县(桃园市),苗栗县(苗栗市),台中县(丰原市),彰化县(彰化市),南投县(南投市),嘉义县(太保市),云林县(斗六市),台南县(新营市),高雄县(凤山市),屏东县(屏东市),台东县(台东市),花莲县(花莲市),澎湖县(马公市)#其它$亚洲,阿富汗,巴林,孟加拉国,不丹,文莱,缅甸,塞浦路斯,印度,印度尼西亚,伊朗,伊拉克,日本,约旦,朝鲜,科威特,老挝,马尔代夫,黎巴嫩,马来西亚,以色列,蒙古,尼泊尔,阿曼,巴基斯坦,巴勒斯坦,菲律宾,沙特阿拉伯,新加坡,斯里兰卡,叙利亚,泰国,柬埔寨,土耳其,阿联酋,越南,也门,韩国,中国,中国香港,中国澳门,中国台湾|非洲,阿尔及利亚,安哥拉,厄里特里亚,法罗群鸟,加那利群岛(西)(拉斯帕尔马斯),贝宁,博茨瓦纳,布基纳法索,布隆迪,喀麦隆,加那利群岛(西)(圣克鲁斯),佛得角,中非,乍得,科摩罗,刚果,吉布提,埃及,埃塞俄比亚,赤道几内亚,加蓬,冈比亚,加纳,几内亚,南非,几内亚比绍,科特迪瓦,肯尼亚,莱索托,利比里亚,利比亚,马达加斯加,马拉维,马里,毛里塔尼亚,毛里求斯,摩洛哥,莫桑比克,尼日尔,尼日利亚,留尼旺岛,卢旺达,塞内加尔,塞舌尔,塞拉利昂,索马里,苏丹,斯威士兰,坦桑尼亚,圣赤勒拿,多哥,突尼斯,乌干达,扎伊尔,赞比亚,津巴布韦,纳米比亚,迪戈加西亚,桑给巴尔,马约特岛,圣多美和普林西比|欧洲,阿尔巴尼亚,安道尔,奥地利,比利时,保加利亚,捷克,丹麦,芬兰,法国,德国,直布罗陀(英),希腊,匈牙利,冰岛,爱尔兰,意大利,列支敦士登,斯洛伐克,卢森堡,马耳他,摩纳哥,荷兰,挪威,波兰,葡萄牙,马其顿,罗马尼亚,南斯拉夫,圣马力诺,西班牙,瑞典,瑞士,英国,科罗地亚,斯洛文尼亚,梵蒂冈,波斯尼亚和塞哥维那,俄罗斯联邦,亚美尼亚共和国,白俄罗斯共和国,格鲁吉亚共和国,哈萨克斯坦共和国,吉尔吉斯坦共和国,乌兹别克斯坦共和国,塔吉克斯坦共和国,土库曼斯坦共和国,乌克兰,立陶宛,拉脱维亚,爱沙尼亚,摩尔多瓦,阿塞拜疆|美洲,安圭拉岛,安提瓜和巴布达,阿根廷,阿鲁巴岛,阿森松,巴哈马,巴巴多斯,伯利兹,百慕大群岛,玻利维亚,巴西,加拿大,开曼群岛,智利,哥伦比亚,多米尼加联邦,哥斯达黎加,古巴,多米尼加共和国,厄瓜多尔,萨尔瓦多,法属圭亚那,格林纳达,危地马拉,圭亚那,海地,洪都拉斯,牙买加,马提尼克(法),墨西哥,蒙特塞拉特岛,荷属安的列斯群岛,尼加拉瓜,巴拿马,巴拉圭,秘鲁,波多黎哥,圣皮埃尔岛密克隆岛(法),圣克里斯托弗和尼维斯,圣卢西亚,福克兰群岛,维尔京群岛(英),圣文森特岛(英),维尔京群岛(美),苏里南,特立尼达和多巴哥,乌拉圭,美国,委内瑞拉,格陵兰岛,特克斯和凯科斯群岛,瓜多罗普|大洋洲,澳大利亚,科克群岛,斐济,法属波里尼西亚、塔希提,瓦努阿图,关岛,基里巴斯,马里亚纳群岛,中途岛,瑙鲁,新咯里多尼亚群岛,新西兰,巴布亚新几内亚,东萨摩亚,西萨摩亚,所罗门群岛,汤加,对诞岛,威克岛,科科斯岛,夏威夷,诺福克岛,帕劳,纽埃岛,图瓦卢,托克鲁,密克罗尼西亚,马绍尔群岛,瓦里斯加富士那群岛"; if (ShowT)PCAD = SPT + "$" + SCT + "," + SAT + "#" + PCAD; PCAArea = []; PCAP = []; PCAC = []; PCAA = []; PCAN = PCAD.split("#"); for (i = 0; i < PCAN.length; i++){PCAA[i] = []; TArea = PCAN[i].split("$")[1].split("|"); for (j = 0; j < TArea.length; j++){PCAA[i][j] = TArea[j].split(","); if (PCAA[i][j].length == 1)PCAA[i][j][1] = SAT; TArea[j] = TArea[j].split(",")[0]}PCAArea[i] = PCAN[i].split("$")[0] + "," + TArea.join(","); PCAP[i] = PCAArea[i].split(",")[0]; PCAC[i] = PCAArea[i].split(',')}function PCAS(){this.SelP = document.getElementsByName(arguments[0])[0]; this.SelC = document.getElementsByName(arguments[1])[0]; this.SelA = document.getElementsByName(arguments[2])[0]; this.DefP = this.SelA?arguments[3]:arguments[2]; this.DefC = this.SelA?arguments[4]:arguments[3]; this.DefA = this.SelA?arguments[5]:arguments[4]; this.SelP.PCA = this; this.SelC.PCA = this; this.SelP.onchange = function(){PCAS.SetC(this.PCA)}; if (this.SelA)this.SelC.onchange = function(){PCAS.SetA(this.PCA)}; PCAS.SetP(this)}; PCAS.SetP = function(PCA){for (i = 0; i < PCAP.length; i++){PCAPT = PCAPV = PCAP[i]; if (PCAPT == SPT)PCAPV = ""; PCA.SelP.options.add(new Option(PCAPT, PCAPV)); if (PCA.DefP == PCAPV)PCA.SelP[i].selected = true}PCAS.SetC(PCA)}; PCAS.SetC = function(PCA){PI = PCA.SelP.selectedIndex; PCA.SelC.length = 0; for (i = 1; i < PCAC[PI].length; i++){PCACT = PCACV = PCAC[PI][i]; if (PCACT == SCT)PCACV = ""; PCA.SelC.options.add(new Option(PCACT, PCACV)); if (PCA.DefC == PCACV)PCA.SelC[i - 1].selected = true}if (PCA.SelA)PCAS.SetA(PCA)}; PCAS.SetA = function(PCA){PI = PCA.SelP.selectedIndex; CI = PCA.SelC.selectedIndex; PCA.SelA.length = 0; for (i = 1; i < PCAA[PI][CI].length; i++){PCAAT = PCAAV = PCAA[PI][CI][i]; if (PCAAT == SAT)PCAAV = ""; PCA.SelA.options.add(new Option(PCAAT, PCAAV)); if (PCA.DefA == PCAAV)PCA.SelA[i - 1].selected = true} try{$(PCA.SelA).trigger('change')} catch (e){}} \ No newline at end of file diff --git a/static/plugs/layui/css/layui.css b/static/plugs/layui/css/layui.css index e86730331..94bb43494 100644 --- a/static/plugs/layui/css/layui.css +++ b/static/plugs/layui/css/layui.css @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - .layui-laypage a,a{text-decoration:none}.layui-btn,.layui-inline,img{vertical-align:middle}.layui-btn,.layui-unselect{-webkit-user-select:none;-ms-user-select:none;-moz-user-select:none}.layui-btn,.layui-tree li i,.layui-unselect{-moz-user-select:none}blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}a:active,a:hover{outline:0}img{display:inline-block;border:none}li{list-style:none}table{border-collapse:collapse;border-spacing:0}h1,h2,h3{font-size:14px;font-weight:400}h4,h5,h6{font-size:100%;font-weight:400}button,input,optgroup,option,select,textarea{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;outline:0}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-button:vertical{display:none}::-webkit-scrollbar-corner,::-webkit-scrollbar-track{background-color:#e2e2e2}::-webkit-scrollbar-thumb{border-radius:0;background-color:rgba(0,0,0,.3)}::-webkit-scrollbar-thumb:vertical:hover{background-color:rgba(0,0,0,.35)}::-webkit-scrollbar-thumb:vertical:active{background-color:rgba(0,0,0,.38)}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=1.0.9);src:url(../font/iconfont.eot?v=1.0.9#iefix) format('embedded-opentype'),url(../font/iconfont.svg?v=1.0.9#iconfont) format('svg'),url(../font/iconfont.woff?v=1.0.9) format('woff'),url(../font/iconfont.ttf?v=1.0.9) format('truetype')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{line-height:24px;font:14px Helvetica Neue,Helvetica,PingFang SC,\5FAE\8F6F\96C5\9ED1,Tahoma,Arial,sans-serif}hr{height:1px;margin:10px 0;border:0;background-color:#e2e2e2;clear:both}a{color:#333}a:hover{color:#777}a cite{font-style:normal;*cursor:pointer}.layui-box,.layui-box *{-webkit-box-sizing:content-box!important;-moz-box-sizing:content-box!important;box-sizing:content-box!important}.layui-border-box,.layui-border-box *{-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-clear{clear:both;*zoom:1}.layui-clear:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-inline{position:relative;display:inline-block;*display:inline;*zoom:1}.layui-edge{position:absolute;width:0;height:0;border-style:dashed;border-color:transparent;overflow:hidden}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-disabled,.layui-disabled:hover{color:#d2d2d2!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}.layui-main{position:relative;width:1140px;margin:0 auto}.layui-header{position:relative;z-index:1000;height:60px}.layui-header a:hover{transition:all .5s;-webkit-transition:all .5s}.layui-side{position:fixed;top:0;bottom:0;z-index:999;width:200px;overflow-x:hidden}.layui-side-scroll{width:220px;height:100%;overflow-x:hidden}.layui-body{position:absolute;left:200px;right:0;top:0;bottom:0;z-index:998;width:auto;overflow:hidden;overflow-y:auto;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.layui-layout-admin .layui-header{background-color:#23262E}.layui-layout-admin .layui-side{top:60px;width:200px;overflow-x:hidden}.layui-layout-admin .layui-body{top:60px;bottom:44px}.layui-layout-admin .layui-main{width:auto;margin:0 15px}.layui-layout-admin .layui-footer{position:fixed;left:200px;right:0;bottom:0;height:44px;background-color:#eee}.layui-btn,.layui-input,.layui-select,.layui-textarea,.layui-upload-button{outline:0;-webkit-transition:border-color .3s cubic-bezier(.65,.05,.35,.5);transition:border-color .3s cubic-bezier(.65,.05,.35,.5);-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-elem-quote{margin-bottom:10px;padding:15px;line-height:22px;border-left:5px solid #009688;border-radius:0 2px 2px 0;background-color:#f2f2f2}.layui-quote-nm{border-color:#e2e2e2;border-style:solid;border-width:1px 1px 1px 5px;background:0 0}.layui-elem-field{margin-bottom:10px;padding:0;border:1px solid #e2e2e2}.layui-elem-field legend{margin-left:20px;padding:0 10px;font-size:20px;font-weight:300}.layui-field-title{margin:10px 0 20px;border:none;border-top:1px solid #e2e2e2}.layui-field-box{padding:10px 15px}.layui-field-title .layui-field-box{padding:10px 0}.layui-progress{position:relative;height:6px;border-radius:20px;background-color:#e2e2e2}.layui-progress-bar{position:absolute;width:0;max-width:100%;height:6px;border-radius:20px;text-align:right;background-color:#5FB878;transition:all .3s;-webkit-transition:all .3s}.layui-progress-big,.layui-progress-big .layui-progress-bar{height:18px;line-height:18px}.layui-progress-text{position:relative;top:-18px;line-height:18px;font-size:12px;color:#666}.layui-progress-big .layui-progress-text{position:static;padding:0 10px;color:#fff}.layui-collapse{border:1px solid #e2e2e2;border-radius:2px}.layui-colla-item{border-top:1px solid #e2e2e2}.layui-colla-item:first-child{border-top:none}.layui-colla-title{position:relative;height:42px;line-height:42px;padding:0 15px 0 35px;color:#333;background-color:#f2f2f2;cursor:pointer}.layui-colla-content{display:none;padding:10px 15px;line-height:22px;border-top:1px solid #e2e2e2;color:#666}.layui-colla-icon{position:absolute;left:15px;top:0;font-size:14px}.layui-bg-red{background-color:#FF5722}.layui-bg-orange{background-color:#F7B824}.layui-bg-green{background-color:#009688}.layui-bg-cyan{background-color:#2F4056}.layui-bg-blue{background-color:#1E9FFF}.layui-bg-black{background-color:#393D49}.layui-bg-gray{background-color:#eee}.layui-word-aux{font-size:12px;color:#999;padding:0 5px}.layui-btn{display:inline-block;height:38px;line-height:38px;padding:0 18px;background-color:#009688;color:#fff;white-space:nowrap;text-align:center;font-size:14px;border:none;border-radius:2px;cursor:pointer;opacity:.9;filter:alpha(opacity=90)}.layui-btn:hover{opacity:.8;filter:alpha(opacity=80);color:#fff}.layui-btn:active{opacity:1;filter:alpha(opacity=100)}.layui-btn+.layui-btn{margin-left:10px}.layui-btn-radius{border-radius:100px}.layui-btn .layui-icon{font-size:18px;vertical-align:bottom}.layui-btn-primary{border:1px solid #C9C9C9;background-color:#fff;color:#555}.layui-btn-primary:hover{border-color:#009688;color:#333}.layui-btn-normal{background-color:#1E9FFF}.layui-btn-warm{background-color:#F7B824}.layui-btn-danger{background-color:#FF5722}.layui-btn-disabled,.layui-btn-disabled:active,.layui-btn-disabled:hover{border:1px solid #e6e6e6;background-color:#FBFBFB;color:#C9C9C9;cursor:not-allowed;opacity:1}.layui-btn-big{height:44px;line-height:44px;padding:0 25px;font-size:16px}.layui-btn-small{height:30px;line-height:30px;padding:0 10px;font-size:12px}.layui-btn-small i{font-size:16px!important}.layui-btn-mini{height:22px;line-height:22px;padding:0 5px;font-size:12px}.layui-btn-mini i{font-size:14px!important}.layui-btn-group{display:inline-block;vertical-align:middle;font-size:0}.layui-btn-group .layui-btn{margin-left:0!important;margin-right:0!important;border-left:1px solid rgba(255,255,255,.5);border-radius:0}.layui-btn-group .layui-btn-primary{border-left:none}.layui-btn-group .layui-btn-primary:hover{border-color:#C9C9C9;color:#009688}.layui-btn-group .layui-btn:first-child{border-left:none;border-radius:2px 0 0 2px}.layui-btn-group .layui-btn-primary:first-child{border-left:1px solid #c9c9c9}.layui-btn-group .layui-btn:last-child{border-radius:0 2px 2px 0}.layui-btn-group .layui-btn+.layui-btn{margin-left:0}.layui-btn-group+.layui-btn-group{margin-left:10px}.layui-input,.layui-select,.layui-textarea{height:38px;line-height:38px;line-height:36px\9;border:1px solid #e6e6e6;background-color:#fff;border-radius:2px}.layui-form-label,.layui-form-mid,.layui-textarea{line-height:20px;position:relative}.layui-input,.layui-textarea{display:block;width:100%;padding-left:10px}.layui-input:hover,.layui-textarea:hover{border-color:#D2D2D2!important}.layui-input:focus,.layui-textarea:focus{border-color:#C9C9C9!important}.layui-textarea{min-height:100px;height:auto;padding:6px 10px;resize:vertical}.layui-select{padding:0 10px}.layui-form input[type=checkbox],.layui-form input[type=radio],.layui-form select{display:none}.layui-form-item{margin-bottom:15px;clear:both;*zoom:1}.layui-form-item:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-form-label{float:left;display:block;padding:9px 15px;width:80px;font-weight:400;text-align:right}.layui-form-item .layui-inline{margin-bottom:5px;margin-right:10px}.layui-input-block,.layui-input-inline{position:relative}.layui-input-block{margin-left:110px;min-height:36px}.layui-input-inline{display:inline-block;vertical-align:middle}.layui-form-item .layui-input-inline{float:left;width:190px;margin-right:10px}.layui-form-text .layui-input-inline{width:auto}.layui-form-mid{float:left;display:block;padding:8px 0;margin-right:10px}.layui-form-danger+.layui-form-select .layui-input,.layui-form-danger:focus{border:1px solid #FF5722!important}.layui-form-select{position:relative}.layui-form-select .layui-input{padding-right:30px;cursor:pointer}.layui-form-select .layui-edge{position:absolute;right:10px;top:50%;margin-top:-3px;cursor:pointer;border-width:6px;border-top-color:#c2c2c2;border-top-style:solid;transition:all .3s;-webkit-transition:all .3s}.layui-form-select dl{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:999;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box}.layui-form-select dl dd,.layui-form-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.layui-form-select dl dt{font-size:12px;color:#999}.layui-form-select dl dd{cursor:pointer}.layui-form-select dl dd:hover{background-color:#f2f2f2}.layui-form-select .layui-select-group dd{padding-left:20px}.layui-form-select dl dd.layui-this{background-color:#5FB878;color:#fff}.layui-form-checkbox,.layui-form-select dl dd.layui-disabled{background-color:#fff}.layui-form-selected dl{display:block}.layui-form-checkbox,.layui-form-checkbox *,.layui-form-radio,.layui-form-radio *,.layui-form-switch{display:inline-block;vertical-align:middle}.layui-form-selected .layui-edge{margin-top:-9px;-webkit-transform:rotate(180deg);transform:rotate(180deg);margin-top:-3px\9}:root .layui-form-selected .layui-edge{margin-top:-9px\0/IE9}.layui-select-none{margin:5px 0;text-align:center;color:#999}.layui-select-disabled .layui-disabled{border-color:#eee!important}.layui-select-disabled .layui-edge{border-top-color:#d2d2d2}.layui-form-checkbox{position:relative;height:30px;line-height:28px;margin-right:10px;padding-right:30px;border:1px solid #d2d2d2;cursor:pointer;font-size:0;border-radius:2px;-webkit-transition:.1s linear;transition:.1s linear;box-sizing:border-box!important}.layui-form-checkbox:hover{border:1px solid #c2c2c2}.layui-form-checkbox span{padding:0 10px;height:100%;font-size:14px;background-color:#d2d2d2;color:#fff;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.layui-form-checkbox:hover span{background-color:#c2c2c2}.layui-form-checkbox i{position:absolute;right:0;width:30px;color:#fff;font-size:20px;text-align:center}.layui-form-checkbox:hover i{color:#c2c2c2}.layui-form-checked,.layui-form-checked:hover{border-color:#5FB878}.layui-form-checked span,.layui-form-checked:hover span{background-color:#5FB878}.layui-form-checked i,.layui-form-checked:hover i{color:#5FB878}.layui-form-item .layui-form-checkbox{margin-top:4px}.layui-form-checkbox[lay-skin=primary]{height:auto!important;line-height:normal!important;border:none!important;margin-right:0;padding-right:0;background:0 0}.layui-form-checkbox[lay-skin=primary] span{float:right;padding-right:15px;line-height:18px;background:0 0;color:#666}.layui-form-checkbox[lay-skin=primary] i{position:relative;top:0;width:16px;line-height:16px;border:1px solid #d2d2d2;font-size:12px;border-radius:2px;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-checkbox[lay-skin=primary]:hover i{border-color:#5FB878;color:#fff}.layui-form-checked[lay-skin=primary] i{border-color:#5FB878;background-color:#5FB878;color:#fff}.layui-checkbox-disbaled[lay-skin=primary] span{background:0 0!important}.layui-checkbox-disbaled[lay-skin=primary]:hover i{border-color:#d2d2d2}.layui-form-item .layui-form-checkbox[lay-skin=primary]{margin-top:10px}.layui-form-switch{position:relative;height:22px;line-height:22px;width:42px;padding:0 5px;margin-top:8px;border:1px solid #d2d2d2;border-radius:20px;cursor:pointer;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch i{position:absolute;left:5px;top:3px;width:16px;height:16px;border-radius:20px;background-color:#d2d2d2;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch em{position:absolute;right:5px;top:0;width:25px;padding:0!important;text-align:center!important;color:#999!important;font-style:normal!important;font-size:12px}.layui-form-onswitch{border-color:#5FB878;background-color:#5FB878}.layui-form-onswitch i{left:32px;background-color:#fff}.layui-form-onswitch em{left:5px;right:auto;color:#fff!important}.layui-checkbox-disbaled{border-color:#e2e2e2!important}.layui-checkbox-disbaled span{background-color:#e2e2e2!important}.layui-checkbox-disbaled:hover i{color:#fff!important}.layui-form-radio{line-height:28px;margin:6px 10px 0 0;padding-right:10px;cursor:pointer;font-size:0}.layui-form-radio i{margin-right:8px;font-size:22px;color:#c2c2c2}.layui-form-radio span{font-size:14px}.layui-form-radio i:hover,.layui-form-radioed i{color:#5FB878}.layui-radio-disbaled i{color:#e2e2e2!important}.layui-form-pane .layui-form-label{width:110px;padding:8px 15px;height:38px;line-height:20px;border:1px solid #e6e6e6;border-radius:2px 0 0 2px;text-align:center;background-color:#FBFBFB;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-form-pane .layui-input-inline{margin-left:-1px}.layui-form-pane .layui-input-block{margin-left:110px;left:-1px}.layui-form-pane .layui-input{border-radius:0 2px 2px 0}.layui-form-pane .layui-form-text .layui-form-label{float:none;width:100%;border-right:1px solid #e6e6e6;border-radius:2px;-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important;text-align:left}.layui-laypage button,.layui-laypage input,.layui-nav{-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important}.layui-form-pane .layui-form-text .layui-input-inline{display:block;margin:0;top:-1px;clear:both}.layui-form-pane .layui-form-text .layui-input-block{margin:0;left:0;top:-1px}.layui-form-pane .layui-form-text .layui-textarea{min-height:100px;border-radius:0 0 2px 2px}.layui-form-pane .layui-form-checkbox{margin:4px 0 4px 10px}.layui-form-pane .layui-form-radio,.layui-form-pane .layui-form-switch{margin-top:6px;margin-left:10px}.layui-form-pane .layui-form-item[pane]{position:relative;border:1px solid #e6e6e6}.layui-form-pane .layui-form-item[pane] .layui-form-label{position:absolute;left:0;top:0;height:100%;border-width:0 1px 0 0}.layui-form-pane .layui-form-item[pane] .layui-input-inline{margin-left:110px}.layui-layedit{border:1px solid #d2d2d2;border-radius:2px}.layui-layedit-tool{padding:3px 5px;border-bottom:1px solid #e2e2e2;font-size:0}.layedit-tool-fixed{position:fixed;top:0;border-top:1px solid #e2e2e2}.layui-layedit-tool .layedit-tool-mid,.layui-layedit-tool .layui-icon{display:inline-block;vertical-align:middle;text-align:center;font-size:14px}.layui-layedit-tool .layui-icon{position:relative;width:32px;height:30px;line-height:30px;margin:3px 5px;color:#777;cursor:pointer;border-radius:2px}.layui-layedit-tool .layui-icon:hover{color:#393D49}.layui-layedit-tool .layui-icon:active{color:#000}.layui-layedit-tool .layedit-tool-active{background-color:#e2e2e2;color:#000}.layui-layedit-tool .layui-disabled,.layui-layedit-tool .layui-disabled:hover{color:#d2d2d2;cursor:not-allowed}.layui-layedit-tool .layedit-tool-mid{width:1px;height:18px;margin:0 10px;background-color:#d2d2d2}.layedit-tool-html{width:50px!important;font-size:30px!important}.layedit-tool-b,.layedit-tool-code,.layedit-tool-help{font-size:16px!important}.layedit-tool-d,.layedit-tool-face,.layedit-tool-image,.layedit-tool-unlink{font-size:18px!important}.layedit-tool-image input{position:absolute;font-size:0;left:0;top:0;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-layedit-iframe iframe{display:block;width:100%}#LAY_layedit_code{overflow:hidden}.layui-table{width:100%;margin:10px 0;background-color:#fff}.layui-table tr{transition:all .3s;-webkit-transition:all .3s}.layui-table thead tr{background-color:#f2f2f2}.layui-table th{text-align:left}.layui-table td,.layui-table th{padding:9px 15px;min-height:20px;line-height:20px;border:1px solid #e2e2e2;font-size:14px}.layui-table tr:hover,.layui-table[lay-even] tr:nth-child(even){background-color:#f8f8f8}.layui-table[lay-skin=line],.layui-table[lay-skin=row]{border:1px solid #e2e2e2}.layui-table[lay-skin=line] td,.layui-table[lay-skin=line] th{border:none;border-bottom:1px solid #e2e2e2}.layui-table[lay-skin=row] td,.layui-table[lay-skin=row] th{border:none;border-right:1px solid #e2e2e2}.layui-table[lay-skin=nob] td,.layui-table[lay-skin=nob] th{border:none}.layui-upload-button{position:relative;display:inline-block;vertical-align:middle;min-width:60px;height:38px;line-height:38px;border:1px solid #DFDFDF;border-radius:2px;overflow:hidden;background-color:#fff;color:#666}.layui-upload-button:hover{border:1px solid #aaa;color:#333}.layui-upload-button:active{border:1px solid #4CAF50;color:#000}.layui-upload-button input,.layui-upload-file{opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-upload-button input{position:absolute;left:0;top:0;z-index:10;font-size:100px;width:100%;height:100%}.layui-upload-icon{display:block;margin:0 15px;text-align:center}.layui-upload-icon i{margin-right:5px;vertical-align:top;font-size:20px;color:#5FB878}.layui-upload-iframe{position:absolute;width:0;height:0;border:0;visibility:hidden}.layui-upload-enter{border:1px solid #009E94;background-color:#009E94;color:#fff;-webkit-transform:scale(1.1);transform:scale(1.1)}.layui-upload-enter .layui-upload-icon,.layui-upload-enter .layui-upload-icon i{color:#fff}.layui-flow-more{margin:10px 0;text-align:center;color:#999;font-size:14px}.layui-flow-more a{height:32px;line-height:32px}.layui-flow-more a *{display:inline-block;vertical-align:top}.layui-flow-more a cite{padding:0 20px;border-radius:3px;background-color:#eee;color:#333;font-style:normal}.layui-flow-more a cite:hover{opacity:.8}.layui-flow-more a i{font-size:30px;color:#737383}.layui-laypage{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;margin:10px 0;font-size:0}.layui-laypage>:first-child,.layui-laypage>:first-child em{border-radius:2px 0 0 2px}.layui-laypage>:last-child,.layui-laypage>:last-child em{border-radius:0 2px 2px 0}.layui-laypage a,.layui-laypage span{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding:0 15px;border:1px solid #e2e2e2;height:28px;line-height:28px;margin:0 -1px 5px 0;background-color:#fff;color:#333;font-size:12px}.layui-laypage em{font-style:normal}.layui-laypage span{color:#999;font-weight:700}.layui-laypage .layui-laypage-curr{position:relative}.layui-laypage .layui-laypage-curr em{position:relative;color:#fff;font-weight:400}.layui-laypage .layui-laypage-curr .layui-laypage-em{position:absolute;left:-1px;top:-1px;padding:1px;width:100%;height:100%;background-color:#009688}.layui-laypage-em{border-radius:2px}.layui-laypage-next em,.layui-laypage-prev em{font-family:Sim sun;font-size:16px}.layui-laypage .layui-laypage-total{height:30px;line-height:30px;margin-left:1px;border:none;font-weight:400}.layui-laypage button,.layui-laypage input{height:30px;line-height:30px;border:1px solid #e2e2e2;border-radius:2px;vertical-align:top;background-color:#fff;box-sizing:border-box!important}.layui-laypage input{width:50px;margin:0 5px;text-align:center}.layui-laypage button{margin-left:5px;padding:0 15px;cursor:pointer}.layui-code{position:relative;margin:10px 0;padding:15px;line-height:20px;border:1px solid #ddd;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New;font-size:12px}.layui-tree{line-height:26px}.layui-tree li{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-tree li .layui-tree-spread,.layui-tree li a{display:inline-block;vertical-align:top;height:26px;*display:inline;*zoom:1;cursor:pointer}.layui-tree li a{font-size:0}.layui-tree li a i{font-size:16px}.layui-tree li a cite{padding:0 6px;font-size:14px;font-style:normal}.layui-tree li i{padding-left:6px;color:#333}.layui-tree li .layui-tree-check{font-size:13px}.layui-tree li .layui-tree-check:hover{color:#009E94}.layui-tree li ul{display:none;margin-left:20px}.layui-tree li .layui-tree-enter{line-height:24px;border:1px dotted #000}.layui-tree-drag{display:none;position:absolute;left:-666px;top:-666px;background-color:#f2f2f2;padding:5px 10px;border:1px dotted #000;white-space:nowrap}.layui-tree-drag i{padding-right:5px}.layui-nav{position:relative;padding:0 20px;background-color:#393D49;color:#c2c2c2;border-radius:2px;font-size:0;box-sizing:border-box!important}.layui-nav *{font-size:14px}.layui-nav .layui-nav-item{position:relative;display:inline-block;*display:inline;*zoom:1;vertical-align:middle;line-height:60px}.layui-nav .layui-nav-item a{display:block;padding:0 20px;color:#c2c2c2;transition:all .3s;-webkit-transition:all .3s}.layui-nav .layui-this:after,.layui-nav-bar,.layui-nav-tree .layui-nav-itemed:after{position:absolute;left:0;top:0;width:0;height:5px;background-color:#5FB878;transition:all .2s;-webkit-transition:all .2s}.layui-nav-bar{z-index:1000}.layui-nav .layui-nav-item a:hover,.layui-nav .layui-this a{color:#fff}.layui-nav .layui-this:after{content:'';top:auto;bottom:0;width:100%}.layui-nav .layui-nav-more{content:'';width:0;height:0;border-style:solid dashed dashed;border-color:#c2c2c2 transparent transparent;overflow:hidden;cursor:pointer;transition:all .2s;-webkit-transition:all .2s;position:absolute;top:28px;right:3px;border-width:6px}.layui-nav .layui-nav-mored,.layui-nav-itemed .layui-nav-more{top:22px;border-style:dashed dashed solid;border-color:transparent transparent #c2c2c2}.layui-nav-child{display:none;position:absolute;left:0;top:65px;min-width:100%;line-height:36px;padding:5px 0;box-shadow:0 2px 4px rgba(0,0,0,.12);border:1px solid #d2d2d2;background-color:#fff;z-index:100;border-radius:2px;white-space:nowrap}.layui-nav .layui-nav-child a{color:#333}.layui-nav .layui-nav-child a:hover{background-color:#f2f2f2;color:#333}.layui-nav-child dd{position:relative}.layui-nav-child dd.layui-this{background-color:#5FB878;color:#fff}.layui-nav-child dd.layui-this a{color:#fff}.layui-nav-child dd.layui-this:after{display:none}.layui-nav-tree{width:200px;padding:0}.layui-nav-tree .layui-nav-item{display:block;width:100%;line-height:45px}.layui-nav-tree .layui-nav-item a{height:45px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-nav-tree .layui-nav-item a:hover{background-color:#4E5465}.layui-nav-tree .layui-nav-child dd.layui-this,.layui-nav-tree .layui-this,.layui-nav-tree .layui-this>a,.layui-nav-tree .layui-this>a:hover{background-color:#009688;color:#fff}.layui-nav-tree .layui-this:after{display:none}.layui-nav-itemed>a,.layui-nav-tree .layui-nav-title a,.layui-nav-tree .layui-nav-title a:hover{background-color:#2B2E37!important;color:#fff!important}.layui-nav-tree .layui-nav-bar{width:5px;height:0;background-color:#009688}.layui-nav-tree .layui-nav-child{position:relative;z-index:0;top:0;border:none;box-shadow:none}.layui-nav-tree .layui-nav-child a{height:40px;line-height:40px;color:#c2c2c2}.layui-nav-tree .layui-nav-child,.layui-nav-tree .layui-nav-child a:hover{background:0 0;color:#fff}.layui-nav-tree .layui-nav-more{top:20px;right:10px}.layui-nav-itemed .layui-nav-more{top:14px}.layui-nav-itemed .layui-nav-child{display:block;padding:0}.layui-nav-side{position:fixed;top:0;bottom:0;left:0;overflow-x:hidden;z-index:999}.layui-breadcrumb{visibility:hidden;font-size:0}.layui-breadcrumb a{padding-right:8px;line-height:22px;font-size:14px;color:#333!important}.layui-breadcrumb a:hover{color:#01AAED!important}.layui-breadcrumb a cite,.layui-breadcrumb a span{color:#666;cursor:text;font-style:normal}.layui-breadcrumb a span{padding-left:8px;font-family:Sim sun}.layui-tab{margin:10px 0;text-align:left!important}.layui-fixbar li,.layui-tab-bar,.layui-tab-title li,.layui-util-face ul li{cursor:pointer;text-align:center}.layui-tab[overflow]>.layui-tab-title{overflow:hidden}.layui-tab-title{position:relative;left:0;height:40px;white-space:nowrap;font-size:0;border-bottom:1px solid #e2e2e2;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;font-size:14px;transition:all .2s;-webkit-transition:all .2s;position:relative;line-height:40px;min-width:65px;padding:0 10px}.layui-tab-title li a{display:block}.layui-tab-title .layui-this{color:#000}.layui-tab-title .layui-this:after{position:absolute;left:0;top:0;content:'';width:100%;height:41px;border:1px solid #e2e2e2;border-bottom-color:#fff;border-radius:2px 2px 0 0;-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important;pointer-events:none}.layui-tab-bar{position:absolute;right:0;top:0;z-index:10;width:30px;height:39px;line-height:39px;border:1px solid #e2e2e2;border-radius:2px;background-color:#fff}.layui-tab-bar .layui-icon{position:relative;display:inline-block;top:3px;transition:all .3s;-webkit-transition:all .3s}.layui-tab-item,.layui-util-face .layui-layer-TipsG{display:none}.layui-tab-more{padding-right:30px;height:auto;white-space:normal}.layui-tab-more li.layui-this:after{border-bottom-color:#e2e2e2;border-radius:2px}.layui-tab-more .layui-tab-bar .layui-icon{top:-2px;top:3px\9;-webkit-transform:rotate(180deg);transform:rotate(180deg)}:root .layui-tab-more .layui-tab-bar .layui-icon{top:-2px\0/IE9}.layui-tab-content{padding:10px}.layui-tab-title li .layui-tab-close{position:relative;margin-left:8px;top:1px;color:#c2c2c2;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li .layui-tab-close:hover{border-radius:2px;background-color:#FF5722;color:#fff}.layui-tab-brief>.layui-tab-title .layui-this{color:#009688}.layui-tab-brief>.layui-tab-more li.layui-this:after,.layui-tab-brief>.layui-tab-title .layui-this:after{border:none;border-radius:0;border-bottom:3px solid #5FB878}.layui-tab-brief[overflow]>.layui-tab-title .layui-this:after{top:-1px}.layui-tab-card{border:1px solid #e2e2e2;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.1)}.layui-tab-card>.layui-tab-title{background-color:#f2f2f2}.layui-tab-card>.layui-tab-title li{margin-right:-1px;margin-left:-1px}.layui-tab-card>.layui-tab-title .layui-this{background-color:#fff}.layui-tab-card>.layui-tab-title .layui-this:after{border-top:none;border-width:1px;border-bottom-color:#fff}.layui-tab-card>.layui-tab-title .layui-tab-bar{height:40px;line-height:40px;border-radius:0;border-top:none;border-right:none}.layui-tab-card>.layui-tab-more .layui-this{background:0 0;color:#5FB878}.layui-tab-card>.layui-tab-more .layui-this:after{border:none}.layui-fixbar{position:fixed;right:15px;bottom:15px;z-index:9999}.layui-fixbar li{width:50px;height:50px;line-height:50px;margin-bottom:1px;font-size:30px;background-color:#9F9F9F;color:#fff;border-radius:2px;opacity:.95}.layui-fixbar li:hover{opacity:.85}.layui-fixbar li:active{opacity:1}.layui-fixbar .layui-fixbar-top{display:none;font-size:40px}body .layui-util-face{border:none;background:0 0}body .layui-util-face .layui-layer-content{padding:0;background-color:#fff;color:#666;box-shadow:none}.layui-util-face ul{position:relative;width:372px;padding:10px;border:1px solid #D9D9D9;background-color:#fff;box-shadow:0 0 20px rgba(0,0,0,.2)}.layui-util-face ul li{float:left;border:1px solid #e8e8e8;height:22px;width:26px;overflow:hidden;margin:-1px 0 0 -1px;padding:4px 2px}.layui-util-face ul li:hover{position:relative;z-index:2;border:1px solid #eb7350;background:#fff9ec}.layui-anim{-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-anim-loop{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}@-webkit-keyframes layui-rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@keyframes layui-rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}}.layui-anim-rotate{-webkit-animation-name:layui-rotate;animation-name:layui-rotate;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:linear;animation-timing-function:linear}@-webkit-keyframes layui-up{from{-webkit-transform:translate3d(0,100%,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-up{from{transform:translate3d(0,100%,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-up{-webkit-animation-name:layui-up;animation-name:layui-up}@-webkit-keyframes layui-upbit{from{-webkit-transform:translate3d(0,30px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-upbit{from{transform:translate3d(0,30px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-upbit{-webkit-animation-name:layui-upbit;animation-name:layui-upbit}@-webkit-keyframes layui-scale{0%{opacity:.3;-webkit-transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale{0%{opacity:.3;-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-ms-transform:scale(1);transform:scale(1)}}.layui-anim-scale{-webkit-animation-name:layui-scale;animation-name:layui-scale}@-webkit-keyframes layui-scale-spring{0%{opacity:.5;-webkit-transform:scale(.5)}80%{opacity:.8;-webkit-transform:scale(1.1)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale-spring{0%{opacity:.5;-ms-transform:scale(.5);transform:scale(.5)}80%{opacity:.8;-ms-transform:scale(1.1);transform:scale(1.1)}100%{opacity:1;-ms-transform:scale(1);transform:scale(1)}}.layui-anim-scaleSpring{-webkit-animation-name:layui-scale-spring;animation-name:layui-scale-spring}@media screen and (max-width:450px){.layui-form-item .layui-form-label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-form-item .layui-inline{display:block;margin-right:0;margin-bottom:20px;clear:both}.layui-form-item .layui-inline:after{content:'\20';clear:both;display:block;height:0}.layui-form-item .layui-input-inline{display:block;float:none;left:-3px;width:auto;margin:0 0 10px 112px}.layui-form-item .layui-input-inline+.layui-form-mid{margin-left:110px;top:-5px;padding:0}.layui-form-item .layui-form-checkbox{margin-right:5px;margin-bottom:5px}} \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + .layui-inline,img{display:inline-block;vertical-align:middle}.layui-rate,li{list-style:none}h1,h2,h3,h4,h5,h6{font-weight:400}.layui-edge,.layui-header,.layui-inline,.layui-main{position:relative}.layui-btn,.layui-edge,.layui-inline,img{vertical-align:middle}.layui-btn,.layui-disabled,.layui-icon,.layui-unselect{-webkit-user-select:none;-ms-user-select:none;-moz-user-select:none}blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}a:active,a:hover{outline:0}img{border:none}table{border-collapse:collapse;border-spacing:0}h4,h5,h6{font-size:100%}button,input,optgroup,option,select,textarea{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;outline:0}pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}body{line-height:24px;font:14px Helvetica Neue,Helvetica,PingFang SC,\5FAE\8F6F\96C5\9ED1,Tahoma,Arial,sans-serif}hr{height:1px;margin:10px 0;border:0;clear:both}a{color:#333;text-decoration:none}a:hover{color:#777}a cite{font-style:normal;*cursor:pointer}.layui-border-box,.layui-border-box *{box-sizing:border-box}.layui-box,.layui-box *{box-sizing:content-box}.layui-clear{clear:both;*zoom:1}.layui-clear:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-inline{*display:inline;*zoom:1}.layui-edge{display:inline-block;width:0;height:0;border-width:6px;border-style:dashed;border-color:transparent;overflow:hidden}.layui-edge-top{top:-4px;border-bottom-color:#999;border-bottom-style:solid}.layui-edge-right{border-left-color:#999;border-left-style:solid}.layui-edge-bottom{top:2px;border-top-color:#999;border-top-style:solid}.layui-edge-left{border-right-color:#999;border-right-style:solid}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-disabled,.layui-disabled:hover{color:#d2d2d2!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=230);src:url(../font/iconfont.eot?v=230#iefix) format('embedded-opentype'),url(../font/iconfont.svg?v=230#iconfont) format('svg'),url(../font/iconfont.woff?v=230) format('woff'),url(../font/iconfont.ttf?v=230) format('truetype')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-icon-reply-fill:before{content:"\e611"}.layui-icon-set-fill:before{content:"\e614"}.layui-icon-menu-fill:before{content:"\e60f"}.layui-icon-search:before{content:"\e615"}.layui-icon-share:before{content:"\e641"}.layui-icon-set-sm:before{content:"\e620"}.layui-icon-engine:before{content:"\e628"}.layui-icon-close:before{content:"\1006"}.layui-icon-close-fill:before{content:"\1007"}.layui-icon-chart-screen:before{content:"\e629"}.layui-icon-star:before{content:"\e600"}.layui-icon-circle-dot:before{content:"\e617"}.layui-icon-chat:before{content:"\e606"}.layui-icon-release:before{content:"\e609"}.layui-icon-list:before{content:"\e60a"}.layui-icon-chart:before{content:"\e62c"}.layui-icon-ok-circle:before{content:"\1005"}.layui-icon-layim-theme:before{content:"\e61b"}.layui-icon-table:before{content:"\e62d"}.layui-icon-right:before{content:"\e602"}.layui-icon-left:before{content:"\e603"}.layui-icon-cart-simple:before{content:"\e698"}.layui-icon-face-cry:before{content:"\e69c"}.layui-icon-face-smile:before{content:"\e6af"}.layui-icon-survey:before{content:"\e6b2"}.layui-icon-tree:before{content:"\e62e"}.layui-icon-upload-circle:before{content:"\e62f"}.layui-icon-add-circle:before{content:"\e61f"}.layui-icon-download-circle:before{content:"\e601"}.layui-icon-templeate-1:before{content:"\e630"}.layui-icon-util:before{content:"\e631"}.layui-icon-face-surprised:before{content:"\e664"}.layui-icon-edit:before{content:"\e642"}.layui-icon-speaker:before{content:"\e645"}.layui-icon-down:before{content:"\e61a"}.layui-icon-file:before{content:"\e621"}.layui-icon-layouts:before{content:"\e632"}.layui-icon-rate-half:before{content:"\e6c9"}.layui-icon-add-circle-fine:before{content:"\e608"}.layui-icon-prev-circle:before{content:"\e633"}.layui-icon-read:before{content:"\e705"}.layui-icon-404:before{content:"\e61c"}.layui-icon-carousel:before{content:"\e634"}.layui-icon-help:before{content:"\e607"}.layui-icon-code-circle:before{content:"\e635"}.layui-icon-water:before{content:"\e636"}.layui-icon-username:before{content:"\e66f"}.layui-icon-find-fill:before{content:"\e670"}.layui-icon-about:before{content:"\e60b"}.layui-icon-location:before{content:"\e715"}.layui-icon-up:before{content:"\e619"}.layui-icon-pause:before{content:"\e651"}.layui-icon-date:before{content:"\e637"}.layui-icon-layim-uploadfile:before{content:"\e61d"}.layui-icon-delete:before{content:"\e640"}.layui-icon-play:before{content:"\e652"}.layui-icon-top:before{content:"\e604"}.layui-icon-friends:before{content:"\e612"}.layui-icon-refresh-3:before{content:"\e9aa"}.layui-icon-ok:before{content:"\e605"}.layui-icon-layer:before{content:"\e638"}.layui-icon-face-smile-fine:before{content:"\e60c"}.layui-icon-dollar:before{content:"\e659"}.layui-icon-group:before{content:"\e613"}.layui-icon-layim-download:before{content:"\e61e"}.layui-icon-picture-fine:before{content:"\e60d"}.layui-icon-link:before{content:"\e64c"}.layui-icon-diamond:before{content:"\e735"}.layui-icon-log:before{content:"\e60e"}.layui-icon-rate-solid:before{content:"\e67a"}.layui-icon-fonts-del:before{content:"\e64f"}.layui-icon-unlink:before{content:"\e64d"}.layui-icon-fonts-clear:before{content:"\e639"}.layui-icon-triangle-r:before{content:"\e623"}.layui-icon-circle:before{content:"\e63f"}.layui-icon-radio:before{content:"\e643"}.layui-icon-align-center:before{content:"\e647"}.layui-icon-align-right:before{content:"\e648"}.layui-icon-align-left:before{content:"\e649"}.layui-icon-loading-1:before{content:"\e63e"}.layui-icon-return:before{content:"\e65c"}.layui-icon-fonts-strong:before{content:"\e62b"}.layui-icon-upload:before{content:"\e67c"}.layui-icon-dialogue:before{content:"\e63a"}.layui-icon-video:before{content:"\e6ed"}.layui-icon-headset:before{content:"\e6fc"}.layui-icon-cellphone-fine:before{content:"\e63b"}.layui-icon-add-1:before{content:"\e654"}.layui-icon-face-smile-b:before{content:"\e650"}.layui-icon-fonts-html:before{content:"\e64b"}.layui-icon-form:before{content:"\e63c"}.layui-icon-cart:before{content:"\e657"}.layui-icon-camera-fill:before{content:"\e65d"}.layui-icon-tabs:before{content:"\e62a"}.layui-icon-fonts-code:before{content:"\e64e"}.layui-icon-fire:before{content:"\e756"}.layui-icon-set:before{content:"\e716"}.layui-icon-fonts-u:before{content:"\e646"}.layui-icon-triangle-d:before{content:"\e625"}.layui-icon-tips:before{content:"\e702"}.layui-icon-picture:before{content:"\e64a"}.layui-icon-more-vertical:before{content:"\e671"}.layui-icon-flag:before{content:"\e66c"}.layui-icon-loading:before{content:"\e63d"}.layui-icon-fonts-i:before{content:"\e644"}.layui-icon-refresh-1:before{content:"\e666"}.layui-icon-rmb:before{content:"\e65e"}.layui-icon-home:before{content:"\e68e"}.layui-icon-user:before{content:"\e770"}.layui-icon-notice:before{content:"\e667"}.layui-icon-login-weibo:before{content:"\e675"}.layui-icon-voice:before{content:"\e688"}.layui-icon-upload-drag:before{content:"\e681"}.layui-icon-login-qq:before{content:"\e676"}.layui-icon-snowflake:before{content:"\e6b1"}.layui-icon-file-b:before{content:"\e655"}.layui-icon-template:before{content:"\e663"}.layui-icon-auz:before{content:"\e672"}.layui-icon-console:before{content:"\e665"}.layui-icon-app:before{content:"\e653"}.layui-icon-prev:before{content:"\e65a"}.layui-icon-website:before{content:"\e7ae"}.layui-icon-next:before{content:"\e65b"}.layui-icon-component:before{content:"\e857"}.layui-icon-more:before{content:"\e65f"}.layui-icon-login-wechat:before{content:"\e677"}.layui-icon-shrink-right:before{content:"\e668"}.layui-icon-spread-left:before{content:"\e66b"}.layui-icon-camera:before{content:"\e660"}.layui-icon-note:before{content:"\e66e"}.layui-icon-refresh:before{content:"\e669"}.layui-icon-female:before{content:"\e661"}.layui-icon-male:before{content:"\e662"}.layui-icon-password:before{content:"\e673"}.layui-icon-senior:before{content:"\e674"}.layui-icon-theme:before{content:"\e66a"}.layui-icon-tread:before{content:"\e6c5"}.layui-icon-praise:before{content:"\e6c6"}.layui-icon-star-fill:before{content:"\e658"}.layui-icon-rate:before{content:"\e67b"}.layui-icon-template-1:before{content:"\e656"}.layui-icon-vercode:before{content:"\e679"}.layui-icon-cellphone:before{content:"\e678"}.layui-icon-screen-full:before{content:"\e622"}.layui-icon-screen-restore:before{content:"\e758"}.layui-main{width:1140px;margin:0 auto}.layui-header{z-index:1000;height:60px}.layui-header a:hover{transition:all .5s;-webkit-transition:all .5s}.layui-side{position:fixed;left:0;top:0;bottom:0;z-index:999;width:200px;overflow-x:hidden}.layui-side-scroll{position:relative;width:220px;height:100%;overflow-x:hidden}.layui-body{position:absolute;left:200px;right:0;top:0;bottom:0;z-index:998;width:auto;overflow:hidden;overflow-y:auto;box-sizing:border-box}.layui-layout-body{overflow:hidden}.layui-layout-admin .layui-header{background-color:#23262E}.layui-layout-admin .layui-side{top:60px;width:200px;overflow-x:hidden}.layui-layout-admin .layui-body{top:60px;bottom:44px}.layui-layout-admin .layui-main{width:auto;margin:0 15px}.layui-layout-admin .layui-footer{position:fixed;left:200px;right:0;bottom:0;height:44px;line-height:44px;padding:0 15px;background-color:#eee}.layui-layout-admin .layui-logo{position:absolute;left:0;top:0;width:200px;height:100%;line-height:60px;text-align:center;color:#009688;font-size:16px}.layui-layout-admin .layui-header .layui-nav{background:0 0}.layui-layout-left{position:absolute!important;left:200px;top:0}.layui-layout-right{position:absolute!important;right:0;top:0}.layui-container{position:relative;margin:0 auto;padding:0 15px;box-sizing:border-box}.layui-fluid{position:relative;margin:0 auto;padding:0 15px}.layui-row:after,.layui-row:before{content:'';display:block;clear:both}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9,.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9,.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9,.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{position:relative;display:block;box-sizing:border-box}.layui-col-xs1,.layui-col-xs10,.layui-col-xs11,.layui-col-xs12,.layui-col-xs2,.layui-col-xs3,.layui-col-xs4,.layui-col-xs5,.layui-col-xs6,.layui-col-xs7,.layui-col-xs8,.layui-col-xs9{float:left}.layui-col-xs1{width:8.33333333%}.layui-col-xs2{width:16.66666667%}.layui-col-xs3{width:25%}.layui-col-xs4{width:33.33333333%}.layui-col-xs5{width:41.66666667%}.layui-col-xs6{width:50%}.layui-col-xs7{width:58.33333333%}.layui-col-xs8{width:66.66666667%}.layui-col-xs9{width:75%}.layui-col-xs10{width:83.33333333%}.layui-col-xs11{width:91.66666667%}.layui-col-xs12{width:100%}.layui-col-xs-offset1{margin-left:8.33333333%}.layui-col-xs-offset2{margin-left:16.66666667%}.layui-col-xs-offset3{margin-left:25%}.layui-col-xs-offset4{margin-left:33.33333333%}.layui-col-xs-offset5{margin-left:41.66666667%}.layui-col-xs-offset6{margin-left:50%}.layui-col-xs-offset7{margin-left:58.33333333%}.layui-col-xs-offset8{margin-left:66.66666667%}.layui-col-xs-offset9{margin-left:75%}.layui-col-xs-offset10{margin-left:83.33333333%}.layui-col-xs-offset11{margin-left:91.66666667%}.layui-col-xs-offset12{margin-left:100%}@media screen and (max-width:768px){.layui-hide-xs{display:none!important}.layui-show-xs-block{display:block!important}.layui-show-xs-inline{display:inline!important}.layui-show-xs-inline-block{display:inline-block!important}}@media screen and (min-width:768px){.layui-container{width:750px}.layui-hide-sm{display:none!important}.layui-show-sm-block{display:block!important}.layui-show-sm-inline{display:inline!important}.layui-show-sm-inline-block{display:inline-block!important}.layui-col-sm1,.layui-col-sm10,.layui-col-sm11,.layui-col-sm12,.layui-col-sm2,.layui-col-sm3,.layui-col-sm4,.layui-col-sm5,.layui-col-sm6,.layui-col-sm7,.layui-col-sm8,.layui-col-sm9{float:left}.layui-col-sm1{width:8.33333333%}.layui-col-sm2{width:16.66666667%}.layui-col-sm3{width:25%}.layui-col-sm4{width:33.33333333%}.layui-col-sm5{width:41.66666667%}.layui-col-sm6{width:50%}.layui-col-sm7{width:58.33333333%}.layui-col-sm8{width:66.66666667%}.layui-col-sm9{width:75%}.layui-col-sm10{width:83.33333333%}.layui-col-sm11{width:91.66666667%}.layui-col-sm12{width:100%}.layui-col-sm-offset1{margin-left:8.33333333%}.layui-col-sm-offset2{margin-left:16.66666667%}.layui-col-sm-offset3{margin-left:25%}.layui-col-sm-offset4{margin-left:33.33333333%}.layui-col-sm-offset5{margin-left:41.66666667%}.layui-col-sm-offset6{margin-left:50%}.layui-col-sm-offset7{margin-left:58.33333333%}.layui-col-sm-offset8{margin-left:66.66666667%}.layui-col-sm-offset9{margin-left:75%}.layui-col-sm-offset10{margin-left:83.33333333%}.layui-col-sm-offset11{margin-left:91.66666667%}.layui-col-sm-offset12{margin-left:100%}}@media screen and (min-width:992px){.layui-container{width:970px}.layui-hide-md{display:none!important}.layui-show-md-block{display:block!important}.layui-show-md-inline{display:inline!important}.layui-show-md-inline-block{display:inline-block!important}.layui-col-md1,.layui-col-md10,.layui-col-md11,.layui-col-md12,.layui-col-md2,.layui-col-md3,.layui-col-md4,.layui-col-md5,.layui-col-md6,.layui-col-md7,.layui-col-md8,.layui-col-md9{float:left}.layui-col-md1{width:8.33333333%}.layui-col-md2{width:16.66666667%}.layui-col-md3{width:25%}.layui-col-md4{width:33.33333333%}.layui-col-md5{width:41.66666667%}.layui-col-md6{width:50%}.layui-col-md7{width:58.33333333%}.layui-col-md8{width:66.66666667%}.layui-col-md9{width:75%}.layui-col-md10{width:83.33333333%}.layui-col-md11{width:91.66666667%}.layui-col-md12{width:100%}.layui-col-md-offset1{margin-left:8.33333333%}.layui-col-md-offset2{margin-left:16.66666667%}.layui-col-md-offset3{margin-left:25%}.layui-col-md-offset4{margin-left:33.33333333%}.layui-col-md-offset5{margin-left:41.66666667%}.layui-col-md-offset6{margin-left:50%}.layui-col-md-offset7{margin-left:58.33333333%}.layui-col-md-offset8{margin-left:66.66666667%}.layui-col-md-offset9{margin-left:75%}.layui-col-md-offset10{margin-left:83.33333333%}.layui-col-md-offset11{margin-left:91.66666667%}.layui-col-md-offset12{margin-left:100%}}@media screen and (min-width:1200px){.layui-container{width:1170px}.layui-hide-lg{display:none!important}.layui-show-lg-block{display:block!important}.layui-show-lg-inline{display:inline!important}.layui-show-lg-inline-block{display:inline-block!important}.layui-col-lg1,.layui-col-lg10,.layui-col-lg11,.layui-col-lg12,.layui-col-lg2,.layui-col-lg3,.layui-col-lg4,.layui-col-lg5,.layui-col-lg6,.layui-col-lg7,.layui-col-lg8,.layui-col-lg9{float:left}.layui-col-lg1{width:8.33333333%}.layui-col-lg2{width:16.66666667%}.layui-col-lg3{width:25%}.layui-col-lg4{width:33.33333333%}.layui-col-lg5{width:41.66666667%}.layui-col-lg6{width:50%}.layui-col-lg7{width:58.33333333%}.layui-col-lg8{width:66.66666667%}.layui-col-lg9{width:75%}.layui-col-lg10{width:83.33333333%}.layui-col-lg11{width:91.66666667%}.layui-col-lg12{width:100%}.layui-col-lg-offset1{margin-left:8.33333333%}.layui-col-lg-offset2{margin-left:16.66666667%}.layui-col-lg-offset3{margin-left:25%}.layui-col-lg-offset4{margin-left:33.33333333%}.layui-col-lg-offset5{margin-left:41.66666667%}.layui-col-lg-offset6{margin-left:50%}.layui-col-lg-offset7{margin-left:58.33333333%}.layui-col-lg-offset8{margin-left:66.66666667%}.layui-col-lg-offset9{margin-left:75%}.layui-col-lg-offset10{margin-left:83.33333333%}.layui-col-lg-offset11{margin-left:91.66666667%}.layui-col-lg-offset12{margin-left:100%}}.layui-col-space1{margin:-.5px}.layui-col-space1>*{padding:.5px}.layui-col-space3{margin:-1.5px}.layui-col-space3>*{padding:1.5px}.layui-col-space5{margin:-2.5px}.layui-col-space5>*{padding:2.5px}.layui-col-space8{margin:-3.5px}.layui-col-space8>*{padding:3.5px}.layui-col-space10{margin:-5px}.layui-col-space10>*{padding:5px}.layui-col-space12{margin:-6px}.layui-col-space12>*{padding:6px}.layui-col-space15{margin:-7.5px}.layui-col-space15>*{padding:7.5px}.layui-col-space18{margin:-9px}.layui-col-space18>*{padding:9px}.layui-col-space20{margin:-10px}.layui-col-space20>*{padding:10px}.layui-col-space22{margin:-11px}.layui-col-space22>*{padding:11px}.layui-col-space25{margin:-12.5px}.layui-col-space25>*{padding:12.5px}.layui-col-space30{margin:-15px}.layui-col-space30>*{padding:15px}.layui-btn,.layui-input,.layui-select,.layui-textarea,.layui-upload-button{outline:0;transition:all .3s;-webkit-transition:all .3s;box-sizing:border-box}.layui-elem-quote{margin-bottom:10px;padding:15px;line-height:22px;border-left:5px solid #009688;border-radius:0 2px 2px 0;background-color:#f2f2f2}.layui-quote-nm{border-style:solid;border-width:1px 1px 1px 5px;background:0 0}.layui-elem-field{margin-bottom:10px;padding:0;border-width:1px;border-style:solid}.layui-elem-field legend{margin-left:20px;padding:0 10px;font-size:20px;font-weight:300}.layui-field-title{margin:10px 0 20px;border-width:1px 0 0}.layui-field-box{padding:10px 15px}.layui-field-title .layui-field-box{padding:10px 0}.layui-progress{position:relative;height:6px;border-radius:20px;background-color:#e2e2e2}.layui-progress-bar{position:absolute;left:0;top:0;width:0;max-width:100%;height:6px;border-radius:20px;text-align:right;background-color:#5FB878;transition:all .3s;-webkit-transition:all .3s}.layui-progress-big,.layui-progress-big .layui-progress-bar{height:18px;line-height:18px}.layui-progress-text{position:relative;top:-20px;line-height:18px;font-size:12px;color:#666}.layui-progress-big .layui-progress-text{position:static;padding:0 10px;color:#fff}.layui-collapse{border-width:1px;border-style:solid;border-radius:2px}.layui-colla-content,.layui-colla-item{border-top-width:1px;border-top-style:solid}.layui-colla-item:first-child{border-top:none}.layui-colla-title{position:relative;height:42px;line-height:42px;padding:0 15px 0 35px;color:#333;background-color:#f2f2f2;cursor:pointer;font-size:14px;overflow:hidden}.layui-colla-content{display:none;padding:10px 15px;line-height:22px;color:#666}.layui-colla-icon{position:absolute;left:15px;top:0;font-size:14px}.layui-card-body,.layui-card-header,.layui-form-label,.layui-form-mid,.layui-form-select,.layui-input-block,.layui-input-inline,.layui-textarea{position:relative}.layui-card{margin-bottom:15px;border-radius:2px;background-color:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.05)}.layui-card:last-child{margin-bottom:0}.layui-card-header{height:42px;line-height:42px;padding:0 15px;border-bottom:1px solid #f6f6f6;color:#333;border-radius:2px 2px 0 0;font-size:14px}.layui-bg-black,.layui-bg-blue,.layui-bg-cyan,.layui-bg-green,.layui-bg-orange,.layui-bg-red{color:#fff!important}.layui-card-body{padding:10px 15px;line-height:24px}.layui-card-body[pad15]{padding:15px}.layui-card-body[pad20]{padding:20px}.layui-card-body .layui-table{margin:5px 0}.layui-card .layui-tab{margin:0}.layui-panel-window{position:relative;padding:15px;border-radius:0;border-top:5px solid #E6E6E6;background-color:#fff}.layui-bg-red{background-color:#FF5722!important}.layui-bg-orange{background-color:#FFB800!important}.layui-bg-green{background-color:#009688!important}.layui-bg-cyan{background-color:#2F4056!important}.layui-bg-blue{background-color:#1E9FFF!important}.layui-bg-black{background-color:#393D49!important}.layui-bg-gray{background-color:#eee!important;color:#666!important}.layui-badge-rim,.layui-colla-content,.layui-colla-item,.layui-collapse,.layui-elem-field,.layui-form-pane .layui-form-item[pane],.layui-form-pane .layui-form-label,.layui-input,.layui-layedit,.layui-layedit-tool,.layui-quote-nm,.layui-select,.layui-tab-bar,.layui-tab-card,.layui-tab-title,.layui-tab-title .layui-this:after,.layui-textarea{border-color:#e6e6e6}.layui-timeline-item:before,hr{background-color:#e6e6e6}.layui-text{line-height:22px;font-size:14px;color:#666}.layui-text h1,.layui-text h2,.layui-text h3{font-weight:500;color:#333}.layui-text h1{font-size:30px}.layui-text h2{font-size:24px}.layui-text h3{font-size:18px}.layui-text a:not(.layui-btn){color:#01AAED}.layui-text a:not(.layui-btn):hover{text-decoration:underline}.layui-text ul{padding:5px 0 5px 15px}.layui-text ul li{margin-top:5px;list-style-type:disc}.layui-text em,.layui-word-aux{color:#999!important;padding:0 5px!important}.layui-btn{display:inline-block;height:38px;line-height:38px;padding:0 18px;background-color:#009688;color:#fff;white-space:nowrap;text-align:center;font-size:14px;border:none;border-radius:2px;cursor:pointer}.layui-btn:hover{opacity:.8;filter:alpha(opacity=80);color:#fff}.layui-btn:active{opacity:1;filter:alpha(opacity=100)}.layui-btn+.layui-btn{margin-left:10px}.layui-btn-container{font-size:0}.layui-btn-container .layui-btn{margin-right:10px;margin-bottom:10px}.layui-btn-container .layui-btn+.layui-btn{margin-left:0}.layui-table .layui-btn-container .layui-btn{margin-bottom:9px}.layui-btn-radius{border-radius:100px}.layui-btn .layui-icon{margin-right:3px;font-size:18px;vertical-align:bottom;vertical-align:middle\9}.layui-btn-primary{border:1px solid #C9C9C9;background-color:#fff;color:#555}.layui-btn-primary:hover{border-color:#009688;color:#333}.layui-btn-normal{background-color:#1E9FFF}.layui-btn-warm{background-color:#FFB800}.layui-btn-danger{background-color:#FF5722}.layui-btn-disabled,.layui-btn-disabled:active,.layui-btn-disabled:hover{border:1px solid #e6e6e6;background-color:#FBFBFB;color:#C9C9C9;cursor:not-allowed;opacity:1}.layui-btn-lg{height:44px;line-height:44px;padding:0 25px;font-size:16px}.layui-btn-sm{height:30px;line-height:30px;padding:0 10px;font-size:12px}.layui-btn-sm i{font-size:16px!important}.layui-btn-xs{height:22px;line-height:22px;padding:0 5px;font-size:12px}.layui-btn-xs i{font-size:14px!important}.layui-btn-group{display:inline-block;vertical-align:middle;font-size:0}.layui-btn-group .layui-btn{margin-left:0!important;margin-right:0!important;border-left:1px solid rgba(255,255,255,.5);border-radius:0}.layui-btn-group .layui-btn-primary{border-left:none}.layui-btn-group .layui-btn-primary:hover{border-color:#C9C9C9;color:#009688}.layui-btn-group .layui-btn:first-child{border-left:none;border-radius:2px 0 0 2px}.layui-btn-group .layui-btn-primary:first-child{border-left:1px solid #c9c9c9}.layui-btn-group .layui-btn:last-child{border-radius:0 2px 2px 0}.layui-btn-group .layui-btn+.layui-btn{margin-left:0}.layui-btn-group+.layui-btn-group{margin-left:10px}.layui-btn-fluid{width:100%}.layui-input,.layui-select,.layui-textarea{height:38px;line-height:1.3;line-height:38px\9;border-width:1px;border-style:solid;background-color:#fff;border-radius:2px}.layui-input::-webkit-input-placeholder,.layui-select::-webkit-input-placeholder,.layui-textarea::-webkit-input-placeholder{line-height:1.3}.layui-input,.layui-textarea{display:block;width:100%;padding-left:10px}.layui-input:hover,.layui-textarea:hover{border-color:#D2D2D2!important}.layui-input:focus,.layui-textarea:focus{border-color:#C9C9C9!important}.layui-textarea{min-height:100px;height:auto;line-height:20px;padding:6px 10px;resize:vertical}.layui-select{padding:0 10px}.layui-form input[type=checkbox],.layui-form input[type=radio],.layui-form select{display:none}.layui-form [lay-ignore]{display:initial}.layui-form-item{margin-bottom:15px;clear:both;*zoom:1}.layui-form-item:after{content:'\20';clear:both;*zoom:1;display:block;height:0}.layui-form-label{float:left;display:block;padding:9px 15px;width:80px;font-weight:400;line-height:20px;text-align:right}.layui-form-label-col{display:block;float:none;padding:9px 0;line-height:20px;text-align:left}.layui-form-item .layui-inline{margin-bottom:5px;margin-right:10px}.layui-input-block{margin-left:110px;min-height:36px}.layui-input-inline{display:inline-block;vertical-align:middle}.layui-form-item .layui-input-inline{float:left;width:190px;margin-right:10px}.layui-form-text .layui-input-inline{width:auto}.layui-form-mid{float:left;display:block;padding:9px 0!important;line-height:20px;margin-right:10px}.layui-form-danger+.layui-form-select .layui-input,.layui-form-danger:focus{border-color:#FF5722!important}.layui-form-select .layui-input{padding-right:30px;cursor:pointer}.layui-form-select .layui-edge{position:absolute;right:10px;top:50%;margin-top:-3px;cursor:pointer;border-width:6px;border-top-color:#c2c2c2;border-top-style:solid;transition:all .3s;-webkit-transition:all .3s}.layui-form-select dl{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:999;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box}.layui-form-select dl dd,.layui-form-select dl dt{padding:0 10px;line-height:36px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.layui-form-select dl dt{font-size:12px;color:#999}.layui-form-select dl dd{cursor:pointer}.layui-form-select dl dd:hover{background-color:#f2f2f2}.layui-form-select .layui-select-group dd{padding-left:20px}.layui-form-select dl dd.layui-select-tips{padding-left:10px!important;color:#999}.layui-form-select dl dd.layui-this{background-color:#5FB878;color:#fff}.layui-form-checkbox,.layui-form-select dl dd.layui-disabled{background-color:#fff}.layui-form-selected dl{display:block}.layui-form-checkbox,.layui-form-checkbox *,.layui-form-switch{display:inline-block;vertical-align:middle}.layui-form-selected .layui-edge{margin-top:-9px;-webkit-transform:rotate(180deg);transform:rotate(180deg);margin-top:-3px\9}:root .layui-form-selected .layui-edge{margin-top:-9px\0/IE9}.layui-form-selectup dl{top:auto;bottom:42px}.layui-select-none{margin:5px 0;text-align:center;color:#999}.layui-select-disabled .layui-disabled{border-color:#eee!important}.layui-select-disabled .layui-edge{border-top-color:#d2d2d2}.layui-form-checkbox{position:relative;height:30px;line-height:30px;margin-right:10px;padding-right:30px;cursor:pointer;font-size:0;-webkit-transition:.1s linear;transition:.1s linear;box-sizing:border-box}.layui-form-checkbox span{padding:0 10px;height:100%;font-size:14px;border-radius:2px 0 0 2px;background-color:#d2d2d2;color:#fff;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.layui-form-checkbox:hover span{background-color:#c2c2c2}.layui-form-checkbox i{position:absolute;right:0;top:0;width:30px;height:28px;border:1px solid #d2d2d2;border-left:none;border-radius:0 2px 2px 0;color:#fff;font-size:20px;text-align:center}.layui-form-checkbox:hover i{border-color:#c2c2c2;color:#c2c2c2}.layui-form-checked,.layui-form-checked:hover{border-color:#5FB878}.layui-form-checked span,.layui-form-checked:hover span{background-color:#5FB878}.layui-form-checked i,.layui-form-checked:hover i{color:#5FB878}.layui-form-item .layui-form-checkbox{margin-top:4px}.layui-form-checkbox[lay-skin=primary]{height:auto!important;line-height:normal!important;border:none!important;margin-right:0;padding-right:0;background:0 0}.layui-form-checkbox[lay-skin=primary] span{float:right;padding-right:15px;line-height:18px;background:0 0;color:#666}.layui-form-checkbox[lay-skin=primary] i{position:relative;top:0;width:16px;height:16px;line-height:16px;border:1px solid #d2d2d2;font-size:12px;border-radius:2px;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-checkbox[lay-skin=primary]:hover i{border-color:#5FB878;color:#fff}.layui-form-checked[lay-skin=primary] i{border-color:#5FB878;background-color:#5FB878;color:#fff}.layui-checkbox-disbaled[lay-skin=primary] span{background:0 0!important;color:#c2c2c2}.layui-checkbox-disbaled[lay-skin=primary]:hover i{border-color:#d2d2d2}.layui-form-item .layui-form-checkbox[lay-skin=primary]{margin-top:10px}.layui-form-switch{position:relative;height:22px;line-height:22px;min-width:35px;padding:0 5px;margin-top:8px;border:1px solid #d2d2d2;border-radius:20px;cursor:pointer;background-color:#fff;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch i{position:absolute;left:5px;top:3px;width:16px;height:16px;border-radius:20px;background-color:#d2d2d2;-webkit-transition:.1s linear;transition:.1s linear}.layui-form-switch em{position:relative;top:0;width:25px;margin-left:21px;padding:0!important;text-align:center!important;color:#999!important;font-style:normal!important;font-size:12px}.layui-form-onswitch{border-color:#5FB878;background-color:#5FB878}.layui-checkbox-disbaled,.layui-checkbox-disbaled i{border-color:#e2e2e2!important}.layui-form-onswitch i{left:100%;margin-left:-21px;background-color:#fff}.layui-form-onswitch em{margin-left:5px;margin-right:21px;color:#fff!important}.layui-checkbox-disbaled span{background-color:#e2e2e2!important}.layui-checkbox-disbaled:hover i{color:#fff!important}[lay-radio]{display:none}.layui-form-radio,.layui-form-radio *{display:inline-block;vertical-align:middle}.layui-form-radio{line-height:28px;margin:6px 10px 0 0;padding-right:10px;cursor:pointer;font-size:0}.layui-form-radio *{font-size:14px}.layui-form-radio>i{margin-right:8px;font-size:22px;color:#c2c2c2}.layui-form-radio>i:hover,.layui-form-radioed>i{color:#5FB878}.layui-radio-disbaled>i{color:#e2e2e2!important}.layui-form-pane .layui-form-label{width:110px;padding:8px 15px;height:38px;line-height:20px;border-width:1px;border-style:solid;border-radius:2px 0 0 2px;text-align:center;background-color:#FBFBFB;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;box-sizing:border-box}.layui-form-pane .layui-input-inline{margin-left:-1px}.layui-form-pane .layui-input-block{margin-left:110px;left:-1px}.layui-form-pane .layui-input{border-radius:0 2px 2px 0}.layui-form-pane .layui-form-text .layui-form-label{float:none;width:100%;border-radius:2px;box-sizing:border-box;text-align:left}.layui-form-pane .layui-form-text .layui-input-inline{display:block;margin:0;top:-1px;clear:both}.layui-form-pane .layui-form-text .layui-input-block{margin:0;left:0;top:-1px}.layui-form-pane .layui-form-text .layui-textarea{min-height:100px;border-radius:0 0 2px 2px}.layui-form-pane .layui-form-checkbox{margin:4px 0 4px 10px}.layui-form-pane .layui-form-radio,.layui-form-pane .layui-form-switch{margin-top:6px;margin-left:10px}.layui-form-pane .layui-form-item[pane]{position:relative;border-width:1px;border-style:solid}.layui-form-pane .layui-form-item[pane] .layui-form-label{position:absolute;left:0;top:0;height:100%;border-width:0 1px 0 0}.layui-form-pane .layui-form-item[pane] .layui-input-inline{margin-left:110px}@media screen and (max-width:450px){.layui-form-item .layui-form-label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-form-item .layui-inline{display:block;margin-right:0;margin-bottom:20px;clear:both}.layui-form-item .layui-inline:after{content:'\20';clear:both;display:block;height:0}.layui-form-item .layui-input-inline{display:block;float:none;left:-3px;width:auto;margin:0 0 10px 112px}.layui-form-item .layui-input-inline+.layui-form-mid{margin-left:110px;top:-5px;padding:0}.layui-form-item .layui-form-checkbox{margin-right:5px;margin-bottom:5px}}.layui-layedit{border-width:1px;border-style:solid;border-radius:2px}.layui-layedit-tool{padding:3px 5px;border-bottom-width:1px;border-bottom-style:solid;font-size:0}.layedit-tool-fixed{position:fixed;top:0;border-top:1px solid #e2e2e2}.layui-layedit-tool .layedit-tool-mid,.layui-layedit-tool .layui-icon{display:inline-block;vertical-align:middle;text-align:center;font-size:14px}.layui-layedit-tool .layui-icon{position:relative;width:32px;height:30px;line-height:30px;margin:3px 5px;color:#777;cursor:pointer;border-radius:2px}.layui-layedit-tool .layui-icon:hover{color:#393D49}.layui-layedit-tool .layui-icon:active{color:#000}.layui-layedit-tool .layedit-tool-active{background-color:#e2e2e2;color:#000}.layui-layedit-tool .layui-disabled,.layui-layedit-tool .layui-disabled:hover{color:#d2d2d2;cursor:not-allowed}.layui-layedit-tool .layedit-tool-mid{width:1px;height:18px;margin:0 10px;background-color:#d2d2d2}.layedit-tool-html{width:50px!important;font-size:30px!important}.layedit-tool-b,.layedit-tool-code,.layedit-tool-help{font-size:16px!important}.layedit-tool-d,.layedit-tool-face,.layedit-tool-image,.layedit-tool-unlink{font-size:18px!important}.layedit-tool-image input{position:absolute;font-size:0;left:0;top:0;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-layedit-iframe iframe{display:block;width:100%}#LAY_layedit_code{overflow:hidden}.layui-laypage{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;margin:10px 0;font-size:0}.layui-laypage>a:first-child,.layui-laypage>a:first-child em{border-radius:2px 0 0 2px}.layui-laypage>a:last-child,.layui-laypage>a:last-child em{border-radius:0 2px 2px 0}.layui-laypage>:first-child{margin-left:0!important}.layui-laypage>:last-child{margin-right:0!important}.layui-laypage a,.layui-laypage button,.layui-laypage input,.layui-laypage select,.layui-laypage span{border:1px solid #e2e2e2}.layui-laypage a,.layui-laypage span{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding:0 15px;height:28px;line-height:28px;margin:0 -1px 5px 0;background-color:#fff;color:#333;font-size:12px}.layui-laypage a:hover{color:#009688}.layui-laypage em{font-style:normal}.layui-laypage .layui-laypage-spr{color:#999;font-weight:700}.layui-laypage a{text-decoration:none}.layui-laypage .layui-laypage-curr{position:relative}.layui-laypage .layui-laypage-curr em{position:relative;color:#fff}.layui-laypage .layui-laypage-curr .layui-laypage-em{position:absolute;left:-1px;top:-1px;padding:1px;width:100%;height:100%;background-color:#009688}.layui-laypage-em{border-radius:2px}.layui-laypage-next em,.layui-laypage-prev em{font-family:Sim sun;font-size:16px}.layui-laypage .layui-laypage-count,.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh,.layui-laypage .layui-laypage-skip{margin-left:10px;margin-right:10px;padding:0;border:none}.layui-laypage .layui-laypage-limits,.layui-laypage .layui-laypage-refresh{vertical-align:top}.layui-laypage .layui-laypage-refresh i{font-size:18px;cursor:pointer}.layui-laypage select{height:22px;padding:3px;border-radius:2px;cursor:pointer}.layui-laypage .layui-laypage-skip{height:30px;line-height:30px;color:#999}.layui-laypage button,.layui-laypage input{height:30px;line-height:30px;border-radius:2px;vertical-align:top;background-color:#fff;box-sizing:border-box}.layui-laypage input{display:inline-block;width:40px;margin:0 10px;padding:0 3px;text-align:center}.layui-laypage input:focus,.layui-laypage select:focus{border-color:#009688!important}.layui-laypage button{margin-left:10px;padding:0 10px;cursor:pointer}.layui-table,.layui-table-view{margin:10px 0}.layui-flow-more{margin:10px 0;text-align:center;color:#999;font-size:14px}.layui-flow-more a{height:32px;line-height:32px}.layui-flow-more a *{display:inline-block;vertical-align:top}.layui-flow-more a cite{padding:0 20px;border-radius:3px;background-color:#eee;color:#333;font-style:normal}.layui-flow-more a cite:hover{opacity:.8}.layui-flow-more a i{font-size:30px;color:#737383}.layui-table{width:100%;background-color:#fff;color:#666}.layui-table tr{transition:all .3s;-webkit-transition:all .3s}.layui-table th{text-align:left;font-weight:400}.layui-table tbody tr:hover,.layui-table thead tr,.layui-table-click,.layui-table-header,.layui-table-hover,.layui-table-mend,.layui-table-patch,.layui-table-tool,.layui-table[lay-even] tr:nth-child(even){background-color:#f2f2f2}.layui-table td,.layui-table th,.layui-table-fixed-r,.layui-table-header,.layui-table-page,.layui-table-tips-main,.layui-table-tool,.layui-table-view,.layui-table[lay-skin=line],.layui-table[lay-skin=row]{border-width:1px;border-style:solid;border-color:#e6e6e6}.layui-table td,.layui-table th{position:relative;padding:9px 15px;min-height:20px;line-height:20px;font-size:14px}.layui-table[lay-skin=line] td,.layui-table[lay-skin=line] th{border-width:0 0 1px}.layui-table[lay-skin=row] td,.layui-table[lay-skin=row] th{border-width:0 1px 0 0}.layui-table[lay-skin=nob] td,.layui-table[lay-skin=nob] th{border:none}.layui-table img{max-width:100px}.layui-table[lay-size=lg] td,.layui-table[lay-size=lg] th{padding:15px 30px}.layui-table-view .layui-table[lay-size=lg] .layui-table-cell{height:40px;line-height:40px}.layui-table[lay-size=sm] td,.layui-table[lay-size=sm] th{font-size:12px;padding:5px 10px}.layui-table-view .layui-table[lay-size=sm] .layui-table-cell{height:20px;line-height:20px}.layui-table[lay-data]{display:none}.layui-table-box,.layui-table-view{position:relative;overflow:hidden}.layui-table-view .layui-table{position:relative;width:auto;margin:0}.layui-table-body,.layui-table-header .layui-table,.layui-table-page{margin-bottom:-1px}.layui-table-view .layui-table[lay-skin=line]{border-width:0 1px 0 0}.layui-table-view .layui-table[lay-skin=row]{border-width:0 0 1px}.layui-table-view .layui-table td,.layui-table-view .layui-table th{padding:5px 0;border-top:none;border-left:none}.layui-table-view .layui-table td{cursor:default}.layui-table-view .layui-form-checkbox[lay-skin=primary] i{width:18px;height:18px}.layui-table-header{border-width:0 0 1px;overflow:hidden}.layui-table-sort{width:10px;height:20px;margin-left:5px;cursor:pointer!important}.layui-table-sort .layui-edge{position:absolute;left:5px;border-width:5px}.layui-table-sort .layui-table-sort-asc{top:4px;border-top:none;border-bottom-style:solid;border-bottom-color:#b2b2b2}.layui-table-sort .layui-table-sort-asc:hover{border-bottom-color:#666}.layui-table-sort .layui-table-sort-desc{bottom:4px;border-bottom:none;border-top-style:solid;border-top-color:#b2b2b2}.layui-table-sort .layui-table-sort-desc:hover{border-top-color:#666}.layui-table-sort[lay-sort=asc] .layui-table-sort-asc{border-bottom-color:#000}.layui-table-sort[lay-sort=desc] .layui-table-sort-desc{border-top-color:#000}.layui-table-cell{height:28px;line-height:28px;padding:0 15px;position:relative;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;box-sizing:border-box}.layui-table-cell .layui-form-checkbox[lay-skin=primary],.layui-table-cell .layui-form-radio[lay-skin=primary]{top:-1px;vertical-align:middle}.layui-table-cell .layui-form-radio{padding-right:0}.layui-table-cell .layui-form-radio>i{margin-right:0}.layui-table-cell .layui-table-link{color:#01AAED}.laytable-cell-checkbox,.laytable-cell-numbers,.laytable-cell-radio,.laytable-cell-space{padding:0;text-align:center}.layui-table-body{position:relative;overflow:auto;margin-right:-1px}.layui-table-body .layui-none{line-height:40px;text-align:center;color:#999}.layui-table-fixed{position:absolute;left:0;top:0}.layui-table-fixed .layui-table-body{overflow:hidden}.layui-table-fixed-l{box-shadow:0 -1px 8px rgba(0,0,0,.08)}.layui-table-fixed-r{left:auto;right:-1px;border-width:0 0 0 1px;box-shadow:-1px 0 8px rgba(0,0,0,.08)}.layui-table-fixed-r .layui-table-header{position:relative;overflow:visible}.layui-table-mend{position:absolute;right:-49px;top:0;height:100%;width:50px}.layui-table-tool{position:relative;width:100%;height:50px;line-height:30px;padding:10px 15px;border-width:0 0 1px}.layui-table-page{position:relative;width:100%;padding:7px 7px 0;border-width:1px 0 0;height:41px;font-size:12px}.layui-table-page>div{height:26px}.layui-table-page .layui-laypage{margin:0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span{height:26px;line-height:26px;margin-bottom:10px;border:none;background:0 0}.layui-table-page .layui-laypage a,.layui-table-page .layui-laypage span.layui-laypage-curr{padding:0 12px}.layui-table-page .layui-laypage span{margin-left:0;padding:0}.layui-table-page .layui-laypage .layui-laypage-prev{margin-left:-7px!important}.layui-table-page .layui-laypage .layui-laypage-curr .layui-laypage-em{left:0;top:0;padding:0}.layui-table-page .layui-laypage button,.layui-table-page .layui-laypage input{height:26px;line-height:26px}.layui-table-page .layui-laypage input{width:40px}.layui-table-page .layui-laypage button{padding:0 10px}.layui-table-page select{height:18px}.layui-table-view select[lay-ignore]{display:inline-block}.layui-table-patch .layui-table-cell{padding:0;width:30px}.layui-table-edit{position:absolute;left:0;top:0;width:100%;height:100%;padding:0 14px 1px;border-radius:0;box-shadow:1px 1px 20px rgba(0,0,0,.15)}.layui-table-edit:focus{border-color:#5FB878!important}select.layui-table-edit{padding:0 0 0 10px;border-color:#C9C9C9}.layui-table-view .layui-form-checkbox,.layui-table-view .layui-form-radio,.layui-table-view .layui-form-switch{top:0;margin:0;box-sizing:content-box}.layui-table-view .layui-form-checkbox{top:-1px;height:26px;line-height:26px}body .layui-table-tips .layui-layer-content{background:0 0;padding:0;box-shadow:0 1px 6px rgba(0,0,0,.1)}.layui-table-tips-main{margin:-44px 0 0 -1px;max-height:150px;padding:8px 15px;font-size:14px;overflow-y:scroll;background-color:#fff;color:#333}.layui-table-tips-c{position:absolute;right:-3px;top:-12px;width:18px;height:18px;padding:3px;text-align:center;font-weight:700;border-radius:100%;font-size:14px;cursor:pointer;background-color:#666}.layui-table-tips-c:hover{background-color:#999}.layui-upload-file{display:none!important;opacity:.01;filter:Alpha(opacity=1)}.layui-upload-drag,.layui-upload-form,.layui-upload-wrap{display:inline-block}.layui-upload-list{margin:10px 0}.layui-upload-choose{padding:0 10px;color:#999}.layui-upload-drag{position:relative;padding:30px;border:1px dashed #e2e2e2;background-color:#fff;text-align:center;cursor:pointer;color:#999}.layui-upload-drag .layui-icon{font-size:50px;color:#009688}.layui-upload-drag[lay-over]{border-color:#009688}.layui-upload-iframe{position:absolute;width:0;height:0;border:0;visibility:hidden}.layui-upload-wrap{position:relative;vertical-align:middle}.layui-upload-wrap .layui-upload-file{display:block!important;position:absolute;left:0;top:0;z-index:10;font-size:100px;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layui-rate,.layui-rate *{display:inline-block;vertical-align:middle}.layui-rate{padding:10px 5px 10px 0;font-size:0}.layui-rate li i.layui-icon{font-size:20px;color:#FFB800;margin-right:5px;transition:all .3s;-webkit-transition:all .3s}.layui-rate li i:hover{cursor:pointer;transform:scale(1.12);-webkit-transform:scale(1.12)}.layui-rate[readonly] li i:hover{cursor:default;transform:scale(1)}.layui-code{position:relative;margin:10px 0;padding:15px;line-height:20px;border:1px solid #ddd;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New;font-size:12px}.layui-tree{line-height:26px}.layui-tree li{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-tree li .layui-tree-spread,.layui-tree li a{display:inline-block;vertical-align:top;height:26px;*display:inline;*zoom:1;cursor:pointer}.layui-tree li a{font-size:0}.layui-tree li a i{font-size:16px}.layui-tree li a cite{padding:0 6px;font-size:14px;font-style:normal}.layui-tree li i{padding-left:6px;color:#333;-moz-user-select:none}.layui-tree li .layui-tree-check{font-size:13px}.layui-tree li .layui-tree-check:hover{color:#009E94}.layui-tree li ul{display:none;margin-left:20px}.layui-tree li .layui-tree-enter{line-height:24px;border:1px dotted #000}.layui-tree-drag{display:none;position:absolute;left:-666px;top:-666px;background-color:#f2f2f2;padding:5px 10px;border:1px dotted #000;white-space:nowrap}.layui-tree-drag i{padding-right:5px}.layui-nav{position:relative;padding:0 20px;background-color:#393D49;color:#fff;border-radius:2px;font-size:0;box-sizing:border-box}.layui-nav *{font-size:14px}.layui-nav .layui-nav-item{position:relative;display:inline-block;*display:inline;*zoom:1;vertical-align:middle;line-height:60px}.layui-nav .layui-nav-item a{display:block;padding:0 20px;color:#fff;color:rgba(255,255,255,.7);transition:all .3s;-webkit-transition:all .3s}.layui-nav .layui-this:after,.layui-nav-bar,.layui-nav-tree .layui-nav-itemed:after{position:absolute;left:0;top:0;width:0;height:5px;background-color:#5FB878;transition:all .2s;-webkit-transition:all .2s}.layui-nav-bar{z-index:1000}.layui-nav .layui-nav-item a:hover,.layui-nav .layui-this a{color:#fff}.layui-nav .layui-this:after{content:'';top:auto;bottom:0;width:100%}.layui-nav-img{width:30px;height:30px;margin-right:10px;border-radius:50%}.layui-nav .layui-nav-more{content:'';width:0;height:0;border-style:solid dashed dashed;border-color:#fff transparent transparent;overflow:hidden;cursor:pointer;transition:all .2s;-webkit-transition:all .2s;position:absolute;top:50%;right:3px;margin-top:-3px;border-width:6px;border-top-color:rgba(255,255,255,.7)}.layui-nav .layui-nav-mored,.layui-nav-itemed>a .layui-nav-more{margin-top:-9px;border-style:dashed dashed solid;border-color:transparent transparent #fff}.layui-nav-child{display:none;position:absolute;left:0;top:65px;min-width:100%;line-height:36px;padding:5px 0;box-shadow:0 2px 4px rgba(0,0,0,.12);border:1px solid #d2d2d2;background-color:#fff;z-index:100;border-radius:2px;white-space:nowrap}.layui-nav .layui-nav-child a{color:#333}.layui-nav .layui-nav-child a:hover{background-color:#f2f2f2;color:#000}.layui-nav-child dd{position:relative}.layui-nav .layui-nav-child dd.layui-this a,.layui-nav-child dd.layui-this{background-color:#5FB878;color:#fff}.layui-nav-child dd.layui-this:after{display:none}.layui-nav-tree{width:200px;padding:0}.layui-nav-tree .layui-nav-item{display:block;width:100%;line-height:45px}.layui-nav-tree .layui-nav-item a{position:relative;height:45px;line-height:45px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-nav-tree .layui-nav-item a:hover{background-color:#4E5465}.layui-nav-tree .layui-nav-bar{width:5px;height:0;background-color:#009688}.layui-nav-tree .layui-nav-child dd.layui-this,.layui-nav-tree .layui-nav-child dd.layui-this a,.layui-nav-tree .layui-this,.layui-nav-tree .layui-this>a,.layui-nav-tree .layui-this>a:hover{background-color:#009688;color:#fff}.layui-nav-tree .layui-this:after{display:none}.layui-nav-itemed>a,.layui-nav-tree .layui-nav-title a,.layui-nav-tree .layui-nav-title a:hover{color:#fff!important}.layui-nav-tree .layui-nav-child{position:relative;z-index:0;top:0;border:none;box-shadow:none}.layui-nav-tree .layui-nav-child a{height:40px;line-height:40px;color:#fff;color:rgba(255,255,255,.7)}.layui-nav-tree .layui-nav-child,.layui-nav-tree .layui-nav-child a:hover{background:0 0;color:#fff}.layui-nav-tree .layui-nav-more{right:10px}.layui-nav-itemed>.layui-nav-child{display:block;padding:0;background-color:rgba(0,0,0,.3)!important}.layui-nav-itemed>.layui-nav-child>.layui-this>.layui-nav-child{display:block}.layui-nav-side{position:fixed;top:0;bottom:0;left:0;overflow-x:hidden;z-index:999}.layui-bg-blue .layui-nav-bar,.layui-bg-blue .layui-nav-itemed:after,.layui-bg-blue .layui-this:after{background-color:#93D1FF}.layui-bg-blue .layui-nav-child dd.layui-this{background-color:#1E9FFF}.layui-bg-blue .layui-nav-itemed>a,.layui-nav-tree.layui-bg-blue .layui-nav-title a,.layui-nav-tree.layui-bg-blue .layui-nav-title a:hover{background-color:#007DDB!important}.layui-breadcrumb{visibility:hidden;font-size:0}.layui-breadcrumb>*{font-size:14px}.layui-breadcrumb a{color:#999!important}.layui-breadcrumb a:hover{color:#5FB878!important}.layui-breadcrumb a cite{color:#666;font-style:normal}.layui-breadcrumb span[lay-separator]{margin:0 10px;color:#999}.layui-tab{margin:10px 0;text-align:left!important}.layui-tab[overflow]>.layui-tab-title{overflow:hidden}.layui-tab-title{position:relative;left:0;height:40px;white-space:nowrap;font-size:0;border-bottom-width:1px;border-bottom-style:solid;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;font-size:14px;transition:all .2s;-webkit-transition:all .2s;position:relative;line-height:40px;min-width:65px;padding:0 15px;text-align:center;cursor:pointer}.layui-tab-title li a{display:block}.layui-tab-title .layui-this{color:#000}.layui-tab-title .layui-this:after{position:absolute;left:0;top:0;content:'';width:100%;height:41px;border-width:1px;border-style:solid;border-bottom-color:#fff;border-radius:2px 2px 0 0;box-sizing:border-box;pointer-events:none}.layui-tab-bar{position:absolute;right:0;top:0;z-index:10;width:30px;height:39px;line-height:39px;border-width:1px;border-style:solid;border-radius:2px;text-align:center;background-color:#fff;cursor:pointer}.layui-tab-bar .layui-icon{position:relative;display:inline-block;top:3px;transition:all .3s;-webkit-transition:all .3s}.layui-tab-item{display:none}.layui-tab-more{padding-right:30px;height:auto!important;white-space:normal!important}.layui-tab-more li.layui-this:after{border-bottom-color:#e2e2e2;border-radius:2px}.layui-tab-more .layui-tab-bar .layui-icon{top:-2px;top:3px\9;-webkit-transform:rotate(180deg);transform:rotate(180deg)}:root .layui-tab-more .layui-tab-bar .layui-icon{top:-2px\0/IE9}.layui-tab-content{padding:10px}.layui-tab-title li .layui-tab-close{position:relative;display:inline-block;width:18px;height:18px;line-height:20px;margin-left:8px;top:1px;text-align:center;font-size:14px;color:#c2c2c2;transition:all .2s;-webkit-transition:all .2s}.layui-tab-title li .layui-tab-close:hover{border-radius:2px;background-color:#FF5722;color:#fff}.layui-tab-brief>.layui-tab-title .layui-this{color:#009688}.layui-tab-brief>.layui-tab-more li.layui-this:after,.layui-tab-brief>.layui-tab-title .layui-this:after{border:none;border-radius:0;border-bottom:2px solid #5FB878}.layui-tab-brief[overflow]>.layui-tab-title .layui-this:after{top:-1px}.layui-tab-card{border-width:1px;border-style:solid;border-radius:2px;box-shadow:0 2px 5px 0 rgba(0,0,0,.1)}.layui-tab-card>.layui-tab-title{background-color:#f2f2f2}.layui-tab-card>.layui-tab-title li{margin-right:-1px;margin-left:-1px}.layui-tab-card>.layui-tab-title .layui-this{background-color:#fff}.layui-tab-card>.layui-tab-title .layui-this:after{border-top:none;border-width:1px;border-bottom-color:#fff}.layui-tab-card>.layui-tab-title .layui-tab-bar{height:40px;line-height:40px;border-radius:0;border-top:none;border-right:none}.layui-tab-card>.layui-tab-more .layui-this{background:0 0;color:#5FB878}.layui-tab-card>.layui-tab-more .layui-this:after{border:none}.layui-timeline{padding-left:5px}.layui-timeline-item{position:relative;padding-bottom:20px}.layui-timeline-axis{position:absolute;left:-5px;top:0;z-index:10;width:20px;height:20px;line-height:20px;background-color:#fff;color:#5FB878;border-radius:50%;text-align:center;cursor:pointer}.layui-timeline-axis:hover{color:#FF5722}.layui-timeline-item:before{content:'';position:absolute;left:5px;top:0;z-index:0;width:1px;height:100%}.layui-timeline-item:last-child:before{display:none}.layui-timeline-item:first-child:before{display:block}.layui-timeline-content{padding-left:25px}.layui-timeline-title{position:relative;margin-bottom:10px}.layui-badge,.layui-badge-dot,.layui-badge-rim{position:relative;display:inline-block;padding:0 6px;font-size:12px;text-align:center;background-color:#FF5722;color:#fff;border-radius:2px}.layui-badge{height:18px;line-height:18px}.layui-badge-dot{width:8px;height:8px;padding:0;border-radius:50%}.layui-badge-rim{height:18px;line-height:18px;border-width:1px;border-style:solid;background-color:#fff;color:#666}.layui-btn .layui-badge,.layui-btn .layui-badge-dot{margin-left:5px}.layui-nav .layui-badge,.layui-nav .layui-badge-dot{position:absolute;top:50%;margin:-8px 6px 0}.layui-tab-title .layui-badge,.layui-tab-title .layui-badge-dot{left:5px;top:-2px}.layui-carousel{position:relative;left:0;top:0;background-color:#f8f8f8}.layui-carousel>[carousel-item]{position:relative;width:100%;height:100%;overflow:hidden}.layui-carousel>[carousel-item]:before{position:absolute;content:'\e63d';left:50%;top:50%;width:100px;line-height:20px;margin:-10px 0 0 -50px;text-align:center;color:#c2c2c2;font-family:layui-icon!important;font-size:30px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-carousel>[carousel-item]>*{display:none;position:absolute;left:0;top:0;width:100%;height:100%;background-color:#f8f8f8;transition-duration:.3s;-webkit-transition-duration:.3s}.layui-carousel-updown>*{-webkit-transition:.3s ease-in-out up;transition:.3s ease-in-out up}.layui-carousel-arrow{display:none\9;opacity:0;position:absolute;left:10px;top:50%;margin-top:-18px;width:36px;height:36px;line-height:36px;text-align:center;font-size:20px;border:0;border-radius:50%;background-color:rgba(0,0,0,.2);color:#fff;-webkit-transition-duration:.3s;transition-duration:.3s;cursor:pointer}.layui-carousel-arrow[lay-type=add]{left:auto!important;right:10px}.layui-carousel:hover .layui-carousel-arrow[lay-type=add],.layui-carousel[lay-arrow=always] .layui-carousel-arrow[lay-type=add]{right:20px}.layui-carousel[lay-arrow=always] .layui-carousel-arrow{opacity:1;left:20px}.layui-carousel[lay-arrow=none] .layui-carousel-arrow{display:none}.layui-carousel-arrow:hover,.layui-carousel-ind ul:hover{background-color:rgba(0,0,0,.35)}.layui-carousel:hover .layui-carousel-arrow{display:block\9;opacity:1;left:20px}.layui-carousel-ind{position:relative;top:-35px;width:100%;line-height:0!important;text-align:center;font-size:0}.layui-carousel[lay-indicator=outside]{margin-bottom:30px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind{top:10px}.layui-carousel[lay-indicator=outside] .layui-carousel-ind ul{background-color:rgba(0,0,0,.5)}.layui-carousel[lay-indicator=none] .layui-carousel-ind{display:none}.layui-carousel-ind ul{display:inline-block;padding:5px;background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li{display:inline-block;width:10px;height:10px;margin:0 3px;font-size:14px;background-color:#e2e2e2;background-color:rgba(255,255,255,.5);border-radius:50%;cursor:pointer;-webkit-transition-duration:.3s;transition-duration:.3s}.layui-carousel-ind li:hover{background-color:rgba(255,255,255,.7)}.layui-carousel-ind li.layui-this{background-color:#fff}.layui-carousel>[carousel-item]>.layui-carousel-next,.layui-carousel>[carousel-item]>.layui-carousel-prev,.layui-carousel>[carousel-item]>.layui-this{display:block}.layui-carousel>[carousel-item]>.layui-this{left:0}.layui-carousel>[carousel-item]>.layui-carousel-prev{left:-100%}.layui-carousel>[carousel-item]>.layui-carousel-next{left:100%}.layui-carousel>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel>[carousel-item]>.layui-carousel-prev.layui-carousel-right{left:0}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-left{left:-100%}.layui-carousel>[carousel-item]>.layui-this.layui-carousel-right{left:100%}.layui-carousel[lay-anim=updown] .layui-carousel-arrow{left:50%!important;top:20px;margin:0 0 0 -18px}.layui-carousel[lay-anim=updown]>[carousel-item]>*,.layui-carousel[lay-anim=fade]>[carousel-item]>*{left:0!important}.layui-carousel[lay-anim=updown] .layui-carousel-arrow[lay-type=add]{top:auto!important;bottom:20px}.layui-carousel[lay-anim=updown] .layui-carousel-ind{position:absolute;top:50%;right:20px;width:auto;height:auto}.layui-carousel[lay-anim=updown] .layui-carousel-ind ul{padding:3px 5px}.layui-carousel[lay-anim=updown] .layui-carousel-ind li{display:block;margin:6px 0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next{top:100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{top:0}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-left{top:-100%}.layui-carousel[lay-anim=updown]>[carousel-item]>.layui-this.layui-carousel-right{top:100%}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev{opacity:0}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-next.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-carousel-prev.layui-carousel-right{opacity:1}.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-left,.layui-carousel[lay-anim=fade]>[carousel-item]>.layui-this.layui-carousel-right{opacity:0}.layui-fixbar{position:fixed;right:15px;bottom:15px;z-index:9999}.layui-fixbar li{width:50px;height:50px;line-height:50px;margin-bottom:1px;text-align:center;cursor:pointer;font-size:30px;background-color:#9F9F9F;color:#fff;border-radius:2px;opacity:.95}.layui-fixbar li:hover{opacity:.85}.layui-fixbar li:active{opacity:1}.layui-fixbar .layui-fixbar-top{display:none;font-size:40px}body .layui-util-face{border:none;background:0 0}body .layui-util-face .layui-layer-content{padding:0;background-color:#fff;color:#666;box-shadow:none}.layui-util-face .layui-layer-TipsG{display:none}.layui-util-face ul{position:relative;width:372px;padding:10px;border:1px solid #D9D9D9;background-color:#fff;box-shadow:0 0 20px rgba(0,0,0,.2)}.layui-util-face ul li{cursor:pointer;float:left;border:1px solid #e8e8e8;height:22px;width:26px;overflow:hidden;margin:-1px 0 0 -1px;padding:4px 2px;text-align:center}.layui-util-face ul li:hover{position:relative;z-index:2;border:1px solid #eb7350;background:#fff9ec}.layui-anim{-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-anim.layui-icon{display:inline-block}.layui-anim-loop{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.layui-trans,.layui-trans a{transition:all .3s;-webkit-transition:all .3s}@-webkit-keyframes layui-rotate{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}@keyframes layui-rotate{from{transform:rotate(0)}to{transform:rotate(360deg)}}.layui-anim-rotate{-webkit-animation-name:layui-rotate;animation-name:layui-rotate;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-timing-function:linear;animation-timing-function:linear}@-webkit-keyframes layui-up{from{-webkit-transform:translate3d(0,100%,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-up{from{transform:translate3d(0,100%,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-up{-webkit-animation-name:layui-up;animation-name:layui-up}@-webkit-keyframes layui-upbit{from{-webkit-transform:translate3d(0,30px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes layui-upbit{from{transform:translate3d(0,30px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-anim-upbit{-webkit-animation-name:layui-upbit;animation-name:layui-upbit}@-webkit-keyframes layui-scale{0%{opacity:.3;-webkit-transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale{0%{opacity:.3;-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-ms-transform:scale(1);transform:scale(1)}}.layui-anim-scale{-webkit-animation-name:layui-scale;animation-name:layui-scale}@-webkit-keyframes layui-scale-spring{0%{opacity:.5;-webkit-transform:scale(.5)}80%{opacity:.8;-webkit-transform:scale(1.1)}100%{opacity:1;-webkit-transform:scale(1)}}@keyframes layui-scale-spring{0%{opacity:.5;transform:scale(.5)}80%{opacity:.8;transform:scale(1.1)}100%{opacity:1;transform:scale(1)}}.layui-anim-scaleSpring{-webkit-animation-name:layui-scale-spring;animation-name:layui-scale-spring}@-webkit-keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}@keyframes layui-fadein{0%{opacity:0}100%{opacity:1}}.layui-anim-fadein{-webkit-animation-name:layui-fadein;animation-name:layui-fadein}@-webkit-keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}@keyframes layui-fadeout{0%{opacity:1}100%{opacity:0}}.layui-anim-fadeout{-webkit-animation-name:layui-fadeout;animation-name:layui-fadeout} \ No newline at end of file diff --git a/static/plugs/layui/css/layui.mobile.css b/static/plugs/layui/css/layui.mobile.css index 06aa5f6a7..cefeb4636 100644 --- a/static/plugs/layui/css/layui.mobile.css +++ b/static/plugs/layui/css/layui.mobile.css @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,legend,li,ol,p,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}html{font:12px 'Helvetica Neue','PingFang SC',STHeitiSC-Light,Helvetica,Arial,sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a,button,input{-webkit-tap-highlight-color:rgba(255,0,0,0)}a{text-decoration:none;background:0 0}a:active,a:hover{outline:0}table{border-collapse:collapse;border-spacing:0}li{list-style:none}b,strong{font-weight:700}h1,h2,h3,h4,h5,h6{font-weight:500}address,cite,dfn,em,var{font-style:normal}dfn{font-style:italic}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}img{border:0;vertical-align:bottom}.layui-inline,input,label{vertical-align:middle}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0;outline:0}button,select{text-transform:none}select{-webkit-appearance:none;border:none}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=1.0.7);src:url(../font/iconfont.eot?v=1.0.7#iefix) format('embedded-opentype'),url(../font/iconfont.woff?v=1.0.7) format('woff'),url(../font/iconfont.ttf?v=1.0.7) format('truetype'),url(../font/iconfont.svg?v=1.0.7#iconfont) format('svg')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-box,.layui-box *{-webkit-box-sizing:content-box!important;-moz-box-sizing:content-box!important;box-sizing:content-box!important}.layui-border-box,.layui-border-box *{-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-inline{position:relative;display:inline-block;*display:inline;*zoom:1}.layui-edge,.layui-upload-iframe{position:absolute;width:0;height:0}.layui-edge{border-style:dashed;border-color:transparent;overflow:hidden}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-unselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-disabled,.layui-disabled:active{background-color:#d2d2d2!important;color:#fff!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}.layui-upload-iframe{border:0;visibility:hidden}.layui-upload-enter{border:1px solid #009E94;background-color:#009E94;color:#fff;-webkit-transform:scale(1.1);transform:scale(1.1)}.layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.layui-m-layermain,.layui-m-layershade{position:fixed;left:0;top:0;width:100%;height:100%}.layui-m-layershade{background-color:rgba(0,0,0,.7);pointer-events:auto}.layui-m-layermain{display:table;font-family:Helvetica,arial,sans-serif;pointer-events:none}.layui-m-layermain .layui-m-layersection{display:table-cell;vertical-align:middle;text-align:center}.layui-m-layerchild{position:relative;display:inline-block;text-align:left;background-color:#fff;font-size:14px;border-radius:5px;box-shadow:0 0 8px rgba(0,0,0,.1);pointer-events:auto;-webkit-overflow-scrolling:touch;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@-webkit-keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.layui-m-anim-scale{animation-name:layui-m-anim-scale;-webkit-animation-name:layui-m-anim-scale}@-webkit-keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.layui-m-anim-up{-webkit-animation-name:layui-m-anim-up;animation-name:layui-m-anim-up}.layui-m-layer0 .layui-m-layerchild{width:90%;max-width:640px}.layui-m-layer1 .layui-m-layerchild{border:none;border-radius:0}.layui-m-layer2 .layui-m-layerchild{width:auto;max-width:260px;min-width:40px;border:none;background:0 0;box-shadow:none;color:#fff}.layui-m-layerchild h3{padding:0 10px;height:60px;line-height:60px;font-size:16px;font-weight:400;border-radius:5px 5px 0 0;text-align:center}.layui-m-layerbtn span,.layui-m-layerchild h3{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-m-layercont{padding:50px 30px;line-height:22px;text-align:center}.layui-m-layer1 .layui-m-layercont{padding:0;text-align:left}.layui-m-layer2 .layui-m-layercont{text-align:center;padding:0;line-height:0}.layui-m-layer2 .layui-m-layercont i{width:25px;height:25px;margin-left:8px;display:inline-block;background-color:#fff;border-radius:100%;-webkit-animation:layui-m-anim-loading 1.4s infinite ease-in-out;animation:layui-m-anim-loading 1.4s infinite ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-m-layerbtn,.layui-m-layerbtn span{position:relative;text-align:center;border-radius:0 0 5px 5px}.layui-m-layer2 .layui-m-layercont p{margin-top:20px}@-webkit-keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}.layui-m-layer2 .layui-m-layercont i:first-child{margin-left:0;-webkit-animation-delay:-.32s;animation-delay:-.32s}.layui-m-layer2 .layui-m-layercont i.layui-m-layerload{-webkit-animation-delay:-.16s;animation-delay:-.16s}.layui-m-layer2 .layui-m-layercont>div{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px} \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + blockquote,body,button,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,input,legend,li,ol,p,td,textarea,th,ul{margin:0;padding:0;-webkit-tap-highlight-color:rgba(0,0,0,0)}html{font:12px 'Helvetica Neue','PingFang SC',STHeitiSC-Light,Helvetica,Arial,sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a,button,input{-webkit-tap-highlight-color:rgba(255,0,0,0)}a{text-decoration:none;background:0 0}a:active,a:hover{outline:0}table{border-collapse:collapse;border-spacing:0}li{list-style:none}b,strong{font-weight:700}h1,h2,h3,h4,h5,h6{font-weight:500}address,cite,dfn,em,var{font-style:normal}dfn{font-style:italic}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}img{border:0;vertical-align:bottom}.layui-inline,input,label{vertical-align:middle}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0;outline:0}button,select{text-transform:none}select{-webkit-appearance:none;border:none}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}@font-face{font-family:layui-icon;src:url(../font/iconfont.eot?v=1.0.7);src:url(../font/iconfont.eot?v=1.0.7#iefix) format('embedded-opentype'),url(../font/iconfont.woff?v=1.0.7) format('woff'),url(../font/iconfont.ttf?v=1.0.7) format('truetype'),url(../font/iconfont.svg?v=1.0.7#iconfont) format('svg')}.layui-icon{font-family:layui-icon!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.layui-box,.layui-box *{-webkit-box-sizing:content-box!important;-moz-box-sizing:content-box!important;box-sizing:content-box!important}.layui-border-box,.layui-border-box *{-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-inline{position:relative;display:inline-block;*display:inline;*zoom:1}.layui-edge,.layui-upload-iframe{position:absolute;width:0;height:0}.layui-edge{border-style:dashed;border-color:transparent;overflow:hidden}.layui-elip{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-unselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-disabled,.layui-disabled:active{background-color:#d2d2d2!important;color:#fff!important;cursor:not-allowed!important}.layui-circle{border-radius:100%}.layui-show{display:block!important}.layui-hide{display:none!important}.layui-upload-iframe{border:0;visibility:hidden}.layui-upload-enter{border:1px solid #009E94;background-color:#009E94;color:#fff;-webkit-transform:scale(1.1);transform:scale(1.1)}@-webkit-keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layui-m-anim-scale{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.layui-m-anim-scale{animation-name:layui-m-anim-scale;-webkit-animation-name:layui-m-anim-scale}@-webkit-keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layui-m-anim-up{0%{opacity:0;-webkit-transform:translateY(800px);transform:translateY(800px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.layui-m-anim-up{-webkit-animation-name:layui-m-anim-up;animation-name:layui-m-anim-up}@-webkit-keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-left{0%{-webkit-transform:translateX(100%);transform:translateX(100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-left{-webkit-animation-name:layui-m-anim-left;animation-name:layui-m-anim-left}@-webkit-keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes layui-m-anim-right{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}100%{-webkit-transform:translateX(0);transform:translateX(0)}}.layui-m-anim-right{-webkit-animation-name:layui-m-anim-right;animation-name:layui-m-anim-right}@-webkit-keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}@keyframes layui-m-anim-lout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(-100%);transform:translateX(-100%)}}.layui-m-anim-lout{-webkit-animation-name:layui-m-anim-lout;animation-name:layui-m-anim-lout}@-webkit-keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}@keyframes layui-m-anim-rout{0%{-webkit-transform:translateX(0);transform:translateX(0)}100%{-webkit-transform:translateX(100%);transform:translateX(100%)}}.layui-m-anim-rout{-webkit-animation-name:layui-m-anim-rout;animation-name:layui-m-anim-rout}.layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.layui-m-layermain,.layui-m-layershade{position:fixed;left:0;top:0;width:100%;height:100%}.layui-m-layershade{background-color:rgba(0,0,0,.7);pointer-events:auto}.layui-m-layermain{display:table;font-family:Helvetica,arial,sans-serif;pointer-events:none}.layui-m-layermain .layui-m-layersection{display:table-cell;vertical-align:middle;text-align:center}.layui-m-layerchild{position:relative;display:inline-block;text-align:left;background-color:#fff;font-size:14px;border-radius:5px;box-shadow:0 0 8px rgba(0,0,0,.1);pointer-events:auto;-webkit-overflow-scrolling:touch;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-m-layer0 .layui-m-layerchild{width:90%;max-width:640px}.layui-m-layer1 .layui-m-layerchild{border:none;border-radius:0}.layui-m-layer2 .layui-m-layerchild{width:auto;max-width:260px;min-width:40px;border:none;background:0 0;box-shadow:none;color:#fff}.layui-m-layerchild h3{padding:0 10px;height:60px;line-height:60px;font-size:16px;font-weight:400;border-radius:5px 5px 0 0;text-align:center}.layui-m-layerbtn span,.layui-m-layerchild h3{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.layui-m-layercont{padding:50px 30px;line-height:22px;text-align:center}.layui-m-layer1 .layui-m-layercont{padding:0;text-align:left}.layui-m-layer2 .layui-m-layercont{text-align:center;padding:0;line-height:0}.layui-m-layer2 .layui-m-layercont i{width:25px;height:25px;margin-left:8px;display:inline-block;background-color:#fff;border-radius:100%;-webkit-animation:layui-m-anim-loading 1.4s infinite ease-in-out;animation:layui-m-anim-loading 1.4s infinite ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.layui-m-layerbtn,.layui-m-layerbtn span{position:relative;text-align:center;border-radius:0 0 5px 5px}.layui-m-layer2 .layui-m-layercont p{margin-top:20px}@-webkit-keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}@keyframes layui-m-anim-loading{0%,100%,80%{transform:scale(0);-webkit-transform:scale(0)}40%{transform:scale(1);-webkit-transform:scale(1)}}.layui-m-layer2 .layui-m-layercont i:first-child{margin-left:0;-webkit-animation-delay:-.32s;animation-delay:-.32s}.layui-m-layer2 .layui-m-layercont i.layui-m-layerload{-webkit-animation-delay:-.16s;animation-delay:-.16s}.layui-m-layer2 .layui-m-layercont>div{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px} \ No newline at end of file diff --git a/static/plugs/layui/css/modules/code.css b/static/plugs/layui/css/modules/code.css index 704b8f3d7..e12278b69 100644 --- a/static/plugs/layui/css/modules/code.css +++ b/static/plugs/layui/css/modules/code.css @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #ddd;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:30px;line-height:30px;border-bottom:1px solid #ddd}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #ddd;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none} \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none} \ No newline at end of file diff --git a/static/plugs/layui/css/modules/laydate/default/laydate.css b/static/plugs/layui/css/modules/laydate/default/laydate.css new file mode 100644 index 000000000..554698b3e --- /dev/null +++ b/static/plugs/layui/css/modules/laydate/default/laydate.css @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + .laydate-set-ym,.layui-laydate,.layui-laydate *,.layui-laydate-list{box-sizing:border-box}html #layuicss-laydate{display:none;position:absolute;width:1989px}.layui-laydate *{margin:0;padding:0}.layui-laydate{position:absolute;z-index:66666666;margin:5px 0;border-radius:2px;font-size:14px;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:laydate-upbit;animation-name:laydate-upbit}.layui-laydate-main{width:272px}.layui-laydate-content td,.layui-laydate-header *,.layui-laydate-list li{transition-duration:.3s;-webkit-transition-duration:.3s}@-webkit-keyframes laydate-upbit{from{-webkit-transform:translate3d(0,20px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes laydate-upbit{from{transform:translate3d(0,20px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-laydate-static{position:relative;z-index:0;display:inline-block;margin:0;-webkit-animation:none;animation:none}.laydate-ym-show .laydate-next-m,.laydate-ym-show .laydate-prev-m{display:none!important}.laydate-ym-show .laydate-next-y,.laydate-ym-show .laydate-prev-y{display:inline-block!important}.laydate-time-show .laydate-set-ym span[lay-type=month],.laydate-time-show .laydate-set-ym span[lay-type=year],.laydate-time-show .layui-laydate-header .layui-icon,.laydate-ym-show .laydate-set-ym span[lay-type=month]{display:none!important}.layui-laydate-header{position:relative;line-height:30px;padding:10px 70px 5px}.laydate-set-ym span,.layui-laydate-header i{padding:0 5px;cursor:pointer}.layui-laydate-header *{display:inline-block;vertical-align:bottom}.layui-laydate-header i{position:absolute;top:10px;color:#999;font-size:18px}.layui-laydate-header i.laydate-prev-y{left:15px}.layui-laydate-header i.laydate-prev-m{left:45px}.layui-laydate-header i.laydate-next-y{right:15px}.layui-laydate-header i.laydate-next-m{right:45px}.laydate-set-ym{width:100%;text-align:center;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.laydate-time-text{cursor:default!important}.layui-laydate-content{position:relative;padding:10px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-laydate-content table{border-collapse:collapse;border-spacing:0}.layui-laydate-content td,.layui-laydate-content th{width:36px;height:30px;padding:5px;text-align:center}.layui-laydate-content td{position:relative;cursor:pointer}.laydate-day-mark{position:absolute;left:0;top:0;width:100%;height:100%;line-height:30px;font-size:12px;overflow:hidden}.laydate-day-mark::after{position:absolute;content:'';right:2px;top:2px;width:5px;height:5px;border-radius:50%}.layui-laydate-footer{position:relative;height:46px;line-height:26px;padding:10px 20px}.layui-laydate-footer span{margin-right:15px;display:inline-block;cursor:pointer;font-size:12px}.layui-laydate-footer span:hover{color:#5FB878}.laydate-footer-btns{position:absolute;right:10px;top:10px}.laydate-footer-btns span{height:26px;line-height:26px;margin:0 0 0 -1px;padding:0 10px;border:1px solid #C9C9C9;background-color:#fff;white-space:nowrap;vertical-align:top;border-radius:2px}.layui-laydate-list>li,.layui-laydate-range .layui-laydate-main{display:inline-block;vertical-align:middle}.layui-laydate-list{position:absolute;left:0;top:0;width:100%;height:100%;padding:10px;background-color:#fff}.layui-laydate-list>li{position:relative;width:33.3%;height:36px;line-height:36px;margin:3px 0;text-align:center;cursor:pointer}.laydate-month-list>li{width:25%;margin:17px 0}.laydate-time-list>li{height:100%;margin:0;line-height:normal;cursor:default}.laydate-time-list p{position:relative;top:-4px;line-height:29px}.laydate-time-list ol{height:181px;overflow:hidden}.laydate-time-list>li:hover ol{overflow-y:auto}.laydate-time-list ol li{width:130%;padding-left:33px;line-height:30px;text-align:left;cursor:pointer}.layui-laydate-hint{position:absolute;top:115px;left:50%;width:250px;margin-left:-125px;line-height:20px;padding:15px;text-align:center;font-size:12px}.layui-laydate-range{width:546px}.layui-laydate-range .laydate-main-list-0 .laydate-next-m,.layui-laydate-range .laydate-main-list-0 .laydate-next-y,.layui-laydate-range .laydate-main-list-1 .laydate-prev-m,.layui-laydate-range .laydate-main-list-1 .laydate-prev-y{display:none}.layui-laydate-range .laydate-main-list-1 .layui-laydate-content{border-left:1px solid #e2e2e2}.layui-laydate,.layui-laydate-hint{border:1px solid #d2d2d2;box-shadow:0 2px 4px rgba(0,0,0,.12);background-color:#fff;color:#666}.layui-laydate-header{border-bottom:1px solid #e2e2e2}.layui-laydate-header i:hover,.layui-laydate-header span:hover{color:#5FB878}.layui-laydate-content{border-top:none 0;border-bottom:none 0}.layui-laydate-content th{font-weight:400;color:#333}.layui-laydate-content td{color:#666}.layui-laydate-content td.laydate-selected{background-color:#00F7DE}.laydate-selected:hover{background-color:#00F7DE!important}.layui-laydate-content td:hover,.layui-laydate-list li:hover{background-color:#eaeaea;color:#333}.laydate-time-list li ol{margin:0;padding:0;border:1px solid #e2e2e2;border-left-width:0}.laydate-time-list li:first-child ol{border-left-width:1px}.laydate-time-list>li:hover{background:0 0}.layui-laydate-content .laydate-day-next,.layui-laydate-content .laydate-day-prev{color:#d2d2d2}.laydate-selected.laydate-day-next,.laydate-selected.laydate-day-prev{background-color:#f8f8f8!important}.layui-laydate-footer{border-top:1px solid #e2e2e2}.layui-laydate-hint{color:#FF5722}.laydate-day-mark::after{background-color:#5FB878}.layui-laydate-content td.layui-this .laydate-day-mark::after{display:none}.layui-laydate-footer span[lay-type=date]{color:#5FB878}.layui-laydate .layui-this{background-color:#009688!important;color:#fff!important}.layui-laydate .laydate-disabled,.layui-laydate .laydate-disabled:hover{background:0 0!important;color:#d2d2d2!important;cursor:not-allowed!important;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.laydate-theme-molv{border:none}.laydate-theme-molv.layui-laydate-range{width:548px}.laydate-theme-molv .layui-laydate-main{width:274px}.laydate-theme-molv .layui-laydate-header{border:none;background-color:#009688}.laydate-theme-molv .layui-laydate-header i,.laydate-theme-molv .layui-laydate-header span{color:#f6f6f6}.laydate-theme-molv .layui-laydate-header i:hover,.laydate-theme-molv .layui-laydate-header span:hover{color:#fff}.laydate-theme-molv .layui-laydate-content{border:1px solid #e2e2e2;border-top:none;border-bottom:none}.laydate-theme-molv .laydate-main-list-1 .layui-laydate-content{border-left:none}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li,.laydate-theme-grid .layui-laydate-content td,.laydate-theme-grid .layui-laydate-content thead,.laydate-theme-molv .layui-laydate-footer{border:1px solid #e2e2e2}.laydate-theme-grid .laydate-selected,.laydate-theme-grid .laydate-selected:hover{background-color:#f2f2f2!important;color:#009688!important}.laydate-theme-grid .laydate-selected.laydate-day-next,.laydate-theme-grid .laydate-selected.laydate-day-prev{color:#d2d2d2!important}.laydate-theme-grid .laydate-month-list,.laydate-theme-grid .laydate-year-list{margin:1px 0 0 1px}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li{margin:0 -1px -1px 0}.laydate-theme-grid .laydate-year-list>li{height:43px;line-height:43px}.laydate-theme-grid .laydate-month-list>li{height:71px;line-height:71px} \ No newline at end of file diff --git a/static/plugs/layui/css/modules/laydate/icon.png b/static/plugs/layui/css/modules/laydate/icon.png deleted file mode 100644 index 5a50673e0..000000000 Binary files a/static/plugs/layui/css/modules/laydate/icon.png and /dev/null differ diff --git a/static/plugs/layui/css/modules/laydate/laydate.css b/static/plugs/layui/css/modules/laydate/laydate.css deleted file mode 100644 index c0eec2f81..000000000 --- a/static/plugs/layui/css/modules/laydate/laydate.css +++ /dev/null @@ -1,2 +0,0 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - #layuicss-laydatecss{display:none;position:absolute;width:1989px}.laydate_body .laydate_box,.laydate_body .laydate_box *{margin:0;padding:0;box-sizing:content-box}.laydate-icon,.laydate-icon-dahong,.laydate-icon-danlan,.laydate-icon-default,.laydate-icon-molv{height:22px;line-height:22px;padding-right:20px;border:1px solid #C6C6C6;background-repeat:no-repeat;background-position:right center;background-color:#fff;outline:0}.laydate-icon-default{background-image:url(../skins/default/icon.png)}.laydate-icon-danlan{border:1px solid #B1D2EC;background-image:url(../skins/danlan/icon.png)}.laydate-icon-dahong{background-image:url(../skins/dahong/icon.png)}.laydate-icon-molv{background-image:url(../skins/molv/icon.png)}.laydate_body .laydate_box{width:240px;font:12px '\5B8B\4F53';z-index:99999999;*overflow:hidden;_margin:0;_position:absolute!important}.laydate_body .laydate_box li{list-style:none}.laydate_body .laydate_box .laydate_void{cursor:text!important}.laydate_body .laydate_box cite,.laydate_body .laydate_box label{position:absolute;width:0;height:0;border-width:5px;border-style:dashed;border-color:transparent;overflow:hidden;cursor:pointer}.laydate_body .laydate_box .laydate_time,.laydate_body .laydate_box .laydate_yms{display:none}.laydate_body .laydate_box .laydate_show{display:block}.laydate_body .laydate_box input{outline:0;font-size:14px;background-color:#fff;color:#333}.laydate_body .laydate_top{position:relative;height:26px;padding:5px;*width:100%;z-index:99}.laydate_body .laydate_ym{position:relative;float:left;height:24px;cursor:pointer}.laydate_body .laydate_ym input{float:left;height:24px;line-height:24px;text-align:center;border:none;cursor:pointer}.laydate_body .laydate_ym .laydate_yms{position:absolute;left:-1px;top:24px;height:181px}.laydate_body .laydate_y{width:121px;margin-right:6px}.laydate_body .laydate_y input{width:64px;margin-right:15px}.laydate_body .laydate_y .laydate_yms{width:121px;text-align:center}.laydate_body .laydate_y .laydate_yms a{position:relative;display:block;height:20px}.laydate_body .laydate_y .laydate_yms ul{height:139px;padding:0;*overflow:hidden}.laydate_body .laydate_y .laydate_yms ul li{float:left;width:60px;height:20px;line-height:20px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.laydate_body .laydate_m{width:99px}.laydate_body .laydate_m .laydate_yms{width:99px;padding:0}.laydate_body .laydate_m input{width:42px;margin-right:15px}.laydate_body .laydate_m .laydate_yms span{display:block;float:left;width:42px;margin:5px 0 0 5px;line-height:24px;text-align:center;_display:inline}.laydate_body .laydate_choose{display:block;float:left;position:relative;width:20px;height:24px}.laydate_body .laydate_choose cite,.laydate_body .laydate_tab cite{left:50%;top:50%}.laydate_body .laydate_chtop cite{margin:-7px 0 0 -5px;border-bottom-style:solid}.laydate_body .laydate_chdown cite,.laydate_body .laydate_ym label{top:50%;margin:-2px 0 0 -5px;border-top-style:solid}.laydate_body .laydate_chprev cite{margin:-5px 0 0 -7px}.laydate_body .laydate_chnext cite{margin:-5px 0 0 -2px}.laydate_body .laydate_ym label{right:28px}.laydate_body .laydate_table{width:230px;margin:0 5px;border-collapse:collapse;border-spacing:0}.laydate_body .laydate_table td{width:31px;text-align:center;cursor:pointer;font-size:12px}.laydate_body .laydate_table thead th{font-weight:400;font-size:12px;text-align:center}.laydate_body .laydate_bottom{position:relative;height:22px;line-height:20px;padding:5px;font-size:12px}.laydate_body .laydate_bottom #laydate_hms{position:relative;z-index:1;float:left}.laydate_body .laydate_time{position:absolute;left:5px;bottom:26px;width:129px;height:125px;*overflow:hidden}.laydate_body .laydate_time .laydate_hmsno{padding:5px 0 0 5px}.laydate_body .laydate_time .laydate_hmsno span{display:block;float:left;width:24px;height:19px;line-height:19px;text-align:center;cursor:pointer;*margin-bottom:-5px}.laydate_body .laydate_time1{width:228px;height:154px}.laydate_body .laydate_time1 .laydate_hmsno{padding:6px 0 0 8px}.laydate_body .laydate_time1 .laydate_hmsno span{width:21px;height:20px;line-height:20px}.laydate_body .laydate_msg{left:49px;bottom:67px;width:141px;height:auto;overflow:hidden}.laydate_body .laydate_msg p{padding:5px 10px}.laydate_body .laydate_bottom li{float:left;height:20px;line-height:20px;border-right:none;font-weight:900}.laydate_body .laydate_bottom .laydate_sj{width:33px;text-align:center;font-weight:400}.laydate_body .laydate_bottom input{float:left;width:21px;height:20px;line-height:20px;border:none;text-align:center;cursor:pointer;font-size:12px;font-weight:400}.laydate_body .laydate_bottom .laydte_hsmtex{height:20px;line-height:20px;text-align:center}.laydate_body .laydate_bottom .laydte_hsmtex span{position:absolute;width:20px;top:0;right:0;cursor:pointer}.laydate_body .laydate_bottom .laydte_hsmtex span:hover{font-size:14px}.laydate_body .laydate_bottom .laydate_btn{position:absolute;right:5px;top:5px}.laydate_body .laydate_bottom .laydate_btn a{float:left;height:20px;padding:0 6px;_padding:0 5px}.laydate_body .laydate_table td,.laydate_body .laydate_table thead{height:21px!important;line-height:21px!important}.laydate-icon{border:1px solid #C6C6C6;background-image:url(icon.png)}.laydate_body .laydate_bottom #laydate_hms,.laydate_body .laydate_bottom .laydate_btn a,.laydate_body .laydate_box,.laydate_body .laydate_table,.laydate_body .laydate_table td,.laydate_body .laydate_time,.laydate_body .laydate_ym,.laydate_body .laydate_ym .laydate_yms{border:1px solid #ccc}.laydate_body .laydate_bottom .laydte_hsmtex,.laydate_body .laydate_choose,.laydate_body .laydate_table thead,.laydate_body .laydate_y .laydate_yms a{background-color:#F6F6F6}.laydate_body .laydate_box,.laydate_body .laydate_time,.laydate_body .laydate_ym .laydate_yms{box-shadow:2px 2px 5px rgba(0,0,0,.1)}.laydate_body .laydate_box{border-top:none;border-bottom:none;background-color:#fff;color:#333}.laydate_body .laydate_box .laydate_void{color:#ccc!important}.laydate_body .laydate_box .laydate_void:hover{background-color:#fff!important}.laydate_body .laydate_box a,.laydate_body .laydate_box a:hover{text-decoration:none;blr:expression(this.onFocus=this.blur());cursor:pointer;color:#333}.laydate_body .laydate_box a:hover{text-decoration:none;color:#666}.laydate_body .laydate_click{background-color:#eee!important}.laydate_body .laydate_bottom #laydate_hms,.laydate_body .laydate_choose:hover,.laydate_body .laydate_table td,.laydate_body .laydate_time,.laydate_body .laydate_y .laydate_yms a:hover{background-color:#fff}.laydate_body .laydate_top{border-top:1px solid #C6C6C6}.laydate_body .laydate_ym .laydate_yms{border:1px solid #C6C6C6;background-color:#fff}.laydate_body .laydate_y .laydate_yms a{border-bottom:1px solid #C6C6C6}.laydate_body .laydate_y .laydate_yms .laydate_chdown{border-top:1px solid #C6C6C6;border-bottom:none}.laydate_body .laydate_choose{border-left:1px solid #C6C6C6}.laydate_body .laydate_chprev{border-left:none;border-right:1px solid #C6C6C6}.laydate_body .laydate_chtop cite{border-bottom-color:#666}.laydate_body .laydate_chdown cite,.laydate_body .laydate_ym label{border-top-color:#666}.laydate_body .laydate_chprev cite{border-right-style:solid;border-right-color:#666}.laydate_body .laydate_chnext cite{border-left-style:solid;border-left-color:#666}.laydate_body .laydate_table td{border:none}.laydate_body .laydate_table .laydate_nothis{color:#999}.laydate_body .laydate_table thead th{border-bottom:1px solid #ccc}.laydate_body .laydate_bottom,.laydate_body .laydate_bottom .laydte_hsmtex{border-bottom:1px solid #C6C6C6}.laydate_body .laydate_bottom .laydate_sj{border-right:1px solid #C6C6C6;background-color:#F6F6F6}.laydate_body .laydate_bottom input{background-color:#fff}.laydate_body .laydate_bottom .laydate_btn{border-right:1px solid #C6C6C6}.laydate_body .laydate_bottom .laydate_v{position:absolute;left:10px;top:6px;font-family:Courier;z-index:0;color:#999}.laydate_body .laydate_bottom .laydate_btn a{border-right:none;background-color:#F6F6F6}.laydate_body .laydate_bottom .laydate_btn a:hover{color:#000;background-color:#fff}.laydate_body .laydate_m .laydate_yms span:hover,.laydate_body .laydate_table td:hover,.laydate_body .laydate_time .laydate_hmsno span:hover,.laydate_body .laydate_y .laydate_yms ul li:hover{background-color:#F3F3F3} \ No newline at end of file diff --git a/static/plugs/layui/css/modules/layer/default/layer.css b/static/plugs/layui/css/modules/layer/default/layer.css index 9f2e52321..2ed7de621 100644 --- a/static/plugs/layui/css/modules/layer/default/layer.css +++ b/static/plugs/layui/css/modules/layer/default/layer.css @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - .layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}*html{background-image:url(about:blank);background-attachment:fixed}html #layuicss-skinlayercss{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layui-layer{border-radius:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim{-webkit-animation-name:layer-bounceIn;animation-name:layer-bounceIn}@-webkit-keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:layer-zoomInDown;animation-name:layer-zoomInDown}@-webkit-keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:layer-fadeInUpBig;animation-name:layer-fadeInUpBig}@-webkit-keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:layer-zoomInLeft;animation-name:layer-zoomInLeft}@-webkit-keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:layer-rollIn;animation-name:layer-rollIn}@keyframes layer-fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:layer-fadeIn;animation-name:layer-fadeIn}@-webkit-keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:layer-shake;animation-name:layer-shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:1px -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 10px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:6px 6px 0;padding:0 15px;border:1px solid #dedede;background-color:#f1f1f1;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#4898d5;background-color:#2e8ded;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:1px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:5px 10px 10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#BBB5B5;border:none}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:.8s;animation-duration:.8s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@-webkit-keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:layer-bounceOut;animation-name:layer-bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + .layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}html #layuicss-layer{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;border-radius:2px;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layer-anim{-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-00{-webkit-animation-name:layer-bounceIn;animation-name:layer-bounceIn}@-webkit-keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:layer-zoomInDown;animation-name:layer-zoomInDown}@-webkit-keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes layer-fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:layer-fadeInUpBig;animation-name:layer-fadeInUpBig}@-webkit-keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes layer-zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:layer-zoomInLeft;animation-name:layer-zoomInLeft}@-webkit-keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes layer-rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:layer-rollIn;animation-name:layer-rollIn}@keyframes layer-fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:layer-fadeIn;animation-name:layer-fadeIn}@-webkit-keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes layer-shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:layer-shake;animation-name:layer-shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:1px -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 15px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:5px 5px 0;padding:0 15px;border:1px solid #dedede;background-color:#fff;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#1E9FFF;background-color:#1E9FFF;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:8px 15px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:5px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:5px 10px 10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#fff;border-color:#E9E7E7;color:#333}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95;border-color:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:230px;height:36px;margin:0 auto;line-height:30px;padding-left:10px;border:1px solid #e6e6e6;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px;padding:6px 10px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;overflow:hidden;cursor:pointer}.layui-layer-tab .layui-layer-title span.layui-this{height:43px;border-left:1px solid #eee;border-right:1px solid #eee;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.layui-this{display:block}.layui-layer-photos{-webkit-animation-duration:.8s;animation-duration:.8s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@-webkit-keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes layer-bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:layer-bounceOut;animation-name:layer-bounceOut;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.2s;animation-duration:.2s}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} \ No newline at end of file diff --git a/static/plugs/layui/font/iconfont.eot b/static/plugs/layui/font/iconfont.eot index bd899838f..51c4f8958 100644 Binary files a/static/plugs/layui/font/iconfont.eot and b/static/plugs/layui/font/iconfont.eot differ diff --git a/static/plugs/layui/font/iconfont.svg b/static/plugs/layui/font/iconfont.svg index 3680fcefe..e05b1895a 100644 --- a/static/plugs/layui/font/iconfont.svg +++ b/static/plugs/layui/font/iconfont.svg @@ -1,387 +1,468 @@ - + + -Created by FontForge 20120731 at Mon Feb 27 22:32:26 2017 - By admin +Created by iconfont + - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/plugs/layui/font/iconfont.ttf b/static/plugs/layui/font/iconfont.ttf index 7410faa92..43a316134 100644 Binary files a/static/plugs/layui/font/iconfont.ttf and b/static/plugs/layui/font/iconfont.ttf differ diff --git a/static/plugs/layui/font/iconfont.woff b/static/plugs/layui/font/iconfont.woff index ccf64e6c4..7fd98e35f 100644 Binary files a/static/plugs/layui/font/iconfont.woff and b/static/plugs/layui/font/iconfont.woff differ diff --git a/static/plugs/layui/lay/dest/layui.all.js b/static/plugs/layui/lay/dest/layui.all.js deleted file mode 100644 index bfecf844e..000000000 --- a/static/plugs/layui/lay/dest/layui.all.js +++ /dev/null @@ -1,5 +0,0 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;!function(e){"use strict";var t=function(){this.v="1.0.9_rls"};t.fn=t.prototype;var n=document,o=t.fn.cache={},i=function(){var e=n.scripts,t=e[e.length-1].src;return t.substring(0,t.lastIndexOf("/")+1)}(),r=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},l="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),a={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",util:"modules/util",flow:"modules/flow",carousel:"modules/carousel",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"dest/layui.all"};o.modules={},o.status={},o.timeout=10,o.event={},t.fn.define=function(e,t){var n=this,i="function"==typeof e,r=function(){return"function"==typeof t&&t(function(e,t){layui[e]=t,o.status[e]=!0}),this};return i&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?r.call(n):(n.use(e,r),n)},t.fn.use=function(e,t,u){function s(e,t){var n="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||n.test((e.currentTarget||e.srcElement).readyState))&&(o.modules[m]=t,y.removeChild(p),function i(){return++v>1e3*o.timeout/4?r(m+" is not a valid module"):void(o.status[m]?c():setTimeout(i,4))}())}function c(){u.push(layui[m]),e.length>1?f.use(e.slice(1),t,u):"function"==typeof t&&t.apply(layui,u)}var f=this,d=o.dir=o.dir?o.dir:i,y=n.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(f.each(e,function(t,n){"jquery"===n&&e.splice(t,1)}),layui.jquery=jQuery);var m=e[0],v=0;if(u=u||[],o.host=o.host||(d.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&a[m]||!layui["layui.all"]&&layui["layui.mobile"]&&a[m])return c(),f;var p=n.createElement("script"),h=(a[m]?d+"lay/":o.base||"")+(f.modules[m]||m)+".js";return p.async=!0,p.charset="utf-8",p.src=h+function(){var e=o.version===!0?o.v||(new Date).getTime():o.version||"";return e?"?v="+e:""}(),o.modules[m]?!function g(){return++v>1e3*o.timeout/4?r(m+" is not a valid module"):void("string"==typeof o.modules[m]&&o.status[m]?c():setTimeout(g,4))}():(y.appendChild(p),!p.attachEvent||p.attachEvent.toString&&p.attachEvent.toString().indexOf("[native code")<0||l?p.addEventListener("load",function(e){s(e,h)},!1):p.attachEvent("onreadystatechange",function(e){s(e,h)})),o.modules[m]=h,f},t.fn.getStyle=function(t,n){var o=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return o[o.getPropertyValue?"getPropertyValue":"getAttribute"](n)},t.fn.link=function(e,t,i){var l=this,a=n.createElement("link"),u=n.getElementsByTagName("head")[0];"string"==typeof t&&(i=t);var s=(i||e).replace(/\.|\//g,""),c=a.id="layuicss-"+s,f=0;a.rel="stylesheet",a.href=e+(o.debug?"?v="+(new Date).getTime():""),a.media="all",n.getElementById(c)||u.appendChild(a),"function"==typeof t&&!function d(){return++f>1e3*o.timeout/100?r(e+" timeout"):void(1989===parseInt(l.getStyle(n.getElementById(c),"width"))?function(){t()}():setTimeout(d,100))}()},t.fn.addcss=function(e,t,n){layui.link(o.dir+"css/"+e,t,n)},t.fn.img=function(e,t,n){var o=new Image;return o.src=e,o.complete?t(o):(o.onload=function(){o.onload=null,t(o)},void(o.onerror=function(e){o.onerror=null,n(e)}))},t.fn.config=function(e){e=e||{};for(var t in e)o[t]=e[t];return this},t.fn.modules=function(){var e={};for(var t in a)e[t]=a[t];return e}(),t.fn.extend=function(e){var t=this;e=e||{};for(var n in e)t[n]||t.modules[n]?r("模块名 "+n+" 已被占用"):t.modules[n]=e[n];return t},t.fn.router=function(e){for(var t,n=(e||location.hash).replace(/^#/,"").split("/")||[],o={dir:[]},i=0;i/g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var n="Laytpl Error:";return"object"==typeof console&&console.error(n+e+"\n"+(r||"")),n+e}},c=n.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=c("^"+r.open+"#",""),l=c(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(c(r.open+"#"),r.open+"# ").replace(c(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(/(?="|')/g,"\\").replace(n.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(n.query(1),function(e){var n='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(c(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),n='"+_escape_('),n+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,n.escape)}catch(u){return delete o.cache,n.error(u,p)}},t.pt.render=function(e,r){var c,t=this;return e?(c=t.cache?t.cache(e,n.escape):t.parse(t.tpl,e),r?void r(c):c):n.error("no data")};var o=function(e){return"string"!=typeof e?n.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var n in e)r[n]=e[n]},o.v="1.2.0",e("laytpl",o)});layui.define(function(a){"use strict";function t(a){new p(a)}var e=document,r="getElementById",n="getElementsByTagName",s=0,p=function(a){var t=this,e=t.config=a||{};e.item=s++,t.render(!0)};p.on=function(a,t,e){return a.attachEvent?a.attachEvent("on"+t,function(){e.call(a,window.even)}):a.addEventListener(t,e,!1),p},p.prototype.type=function(){var a=this.config;if("object"==typeof a.cont)return void 0===a.cont.length?2:3},p.prototype.view=function(){var a=this,t=a.config,e=[],r={};if(t.pages=0|t.pages,t.curr=0|t.curr||1,t.groups="groups"in t?0|t.groups:5,t.first="first"in t?t.first:"首页",t.last="last"in t?t.last:"末页",t.prev="prev"in t?t.prev:"上一页",t.next="next"in t?t.next:"下一页",t.pages<=1)return"";for(t.groups>t.pages&&(t.groups=t.pages),r.index=Math.ceil((t.curr+(t.groups>1&&t.groups!==t.pages?1:0))/(0===t.groups?1:t.groups)),t.curr>1&&t.prev&&e.push(''+t.prev+""),r.index>1&&t.first&&0!==t.groups&&e.push(''+t.first+""),r.poor=Math.floor((t.groups-1)/2),r.start=r.index>1?t.curr-r.poor:1,r.end=r.index>1?function(){var a=t.curr+(t.groups-r.poor-1);return a>t.pages?t.pages:a}():t.groups,r.end-r.start"+r.start+""):e.push(''+r.start+"");return t.pages>t.groups&&r.end'+t.last+""),r.flow=!t.prev&&0===t.groups,(t.curr!==t.pages&&t.next||r.flow)&&e.push(function(){return r.flow&&t.curr===t.pages?''+t.next+"":''+t.next+""}()),'
    '+e.join("")+function(){return t.skip?'到第 ':""}()+"
    "},p.prototype.jump=function(a){if(a){for(var t=this,e=t.config,r=a.children,s=a[n]("button")[0],i=a[n]("input")[0],u=0,o=r.length;un.maxs[0]?s=["y",1]:e>=n.mins[0]&&e<=n.maxs[0]&&(e==n.mins[0]&&(tn.maxs[1]?s=["m",1]:t==n.maxs[1]&&a>n.maxs[2]&&(s=["d",1]))),s},n.timeVoid=function(e,t){if(n.ymd[1]+1==n.mins[1]&&n.ymd[2]==n.mins[2]){if(0===t&&en.maxs[3])return 1;if(1===t&&e>n.maxs[4])return 1;if(2===t&&e>n.maxs[5])return 1}if(e>(t?59:23))return 1},n.check=function(){var e=n.options.format.replace(/YYYY|MM|DD|hh|mm|ss/g,"\\d+\\").replace(/\\$/g,""),t=new RegExp(e),a=n.elem[d.elemv],s=a.match(/\d+/g)||[],i=n.checkVoid(s[0],s[1],s[2]);if(""!==a.replace(/\s/g,"")){if(!t.test(a))return n.elem[d.elemv]="",n.msg("日期不符合格式,请重新选择。"),1;if(i[0])return n.elem[d.elemv]="",n.msg("日期不在有效期内,请重新选择。"),1;i.value=n.elem[d.elemv].match(t).join(),s=i.value.match(/\d+/g),s[1]<1?(s[1]=1,i.auto=1):s[1]>12?(s[1]=12,i.auto=1):s[1].length<2&&(i.auto=1),s[2]<1?(s[2]=1,i.auto=1):s[2]>n.months[(0|s[1])-1]?(s[2]=31,i.auto=1):s[2].length<2&&(i.auto=1),s.length>3&&(n.timeVoid(s[3],0)&&(i.auto=1),n.timeVoid(s[4],1)&&(i.auto=1),n.timeVoid(s[5],2)&&(i.auto=1)),i.auto?n.creation([s[0],0|s[1],0|s[2]],1):i.value!==n.elem[d.elemv]&&(n.elem[d.elemv]=i.value)}},n.months=[31,null,31,30,31,30,31,31,30,31,30,31],n.viewDate=function(e,t,a){var s=(n.query,{}),i=new Date;e<(0|n.mins[0])&&(e=0|n.mins[0]),e>(0|n.maxs[0])&&(e=0|n.maxs[0]),i.setFullYear(e,t,a),s.ymd=[i.getFullYear(),i.getMonth(),i.getDate()],n.months[1]=n.isleap(s.ymd[0])?29:28,i.setFullYear(s.ymd[0],s.ymd[1],1),s.FDay=i.getDay(),s.PDay=n.months[0===t?11:t-1]-s.FDay+1,s.NDay=1,n.each(d.tds,function(e,t){var a,i=s.ymd[0],o=s.ymd[1]+1;t.className="",e=s.FDay&&e'+e+"年":'
  • '+(e-7+t)+"年
  • "}),t("#laydate_ys").innerHTML=a,n.each(t("#laydate_ys li"),function(e,t){"y"===n.checkVoid(t.getAttribute("y"))[0]?n.addClass(t,d[1]):n.on(t,"click",function(e){n.stopmp(e).reshow(),n.viewDate(0|this.getAttribute("y"),n.ymd[1],n.ymd[2])})})},n.initDate=function(){var e=(n.query,new Date),t=n.elem[d.elemv].match(/\d+/g)||[];t.length<3&&(t=n.options.start.match(/\d+/g)||[],t.length<3&&(t=[e.getFullYear(),e.getMonth()+1,e.getDate()])),n.inymd=t,n.viewDate(t[0],t[1]-1,t[2])},n.iswrite=function(){var e=n.query,t={time:e("#laydate_hms")};n.shde(t.time,!n.options.istime),n.shde(d.oclear,!("isclear"in n.options?n.options.isclear:1)),n.shde(d.otoday,!("istoday"in n.options?n.options.istoday:1)),n.shde(d.ok,!("issure"in n.options?n.options.issure:1))},n.orien=function(e,t){var a,s=n.elem.getBoundingClientRect();e.style.left=s.left+(t?0:n.scroll(1))+"px",a=s.bottom+e.offsetHeight/1.5<=n.winarea()?s.bottom-1:s.top>e.offsetHeight/1.5?s.top-e.offsetHeight+1:n.winarea()-e.offsetHeight,e.style.top=Math.max(a+(t?0:n.scroll()),1)+"px"},n.follow=function(e){n.options.fixed?(e.style.position="fixed",n.orien(e,1)):(e.style.position="absolute",n.orien(e))},n.viewtb=function(){var e,t=[],a=["日","一","二","三","四","五","六"],o={},d=s[i]("table"),r=s[i]("thead");return r.appendChild(s[i]("tr")),o.creath=function(e){var t=s[i]("th");t.innerHTML=a[e],r[l]("tr")[0].appendChild(t),t=null},n.each(new Array(6),function(a){t.push([]),e=d.insertRow(0),n.each(new Array(7),function(n){t[a][n]=0,0===a&&o.creath(n),e.insertCell(n)})}),d.insertBefore(r,d.children[0]),d.id=d.className="laydate_table",e=t=null,d.outerHTML.toLowerCase()}(),n.view=function(e,t){var o,l=n.query,r={};t=t||e,n.elem=e,n.options=t,n.options.format||(n.options.format=a.format),n.options.start=n.options.start||"",n.mm=r.mm=[n.options.min||a.min,n.options.max||a.max],n.mins=r.mm[0].match(/\d+/g),n.maxs=r.mm[1].match(/\d+/g),n.box?n.shde(n.box):(o=s[i]("div"),o.id=d[0],o.className=d[0],o.style.cssText="position: absolute;",o.setAttribute("name","laydate-v"+laydate.v),o.innerHTML=r.html='
      '+function(){var e="";return n.each(new Array(12),function(t){e+=''+n.digit(t+1)+"月"}),e}()+"
      "+n.viewtb+'",s.body.appendChild(o),n.box=l("#"+d[0]),n.events(),o=null),n.follow(n.box),t.zIndex?n.box.style.zIndex=t.zIndex:n.removeCssAttr(n.box,"z-index"),n.stopMosup("click",n.box),n.initDate(),n.iswrite(),n.check()},n.reshow=function(){return n.each(n.query("#"+d[0]+" .laydate_show"),function(e,t){n.removeClass(t,"laydate_show")}),this},n.close=function(){n.reshow(),n.shde(n.query("#"+d[0]),1),n.elem=null},n.parse=function(e,t,s){return e=e.concat(t),s=s||(n.options?n.options.format:a.format),s.replace(/YYYY|MM|DD|hh|mm|ss/g,function(t,a){return e.index=0|++e.index,n.digit(e[e.index])})},n.creation=function(e,t){var a=(n.query,n.hmsin),s=n.parse(e,[a[0].value,a[1].value,a[2].value]);n.elem[d.elemv]=s,t||(n.close(),"function"==typeof n.options.choose&&n.options.choose(s))},n.events=function(){var e=n.query,a={box:"#"+d[0]};n.addClass(s.body,"laydate_body"),d.tds=e("#laydate_table td"),d.mms=e("#laydate_ms span"),d.year=e("#laydate_y"),d.month=e("#laydate_m"),n.each(e(a.box+" .laydate_ym"),function(e,t){n.on(t,"click",function(t){n.stopmp(t).reshow(),n.addClass(this[l]("div")[0],"laydate_show"),e||(a.YY=parseInt(d.year.value),n.viewYears(a.YY))})}),n.on(e(a.box),"click",function(){n.reshow()}),a.tabYear=function(e){0===e?n.ymd[0]--:1===e?n.ymd[0]++:2===e?a.YY-=14:a.YY+=14,e<2?(n.viewDate(n.ymd[0],n.ymd[1],n.ymd[2]),n.reshow()):n.viewYears(a.YY)},n.each(e("#laydate_YY .laydate_tab"),function(e,t){n.on(t,"click",function(t){n.stopmp(t),a.tabYear(e)})}),a.tabMonth=function(e){e?(n.ymd[1]++,12===n.ymd[1]&&(n.ymd[0]++,n.ymd[1]=0)):(n.ymd[1]--,n.ymd[1]===-1&&(n.ymd[0]--,n.ymd[1]=11)),n.viewDate(n.ymd[0],n.ymd[1],n.ymd[2])},n.each(e("#laydate_MM .laydate_tab"),function(e,t){n.on(t,"click",function(t){n.stopmp(t).reshow(),a.tabMonth(e)})}),n.each(e("#laydate_ms span"),function(e,t){n.on(t,"click",function(e){n.stopmp(e).reshow(),n.hasClass(this,d[1])||n.viewDate(n.ymd[0],0|this.getAttribute("m"),n.ymd[2])})}),n.each(e("#laydate_table td"),function(e,t){n.on(t,"click",function(e){n.hasClass(this,d[1])||(n.stopmp(e),n.creation([0|this.getAttribute("y"),0|this.getAttribute("m"),0|this.getAttribute("d")]))})}),d.oclear=e("#laydate_clear"),n.on(d.oclear,"click",function(){n.elem[d.elemv]="",n.close()}),d.otoday=e("#laydate_today"),n.on(d.otoday,"click",function(){var e=new Date;n.creation([e.getFullYear(),e.getMonth()+1,e.getDate()])}),d.ok=e("#laydate_ok"),n.on(d.ok,"click",function(){n.valid&&n.creation([n.ymd[0],n.ymd[1]+1,n.ymd[2]])}),a.times=e("#laydate_time"),n.hmsin=a.hmsin=e("#laydate_hms input"),a.hmss=["小时","分钟","秒数"],a.hmsarr=[],n.msg=function(t,s){var i='
      '+(s||"提示")+"×
      ";"string"==typeof t?(i+="

      "+t+"

      ",n.shde(e("#"+d[0])),n.removeClass(a.times,"laydate_time1").addClass(a.times,"laydate_msg")):(a.hmsarr[t]?i=a.hmsarr[t]:(i+='
      ',n.each(new Array(0===t?24:60),function(e){i+=""+e+""}),i+="
      ",a.hmsarr[t]=i),n.removeClass(a.times,"laydate_msg"),n[0===t?"removeClass":"addClass"](a.times,"laydate_time1")),n.addClass(a.times,"laydate_show"),a.times.innerHTML=i},a.hmson=function(t,a){var s=e("#laydate_hmsno span"),i=n.valid?null:1;n.each(s,function(e,s){i?n.addClass(s,d[1]):n.timeVoid(e,a)?n.addClass(s,d[1]):n.on(s,"click",function(e){n.hasClass(this,d[1])||(t.value=n.digit(0|this.innerHTML))})}),n.addClass(s[0|t.value],"laydate_click")},n.each(a.hmsin,function(e,t){n.on(t,"click",function(t){n.stopmp(t).reshow(),n.msg(e,a.hmss[e]),a.hmson(this,e)})}),n.on(s,"mouseup",function(){var t=e("#"+d[0]);t&&"none"!==t.style.display&&(n.check()||n.close())}).on(s,"keydown",function(e){e=e||t.event;var a=e.keyCode;13===a&&n.elem&&n.creation([n.ymd[0],n.ymd[1]+1,n.ymd[2]])})},laydate.reset=function(){n.box&&n.elem&&n.follow(n.box)},laydate.now=function(e,t){var a=new Date(0|e?function(e){return e<864e5?+new Date+864e5*e:e}(parseInt(e)):+new Date);return n.parse([a.getFullYear(),a.getMonth()+1,a.getDate()],[a.getHours(),a.getMinutes(),a.getSeconds()],t)},layui.addcss("modules/laydate/laydate.css",function(){},"laydatecss"),e("laydate",laydate)});!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=!!e&&"length"in e&&e.length,n=pe.type(e);return"function"!==n&&!pe.isWindow(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(!fe.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ge,"ms-").replace(me,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var r,i=0;if(n(e))for(r=e.length;iT.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[P]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function f(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;i-1&&(r[l]=!(a[l]=f))}}else x=m(x===a?x.splice(h,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s1&&h(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(r,a,s,u,l){var c,f,d,p=0,h="0",g=r&&[],y=[],v=A,x=r||o&&T.find.TAG("*",l),b=W+=null==v?1:Math.random()||.1,w=x.length;for(l&&(A=a===H||a||l);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===H||(L(c),s=!_);d=e[f++];)if(d(c,a||H,s)){u.push(c);break}l&&(W=b)}i&&((c=!d&&c)&&p--,r&&g.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(g,y,a,s);if(r){if(p>0)for(;h--;)g[h]||y[h]||(y[h]=G.call(u));y=m(y)}Q.apply(u,y),l&&!r&&y.length>0&&p+n.length>1&&t.uniqueSort(u)}return l&&(W=b,A=v),g};return i?r(a):a}var b,w,T,C,E,N,k,S,A,D,j,L,H,q,_,F,M,O,R,P="sizzle"+1*new Date,B=e.document,W=0,I=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(j=!0),0},V=1<<31,Y={}.hasOwnProperty,J=[],G=J.pop,K=J.push,Q=J.push,Z=J.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,xe=/'|\\/g,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Te=function(){L()};try{Q.apply(J=Z.call(B.childNodes),B.childNodes),J[B.childNodes.length].nodeType}catch(Ce){Q={apply:J.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:B;return r!==H&&9===r.nodeType&&r.documentElement?(H=r,q=H.documentElement,_=!E(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=me.test(H.getElementsByClassName),w.getById=i(function(e){return q.appendChild(e).id=P,!H.getElementsByName||!H.getElementsByName(P).length}),w.getById?(T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&_){var n=t.getElementById(e);return n?[n]:[]}},T.filter.ID=function(e){var t=e.replace(be,we);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(be,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&_)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=me.test(H.querySelectorAll))&&(i(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+P+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+P+"+*").length||F.push(".#.+[+~]")}),i(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=me.test(O=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=me.test(q.compareDocumentPosition),R=t||me.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===B&&R(B,e)?-1:t===H||t.ownerDocument===B&&R(B,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===H?-1:t===H?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===B?-1:u[r]===B?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&_&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!_):void 0;return void 0!==r?r:w.attributes||!_?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!w.detectDuplicates,D=!w.sortStable&&e.slice(0),e.sort(U),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},C=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,we),e[3]=(e[3]||e[4]||e[5]||"").replace(be,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s,x=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(d=m,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}), -l=c[e]||[],p=l[0]===W&&l[1],x=p&&l[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[W,p,x];break}}else if(v&&(d=t,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),l=c[e]||[],p=l[0]===W&&l[1],x=p),x===!1)for(;(d=++p&&d&&d[g]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==y:1!==d.nodeType)||!++x||(v&&(f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),c[e]=[W,x]),d!==t)););return x-=i,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[P]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=ee(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(se,"$1"));return i[P]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,we),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,we).toLowerCase(),function(t){var n;do if(n=_?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&_&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(be,we),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((u=T.find[s])&&(r=u(a.matches[0].replace(be,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Q.apply(n,r),n;break}}return(l||k(e,f))(r,t,!_,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=P.split("").sort(U).join("")===P,w.detectDuplicates=!!j,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var xe=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&pe(e).is(n))break;r.push(e)}return r},be=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Te=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Ce=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?pe.find.matchesSelector(r,e)?[r]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Ee,Ne=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ee,"string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ne.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:re,!0)),Te.test(r[1])&&pe.isPlainObject(t))for(r in t)pe.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}if(i=re.getElementById(r[2]),i&&i.parentNode){if(i.id!==r[2])return Ee.find(e);this.length=1,this[0]=i}return this.context=re,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Ee=pe(re);var Se=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),r=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return xe(e,"parentNode")},parentsUntil:function(e,t,n){return xe(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return xe(e,"nextSibling")},prevAll:function(e){return xe(e,"previousSibling")},nextUntil:function(e,t,n){return xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return xe(e,"previousSibling",n)},siblings:function(e){return be((e.parentNode||{}).firstChild,e)},children:function(e){return be(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,r){var i=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=pe.filter(r,i)),this.length>1&&(Ae[e]||(i=pe.uniqueSort(i)),Se.test(e)&&(i=i.reverse())),this.pushStack(i)}});var De=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,r,i,a=[],s=[],u=-1,l=function(){for(i=e.once,r=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),n<=u&&u--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=!0,n||c.disable(),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,r):r}},i={};return r.pipe=r.then,pe.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=ie.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,u=1===s?e:pe.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?ie.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);i0||(je.resolveWith(re,[pe]),pe.fn.triggerHandler&&(pe(re).triggerHandler("ready"),pe(re).off("ready"))))}}),pe.ready.promise=function(t){if(!je)if(je=pe.Deferred(),"complete"===re.readyState||"loading"!==re.readyState&&!re.documentElement.doScroll)e.setTimeout(pe.ready);else if(re.addEventListener)re.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{re.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&re.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!pe.isReady){try{n.doScroll("left")}catch(t){return e.setTimeout(i,50)}a(),pe.ready()}}()}return je.promise(t)},pe.ready.promise();var Le;for(Le in pe(fe))break;fe.ownFirst="0"===Le,fe.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,r;n=re.getElementsByTagName("body")[0],n&&n.style&&(t=re.createElement("div"),r=re.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",fe.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=re.createElement("div");fe.deleteExpando=!0;try{delete e.test}catch(t){fe.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,_e=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!l(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return f(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return f(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=pe.camelCase(r.slice(5)),u(o,r,i[r])));pe._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?u(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=pe._data(e,t),n&&(!r||pe.isArray(n)?r=pe._data(e,t,pe.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),r=n.length,i=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
      a",fe.leadingWhitespace=3===e.firstChild.nodeType,fe.tbody=!e.getElementsByTagName("tbody").length,fe.htmlSerialize=!!e.getElementsByTagName("link").length,fe.html5Clone="<:nav>"!==re.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),fe.appendChecked=n.checked,e.innerHTML="",fe.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=re.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),fe.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,fe.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
      ","
      "],area:[1,"",""],param:[1,"",""],thead:[1,"","
      "],tr:[2,"","
      "],col:[2,"","
      "],td:[3,"","
      "],_default:fe.htmlSerialize?[0,"",""]:[1,"X
      ","
      "]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:pe.makeArray(n,[t]),l=pe.event.special[p]||{},i||!l.trigger||l.trigger.apply(r,n)!==!1)){if(!i&&!l.noBubble&&!pe.isWindow(r)){for(u=l.delegateType||p,Ke.test(u+p)||(s=s.parentNode);s;s=s.parentNode)d.push(s),c=s;c===(r.ownerDocument||re)&&d.push(c.defaultView||c.parentWindow||e)}for(f=0;(s=d[f++])&&!t.isPropagationStopped();)t.type=f>1?u:l.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!l._default||l._default.apply(d.pop(),n)===!1)&&He(r)&&a&&r[p]&&!pe.isWindow(r)){c=r[a],c&&(r[a]=null),pe.event.triggered=p;try{r[p]()}catch(g){}pe.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,r,i,o,a=[],s=ie.call(arguments),u=(pe._data(this,"events")||{})[e.type]||[],l=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,u),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,r=((pe.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,u=e.target;if(s&&u.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(r=[],n=0;n-1:pe.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&a.push({elem:u,handlers:r})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(re),st=at.appendChild(re.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var r,i,o,a,s,u=pe.contains(e.ownerDocument,e);if(fe.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(fe.noCloneEvent&&fe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(r=h(o),s=h(e),a=0;null!=(i=s[a]);++a)r[a]&&k(i,r[a]);if(t)if(n)for(s=s||h(e),r=r||h(o),a=0;null!=(i=s[a]);a++)N(i,r[a]);else N(e,o);return r=h(o,"script"),r.length>0&&g(r,!u&&h(e,"script")),r=s=i=null,o},cleanData:function(e,t){for(var n,r,i,o,a=0,s=pe.expando,u=pe.cache,l=fe.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(i=n[s],o=i&&u[i])){if(o.events)for(r in o.events)c[r]?pe.event.remove(n,r):pe.removeEvent(n,r,o.handle);u[i]&&(delete u[i],l||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(i))}}}),pe.fn.extend({domManip:S,detach:function(e){return A(this,e,!0)},remove:function(e){return A(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||re).createTextNode(e))},null,e,arguments.length)},append:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.appendChild(e)}})},prepend:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(fe.htmlSerialize||!et.test(e))&&(fe.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=l.getElementsByTagName("td"),t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),f.removeChild(u)}var n,r,i,o,a,s,u=re.createElement("div"),l=re.createElement("div");l.style&&(l.style.cssText="float:left;opacity:.5",fe.opacity="0.5"===l.style.opacity,fe.cssFloat=!!l.style.cssFloat,l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",fe.clearCloneStyle="content-box"===l.style.backgroundClip,u=re.createElement("div"),u.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",l.innerHTML="",u.appendChild(l),fe.boxSizing=""===l.style.boxSizing||""===l.style.MozBoxSizing||""===l.style.WebkitBoxSizing,pe.extend(fe,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),i},pixelMarginRight:function(){return null==n&&t(),r},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,gt,mt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!fe.pixelMarginRight()&&ft.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),ft.test(a)&&!mt.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,xt=/^(none|table(?!-c[ea]).+)/,bt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Tt={letterSpacing:"0",fontWeight:"400"},Ct=["Webkit","O","Moz","ms"],Et=re.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=gt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":fe.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=pe.camelCase(t),u=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];if(o=typeof n,"string"===o&&(i=Me.exec(n))&&i[1]&&(n=d(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(pe.cssNumber[s]?"":"px")),fe.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{u[t]=n}catch(l){}}},css:function(e,t,n,r){var i,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=gt(e,t,r)),"normal"===o&&t in Tt&&(o=Tt[t]),""===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,r){if(n)return xt.test(pe.css(e,"display"))&&0===e.offsetWidth?dt(e,wt,function(){return M(e,t,r)}):M(e,t,r)},set:function(e,n,r){var i=r&&ht(e);return _(e,n,r?F(e,t,r,fe.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,i),i):0)}}}),fe.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=yt.test(o)?o.replace(yt,i):o+" "+i)}}),pe.cssHooks.marginRight=L(fe.reliableMarginRight,function(e,t){if(t)return dt(e,{display:"inline-block"},gt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(fe.reliableMarginLeft,function(e,t){if(t)return(parseFloat(gt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-dt(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Oe[r]+t]=o[r]||o[r-2]||o[0];return i}},ct.test(e)||(pe.cssHooks[e+t].set=_)}),pe.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(pe.isArray(t)){for(r=ht(e),i=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Re(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=O.prototype.init,pe.fx.step={};var Nt,kt,St=/^(?:toggle|show|hide)$/,At=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(De);for(var n,r=0,i=e.length;r
      a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",fe.getSetAttribute="t"!==n.className,fe.style=/top/.test(e.getAttribute("style")),fe.hrefNormalized="/a"===e.getAttribute("href"),fe.checkOn=!!t.value,fe.optSelected=i.selected,fe.enctype=!!re.createElement("form").enctype,r.disabled=!0,fe.optDisabled=!i.disabled,t=re.createElement("input"),t.setAttribute("value",""),fe.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),fe.radioValue="t"===t.value}();var Dt=/\r/g,jt=/[\x20\t\r\n\f]+/g;pe.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=pe.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,pe(this).val()):e,null==i?i="":"number"==typeof i?i+="":pe.isArray(i)&&(i=pe.map(i,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=pe.valHooks[i.type]||pe.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(Dt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e)).replace(jt," ")}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,a=o?null:[],s=o?i+1:r.length,u=i<0?s:o?i:0;u-1)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},fe.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Lt,Ht,qt=pe.expr.attrHandle,_t=/^(?:checked|selected)$/i,Ft=fe.getSetAttribute,Mt=fe.input;pe.fn.extend({attr:function(e,t){return Pe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),i=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Ht:Lt)),void 0!==n?null===n?void pe.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=pe.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!fe.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(De);if(o&&1===e.nodeType)for(;n=o[i++];)r=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Mt&&Ft||!_t.test(n)?e[r]=!1:e[pe.camelCase("default-"+n)]=e[r]=!1:pe.attr(e,n,""),e.removeAttribute(Ft?n:r)}}),Ht={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Mt&&Ft||!_t.test(n)?e.setAttribute(!Ft&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=qt[t]||pe.find.attr;Mt&&Ft||!_t.test(t)?qt[t]=function(e,t,r){var i,o;return r||(o=qt[t],qt[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,qt[t]=o),i}:qt[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Mt&&Ft||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Lt&&Lt.set(e,t,n)}}),Ft||(Lt={set:function(e,t,n){var r=e.getAttributeNode(n);if(r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n))return t}},qt.id=qt.name=qt.coords=function(e,t,n){var r;if(!n)return(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Lt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Lt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),fe.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Ot=/^(?:input|select|textarea|button|object)$/i,Rt=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Pe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),pe.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,i=pe.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Ot.test(e.nodeName)||Rt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),fe.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),fe.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),fe.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=pe(this),o=e.match(De)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Bt=e.location,Wt=pe.now(),It=/\?/,$t=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=pe.trim(t+"");return i&&!pe.trim(i.replace($t,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new e.DOMParser,n=r.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var zt=/#.*$/,Xt=/([?&])_=[^&]*/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Vt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Yt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Kt={},Qt={},Zt="*/".concat("*"),en=Bt.href,tn=Gt.exec(en.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:en,type:"GET",isLocal:Vt.test(tn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Kt),ajaxTransport:X(Qt),ajax:function(t,n){function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c=void 0,s=i||"",T.readyState=t>0?4:0,o=t>=200&&t<300||304===t,r&&(x=Y(d,T,r)),x=J(d,x,T,o),o?(d.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=T.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===d.type?C="nocontent":304===t?C="notmodified":(C=x.state,f=x.data,v=x.error,o=!v)):(v=C,!t&&C||(C="error",t<0&&(t=0))),T.status=t,T.statusText=(n||C)+"",o?g.resolveWith(p,[f,C,T]):g.rejectWith(p,[T,C,v]),T.statusCode(y),y=void 0,l&&h.trigger(o?"ajaxSuccess":"ajaxError",[T,d,o?f:v]),m.fireWith(p,[T,C]),l&&(h.trigger("ajaxComplete",[T,d]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,d=pe.ajaxSetup({},n),p=d.context||d,h=d.context&&(p.nodeType||p.jquery)?pe(p):pe.event,g=pe.Deferred(),m=pe.Callbacks("once memory"),y=d.statusCode||{},v={},x={},b=0,w="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!f)for(f={};t=Ut.exec(s);)f[t[1].toLowerCase()]=t[2];t=f[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=x[n]=x[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(b<2)for(t in e)y[t]=[y[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),r(0,t),this}};if(g.promise(T).complete=m.add,T.success=T.done,T.error=T.fail,d.url=((t||d.url||en)+"").replace(zt,"").replace(Jt,tn[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=pe.trim(d.dataType||"*").toLowerCase().match(De)||[""],null==d.crossDomain&&(i=Gt.exec(d.url.toLowerCase()),d.crossDomain=!(!i||i[1]===tn[1]&&i[2]===tn[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(tn[3]||("http:"===tn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=pe.param(d.data,d.traditional)),U(Kt,d,n,T),2===b)return T;l=pe.event&&d.global,l&&0===pe.active++&&pe.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Yt.test(d.type),a=d.url,d.hasContent||(d.data&&(a=d.url+=(It.test(a)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Xt.test(a)?a.replace(Xt,"$1_="+Wt++):a+(It.test(a)?"&":"?")+"_="+Wt++)),d.ifModified&&(pe.lastModified[a]&&T.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&T.setRequestHeader("If-None-Match",pe.etag[a])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",d.contentType),T.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Zt+"; q=0.01":""):d.accepts["*"]);for(o in d.headers)T.setRequestHeader(o,d.headers[o]);if(d.beforeSend&&(d.beforeSend.call(p,T,d)===!1||2===b))return T.abort();w="abort";for(o in{success:1,error:1,complete:1})T[o](d[o]);if(c=U(Qt,d,n,T)){if(T.readyState=1,l&&h.trigger("ajaxSend",[T,d]),2===b)return T;d.async&&d.timeout>0&&(u=e.setTimeout(function(){T.abort("timeout")},d.timeout));try{b=1,c.send(v,r)}catch(C){if(!(b<2))throw C;r(-1,C)}}else r(-1,"No Transport");return T},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,r,i){return pe.isFunction(n)&&(i=i||r,r=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:i,data:n,success:r},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return fe.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:K(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var nn=/%20/g,rn=/\[\]$/,on=/\r?\n/g,an=/^(?:submit|button|image|reset|file)$/i,sn=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,r=[],i=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){i(this.name,this.value)});else for(n in e)Q(n,e[n],t,i);return r.join("&").replace(nn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&sn.test(this.nodeName)&&!an.test(e)&&(this.checked||!Be.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():re.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var un=0,ln={},cn=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),fe.cors=!!cn&&"withCredentials"in cn,cn=fe.ajax=!!cn,cn&&pe.ajaxTransport(function(t){if(!t.crossDomain||fe.cors){var n;return{send:function(r,i){var o,a=t.xhr(),s=++un;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)void 0!==r[o]&&a.setRequestHeader(o,r[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,r){var o,u,l;if(n&&(r||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,r)4!==a.readyState&&a.abort();else{l={},o=a.status,"string"==typeof a.responseText&&(l.text=a.responseText);try{u=a.statusText}catch(c){u=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=l.text?200:404}l&&i(o,u,l,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=re.head||pe("head")[0]||re.documentElement;return{send:function(r,i){t=re.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var fn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=fn.pop()||pe.expando+"_"+Wt++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(It.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||pe.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?pe(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,fn.push(i)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||re;var r=Te.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=y([e],t,i),i&&i.length&&pe(i).remove(),pe.merge([],r.childNodes))};var pn=pe.fn.load;return pe.fn.load=function(e,t,n){if("string"!=typeof e&&pn)return pn.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&pe.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?pe("
      ").append(pe.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=pe.css(e,"position"),f=pe(e),d={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=pe.css(e,"top"),u=pe.css(e,"left"),l=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,u])>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+i),"using"in t?t.using.call(e,d):f.css(d)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,i)?("undefined"!=typeof i.getBoundingClientRect&&(r=i.getBoundingClientRect()),n=te(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===pe.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(r,"marginTop",!0),left:t.left-n.left-pe.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){ -for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){e("jquery",pe)}),pe});!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.scripts,t=e[e.length-1],i=t.src;if(!t.getAttribute("merge"))return i.substring(0,i.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"]},r={v:"3.0.3",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):r.link("skin/"+e.extend),this):this},link:function(t,n,a){if(r.path){var o=i("head")[0],s=document.createElement("link");"string"==typeof n&&(a=n);var l=(a||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,i("#"+f)[0]||o.appendChild(s),"function"==typeof n&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(i("#"+f).css("width"))?n():setTimeout(u,100))}()}},ready:function(e){var t="skinlayercss",i="303";return a?layui.addcss("modules/layer/default/layer.css?v="+r.v+i,e,t):r.link("skin/default/layer.css?v="+r.v+i,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
      '+(f?r.title[0]:r.title)+"
      ":"";return r.zIndex=s,t([r.shade?'
      ':"",'
      '+(e&&2!=r.type?"":u)+'
      '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
      '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
      '+e+"
      "}():"")+(r.resize?'':"")+"
      "],u,i('
      ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]&&e.layero.addClass(l.anim[t.anim]),t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){function t(e){e=s.find(e),e.height(f[1]-c-u-2*(0|parseFloat(e.css("padding-top"))))}var a=this,o=a.config,s=i("#"+l[0]+e);""===o.area[0]&&o.maxWidth>0&&(r.ie&&r.ie<8&&o.btn&&s.width(s.innerWidth()),s.outerWidth()>o.maxWidth&&s.width(o.maxWidth));var f=[s.innerWidth(),s.innerHeight()],c=s.find(l[1]).outerHeight()||0,u=s.find("."+l[6]).outerHeight()||0;switch(o.type){case 2:t("iframe");break;default:""===o.area[1]?o.fixed&&f[1]>=n.height()&&(f[1]=n.height(),t("."+l[5])):t("."+l[5])}return a},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass(a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(e){s=e.find(".layui-layer-input"),s.focus(),"function"==typeof f&&f(e)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,n="";if(e>0)for(n=''+t[0].title+"";i"+t[i].title+"";return n}(),content:'
        '+function(){var e=t.length,i=1,n="";if(e>0)for(n='
      • '+(t[0].content||"no content")+"
      • ";i'+(t[i].content||"no content")+"";return n}()+"
      ",success:function(t){var a=t.find(".layui-layer-title").children(),o=t.find(".layui-layer-tabmain").children();a.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var n=i(this),a=n.index();n.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),o.eq(a).show().siblings().hide(),"function"==typeof e.change&&e.change(a)}),"function"==typeof n&&n(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
      '+(u.length>1?'':"")+'
      '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
      ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
      是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.jquery),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window);layui.define("jquery",function(i){"use strict";var a=layui.jquery,t=(layui.hint(),layui.device()),l="element",e="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(i){var t=this;return a.extend(!0,t.config,i),t},s.prototype.on=function(i,a){return layui.onevent(l,i,a)},s.prototype.tabAdd=function(i,t){var l=".layui-tab-title",e=a(".layui-tab[lay-filter="+i+"]"),n=e.children(l),s=e.children(".layui-tab-content");return n.append('
    • '+(t.title||"unnaming")+"
    • "),s.append('
      '+(t.content||"")+"
      "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(i,t){var l=".layui-tab-title",e=a(".layui-tab[lay-filter="+i+"]"),n=e.children(l),s=n.find('>li[lay-id="'+t+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(i,t){var l=".layui-tab-title",e=a(".layui-tab[lay-filter="+i+"]"),n=e.children(l),s=n.find('>li[lay-id="'+t+'"]');return f.tabClick(null,null,s),this},s.prototype.progress=function(i,t){var l="layui-progress",e=a("."+l+"[lay-filter="+i+"]"),n=e.find("."+l+"-bar"),s=n.find("."+l+"-text");return n.css("width",t),s.text(t),this};var o=".layui-nav",c="layui-nav-item",r="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",h="layui-nav-more",y="layui-anim layui-anim-upbit",f={tabClick:function(i,t,s){var o=s||a(this),t=t||o.parent().children("li").index(o),c=o.parents(".layui-tab").eq(0),r=c.children(".layui-tab-content").children(".layui-tab-item"),u=c.attr("lay-filter");o.addClass(e).siblings().removeClass(e),r.eq(t).addClass(n).siblings().removeClass(n),layui.event.call(this,l,"tab("+u+")",{elem:c,index:t})},tabDelete:function(i,t){var l=t||a(this).parent(),n=l.index(),s=l.parents(".layui-tab").eq(0),o=s.children(".layui-tab-content").children(".layui-tab-item");l.hasClass(e)&&(l.next()[0]?f.tabClick.call(l.next()[0],null,n+1):l.prev()[0]&&f.tabClick.call(l.prev()[0],null,n-1)),l.remove(),o.eq(n).remove(),setTimeout(function(){f.tabAuto()},50)},tabAuto:function(){var i="layui-tab-more",l="layui-tab-bar",e="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),c=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),r=a('');if(n===window&&8!=t.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var i=a(this);if(!i.find("."+e)[0]){var t=a('');t.on("click",f.tabDelete),i.append(t)}}),o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+l)[0])return;o.append(r),s.attr("overflow",""),r.on("click",function(a){o[this.title?"removeClass":"addClass"](i),this.title=this.title?"":"收缩"})}else o.find("."+l).remove(),s.removeAttr("overflow")})},hideTabMore:function(i){var t=a(".layui-tab-title");i!==!0&&"tabmore"===a(i.target).attr("lay-stope")||(t.removeClass("layui-tab-more"),t.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var i=a(this),t=i.parents(o),n=t.attr("lay-filter");i.find("."+d)[0]||(t.find("."+e).removeClass(e),i.addClass(e),layui.event.call(this,l,"nav("+n+")",i))},clickChild:function(){var i=a(this),t=i.parents(o),n=t.attr("lay-filter");t.find("."+e).removeClass(e),i.addClass(e),layui.event.call(this,l,"nav("+n+")",i)},showChild:function(){var i=a(this),t=i.parents(o),l=i.parent(),e=i.siblings("."+d);t.hasClass(u)&&(e.removeClass(y),l["none"===e.css("display")?"addClass":"removeClass"](c+"ed"))},collapse:function(){var i=a(this),t=i.find(".layui-colla-icon"),e=i.siblings(".layui-colla-content"),s=i.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),c="none"===e.css("display");if("string"==typeof s.attr("lay-accordion")){var r=s.children(".layui-colla-item").children("."+n);r.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),r.removeClass(n)}e[c?"addClass":"removeClass"](n),t.html(c?"":""),layui.event.call(this,l,"collapse("+o+")",{title:i,content:e,show:c})}};s.prototype.init=function(i){var l={tab:function(){f.tabAuto.call({})},nav:function(){var i,l,e,s=200,p=function(o,c){var r=a(this),f=r.find("."+d);c.hasClass(u)?o.css({top:r.position().top,height:r.children("a").height(),opacity:1}):(f.addClass(y),o.css({left:r.position().left+parseFloat(r.css("marginLeft")),top:r.position().top+r.height()-5}),i=setTimeout(function(){o.css({width:r.width(),opacity:1})},t.ie&&t.ie<10?0:s),clearTimeout(e),"block"===f.css("display")&&clearTimeout(l),l=setTimeout(function(){f.addClass(n),r.find("."+h).addClass(h+"d")},300))};a(o).each(function(){var t=a(this),o=a(''),y=t.find("."+c);t.find("."+r)[0]||(t.append(o),y.on("mouseenter",function(){p.call(this,o,t)}).on("mouseleave",function(){t.hasClass(u)||(clearTimeout(l),l=setTimeout(function(){t.find("."+d).removeClass(n),t.find("."+h).removeClass(h+"d")},300))}),t.on("mouseleave",function(){clearTimeout(i),e=setTimeout(function(){t.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},s)})),y.each(function(){var i=a(this),t=i.find("."+d);if(t[0]&&!i.find("."+h)[0]){var l=i.children("a");l.append('')}i.off("click",f.clickThis).on("click",f.clickThis),i.children("a").off("click",f.showChild).on("click",f.showChild),t.children("dd").off("click",f.clickChild).on("click",f.clickChild)})})},breadcrumb:function(){var i=".layui-breadcrumb";a(i).each(function(){var i=a(this),t=i.attr("lay-separator")||">",l=i.find("a");l.find(".layui-box")[0]||(l.each(function(i){i!==l.length-1&&a(this).append(''+t+"")}),i.css("visibility","visible"))})},progress:function(){var i="layui-progress";a("."+i).each(function(){var t=a(this),l=t.find(".layui-progress-bar"),e=l.attr("lay-percent");l.css("width",e),t.attr("lay-showPercent")&&setTimeout(function(){var a=Math.round(l.width()/t.width()*100);a>100&&(a=100),l.html(''+a+"%")},350)})},collapse:function(){var i="layui-collapse";a("."+i).each(function(){var i=a(this).find(".layui-colla-item");i.each(function(){var i=a(this),t=i.find(".layui-colla-title"),l=i.find(".layui-colla-content"),e="none"===l.css("display");t.find(".layui-colla-icon").remove(),t.append(''+(e?"":"")+""),t.off("click",f.collapse).on("click",f.collapse)})})}};return layui.each(l,function(i,a){a()})};var p=new s,v=a(document);p.init();var b=".layui-tab-title li";v.on("click",b,f.tabClick),v.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),i(l,function(i){return p.set(i)})});layui.define("layer",function(e){"use strict";var a=layui.jquery,t=layui.layer,i=(layui.device(),"layui-upload-enter"),n="layui-upload-iframe",r={icon:2,shift:6},o={file:"文件",video:"视频",audio:"音频"},s=function(e){this.options=e};s.prototype.init=function(){var e=this,t=e.options,r=a("body"),s=a(t.elem||".layui-upload-file"),u=a('');return a("#"+n)[0]||r.append(u),s.each(function(r,s){s=a(s);var u='
      ',l=s.attr("lay-type")||t.type;t.unwrap||(u='
      '+u+''+(s.attr("lay-title")||t.title||"上传"+(o[l]||"图片"))+"
      "),u=a(u),t.unwrap||u.on("dragover",function(e){e.preventDefault(),a(this).addClass(i)}).on("dragleave",function(){a(this).removeClass(i)}).on("drop",function(){a(this).removeClass(i)}),s.parent("form").attr("target")===n&&(t.unwrap?s.unwrap():(s.parent().next().remove(),s.unwrap().unwrap())),s.wrap(u),s.off("change").on("change",function(){e.action(this,l)})})},s.prototype.action=function(e,i){var o=this,s=o.options,u=e.value,l=a(e),p=l.attr("lay-ext")||s.ext||"";if(u){switch(i){case"file":if(p&&!RegExp("\\w\\.("+p+")$","i").test(escape(u)))return t.msg("不支持该文件格式",r),e.value="";break;case"video":if(!RegExp("\\w\\.("+(p||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(u)))return t.msg("不支持该视频格式",r),e.value="";break;case"audio":if(!RegExp("\\w\\.("+(p||"mp3|wav|mid")+")$","i").test(escape(u)))return t.msg("不支持该音频格式",r),e.value="";break;default:if(!RegExp("\\w\\.("+(p||"jpg|png|gif|bmp|jpeg")+")$","i").test(escape(u)))return t.msg("不支持该图片格式",r),e.value=""}s.before&&s.before(e),l.parent().submit();var c=a("#"+n),f=setInterval(function(){var a;try{a=c.contents().find("body").text()}catch(i){t.msg("上传接口存在跨域",r),clearInterval(f)}if(a){clearInterval(f),c.contents().find("body").html("");try{a=JSON.parse(a)}catch(i){return a={},t.msg("请对上传接口返回JSON字符",r)}"function"==typeof s.success&&s.success(a,e)}},30);e.value=""}},e("upload",function(e){var a=new s(e=e||{});a.init()})});layui.define("layer",function(e){"use strict";var i=layui.jquery,t=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:[/^\d+$/,"只能填写数字"],date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var t=this;return i.extend(!0,t.config,e),t},u.prototype.verify=function(e){var t=this;return i.extend(!0,t.config.verify,e),t},u.prototype.on=function(e,i){return layui.onevent(l,e,i)},u.prototype.render=function(e){var t=this,n={select:function(){var e,t="请选择",a="layui-form-select",n="layui-select-title",u="layui-select-none",d="",f=i(r).find("select"),y=function(t,l){i(t.target).parent().hasClass(n)&&!l||(i("."+a).removeClass(a+"ed"),e&&d&&e.val(d)),e=null},v=function(t,r,f){var v=i(this),h=t.find("."+n),p=h.find("input"),m=t.find("dl"),k=m.children("dd");if(!r){var b=function(){t.addClass(a+"ed"),k.removeClass(o)},x=function(){t.removeClass(a+"ed"),p.blur(),g(p.val(),function(e){e&&(d=m.find("."+s).html(),p&&p.val(d))})};h.on("click",function(e){t.hasClass(a+"ed")?x():(y(e,!0),b()),m.find("."+u).remove()}),h.find(".layui-edge").on("click",function(){p.focus()}),p.on("keyup",function(e){var i=e.keyCode;9===i&&b()}).on("keydown",function(e){var i=e.keyCode;9===i?x():13===i&&e.preventDefault()});var g=function(e,t,a){var n=0;layui.each(k,function(){var t=i(this),l=t.text(),r=l.indexOf(e)===-1;(""===e||"blur"===a?e!==l:r)&&n++,"keyup"===a&&t[r?"addClass":"removeClass"](o)});var l=n===k.length;return t(l),l},C=function(e){var i=this.value,t=e.keyCode;return 9!==t&&13!==t&&37!==t&&38!==t&&39!==t&&40!==t&&(g(i,function(e){e?m.find("."+u)[0]||m.append('

      无匹配项

      '):m.find("."+u).remove()},"keyup"),void(""===i&&m.find("."+u).remove()))};f&&p.on("keyup",C).on("blur",function(i){e=p,d=m.find("."+s).html(),setTimeout(function(){g(p.val(),function(e){e&&!d&&p.val("")},"blur")},200)}),k.on("click",function(){var e=i(this),a=e.attr("lay-value"),n=v.attr("lay-filter");return!e.hasClass(c)&&(v.val(a).removeClass("layui-form-danger"),p.val(e.text()),e.addClass(s).siblings().removeClass(s),layui.event.call(this,l,"select("+n+")",{elem:v[0],value:a,othis:t}),x(),!1)}),t.find("dl>dt").on("click",function(e){return!1}),i(document).off("click",y).on("click",y)}};f.each(function(e,l){var r=i(this),o=r.next("."+a),u=this.disabled,d=l.value,f=i(l.options[l.selectedIndex]);if("string"==typeof r.attr("lay-ignore"))return r.show();var y="string"==typeof r.attr("lay-search"),h=i(['
      ','
      ','
      ','
      '+function(e){var i=[];return layui.each(e,function(e,t){(0!==e||t.value)&&("optgroup"===t.tagName.toLowerCase()?i.push("
      "+t.label+"
      "):i.push('
      '+t.innerHTML+"
      "))}),i.join("")}(r.find("*"))+"
      ","
      "].join(""));o[0]&&o.remove(),r.after(h),v.call(this,h,u,y)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},t=i(r).find("input[type=checkbox]"),a=function(e,t){var a=i(this);e.on("click",function(){var i=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(t[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(t[1]).find("em").text(n[0])),layui.event.call(a[0],l,t[2]+"("+i+")",{elem:a[0],value:a[0].value,othis:e}))})};t.each(function(t,n){var l=i(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=i(['
      ',{_switch:""+((n.checked?s[0]:s[1])||"")+""}[r]||(n.title.replace(/\s/g,"")?""+n.title+"":"")+''+(r?"":"")+"","
      "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",t=["",""],a=i(r).find("input[type=radio]"),n=function(a){var n=i(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=i(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(t[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(t[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=i(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();var u=i(['
      ',''+t[l.checked?0:1]+"",""+(l.title||"未命名")+"","
      "].join(""));s[0]&&s.remove(),r.after(u),n.call(this,u)})}};return e?n[e]?n[e]():a.error("不支持的"+e+"表单渲染"):layui.each(n,function(e,i){i()}),t};var d=function(){var e=i(this),a=f.config.verify,s=null,o="layui-form-danger",c={},u=e.parents(r),d=u.find("*[lay-verify]"),y=e.parents("form")[0],v=u.find("input,select,textarea"),h=e.attr("lay-filter");return layui.each(d,function(e,l){var r=i(this),c=r.attr("lay-verify").split("|"),u="",d=r.val();if(r.removeClass(o),layui.each(c,function(e,i){var c="function"==typeof a[i];if(a[i]&&(c?u=a[i](d,l):!a[i][0].test(d)))return t.msg(u||a[i][1],{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}),s)return s}),!s&&(layui.each(v,function(e,i){i.name&&(/^checkbox|radio$/.test(i.type)&&!i.checked||(c[i.name]=i.value))}),layui.event.call(this,l,"submit("+h+")",{elem:this,form:y,field:c}))},f=new u,y=i(document);f.render(),y.on("reset",r,function(){setTimeout(function(){f.render()},50)}),y.on("submit",r,d).on("click","*[lay-submit]",d),e(l,function(e){return f.set(e)})});layui.define("jquery",function(e){"use strict";var o=layui.jquery,a=layui.hint(),r="layui-tree-enter",i=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};i.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},i.prototype.tree=function(e,a){var r=this,i=r.options,n=a||i.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
        '),s=o(["
      • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return i.check?''+("checkbox"===i.check?t.checkbox[0]:"radio"===i.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
      • "].join(""));l&&(s.append(c),r.tree(c,n.children)),e.append(s),"function"==typeof i.click&&r.click(s,n),r.spread(s,n),i.drag&&r.drag(s,n)})},i.prototype.click=function(e,o){var a=this,r=a.options;e.children("a").on("click",function(e){layui.stope(e),r.click(o)})},i.prototype.spread=function(e,o){var a=this,r=(a.options,e.children(".layui-tree-spread")),i=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),i.removeClass("layui-show"),r.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),i.addClass("layui-show"),r.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};i[0]&&(r.on("click",l),n.on("dblclick",l))},i.prototype.on=function(e){var a=this,i=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),i.drag&&o(document).on("mousemove",function(e){var r=a.move;if(r.from){var i=(r.to,o('
        '));e.preventDefault(),o("."+t)[0]||o("body").append(i);var n=o("."+t)[0]?o("."+t):i;n.addClass("layui-show").html(r.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(r),e.to&&e.to.elem.children("a").removeClass(r),a.move={},o("."+t).remove())})},i.prototype.move={},i.prototype.drag=function(e,a){var i=this,t=(i.options,e.children("a")),n=function(){var t=o(this),n=i.move;n.from&&(n.to={item:a,elem:e},t.addClass(r))};t.on("mousedown",function(){var o=i.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=i.move;a.from&&(delete a.to,e.removeClass(r))})},e("tree",function(e){var r=new i(e=e||{}),t=o(e.elem);return t[0]?void r.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})});layui.define("jquery",function(l){"use strict";var o=layui.jquery,i={fixbar:function(l){l=l||{},l.bgcolor=l.bgcolor?"background-color:"+l.bgcolor:"";var i,a,c="layui-fixbar-top",t=[l.bar1===!0?"":l.bar1,l.bar2===!0?"":l.bar2,""],r=o(['
          ',l.bar1?'
        • '+t[0]+"
        • ":"",l.bar2?'
        • '+t[1]+"
        • ":"",'
        • '+t[2]+"
        • ","
        "].join("")),e=r.find("."+c),s=function(){var i=o(document).scrollTop();i>=(l.showHeight||200)?a||(e.show(),a=1):a&&(e.hide(),a=0)};o(".layui-fixbar")[0]||("object"==typeof l.css&&r.css(l.css),o("body").append(r),s(),r.find("li").on("click",function(){var i=o(this),a=i.attr("lay-type");"top"===a&&o("html,body").animate({scrollTop:0},200),l.click&&l.click.call(this,a)}),o(document).on("scroll",function(){i&&clearTimeout(i),i=setTimeout(function(){s()},100)}))}};l("util",i)});layui.define("jquery",function(e){"use strict";var l=layui.jquery,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var u=l(e.elem);if(u[0]){var f=l(e.scrollElem||document),m=e.mb||50,s=!("isAuto"in e)||e.isAuto,y=e.end||"没有更多了",v=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");u.find(".layui-flow-more")[0]||u.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(y):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(f.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=v?e.height():l(window).height(),n=v?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=m&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var f=e.attr("lay-src");layui.img(f,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",f).removeAttr("lay-src"),l[0]&&u(l),i++})}},u=function(e,o){var u=a?(o||n).height():l(window).height(),f=n.scrollTop(),m=f+u;if(t.lazyimg.elem=l(r),e)c(e,u);else for(var s=0;sm)break}};if(u(),!o){var f;n.on("scroll",function(){var e=l(this);f&&clearTimeout(f),f=setTimeout(function(){u(null,e)},50)}),o=!0}return u},e("flow",new o)});layui.define(["layer","form"],function(t){"use strict";var e=layui.jquery,i=layui.layer,a=layui.form(),l=(layui.hint(),layui.device()),n="layedit",o="layui-show",r="layui-disabled",s=function(){var t=this;t.index=0,t.config={tool:["strong","italic","underline","del","|","left","center","right","|","link","unlink","face","image"],hideTool:[],height:280}};s.prototype.set=function(t){var i=this;return e.extend(!0,i.config,t),i},s.prototype.on=function(t,e){return layui.onevent(n,t,e)},s.prototype.build=function(t,i){i=i||{};var a=this,n=a.config,r="layui-layedit",s=e("#"+t),u="LAY_layedit_"+ ++a.index,d=s.next("."+r),y=e.extend({},n,i),f=function(){var t=[],e={};return layui.each(y.hideTool,function(t,i){e[i]=!0}),layui.each(y.tool,function(i,a){C[a]&&!e[a]&&t.push(C[a])}),t.join("")}(),m=e(['
        ','
        '+f+"
        ",'
        ','',"
        ","
        "].join(""));return l.ie&&l.ie<8?s.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),c.call(a,m,s[0],y),s.addClass("layui-hide").after(m),a.index)},s.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},s.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},s.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},s.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var c=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),s=o.find("head"),c=e([""].join("")),u=o.find("body");s.append(c),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,s=e(r.body);s.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

        ")}}),e(n).parents("form").on("submit",function(){var t=s.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),s.on("paste",function(e){r.execCommand("formatBlock",!1,"

        "),setTimeout(function(){f.call(t,s),n.value=s.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),s={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o({url:r.url,method:r.type,elem:e(n).find("input")[0],unwrap:!0,success:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},c=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

        "),setTimeout(function(){o.focus()},10)):s[a]&&s[a].call(this,u),h.call(t,c,i)}},d=/image/;c.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,c),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

          ','
        • ','','
          ','',"
          ","
        • ",'
        • ','','
          ','",'","
          ","
        • ",'
        • ','','',"
        • ","
        "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
      • '+e+'
      • ')}),'
          '+t.join("")+"
        "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
          ','
        • ','','
          ','","
          ","
        • ",'
        • ','','
          ','',"
          ","
        • ",'
        • ','','',"
        • ","
        "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new s;t(n,w)});layui.define("jquery",function(e){"use strict";var a=layui.jquery,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
        1. '+o.replace(/[\r\t\n]+/g,"
        2. ")+"
        "),c.find(">.layui-code-h3")[0]||c.prepend('

        '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

        ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/carousel.js b/static/plugs/layui/lay/modules/carousel.js new file mode 100644 index 000000000..58553943b --- /dev/null +++ b/static/plugs/layui/lay/modules/carousel.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
          ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
        "].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a/g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
        1. '+o.replace(/[\r\t\n]+/g,"
        2. ")+"
        "),c.find(">.layui-code-h3")[0]||c.prepend('

        '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

        ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
        1. '+o.replace(/[\r\t\n]+/g,"
        2. ")+"
        "),c.find(">.layui-code-h3")[0]||c.prepend('

        '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

        ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/element.js b/static/plugs/layui/lay/modules/element.js index e72432415..cddd20a9c 100644 --- a/static/plugs/layui/lay/modules/element.js +++ b/static/plugs/layui/lay/modules/element.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define("jquery",function(i){"use strict";var a=layui.jquery,t=(layui.hint(),layui.device()),l="element",e="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(i){var t=this;return a.extend(!0,t.config,i),t},s.prototype.on=function(i,a){return layui.onevent(l,i,a)},s.prototype.tabAdd=function(i,t){var l=".layui-tab-title",e=a(".layui-tab[lay-filter="+i+"]"),n=e.children(l),s=e.children(".layui-tab-content");return n.append('
      • '+(t.title||"unnaming")+"
      • "),s.append('
        '+(t.content||"")+"
        "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(i,t){var l=".layui-tab-title",e=a(".layui-tab[lay-filter="+i+"]"),n=e.children(l),s=n.find('>li[lay-id="'+t+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(i,t){var l=".layui-tab-title",e=a(".layui-tab[lay-filter="+i+"]"),n=e.children(l),s=n.find('>li[lay-id="'+t+'"]');return f.tabClick(null,null,s),this},s.prototype.progress=function(i,t){var l="layui-progress",e=a("."+l+"[lay-filter="+i+"]"),n=e.find("."+l+"-bar"),s=n.find("."+l+"-text");return n.css("width",t),s.text(t),this};var o=".layui-nav",c="layui-nav-item",r="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",h="layui-nav-more",y="layui-anim layui-anim-upbit",f={tabClick:function(i,t,s){var o=s||a(this),t=t||o.parent().children("li").index(o),c=o.parents(".layui-tab").eq(0),r=c.children(".layui-tab-content").children(".layui-tab-item"),u=c.attr("lay-filter");o.addClass(e).siblings().removeClass(e),r.eq(t).addClass(n).siblings().removeClass(n),layui.event.call(this,l,"tab("+u+")",{elem:c,index:t})},tabDelete:function(i,t){var l=t||a(this).parent(),n=l.index(),s=l.parents(".layui-tab").eq(0),o=s.children(".layui-tab-content").children(".layui-tab-item");l.hasClass(e)&&(l.next()[0]?f.tabClick.call(l.next()[0],null,n+1):l.prev()[0]&&f.tabClick.call(l.prev()[0],null,n-1)),l.remove(),o.eq(n).remove(),setTimeout(function(){f.tabAuto()},50)},tabAuto:function(){var i="layui-tab-more",l="layui-tab-bar",e="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),c=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),r=a('');if(n===window&&8!=t.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var i=a(this);if(!i.find("."+e)[0]){var t=a('');t.on("click",f.tabDelete),i.append(t)}}),o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+l)[0])return;o.append(r),s.attr("overflow",""),r.on("click",function(a){o[this.title?"removeClass":"addClass"](i),this.title=this.title?"":"收缩"})}else o.find("."+l).remove(),s.removeAttr("overflow")})},hideTabMore:function(i){var t=a(".layui-tab-title");i!==!0&&"tabmore"===a(i.target).attr("lay-stope")||(t.removeClass("layui-tab-more"),t.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var i=a(this),t=i.parents(o),n=t.attr("lay-filter");i.find("."+d)[0]||(t.find("."+e).removeClass(e),i.addClass(e),layui.event.call(this,l,"nav("+n+")",i))},clickChild:function(){var i=a(this),t=i.parents(o),n=t.attr("lay-filter");t.find("."+e).removeClass(e),i.addClass(e),layui.event.call(this,l,"nav("+n+")",i)},showChild:function(){var i=a(this),t=i.parents(o),l=i.parent(),e=i.siblings("."+d);t.hasClass(u)&&(e.removeClass(y),l["none"===e.css("display")?"addClass":"removeClass"](c+"ed"))},collapse:function(){var i=a(this),t=i.find(".layui-colla-icon"),e=i.siblings(".layui-colla-content"),s=i.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),c="none"===e.css("display");if("string"==typeof s.attr("lay-accordion")){var r=s.children(".layui-colla-item").children("."+n);r.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),r.removeClass(n)}e[c?"addClass":"removeClass"](n),t.html(c?"":""),layui.event.call(this,l,"collapse("+o+")",{title:i,content:e,show:c})}};s.prototype.init=function(i){var l={tab:function(){f.tabAuto.call({})},nav:function(){var i,l,e,s=200,p=function(o,c){var r=a(this),f=r.find("."+d);c.hasClass(u)?o.css({top:r.position().top,height:r.children("a").height(),opacity:1}):(f.addClass(y),o.css({left:r.position().left+parseFloat(r.css("marginLeft")),top:r.position().top+r.height()-5}),i=setTimeout(function(){o.css({width:r.width(),opacity:1})},t.ie&&t.ie<10?0:s),clearTimeout(e),"block"===f.css("display")&&clearTimeout(l),l=setTimeout(function(){f.addClass(n),r.find("."+h).addClass(h+"d")},300))};a(o).each(function(){var t=a(this),o=a(''),y=t.find("."+c);t.find("."+r)[0]||(t.append(o),y.on("mouseenter",function(){p.call(this,o,t)}).on("mouseleave",function(){t.hasClass(u)||(clearTimeout(l),l=setTimeout(function(){t.find("."+d).removeClass(n),t.find("."+h).removeClass(h+"d")},300))}),t.on("mouseleave",function(){clearTimeout(i),e=setTimeout(function(){t.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},s)})),y.each(function(){var i=a(this),t=i.find("."+d);if(t[0]&&!i.find("."+h)[0]){var l=i.children("a");l.append('')}i.off("click",f.clickThis).on("click",f.clickThis),i.children("a").off("click",f.showChild).on("click",f.showChild),t.children("dd").off("click",f.clickChild).on("click",f.clickChild)})})},breadcrumb:function(){var i=".layui-breadcrumb";a(i).each(function(){var i=a(this),t=i.attr("lay-separator")||">",l=i.find("a");l.find(".layui-box")[0]||(l.each(function(i){i!==l.length-1&&a(this).append(''+t+"")}),i.css("visibility","visible"))})},progress:function(){var i="layui-progress";a("."+i).each(function(){var t=a(this),l=t.find(".layui-progress-bar"),e=l.attr("lay-percent");l.css("width",e),t.attr("lay-showPercent")&&setTimeout(function(){var a=Math.round(l.width()/t.width()*100);a>100&&(a=100),l.html(''+a+"%")},350)})},collapse:function(){var i="layui-collapse";a("."+i).each(function(){var i=a(this).find(".layui-colla-item");i.each(function(){var i=a(this),t=i.find(".layui-colla-title"),l=i.find(".layui-colla-content"),e="none"===l.css("display");t.find(".layui-colla-icon").remove(),t.append(''+(e?"":"")+""),t.off("click",f.collapse).on("click",f.collapse)})})}};return layui.each(l,function(i,a){a()})};var p=new s,v=a(document);p.init();var b=".layui-tab-title li";v.on("click",b,f.tabClick),v.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),i(l,function(i){return p.set(i)})}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
      • "+(i.title||"unnaming")+"
      • ";return s[0]?s.before(r):n.append(r),o.append('
        '+(i.content||"")+"
        "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/flow.js b/static/plugs/layui/lay/modules/flow.js index 295d08464..0528c561b 100644 --- a/static/plugs/layui/lay/modules/flow.js +++ b/static/plugs/layui/lay/modules/flow.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define("jquery",function(e){"use strict";var l=layui.jquery,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var u=l(e.elem);if(u[0]){var f=l(e.scrollElem||document),m=e.mb||50,s=!("isAuto"in e)||e.isAuto,y=e.end||"没有更多了",v=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");u.find(".layui-flow-more")[0]||u.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(y):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(f.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=v?e.height():l(window).height(),n=v?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=m&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var f=e.attr("lay-src");layui.img(f,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",f).removeAttr("lay-src"),l[0]&&u(l),i++})}},u=function(e,o){var u=a?(o||n).height():l(window).height(),f=n.scrollTop(),m=f+u;if(t.lazyimg.elem=l(r),e)c(e,u);else for(var s=0;sm)break}};if(u(),!o){var f;n.on("scroll",function(){var e=l(this);f&&clearTimeout(f),f=setTimeout(function(){u(null,e)},50)}),o=!0}return u},e("flow",new o)}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/form.js b/static/plugs/layui/lay/modules/form.js index a47339a33..2d9373fe3 100644 --- a/static/plugs/layui/lay/modules/form.js +++ b/static/plugs/layui/lay/modules/form.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define("layer",function(e){"use strict";var i=layui.jquery,t=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:[/^\d+$/,"只能填写数字"],date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var t=this;return i.extend(!0,t.config,e),t},u.prototype.verify=function(e){var t=this;return i.extend(!0,t.config.verify,e),t},u.prototype.on=function(e,i){return layui.onevent(l,e,i)},u.prototype.render=function(e){var t=this,n={select:function(){var e,t="请选择",a="layui-form-select",n="layui-select-title",u="layui-select-none",d="",f=i(r).find("select"),y=function(t,l){i(t.target).parent().hasClass(n)&&!l||(i("."+a).removeClass(a+"ed"),e&&d&&e.val(d)),e=null},v=function(t,r,f){var v=i(this),h=t.find("."+n),p=h.find("input"),m=t.find("dl"),k=m.children("dd");if(!r){var b=function(){t.addClass(a+"ed"),k.removeClass(o)},x=function(){t.removeClass(a+"ed"),p.blur(),g(p.val(),function(e){e&&(d=m.find("."+s).html(),p&&p.val(d))})};h.on("click",function(e){t.hasClass(a+"ed")?x():(y(e,!0),b()),m.find("."+u).remove()}),h.find(".layui-edge").on("click",function(){p.focus()}),p.on("keyup",function(e){var i=e.keyCode;9===i&&b()}).on("keydown",function(e){var i=e.keyCode;9===i?x():13===i&&e.preventDefault()});var g=function(e,t,a){var n=0;layui.each(k,function(){var t=i(this),l=t.text(),r=l.indexOf(e)===-1;(""===e||"blur"===a?e!==l:r)&&n++,"keyup"===a&&t[r?"addClass":"removeClass"](o)});var l=n===k.length;return t(l),l},C=function(e){var i=this.value,t=e.keyCode;return 9!==t&&13!==t&&37!==t&&38!==t&&39!==t&&40!==t&&(g(i,function(e){e?m.find("."+u)[0]||m.append('

        无匹配项

        '):m.find("."+u).remove()},"keyup"),void(""===i&&m.find("."+u).remove()))};f&&p.on("keyup",C).on("blur",function(i){e=p,d=m.find("."+s).html(),setTimeout(function(){g(p.val(),function(e){e&&!d&&p.val("")},"blur")},200)}),k.on("click",function(){var e=i(this),a=e.attr("lay-value"),n=v.attr("lay-filter");return!e.hasClass(c)&&(v.val(a).removeClass("layui-form-danger"),p.val(e.text()),e.addClass(s).siblings().removeClass(s),layui.event.call(this,l,"select("+n+")",{elem:v[0],value:a,othis:t}),x(),!1)}),t.find("dl>dt").on("click",function(e){return!1}),i(document).off("click",y).on("click",y)}};f.each(function(e,l){var r=i(this),o=r.next("."+a),u=this.disabled,d=l.value,f=i(l.options[l.selectedIndex]);if("string"==typeof r.attr("lay-ignore"))return r.show();var y="string"==typeof r.attr("lay-search"),h=i(['
        ','
        ','
        ','
        '+function(e){var i=[];return layui.each(e,function(e,t){(0!==e||t.value)&&("optgroup"===t.tagName.toLowerCase()?i.push("
        "+t.label+"
        "):i.push('
        '+t.innerHTML+"
        "))}),i.join("")}(r.find("*"))+"
        ","
        "].join(""));o[0]&&o.remove(),r.after(h),v.call(this,h,u,y)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},t=i(r).find("input[type=checkbox]"),a=function(e,t){var a=i(this);e.on("click",function(){var i=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(t[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(t[1]).find("em").text(n[0])),layui.event.call(a[0],l,t[2]+"("+i+")",{elem:a[0],value:a[0].value,othis:e}))})};t.each(function(t,n){var l=i(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=i(['
        ',{_switch:""+((n.checked?s[0]:s[1])||"")+""}[r]||(n.title.replace(/\s/g,"")?""+n.title+"":"")+''+(r?"":"")+"","
        "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",t=["",""],a=i(r).find("input[type=radio]"),n=function(a){var n=i(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=i(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(t[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(t[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=i(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();var u=i(['
        ',''+t[l.checked?0:1]+"",""+(l.title||"未命名")+"","
        "].join(""));s[0]&&s.remove(),r.after(u),n.call(this,u)})}};return e?n[e]?n[e]():a.error("不支持的"+e+"表单渲染"):layui.each(n,function(e,i){i()}),t};var d=function(){var e=i(this),a=f.config.verify,s=null,o="layui-form-danger",c={},u=e.parents(r),d=u.find("*[lay-verify]"),y=e.parents("form")[0],v=u.find("input,select,textarea"),h=e.attr("lay-filter");return layui.each(d,function(e,l){var r=i(this),c=r.attr("lay-verify").split("|"),u="",d=r.val();if(r.removeClass(o),layui.each(c,function(e,i){var c="function"==typeof a[i];if(a[i]&&(c?u=a[i](d,l):!a[i][0].test(d)))return t.msg(u||a[i][1],{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}),s)return s}),!s&&(layui.each(v,function(e,i){i.name&&(/^checkbox|radio$/.test(i.type)&&!i.checked||(c[i.name]=i.value))}),layui.event.call(this,l,"submit("+h+")",{elem:this,form:y,field:c}))},f=new u,y=i(document);f.render(),y.on("reset",r,function(){setTimeout(function(){f.render()},50)}),y.on("submit",r,d).on("click","*[lay-submit]",d),e(l,function(e){return f.set(e)})}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var t=this;return i.extend(!0,t.config,e),t},u.prototype.verify=function(e){var t=this;return i.extend(!0,t.config.verify,e),t},u.prototype.on=function(e,i){return layui.onevent.call(this,l,e,i)},u.prototype.val=function(e,t){var a=i(r+'[lay-filter="'+e+'"]');a.each(function(e,a){var n=i(this);layui.each(t,function(e,i){var t,a=n.find('[name="'+e+'"]');a[0]&&(t=a[0].type,"checkbox"===t?a[0].checked=i:"radio"===t?a.each(function(){this.value===i&&(this.checked=!0)}):a.val(i))})}),f.render(null,e)},u.prototype.render=function(e,t){var n=this,u=i(r+function(){return t?'[lay-filter="'+t+'"]':""}()),d={select:function(){var e,t="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=u.find("select"),v=function(t,l){i(t.target).parent().hasClass(n)&&!l||(i("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},y=function(t,u,f){var y,p=i(this),m=t.find("."+n),k=m.find("input"),g=t.find("dl"),x=g.children("dd"),b=this.selectedIndex;if(!u){var C=function(){var e=t.offset().top+t.outerHeight()+5-h.scrollTop(),i=g.outerHeight();b=p[0].selectedIndex,t.addClass(a+"ed"),x.removeClass(o),y=null,x.eq(b).addClass(s).siblings().removeClass(s),e+i>h.height()&&e>=i&&t.addClass(a+"up")},w=function(e){t.removeClass(a+"ed "+a+"up"),k.blur(),y=null,e||$(k.val(),function(e){e&&(d=g.find("."+s).html(),k&&k.val(d))})};m.on("click",function(e){t.hasClass(a+"ed")?w():(v(e,!0),C()),g.find("."+r).remove()}),m.find(".layui-edge").on("click",function(){k.focus()}),k.on("keyup",function(e){var i=e.keyCode;9===i&&C()}).on("keydown",function(e){var i=e.keyCode;9===i&&w();var t=function(i,a){var n,l;if(e.preventDefault(),a=function(){return a&&a[0]?a:y&&y[0]?y:x.eq(b)}(),l=a[i](),n=a[i]("dd"),l[0]){if(y=a[i](),!n[0]||n.hasClass(c))return t(i,y);n.addClass(s).siblings().removeClass(s);var r=g.children("dd.layui-this"),o=r.position().top,u=g.height(),d=r.height();o>u&&g.scrollTop(o+g.scrollTop()-u+d-5),o<0&&g.scrollTop(o+g.scrollTop())}};38===i&&t("prev"),40===i&&t("next"),13===i&&(e.preventDefault(),g.children("dd."+s).trigger("click"))});var $=function(e,t,a){var n=0;layui.each(x,function(){var t=i(this),l=t.text(),r=l.indexOf(e)===-1;(""===e||"blur"===a?e!==l:r)&&n++,"keyup"===a&&t[r?"addClass":"removeClass"](o)});var l=n===x.length;return t(l),l},T=function(e){var i=this.value,t=e.keyCode;return 9!==t&&13!==t&&37!==t&&38!==t&&39!==t&&40!==t&&($(i,function(e){e?g.find("."+r)[0]||g.append('

        无匹配项

        '):g.find("."+r).remove()},"keyup"),void(""===i&&g.find("."+r).remove()))};f&&k.on("keyup",T).on("blur",function(t){var a=p[0].selectedIndex;e=k,d=i(p[0].options[a]).html(),setTimeout(function(){$(k.val(),function(e){d||k.val("")},"blur")},200)}),x.on("click",function(){var e=i(this),a=e.attr("lay-value"),n=p.attr("lay-filter");return!e.hasClass(c)&&(e.hasClass("layui-select-tips")?k.val(""):(k.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),p.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:p[0],value:a,othis:t}),w(!0),!1)}),t.find("dl>dt").on("click",function(e){return!1}),i(document).off("click",v).on("click",v)}};f.each(function(e,l){var r=i(this),o=r.next("."+a),u=this.disabled,d=l.value,f=i(l.options[l.selectedIndex]),v=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var h="string"==typeof r.attr("lay-search"),p=v?v.value?t:v.innerHTML||t:t,m=i(['
        ','
        ','','
        ','
        ',function(e){var i=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?i.push("
        "+a.label+"
        "):i.push('
        '+a.innerHTML+"
        "):i.push('
        '+(a.innerHTML||t)+"
        ")}),0===i.length&&i.push('
        没有选项
        '),i.join("")}(r.find("*"))+"
        ","
        "].join(""));o[0]&&o.remove(),r.after(m),y.call(this,m,u,h)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},t=u.find("input[type=checkbox]"),a=function(e,t){var a=i(this);e.on("click",function(){var i=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(t[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(t[1]).find("em").text(n[0])),layui.event.call(a[0],l,t[2]+"("+i+")",{elem:a[0],value:a[0].value,othis:e}))})};t.each(function(t,n){var l=i(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=i(['
        ",function(){var e=n.title.replace(/\s/g,""),i={checkbox:[e?""+n.title+"":"",''].join(""),_switch:""+((n.checked?s[0]:s[1])||"")+""};return i[r]||i.checkbox}(),"
        "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",t=["",""],a=u.find("input[type=radio]"),n=function(a){var n=i(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=i(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(t[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(t[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=i(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var u=i(['
        ',''+t[l.checked?0:1]+"","
        "+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"
        ","
        "].join(""));r.after(u),n.call(this,u)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,i){i()}),n};var d=function(){var e=i(this),a=f.config.verify,s=null,o="layui-form-danger",c={},u=e.parents(r),d=u.find("*[lay-verify]"),v=e.parents("form")[0],h=u.find("input,select,textarea"),y=e.attr("lay-filter");if(layui.each(d,function(e,l){var r=i(this),c=r.attr("lay-verify").split("|"),u=r.attr("lay-verType"),d=r.val();if(r.removeClass(o),layui.each(c,function(e,i){var c,f="",v="function"==typeof a[i];if(a[i]){var c=v?f=a[i](d,l):!a[i][0].test(d);if(f=f||a[i][1],c)return"tips"===u?t.tips(f,function(){return"string"==typeof r.attr("lay-ignore")||"select"!==l.tagName.toLowerCase()&&!/^checkbox|radio$/.test(l.type)?r:r.next()}(),{tips:1}):"alert"===u?t.alert(f,{title:"提示",shadeClose:!0}):t.msg(f,{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}}),s)return s}),s)return!1;var p={};return layui.each(h,function(e,i){if(i.name=(i.name||"").replace(/^\s*|\s*&/,""),i.name){if(/^.*\[\]$/.test(i.name)){var t=i.name.match(/^(.*)\[\]$/g)[0];p[t]=0|p[t],i.name=i.name.replace(/^(.*)\[\]$/,"$1["+p[t]++ +"]")}/^checkbox|radio$/.test(i.type)&&!i.checked||(c[i.name]=i.value)}}),layui.event.call(this,l,"submit("+y+")",{elem:this,form:v,field:c})},f=new u,v=i(document),h=i(window);f.render(),v.on("reset",r,function(){var e=i(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),v.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/jquery.js b/static/plugs/layui/lay/modules/jquery.js index 015155e29..f6d58cdc3 100644 --- a/static/plugs/layui/lay/modules/jquery.js +++ b/static/plugs/layui/lay/modules/jquery.js @@ -1,5 +1,5 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ +/** layui-v2.3.0 MIT License By https://www.layui.com */ ;!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=!!e&&"length"in e&&e.length,n=pe.type(e);return"function"!==n&&!pe.isWindow(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(!fe.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ge,"ms-").replace(me,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var r,i=0;if(n(e))for(r=e.length;iT.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[P]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function f(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;i-1&&(r[l]=!(a[l]=f))}}else x=m(x===a?x.splice(h,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s1&&h(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(r,a,s,u,l){var c,f,d,p=0,h="0",g=r&&[],y=[],v=A,x=r||o&&T.find.TAG("*",l),b=W+=null==v?1:Math.random()||.1,w=x.length;for(l&&(A=a===H||a||l);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===H||(L(c),s=!_);d=e[f++];)if(d(c,a||H,s)){u.push(c);break}l&&(W=b)}i&&((c=!d&&c)&&p--,r&&g.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(g,y,a,s);if(r){if(p>0)for(;h--;)g[h]||y[h]||(y[h]=G.call(u));y=m(y)}Q.apply(u,y),l&&!r&&y.length>0&&p+n.length>1&&t.uniqueSort(u)}return l&&(W=b,A=v),g};return i?r(a):a}var b,w,T,C,E,N,k,S,A,D,j,L,H,q,_,F,M,O,R,P="sizzle"+1*new Date,B=e.document,W=0,I=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(j=!0),0},V=1<<31,Y={}.hasOwnProperty,J=[],G=J.pop,K=J.push,Q=J.push,Z=J.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,xe=/'|\\/g,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Te=function(){L()};try{Q.apply(J=Z.call(B.childNodes),B.childNodes),J[B.childNodes.length].nodeType}catch(Ce){Q={apply:J.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:B;return r!==H&&9===r.nodeType&&r.documentElement?(H=r,q=H.documentElement,_=!E(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=me.test(H.getElementsByClassName),w.getById=i(function(e){return q.appendChild(e).id=P,!H.getElementsByName||!H.getElementsByName(P).length}),w.getById?(T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&_){var n=t.getElementById(e);return n?[n]:[]}},T.filter.ID=function(e){var t=e.replace(be,we);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(be,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&_)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=me.test(H.querySelectorAll))&&(i(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+P+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+P+"+*").length||F.push(".#.+[+~]")}),i(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=me.test(O=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=me.test(q.compareDocumentPosition),R=t||me.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===B&&R(B,e)?-1:t===H||t.ownerDocument===B&&R(B,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===H?-1:t===H?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===B?-1:u[r]===B?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&_&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!_):void 0;return void 0!==r?r:w.attributes||!_?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!w.detectDuplicates,D=!w.sortStable&&e.slice(0),e.sort(U),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},C=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,we),e[3]=(e[3]||e[4]||e[5]||"").replace(be,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s,x=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(d=m,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}), l=c[e]||[],p=l[0]===W&&l[1],x=p&&l[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[W,p,x];break}}else if(v&&(d=t,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),l=c[e]||[],p=l[0]===W&&l[1],x=p),x===!1)for(;(d=++p&&d&&d[g]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==y:1!==d.nodeType)||!++x||(v&&(f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),c[e]=[W,x]),d!==t)););return x-=i,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[P]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=ee(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(se,"$1"));return i[P]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,we),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,we).toLowerCase(),function(t){var n;do if(n=_?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&_&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(be,we),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((u=T.find[s])&&(r=u(a.matches[0].replace(be,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Q.apply(n,r),n;break}}return(l||k(e,f))(r,t,!_,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=P.split("").sort(U).join("")===P,w.detectDuplicates=!!j,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var xe=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&pe(e).is(n))break;r.push(e)}return r},be=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Te=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Ce=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?pe.find.matchesSelector(r,e)?[r]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Ee,Ne=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ee,"string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ne.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:re,!0)),Te.test(r[1])&&pe.isPlainObject(t))for(r in t)pe.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}if(i=re.getElementById(r[2]),i&&i.parentNode){if(i.id!==r[2])return Ee.find(e);this.length=1,this[0]=i}return this.context=re,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Ee=pe(re);var Se=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),r=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return xe(e,"parentNode")},parentsUntil:function(e,t,n){return xe(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return xe(e,"nextSibling")},prevAll:function(e){return xe(e,"previousSibling")},nextUntil:function(e,t,n){return xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return xe(e,"previousSibling",n)},siblings:function(e){return be((e.parentNode||{}).firstChild,e)},children:function(e){return be(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,r){var i=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=pe.filter(r,i)),this.length>1&&(Ae[e]||(i=pe.uniqueSort(i)),Se.test(e)&&(i=i.reverse())),this.pushStack(i)}});var De=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,r,i,a=[],s=[],u=-1,l=function(){for(i=e.once,r=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),n<=u&&u--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=!0,n||c.disable(),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,r):r}},i={};return r.pipe=r.then,pe.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=ie.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,u=1===s?e:pe.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?ie.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);i0||(je.resolveWith(re,[pe]),pe.fn.triggerHandler&&(pe(re).triggerHandler("ready"),pe(re).off("ready"))))}}),pe.ready.promise=function(t){if(!je)if(je=pe.Deferred(),"complete"===re.readyState||"loading"!==re.readyState&&!re.documentElement.doScroll)e.setTimeout(pe.ready);else if(re.addEventListener)re.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{re.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&re.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!pe.isReady){try{n.doScroll("left")}catch(t){return e.setTimeout(i,50)}a(),pe.ready()}}()}return je.promise(t)},pe.ready.promise();var Le;for(Le in pe(fe))break;fe.ownFirst="0"===Le,fe.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,r;n=re.getElementsByTagName("body")[0],n&&n.style&&(t=re.createElement("div"),r=re.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",fe.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=re.createElement("div");fe.deleteExpando=!0;try{delete e.test}catch(t){fe.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,_e=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!l(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return f(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return f(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=pe.camelCase(r.slice(5)),u(o,r,i[r])));pe._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?u(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=pe._data(e,t),n&&(!r||pe.isArray(n)?r=pe._data(e,t,pe.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),r=n.length,i=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
        a",fe.leadingWhitespace=3===e.firstChild.nodeType,fe.tbody=!e.getElementsByTagName("tbody").length,fe.htmlSerialize=!!e.getElementsByTagName("link").length,fe.html5Clone="<:nav>"!==re.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),fe.appendChecked=n.checked,e.innerHTML="",fe.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=re.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),fe.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,fe.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
        ","
        "],area:[1,"",""],param:[1,"",""],thead:[1,"","
        "],tr:[2,"","
        "],col:[2,"","
        "],td:[3,"","
        "],_default:fe.htmlSerialize?[0,"",""]:[1,"X
        ","
        "]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:pe.makeArray(n,[t]),l=pe.event.special[p]||{},i||!l.trigger||l.trigger.apply(r,n)!==!1)){if(!i&&!l.noBubble&&!pe.isWindow(r)){for(u=l.delegateType||p,Ke.test(u+p)||(s=s.parentNode);s;s=s.parentNode)d.push(s),c=s;c===(r.ownerDocument||re)&&d.push(c.defaultView||c.parentWindow||e)}for(f=0;(s=d[f++])&&!t.isPropagationStopped();)t.type=f>1?u:l.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!l._default||l._default.apply(d.pop(),n)===!1)&&He(r)&&a&&r[p]&&!pe.isWindow(r)){c=r[a],c&&(r[a]=null),pe.event.triggered=p;try{r[p]()}catch(g){}pe.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,r,i,o,a=[],s=ie.call(arguments),u=(pe._data(this,"events")||{})[e.type]||[],l=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,u),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,r=((pe.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,u=e.target;if(s&&u.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(r=[],n=0;n-1:pe.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&a.push({elem:u,handlers:r})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(re),st=at.appendChild(re.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var r,i,o,a,s,u=pe.contains(e.ownerDocument,e);if(fe.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(fe.noCloneEvent&&fe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(r=h(o),s=h(e),a=0;null!=(i=s[a]);++a)r[a]&&k(i,r[a]);if(t)if(n)for(s=s||h(e),r=r||h(o),a=0;null!=(i=s[a]);a++)N(i,r[a]);else N(e,o);return r=h(o,"script"),r.length>0&&g(r,!u&&h(e,"script")),r=s=i=null,o},cleanData:function(e,t){for(var n,r,i,o,a=0,s=pe.expando,u=pe.cache,l=fe.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(i=n[s],o=i&&u[i])){if(o.events)for(r in o.events)c[r]?pe.event.remove(n,r):pe.removeEvent(n,r,o.handle);u[i]&&(delete u[i],l||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(i))}}}),pe.fn.extend({domManip:S,detach:function(e){return A(this,e,!0)},remove:function(e){return A(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||re).createTextNode(e))},null,e,arguments.length)},append:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.appendChild(e)}})},prepend:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(fe.htmlSerialize||!et.test(e))&&(fe.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=l.getElementsByTagName("td"),t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),f.removeChild(u)}var n,r,i,o,a,s,u=re.createElement("div"),l=re.createElement("div");l.style&&(l.style.cssText="float:left;opacity:.5",fe.opacity="0.5"===l.style.opacity,fe.cssFloat=!!l.style.cssFloat,l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",fe.clearCloneStyle="content-box"===l.style.backgroundClip,u=re.createElement("div"),u.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",l.innerHTML="",u.appendChild(l),fe.boxSizing=""===l.style.boxSizing||""===l.style.MozBoxSizing||""===l.style.WebkitBoxSizing,pe.extend(fe,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),i},pixelMarginRight:function(){return null==n&&t(),r},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,gt,mt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!fe.pixelMarginRight()&&ft.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),ft.test(a)&&!mt.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,xt=/^(none|table(?!-c[ea]).+)/,bt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Tt={letterSpacing:"0",fontWeight:"400"},Ct=["Webkit","O","Moz","ms"],Et=re.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=gt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":fe.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=pe.camelCase(t),u=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];if(o=typeof n,"string"===o&&(i=Me.exec(n))&&i[1]&&(n=d(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(pe.cssNumber[s]?"":"px")),fe.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{u[t]=n}catch(l){}}},css:function(e,t,n,r){var i,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=gt(e,t,r)),"normal"===o&&t in Tt&&(o=Tt[t]),""===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,r){if(n)return xt.test(pe.css(e,"display"))&&0===e.offsetWidth?dt(e,wt,function(){return M(e,t,r)}):M(e,t,r)},set:function(e,n,r){var i=r&&ht(e);return _(e,n,r?F(e,t,r,fe.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,i),i):0)}}}),fe.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=yt.test(o)?o.replace(yt,i):o+" "+i)}}),pe.cssHooks.marginRight=L(fe.reliableMarginRight,function(e,t){if(t)return dt(e,{display:"inline-block"},gt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(fe.reliableMarginLeft,function(e,t){if(t)return(parseFloat(gt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-dt(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Oe[r]+t]=o[r]||o[r-2]||o[0];return i}},ct.test(e)||(pe.cssHooks[e+t].set=_)}),pe.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(pe.isArray(t)){for(r=ht(e),i=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Re(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=O.prototype.init,pe.fx.step={};var Nt,kt,St=/^(?:toggle|show|hide)$/,At=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(De);for(var n,r=0,i=e.length;r
        a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",fe.getSetAttribute="t"!==n.className,fe.style=/top/.test(e.getAttribute("style")),fe.hrefNormalized="/a"===e.getAttribute("href"),fe.checkOn=!!t.value,fe.optSelected=i.selected,fe.enctype=!!re.createElement("form").enctype,r.disabled=!0,fe.optDisabled=!i.disabled,t=re.createElement("input"),t.setAttribute("value",""),fe.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),fe.radioValue="t"===t.value}();var Dt=/\r/g,jt=/[\x20\t\r\n\f]+/g;pe.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=pe.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,pe(this).val()):e,null==i?i="":"number"==typeof i?i+="":pe.isArray(i)&&(i=pe.map(i,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=pe.valHooks[i.type]||pe.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(Dt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e)).replace(jt," ")}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,a=o?null:[],s=o?i+1:r.length,u=i<0?s:o?i:0;u-1)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},fe.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Lt,Ht,qt=pe.expr.attrHandle,_t=/^(?:checked|selected)$/i,Ft=fe.getSetAttribute,Mt=fe.input;pe.fn.extend({attr:function(e,t){return Pe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),i=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Ht:Lt)),void 0!==n?null===n?void pe.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=pe.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!fe.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(De);if(o&&1===e.nodeType)for(;n=o[i++];)r=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Mt&&Ft||!_t.test(n)?e[r]=!1:e[pe.camelCase("default-"+n)]=e[r]=!1:pe.attr(e,n,""),e.removeAttribute(Ft?n:r)}}),Ht={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Mt&&Ft||!_t.test(n)?e.setAttribute(!Ft&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=qt[t]||pe.find.attr;Mt&&Ft||!_t.test(t)?qt[t]=function(e,t,r){var i,o;return r||(o=qt[t],qt[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,qt[t]=o),i}:qt[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Mt&&Ft||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Lt&&Lt.set(e,t,n)}}),Ft||(Lt={set:function(e,t,n){var r=e.getAttributeNode(n);if(r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n))return t}},qt.id=qt.name=qt.coords=function(e,t,n){var r;if(!n)return(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Lt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Lt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),fe.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Ot=/^(?:input|select|textarea|button|object)$/i,Rt=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Pe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),pe.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,i=pe.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Ot.test(e.nodeName)||Rt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),fe.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),fe.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),fe.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=pe(this),o=e.match(De)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Bt=e.location,Wt=pe.now(),It=/\?/,$t=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=pe.trim(t+"");return i&&!pe.trim(i.replace($t,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new e.DOMParser,n=r.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var zt=/#.*$/,Xt=/([?&])_=[^&]*/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Vt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Yt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Kt={},Qt={},Zt="*/".concat("*"),en=Bt.href,tn=Gt.exec(en.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:en,type:"GET",isLocal:Vt.test(tn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Kt),ajaxTransport:X(Qt),ajax:function(t,n){function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c=void 0,s=i||"",T.readyState=t>0?4:0,o=t>=200&&t<300||304===t,r&&(x=Y(d,T,r)),x=J(d,x,T,o),o?(d.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=T.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===d.type?C="nocontent":304===t?C="notmodified":(C=x.state,f=x.data,v=x.error,o=!v)):(v=C,!t&&C||(C="error",t<0&&(t=0))),T.status=t,T.statusText=(n||C)+"",o?g.resolveWith(p,[f,C,T]):g.rejectWith(p,[T,C,v]),T.statusCode(y),y=void 0,l&&h.trigger(o?"ajaxSuccess":"ajaxError",[T,d,o?f:v]),m.fireWith(p,[T,C]),l&&(h.trigger("ajaxComplete",[T,d]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,d=pe.ajaxSetup({},n),p=d.context||d,h=d.context&&(p.nodeType||p.jquery)?pe(p):pe.event,g=pe.Deferred(),m=pe.Callbacks("once memory"),y=d.statusCode||{},v={},x={},b=0,w="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!f)for(f={};t=Ut.exec(s);)f[t[1].toLowerCase()]=t[2];t=f[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=x[n]=x[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(b<2)for(t in e)y[t]=[y[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),r(0,t),this}};if(g.promise(T).complete=m.add,T.success=T.done,T.error=T.fail,d.url=((t||d.url||en)+"").replace(zt,"").replace(Jt,tn[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=pe.trim(d.dataType||"*").toLowerCase().match(De)||[""],null==d.crossDomain&&(i=Gt.exec(d.url.toLowerCase()),d.crossDomain=!(!i||i[1]===tn[1]&&i[2]===tn[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(tn[3]||("http:"===tn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=pe.param(d.data,d.traditional)),U(Kt,d,n,T),2===b)return T;l=pe.event&&d.global,l&&0===pe.active++&&pe.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Yt.test(d.type),a=d.url,d.hasContent||(d.data&&(a=d.url+=(It.test(a)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Xt.test(a)?a.replace(Xt,"$1_="+Wt++):a+(It.test(a)?"&":"?")+"_="+Wt++)),d.ifModified&&(pe.lastModified[a]&&T.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&T.setRequestHeader("If-None-Match",pe.etag[a])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",d.contentType),T.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Zt+"; q=0.01":""):d.accepts["*"]);for(o in d.headers)T.setRequestHeader(o,d.headers[o]);if(d.beforeSend&&(d.beforeSend.call(p,T,d)===!1||2===b))return T.abort();w="abort";for(o in{success:1,error:1,complete:1})T[o](d[o]);if(c=U(Qt,d,n,T)){if(T.readyState=1,l&&h.trigger("ajaxSend",[T,d]),2===b)return T;d.async&&d.timeout>0&&(u=e.setTimeout(function(){T.abort("timeout")},d.timeout));try{b=1,c.send(v,r)}catch(C){if(!(b<2))throw C;r(-1,C)}}else r(-1,"No Transport");return T},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,r,i){return pe.isFunction(n)&&(i=i||r,r=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:i,data:n,success:r},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return fe.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:K(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var nn=/%20/g,rn=/\[\]$/,on=/\r?\n/g,an=/^(?:submit|button|image|reset|file)$/i,sn=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,r=[],i=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){i(this.name,this.value)});else for(n in e)Q(n,e[n],t,i);return r.join("&").replace(nn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&sn.test(this.nodeName)&&!an.test(e)&&(this.checked||!Be.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():re.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var un=0,ln={},cn=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),fe.cors=!!cn&&"withCredentials"in cn,cn=fe.ajax=!!cn,cn&&pe.ajaxTransport(function(t){if(!t.crossDomain||fe.cors){var n;return{send:function(r,i){var o,a=t.xhr(),s=++un;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)void 0!==r[o]&&a.setRequestHeader(o,r[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,r){var o,u,l;if(n&&(r||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,r)4!==a.readyState&&a.abort();else{l={},o=a.status,"string"==typeof a.responseText&&(l.text=a.responseText);try{u=a.statusText}catch(c){u=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=l.text?200:404}l&&i(o,u,l,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=re.head||pe("head")[0]||re.documentElement;return{send:function(r,i){t=re.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var fn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=fn.pop()||pe.expando+"_"+Wt++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(It.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||pe.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?pe(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,fn.push(i)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||re;var r=Te.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=y([e],t,i),i&&i.length&&pe(i).remove(),pe.merge([],r.childNodes))};var pn=pe.fn.load;return pe.fn.load=function(e,t,n){if("string"!=typeof e&&pn)return pn.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&pe.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?pe("
        ").append(pe.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=pe.css(e,"position"),f=pe(e),d={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=pe.css(e,"top"),u=pe.css(e,"left"),l=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,u])>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+i),"using"in t?t.using.call(e,d):f.css(d)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,i)?("undefined"!=typeof i.getBoundingClientRect&&(r=i.getBoundingClientRect()),n=te(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===pe.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(r,"marginTop",!0),left:t.left-n.left-pe.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){ -for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){e("jquery",pe)}),pe}); \ No newline at end of file +for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){layui.$=pe,e("jquery",pe)}),pe}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/laydate.js b/static/plugs/layui/lay/modules/laydate.js index 40b83f4b1..345a21786 100644 --- a/static/plugs/layui/lay/modules/laydate.js +++ b/static/plugs/layui/lay/modules/laydate.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define(function(e){"use strict";var t=window,a={path:"",skin:"default",format:"YYYY-MM-DD",min:"1900-01-01 00:00:00",max:"2099-12-31 23:59:59",isv:!1,init:!0},n={},s=document,i="createElement",o="getElementById",l="getElementsByTagName",d=["laydate_box","laydate_void","laydate_click","LayDateSkin","skins/","/laydate.css"];t.laydate=function(e){return e=e||{},n.run(e),laydate},laydate.v="1.1",n.trim=function(e){return e=e||"",e.replace(/^\s|\s$/g,"").replace(/\s+/g," ")},n.digit=function(e){return e<10?"0"+(0|e):e},n.stopmp=function(e){return e=e||t.event,e.stopPropagation?e.stopPropagation():e.cancelBubble=!0,this},n.each=function(e,t){for(var a=0,n=e.length;an.maxs[0]?s=["y",1]:e>=n.mins[0]&&e<=n.maxs[0]&&(e==n.mins[0]&&(tn.maxs[1]?s=["m",1]:t==n.maxs[1]&&a>n.maxs[2]&&(s=["d",1]))),s},n.timeVoid=function(e,t){if(n.ymd[1]+1==n.mins[1]&&n.ymd[2]==n.mins[2]){if(0===t&&en.maxs[3])return 1;if(1===t&&e>n.maxs[4])return 1;if(2===t&&e>n.maxs[5])return 1}if(e>(t?59:23))return 1},n.check=function(){var e=n.options.format.replace(/YYYY|MM|DD|hh|mm|ss/g,"\\d+\\").replace(/\\$/g,""),t=new RegExp(e),a=n.elem[d.elemv],s=a.match(/\d+/g)||[],i=n.checkVoid(s[0],s[1],s[2]);if(""!==a.replace(/\s/g,"")){if(!t.test(a))return n.elem[d.elemv]="",n.msg("日期不符合格式,请重新选择。"),1;if(i[0])return n.elem[d.elemv]="",n.msg("日期不在有效期内,请重新选择。"),1;i.value=n.elem[d.elemv].match(t).join(),s=i.value.match(/\d+/g),s[1]<1?(s[1]=1,i.auto=1):s[1]>12?(s[1]=12,i.auto=1):s[1].length<2&&(i.auto=1),s[2]<1?(s[2]=1,i.auto=1):s[2]>n.months[(0|s[1])-1]?(s[2]=31,i.auto=1):s[2].length<2&&(i.auto=1),s.length>3&&(n.timeVoid(s[3],0)&&(i.auto=1),n.timeVoid(s[4],1)&&(i.auto=1),n.timeVoid(s[5],2)&&(i.auto=1)),i.auto?n.creation([s[0],0|s[1],0|s[2]],1):i.value!==n.elem[d.elemv]&&(n.elem[d.elemv]=i.value)}},n.months=[31,null,31,30,31,30,31,31,30,31,30,31],n.viewDate=function(e,t,a){var s=(n.query,{}),i=new Date;e<(0|n.mins[0])&&(e=0|n.mins[0]),e>(0|n.maxs[0])&&(e=0|n.maxs[0]),i.setFullYear(e,t,a),s.ymd=[i.getFullYear(),i.getMonth(),i.getDate()],n.months[1]=n.isleap(s.ymd[0])?29:28,i.setFullYear(s.ymd[0],s.ymd[1],1),s.FDay=i.getDay(),s.PDay=n.months[0===t?11:t-1]-s.FDay+1,s.NDay=1,n.each(d.tds,function(e,t){var a,i=s.ymd[0],o=s.ymd[1]+1;t.className="",e=s.FDay&&e'+e+"年":'
      • '+(e-7+t)+"年
      • "}),t("#laydate_ys").innerHTML=a,n.each(t("#laydate_ys li"),function(e,t){"y"===n.checkVoid(t.getAttribute("y"))[0]?n.addClass(t,d[1]):n.on(t,"click",function(e){n.stopmp(e).reshow(),n.viewDate(0|this.getAttribute("y"),n.ymd[1],n.ymd[2])})})},n.initDate=function(){var e=(n.query,new Date),t=n.elem[d.elemv].match(/\d+/g)||[];t.length<3&&(t=n.options.start.match(/\d+/g)||[],t.length<3&&(t=[e.getFullYear(),e.getMonth()+1,e.getDate()])),n.inymd=t,n.viewDate(t[0],t[1]-1,t[2])},n.iswrite=function(){var e=n.query,t={time:e("#laydate_hms")};n.shde(t.time,!n.options.istime),n.shde(d.oclear,!("isclear"in n.options?n.options.isclear:1)),n.shde(d.otoday,!("istoday"in n.options?n.options.istoday:1)),n.shde(d.ok,!("issure"in n.options?n.options.issure:1))},n.orien=function(e,t){var a,s=n.elem.getBoundingClientRect();e.style.left=s.left+(t?0:n.scroll(1))+"px",a=s.bottom+e.offsetHeight/1.5<=n.winarea()?s.bottom-1:s.top>e.offsetHeight/1.5?s.top-e.offsetHeight+1:n.winarea()-e.offsetHeight,e.style.top=Math.max(a+(t?0:n.scroll()),1)+"px"},n.follow=function(e){n.options.fixed?(e.style.position="fixed",n.orien(e,1)):(e.style.position="absolute",n.orien(e))},n.viewtb=function(){var e,t=[],a=["日","一","二","三","四","五","六"],o={},d=s[i]("table"),r=s[i]("thead");return r.appendChild(s[i]("tr")),o.creath=function(e){var t=s[i]("th");t.innerHTML=a[e],r[l]("tr")[0].appendChild(t),t=null},n.each(new Array(6),function(a){t.push([]),e=d.insertRow(0),n.each(new Array(7),function(n){t[a][n]=0,0===a&&o.creath(n),e.insertCell(n)})}),d.insertBefore(r,d.children[0]),d.id=d.className="laydate_table",e=t=null,d.outerHTML.toLowerCase()}(),n.view=function(e,t){var o,l=n.query,r={};t=t||e,n.elem=e,n.options=t,n.options.format||(n.options.format=a.format),n.options.start=n.options.start||"",n.mm=r.mm=[n.options.min||a.min,n.options.max||a.max],n.mins=r.mm[0].match(/\d+/g),n.maxs=r.mm[1].match(/\d+/g),n.box?n.shde(n.box):(o=s[i]("div"),o.id=d[0],o.className=d[0],o.style.cssText="position: absolute;",o.setAttribute("name","laydate-v"+laydate.v),o.innerHTML=r.html='
          '+function(){var e="";return n.each(new Array(12),function(t){e+=''+n.digit(t+1)+"月"}),e}()+"
          "+n.viewtb+'",s.body.appendChild(o),n.box=l("#"+d[0]),n.events(),o=null),n.follow(n.box),t.zIndex?n.box.style.zIndex=t.zIndex:n.removeCssAttr(n.box,"z-index"),n.stopMosup("click",n.box),n.initDate(),n.iswrite(),n.check()},n.reshow=function(){return n.each(n.query("#"+d[0]+" .laydate_show"),function(e,t){n.removeClass(t,"laydate_show")}),this},n.close=function(){n.reshow(),n.shde(n.query("#"+d[0]),1),n.elem=null},n.parse=function(e,t,s){return e=e.concat(t),s=s||(n.options?n.options.format:a.format),s.replace(/YYYY|MM|DD|hh|mm|ss/g,function(t,a){return e.index=0|++e.index,n.digit(e[e.index])})},n.creation=function(e,t){var a=(n.query,n.hmsin),s=n.parse(e,[a[0].value,a[1].value,a[2].value]);n.elem[d.elemv]=s,t||(n.close(),"function"==typeof n.options.choose&&n.options.choose(s))},n.events=function(){var e=n.query,a={box:"#"+d[0]};n.addClass(s.body,"laydate_body"),d.tds=e("#laydate_table td"),d.mms=e("#laydate_ms span"),d.year=e("#laydate_y"),d.month=e("#laydate_m"),n.each(e(a.box+" .laydate_ym"),function(e,t){n.on(t,"click",function(t){n.stopmp(t).reshow(),n.addClass(this[l]("div")[0],"laydate_show"),e||(a.YY=parseInt(d.year.value),n.viewYears(a.YY))})}),n.on(e(a.box),"click",function(){n.reshow()}),a.tabYear=function(e){0===e?n.ymd[0]--:1===e?n.ymd[0]++:2===e?a.YY-=14:a.YY+=14,e<2?(n.viewDate(n.ymd[0],n.ymd[1],n.ymd[2]),n.reshow()):n.viewYears(a.YY)},n.each(e("#laydate_YY .laydate_tab"),function(e,t){n.on(t,"click",function(t){n.stopmp(t),a.tabYear(e)})}),a.tabMonth=function(e){e?(n.ymd[1]++,12===n.ymd[1]&&(n.ymd[0]++,n.ymd[1]=0)):(n.ymd[1]--,n.ymd[1]===-1&&(n.ymd[0]--,n.ymd[1]=11)),n.viewDate(n.ymd[0],n.ymd[1],n.ymd[2])},n.each(e("#laydate_MM .laydate_tab"),function(e,t){n.on(t,"click",function(t){n.stopmp(t).reshow(),a.tabMonth(e)})}),n.each(e("#laydate_ms span"),function(e,t){n.on(t,"click",function(e){n.stopmp(e).reshow(),n.hasClass(this,d[1])||n.viewDate(n.ymd[0],0|this.getAttribute("m"),n.ymd[2])})}),n.each(e("#laydate_table td"),function(e,t){n.on(t,"click",function(e){n.hasClass(this,d[1])||(n.stopmp(e),n.creation([0|this.getAttribute("y"),0|this.getAttribute("m"),0|this.getAttribute("d")]))})}),d.oclear=e("#laydate_clear"),n.on(d.oclear,"click",function(){n.elem[d.elemv]="",n.close()}),d.otoday=e("#laydate_today"),n.on(d.otoday,"click",function(){var e=new Date;n.creation([e.getFullYear(),e.getMonth()+1,e.getDate()])}),d.ok=e("#laydate_ok"),n.on(d.ok,"click",function(){n.valid&&n.creation([n.ymd[0],n.ymd[1]+1,n.ymd[2]])}),a.times=e("#laydate_time"),n.hmsin=a.hmsin=e("#laydate_hms input"),a.hmss=["小时","分钟","秒数"],a.hmsarr=[],n.msg=function(t,s){var i='
          '+(s||"提示")+"×
          ";"string"==typeof t?(i+="

          "+t+"

          ",n.shde(e("#"+d[0])),n.removeClass(a.times,"laydate_time1").addClass(a.times,"laydate_msg")):(a.hmsarr[t]?i=a.hmsarr[t]:(i+='
          ',n.each(new Array(0===t?24:60),function(e){i+=""+e+""}),i+="
          ",a.hmsarr[t]=i),n.removeClass(a.times,"laydate_msg"),n[0===t?"removeClass":"addClass"](a.times,"laydate_time1")),n.addClass(a.times,"laydate_show"),a.times.innerHTML=i},a.hmson=function(t,a){var s=e("#laydate_hmsno span"),i=n.valid?null:1;n.each(s,function(e,s){i?n.addClass(s,d[1]):n.timeVoid(e,a)?n.addClass(s,d[1]):n.on(s,"click",function(e){n.hasClass(this,d[1])||(t.value=n.digit(0|this.innerHTML))})}),n.addClass(s[0|t.value],"laydate_click")},n.each(a.hmsin,function(e,t){n.on(t,"click",function(t){n.stopmp(t).reshow(),n.msg(e,a.hmss[e]),a.hmson(this,e)})}),n.on(s,"mouseup",function(){var t=e("#"+d[0]);t&&"none"!==t.style.display&&(n.check()||n.close())}).on(s,"keydown",function(e){e=e||t.event;var a=e.keyCode;13===a&&n.elem&&n.creation([n.ymd[0],n.ymd[1]+1,n.ymd[2]])})},laydate.reset=function(){n.box&&n.elem&&n.follow(n.box)},laydate.now=function(e,t){var a=new Date(0|e?function(e){return e<864e5?+new Date+864e5*e:e}(parseInt(e)):+new Date);return n.parse([a.getFullYear(),a.getMonth()+1,a.getDate()],[a.getHours(),a.getMinutes(),a.getSeconds()],t)},layui.addcss("modules/laydate/laydate.css",function(){},"laydatecss"),e("laydate",laydate)}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;!function(){"use strict";var e=window.layui&&layui.define,t={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,n=t.length-1,a=n;a>0;a--)if("interactive"===t[a].readyState){e=t[a].src;break}return e||t[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),getStyle:function(e,t){var n=e.currentStyle?e.currentStyle:window.getComputedStyle(e,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](t)},link:function(e,a,i){if(n.path){var r=document.getElementsByTagName("head")[0],o=document.createElement("link");"string"==typeof a&&(i=a);var s=(i||e).replace(/\.|\//g,""),l="layuicss-"+s,d=0;o.rel="stylesheet",o.href=n.path+e,o.id=l,document.getElementById(l)||r.appendChild(o),"function"==typeof a&&!function c(){return++d>80?window.console&&console.error("laydate.css: Invalid"):void(1989===parseInt(t.getStyle(document.getElementById(l),"width"))?a():setTimeout(c,100))}()}}},n={v:"5.0.9",config:{},index:window.laydate&&window.laydate.v?1e5:0,path:t.getPath,set:function(e){var t=this;return t.config=w.extend({},t.config,e),t},ready:function(a){var i="laydate",r="",o=(e?"modules/laydate/":"theme/")+"default/laydate.css?v="+n.v+r;return e?layui.addcss(o,a,i):t.link(o,a,i),this}},a=function(){var e=this;return{hint:function(t){e.hint.call(e,t)},config:e.config}},i="laydate",r=".layui-laydate",o="layui-this",s="laydate-disabled",l="开始日期超出了结束日期
          建议重新选择",d=[100,2e5],c="layui-laydate-static",m="layui-laydate-list",u="laydate-selected",h="layui-laydate-hint",y="laydate-day-prev",f="laydate-day-next",p="layui-laydate-footer",g=".laydate-btns-confirm",v="laydate-time-text",D=".laydate-btns-time",T=function(e){var t=this;t.index=++n.index,t.config=w.extend({},t.config,n.config,e),n.ready(function(){t.init()})},w=function(e){return new C(e)},C=function(e){for(var t=0,n="object"==typeof e?[e]:(this.selector=e,document.querySelectorAll(e||null));t0)return n[0].getAttribute(e)}():n.each(function(n,a){a.setAttribute(e,t)})},C.prototype.removeAttr=function(e){return this.each(function(t,n){n.removeAttribute(e)})},C.prototype.html=function(e){return this.each(function(t,n){n.innerHTML=e})},C.prototype.val=function(e){return this.each(function(t,n){n.value=e})},C.prototype.append=function(e){return this.each(function(t,n){"object"==typeof e?n.appendChild(e):n.innerHTML=n.innerHTML+e})},C.prototype.remove=function(e){return this.each(function(t,n){e?n.removeChild(e):n.parentNode.removeChild(n)})},C.prototype.on=function(e,t){return this.each(function(n,a){a.attachEvent?a.attachEvent("on"+e,function(e){e.target=e.srcElement,t.call(a,e)}):a.addEventListener(e,t,!1)})},C.prototype.off=function(e,t){return this.each(function(n,a){a.detachEvent?a.detachEvent("on"+e,t):a.removeEventListener(e,t,!1)})},T.isLeapYear=function(e){return e%4===0&&e%100!==0||e%400===0},T.prototype.config={type:"date",range:!1,format:"yyyy-MM-dd",value:null,isInitValue:!0,min:"1900-1-1",max:"2099-12-31",trigger:"focus",show:!1,showBottom:!0,btns:["clear","now","confirm"],lang:"cn",theme:"default",position:null,calendar:!1,mark:{},zIndex:null,done:null,change:null},T.prototype.lang=function(){var e=this,t=e.config,n={cn:{weeks:["日","一","二","三","四","五","六"],time:["时","分","秒"],timeTips:"选择时间",startTime:"开始时间",endTime:"结束时间",dateTips:"返回日期",month:["一","二","三","四","五","六","七","八","九","十","十一","十二"],tools:{confirm:"确定",clear:"清空",now:"现在"}},en:{weeks:["Su","Mo","Tu","We","Th","Fr","Sa"],time:["Hours","Minutes","Seconds"],timeTips:"Select Time",startTime:"Start Time",endTime:"End Time",dateTips:"Select Date",month:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],tools:{confirm:"Confirm",clear:"Clear",now:"Now"}}};return n[t.lang]||n.cn},T.prototype.init=function(){var e=this,t=e.config,n="yyyy|y|MM|M|dd|d|HH|H|mm|m|ss|s",a="static"===t.position,i={year:"yyyy",month:"yyyy-MM",date:"yyyy-MM-dd",time:"HH:mm:ss",datetime:"yyyy-MM-dd HH:mm:ss"};t.elem=w(t.elem),t.eventElem=w(t.eventElem),t.elem[0]&&(t.range===!0&&(t.range="-"),t.format===i.date&&(t.format=i[t.type]),e.format=t.format.match(new RegExp(n+"|.","g"))||[],e.EXP_IF="",e.EXP_SPLIT="",w.each(e.format,function(t,a){var i=new RegExp(n).test(a)?"\\d{"+function(){return new RegExp(n).test(e.format[0===t?t+1:t-1]||"")?/^yyyy|y$/.test(a)?4:a.length:/^yyyy$/.test(a)?"1,4":/^y$/.test(a)?"1,308":"1,2"}()+"}":"\\"+a;e.EXP_IF=e.EXP_IF+i,e.EXP_SPLIT=e.EXP_SPLIT+"("+i+")"}),e.EXP_IF=new RegExp("^"+(t.range?e.EXP_IF+"\\s\\"+t.range+"\\s"+e.EXP_IF:e.EXP_IF)+"$"),e.EXP_SPLIT=new RegExp("^"+e.EXP_SPLIT+"$",""),e.isInput(t.elem[0])||"focus"===t.trigger&&(t.trigger="click"),t.elem.attr("lay-key")||(t.elem.attr("lay-key",e.index),t.eventElem.attr("lay-key",e.index)),t.mark=w.extend({},t.calendar&&"cn"===t.lang?{"0-1-1":"元旦","0-2-14":"情人","0-3-8":"妇女","0-3-12":"植树","0-4-1":"愚人","0-5-1":"劳动","0-5-4":"青年","0-6-1":"儿童","0-9-10":"教师","0-9-18":"国耻","0-10-1":"国庆","0-12-25":"圣诞"}:{},t.mark),w.each(["min","max"],function(e,n){var a=[],i=[];if("number"==typeof t[n]){var r=t[n],o=(new Date).getTime(),s=864e5,l=new Date(r?r0)return!0;var a=w.elem("div",{"class":"layui-laydate-header"}),i=[function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-y"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-m"});return e.innerHTML="",e}(),function(){var e=w.elem("div",{"class":"laydate-set-ym"}),t=w.elem("span"),n=w.elem("span");return e.appendChild(t),e.appendChild(n),e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-m"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-y"});return e.innerHTML="",e}()],d=w.elem("div",{"class":"layui-laydate-content"}),c=w.elem("table"),m=w.elem("thead"),u=w.elem("tr");w.each(i,function(e,t){a.appendChild(t)}),m.appendChild(u),w.each(new Array(6),function(e){var t=c.insertRow(0);w.each(new Array(7),function(a){if(0===e){var i=w.elem("th");i.innerHTML=n.weeks[a],u.appendChild(i)}t.insertCell(a)})}),c.insertBefore(m,c.children[0]),d.appendChild(c),r[e]=w.elem("div",{"class":"layui-laydate-main laydate-main-list-"+e}),r[e].appendChild(a),r[e].appendChild(d),o.push(i),s.push(d),l.push(c)}),w(d).html(function(){var e=[],i=[];return"datetime"===t.type&&e.push(''+n.timeTips+""),w.each(t.btns,function(e,r){var o=n.tools[r]||"btn";t.range&&"now"===r||(a&&"clear"===r&&(o="cn"===t.lang?"重置":"Reset"),i.push(''+o+""))}),e.push('"),e.join("")}()),w.each(r,function(e,t){i.appendChild(t)}),t.showBottom&&i.appendChild(d),/^#/.test(t.theme)){var m=w.elem("style"),u=["#{{id}} .layui-laydate-header{background-color:{{theme}};}","#{{id}} .layui-this{background-color:{{theme}} !important;}"].join("").replace(/{{id}}/g,e.elemID).replace(/{{theme}}/g,t.theme);"styleSheet"in m?(m.setAttribute("type","text/css"),m.styleSheet.cssText=u):m.innerHTML=u,w(i).addClass("laydate-theme-molv"),i.appendChild(m)}e.remove(T.thisElemDate),a?t.elem.append(i):(document.body.appendChild(i),e.position()),e.checkDate().calendar(),e.changeEvent(),T.thisElemDate=e.elemID,"function"==typeof t.ready&&t.ready(w.extend({},t.dateTime,{month:t.dateTime.month+1}))},T.prototype.remove=function(e){var t=this,n=(t.config,w("#"+(e||t.elemID)));return n.hasClass(c)||t.checkDate(function(){n.remove()}),t},T.prototype.position=function(){var e=this,t=e.config,n=e.bindElem||t.elem[0],a=n.getBoundingClientRect(),i=e.elem.offsetWidth,r=e.elem.offsetHeight,o=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},s=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},l=5,d=a.left,c=a.bottom;d+i+l>s("width")&&(d=s("width")-i-l),c+r+l>s()&&(c=a.top>r?a.top-r:s()-r,c-=2*l),t.position&&(e.elem.style.position=t.position),e.elem.style.left=d+("fixed"===t.position?0:o(1))+"px",e.elem.style.top=c+("fixed"===t.position?0:o())+"px"},T.prototype.hint=function(e){var t=this,n=(t.config,w.elem("div",{"class":h}));n.innerHTML=e||"",w(t.elem).find("."+h).remove(),t.elem.appendChild(n),clearTimeout(t.hinTimer),t.hinTimer=setTimeout(function(){w(t.elem).find("."+h).remove()},3e3)},T.prototype.getAsYM=function(e,t,n){return n?t--:t++,t<0&&(t=11,e--),t>11&&(t=0,e++),[e,t]},T.prototype.systemDate=function(e){var t=e||new Date;return{year:t.getFullYear(),month:t.getMonth(),date:t.getDate(),hours:e?e.getHours():0,minutes:e?e.getMinutes():0,seconds:e?e.getSeconds():0}},T.prototype.checkDate=function(e){var t,a,i=this,r=(new Date,i.config),o=r.dateTime=r.dateTime||i.systemDate(),s=i.bindElem||r.elem[0],l=(i.isInput(s)?"val":"html",i.isInput(s)?s.value:"static"===r.position?"":s.innerHTML),c=function(e){e.year>d[1]&&(e.year=d[1],a=!0),e.month>11&&(e.month=11,a=!0),e.hours>23&&(e.hours=0,a=!0),e.minutes>59&&(e.minutes=0,e.hours++,a=!0),e.seconds>59&&(e.seconds=0,e.minutes++,a=!0),t=n.getEndDate(e.month+1,e.year),e.date>t&&(e.date=t,a=!0)},m=function(e,t,n){var o=["startTime","endTime"];t=(t.match(i.EXP_SPLIT)||[]).slice(1),n=n||0,r.range&&(i[o[n]]=i[o[n]]||{}),w.each(i.format,function(s,l){var c=parseFloat(t[s]);t[s].length必须遵循下述格式:
          "+(r.range?r.format+" "+r.range+" "+r.format:r.format)+"
          已为你重置"),a=!0):l&&l.constructor===Date?r.dateTime=i.systemDate(l):(r.dateTime=i.systemDate(),delete i.startState,delete i.endState,delete i.startDate,delete i.endDate,delete i.startTime,delete i.endTime),c(o),a&&l&&i.setValue(r.range?i.endDate?i.parse():"":i.parse()),e&&e(),i)},T.prototype.mark=function(e,t){var n,a=this,i=a.config;return w.each(i.mark,function(e,a){var i=e.split("-");i[0]!=t[0]&&0!=i[0]||i[1]!=t[1]&&0!=i[1]||i[2]!=t[2]||(n=a||t[2])}),n&&e.html(''+n+""),a},T.prototype.limit=function(e,t,n,a){var i,r=this,o=r.config,l={},d=o[n>41?"endDate":"dateTime"],c=w.extend({},d,t||{});return w.each({now:c,min:o.min,max:o.max},function(e,t){l[e]=r.newDate(w.extend({year:t.year,month:t.month,date:t.date},function(){var e={};return w.each(a,function(n,a){e[a]=t[a]}),e}())).getTime()}),i=l.nowl.max,e&&e[i?"addClass":"removeClass"](s),i},T.prototype.calendar=function(e){var t,a,i,r=this,s=r.config,l=e||s.dateTime,c=new Date,m=r.lang(),u="date"!==s.type&&"datetime"!==s.type,h=e?1:0,y=w(r.table[h]).find("td"),f=w(r.elemHeader[h][2]).find("span");if(l.yeard[1]&&(l.year=d[1],r.hint("最高只能支持到公元"+d[1]+"年")),r.firstDate||(r.firstDate=w.extend({},l)),c.setFullYear(l.year,l.month,1),t=c.getDay(),a=n.getEndDate(l.month||12,l.year),i=n.getEndDate(l.month+1,l.year),w.each(y,function(e,n){var d=[l.year,l.month],c=0;n=w(n),n.removeAttr("class"),e=t&&e=n.firstDate.year&&(r.month=a.max.month,r.date=a.max.date),n.limit(w(i),r,t),M++}),w(u[f?0:1]).attr("lay-ym",M-8+"-"+T[1]).html(b+p+" - "+(M-1+p))}else if("month"===e)w.each(new Array(12),function(e){var i=w.elem("li",{"lay-ym":e}),s={year:T[0],month:e};e+1==T[1]&&w(i).addClass(o),i.innerHTML=r.month[e]+(f?"月":""),d.appendChild(i),T[0]=n.firstDate.year&&(s.date=a.max.date),n.limit(w(i),s,t)}),w(u[f?0:1]).attr("lay-ym",T[0]+"-"+T[1]).html(T[0]+p);else if("time"===e){var E=function(){w(d).find("ol").each(function(e,a){w(a).find("li").each(function(a,i){n.limit(w(i),[{hours:a},{hours:n[x].hours,minutes:a},{hours:n[x].hours,minutes:n[x].minutes,seconds:a}][e],t,[["hours"],["hours","minutes"],["hours","minutes","seconds"]][e])})}),a.range||n.limit(w(n.footer).find(g),n[x],0,["hours","minutes","seconds"])};a.range?n[x]||(n[x]={hours:0,minutes:0,seconds:0}):n[x]=i,w.each([24,60,60],function(e,t){var a=w.elem("li"),i=["

          "+r.time[e]+"

            "];w.each(new Array(t),function(t){i.push(""+w.digit(t,2)+"")}),a.innerHTML=i.join("")+"
          ",d.appendChild(a)}),E()}if(y&&h.removeChild(y),h.appendChild(d),"year"===e||"month"===e)w(n.elemMain[t]).addClass("laydate-ym-show"),w(d).find("li").on("click",function(){var r=0|w(this).attr("lay-ym");if(!w(this).hasClass(s)){if(0===t)i[e]=r,l&&(n.startDate[e]=r),n.limit(w(n.footer).find(g),null,0);else if(l)n.endDate[e]=r;else{var c="year"===e?n.getAsYM(r,T[1]-1,"sub"):n.getAsYM(T[0],r,"sub");w.extend(i,{year:c[0],month:c[1]})}"year"===a.type||"month"===a.type?(w(d).find("."+o).removeClass(o),w(this).addClass(o),"month"===a.type&&"year"===e&&(n.listYM[t][0]=r,l&&(n[["startDate","endDate"][t]].year=r),n.list("month",t))):(n.checkDate("limit").calendar(),n.closeList()),n.setBtnStatus(),a.range||n.done(null,"change"),w(n.footer).find(D).removeClass(s)}});else{var S=w.elem("span",{"class":v}),k=function(){w(d).find("ol").each(function(e){var t=this,a=w(t).find("li");t.scrollTop=30*(n[x][C[e]]-2),t.scrollTop<=0&&a.each(function(e,n){if(!w(this).hasClass(s))return t.scrollTop=30*(e-2),!0})})},H=w(c[2]).find("."+v);k(),S.innerHTML=a.range?[r.startTime,r.endTime][t]:r.timeTips,w(n.elemMain[t]).addClass("laydate-time-show"),H[0]&&H.remove(),c[2].appendChild(S),w(d).find("ol").each(function(e){var t=this;w(t).find("li").on("click",function(){var r=0|this.innerHTML;w(this).hasClass(s)||(a.range?n[x][C[e]]=r:i[C[e]]=r,w(t).find("."+o).removeClass(o),w(this).addClass(o),E(),k(),(n.endDate||"time"===a.type)&&n.done(null,"change"),n.setBtnStatus())})})}return n},T.prototype.listYM=[],T.prototype.closeList=function(){var e=this;e.config;w.each(e.elemCont,function(t,n){w(this).find("."+m).remove(),w(e.elemMain[t]).removeClass("laydate-ym-show laydate-time-show")}),w(e.elem).find("."+v).remove()},T.prototype.setBtnStatus=function(e,t,n){var a,i=this,r=i.config,o=w(i.footer).find(g),d=r.range&&"date"!==r.type&&"time"!==r.type;d&&(t=t||i.startDate,n=n||i.endDate,a=i.newDate(t).getTime()>i.newDate(n).getTime(),i.limit(null,t)||i.limit(null,n)?o.addClass(s):o[a?"addClass":"removeClass"](s),e&&a&&i.hint("string"==typeof e?l.replace(/日期/g,e):l))},T.prototype.parse=function(e,t){var n=this,a=n.config,i=t||(e?w.extend({},n.endDate,n.endTime):a.range?w.extend({},n.startDate,n.startTime):a.dateTime),r=n.format.concat();return w.each(r,function(e,t){/yyyy|y/.test(t)?r[e]=w.digit(i.year,t.length):/MM|M/.test(t)?r[e]=w.digit(i.month+1,t.length):/dd|d/.test(t)?r[e]=w.digit(i.date,t.length):/HH|H/.test(t)?r[e]=w.digit(i.hours,t.length):/mm|m/.test(t)?r[e]=w.digit(i.minutes,t.length):/ss|s/.test(t)&&(r[e]=w.digit(i.seconds,t.length))}),a.range&&!e?r.join("")+" "+a.range+" "+n.parse(1):r.join("")},T.prototype.newDate=function(e){return e=e||{},new Date(e.year||1,e.month||0,e.date||1,e.hours||0,e.minutes||0,e.seconds||0)},T.prototype.setValue=function(e){var t=this,n=t.config,a=t.bindElem||n.elem[0],i=t.isInput(a)?"val":"html";return"static"===n.position||w(a)[i](e||""),this},T.prototype.stampRange=function(){var e,t,n=this,a=n.config,i=w(n.elem).find("td");if(a.range&&!n.endDate&&w(n.footer).find(g).addClass(s),n.endDate)return e=n.newDate({year:n.startDate.year,month:n.startDate.month,date:n.startDate.date}).getTime(),t=n.newDate({year:n.endDate.year,month:n.endDate.month,date:n.endDate.date}).getTime(),e>t?n.hint(l):void w.each(i,function(a,i){var r=w(i).attr("lay-ymd").split("-"),s=n.newDate({year:r[0],month:r[1]-1,date:r[2]}).getTime();w(i).removeClass(u+" "+o),s!==e&&s!==t||w(i).addClass(w(i).hasClass(y)||w(i).hasClass(f)?u:o),s>e&&s','
          '+f+"
          ",'
          ','',"
          ","
          "].join(""));return l.ie&&l.ie<8?s.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),c.call(a,m,s[0],y),s.addClass("layui-hide").after(m),a.index)},s.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},s.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},s.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},s.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var c=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),s=o.find("head"),c=e([""].join("")),u=o.find("body");s.append(c),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,s=e(r.body);s.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

          ")}}),e(n).parents("form").on("submit",function(){var t=s.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),s.on("paste",function(e){r.execCommand("formatBlock",!1,"

          "),setTimeout(function(){f.call(t,s),n.value=s.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),s={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o({url:r.url,method:r.type,elem:e(n).find("input")[0],unwrap:!0,success:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},c=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

          "),setTimeout(function(){o.focus()},10)):s[a]&&s[a].call(this,u),h.call(t,c,i)}},d=/image/;c.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,c),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

            ','
          • ','','
            ','',"
            ","
          • ",'
          • ','','
            ','",'","
            ","
          • ",'
          • ','','',"
          • ","
          "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
        • '+e+'
        • ')}),'
            '+t.join("")+"
          "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
            ','
          • ','','
            ','","
            ","
          • ",'
          • ','','
            ','',"
            ","
          • ",'
          • ','','',"
          • ","
          "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new s;t(n,w)}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define(["layer","form"],function(t){"use strict";var e=layui.$,i=layui.layer,a=layui.form,l=(layui.hint(),layui.device()),n="layedit",o="layui-show",r="layui-disabled",c=function(){var t=this;t.index=0,t.config={tool:["strong","italic","underline","del","|","left","center","right","|","link","unlink","face","image"],hideTool:[],height:280}};c.prototype.set=function(t){var i=this;return e.extend(!0,i.config,t),i},c.prototype.on=function(t,e){return layui.onevent(n,t,e)},c.prototype.build=function(t,i){i=i||{};var a=this,n=a.config,r="layui-layedit",c=e("string"==typeof t?"#"+t:t),u="LAY_layedit_"+ ++a.index,d=c.next("."+r),y=e.extend({},n,i),f=function(){var t=[],e={};return layui.each(y.hideTool,function(t,i){e[i]=!0}),layui.each(y.tool,function(i,a){C[a]&&!e[a]&&t.push(C[a])}),t.join("")}(),m=e(['
          ','
          '+f+"
          ",'
          ','',"
          ","
          "].join(""));return l.ie&&l.ie<8?c.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),s.call(a,m,c[0],y),c.addClass("layui-hide").after(m),a.index)},c.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},c.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},c.prototype.setContent=function(t,i,a){var l=u(t);l[0]&&(a?e(l[0].document.body).append(i):e(l[0].document.body).html(i),layedit.sync(t))},c.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},c.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var s=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),c=o.find("head"),s=e([""].join("")),u=o.find("body");c.append(s),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,c=e(r.body);c.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

          ")}}),e(n).parents("form").on("submit",function(){var t=c.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),c.on("paste",function(e){r.execCommand("formatBlock",!1,"

          "),setTimeout(function(){f.call(t,c),n.value=c.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),c={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o.render({url:r.url,method:r.type,elem:e(n).find("input")[0],done:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},s=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

          "),setTimeout(function(){o.focus()},10)):c[a]&&c[a].call(this,u),h.call(t,s,i)}},d=/image/;s.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,s),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

            ','
          • ','','
            ','',"
            ","
          • ",'
          • ','','
            ','",'","
            ","
          • ",'
          • ','','',"
          • ","
          "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
        • '+e+'
        • ')}),'
            '+t.join("")+"
          "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
            ','
          • ','','
            ','","
            ","
          • ",'
          • ','','
            ','',"
            ","
          • ",'
          • ','','',"
          • ","
          "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new c;t(n,w)}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/layer.js b/static/plugs/layui/lay/modules/layer.js index 321c5add2..a8975b9a1 100644 --- a/static/plugs/layui/lay/modules/layer.js +++ b/static/plugs/layui/lay/modules/layer.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.scripts,t=e[e.length-1],i=t.src;if(!t.getAttribute("merge"))return i.substring(0,i.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"]},r={v:"3.0.3",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):r.link("skin/"+e.extend),this):this},link:function(t,n,a){if(r.path){var o=i("head")[0],s=document.createElement("link");"string"==typeof n&&(a=n);var l=(a||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,i("#"+f)[0]||o.appendChild(s),"function"==typeof n&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(i("#"+f).css("width"))?n():setTimeout(u,100))}()}},ready:function(e){var t="skinlayercss",i="303";return a?layui.addcss("modules/layer/default/layer.css?v="+r.v+i,e,t):r.link("skin/default/layer.css?v="+r.v+i,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
          '+(f?r.title[0]:r.title)+"
          ":"";return r.zIndex=s,t([r.shade?'
          ':"",'
          '+(e&&2!=r.type?"":u)+'
          '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
          '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
          '+e+"
          "}():"")+(r.resize?'':"")+"
          "],u,i('
          ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]&&e.layero.addClass(l.anim[t.anim]),t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){function t(e){e=s.find(e),e.height(f[1]-c-u-2*(0|parseFloat(e.css("padding-top"))))}var a=this,o=a.config,s=i("#"+l[0]+e);""===o.area[0]&&o.maxWidth>0&&(r.ie&&r.ie<8&&o.btn&&s.width(s.innerWidth()),s.outerWidth()>o.maxWidth&&s.width(o.maxWidth));var f=[s.innerWidth(),s.innerHeight()],c=s.find(l[1]).outerHeight()||0,u=s.find("."+l[6]).outerHeight()||0;switch(o.type){case 2:t("iframe");break;default:""===o.area[1]?o.fixed&&f[1]>=n.height()&&(f[1]=n.height(),t("."+l[5])):t("."+l[5])}return a},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass(a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(e){s=e.find(".layui-layer-input"),s.focus(),"function"==typeof f&&f(e)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,n="";if(e>0)for(n=''+t[0].title+"";i"+t[i].title+"";return n}(),content:'
            '+function(){var e=t.length,i=1,n="";if(e>0)for(n='
          • '+(t[0].content||"no content")+"
          • ";i'+(t[i].content||"no content")+"";return n}()+"
          ",success:function(t){var a=t.find(".layui-layer-title").children(),o=t.find(".layui-layer-tabmain").children();a.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var n=i(this),a=n.index();n.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),o.eq(a).show().siblings().hide(),"function"==typeof e.change&&e.change(a)}),"function"==typeof n&&n(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
          '+(u.length>1?'':"")+'
          '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
          ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
          是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.jquery),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
          '+(f?r.title[0]:r.title)+"
          ":"";return r.zIndex=s,t([r.shade?'
          ':"",'
          '+(e&&2!=r.type?"":u)+'
          '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
          '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
          '+e+"
          "}():"")+(r.resize?'':"")+"
          "],u,i('
          ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(t){s=t.find(".layui-layer-input"),s.val(e.value||"").focus(),"function"==typeof f&&f(t)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"";return a}(),content:'
            '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
          • '+(t[0].content||"no content")+"
          • ";i'+(t[i].content||"no content")+"";return a}()+"
          ",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
          '+(u.length>1?'':"")+'
          '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
          ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
          是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/laypage.js b/static/plugs/layui/lay/modules/laypage.js index ed2ffa3fd..2cf64d54e 100644 --- a/static/plugs/layui/lay/modules/laypage.js +++ b/static/plugs/layui/lay/modules/laypage.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define(function(a){"use strict";function t(a){new p(a)}var e=document,r="getElementById",n="getElementsByTagName",s=0,p=function(a){var t=this,e=t.config=a||{};e.item=s++,t.render(!0)};p.on=function(a,t,e){return a.attachEvent?a.attachEvent("on"+t,function(){e.call(a,window.even)}):a.addEventListener(t,e,!1),p},p.prototype.type=function(){var a=this.config;if("object"==typeof a.cont)return void 0===a.cont.length?2:3},p.prototype.view=function(){var a=this,t=a.config,e=[],r={};if(t.pages=0|t.pages,t.curr=0|t.curr||1,t.groups="groups"in t?0|t.groups:5,t.first="first"in t?t.first:"首页",t.last="last"in t?t.last:"末页",t.prev="prev"in t?t.prev:"上一页",t.next="next"in t?t.next:"下一页",t.pages<=1)return"";for(t.groups>t.pages&&(t.groups=t.pages),r.index=Math.ceil((t.curr+(t.groups>1&&t.groups!==t.pages?1:0))/(0===t.groups?1:t.groups)),t.curr>1&&t.prev&&e.push(''+t.prev+""),r.index>1&&t.first&&0!==t.groups&&e.push(''+t.first+""),r.poor=Math.floor((t.groups-1)/2),r.start=r.index>1?t.curr-r.poor:1,r.end=r.index>1?function(){var a=t.curr+(t.groups-r.poor-1);return a>t.pages?t.pages:a}():t.groups,r.end-r.start"+r.start+""):e.push(''+r.start+"");return t.pages>t.groups&&r.end'+t.last+""),r.flow=!t.prev&&0===t.groups,(t.curr!==t.pages&&t.next||r.flow)&&e.push(function(){return r.flow&&t.curr===t.pages?''+t.next+"":''+t.next+""}()),'
          '+e.join("")+function(){return t.skip?'到第 ':""}()+"
          "},p.prototype.jump=function(a){if(a){for(var t=this,e=t.config,r=a.children,s=a[n]("button")[0],i=a[n]("input")[0],u=0,o=r.length;ua.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+"":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+"");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('');r<=u;r++)r===a.curr?e.push('"+r+""):e.push(''+r+"");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+"")),e.join("")}(),next:function(){return a.next?''+a.next+"":""}(),count:'共 '+a.count+" 条",limit:function(){var e=['"}(),refresh:['','',""].join(""),skip:function(){return['到第','','页',""].join("")}()};return['
          ',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
          "].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/laytpl.js b/static/plugs/layui/lay/modules/laytpl.js index 524283e02..9e694d7f0 100644 --- a/static/plugs/layui/lay/modules/laytpl.js +++ b/static/plugs/layui/lay/modules/laytpl.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},n={exp:function(e){return new RegExp(e,"g")},query:function(e,n,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return c((n||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var n="Laytpl Error:";return"object"==typeof console&&console.error(n+e+"\n"+(r||"")),n+e}},c=n.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=c("^"+r.open+"#",""),l=c(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(c(r.open+"#"),r.open+"# ").replace(c(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(/(?="|')/g,"\\").replace(n.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(n.query(1),function(e){var n='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(c(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),n='"+_escape_('),n+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,n.escape)}catch(u){return delete o.cache,n.error(u,p)}},t.pt.render=function(e,r){var c,t=this;return e?(c=t.cache?t.cache(e,n.escape):t.parse(t.tpl,e),r?void r(c):c):n.error("no data")};var o=function(e){return"string"!=typeof e?n.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var n in e)r[n]=e[n]},o.v="1.2.0",e("laytpl",o)}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/mobile.js b/static/plugs/layui/lay/modules/mobile.js index 56e98b6ce..58621708f 100644 --- a/static/plugs/layui/lay/modules/mobile.js +++ b/static/plugs/layui/lay/modules/mobile.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define(function(i){i("layui.mobile",layui.v)});layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},n={exp:function(e){return new RegExp(e,"g")},query:function(e,n,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return c((n||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var n="Laytpl Error:";return"object"==typeof console&&console.error(n+e+"\n"+(r||"")),n+e}},c=n.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=c("^"+r.open+"#",""),l=c(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(c(r.open+"#"),r.open+"# ").replace(c(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(/(?="|')/g,"\\").replace(n.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(n.query(1),function(e){var n='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(c(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),n='"+_escape_('),n+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,n.escape)}catch(u){return delete o.cache,n.error(u,p)}},t.pt.render=function(e,r){var c,t=this;return e?(c=t.cache?t.cache(e,n.escape):t.parse(t.tpl,e),r?void r(c):c):n.error("no data")};var o=function(e){return"string"!=typeof e?n.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var n in e)r[n]=e[n]},o.v="1.2.0",e("laytpl",o)});layui.define(function(e){"use strict";var t=(window,document),i="querySelectorAll",n="getElementsByClassName",a=function(e){return t[i](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var i in e)t[i]=e[i];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var o=0,r=["layui-m-layer"],d=function(e){var t=this;t.config=l.extend(e),t.view()};d.prototype.view=function(){var e=this,i=e.config,s=t.createElement("div");e.id=s.id=r[0]+o,s.setAttribute("class",r[0]+" "+r[0]+(i.type||0)),s.setAttribute("index",o);var l=function(){var e="object"==typeof i.title;return i.title?'

          '+(e?i.title[0]:i.title)+"

          ":""}(),d=function(){"string"==typeof i.btn&&(i.btn=[i.btn]);var e,t=(i.btn||[]).length;return 0!==t&&i.btn?(e=''+i.btn[0]+"",2===t&&(e=''+i.btn[1]+""+e),'
          '+e+"
          "):""}();if(i.fixed||(i.top=i.hasOwnProperty("top")?i.top:100,i.style=i.style||"",i.style+=" top:"+(t.body.scrollTop+i.top)+"px"),2===i.type&&(i.content='

          '+(i.content||"")+"

          "),i.skin&&(i.anim="up"),"msg"===i.skin&&(i.shade=!1),s.innerHTML=(i.shade?"
          ':"")+'
          "+l+'
          '+i.content+"
          "+d+"
          ",!i.type||2===i.type){var y=t[n](r[0]+i.type),u=y.length;u>=1&&c.close(y[0].getAttribute("index"))}document.body.appendChild(s);var m=e.elem=a("#"+e.id)[0];i.success&&i.success(m),e.index=o++,e.action(i,m)},d.prototype.action=function(e,t){var i=this;e.time&&(l.timer[i.index]=setTimeout(function(){c.close(i.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),c.close(i.index)):e.yes?e.yes(i.index):c.close(i.index)};if(e.btn)for(var s=t[n]("layui-m-layerbtn")[0].children,o=s.length,r=0;r0&&e-1 in t)}function s(t){return A.call(t,function(t){return null!=t})}function u(t){return t.length>0?T.fn.concat.apply([],t):t}function c(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function l(t){return t in F?F[t]:F[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function f(t,e){return"number"!=typeof e||k[c(t)]?e:e+"px"}function h(t){var e,n;return $[t]||(e=L.createElement(t),L.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),$[t]=n),$[t]}function p(t){return"children"in t?D.call(t.children):T.map(t.childNodes,function(t){if(1==t.nodeType)return t})}function d(t,e){var n,r=t?t.length:0;for(n=0;n]*>/,R=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,Z=/^(?:body|html)$/i,q=/([A-Z])/g,H=["val","css","html","text","data","width","height","offset"],I=["after","prepend","before","append"],V=L.createElement("table"),_=L.createElement("tr"),B={tr:L.createElement("tbody"),tbody:V,thead:V,tfoot:V,td:_,th:_,"*":L.createElement("div")},U=/complete|loaded|interactive/,X=/^[\w-]*$/,J={},W=J.toString,Y={},G=L.createElement("div"),K={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},Q=Array.isArray||function(t){return t instanceof Array};return Y.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var r,i=t.parentNode,o=!i;return o&&(i=G).appendChild(t),r=~Y.qsa(i,e).indexOf(t),o&&G.removeChild(t),r},C=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},N=function(t){return A.call(t,function(e,n){return t.indexOf(e)==n})},Y.fragment=function(t,e,n){var r,i,a;return R.test(t)&&(r=T(L.createElement(RegExp.$1))),r||(t.replace&&(t=t.replace(z,"<$1>")),e===E&&(e=M.test(t)&&RegExp.$1),e in B||(e="*"),a=B[e],a.innerHTML=""+t,r=T.each(D.call(a.childNodes),function(){a.removeChild(this)})),o(n)&&(i=T(r),T.each(n,function(t,e){H.indexOf(t)>-1?i[t](e):i.attr(t,e)})),r},Y.Z=function(t,e){return new d(t,e)},Y.isZ=function(t){return t instanceof Y.Z},Y.init=function(t,n){var r;if(!t)return Y.Z();if("string"==typeof t)if(t=t.trim(),"<"==t[0]&&M.test(t))r=Y.fragment(t,RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}else{if(e(t))return T(L).ready(t);if(Y.isZ(t))return t;if(Q(t))r=s(t);else if(i(t))r=[t],t=null;else if(M.test(t))r=Y.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}}return Y.Z(r,t)},T=function(t,e){return Y.init(t,e)},T.extend=function(t){var e,n=D.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){m(t,n,e)}),t},Y.qsa=function(t,e){var n,r="#"==e[0],i=!r&&"."==e[0],o=r||i?e.slice(1):e,a=X.test(o);return t.getElementById&&a&&r?(n=t.getElementById(o))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:D.call(a&&!r&&t.getElementsByClassName?i?t.getElementsByClassName(o):t.getElementsByTagName(e):t.querySelectorAll(e))},T.contains=L.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},T.type=t,T.isFunction=e,T.isWindow=n,T.isArray=Q,T.isPlainObject=o,T.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},T.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},T.inArray=function(t,e,n){return O.indexOf.call(e,t,n)},T.camelCase=C,T.trim=function(t){return null==t?"":String.prototype.trim.call(t)},T.uuid=0,T.support={},T.expr={},T.noop=function(){},T.map=function(t,e){var n,r,i,o=[];if(a(t))for(r=0;r=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return O.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return e(t)?this.not(this.not(t)):T(A.call(this,function(e){return Y.matches(e,t)}))},add:function(t,e){return T(N(this.concat(T(t,e))))},is:function(t){return this.length>0&&Y.matches(this[0],t)},not:function(t){var n=[];if(e(t)&&t.call!==E)this.each(function(e){t.call(this,e)||n.push(this)});else{var r="string"==typeof t?this.filter(t):a(t)&&e(t.item)?D.call(t):T(t);this.forEach(function(t){r.indexOf(t)<0&&n.push(t)})}return T(n)},has:function(t){return this.filter(function(){return i(t)?T.contains(this,t):T(this).find(t).size()})},eq:function(t){return t===-1?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!i(t)?t:T(t)},last:function(){var t=this[this.length-1];return t&&!i(t)?t:T(t)},find:function(t){var e,n=this;return e=t?"object"==typeof t?T(t).filter(function(){var t=this;return O.some.call(n,function(e){return T.contains(e,t)})}):1==this.length?T(Y.qsa(this[0],t)):this.map(function(){return Y.qsa(this,t)}):T()},closest:function(t,e){var n=[],i="object"==typeof t&&T(t);return this.each(function(o,a){for(;a&&!(i?i.indexOf(a)>=0:Y.matches(a,t));)a=a!==e&&!r(a)&&a.parentNode;a&&n.indexOf(a)<0&&n.push(a)}),T(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=T.map(n,function(t){if((t=t.parentNode)&&!r(t)&&e.indexOf(t)<0)return e.push(t),t});return v(e,t)},parent:function(t){return v(N(this.pluck("parentNode")),t)},children:function(t){return v(this.map(function(){return p(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||D.call(this.childNodes)})},siblings:function(t){return v(this.map(function(t,e){return A.call(p(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return T.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=h(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var n=e(t);if(this[0]&&!n)var r=T(t).get(0),i=r.parentNode||this.length>1;return this.each(function(e){T(this).wrapAll(n?t.call(this,e):i?r.cloneNode(!0):r)})},wrapAll:function(t){if(this[0]){T(this[0]).before(t=T(t));for(var e;(e=t.children()).length;)t=e.first();T(t).append(this)}return this},wrapInner:function(t){var n=e(t);return this.each(function(e){var r=T(this),i=r.contents(),o=n?t.call(this,e):t;i.length?i.wrapAll(o):r.append(o)})},unwrap:function(){return this.parent().each(function(){T(this).replaceWith(T(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var e=T(this);(t===E?"none"==e.css("display"):t)?e.show():e.hide()})},prev:function(t){return T(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return T(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;T(this).empty().append(g(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=g(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(t,e){var n;return"string"!=typeof t||1 in arguments?this.each(function(n){if(1===this.nodeType)if(i(t))for(j in t)y(this,j,t[j]);else y(this,t,g(this,e,n,this.getAttribute(t)))}):0 in this&&1==this[0].nodeType&&null!=(n=this[0].getAttribute(t))?n:E},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){y(this,t)},this)})},prop:function(t,e){return t=K[t]||t,1 in arguments?this.each(function(n){this[t]=g(this,e,n,this[t])}):this[0]&&this[0][t]},removeProp:function(t){return t=K[t]||t,this.each(function(){delete this[t]})},data:function(t,e){var n="data-"+t.replace(q,"-$1").toLowerCase(),r=1 in arguments?this.attr(n,e):this.attr(n);return null!==r?b(r):E},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each(function(e){this.value=g(this,t,e,this.value)})):this[0]&&(this[0].multiple?T(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var n=T(this),r=g(this,t,e,n.offset()),i=n.offsetParent().offset(),o={top:r.top-i.top,left:r.left-i.left};"static"==n.css("position")&&(o.position="relative"),n.css(o)});if(!this.length)return null;if(L.documentElement!==this[0]&&!T.contains(L.documentElement,this[0]))return{top:0,left:0};var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(e,n){if(arguments.length<2){var r=this[0];if("string"==typeof e){if(!r)return;return r.style[C(e)]||getComputedStyle(r,"").getPropertyValue(e)}if(Q(e)){if(!r)return;var i={},o=getComputedStyle(r,"");return T.each(e,function(t,e){i[e]=r.style[C(e)]||o.getPropertyValue(e)}),i}}var a="";if("string"==t(e))n||0===n?a=c(e)+":"+f(e,n):this.each(function(){this.style.removeProperty(c(e))});else for(j in e)e[j]||0===e[j]?a+=c(j)+":"+f(j,e[j])+";":this.each(function(){this.style.removeProperty(c(j))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(T(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return!!t&&O.some.call(this,function(t){return this.test(x(t))},l(t))},addClass:function(t){return t?this.each(function(e){if("className"in this){S=[];var n=x(this),r=g(this,t,e,n);r.split(/\s+/g).forEach(function(t){T(this).hasClass(t)||S.push(t)},this),S.length&&x(this,n+(n?" ":"")+S.join(" "))}}):this},removeClass:function(t){return this.each(function(e){if("className"in this){if(t===E)return x(this,"");S=x(this),g(this,t,e,S).split(/\s+/g).forEach(function(t){S=S.replace(l(t)," ")}),x(this,S.trim())}})},toggleClass:function(t,e){return t?this.each(function(n){var r=T(this),i=g(this,t,n,x(this));i.split(/\s+/g).forEach(function(t){(e===E?!r.hasClass(t):e)?r.addClass(t):r.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var e="scrollTop"in this[0];return t===E?e?this[0].scrollTop:this[0].pageYOffset:this.each(e?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var e="scrollLeft"in this[0];return t===E?e?this[0].scrollLeft:this[0].pageXOffset:this.each(e?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),r=Z.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(T(t).css("margin-top"))||0,n.left-=parseFloat(T(t).css("margin-left"))||0,r.top+=parseFloat(T(e[0]).css("border-top-width"))||0,r.left+=parseFloat(T(e[0]).css("border-left-width"))||0,{top:n.top-r.top,left:n.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||L.body;t&&!Z.test(t.nodeName)&&"static"==T(t).css("position");)t=t.offsetParent;return t})}},T.fn.detach=T.fn.remove,["width","height"].forEach(function(t){var e=t.replace(/./,function(t){return t[0].toUpperCase()});T.fn[t]=function(i){var o,a=this[0];return i===E?n(a)?a["inner"+e]:r(a)?a.documentElement["scroll"+e]:(o=this.offset())&&o[t]:this.each(function(e){a=T(this),a.css(t,g(this,i,e,a[t]()))})}}),I.forEach(function(e,n){var r=n%2;T.fn[e]=function(){var e,i,o=T.map(arguments,function(n){var r=[];return e=t(n),"array"==e?(n.forEach(function(t){return t.nodeType!==E?r.push(t):T.zepto.isZ(t)?r=r.concat(t.get()):void(r=r.concat(Y.fragment(t)))}),r):"object"==e||null==n?n:Y.fragment(n)}),a=this.length>1;return o.length<1?this:this.each(function(t,e){i=r?e:e.parentNode,e=0==n?e.nextSibling:1==n?e.firstChild:2==n?e:null;var s=T.contains(L.documentElement,i);o.forEach(function(t){if(a)t=t.cloneNode(!0);else if(!i)return T(t).remove();i.insertBefore(t,e),s&&w(t,function(t){if(!(null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src)){var e=t.ownerDocument?t.ownerDocument.defaultView:window;e.eval.call(e,t.innerHTML)}})})})},T.fn[r?e+"To":"insert"+(n?"Before":"After")]=function(t){return T(t)[e](this),this}}),Y.Z.prototype=d.prototype=T.fn,Y.uniq=N,Y.deserializeValue=b,T.zepto=Y,T}();!function(t){function e(t){return t._zid||(t._zid=h++)}function n(t,n,o,a){if(n=r(n),n.ns)var s=i(n.ns);return(v[e(t)]||[]).filter(function(t){return t&&(!n.e||t.e==n.e)&&(!n.ns||s.test(t.ns))&&(!o||e(t.fn)===e(o))&&(!a||t.sel==a)})}function r(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function i(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function o(t,e){return t.del&&!y&&t.e in x||!!e}function a(t){return b[t]||y&&x[t]||t}function s(n,i,s,u,l,h,p){var d=e(n),m=v[d]||(v[d]=[]);i.split(/\s/).forEach(function(e){if("ready"==e)return t(document).ready(s);var i=r(e);i.fn=s,i.sel=l,i.e in b&&(s=function(e){var n=e.relatedTarget;if(!n||n!==this&&!t.contains(this,n))return i.fn.apply(this,arguments)}),i.del=h;var d=h||s;i.proxy=function(t){if(t=c(t),!t.isImmediatePropagationStopped()){t.data=u;var e=d.apply(n,t._args==f?[t]:[t].concat(t._args));return e===!1&&(t.preventDefault(),t.stopPropagation()),e}},i.i=m.length,m.push(i),"addEventListener"in n&&n.addEventListener(a(i.e),i.proxy,o(i,p))})}function u(t,r,i,s,u){var c=e(t);(r||"").split(/\s/).forEach(function(e){n(t,e,i,s).forEach(function(e){delete v[c][e.i],"removeEventListener"in t&&t.removeEventListener(a(e.e),e.proxy,o(e,u))})})}function c(e,n){return!n&&e.isDefaultPrevented||(n||(n=e),t.each(T,function(t,r){var i=n[t];e[t]=function(){return this[r]=w,i&&i.apply(n,arguments)},e[r]=E}),e.timeStamp||(e.timeStamp=Date.now()),(n.defaultPrevented!==f?n.defaultPrevented:"returnValue"in n?n.returnValue===!1:n.getPreventDefault&&n.getPreventDefault())&&(e.isDefaultPrevented=w)),e}function l(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===f||(n[e]=t[e]);return c(n,t)}var f,h=1,p=Array.prototype.slice,d=t.isFunction,m=function(t){return"string"==typeof t},v={},g={},y="onfocusin"in window,x={focus:"focusin",blur:"focusout"},b={mouseenter:"mouseover",mouseleave:"mouseout"};g.click=g.mousedown=g.mouseup=g.mousemove="MouseEvents",t.event={add:s,remove:u},t.proxy=function(n,r){var i=2 in arguments&&p.call(arguments,2);if(d(n)){var o=function(){return n.apply(r,i?i.concat(p.call(arguments)):arguments)};return o._zid=e(n),o}if(m(r))return i?(i.unshift(n[r],n),t.proxy.apply(null,i)):t.proxy(n[r],n);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,r){return this.on(t,e,n,r,1)};var w=function(){return!0},E=function(){return!1},j=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,T={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,n,r,i,o){var a,c,h=this;return e&&!m(e)?(t.each(e,function(t,e){h.on(t,n,r,e,o)}),h):(m(n)||d(i)||i===!1||(i=r,r=n,n=f),i!==f&&r!==!1||(i=r,r=f),i===!1&&(i=E),h.each(function(f,h){o&&(a=function(t){return u(h,t.type,i),i.apply(this,arguments)}),n&&(c=function(e){var r,o=t(e.target).closest(n,h).get(0);if(o&&o!==h)return r=t.extend(l(e),{currentTarget:o,liveFired:h}),(a||i).apply(o,[r].concat(p.call(arguments,1)))}),s(h,e,i,r,n,c||a)}))},t.fn.off=function(e,n,r){var i=this;return e&&!m(e)?(t.each(e,function(t,e){i.off(t,n,e)}),i):(m(n)||d(r)||r===!1||(r=n,n=f),r===!1&&(r=E),i.each(function(){u(this,e,r,n)}))},t.fn.trigger=function(e,n){return e=m(e)||t.isPlainObject(e)?t.Event(e):c(e),e._args=n,this.each(function(){e.type in x&&"function"==typeof this[e.type]?this[e.type]():"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,r){var i,o;return this.each(function(a,s){i=l(m(e)?t.Event(e):e),i._args=r,i.target=s,t.each(n(s,e.type||e),function(t,e){if(o=e.proxy(i),i.isImmediatePropagationStopped())return!1})}),o},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return 0 in arguments?this.bind(e,t):this.trigger(e)}}),t.Event=function(t,e){m(t)||(e=t,t=e.type);var n=document.createEvent(g[t]||"Events"),r=!0;if(e)for(var i in e)"bubbles"==i?r=!!e[i]:n[i]=e[i];return n.initEvent(t,r,!0),c(n)}}(e),function(t){function e(e,n,r){var i=t.Event(n);return t(e).trigger(i,r),!i.isDefaultPrevented()}function n(t,n,r,i){if(t.global)return e(n||x,r,i)}function r(e){e.global&&0===t.active++&&n(e,null,"ajaxStart")}function i(e){e.global&&!--t.active&&n(e,null,"ajaxStop")}function o(t,e){var r=e.context;return e.beforeSend.call(r,t,e)!==!1&&n(e,r,"ajaxBeforeSend",[t,e])!==!1&&void n(e,r,"ajaxSend",[t,e])}function a(t,e,r,i){var o=r.context,a="success";r.success.call(o,t,a,e),i&&i.resolveWith(o,[t,a,e]),n(r,o,"ajaxSuccess",[e,r,t]),u(a,e,r)}function s(t,e,r,i,o){var a=i.context;i.error.call(a,r,e,t),o&&o.rejectWith(a,[r,e,t]),n(i,a,"ajaxError",[r,i,t||e]),u(e,r,i)}function u(t,e,r){var o=r.context;r.complete.call(o,e,t),n(r,o,"ajaxComplete",[e,r]),i(r)}function c(t,e,n){if(n.dataFilter==l)return t;var r=n.context;return n.dataFilter.call(r,t,e)}function l(){}function f(t){return t&&(t=t.split(";",2)[0]),t&&(t==T?"html":t==j?"json":w.test(t)?"script":E.test(t)&&"xml")||"text"}function h(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function p(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()&&"jsonp"!=e.dataType||(e.url=h(e.url,e.data),e.data=void 0)}function d(e,n,r,i){return t.isFunction(n)&&(i=r,r=n,n=void 0),t.isFunction(r)||(i=r,r=void 0),{url:e,data:n,success:r,dataType:i}}function m(e,n,r,i){var o,a=t.isArray(n),s=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),i&&(n=r?i:i+"["+(s||"object"==o||"array"==o?n:"")+"]"),!i&&a?e.add(u.name,u.value):"array"==o||!r&&"object"==o?m(e,u,r,n):e.add(n,u)})}var v,g,y=+new Date,x=window.document,b=/)<[^<]*)*<\/script>/gi,w=/^(?:text|application)\/javascript/i,E=/^(?:text|application)\/xml/i,j="application/json",T="text/html",S=/^\s*$/,C=x.createElement("a");C.href=window.location.href,t.active=0,t.ajaxJSONP=function(e,n){if(!("type"in e))return t.ajax(e);var r,i,u=e.jsonpCallback,c=(t.isFunction(u)?u():u)||"Zepto"+y++,l=x.createElement("script"),f=window[c],h=function(e){t(l).triggerHandler("error",e||"abort")},p={abort:h};return n&&n.promise(p),t(l).on("load error",function(o,u){clearTimeout(i),t(l).off().remove(),"error"!=o.type&&r?a(r[0],p,e,n):s(null,u||"error",p,e,n),window[c]=f,r&&t.isFunction(f)&&f(r[0]),f=r=void 0}),o(p,e)===!1?(h("abort"),p):(window[c]=function(){r=arguments},l.src=e.url.replace(/\?(.+)=\?/,"?$1="+c),x.head.appendChild(l),e.timeout>0&&(i=setTimeout(function(){h("timeout")},e.timeout)),p)},t.ajaxSettings={type:"GET",beforeSend:l,success:l,error:l,complete:l,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:j,xml:"application/xml, text/xml",html:T,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0,dataFilter:l},t.ajax=function(e){var n,i,u=t.extend({},e||{}),d=t.Deferred&&t.Deferred();for(v in t.ajaxSettings)void 0===u[v]&&(u[v]=t.ajaxSettings[v]);r(u),u.crossDomain||(n=x.createElement("a"),n.href=u.url,n.href=n.href,u.crossDomain=C.protocol+"//"+C.host!=n.protocol+"//"+n.host),u.url||(u.url=window.location.toString()),(i=u.url.indexOf("#"))>-1&&(u.url=u.url.slice(0,i)),p(u);var m=u.dataType,y=/\?.+=\?/.test(u.url);if(y&&(m="jsonp"),u.cache!==!1&&(e&&e.cache===!0||"script"!=m&&"jsonp"!=m)||(u.url=h(u.url,"_="+Date.now())),"jsonp"==m)return y||(u.url=h(u.url,u.jsonp?u.jsonp+"=?":u.jsonp===!1?"":"callback=?")),t.ajaxJSONP(u,d);var b,w=u.accepts[m],E={},j=function(t,e){E[t.toLowerCase()]=[t,e]},T=/^([\w-]+:)\/\//.test(u.url)?RegExp.$1:window.location.protocol,N=u.xhr(),O=N.setRequestHeader;if(d&&d.promise(N),u.crossDomain||j("X-Requested-With","XMLHttpRequest"),j("Accept",w||"*/*"),(w=u.mimeType||w)&&(w.indexOf(",")>-1&&(w=w.split(",",2)[0]),N.overrideMimeType&&N.overrideMimeType(w)),(u.contentType||u.contentType!==!1&&u.data&&"GET"!=u.type.toUpperCase())&&j("Content-Type",u.contentType||"application/x-www-form-urlencoded"),u.headers)for(g in u.headers)j(g,u.headers[g]);if(N.setRequestHeader=j,N.onreadystatechange=function(){if(4==N.readyState){N.onreadystatechange=l,clearTimeout(b);var e,n=!1;if(N.status>=200&&N.status<300||304==N.status||0==N.status&&"file:"==T){if(m=m||f(u.mimeType||N.getResponseHeader("content-type")),"arraybuffer"==N.responseType||"blob"==N.responseType)e=N.response;else{e=N.responseText;try{e=c(e,m,u),"script"==m?(0,eval)(e):"xml"==m?e=N.responseXML:"json"==m&&(e=S.test(e)?null:t.parseJSON(e))}catch(r){n=r}if(n)return s(n,"parsererror",N,u,d)}a(e,N,u,d)}else s(N.statusText||null,N.status?"error":"abort",N,u,d)}},o(N,u)===!1)return N.abort(),s(null,"abort",N,u,d),N;var P=!("async"in u)||u.async;if(N.open(u.type,u.url,P,u.username,u.password),u.xhrFields)for(g in u.xhrFields)N[g]=u.xhrFields[g];for(g in E)O.apply(N,E[g]);return u.timeout>0&&(b=setTimeout(function(){N.onreadystatechange=l,N.abort(),s(null,"timeout",N,u,d)},u.timeout)),N.send(u.data?u.data:null),N},t.get=function(){return t.ajax(d.apply(null,arguments))},t.post=function(){var e=d.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=d.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,r){if(!this.length)return this;var i,o=this,a=e.split(/\s/),s=d(e,n,r),u=s.success;return a.length>1&&(s.url=a[0],i=a[1]),s.success=function(e){o.html(i?t("
          ").html(e.replace(b,"")).find(i):e),u&&u.apply(o,arguments)},t.ajax(s),this};var N=encodeURIComponent;t.param=function(e,n){var r=[];return r.add=function(e,n){t.isFunction(n)&&(n=n()),null==n&&(n=""),this.push(N(e)+"="+N(n))},m(r,e,n),r.join("&").replace(/%20/g,"+")}}(e),function(t){t.fn.serializeArray=function(){var e,n,r=[],i=function(t){return t.forEach?t.forEach(i):void r.push({name:e,value:t})};return this[0]&&t.each(this[0].elements,function(r,o){n=o.type,e=o.name,e&&"fieldset"!=o.nodeName.toLowerCase()&&!o.disabled&&"submit"!=n&&"reset"!=n&&"button"!=n&&"file"!=n&&("radio"!=n&&"checkbox"!=n||o.checked)&&i(t(o).val())}),r},t.fn.serialize=function(){var t=[];return this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(0 in arguments)this.bind("submit",e);else if(this.length){var n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return this}}(e),function(){try{getComputedStyle(void 0)}catch(t){var e=getComputedStyle;window.getComputedStyle=function(t,n){try{return e(t,n)}catch(r){return null}}}}(),t("zepto",e)});layui.define(["layer-mobile","zepto"],function(e){"use strict";var t=layui.zepto,a=layui["layer-mobile"],i=(layui.device(),"layui-upload-enter"),n="layui-upload-iframe",r={icon:2,shift:6},o={file:"文件",video:"视频",audio:"音频"};a.msg=function(e){return a.open({content:e||"",skin:"msg",time:0})};var s=function(e){this.options=e};s.prototype.init=function(){var e=this,a=e.options,r=t("body"),s=t(a.elem||".layui-upload-file"),u=t('');return t("#"+n)[0]||r.append(u),s.each(function(r,s){s=t(s);var u='
          ',l=s.attr("lay-type")||a.type;a.unwrap||(u='
          '+u+''+(s.attr("lay-title")||a.title||"上传"+(o[l]||"图片"))+"
          "),u=t(u),a.unwrap||u.on("dragover",function(e){e.preventDefault(),t(this).addClass(i)}).on("dragleave",function(){t(this).removeClass(i)}).on("drop",function(){t(this).removeClass(i)}),s.parent("form").attr("target")===n&&(a.unwrap?s.unwrap():(s.parent().next().remove(),s.unwrap().unwrap())),s.wrap(u),s.off("change").on("change",function(){e.action(this,l)})})},s.prototype.action=function(e,i){var o=this,s=o.options,u=e.value,l=t(e),p=l.attr("lay-ext")||s.ext||"";if(u){switch(i){case"file":if(p&&!RegExp("\\w\\.("+p+")$","i").test(escape(u)))return a.msg("不支持该文件格式",r),e.value="";break;case"video":if(!RegExp("\\w\\.("+(p||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(u)))return a.msg("不支持该视频格式",r),e.value="";break;case"audio":if(!RegExp("\\w\\.("+(p||"mp3|wav|mid")+")$","i").test(escape(u)))return a.msg("不支持该音频格式",r),e.value="";break;default:if(!RegExp("\\w\\.("+(p||"jpg|png|gif|bmp|jpeg")+")$","i").test(escape(u)))return a.msg("不支持该图片格式",r),e.value=""}s.before&&s.before(e),l.parent().submit();var c=t("#"+n),f=setInterval(function(){var t;try{t=c.contents().find("body").text()}catch(i){a.msg("上传接口存在跨域",r),clearInterval(f)}if(t){clearInterval(f),c.contents().find("body").html("");try{t=JSON.parse(t)}catch(i){return t={},a.msg("请对上传接口返回JSON字符",r)}"function"==typeof s.success&&s.success(t,e)}},30);e.value=""}},e("upload-mobile",function(e){var t=new s(e=e||{});t.init()})});layui.define(function(i){i("layim-mobile",layui.v)});layui["layui.mobile"]||layui.config({base:layui.cache.dir+"lay/modules/mobile/"}).extend({"layer-mobile":"layer-mobile",zepto:"zepto","upload-mobile":"upload-mobile","layim-mobile":"layim-mobile"}),layui.define(["layer-mobile","zepto","layim-mobile"],function(l){l("mobile",{layer:layui["layer-mobile"],layim:layui["layim-mobile"]})}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define(function(i){i("layui.mobile",layui.v)});layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});layui.define(function(e){"use strict";var t=(window,document),i="querySelectorAll",n="getElementsByClassName",a=function(e){return t[i](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var i in e)t[i]=e[i];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var o=0,r=["layui-m-layer"],d=function(e){var t=this;t.config=l.extend(e),t.view()};d.prototype.view=function(){var e=this,i=e.config,s=t.createElement("div");e.id=s.id=r[0]+o,s.setAttribute("class",r[0]+" "+r[0]+(i.type||0)),s.setAttribute("index",o);var l=function(){var e="object"==typeof i.title;return i.title?'

          '+(e?i.title[0]:i.title)+"

          ":""}(),d=function(){"string"==typeof i.btn&&(i.btn=[i.btn]);var e,t=(i.btn||[]).length;return 0!==t&&i.btn?(e=''+i.btn[0]+"",2===t&&(e=''+i.btn[1]+""+e),'
          '+e+"
          "):""}();if(i.fixed||(i.top=i.hasOwnProperty("top")?i.top:100,i.style=i.style||"",i.style+=" top:"+(t.body.scrollTop+i.top)+"px"),2===i.type&&(i.content='

          '+(i.content||"")+"

          "),i.skin&&(i.anim="up"),"msg"===i.skin&&(i.shade=!1),s.innerHTML=(i.shade?"
          ':"")+'
          "+l+'
          '+i.content+"
          "+d+"
          ",!i.type||2===i.type){var y=t[n](r[0]+i.type),u=y.length;u>=1&&c.close(y[0].getAttribute("index"))}document.body.appendChild(s);var m=e.elem=a("#"+e.id)[0];i.success&&i.success(m),e.index=o++,e.action(i,m)},d.prototype.action=function(e,t){var i=this;e.time&&(l.timer[i.index]=setTimeout(function(){c.close(i.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),c.close(i.index)):e.yes?e.yes(i.index):c.close(i.index)};if(e.btn)for(var s=t[n]("layui-m-layerbtn")[0].children,o=s.length,r=0;r0&&e-1 in t)}function s(t){return A.call(t,function(t){return null!=t})}function u(t){return t.length>0?T.fn.concat.apply([],t):t}function c(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function l(t){return t in F?F[t]:F[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function f(t,e){return"number"!=typeof e||k[c(t)]?e:e+"px"}function h(t){var e,n;return $[t]||(e=L.createElement(t),L.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),$[t]=n),$[t]}function p(t){return"children"in t?D.call(t.children):T.map(t.childNodes,function(t){if(1==t.nodeType)return t})}function d(t,e){var n,r=t?t.length:0;for(n=0;n]*>/,R=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,Z=/^(?:body|html)$/i,q=/([A-Z])/g,H=["val","css","html","text","data","width","height","offset"],I=["after","prepend","before","append"],V=L.createElement("table"),_=L.createElement("tr"),B={tr:L.createElement("tbody"),tbody:V,thead:V,tfoot:V,td:_,th:_,"*":L.createElement("div")},U=/complete|loaded|interactive/,X=/^[\w-]*$/,J={},W=J.toString,Y={},G=L.createElement("div"),K={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},Q=Array.isArray||function(t){return t instanceof Array};return Y.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var r,i=t.parentNode,o=!i;return o&&(i=G).appendChild(t),r=~Y.qsa(i,e).indexOf(t),o&&G.removeChild(t),r},C=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},N=function(t){return A.call(t,function(e,n){return t.indexOf(e)==n})},Y.fragment=function(t,e,n){var r,i,a;return R.test(t)&&(r=T(L.createElement(RegExp.$1))),r||(t.replace&&(t=t.replace(z,"<$1>")),e===E&&(e=M.test(t)&&RegExp.$1),e in B||(e="*"),a=B[e],a.innerHTML=""+t,r=T.each(D.call(a.childNodes),function(){a.removeChild(this)})),o(n)&&(i=T(r),T.each(n,function(t,e){H.indexOf(t)>-1?i[t](e):i.attr(t,e)})),r},Y.Z=function(t,e){return new d(t,e)},Y.isZ=function(t){return t instanceof Y.Z},Y.init=function(t,n){var r;if(!t)return Y.Z();if("string"==typeof t)if(t=t.trim(),"<"==t[0]&&M.test(t))r=Y.fragment(t,RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}else{if(e(t))return T(L).ready(t);if(Y.isZ(t))return t;if(Q(t))r=s(t);else if(i(t))r=[t],t=null;else if(M.test(t))r=Y.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==E)return T(n).find(t);r=Y.qsa(L,t)}}return Y.Z(r,t)},T=function(t,e){return Y.init(t,e)},T.extend=function(t){var e,n=D.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){m(t,n,e)}),t},Y.qsa=function(t,e){var n,r="#"==e[0],i=!r&&"."==e[0],o=r||i?e.slice(1):e,a=X.test(o);return t.getElementById&&a&&r?(n=t.getElementById(o))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:D.call(a&&!r&&t.getElementsByClassName?i?t.getElementsByClassName(o):t.getElementsByTagName(e):t.querySelectorAll(e))},T.contains=L.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},T.type=t,T.isFunction=e,T.isWindow=n,T.isArray=Q,T.isPlainObject=o,T.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},T.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},T.inArray=function(t,e,n){return O.indexOf.call(e,t,n)},T.camelCase=C,T.trim=function(t){return null==t?"":String.prototype.trim.call(t)},T.uuid=0,T.support={},T.expr={},T.noop=function(){},T.map=function(t,e){var n,r,i,o=[];if(a(t))for(r=0;r=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return O.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return e(t)?this.not(this.not(t)):T(A.call(this,function(e){return Y.matches(e,t)}))},add:function(t,e){return T(N(this.concat(T(t,e))))},is:function(t){return this.length>0&&Y.matches(this[0],t)},not:function(t){var n=[];if(e(t)&&t.call!==E)this.each(function(e){t.call(this,e)||n.push(this)});else{var r="string"==typeof t?this.filter(t):a(t)&&e(t.item)?D.call(t):T(t);this.forEach(function(t){r.indexOf(t)<0&&n.push(t)})}return T(n)},has:function(t){return this.filter(function(){return i(t)?T.contains(this,t):T(this).find(t).size()})},eq:function(t){return t===-1?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!i(t)?t:T(t)},last:function(){var t=this[this.length-1];return t&&!i(t)?t:T(t)},find:function(t){var e,n=this;return e=t?"object"==typeof t?T(t).filter(function(){var t=this;return O.some.call(n,function(e){return T.contains(e,t)})}):1==this.length?T(Y.qsa(this[0],t)):this.map(function(){return Y.qsa(this,t)}):T()},closest:function(t,e){var n=[],i="object"==typeof t&&T(t);return this.each(function(o,a){for(;a&&!(i?i.indexOf(a)>=0:Y.matches(a,t));)a=a!==e&&!r(a)&&a.parentNode;a&&n.indexOf(a)<0&&n.push(a)}),T(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=T.map(n,function(t){if((t=t.parentNode)&&!r(t)&&e.indexOf(t)<0)return e.push(t),t});return v(e,t)},parent:function(t){return v(N(this.pluck("parentNode")),t)},children:function(t){return v(this.map(function(){return p(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||D.call(this.childNodes)})},siblings:function(t){return v(this.map(function(t,e){return A.call(p(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return T.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=h(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var n=e(t);if(this[0]&&!n)var r=T(t).get(0),i=r.parentNode||this.length>1;return this.each(function(e){T(this).wrapAll(n?t.call(this,e):i?r.cloneNode(!0):r)})},wrapAll:function(t){if(this[0]){T(this[0]).before(t=T(t));for(var e;(e=t.children()).length;)t=e.first();T(t).append(this)}return this},wrapInner:function(t){var n=e(t);return this.each(function(e){var r=T(this),i=r.contents(),o=n?t.call(this,e):t;i.length?i.wrapAll(o):r.append(o)})},unwrap:function(){return this.parent().each(function(){T(this).replaceWith(T(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var e=T(this);(t===E?"none"==e.css("display"):t)?e.show():e.hide()})},prev:function(t){return T(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return T(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;T(this).empty().append(g(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=g(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(t,e){var n;return"string"!=typeof t||1 in arguments?this.each(function(n){if(1===this.nodeType)if(i(t))for(j in t)y(this,j,t[j]);else y(this,t,g(this,e,n,this.getAttribute(t)))}):0 in this&&1==this[0].nodeType&&null!=(n=this[0].getAttribute(t))?n:E},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){y(this,t)},this)})},prop:function(t,e){return t=K[t]||t,1 in arguments?this.each(function(n){this[t]=g(this,e,n,this[t])}):this[0]&&this[0][t]},removeProp:function(t){return t=K[t]||t,this.each(function(){delete this[t]})},data:function(t,e){var n="data-"+t.replace(q,"-$1").toLowerCase(),r=1 in arguments?this.attr(n,e):this.attr(n);return null!==r?b(r):E},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each(function(e){this.value=g(this,t,e,this.value)})):this[0]&&(this[0].multiple?T(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var n=T(this),r=g(this,t,e,n.offset()),i=n.offsetParent().offset(),o={top:r.top-i.top,left:r.left-i.left};"static"==n.css("position")&&(o.position="relative"),n.css(o)});if(!this.length)return null;if(L.documentElement!==this[0]&&!T.contains(L.documentElement,this[0]))return{top:0,left:0};var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(e,n){if(arguments.length<2){var r=this[0];if("string"==typeof e){if(!r)return;return r.style[C(e)]||getComputedStyle(r,"").getPropertyValue(e)}if(Q(e)){if(!r)return;var i={},o=getComputedStyle(r,"");return T.each(e,function(t,e){i[e]=r.style[C(e)]||o.getPropertyValue(e)}),i}}var a="";if("string"==t(e))n||0===n?a=c(e)+":"+f(e,n):this.each(function(){this.style.removeProperty(c(e))});else for(j in e)e[j]||0===e[j]?a+=c(j)+":"+f(j,e[j])+";":this.each(function(){this.style.removeProperty(c(j))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(T(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return!!t&&O.some.call(this,function(t){return this.test(x(t))},l(t))},addClass:function(t){return t?this.each(function(e){if("className"in this){S=[];var n=x(this),r=g(this,t,e,n);r.split(/\s+/g).forEach(function(t){T(this).hasClass(t)||S.push(t)},this),S.length&&x(this,n+(n?" ":"")+S.join(" "))}}):this},removeClass:function(t){return this.each(function(e){if("className"in this){if(t===E)return x(this,"");S=x(this),g(this,t,e,S).split(/\s+/g).forEach(function(t){S=S.replace(l(t)," ")}),x(this,S.trim())}})},toggleClass:function(t,e){return t?this.each(function(n){var r=T(this),i=g(this,t,n,x(this));i.split(/\s+/g).forEach(function(t){(e===E?!r.hasClass(t):e)?r.addClass(t):r.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var e="scrollTop"in this[0];return t===E?e?this[0].scrollTop:this[0].pageYOffset:this.each(e?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var e="scrollLeft"in this[0];return t===E?e?this[0].scrollLeft:this[0].pageXOffset:this.each(e?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),r=Z.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(T(t).css("margin-top"))||0,n.left-=parseFloat(T(t).css("margin-left"))||0,r.top+=parseFloat(T(e[0]).css("border-top-width"))||0,r.left+=parseFloat(T(e[0]).css("border-left-width"))||0,{top:n.top-r.top,left:n.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||L.body;t&&!Z.test(t.nodeName)&&"static"==T(t).css("position");)t=t.offsetParent;return t})}},T.fn.detach=T.fn.remove,["width","height"].forEach(function(t){var e=t.replace(/./,function(t){return t[0].toUpperCase()});T.fn[t]=function(i){var o,a=this[0];return i===E?n(a)?a["inner"+e]:r(a)?a.documentElement["scroll"+e]:(o=this.offset())&&o[t]:this.each(function(e){a=T(this),a.css(t,g(this,i,e,a[t]()))})}}),I.forEach(function(e,n){var r=n%2;T.fn[e]=function(){var e,i,o=T.map(arguments,function(n){var r=[];return e=t(n),"array"==e?(n.forEach(function(t){return t.nodeType!==E?r.push(t):T.zepto.isZ(t)?r=r.concat(t.get()):void(r=r.concat(Y.fragment(t)))}),r):"object"==e||null==n?n:Y.fragment(n)}),a=this.length>1;return o.length<1?this:this.each(function(t,e){i=r?e:e.parentNode,e=0==n?e.nextSibling:1==n?e.firstChild:2==n?e:null;var s=T.contains(L.documentElement,i);o.forEach(function(t){if(a)t=t.cloneNode(!0);else if(!i)return T(t).remove();i.insertBefore(t,e),s&&w(t,function(t){if(!(null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src)){var e=t.ownerDocument?t.ownerDocument.defaultView:window;e.eval.call(e,t.innerHTML)}})})})},T.fn[r?e+"To":"insert"+(n?"Before":"After")]=function(t){return T(t)[e](this),this}}),Y.Z.prototype=d.prototype=T.fn,Y.uniq=N,Y.deserializeValue=b,T.zepto=Y,T}();!function(t){function e(t){return t._zid||(t._zid=h++)}function n(t,n,o,a){if(n=r(n),n.ns)var s=i(n.ns);return(v[e(t)]||[]).filter(function(t){return t&&(!n.e||t.e==n.e)&&(!n.ns||s.test(t.ns))&&(!o||e(t.fn)===e(o))&&(!a||t.sel==a)})}function r(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function i(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function o(t,e){return t.del&&!y&&t.e in x||!!e}function a(t){return b[t]||y&&x[t]||t}function s(n,i,s,u,l,h,p){var d=e(n),m=v[d]||(v[d]=[]);i.split(/\s/).forEach(function(e){if("ready"==e)return t(document).ready(s);var i=r(e);i.fn=s,i.sel=l,i.e in b&&(s=function(e){var n=e.relatedTarget;if(!n||n!==this&&!t.contains(this,n))return i.fn.apply(this,arguments)}),i.del=h;var d=h||s;i.proxy=function(t){if(t=c(t),!t.isImmediatePropagationStopped()){t.data=u;var e=d.apply(n,t._args==f?[t]:[t].concat(t._args));return e===!1&&(t.preventDefault(),t.stopPropagation()),e}},i.i=m.length,m.push(i),"addEventListener"in n&&n.addEventListener(a(i.e),i.proxy,o(i,p))})}function u(t,r,i,s,u){var c=e(t);(r||"").split(/\s/).forEach(function(e){n(t,e,i,s).forEach(function(e){delete v[c][e.i],"removeEventListener"in t&&t.removeEventListener(a(e.e),e.proxy,o(e,u))})})}function c(e,n){return!n&&e.isDefaultPrevented||(n||(n=e),t.each(T,function(t,r){var i=n[t];e[t]=function(){return this[r]=w,i&&i.apply(n,arguments)},e[r]=E}),e.timeStamp||(e.timeStamp=Date.now()),(n.defaultPrevented!==f?n.defaultPrevented:"returnValue"in n?n.returnValue===!1:n.getPreventDefault&&n.getPreventDefault())&&(e.isDefaultPrevented=w)),e}function l(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===f||(n[e]=t[e]);return c(n,t)}var f,h=1,p=Array.prototype.slice,d=t.isFunction,m=function(t){return"string"==typeof t},v={},g={},y="onfocusin"in window,x={focus:"focusin",blur:"focusout"},b={mouseenter:"mouseover",mouseleave:"mouseout"};g.click=g.mousedown=g.mouseup=g.mousemove="MouseEvents",t.event={add:s,remove:u},t.proxy=function(n,r){var i=2 in arguments&&p.call(arguments,2);if(d(n)){var o=function(){return n.apply(r,i?i.concat(p.call(arguments)):arguments)};return o._zid=e(n),o}if(m(r))return i?(i.unshift(n[r],n),t.proxy.apply(null,i)):t.proxy(n[r],n);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,r){return this.on(t,e,n,r,1)};var w=function(){return!0},E=function(){return!1},j=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,T={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,n,r,i,o){var a,c,h=this;return e&&!m(e)?(t.each(e,function(t,e){h.on(t,n,r,e,o)}),h):(m(n)||d(i)||i===!1||(i=r,r=n,n=f),i!==f&&r!==!1||(i=r,r=f),i===!1&&(i=E),h.each(function(f,h){o&&(a=function(t){return u(h,t.type,i),i.apply(this,arguments)}),n&&(c=function(e){var r,o=t(e.target).closest(n,h).get(0);if(o&&o!==h)return r=t.extend(l(e),{currentTarget:o,liveFired:h}),(a||i).apply(o,[r].concat(p.call(arguments,1)))}),s(h,e,i,r,n,c||a)}))},t.fn.off=function(e,n,r){var i=this;return e&&!m(e)?(t.each(e,function(t,e){i.off(t,n,e)}),i):(m(n)||d(r)||r===!1||(r=n,n=f),r===!1&&(r=E),i.each(function(){u(this,e,r,n)}))},t.fn.trigger=function(e,n){return e=m(e)||t.isPlainObject(e)?t.Event(e):c(e),e._args=n,this.each(function(){e.type in x&&"function"==typeof this[e.type]?this[e.type]():"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,r){var i,o;return this.each(function(a,s){i=l(m(e)?t.Event(e):e),i._args=r,i.target=s,t.each(n(s,e.type||e),function(t,e){if(o=e.proxy(i),i.isImmediatePropagationStopped())return!1})}),o},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return 0 in arguments?this.bind(e,t):this.trigger(e)}}),t.Event=function(t,e){m(t)||(e=t,t=e.type);var n=document.createEvent(g[t]||"Events"),r=!0;if(e)for(var i in e)"bubbles"==i?r=!!e[i]:n[i]=e[i];return n.initEvent(t,r,!0),c(n)}}(e),function(t){function e(e,n,r){var i=t.Event(n);return t(e).trigger(i,r),!i.isDefaultPrevented()}function n(t,n,r,i){if(t.global)return e(n||x,r,i)}function r(e){e.global&&0===t.active++&&n(e,null,"ajaxStart")}function i(e){e.global&&!--t.active&&n(e,null,"ajaxStop")}function o(t,e){var r=e.context;return e.beforeSend.call(r,t,e)!==!1&&n(e,r,"ajaxBeforeSend",[t,e])!==!1&&void n(e,r,"ajaxSend",[t,e])}function a(t,e,r,i){var o=r.context,a="success";r.success.call(o,t,a,e),i&&i.resolveWith(o,[t,a,e]),n(r,o,"ajaxSuccess",[e,r,t]),u(a,e,r)}function s(t,e,r,i,o){var a=i.context;i.error.call(a,r,e,t),o&&o.rejectWith(a,[r,e,t]),n(i,a,"ajaxError",[r,i,t||e]),u(e,r,i)}function u(t,e,r){var o=r.context;r.complete.call(o,e,t),n(r,o,"ajaxComplete",[e,r]),i(r)}function c(t,e,n){if(n.dataFilter==l)return t;var r=n.context;return n.dataFilter.call(r,t,e)}function l(){}function f(t){return t&&(t=t.split(";",2)[0]),t&&(t==T?"html":t==j?"json":w.test(t)?"script":E.test(t)&&"xml")||"text"}function h(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function p(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()&&"jsonp"!=e.dataType||(e.url=h(e.url,e.data),e.data=void 0)}function d(e,n,r,i){return t.isFunction(n)&&(i=r,r=n,n=void 0),t.isFunction(r)||(i=r,r=void 0),{url:e,data:n,success:r,dataType:i}}function m(e,n,r,i){var o,a=t.isArray(n),s=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),i&&(n=r?i:i+"["+(s||"object"==o||"array"==o?n:"")+"]"),!i&&a?e.add(u.name,u.value):"array"==o||!r&&"object"==o?m(e,u,r,n):e.add(n,u)})}var v,g,y=+new Date,x=window.document,b=/)<[^<]*)*<\/script>/gi,w=/^(?:text|application)\/javascript/i,E=/^(?:text|application)\/xml/i,j="application/json",T="text/html",S=/^\s*$/,C=x.createElement("a");C.href=window.location.href,t.active=0,t.ajaxJSONP=function(e,n){if(!("type"in e))return t.ajax(e);var r,i,u=e.jsonpCallback,c=(t.isFunction(u)?u():u)||"Zepto"+y++,l=x.createElement("script"),f=window[c],h=function(e){t(l).triggerHandler("error",e||"abort")},p={abort:h};return n&&n.promise(p),t(l).on("load error",function(o,u){clearTimeout(i),t(l).off().remove(),"error"!=o.type&&r?a(r[0],p,e,n):s(null,u||"error",p,e,n),window[c]=f,r&&t.isFunction(f)&&f(r[0]),f=r=void 0}),o(p,e)===!1?(h("abort"),p):(window[c]=function(){r=arguments},l.src=e.url.replace(/\?(.+)=\?/,"?$1="+c),x.head.appendChild(l),e.timeout>0&&(i=setTimeout(function(){h("timeout")},e.timeout)),p)},t.ajaxSettings={type:"GET",beforeSend:l,success:l,error:l,complete:l,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:j,xml:"application/xml, text/xml",html:T,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0,dataFilter:l},t.ajax=function(e){var n,i,u=t.extend({},e||{}),d=t.Deferred&&t.Deferred();for(v in t.ajaxSettings)void 0===u[v]&&(u[v]=t.ajaxSettings[v]);r(u),u.crossDomain||(n=x.createElement("a"),n.href=u.url,n.href=n.href,u.crossDomain=C.protocol+"//"+C.host!=n.protocol+"//"+n.host),u.url||(u.url=window.location.toString()),(i=u.url.indexOf("#"))>-1&&(u.url=u.url.slice(0,i)),p(u);var m=u.dataType,y=/\?.+=\?/.test(u.url);if(y&&(m="jsonp"),u.cache!==!1&&(e&&e.cache===!0||"script"!=m&&"jsonp"!=m)||(u.url=h(u.url,"_="+Date.now())),"jsonp"==m)return y||(u.url=h(u.url,u.jsonp?u.jsonp+"=?":u.jsonp===!1?"":"callback=?")),t.ajaxJSONP(u,d);var b,w=u.accepts[m],E={},j=function(t,e){E[t.toLowerCase()]=[t,e]},T=/^([\w-]+:)\/\//.test(u.url)?RegExp.$1:window.location.protocol,N=u.xhr(),O=N.setRequestHeader;if(d&&d.promise(N),u.crossDomain||j("X-Requested-With","XMLHttpRequest"),j("Accept",w||"*/*"),(w=u.mimeType||w)&&(w.indexOf(",")>-1&&(w=w.split(",",2)[0]),N.overrideMimeType&&N.overrideMimeType(w)),(u.contentType||u.contentType!==!1&&u.data&&"GET"!=u.type.toUpperCase())&&j("Content-Type",u.contentType||"application/x-www-form-urlencoded"),u.headers)for(g in u.headers)j(g,u.headers[g]);if(N.setRequestHeader=j,N.onreadystatechange=function(){if(4==N.readyState){N.onreadystatechange=l,clearTimeout(b);var e,n=!1;if(N.status>=200&&N.status<300||304==N.status||0==N.status&&"file:"==T){if(m=m||f(u.mimeType||N.getResponseHeader("content-type")),"arraybuffer"==N.responseType||"blob"==N.responseType)e=N.response;else{e=N.responseText;try{e=c(e,m,u),"script"==m?(0,eval)(e):"xml"==m?e=N.responseXML:"json"==m&&(e=S.test(e)?null:t.parseJSON(e))}catch(r){n=r}if(n)return s(n,"parsererror",N,u,d)}a(e,N,u,d)}else s(N.statusText||null,N.status?"error":"abort",N,u,d)}},o(N,u)===!1)return N.abort(),s(null,"abort",N,u,d),N;var P=!("async"in u)||u.async;if(N.open(u.type,u.url,P,u.username,u.password),u.xhrFields)for(g in u.xhrFields)N[g]=u.xhrFields[g];for(g in E)O.apply(N,E[g]);return u.timeout>0&&(b=setTimeout(function(){N.onreadystatechange=l,N.abort(),s(null,"timeout",N,u,d)},u.timeout)),N.send(u.data?u.data:null),N},t.get=function(){return t.ajax(d.apply(null,arguments))},t.post=function(){var e=d.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=d.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,r){if(!this.length)return this;var i,o=this,a=e.split(/\s/),s=d(e,n,r),u=s.success;return a.length>1&&(s.url=a[0],i=a[1]),s.success=function(e){o.html(i?t("
          ").html(e.replace(b,"")).find(i):e),u&&u.apply(o,arguments)},t.ajax(s),this};var N=encodeURIComponent;t.param=function(e,n){var r=[];return r.add=function(e,n){t.isFunction(n)&&(n=n()),null==n&&(n=""),this.push(N(e)+"="+N(n))},m(r,e,n),r.join("&").replace(/%20/g,"+")}}(e),function(t){t.fn.serializeArray=function(){var e,n,r=[],i=function(t){return t.forEach?t.forEach(i):void r.push({name:e,value:t})};return this[0]&&t.each(this[0].elements,function(r,o){n=o.type,e=o.name,e&&"fieldset"!=o.nodeName.toLowerCase()&&!o.disabled&&"submit"!=n&&"reset"!=n&&"button"!=n&&"file"!=n&&("radio"!=n&&"checkbox"!=n||o.checked)&&i(t(o).val())}),r},t.fn.serialize=function(){var t=[];return this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(0 in arguments)this.bind("submit",e);else if(this.length){var n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return this}}(e),function(){try{getComputedStyle(void 0)}catch(t){var e=getComputedStyle;window.getComputedStyle=function(t,n){try{return e(t,n)}catch(r){return null}}}}(),t("zepto",e)});layui.define(["layer-mobile","zepto"],function(e){"use strict";var t=layui.zepto,a=layui["layer-mobile"],i=(layui.device(),"layui-upload-enter"),n="layui-upload-iframe",r={icon:2,shift:6},o={file:"文件",video:"视频",audio:"音频"};a.msg=function(e){return a.open({content:e||"",skin:"msg",time:2})};var s=function(e){this.options=e};s.prototype.init=function(){var e=this,a=e.options,r=t("body"),s=t(a.elem||".layui-upload-file"),u=t('');return t("#"+n)[0]||r.append(u),s.each(function(r,s){s=t(s);var u='
          ',l=s.attr("lay-type")||a.type;a.unwrap||(u='
          '+u+''+(s.attr("lay-title")||a.title||"上传"+(o[l]||"图片"))+"
          "),u=t(u),a.unwrap||u.on("dragover",function(e){e.preventDefault(),t(this).addClass(i)}).on("dragleave",function(){t(this).removeClass(i)}).on("drop",function(){t(this).removeClass(i)}),s.parent("form").attr("target")===n&&(a.unwrap?s.unwrap():(s.parent().next().remove(),s.unwrap().unwrap())),s.wrap(u),s.off("change").on("change",function(){e.action(this,l)})})},s.prototype.action=function(e,i){var o=this,s=o.options,u=e.value,l=t(e),p=l.attr("lay-ext")||s.ext||"";if(u){switch(i){case"file":if(p&&!RegExp("\\w\\.("+p+")$","i").test(escape(u)))return a.msg("不支持该文件格式",r),e.value="";break;case"video":if(!RegExp("\\w\\.("+(p||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(u)))return a.msg("不支持该视频格式",r),e.value="";break;case"audio":if(!RegExp("\\w\\.("+(p||"mp3|wav|mid")+")$","i").test(escape(u)))return a.msg("不支持该音频格式",r),e.value="";break;default:if(!RegExp("\\w\\.("+(p||"jpg|png|gif|bmp|jpeg")+")$","i").test(escape(u)))return a.msg("不支持该图片格式",r),e.value=""}s.before&&s.before(e),l.parent().submit();var c=t("#"+n),f=setInterval(function(){var t;try{t=c.contents().find("body").text()}catch(i){a.msg("上传接口存在跨域",r),clearInterval(f)}if(t){clearInterval(f),c.contents().find("body").html("");try{t=JSON.parse(t)}catch(i){return t={},a.msg("请对上传接口返回JSON字符",r)}"function"==typeof s.success&&s.success(t,e)}},30);e.value=""}},e("upload-mobile",function(e){var t=new s(e=e||{});t.init()})});layui.define(function(i){i("layim-mobile",layui.v)});layui["layui.mobile"]||layui.config({base:layui.cache.dir+"lay/modules/mobile/"}).extend({"layer-mobile":"layer-mobile",zepto:"zepto","upload-mobile":"upload-mobile","layim-mobile":"layim-mobile"}),layui.define(["layer-mobile","zepto","layim-mobile"],function(l){l("mobile",{layer:layui["layer-mobile"],layim:layui["layim-mobile"]})}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/rate.js b/static/plugs/layui/lay/modules/rate.js new file mode 100644 index 000000000..3c98909c2 --- /dev/null +++ b/static/plugs/layui/lay/modules/rate.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var a=layui.jquery,i={config:{},index:layui.rate?layui.rate.index+1e4:0,set:function(e){var i=this;return i.config=a.extend({},i.config,e),i},on:function(e,a){return layui.onevent.call(this,n,e,a)}},l=function(){var e=this,a=e.config;return{setvalue:function(a){e.setvalue.call(e,a)},config:a}},n="rate",t="layui-rate",o="layui-icon-rate",s="layui-icon-rate-solid",u="layui-icon-rate-half",r="layui-icon-rate-solid layui-icon-rate-half",c="layui-icon-rate-solid layui-icon-rate",f="layui-icon-rate layui-icon-rate-half",v=function(e){var l=this;l.index=++i.index,l.config=a.extend({},l.config,i.config,e),l.render()};v.prototype.config={length:5,text:!1,readonly:!1,half:!1,value:0,theme:""},v.prototype.render=function(){var e=this,i=e.config,l=i.theme?'style="color: '+i.theme+';"':"";i.elem=a(i.elem),parseInt(i.value)!==i.value&&(i.half||(i.value=Math.ceil(i.value)-i.value<.5?Math.ceil(i.value):Math.floor(i.value)));for(var n='
            ",u=1;u<=i.length;u++){var r='
          • ";i.half&&parseInt(i.value)!==i.value&&u==Math.ceil(i.value)?n=n+'
          • ":n+=r}n+="
          "+(i.text?''+i.value+"星":"")+"";var c=i.elem,f=c.next("."+t);f[0]&&f.remove(),e.elemTemp=a(n),i.span=e.elemTemp.next("span"),i.setText&&i.setText(i.value),c.html(e.elemTemp),c.addClass("layui-inline"),i.readonly||e.action()},v.prototype.setvalue=function(e){var a=this,i=a.config;i.value=e,a.render()},v.prototype.action=function(){var e=this,i=e.config,l=e.elemTemp,n=l.find("i").width();l.children("li").each(function(e){var t=e+1,v=a(this);v.on("click",function(e){if(i.value=t,i.half){var o=e.pageX-a(this).offset().left;o<=n/2&&(i.value=i.value-.5)}i.text&&l.next("span").text(i.value+"星"),i.choose&&i.choose(i.value),i.setText&&i.setText(i.value)}),v.on("mousemove",function(e){if(l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+t+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half){var c=e.pageX-a(this).offset().left;c<=n/2&&v.children("i").addClass(u).removeClass(s)}}),v.on("mouseleave",function(){l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+Math.floor(i.value)+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half&&parseInt(i.value)!==i.value&&l.children("li:eq("+Math.floor(i.value)+")").children("i").addClass(u).removeClass(c)})})},v.prototype.events=function(){var e=this;e.config},i.render=function(e){var a=new v(e);return l.call(a)},e(n,i)}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/table.js b/static/plugs/layui/lay/modules/table.js new file mode 100644 index 000000000..fe4e4ee74 --- /dev/null +++ b/static/plugs/layui/lay/modules/table.js @@ -0,0 +1,2 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define(["laytpl","laypage","layer","form"],function(e){"use strict";var t=layui.$,i=layui.laytpl,a=layui.laypage,l=layui.layer,n=layui.form,o=layui.hint(),r=layui.device(),d={config:{checkName:"LAY_CHECKED",indexName:"LAY_TABLE_INDEX"},cache:{},index:layui.table?layui.table.index+1e4:0,set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,s,e,t)}},c=function(){var e=this,t=e.config,i=t.id;return i&&(c.config[i]=t),{reload:function(t){e.reload.call(e,t)},config:t}},s="table",u=".layui-table",h="layui-hide",f="layui-none",y="layui-table-view",p=".layui-table-header",m=".layui-table-body",v=".layui-table-main",g=".layui-table-fixed",x=".layui-table-fixed-l",b=".layui-table-fixed-r",k=".layui-table-tool",C=".layui-table-page",w=".layui-table-sort",N="layui-table-edit",T="layui-table-hover",F=function(e){var t='{{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}}';return e=e||{},['',"","{{# layui.each(d.data.cols, function(i1, item1){ }}","","{{# layui.each(item1, function(i2, item2){ }}",'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}','{{# if(item2.fixed === "right"){ right = true; } }}',function(){return e.fixed&&"right"!==e.fixed?'{{# if(item2.fixed && item2.fixed !== "right"){ }}':"right"===e.fixed?'{{# if(item2.fixed === "right"){ }}':""}(),'",e.fixed?"{{# }; }}":"","{{# }); }}","","{{# }); }}","","
          ','
          1){ }}","group","{{# } else { }}","{{d.index}}-{{item2.field || i2}}",'{{# if(item2.type !== "normal"){ }}'," laytable-cell-{{ item2.type }}","{{# } }}","{{# } }}",'" {{#if(item2.align){}}align="{{item2.align}}"{{#}}}>','{{# if(item2.type === "checkbox"){ }}','',"{{# } else { }}",'{{item2.title||""}}',"{{# if(!(item2.colspan > 1) && item2.sort){ }}",'',"{{# } }}","{{# } }}","
          ","
          "].join("")},W=['',"","
          "].join(""),z=['
          ',"{{# if(d.data.toolbar){ }}",'
          ',"{{# } }}",'
          ',"{{# var left, right; }}",'
          ',F(),"
          ",'
          ',W,"
          ","{{# if(left){ }}",'
          ','
          ',F({fixed:!0}),"
          ",'
          ',W,"
          ","
          ","{{# }; }}","{{# if(right){ }}",'
          ','
          ',F({fixed:"right"}),'
          ',"
          ",'
          ',W,"
          ","
          ","{{# }; }}","
          ","{{# if(d.data.page){ }}",'
          ','
          ',"
          ","{{# } }}","","
          "].join(""),A=t(window),S=t(document),M=function(e){var i=this;i.index=++d.index,i.config=t.extend({},i.config,d.config,e),i.render()};M.prototype.config={limit:10,loading:!0,cellMinWidth:60,text:{none:"无数据"}},M.prototype.render=function(){var e=this,a=e.config;if(a.elem=t(a.elem),a.where=a.where||{},a.id=a.id||a.elem.attr("id"),a.request=t.extend({pageName:"page",limitName:"limit"},a.request),a.response=t.extend({statusName:"code",statusCode:0,msgName:"msg",dataName:"data",countName:"count"},a.response),"object"==typeof a.page&&(a.limit=a.page.limit||a.limit,a.limits=a.page.limits||a.limits,e.page=a.page.curr=a.page.curr||1,delete a.page.elem,delete a.page.jump),!a.elem[0])return e;e.setArea();var l=a.elem,n=l.next("."+y),o=e.elem=t(i(z).render({VIEW_CLASS:y,data:a,index:e.index}));if(a.index=e.index,n[0]&&n.remove(),l.after(o),e.layHeader=o.find(p),e.layMain=o.find(v),e.layBody=o.find(m),e.layFixed=o.find(g),e.layFixLeft=o.find(x),e.layFixRight=o.find(b),e.layTool=o.find(k),e.layPage=o.find(C),e.layTool.html(i(t(a.toolbar).html()||"").render(a)),a.height&&e.fullSize(),a.cols.length>1){var r=e.layFixed.find(p).find("th");r.height(e.layHeader.height()-1-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom")))}e.pullData(e.page),e.events()},M.prototype.initOpts=function(e){var t=this,i=(t.config,{checkbox:48,space:15,numbers:40});e.checkbox&&(e.type="checkbox"),e.space&&(e.type="space"),e.type||(e.type="normal"),"normal"!==e.type&&(e.unresize=!0,e.width=e.width||i[e.type])},M.prototype.setArea=function(){var e=this,t=e.config,i=0,a=0,l=0,n=0,o=t.width||function(){var e=function(i){var a,l;i=i||t.elem.parent(),a=i.width();try{l="none"===i.css("display")}catch(n){}return!i[0]||a&&!l?a:e(i.parent())};return e()}();e.eachCols(function(){i++}),o-=function(){return"line"===t.skin||"nob"===t.skin?2:i+1}(),layui.each(t.cols,function(t,i){layui.each(i,function(t,l){var r;return l?(e.initOpts(l),r=l.width||0,void(l.colspan>1||(/\d+%$/.test(r)?l.width=r=Math.floor(parseFloat(r)/100*o):r||(l.width=r=0,a++),n+=r))):void i.splice(t,1)})}),e.autoColNums=a,o>n&&a&&(l=(o-n)/a),layui.each(t.cols,function(e,i){layui.each(i,function(e,i){var a=i.minWidth||t.cellMinWidth;i.colspan>1||0===i.width&&(i.width=Math.floor(l>=a?l:a))})}),t.height&&/^full-\d+$/.test(t.height)&&(e.fullHeightGap=t.height.split("-")[1],t.height=A.height()-e.fullHeightGap)},M.prototype.reload=function(e){var i=this;i.config.data&&i.config.data.constructor===Array&&delete i.config.data,i.config=t.extend({},i.config,e),i.render()},M.prototype.page=1,M.prototype.pullData=function(e,i){var a=this,n=a.config,o=n.request,r=n.response,d=function(){"object"==typeof n.initSort&&a.sort(n.initSort.field,n.initSort.type)};if(a.startTime=(new Date).getTime(),n.url){var c={};c[o.pageName]=e,c[o.limitName]=n.limit;var s=t.extend(c,n.where);n.contentType&&0==n.contentType.indexOf("application/json")&&(s=JSON.stringify(s)),t.ajax({type:n.method||"get",url:n.url,contentType:n.contentType,data:s,dataType:"json",headers:n.headers||{},success:function(t){t[r.statusName]!=r.statusCode?(a.renderForm(),a.layMain.html('
          '+(t[r.msgName]||"返回的数据状态异常")+"
          ")):(a.renderData(t,e,t[r.countName]),d(),n.time=(new Date).getTime()-a.startTime+" ms"),i&&l.close(i),"function"==typeof n.done&&n.done(t,e,t[r.countName])},error:function(e,t){a.layMain.html('
          数据接口请求异常
          '),a.renderForm(),i&&l.close(i)}})}else if(n.data&&n.data.constructor===Array){var u={},h=e*n.limit-n.limit;u[r.dataName]=n.data.concat().splice(h,n.limit),u[r.countName]=n.data.length,a.renderData(u,e,n.data.length),d(),"function"==typeof n.done&&n.done(u,e,u[r.countName])}},M.prototype.eachCols=function(e){var i=t.extend(!0,[],this.config.cols),a=[],l=0;layui.each(i,function(e,t){layui.each(t,function(t,n){if(n.colspan>1){var o=0;l++,n.CHILD_COLS=[],layui.each(i[e+1],function(e,t){t.PARENT_COL||o==n.colspan||(t.PARENT_COL=l,n.CHILD_COLS.push(t),o+=t.colspan>1?t.colspan:1)})}n.PARENT_COL||a.push(n)})});var n=function(t){layui.each(t||a,function(t,i){return i.CHILD_COLS?n(i.CHILD_COLS):void e(t,i)})};n()},M.prototype.renderData=function(e,n,o,r){var c=this,s=c.config,u=e[s.response.dataName]||[],y=[],p=[],m=[],v=function(){return!r&&c.sortKey?c.sort(c.sortKey.field,c.sortKey.sort,!0):(layui.each(u,function(e,a){var l=[],o=[],u=[],h=e+s.limit*(n-1)+1;0!==a.length&&(r||(a[d.config.indexName]=e),c.eachCols(function(e,n){var r=n.field||e,f=a[r];c.getColElem(c.layHeader,r);if(void 0!==f&&null!==f||(f=""),!(n.colspan>1)){var y=['",'
          '+function(){var e=t.extend(!0,{LAY_INDEX:h},a);return"checkbox"===n.type?'":"numbers"===n.type?h:n.toolbar?i(t(n.toolbar).html()||"").render(e):n.templet?function(){return"function"==typeof n.templet?n.templet(e):i(t(n.templet).html()||String(f)).render(e)}():f}(),"
          "].join("");l.push(y),n.fixed&&"right"!==n.fixed&&o.push(y),"right"===n.fixed&&u.push(y)}}),y.push(''+l.join("")+""),p.push(''+o.join("")+""),m.push(''+u.join("")+""))}),c.layBody.scrollTop(0),c.layMain.find("."+f).remove(),c.layMain.find("tbody").html(y.join("")),c.layFixLeft.find("tbody").html(p.join("")),c.layFixRight.find("tbody").html(m.join("")),c.renderForm(),c.syncCheckAll(),c.haveInit?c.scrollPatch():setTimeout(function(){c.scrollPatch()},50),c.haveInit=!0,void l.close(c.tipsIndex))};return c.key=s.id||s.index,d.cache[c.key]=u,c.layPage[0===u.length&&1==n?"addClass":"removeClass"](h),r?v():0===u.length?(c.renderForm(),c.layFixed.remove(),c.layMain.find("tbody").html(""),c.layMain.find("."+f).remove(),c.layMain.append('
          '+s.text.none+"
          ")):(v(),void(s.page&&(s.page=t.extend({elem:"layui-table-page"+s.index,count:o,limit:s.limit,limits:s.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(c.page=e.curr,s.limit=e.limit,c.pullData(e.curr,c.loading()))}},s.page),s.page.count=o,a.render(s.page))))},M.prototype.getColElem=function(e,t){var i=this,a=i.config;return e.eq(0).find(".laytable-cell-"+(a.index+"-"+t)+":eq(0)")},M.prototype.renderForm=function(e){n.render(e,"LAY-table-"+this.index)},M.prototype.sort=function(e,i,a,l){var n,r,c=this,u={},h=c.config,f=h.elem.attr("lay-filter"),y=d.cache[c.key];"string"==typeof e&&c.layHeader.find("th").each(function(i,a){var l=t(this),o=l.data("field");if(o===e)return e=l,n=o,!1});try{var n=n||e.data("field");if(c.sortKey&&!a&&n===c.sortKey.field&&i===c.sortKey.sort)return;var p=c.layHeader.find("th .laytable-cell-"+h.index+"-"+n).find(w);c.layHeader.find("th").find(w).removeAttr("lay-sort"),p.attr("lay-sort",i||null),c.layFixed.find("th")}catch(m){return o.error("Table modules: Did not match to field")}c.sortKey={field:n,sort:i},"asc"===i?r=layui.sort(y,n):"desc"===i?r=layui.sort(y,n,!0):(r=layui.sort(y,d.config.indexName),delete c.sortKey),u[h.response.dataName]=r,c.renderData(u,c.page,c.count,!0),l&&layui.event.call(e,s,"sort("+f+")",{field:n,type:i})},M.prototype.loading=function(){var e=this,t=e.config;if(t.loading&&t.url)return l.msg("数据请求中",{icon:16,offset:[e.elem.offset().top+e.elem.height()/2-35-A.scrollTop()+"px",e.elem.offset().left+e.elem.width()/2-90-A.scrollLeft()+"px"],time:-1,anim:-1,fixed:!1})},M.prototype.setCheckData=function(e,t){var i=this,a=i.config,l=d.cache[i.key];l[e]&&l[e].constructor!==Array&&(l[e][a.checkName]=t)},M.prototype.syncCheckAll=function(){var e=this,t=e.config,i=e.layHeader.find('input[name="layTableCheckbox"]'),a=function(i){return e.eachCols(function(e,a){"checkbox"===a.type&&(a[t.checkName]=i)}),i};i[0]&&(d.checkStatus(e.key).isAll?(i[0].checked||(i.prop("checked",!0),e.renderForm("checkbox")),a(!0)):(i[0].checked&&(i.prop("checked",!1),e.renderForm("checkbox")),a(!1)))},M.prototype.getCssRule=function(e,t){var i=this,a=i.elem.find("style")[0],l=a.sheet||a.styleSheet||{},n=l.cssRules||l.rules;layui.each(n,function(a,l){if(l.selectorText===".laytable-cell-"+i.index+"-"+e)return t(l),!0})},M.prototype.fullSize=function(){var e,t=this,i=t.config,a=i.height;t.fullHeightGap&&(a=A.height()-t.fullHeightGap,a<135&&(a=135),t.elem.css("height",a)),e=parseFloat(a)-parseFloat(t.layHeader.height())-1,i.toolbar&&(e-=t.layTool.outerHeight()),i.page&&(e=e-t.layPage.outerHeight()-1),t.layMain.css("height",e)},M.prototype.getScrollWidth=function(e){var t=0;return e?t=e.offsetWidth-e.clientWidth:(e=document.createElement("div"),e.style.width="100px",e.style.height="100px",e.style.overflowY="scroll",document.body.appendChild(e),t=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),t},M.prototype.scrollPatch=function(){var e=this,i=e.layMain.children("table"),a=e.layMain.width()-e.layMain.prop("clientWidth"),l=e.layMain.height()-e.layMain.prop("clientHeight"),n=e.getScrollWidth(e.layMain[0]),o=i.outerWidth()-e.layMain.width();if(e.autoColNums&&o<5&&!e.scrollPatchWStatus){var r=e.layHeader.eq(0).find("thead th:last-child"),d=r.data("field");e.getCssRule(d,function(t){var i=t.style.width||r.outerWidth();t.style.width=parseFloat(i)-n-o+"px",e.layMain.height()-e.layMain.prop("clientHeight")>0&&(t.style.width=parseFloat(t.style.width)-1+"px"),e.scrollPatchWStatus=!0})}if(a&&l){if(!e.elem.find(".layui-table-patch")[0]){var c=t('
          ');c.find("div").css({width:a}),e.layHeader.eq(0).find("thead tr").append(c)}}else e.layHeader.eq(0).find(".layui-table-patch").remove();var s=e.layMain.height(),u=s-l;e.layFixed.find(m).css("height",i.height()>u?u:"auto"),e.layFixRight[o>0?"removeClass":"addClass"](h),e.layFixRight.css("right",a-1)},M.prototype.events=function(){var e,a=this,n=a.config,o=t("body"),c={},u=a.layHeader.find("th"),h=".layui-table-cell",f=n.elem.attr("lay-filter");u.on("mousemove",function(e){var i=t(this),a=i.offset().left,l=e.clientX-a;i.attr("colspan")>1||i.data("unresize")||c.resizeStart||(c.allowResize=i.width()-l<=10,o.css("cursor",c.allowResize?"col-resize":""))}).on("mouseleave",function(){t(this);c.resizeStart||o.css("cursor","")}).on("mousedown",function(e){var i=t(this);if(c.allowResize){var l=i.data("field");e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],a.getCssRule(l,function(e){var t=e.style.width||i.outerWidth();c.rule=e,c.ruleWidth=parseFloat(t),c.minWidth=i.data("minwidth")||n.cellMinWidth})}}),S.on("mousemove",function(t){if(c.resizeStart){if(t.preventDefault(),c.rule){var i=c.ruleWidth+t.clientX-c.offset[0];i');d[0].value=e.data("content")||o.text(),e.find("."+N)[0]||e.append(d),d.focus()}else o.find(".layui-form-switch,.layui-form-checkbox")[0]||Math.round(o.prop("scrollWidth"))>Math.round(o.outerWidth())&&(a.tipsIndex=l.tips(['
          ',o.html(),"
          ",''].join(""),o[0],{tips:[3,""],time:-1,anim:-1,maxWidth:r.ios||r.android?300:600,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){l.close(t)})}}))}),a.layBody.on("click","*[lay-event]",function(){var e=t(this),l=e.parents("tr").eq(0).data("index"),n=a.layBody.find('tr[data-index="'+l+'"]'),o="layui-table-click",r=d.cache[a.key][l];layui.event.call(this,s,"tool("+f+")",{data:d.clearCacheKey(r),event:e.attr("lay-event"),tr:n,del:function(){d.cache[a.key][l]=[],n.remove(),a.scrollPatch()},update:function(e){e=e||{},layui.each(e,function(e,l){if(e in r){var o,d=n.children('td[data-field="'+e+'"]');r[e]=l,a.eachCols(function(t,i){i.field==e&&i.templet&&(o=i.templet)}),d.children(h).html(o?i(t(o).html()||l).render(r):l),d.data("content",l)}})}}),n.addClass(o).siblings("tr").removeClass(o)}),a.layMain.on("scroll",function(){var e=t(this),i=e.scrollLeft(),n=e.scrollTop();a.layHeader.scrollLeft(i),a.layFixed.find(m).scrollTop(n),l.close(a.tipsIndex)}),A.on("resize",function(){a.fullSize(),a.scrollPatch()})},d.init=function(e,i){i=i||{};var a=this,l=t(e?'table[lay-filter="'+e+'"]':u+"[lay-data]"),n="Table element property lay-data configuration item has a syntax error: ";return l.each(function(){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){o.error(n+l)}var c=[],s=t.extend({elem:this,cols:[],data:[],skin:a.attr("lay-skin"),size:a.attr("lay-size"),even:"string"==typeof a.attr("lay-even")},d.config,i,l);e&&a.hide(),a.find("thead>tr").each(function(e){s.cols[e]=[],t(this).children().each(function(i){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){return o.error(n+l)}var d=t.extend({title:a.text(),colspan:a.attr("colspan")||0,rowspan:a.attr("rowspan")||0},l);d.colspan<2&&c.push(d),s.cols[e].push(d)})}),a.find("tbody>tr").each(function(e){var i=t(this),a={};i.children("td").each(function(e,i){var l=t(this),n=l.data("field");if(n)return a[n]=l.html()}),layui.each(c,function(e,t){var l=i.children("td").eq(e);a[t.field]=l.html()}),s.data[e]=a}),d.render(s)}),a},d.checkStatus=function(e){var t=0,i=0,a=[],l=d.cache[e]||[];return layui.each(l,function(e,l){return l.constructor===Array?void i++:void(l[d.config.checkName]&&(t++,a.push(d.clearCacheKey(l))))}),{data:a,isAll:!!l.length&&t===l.length-i}},c.config={},d.reload=function(e,i){var a=c.config[e];return i=i||{},a?(i.data&&i.data.constructor===Array&&delete a.data,d.render(t.extend(!0,{},a,i))):o.error("The ID option was not found in the table instance")},d.render=function(e){var t=new M(e);return c.call(t)},d.clearCacheKey=function(e){return e=t.extend({},e),delete e[d.config.checkName],delete e[d.config.indexName],e},d.init(),e(s,d)}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/tree.js b/static/plugs/layui/lay/modules/tree.js index 221c0629c..ed01acbbe 100644 --- a/static/plugs/layui/lay/modules/tree.js +++ b/static/plugs/layui/lay/modules/tree.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define("jquery",function(e){"use strict";var o=layui.jquery,a=layui.hint(),r="layui-tree-enter",i=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};i.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},i.prototype.tree=function(e,a){var r=this,i=r.options,n=a||i.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
            '),s=o(["
          • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return i.check?''+("checkbox"===i.check?t.checkbox[0]:"radio"===i.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
          • "].join(""));l&&(s.append(c),r.tree(c,n.children)),e.append(s),"function"==typeof i.click&&r.click(s,n),r.spread(s,n),i.drag&&r.drag(s,n)})},i.prototype.click=function(e,o){var a=this,r=a.options;e.children("a").on("click",function(e){layui.stope(e),r.click(o)})},i.prototype.spread=function(e,o){var a=this,r=(a.options,e.children(".layui-tree-spread")),i=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),i.removeClass("layui-show"),r.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),i.addClass("layui-show"),r.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};i[0]&&(r.on("click",l),n.on("dblclick",l))},i.prototype.on=function(e){var a=this,i=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),i.drag&&o(document).on("mousemove",function(e){var r=a.move;if(r.from){var i=(r.to,o('
            '));e.preventDefault(),o("."+t)[0]||o("body").append(i);var n=o("."+t)[0]?o("."+t):i;n.addClass("layui-show").html(r.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(r),e.to&&e.to.elem.children("a").removeClass(r),a.move={},o("."+t).remove())})},i.prototype.move={},i.prototype.drag=function(e,a){var i=this,t=(i.options,e.children("a")),n=function(){var t=o(this),n=i.move;n.from&&(n.to={item:a,elem:e},t.addClass(r))};t.on("mousedown",function(){var o=i.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=i.move;a.from&&(delete a.to,e.removeClass(r))})},e("tree",function(e){var r=new i(e=e||{}),t=o(e.elem);return t[0]?void r.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var o=layui.$,a=layui.hint(),i="layui-tree-enter",r=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};r.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},r.prototype.tree=function(e,a){var i=this,r=i.options,n=a||r.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
              '),s=o(["
            • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return r.check?''+("checkbox"===r.check?t.checkbox[0]:"radio"===r.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
            • "].join(""));l&&(s.append(c),i.tree(c,n.children)),e.append(s),"function"==typeof r.click&&i.click(s,n),i.spread(s,n),r.drag&&i.drag(s,n)})},r.prototype.click=function(e,o){var a=this,i=a.options;e.children("a").on("click",function(e){layui.stope(e),i.click(o)})},r.prototype.spread=function(e,o){var a=this,i=(a.options,e.children(".layui-tree-spread")),r=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),r.removeClass("layui-show"),i.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),r.addClass("layui-show"),i.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};r[0]&&(i.on("click",l),n.on("dblclick",l))},r.prototype.on=function(e){var a=this,r=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),r.drag&&o(document).on("mousemove",function(e){var i=a.move;if(i.from){var r=(i.to,o('
              '));e.preventDefault(),o("."+t)[0]||o("body").append(r);var n=o("."+t)[0]?o("."+t):r;n.addClass("layui-show").html(i.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(i),e.to&&e.to.elem.children("a").removeClass(i),a.move={},o("."+t).remove())})},r.prototype.move={},r.prototype.drag=function(e,a){var r=this,t=(r.options,e.children("a")),n=function(){var t=o(this),n=r.move;n.from&&(n.to={item:a,elem:e},t.addClass(i))};t.on("mousedown",function(){var o=r.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=r.move;a.from&&(delete a.to,e.removeClass(i))})},e("tree",function(e){var i=new r(e=e||{}),t=o(e.elem);return t[0]?void i.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/upload.js b/static/plugs/layui/lay/modules/upload.js index d5b78f943..469a13f97 100644 --- a/static/plugs/layui/lay/modules/upload.js +++ b/static/plugs/layui/lay/modules/upload.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define("layer",function(e){"use strict";var a=layui.jquery,t=layui.layer,i=(layui.device(),"layui-upload-enter"),n="layui-upload-iframe",r={icon:2,shift:6},o={file:"文件",video:"视频",audio:"音频"},s=function(e){this.options=e};s.prototype.init=function(){var e=this,t=e.options,r=a("body"),s=a(t.elem||".layui-upload-file"),u=a('');return a("#"+n)[0]||r.append(u),s.each(function(r,s){s=a(s);var u='
              ',l=s.attr("lay-type")||t.type;t.unwrap||(u='
              '+u+''+(s.attr("lay-title")||t.title||"上传"+(o[l]||"图片"))+"
              "),u=a(u),t.unwrap||u.on("dragover",function(e){e.preventDefault(),a(this).addClass(i)}).on("dragleave",function(){a(this).removeClass(i)}).on("drop",function(){a(this).removeClass(i)}),s.parent("form").attr("target")===n&&(t.unwrap?s.unwrap():(s.parent().next().remove(),s.unwrap().unwrap())),s.wrap(u),s.off("change").on("change",function(){e.action(this,l)})})},s.prototype.action=function(e,i){var o=this,s=o.options,u=e.value,l=a(e),p=l.attr("lay-ext")||s.ext||"";if(u){switch(i){case"file":if(p&&!RegExp("\\w\\.("+p+")$","i").test(escape(u)))return t.msg("不支持该文件格式",r),e.value="";break;case"video":if(!RegExp("\\w\\.("+(p||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(u)))return t.msg("不支持该视频格式",r),e.value="";break;case"audio":if(!RegExp("\\w\\.("+(p||"mp3|wav|mid")+")$","i").test(escape(u)))return t.msg("不支持该音频格式",r),e.value="";break;default:if(!RegExp("\\w\\.("+(p||"jpg|png|gif|bmp|jpeg")+")$","i").test(escape(u)))return t.msg("不支持该图片格式",r),e.value=""}s.before&&s.before(e),l.parent().submit();var c=a("#"+n),f=setInterval(function(){var a;try{a=c.contents().find("body").text()}catch(i){t.msg("上传接口存在跨域",r),clearInterval(f)}if(a){clearInterval(f),c.contents().find("body").html("");try{a=JSON.parse(a)}catch(i){return a={},t.msg("请对上传接口返回JSON字符",r)}"function"==typeof s.success&&s.success(a,e)}},30);e.value=""}},e("upload",function(e){var a=new s(e=e||{});a.init()})}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,n=layui.hint(),a=layui.device(),o={config:{},set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,r,e,i)}},l=function(){var e=this;return{upload:function(i){e.upload.call(e,i)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var t=this;t.config=i.extend({},t.config,o.config,e),t.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var t=this,e=t.config;e.elem=i(e.elem),e.bindAction=i(e.bindAction),t.file(),t.events()},p.prototype.file=function(){var e=this,t=e.config,n=e.elemFile=i(['"].join("")),o=t.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&t.elem.wrap('
              '),e.isFile()?(e.elemFile=t.elem,t.field=t.elem[0].name):t.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,t=e.config,n=i(''),a=i(['
              ',"
              "].join(""));i("#"+f)[0]||i("body").append(n),t.elem.next().hasClass(c)||(e.elemFile.wrap(a),t.elem.next("."+c).append(function(){var e=[];return layui.each(t.data,function(i,t){t="function"==typeof t?t():t,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return t.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var i=this;window.FileReader&&layui.each(i.chooseFiles,function(i,t){var n=new FileReader;n.readAsDataURL(t),n.onload=function(){e&&e(i,t,this.result)}})},p.prototype.upload=function(e,t){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var t=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&t+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:t,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,i){i="function"==typeof i?i():i,r.append(e,i)}),i.ajax({url:l.url,type:l.method,data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(i){t++,d(e,i),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=i("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var i,t=e.contents().find("body");try{i=t.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}i&&(clearInterval(p.timer),t.html(""),d(0,i))},30)},d=function(e,i){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof i)try{i=JSON.parse(i)}catch(t){return i={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(i,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var i=[];return layui.each(e||o.chooseFiles,function(e,t){i.push(t.name)}),i}(),g={preview:function(e){o.preview(e)},upload:function(e,i){var t={};t[e]=i,o.upload(t)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,i){o.files[e]=i}),o.files},resetFile:function(e,i,t){var n=new File([i],t);o.files=o.files||{},o.files[e]=n}},y=function(){if("choose"!==t&&!l.auto||(l.choose&&l.choose(g),"choose"!==t))return l.before&&l.before(g),a.ie?a.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,i){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(i))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var i=0,t=e||o.files||o.chooseFiles||r.files;return layui.each(t,function(){i++}),i}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,i){if(i.size>1024*l.size){var t=l.size/1024;t=t>=1?t.toFixed(2)+"MB":l.size+"KB",r.value="",F=t}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,t=e.config,o=function(i){e.chooseFiles={},layui.each(i,function(i,t){var n=(new Date).getTime();e.chooseFiles[n+"-"+i]=t})},l=function(i,n){var a=e.elemFile,o=i.length>1?i.length+"个文件":(i[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||t.choose||a.after(''+o+"")};t.elem.off("upload.start").on("upload.start",function(){var a=i(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=i.extend({},t,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||t.elem.off("upload.over").on("upload.over",function(){var e=i(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=i(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=i(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),t.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var i=this.files||[];o(i),t.auto?e.upload():l(i)}),t.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),t.elem.data("haveEvents")||(e.elemFile.on("change",function(){i(this).trigger("upload.change")}),t.elem.on("click",function(){e.isFile()||i(this).trigger("upload.start")}),t.drag&&t.elem.on("dragover",function(e){e.preventDefault(),i(this).trigger("upload.over")}).on("dragleave",function(e){i(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),i(this).trigger("upload.drop",e)}),t.bindAction.on("click",function(){i(this).trigger("upload.action")}),t.elem.data("haveEvents",!0))},o.render=function(e){var i=new p(e);return l.call(i)},e(r,o)}); \ No newline at end of file diff --git a/static/plugs/layui/lay/modules/util.js b/static/plugs/layui/lay/modules/util.js index 2f6938f4e..32bffa421 100644 --- a/static/plugs/layui/lay/modules/util.js +++ b/static/plugs/layui/lay/modules/util.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;layui.define("jquery",function(l){"use strict";var o=layui.jquery,i={fixbar:function(l){l=l||{},l.bgcolor=l.bgcolor?"background-color:"+l.bgcolor:"";var i,a,c="layui-fixbar-top",t=[l.bar1===!0?"":l.bar1,l.bar2===!0?"":l.bar2,""],r=o(['
                ',l.bar1?'
              • '+t[0]+"
              • ":"",l.bar2?'
              • '+t[1]+"
              • ":"",'
              • '+t[2]+"
              • ","
              "].join("")),e=r.find("."+c),s=function(){var i=o(document).scrollTop();i>=(l.showHeight||200)?a||(e.show(),a=1):a&&(e.hide(),a=0)};o(".layui-fixbar")[0]||("object"==typeof l.css&&r.css(l.css),o("body").append(r),s(),r.find("li").on("click",function(){var i=o(this),a=i.attr("lay-type");"top"===a&&o("html,body").animate({scrollTop:0},200),l.click&&l.click.call(this,a)}),o(document).on("scroll",function(){i&&clearTimeout(i),i=setTimeout(function(){s()},100)}))}};l("util",i)}); \ No newline at end of file +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;layui.define("jquery",function(e){"use strict";var t=layui.$,i={fixbar:function(e){var i,a,o="layui-fixbar",r="layui-fixbar-top",l=t(document),n=t("body");e=t.extend({showHeight:200},e),e.bar1=e.bar1===!0?"":e.bar1,e.bar2=e.bar2===!0?"":e.bar2,e.bgcolor=e.bgcolor?"background-color:"+e.bgcolor:"";var c=[e.bar1,e.bar2,""],g=t(['
                ',e.bar1?'
              • '+c[0]+"
              • ":"",e.bar2?'
              • '+c[1]+"
              • ":"",'
              • '+c[2]+"
              • ","
              "].join("")),u=g.find("."+r),s=function(){var t=l.scrollTop();t>=e.showHeight?i||(u.show(),i=1):i&&(u.hide(),i=0)};t("."+o)[0]||("object"==typeof e.css&&g.css(e.css),n.append(g),s(),g.find("li").on("click",function(){var i=t(this),a=i.attr("lay-type");"top"===a&&t("html,body").animate({scrollTop:0},200),e.click&&e.click.call(this,a)}),l.on("scroll",function(){clearTimeout(a),a=setTimeout(function(){s()},100)}))},countdown:function(e,t,i){var a=this,o="function"==typeof t,r=new Date(e).getTime(),l=new Date(!t||o?(new Date).getTime():t).getTime(),n=r-l,c=[Math.floor(n/864e5),Math.floor(n/36e5)%24,Math.floor(n/6e4)%60,Math.floor(n/1e3)%60];o&&(i=t);var g=setTimeout(function(){a.countdown(e,l+1e3,i)},1e3);return i&&i(n>0?c:[0,0,0,0],t,g),n<=0&&clearTimeout(g),g},timeAgo:function(e,t){var i=this,a=[[],[]],o=(new Date).getTime()-new Date(e).getTime();return o>6912e5?(o=new Date(e),a[0][0]=i.digit(o.getFullYear(),4),a[0][1]=i.digit(o.getMonth()+1),a[0][2]=i.digit(o.getDate()),t||(a[1][0]=i.digit(o.getHours()),a[1][1]=i.digit(o.getMinutes()),a[1][2]=i.digit(o.getSeconds())),a[0].join("-")+" "+a[1].join(":")):o>=864e5?(o/1e3/60/60/24|0)+"天前":o>=36e5?(o/1e3/60/60|0)+"小时前":o>=12e4?(o/1e3/60|0)+"分钟前":o<0?"未来":"刚刚"},digit:function(e,t){var i="";e=String(e),t=t||2;for(var a=e.length;a/g,">").replace(/'/g,"'").replace(/"/g,""")}};e("util",i)}); \ No newline at end of file diff --git a/static/plugs/layui/laydate/laydate.js b/static/plugs/layui/laydate/laydate.js deleted file mode 100644 index 8f7656eb5..000000000 --- a/static/plugs/layui/laydate/laydate.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - - @Name : layDate v1.1 日期控件 - @Author: 贤心 - @Date: 2014-06-25 - @QQ群:176047195 - @Site:http://sentsin.com/layui/laydate - - */ - -;!function(a){var b={path:"",defSkin:"molv",format:"YYYY-MM-DD",min:"1900-01-01 00:00:00",max:"2099-12-31 23:59:59",isv:!1},c={},d=document,e="createElement",f="getElementById",g="getElementsByTagName",h=["laydate_box","laydate_void","laydate_click","LayDateSkin","skins/","/laydate.css"];a.laydate=function(b){b=b||{};try{h.event=a.event?a.event:laydate.caller.arguments[0]}catch(d){}return c.run(b),laydate},laydate.v="1.1",c.getPath=window.LAYDATE_PATH||function(){var a=document.scripts,c=a[a.length-1].src;return b.path?b.path:c.substring(0,c.lastIndexOf("/")+1)}(),c.use=function(a,b){var f=d[e]("link");f.type="text/css",f.rel="stylesheet",f.href=c.getPath+a+h[5],b&&(f.id=b),d[g]("head")[0].appendChild(f),f=null},c.trim=function(a){return a=a||"",a.replace(/^\s|\s$/g,"").replace(/\s+/g," ")},c.digit=function(a){return 10>a?"0"+(0|a):a},c.stopmp=function(b){return b=b||a.event,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0,this},c.each=function(a,b){for(var c=0,d=a.length;d>c&&b(c,a[c])!==!1;c++);},c.hasClass=function(a,b){return a=a||{},new RegExp("\\b"+b+"\\b").test(a.className)},c.addClass=function(a,b){return a=a||{},c.hasClass(a,b)||(a.className+=" "+b),a.className=c.trim(a.className),this},c.removeClass=function(a,b){if(a=a||{},c.hasClass(a,b)){var d=new RegExp("\\b"+b+"\\b");a.className=a.className.replace(d,"")}return this},c.removeCssAttr=function(a,b){var c=a.style;c.removeProperty?c.removeProperty(b):c.removeAttribute(b)},c.shde=function(a,b){a.style.display=b?"none":"block"},c.query=function(a){var e,b,h,i,j;return a=c.trim(a).split(" "),b=d[f](a[0].substr(1)),b?a[1]?/^\./.test(a[1])?(i=a[1].substr(1),j=new RegExp("\\b"+i+"\\b"),e=[],h=d.getElementsByClassName?b.getElementsByClassName(i):b[g]("*"),c.each(h,function(a,b){j.test(b.className)&&e.push(b)}),e[0]?e:""):(e=b[g](a[1]),e[0]?b[g](a[1]):""):b:void 0},c.on=function(b,d,e){return b.attachEvent?b.attachEvent("on"+d,function(){e.call(b,a.even)}):b.addEventListener(d,e,!1),c},c.stopMosup=function(a,b){"mouseup"!==a&&c.on(b,"mouseup",function(a){c.stopmp(a)})},c.run=function(a){var d,e,g,b=c.query,f=h.event;try{g=f.target||f.srcElement||{}}catch(i){g={}}if(d=a.elem?b(a.elem):g,f&&g.tagName){if(!d||d===c.elem)return;c.stopMosup(f.type,d),c.stopmp(f),c.view(d,a),c.reshow()}else e=a.event||"click",c.each((0|d.length)>0?d:[d],function(b,d){c.stopMosup(e,d),c.on(d,e,function(b){c.stopmp(b),d!==c.elem&&(c.view(d,a),c.reshow())})})},c.scroll=function(a){return a=a?"scrollLeft":"scrollTop",d.body[a]|d.documentElement[a]},c.winarea=function(a){return document.documentElement[a?"clientWidth":"clientHeight"]},c.isleap=function(a){return 0===a%4&&0!==a%100||0===a%400},c.checkVoid=function(a,b,d){var e=[];return a=0|a,b=0|b,d=0|d,ac.maxs[0]?e=["y",1]:a>=c.mins[0]&&a<=c.maxs[0]&&(a==c.mins[0]&&(bc.maxs[1]?e=["m",1]:b==c.maxs[1]&&d>c.maxs[2]&&(e=["d",1]))),e},c.timeVoid=function(a,b){if(c.ymd[1]+1==c.mins[1]&&c.ymd[2]==c.mins[2]){if(0===b&&ac.maxs[3])return 1;if(1===b&&a>c.maxs[4])return 1;if(2===b&&a>c.maxs[5])return 1}return a>(b?59:23)?1:void 0},c.check=function(){var a=c.options.format.replace(/YYYY|MM|DD|hh|mm|ss/g,"\\d+\\").replace(/\\$/g,""),b=new RegExp(a),d=c.elem[h.elemv],e=d.match(/\d+/g)||[],f=c.checkVoid(e[0],e[1],e[2]);if(""!==d.replace(/\s/g,"")){if(!b.test(d))return c.elem[h.elemv]="",c.msg("日期不符合格式,请重新选择。"),1;if(f[0])return c.elem[h.elemv]="",c.msg("日期不在有效期内,请重新选择。"),1;f.value=c.elem[h.elemv].match(b).join(),e=f.value.match(/\d+/g),e[1]<1?(e[1]=1,f.auto=1):e[1]>12?(e[1]=12,f.auto=1):e[1].length<2&&(f.auto=1),e[2]<1?(e[2]=1,f.auto=1):e[2]>c.months[(0|e[1])-1]?(e[2]=31,f.auto=1):e[2].length<2&&(f.auto=1),e.length>3&&(c.timeVoid(e[3],0)&&(f.auto=1),c.timeVoid(e[4],1)&&(f.auto=1),c.timeVoid(e[5],2)&&(f.auto=1)),f.auto?c.creation([e[0],0|e[1],0|e[2]],1):f.value!==c.elem[h.elemv]&&(c.elem[h.elemv]=f.value)}},c.months=[31,null,31,30,31,30,31,31,30,31,30,31],c.viewDate=function(a,b,d){var f=(c.query,{}),g=new Date;a<(0|c.mins[0])&&(a=0|c.mins[0]),a>(0|c.maxs[0])&&(a=0|c.maxs[0]),g.setFullYear(a,b,d),f.ymd=[g.getFullYear(),g.getMonth(),g.getDate()],c.months[1]=c.isleap(f.ymd[0])?29:28,g.setFullYear(f.ymd[0],f.ymd[1],1),f.FDay=g.getDay(),f.PDay=c.months[0===b?11:b-1]-f.FDay+1,f.NDay=1,c.each(h.tds,function(a,b){var g,d=f.ymd[0],e=f.ymd[1]+1;b.className="",a=f.FDay&&a'+a+"年":'
            • '+(a-7+b)+"年
            • "}),b("#laydate_ys").innerHTML=d,c.each(b("#laydate_ys li"),function(a,b){"y"===c.checkVoid(b.getAttribute("y"))[0]?c.addClass(b,h[1]):c.on(b,"click",function(a){c.stopmp(a).reshow(),c.viewDate(0|this.getAttribute("y"),c.ymd[1],c.ymd[2])})})},c.initDate=function(){var d=(c.query,new Date),e=c.elem[h.elemv].match(/\d+/g)||[];e.length<3&&(e=c.options.start.match(/\d+/g)||[],e.length<3&&(e=[d.getFullYear(),d.getMonth()+1,d.getDate()])),c.inymd=e,c.viewDate(e[0],e[1]-1,e[2])},c.iswrite=function(){var a=c.query,b={time:a("#laydate_hms")};c.shde(b.time,!c.options.istime),c.shde(h.oclear,!("isclear"in c.options?c.options.isclear:1)),c.shde(h.otoday,!("istoday"in c.options?c.options.istoday:1)),c.shde(h.ok,!("issure"in c.options?c.options.issure:1))},c.orien=function(a,b){var d,e=c.elem.getBoundingClientRect();a.style.left=e.left+(b?0:c.scroll(1))+"px",d=e.bottom+a.offsetHeight/1.5<=c.winarea()?e.bottom-1:e.top>a.offsetHeight/1.5?e.top-a.offsetHeight+1:c.winarea()-a.offsetHeight,a.style.top=d+(b?0:c.scroll())+"px"},c.follow=function(a){c.options.fixed?(a.style.position="fixed",c.orien(a,1)):(a.style.position="absolute",c.orien(a))},c.viewtb=function(){var a,b=[],f=["日","一","二","三","四","五","六"],h={},i=d[e]("table"),j=d[e]("thead");return j.appendChild(d[e]("tr")),h.creath=function(a){var b=d[e]("th");b.innerHTML=f[a],j[g]("tr")[0].appendChild(b),b=null},c.each(new Array(6),function(d){b.push([]),a=i.insertRow(0),c.each(new Array(7),function(c){b[d][c]=0,0===d&&h.creath(c),a.insertCell(c)})}),i.insertBefore(j,i.children[0]),i.id=i.className="laydate_table",a=b=null,i.outerHTML.toLowerCase()}(),c.view=function(a,f){var i,g=c.query,j={};f=f||a,c.elem=a,c.options=f,c.options.format||(c.options.format=b.format),c.options.start=c.options.start||"",c.mm=j.mm=[c.options.min||b.min,c.options.max||b.max],c.mins=j.mm[0].match(/\d+/g),c.maxs=j.mm[1].match(/\d+/g),h.elemv=/textarea|input/.test(c.elem.tagName.toLocaleLowerCase())?"value":"innerHTML",c.box?c.shde(c.box):(i=d[e]("div"),i.id=h[0],i.className=h[0],i.style.cssText="position: absolute;",i.setAttribute("name","laydate-v"+laydate.v),i.innerHTML=j.html='
                '+function(){var a="";return c.each(new Array(12),function(b){a+=''+c.digit(b+1)+"月"}),a}()+"
                "+"
                "+"
                "+c.viewtb+'
                '+'
                  '+'
                • 时间
                • '+"
                • :
                • "+"
                • :
                • "+"
                • "+"
                "+'
                '+'
                '+'清空'+'今天'+'确认'+"
                "+(b.isv?'laydate-v'+laydate.v+"":"")+"
                ",d.body.appendChild(i),c.box=g("#"+h[0]),c.events(),i=null),c.follow(c.box),f.zIndex?c.box.style.zIndex=f.zIndex:c.removeCssAttr(c.box,"z-index"),c.stopMosup("click",c.box),c.initDate(),c.iswrite(),c.check()},c.reshow=function(){return c.each(c.query("#"+h[0]+" .laydate_show"),function(a,b){c.removeClass(b,"laydate_show")}),this},c.close=function(){c.reshow(),c.shde(c.query("#"+h[0]),1),c.elem=null},c.parse=function(a,d,e){return a=a.concat(d),e=e||(c.options?c.options.format:b.format),e.replace(/YYYY|MM|DD|hh|mm|ss/g,function(){return a.index=0|++a.index,c.digit(a[a.index])})},c.creation=function(a,b){var e=(c.query,c.hmsin),f=c.parse(a,[e[0].value,e[1].value,e[2].value]);c.elem[h.elemv]=f,$(c.elem).triggerHandler('change'),b||(c.close(),"function"==typeof c.options.choose&&c.options.choose(f))},c.events=function(){var b=c.query,e={box:"#"+h[0]};c.addClass(d.body,"laydate_body"),h.tds=b("#laydate_table td"),h.mms=b("#laydate_ms span"),h.year=b("#laydate_y"),h.month=b("#laydate_m"),c.each(b(e.box+" .laydate_ym"),function(a,b){c.on(b,"click",function(b){c.stopmp(b).reshow(),c.addClass(this[g]("div")[0],"laydate_show"),a||(e.YY=parseInt(h.year.value),c.viewYears(e.YY))})}),c.on(b(e.box),"click",function(){c.reshow()}),e.tabYear=function(a){0===a?c.ymd[0]--:1===a?c.ymd[0]++:2===a?e.YY-=14:e.YY+=14,2>a?(c.viewDate(c.ymd[0],c.ymd[1],c.ymd[2]),c.reshow()):c.viewYears(e.YY)},c.each(b("#laydate_YY .laydate_tab"),function(a,b){c.on(b,"click",function(b){c.stopmp(b),e.tabYear(a)})}),e.tabMonth=function(a){a?(c.ymd[1]++,12===c.ymd[1]&&(c.ymd[0]++,c.ymd[1]=0)):(c.ymd[1]--,-1===c.ymd[1]&&(c.ymd[0]--,c.ymd[1]=11)),c.viewDate(c.ymd[0],c.ymd[1],c.ymd[2])},c.each(b("#laydate_MM .laydate_tab"),function(a,b){c.on(b,"click",function(b){c.stopmp(b).reshow(),e.tabMonth(a)})}),c.each(b("#laydate_ms span"),function(a,b){c.on(b,"click",function(a){c.stopmp(a).reshow(),c.hasClass(this,h[1])||c.viewDate(c.ymd[0],0|this.getAttribute("m"),c.ymd[2])})}),c.each(b("#laydate_table td"),function(a,b){c.on(b,"click",function(a){c.hasClass(this,h[1])||(c.stopmp(a),c.creation([0|this.getAttribute("y"),0|this.getAttribute("m"),0|this.getAttribute("d")]))})}),h.oclear=b("#laydate_clear"),c.on(h.oclear,"click",function(){c.elem[h.elemv]="",$(c.elem).trigger('change'),c.close()}),h.otoday=b("#laydate_today"),c.on(h.otoday,"click",function(){c.elem[h.elemv]=laydate.now(0,c.options.format),c.close()}),h.ok=b("#laydate_ok"),c.on(h.ok,"click",function(){c.valid&&c.creation([c.ymd[0],c.ymd[1]+1,c.ymd[2]])}),e.times=b("#laydate_time"),c.hmsin=e.hmsin=b("#laydate_hms input"),e.hmss=["小时","分钟","秒数"],e.hmsarr=[],c.msg=function(a,d){var f='
                '+(d||"提示")+"×
                ";"string"==typeof a?(f+="

                "+a+"

                ",c.shde(b("#"+h[0])),c.removeClass(e.times,"laydate_time1").addClass(e.times,"laydate_msg")):(e.hmsarr[a]?f=e.hmsarr[a]:(f+='
                ',c.each(new Array(0===a?24:60),function(a){f+=""+a+""}),f+="
                ",e.hmsarr[a]=f),c.removeClass(e.times,"laydate_msg"),c[0===a?"removeClass":"addClass"](e.times,"laydate_time1")),c.addClass(e.times,"laydate_show"),e.times.innerHTML=f},e.hmson=function(a,d){var e=b("#laydate_hmsno span"),f=c.valid?null:1;c.each(e,function(b,e){f?c.addClass(e,h[1]):c.timeVoid(b,d)?c.addClass(e,h[1]):c.on(e,"click",function(){c.hasClass(this,h[1])||(a.value=c.digit(0|this.innerHTML))})}),c.addClass(e[0|a.value],"laydate_click")},c.each(e.hmsin,function(a,b){c.on(b,"click",function(b){c.stopmp(b).reshow(),c.msg(a,e.hmss[a]),e.hmson(this,a)})}),c.on(d,"mouseup",function(){var a=b("#"+h[0]);a&&"none"!==a.style.display&&(c.check()||c.close())}).on(d,"keydown",function(b){b=b||a.event;var d=b.keyCode;13===d&&c.creation([c.ymd[0],c.ymd[1]+1,c.ymd[2]])})},c.init=function(){c.use("need"),c.use(h[4]+b.defSkin,h[3]),c.skinLink=c.query("#"+h[3])}(),laydate.reset=function(){c.box&&c.elem&&c.follow(c.box)},laydate.now=function(a,b){var d=new Date(0|a?function(a){return 864e5>a?+new Date+864e5*a:a}(parseInt(a)):+new Date);return c.parse([d.getFullYear(),d.getMonth()+1,d.getDate()],[d.getHours(),d.getMinutes(),d.getSeconds()],b)},laydate.skin=function(a){c.skinLink.href=c.getPath+h[4]+a+h[5]}}(window); \ No newline at end of file diff --git a/static/plugs/layui/laydate/need/laydate.css b/static/plugs/layui/laydate/need/laydate.css deleted file mode 100644 index a1b04e198..000000000 --- a/static/plugs/layui/laydate/need/laydate.css +++ /dev/null @@ -1,71 +0,0 @@ -/** - @Name: laydate 核心样式 - @Author:贤心 - @Site:http://sentsin.com/layui/laydate -**/ - -html{_background-image:url(about:blank); _background-attachment:fixed;} -.laydate_body .laydate_box, .laydate_body .laydate_box *{margin:0; padding:0;} -.laydate-icon, -.laydate-icon-default, -.laydate-icon-danlan, -.laydate-icon-dahong, -.laydate-icon-molv{height:22px; line-height:22px; padding-right:20px; border:1px solid #C6C6C6; background-repeat:no-repeat; background-position:right center; background-color:#fff; outline:0;} -.laydate-icon-default{ background-image:url(../skins/default/icon.png)} -.laydate-icon-danlan{border:1px solid #B1D2EC; background-image:url(../skins/danlan/icon.png)} -.laydate-icon-dahong{background-image:url(../skins/dahong/icon.png)} -.laydate-icon-molv{background-image:url(../skins/molv/icon.png)} -.laydate_body .laydate_box{width:240px; font:12px '\5B8B\4F53'; z-index:99999999; *margin:-2px 0 0 -2px; *overflow:hidden; _margin:0; _position:absolute!important; background-color:#fff;} -.laydate_body .laydate_box li{list-style:none;} -.laydate_body .laydate_box .laydate_void{cursor:text!important;} -.laydate_body .laydate_box a, .laydate_body .laydate_box a:hover{text-decoration:none; blr:expression(this.onFocus=this.blur()); cursor:pointer;} -.laydate_body .laydate_box a:hover{text-decoration:none;} -.laydate_body .laydate_box cite, .laydate_body .laydate_box label{position:absolute; width:0; height:0; border-width:5px; border-style:dashed; border-color:transparent; overflow:hidden; cursor:pointer;} -.laydate_body .laydate_box .laydate_yms, .laydate_body .laydate_box .laydate_time{display:none;} -.laydate_body .laydate_box .laydate_show{display:block;} -.laydate_body .laydate_box input{outline:0; font-size:14px; background-color:#fff;} -.laydate_body .laydate_top{position:relative; height:35px; padding:5px; *width:100%; z-index:99;} -.laydate_body .laydate_ym{position:relative; float:left; height:24px; cursor:pointer;} -.laydate_body .laydate_ym input{float:left; height:24px; line-height:24px; text-align:center; border:none; cursor:pointer;} -.laydate_body .laydate_ym .laydate_yms{position:absolute; left: -1px; top: 24px; height:181px;} -.laydate_body .laydate_y{width:121px; margin-right:6px;} -.laydate_body .laydate_y input{width:64px; margin-right:15px;} -.laydate_body .laydate_y .laydate_yms{width:121px; text-align:center;} -.laydate_body .laydate_y .laydate_yms a{position:relative; display:block; height:20px;} -.laydate_body .laydate_y .laydate_yms ul{height:139px; padding:0; overflow:hidden;} -.laydate_body .laydate_y .laydate_yms ul li{float:left; width:58px; height:20px; line-height: 20px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;} -.laydate_body .laydate_m{width:99px;} -.laydate_body .laydate_m .laydate_yms{width:99px; padding:0;} -.laydate_body .laydate_m input{width:42px; margin-right:15px;} -.laydate_body .laydate_m .laydate_yms span{display:block; float:left; width:42px; margin: 5px 0 0 5px; line-height:24px; text-align:center; _display:inline;} -.laydate_body .laydate_choose{display:block; float:left; position:relative; width:20px; height:24px;} -.laydate_body .laydate_choose cite, .laydate_body .laydate_tab cite{left:50%; top:50%;} -.laydate_body .laydate_chtop cite{margin:-7px 0 0 -5px; border-bottom-style:solid;} -.laydate_body .laydate_chdown cite, .laydate_body .laydate_ym label{top:50%; margin:-2px 0 0 -5px; border-top-style:solid;} -.laydate_body .laydate_chprev cite{margin:-5px 0 0 -7px;} -.laydate_body .laydate_chnext cite{margin:-5px 0 0 -2px;} -.laydate_body .laydate_ym label{right:28px;} -.laydate_body .laydate_table{ width:230px; margin:0 5px; border-collapse:collapse; border-spacing:0px; } -.laydate_body .laydate_table td{width:31px; height:19px; line-height:19px; text-align: center; cursor:pointer; font-size: 12px;} -.laydate_body .laydate_table thead{height:22px; line-height:22px;} -.laydate_body .laydate_table thead th{font-weight:400; font-size:12px; text-align:center;} -.laydate_body .laydate_bottom{position:relative; height:35px; line-height:20px; padding:5px; font-size:12px;} -.laydate_body .laydate_bottom #laydate_hms{position: relative; z-index: 1; float:left; } -.laydate_body .laydate_time{ position:absolute; left:5px; bottom: 26px; width:129px; height:125px; *overflow:hidden;} -.laydate_body .laydate_time .laydate_hmsno{ padding:5px 0 0 5px;} -.laydate_body .laydate_time .laydate_hmsno span{display:block; float:left; width:24px; height:19px; line-height:19px; text-align:center; cursor:pointer; *margin-bottom:-5px;} -.laydate_body .laydate_time1{width:228px; height:154px;} -.laydate_body .laydate_time1 .laydate_hmsno{padding: 6px 0 0 8px;} -.laydate_body .laydate_time1 .laydate_hmsno span{width:21px; height:20px; line-height:20px;} -.laydate_body .laydate_msg{left:49px; bottom:67px; width:141px; height:auto; overflow: hidden;} -.laydate_body .laydate_msg p{padding:5px 10px;} -.laydate_body .laydate_bottom li{float:left; height:20px; line-height:20px; border-right:none; font-weight:900;} -.laydate_body .laydate_bottom .laydate_sj{width:33px; text-align:center; font-weight:400;} -.laydate_body .laydate_bottom input{float:left; width:21px; height:20px; line-height:20px; border:none; text-align:center; cursor:pointer; font-size:12px; font-weight:400;} -.laydate_body .laydate_bottom .laydte_hsmtex{height:20px; line-height:20px; text-align:center;} -.laydate_body .laydate_bottom .laydte_hsmtex span{position:absolute; width:20px; top:0; right:0px; cursor:pointer;} -.laydate_body .laydate_bottom .laydte_hsmtex span:hover{font-size:14px;} -.laydate_body .laydate_bottom .laydate_btn{position:absolute; right:5px; top:5px;} -.laydate_body .laydate_bottom .laydate_btn a{float:left; height:20px; padding:0 6px; _padding:0 5px;} -.laydate_body .laydate_bottom .laydate_v{position:absolute; left:10px; top:6px; font-family:Courier; z-index:0;} - diff --git a/static/plugs/layui/laydate/skins/dahong/icon.png b/static/plugs/layui/laydate/skins/dahong/icon.png deleted file mode 100644 index dfd870d91..000000000 Binary files a/static/plugs/layui/laydate/skins/dahong/icon.png and /dev/null differ diff --git a/static/plugs/layui/laydate/skins/dahong/laydate.css b/static/plugs/layui/laydate/skins/dahong/laydate.css deleted file mode 100644 index 6b2cee22c..000000000 --- a/static/plugs/layui/laydate/skins/dahong/laydate.css +++ /dev/null @@ -1,57 +0,0 @@ -/** - - @Name: laydate皮肤:大红 - @Author:贤心 - @Site:http://sentsin.com/layui/laydate - -**/ - -.laydate-icon{border:1px solid #ccc; background-image:url(icon.png)} - -.laydate_body .laydate_bottom #laydate_hms, -.laydate_body .laydate_time{border:1px solid #ccc;} - -.laydate_body .laydate_box, -.laydate_body .laydate_time{box-shadow: 2px 2px 5px rgba(0,0,0,.1);} - -.laydate_body .laydate_box{border-top:none; border-bottom:none; background-color:#fff; color:#333;} -.laydate_body .laydate_box input{background:none!important; color:#fff;} -.laydate_body .laydate_box .laydate_void{color:#ccc!important;} -.laydate_body .laydate_box a, .laydate_body .laydate_box a:hover{color:#333;} -.laydate_body .laydate_box a:hover{color:#666;} -.laydate_body .laydate_click{background-color:#F32043!important; color:#fff!important;} -.laydate_body .laydate_top{border-top:1px solid #D91600; background-color:#D91600} -.laydate_body .laydate_ym{border:1px solid #D91600; background-color:#D91600;} -.laydate_body .laydate_ym .laydate_yms{border:1px solid #D91600; background-color:#D91600; color:#fff;} -.laydate_body .laydate_y .laydate_yms a{border-bottom:1px solid #D91600;} -.laydate_body .laydate_y .laydate_yms .laydate_chdown{border-top:1px solid #D91600; border-bottom:none;} -.laydate_body .laydate_choose{border-left:1px solid #D91600;} -.laydate_body .laydate_chprev{border-left:none; border-right:1px solid #D91600;} -.laydate_body .laydate_choose:hover, -.laydate_body .laydate_y .laydate_yms a:hover{background-color:#F54766;} -.laydate_body .laydate_chtop cite{border-bottom-color:#fff;} -.laydate_body .laydate_chdown cite, .laydate_body .laydate_ym label{border-top-color:#fff;} -.laydate_body .laydate_chprev cite{border-right-style:solid; border-right-color:#fff;} -.laydate_body .laydate_chnext cite{border-left-style:solid; border-left-color:#fff;} -.laydate_body .laydate_table{width: 240px!important; margin: 0!important; border:1px solid #ccc; border-top:none; border-bottom:none;} -.laydate_body .laydate_table td{border:none; height:21px!important; line-height:21px!important; background-color:#fff; color:#333;} -.laydate_body .laydate_table .laydate_nothis{color:#999;} -.laydate_body .laydate_table thead{border-bottom:1px solid #ccc; height:21px!important; line-height:21px!important;} -.laydate_body .laydate_table thead th{} -.laydate_body .laydate_bottom{border:1px solid #ccc; border-top:none;} -.laydate_body .laydate_bottom #laydate_hms{background-color:#fff;} -.laydate_body .laydate_time{background-color:#fff;} -.laydate_body .laydate_time1{width: 226px!important; height: 152px!important;} -.laydate_body .laydate_bottom .laydate_sj{width:31px!important; border-right:1px solid #ccc; background-color:#fff;} -.laydate_body .laydate_bottom input{background-color:#fff; color:#333;} -.laydate_body .laydate_bottom .laydte_hsmtex{border-bottom:1px solid #ccc;} -.laydate_body .laydate_bottom .laydate_btn{border-right:1px solid #ccc;} -.laydate_body .laydate_bottom .laydate_v{color:#999} -.laydate_body .laydate_bottom .laydate_btn a{border: 1px solid #ccc; border-right:none; background-color:#fff;} -.laydate_body .laydate_bottom .laydate_btn a:hover{background-color:#F6F6F6; color:#333;} -.laydate_body .laydate_m .laydate_yms span:hover, -.laydate_body .laydate_time .laydate_hmsno span:hover, -.laydate_body .laydate_y .laydate_yms ul li:hover, -.laydate_body .laydate_table td:hover{background-color:#F54766; color:#fff;} - - diff --git a/static/plugs/layui/laydate/skins/default/icon.png b/static/plugs/layui/laydate/skins/default/icon.png deleted file mode 100644 index 5a50673e0..000000000 Binary files a/static/plugs/layui/laydate/skins/default/icon.png and /dev/null differ diff --git a/static/plugs/layui/laydate/skins/default/laydate.css b/static/plugs/layui/laydate/skins/default/laydate.css deleted file mode 100644 index f43f6cf59..000000000 --- a/static/plugs/layui/laydate/skins/default/laydate.css +++ /dev/null @@ -1,68 +0,0 @@ -/** - - @Name: laydate皮肤:默认 - @Author:贤心 - @Site:http://sentsin.com/layui/laydate - -**/ - -.laydate-icon{border:1px solid #C6C6C6; background-image:url(icon.png)} - -.laydate_body .laydate_box, -.laydate_body .laydate_ym, -.laydate_body .laydate_ym .laydate_yms, -.laydate_body .laydate_table, -.laydate_body .laydate_table td, -.laydate_body .laydate_bottom #laydate_hms, -.laydate_body .laydate_time, -.laydate_body .laydate_bottom .laydate_btn a{border:1px solid #ccc;} - -.laydate_body .laydate_y .laydate_yms a, -.laydate_body .laydate_choose, -.laydate_body .laydate_table thead, -.laydate_body .laydate_bottom .laydte_hsmtex{background-color:#F6F6F6;} - -.laydate_body .laydate_box, -.laydate_body .laydate_ym .laydate_yms, -.laydate_body .laydate_time{box-shadow: 2px 2px 5px rgba(0,0,0,.1);} - -.laydate_body .laydate_box{border-top:none; border-bottom:none; background-color:#fff; color:#333;} -.laydate_body .laydate_box input{color:#333;} -.laydate_body .laydate_box .laydate_void{color:#ccc!important; /*text-decoration:line-through;*/} -.laydate_body .laydate_box .laydate_void:hover{background-color:#fff!important} -.laydate_body .laydate_box a, .laydate_body .laydate_box a:hover{color:#333;} -.laydate_body .laydate_box a:hover{color:#666;} -.laydate_body .laydate_click{background-color:#eee!important;} -.laydate_body .laydate_top{border-top:1px solid #C6C6C6;} -.laydate_body .laydate_ym .laydate_yms{border:1px solid #C6C6C6; background-color:#fff;} -.laydate_body .laydate_y .laydate_yms a{border-bottom:1px solid #C6C6C6;} -.laydate_body .laydate_y .laydate_yms .laydate_chdown{border-top:1px solid #C6C6C6; border-bottom:none;} -.laydate_body .laydate_choose{border-left:1px solid #C6C6C6;} -.laydate_body .laydate_chprev{border-left:none; border-right:1px solid #C6C6C6;} -.laydate_body .laydate_choose:hover, -.laydate_body .laydate_y .laydate_yms a:hover{background-color:#fff;} -.laydate_body .laydate_chtop cite{border-bottom-color:#666;} -.laydate_body .laydate_chdown cite, .laydate_body .laydate_ym label{border-top-color:#666;} -.laydate_body .laydate_chprev cite{border-right-style:solid; border-right-color:#666;} -.laydate_body .laydate_chnext cite{border-left-style:solid; border-left-color:#666;} -.laydate_body .laydate_table td{border:none; height:21px!important; line-height:21px!important; background-color:#fff;} -.laydate_body .laydate_table .laydate_nothis{color:#999;} -.laydate_body .laydate_table thead{height:21px!important; line-height:21px!important;} -.laydate_body .laydate_table thead th{border-bottom:1px solid #ccc;} -.laydate_body .laydate_bottom{border-bottom:1px solid #C6C6C6;} -.laydate_body .laydate_bottom #laydate_hms{background-color:#fff;} -.laydate_body .laydate_time{background-color:#fff;} -.laydate_body .laydate_bottom .laydate_sj{border-right:1px solid #C6C6C6; background-color:#F6F6F6;} -.laydate_body .laydate_bottom input{background-color:#fff;} -.laydate_body .laydate_bottom .laydte_hsmtex{border-bottom:1px solid #C6C6C6;} -.laydate_body .laydate_bottom .laydate_btn{border-right:1px solid #C6C6C6;} -.laydate_body .laydate_bottom .laydate_v{color:#999} -.laydate_body .laydate_bottom .laydate_btn a{border-right:none; background-color:#F6F6F6;} -.laydate_body .laydate_bottom .laydate_btn a:hover{color:#000; background-color:#fff;} - -.laydate_body .laydate_m .laydate_yms span:hover, -.laydate_body .laydate_y .laydate_yms ul li:hover, -.laydate_body .laydate_table td:hover, -.laydate_body .laydate_time .laydate_hmsno span:hover{background-color:#F3F3F3} - - diff --git a/static/plugs/layui/laydate/skins/molv/icon.png b/static/plugs/layui/laydate/skins/molv/icon.png deleted file mode 100644 index 948660fb5..000000000 Binary files a/static/plugs/layui/laydate/skins/molv/icon.png and /dev/null differ diff --git a/static/plugs/layui/laydate/skins/molv/laydate.css b/static/plugs/layui/laydate/skins/molv/laydate.css deleted file mode 100644 index 22ae8ea68..000000000 --- a/static/plugs/layui/laydate/skins/molv/laydate.css +++ /dev/null @@ -1,59 +0,0 @@ -/** - - @Name: laydate皮肤:墨绿 - @Author:贤心 - @Site:http://sentsin.com/layui/laydate - -**/ - -.laydate-icon{border:1px solid #ccc; background-image:url(icon.png)} - -.laydate_body .laydate_bottom #laydate_hms, -.laydate_body .laydate_time{border:1px solid #ccc;} - -.laydate_body .laydate_box, -.laydate_body .laydate_ym .laydate_yms, -.laydate_body .laydate_time{box-shadow: 2px 2px 5px rgba(0,0,0,.1);} - -.laydate_body .laydate_box{border-top:none; border-bottom:none; background-color:#fff; color:#00625A;} -.laydate_body .laydate_box input{background:none!important; color:#fff;} -.laydate_body .laydate_box .laydate_void{color:#00E8D7!important;} -.laydate_body .laydate_box a, .laydate_body .laydate_box a:hover{color:#00625A;} -.laydate_body .laydate_box a:hover{color:#666;} -.laydate_body .laydate_click{background-color:#009F95!important; color:#fff!important;} -.laydate_body .laydate_top{border-top:1px solid #009F95; background-color:#009F95} -.laydate_body .laydate_ym{border:1px solid #009F95; background-color:#009F95;} -.laydate_body .laydate_ym .laydate_yms{border:1px solid #009F95; background-color:#009F95; color:#fff;} -.laydate_body .laydate_y .laydate_yms a{border-bottom:1px solid #009F95;} -.laydate_body .laydate_y .laydate_yms .laydate_chdown{border-top:1px solid #009F95; border-bottom:none;} -.laydate_body .laydate_choose{border-left:1px solid #009F95;} -.laydate_body .laydate_chprev{border-left:none; border-right:1px solid #009F95;} -.laydate_body .laydate_choose:hover, -.laydate_body .laydate_y .laydate_yms a:hover{background-color:#00C1B3;} -.laydate_body .laydate_chtop cite{border-bottom-color:#fff;} -.laydate_body .laydate_chdown cite, .laydate_body .laydate_ym label{border-top-color:#fff;} -.laydate_body .laydate_chprev cite{border-right-style:solid; border-right-color:#fff;} -.laydate_body .laydate_chnext cite{border-left-style:solid; border-left-color:#fff;} -.laydate_body .laydate_table{width: 240px!important; margin: 0!important; border:1px solid #ccc; border-top:none; border-bottom:none;} -.laydate_body .laydate_table td{border:none; height:21px!important; line-height:21px!important; background-color:#fff; color:#00625A;} -.laydate_body .laydate_table .laydate_nothis{color:#999;} -.laydate_body .laydate_table thead{border-bottom:1px solid #ccc; height:21px!important; line-height:21px!important;} -.laydate_body .laydate_table thead th{} -.laydate_body .laydate_bottom{border:1px solid #ccc; border-top:none;} -.laydate_body .laydate_bottom #laydate_hms{background-color:#fff;} -.laydate_body .laydate_time{background-color:#fff;} -.laydate_body .laydate_time1{width: 226px!important; height: 152px!important;} -.laydate_body .laydate_bottom .laydate_sj{width:31px!important; border-right:1px solid #ccc; background-color:#fff;} -.laydate_body .laydate_bottom input{background-color:#fff; color:#00625A;} -.laydate_body .laydate_bottom .laydte_hsmtex{border-bottom:1px solid #ccc;} -.laydate_body .laydate_bottom .laydate_btn{border-right:1px solid #ccc;} -.laydate_body .laydate_bottom .laydate_v{color:#999} -.laydate_body .laydate_bottom .laydate_btn a{border: 1px solid #ccc; border-right:none; background-color:#fff;} -.laydate_body .laydate_bottom .laydate_btn a:hover{background-color:#F6F6F6; color:#00625A;} - -.laydate_body .laydate_m .laydate_yms span:hover, -.laydate_body .laydate_time .laydate_hmsno span:hover, -.laydate_body .laydate_y .laydate_yms ul li:hover, -.laydate_body .laydate_table td:hover{background-color:#00C1B3; color:#fff;} - - diff --git a/static/plugs/layui/layui.all.js b/static/plugs/layui/layui.all.js new file mode 100644 index 000000000..e2a7e0109 --- /dev/null +++ b/static/plugs/layui/layui.all.js @@ -0,0 +1,5 @@ +/** layui-v2.3.0 MIT License By https://www.layui.com */ + ;!function(e){"use strict";var t=document,n={modules:{},status:{},timeout:10,event:{}},o=function(){this.v="2.3.0"},r=function(){var e=t.currentScript?t.currentScript.src:function(){for(var e,n=t.scripts,o=n.length-1,r=o;r>0;r--)if("interactive"===n[r].readyState){e=n[r].src;break}return e||n[o].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),a=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},i="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",rate:"modules/rate",carousel:"modules/carousel",flow:"modules/flow",util:"modules/util",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};o.prototype.cache=n,o.prototype.define=function(e,t){var o=this,r="function"==typeof e,a=function(){var e=function(e,t){layui[e]=t,n.status[e]=!0};return"function"==typeof t&&t(function(o,r){e(o,r),n.callback[o]=function(){t(e)}}),this};return r&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?a.call(o):(o.use(e,a),o)},o.prototype.use=function(e,o,l){function s(e,t){var o="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||o.test((e.currentTarget||e.srcElement).readyState))&&(n.modules[d]=t,f.removeChild(v),function r(){return++m>1e3*n.timeout/4?a(d+" is not a valid module"):void(n.status[d]?c():setTimeout(r,4))}())}function c(){l.push(layui[d]),e.length>1?y.use(e.slice(1),o,l):"function"==typeof o&&o.apply(layui,l)}var y=this,p=n.dir=n.dir?n.dir:r,f=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,n){"jquery"===n&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var d=e[0],m=0;if(l=l||[],n.host=n.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[d]||!layui["layui.all"]&&layui["layui.mobile"]&&u[d])return c(),y;if(n.modules[d])!function g(){return++m>1e3*n.timeout/4?a(d+" is not a valid module"):void("string"==typeof n.modules[d]&&n.status[d]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[d]?p+"lay/":/^\{\/\}/.test(y.modules[d])?"":n.base||"")+(y.modules[d]||d)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=n.version===!0?n.v||(new Date).getTime():n.version||"";return e?"?v="+e:""}(),f.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||i?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),n.modules[d]=h}return y},o.prototype.getStyle=function(t,n){var o=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return o[o.getPropertyValue?"getPropertyValue":"getAttribute"](n)},o.prototype.link=function(e,o,r){var i=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof o&&(r=o);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(n.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof o?i:(function p(){return++y>1e3*n.timeout/100?a(e+" timeout"):void(1989===parseInt(i.getStyle(t.getElementById(c),"width"))?function(){o()}():setTimeout(p,100))}(),i)},n.callback={},o.prototype.factory=function(e){if(layui[e])return"function"==typeof n.callback[e]?n.callback[e]:null},o.prototype.addcss=function(e,t,o){return layui.link(n.dir+"css/"+e,t,o)},o.prototype.img=function(e,t,n){var o=new Image;return o.src=e,o.complete?t(o):(o.onload=function(){o.onload=null,"function"==typeof t&&t(o)},void(o.onerror=function(e){o.onerror=null,"function"==typeof n&&n(e)}))},o.prototype.config=function(e){e=e||{};for(var t in e)n[t]=e[t];return this},o.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),o.prototype.extend=function(e){var t=this;e=e||{};for(var n in e)t[n]||t.modules[n]?a("模块名 "+n+" 已被占用"):t.modules[n]=e[n];return t},o.prototype.router=function(e){var t=this,e=e||location.hash,n={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),n.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),n.search[t[0]]=t[1]}():n.path.push(t)}),n):n},o.prototype.data=function(t,n,o){if(t=t||"layui",o=o||localStorage,e.JSON&&e.JSON.parse){if(null===n)return delete o[t];n="object"==typeof n?n:{key:n};try{var r=JSON.parse(o[t])}catch(a){var r={}}return"value"in n&&(r[n.key]=n.value),n.remove&&delete r[n.key],o[t]=JSON.stringify(r),n.key?r[n.key]:r}},o.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},o.prototype.device=function(t){var n=navigator.userAgent.toLowerCase(),o=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(n.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(n)?"windows":/linux/.test(n)?"linux":/iphone|ipod|ipad|ios/.test(n)?"ios":/mac/.test(n)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((n.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:o("micromessenger")};return t&&!r[t]&&(r[t]=o(t)),r.android=/android/.test(n),r.ios="ios"===r.os,r},o.prototype.hint=function(){return{error:a}},o.prototype.each=function(e,t){var n,o=this;if("function"!=typeof t)return o;if(e=e||[],e.constructor===Object){for(n in e)if(t.call(e[n],n,e[n]))break}else for(n=0;na?1:r/g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});layui.define(function(e){"use strict";var a=document,t="getElementById",n="getElementsByTagName",i="laypage",r="layui-disabled",u=function(e){var a=this;a.config=e||{},a.config.index=++s.index,a.render(!0)};u.prototype.type=function(){var e=this.config;if("object"==typeof e.elem)return void 0===e.elem.length?2:3},u.prototype.view=function(){var e=this,a=e.config,t=a.groups="groups"in a?0|a.groups:5;a.layout="object"==typeof a.layout?a.layout:["prev","page","next"],a.count=0|a.count,a.curr=0|a.curr||1,a.limits="object"==typeof a.limits?a.limits:[10,20,30,40,50],a.limit=0|a.limit||10,a.pages=Math.ceil(a.count/a.limit)||1,a.curr>a.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+"":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+"");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('');r<=u;r++)r===a.curr?e.push('"+r+""):e.push(''+r+"");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+"")),e.join("")}(),next:function(){return a.next?''+a.next+"":""}(),count:'共 '+a.count+" 条",limit:function(){var e=['"}(),refresh:['','',""].join(""),skip:function(){return['到第','','页',""].join("")}()};return['
                ',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
                "].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)});!function(){"use strict";var e=window.layui&&layui.define,t={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,n=t.length-1,a=n;a>0;a--)if("interactive"===t[a].readyState){e=t[a].src;break}return e||t[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),getStyle:function(e,t){var n=e.currentStyle?e.currentStyle:window.getComputedStyle(e,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](t)},link:function(e,a,i){if(n.path){var r=document.getElementsByTagName("head")[0],o=document.createElement("link");"string"==typeof a&&(i=a);var s=(i||e).replace(/\.|\//g,""),l="layuicss-"+s,d=0;o.rel="stylesheet",o.href=n.path+e,o.id=l,document.getElementById(l)||r.appendChild(o),"function"==typeof a&&!function c(){return++d>80?window.console&&console.error("laydate.css: Invalid"):void(1989===parseInt(t.getStyle(document.getElementById(l),"width"))?a():setTimeout(c,100))}()}}},n={v:"5.0.9",config:{},index:window.laydate&&window.laydate.v?1e5:0,path:t.getPath,set:function(e){var t=this;return t.config=w.extend({},t.config,e),t},ready:function(a){var i="laydate",r="",o=(e?"modules/laydate/":"theme/")+"default/laydate.css?v="+n.v+r;return e?layui.addcss(o,a,i):t.link(o,a,i),this}},a=function(){var e=this;return{hint:function(t){e.hint.call(e,t)},config:e.config}},i="laydate",r=".layui-laydate",o="layui-this",s="laydate-disabled",l="开始日期超出了结束日期
                建议重新选择",d=[100,2e5],c="layui-laydate-static",m="layui-laydate-list",u="laydate-selected",h="layui-laydate-hint",y="laydate-day-prev",f="laydate-day-next",p="layui-laydate-footer",g=".laydate-btns-confirm",v="laydate-time-text",D=".laydate-btns-time",T=function(e){var t=this;t.index=++n.index,t.config=w.extend({},t.config,n.config,e),n.ready(function(){t.init()})},w=function(e){return new C(e)},C=function(e){for(var t=0,n="object"==typeof e?[e]:(this.selector=e,document.querySelectorAll(e||null));t0)return n[0].getAttribute(e)}():n.each(function(n,a){a.setAttribute(e,t)})},C.prototype.removeAttr=function(e){return this.each(function(t,n){n.removeAttribute(e)})},C.prototype.html=function(e){return this.each(function(t,n){n.innerHTML=e})},C.prototype.val=function(e){return this.each(function(t,n){n.value=e})},C.prototype.append=function(e){return this.each(function(t,n){"object"==typeof e?n.appendChild(e):n.innerHTML=n.innerHTML+e})},C.prototype.remove=function(e){return this.each(function(t,n){e?n.removeChild(e):n.parentNode.removeChild(n)})},C.prototype.on=function(e,t){return this.each(function(n,a){a.attachEvent?a.attachEvent("on"+e,function(e){e.target=e.srcElement,t.call(a,e)}):a.addEventListener(e,t,!1)})},C.prototype.off=function(e,t){return this.each(function(n,a){a.detachEvent?a.detachEvent("on"+e,t):a.removeEventListener(e,t,!1)})},T.isLeapYear=function(e){return e%4===0&&e%100!==0||e%400===0},T.prototype.config={type:"date",range:!1,format:"yyyy-MM-dd",value:null,isInitValue:!0,min:"1900-1-1",max:"2099-12-31",trigger:"focus",show:!1,showBottom:!0,btns:["clear","now","confirm"],lang:"cn",theme:"default",position:null,calendar:!1,mark:{},zIndex:null,done:null,change:null},T.prototype.lang=function(){var e=this,t=e.config,n={cn:{weeks:["日","一","二","三","四","五","六"],time:["时","分","秒"],timeTips:"选择时间",startTime:"开始时间",endTime:"结束时间",dateTips:"返回日期",month:["一","二","三","四","五","六","七","八","九","十","十一","十二"],tools:{confirm:"确定",clear:"清空",now:"现在"}},en:{weeks:["Su","Mo","Tu","We","Th","Fr","Sa"],time:["Hours","Minutes","Seconds"],timeTips:"Select Time",startTime:"Start Time",endTime:"End Time",dateTips:"Select Date",month:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],tools:{confirm:"Confirm",clear:"Clear",now:"Now"}}};return n[t.lang]||n.cn},T.prototype.init=function(){var e=this,t=e.config,n="yyyy|y|MM|M|dd|d|HH|H|mm|m|ss|s",a="static"===t.position,i={year:"yyyy",month:"yyyy-MM",date:"yyyy-MM-dd",time:"HH:mm:ss",datetime:"yyyy-MM-dd HH:mm:ss"};t.elem=w(t.elem),t.eventElem=w(t.eventElem),t.elem[0]&&(t.range===!0&&(t.range="-"),t.format===i.date&&(t.format=i[t.type]),e.format=t.format.match(new RegExp(n+"|.","g"))||[],e.EXP_IF="",e.EXP_SPLIT="",w.each(e.format,function(t,a){var i=new RegExp(n).test(a)?"\\d{"+function(){return new RegExp(n).test(e.format[0===t?t+1:t-1]||"")?/^yyyy|y$/.test(a)?4:a.length:/^yyyy$/.test(a)?"1,4":/^y$/.test(a)?"1,308":"1,2"}()+"}":"\\"+a;e.EXP_IF=e.EXP_IF+i,e.EXP_SPLIT=e.EXP_SPLIT+"("+i+")"}),e.EXP_IF=new RegExp("^"+(t.range?e.EXP_IF+"\\s\\"+t.range+"\\s"+e.EXP_IF:e.EXP_IF)+"$"),e.EXP_SPLIT=new RegExp("^"+e.EXP_SPLIT+"$",""),e.isInput(t.elem[0])||"focus"===t.trigger&&(t.trigger="click"),t.elem.attr("lay-key")||(t.elem.attr("lay-key",e.index),t.eventElem.attr("lay-key",e.index)),t.mark=w.extend({},t.calendar&&"cn"===t.lang?{"0-1-1":"元旦","0-2-14":"情人","0-3-8":"妇女","0-3-12":"植树","0-4-1":"愚人","0-5-1":"劳动","0-5-4":"青年","0-6-1":"儿童","0-9-10":"教师","0-9-18":"国耻","0-10-1":"国庆","0-12-25":"圣诞"}:{},t.mark),w.each(["min","max"],function(e,n){var a=[],i=[];if("number"==typeof t[n]){var r=t[n],o=(new Date).getTime(),s=864e5,l=new Date(r?r0)return!0;var a=w.elem("div",{"class":"layui-laydate-header"}),i=[function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-y"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-prev-m"});return e.innerHTML="",e}(),function(){var e=w.elem("div",{"class":"laydate-set-ym"}),t=w.elem("span"),n=w.elem("span");return e.appendChild(t),e.appendChild(n),e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-m"});return e.innerHTML="",e}(),function(){var e=w.elem("i",{"class":"layui-icon laydate-icon laydate-next-y"});return e.innerHTML="",e}()],d=w.elem("div",{"class":"layui-laydate-content"}),c=w.elem("table"),m=w.elem("thead"),u=w.elem("tr");w.each(i,function(e,t){a.appendChild(t)}),m.appendChild(u),w.each(new Array(6),function(e){var t=c.insertRow(0);w.each(new Array(7),function(a){if(0===e){var i=w.elem("th");i.innerHTML=n.weeks[a],u.appendChild(i)}t.insertCell(a)})}),c.insertBefore(m,c.children[0]),d.appendChild(c),r[e]=w.elem("div",{"class":"layui-laydate-main laydate-main-list-"+e}),r[e].appendChild(a),r[e].appendChild(d),o.push(i),s.push(d),l.push(c)}),w(d).html(function(){var e=[],i=[];return"datetime"===t.type&&e.push(''+n.timeTips+""),w.each(t.btns,function(e,r){var o=n.tools[r]||"btn";t.range&&"now"===r||(a&&"clear"===r&&(o="cn"===t.lang?"重置":"Reset"),i.push(''+o+""))}),e.push('"),e.join("")}()),w.each(r,function(e,t){i.appendChild(t)}),t.showBottom&&i.appendChild(d),/^#/.test(t.theme)){var m=w.elem("style"),u=["#{{id}} .layui-laydate-header{background-color:{{theme}};}","#{{id}} .layui-this{background-color:{{theme}} !important;}"].join("").replace(/{{id}}/g,e.elemID).replace(/{{theme}}/g,t.theme);"styleSheet"in m?(m.setAttribute("type","text/css"),m.styleSheet.cssText=u):m.innerHTML=u,w(i).addClass("laydate-theme-molv"),i.appendChild(m)}e.remove(T.thisElemDate),a?t.elem.append(i):(document.body.appendChild(i),e.position()),e.checkDate().calendar(),e.changeEvent(),T.thisElemDate=e.elemID,"function"==typeof t.ready&&t.ready(w.extend({},t.dateTime,{month:t.dateTime.month+1}))},T.prototype.remove=function(e){var t=this,n=(t.config,w("#"+(e||t.elemID)));return n.hasClass(c)||t.checkDate(function(){n.remove()}),t},T.prototype.position=function(){var e=this,t=e.config,n=e.bindElem||t.elem[0],a=n.getBoundingClientRect(),i=e.elem.offsetWidth,r=e.elem.offsetHeight,o=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},s=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},l=5,d=a.left,c=a.bottom;d+i+l>s("width")&&(d=s("width")-i-l),c+r+l>s()&&(c=a.top>r?a.top-r:s()-r,c-=2*l),t.position&&(e.elem.style.position=t.position),e.elem.style.left=d+("fixed"===t.position?0:o(1))+"px",e.elem.style.top=c+("fixed"===t.position?0:o())+"px"},T.prototype.hint=function(e){var t=this,n=(t.config,w.elem("div",{"class":h}));n.innerHTML=e||"",w(t.elem).find("."+h).remove(),t.elem.appendChild(n),clearTimeout(t.hinTimer),t.hinTimer=setTimeout(function(){w(t.elem).find("."+h).remove()},3e3)},T.prototype.getAsYM=function(e,t,n){return n?t--:t++,t<0&&(t=11,e--),t>11&&(t=0,e++),[e,t]},T.prototype.systemDate=function(e){var t=e||new Date;return{year:t.getFullYear(),month:t.getMonth(),date:t.getDate(),hours:e?e.getHours():0,minutes:e?e.getMinutes():0,seconds:e?e.getSeconds():0}},T.prototype.checkDate=function(e){var t,a,i=this,r=(new Date,i.config),o=r.dateTime=r.dateTime||i.systemDate(),s=i.bindElem||r.elem[0],l=(i.isInput(s)?"val":"html",i.isInput(s)?s.value:"static"===r.position?"":s.innerHTML),c=function(e){e.year>d[1]&&(e.year=d[1],a=!0),e.month>11&&(e.month=11,a=!0),e.hours>23&&(e.hours=0,a=!0),e.minutes>59&&(e.minutes=0,e.hours++,a=!0),e.seconds>59&&(e.seconds=0,e.minutes++,a=!0),t=n.getEndDate(e.month+1,e.year),e.date>t&&(e.date=t,a=!0)},m=function(e,t,n){var o=["startTime","endTime"];t=(t.match(i.EXP_SPLIT)||[]).slice(1),n=n||0,r.range&&(i[o[n]]=i[o[n]]||{}),w.each(i.format,function(s,l){var c=parseFloat(t[s]);t[s].length必须遵循下述格式:
                "+(r.range?r.format+" "+r.range+" "+r.format:r.format)+"
                已为你重置"),a=!0):l&&l.constructor===Date?r.dateTime=i.systemDate(l):(r.dateTime=i.systemDate(),delete i.startState,delete i.endState,delete i.startDate,delete i.endDate,delete i.startTime,delete i.endTime),c(o),a&&l&&i.setValue(r.range?i.endDate?i.parse():"":i.parse()),e&&e(),i)},T.prototype.mark=function(e,t){var n,a=this,i=a.config;return w.each(i.mark,function(e,a){var i=e.split("-");i[0]!=t[0]&&0!=i[0]||i[1]!=t[1]&&0!=i[1]||i[2]!=t[2]||(n=a||t[2])}),n&&e.html(''+n+""),a},T.prototype.limit=function(e,t,n,a){var i,r=this,o=r.config,l={},d=o[n>41?"endDate":"dateTime"],c=w.extend({},d,t||{});return w.each({now:c,min:o.min,max:o.max},function(e,t){l[e]=r.newDate(w.extend({year:t.year,month:t.month,date:t.date},function(){var e={};return w.each(a,function(n,a){e[a]=t[a]}),e}())).getTime()}),i=l.nowl.max,e&&e[i?"addClass":"removeClass"](s),i},T.prototype.calendar=function(e){var t,a,i,r=this,s=r.config,l=e||s.dateTime,c=new Date,m=r.lang(),u="date"!==s.type&&"datetime"!==s.type,h=e?1:0,y=w(r.table[h]).find("td"),f=w(r.elemHeader[h][2]).find("span");if(l.yeard[1]&&(l.year=d[1],r.hint("最高只能支持到公元"+d[1]+"年")),r.firstDate||(r.firstDate=w.extend({},l)),c.setFullYear(l.year,l.month,1),t=c.getDay(),a=n.getEndDate(l.month||12,l.year),i=n.getEndDate(l.month+1,l.year),w.each(y,function(e,n){var d=[l.year,l.month],c=0;n=w(n),n.removeAttr("class"),e=t&&e=n.firstDate.year&&(r.month=a.max.month,r.date=a.max.date),n.limit(w(i),r,t),M++}),w(u[f?0:1]).attr("lay-ym",M-8+"-"+T[1]).html(b+p+" - "+(M-1+p))}else if("month"===e)w.each(new Array(12),function(e){var i=w.elem("li",{"lay-ym":e}),s={year:T[0],month:e};e+1==T[1]&&w(i).addClass(o),i.innerHTML=r.month[e]+(f?"月":""),d.appendChild(i),T[0]=n.firstDate.year&&(s.date=a.max.date),n.limit(w(i),s,t)}),w(u[f?0:1]).attr("lay-ym",T[0]+"-"+T[1]).html(T[0]+p);else if("time"===e){var E=function(){w(d).find("ol").each(function(e,a){w(a).find("li").each(function(a,i){n.limit(w(i),[{hours:a},{hours:n[x].hours,minutes:a},{hours:n[x].hours,minutes:n[x].minutes,seconds:a}][e],t,[["hours"],["hours","minutes"],["hours","minutes","seconds"]][e])})}),a.range||n.limit(w(n.footer).find(g),n[x],0,["hours","minutes","seconds"])};a.range?n[x]||(n[x]={hours:0,minutes:0,seconds:0}):n[x]=i,w.each([24,60,60],function(e,t){var a=w.elem("li"),i=["

                "+r.time[e]+"

                  "];w.each(new Array(t),function(t){i.push(""+w.digit(t,2)+"")}),a.innerHTML=i.join("")+"
                ",d.appendChild(a)}),E()}if(y&&h.removeChild(y),h.appendChild(d),"year"===e||"month"===e)w(n.elemMain[t]).addClass("laydate-ym-show"),w(d).find("li").on("click",function(){var r=0|w(this).attr("lay-ym");if(!w(this).hasClass(s)){if(0===t)i[e]=r,l&&(n.startDate[e]=r),n.limit(w(n.footer).find(g),null,0);else if(l)n.endDate[e]=r;else{var c="year"===e?n.getAsYM(r,T[1]-1,"sub"):n.getAsYM(T[0],r,"sub");w.extend(i,{year:c[0],month:c[1]})}"year"===a.type||"month"===a.type?(w(d).find("."+o).removeClass(o),w(this).addClass(o),"month"===a.type&&"year"===e&&(n.listYM[t][0]=r,l&&(n[["startDate","endDate"][t]].year=r),n.list("month",t))):(n.checkDate("limit").calendar(),n.closeList()),n.setBtnStatus(),a.range||n.done(null,"change"),w(n.footer).find(D).removeClass(s)}});else{var S=w.elem("span",{"class":v}),k=function(){w(d).find("ol").each(function(e){var t=this,a=w(t).find("li");t.scrollTop=30*(n[x][C[e]]-2),t.scrollTop<=0&&a.each(function(e,n){if(!w(this).hasClass(s))return t.scrollTop=30*(e-2),!0})})},H=w(c[2]).find("."+v);k(),S.innerHTML=a.range?[r.startTime,r.endTime][t]:r.timeTips,w(n.elemMain[t]).addClass("laydate-time-show"),H[0]&&H.remove(),c[2].appendChild(S),w(d).find("ol").each(function(e){var t=this;w(t).find("li").on("click",function(){var r=0|this.innerHTML;w(this).hasClass(s)||(a.range?n[x][C[e]]=r:i[C[e]]=r,w(t).find("."+o).removeClass(o),w(this).addClass(o),E(),k(),(n.endDate||"time"===a.type)&&n.done(null,"change"),n.setBtnStatus())})})}return n},T.prototype.listYM=[],T.prototype.closeList=function(){var e=this;e.config;w.each(e.elemCont,function(t,n){w(this).find("."+m).remove(),w(e.elemMain[t]).removeClass("laydate-ym-show laydate-time-show")}),w(e.elem).find("."+v).remove()},T.prototype.setBtnStatus=function(e,t,n){var a,i=this,r=i.config,o=w(i.footer).find(g),d=r.range&&"date"!==r.type&&"time"!==r.type;d&&(t=t||i.startDate,n=n||i.endDate,a=i.newDate(t).getTime()>i.newDate(n).getTime(),i.limit(null,t)||i.limit(null,n)?o.addClass(s):o[a?"addClass":"removeClass"](s),e&&a&&i.hint("string"==typeof e?l.replace(/日期/g,e):l))},T.prototype.parse=function(e,t){var n=this,a=n.config,i=t||(e?w.extend({},n.endDate,n.endTime):a.range?w.extend({},n.startDate,n.startTime):a.dateTime),r=n.format.concat();return w.each(r,function(e,t){/yyyy|y/.test(t)?r[e]=w.digit(i.year,t.length):/MM|M/.test(t)?r[e]=w.digit(i.month+1,t.length):/dd|d/.test(t)?r[e]=w.digit(i.date,t.length):/HH|H/.test(t)?r[e]=w.digit(i.hours,t.length):/mm|m/.test(t)?r[e]=w.digit(i.minutes,t.length):/ss|s/.test(t)&&(r[e]=w.digit(i.seconds,t.length))}),a.range&&!e?r.join("")+" "+a.range+" "+n.parse(1):r.join("")},T.prototype.newDate=function(e){return e=e||{},new Date(e.year||1,e.month||0,e.date||1,e.hours||0,e.minutes||0,e.seconds||0)},T.prototype.setValue=function(e){var t=this,n=t.config,a=t.bindElem||n.elem[0],i=t.isInput(a)?"val":"html";return"static"===n.position||w(a)[i](e||""),this},T.prototype.stampRange=function(){var e,t,n=this,a=n.config,i=w(n.elem).find("td");if(a.range&&!n.endDate&&w(n.footer).find(g).addClass(s),n.endDate)return e=n.newDate({year:n.startDate.year,month:n.startDate.month,date:n.startDate.date}).getTime(),t=n.newDate({year:n.endDate.year,month:n.endDate.month,date:n.endDate.date}).getTime(),e>t?n.hint(l):void w.each(i,function(a,i){var r=w(i).attr("lay-ymd").split("-"),s=n.newDate({year:r[0],month:r[1]-1,date:r[2]}).getTime();w(i).removeClass(u+" "+o),s!==e&&s!==t||w(i).addClass(w(i).hasClass(y)||w(i).hasClass(f)?u:o),s>e&&s0&&t-1 in e)}function r(e,t,n){if(pe.isFunction(t))return pe.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return pe.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(Ce.test(t))return pe.filter(t,e,n);t=pe.filter(t,e)}return pe.grep(e,function(e){return pe.inArray(e,t)>-1!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t={};return pe.each(e.match(De)||[],function(e,n){t[n]=!0}),t}function a(){re.addEventListener?(re.removeEventListener("DOMContentLoaded",s),e.removeEventListener("load",s)):(re.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(re.addEventListener||"load"===e.event.type||"complete"===re.readyState)&&(a(),pe.ready())}function u(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(_e,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n||"false"!==n&&("null"===n?null:+n+""===n?+n:qe.test(n)?pe.parseJSON(n):n)}catch(i){}pe.data(e,t,n)}else n=void 0}return n}function l(e){var t;for(t in e)if(("data"!==t||!pe.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(He(e)){var i,o,a=pe.expando,s=e.nodeType,u=s?pe.cache:e,l=s?e[a]:e[a]&&a;if(l&&u[l]&&(r||u[l].data)||void 0!==n||"string"!=typeof t)return l||(l=s?e[a]=ne.pop()||pe.guid++:a),u[l]||(u[l]=s?{}:{toJSON:pe.noop}),"object"!=typeof t&&"function"!=typeof t||(r?u[l]=pe.extend(u[l],t):u[l].data=pe.extend(u[l].data,t)),o=u[l],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[pe.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[pe.camelCase(t)])):i=o,i}}function f(e,t,n){if(He(e)){var r,i,o=e.nodeType,a=o?pe.cache:e,s=o?e[pe.expando]:pe.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){pe.isArray(t)?t=t.concat(pe.map(t,pe.camelCase)):t in r?t=[t]:(t=pe.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!l(r):!pe.isEmptyObject(r))return}(n||(delete a[s].data,l(a[s])))&&(o?pe.cleanData([e],!0):fe.deleteExpando||a!=a.window?delete a[s]:a[s]=void 0)}}}function d(e,t,n,r){var i,o=1,a=20,s=r?function(){return r.cur()}:function(){return pe.css(e,t,"")},u=s(),l=n&&n[3]||(pe.cssNumber[t]?"":"px"),c=(pe.cssNumber[t]||"px"!==l&&+u)&&Me.exec(pe.css(e,t));if(c&&c[3]!==l){l=l||c[3],n=n||[],c=+u||1;do o=o||".5",c/=o,pe.style(e,t,c+l);while(o!==(o=s()/u)&&1!==o&&--a)}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}function p(e){var t=ze.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function h(e,t){var n,r,i=0,o="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||pe.nodeName(r,t)?o.push(r):pe.merge(o,h(r,t));return void 0===t||t&&pe.nodeName(e,t)?pe.merge([e],o):o}function g(e,t){for(var n,r=0;null!=(n=e[r]);r++)pe._data(n,"globalEval",!t||pe._data(t[r],"globalEval"))}function m(e){Be.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t,n,r,i){for(var o,a,s,u,l,c,f,d=e.length,y=p(t),v=[],x=0;x"!==f[1]||Ve.test(a)?0:u:u.firstChild,o=a&&a.childNodes.length;o--;)pe.nodeName(c=a.childNodes[o],"tbody")&&!c.childNodes.length&&a.removeChild(c);for(pe.merge(v,u.childNodes),u.textContent="";u.firstChild;)u.removeChild(u.firstChild);u=y.lastChild}else v.push(t.createTextNode(a));for(u&&y.removeChild(u),fe.appendChecked||pe.grep(h(v,"input"),m),x=0;a=v[x++];)if(r&&pe.inArray(a,r)>-1)i&&i.push(a);else if(s=pe.contains(a.ownerDocument,a),u=h(y.appendChild(a),"script"),s&&g(u),n)for(o=0;a=u[o++];)Ie.test(a.type||"")&&n.push(a);return u=null,y}function v(){return!0}function x(){return!1}function b(){try{return re.activeElement}catch(e){}}function w(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)w(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),i===!1)i=x;else if(!i)return e;return 1===o&&(a=i,i=function(e){return pe().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=pe.guid++)),e.each(function(){pe.event.add(this,t,i,r,n)})}function T(e,t){return pe.nodeName(e,"table")&&pe.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function C(e){return e.type=(null!==pe.find.attr(e,"type"))+"/"+e.type,e}function E(e){var t=it.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function N(e,t){if(1===t.nodeType&&pe.hasData(e)){var n,r,i,o=pe._data(e),a=pe._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;r1&&"string"==typeof p&&!fe.checkClone&&rt.test(p))return e.each(function(i){var o=e.eq(i);g&&(t[0]=p.call(this,i,o.html())),S(o,t,n,r)});if(f&&(l=y(t,e[0].ownerDocument,!1,e,r),i=l.firstChild,1===l.childNodes.length&&(l=i),i||r)){for(s=pe.map(h(l,"script"),C),a=s.length;c")).appendTo(t.documentElement),t=(ut[0].contentWindow||ut[0].contentDocument).document,t.write(),t.close(),n=D(e,t),ut.detach()),lt[e]=n),n}function L(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function H(e){if(e in Et)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),n=Ct.length;n--;)if(e=Ct[n]+t,e in Et)return e}function q(e,t){for(var n,r,i,o=[],a=0,s=e.length;a=0&&n=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==pe.type(e)||e.nodeType||pe.isWindow(e))return!1;try{if(e.constructor&&!ce.call(e,"constructor")&&!ce.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(!fe.ownFirst)for(t in e)return ce.call(e,t);for(t in e);return void 0===t||ce.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?ue[le.call(e)]||"object":typeof e},globalEval:function(t){t&&pe.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ge,"ms-").replace(me,ye)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t){var r,i=0;if(n(e))for(r=e.length;iT.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[P]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=n.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||V)-(~e.sourceIndex||V);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function f(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var i=0,o=n.length;i-1&&(r[l]=!(a[l]=f))}}else x=m(x===a?x.splice(h,x.length):x),o?o(null,a,x,u):Q.apply(a,x)})}function v(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,u=p(function(e){return e===t},a,!0),l=p(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==A)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,i}];s1&&h(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,o=e.length>0,a=function(r,a,s,u,l){var c,f,d,p=0,h="0",g=r&&[],y=[],v=A,x=r||o&&T.find.TAG("*",l),b=W+=null==v?1:Math.random()||.1,w=x.length;for(l&&(A=a===H||a||l);h!==w&&null!=(c=x[h]);h++){if(o&&c){for(f=0,a||c.ownerDocument===H||(L(c),s=!_);d=e[f++];)if(d(c,a||H,s)){u.push(c);break}l&&(W=b)}i&&((c=!d&&c)&&p--,r&&g.push(c))}if(p+=h,i&&h!==p){for(f=0;d=n[f++];)d(g,y,a,s);if(r){if(p>0)for(;h--;)g[h]||y[h]||(y[h]=G.call(u));y=m(y)}Q.apply(u,y),l&&!r&&y.length>0&&p+n.length>1&&t.uniqueSort(u)}return l&&(W=b,A=v),g};return i?r(a):a}var b,w,T,C,E,N,k,S,A,D,j,L,H,q,_,F,M,O,R,P="sizzle"+1*new Date,B=e.document,W=0,I=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(j=!0),0},V=1<<31,Y={}.hasOwnProperty,J=[],G=J.pop,K=J.push,Q=J.push,Z=J.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(oe),de=new RegExp("^"+re+"$"),pe={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ge=/^h\d$/i,me=/^[^{]+\{\s*\[native \w/,ye=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,xe=/'|\\/g,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),we=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Te=function(){L()};try{Q.apply(J=Z.call(B.childNodes),B.childNodes),J[B.childNodes.length].nodeType}catch(Ce){Q={apply:J.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},L=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:B;return r!==H&&9===r.nodeType&&r.documentElement?(H=r,q=H.documentElement,_=!E(H),(n=H.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Te,!1):n.attachEvent&&n.attachEvent("onunload",Te)),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(H.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=me.test(H.getElementsByClassName),w.getById=i(function(e){return q.appendChild(e).id=P,!H.getElementsByName||!H.getElementsByName(P).length}),w.getById?(T.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&_){var n=t.getElementById(e);return n?[n]:[]}},T.filter.ID=function(e){var t=e.replace(be,we);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(be,we);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):w.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&_)return t.getElementsByClassName(e)},M=[],F=[],(w.qsa=me.test(H.querySelectorAll))&&(i(function(e){q.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&F.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+P+"-]").length||F.push("~="),e.querySelectorAll(":checked").length||F.push(":checked"),e.querySelectorAll("a#"+P+"+*").length||F.push(".#.+[+~]")}),i(function(e){var t=H.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+ne+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=me.test(O=q.matches||q.webkitMatchesSelector||q.mozMatchesSelector||q.oMatchesSelector||q.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",oe)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=me.test(q.compareDocumentPosition),R=t||me.test(q.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!w.sortDetached&&t.compareDocumentPosition(e)===n?e===H||e.ownerDocument===B&&R(B,e)?-1:t===H||t.ownerDocument===B&&R(B,t)?1:D?ee(D,e)-ee(D,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,s=[e],u=[t];if(!i||!o)return e===H?-1:t===H?1:i?-1:o?1:D?ee(D,e)-ee(D,t):0;if(i===o)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===B?-1:u[r]===B?1:0},H):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(ce,"='$1']"),w.matchesSelector&&_&&!X[n+" "]&&(!M||!M.test(n))&&(!F||!F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!_):void 0;return void 0!==r?r:w.attributes||!_?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(j=!w.detectDuplicates,D=!w.sortStable&&e.slice(0),e.sort(U),j){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return D=null,e},C=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:pe,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,we),e[3]=(e[3]||e[4]||e[5]||"").replace(be,we),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,we).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:!n||(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(o===r||o.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s,x=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(d=m,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}), +l=c[e]||[],p=l[0]===W&&l[1],x=p&&l[2],d=p&&m.childNodes[p];d=++p&&d&&d[g]||(x=p=0)||h.pop();)if(1===d.nodeType&&++x&&d===t){c[e]=[W,p,x];break}}else if(v&&(d=t,f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),l=c[e]||[],p=l[0]===W&&l[1],x=p),x===!1)for(;(d=++p&&d&&d[g]||(x=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==y:1!==d.nodeType)||!++x||(v&&(f=d[P]||(d[P]={}),c=f[d.uniqueID]||(f[d.uniqueID]={}),c[e]=[W,x]),d!==t)););return x-=i,x===r||x%r===0&&x/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[P]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=ee(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(se,"$1"));return i[P]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,we),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return de.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,we).toLowerCase(),function(t){var n;do if(n=_?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===q},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return ge.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:l(function(){return[0]}),last:l(function(e,t){return[t-1]}),eq:l(function(e,t,n){return[n<0?n+t:n]}),even:l(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:l(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&_&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(be,we),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pe.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((u=T.find[s])&&(r=u(a.matches[0].replace(be,we),ve.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&d(o),!e)return Q.apply(n,r),n;break}}return(l||k(e,f))(r,t,!_,n,!t||ve.test(e)&&c(t.parentNode)||t),n},w.sortStable=P.split("").sort(U).join("")===P,w.detectDuplicates=!!j,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);pe.find=ve,pe.expr=ve.selectors,pe.expr[":"]=pe.expr.pseudos,pe.uniqueSort=pe.unique=ve.uniqueSort,pe.text=ve.getText,pe.isXMLDoc=ve.isXML,pe.contains=ve.contains;var xe=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&pe(e).is(n))break;r.push(e)}return r},be=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},we=pe.expr.match.needsContext,Te=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Ce=/^.[^:#\[\.,]*$/;pe.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?pe.find.matchesSelector(r,e)?[r]:[]:pe.find.matches(e,pe.grep(t,function(e){return 1===e.nodeType}))},pe.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(pe(e).filter(function(){for(t=0;t1?pe.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&we.test(e)?pe(e):e||[],!1).length}});var Ee,Ne=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,ke=pe.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||Ee,"string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Ne.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof pe?t[0]:t,pe.merge(this,pe.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:re,!0)),Te.test(r[1])&&pe.isPlainObject(t))for(r in t)pe.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}if(i=re.getElementById(r[2]),i&&i.parentNode){if(i.id!==r[2])return Ee.find(e);this.length=1,this[0]=i}return this.context=re,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):pe.isFunction(e)?"undefined"!=typeof n.ready?n.ready(e):e(pe):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),pe.makeArray(e,this))};ke.prototype=pe.fn,Ee=pe(re);var Se=/^(?:parents|prev(?:Until|All))/,Ae={children:!0,contents:!0,next:!0,prev:!0};pe.fn.extend({has:function(e){var t,n=pe(e,this),r=n.length;return this.filter(function(){for(t=0;t-1:1===n.nodeType&&pe.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?pe.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?pe.inArray(this[0],pe(e)):pe.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(pe.uniqueSort(pe.merge(this.get(),pe(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),pe.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return xe(e,"parentNode")},parentsUntil:function(e,t,n){return xe(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return xe(e,"nextSibling")},prevAll:function(e){return xe(e,"previousSibling")},nextUntil:function(e,t,n){return xe(e,"nextSibling",n)},prevUntil:function(e,t,n){return xe(e,"previousSibling",n)},siblings:function(e){return be((e.parentNode||{}).firstChild,e)},children:function(e){return be(e.firstChild)},contents:function(e){return pe.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:pe.merge([],e.childNodes)}},function(e,t){pe.fn[e]=function(n,r){var i=pe.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=pe.filter(r,i)),this.length>1&&(Ae[e]||(i=pe.uniqueSort(i)),Se.test(e)&&(i=i.reverse())),this.pushStack(i)}});var De=/\S+/g;pe.Callbacks=function(e){e="string"==typeof e?o(e):pe.extend({},e);var t,n,r,i,a=[],s=[],u=-1,l=function(){for(i=e.once,r=t=!0;s.length;u=-1)for(n=s.shift();++u-1;)a.splice(n,1),n<=u&&u--}),this},has:function(e){return e?pe.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=!0,n||c.disable(),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||l()),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},pe.extend({Deferred:function(e){var t=[["resolve","done",pe.Callbacks("once memory"),"resolved"],["reject","fail",pe.Callbacks("once memory"),"rejected"],["notify","progress",pe.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return pe.Deferred(function(n){pe.each(t,function(t,o){var a=pe.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&pe.isFunction(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?pe.extend(e,r):r}},i={};return r.pipe=r.then,pe.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=ie.call(arguments),a=o.length,s=1!==a||e&&pe.isFunction(e.promise)?a:0,u=1===s?e:pe.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?ie.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);i0||(je.resolveWith(re,[pe]),pe.fn.triggerHandler&&(pe(re).triggerHandler("ready"),pe(re).off("ready"))))}}),pe.ready.promise=function(t){if(!je)if(je=pe.Deferred(),"complete"===re.readyState||"loading"!==re.readyState&&!re.documentElement.doScroll)e.setTimeout(pe.ready);else if(re.addEventListener)re.addEventListener("DOMContentLoaded",s),e.addEventListener("load",s);else{re.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&re.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!pe.isReady){try{n.doScroll("left")}catch(t){return e.setTimeout(i,50)}a(),pe.ready()}}()}return je.promise(t)},pe.ready.promise();var Le;for(Le in pe(fe))break;fe.ownFirst="0"===Le,fe.inlineBlockNeedsLayout=!1,pe(function(){var e,t,n,r;n=re.getElementsByTagName("body")[0],n&&n.style&&(t=re.createElement("div"),r=re.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),"undefined"!=typeof t.style.zoom&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",fe.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=re.createElement("div");fe.deleteExpando=!0;try{delete e.test}catch(t){fe.deleteExpando=!1}e=null}();var He=function(e){var t=pe.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return(1===n||9===n)&&(!t||t!==!0&&e.getAttribute("classid")===t)},qe=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,_e=/([A-Z])/g;pe.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?pe.cache[e[pe.expando]]:e[pe.expando],!!e&&!l(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return f(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return f(e,t,!0)}}),pe.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=pe.data(o),1===o.nodeType&&!pe._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=pe.camelCase(r.slice(5)),u(o,r,i[r])));pe._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){pe.data(this,e)}):arguments.length>1?this.each(function(){pe.data(this,e,t)}):o?u(o,e,pe.data(o,e)):void 0},removeData:function(e){return this.each(function(){pe.removeData(this,e)})}}),pe.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=pe._data(e,t),n&&(!r||pe.isArray(n)?r=pe._data(e,t,pe.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=pe.queue(e,t),r=n.length,i=n.shift(),o=pe._queueHooks(e,t),a=function(){pe.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return pe._data(e,n)||pe._data(e,n,{empty:pe.Callbacks("once memory").add(function(){pe._removeData(e,t+"queue"),pe._removeData(e,n)})})}}),pe.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length
                a",fe.leadingWhitespace=3===e.firstChild.nodeType,fe.tbody=!e.getElementsByTagName("tbody").length,fe.htmlSerialize=!!e.getElementsByTagName("link").length,fe.html5Clone="<:nav>"!==re.createElement("nav").cloneNode(!0).outerHTML,n.type="checkbox",n.checked=!0,t.appendChild(n),fe.appendChecked=n.checked,e.innerHTML="",fe.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue,t.appendChild(e),n=re.createElement("input"),n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),e.appendChild(n),fe.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.noCloneEvent=!!e.addEventListener,e[pe.expando]=1,fe.attributes=!e.getAttribute(pe.expando)}();var Xe={option:[1,""],legend:[1,"
                ","
                "],area:[1,"",""],param:[1,"",""],thead:[1,"","
                "],tr:[2,"","
                "],col:[2,"","
                "],td:[3,"","
                "],_default:fe.htmlSerialize?[0,"",""]:[1,"X
                ","
                "]};Xe.optgroup=Xe.option,Xe.tbody=Xe.tfoot=Xe.colgroup=Xe.caption=Xe.thead,Xe.th=Xe.td;var Ue=/<|&#?\w+;/,Ve=/-1&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[pe.expando]?t:new pe.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:pe.makeArray(n,[t]),l=pe.event.special[p]||{},i||!l.trigger||l.trigger.apply(r,n)!==!1)){if(!i&&!l.noBubble&&!pe.isWindow(r)){for(u=l.delegateType||p,Ke.test(u+p)||(s=s.parentNode);s;s=s.parentNode)d.push(s),c=s;c===(r.ownerDocument||re)&&d.push(c.defaultView||c.parentWindow||e)}for(f=0;(s=d[f++])&&!t.isPropagationStopped();)t.type=f>1?u:l.bindType||p,o=(pe._data(s,"events")||{})[t.type]&&pe._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&He(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!l._default||l._default.apply(d.pop(),n)===!1)&&He(r)&&a&&r[p]&&!pe.isWindow(r)){c=r[a],c&&(r[a]=null),pe.event.triggered=p;try{r[p]()}catch(g){}pe.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=pe.event.fix(e);var t,n,r,i,o,a=[],s=ie.call(arguments),u=(pe._data(this,"events")||{})[e.type]||[],l=pe.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){for(a=pe.event.handlers.call(this,e,u),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)e.rnamespace&&!e.rnamespace.test(o.namespace)||(e.handleObj=o,e.data=o.data,r=((pe.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,u=e.target;if(s&&u.nodeType&&("click"!==e.type||isNaN(e.button)||e.button<1))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(r=[],n=0;n-1:pe.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&a.push({elem:u,handlers:r})}return s]","i"),tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,nt=/\s*$/g,at=p(re),st=at.appendChild(re.createElement("div"));pe.extend({htmlPrefilter:function(e){return e.replace(tt,"<$1>")},clone:function(e,t,n){var r,i,o,a,s,u=pe.contains(e.ownerDocument,e);if(fe.html5Clone||pe.isXMLDoc(e)||!et.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(st.innerHTML=e.outerHTML,st.removeChild(o=st.firstChild)),!(fe.noCloneEvent&&fe.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||pe.isXMLDoc(e)))for(r=h(o),s=h(e),a=0;null!=(i=s[a]);++a)r[a]&&k(i,r[a]);if(t)if(n)for(s=s||h(e),r=r||h(o),a=0;null!=(i=s[a]);a++)N(i,r[a]);else N(e,o);return r=h(o,"script"),r.length>0&&g(r,!u&&h(e,"script")),r=s=i=null,o},cleanData:function(e,t){for(var n,r,i,o,a=0,s=pe.expando,u=pe.cache,l=fe.attributes,c=pe.event.special;null!=(n=e[a]);a++)if((t||He(n))&&(i=n[s],o=i&&u[i])){if(o.events)for(r in o.events)c[r]?pe.event.remove(n,r):pe.removeEvent(n,r,o.handle);u[i]&&(delete u[i],l||"undefined"==typeof n.removeAttribute?n[s]=void 0:n.removeAttribute(s),ne.push(i))}}}),pe.fn.extend({domManip:S,detach:function(e){return A(this,e,!0)},remove:function(e){return A(this,e)},text:function(e){return Pe(this,function(e){return void 0===e?pe.text(this):this.empty().append((this[0]&&this[0].ownerDocument||re).createTextNode(e))},null,e,arguments.length)},append:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.appendChild(e)}})},prepend:function(){return S(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=T(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return S(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&pe.cleanData(h(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&pe.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return pe.clone(this,e,t)})},html:function(e){return Pe(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ze,""):void 0;if("string"==typeof e&&!nt.test(e)&&(fe.htmlSerialize||!et.test(e))&&(fe.leadingWhitespace||!$e.test(e))&&!Xe[(We.exec(e)||["",""])[1].toLowerCase()]){e=pe.htmlPrefilter(e);try{for(;nt",t=l.getElementsByTagName("td"),t[0].style.cssText="margin:0;border:0;padding:0;display:none",o=0===t[0].offsetHeight,o&&(t[0].style.display="",t[1].style.display="none",o=0===t[0].offsetHeight)),f.removeChild(u)}var n,r,i,o,a,s,u=re.createElement("div"),l=re.createElement("div");l.style&&(l.style.cssText="float:left;opacity:.5",fe.opacity="0.5"===l.style.opacity,fe.cssFloat=!!l.style.cssFloat,l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",fe.clearCloneStyle="content-box"===l.style.backgroundClip,u=re.createElement("div"),u.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",l.innerHTML="",u.appendChild(l),fe.boxSizing=""===l.style.boxSizing||""===l.style.MozBoxSizing||""===l.style.WebkitBoxSizing,pe.extend(fe,{reliableHiddenOffsets:function(){return null==n&&t(),o},boxSizingReliable:function(){return null==n&&t(),i},pixelMarginRight:function(){return null==n&&t(),r},pixelPosition:function(){return null==n&&t(),n},reliableMarginRight:function(){return null==n&&t(),a},reliableMarginLeft:function(){return null==n&&t(),s}}))}();var ht,gt,mt=/^(top|right|bottom|left)$/;e.getComputedStyle?(ht=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n.getPropertyValue(t)||n[t]:void 0,""!==a&&void 0!==a||pe.contains(e.ownerDocument,e)||(a=pe.style(e,t)),n&&!fe.pixelMarginRight()&&ft.test(a)&&ct.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o),void 0===a?a:a+""}):pt.currentStyle&&(ht=function(e){return e.currentStyle},gt=function(e,t,n){var r,i,o,a,s=e.style;return n=n||ht(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),ft.test(a)&&!mt.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"});var yt=/alpha\([^)]*\)/i,vt=/opacity\s*=\s*([^)]*)/i,xt=/^(none|table(?!-c[ea]).+)/,bt=new RegExp("^("+Fe+")(.*)$","i"),wt={position:"absolute",visibility:"hidden",display:"block"},Tt={letterSpacing:"0",fontWeight:"400"},Ct=["Webkit","O","Moz","ms"],Et=re.createElement("div").style;pe.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=gt(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":fe.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=pe.camelCase(t),u=e.style;if(t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:u[t];if(o=typeof n,"string"===o&&(i=Me.exec(n))&&i[1]&&(n=d(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(pe.cssNumber[s]?"":"px")),fe.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{u[t]=n}catch(l){}}},css:function(e,t,n,r){var i,o,a,s=pe.camelCase(t);return t=pe.cssProps[s]||(pe.cssProps[s]=H(s)||s),a=pe.cssHooks[t]||pe.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=gt(e,t,r)),"normal"===o&&t in Tt&&(o=Tt[t]),""===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),pe.each(["height","width"],function(e,t){pe.cssHooks[t]={get:function(e,n,r){if(n)return xt.test(pe.css(e,"display"))&&0===e.offsetWidth?dt(e,wt,function(){return M(e,t,r)}):M(e,t,r)},set:function(e,n,r){var i=r&&ht(e);return _(e,n,r?F(e,t,r,fe.boxSizing&&"border-box"===pe.css(e,"boxSizing",!1,i),i):0)}}}),fe.opacity||(pe.cssHooks.opacity={get:function(e,t){return vt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=pe.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===pe.trim(o.replace(yt,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=yt.test(o)?o.replace(yt,i):o+" "+i)}}),pe.cssHooks.marginRight=L(fe.reliableMarginRight,function(e,t){if(t)return dt(e,{display:"inline-block"},gt,[e,"marginRight"])}),pe.cssHooks.marginLeft=L(fe.reliableMarginLeft,function(e,t){if(t)return(parseFloat(gt(e,"marginLeft"))||(pe.contains(e.ownerDocument,e)?e.getBoundingClientRect().left-dt(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}):0))+"px"}),pe.each({margin:"",padding:"",border:"Width"},function(e,t){pe.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+Oe[r]+t]=o[r]||o[r-2]||o[0];return i}},ct.test(e)||(pe.cssHooks[e+t].set=_)}),pe.fn.extend({css:function(e,t){return Pe(this,function(e,t,n){var r,i,o={},a=0;if(pe.isArray(t)){for(r=ht(e),i=t.length;a1)},show:function(){return q(this,!0)},hide:function(){return q(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Re(this)?pe(this).show():pe(this).hide()})}}),pe.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||pe.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(pe.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.options.duration?this.pos=t=pe.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=pe.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){pe.fx.step[e.prop]?pe.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[pe.cssProps[e.prop]]&&!pe.cssHooks[e.prop]?e.elem[e.prop]=e.now:pe.style(e.elem,e.prop,e.now+e.unit)}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},pe.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},pe.fx=O.prototype.init,pe.fx.step={};var Nt,kt,St=/^(?:toggle|show|hide)$/,At=/queueHooks$/;pe.Animation=pe.extend($,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return d(n.elem,e,Me.exec(t),n),n}]},tweener:function(e,t){pe.isFunction(e)?(t=e,e=["*"]):e=e.match(De);for(var n,r=0,i=e.length;r
                a",e=n.getElementsByTagName("a")[0],t.setAttribute("type","checkbox"),n.appendChild(t),e=n.getElementsByTagName("a")[0],e.style.cssText="top:1px",fe.getSetAttribute="t"!==n.className,fe.style=/top/.test(e.getAttribute("style")),fe.hrefNormalized="/a"===e.getAttribute("href"),fe.checkOn=!!t.value,fe.optSelected=i.selected,fe.enctype=!!re.createElement("form").enctype,r.disabled=!0,fe.optDisabled=!i.disabled,t=re.createElement("input"),t.setAttribute("value",""),fe.input=""===t.getAttribute("value"),t.value="t",t.setAttribute("type","radio"),fe.radioValue="t"===t.value}();var Dt=/\r/g,jt=/[\x20\t\r\n\f]+/g;pe.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=pe.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,pe(this).val()):e,null==i?i="":"number"==typeof i?i+="":pe.isArray(i)&&(i=pe.map(i,function(e){return null==e?"":e+""})),t=pe.valHooks[this.type]||pe.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=pe.valHooks[i.type]||pe.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(Dt,""):null==n?"":n)}}}),pe.extend({valHooks:{option:{get:function(e){var t=pe.find.attr(e,"value");return null!=t?t:pe.trim(pe.text(e)).replace(jt," ")}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,a=o?null:[],s=o?i+1:r.length,u=i<0?s:o?i:0;u-1)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),pe.each(["radio","checkbox"],function(){pe.valHooks[this]={set:function(e,t){if(pe.isArray(t))return e.checked=pe.inArray(pe(e).val(),t)>-1}},fe.checkOn||(pe.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Lt,Ht,qt=pe.expr.attrHandle,_t=/^(?:checked|selected)$/i,Ft=fe.getSetAttribute,Mt=fe.input;pe.fn.extend({attr:function(e,t){return Pe(this,pe.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){pe.removeAttr(this,e)})}}),pe.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?pe.prop(e,t,n):(1===o&&pe.isXMLDoc(e)||(t=t.toLowerCase(),i=pe.attrHooks[t]||(pe.expr.match.bool.test(t)?Ht:Lt)),void 0!==n?null===n?void pe.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:(r=pe.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!fe.radioValue&&"radio"===t&&pe.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(De);if(o&&1===e.nodeType)for(;n=o[i++];)r=pe.propFix[n]||n,pe.expr.match.bool.test(n)?Mt&&Ft||!_t.test(n)?e[r]=!1:e[pe.camelCase("default-"+n)]=e[r]=!1:pe.attr(e,n,""),e.removeAttribute(Ft?n:r)}}),Ht={set:function(e,t,n){return t===!1?pe.removeAttr(e,n):Mt&&Ft||!_t.test(n)?e.setAttribute(!Ft&&pe.propFix[n]||n,n):e[pe.camelCase("default-"+n)]=e[n]=!0,n}},pe.each(pe.expr.match.bool.source.match(/\w+/g),function(e,t){var n=qt[t]||pe.find.attr;Mt&&Ft||!_t.test(t)?qt[t]=function(e,t,r){var i,o;return r||(o=qt[t],qt[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,qt[t]=o),i}:qt[t]=function(e,t,n){if(!n)return e[pe.camelCase("default-"+t)]?t.toLowerCase():null}}),Mt&&Ft||(pe.attrHooks.value={set:function(e,t,n){return pe.nodeName(e,"input")?void(e.defaultValue=t):Lt&&Lt.set(e,t,n)}}),Ft||(Lt={set:function(e,t,n){var r=e.getAttributeNode(n);if(r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n))return t}},qt.id=qt.name=qt.coords=function(e,t,n){var r;if(!n)return(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},pe.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);if(n&&n.specified)return n.value},set:Lt.set},pe.attrHooks.contenteditable={set:function(e,t,n){Lt.set(e,""!==t&&t,n)}},pe.each(["width","height"],function(e,t){pe.attrHooks[t]={set:function(e,n){if(""===n)return e.setAttribute(t,"auto"),n}}})),fe.style||(pe.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var Ot=/^(?:input|select|textarea|button|object)$/i,Rt=/^(?:a|area)$/i;pe.fn.extend({prop:function(e,t){return Pe(this,pe.prop,e,t,arguments.length>1)},removeProp:function(e){return e=pe.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),pe.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&pe.isXMLDoc(e)||(t=pe.propFix[t]||t,i=pe.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=pe.find.attr(e,"tabindex");return t?parseInt(t,10):Ot.test(e.nodeName)||Rt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),fe.hrefNormalized||pe.each(["href","src"],function(e,t){pe.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),fe.optSelected||(pe.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),pe.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){pe.propFix[this.toLowerCase()]=this}),fe.enctype||(pe.propFix.enctype="encoding");var Pt=/[\t\r\n\f]/g;pe.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).addClass(e.call(this,t,z(this)))});if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(pe.isFunction(e))return this.each(function(t){pe(this).removeClass(e.call(this,t,z(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof e&&e)for(t=e.match(De)||[];n=this[u++];)if(i=z(n),r=1===n.nodeType&&(" "+i+" ").replace(Pt," ")){for(a=0;o=t[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");s=pe.trim(r),i!==s&&pe.attr(n,"class",s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):pe.isFunction(e)?this.each(function(n){pe(this).toggleClass(e.call(this,n,z(this),t),t)}):this.each(function(){var t,r,i,o;if("string"===n)for(r=0,i=pe(this),o=e.match(De)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=z(this),t&&pe._data(this,"__className__",t),pe.attr(this,"class",t||e===!1?"":pe._data(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+z(n)+" ").replace(Pt," ").indexOf(t)>-1)return!0;return!1}}),pe.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){pe.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),pe.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}});var Bt=e.location,Wt=pe.now(),It=/\?/,$t=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;pe.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=pe.trim(t+"");return i&&!pe.trim(i.replace($t,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():pe.error("Invalid JSON: "+t)},pe.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new e.DOMParser,n=r.parseFromString(t,"text/xml")):(n=new e.ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||pe.error("Invalid XML: "+t),n};var zt=/#.*$/,Xt=/([?&])_=[^&]*/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Vt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Yt=/^(?:GET|HEAD)$/,Jt=/^\/\//,Gt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Kt={},Qt={},Zt="*/".concat("*"),en=Bt.href,tn=Gt.exec(en.toLowerCase())||[];pe.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:en,type:"GET",isLocal:Vt.test(tn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":pe.parseJSON,"text xml":pe.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?V(V(e,pe.ajaxSettings),t):V(pe.ajaxSettings,e)},ajaxPrefilter:X(Kt),ajaxTransport:X(Qt),ajax:function(t,n){function r(t,n,r,i){var o,f,v,x,w,C=n;2!==b&&(b=2,u&&e.clearTimeout(u),c=void 0,s=i||"",T.readyState=t>0?4:0,o=t>=200&&t<300||304===t,r&&(x=Y(d,T,r)),x=J(d,x,T,o),o?(d.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(pe.lastModified[a]=w),w=T.getResponseHeader("etag"),w&&(pe.etag[a]=w)),204===t||"HEAD"===d.type?C="nocontent":304===t?C="notmodified":(C=x.state,f=x.data,v=x.error,o=!v)):(v=C,!t&&C||(C="error",t<0&&(t=0))),T.status=t,T.statusText=(n||C)+"",o?g.resolveWith(p,[f,C,T]):g.rejectWith(p,[T,C,v]),T.statusCode(y),y=void 0,l&&h.trigger(o?"ajaxSuccess":"ajaxError",[T,d,o?f:v]),m.fireWith(p,[T,C]),l&&(h.trigger("ajaxComplete",[T,d]),--pe.active||pe.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,d=pe.ajaxSetup({},n),p=d.context||d,h=d.context&&(p.nodeType||p.jquery)?pe(p):pe.event,g=pe.Deferred(),m=pe.Callbacks("once memory"),y=d.statusCode||{},v={},x={},b=0,w="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!f)for(f={};t=Ut.exec(s);)f[t[1].toLowerCase()]=t[2];t=f[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?s:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=x[n]=x[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(b<2)for(t in e)y[t]=[y[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||w;return c&&c.abort(t),r(0,t),this}};if(g.promise(T).complete=m.add,T.success=T.done,T.error=T.fail,d.url=((t||d.url||en)+"").replace(zt,"").replace(Jt,tn[1]+"//"),d.type=n.method||n.type||d.method||d.type,d.dataTypes=pe.trim(d.dataType||"*").toLowerCase().match(De)||[""],null==d.crossDomain&&(i=Gt.exec(d.url.toLowerCase()),d.crossDomain=!(!i||i[1]===tn[1]&&i[2]===tn[2]&&(i[3]||("http:"===i[1]?"80":"443"))===(tn[3]||("http:"===tn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=pe.param(d.data,d.traditional)),U(Kt,d,n,T),2===b)return T;l=pe.event&&d.global,l&&0===pe.active++&&pe.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Yt.test(d.type),a=d.url,d.hasContent||(d.data&&(a=d.url+=(It.test(a)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=Xt.test(a)?a.replace(Xt,"$1_="+Wt++):a+(It.test(a)?"&":"?")+"_="+Wt++)),d.ifModified&&(pe.lastModified[a]&&T.setRequestHeader("If-Modified-Since",pe.lastModified[a]),pe.etag[a]&&T.setRequestHeader("If-None-Match",pe.etag[a])),(d.data&&d.hasContent&&d.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",d.contentType),T.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Zt+"; q=0.01":""):d.accepts["*"]);for(o in d.headers)T.setRequestHeader(o,d.headers[o]);if(d.beforeSend&&(d.beforeSend.call(p,T,d)===!1||2===b))return T.abort();w="abort";for(o in{success:1,error:1,complete:1})T[o](d[o]);if(c=U(Qt,d,n,T)){if(T.readyState=1,l&&h.trigger("ajaxSend",[T,d]),2===b)return T;d.async&&d.timeout>0&&(u=e.setTimeout(function(){T.abort("timeout")},d.timeout));try{b=1,c.send(v,r)}catch(C){if(!(b<2))throw C;r(-1,C)}}else r(-1,"No Transport");return T},getJSON:function(e,t,n){return pe.get(e,t,n,"json")},getScript:function(e,t){return pe.get(e,void 0,t,"script")}}),pe.each(["get","post"],function(e,t){pe[t]=function(e,n,r,i){return pe.isFunction(n)&&(i=i||r,r=n,n=void 0),pe.ajax(pe.extend({url:e,type:t,dataType:i,data:n,success:r},pe.isPlainObject(e)&&e))}}),pe._evalUrl=function(e){return pe.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},pe.fn.extend({wrapAll:function(e){if(pe.isFunction(e))return this.each(function(t){pe(this).wrapAll(e.call(this,t))});if(this[0]){var t=pe(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return pe.isFunction(e)?this.each(function(t){pe(this).wrapInner(e.call(this,t))}):this.each(function(){var t=pe(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=pe.isFunction(e);return this.each(function(n){pe(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){pe.nodeName(this,"body")||pe(this).replaceWith(this.childNodes)}).end()}}),pe.expr.filters.hidden=function(e){return fe.reliableHiddenOffsets()?e.offsetWidth<=0&&e.offsetHeight<=0&&!e.getClientRects().length:K(e)},pe.expr.filters.visible=function(e){return!pe.expr.filters.hidden(e)};var nn=/%20/g,rn=/\[\]$/,on=/\r?\n/g,an=/^(?:submit|button|image|reset|file)$/i,sn=/^(?:input|select|textarea|keygen)/i;pe.param=function(e,t){var n,r=[],i=function(e,t){t=pe.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=pe.ajaxSettings&&pe.ajaxSettings.traditional),pe.isArray(e)||e.jquery&&!pe.isPlainObject(e))pe.each(e,function(){i(this.name,this.value)});else for(n in e)Q(n,e[n],t,i);return r.join("&").replace(nn,"+")},pe.fn.extend({serialize:function(){return pe.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=pe.prop(this,"elements");return e?pe.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!pe(this).is(":disabled")&&sn.test(this.nodeName)&&!an.test(e)&&(this.checked||!Be.test(e))}).map(function(e,t){var n=pe(this).val();return null==n?null:pe.isArray(n)?pe.map(n,function(e){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),pe.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return this.isLocal?ee():re.documentMode>8?Z():/^(get|post|head|put|delete|options)$/i.test(this.type)&&Z()||ee()}:Z;var un=0,ln={},cn=pe.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in ln)ln[e](void 0,!0)}),fe.cors=!!cn&&"withCredentials"in cn,cn=fe.ajax=!!cn,cn&&pe.ajaxTransport(function(t){if(!t.crossDomain||fe.cors){var n;return{send:function(r,i){var o,a=t.xhr(),s=++un;if(a.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(o in t.xhrFields)a[o]=t.xhrFields[o];t.mimeType&&a.overrideMimeType&&a.overrideMimeType(t.mimeType),t.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest");for(o in r)void 0!==r[o]&&a.setRequestHeader(o,r[o]+"");a.send(t.hasContent&&t.data||null),n=function(e,r){var o,u,l;if(n&&(r||4===a.readyState))if(delete ln[s],n=void 0,a.onreadystatechange=pe.noop,r)4!==a.readyState&&a.abort();else{l={},o=a.status,"string"==typeof a.responseText&&(l.text=a.responseText);try{u=a.statusText}catch(c){u=""}o||!t.isLocal||t.crossDomain?1223===o&&(o=204):o=l.text?200:404}l&&i(o,u,l,a.getAllResponseHeaders())},t.async?4===a.readyState?e.setTimeout(n):a.onreadystatechange=ln[s]=n:n()},abort:function(){n&&n(void 0,!0)}}}}),pe.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return pe.globalEval(e),e}}}),pe.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),pe.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=re.head||pe("head")[0]||re.documentElement;return{send:function(r,i){t=re.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var fn=[],dn=/(=)\?(?=&|$)|\?\?/;pe.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=fn.pop()||pe.expando+"_"+Wt++;return this[e]=!0,e}}),pe.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=pe.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(It.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||pe.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?pe(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,fn.push(i)),a&&pe.isFunction(o)&&o(a[0]),a=o=void 0}),"script"}),pe.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||re;var r=Te.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=y([e],t,i),i&&i.length&&pe(i).remove(),pe.merge([],r.childNodes))};var pn=pe.fn.load;return pe.fn.load=function(e,t,n){if("string"!=typeof e&&pn)return pn.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=pe.trim(e.slice(s,e.length)),e=e.slice(0,s)),pe.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&pe.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?pe("
                ").append(pe.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},pe.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){pe.fn[t]=function(e){return this.on(t,e)}}),pe.expr.filters.animated=function(e){return pe.grep(pe.timers,function(t){return e===t.elem}).length},pe.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=pe.css(e,"position"),f=pe(e),d={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=pe.css(e,"top"),u=pe.css(e,"left"),l=("absolute"===c||"fixed"===c)&&pe.inArray("auto",[o,u])>-1,l?(r=f.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),pe.isFunction(t)&&(t=t.call(e,n,pe.extend({},s))),null!=t.top&&(d.top=t.top-s.top+a),null!=t.left&&(d.left=t.left-s.left+i),"using"in t?t.using.call(e,d):f.css(d)}},pe.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){pe.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,pe.contains(t,i)?("undefined"!=typeof i.getBoundingClientRect&&(r=i.getBoundingClientRect()),n=te(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===pe.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),pe.nodeName(e[0],"html")||(n=e.offset()),n.top+=pe.css(e[0],"borderTopWidth",!0),n.left+=pe.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-pe.css(r,"marginTop",!0),left:t.left-n.left-pe.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){ +for(var e=this.offsetParent;e&&!pe.nodeName(e,"html")&&"static"===pe.css(e,"position");)e=e.offsetParent;return e||pt})}}),pe.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);pe.fn[e]=function(r){return Pe(this,function(e,r,i){var o=te(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?pe(o).scrollLeft():i,n?i:pe(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),pe.each(["top","left"],function(e,t){pe.cssHooks[t]=L(fe.pixelPosition,function(e,n){if(n)return n=gt(e,t),ft.test(n)?pe(e).position()[t]+"px":n})}),pe.each({Height:"height",Width:"width"},function(e,t){pe.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){pe.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return Pe(this,function(t,n,r){var i;return pe.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?pe.css(t,n,a):pe.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),pe.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),pe.fn.size=function(){return this.length},pe.fn.andSelf=pe.fn.addBack,layui.define(function(e){layui.$=pe,e("jquery",pe)}),pe});!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.currentScript?document.currentScript.src:function(){for(var e,t=document.scripts,i=t.length-1,n=i;n>0;n--)if("interactive"===t[n].readyState){e=t[n].src;break}return e||t[i].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["确定","取消"],type:["dialog","page","iframe","loading","tips"],getStyle:function(t,i){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](i)},link:function(t,i,n){if(r.path){var a=document.getElementsByTagName("head")[0],s=document.createElement("link");"string"==typeof i&&(n=i);var l=(n||t).replace(/\.|\//g,""),f="layuicss-"+l,c=0;s.rel="stylesheet",s.href=r.path+t,s.id=f,document.getElementById(f)||a.appendChild(s),"function"==typeof i&&!function u(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(o.getStyle(document.getElementById(f),"width"))?i():setTimeout(u,100))}()}}},r={v:"3.1.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):o.link("theme/"+e.extend),this):this},ready:function(e){var t="layer",i="",n=(a?"modules/layer/":"theme/")+"default/layer.css?v="+r.v+i;return a?layui.addcss(n,e,t):o.link(n,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var s="function"==typeof t;return s&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},s?{}:t))},msg:function(e,n,a){var s="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",u=l.anim.length-1;return s&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},s&&!o.config.skin?{skin:c+" layui-layer-hui",anim:u}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},s=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},30)};s.pt=s.prototype;var l=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];l.anim=["layer-anim-00","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],s.pt.config={type:0,shade:.3,fixed:!0,move:l[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,isOutAnim:!0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},s.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,s=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),u=r.title?'
                '+(f?r.title[0]:r.title)+"
                ":"";return r.zIndex=s,t([r.shade?'
                ':"",'
                '+(e&&2!=r.type?"":u)+'
                '+(0==r.type&&r.icon!==-1?'':"")+(1==r.type&&e?"":r.content||"")+'
                '+function(){var e=c?'':"";return r.closeBtn&&(e+=''),e}()+""+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t'+r.btn[t]+"";return'
                '+e+"
                "}():"")+(r.resize?'':"")+"
                "],u,i('
                ')),n},s.pt.creat=function(){var e=this,t=e.config,a=e.index,s=t.content,f="object"==typeof s,c=i("body");if(!t.id||!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var s=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+'',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}if(e.vessel(f,function(n,r,u){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){s.parents("."+l[0])[0]||(s.data("display",s.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+l[0]+a).find("."+l[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=u),e.layero=i("#"+l[0]+a),t.scrollbar||l.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),i("#layui-layer-shade"+e.index).css({"background-color":t.shade[1]||"#000",opacity:t.shade[0]||t.shade}),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",s[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),l.anim[t.anim]){var u="layer-anim "+l.anim[t.anim];e.layero.addClass(u).one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){i(this).removeClass(u)})}t.isOutAnim&&e.layero.data("isOutAnim",!0)}},s.pt.auto=function(e){var t=this,a=t.config,o=i("#"+l[0]+e);""===a.area[0]&&a.maxWidth>0&&(r.ie&&r.ie<8&&a.btn&&o.width(o.innerWidth()),o.outerWidth()>a.maxWidth&&o.width(a.maxWidth));var s=[o.innerWidth(),o.innerHeight()],f=o.find(l[1]).outerHeight()||0,c=o.find("."+l[6]).outerHeight()||0,u=function(e){e=o.find(e),e.height(s[1]-f-c-2*(0|parseFloat(e.css("padding-top"))))};switch(a.type){case 2:u("iframe");break;default:""===a.area[1]?a.maxHeight>0&&o.outerHeight()>a.maxHeight?(s[1]=a.maxHeight,u("."+l[5])):a.fixed&&s[1]>=n.height()&&(s[1]=n.height(),u("."+l[5])):u("."+l[5])}return t},s.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(l[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},s.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var s={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),s.autoLeft=function(){s.left+o[0]-n.width()>0?(s.tipLeft=s.left+s.width-o[0],f.css({right:12,left:"auto"})):s.tipLeft=s.left},s.where=[function(){s.autoLeft(),s.tipTop=s.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left+s.width+10,s.tipTop=s.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){s.autoLeft(),s.tipTop=s.top+s.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){s.tipLeft=s.left-o[0]-10,s.tipTop=s.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],s.where[c-1](),1===c?s.top-(n.scrollTop()+o[1]+16)<0&&s.where[2]():2===c?n.width()-(s.left+s.width+o[0]+16)>0||s.where[3]():3===c?s.top-n.scrollTop()+s.height+o[1]+16-n.height()>0&&s.where[0]():4===c&&o[0]+16-s.left>0&&s.where[1](),a.find("."+l[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:s.tipLeft-(t.fixed?n.scrollLeft():0),top:s.tipTop-(t.fixed?n.scrollTop():0)})},s.pt.move=function(){var e=this,t=e.config,a=i(document),s=e.layero,l=s.find(t.move),f=s.find(".layui-layer-resize"),c={};return t.move&&l.css("cursor","move"),l.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(s.css("left")),e.clientY-parseFloat(s.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[s.outerWidth(),s.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],l="fixed"===s.css("position");if(i.preventDefault(),c.stX=l?0:n.scrollLeft(),c.stY=l?0:n.scrollTop(),!t.moveOut){var f=n.width()-s.outerWidth()+c.stX,u=n.height()-s.outerHeight()+c.stY;af&&(a=f),ou&&(o=u)}s.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0,t.resizing&&t.resizing(s)}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd(s)),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},s.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+l[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+l[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+l[0])[0]||1==n.attr("layer")&&i("."+l[0]).length<1&&n.removeAttr("layer").show(),n=null})},s.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+l[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},s.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){l.html.attr("layer-full")==e&&(l.html[0].style.removeProperty?l.html[0].style.removeProperty("overflow"):l.html[0].style.removeAttribute("overflow"),l.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+l[4]).attr("times"),i("#"+l[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+l[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+l[0]+e),a=n.find(l[1]).outerHeight()||0,o=n.find("."+l[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+l[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+l[0]+e),r=a.find(".layui-layer-content"),s=a.attr("type"),f=a.find(l[1]).outerHeight()||0,c=a.find("."+l[6]).outerHeight()||0;a.attr("minLeft");s!==o.type[3]&&s!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+l[6]).outerHeight(),s===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+l[0]+e),s=a.find(l[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:s,left:f,top:n.height()-s,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(l[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+l[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(l[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+l[0]+e);o.record(a),l.html.attr("layer-full")||l.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+l[0]+(t||r.index)).find(l[1]);n.html(e)},r.close=function(e){var t=i("#"+l[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var s="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+l[5]+")").remove();for(var a=t.find("."+s),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(s)}else{if(n===o.type[2])try{var f=i("#"+l[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+l[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("isOutAnim")&&t.addClass("layer-anim "+a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),r.ie&&r.ie<10||!t.data("isOutAnim")?f():setTimeout(function(){f()},200)}},r.closeAll=function(e){i.each(i("."+l[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var s,l=2==e.formType?'":function(){return''}(),f=e.success;return delete e.success,r.open(i.extend({type:1,btn:["确定","取消"],content:l,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(t){s=t.find(".layui-layer-input"),s.val(e.value||"").focus(),"function"==typeof f&&f(t)},resize:!1,yes:function(i){var n=s.val();""===n?s.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",s,{tips:1}):t&&t(n,i,s)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{},n="layui-this",a=e.success;return delete e.success,r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,a="";if(e>0)for(a=''+t[0].title+"";i"+t[i].title+"";return a}(),content:'
                  '+function(){var e=t.length,i=1,a="";if(e>0)for(a='
                • '+(t[0].content||"no content")+"
                • ";i'+(t[i].content||"no content")+"";return a}()+"
                ",success:function(t){var o=t.find(".layui-layer-title").children(),r=t.find(".layui-layer-tabmain").children();o.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var a=i(this),o=a.index();a.addClass(n).siblings().removeClass(n),r.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)}),"function"==typeof a&&a(t)}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var s={};if(t=t||{},t.photos){var l=t.photos.constructor===Object,f=l?t.photos:{},u=f.data||[],d=f.start||0;s.imgIndex=(0|d)+1,t.img=t.img||"img";var y=t.success;if(delete t.success,l){if(0===u.length)return r.msg("没有图片")}else{var p=i(t.photos),h=function(){u=[],p.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),u.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(h(),0===u.length)return;if(n||p.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:u,tab:t.tab},full:t.full}),!0),h()}),!n)return}s.imgprev=function(e){s.imgIndex--,s.imgIndex<1&&(s.imgIndex=u.length),s.tabimg(e)},s.imgnext=function(e,t){s.imgIndex++,s.imgIndex>u.length&&(s.imgIndex=1,t)||s.tabimg(e)},s.keyup=function(e){if(!s.end){var t=e.keyCode;e.preventDefault(),37===t?s.imgprev(!0):39===t?s.imgnext(!0):27===t&&r.close(s.index)}},s.tabimg=function(e){if(!(u.length<=1))return f.start=s.imgIndex-1,r.close(s.index),r.photos(t,!0,e)},s.event=function(){s.bigimg.hover(function(){s.imgsee.show()},function(){s.imgsee.hide()}),s.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),s.imgprev()}),s.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),s.imgnext()}),i(document).on("keyup",s.keyup)},s.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(u[d].src,function(n){r.close(s.loadi),s.index=r.open(i.extend({type:1,id:"layui-layer-photos",area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0]'+(u[d].alt||
                '+(u.length>1?'':"")+'
                '+(u[d].alt||"")+""+s.imgIndex+"/"+u.length+"
                ",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
                是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window);layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
              • "+(i.title||"unnaming")+"
              • ";return s[0]?s.before(r):n.append(r),o.append('
                '+(i.content||"")+"
                "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)});layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,n=layui.hint(),a=layui.device(),o={config:{},set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,r,e,i)}},l=function(){var e=this;return{upload:function(i){e.upload.call(e,i)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var t=this;t.config=i.extend({},t.config,o.config,e),t.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var t=this,e=t.config;e.elem=i(e.elem),e.bindAction=i(e.bindAction),t.file(),t.events()},p.prototype.file=function(){var e=this,t=e.config,n=e.elemFile=i(['"].join("")),o=t.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&t.elem.wrap('
                '),e.isFile()?(e.elemFile=t.elem,t.field=t.elem[0].name):t.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,t=e.config,n=i(''),a=i(['
                ',"
                "].join(""));i("#"+f)[0]||i("body").append(n),t.elem.next().hasClass(c)||(e.elemFile.wrap(a),t.elem.next("."+c).append(function(){var e=[];return layui.each(t.data,function(i,t){t="function"==typeof t?t():t,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return t.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var i=this;window.FileReader&&layui.each(i.chooseFiles,function(i,t){var n=new FileReader;n.readAsDataURL(t),n.onload=function(){e&&e(i,t,this.result)}})},p.prototype.upload=function(e,t){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var t=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&t+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:t,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,i){i="function"==typeof i?i():i,r.append(e,i)}),i.ajax({url:l.url,type:l.method,data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(i){t++,d(e,i),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=i("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var i,t=e.contents().find("body");try{i=t.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}i&&(clearInterval(p.timer),t.html(""),d(0,i))},30)},d=function(e,i){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof i)try{i=JSON.parse(i)}catch(t){return i={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(i,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var i=[];return layui.each(e||o.chooseFiles,function(e,t){i.push(t.name)}),i}(),g={preview:function(e){o.preview(e)},upload:function(e,i){var t={};t[e]=i,o.upload(t)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,i){o.files[e]=i}),o.files},resetFile:function(e,i,t){var n=new File([i],t);o.files=o.files||{},o.files[e]=n}},y=function(){if("choose"!==t&&!l.auto||(l.choose&&l.choose(g),"choose"!==t))return l.before&&l.before(g),a.ie?a.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,i){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(i))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var i=0,t=e||o.files||o.chooseFiles||r.files;return layui.each(t,function(){i++}),i}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,i){if(i.size>1024*l.size){var t=l.size/1024;t=t>=1?t.toFixed(2)+"MB":l.size+"KB",r.value="",F=t}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,t=e.config,o=function(i){e.chooseFiles={},layui.each(i,function(i,t){var n=(new Date).getTime();e.chooseFiles[n+"-"+i]=t})},l=function(i,n){var a=e.elemFile,o=i.length>1?i.length+"个文件":(i[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||t.choose||a.after(''+o+"")};t.elem.off("upload.start").on("upload.start",function(){var a=i(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=i.extend({},t,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||t.elem.off("upload.over").on("upload.over",function(){var e=i(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=i(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=i(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),t.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var i=this.files||[];o(i),t.auto?e.upload():l(i)}),t.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),t.elem.data("haveEvents")||(e.elemFile.on("change",function(){i(this).trigger("upload.change")}),t.elem.on("click",function(){e.isFile()||i(this).trigger("upload.start")}),t.drag&&t.elem.on("dragover",function(e){e.preventDefault(),i(this).trigger("upload.over")}).on("dragleave",function(e){i(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),i(this).trigger("upload.drop",e)}),t.bindAction.on("click",function(){i(this).trigger("upload.action")}),t.elem.data("haveEvents",!0))},o.render=function(e){var i=new p(e);return l.call(i)},e(r,o)});layui.define("layer",function(e){"use strict";var i=layui.$,t=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var t=this;return i.extend(!0,t.config,e),t},u.prototype.verify=function(e){var t=this;return i.extend(!0,t.config.verify,e),t},u.prototype.on=function(e,i){return layui.onevent.call(this,l,e,i)},u.prototype.val=function(e,t){var a=i(r+'[lay-filter="'+e+'"]');a.each(function(e,a){var n=i(this);layui.each(t,function(e,i){var t,a=n.find('[name="'+e+'"]');a[0]&&(t=a[0].type,"checkbox"===t?a[0].checked=i:"radio"===t?a.each(function(){this.value===i&&(this.checked=!0)}):a.val(i))})}),f.render(null,e)},u.prototype.render=function(e,t){var n=this,u=i(r+function(){return t?'[lay-filter="'+t+'"]':""}()),d={select:function(){var e,t="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=u.find("select"),v=function(t,l){i(t.target).parent().hasClass(n)&&!l||(i("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},y=function(t,u,f){var y,p=i(this),m=t.find("."+n),k=m.find("input"),g=t.find("dl"),x=g.children("dd"),b=this.selectedIndex;if(!u){var C=function(){var e=t.offset().top+t.outerHeight()+5-h.scrollTop(),i=g.outerHeight();b=p[0].selectedIndex,t.addClass(a+"ed"),x.removeClass(o),y=null,x.eq(b).addClass(s).siblings().removeClass(s),e+i>h.height()&&e>=i&&t.addClass(a+"up")},w=function(e){t.removeClass(a+"ed "+a+"up"),k.blur(),y=null,e||$(k.val(),function(e){e&&(d=g.find("."+s).html(),k&&k.val(d))})};m.on("click",function(e){t.hasClass(a+"ed")?w():(v(e,!0),C()),g.find("."+r).remove()}),m.find(".layui-edge").on("click",function(){k.focus()}),k.on("keyup",function(e){var i=e.keyCode;9===i&&C()}).on("keydown",function(e){var i=e.keyCode;9===i&&w();var t=function(i,a){var n,l;if(e.preventDefault(),a=function(){return a&&a[0]?a:y&&y[0]?y:x.eq(b)}(),l=a[i](),n=a[i]("dd"),l[0]){if(y=a[i](),!n[0]||n.hasClass(c))return t(i,y);n.addClass(s).siblings().removeClass(s);var r=g.children("dd.layui-this"),o=r.position().top,u=g.height(),d=r.height();o>u&&g.scrollTop(o+g.scrollTop()-u+d-5),o<0&&g.scrollTop(o+g.scrollTop())}};38===i&&t("prev"),40===i&&t("next"),13===i&&(e.preventDefault(),g.children("dd."+s).trigger("click"))});var $=function(e,t,a){var n=0;layui.each(x,function(){var t=i(this),l=t.text(),r=l.indexOf(e)===-1;(""===e||"blur"===a?e!==l:r)&&n++,"keyup"===a&&t[r?"addClass":"removeClass"](o)});var l=n===x.length;return t(l),l},T=function(e){var i=this.value,t=e.keyCode;return 9!==t&&13!==t&&37!==t&&38!==t&&39!==t&&40!==t&&($(i,function(e){e?g.find("."+r)[0]||g.append('

                无匹配项

                '):g.find("."+r).remove()},"keyup"),void(""===i&&g.find("."+r).remove()))};f&&k.on("keyup",T).on("blur",function(t){var a=p[0].selectedIndex;e=k,d=i(p[0].options[a]).html(),setTimeout(function(){$(k.val(),function(e){d||k.val("")},"blur")},200)}),x.on("click",function(){var e=i(this),a=e.attr("lay-value"),n=p.attr("lay-filter");return!e.hasClass(c)&&(e.hasClass("layui-select-tips")?k.val(""):(k.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),p.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:p[0],value:a,othis:t}),w(!0),!1)}),t.find("dl>dt").on("click",function(e){return!1}),i(document).off("click",v).on("click",v)}};f.each(function(e,l){var r=i(this),o=r.next("."+a),u=this.disabled,d=l.value,f=i(l.options[l.selectedIndex]),v=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var h="string"==typeof r.attr("lay-search"),p=v?v.value?t:v.innerHTML||t:t,m=i(['
                ','
                ','','
                ','
                ',function(e){var i=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?i.push("
                "+a.label+"
                "):i.push('
                '+a.innerHTML+"
                "):i.push('
                '+(a.innerHTML||t)+"
                ")}),0===i.length&&i.push('
                没有选项
                '),i.join("")}(r.find("*"))+"
                ","
                "].join(""));o[0]&&o.remove(),r.after(m),y.call(this,m,u,h)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},t=u.find("input[type=checkbox]"),a=function(e,t){var a=i(this);e.on("click",function(){var i=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(t[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(t[1]).find("em").text(n[0])),layui.event.call(a[0],l,t[2]+"("+i+")",{elem:a[0],value:a[0].value,othis:e}))})};t.each(function(t,n){var l=i(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=i(['
                ",function(){var e=n.title.replace(/\s/g,""),i={checkbox:[e?""+n.title+"":"",''].join(""),_switch:""+((n.checked?s[0]:s[1])||"")+""};return i[r]||i.checkbox}(),"
                "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",t=["",""],a=u.find("input[type=radio]"),n=function(a){var n=i(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=i(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(t[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(t[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=i(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var u=i(['
                ',''+t[l.checked?0:1]+"","
                "+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"
                ","
                "].join(""));r.after(u),n.call(this,u)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,i){i()}),n};var d=function(){var e=i(this),a=f.config.verify,s=null,o="layui-form-danger",c={},u=e.parents(r),d=u.find("*[lay-verify]"),v=e.parents("form")[0],h=u.find("input,select,textarea"),y=e.attr("lay-filter");if(layui.each(d,function(e,l){var r=i(this),c=r.attr("lay-verify").split("|"),u=r.attr("lay-verType"),d=r.val();if(r.removeClass(o),layui.each(c,function(e,i){var c,f="",v="function"==typeof a[i];if(a[i]){var c=v?f=a[i](d,l):!a[i][0].test(d);if(f=f||a[i][1],c)return"tips"===u?t.tips(f,function(){return"string"==typeof r.attr("lay-ignore")||"select"!==l.tagName.toLowerCase()&&!/^checkbox|radio$/.test(l.type)?r:r.next()}(),{tips:1}):"alert"===u?t.alert(f,{title:"提示",shadeClose:!0}):t.msg(f,{icon:5,shift:6}),n.android||n.ios||l.focus(),r.addClass(o),s=!0}}),s)return s}),s)return!1;var p={};return layui.each(h,function(e,i){if(i.name=(i.name||"").replace(/^\s*|\s*&/,""),i.name){if(/^.*\[\]$/.test(i.name)){var t=i.name.match(/^(.*)\[\]$/g)[0];p[t]=0|p[t],i.name=i.name.replace(/^(.*)\[\]$/,"$1["+p[t]++ +"]")}/^checkbox|radio$/.test(i.type)&&!i.checked||(c[i.name]=i.value)}}),layui.event.call(this,l,"submit("+y+")",{elem:this,form:v,field:c})},f=new u,v=i(document),h=i(window);f.render(),v.on("reset",r,function(){var e=i(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),v.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)});layui.define("jquery",function(e){"use strict";var o=layui.$,a=layui.hint(),i="layui-tree-enter",r=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};r.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},r.prototype.tree=function(e,a){var i=this,r=i.options,n=a||r.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o('
                  '),s=o(["
                • ",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+"":""}(),function(){return r.check?''+("checkbox"===r.check?t.checkbox[0]:"radio"===r.check?t.radio[0]:"")+"":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+"")+(""+(n.name||"未命名")+"")}(),"
                • "].join(""));l&&(s.append(c),i.tree(c,n.children)),e.append(s),"function"==typeof r.click&&i.click(s,n),i.spread(s,n),r.drag&&i.drag(s,n)})},r.prototype.click=function(e,o){var a=this,i=a.options;e.children("a").on("click",function(e){layui.stope(e),i.click(o)})},r.prototype.spread=function(e,o){var a=this,i=(a.options,e.children(".layui-tree-spread")),r=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),r.removeClass("layui-show"),i.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),r.addClass("layui-show"),i.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};r[0]&&(i.on("click",l),n.on("dblclick",l))},r.prototype.on=function(e){var a=this,r=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),r.drag&&o(document).on("mousemove",function(e){var i=a.move;if(i.from){var r=(i.to,o('
                  '));e.preventDefault(),o("."+t)[0]||o("body").append(r);var n=o("."+t)[0]?o("."+t):r;n.addClass("layui-show").html(i.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(i),e.to&&e.to.elem.children("a").removeClass(i),a.move={},o("."+t).remove())})},r.prototype.move={},r.prototype.drag=function(e,a){var r=this,t=(r.options,e.children("a")),n=function(){var t=o(this),n=r.move;n.from&&(n.to={item:a,elem:e},t.addClass(i))};t.on("mousedown",function(){var o=r.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=r.move;a.from&&(delete a.to,e.removeClass(i))})},e("tree",function(e){var i=new r(e=e||{}),t=o(e.elem);return t[0]?void i.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})});layui.define(["laytpl","laypage","layer","form"],function(e){"use strict";var t=layui.$,i=layui.laytpl,a=layui.laypage,l=layui.layer,n=layui.form,o=layui.hint(),r=layui.device(),d={config:{checkName:"LAY_CHECKED",indexName:"LAY_TABLE_INDEX"},cache:{},index:layui.table?layui.table.index+1e4:0,set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,s,e,t)}},c=function(){var e=this,t=e.config,i=t.id;return i&&(c.config[i]=t),{reload:function(t){e.reload.call(e,t)},config:t}},s="table",u=".layui-table",h="layui-hide",f="layui-none",y="layui-table-view",p=".layui-table-header",m=".layui-table-body",v=".layui-table-main",g=".layui-table-fixed",x=".layui-table-fixed-l",b=".layui-table-fixed-r",k=".layui-table-tool",C=".layui-table-page",w=".layui-table-sort",N="layui-table-edit",T="layui-table-hover",F=function(e){var t='{{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}}';return e=e||{},['',"","{{# layui.each(d.data.cols, function(i1, item1){ }}","","{{# layui.each(item1, function(i2, item2){ }}",'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}','{{# if(item2.fixed === "right"){ right = true; } }}',function(){return e.fixed&&"right"!==e.fixed?'{{# if(item2.fixed && item2.fixed !== "right"){ }}':"right"===e.fixed?'{{# if(item2.fixed === "right"){ }}':""}(),'",e.fixed?"{{# }; }}":"","{{# }); }}","","{{# }); }}","","
                  ','
                  1){ }}","group","{{# } else { }}","{{d.index}}-{{item2.field || i2}}",'{{# if(item2.type !== "normal"){ }}'," laytable-cell-{{ item2.type }}","{{# } }}","{{# } }}",'" {{#if(item2.align){}}align="{{item2.align}}"{{#}}}>','{{# if(item2.type === "checkbox"){ }}','',"{{# } else { }}",'{{item2.title||""}}',"{{# if(!(item2.colspan > 1) && item2.sort){ }}",'',"{{# } }}","{{# } }}","
                  ","
                  "].join("")},W=['',"","
                  "].join(""),z=['
                  ',"{{# if(d.data.toolbar){ }}",'
                  ',"{{# } }}",'
                  ',"{{# var left, right; }}",'
                  ',F(),"
                  ",'
                  ',W,"
                  ","{{# if(left){ }}",'
                  ','
                  ',F({fixed:!0}),"
                  ",'
                  ',W,"
                  ","
                  ","{{# }; }}","{{# if(right){ }}",'
                  ','
                  ',F({fixed:"right"}),'
                  ',"
                  ",'
                  ',W,"
                  ","
                  ","{{# }; }}","
                  ","{{# if(d.data.page){ }}",'
                  ','
                  ',"
                  ","{{# } }}","","
                  "].join(""),A=t(window),S=t(document),M=function(e){var i=this;i.index=++d.index,i.config=t.extend({},i.config,d.config,e),i.render()};M.prototype.config={limit:10,loading:!0,cellMinWidth:60,text:{none:"无数据"}},M.prototype.render=function(){var e=this,a=e.config;if(a.elem=t(a.elem),a.where=a.where||{},a.id=a.id||a.elem.attr("id"),a.request=t.extend({pageName:"page",limitName:"limit"},a.request),a.response=t.extend({statusName:"code",statusCode:0,msgName:"msg",dataName:"data",countName:"count"},a.response),"object"==typeof a.page&&(a.limit=a.page.limit||a.limit,a.limits=a.page.limits||a.limits,e.page=a.page.curr=a.page.curr||1,delete a.page.elem,delete a.page.jump),!a.elem[0])return e;e.setArea();var l=a.elem,n=l.next("."+y),o=e.elem=t(i(z).render({VIEW_CLASS:y,data:a,index:e.index}));if(a.index=e.index,n[0]&&n.remove(),l.after(o),e.layHeader=o.find(p),e.layMain=o.find(v),e.layBody=o.find(m),e.layFixed=o.find(g),e.layFixLeft=o.find(x),e.layFixRight=o.find(b),e.layTool=o.find(k),e.layPage=o.find(C),e.layTool.html(i(t(a.toolbar).html()||"").render(a)),a.height&&e.fullSize(),a.cols.length>1){var r=e.layFixed.find(p).find("th");r.height(e.layHeader.height()-1-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom")))}e.pullData(e.page),e.events()},M.prototype.initOpts=function(e){var t=this,i=(t.config,{checkbox:48,space:15,numbers:40});e.checkbox&&(e.type="checkbox"),e.space&&(e.type="space"),e.type||(e.type="normal"),"normal"!==e.type&&(e.unresize=!0,e.width=e.width||i[e.type])},M.prototype.setArea=function(){var e=this,t=e.config,i=0,a=0,l=0,n=0,o=t.width||function(){var e=function(i){var a,l;i=i||t.elem.parent(),a=i.width();try{l="none"===i.css("display")}catch(n){}return!i[0]||a&&!l?a:e(i.parent())};return e()}();e.eachCols(function(){i++}),o-=function(){return"line"===t.skin||"nob"===t.skin?2:i+1}(),layui.each(t.cols,function(t,i){layui.each(i,function(t,l){var r;return l?(e.initOpts(l),r=l.width||0,void(l.colspan>1||(/\d+%$/.test(r)?l.width=r=Math.floor(parseFloat(r)/100*o):r||(l.width=r=0,a++),n+=r))):void i.splice(t,1)})}),e.autoColNums=a,o>n&&a&&(l=(o-n)/a),layui.each(t.cols,function(e,i){layui.each(i,function(e,i){var a=i.minWidth||t.cellMinWidth;i.colspan>1||0===i.width&&(i.width=Math.floor(l>=a?l:a))})}),t.height&&/^full-\d+$/.test(t.height)&&(e.fullHeightGap=t.height.split("-")[1],t.height=A.height()-e.fullHeightGap)},M.prototype.reload=function(e){var i=this;i.config.data&&i.config.data.constructor===Array&&delete i.config.data,i.config=t.extend({},i.config,e),i.render()},M.prototype.page=1,M.prototype.pullData=function(e,i){var a=this,n=a.config,o=n.request,r=n.response,d=function(){"object"==typeof n.initSort&&a.sort(n.initSort.field,n.initSort.type)};if(a.startTime=(new Date).getTime(),n.url){var c={};c[o.pageName]=e,c[o.limitName]=n.limit;var s=t.extend(c,n.where);n.contentType&&0==n.contentType.indexOf("application/json")&&(s=JSON.stringify(s)),t.ajax({type:n.method||"get",url:n.url,contentType:n.contentType,data:s,dataType:"json",headers:n.headers||{},success:function(t){t[r.statusName]!=r.statusCode?(a.renderForm(),a.layMain.html('
                  '+(t[r.msgName]||"返回的数据状态异常")+"
                  ")):(a.renderData(t,e,t[r.countName]),d(),n.time=(new Date).getTime()-a.startTime+" ms"),i&&l.close(i),"function"==typeof n.done&&n.done(t,e,t[r.countName])},error:function(e,t){a.layMain.html('
                  数据接口请求异常
                  '),a.renderForm(),i&&l.close(i)}})}else if(n.data&&n.data.constructor===Array){var u={},h=e*n.limit-n.limit;u[r.dataName]=n.data.concat().splice(h,n.limit),u[r.countName]=n.data.length,a.renderData(u,e,n.data.length),d(),"function"==typeof n.done&&n.done(u,e,u[r.countName])}},M.prototype.eachCols=function(e){var i=t.extend(!0,[],this.config.cols),a=[],l=0;layui.each(i,function(e,t){layui.each(t,function(t,n){if(n.colspan>1){var o=0;l++,n.CHILD_COLS=[],layui.each(i[e+1],function(e,t){t.PARENT_COL||o==n.colspan||(t.PARENT_COL=l,n.CHILD_COLS.push(t),o+=t.colspan>1?t.colspan:1)})}n.PARENT_COL||a.push(n)})});var n=function(t){layui.each(t||a,function(t,i){return i.CHILD_COLS?n(i.CHILD_COLS):void e(t,i)})};n()},M.prototype.renderData=function(e,n,o,r){var c=this,s=c.config,u=e[s.response.dataName]||[],y=[],p=[],m=[],v=function(){return!r&&c.sortKey?c.sort(c.sortKey.field,c.sortKey.sort,!0):(layui.each(u,function(e,a){var l=[],o=[],u=[],h=e+s.limit*(n-1)+1;0!==a.length&&(r||(a[d.config.indexName]=e),c.eachCols(function(e,n){var r=n.field||e,f=a[r];c.getColElem(c.layHeader,r);if(void 0!==f&&null!==f||(f=""),!(n.colspan>1)){var y=['",'
                  '+function(){var e=t.extend(!0,{LAY_INDEX:h},a);return"checkbox"===n.type?'":"numbers"===n.type?h:n.toolbar?i(t(n.toolbar).html()||"").render(e):n.templet?function(){return"function"==typeof n.templet?n.templet(e):i(t(n.templet).html()||String(f)).render(e)}():f}(),"
                  "].join("");l.push(y),n.fixed&&"right"!==n.fixed&&o.push(y),"right"===n.fixed&&u.push(y)}}),y.push(''+l.join("")+""),p.push(''+o.join("")+""),m.push(''+u.join("")+""))}),c.layBody.scrollTop(0),c.layMain.find("."+f).remove(),c.layMain.find("tbody").html(y.join("")),c.layFixLeft.find("tbody").html(p.join("")),c.layFixRight.find("tbody").html(m.join("")),c.renderForm(),c.syncCheckAll(),c.haveInit?c.scrollPatch():setTimeout(function(){c.scrollPatch()},50),c.haveInit=!0,void l.close(c.tipsIndex))};return c.key=s.id||s.index,d.cache[c.key]=u,c.layPage[0===u.length&&1==n?"addClass":"removeClass"](h),r?v():0===u.length?(c.renderForm(),c.layFixed.remove(),c.layMain.find("tbody").html(""),c.layMain.find("."+f).remove(),c.layMain.append('
                  '+s.text.none+"
                  ")):(v(),void(s.page&&(s.page=t.extend({elem:"layui-table-page"+s.index,count:o,limit:s.limit,limits:s.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(c.page=e.curr,s.limit=e.limit,c.pullData(e.curr,c.loading()))}},s.page),s.page.count=o,a.render(s.page))))},M.prototype.getColElem=function(e,t){var i=this,a=i.config;return e.eq(0).find(".laytable-cell-"+(a.index+"-"+t)+":eq(0)")},M.prototype.renderForm=function(e){n.render(e,"LAY-table-"+this.index)},M.prototype.sort=function(e,i,a,l){var n,r,c=this,u={},h=c.config,f=h.elem.attr("lay-filter"),y=d.cache[c.key];"string"==typeof e&&c.layHeader.find("th").each(function(i,a){var l=t(this),o=l.data("field");if(o===e)return e=l,n=o,!1});try{var n=n||e.data("field");if(c.sortKey&&!a&&n===c.sortKey.field&&i===c.sortKey.sort)return;var p=c.layHeader.find("th .laytable-cell-"+h.index+"-"+n).find(w);c.layHeader.find("th").find(w).removeAttr("lay-sort"),p.attr("lay-sort",i||null),c.layFixed.find("th")}catch(m){return o.error("Table modules: Did not match to field")}c.sortKey={field:n,sort:i},"asc"===i?r=layui.sort(y,n):"desc"===i?r=layui.sort(y,n,!0):(r=layui.sort(y,d.config.indexName),delete c.sortKey),u[h.response.dataName]=r,c.renderData(u,c.page,c.count,!0),l&&layui.event.call(e,s,"sort("+f+")",{field:n,type:i})},M.prototype.loading=function(){var e=this,t=e.config;if(t.loading&&t.url)return l.msg("数据请求中",{icon:16,offset:[e.elem.offset().top+e.elem.height()/2-35-A.scrollTop()+"px",e.elem.offset().left+e.elem.width()/2-90-A.scrollLeft()+"px"],time:-1,anim:-1,fixed:!1})},M.prototype.setCheckData=function(e,t){var i=this,a=i.config,l=d.cache[i.key];l[e]&&l[e].constructor!==Array&&(l[e][a.checkName]=t)},M.prototype.syncCheckAll=function(){var e=this,t=e.config,i=e.layHeader.find('input[name="layTableCheckbox"]'),a=function(i){return e.eachCols(function(e,a){"checkbox"===a.type&&(a[t.checkName]=i)}),i};i[0]&&(d.checkStatus(e.key).isAll?(i[0].checked||(i.prop("checked",!0),e.renderForm("checkbox")),a(!0)):(i[0].checked&&(i.prop("checked",!1),e.renderForm("checkbox")),a(!1)))},M.prototype.getCssRule=function(e,t){var i=this,a=i.elem.find("style")[0],l=a.sheet||a.styleSheet||{},n=l.cssRules||l.rules;layui.each(n,function(a,l){if(l.selectorText===".laytable-cell-"+i.index+"-"+e)return t(l),!0})},M.prototype.fullSize=function(){var e,t=this,i=t.config,a=i.height;t.fullHeightGap&&(a=A.height()-t.fullHeightGap,a<135&&(a=135),t.elem.css("height",a)),e=parseFloat(a)-parseFloat(t.layHeader.height())-1,i.toolbar&&(e-=t.layTool.outerHeight()),i.page&&(e=e-t.layPage.outerHeight()-1),t.layMain.css("height",e)},M.prototype.getScrollWidth=function(e){var t=0;return e?t=e.offsetWidth-e.clientWidth:(e=document.createElement("div"),e.style.width="100px",e.style.height="100px",e.style.overflowY="scroll",document.body.appendChild(e),t=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),t},M.prototype.scrollPatch=function(){var e=this,i=e.layMain.children("table"),a=e.layMain.width()-e.layMain.prop("clientWidth"),l=e.layMain.height()-e.layMain.prop("clientHeight"),n=e.getScrollWidth(e.layMain[0]),o=i.outerWidth()-e.layMain.width();if(e.autoColNums&&o<5&&!e.scrollPatchWStatus){var r=e.layHeader.eq(0).find("thead th:last-child"),d=r.data("field");e.getCssRule(d,function(t){var i=t.style.width||r.outerWidth();t.style.width=parseFloat(i)-n-o+"px",e.layMain.height()-e.layMain.prop("clientHeight")>0&&(t.style.width=parseFloat(t.style.width)-1+"px"),e.scrollPatchWStatus=!0})}if(a&&l){if(!e.elem.find(".layui-table-patch")[0]){var c=t('
                  ');c.find("div").css({width:a}),e.layHeader.eq(0).find("thead tr").append(c)}}else e.layHeader.eq(0).find(".layui-table-patch").remove();var s=e.layMain.height(),u=s-l;e.layFixed.find(m).css("height",i.height()>u?u:"auto"),e.layFixRight[o>0?"removeClass":"addClass"](h),e.layFixRight.css("right",a-1)},M.prototype.events=function(){var e,a=this,n=a.config,o=t("body"),c={},u=a.layHeader.find("th"),h=".layui-table-cell",f=n.elem.attr("lay-filter");u.on("mousemove",function(e){var i=t(this),a=i.offset().left,l=e.clientX-a;i.attr("colspan")>1||i.data("unresize")||c.resizeStart||(c.allowResize=i.width()-l<=10,o.css("cursor",c.allowResize?"col-resize":""))}).on("mouseleave",function(){t(this);c.resizeStart||o.css("cursor","")}).on("mousedown",function(e){var i=t(this);if(c.allowResize){var l=i.data("field");e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],a.getCssRule(l,function(e){var t=e.style.width||i.outerWidth();c.rule=e,c.ruleWidth=parseFloat(t),c.minWidth=i.data("minwidth")||n.cellMinWidth})}}),S.on("mousemove",function(t){if(c.resizeStart){if(t.preventDefault(),c.rule){var i=c.ruleWidth+t.clientX-c.offset[0];i');d[0].value=e.data("content")||o.text(),e.find("."+N)[0]||e.append(d),d.focus()}else o.find(".layui-form-switch,.layui-form-checkbox")[0]||Math.round(o.prop("scrollWidth"))>Math.round(o.outerWidth())&&(a.tipsIndex=l.tips(['
                  ',o.html(),"
                  ",''].join(""),o[0],{tips:[3,""],time:-1,anim:-1,maxWidth:r.ios||r.android?300:600,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){l.close(t)})}}))}),a.layBody.on("click","*[lay-event]",function(){var e=t(this),l=e.parents("tr").eq(0).data("index"),n=a.layBody.find('tr[data-index="'+l+'"]'),o="layui-table-click",r=d.cache[a.key][l];layui.event.call(this,s,"tool("+f+")",{data:d.clearCacheKey(r),event:e.attr("lay-event"),tr:n,del:function(){d.cache[a.key][l]=[],n.remove(),a.scrollPatch()},update:function(e){e=e||{},layui.each(e,function(e,l){if(e in r){var o,d=n.children('td[data-field="'+e+'"]');r[e]=l,a.eachCols(function(t,i){i.field==e&&i.templet&&(o=i.templet)}),d.children(h).html(o?i(t(o).html()||l).render(r):l),d.data("content",l)}})}}),n.addClass(o).siblings("tr").removeClass(o)}),a.layMain.on("scroll",function(){var e=t(this),i=e.scrollLeft(),n=e.scrollTop();a.layHeader.scrollLeft(i),a.layFixed.find(m).scrollTop(n),l.close(a.tipsIndex)}),A.on("resize",function(){a.fullSize(),a.scrollPatch()})},d.init=function(e,i){i=i||{};var a=this,l=t(e?'table[lay-filter="'+e+'"]':u+"[lay-data]"),n="Table element property lay-data configuration item has a syntax error: ";return l.each(function(){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){o.error(n+l)}var c=[],s=t.extend({elem:this,cols:[],data:[],skin:a.attr("lay-skin"),size:a.attr("lay-size"),even:"string"==typeof a.attr("lay-even")},d.config,i,l);e&&a.hide(),a.find("thead>tr").each(function(e){s.cols[e]=[],t(this).children().each(function(i){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){return o.error(n+l)}var d=t.extend({title:a.text(),colspan:a.attr("colspan")||0,rowspan:a.attr("rowspan")||0},l);d.colspan<2&&c.push(d),s.cols[e].push(d)})}),a.find("tbody>tr").each(function(e){var i=t(this),a={};i.children("td").each(function(e,i){var l=t(this),n=l.data("field");if(n)return a[n]=l.html()}),layui.each(c,function(e,t){var l=i.children("td").eq(e);a[t.field]=l.html()}),s.data[e]=a}),d.render(s)}),a},d.checkStatus=function(e){var t=0,i=0,a=[],l=d.cache[e]||[];return layui.each(l,function(e,l){return l.constructor===Array?void i++:void(l[d.config.checkName]&&(t++,a.push(d.clearCacheKey(l))))}),{data:a,isAll:!!l.length&&t===l.length-i}},c.config={},d.reload=function(e,i){var a=c.config[e];return i=i||{},a?(i.data&&i.data.constructor===Array&&delete a.data,d.render(t.extend(!0,{},a,i))):o.error("The ID option was not found in the table instance")},d.render=function(e){var t=new M(e);return c.call(t)},d.clearCacheKey=function(e){return e=t.extend({},e),delete e[d.config.checkName],delete e[d.config.indexName],e},d.init(),e(s,d)});layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
                    ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
                  "].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a",u=1;u<=i.length;u++){var r='
                • ";i.half&&parseInt(i.value)!==i.value&&u==Math.ceil(i.value)?n=n+'
                • ":n+=r}n+=""+(i.text?''+i.value+"星":"")+"";var c=i.elem,f=c.next("."+t);f[0]&&f.remove(),e.elemTemp=a(n),i.span=e.elemTemp.next("span"),i.setText&&i.setText(i.value),c.html(e.elemTemp),c.addClass("layui-inline"),i.readonly||e.action()},v.prototype.setvalue=function(e){var a=this,i=a.config;i.value=e,a.render()},v.prototype.action=function(){var e=this,i=e.config,l=e.elemTemp,n=l.find("i").width();l.children("li").each(function(e){var t=e+1,v=a(this);v.on("click",function(e){if(i.value=t,i.half){var o=e.pageX-a(this).offset().left;o<=n/2&&(i.value=i.value-.5)}i.text&&l.next("span").text(i.value+"星"),i.choose&&i.choose(i.value),i.setText&&i.setText(i.value)}),v.on("mousemove",function(e){if(l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+t+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half){var c=e.pageX-a(this).offset().left;c<=n/2&&v.children("i").addClass(u).removeClass(s)}}),v.on("mouseleave",function(){l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+Math.floor(i.value)+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half&&parseInt(i.value)!==i.value&&l.children("li:eq("+Math.floor(i.value)+")").children("i").addClass(u).removeClass(c)})})},v.prototype.events=function(){var e=this;e.config},i.render=function(e){var a=new v(e);return l.call(a)},e(n,i)});layui.define("jquery",function(e){"use strict";var t=layui.$,i={fixbar:function(e){var i,a,o="layui-fixbar",r="layui-fixbar-top",l=t(document),n=t("body");e=t.extend({showHeight:200},e),e.bar1=e.bar1===!0?"":e.bar1,e.bar2=e.bar2===!0?"":e.bar2,e.bgcolor=e.bgcolor?"background-color:"+e.bgcolor:"";var c=[e.bar1,e.bar2,""],g=t(['
                    ',e.bar1?'
                  • '+c[0]+"
                  • ":"",e.bar2?'
                  • '+c[1]+"
                  • ":"",'
                  • '+c[2]+"
                  • ","
                  "].join("")),u=g.find("."+r),s=function(){var t=l.scrollTop();t>=e.showHeight?i||(u.show(),i=1):i&&(u.hide(),i=0)};t("."+o)[0]||("object"==typeof e.css&&g.css(e.css),n.append(g),s(),g.find("li").on("click",function(){var i=t(this),a=i.attr("lay-type");"top"===a&&t("html,body").animate({scrollTop:0},200),e.click&&e.click.call(this,a)}),l.on("scroll",function(){clearTimeout(a),a=setTimeout(function(){s()},100)}))},countdown:function(e,t,i){var a=this,o="function"==typeof t,r=new Date(e).getTime(),l=new Date(!t||o?(new Date).getTime():t).getTime(),n=r-l,c=[Math.floor(n/864e5),Math.floor(n/36e5)%24,Math.floor(n/6e4)%60,Math.floor(n/1e3)%60];o&&(i=t);var g=setTimeout(function(){a.countdown(e,l+1e3,i)},1e3);return i&&i(n>0?c:[0,0,0,0],t,g),n<=0&&clearTimeout(g),g},timeAgo:function(e,t){var i=this,a=[[],[]],o=(new Date).getTime()-new Date(e).getTime();return o>6912e5?(o=new Date(e),a[0][0]=i.digit(o.getFullYear(),4),a[0][1]=i.digit(o.getMonth()+1),a[0][2]=i.digit(o.getDate()),t||(a[1][0]=i.digit(o.getHours()),a[1][1]=i.digit(o.getMinutes()),a[1][2]=i.digit(o.getSeconds())),a[0].join("-")+" "+a[1].join(":")):o>=864e5?(o/1e3/60/60/24|0)+"天前":o>=36e5?(o/1e3/60/60|0)+"小时前":o>=12e4?(o/1e3/60|0)+"分钟前":o<0?"未来":"刚刚"},digit:function(e,t){var i="";e=String(e),t=t||2;for(var a=e.length;a/g,">").replace(/'/g,"'").replace(/"/g,""")}};e("util",i)});layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)});layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
                  1. '+o.replace(/[\r\t\n]+/g,"
                  2. ")+"
                  "),c.find(">.layui-code-h3")[0]||c.prepend('

                  '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

                  ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss");layui.define(["layer","form"],function(t){"use strict";var e=layui.$,i=layui.layer,a=layui.form,l=(layui.hint(),layui.device()),n="layedit",o="layui-show",r="layui-disabled",c=function(){var t=this;t.index=0,t.config={tool:["strong","italic","underline","del","|","left","center","right","|","link","unlink","face","image"],hideTool:[],height:280}};c.prototype.set=function(t){var i=this;return e.extend(!0,i.config,t),i},c.prototype.on=function(t,e){return layui.onevent(n,t,e)},c.prototype.build=function(t,i){i=i||{};var a=this,n=a.config,r="layui-layedit",c=e("string"==typeof t?"#"+t:t),u="LAY_layedit_"+ ++a.index,d=c.next("."+r),y=e.extend({},n,i),f=function(){var t=[],e={};return layui.each(y.hideTool,function(t,i){e[i]=!0}),layui.each(y.tool,function(i,a){C[a]&&!e[a]&&t.push(C[a])}),t.join("")}(),m=e(['
                  ','
                  '+f+"
                  ",'
                  ','',"
                  ","
                  "].join(""));return l.ie&&l.ie<8?c.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),s.call(a,m,c[0],y),c.addClass("layui-hide").after(m),a.index)},c.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},c.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},c.prototype.setContent=function(t,i,a){var l=u(t);l[0]&&(a?e(l[0].document.body).append(i):e(l[0].document.body).html(i),layedit.sync(t))},c.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},c.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var s=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),c=o.find("head"),s=e([""].join("")),u=o.find("body");c.append(s),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,c=e(r.body);c.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

                  ")}}),e(n).parents("form").on("submit",function(){var t=c.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),c.on("paste",function(e){r.execCommand("formatBlock",!1,"

                  "),setTimeout(function(){f.call(t,c),n.value=c.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),c={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o.render({url:r.url,method:r.type,elem:e(n).find("input")[0],done:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},s=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

                  "),setTimeout(function(){o.focus()},10)):c[a]&&c[a].call(this,u),h.call(t,s,i)}},d=/image/;s.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,s),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

                    ','
                  • ','','
                    ','',"
                    ","
                  • ",'
                  • ','','
                    ','",'","
                    ","
                  • ",'
                  • ','','',"
                  • ","
                  "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
                • '+e+'
                • ')}),'
                    '+t.join("")+"
                  "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
                    ','
                  • ','','
                    ','","
                    ","
                  • ",'
                  • ','','
                    ','',"
                    ","
                  • ",'
                  • ','','',"
                  • ","
                  "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new c;t(n,w)}); \ No newline at end of file diff --git a/static/plugs/layui/layui.js b/static/plugs/layui/layui.js index ed8835fa6..0806c132b 100644 --- a/static/plugs/layui/layui.js +++ b/static/plugs/layui/layui.js @@ -1,2 +1,2 @@ -/** layui-v1.0.9_rls MIT License By http://www.layui.com */ - ;!function(e){"use strict";var t=function(){this.v="1.0.9_rls"};t.fn=t.prototype;var n=document,o=t.fn.cache={},i=function(){var e=n.scripts,t=e[e.length-1].src;return t.substring(0,t.lastIndexOf("/")+1)}(),r=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},l="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),a={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",util:"modules/util",flow:"modules/flow",carousel:"modules/carousel",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"dest/layui.all"};o.modules={},o.status={},o.timeout=10,o.event={},t.fn.define=function(e,t){var n=this,i="function"==typeof e,r=function(){return"function"==typeof t&&t(function(e,t){layui[e]=t,o.status[e]=!0}),this};return i&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?r.call(n):(n.use(e,r),n)},t.fn.use=function(e,t,u){function s(e,t){var n="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||n.test((e.currentTarget||e.srcElement).readyState))&&(o.modules[m]=t,y.removeChild(p),function i(){return++v>1e3*o.timeout/4?r(m+" is not a valid module"):void(o.status[m]?c():setTimeout(i,4))}())}function c(){u.push(layui[m]),e.length>1?f.use(e.slice(1),t,u):"function"==typeof t&&t.apply(layui,u)}var f=this,d=o.dir=o.dir?o.dir:i,y=n.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(f.each(e,function(t,n){"jquery"===n&&e.splice(t,1)}),layui.jquery=jQuery);var m=e[0],v=0;if(u=u||[],o.host=o.host||(d.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&a[m]||!layui["layui.all"]&&layui["layui.mobile"]&&a[m])return c(),f;var p=n.createElement("script"),h=(a[m]?d+"lay/":o.base||"")+(f.modules[m]||m)+".js";return p.async=!0,p.charset="utf-8",p.src=h+function(){var e=o.version===!0?o.v||(new Date).getTime():o.version||"";return e?"?v="+e:""}(),o.modules[m]?!function g(){return++v>1e3*o.timeout/4?r(m+" is not a valid module"):void("string"==typeof o.modules[m]&&o.status[m]?c():setTimeout(g,4))}():(y.appendChild(p),!p.attachEvent||p.attachEvent.toString&&p.attachEvent.toString().indexOf("[native code")<0||l?p.addEventListener("load",function(e){s(e,h)},!1):p.attachEvent("onreadystatechange",function(e){s(e,h)})),o.modules[m]=h,f},t.fn.getStyle=function(t,n){var o=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return o[o.getPropertyValue?"getPropertyValue":"getAttribute"](n)},t.fn.link=function(e,t,i){var l=this,a=n.createElement("link"),u=n.getElementsByTagName("head")[0];"string"==typeof t&&(i=t);var s=(i||e).replace(/\.|\//g,""),c=a.id="layuicss-"+s,f=0;a.rel="stylesheet",a.href=e+(o.debug?"?v="+(new Date).getTime():""),a.media="all",n.getElementById(c)||u.appendChild(a),"function"==typeof t&&!function d(){return++f>1e3*o.timeout/100?r(e+" timeout"):void(1989===parseInt(l.getStyle(n.getElementById(c),"width"))?function(){t()}():setTimeout(d,100))}()},t.fn.addcss=function(e,t,n){layui.link(o.dir+"css/"+e,t,n)},t.fn.img=function(e,t,n){var o=new Image;return o.src=e,o.complete?t(o):(o.onload=function(){o.onload=null,t(o)},void(o.onerror=function(e){o.onerror=null,n(e)}))},t.fn.config=function(e){e=e||{};for(var t in e)o[t]=e[t];return this},t.fn.modules=function(){var e={};for(var t in a)e[t]=a[t];return e}(),t.fn.extend=function(e){var t=this;e=e||{};for(var n in e)t[n]||t.modules[n]?r("模块名 "+n+" 已被占用"):t.modules[n]=e[n];return t},t.fn.router=function(e){for(var t,n=(e||location.hash).replace(/^#/,"").split("/")||[],o={dir:[]},i=0;i0;r--)if("interactive"===n[r].readyState){e=n[r].src;break}return e||n[o].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),a=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},i="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",rate:"modules/rate",carousel:"modules/carousel",flow:"modules/flow",util:"modules/util",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};o.prototype.cache=n,o.prototype.define=function(e,t){var o=this,r="function"==typeof e,a=function(){var e=function(e,t){layui[e]=t,n.status[e]=!0};return"function"==typeof t&&t(function(o,r){e(o,r),n.callback[o]=function(){t(e)}}),this};return r&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?a.call(o):(o.use(e,a),o)},o.prototype.use=function(e,o,l){function s(e,t){var o="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||o.test((e.currentTarget||e.srcElement).readyState))&&(n.modules[d]=t,f.removeChild(v),function r(){return++m>1e3*n.timeout/4?a(d+" is not a valid module"):void(n.status[d]?c():setTimeout(r,4))}())}function c(){l.push(layui[d]),e.length>1?y.use(e.slice(1),o,l):"function"==typeof o&&o.apply(layui,l)}var y=this,p=n.dir=n.dir?n.dir:r,f=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,n){"jquery"===n&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var d=e[0],m=0;if(l=l||[],n.host=n.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[d]||!layui["layui.all"]&&layui["layui.mobile"]&&u[d])return c(),y;if(n.modules[d])!function g(){return++m>1e3*n.timeout/4?a(d+" is not a valid module"):void("string"==typeof n.modules[d]&&n.status[d]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[d]?p+"lay/":/^\{\/\}/.test(y.modules[d])?"":n.base||"")+(y.modules[d]||d)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=n.version===!0?n.v||(new Date).getTime():n.version||"";return e?"?v="+e:""}(),f.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||i?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),n.modules[d]=h}return y},o.prototype.getStyle=function(t,n){var o=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return o[o.getPropertyValue?"getPropertyValue":"getAttribute"](n)},o.prototype.link=function(e,o,r){var i=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof o&&(r=o);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(n.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof o?i:(function p(){return++y>1e3*n.timeout/100?a(e+" timeout"):void(1989===parseInt(i.getStyle(t.getElementById(c),"width"))?function(){o()}():setTimeout(p,100))}(),i)},n.callback={},o.prototype.factory=function(e){if(layui[e])return"function"==typeof n.callback[e]?n.callback[e]:null},o.prototype.addcss=function(e,t,o){return layui.link(n.dir+"css/"+e,t,o)},o.prototype.img=function(e,t,n){var o=new Image;return o.src=e,o.complete?t(o):(o.onload=function(){o.onload=null,"function"==typeof t&&t(o)},void(o.onerror=function(e){o.onerror=null,"function"==typeof n&&n(e)}))},o.prototype.config=function(e){e=e||{};for(var t in e)n[t]=e[t];return this},o.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),o.prototype.extend=function(e){var t=this;e=e||{};for(var n in e)t[n]||t.modules[n]?a("模块名 "+n+" 已被占用"):t.modules[n]=e[n];return t},o.prototype.router=function(e){var t=this,e=e||location.hash,n={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),n.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),n.search[t[0]]=t[1]}():n.path.push(t)}),n):n},o.prototype.data=function(t,n,o){if(t=t||"layui",o=o||localStorage,e.JSON&&e.JSON.parse){if(null===n)return delete o[t];n="object"==typeof n?n:{key:n};try{var r=JSON.parse(o[t])}catch(a){var r={}}return"value"in n&&(r[n.key]=n.value),n.remove&&delete r[n.key],o[t]=JSON.stringify(r),n.key?r[n.key]:r}},o.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},o.prototype.device=function(t){var n=navigator.userAgent.toLowerCase(),o=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(n.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(n)?"windows":/linux/.test(n)?"linux":/iphone|ipod|ipad|ios/.test(n)?"ios":/mac/.test(n)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((n.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:o("micromessenger")};return t&&!r[t]&&(r[t]=o(t)),r.android=/android/.test(n),r.ios="ios"===r.os,r},o.prototype.hint=function(){return{error:a}},o.prototype.each=function(e,t){var n,o=this;if("function"!=typeof t)return o;if(e=e||[],e.constructor===Object){for(n in e)if(t.call(e[n],n,e[n]))break}else for(n=0;na?1:r-1&&(!e[i]||!t(e[i],i,e));i-=1);}}function hasProp(e,t){return hasOwn.call(e,t)}function getOwn(e,t){return hasProp(e,t)&&e[t]}function eachProp(e,t){var i;for(i in e)if(hasProp(e,i)&&t(e[i],i))break}function mixin(e,t,i,r){return t&&eachProp(t,function(t,n){!i&&hasProp(e,n)||(!r||"object"!=typeof t||!t||isArray(t)||isFunction(t)||t instanceof RegExp?e[n]=t:(e[n]||(e[n]={}),mixin(e[n],t,i,r)))}),e}function bind(e,t){return function(){return t.apply(e,arguments)}}function scripts(){return document.getElementsByTagName("script")}function defaultOnError(e){throw e}function getGlobal(e){if(!e)return e;var t=global;return each(e.split("."),function(e){t=t[e]}),t}function makeError(e,t,i,r){var n=new Error(t+"\nhttp://requirejs.org/docs/errors.html#"+e);return n.requireType=e,n.requireModules=r,i&&(n.originalError=i),n}function newContext(e){function t(e){var t,i;for(t=0;t0&&(e.splice(t-1,2),t-=2)}}function i(e,i,r){var n,o,a,s,u,c,d,p,f,l,h,m,g=i&&i.split("/"),v=y.map,x=v&&v["*"];if(e&&(e=e.split("/"),d=e.length-1,y.nodeIdCompat&&jsSuffixRegExp.test(e[d])&&(e[d]=e[d].replace(jsSuffixRegExp,"")),"."===e[0].charAt(0)&&g&&(m=g.slice(0,g.length-1),e=m.concat(e)),t(e),e=e.join("/")),r&&v&&(g||x)){a=e.split("/");e:for(s=a.length;s>0;s-=1){if(c=a.slice(0,s).join("/"),g)for(u=g.length;u>0;u-=1)if(o=getOwn(v,g.slice(0,u).join("/")),o&&(o=getOwn(o,c))){p=o,f=s;break e}!l&&x&&getOwn(x,c)&&(l=getOwn(x,c),h=s)}!p&&l&&(p=l,f=h),p&&(a.splice(0,f,p),e=a.join("/"))}return n=getOwn(y.pkgs,e),n?n:e}function r(e){isBrowser&&each(scripts(),function(t){if(t.getAttribute("data-requiremodule")===e&&t.getAttribute("data-requirecontext")===q.contextName)return t.parentNode.removeChild(t),!0})}function n(e){var t=getOwn(y.paths,e);if(t&&isArray(t)&&t.length>1)return t.shift(),q.require.undef(e),q.makeRequire(null,{skipMap:!0})([e]),!0}function o(e){var t,i=e?e.indexOf("!"):-1;return i>-1&&(t=e.substring(0,i),e=e.substring(i+1,e.length)),[t,e]}function a(e,t,r,n){var a,s,u,c,d=null,p=t?t.name:null,f=e,l=!0,h="";return e||(l=!1,e="_@r"+(T+=1)),c=o(e),d=c[0],e=c[1],d&&(d=i(d,p,n),s=getOwn(j,d)),e&&(d?h=s&&s.normalize?s.normalize(e,function(e){return i(e,p,n)}):e.indexOf("!")===-1?i(e,p,n):e:(h=i(e,p,n),c=o(h),d=c[0],h=c[1],r=!0,a=q.nameToUrl(h))),u=!d||s||r?"":"_unnormalized"+(A+=1),{prefix:d,name:h,parentMap:t,unnormalized:!!u,url:a,originalName:f,isDefine:l,id:(d?d+"!"+h:h)+u}}function s(e){var t=e.id,i=getOwn(S,t);return i||(i=S[t]=new q.Module(e)),i}function u(e,t,i){var r=e.id,n=getOwn(S,r);!hasProp(j,r)||n&&!n.defineEmitComplete?(n=s(e),n.error&&"error"===t?i(n.error):n.on(t,i)):"defined"===t&&i(j[r])}function c(e,t){var i=e.requireModules,r=!1;t?t(e):(each(i,function(t){var i=getOwn(S,t);i&&(i.error=e,i.events.error&&(r=!0,i.emit("error",e)))}),r||req.onError(e))}function d(){globalDefQueue.length&&(each(globalDefQueue,function(e){var t=e[0];"string"==typeof t&&(q.defQueueMap[t]=!0),O.push(e)}),globalDefQueue=[])}function p(e){delete S[e],delete k[e]}function f(e,t,i){var r=e.map.id;e.error?e.emit("error",e.error):(t[r]=!0,each(e.depMaps,function(r,n){var o=r.id,a=getOwn(S,o);!a||e.depMatched[n]||i[o]||(getOwn(t,o)?(e.defineDep(n,j[o]),e.check()):f(a,t,i))}),i[r]=!0)}function l(){var e,t,i=1e3*y.waitSeconds,o=i&&q.startTime+i<(new Date).getTime(),a=[],s=[],u=!1,d=!0;if(!x){if(x=!0,eachProp(k,function(e){var i=e.map,c=i.id;if(e.enabled&&(i.isDefine||s.push(e),!e.error))if(!e.inited&&o)n(c)?(t=!0,u=!0):(a.push(c),r(c));else if(!e.inited&&e.fetched&&i.isDefine&&(u=!0,!i.prefix))return d=!1}),o&&a.length)return e=makeError("timeout","Load timeout for modules: "+a,null,a),e.contextName=q.contextName,c(e);d&&each(s,function(e){f(e,{},{})}),o&&!t||!u||!isBrowser&&!isWebWorker||w||(w=setTimeout(function(){w=0,l()},50)),x=!1}}function h(e){hasProp(j,e[0])||s(a(e[0],null,!0)).init(e[1],e[2])}function m(e,t,i,r){e.detachEvent&&!isOpera?r&&e.detachEvent(r,t):e.removeEventListener(i,t,!1)}function g(e){var t=e.currentTarget||e.srcElement;return m(t,q.onScriptLoad,"load","onreadystatechange"),m(t,q.onScriptError,"error"),{node:t,id:t&&t.getAttribute("data-requiremodule")}}function v(){var e;for(d();O.length;){if(e=O.shift(),null===e[0])return c(makeError("mismatch","Mismatched anonymous define() module: "+e[e.length-1]));h(e)}q.defQueueMap={}}var x,b,q,E,w,y={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},S={},k={},M={},O=[],j={},P={},R={},T=1,A=1;return E={require:function(e){return e.require?e.require:e.require=q.makeRequire(e.map)},exports:function(e){if(e.usingExports=!0,e.map.isDefine)return e.exports?j[e.map.id]=e.exports:e.exports=j[e.map.id]={}},module:function(e){return e.module?e.module:e.module={id:e.map.id,uri:e.map.url,config:function(){return getOwn(y.config,e.map.id)||{}},exports:e.exports||(e.exports={})}}},b=function(e){this.events=getOwn(M,e.id)||{},this.map=e,this.shim=getOwn(y.shim,e.id),this.depExports=[],this.depMaps=[],this.depMatched=[],this.pluginMaps={},this.depCount=0},b.prototype={init:function(e,t,i,r){r=r||{},this.inited||(this.factory=t,i?this.on("error",i):this.events.error&&(i=bind(this,function(e){this.emit("error",e)})),this.depMaps=e&&e.slice(0),this.errback=i,this.inited=!0,this.ignore=r.ignore,r.enabled||this.enabled?this.enable():this.check())},defineDep:function(e,t){this.depMatched[e]||(this.depMatched[e]=!0,this.depCount-=1,this.depExports[e]=t)},fetch:function(){if(!this.fetched){this.fetched=!0,q.startTime=(new Date).getTime();var e=this.map;return this.shim?void q.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],bind(this,function(){return e.prefix?this.callPlugin():this.load()})):e.prefix?this.callPlugin():this.load()}},load:function(){var e=this.map.url;P[e]||(P[e]=!0,q.load(this.map.id,e))},check:function(){if(this.enabled&&!this.enabling){var e,t,i=this.map.id,r=this.depExports,n=this.exports,o=this.factory;if(this.inited){if(this.error)this.emit("error",this.error);else if(!this.defining){if(this.defining=!0,this.depCount<1&&!this.defined){if(isFunction(o)){if(this.events.error&&this.map.isDefine||req.onError!==defaultOnError)try{n=q.execCb(i,o,r,n)}catch(t){e=t}else n=q.execCb(i,o,r,n);if(this.map.isDefine&&void 0===n&&(t=this.module,t?n=t.exports:this.usingExports&&(n=this.exports)),e)return e.requireMap=this.map,e.requireModules=this.map.isDefine?[this.map.id]:null,e.requireType=this.map.isDefine?"define":"require",c(this.error=e)}else n=o;if(this.exports=n,this.map.isDefine&&!this.ignore&&(j[i]=n,req.onResourceLoad)){var a=[];each(this.depMaps,function(e){a.push(e.normalizedMap||e)}),req.onResourceLoad(q,this.map,a)}p(i),this.defined=!0}this.defining=!1,this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else hasProp(q.defQueueMap,i)||this.fetch()}},callPlugin:function(){var e=this.map,t=e.id,r=a(e.prefix);this.depMaps.push(r),u(r,"defined",bind(this,function(r){var n,o,d,f=getOwn(R,this.map.id),l=this.map.name,h=this.map.parentMap?this.map.parentMap.name:null,m=q.makeRequire(e.parentMap,{enableBuildCallback:!0});return this.map.unnormalized?(r.normalize&&(l=r.normalize(l,function(e){return i(e,h,!0)})||""),o=a(e.prefix+"!"+l,this.map.parentMap),u(o,"defined",bind(this,function(e){this.map.normalizedMap=o,this.init([],function(){return e},null,{enabled:!0,ignore:!0})})),d=getOwn(S,o.id),void(d&&(this.depMaps.push(o),this.events.error&&d.on("error",bind(this,function(e){this.emit("error",e)})),d.enable()))):f?(this.map.url=q.nameToUrl(f),void this.load()):(n=bind(this,function(e){this.init([],function(){return e},null,{enabled:!0})}),n.error=bind(this,function(e){this.inited=!0,this.error=e,e.requireModules=[t],eachProp(S,function(e){0===e.map.id.indexOf(t+"_unnormalized")&&p(e.map.id)}),c(e)}),n.fromText=bind(this,function(i,r){var o=e.name,u=a(o),d=useInteractive;r&&(i=r),d&&(useInteractive=!1),s(u),hasProp(y.config,t)&&(y.config[o]=y.config[t]);try{req.exec(i)}catch(e){return c(makeError("fromtexteval","fromText eval for "+t+" failed: "+e,e,[t]))}d&&(useInteractive=!0),this.depMaps.push(u),q.completeLoad(o),m([o],n)}),void r.load(e.name,m,n,y))})),q.enable(r,this),this.pluginMaps[r.id]=r},enable:function(){k[this.map.id]=this,this.enabled=!0,this.enabling=!0,each(this.depMaps,bind(this,function(e,t){var i,r,n;if("string"==typeof e){if(e=a(e,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap),this.depMaps[t]=e,n=getOwn(E,e.id))return void(this.depExports[t]=n(this));this.depCount+=1,u(e,"defined",bind(this,function(e){this.undefed||(this.defineDep(t,e),this.check())})),this.errback?u(e,"error",bind(this,this.errback)):this.events.error&&u(e,"error",bind(this,function(e){this.emit("error",e)}))}i=e.id,r=S[i],hasProp(E,i)||!r||r.enabled||q.enable(e,this)})),eachProp(this.pluginMaps,bind(this,function(e){var t=getOwn(S,e.id);t&&!t.enabled&&q.enable(e,this)})),this.enabling=!1,this.check()},on:function(e,t){var i=this.events[e];i||(i=this.events[e]=[]),i.push(t)},emit:function(e,t){each(this.events[e],function(e){e(t)}),"error"===e&&delete this.events[e]}},q={config:y,contextName:e,registry:S,defined:j,urlFetched:P,defQueue:O,defQueueMap:{},Module:b,makeModuleMap:a,nextTick:req.nextTick,onError:c,configure:function(e){if(e.baseUrl&&"/"!==e.baseUrl.charAt(e.baseUrl.length-1)&&(e.baseUrl+="/"),"string"==typeof e.urlArgs){var t=e.urlArgs;e.urlArgs=function(e,i){return(i.indexOf("?")===-1?"?":"&")+t}}var i=y.shim,r={paths:!0,bundles:!0,config:!0,map:!0};eachProp(e,function(e,t){r[t]?(y[t]||(y[t]={}),mixin(y[t],e,!0,!0)):y[t]=e}),e.bundles&&eachProp(e.bundles,function(e,t){each(e,function(e){e!==t&&(R[e]=t)})}),e.shim&&(eachProp(e.shim,function(e,t){isArray(e)&&(e={deps:e}),!e.exports&&!e.init||e.exportsFn||(e.exportsFn=q.makeShimExports(e)),i[t]=e}),y.shim=i),e.packages&&each(e.packages,function(e){var t,i;e="string"==typeof e?{name:e}:e,i=e.name,t=e.location,t&&(y.paths[i]=e.location),y.pkgs[i]=e.name+"/"+(e.main||"main").replace(currDirRegExp,"").replace(jsSuffixRegExp,"")}),eachProp(S,function(e,t){e.inited||e.map.unnormalized||(e.map=a(t,null,!0))}),(e.deps||e.callback)&&q.require(e.deps||[],e.callback)},makeShimExports:function(e){function t(){var t;return e.init&&(t=e.init.apply(global,arguments)),t||e.exports&&getGlobal(e.exports)}return t},makeRequire:function(t,n){function o(i,r,u){var d,p,f;return n.enableBuildCallback&&r&&isFunction(r)&&(r.__requireJsBuild=!0),"string"==typeof i?isFunction(r)?c(makeError("requireargs","Invalid require call"),u):t&&hasProp(E,i)?E[i](S[t.id]):req.get?req.get(q,i,t,o):(p=a(i,t,!1,!0),d=p.id,hasProp(j,d)?j[d]:c(makeError("notloaded",'Module name "'+d+'" has not been loaded yet for context: '+e+(t?"":". Use require([])")))):(v(),q.nextTick(function(){v(),f=s(a(null,t)),f.skipMap=n.skipMap,f.init(i,r,u,{enabled:!0}),l()}),o)}return n=n||{},mixin(o,{isBrowser:isBrowser,toUrl:function(e){var r,n=e.lastIndexOf("."),o=e.split("/")[0],a="."===o||".."===o;return n!==-1&&(!a||n>1)&&(r=e.substring(n,e.length),e=e.substring(0,n)),q.nameToUrl(i(e,t&&t.id,!0),r,!0)},defined:function(e){return hasProp(j,a(e,t,!1,!0).id)},specified:function(e){return e=a(e,t,!1,!0).id,hasProp(j,e)||hasProp(S,e)}}),t||(o.undef=function(e){d();var i=a(e,t,!0),n=getOwn(S,e);n.undefed=!0,r(e),delete j[e],delete P[i.url],delete M[e],eachReverse(O,function(t,i){t[0]===e&&O.splice(i,1)}),delete q.defQueueMap[e],n&&(n.events.defined&&(M[e]=n.events),p(e))}),o},enable:function(e){var t=getOwn(S,e.id);t&&s(e).enable()},completeLoad:function(e){var t,i,r,o=getOwn(y.shim,e)||{},a=o.exports;for(d();O.length;){if(i=O.shift(),null===i[0]){if(i[0]=e,t)break;t=!0}else i[0]===e&&(t=!0);h(i)}if(q.defQueueMap={},r=getOwn(S,e),!t&&!hasProp(j,e)&&r&&!r.inited){if(!(!y.enforceDefine||a&&getGlobal(a)))return n(e)?void 0:c(makeError("nodefine","No define call for "+e,null,[e]));h([e,o.deps||[],o.exportsFn])}l()},nameToUrl:function(e,t,i){var r,n,o,a,s,u,c,d=getOwn(y.pkgs,e);if(d&&(e=d),c=getOwn(R,e))return q.nameToUrl(c,t,i);if(req.jsExtRegExp.test(e))s=e+(t||"");else{for(r=y.paths,n=e.split("/"),o=n.length;o>0;o-=1)if(a=n.slice(0,o).join("/"),u=getOwn(r,a)){isArray(u)&&(u=u[0]),n.splice(0,o,u);break}s=n.join("/"),s+=t||(/^data\:|^blob\:|\?/.test(s)||i?"":".js"),s=("/"===s.charAt(0)||s.match(/^[\w\+\.\-]+:/)?"":y.baseUrl)+s}return y.urlArgs&&!/^blob\:/.test(s)?s+y.urlArgs(e,s):s},load:function(e,t){req.load(q,e,t)},execCb:function(e,t,i,r){return t.apply(r,i)},onScriptLoad:function(e){if("load"===e.type||readyRegExp.test((e.currentTarget||e.srcElement).readyState)){interactiveScript=null;var t=g(e);q.completeLoad(t.id)}},onScriptError:function(e){var t=g(e);if(!n(t.id)){var i=[];return eachProp(S,function(e,r){0!==r.indexOf("_@r")&&each(e.depMaps,function(e){if(e.id===t.id)return i.push(r),!0})}),c(makeError("scripterror",'Script error for "'+t.id+(i.length?'", needed by: '+i.join(", "):'"'),e,[t.id]))}}},q.require=q.makeRequire(),q}function getInteractiveScript(){return interactiveScript&&"interactive"===interactiveScript.readyState?interactiveScript:(eachReverse(scripts(),function(e){if("interactive"===e.readyState)return interactiveScript=e}),interactiveScript)}var req,s,head,baseElement,dataMain,src,interactiveScript,currentlyAddingScript,mainScript,subPath,version="2.3.2",commentRegExp=/\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,cjsRequireRegExp=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,jsSuffixRegExp=/\.js$/,currDirRegExp=/^\.\//,op=Object.prototype,ostring=op.toString,hasOwn=op.hasOwnProperty,isBrowser=!("undefined"==typeof window||"undefined"==typeof navigator||!window.document),isWebWorker=!isBrowser&&"undefined"!=typeof importScripts,readyRegExp=isBrowser&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,defContextName="_",isOpera="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),contexts={},cfg={},globalDefQueue=[],useInteractive=!1;if("undefined"==typeof define){if("undefined"!=typeof requirejs){if(isFunction(requirejs))return;cfg=requirejs,requirejs=void 0}"undefined"==typeof require||isFunction(require)||(cfg=require,require=void 0),req=requirejs=function(e,t,i,r){var n,o,a=defContextName;return isArray(e)||"string"==typeof e||(o=e,isArray(t)?(e=t,t=i,i=r):e=[]),o&&o.context&&(a=o.context),n=getOwn(contexts,a),n||(n=contexts[a]=req.s.newContext(a)),o&&n.configure(o),n.require(e,t,i)},req.config=function(e){return req(e)},req.nextTick="undefined"!=typeof setTimeout?function(e){setTimeout(e,4)}:function(e){e()},require||(require=req),req.version=version,req.jsExtRegExp=/^\/|:|\?|\.js$/,req.isBrowser=isBrowser,s=req.s={contexts:contexts,newContext:newContext},req({}),each(["toUrl","undef","defined","specified"],function(e){req[e]=function(){var t=contexts[defContextName];return t.require[e].apply(t,arguments)}}),isBrowser&&(head=s.head=document.getElementsByTagName("head")[0],baseElement=document.getElementsByTagName("base")[0],baseElement&&(head=s.head=baseElement.parentNode)),req.onError=defaultOnError,req.createNode=function(e,t,i){var r=e.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");return r.type=e.scriptType||"text/javascript",r.charset="utf-8",r.async=!0,r},req.load=function(e,t,i){var r,n=e&&e.config||{};if(isBrowser)return r=req.createNode(n,t,i),r.setAttribute("data-requirecontext",e.contextName),r.setAttribute("data-requiremodule",t),!r.attachEvent||r.attachEvent.toString&&r.attachEvent.toString().indexOf("[native code")<0||isOpera?(r.addEventListener("load",e.onScriptLoad,!1),r.addEventListener("error",e.onScriptError,!1)):(useInteractive=!0,r.attachEvent("onreadystatechange",e.onScriptLoad)),r.src=i,n.onNodeCreated&&n.onNodeCreated(r,n,t,i),currentlyAddingScript=r,baseElement?head.insertBefore(r,baseElement):head.appendChild(r),currentlyAddingScript=null,r;if(isWebWorker)try{setTimeout(function(){},0),importScripts(i),e.completeLoad(t)}catch(r){e.onError(makeError("importscripts","importScripts failed for "+t+" at "+i,r,[t]))}},isBrowser&&!cfg.skipDataMain&&eachReverse(scripts(),function(e){if(head||(head=e.parentNode),dataMain=e.getAttribute("data-main"))return mainScript=dataMain,cfg.baseUrl||mainScript.indexOf("!")!==-1||(src=mainScript.split("/"),mainScript=src.pop(),subPath=src.length?src.join("/")+"/":"./",cfg.baseUrl=subPath),mainScript=mainScript.replace(jsSuffixRegExp,""),req.jsExtRegExp.test(mainScript)&&(mainScript=dataMain),cfg.deps=cfg.deps?cfg.deps.concat(mainScript):[mainScript],!0}),define=function(e,t,i){var r,n;"string"!=typeof e&&(i=t,t=e,e=null),isArray(t)||(i=t,t=null),!t&&isFunction(i)&&(t=[],i.length&&(i.toString().replace(commentRegExp,commentReplace).replace(cjsRequireRegExp,function(e,i){t.push(i)}),t=(1===i.length?["require"]:["require","exports","module"]).concat(t))),useInteractive&&(r=currentlyAddingScript||getInteractiveScript(),r&&(e||(e=r.getAttribute("data-requiremodule")),n=contexts[r.getAttribute("data-requirecontext")])),n?(n.defQueue.push([e,t,i]),n.defQueueMap[e]=!0):globalDefQueue.push([e,t,i])},define.amd={jQuery:!0},req.exec=function(text){return eval(text)},req(cfg)}}(this,"undefined"==typeof setTimeout?void 0:setTimeout); \ No newline at end of file +var requirejs,require,define;!function(global,setTimeout){function commentReplace(e,t){return t||""}function isFunction(e){return"[object Function]"===ostring.call(e)}function isArray(e){return"[object Array]"===ostring.call(e)}function each(e,t){if(e){var i;for(i=0;i-1&&(!e[i]||!t(e[i],i,e));i-=1);}}function hasProp(e,t){return hasOwn.call(e,t)}function getOwn(e,t){return hasProp(e,t)&&e[t]}function eachProp(e,t){var i;for(i in e)if(hasProp(e,i)&&t(e[i],i))break}function mixin(e,t,i,r){return t&&eachProp(t,function(t,n){!i&&hasProp(e,n)||(!r||"object"!=typeof t||!t||isArray(t)||isFunction(t)||t instanceof RegExp?e[n]=t:(e[n]||(e[n]={}),mixin(e[n],t,i,r)))}),e}function bind(e,t){return function(){return t.apply(e,arguments)}}function scripts(){return document.getElementsByTagName("script")}function defaultOnError(e){throw e}function getGlobal(e){if(!e)return e;var t=global;return each(e.split("."),function(e){t=t[e]}),t}function makeError(e,t,i,r){var n=new Error(t+"\nhttp://requirejs.org/docs/errors.html#"+e);return n.requireType=e,n.requireModules=r,i&&(n.originalError=i),n}function newContext(e){function t(e){var t,i;for(t=0;t0&&(e.splice(t-1,2),t-=2)}}function i(e,i,r){var n,o,a,s,u,c,d,p,f,l,h=i&&i.split("/"),m=y.map,g=m&&m["*"];if(e&&(c=(e=e.split("/")).length-1,y.nodeIdCompat&&jsSuffixRegExp.test(e[c])&&(e[c]=e[c].replace(jsSuffixRegExp,"")),"."===e[0].charAt(0)&&h&&(e=h.slice(0,h.length-1).concat(e)),t(e),e=e.join("/")),r&&m&&(h||g)){e:for(a=(o=e.split("/")).length;a>0;a-=1){if(u=o.slice(0,a).join("/"),h)for(s=h.length;s>0;s-=1)if((n=getOwn(m,h.slice(0,s).join("/")))&&(n=getOwn(n,u))){d=n,p=a;break e}!f&&g&&getOwn(g,u)&&(f=getOwn(g,u),l=a)}!d&&f&&(d=f,p=l),d&&(o.splice(0,p,d),e=o.join("/"))}return getOwn(y.pkgs,e)||e}function r(e){isBrowser&&each(scripts(),function(t){if(t.getAttribute("data-requiremodule")===e&&t.getAttribute("data-requirecontext")===q.contextName)return t.parentNode.removeChild(t),!0})}function n(e){var t=getOwn(y.paths,e);if(t&&isArray(t)&&t.length>1)return t.shift(),q.require.undef(e),q.makeRequire(null,{skipMap:!0})([e]),!0}function o(e){var t,i=e?e.indexOf("!"):-1;return i>-1&&(t=e.substring(0,i),e=e.substring(i+1,e.length)),[t,e]}function a(e,t,r,n){var a,s,u,c,d=null,p=t?t.name:null,f=e,l=!0,h="";return e||(l=!1,e="_@r"+(T+=1)),c=o(e),d=c[0],e=c[1],d&&(d=i(d,p,n),s=getOwn(j,d)),e&&(d?h=r?e:s&&s.normalize?s.normalize(e,function(e){return i(e,p,n)}):-1===e.indexOf("!")?i(e,p,n):e:(d=(c=o(h=i(e,p,n)))[0],h=c[1],r=!0,a=q.nameToUrl(h))),u=!d||s||r?"":"_unnormalized"+(A+=1),{prefix:d,name:h,parentMap:t,unnormalized:!!u,url:a,originalName:f,isDefine:l,id:(d?d+"!"+h:h)+u}}function s(e){var t=e.id,i=getOwn(S,t);return i||(i=S[t]=new q.Module(e)),i}function u(e,t,i){var r=e.id,n=getOwn(S,r);!hasProp(j,r)||n&&!n.defineEmitComplete?(n=s(e)).error&&"error"===t?i(n.error):n.on(t,i):"defined"===t&&i(j[r])}function c(e,t){var i=e.requireModules,r=!1;t?t(e):(each(i,function(t){var i=getOwn(S,t);i&&(i.error=e,i.events.error&&(r=!0,i.emit("error",e)))}),r||req.onError(e))}function d(){globalDefQueue.length&&(each(globalDefQueue,function(e){var t=e[0];"string"==typeof t&&(q.defQueueMap[t]=!0),O.push(e)}),globalDefQueue=[])}function p(e){delete S[e],delete k[e]}function f(e,t,i){var r=e.map.id;e.error?e.emit("error",e.error):(t[r]=!0,each(e.depMaps,function(r,n){var o=r.id,a=getOwn(S,o);!a||e.depMatched[n]||i[o]||(getOwn(t,o)?(e.defineDep(n,j[o]),e.check()):f(a,t,i))}),i[r]=!0)}function l(){var e,t,i=1e3*y.waitSeconds,o=i&&q.startTime+i<(new Date).getTime(),a=[],s=[],u=!1,d=!0;if(!x){if(x=!0,eachProp(k,function(e){var i=e.map,c=i.id;if(e.enabled&&(i.isDefine||s.push(e),!e.error))if(!e.inited&&o)n(c)?(t=!0,u=!0):(a.push(c),r(c));else if(!e.inited&&e.fetched&&i.isDefine&&(u=!0,!i.prefix))return d=!1}),o&&a.length)return e=makeError("timeout","Load timeout for modules: "+a,null,a),e.contextName=q.contextName,c(e);d&&each(s,function(e){f(e,{},{})}),o&&!t||!u||!isBrowser&&!isWebWorker||w||(w=setTimeout(function(){w=0,l()},50)),x=!1}}function h(e){hasProp(j,e[0])||s(a(e[0],null,!0)).init(e[1],e[2])}function m(e,t,i,r){e.detachEvent&&!isOpera?r&&e.detachEvent(r,t):e.removeEventListener(i,t,!1)}function g(e){var t=e.currentTarget||e.srcElement;return m(t,q.onScriptLoad,"load","onreadystatechange"),m(t,q.onScriptError,"error"),{node:t,id:t&&t.getAttribute("data-requiremodule")}}function v(){var e;for(d();O.length;){if(null===(e=O.shift())[0])return c(makeError("mismatch","Mismatched anonymous define() module: "+e[e.length-1]));h(e)}q.defQueueMap={}}var x,b,q,E,w,y={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},S={},k={},M={},O=[],j={},P={},R={},T=1,A=1;return E={require:function(e){return e.require?e.require:e.require=q.makeRequire(e.map)},exports:function(e){if(e.usingExports=!0,e.map.isDefine)return e.exports?j[e.map.id]=e.exports:e.exports=j[e.map.id]={}},module:function(e){return e.module?e.module:e.module={id:e.map.id,uri:e.map.url,config:function(){return getOwn(y.config,e.map.id)||{}},exports:e.exports||(e.exports={})}}},b=function(e){this.events=getOwn(M,e.id)||{},this.map=e,this.shim=getOwn(y.shim,e.id),this.depExports=[],this.depMaps=[],this.depMatched=[],this.pluginMaps={},this.depCount=0},b.prototype={init:function(e,t,i,r){r=r||{},this.inited||(this.factory=t,i?this.on("error",i):this.events.error&&(i=bind(this,function(e){this.emit("error",e)})),this.depMaps=e&&e.slice(0),this.errback=i,this.inited=!0,this.ignore=r.ignore,r.enabled||this.enabled?this.enable():this.check())},defineDep:function(e,t){this.depMatched[e]||(this.depMatched[e]=!0,this.depCount-=1,this.depExports[e]=t)},fetch:function(){if(!this.fetched){this.fetched=!0,q.startTime=(new Date).getTime();var e=this.map;if(!this.shim)return e.prefix?this.callPlugin():this.load();q.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],bind(this,function(){return e.prefix?this.callPlugin():this.load()}))}},load:function(){var e=this.map.url;P[e]||(P[e]=!0,q.load(this.map.id,e))},check:function(){if(this.enabled&&!this.enabling){var e,t,i=this.map.id,r=this.depExports,n=this.exports,o=this.factory;if(this.inited){if(this.error)this.emit("error",this.error);else if(!this.defining){if(this.defining=!0,this.depCount<1&&!this.defined){if(isFunction(o)){if(this.events.error&&this.map.isDefine||req.onError!==defaultOnError)try{n=q.execCb(i,o,r,n)}catch(t){e=t}else n=q.execCb(i,o,r,n);if(this.map.isDefine&&void 0===n&&((t=this.module)?n=t.exports:this.usingExports&&(n=this.exports)),e)return e.requireMap=this.map,e.requireModules=this.map.isDefine?[this.map.id]:null,e.requireType=this.map.isDefine?"define":"require",c(this.error=e)}else n=o;if(this.exports=n,this.map.isDefine&&!this.ignore&&(j[i]=n,req.onResourceLoad)){var a=[];each(this.depMaps,function(e){a.push(e.normalizedMap||e)}),req.onResourceLoad(q,this.map,a)}p(i),this.defined=!0}this.defining=!1,this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else hasProp(q.defQueueMap,i)||this.fetch()}},callPlugin:function(){var e=this.map,t=e.id,r=a(e.prefix);this.depMaps.push(r),u(r,"defined",bind(this,function(r){var n,o,d,f=getOwn(R,this.map.id),l=this.map.name,h=this.map.parentMap?this.map.parentMap.name:null,m=q.makeRequire(e.parentMap,{enableBuildCallback:!0});return this.map.unnormalized?(r.normalize&&(l=r.normalize(l,function(e){return i(e,h,!0)})||""),o=a(e.prefix+"!"+l,this.map.parentMap,!0),u(o,"defined",bind(this,function(e){this.map.normalizedMap=o,this.init([],function(){return e},null,{enabled:!0,ignore:!0})})),void((d=getOwn(S,o.id))&&(this.depMaps.push(o),this.events.error&&d.on("error",bind(this,function(e){this.emit("error",e)})),d.enable()))):f?(this.map.url=q.nameToUrl(f),void this.load()):((n=bind(this,function(e){this.init([],function(){return e},null,{enabled:!0})})).error=bind(this,function(e){this.inited=!0,this.error=e,e.requireModules=[t],eachProp(S,function(e){0===e.map.id.indexOf(t+"_unnormalized")&&p(e.map.id)}),c(e)}),n.fromText=bind(this,function(i,r){var o=e.name,u=a(o),d=useInteractive;r&&(i=r),d&&(useInteractive=!1),s(u),hasProp(y.config,t)&&(y.config[o]=y.config[t]);try{req.exec(i)}catch(e){return c(makeError("fromtexteval","fromText eval for "+t+" failed: "+e,e,[t]))}d&&(useInteractive=!0),this.depMaps.push(u),q.completeLoad(o),m([o],n)}),void r.load(e.name,m,n,y))})),q.enable(r,this),this.pluginMaps[r.id]=r},enable:function(){k[this.map.id]=this,this.enabled=!0,this.enabling=!0,each(this.depMaps,bind(this,function(e,t){var i,r,n;if("string"==typeof e){if(e=a(e,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap),this.depMaps[t]=e,n=getOwn(E,e.id))return void(this.depExports[t]=n(this));this.depCount+=1,u(e,"defined",bind(this,function(e){this.undefed||(this.defineDep(t,e),this.check())})),this.errback?u(e,"error",bind(this,this.errback)):this.events.error&&u(e,"error",bind(this,function(e){this.emit("error",e)}))}i=e.id,r=S[i],hasProp(E,i)||!r||r.enabled||q.enable(e,this)})),eachProp(this.pluginMaps,bind(this,function(e){var t=getOwn(S,e.id);t&&!t.enabled&&q.enable(e,this)})),this.enabling=!1,this.check()},on:function(e,t){var i=this.events[e];i||(i=this.events[e]=[]),i.push(t)},emit:function(e,t){each(this.events[e],function(e){e(t)}),"error"===e&&delete this.events[e]}},q={config:y,contextName:e,registry:S,defined:j,urlFetched:P,defQueue:O,defQueueMap:{},Module:b,makeModuleMap:a,nextTick:req.nextTick,onError:c,configure:function(e){if(e.baseUrl&&"/"!==e.baseUrl.charAt(e.baseUrl.length-1)&&(e.baseUrl+="/"),"string"==typeof e.urlArgs){var t=e.urlArgs;e.urlArgs=function(e,i){return(-1===i.indexOf("?")?"?":"&")+t}}var i=y.shim,r={paths:!0,bundles:!0,config:!0,map:!0};eachProp(e,function(e,t){r[t]?(y[t]||(y[t]={}),mixin(y[t],e,!0,!0)):y[t]=e}),e.bundles&&eachProp(e.bundles,function(e,t){each(e,function(e){e!==t&&(R[e]=t)})}),e.shim&&(eachProp(e.shim,function(e,t){isArray(e)&&(e={deps:e}),!e.exports&&!e.init||e.exportsFn||(e.exportsFn=q.makeShimExports(e)),i[t]=e}),y.shim=i),e.packages&&each(e.packages,function(e){var t;t=(e="string"==typeof e?{name:e}:e).name,e.location&&(y.paths[t]=e.location),y.pkgs[t]=e.name+"/"+(e.main||"main").replace(currDirRegExp,"").replace(jsSuffixRegExp,"")}),eachProp(S,function(e,t){e.inited||e.map.unnormalized||(e.map=a(t,null,!0))}),(e.deps||e.callback)&&q.require(e.deps||[],e.callback)},makeShimExports:function(e){return function(){var t;return e.init&&(t=e.init.apply(global,arguments)),t||e.exports&&getGlobal(e.exports)}},makeRequire:function(t,n){function o(i,r,u){var d,p,f;return n.enableBuildCallback&&r&&isFunction(r)&&(r.__requireJsBuild=!0),"string"==typeof i?isFunction(r)?c(makeError("requireargs","Invalid require call"),u):t&&hasProp(E,i)?E[i](S[t.id]):req.get?req.get(q,i,t,o):(p=a(i,t,!1,!0),d=p.id,hasProp(j,d)?j[d]:c(makeError("notloaded",'Module name "'+d+'" has not been loaded yet for context: '+e+(t?"":". Use require([])")))):(v(),q.nextTick(function(){v(),(f=s(a(null,t))).skipMap=n.skipMap,f.init(i,r,u,{enabled:!0}),l()}),o)}return n=n||{},mixin(o,{isBrowser:isBrowser,toUrl:function(e){var r,n=e.lastIndexOf("."),o=e.split("/")[0],a="."===o||".."===o;return-1!==n&&(!a||n>1)&&(r=e.substring(n,e.length),e=e.substring(0,n)),q.nameToUrl(i(e,t&&t.id,!0),r,!0)},defined:function(e){return hasProp(j,a(e,t,!1,!0).id)},specified:function(e){return e=a(e,t,!1,!0).id,hasProp(j,e)||hasProp(S,e)}}),t||(o.undef=function(e){d();var i=a(e,t,!0),n=getOwn(S,e);n.undefed=!0,r(e),delete j[e],delete P[i.url],delete M[e],eachReverse(O,function(t,i){t[0]===e&&O.splice(i,1)}),delete q.defQueueMap[e],n&&(n.events.defined&&(M[e]=n.events),p(e))}),o},enable:function(e){getOwn(S,e.id)&&s(e).enable()},completeLoad:function(e){var t,i,r,o=getOwn(y.shim,e)||{},a=o.exports;for(d();O.length;){if(null===(i=O.shift())[0]){if(i[0]=e,t)break;t=!0}else i[0]===e&&(t=!0);h(i)}if(q.defQueueMap={},r=getOwn(S,e),!t&&!hasProp(j,e)&&r&&!r.inited){if(!(!y.enforceDefine||a&&getGlobal(a)))return n(e)?void 0:c(makeError("nodefine","No define call for "+e,null,[e]));h([e,o.deps||[],o.exportsFn])}l()},nameToUrl:function(e,t,i){var r,n,o,a,s,u,c,d=getOwn(y.pkgs,e);if(d&&(e=d),c=getOwn(R,e))return q.nameToUrl(c,t,i);if(req.jsExtRegExp.test(e))s=e+(t||"");else{for(r=y.paths,o=(n=e.split("/")).length;o>0;o-=1)if(a=n.slice(0,o).join("/"),u=getOwn(r,a)){isArray(u)&&(u=u[0]),n.splice(0,o,u);break}s=n.join("/"),s=("/"===(s+=t||(/^data\:|^blob\:|\?/.test(s)||i?"":".js")).charAt(0)||s.match(/^[\w\+\.\-]+:/)?"":y.baseUrl)+s}return y.urlArgs&&!/^blob\:/.test(s)?s+y.urlArgs(e,s):s},load:function(e,t){req.load(q,e,t)},execCb:function(e,t,i,r){return t.apply(r,i)},onScriptLoad:function(e){if("load"===e.type||readyRegExp.test((e.currentTarget||e.srcElement).readyState)){interactiveScript=null;var t=g(e);q.completeLoad(t.id)}},onScriptError:function(e){var t=g(e);if(!n(t.id)){var i=[];return eachProp(S,function(e,r){0!==r.indexOf("_@r")&&each(e.depMaps,function(e){if(e.id===t.id)return i.push(r),!0})}),c(makeError("scripterror",'Script error for "'+t.id+(i.length?'", needed by: '+i.join(", "):'"'),e,[t.id]))}}},q.require=q.makeRequire(),q}function getInteractiveScript(){return interactiveScript&&"interactive"===interactiveScript.readyState?interactiveScript:(eachReverse(scripts(),function(e){if("interactive"===e.readyState)return interactiveScript=e}),interactiveScript)}var req,s,head,baseElement,dataMain,src,interactiveScript,currentlyAddingScript,mainScript,subPath,version="2.3.5",commentRegExp=/\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,cjsRequireRegExp=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,jsSuffixRegExp=/\.js$/,currDirRegExp=/^\.\//,op=Object.prototype,ostring=op.toString,hasOwn=op.hasOwnProperty,isBrowser=!("undefined"==typeof window||"undefined"==typeof navigator||!window.document),isWebWorker=!isBrowser&&"undefined"!=typeof importScripts,readyRegExp=isBrowser&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,defContextName="_",isOpera="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),contexts={},cfg={},globalDefQueue=[],useInteractive=!1;if(void 0===define){if(void 0!==requirejs){if(isFunction(requirejs))return;cfg=requirejs,requirejs=void 0}void 0===require||isFunction(require)||(cfg=require,require=void 0),req=requirejs=function(e,t,i,r){var n,o,a=defContextName;return isArray(e)||"string"==typeof e||(o=e,isArray(t)?(e=t,t=i,i=r):e=[]),o&&o.context&&(a=o.context),(n=getOwn(contexts,a))||(n=contexts[a]=req.s.newContext(a)),o&&n.configure(o),n.require(e,t,i)},req.config=function(e){return req(e)},req.nextTick=void 0!==setTimeout?function(e){setTimeout(e,4)}:function(e){e()},require||(require=req),req.version=version,req.jsExtRegExp=/^\/|:|\?|\.js$/,req.isBrowser=isBrowser,s=req.s={contexts:contexts,newContext:newContext},req({}),each(["toUrl","undef","defined","specified"],function(e){req[e]=function(){var t=contexts[defContextName];return t.require[e].apply(t,arguments)}}),isBrowser&&(head=s.head=document.getElementsByTagName("head")[0],(baseElement=document.getElementsByTagName("base")[0])&&(head=s.head=baseElement.parentNode)),req.onError=defaultOnError,req.createNode=function(e,t,i){var r=e.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");return r.type=e.scriptType||"text/javascript",r.charset="utf-8",r.async=!0,r},req.load=function(e,t,i){var r,n=e&&e.config||{};if(isBrowser)return(r=req.createNode(n,t,i)).setAttribute("data-requirecontext",e.contextName),r.setAttribute("data-requiremodule",t),!r.attachEvent||r.attachEvent.toString&&r.attachEvent.toString().indexOf("[native code")<0||isOpera?(r.addEventListener("load",e.onScriptLoad,!1),r.addEventListener("error",e.onScriptError,!1)):(useInteractive=!0,r.attachEvent("onreadystatechange",e.onScriptLoad)),r.src=i,n.onNodeCreated&&n.onNodeCreated(r,n,t,i),currentlyAddingScript=r,baseElement?head.insertBefore(r,baseElement):head.appendChild(r),currentlyAddingScript=null,r;if(isWebWorker)try{setTimeout(function(){},0),importScripts(i),e.completeLoad(t)}catch(r){e.onError(makeError("importscripts","importScripts failed for "+t+" at "+i,r,[t]))}},isBrowser&&!cfg.skipDataMain&&eachReverse(scripts(),function(e){if(head||(head=e.parentNode),dataMain=e.getAttribute("data-main"))return mainScript=dataMain,cfg.baseUrl||-1!==mainScript.indexOf("!")||(src=mainScript.split("/"),mainScript=src.pop(),subPath=src.length?src.join("/")+"/":"./",cfg.baseUrl=subPath),mainScript=mainScript.replace(jsSuffixRegExp,""),req.jsExtRegExp.test(mainScript)&&(mainScript=dataMain),cfg.deps=cfg.deps?cfg.deps.concat(mainScript):[mainScript],!0}),define=function(e,t,i){var r,n;"string"!=typeof e&&(i=t,t=e,e=null),isArray(t)||(i=t,t=null),!t&&isFunction(i)&&(t=[],i.length&&(i.toString().replace(commentRegExp,commentReplace).replace(cjsRequireRegExp,function(e,i){t.push(i)}),t=(1===i.length?["require"]:["require","exports","module"]).concat(t))),useInteractive&&(r=currentlyAddingScript||getInteractiveScript())&&(e||(e=r.getAttribute("data-requiremodule")),n=contexts[r.getAttribute("data-requirecontext")]),n?(n.defQueue.push([e,t,i]),n.defQueueMap[e]=!0):globalDefQueue.push([e,t,i])},define.amd={jQuery:!0},req.exec=function(text){return eval(text)},req(cfg)}}(this,"undefined"==typeof setTimeout?void 0:setTimeout); \ No newline at end of file diff --git a/static/plugs/ueditor/back/ueditor.all.js b/static/plugs/ueditor/back/ueditor.all.js deleted file mode 100644 index 31c51e667..000000000 --- a/static/plugs/ueditor/back/ueditor.all.js +++ /dev/null @@ -1,29431 +0,0 @@ -/*! - * UEditor - * version: ueditor - * build: Tue Aug 25 2015 15:23:01 GMT+0800 (CST) - */ - -(function(){ - -// editor.js -UEDITOR_CONFIG = window.UEDITOR_CONFIG || {}; - -var baidu = window.baidu || {}; - -window.baidu = baidu; - -window.UE = baidu.editor = window.UE || {}; - -UE.plugins = {}; - -UE.commands = {}; - -UE.instants = {}; - -UE.I18N = {}; - -UE._customizeUI = {}; - -UE.version = "1.4.3"; - -var dom = UE.dom = {}; - -// core/browser.js -/** - * 浏览器判断模块 - * @file - * @module UE.browser - * @since 1.2.6.1 - */ - -/** - * 提供浏览器检测的模块 - * @unfile - * @module UE.browser - */ -var browser = UE.browser = function(){ - var agent = navigator.userAgent.toLowerCase(), - opera = window.opera, - browser = { - /** - * @property {boolean} ie 检测当前浏览器是否为IE - * @example - * ```javascript - * if ( UE.browser.ie ) { - * console.log( '当前浏览器是IE' ); - * } - * ``` - */ - ie : /(msie\s|trident.*rv:)([\w.]+)/.test(agent), - - /** - * @property {boolean} opera 检测当前浏览器是否为Opera - * @example - * ```javascript - * if ( UE.browser.opera ) { - * console.log( '当前浏览器是Opera' ); - * } - * ``` - */ - opera : ( !!opera && opera.version ), - - /** - * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器 - * @example - * ```javascript - * if ( UE.browser.webkit ) { - * console.log( '当前浏览器是webkit内核浏览器' ); - * } - * ``` - */ - webkit : ( agent.indexOf( ' applewebkit/' ) > -1 ), - - /** - * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下 - * @example - * ```javascript - * if ( UE.browser.mac ) { - * console.log( '当前浏览器运行在mac平台下' ); - * } - * ``` - */ - mac : ( agent.indexOf( 'macintosh' ) > -1 ), - - /** - * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下 - * @example - * ```javascript - * if ( UE.browser.quirks ) { - * console.log( '当前浏览器运行处于“怪异模式”' ); - * } - * ``` - */ - quirks : ( document.compatMode == 'BackCompat' ) - }; - - /** - * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核 - * @example - * ```javascript - * if ( UE.browser.gecko ) { - * console.log( '当前浏览器内核是gecko内核' ); - * } - * ``` - */ - browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie); - - var version = 0; - - // Internet Explorer 6.0+ - if ( browser.ie ){ - - var v1 = agent.match(/(?:msie\s([\w.]+))/); - var v2 = agent.match(/(?:trident.*rv:([\w.]+))/); - if(v1 && v2 && v1[1] && v2[1]){ - version = Math.max(v1[1]*1,v2[1]*1); - }else if(v1 && v1[1]){ - version = v1[1]*1; - }else if(v2 && v2[1]){ - version = v2[1]*1; - }else{ - version = 0; - } - - browser.ie11Compat = document.documentMode == 11; - /** - * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式 - * @warning 如果浏览器不是IE, 则该值为undefined - * @example - * ```javascript - * if ( UE.browser.ie9Compat ) { - * console.log( '当前浏览器运行在IE9兼容模式下' ); - * } - * ``` - */ - browser.ie9Compat = document.documentMode == 9; - - /** - * @property { boolean } ie8 检测浏览器是否是IE8浏览器 - * @warning 如果浏览器不是IE, 则该值为undefined - * @example - * ```javascript - * if ( UE.browser.ie8 ) { - * console.log( '当前浏览器是IE8浏览器' ); - * } - * ``` - */ - browser.ie8 = !!document.documentMode; - - /** - * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式 - * @warning 如果浏览器不是IE, 则该值为undefined - * @example - * ```javascript - * if ( UE.browser.ie8Compat ) { - * console.log( '当前浏览器运行在IE8兼容模式下' ); - * } - * ``` - */ - browser.ie8Compat = document.documentMode == 8; - - /** - * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式 - * @warning 如果浏览器不是IE, 则该值为undefined - * @example - * ```javascript - * if ( UE.browser.ie7Compat ) { - * console.log( '当前浏览器运行在IE7兼容模式下' ); - * } - * ``` - */ - browser.ie7Compat = ( ( version == 7 && !document.documentMode ) - || document.documentMode == 7 ); - - /** - * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式 - * @warning 如果浏览器不是IE, 则该值为undefined - * @example - * ```javascript - * if ( UE.browser.ie6Compat ) { - * console.log( '当前浏览器运行在IE6模式或者怪异模式下' ); - * } - * ``` - */ - browser.ie6Compat = ( version < 7 || browser.quirks ); - - browser.ie9above = version > 8; - - browser.ie9below = version < 9; - - browser.ie11above = version > 10; - - browser.ie11below = version < 11; - - } - - // Gecko. - if ( browser.gecko ){ - var geckoRelease = agent.match( /rv:([\d\.]+)/ ); - if ( geckoRelease ) - { - geckoRelease = geckoRelease[1].split( '.' ); - version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1; - } - } - - /** - * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号 - * @warning 如果浏览器不是chrome, 则该值为undefined - * @example - * ```javascript - * if ( UE.browser.chrome ) { - * console.log( '当前浏览器是Chrome' ); - * } - * ``` - */ - if (/chrome\/(\d+\.\d)/i.test(agent)) { - browser.chrome = + RegExp['\x241']; - } - - /** - * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号 - * @warning 如果浏览器不是safari, 则该值为undefined - * @example - * ```javascript - * if ( UE.browser.safari ) { - * console.log( '当前浏览器是Safari' ); - * } - * ``` - */ - if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)){ - browser.safari = + (RegExp['\x241'] || RegExp['\x242']); - } - - - // Opera 9.50+ - if ( browser.opera ) - version = parseFloat( opera.version() ); - - // WebKit 522+ (Safari 3+) - if ( browser.webkit ) - version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] ); - - /** - * @property { Number } version 检测当前浏览器版本号 - * @remind - *
                    - *
                  • IE系列返回值为5,6,7,8,9,10等
                  • - *
                  • gecko系列会返回10900,158900等
                  • - *
                  • webkit系列会返回其build号 (如 522等)
                  • - *
                  - * @example - * ```javascript - * console.log( '当前浏览器版本号是: ' + UE.browser.version ); - * ``` - */ - browser.version = version; - - /** - * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容 - * @example - * ```javascript - * if ( UE.browser.isCompatible ) { - * console.log( '浏览器与UEditor能够良好兼容' ); - * } - * ``` - */ - browser.isCompatible = - !browser.mobile && ( - ( browser.ie && version >= 6 ) || - ( browser.gecko && version >= 10801 ) || - ( browser.opera && version >= 9.5 ) || - ( browser.air && version >= 1 ) || - ( browser.webkit && version >= 522 ) || - false ); - return browser; -}(); -//快捷方式 -var ie = browser.ie, - webkit = browser.webkit, - gecko = browser.gecko, - opera = browser.opera; - -// core/utils.js -/** - * 工具函数包 - * @file - * @module UE.utils - * @since 1.2.6.1 - */ - -/** - * UEditor封装使用的静态工具函数 - * @module UE.utils - * @unfile - */ - -var utils = UE.utils = { - - /** - * 用给定的迭代器遍历对象 - * @method each - * @param { Object } obj 需要遍历的对象 - * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key - * @example - * ```javascript - * var demoObj = { - * key1: 1, - * key2: 2 - * }; - * - * //output: key1: 1, key2: 2 - * UE.utils.each( demoObj, funciton ( value, key ) { - * - * console.log( key + ":" + value ); - * - * } ); - * ``` - */ - - /** - * 用给定的迭代器遍历数组或类数组对象 - * @method each - * @param { Array } array 需要遍历的数组或者类数组 - * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key - * @example - * ```javascript - * var divs = document.getElmentByTagNames( "div" ); - * - * //output: 0: DIV, 1: DIV ... - * UE.utils.each( divs, funciton ( value, key ) { - * - * console.log( key + ":" + value.tagName ); - * - * } ); - * ``` - */ - each : function(obj, iterator, context) { - if (obj == null) return; - if (obj.length === +obj.length) { - for (var i = 0, l = obj.length; i < l; i++) { - if(iterator.call(context, obj[i], i, obj) === false) - return false; - } - } else { - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - if(iterator.call(context, obj[key], key, obj) === false) - return false; - } - } - } - }, - - /** - * 以给定对象作为原型创建一个新对象 - * @method makeInstance - * @param { Object } protoObject 该对象将作为新创建对象的原型 - * @return { Object } 新的对象, 该对象的原型是给定的protoObject对象 - * @example - * ```javascript - * - * var protoObject = { sayHello: function () { console.log('Hello UEditor!'); } }; - * - * var newObject = UE.utils.makeInstance( protoObject ); - * //output: Hello UEditor! - * newObject.sayHello(); - * ``` - */ - makeInstance:function (obj) { - var noop = new Function(); - noop.prototype = obj; - obj = new noop; - noop.prototype = null; - return obj; - }, - - /** - * 将source对象中的属性扩展到target对象上 - * @method extend - * @remind 该方法将强制把source对象上的属性复制到target对象上 - * @see UE.utils.extend(Object,Object,Boolean) - * @param { Object } target 目标对象, 新的属性将附加到该对象上 - * @param { Object } source 源对象, 该对象的属性会被附加到target对象上 - * @return { Object } 返回target对象 - * @example - * ```javascript - * - * var target = { name: 'target', sex: 1 }, - * source = { name: 'source', age: 17 }; - * - * UE.utils.extend( target, source ); - * - * //output: { name: 'source', sex: 1, age: 17 } - * console.log( target ); - * - * ``` - */ - - /** - * 将source对象中的属性扩展到target对象上, 根据指定的isKeepTarget值决定是否保留目标对象中与 - * 源对象属性名相同的属性值。 - * @method extend - * @param { Object } target 目标对象, 新的属性将附加到该对象上 - * @param { Object } source 源对象, 该对象的属性会被附加到target对象上 - * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性 - * @return { Object } 返回target对象 - * @example - * ```javascript - * - * var target = { name: 'target', sex: 1 }, - * source = { name: 'source', age: 17 }; - * - * UE.utils.extend( target, source, true ); - * - * //output: { name: 'target', sex: 1, age: 17 } - * console.log( target ); - * - * ``` - */ - extend:function (t, s, b) { - if (s) { - for (var k in s) { - if (!b || !t.hasOwnProperty(k)) { - t[k] = s[k]; - } - } - } - return t; - }, - - /** - * 将给定的多个对象的属性复制到目标对象target上 - * @method extend2 - * @remind 该方法将强制把源对象上的属性复制到target对象上 - * @remind 该方法支持两个及以上的参数, 从第二个参数开始, 其属性都会被复制到第一个参数上。 如果遇到同名的属性, - * 将会覆盖掉之前的值。 - * @param { Object } target 目标对象, 新的属性将附加到该对象上 - * @param { Object... } source 源对象, 支持多个对象, 该对象的属性会被附加到target对象上 - * @return { Object } 返回target对象 - * @example - * ```javascript - * - * var target = {}, - * source1 = { name: 'source', age: 17 }, - * source2 = { title: 'dev' }; - * - * UE.utils.extend2( target, source1, source2 ); - * - * //output: { name: 'source', age: 17, title: 'dev' } - * console.log( target ); - * - * ``` - */ - extend2:function (t) { - var a = arguments; - for (var i = 1; i < a.length; i++) { - var x = a[i]; - for (var k in x) { - if (!t.hasOwnProperty(k)) { - t[k] = x[k]; - } - } - } - return t; - }, - - /** - * 模拟继承机制, 使得subClass继承自superClass - * @method inherits - * @param { Object } subClass 子类对象 - * @param { Object } superClass 超类对象 - * @warning 该方法只能让subClass继承超类的原型, subClass对象自身的属性和方法不会被继承 - * @return { Object } 继承superClass后的子类对象 - * @example - * ```javascript - * function SuperClass(){ - * this.name = "小李"; - * } - * - * SuperClass.prototype = { - * hello:function(str){ - * console.log(this.name + str); - * } - * } - * - * function SubClass(){ - * this.name = "小张"; - * } - * - * UE.utils.inherits(SubClass,SuperClass); - * - * var sub = new SubClass(); - * //output: '小张早上好! - * sub.hello("早上好!"); - * ``` - */ - inherits:function (subClass, superClass) { - var oldP = subClass.prototype, - newP = utils.makeInstance(superClass.prototype); - utils.extend(newP, oldP, true); - subClass.prototype = newP; - return (newP.constructor = subClass); - }, - - /** - * 用指定的context对象作为函数fn的上下文 - * @method bind - * @param { Function } fn 需要绑定上下文的函数对象 - * @param { Object } content 函数fn新的上下文对象 - * @return { Function } 一个新的函数, 该函数作为原始函数fn的代理, 将完成fn的上下文调换工作。 - * @example - * ```javascript - * - * var name = 'window', - * newTest = null; - * - * function test () { - * console.log( this.name ); - * } - * - * newTest = UE.utils.bind( test, { name: 'object' } ); - * - * //output: object - * newTest(); - * - * //output: window - * test(); - * - * ``` - */ - bind:function (fn, context) { - return function () { - return fn.apply(context, arguments); - }; - }, - - /** - * 创建延迟指定时间后执行的函数fn - * @method defer - * @param { Function } fn 需要延迟执行的函数对象 - * @param { int } delay 延迟的时间, 单位是毫秒 - * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后, - * 而不能保证刚好到达延迟时间时执行。 - * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果 - * @example - * ```javascript - * var start = 0; - * - * function test(){ - * console.log( new Date() - start ); - * } - * - * var testDefer = UE.utils.defer( test, 1000 ); - * // - * start = new Date(); - * //output: (大约在1000毫秒之后输出) 1000 - * testDefer(); - * ``` - */ - - /** - * 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法, 将会根据指定的exclusion的值, - * 决定是否取消前一次函数的执行, 如果exclusion的值为true, 则取消执行,反之,将继续执行前一个方法。 - * @method defer - * @param { Function } fn 需要延迟执行的函数对象 - * @param { int } delay 延迟的时间, 单位是毫秒 - * @param { Boolean } exclusion 如果在延迟时间内再次执行该函数,该值将决定是否取消执行前一次函数的执行, - * 值为true表示取消执行, 反之则将在执行前一次函数之后才执行本次函数调用。 - * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后, - * 而不能保证刚好到达延迟时间时执行。 - * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果 - * @example - * ```javascript - * - * function test(){ - * console.log(1); - * } - * - * var testDefer = UE.utils.defer( test, 1000, true ); - * - * //output: (两次调用仅有一次输出) 1 - * testDefer(); - * testDefer(); - * ``` - */ - defer:function (fn, delay, exclusion) { - var timerID; - return function () { - if (exclusion) { - clearTimeout(timerID); - } - timerID = setTimeout(fn, delay); - }; - }, - - /** - * 获取元素item在数组array中首次出现的位置, 如果未找到item, 则返回-1 - * @method indexOf - * @remind 该方法的匹配过程使用的是恒等“===” - * @param { Array } array 需要查找的数组对象 - * @param { * } item 需要在目标数组中查找的值 - * @return { int } 返回item在目标数组array中首次出现的位置, 如果在数组中未找到item, 则返回-1 - * @example - * ```javascript - * var item = 1, - * arr = [ 3, 4, 6, 8, 1, 1, 2 ]; - * - * //output: 4 - * console.log( UE.utils.indexOf( arr, item ) ); - * ``` - */ - - /** - * 获取元素item数组array中首次出现的位置, 如果未找到item, 则返回-1。通过start的值可以指定搜索的起始位置。 - * @method indexOf - * @remind 该方法的匹配过程使用的是恒等“===” - * @param { Array } array 需要查找的数组对象 - * @param { * } item 需要在目标数组中查找的值 - * @param { int } start 搜索的起始位置 - * @return { int } 返回item在目标数组array中的start位置之后首次出现的位置, 如果在数组中未找到item, 则返回-1 - * @example - * ```javascript - * var item = 1, - * arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ]; - * - * //output: 9 - * console.log( UE.utils.indexOf( arr, item, 5 ) ); - * ``` - */ - indexOf:function (array, item, start) { - var index = -1; - start = this.isNumber(start) ? start : 0; - this.each(array, function (v, i) { - if (i >= start && v === item) { - index = i; - return false; - } - }); - return index; - }, - - /** - * 移除数组array中所有的元素item - * @method removeItem - * @param { Array } array 要移除元素的目标数组 - * @param { * } item 将要被移除的元素 - * @remind 该方法的匹配过程使用的是恒等“===” - * @example - * ```javascript - * var arr = [ 4, 5, 7, 1, 3, 4, 6 ]; - * - * UE.utils.removeItem( arr, 4 ); - * //output: [ 5, 7, 1, 3, 6 ] - * console.log( arr ); - * - * ``` - */ - removeItem:function (array, item) { - for (var i = 0, l = array.length; i < l; i++) { - if (array[i] === item) { - array.splice(i, 1); - i--; - } - } - }, - - /** - * 删除字符串str的首尾空格 - * @method trim - * @param { String } str 需要删除首尾空格的字符串 - * @return { String } 删除了首尾的空格后的字符串 - * @example - * ```javascript - * - * var str = " UEdtior "; - * - * //output: 9 - * console.log( str.length ); - * - * //output: 7 - * console.log( UE.utils.trim( " UEdtior " ).length ); - * - * //output: 9 - * console.log( str.length ); - * - * ``` - */ - trim:function (str) { - return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, ''); - }, - - /** - * 将字符串str以','分隔成数组后,将该数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1 - * @method listToMap - * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。 - * @param { String } str 该字符串将被以','分割为数组, 然后进行转化 - * @return { Object } 转化之后的hash对象 - * @example - * ```javascript - * - * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1} - * console.log( UE.utils.listToMap( 'UEdtior,Hello' ) ); - * - * ``` - */ - - /** - * 将字符串数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1 - * @method listToMap - * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。 - * @param { Array } arr 字符串数组 - * @return { Object } 转化之后的hash对象 - * @example - * ```javascript - * - * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1} - * console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) ); - * - * ``` - */ - listToMap:function (list) { - if (!list)return {}; - list = utils.isArray(list) ? list : list.split(','); - for (var i = 0, ci, obj = {}; ci = list[i++];) { - obj[ci.toUpperCase()] = obj[ci] = 1; - } - return obj; - }, - - /** - * 将str中的html符号转义,将转义“',&,<,",>”五个字符 - * @method unhtml - * @param { String } str 需要转义的字符串 - * @return { String } 转义后的字符串 - * @example - * ```javascript - * var html = '&'; - * - * //output: <body>&</body> - * console.log( UE.utils.unhtml( html ) ); - * - * ``` - */ - unhtml:function (str, reg) { - return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) { - if (b) { - return a; - } else { - return { - '<':'<', - '&':'&', - '"':'"', - '>':'>', - "'":''' - }[a] - } - - }) : ''; - }, - - /** - * 将str中的转义字符还原成html字符 - * @see UE.utils.unhtml(String); - * @method html - * @param { String } str 需要逆转义的字符串 - * @return { String } 逆转义后的字符串 - * @example - * ```javascript - * - * var str = '<body>&</body>'; - * - * //output: & - * console.log( UE.utils.html( str ) ); - * - * ``` - */ - html:function (str) { - return str ? str.replace(/&((g|l|quo)t|amp|#39|nbsp);/g, function (m) { - return { - '<':'<', - '&':'&', - '"':'"', - '>':'>', - ''':"'", - ' ':' ' - }[m] - }) : ''; - }, - - /** - * 将css样式转换为驼峰的形式 - * @method cssStyleToDomStyle - * @param { String } cssName 需要转换的css样式名 - * @return { String } 转换成驼峰形式后的css样式名 - * @example - * ```javascript - * - * var str = 'border-top'; - * - * //output: borderTop - * console.log( UE.utils.cssStyleToDomStyle( str ) ); - * - * ``` - */ - cssStyleToDomStyle:function () { - var test = document.createElement('div').style, - cache = { - 'float':test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float' - }; - - return function (cssName) { - return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) { - return match.charAt(1).toUpperCase(); - })); - }; - }(), - - /** - * 动态加载文件到doc中 - * @method loadFile - * @param { DomDocument } document 需要加载资源文件的文档对象 - * @param { Object } options 加载资源文件的属性集合, 取值请参考代码示例 - * @example - * ```javascript - * - * UE.utils.loadFile( document, { - * src:"test.js", - * tag:"script", - * type:"text/javascript", - * defer:"defer" - * } ); - * - * ``` - */ - - /** - * 动态加载文件到doc中,加载成功后执行的回调函数fn - * @method loadFile - * @param { DomDocument } document 需要加载资源文件的文档对象 - * @param { Object } options 加载资源文件的属性集合, 该集合支持的值是script标签和style标签支持的所有属性。 - * @param { Function } fn 资源文件加载成功之后执行的回调 - * @warning 对于在同一个文档中多次加载同一URL的文件, 该方法会在第一次加载之后缓存该请求, - * 在此之后的所有同一URL的请求, 将会直接触发回调。 - * @example - * ```javascript - * - * UE.utils.loadFile( document, { - * src:"test.js", - * tag:"script", - * type:"text/javascript", - * defer:"defer" - * }, function () { - * console.log('加载成功'); - * } ); - * - * ``` - */ - loadFile:function () { - var tmpList = []; - - function getItem(doc, obj) { - try { - for (var i = 0, ci; ci = tmpList[i++];) { - if (ci.doc === doc && ci.url == (obj.src || obj.href)) { - return ci; - } - } - } catch (e) { - return null; - } - - } - - return function (doc, obj, fn) { - var item = getItem(doc, obj); - if (item) { - if (item.ready) { - fn && fn(); - } else { - item.funs.push(fn) - } - return; - } - tmpList.push({ - doc:doc, - url:obj.src || obj.href, - funs:[fn] - }); - if (!doc.body) { - var html = []; - for (var p in obj) { - if (p == 'tag')continue; - html.push(p + '="' + obj[p] + '"') - } - doc.write('<' + obj.tag + ' ' + html.join(' ') + ' >'); - return; - } - if (obj.id && doc.getElementById(obj.id)) { - return; - } - var element = doc.createElement(obj.tag); - delete obj.tag; - for (var p in obj) { - element.setAttribute(p, obj[p]); - } - element.onload = element.onreadystatechange = function () { - if (!this.readyState || /loaded|complete/.test(this.readyState)) { - item = getItem(doc, obj); - if (item.funs.length > 0) { - item.ready = 1; - for (var fi; fi = item.funs.pop();) { - fi(); - } - } - element.onload = element.onreadystatechange = null; - } - }; - element.onerror = function () { - throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file ueditor.config.js ') - }; - doc.getElementsByTagName("head")[0].appendChild(element); - } - }(), - - /** - * 判断obj对象是否为空 - * @method isEmptyObject - * @param { * } obj 需要判断的对象 - * @remind 如果判断的对象是NULL, 将直接返回true, 如果是数组且为空, 返回true, 如果是字符串, 且字符串为空, - * 返回true, 如果是普通对象, 且该对象没有任何实例属性, 返回true - * @return { Boolean } 对象是否为空 - * @example - * ```javascript - * - * //output: true - * console.log( UE.utils.isEmptyObject( {} ) ); - * - * //output: true - * console.log( UE.utils.isEmptyObject( [] ) ); - * - * //output: true - * console.log( UE.utils.isEmptyObject( "" ) ); - * - * //output: false - * console.log( UE.utils.isEmptyObject( { key: 1 } ) ); - * - * //output: false - * console.log( UE.utils.isEmptyObject( [1] ) ); - * - * //output: false - * console.log( UE.utils.isEmptyObject( "1" ) ); - * - * ``` - */ - isEmptyObject:function (obj) { - if (obj == null) return true; - if (this.isArray(obj) || this.isString(obj)) return obj.length === 0; - for (var key in obj) if (obj.hasOwnProperty(key)) return false; - return true; - }, - - /** - * 把rgb格式的颜色值转换成16进制格式 - * @method fixColor - * @param { String } rgb格式的颜色值 - * @param { String } - * @example - * rgb(255,255,255) => "#ffffff" - */ - fixColor:function (name, value) { - if (/color/i.test(name) && /rgba?/.test(value)) { - var array = value.split(","); - if (array.length > 3) - return ""; - value = "#"; - for (var i = 0, color; color = array[i++];) { - color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16); - value += color.length == 1 ? "0" + color : color; - } - value = value.toUpperCase(); - } - return value; - }, - /** - * 只针对border,padding,margin做了处理,因为性能问题 - * @public - * @function - * @param {String} val style字符串 - */ - optCss:function (val) { - var padding, margin, border; - val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function (str, key, name, val) { - if (val.split(' ').length == 1) { - switch (key) { - case 'padding': - !padding && (padding = {}); - padding[name] = val; - return ''; - case 'margin': - !margin && (margin = {}); - margin[name] = val; - return ''; - case 'border': - return val == 'initial' ? '' : str; - } - } - return str; - }); - - function opt(obj, name) { - if (!obj) { - return ''; - } - var t = obj.top , b = obj.bottom, l = obj.left, r = obj.right, val = ''; - if (!t || !l || !b || !r) { - for (var p in obj) { - val += ';' + name + '-' + p + ':' + obj[p] + ';'; - } - } else { - val += ';' + name + ':' + - (t == b && b == l && l == r ? t : - t == b && l == r ? (t + ' ' + l) : - l == r ? (t + ' ' + l + ' ' + b) : (t + ' ' + r + ' ' + b + ' ' + l)) + ';' - } - return val; - } - - val += opt(padding, 'padding') + opt(margin, 'margin'); - return val.replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '').replace(/;([ \n\r\t]+)|\1;/g, ';') - .replace(/(&((l|g)t|quot|#39))?;{2,}/g, function (a, b) { - return b ? b + ";;" : ';' - }); - }, - - /** - * 克隆对象 - * @method clone - * @param { Object } source 源对象 - * @return { Object } source的一个副本 - */ - - /** - * 深度克隆对象,将source的属性克隆到target对象, 会覆盖target重名的属性。 - * @method clone - * @param { Object } source 源对象 - * @param { Object } target 目标对象 - * @return { Object } 附加了source对象所有属性的target对象 - */ - clone:function (source, target) { - var tmp; - target = target || {}; - for (var i in source) { - if (source.hasOwnProperty(i)) { - tmp = source[i]; - if (typeof tmp == 'object') { - target[i] = utils.isArray(tmp) ? [] : {}; - utils.clone(source[i], target[i]) - } else { - target[i] = tmp; - } - } - } - return target; - }, - - /** - * 把cm/pt为单位的值转换为px为单位的值 - * @method transUnitToPx - * @param { String } 待转换的带单位的字符串 - * @return { String } 转换为px为计量单位的值的字符串 - * @example - * ```javascript - * - * //output: 500px - * console.log( UE.utils.transUnitToPx( '20cm' ) ); - * - * //output: 27px - * console.log( UE.utils.transUnitToPx( '20pt' ) ); - * - * ``` - */ - transUnitToPx:function (val) { - if (!/(pt|cm)/.test(val)) { - return val - } - var unit; - val.replace(/([\d.]+)(\w+)/, function (str, v, u) { - val = v; - unit = u; - }); - switch (unit) { - case 'cm': - val = parseFloat(val) * 25; - break; - case 'pt': - val = Math.round(parseFloat(val) * 96 / 72); - } - return val + (val ? 'px' : ''); - }, - - /** - * 在dom树ready之后执行给定的回调函数 - * @method domReady - * @remind 如果在执行该方法的时候, dom树已经ready, 那么回调函数将立刻执行 - * @param { Function } fn dom树ready之后的回调函数 - * @example - * ```javascript - * - * UE.utils.domReady( function () { - * - * console.log('123'); - * - * } ); - * - * ``` - */ - domReady:function () { - - var fnArr = []; - - function doReady(doc) { - //确保onready只执行一次 - doc.isReady = true; - for (var ci; ci = fnArr.pop(); ci()) { - } - } - - return function (onready, win) { - win = win || window; - var doc = win.document; - onready && fnArr.push(onready); - if (doc.readyState === "complete") { - doReady(doc); - } else { - doc.isReady && doReady(doc); - if (browser.ie && browser.version != 11) { - (function () { - if (doc.isReady) return; - try { - doc.documentElement.doScroll("left"); - } catch (error) { - setTimeout(arguments.callee, 0); - return; - } - doReady(doc); - })(); - win.attachEvent('onload', function () { - doReady(doc) - }); - } else { - doc.addEventListener("DOMContentLoaded", function () { - doc.removeEventListener("DOMContentLoaded", arguments.callee, false); - doReady(doc); - }, false); - win.addEventListener('load', function () { - doReady(doc) - }, false); - } - } - - } - }(), - - /** - * 动态添加css样式 - * @method cssRule - * @param { String } 节点名称 - * @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上']) - * @grammar UE.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色 - * @grammar UE.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc} - * @grammar UE.utils.cssRule('body',document) => 返回指定key的样式,并且指定是哪个document - * @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色 - */ - cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) { - var indexList, index; - if(style === undefined || style && style.nodeType && style.nodeType == 9){ - //获取样式 - doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document); - indexList = doc.indexList || (doc.indexList = {}); - index = indexList[key]; - if(index !== undefined){ - return doc.styleSheets[index].cssText - } - return undefined; - } - doc = doc || document; - indexList = doc.indexList || (doc.indexList = {}); - index = indexList[key]; - //清除样式 - if(style === ''){ - if(index!== undefined){ - doc.styleSheets[index].cssText = ''; - delete indexList[key]; - return true - } - return false; - } - - //添加样式 - if(index!== undefined){ - sheetStyle = doc.styleSheets[index]; - }else{ - sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length); - indexList[key] = index; - } - sheetStyle.cssText = style; - }: function (key, style, doc) { - var head, node; - if(style === undefined || style && style.nodeType && style.nodeType == 9){ - //获取样式 - doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document); - node = doc.getElementById(key); - return node ? node.innerHTML : undefined; - } - doc = doc || document; - node = doc.getElementById(key); - - //清除样式 - if(style === ''){ - if(node){ - node.parentNode.removeChild(node); - return true - } - return false; - } - - //添加样式 - if(node){ - node.innerHTML = style; - }else{ - node = doc.createElement('style'); - node.id = key; - node.innerHTML = style; - doc.getElementsByTagName('head')[0].appendChild(node); - } - }, - sort:function(array,compareFn){ - compareFn = compareFn || function(item1, item2){ return item1.localeCompare(item2);}; - for(var i= 0,len = array.length; i 0){ - var t = array[i]; - array[i] = array[j]; - array[j] = t; - } - } - } - return array; - }, - serializeParam:function (json) { - var strArr = []; - for (var i in json) { - //忽略默认的几个参数 - if(i=="method" || i=="timeout" || i=="async") continue; - //传递过来的对象和函数不在提交之列 - if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) { - strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) ); - } else if (utils.isArray(json[i])) { - //支持传数组内容 - for(var j = 0; j < json[i].length; j++) { - strArr.push( encodeURIComponent(i) + "[]="+encodeURIComponent(json[i][j]) ); - } - } - } - return strArr.join("&"); - }, - formatUrl:function (url) { - var u = url.replace(/&&/g, '&'); - u = u.replace(/\?&/g, '?'); - u = u.replace(/&$/g, ''); - u = u.replace(/&#/g, '#'); - u = u.replace(/&+/g, '&'); - return u; - }, - isCrossDomainUrl:function (url) { - var a = document.createElement('a'); - a.href = url; - if (browser.ie) { - a.href = a.href; - } - return !(a.protocol == location.protocol && a.hostname == location.hostname && - (a.port == location.port || (a.port == '80' && location.port == '') || (a.port == '' && location.port == '80'))); - }, - clearEmptyAttrs : function(obj){ - for(var p in obj){ - if(obj[p] === ''){ - delete obj[p] - } - } - return obj; - }, - str2json : function(s){ - - if (!utils.isString(s)) return null; - if (window.JSON) { - return JSON.parse(s); - } else { - return (new Function("return " + utils.trim(s || '')))(); - } - - }, - json2str : (function(){ - - if (window.JSON) { - - return JSON.stringify; - - } else { - - var escapeMap = { - "\b": '\\b', - "\t": '\\t', - "\n": '\\n', - "\f": '\\f', - "\r": '\\r', - '"' : '\\"', - "\\": '\\\\' - }; - - function encodeString(source) { - if (/["\\\x00-\x1f]/.test(source)) { - source = source.replace( - /["\\\x00-\x1f]/g, - function (match) { - var c = escapeMap[match]; - if (c) { - return c; - } - c = match.charCodeAt(); - return "\\u00" - + Math.floor(c / 16).toString(16) - + (c % 16).toString(16); - }); - } - return '"' + source + '"'; - } - - function encodeArray(source) { - var result = ["["], - l = source.length, - preComma, i, item; - - for (i = 0; i < l; i++) { - item = source[i]; - - switch (typeof item) { - case "undefined": - case "function": - case "unknown": - break; - default: - if(preComma) { - result.push(','); - } - result.push(utils.json2str(item)); - preComma = 1; - } - } - result.push("]"); - return result.join(""); - } - - function pad(source) { - return source < 10 ? '0' + source : source; - } - - function encodeDate(source){ - return '"' + source.getFullYear() + "-" - + pad(source.getMonth() + 1) + "-" - + pad(source.getDate()) + "T" - + pad(source.getHours()) + ":" - + pad(source.getMinutes()) + ":" - + pad(source.getSeconds()) + '"'; - } - - return function (value) { - switch (typeof value) { - case 'undefined': - return 'undefined'; - - case 'number': - return isFinite(value) ? String(value) : "null"; - - case 'string': - return encodeString(value); - - case 'boolean': - return String(value); - - default: - if (value === null) { - return 'null'; - } else if (utils.isArray(value)) { - return encodeArray(value); - } else if (utils.isDate(value)) { - return encodeDate(value); - } else { - var result = ['{'], - encode = utils.json2str, - preComma, - item; - - for (var key in value) { - if (Object.prototype.hasOwnProperty.call(value, key)) { - item = value[key]; - switch (typeof item) { - case 'undefined': - case 'unknown': - case 'function': - break; - default: - if (preComma) { - result.push(','); - } - preComma = 1; - result.push(encode(key) + ':' + encode(item)); - } - } - } - result.push('}'); - return result.join(''); - } - } - }; - } - - })() - -}; -/** - * 判断给定的对象是否是字符串 - * @method isString - * @param { * } object 需要判断的对象 - * @return { Boolean } 给定的对象是否是字符串 - */ - -/** - * 判断给定的对象是否是数组 - * @method isArray - * @param { * } object 需要判断的对象 - * @return { Boolean } 给定的对象是否是数组 - */ - -/** - * 判断给定的对象是否是一个Function - * @method isFunction - * @param { * } object 需要判断的对象 - * @return { Boolean } 给定的对象是否是Function - */ - -/** - * 判断给定的对象是否是Number - * @method isNumber - * @param { * } object 需要判断的对象 - * @return { Boolean } 给定的对象是否是Number - */ - -/** - * 判断给定的对象是否是一个正则表达式 - * @method isRegExp - * @param { * } object 需要判断的对象 - * @return { Boolean } 给定的对象是否是正则表达式 - */ - -/** - * 判断给定的对象是否是一个普通对象 - * @method isObject - * @param { * } object 需要判断的对象 - * @return { Boolean } 给定的对象是否是普通对象 - */ -utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object', 'Date'], function (v) { - UE.utils['is' + v] = function (obj) { - return Object.prototype.toString.apply(obj) == '[object ' + v + ']'; - } -}); - -// core/EventBase.js -/** - * UE采用的事件基类 - * @file - * @module UE - * @class EventBase - * @since 1.2.6.1 - */ - -/** - * UEditor公用空间,UEditor所有的功能都挂载在该空间下 - * @unfile - * @module UE - */ - -/** - * UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。 - * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。 - * @unfile - * @module UE - * @class EventBase - */ - -/** - * 通过此构造器,子类可以继承EventBase获取事件监听的方法 - * @constructor - * @example - * ```javascript - * UE.EventBase.call(editor); - * ``` - */ -var EventBase = UE.EventBase = function () {}; - -EventBase.prototype = { - - /** - * 注册事件监听器 - * @method addListener - * @param { String } types 监听的事件名称,同时监听多个事件使用空格分隔 - * @param { Function } fn 监听的事件被触发时,会执行该回调函数 - * @waining 事件被触发时,监听的函数假如返回的值恒等于true,回调函数的队列中后面的函数将不执行 - * @example - * ```javascript - * editor.addListener('selectionchange',function(){ - * console.log("选区已经变化!"); - * }) - * editor.addListener('beforegetcontent aftergetcontent',function(type){ - * if(type == 'beforegetcontent'){ - * //do something - * }else{ - * //do something - * } - * console.log(this.getContent) // this是注册的事件的编辑器实例 - * }) - * ``` - * @see UE.EventBase:fireEvent(String) - */ - addListener:function (types, listener) { - types = utils.trim(types).split(/\s+/); - for (var i = 0, ti; ti = types[i++];) { - getListener(this, ti, true).push(listener); - } - }, - - on : function(types, listener){ - return this.addListener(types,listener); - }, - off : function(types, listener){ - return this.removeListener(types, listener) - }, - trigger:function(){ - return this.fireEvent.apply(this,arguments); - }, - /** - * 移除事件监听器 - * @method removeListener - * @param { String } types 移除的事件名称,同时移除多个事件使用空格分隔 - * @param { Function } fn 移除监听事件的函数引用 - * @example - * ```javascript - * //changeCallback为方法体 - * editor.removeListener("selectionchange",changeCallback); - * ``` - */ - removeListener:function (types, listener) { - types = utils.trim(types).split(/\s+/); - for (var i = 0, ti; ti = types[i++];) { - utils.removeItem(getListener(this, ti) || [], listener); - } - }, - - /** - * 触发事件 - * @method fireEvent - * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔 - * @remind 该方法会触发addListener - * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值 - * @example - * ```javascript - * editor.fireEvent("selectionchange"); - * ``` - */ - - /** - * 触发事件 - * @method fireEvent - * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔 - * @param { *... } options 可选参数,可以传入一个或多个参数,会传给事件触发的回调函数 - * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值 - * @example - * ```javascript - * - * editor.addListener( "selectionchange", function ( type, arg1, arg2 ) { - * - * console.log( arg1 + " " + arg2 ); - * - * } ); - * - * //触发selectionchange事件, 会执行上面的事件监听器 - * //output: Hello World - * editor.fireEvent("selectionchange", "Hello", "World"); - * ``` - */ - fireEvent:function () { - var types = arguments[0]; - types = utils.trim(types).split(' '); - for (var i = 0, ti; ti = types[i++];) { - var listeners = getListener(this, ti), - r, t, k; - if (listeners) { - k = listeners.length; - while (k--) { - if(!listeners[k])continue; - t = listeners[k].apply(this, arguments); - if(t === true){ - return t; - } - if (t !== undefined) { - r = t; - } - } - } - if (t = this['on' + ti.toLowerCase()]) { - r = t.apply(this, arguments); - } - } - return r; - } -}; -/** - * 获得对象所拥有监听类型的所有监听器 - * @unfile - * @module UE - * @since 1.2.6.1 - * @method getListener - * @public - * @param { Object } obj 查询监听器的对象 - * @param { String } type 事件类型 - * @param { Boolean } force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组 - * @return { Array } 监听器数组 - */ -function getListener(obj, type, force) { - var allListeners; - type = type.toLowerCase(); - return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) ) - && ( allListeners[type] || force && ( allListeners[type] = [] ) ) ); -} - - - -// core/dtd.js -///import editor.js -///import core/dom/dom.js -///import core/utils.js -/** - * dtd html语义化的体现类 - * @constructor - * @namespace dtd - */ -var dtd = dom.dtd = (function() { - function _( s ) { - for (var k in s) { - s[k.toUpperCase()] = s[k]; - } - return s; - } - var X = utils.extend2; - var A = _({isindex:1,fieldset:1}), - B = _({input:1,button:1,select:1,textarea:1,label:1}), - C = X( _({a:1}), B ), - D = X( {iframe:1}, C ), - E = _({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}), - F = _({ins:1,del:1,script:1,style:1}), - G = X( _({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}), F ), - H = X( _({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}), G ), - I = X( _({p:1}), H ), - J = X( _({iframe:1}), H, B ), - K = _({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}), - - L = X( _({a:0}), J ),//a不能被切开,所以把他 - M = _({tr:1}), - N = _({'#':1}), - O = X( _({param:1}), K ), - P = X( _({form:1}), A, D, E, I ), - Q = _({li:1,ol:1,ul:1}), - R = _({style:1,script:1}), - S = _({base:1,link:1,meta:1,title:1}), - T = X( S, R ), - U = _({head:1,body:1}), - V = _({html:1}); - - var block = _({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}), - - empty = _({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1}); - - return _({ - - // $ 表示自定的属性 - - // body外的元素列表. - $nonBodyContent: X( V, U, S ), - - //块结构元素列表 - $block : block, - - //内联元素列表 - $inline : L, - - $inlineWithA : X(_({a:1}),L), - - $body : X( _({script:1,style:1}), block ), - - $cdata : _({script:1,style:1}), - - //自闭和元素 - $empty : empty, - - //不是自闭合,但不能让range选中里边 - $nonChild : _({iframe:1,textarea:1}), - //列表元素列表 - $listItem : _({dd:1,dt:1,li:1}), - - //列表根元素列表 - $list: _({ul:1,ol:1,dl:1}), - - //不能认为是空的元素 - $isNotEmpty : _({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}), - - //如果没有子节点就可以删除的元素列表,像span,a - $removeEmpty : _({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1}), - - $removeEmptyBlock : _({'p':1,'div':1}), - - //在table元素里的元素列表 - $tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}), - //不转换的标签 - $notTransContent : _({pre:1,script:1,style:1,textarea:1}), - html: U, - head: T, - style: N, - script: N, - body: P, - base: {}, - link: {}, - meta: {}, - title: N, - col : {}, - tr : _({td:1,th:1}), - img : {}, - embed: {}, - colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}), - noscript : P, - td : P, - br : {}, - th : P, - center : P, - kbd : L, - button : X( I, E ), - basefont : {}, - h5 : L, - h4 : L, - samp : L, - h6 : L, - ol : Q, - h1 : L, - h3 : L, - option : N, - h2 : L, - form : X( A, D, E, I ), - select : _({optgroup:1,option:1}), - font : L, - ins : L, - menu : Q, - abbr : L, - label : L, - table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}), - code : L, - tfoot : M, - cite : L, - li : P, - input : {}, - iframe : P, - strong : L, - textarea : N, - noframes : P, - big : L, - small : L, - //trace: - span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}), - hr : L, - dt : L, - sub : L, - optgroup : _({option:1}), - param : {}, - bdo : L, - 'var' : L, - div : P, - object : O, - sup : L, - dd : P, - strike : L, - area : {}, - dir : Q, - map : X( _({area:1,form:1,p:1}), A, F, E ), - applet : O, - dl : _({dt:1,dd:1}), - del : L, - isindex : {}, - fieldset : X( _({legend:1}), K ), - thead : M, - ul : Q, - acronym : L, - b : L, - a : X( _({a:1}), J ), - blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P), - caption : L, - i : L, - u : L, - tbody : M, - s : L, - address : X( D, I ), - tt : L, - legend : L, - q : L, - pre : X( G, C ), - p : X(_({'a':1}),L), - em :L, - dfn : L - }); -})(); - - -// core/domUtils.js -/** - * Dom操作工具包 - * @file - * @module UE.dom.domUtils - * @since 1.2.6.1 - */ - -/** - * Dom操作工具包 - * @unfile - * @module UE.dom.domUtils - */ -function getDomNode(node, start, ltr, startFromChild, fn, guard) { - var tmpNode = startFromChild && node[start], - parent; - !tmpNode && (tmpNode = node[ltr]); - while (!tmpNode && (parent = (parent || node).parentNode)) { - if (parent.tagName == 'BODY' || guard && !guard(parent)) { - return null; - } - tmpNode = parent[ltr]; - } - if (tmpNode && fn && !fn(tmpNode)) { - return getDomNode(tmpNode, start, ltr, false, fn); - } - return tmpNode; -} -var attrFix = ie && browser.version < 9 ? { - tabindex:"tabIndex", - readonly:"readOnly", - "for":"htmlFor", - "class":"className", - maxlength:"maxLength", - cellspacing:"cellSpacing", - cellpadding:"cellPadding", - rowspan:"rowSpan", - colspan:"colSpan", - usemap:"useMap", - frameborder:"frameBorder" - } : { - tabindex:"tabIndex", - readonly:"readOnly" - }, - styleBlock = utils.listToMap([ - '-webkit-box', '-moz-box', 'block' , - 'list-item' , 'table' , 'table-row-group' , - 'table-header-group', 'table-footer-group' , - 'table-row' , 'table-column-group' , 'table-column' , - 'table-cell' , 'table-caption' - ]); -var domUtils = dom.domUtils = { - //节点常量 - NODE_ELEMENT:1, - NODE_DOCUMENT:9, - NODE_TEXT:3, - NODE_COMMENT:8, - NODE_DOCUMENT_FRAGMENT:11, - - //位置关系 - POSITION_IDENTICAL:0, - POSITION_DISCONNECTED:1, - POSITION_FOLLOWING:2, - POSITION_PRECEDING:4, - POSITION_IS_CONTAINED:8, - POSITION_CONTAINS:16, - //ie6使用其他的会有一段空白出现 - fillChar:ie && browser.version == '6' ? '\ufeff' : '\u200B', - //-------------------------Node部分-------------------------------- - keys:{ - /*Backspace*/ 8:1, /*Delete*/ 46:1, - /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1, - 37:1, 38:1, 39:1, 40:1, - 13:1 /*enter*/ - }, - /** - * 获取节点A相对于节点B的位置关系 - * @method getPosition - * @param { Node } nodeA 需要查询位置关系的节点A - * @param { Node } nodeB 需要查询位置关系的节点B - * @return { Number } 节点A与节点B的关系 - * @example - * ```javascript - * //output: 20 - * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body ); - * - * switch ( position ) { - * - * //0 - * case UE.dom.domUtils.POSITION_IDENTICAL: - * console.log('元素相同'); - * break; - * //1 - * case UE.dom.domUtils.POSITION_DISCONNECTED: - * console.log('两个节点在不同的文档中'); - * break; - * //2 - * case UE.dom.domUtils.POSITION_FOLLOWING: - * console.log('节点A在节点B之后'); - * break; - * //4 - * case UE.dom.domUtils.POSITION_PRECEDING; - * console.log('节点A在节点B之前'); - * break; - * //8 - * case UE.dom.domUtils.POSITION_IS_CONTAINED: - * console.log('节点A被节点B包含'); - * break; - * case 10: - * console.log('节点A被节点B包含且节点A在节点B之后'); - * break; - * //16 - * case UE.dom.domUtils.POSITION_CONTAINS: - * console.log('节点A包含节点B'); - * break; - * case 20: - * console.log('节点A包含节点B且节点A在节点B之前'); - * break; - * - * } - * ``` - */ - getPosition:function (nodeA, nodeB) { - // 如果两个节点是同一个节点 - if (nodeA === nodeB) { - // domUtils.POSITION_IDENTICAL - return 0; - } - var node, - parentsA = [nodeA], - parentsB = [nodeB]; - node = nodeA; - while (node = node.parentNode) { - // 如果nodeB是nodeA的祖先节点 - if (node === nodeB) { - // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING - return 10; - } - parentsA.push(node); - } - node = nodeB; - while (node = node.parentNode) { - // 如果nodeA是nodeB的祖先节点 - if (node === nodeA) { - // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING - return 20; - } - parentsB.push(node); - } - parentsA.reverse(); - parentsB.reverse(); - if (parentsA[0] !== parentsB[0]) { - // domUtils.POSITION_DISCONNECTED - return 1; - } - var i = -1; - while (i++, parentsA[i] === parentsB[i]) { - } - nodeA = parentsA[i]; - nodeB = parentsB[i]; - while (nodeA = nodeA.nextSibling) { - if (nodeA === nodeB) { - // domUtils.POSITION_PRECEDING - return 4 - } - } - // domUtils.POSITION_FOLLOWING - return 2; - }, - - /** - * 检测节点node在父节点中的索引位置 - * @method getNodeIndex - * @param { Node } node 需要检测的节点对象 - * @return { Number } 该节点在父节点中的位置 - * @see UE.dom.domUtils.getNodeIndex(Node,Boolean) - */ - - /** - * 检测节点node在父节点中的索引位置, 根据给定的mergeTextNode参数决定是否要合并多个连续的文本节点为一个节点 - * @method getNodeIndex - * @param { Node } node 需要检测的节点对象 - * @param { Boolean } mergeTextNode 是否合并多个连续的文本节点为一个节点 - * @return { Number } 该节点在父节点中的位置 - * @example - * ```javascript - * - * var node = document.createElement("div"); - * - * node.appendChild( document.createTextNode( "hello" ) ); - * node.appendChild( document.createTextNode( "world" ) ); - * node.appendChild( node = document.createElement( "div" ) ); - * - * //output: 2 - * console.log( UE.dom.domUtils.getNodeIndex( node ) ); - * - * //output: 1 - * console.log( UE.dom.domUtils.getNodeIndex( node, true ) ); - * - * ``` - */ - getNodeIndex:function (node, ignoreTextNode) { - var preNode = node, - i = 0; - while (preNode = preNode.previousSibling) { - if (ignoreTextNode && preNode.nodeType == 3) { - if(preNode.nodeType != preNode.nextSibling.nodeType ){ - i++; - } - continue; - } - i++; - } - return i; - }, - - /** - * 检测节点node是否在给定的document对象上 - * @method inDoc - * @param { Node } node 需要检测的节点对象 - * @param { DomDocument } doc 需要检测的document对象 - * @return { Boolean } 该节点node是否在给定的document的dom树上 - * @example - * ```javascript - * - * var node = document.createElement("div"); - * - * //output: false - * console.log( UE.do.domUtils.inDoc( node, document ) ); - * - * document.body.appendChild( node ); - * - * //output: true - * console.log( UE.do.domUtils.inDoc( node, document ) ); - * - * ``` - */ - inDoc:function (node, doc) { - return domUtils.getPosition(node, doc) == 10; - }, - /** - * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点, - * 查找的起点是给定node节点的父节点。 - * @method findParent - * @param { Node } node 需要查找的节点 - * @param { Function } filterFn 自定义的过滤方法。 - * @warning 查找的终点是到body节点为止 - * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该 - * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。 - * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL - * @example - * ```javascript - * var filterNode = UE.dom.domUtils.findParent( document.body.firstChild, function ( node ) { - * - * //由于查找的终点是body节点, 所以永远也不会匹配当前过滤器的条件, 即这里永远会返回false - * return node.tagName === "HTML"; - * - * } ); - * - * //output: true - * console.log( filterNode === null ); - * ``` - */ - - /** - * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点, - * 如果includeSelf的值为true,则查找的起点是给定的节点node, 否则, 起点是node的父节点 - * @method findParent - * @param { Node } node 需要查找的节点 - * @param { Function } filterFn 自定义的过滤方法。 - * @param { Boolean } includeSelf 查找过程是否包含自身 - * @warning 查找的终点是到body节点为止 - * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该 - * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。 - * @remind 如果includeSelf为true, 则过滤器第一次执行时的参数会是节点本身。 - * 反之, 过滤器第一次执行时的参数将是该节点的父节点。 - * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL - * @example - * ```html - * - * - *
                  - *
                  - * - * - * - * ``` - */ - findParent:function (node, filterFn, includeSelf) { - if (node && !domUtils.isBody(node)) { - node = includeSelf ? node : node.parentNode; - while (node) { - if (!filterFn || filterFn(node) || domUtils.isBody(node)) { - return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node; - } - node = node.parentNode; - } - } - return null; - }, - /** - * 查找node的节点名为tagName的第一个祖先节点, 查找的起点是node节点的父节点。 - * @method findParentByTagName - * @param { Node } node 需要查找的节点对象 - * @param { Array } tagNames 需要查找的父节点的名称数组 - * @warning 查找的终点是到body节点为止 - * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL - * @example - * ```javascript - * var node = UE.dom.domUtils.findParentByTagName( document.getElementsByTagName("div")[0], [ "BODY" ] ); - * //output: BODY - * console.log( node.tagName ); - * ``` - */ - - /** - * 查找node的节点名为tagName的祖先节点, 如果includeSelf的值为true,则查找的起点是给定的节点node, - * 否则, 起点是node的父节点。 - * @method findParentByTagName - * @param { Node } node 需要查找的节点对象 - * @param { Array } tagNames 需要查找的父节点的名称数组 - * @param { Boolean } includeSelf 查找过程是否包含node节点自身 - * @warning 查找的终点是到body节点为止 - * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL - * @example - * ```javascript - * var queryTarget = document.getElementsByTagName("div")[0]; - * var node = UE.dom.domUtils.findParentByTagName( queryTarget, [ "DIV" ], true ); - * //output: true - * console.log( queryTarget === node ); - * ``` - */ - findParentByTagName:function (node, tagNames, includeSelf, excludeFn) { - tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]); - return domUtils.findParent(node, function (node) { - return tagNames[node.tagName] && !(excludeFn && excludeFn(node)); - }, includeSelf); - }, - /** - * 查找节点node的祖先节点集合, 查找的起点是给定节点的父节点,结果集中不包含给定的节点。 - * @method findParents - * @param { Node } node 需要查找的节点对象 - * @return { Array } 给定节点的祖先节点数组 - * @grammar UE.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身 - * @grammar UE.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身 - * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取 - * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个 - */ - - /** - * 查找节点node的祖先节点集合, 如果includeSelf的值为true, - * 则返回的结果集中允许出现当前给定的节点, 否则, 该节点不会出现在其结果集中。 - * @method findParents - * @param { Node } node 需要查找的节点对象 - * @param { Boolean } includeSelf 查找的结果中是否允许包含当前查找的节点对象 - * @return { Array } 给定节点的祖先节点数组 - */ - findParents:function (node, includeSelf, filterFn, closerFirst) { - var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : []; - while (node = domUtils.findParent(node, filterFn)) { - parents.push(node); - } - return closerFirst ? parents : parents.reverse(); - }, - - /** - * 在节点node后面插入新节点newNode - * @method insertAfter - * @param { Node } node 目标节点 - * @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后 - * @return { Node } 新插入的节点 - */ - insertAfter:function (node, newNode) { - return node.nextSibling ? node.parentNode.insertBefore(newNode, node.nextSibling): - node.parentNode.appendChild(newNode); - }, - - /** - * 删除节点node及其下属的所有节点 - * @method remove - * @param { Node } node 需要删除的节点对象 - * @return { Node } 返回刚删除的节点对象 - * @example - * ```html - *
                  - *
                  你好
                  - *
                  - * - * ``` - */ - - /** - * 删除节点node,并根据keepChildren的值决定是否保留子节点 - * @method remove - * @param { Node } node 需要删除的节点对象 - * @param { Boolean } keepChildren 是否需要保留子节点 - * @return { Node } 返回刚删除的节点对象 - * @example - * ```html - *
                  - *
                  你好
                  - *
                  - * - * ``` - */ - remove:function (node, keepChildren) { - var parent = node.parentNode, - child; - if (parent) { - if (keepChildren && node.hasChildNodes()) { - while (child = node.firstChild) { - parent.insertBefore(child, node); - } - } - parent.removeChild(node); - } - return node; - }, - - /** - * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点, - * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。 - * @method getNextDomNode - * @param { Node } node 需要获取其后的兄弟节点的节点对象 - * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL - * @example - * ```html - * - *
                  - * - *
                  - * xxx - * - * - * ``` - * @example - * ```html - * - *
                  - * - * xxx - *
                  - * xxx - * - * - * ``` - */ - - /** - * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点, - * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false, - * 则执行getNextDomNode(Node node)的查找过程。 - * @method getNextDomNode - * @param { Node } node 需要获取其后的兄弟节点的节点对象 - * @param { Boolean } startFromChild 查找过程是否从其子节点开始 - * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL - * @see UE.dom.domUtils.getNextDomNode(Node) - */ - getNextDomNode:function (node, startFromChild, filterFn, guard) { - return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard); - }, - getPreDomNode:function (node, startFromChild, filterFn, guard) { - return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard); - }, - /** - * 检测节点node是否属是UEditor定义的bookmark节点 - * @method isBookmarkNode - * @private - * @param { Node } node 需要检测的节点对象 - * @return { Boolean } 是否是bookmark节点 - * @example - * ```html - * - * - * ``` - */ - isBookmarkNode:function (node) { - return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id); - }, - /** - * 获取节点node所属的window对象 - * @method getWindow - * @param { Node } node 节点对象 - * @return { Window } 当前节点所属的window对象 - * @example - * ```javascript - * //output: true - * console.log( UE.dom.domUtils.getWindow( document.body ) === window ); - * ``` - */ - getWindow:function (node) { - var doc = node.ownerDocument || node; - return doc.defaultView || doc.parentWindow; - }, - /** - * 获取离nodeA与nodeB最近的公共的祖先节点 - * @method getCommonAncestor - * @param { Node } nodeA 第一个节点 - * @param { Node } nodeB 第二个节点 - * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。 - * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。 - * @example - * ```javascript - * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild ); - * //output: true - * console.log( commonAncestor.tagName.toLowerCase() === 'body' ); - * ``` - */ - getCommonAncestor:function (nodeA, nodeB) { - if (nodeA === nodeB) - return nodeA; - var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1; - while (parent = parent.parentNode) { - if (parent === nodeB) { - return parent; - } - parentsA.push(parent); - } - parent = nodeB; - while (parent = parent.parentNode) { - if (parent === nodeA) - return parent; - parentsB.push(parent); - } - parentsA.reverse(); - parentsB.reverse(); - while (i++, parentsA[i] === parentsB[i]) { - } - return i == 0 ? null : parentsA[i - 1]; - - }, - /** - * 清除node节点左右连续为空的兄弟inline节点 - * @method clearEmptySibling - * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点, - * 则这些兄弟节点将被删除 - * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点 - * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点 - * @example - * ```html - * - *
                  - * - * - * - * xxx - * - * - * - * ``` - */ - - /** - * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true, - * 则忽略对右边兄弟节点的操作。 - * @method clearEmptySibling - * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点, - * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作 - * 则这些兄弟节点将被删除 - * @see UE.dom.domUtils.clearEmptySibling(Node) - */ - - /** - * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true, - * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。 - * @method clearEmptySibling - * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点, - * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作 - * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作 - * 则这些兄弟节点将被删除 - * @see UE.dom.domUtils.clearEmptySibling(Node) - */ - clearEmptySibling:function (node, ignoreNext, ignorePre) { - function clear(next, dir) { - var tmpNode; - while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next) - //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了 - || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) { - tmpNode = next[dir]; - domUtils.remove(next); - next = tmpNode; - } - } - !ignoreNext && clear(node.nextSibling, 'nextSibling'); - !ignorePre && clear(node.previousSibling, 'previousSibling'); - }, - /** - * 将一个文本节点textNode拆分成两个文本节点,offset指定拆分位置 - * @method split - * @param { Node } textNode 需要拆分的文本节点对象 - * @param { int } offset 需要拆分的位置, 位置计算从0开始 - * @return { Node } 拆分后形成的新节点 - * @example - * ```html - *
                  abcdef
                  - * - * ``` - */ - split:function (node, offset) { - var doc = node.ownerDocument; - if (browser.ie && offset == node.nodeValue.length) { - var next = doc.createTextNode(''); - return domUtils.insertAfter(node, next); - } - var retval = node.splitText(offset); - //ie8下splitText不会跟新childNodes,我们手动触发他的更新 - if (browser.ie8) { - var tmpNode = doc.createTextNode(''); - domUtils.insertAfter(retval, tmpNode); - domUtils.remove(tmpNode); - } - return retval; - }, - - /** - * 检测文本节点textNode是否为空节点(包括空格、换行、占位符等字符) - * @method isWhitespace - * @param { Node } node 需要检测的节点对象 - * @return { Boolean } 检测的节点是否为空 - * @example - * ```html - *
                  - * - *
                  - * - * ``` - */ - isWhitespace:function (node) { - return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue); - }, - /** - * 获取元素element相对于viewport的位置坐标 - * @method getXY - * @param { Node } element 需要计算位置的节点对象 - * @return { Object } 返回形如{x:left,y:top}的一个key-value映射对象, 其中键x代表水平偏移距离, - * y代表垂直偏移距离。 - * - * @example - * ```javascript - * var location = UE.dom.domUtils.getXY( document.getElementById("test") ); - * //output: test的坐标为: 12, 24 - * console.log( 'test的坐标为: ', location.x, ',', location.y ); - * ``` - */ - getXY:function (element) { - var x = 0, y = 0; - while (element.offsetParent) { - y += element.offsetTop; - x += element.offsetLeft; - element = element.offsetParent; - } - return { 'x':x, 'y':y}; - }, - /** - * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数 - * @method on - * @param { Node } element 需要绑定事件的节点对象 - * @param { String } type 绑定的事件类型 - * @param { Function } handler 事件处理器 - * @example - * ```javascript - * UE.dom.domUtils.on(document.body,"click",function(e){ - * //e为事件对象,this为被点击元素对戏那个 - * }); - * ``` - */ - - /** - * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数 - * @method on - * @param { Node } element 需要绑定事件的节点对象 - * @param { Array } type 绑定的事件类型数组 - * @param { Function } handler 事件处理器 - * @example - * ```javascript - * UE.dom.domUtils.on(document.body,["click","mousedown"],function(evt){ - * //evt为事件对象,this为被点击元素对象 - * }); - * ``` - */ - on:function (element, type, handler) { - - var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/), - k = types.length; - if (k) while (k--) { - type = types[k]; - if (element.addEventListener) { - element.addEventListener(type, handler, false); - } else { - if (!handler._d) { - handler._d = { - els : [] - }; - } - var key = type + handler.toString(),index = utils.indexOf(handler._d.els,element); - if (!handler._d[key] || index == -1) { - if(index == -1){ - handler._d.els.push(element); - } - if(!handler._d[key]){ - handler._d[key] = function (evt) { - return handler.call(evt.srcElement, evt || window.event); - }; - } - - - element.attachEvent('on' + type, handler._d[key]); - } - } - } - element = null; - }, - /** - * 解除DOM事件绑定 - * @method un - * @param { Node } element 需要解除事件绑定的节点对象 - * @param { String } type 需要接触绑定的事件类型 - * @param { Function } handler 对应的事件处理器 - * @example - * ```javascript - * UE.dom.domUtils.un(document.body,"click",function(evt){ - * //evt为事件对象,this为被点击元素对象 - * }); - * ``` - */ - - /** - * 解除DOM事件绑定 - * @method un - * @param { Node } element 需要解除事件绑定的节点对象 - * @param { Array } type 需要接触绑定的事件类型数组 - * @param { Function } handler 对应的事件处理器 - * @example - * ```javascript - * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){ - * //evt为事件对象,this为被点击元素对象 - * }); - * ``` - */ - un:function (element, type, handler) { - var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/), - k = types.length; - if (k) while (k--) { - type = types[k]; - if (element.removeEventListener) { - element.removeEventListener(type, handler, false); - } else { - var key = type + handler.toString(); - try{ - element.detachEvent('on' + type, handler._d ? handler._d[key] : handler); - }catch(e){} - if (handler._d && handler._d[key]) { - var index = utils.indexOf(handler._d.els,element); - if(index!=-1){ - handler._d.els.splice(index,1); - } - handler._d.els.length == 0 && delete handler._d[key]; - } - } - } - }, - - /** - * 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值 - * @method isSameElement - * @param { Node } nodeA 需要比较的节点 - * @param { Node } nodeB 需要比较的节点 - * @return { Boolean } 两个节点是否具有相同的标签名、属性名以及属性值 - * @example - * ```html - * ssss - * bbbbb - * ssss - * bbbbb - * - * - * ``` - */ - isSameElement:function (nodeA, nodeB) { - if (nodeA.tagName != nodeB.tagName) { - return false; - } - var thisAttrs = nodeA.attributes, - otherAttrs = nodeB.attributes; - if (!ie && thisAttrs.length != otherAttrs.length) { - return false; - } - var attrA, attrB, al = 0, bl = 0; - for (var i = 0; attrA = thisAttrs[i++];) { - if (attrA.nodeName == 'style') { - if (attrA.specified) { - al++; - } - if (domUtils.isSameStyle(nodeA, nodeB)) { - continue; - } else { - return false; - } - } - if (ie) { - if (attrA.specified) { - al++; - attrB = otherAttrs.getNamedItem(attrA.nodeName); - } else { - continue; - } - } else { - attrB = nodeB.attributes[attrA.nodeName]; - } - if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) { - return false; - } - } - // 有可能attrB的属性包含了attrA的属性之外还有自己的属性 - if (ie) { - for (i = 0; attrB = otherAttrs[i++];) { - if (attrB.specified) { - bl++; - } - } - if (al != bl) { - return false; - } - } - return true; - }, - - /** - * 判断节点nodeA与节点nodeB的元素的style属性是否一致 - * @method isSameStyle - * @param { Node } nodeA 需要比较的节点 - * @param { Node } nodeB 需要比较的节点 - * @return { Boolean } 两个节点是否具有相同的style属性值 - * @example - * ```html - * ssss - * bbbbb - * ssss - * bbbbb - * - * - * ``` - */ - isSameStyle:function (nodeA, nodeB) { - var styleA = nodeA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'), - styleB = nodeB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'); - if (browser.opera) { - styleA = nodeA.style; - styleB = nodeB.style; - if (styleA.length != styleB.length) - return false; - for (var p in styleA) { - if (/^(\d+|csstext)$/i.test(p)) { - continue; - } - if (styleA[p] != styleB[p]) { - return false; - } - } - return true; - } - if (!styleA || !styleB) { - return styleA == styleB; - } - styleA = styleA.split(';'); - styleB = styleB.split(';'); - if (styleA.length != styleB.length) { - return false; - } - for (var i = 0, ci; ci = styleA[i++];) { - if (utils.indexOf(styleB, ci) == -1) { - return false; - } - } - return true; - }, - /** - * 检查节点node是否为block元素 - * @method isBlockElm - * @param { Node } node 需要检测的节点对象 - * @return { Boolean } 是否是block元素节点 - * @warning 该方法的判断规则如下: 如果该元素原本是block元素, 则不论该元素当前的css样式是什么都会返回true; - * 否则,检测该元素的css样式, 如果该元素当前是block元素, 则返回true。 其余情况下都返回false。 - * @example - * ```html - * - * - *
                  - * - * - * ``` - */ - isBlockElm:function (node) { - return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName]; - }, - /** - * 检测node节点是否为body节点 - * @method isBody - * @param { Element } node 需要检测的dom元素 - * @return { Boolean } 给定的元素是否是body元素 - * @example - * ```javascript - * //output: true - * console.log( UE.dom.domUtils.isBody( document.body ) ); - * ``` - */ - isBody:function (node) { - return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body'; - }, - /** - * 以node节点为分界,将该节点的指定祖先节点parent拆分成两个独立的节点, - * 拆分形成的两个节点之间是node节点 - * @method breakParent - * @param { Node } node 作为分界的节点对象 - * @param { Node } parent 该节点必须是node节点的祖先节点, 且是block节点。 - * @return { Node } 给定的node分界节点 - * @example - * ```javascript - * - * var node = document.createElement("span"), - * wrapNode = document.createElement( "div" ), - * parent = document.createElement("p"); - * - * parent.appendChild( node ); - * wrapNode.appendChild( parent ); - * - * //拆分前 - * //output:

                  - * console.log( wrapNode.innerHTML ); - * - * - * UE.dom.domUtils.breakParent( node, parent ); - * //拆分后 - * //output:

                  - * console.log( wrapNode.innerHTML ); - * - * ``` - */ - breakParent:function (node, parent) { - var tmpNode, - parentClone = node, - clone = node, - leftNodes, - rightNodes; - do { - parentClone = parentClone.parentNode; - if (leftNodes) { - tmpNode = parentClone.cloneNode(false); - tmpNode.appendChild(leftNodes); - leftNodes = tmpNode; - tmpNode = parentClone.cloneNode(false); - tmpNode.appendChild(rightNodes); - rightNodes = tmpNode; - } else { - leftNodes = parentClone.cloneNode(false); - rightNodes = leftNodes.cloneNode(false); - } - while (tmpNode = clone.previousSibling) { - leftNodes.insertBefore(tmpNode, leftNodes.firstChild); - } - while (tmpNode = clone.nextSibling) { - rightNodes.appendChild(tmpNode); - } - clone = parentClone; - } while (parent !== parentClone); - tmpNode = parent.parentNode; - tmpNode.insertBefore(leftNodes, parent); - tmpNode.insertBefore(rightNodes, parent); - tmpNode.insertBefore(node, rightNodes); - domUtils.remove(parent); - return node; - }, - /** - * 检查节点node是否是空inline节点 - * @method isEmptyInlineElement - * @param { Node } node 需要检测的节点对象 - * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。 - * @example - * ```html - * => 1 - * => 1 - * => 1 - * xx => 0 - * ``` - */ - isEmptyInlineElement:function (node) { - if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) { - return 0; - } - node = node.firstChild; - while (node) { - //如果是创建的bookmark就跳过 - if (domUtils.isBookmarkNode(node)) { - return 0; - } - if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) || - node.nodeType == 3 && !domUtils.isWhitespace(node) - ) { - return 0; - } - node = node.nextSibling; - } - return 1; - - }, - - /** - * 删除node节点下首尾两端的空白文本子节点 - * @method trimWhiteTextNode - * @param { Element } node 需要执行删除操作的元素对象 - * @example - * ```javascript - * var node = document.createElement("div"); - * - * node.appendChild( document.createTextNode( "" ) ); - * - * node.appendChild( document.createElement("div") ); - * - * node.appendChild( document.createTextNode( "" ) ); - * - * //3 - * console.log( node.childNodes.length ); - * - * UE.dom.domUtils.trimWhiteTextNode( node ); - * - * //1 - * console.log( node.childNodes.length ); - * ``` - */ - trimWhiteTextNode:function (node) { - function remove(dir) { - var child; - while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) { - node.removeChild(child); - } - } - remove('firstChild'); - remove('lastChild'); - }, - - /** - * 合并node节点下相同的子节点 - * @name mergeChild - * @desc - * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签 - * @example - *

                  xxaaxx

                  - * ==> UE.dom.domUtils.mergeChild(node,'span') - *

                  xxaaxx

                  - */ - mergeChild:function (node, tagName, attrs) { - var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase()); - for (var i = 0, ci; ci = list[i++];) { - if (!ci.parentNode || domUtils.isBookmarkNode(ci)) { - continue; - } - //span单独处理 - if (ci.tagName.toLowerCase() == 'span') { - if (node === ci.parentNode) { - domUtils.trimWhiteTextNode(node); - if (node.childNodes.length == 1) { - node.style.cssText = ci.style.cssText + ";" + node.style.cssText; - domUtils.remove(ci, true); - continue; - } - } - ci.style.cssText = node.style.cssText + ';' + ci.style.cssText; - if (attrs) { - var style = attrs.style; - if (style) { - style = style.split(';'); - for (var j = 0, s; s = style[j++];) { - ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] = s.split(':')[1]; - } - } - } - if (domUtils.isSameStyle(ci, node)) { - domUtils.remove(ci, true); - } - continue; - } - if (domUtils.isSameElement(node, ci)) { - domUtils.remove(ci, true); - } - } - }, - - /** - * 原生方法getElementsByTagName的封装 - * @method getElementsByTagName - * @param { Node } node 目标节点对象 - * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割 - * @return { Array } 符合条件的节点集合 - */ - getElementsByTagName:function (node, name,filter) { - if(filter && utils.isString(filter)){ - var className = filter; - filter = function(node){return domUtils.hasClass(node,className)} - } - name = utils.trim(name).replace(/[ ]{2,}/g,' ').split(' '); - var arr = []; - for(var n = 0,ni;ni=name[n++];){ - var list = node.getElementsByTagName(ni); - for (var i = 0, ci; ci = list[i++];) { - if(!filter || filter(ci)) - arr.push(ci); - } - } - - return arr; - }, - /** - * 将节点node提取到父节点上 - * @method mergeToParent - * @param { Element } node 需要提取的元素对象 - * @example - * ```html - *
                  - *
                  - * - *
                  - *
                  - * - * - * ``` - */ - mergeToParent:function (node) { - var parent = node.parentNode; - while (parent && dtd.$removeEmpty[parent.tagName]) { - if (parent.tagName == node.tagName || parent.tagName == 'A') {//针对a标签单独处理 - domUtils.trimWhiteTextNode(parent); - //span需要特殊处理 不处理这样的情况 xxxxxxxxx - if (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node) - || (parent.tagName == 'A' && node.tagName == 'SPAN')) { - if (parent.childNodes.length > 1 || parent !== node.parentNode) { - node.style.cssText = parent.style.cssText + ";" + node.style.cssText; - parent = parent.parentNode; - continue; - } else { - parent.style.cssText += ";" + node.style.cssText; - //trace:952 a标签要保持下划线 - if (parent.tagName == 'A') { - parent.style.textDecoration = 'underline'; - } - } - } - if (parent.tagName != 'A') { - parent === node.parentNode && domUtils.remove(node, true); - break; - } - } - parent = parent.parentNode; - } - }, - /** - * 合并节点node的左右兄弟节点 - * @method mergeSibling - * @param { Element } node 需要合并的目标节点 - * @example - * ```html - * xxxxoooxxxx - * - * - * ``` - */ - - /** - * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。 - * @method mergeSibling - * @param { Element } node 需要合并的目标节点 - * @param { Boolean } ignorePre 是否忽略合并左节点 - * @example - * ```html - * xxxxoooxxxx - * - * - * ``` - */ - - /** - * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。 - * @method mergeSibling - * @param { Element } node 需要合并的目标节点 - * @param { Boolean } ignorePre 是否忽略合并左节点 - * @param { Boolean } ignoreNext 是否忽略合并右节点 - * @remind 如果同时忽略左右节点, 则该操作什么也不会做 - * @example - * ```html - * xxxxoooxxxx - * - * - * ``` - */ - mergeSibling:function (node, ignorePre, ignoreNext) { - function merge(rtl, start, node) { - var next; - if ((next = node[rtl]) && !domUtils.isBookmarkNode(next) && next.nodeType == 1 && domUtils.isSameElement(node, next)) { - while (next.firstChild) { - if (start == 'firstChild') { - node.insertBefore(next.lastChild, node.firstChild); - } else { - node.appendChild(next.firstChild); - } - } - domUtils.remove(next); - } - } - !ignorePre && merge('previousSibling', 'firstChild', node); - !ignoreNext && merge('nextSibling', 'lastChild', node); - }, - - /** - * 设置节点node及其子节点不会被选中 - * @method unSelectable - * @param { Element } node 需要执行操作的dom元素 - * @remind 执行该操作后的节点, 将不能被鼠标选中 - * @example - * ```javascript - * UE.dom.domUtils.unSelectable( document.body ); - * ``` - */ - unSelectable:ie && browser.ie9below || browser.opera ? function (node) { - //for ie9 - node.onselectstart = function () { - return false; - }; - node.onclick = node.onkeyup = node.onkeydown = function () { - return false; - }; - node.unselectable = 'on'; - node.setAttribute("unselectable", "on"); - for (var i = 0, ci; ci = node.all[i++];) { - switch (ci.tagName.toLowerCase()) { - case 'iframe' : - case 'textarea' : - case 'input' : - case 'select' : - break; - default : - ci.unselectable = 'on'; - node.setAttribute("unselectable", "on"); - } - } - } : function (node) { - node.style.MozUserSelect = - node.style.webkitUserSelect = - node.style.msUserSelect = - node.style.KhtmlUserSelect = 'none'; - }, - /** - * 删除节点node上的指定属性名称的属性 - * @method removeAttributes - * @param { Node } node 需要删除属性的节点对象 - * @param { String } attrNames 可以是空格隔开的多个属性名称,该操作将会依次删除相应的属性 - * @example - * ```html - *
                  - * xxxxx - *
                  - * - * - * ``` - */ - - /** - * 删除节点node上的指定属性名称的属性 - * @method removeAttributes - * @param { Node } node 需要删除属性的节点对象 - * @param { Array } attrNames 需要删除的属性名数组 - * @example - * ```html - *
                  - * xxxxx - *
                  - * - * - * ``` - */ - removeAttributes:function (node, attrNames) { - attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g,' ').split(' '); - for (var i = 0, ci; ci = attrNames[i++];) { - ci = attrFix[ci] || ci; - switch (ci) { - case 'className': - node[ci] = ''; - break; - case 'style': - node.style.cssText = ''; - var val = node.getAttributeNode('style'); - !browser.ie && val && node.removeAttributeNode(val); - } - node.removeAttribute(ci); - } - }, - /** - * 在doc下创建一个标签名为tag,属性为attrs的元素 - * @method createElement - * @param { DomDocument } doc 新创建的元素属于该document节点创建 - * @param { String } tagName 需要创建的元素的标签名 - * @param { Object } attrs 新创建的元素的属性key-value集合 - * @return { Element } 新创建的元素对象 - * @example - * ```javascript - * var ele = UE.dom.domUtils.createElement( document, 'div', { - * id: 'test' - * } ); - * - * //output: DIV - * console.log( ele.tagName ); - * - * //output: test - * console.log( ele.id ); - * - * ``` - */ - createElement:function (doc, tag, attrs) { - return domUtils.setAttributes(doc.createElement(tag), attrs) - }, - /** - * 为节点node添加属性attrs,attrs为属性键值对 - * @method setAttributes - * @param { Element } node 需要设置属性的元素对象 - * @param { Object } attrs 需要设置的属性名-值对 - * @return { Element } 设置属性的元素对象 - * @example - * ```html - * - * - * - * - */ - setAttributes:function (node, attrs) { - for (var attr in attrs) { - if(attrs.hasOwnProperty(attr)){ - var value = attrs[attr]; - switch (attr) { - case 'class': - //ie下要这样赋值,setAttribute不起作用 - node.className = value; - break; - case 'style' : - node.style.cssText = node.style.cssText + ";" + value; - break; - case 'innerHTML': - node[attr] = value; - break; - case 'value': - node.value = value; - break; - default: - node.setAttribute(attrFix[attr] || attr, value); - } - } - } - return node; - }, - - /** - * 获取元素element经过计算后的样式值 - * @method getComputedStyle - * @param { Element } element 需要获取样式的元素对象 - * @param { String } styleName 需要获取的样式名 - * @return { String } 获取到的样式值 - * @example - * ```html - * - * - * - * - * - * ``` - */ - getComputedStyle:function (element, styleName) { - //一下的属性单独处理 - var pros = 'width height top left'; - - if(pros.indexOf(styleName) > -1){ - return element['offset' + styleName.replace(/^\w/,function(s){return s.toUpperCase()})] + 'px'; - } - //忽略文本节点 - if (element.nodeType == 3) { - element = element.parentNode; - } - //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改. - if (browser.ie && browser.version < 9 && styleName == 'font-size' && !element.style.fontSize && - !dtd.$empty[element.tagName] && !dtd.$nonChild[element.tagName]) { - var span = element.ownerDocument.createElement('span'); - span.style.cssText = 'padding:0;border:0;font-family:simsun;'; - span.innerHTML = '.'; - element.appendChild(span); - var result = span.offsetHeight; - element.removeChild(span); - span = null; - return result + 'px'; - } - try { - var value = domUtils.getStyle(element, styleName) || - (window.getComputedStyle ? domUtils.getWindow(element).getComputedStyle(element, '').getPropertyValue(styleName) : - ( element.currentStyle || element.style )[utils.cssStyleToDomStyle(styleName)]); - - } catch (e) { - return ""; - } - return utils.transUnitToPx(utils.fixColor(styleName, value)); - }, - /** - * 删除元素element指定的className - * @method removeClasses - * @param { Element } ele 需要删除class的元素节点 - * @param { String } classNames 需要删除的className, 多个className之间以空格分开 - * @example - * ```html - * xxx - * - * - * ``` - */ - - /** - * 删除元素element指定的className - * @method removeClasses - * @param { Element } ele 需要删除class的元素节点 - * @param { Array } classNames 需要删除的className数组 - * @example - * ```html - * xxx - * - * - * ``` - */ - removeClasses:function (elm, classNames) { - classNames = utils.isArray(classNames) ? classNames : - utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' '); - for(var i = 0,ci,cls = elm.className;ci=classNames[i++];){ - cls = cls.replace(new RegExp('\\b' + ci + '\\b'),'') - } - cls = utils.trim(cls).replace(/[ ]{2,}/g,' '); - if(cls){ - elm.className = cls; - }else{ - domUtils.removeAttributes(elm,['class']); - } - }, - /** - * 给元素element添加className - * @method addClass - * @param { Node } ele 需要增加className的元素 - * @param { String } classNames 需要添加的className, 多个className之间以空格分割 - * @remind 相同的类名不会被重复添加 - * @example - * ```html - * - * - * - * ``` - */ - - /** - * 判断元素element是否包含给定的样式类名className - * @method hasClass - * @param { Node } ele 需要检测的元素 - * @param { Array } classNames 需要检测的className数组 - * @return { Boolean } 元素是否包含所有给定的className - * @example - * ```html - * - * - * - * ``` - */ - hasClass:function (element, className) { - if(utils.isRegExp(className)){ - return className.test(element.className) - } - className = utils.trim(className).replace(/[ ]{2,}/g,' ').split(' '); - for(var i = 0,ci,cls = element.className;ci=className[i++];){ - if(!new RegExp('\\b' + ci + '\\b','i').test(cls)){ - return false; - } - } - return i - 1 == className.length; - }, - - /** - * 阻止事件默认行为 - * @method preventDefault - * @param { Event } evt 需要阻止默认行为的事件对象 - * @example - * ```javascript - * UE.dom.domUtils.preventDefault( evt ); - * ``` - */ - preventDefault:function (evt) { - evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false); - }, - /** - * 删除元素element指定的样式 - * @method removeStyle - * @param { Element } element 需要删除样式的元素 - * @param { String } styleName 需要删除的样式名 - * @example - * ```html - * - * - * - * ``` - */ - removeStyle:function (element, name) { - if(browser.ie ){ - //针对color先单独处理一下 - if(name == 'color'){ - name = '(^|;)' + name; - } - element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'') - }else{ - if (element.style.removeProperty) { - element.style.removeProperty (name); - }else { - element.style.removeAttribute (utils.cssStyleToDomStyle(name)); - } - } - - - if (!element.style.cssText) { - domUtils.removeAttributes(element, ['style']); - } - }, - /** - * 获取元素element的style属性的指定值 - * @method getStyle - * @param { Element } element 需要获取属性值的元素 - * @param { String } styleName 需要获取的style的名称 - * @warning 该方法仅获取元素style属性中所标明的值 - * @return { String } 该元素包含指定的style属性值 - * @example - * ```html - *
                  - * - * - * ``` - */ - getStyle:function (element, name) { - var value = element.style[ utils.cssStyleToDomStyle(name) ]; - return utils.fixColor(name, value); - }, - /** - * 为元素element设置样式属性值 - * @method setStyle - * @param { Element } element 需要设置样式的元素 - * @param { String } styleName 样式名 - * @param { String } styleValue 样式值 - * @example - * ```html - *
                  - * - * - * ``` - */ - setStyle:function (element, name, value) { - element.style[utils.cssStyleToDomStyle(name)] = value; - if(!utils.trim(element.style.cssText)){ - this.removeAttributes(element,'style') - } - }, - /** - * 为元素element设置多个样式属性值 - * @method setStyles - * @param { Element } element 需要设置样式的元素 - * @param { Object } styles 样式名值对 - * @example - * ```html - *
                  - * - * - * ``` - */ - setStyles:function (element, styles) { - for (var name in styles) { - if (styles.hasOwnProperty(name)) { - domUtils.setStyle(element, name, styles[name]); - } - } - }, - /** - * 删除_moz_dirty属性 - * @private - * @method removeDirtyAttr - */ - removeDirtyAttr:function (node) { - for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) { - ci.removeAttribute('_moz_dirty'); - } - node.removeAttribute('_moz_dirty'); - }, - /** - * 获取子节点的数量 - * @method getChildCount - * @param { Element } node 需要检测的元素 - * @return { Number } 给定的node元素的子节点数量 - * @example - * ```html - *
                  - * - *
                  - * - * - * ``` - */ - - /** - * 根据给定的过滤规则, 获取符合条件的子节点的数量 - * @method getChildCount - * @param { Element } node 需要检测的元素 - * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false - * @return { Number } 符合过滤条件的node元素的子节点数量 - * @example - * ```html - *
                  - * - *
                  - * - * - * ``` - */ - getChildCount:function (node, fn) { - var count = 0, first = node.firstChild; - fn = fn || function () { - return 1; - }; - while (first) { - if (fn(first)) { - count++; - } - first = first.nextSibling; - } - return count; - }, - - /** - * 判断给定节点是否为空节点 - * @method isEmptyNode - * @param { Node } node 需要检测的节点对象 - * @return { Boolean } 节点是否为空 - * @example - * ```javascript - * UE.dom.domUtils.isEmptyNode( document.body ); - * ``` - */ - isEmptyNode:function (node) { - return !node.firstChild || domUtils.getChildCount(node, function (node) { - return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node) - }) == 0 - }, - clearSelectedArr:function (nodes) { - var node; - while (node = nodes.pop()) { - domUtils.removeAttributes(node, ['class']); - } - }, - /** - * 将显示区域滚动到指定节点的位置 - * @method scrollToView - * @param {Node} node 节点 - * @param {window} win window对象 - * @param {Number} offsetTop 距离上方的偏移量 - */ - scrollToView:function (node, win, offsetTop) { - var getViewPaneSize = function () { - var doc = win.document, - mode = doc.compatMode == 'CSS1Compat'; - return { - width:( mode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0, - height:( mode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0 - }; - }, - getScrollPosition = function (win) { - if ('pageXOffset' in win) { - return { - x:win.pageXOffset || 0, - y:win.pageYOffset || 0 - }; - } - else { - var doc = win.document; - return { - x:doc.documentElement.scrollLeft || doc.body.scrollLeft || 0, - y:doc.documentElement.scrollTop || doc.body.scrollTop || 0 - }; - } - }; - var winHeight = getViewPaneSize().height, offset = winHeight * -1 + offsetTop; - offset += (node.offsetHeight || 0); - var elementPosition = domUtils.getXY(node); - offset += elementPosition.y; - var currentScroll = getScrollPosition(win).y; - // offset += 50; - if (offset > currentScroll || offset < currentScroll - winHeight) { - win.scrollTo(0, offset + (offset < 0 ? -20 : 20)); - } - }, - /** - * 判断给定节点是否为br - * @method isBr - * @param { Node } node 需要判断的节点对象 - * @return { Boolean } 给定的节点是否是br节点 - */ - isBr:function (node) { - return node.nodeType == 1 && node.tagName == 'BR'; - }, - /** - * 判断给定的节点是否是一个“填充”节点 - * @private - * @method isFillChar - * @param { Node } node 需要判断的节点 - * @param { Boolean } isInStart 是否从节点内容的开始位置匹配 - * @returns { Boolean } 节点是否是填充节点 - */ - isFillChar:function (node,isInStart) { - if(node.nodeType != 3) - return false; - var text = node.nodeValue; - if(isInStart){ - return new RegExp('^' + domUtils.fillChar).test(text) - } - return !text.replace(new RegExp(domUtils.fillChar,'g'), '').length - }, - isStartInblock:function (range) { - var tmpRange = range.cloneRange(), - flag = 0, - start = tmpRange.startContainer, - tmp; - if(start.nodeType == 1 && start.childNodes[tmpRange.startOffset]){ - start = start.childNodes[tmpRange.startOffset]; - var pre = start.previousSibling; - while(pre && domUtils.isFillChar(pre)){ - start = pre; - pre = pre.previousSibling; - } - } - if(this.isFillChar(start,true) && tmpRange.startOffset == 1){ - tmpRange.setStartBefore(start); - start = tmpRange.startContainer; - } - - while (start && domUtils.isFillChar(start)) { - tmp = start; - start = start.previousSibling - } - if (tmp) { - tmpRange.setStartBefore(tmp); - start = tmpRange.startContainer; - } - if (start.nodeType == 1 && domUtils.isEmptyNode(start) && tmpRange.startOffset == 1) { - tmpRange.setStart(start, 0).collapse(true); - } - while (!tmpRange.startOffset) { - start = tmpRange.startContainer; - if (domUtils.isBlockElm(start) || domUtils.isBody(start)) { - flag = 1; - break; - } - var pre = tmpRange.startContainer.previousSibling, - tmpNode; - if (!pre) { - tmpRange.setStartBefore(tmpRange.startContainer); - } else { - while (pre && domUtils.isFillChar(pre)) { - tmpNode = pre; - pre = pre.previousSibling; - } - if (tmpNode) { - tmpRange.setStartBefore(tmpNode); - } else { - tmpRange.setStartBefore(tmpRange.startContainer); - } - } - } - return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0; - }, - - /** - * 判断给定的元素是否是一个空元素 - * @method isEmptyBlock - * @param { Element } node 需要判断的元素 - * @return { Boolean } 是否是空元素 - * @example - * ```html - *
                  - * - * - * ``` - */ - - /** - * 根据指定的判断规则判断给定的元素是否是一个空元素 - * @method isEmptyBlock - * @param { Element } node 需要判断的元素 - * @param { RegExp } reg 对内容执行判断的正则表达式对象 - * @return { Boolean } 是否是空元素 - */ - isEmptyBlock:function (node,reg) { - if(node.nodeType != 1) - return 0; - reg = reg || new RegExp('[ \xa0\t\r\n' + domUtils.fillChar + ']', 'g'); - - if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) { - return 0; - } - for (var n in dtd.$isNotEmpty) { - if (node.getElementsByTagName(n).length) { - return 0; - } - } - return 1; - }, - - /** - * 移动元素使得该元素的位置移动指定的偏移量的距离 - * @method setViewportOffset - * @param { Element } element 需要设置偏移量的元素 - * @param { Object } offset 偏移量, 形如{ left: 100, top: 50 }的一个键值对, 表示该元素将在 - * 现有的位置上向水平方向偏移offset.left的距离, 在竖直方向上偏移 - * offset.top的距离 - * @example - * ```html - *
                  - * - * - * ``` - */ - setViewportOffset:function (element, offset) { - var left = parseInt(element.style.left) | 0; - var top = parseInt(element.style.top) | 0; - var rect = element.getBoundingClientRect(); - var offsetLeft = offset.left - rect.left; - var offsetTop = offset.top - rect.top; - if (offsetLeft) { - element.style.left = left + offsetLeft + 'px'; - } - if (offsetTop) { - element.style.top = top + offsetTop + 'px'; - } - }, - - /** - * 用“填充字符”填充节点 - * @method fillNode - * @private - * @param { DomDocument } doc 填充的节点所在的docment对象 - * @param { Node } node 需要填充的节点对象 - * @example - * ```html - *
                  - * - * - * ``` - */ - fillNode:function (doc, node) { - var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br'); - node.innerHTML = ''; - node.appendChild(tmpNode); - }, - - /** - * 把节点src的所有子节点追加到另一个节点tag上去 - * @method moveChild - * @param { Node } src 源节点, 该节点下的所有子节点将被移除 - * @param { Node } tag 目标节点, 从源节点移除的子节点将被追加到该节点下 - * @example - * ```html - *
                  - * - *
                  - *
                  - *
                  - *
                  - * - * - * ``` - */ - - /** - * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部” - * @method moveChild - * @param { Node } src 源节点, 该节点下的所有子节点将被移除 - * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下 - * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾 - * @example - * ```html - *
                  - * - *
                  - *
                  - *
                  - *
                  - * - * - * ``` - */ - moveChild:function (src, tag, dir) { - while (src.firstChild) { - if (dir && tag.firstChild) { - tag.insertBefore(src.lastChild, tag.firstChild); - } else { - tag.appendChild(src.firstChild); - } - } - }, - - /** - * 判断节点的标签上是否不存在任何属性 - * @method hasNoAttributes - * @private - * @param { Node } node 需要检测的节点对象 - * @return { Boolean } 节点是否不包含任何属性 - * @example - * ```html - *
                  xxxx
                  - * - * - * ``` - */ - hasNoAttributes:function (node) { - return browser.ie ? /^<\w+\s*?>/.test(node.outerHTML) : node.attributes.length == 0; - }, - - /** - * 检测节点是否是UEditor所使用的辅助节点 - * @method isCustomeNode - * @private - * @param { Node } node 需要检测的节点 - * @remind 辅助节点是指编辑器要完成工作临时添加的节点, 在输出的时候将会从编辑器内移除, 不会影响最终的结果。 - * @return { Boolean } 给定的节点是否是一个辅助节点 - */ - isCustomeNode:function (node) { - return node.nodeType == 1 && node.getAttribute('_ue_custom_node_'); - }, - - /** - * 检测节点的标签是否是给定的标签 - * @method isTagNode - * @param { Node } node 需要检测的节点对象 - * @param { String } tagName 标签 - * @return { Boolean } 节点的标签是否是给定的标签 - * @example - * ```html - *
                  - * - * - * ``` - */ - isTagNode:function (node, tagNames) { - return node.nodeType == 1 && new RegExp('\\b' + node.tagName + '\\b','i').test(tagNames) - }, - - /** - * 给定一个节点数组,在通过指定的过滤器过滤后, 获取其中满足过滤条件的第一个节点 - * @method filterNodeList - * @param { Array } nodeList 需要过滤的节点数组 - * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false - * @return { Node | NULL } 如果找到符合过滤条件的节点, 则返回该节点, 否则返回NULL - * @example - * ```javascript - * var divNodes = document.getElementsByTagName("div"); - * divNodes = [].slice.call( divNodes, 0 ); - * - * //output: null - * console.log( UE.dom.domUtils.filterNodeList( divNodes, function ( node ) { - * return node.tagName.toLowerCase() !== 'div'; - * } ) ); - * ``` - */ - - /** - * 给定一个节点数组nodeList和一组标签名tagNames, 获取其中能够匹配标签名的节点集合中的第一个节点 - * @method filterNodeList - * @param { Array } nodeList 需要过滤的节点数组 - * @param { String } tagNames 需要匹配的标签名, 多个标签名之间用空格分割 - * @return { Node | NULL } 如果找到标签名匹配的节点, 则返回该节点, 否则返回NULL - * @example - * ```javascript - * var divNodes = document.getElementsByTagName("div"); - * divNodes = [].slice.call( divNodes, 0 ); - * - * //output: null - * console.log( UE.dom.domUtils.filterNodeList( divNodes, 'a span' ) ); - * ``` - */ - - /** - * 给定一个节点数组,在通过指定的过滤器过滤后, 如果参数forAll为true, 则会返回所有满足过滤 - * 条件的节点集合, 否则, 返回满足条件的节点集合中的第一个节点 - * @method filterNodeList - * @param { Array } nodeList 需要过滤的节点数组 - * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false - * @param { Boolean } forAll 是否返回整个节点数组, 如果该参数为false, 则返回节点集合中的第一个节点 - * @return { Array | Node | NULL } 如果找到符合过滤条件的节点, 则根据参数forAll的值决定返回满足 - * 过滤条件的节点数组或第一个节点, 否则返回NULL - * @example - * ```javascript - * var divNodes = document.getElementsByTagName("div"); - * divNodes = [].slice.call( divNodes, 0 ); - * - * //output: 3(假定有3个div) - * console.log( divNodes.length ); - * - * var nodes = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) { - * return node.tagName.toLowerCase() === 'div'; - * }, true ); - * - * //output: 3 - * console.log( nodes.length ); - * - * var node = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) { - * return node.tagName.toLowerCase() === 'div'; - * }, false ); - * - * //output: div - * console.log( node.nodeName ); - * ``` - */ - filterNodeList : function(nodelist,filter,forAll){ - var results = []; - if(!utils .isFunction(filter)){ - var str = filter; - filter = function(n){ - return utils.indexOf(utils.isArray(str) ? str:str.split(' '), n.tagName.toLowerCase()) != -1 - }; - } - utils.each(nodelist,function(n){ - filter(n) && results.push(n) - }); - return results.length == 0 ? null : results.length == 1 || !forAll ? results[0] : results - }, - - /** - * 查询给定的range选区是否在给定的node节点内,且在该节点的最末尾 - * @method isInNodeEndBoundary - * @param { UE.dom.Range } rng 需要判断的range对象, 该对象的startContainer不能为NULL - * @param node 需要检测的节点对象 - * @return { Number } 如果给定的选取range对象是在node内部的最末端, 则返回1, 否则返回0 - */ - isInNodeEndBoundary : function (rng,node){ - var start = rng.startContainer; - if(start.nodeType == 3 && rng.startOffset != start.nodeValue.length){ - return 0; - } - if(start.nodeType == 1 && rng.startOffset != start.childNodes.length){ - return 0; - } - while(start !== node){ - if(start.nextSibling){ - return 0 - }; - start = start.parentNode; - } - return 1; - }, - isBoundaryNode : function (node,dir){ - var tmp; - while(!domUtils.isBody(node)){ - tmp = node; - node = node.parentNode; - if(tmp !== node[dir]){ - return false; - } - } - return true; - }, - fillHtml : browser.ie11below ? ' ' : '
                  ' -}; -var fillCharReg = new RegExp(domUtils.fillChar, 'g'); - -// core/Range.js -/** - * Range封装 - * @file - * @module UE.dom - * @class Range - * @since 1.2.6.1 - */ - -/** - * dom操作封装 - * @unfile - * @module UE.dom - */ - -/** - * Range实现类,本类是UEditor底层核心类,封装不同浏览器之间的Range操作。 - * @unfile - * @module UE.dom - * @class Range - */ - - -(function () { - var guid = 0, - fillChar = domUtils.fillChar, - fillData; - - /** - * 更新range的collapse状态 - * @param {Range} range range对象 - */ - function updateCollapse(range) { - range.collapsed = - range.startContainer && range.endContainer && - range.startContainer === range.endContainer && - range.startOffset == range.endOffset; - } - - function selectOneNode(rng){ - return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1 - } - function setEndPoint(toStart, node, offset, range) { - //如果node是自闭合标签要处理 - if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) { - offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1); - node = node.parentNode; - } - if (toStart) { - range.startContainer = node; - range.startOffset = offset; - if (!range.endContainer) { - range.collapse(true); - } - } else { - range.endContainer = node; - range.endOffset = offset; - if (!range.startContainer) { - range.collapse(false); - } - } - updateCollapse(range); - return range; - } - - function execContentsAction(range, action) { - //调整边界 - //range.includeBookmark(); - var start = range.startContainer, - end = range.endContainer, - startOffset = range.startOffset, - endOffset = range.endOffset, - doc = range.document, - frag = doc.createDocumentFragment(), - tmpStart, tmpEnd; - if (start.nodeType == 1) { - start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode(''))); - } - if (end.nodeType == 1) { - end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode(''))); - } - if (start === end && start.nodeType == 3) { - frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset))); - //is not clone - if (action) { - start.deleteData(startOffset, endOffset - startOffset); - range.collapse(true); - } - return frag; - } - var current, currentLevel, clone = frag, - startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true); - for (var i = 0; startParents[i] == endParents[i];) { - i++; - } - for (var j = i, si; si = startParents[j]; j++) { - current = si.nextSibling; - if (si == start) { - if (!tmpStart) { - if (range.startContainer.nodeType == 3) { - clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset))); - //is not clone - if (action) { - start.deleteData(startOffset, start.nodeValue.length - startOffset); - } - } else { - clone.appendChild(!action ? start.cloneNode(true) : start); - } - } - } else { - currentLevel = si.cloneNode(false); - clone.appendChild(currentLevel); - } - while (current) { - if (current === end || current === endParents[j]) { - break; - } - si = current.nextSibling; - clone.appendChild(!action ? current.cloneNode(true) : current); - current = si; - } - clone = currentLevel; - } - clone = frag; - if (!startParents[i]) { - clone.appendChild(startParents[i - 1].cloneNode(false)); - clone = clone.firstChild; - } - for (var j = i, ei; ei = endParents[j]; j++) { - current = ei.previousSibling; - if (ei == end) { - if (!tmpEnd && range.endContainer.nodeType == 3) { - clone.appendChild(doc.createTextNode(end.substringData(0, endOffset))); - //is not clone - if (action) { - end.deleteData(0, endOffset); - } - } - } else { - currentLevel = ei.cloneNode(false); - clone.appendChild(currentLevel); - } - //如果两端同级,右边第一次已经被开始做了 - if (j != i || !startParents[i]) { - while (current) { - if (current === start) { - break; - } - ei = current.previousSibling; - clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild); - current = ei; - } - } - clone = currentLevel; - } - if (action) { - range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true); - } - tmpStart && domUtils.remove(tmpStart); - tmpEnd && domUtils.remove(tmpEnd); - return frag; - } - - /** - * 创建一个跟document绑定的空的Range实例 - * @constructor - * @param { Document } document 新建的选区所属的文档对象 - */ - - /** - * @property { Node } startContainer 当前Range的开始边界的容器节点, 可以是一个元素节点或者是文本节点 - */ - - /** - * @property { Node } startOffset 当前Range的开始边界容器节点的偏移量, 如果是元素节点, - * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符 - */ - - /** - * @property { Node } endContainer 当前Range的结束边界的容器节点, 可以是一个元素节点或者是文本节点 - */ - - /** - * @property { Node } endOffset 当前Range的结束边界容器节点的偏移量, 如果是元素节点, - * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符 - */ - - /** - * @property { Boolean } collapsed 当前Range是否闭合 - * @default true - * @remind Range是闭合的时候, startContainer === endContainer && startOffset === endOffset - */ - - /** - * @property { Document } document 当前Range所属的Document对象 - * @remind 不同range的的document属性可以是不同的 - */ - var Range = dom.Range = function (document) { - var me = this; - me.startContainer = - me.startOffset = - me.endContainer = - me.endOffset = null; - me.document = document; - me.collapsed = true; - }; - - /** - * 删除fillData - * @param doc - * @param excludeNode - */ - function removeFillData(doc, excludeNode) { - try { - if (fillData && domUtils.inDoc(fillData, doc)) { - if (!fillData.nodeValue.replace(fillCharReg, '').length) { - var tmpNode = fillData.parentNode; - domUtils.remove(fillData); - while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) && - //safari的contains有bug - (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode)) - ) { - fillData = tmpNode.parentNode; - domUtils.remove(tmpNode); - tmpNode = fillData; - } - } else { - fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, ''); - } - } - } catch (e) { - } - } - - /** - * @param node - * @param dir - */ - function mergeSibling(node, dir) { - var tmpNode; - node = node[dir]; - while (node && domUtils.isFillChar(node)) { - tmpNode = node[dir]; - domUtils.remove(node); - node = tmpNode; - } - } - - Range.prototype = { - - /** - * 克隆选区的内容到一个DocumentFragment里 - * @method cloneContents - * @return { DocumentFragment | NULL } 如果选区是闭合的将返回null, 否则, 返回包含所clone内容的DocumentFragment元素 - * @example - * ```html - * - * - * xx[xxx]x - * - * - * - * ``` - */ - cloneContents:function () { - return this.collapsed ? null : execContentsAction(this, 0); - }, - - /** - * 删除当前选区范围中的所有内容 - * @method deleteContents - * @remind 执行完该操作后, 当前Range对象变成了闭合状态 - * @return { UE.dom.Range } 当前操作的Range对象 - * @example - * ```html - * - * - * xx[xxx]x - * - * - * - * ``` - */ - deleteContents:function () { - var txt; - if (!this.collapsed) { - execContentsAction(this, 1); - } - if (browser.webkit) { - txt = this.startContainer; - if (txt.nodeType == 3 && !txt.nodeValue.length) { - this.setStartBefore(txt).collapse(true); - domUtils.remove(txt); - } - } - return this; - }, - - /** - * 将当前选区的内容提取到一个DocumentFragment里 - * @method extractContents - * @remind 执行该操作后, 选区将变成闭合状态 - * @warning 执行该操作后, 原来选区所选中的内容将从dom树上剥离出来 - * @return { DocumentFragment } 返回包含所提取内容的DocumentFragment对象 - * @example - * ```html - * - * - * xx[xxx]x - * - * - * - */ - extractContents:function () { - return this.collapsed ? null : execContentsAction(this, 2); - }, - - /** - * 设置Range的开始容器节点和偏移量 - * @method setStart - * @remind 如果给定的节点是元素节点,那么offset指的是其子元素中索引为offset的元素, - * 如果是文本节点,那么offset指的是其文本内容的第offset个字符 - * @remind 如果提供的容器节点是一个不能包含子元素的节点, 则该选区的开始容器将被设置 - * 为该节点的父节点, 此时, 其距离开始容器的偏移量也变成了该节点在其父节点 - * 中的索引 - * @param { Node } node 将被设为当前选区开始边界容器的节点对象 - * @param { int } offset 选区的开始位置偏移量 - * @return { UE.dom.Range } 当前range对象 - * @example - * ```html - * - * xxxxxxxxxxxxx[xxx] - * - * - * ``` - * @example - * ```html - * - * xxx[xx]x - * - * - * ``` - */ - setStart:function (node, offset) { - return setEndPoint(true, node, offset, this); - }, - - /** - * 设置Range的结束容器和偏移量 - * @method setEnd - * @param { Node } node 作为当前选区结束边界容器的节点对象 - * @param { int } offset 结束边界的偏移量 - * @see UE.dom.Range:setStart(Node,int) - * @return { UE.dom.Range } 当前range对象 - */ - setEnd:function (node, offset) { - return setEndPoint(false, node, offset, this); - }, - - /** - * 将Range开始位置设置到node节点之后 - * @method setStartAfter - * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引+1 - * @param { Node } node 选区的开始边界将紧接着该节点之后 - * @return { UE.dom.Range } 当前range对象 - * @example - * ```html - * - * xxxxxxx[xxxx] - * - * - * ``` - */ - setStartAfter:function (node) { - return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1); - }, - - /** - * 将Range开始位置设置到node节点之前 - * @method setStartBefore - * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引 - * @param { Node } node 新的选区开始位置在该节点之前 - * @see UE.dom.Range:setStartAfter(Node) - * @return { UE.dom.Range } 当前range对象 - */ - setStartBefore:function (node) { - return this.setStart(node.parentNode, domUtils.getNodeIndex(node)); - }, - - /** - * 将Range结束位置设置到node节点之后 - * @method setEndAfter - * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引+1 - * @param { Node } node 目标节点 - * @see UE.dom.Range:setStartAfter(Node) - * @return { UE.dom.Range } 当前range对象 - * @example - * ```html - * - * [xxxxxxx]xxxx - * - * - * ``` - */ - setEndAfter:function (node) { - return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1); - }, - - /** - * 将Range结束位置设置到node节点之前 - * @method setEndBefore - * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引 - * @param { Node } node 目标节点 - * @see UE.dom.Range:setEndAfter(Node) - * @return { UE.dom.Range } 当前range对象 - */ - setEndBefore:function (node) { - return this.setEnd(node.parentNode, domUtils.getNodeIndex(node)); - }, - - /** - * 设置Range的开始位置到node节点内的第一个子节点之前 - * @method setStartAtFirst - * @remind 选区的开始容器将变成给定的节点, 且偏移量为0 - * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。 - * @param { Node } node 目标节点 - * @see UE.dom.Range:setStartBefore(Node) - * @return { UE.dom.Range } 当前range对象 - * @example - * ```html - * - * xxxxx[xx]xxxx - * - * - * ``` - */ - setStartAtFirst:function (node) { - return this.setStart(node, 0); - }, - - /** - * 设置Range的开始位置到node节点内的最后一个节点之后 - * @method setStartAtLast - * @remind 选区的开始容器将变成给定的节点, 且偏移量为该节点的子节点数 - * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。 - * @param { Node } node 目标节点 - * @see UE.dom.Range:setStartAtFirst(Node) - * @return { UE.dom.Range } 当前range对象 - */ - setStartAtLast:function (node) { - return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length); - }, - - /** - * 设置Range的结束位置到node节点内的第一个节点之前 - * @method setEndAtFirst - * @param { Node } node 目标节点 - * @remind 选区的结束容器将变成给定的节点, 且偏移量为0 - * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。 - * @see UE.dom.Range:setStartAtFirst(Node) - * @return { UE.dom.Range } 当前range对象 - */ - setEndAtFirst:function (node) { - return this.setEnd(node, 0); - }, - - /** - * 设置Range的结束位置到node节点内的最后一个节点之后 - * @method setEndAtLast - * @param { Node } node 目标节点 - * @remind 选区的结束容器将变成给定的节点, 且偏移量为该节点的子节点数量 - * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。 - * @see UE.dom.Range:setStartAtFirst(Node) - * @return { UE.dom.Range } 当前range对象 - */ - setEndAtLast:function (node) { - return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length); - }, - - /** - * 选中给定节点 - * @method selectNode - * @remind 此时, 选区的开始容器和结束容器都是该节点的父节点, 其startOffset是该节点在父节点中的位置索引, - * 而endOffset为startOffset+1 - * @param { Node } node 需要选中的节点 - * @return { UE.dom.Range } 当前range对象,此时的range仅包含当前给定的节点对象 - * @example - * ```html - * - * xxxxx[xx]xxxx - * - * - * ``` - */ - selectNode:function (node) { - return this.setStartBefore(node).setEndAfter(node); - }, - - /** - * 选中给定节点内部的所有节点 - * @method selectNodeContents - * @remind 此时, 选区的开始容器和结束容器都是该节点, 其startOffset为0, - * 而endOffset是该节点的子节点数。 - * @param { Node } node 目标节点, 当前range将包含该节点内的所有节点 - * @return { UE.dom.Range } 当前range对象, 此时range仅包含给定节点的所有子节点 - * @example - * ```html - * - * xxxxx[xx]xxxx - * - * - * ``` - */ - selectNodeContents:function (node) { - return this.setStart(node, 0).setEndAtLast(node); - }, - - /** - * clone当前Range对象 - * @method cloneRange - * @remind 返回的range是一个全新的range对象, 其内部所有属性与当前被clone的range相同。 - * @return { UE.dom.Range } 当前range对象的一个副本 - */ - cloneRange:function () { - var me = this; - return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset); - - }, - - /** - * 向当前选区的结束处闭合选区 - * @method collapse - * @return { UE.dom.Range } 当前range对象 - * @example - * ```html - * - * xxxxx[xx]xxxx - * - * - * ``` - */ - - /** - * 闭合当前选区,根据给定的toStart参数项决定是向当前选区开始处闭合还是向结束处闭合, - * 如果toStart的值为true,则向开始位置闭合, 反之,向结束位置闭合。 - * @method collapse - * @param { Boolean } toStart 是否向选区开始处闭合 - * @return { UE.dom.Range } 当前range对象,此时range对象处于闭合状态 - * @see UE.dom.Range:collapse() - * @example - * ```html - * - * xxxxx[xx]xxxx - * - * - * ``` - */ - collapse:function (toStart) { - var me = this; - if (toStart) { - me.endContainer = me.startContainer; - me.endOffset = me.startOffset; - } else { - me.startContainer = me.endContainer; - me.startOffset = me.endOffset; - } - me.collapsed = true; - return me; - }, - - /** - * 调整range的开始位置和结束位置,使其"收缩"到最小的位置 - * @method shrinkBoundary - * @return { UE.dom.Range } 当前range对象 - * @example - * ```html - * xxxx[xxxxx] => xxxx[xxxxx] - * ``` - * - * @example - * ```html - * - * x[xx]xxx - * - * - * ``` - * - * @example - * ```html - * [xxxxxxxxxxx] => [xxxxxxxxxxx] - * ``` - */ - - /** - * 调整range的开始位置和结束位置,使其"收缩"到最小的位置, - * 如果ignoreEnd的值为true,则忽略对结束位置的调整 - * @method shrinkBoundary - * @param { Boolean } ignoreEnd 是否忽略对结束位置的调整 - * @return { UE.dom.Range } 当前range对象 - * @see UE.dom.domUtils.Range:shrinkBoundary() - */ - shrinkBoundary:function (ignoreEnd) { - var me = this, child, - collapsed = me.collapsed; - function check(node){ - return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName] - } - while (me.startContainer.nodeType == 1 //是element - && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element - && check(child)) { - me.setStart(child, 0); - } - if (collapsed) { - return me.collapse(true); - } - if (!ignoreEnd) { - while (me.endContainer.nodeType == 1//是element - && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错 - && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element - && check(child)) { - me.setEnd(child, child.childNodes.length); - } - } - return me; - }, - - /** - * 获取离当前选区内包含的所有节点最近的公共祖先节点, - * @method getCommonAncestor - * @remind 返回的公共祖先节点一定不是range自身的容器节点, 但有可能是一个文本节点 - * @return { Node } 当前range对象内所有节点的公共祖先节点 - * @example - * ```html - * //选区示例 - * xxxx[xxx]xxxxxx - * - * ``` - */ - - /** - * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到 - * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf - * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点 - * @method getCommonAncestor - * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点 - * @return { Node } 当前range对象内所有节点的公共祖先节点 - * @see UE.dom.Range:getCommonAncestor() - * @example - * ```html - * - * - * - * xxxxxxxxx[xxx]xxxxxxxx - * - * - * - * - * ``` - */ - - /** - * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到 - * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf - * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点; 同时可以根据 - * ignoreTextNode 参数的取值决定是否忽略类型为文本节点的祖先节点。 - * @method getCommonAncestor - * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点 - * @param { Boolean } ignoreTextNode 获取祖先节点的过程中是否忽略类型为文本节点的祖先节点 - * @return { Node } 当前range对象内所有节点的公共祖先节点 - * @see UE.dom.Range:getCommonAncestor() - * @see UE.dom.Range:getCommonAncestor(Boolean) - * @example - * ```html - * - * - * - * xxxxxxxx[x]xxxxxxxxxxx - * - * - * - * - * ``` - */ - getCommonAncestor:function (includeSelf, ignoreTextNode) { - var me = this, - start = me.startContainer, - end = me.endContainer; - if (start === end) { - if (includeSelf && selectOneNode(this)) { - start = start.childNodes[me.startOffset]; - if(start.nodeType == 1) - return start; - } - //只有在上来就相等的情况下才会出现是文本的情况 - return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start; - } - return domUtils.getCommonAncestor(start, end); - }, - - /** - * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上 - * @method trimBoundary - * @remind 该操作有可能会引起文本节点被切开 - * @return { UE.dom.Range } 当前range对象 - * @example - * ```html - * - * //选区示例 - * xxx[xxxxx]xxx - * - * - * ``` - */ - - /** - * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上, - * 可以根据 ignoreEnd 参数的值决定是否调整对结束边界的调整 - * @method trimBoundary - * @param { Boolean } ignoreEnd 是否忽略对结束边界的调整 - * @return { UE.dom.Range } 当前range对象 - * @example - * ```html - * - * //选区示例 - * xxx[xxxxx]xxx - * - * - * ``` - */ - trimBoundary:function (ignoreEnd) { - this.txtToElmBoundary(); - var start = this.startContainer, - offset = this.startOffset, - collapsed = this.collapsed, - end = this.endContainer; - if (start.nodeType == 3) { - if (offset == 0) { - this.setStartBefore(start); - } else { - if (offset >= start.nodeValue.length) { - this.setStartAfter(start); - } else { - var textNode = domUtils.split(start, offset); - //跟新结束边界 - if (start === end) { - this.setEnd(textNode, this.endOffset - offset); - } else if (start.parentNode === end) { - this.endOffset += 1; - } - this.setStartBefore(textNode); - } - } - if (collapsed) { - return this.collapse(true); - } - } - if (!ignoreEnd) { - offset = this.endOffset; - end = this.endContainer; - if (end.nodeType == 3) { - if (offset == 0) { - this.setEndBefore(end); - } else { - offset < end.nodeValue.length && domUtils.split(end, offset); - this.setEndAfter(end); - } - } - } - return this; - }, - - /** - * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则什么也不做 - * @method txtToElmBoundary - * @remind 该操作不会修改dom节点 - * @return { UE.dom.Range } 当前range对象 - */ - - /** - * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则根据参数项 - * ignoreCollapsed 的值决定是否执行该调整 - * @method txtToElmBoundary - * @param { Boolean } ignoreCollapsed 是否忽略选区的闭合状态, 如果该参数取值为true, 则 - * 不论选区是否闭合, 都会执行该操作, 反之, 则不会对闭合的选区执行该操作 - * @return { UE.dom.Range } 当前range对象 - */ - txtToElmBoundary:function (ignoreCollapsed) { - function adjust(r, c) { - var container = r[c + 'Container'], - offset = r[c + 'Offset']; - if (container.nodeType == 3) { - if (!offset) { - r['set' + c.replace(/(\w)/, function (a) { - return a.toUpperCase(); - }) + 'Before'](container); - } else if (offset >= container.nodeValue.length) { - r['set' + c.replace(/(\w)/, function (a) { - return a.toUpperCase(); - }) + 'After' ](container); - } - } - } - - if (ignoreCollapsed || !this.collapsed) { - adjust(this, 'start'); - adjust(this, 'end'); - } - return this; - }, - - /** - * 在当前选区的开始位置前插入节点,新插入的节点会被该range包含 - * @method insertNode - * @param { Node } node 需要插入的节点 - * @remind 插入的节点可以是一个DocumentFragment依次插入多个节点 - * @return { UE.dom.Range } 当前range对象 - */ - insertNode:function (node) { - var first = node, length = 1; - if (node.nodeType == 11) { - first = node.firstChild; - length = node.childNodes.length; - } - this.trimBoundary(true); - var start = this.startContainer, - offset = this.startOffset; - var nextNode = start.childNodes[ offset ]; - if (nextNode) { - start.insertBefore(node, nextNode); - } else { - start.appendChild(node); - } - if (first.parentNode === this.endContainer) { - this.endOffset = this.endOffset + length; - } - return this.setStartBefore(first); - }, - - /** - * 闭合选区到当前选区的开始位置, 并且定位光标到闭合后的位置 - * @method setCursor - * @return { UE.dom.Range } 当前range对象 - * @see UE.dom.Range:collapse() - */ - - /** - * 闭合选区,可以根据参数toEnd的值控制选区是向前闭合还是向后闭合, 并且定位光标到闭合后的位置。 - * @method setCursor - * @param { Boolean } toEnd 是否向后闭合, 如果为true, 则闭合选区时, 将向结束容器方向闭合, - * 反之,则向开始容器方向闭合 - * @return { UE.dom.Range } 当前range对象 - * @see UE.dom.Range:collapse(Boolean) - */ - setCursor:function (toEnd, noFillData) { - return this.collapse(!toEnd).select(noFillData); - }, - - /** - * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置 - * @method createBookmark - * @param { Boolean } serialize 控制返回的标记位置是对当前位置的引用还是ID,如果该值为true,则 - * 返回标记位置的ID, 反之则返回标记位置节点的引用 - * @return { Object } 返回一个书签记录键值对, 其包含的key有: start => 开始标记的ID或者引用, - * end => 结束标记的ID或引用, id => 当前标记的类型, 如果为true,则表示 - * 返回的记录的类型为ID, 反之则为引用 - */ - createBookmark:function (serialize, same) { - var endNode, - startNode = this.document.createElement('span'); - startNode.style.cssText = 'display:none;line-height:0px;'; - startNode.appendChild(this.document.createTextNode('\u200D')); - startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++); - - if (!this.collapsed) { - endNode = startNode.cloneNode(true); - endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++); - } - this.insertNode(startNode); - if (endNode) { - this.collapse().insertNode(endNode).setEndBefore(endNode); - } - this.setStartAfter(startNode); - return { - start:serialize ? startNode.id : startNode, - end:endNode ? serialize ? endNode.id : endNode : null, - id:serialize - } - }, - - /** - * 调整当前range的边界到书签位置,并删除该书签对象所标记的位置内的节点 - * @method moveToBookmark - * @param { BookMark } bookmark createBookmark所创建的标签对象 - * @return { UE.dom.Range } 当前range对象 - * @see UE.dom.Range:createBookmark(Boolean) - */ - moveToBookmark:function (bookmark) { - var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start, - end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end; - this.setStartBefore(start); - domUtils.remove(start); - if (end) { - this.setEndBefore(end); - domUtils.remove(end); - } else { - this.collapse(true); - } - return this; - }, - - /** - * 调整range的边界,使其"放大"到最近的父节点 - * @method enlarge - * @remind 会引起选区的变化 - * @return { UE.dom.Range } 当前range对象 - */ - - /** - * 调整range的边界,使其"放大"到最近的父节点,根据参数 toBlock 的取值, 可以 - * 要求扩大之后的父节点是block节点 - * @method enlarge - * @param { Boolean } toBlock 是否要求扩大之后的父节点必须是block节点 - * @return { UE.dom.Range } 当前range对象 - */ - enlarge:function (toBlock, stopFn) { - var isBody = domUtils.isBody, - pre, node, tmp = this.document.createTextNode(''); - if (toBlock) { - node = this.startContainer; - if (node.nodeType == 1) { - if (node.childNodes[this.startOffset]) { - pre = node = node.childNodes[this.startOffset] - } else { - node.appendChild(tmp); - pre = node = tmp; - } - } else { - pre = node; - } - while (1) { - if (domUtils.isBlockElm(node)) { - node = pre; - while ((pre = node.previousSibling) && !domUtils.isBlockElm(pre)) { - node = pre; - } - this.setStartBefore(node); - break; - } - pre = node; - node = node.parentNode; - } - node = this.endContainer; - if (node.nodeType == 1) { - if (pre = node.childNodes[this.endOffset]) { - node.insertBefore(tmp, pre); - } else { - node.appendChild(tmp); - } - pre = node = tmp; - } else { - pre = node; - } - while (1) { - if (domUtils.isBlockElm(node)) { - node = pre; - while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) { - node = pre; - } - this.setEndAfter(node); - break; - } - pre = node; - node = node.parentNode; - } - if (tmp.parentNode === this.endContainer) { - this.endOffset--; - } - domUtils.remove(tmp); - } - - // 扩展边界到最大 - if (!this.collapsed) { - while (this.startOffset == 0) { - if (stopFn && stopFn(this.startContainer)) { - break; - } - if (isBody(this.startContainer)) { - break; - } - this.setStartBefore(this.startContainer); - } - while (this.endOffset == (this.endContainer.nodeType == 1 ? this.endContainer.childNodes.length : this.endContainer.nodeValue.length)) { - if (stopFn && stopFn(this.endContainer)) { - break; - } - if (isBody(this.endContainer)) { - break; - } - this.setEndAfter(this.endContainer); - } - } - return this; - }, - enlargeToBlockElm:function(ignoreEnd){ - while(!domUtils.isBlockElm(this.startContainer)){ - this.setStartBefore(this.startContainer); - } - if(!ignoreEnd){ - while(!domUtils.isBlockElm(this.endContainer)){ - this.setEndAfter(this.endContainer); - } - } - return this; - }, - /** - * 调整Range的边界,使其"缩小"到最合适的位置 - * @method adjustmentBoundary - * @return { UE.dom.Range } 当前range对象 - * @see UE.dom.Range:shrinkBoundary() - */ - adjustmentBoundary:function () { - if (!this.collapsed) { - while (!domUtils.isBody(this.startContainer) && - this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length && - this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length - ) { - - this.setStartAfter(this.startContainer); - } - while (!domUtils.isBody(this.endContainer) && !this.endOffset && - this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length - ) { - this.setEndBefore(this.endContainer); - } - } - return this; - }, - - /** - * 给range选区中的内容添加给定的inline标签 - * @method applyInlineStyle - * @param { String } tagName 需要添加的标签名 - * @example - * ```html - *

                  xxxx[xxxx]x

                  ==> range.applyInlineStyle("strong") ==>

                  xxxx[xxxx]x

                  - * ``` - */ - - /** - * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。 - * @method applyInlineStyle - * @param { String } tagName 需要添加的标签名 - * @param { Object } attrs 跟随新添加的标签的属性 - * @return { UE.dom.Range } 当前选区 - * @example - * ```html - *

                  xxxx[xxxx]x

                  - * - * ==> - * - * - * range.applyInlineStyle("strong",{"style":"font-size:12px"}) - * - * ==> - * - *

                  xxxx[xxxx]x

                  - * ``` - */ - applyInlineStyle:function (tagName, attrs, list) { - if (this.collapsed)return this; - this.trimBoundary().enlarge(false, - function (node) { - return node.nodeType == 1 && domUtils.isBlockElm(node) - }).adjustmentBoundary(); - var bookmark = this.createBookmark(), - end = bookmark.end, - filterFn = function (node) { - return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node); - }, - current = domUtils.getNextDomNode(bookmark.start, false, filterFn), - node, - pre, - range = this.cloneRange(); - while (current && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) { - if (current.nodeType == 3 || dtd[tagName][current.tagName]) { - range.setStartBefore(current); - node = current; - while (node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end) { - pre = node; - node = domUtils.getNextDomNode(node, node.nodeType == 1, null, function (parent) { - return dtd[tagName][parent.tagName]; - }); - } - var frag = range.setEndAfter(pre).extractContents(), elm; - if (list && list.length > 0) { - var level, top; - top = level = list[0].cloneNode(false); - for (var i = 1, ci; ci = list[i++];) { - level.appendChild(ci.cloneNode(false)); - level = level.firstChild; - } - elm = level; - } else { - elm = range.document.createElement(tagName); - } - if (attrs) { - domUtils.setAttributes(elm, attrs); - } - elm.appendChild(frag); - range.insertNode(list ? top : elm); - //处理下滑线在a上的情况 - var aNode; - if (tagName == 'span' && attrs.style && /text\-decoration/.test(attrs.style) && (aNode = domUtils.findParentByTagName(elm, 'a', true))) { - domUtils.setAttributes(aNode, attrs); - domUtils.remove(elm, true); - elm = aNode; - } else { - domUtils.mergeSibling(elm); - domUtils.clearEmptySibling(elm); - } - //去除子节点相同的 - domUtils.mergeChild(elm, attrs); - current = domUtils.getNextDomNode(elm, false, filterFn); - domUtils.mergeToParent(elm); - if (node === end) { - break; - } - } else { - current = domUtils.getNextDomNode(current, true, filterFn); - } - } - return this.moveToBookmark(bookmark); - }, - - /** - * 移除当前选区内指定的inline标签,但保留其中的内容 - * @method removeInlineStyle - * @param { String } tagName 需要移除的标签名 - * @return { UE.dom.Range } 当前的range对象 - * @example - * ```html - * xx[xxxxyyyzz]z => range.removeInlineStyle(["em"]) => xx[xxxxyyyzz]z - * ``` - */ - - /** - * 移除当前选区内指定的一组inline标签,但保留其中的内容 - * @method removeInlineStyle - * @param { Array } tagNameArr 需要移除的标签名的数组 - * @return { UE.dom.Range } 当前的range对象 - * @see UE.dom.Range:removeInlineStyle(String) - */ - removeInlineStyle:function (tagNames) { - if (this.collapsed)return this; - tagNames = utils.isArray(tagNames) ? tagNames : [tagNames]; - this.shrinkBoundary().adjustmentBoundary(); - var start = this.startContainer, end = this.endContainer; - while (1) { - if (start.nodeType == 1) { - if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) { - break; - } - if (start.tagName.toLowerCase() == 'body') { - start = null; - break; - } - } - start = start.parentNode; - } - while (1) { - if (end.nodeType == 1) { - if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) { - break; - } - if (end.tagName.toLowerCase() == 'body') { - end = null; - break; - } - } - end = end.parentNode; - } - var bookmark = this.createBookmark(), - frag, - tmpRange; - if (start) { - tmpRange = this.cloneRange().setEndBefore(bookmark.start).setStartBefore(start); - frag = tmpRange.extractContents(); - tmpRange.insertNode(frag); - domUtils.clearEmptySibling(start, true); - start.parentNode.insertBefore(bookmark.start, start); - } - if (end) { - tmpRange = this.cloneRange().setStartAfter(bookmark.end).setEndAfter(end); - frag = tmpRange.extractContents(); - tmpRange.insertNode(frag); - domUtils.clearEmptySibling(end, false, true); - end.parentNode.insertBefore(bookmark.end, end.nextSibling); - } - var current = domUtils.getNextDomNode(bookmark.start, false, function (node) { - return node.nodeType == 1; - }), next; - while (current && current !== bookmark.end) { - next = domUtils.getNextDomNode(current, true, function (node) { - return node.nodeType == 1; - }); - if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) { - domUtils.remove(current, true); - } - current = next; - } - return this.moveToBookmark(bookmark); - }, - - /** - * 获取当前选中的自闭合的节点 - * @method getClosedNode - * @return { Node | NULL } 如果当前选中的是自闭合节点, 则返回该节点, 否则返回NULL - */ - getClosedNode:function () { - var node; - if (!this.collapsed) { - var range = this.cloneRange().adjustmentBoundary().shrinkBoundary(); - if (selectOneNode(range)) { - var child = range.startContainer.childNodes[range.startOffset]; - if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) { - node = child; - } - } - } - return node; - }, - - /** - * 在页面上高亮range所表示的选区 - * @method select - * @return { UE.dom.Range } 返回当前Range对象 - */ - //这里不区分ie9以上,trace:3824 - select:browser.ie ? function (noFillData, textRange) { - var nativeRange; - if (!this.collapsed) - this.shrinkBoundary(); - var node = this.getClosedNode(); - if (node && !textRange) { - try { - nativeRange = this.document.body.createControlRange(); - nativeRange.addElement(node); - nativeRange.select(); - } catch (e) {} - return this; - } - var bookmark = this.createBookmark(), - start = bookmark.start, - end; - nativeRange = this.document.body.createTextRange(); - nativeRange.moveToElementText(start); - nativeRange.moveStart('character', 1); - if (!this.collapsed) { - var nativeRangeEnd = this.document.body.createTextRange(); - end = bookmark.end; - nativeRangeEnd.moveToElementText(end); - nativeRange.setEndPoint('EndToEnd', nativeRangeEnd); - } else { - if (!noFillData && this.startContainer.nodeType != 3) { - //使用|x固定住光标 - var tmpText = this.document.createTextNode(fillChar), - tmp = this.document.createElement('span'); - tmp.appendChild(this.document.createTextNode(fillChar)); - start.parentNode.insertBefore(tmp, start); - start.parentNode.insertBefore(tmpText, start); - //当点b,i,u时,不能清除i上边的b - removeFillData(this.document, tmpText); - fillData = tmpText; - mergeSibling(tmp, 'previousSibling'); - mergeSibling(start, 'nextSibling'); - nativeRange.moveStart('character', -1); - nativeRange.collapse(true); - } - } - this.moveToBookmark(bookmark); - tmp && domUtils.remove(tmp); - //IE在隐藏状态下不支持range操作,catch一下 - try { - nativeRange.select(); - } catch (e) { - } - return this; - } : function (notInsertFillData) { - function checkOffset(rng){ - - function check(node,offset,dir){ - if(node.nodeType == 3 && node.nodeValue.length < offset){ - rng[dir + 'Offset'] = node.nodeValue.length - } - } - check(rng.startContainer,rng.startOffset,'start'); - check(rng.endContainer,rng.endOffset,'end'); - } - var win = domUtils.getWindow(this.document), - sel = win.getSelection(), - txtNode; - //FF下关闭自动长高时滚动条在关闭dialog时会跳 - //ff下如果不body.focus将不能定位闭合光标到编辑器内 - browser.gecko ? this.document.body.focus() : win.focus(); - if (sel) { - sel.removeAllRanges(); - // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断 - // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR' - if (this.collapsed && !notInsertFillData) { -// //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点 -// if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) { -// var tmp = this.document.createTextNode(''); -// this.insertNode(tmp).setStart(tmp, 0).collapse(true); -// } -// - //处理光标落在文本节点的情况 - //处理以下的情况 - //|xxxx - //xxxx|xxxx - //xxxx| - var start = this.startContainer,child = start; - if(start.nodeType == 1){ - child = start.childNodes[this.startOffset]; - - } - if( !(start.nodeType == 3 && this.startOffset) && - (child ? - (!child.previousSibling || child.previousSibling.nodeType != 3) - : - (!start.lastChild || start.lastChild.nodeType != 3) - ) - ){ - txtNode = this.document.createTextNode(fillChar); - //跟着前边走 - this.insertNode(txtNode); - removeFillData(this.document, txtNode); - mergeSibling(txtNode, 'previousSibling'); - mergeSibling(txtNode, 'nextSibling'); - fillData = txtNode; - this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true); - } - } - var nativeRange = this.document.createRange(); - if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){ - var child = this.startContainer.childNodes[this.startOffset]; - if(!child){ - //往前靠拢 - child = this.startContainer.lastChild; - if( child && domUtils.isBr(child)){ - this.setStartBefore(child).collapse(true); - } - }else{ - //向后靠拢 - while(child && domUtils.isBlockElm(child)){ - if(child.nodeType == 1 && child.childNodes[0]){ - child = child.childNodes[0] - }else{ - break; - } - } - child && this.setStartBefore(child).collapse(true) - } - - } - //是createAddress最后一位算的不准,现在这里进行微调 - checkOffset(this); - nativeRange.setStart(this.startContainer, this.startOffset); - nativeRange.setEnd(this.endContainer, this.endOffset); - sel.addRange(nativeRange); - } - return this; - }, - - /** - * 滚动到当前range开始的位置 - * @method scrollToView - * @param { Window } win 当前range对象所属的window对象 - * @return { UE.dom.Range } 当前Range对象 - */ - - /** - * 滚动到距离当前range开始位置 offset 的位置处 - * @method scrollToView - * @param { Window } win 当前range对象所属的window对象 - * @param { Number } offset 距离range开始位置处的偏移量, 如果为正数, 则向下偏移, 反之, 则向上偏移 - * @return { UE.dom.Range } 当前Range对象 - */ - scrollToView:function (win, offset) { - win = win ? window : domUtils.getWindow(this.document); - var me = this, - span = me.document.createElement('span'); - //trace:717 - span.innerHTML = ' '; - me.cloneRange().insertNode(span); - domUtils.scrollToView(span, win, offset); - domUtils.remove(span); - return me; - }, - - /** - * 判断当前选区内容是否占位符 - * @private - * @method inFillChar - * @return { Boolean } 如果是占位符返回true,否则返回false - */ - inFillChar : function(){ - var start = this.startContainer; - if(this.collapsed && start.nodeType == 3 - && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length - ){ - return true; - } - return false; - }, - - /** - * 保存 - * @method createAddress - * @private - * @return { Boolean } 返回开始和结束的位置 - * @example - * ```html - * - *

                  - * aaaa - * - * - * bbbb - * - * - *

                  - * - * - * - * ``` - */ - createAddress : function(ignoreEnd,ignoreTxt){ - var addr = {},me = this; - - function getAddress(isStart){ - var node = isStart ? me.startContainer : me.endContainer; - var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}), - addrs = []; - for(var i = 0,ci;ci = parents[i++];){ - addrs.push(domUtils.getNodeIndex(ci,ignoreTxt)); - } - var firstIndex = 0; - - if(ignoreTxt){ - if(node.nodeType == 3){ - var tmpNode = node.previousSibling; - while(tmpNode && tmpNode.nodeType == 3){ - firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length; - tmpNode = tmpNode.previousSibling; - } - firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 ) - }else{ - node = node.childNodes[ isStart ? me.startOffset : me.endOffset]; - if(node){ - firstIndex = domUtils.getNodeIndex(node,ignoreTxt); - }else{ - node = isStart ? me.startContainer : me.endContainer; - var first = node.firstChild; - while(first){ - if(domUtils.isFillChar(first)){ - first = first.nextSibling; - continue; - } - firstIndex++; - if(first.nodeType == 3){ - while( first && first.nodeType == 3){ - first = first.nextSibling; - } - }else{ - first = first.nextSibling; - } - } - } - } - - }else{ - firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset - } - if(firstIndex < 0){ - firstIndex = 0; - } - addrs.push(firstIndex); - return addrs; - } - addr.startAddress = getAddress(true); - if(!ignoreEnd){ - addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress(); - } - return addr; - }, - - /** - * 保存 - * @method createAddress - * @private - * @return { Boolean } 返回开始和结束的位置 - * @example - * ```html - * - *

                  - * aaaa - * - * - * bbbb - * - * - *

                  - * - * - * - * ``` - */ - moveToAddress : function(addr,ignoreEnd){ - var me = this; - function getNode(address,isStart){ - var tmpNode = me.document.body, - parentNode,offset; - for(var i= 0,ci,l=address.length;i - * - * - * - * - * - * - * - * - * ``` - */ - - /** - * 遍历range内的节点。 - * 每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点 - * 作为其参数。 - * 可以通过参数项 filterFn 来指定一个过滤器, 只有符合该过滤器过滤规则的节点才会触 - * 发doFn函数的执行 - * @method traversal - * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数 - * @param { Function } filterFn 过滤器, 该函数接受当前遍历的节点作为参数, 如果该节点满足过滤 - * 规则, 请返回true, 该节点会触发doFn, 否则, 请返回false, 则该节点不 - * 会触发doFn。 - * @return { UE.dom.Range } 当前range对象 - * @see UE.dom.Range:traversal(Function) - * @example - * ```html - * - * - * - * - * - * - * - * - * - * - * ``` - */ - traversal:function(doFn,filterFn){ - if (this.collapsed) - return this; - var bookmark = this.createBookmark(), - end = bookmark.end, - current = domUtils.getNextDomNode(bookmark.start, false, filterFn); - while (current && current !== end && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) { - var tmpNode = domUtils.getNextDomNode(current,false,filterFn); - doFn(current); - current = tmpNode; - } - return this.moveToBookmark(bookmark); - } - }; -})(); - -// core/Selection.js -/** - * 选集 - * @file - * @module UE.dom - * @class Selection - * @since 1.2.6.1 - */ - -/** - * 选区集合 - * @unfile - * @module UE.dom - * @class Selection - */ -(function () { - - function getBoundaryInformation( range, start ) { - var getIndex = domUtils.getNodeIndex; - range = range.duplicate(); - range.collapse( start ); - var parent = range.parentElement(); - //如果节点里没有子节点,直接退出 - if ( !parent.hasChildNodes() ) { - return {container:parent, offset:0}; - } - var siblings = parent.children, - child, - testRange = range.duplicate(), - startIndex = 0, endIndex = siblings.length - 1, index = -1, - distance; - while ( startIndex <= endIndex ) { - index = Math.floor( (startIndex + endIndex) / 2 ); - child = siblings[index]; - testRange.moveToElementText( child ); - var position = testRange.compareEndPoints( 'StartToStart', range ); - if ( position > 0 ) { - endIndex = index - 1; - } else if ( position < 0 ) { - startIndex = index + 1; - } else { - //trace:1043 - return {container:parent, offset:getIndex( child )}; - } - } - if ( index == -1 ) { - testRange.moveToElementText( parent ); - testRange.setEndPoint( 'StartToStart', range ); - distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; - siblings = parent.childNodes; - if ( !distance ) { - child = siblings[siblings.length - 1]; - return {container:child, offset:child.nodeValue.length}; - } - - var i = siblings.length; - while ( distance > 0 ){ - distance -= siblings[ --i ].nodeValue.length; - } - return {container:siblings[i], offset:-distance}; - } - testRange.collapse( position > 0 ); - testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range ); - distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; - if ( !distance ) { - return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ? - {container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} : - {container:child, offset:position > 0 ? 0 : child.childNodes.length} - } - while ( distance > 0 ) { - try { - var pre = child; - child = child[position > 0 ? 'previousSibling' : 'nextSibling']; - distance -= child.nodeValue.length; - } catch ( e ) { - return {container:parent, offset:getIndex( pre )}; - } - } - return {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance} - } - - /** - * 将ieRange转换为Range对象 - * @param {Range} ieRange ieRange对象 - * @param {Range} range Range对象 - * @return {Range} range 返回转换后的Range对象 - */ - function transformIERangeToRange( ieRange, range ) { - if ( ieRange.item ) { - range.selectNode( ieRange.item( 0 ) ); - } else { - var bi = getBoundaryInformation( ieRange, true ); - range.setStart( bi.container, bi.offset ); - if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) { - bi = getBoundaryInformation( ieRange, false ); - range.setEnd( bi.container, bi.offset ); - } - } - return range; - } - - /** - * 获得ieRange - * @param {Selection} sel Selection对象 - * @return {ieRange} 得到ieRange - */ - function _getIERange( sel ) { - var ieRange; - //ie下有可能报错 - try { - ieRange = sel.getNative().createRange(); - } catch ( e ) { - return null; - } - var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement(); - if ( ( el.ownerDocument || el ) === sel.document ) { - return ieRange; - } - return null; - } - - var Selection = dom.Selection = function ( doc ) { - var me = this, iframe; - me.document = doc; - if ( browser.ie9below ) { - iframe = domUtils.getWindow( doc ).frameElement; - domUtils.on( iframe, 'beforedeactivate', function () { - me._bakIERange = me.getIERange(); - } ); - domUtils.on( iframe, 'activate', function () { - try { - if ( !_getIERange( me ) && me._bakIERange ) { - me._bakIERange.select(); - } - } catch ( ex ) { - } - me._bakIERange = null; - } ); - } - iframe = doc = null; - }; - - Selection.prototype = { - - rangeInBody : function(rng,txtRange){ - var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer; - - return node === this.document.body || domUtils.inDoc(node,this.document); - }, - - /** - * 获取原生seleciton对象 - * @method getNative - * @return { Object } 获得selection对象 - * @example - * ```javascript - * editor.selection.getNative(); - * ``` - */ - getNative:function () { - var doc = this.document; - try { - return !doc ? null : browser.ie9below ? doc.selection : domUtils.getWindow( doc ).getSelection(); - } catch ( e ) { - return null; - } - }, - - /** - * 获得ieRange - * @method getIERange - * @return { Object } 返回ie原生的Range - * @example - * ```javascript - * editor.selection.getIERange(); - * ``` - */ - getIERange:function () { - var ieRange = _getIERange( this ); - if ( !ieRange ) { - if ( this._bakIERange ) { - return this._bakIERange; - } - } - return ieRange; - }, - - /** - * 缓存当前选区的range和选区的开始节点 - * @method cache - */ - cache:function () { - this.clear(); - this._cachedRange = this.getRange(); - this._cachedStartElement = this.getStart(); - this._cachedStartElementPath = this.getStartElementPath(); - }, - - /** - * 获取选区开始位置的父节点到body - * @method getStartElementPath - * @return { Array } 返回父节点集合 - * @example - * ```javascript - * editor.selection.getStartElementPath(); - * ``` - */ - getStartElementPath:function () { - if ( this._cachedStartElementPath ) { - return this._cachedStartElementPath; - } - var start = this.getStart(); - if ( start ) { - return domUtils.findParents( start, true, null, true ) - } - return []; - }, - - /** - * 清空缓存 - * @method clear - */ - clear:function () { - this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null; - }, - - /** - * 编辑器是否得到了选区 - * @method isFocus - */ - isFocus:function () { - try { - if(browser.ie9below){ - - var nativeRange = _getIERange(this); - return !!(nativeRange && this.rangeInBody(nativeRange)); - }else{ - return !!this.getNative().rangeCount; - } - } catch ( e ) { - return false; - } - - }, - - /** - * 获取选区对应的Range - * @method getRange - * @return { Object } 得到Range对象 - * @example - * ```javascript - * editor.selection.getRange(); - * ``` - */ - getRange:function () { - var me = this; - function optimze( range ) { - var child = me.document.body.firstChild, - collapsed = range.collapsed; - while ( child && child.firstChild ) { - range.setStart( child, 0 ); - child = child.firstChild; - } - if ( !range.startContainer ) { - range.setStart( me.document.body, 0 ) - } - if ( collapsed ) { - range.collapse( true ); - } - } - - if ( me._cachedRange != null ) { - return this._cachedRange; - } - var range = new baidu.editor.dom.Range( me.document ); - - if ( browser.ie9below ) { - var nativeRange = me.getIERange(); - if ( nativeRange ) { - //备份的_bakIERange可能已经实效了,dom树发生了变化比如从源码模式切回来,所以try一下,实效就放到body开始位置 - try{ - transformIERangeToRange( nativeRange, range ); - }catch(e){ - optimze( range ); - } - - } else { - optimze( range ); - } - } else { - var sel = me.getNative(); - if ( sel && sel.rangeCount ) { - var firstRange = sel.getRangeAt( 0 ); - var lastRange = sel.getRangeAt( sel.rangeCount - 1 ); - range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset ); - if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) { - optimze( range ); - } - } else { - //trace:1734 有可能已经不在dom树上了,标识的节点 - if ( this._bakRange && domUtils.inDoc( this._bakRange.startContainer, this.document ) ){ - return this._bakRange; - } - optimze( range ); - } - } - return this._bakRange = range; - }, - - /** - * 获取开始元素,用于状态反射 - * @method getStart - * @return { Element } 获得开始元素 - * @example - * ```javascript - * editor.selection.getStart(); - * ``` - */ - getStart:function () { - if ( this._cachedStartElement ) { - return this._cachedStartElement; - } - var range = browser.ie9below ? this.getIERange() : this.getRange(), - tmpRange, - start, tmp, parent; - if ( browser.ie9below ) { - if ( !range ) { - //todo 给第一个值可能会有问题 - return this.document.body.firstChild; - } - //control元素 - if ( range.item ){ - return range.item( 0 ); - } - tmpRange = range.duplicate(); - //修正ie下x[xx] 闭合后 x|xx - tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 ); - tmpRange.collapse( 1 ); - start = tmpRange.parentElement(); - parent = tmp = range.parentElement(); - while ( tmp = tmp.parentNode ) { - if ( tmp == start ) { - start = parent; - break; - } - } - } else { - range.shrinkBoundary(); - start = range.startContainer; - if ( start.nodeType == 1 && start.hasChildNodes() ){ - start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )]; - } - if ( start.nodeType == 3 ){ - return start.parentNode; - } - } - return start; - }, - - /** - * 得到选区中的文本 - * @method getText - * @return { String } 选区中包含的文本 - * @example - * ```javascript - * editor.selection.getText(); - * ``` - */ - getText:function () { - var nativeSel, nativeRange; - if ( this.isFocus() && (nativeSel = this.getNative()) ) { - nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 ); - return browser.ie9below ? nativeRange.text : nativeRange.toString(); - } - return ''; - }, - - /** - * 清除选区 - * @method clearRange - * @example - * ```javascript - * editor.selection.clearRange(); - * ``` - */ - clearRange : function(){ - this.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges'](); - } - }; -})(); - -// core/Editor.js -/** - * 编辑器主类,包含编辑器提供的大部分公用接口 - * @file - * @module UE - * @class Editor - * @since 1.2.6.1 - */ - -/** - * UEditor公用空间,UEditor所有的功能都挂载在该空间下 - * @unfile - * @module UE - */ - -/** - * UEditor的核心类,为用户提供与编辑器交互的接口。 - * @unfile - * @module UE - * @class Editor - */ - -(function () { - var uid = 0, _selectionChangeTimer; - - /** - * 获取编辑器的html内容,赋值到编辑器所在表单的textarea文本域里面 - * @private - * @method setValue - * @param { UE.Editor } editor 编辑器事例 - */ - function setValue(form, editor) { - var textarea; - if (editor.textarea) { - if (utils.isString(editor.textarea)) { - for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) { - if (ti.id == 'ueditor_textarea_' + editor.options.textarea) { - textarea = ti; - break; - } - } - } else { - textarea = editor.textarea; - } - } - if (!textarea) { - form.appendChild(textarea = domUtils.createElement(document, 'textarea', { - 'name': editor.options.textarea, - 'id': 'ueditor_textarea_' + editor.options.textarea, - 'style': "display:none" - })); - //不要产生多个textarea - editor.textarea = textarea; - } - !textarea.getAttribute('name') && textarea.setAttribute('name', editor.options.textarea ); - textarea.value = editor.hasContents() ? - (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) : - '' - } - function loadPlugins(me){ - //初始化插件 - for (var pi in UE.plugins) { - UE.plugins[pi].call(me); - } - - } - function checkCurLang(I18N){ - for(var lang in I18N){ - return lang - } - } - - function langReadied(me){ - me.langIsReady = true; - - me.fireEvent("langReady"); - } - - /** - * 编辑器准备就绪后会触发该事件 - * @module UE - * @class Editor - * @event ready - * @remind render方法执行完成之后,会触发该事件 - * @remind - * @example - * ```javascript - * editor.addListener( 'ready', function( editor ) { - * editor.execCommand( 'focus' ); //编辑器家在完成后,让编辑器拿到焦点 - * } ); - * ``` - */ - /** - * 执行destroy方法,会触发该事件 - * @module UE - * @class Editor - * @event destroy - * @see UE.Editor:destroy() - */ - /** - * 执行reset方法,会触发该事件 - * @module UE - * @class Editor - * @event reset - * @see UE.Editor:reset() - */ - /** - * 执行focus方法,会触发该事件 - * @module UE - * @class Editor - * @event focus - * @see UE.Editor:focus(Boolean) - */ - /** - * 语言加载完成会触发该事件 - * @module UE - * @class Editor - * @event langReady - */ - /** - * 运行命令之后会触发该命令 - * @module UE - * @class Editor - * @event beforeExecCommand - */ - /** - * 运行命令之后会触发该命令 - * @module UE - * @class Editor - * @event afterExecCommand - */ - /** - * 运行命令之前会触发该命令 - * @module UE - * @class Editor - * @event firstBeforeExecCommand - */ - /** - * 在getContent方法执行之前会触发该事件 - * @module UE - * @class Editor - * @event beforeGetContent - * @see UE.Editor:getContent() - */ - /** - * 在getContent方法执行之后会触发该事件 - * @module UE - * @class Editor - * @event afterGetContent - * @see UE.Editor:getContent() - */ - /** - * 在getAllHtml方法执行时会触发该事件 - * @module UE - * @class Editor - * @event getAllHtml - * @see UE.Editor:getAllHtml() - */ - /** - * 在setContent方法执行之前会触发该事件 - * @module UE - * @class Editor - * @event beforeSetContent - * @see UE.Editor:setContent(String) - */ - /** - * 在setContent方法执行之后会触发该事件 - * @module UE - * @class Editor - * @event afterSetContent - * @see UE.Editor:setContent(String) - */ - /** - * 每当编辑器内部选区发生改变时,将触发该事件 - * @event selectionchange - * @warning 该事件的触发非常频繁,不建议在该事件的处理过程中做重量级的处理 - * @example - * ```javascript - * editor.addListener( 'selectionchange', function( editor ) { - * console.log('选区发生改变'); - * } - */ - /** - * 在所有selectionchange的监听函数执行之前,会触发该事件 - * @module UE - * @class Editor - * @event beforeSelectionChange - * @see UE.Editor:selectionchange - */ - /** - * 在所有selectionchange的监听函数执行完之后,会触发该事件 - * @module UE - * @class Editor - * @event afterSelectionChange - * @see UE.Editor:selectionchange - */ - /** - * 编辑器内容发生改变时会触发该事件 - * @module UE - * @class Editor - * @event contentChange - */ - - - /** - * 以默认参数构建一个编辑器实例 - * @constructor - * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面 - * @example - * ```javascript - * var editor = new UE.Editor(); - * editor.execCommand('blod'); - * ``` - * @see UE.Config - */ - - /** - * 以给定的参数集合创建一个编辑器实例,对于未指定的参数,将应用默认参数。 - * @constructor - * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面 - * @param { Object } setting 创建编辑器的参数 - * @example - * ```javascript - * var editor = new UE.Editor(); - * editor.execCommand('blod'); - * ``` - * @see UE.Config - */ - var Editor = UE.Editor = function (options) { - var me = this; - me.uid = uid++; - EventBase.call(me); - me.commands = {}; - me.options = utils.extend(utils.clone(options || {}), UEDITOR_CONFIG, true); - me.shortcutkeys = {}; - me.inputRules = []; - me.outputRules = []; - //设置默认的常用属性 - me.setOpt(Editor.defaultOptions(me)); - - /* 尝试异步加载后台配置 */ - me.loadServerConfig(); - - if(!utils.isEmptyObject(UE.I18N)){ - //修改默认的语言类型 - me.options.lang = checkCurLang(UE.I18N); - UE.plugin.load(me); - langReadied(me); - - }else{ - utils.loadFile(document, { - src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js", - tag: "script", - type: "text/javascript", - defer: "defer" - }, function () { - UE.plugin.load(me); - langReadied(me); - }); - } - - UE.instants['ueditorInstant' + me.uid] = me; - }; - Editor.prototype = { - registerCommand : function(name,obj){ - this.commands[name] = obj; - }, - /** - * 编辑器对外提供的监听ready事件的接口, 通过调用该方法,达到的效果与监听ready事件是一致的 - * @method ready - * @param { Function } fn 编辑器ready之后所执行的回调, 如果在注册事件之前编辑器已经ready,将会 - * 立即触发该回调。 - * @remind 需要等待编辑器加载完成后才能执行的代码,可以使用该方法传入 - * @example - * ```javascript - * editor.ready( function( editor ) { - * editor.setContent('初始化完毕'); - * } ); - * ``` - * @see UE.Editor.event:ready - */ - ready: function (fn) { - var me = this; - if (fn) { - me.isReady ? fn.apply(me) : me.addListener('ready', fn); - } - }, - - /** - * 该方法是提供给插件里面使用,设置配置项默认值 - * @method setOpt - * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置 - * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。 - * @param { String } key 编辑器的可接受的选项名称 - * @param { * } val 该选项可接受的值 - * @example - * ```javascript - * editor.setOpt( 'initContent', '欢迎使用编辑器' ); - * ``` - */ - - /** - * 该方法是提供给插件里面使用,以{key:value}集合的方式设置插件内用到的配置项默认值 - * @method setOpt - * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置 - * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。 - * @param { Object } options 将要设置的选项的键值对对象 - * @example - * ```javascript - * editor.setOpt( { - * 'initContent': '欢迎使用编辑器' - * } ); - * ``` - */ - setOpt: function (key, val) { - var obj = {}; - if (utils.isString(key)) { - obj[key] = val - } else { - obj = key; - } - utils.extend(this.options, obj, true); - }, - getOpt:function(key){ - return this.options[key] - }, - /** - * 销毁编辑器实例,使用textarea代替 - * @method destroy - * @example - * ```javascript - * editor.destroy(); - * ``` - */ - destroy: function () { - - var me = this; - me.fireEvent('destroy'); - var container = me.container.parentNode; - var textarea = me.textarea; - if (!textarea) { - textarea = document.createElement('textarea'); - container.parentNode.insertBefore(textarea, container); - } else { - textarea.style.display = '' - } - - textarea.style.width = me.iframe.offsetWidth + 'px'; - textarea.style.height = me.iframe.offsetHeight + 'px'; - textarea.value = me.getContent(); - textarea.id = me.key; - container.innerHTML = ''; - domUtils.remove(container); - var key = me.key; - //trace:2004 - for (var p in me) { - if (me.hasOwnProperty(p)) { - delete this[p]; - } - } - UE.delEditor(key); - }, - - /** - * 渲染编辑器的DOM到指定容器 - * @method render - * @param { String } containerId 指定一个容器ID - * @remind 执行该方法,会触发ready事件 - * @warning 必须且只能调用一次 - */ - - /** - * 渲染编辑器的DOM到指定容器 - * @method render - * @param { Element } containerDom 直接指定容器对象 - * @remind 执行该方法,会触发ready事件 - * @warning 必须且只能调用一次 - */ - render: function (container) { - var me = this, - options = me.options, - getStyleValue=function(attr){ - return parseInt(domUtils.getComputedStyle(container,attr)); - }; - if (utils.isString(container)) { - container = document.getElementById(container); - } - if (container) { - if(options.initialFrameWidth){ - options.minFrameWidth = options.initialFrameWidth - }else{ - options.minFrameWidth = options.initialFrameWidth = container.offsetWidth; - } - if(options.initialFrameHeight){ - options.minFrameHeight = options.initialFrameHeight - }else{ - options.initialFrameHeight = options.minFrameHeight = container.offsetHeight; - } - - container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth- - getStyleValue("padding-left")- getStyleValue("padding-right") +'px'; - container.style.height = /%$/.test(options.initialFrameHeight) ? '100%' : options.initialFrameHeight - - getStyleValue("padding-top")- getStyleValue("padding-bottom") +'px'; - - container.style.zIndex = options.zIndex; - - var html = ( ie && browser.version < 9 ? '' : '') + - '' + - '' + - ( options.iframeCssUrl ? '' : '' ) + - (options.initialStyle ? '' : '') + - '' + - ''; - container.appendChild(domUtils.createElement(document, 'iframe', { - id: 'ueditor_' + me.uid, - width: "100%", - height: "100%", - frameborder: "0", - //先注释掉了,加的原因忘记了,但开启会直接导致全屏模式下内容多时不会出现滚动条 -// scrolling :'no', - src: 'javascript:void(function(){document.open();' + (options.customDomain && document.domain != location.hostname ? 'document.domain="' + document.domain + '";' : '') + - 'document.write("' + html + '");document.close();}())' - })); - container.style.overflow = 'hidden'; - //解决如果是给定的百分比,会导致高度算不对的问题 - setTimeout(function(){ - if( /%$/.test(options.initialFrameWidth)){ - options.minFrameWidth = options.initialFrameWidth = container.offsetWidth; - //如果这里给定宽度,会导致ie在拖动窗口大小时,编辑区域不随着变化 -// container.style.width = options.initialFrameWidth + 'px'; - } - if(/%$/.test(options.initialFrameHeight)){ - options.minFrameHeight = options.initialFrameHeight = container.offsetHeight; - container.style.height = options.initialFrameHeight + 'px'; - } - }) - } - }, - - /** - * 编辑器初始化 - * @method _setup - * @private - * @param { Element } doc 编辑器Iframe中的文档对象 - */ - _setup: function (doc) { - - var me = this, - options = me.options; - if (ie) { - doc.body.disabled = true; - doc.body.contentEditable = true; - doc.body.disabled = false; - } else { - doc.body.contentEditable = true; - } - doc.body.spellcheck = false; - me.document = doc; - me.window = doc.defaultView || doc.parentWindow; - me.iframe = me.window.frameElement; - me.body = doc.body; - me.selection = new dom.Selection(doc); - //gecko初始化就能得到range,无法判断isFocus了 - var geckoSel; - if (browser.gecko && (geckoSel = this.selection.getNative())) { - geckoSel.removeAllRanges(); - } - this._initEvents(); - //为form提交提供一个隐藏的textarea - for (var form = this.iframe.parentNode; !domUtils.isBody(form); form = form.parentNode) { - if (form.tagName == 'FORM') { - me.form = form; - if(me.options.autoSyncData){ - domUtils.on(me.window,'blur',function(){ - setValue(form,me); - }); - }else{ - domUtils.on(form, 'submit', function () { - setValue(this, me); - }); - } - break; - } - } - if (options.initialContent) { - if (options.autoClearinitialContent) { - var oldExecCommand = me.execCommand; - me.execCommand = function () { - me.fireEvent('firstBeforeExecCommand'); - return oldExecCommand.apply(me, arguments); - }; - this._setDefaultContent(options.initialContent); - } else - this.setContent(options.initialContent, false, true); - } - - //编辑器不能为空内容 - - if (domUtils.isEmptyNode(me.body)) { - me.body.innerHTML = '

                  ' + (browser.ie ? '' : '
                  ') + '

                  '; - } - //如果要求focus, 就把光标定位到内容开始 - if (options.focus) { - setTimeout(function () { - me.focus(me.options.focusInEnd); - //如果自动清除开着,就不需要做selectionchange; - !me.options.autoClearinitialContent && me._selectionChange(); - }, 0); - } - if (!me.container) { - me.container = this.iframe.parentNode; - } - if (options.fullscreen && me.ui) { - me.ui.setFullScreen(true); - } - - try { - me.document.execCommand('2D-position', false, false); - } catch (e) { - } - try { - me.document.execCommand('enableInlineTableEditing', false, false); - } catch (e) { - } - try { - me.document.execCommand('enableObjectResizing', false, false); - } catch (e) { - } - - //挂接快捷键 - me._bindshortcutKeys(); - me.isReady = 1; - me.fireEvent('ready'); - options.onready && options.onready.call(me); - if (!browser.ie9below) { - domUtils.on(me.window, ['blur', 'focus'], function (e) { - //chrome下会出现alt+tab切换时,导致选区位置不对 - if (e.type == 'blur') { - me._bakRange = me.selection.getRange(); - try { - me._bakNativeRange = me.selection.getNative().getRangeAt(0); - me.selection.getNative().removeAllRanges(); - } catch (e) { - me._bakNativeRange = null; - } - - } else { - try { - me._bakRange && me._bakRange.select(); - } catch (e) { - } - } - }); - } - //trace:1518 ff3.6body不够寛,会导致点击空白处无法获得焦点 - if (browser.gecko && browser.version <= 10902) { - //修复ff3.6初始化进来,不能点击获得焦点 - me.body.contentEditable = false; - setTimeout(function () { - me.body.contentEditable = true; - }, 100); - setInterval(function () { - me.body.style.height = me.iframe.offsetHeight - 20 + 'px' - }, 100) - } - - !options.isShow && me.setHide(); - options.readonly && me.setDisabled(); - }, - - /** - * 同步数据到编辑器所在的form - * 从编辑器的容器节点向上查找form元素,若找到,就同步编辑内容到找到的form里,为提交数据做准备,主要用于是手动提交的情况 - * 后台取得数据的键值,使用你容器上的name属性,如果没有就使用参数里的textarea项 - * @method sync - * @example - * ```javascript - * editor.sync(); - * form.sumbit(); //form变量已经指向了form元素 - * ``` - */ - - /** - * 根据传入的formId,在页面上查找要同步数据的表单,若找到,就同步编辑内容到找到的form里,为提交数据做准备 - * 后台取得数据的键值,该键值默认使用给定的编辑器容器的name属性,如果没有name属性则使用参数项里给定的“textarea”项 - * @method sync - * @param { String } formID 指定一个要同步数据的form的id,编辑器的数据会同步到你指定form下 - */ - sync: function (formId) { - var me = this, - form = formId ? document.getElementById(formId) : - domUtils.findParent(me.iframe.parentNode, function (node) { - return node.tagName == 'FORM' - }, true); - form && setValue(form, me); - }, - - /** - * 设置编辑器高度 - * @method setHeight - * @remind 当配置项autoHeightEnabled为真时,该方法无效 - * @param { Number } number 设置的高度值,纯数值,不带单位 - * @example - * ```javascript - * editor.setHeight(number); - * ``` - */ - setHeight: function (height,notSetHeight) { - if (height !== parseInt(this.iframe.parentNode.style.height)) { - this.iframe.parentNode.style.height = height + 'px'; - } - !notSetHeight && (this.options.minFrameHeight = this.options.initialFrameHeight = height); - this.body.style.height = height + 'px'; - !notSetHeight && this.trigger('setHeight') - }, - - /** - * 为编辑器的编辑命令提供快捷键 - * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口 - * @method addshortcutkey - * @param { Object } keyset 命令名和快捷键键值对对象,多个按钮的快捷键用“+”分隔 - * @example - * ```javascript - * editor.addshortcutkey({ - * "Bold" : "ctrl+66",//^B - * "Italic" : "ctrl+73", //^I - * }); - * ``` - */ - /** - * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口 - * @method addshortcutkey - * @param { String } cmd 触发快捷键时,响应的命令 - * @param { String } keys 快捷键的字符串,多个按钮用“+”分隔 - * @example - * ```javascript - * editor.addshortcutkey("Underline", "ctrl+85"); //^U - * ``` - */ - addshortcutkey: function (cmd, keys) { - var obj = {}; - if (keys) { - obj[cmd] = keys - } else { - obj = cmd; - } - utils.extend(this.shortcutkeys, obj) - }, - - /** - * 对编辑器设置keydown事件监听,绑定快捷键和命令,当快捷键组合触发成功,会响应对应的命令 - * @method _bindshortcutKeys - * @private - */ - _bindshortcutKeys: function () { - var me = this, shortcutkeys = this.shortcutkeys; - me.addListener('keydown', function (type, e) { - var keyCode = e.keyCode || e.which; - for (var i in shortcutkeys) { - var tmp = shortcutkeys[i].split(','); - for (var t = 0, ti; ti = tmp[t++];) { - ti = ti.split(':'); - var key = ti[0], param = ti[1]; - if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) { - if (( (RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0) - && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1) - && keyCode == RegExp.$3 - ) || - keyCode == RegExp.$1 - ) { - if (me.queryCommandState(i,param) != -1) - me.execCommand(i, param); - domUtils.preventDefault(e); - } - } - } - - } - }); - }, - - /** - * 获取编辑器的内容 - * @method getContent - * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容 - * @return { String } 编辑器的内容字符串, 如果编辑器的内容为空,或者是空的标签内容(如:”<p><br/></p>“), 则返回空字符串 - * @example - * ```javascript - * //编辑器html内容:

                  123456

                  - * var content = editor.getContent(); //返回值:

                  123456

                  - * ``` - */ - - /** - * 获取编辑器的内容。 可以通过参数定义编辑器内置的判空规则 - * @method getContent - * @param { Function } fn 自定的判空规则, 要求该方法返回一个boolean类型的值, - * 代表当前编辑器的内容是否空, - * 如果返回true, 则该方法将直接返回空字符串;如果返回false,则编辑器将返回 - * 经过内置过滤规则处理后的内容。 - * @remind 该方法在处理包含有初始化内容的时候能起到很好的作用。 - * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容 - * @return { String } 编辑器的内容字符串 - * @example - * ```javascript - * // editor 是一个编辑器的实例 - * var content = editor.getContent( function ( editor ) { - * return editor.body.innerHTML === '欢迎使用UEditor'; //返回空字符串 - * } ); - * ``` - */ - getContent: function (cmd, fn,notSetCursor,ignoreBlank,formatter) { - var me = this; - if (cmd && utils.isFunction(cmd)) { - fn = cmd; - cmd = ''; - } - if (fn ? !fn() : !this.hasContents()) { - return ''; - } - me.fireEvent('beforegetcontent'); - var root = UE.htmlparser(me.body.innerHTML,ignoreBlank); - me.filterOutputRule(root); - me.fireEvent('aftergetcontent', cmd,root); - return root.toHtml(formatter); - }, - - /** - * 取得完整的html代码,可以直接显示成完整的html文档 - * @method getAllHtml - * @return { String } 编辑器的内容html文档字符串 - * @eaxmple - * ```javascript - * editor.getAllHtml(); //返回格式大致是: ...... - * ``` - */ - getAllHtml: function () { - var me = this, - headHtml = [], - html = ''; - me.fireEvent('getAllHtml', headHtml); - if (browser.ie && browser.version > 8) { - var headHtmlForIE9 = ''; - utils.each(me.document.styleSheets, function (si) { - headHtmlForIE9 += ( si.href ? '' : ''); - }); - utils.each(me.document.getElementsByTagName('script'), function (si) { - headHtmlForIE9 += si.outerHTML; - }); - - } - return '' + (me.options.charset ? '' : '') - + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '' - + '' + me.getContent(null, null, true) + ''; - }, - - /** - * 得到编辑器的纯文本内容,但会保留段落格式 - * @method getPlainTxt - * @return { String } 编辑器带段落格式的纯文本内容字符串 - * @example - * ```javascript - * //编辑器html内容:

                  1

                  2

                  - * console.log(editor.getPlainTxt()); //输出:"1\n2\n - * ``` - */ - getPlainTxt: function () { - var reg = new RegExp(domUtils.fillChar, 'g'), - html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理 - html = html.replace(/<(p|div)[^>]*>(| )<\/\1>/gi, '\n') - .replace(//gi, '\n') - .replace(/<[^>/]+>/g, '') - .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) { - return dtd.$block[c] ? '\n' : b ? b : ''; - }); - //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0 - return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/ /g, ' '); - }, - - /** - * 获取编辑器中的纯文本内容,没有段落格式 - * @method getContentTxt - * @return { String } 编辑器不带段落格式的纯文本内容字符串 - * @example - * ```javascript - * //编辑器html内容:

                  1

                  2

                  - * console.log(editor.getPlainTxt()); //输出:"12 - * ``` - */ - getContentTxt: function () { - var reg = new RegExp(domUtils.fillChar, 'g'); - //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0 - return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' '); - }, - - /** - * 设置编辑器的内容,可修改编辑器当前的html内容 - * @method setContent - * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容 - * @warning 该方法会触发selectionchange事件 - * @param { String } html 要插入的html内容 - * @example - * ```javascript - * editor.getContent('

                  test

                  '); - * ``` - */ - - /** - * 设置编辑器的内容,可修改编辑器当前的html内容 - * @method setContent - * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容 - * @warning 该方法会触发selectionchange事件 - * @param { String } html 要插入的html内容 - * @param { Boolean } isAppendTo 若传入true,不清空原来的内容,在最后插入内容,否则,清空内容再插入 - * @example - * ```javascript - * //假设设置前的编辑器内容是

                  old text

                  - * editor.setContent('

                  new text

                  ', true); //插入的结果是

                  old text

                  new text

                  - * ``` - */ - setContent: function (html, isAppendTo, notFireSelectionchange) { - var me = this; - - me.fireEvent('beforesetcontent', html); - var root = UE.htmlparser(html); - me.filterInputRule(root); - html = root.toHtml(); - - me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html; - - - function isCdataDiv(node){ - return node.tagName == 'DIV' && node.getAttribute('cdata_tag'); - } - //给文本或者inline节点套p标签 - if (me.options.enterTag == 'p') { - - var child = this.body.firstChild, tmpNode; - if (!child || child.nodeType == 1 && - (dtd.$cdata[child.tagName] || isCdataDiv(child) || - domUtils.isCustomeNode(child) - ) - && child === this.body.lastChild) { - this.body.innerHTML = '

                  ' + (browser.ie ? ' ' : '
                  ') + '

                  ' + this.body.innerHTML; - - } else { - var p = me.document.createElement('p'); - while (child) { - while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) { - tmpNode = child.nextSibling; - p.appendChild(child); - child = tmpNode; - } - if (p.firstChild) { - if (!child) { - me.body.appendChild(p); - break; - } else { - child.parentNode.insertBefore(p, child); - p = me.document.createElement('p'); - } - } - child = child.nextSibling; - } - } - } - me.fireEvent('aftersetcontent'); - me.fireEvent('contentchange'); - - !notFireSelectionchange && me._selectionChange(); - //清除保存的选区 - me._bakRange = me._bakIERange = me._bakNativeRange = null; - //trace:1742 setContent后gecko能得到焦点问题 - var geckoSel; - if (browser.gecko && (geckoSel = this.selection.getNative())) { - geckoSel.removeAllRanges(); - } - if(me.options.autoSyncData){ - me.form && setValue(me.form,me); - } - }, - - /** - * 让编辑器获得焦点,默认focus到编辑器头部 - * @method focus - * @example - * ```javascript - * editor.focus() - * ``` - */ - - /** - * 让编辑器获得焦点,toEnd确定focus位置 - * @method focus - * @param { Boolean } toEnd 默认focus到编辑器头部,toEnd为true时focus到内容尾部 - * @example - * ```javascript - * editor.focus(true) - * ``` - */ - focus: function (toEnd) { - try { - var me = this, - rng = me.selection.getRange(); - if (toEnd) { - var node = me.body.lastChild; - if(node && node.nodeType == 1 && !dtd.$empty[node.tagName]){ - if(domUtils.isEmptyBlock(node)){ - rng.setStartAtFirst(node) - }else{ - rng.setStartAtLast(node) - } - rng.collapse(true); - } - rng.setCursor(true); - } else { - if(!rng.collapsed && domUtils.isBody(rng.startContainer) && rng.startOffset == 0){ - - var node = me.body.firstChild; - if(node && node.nodeType == 1 && !dtd.$empty[node.tagName]){ - rng.setStartAtFirst(node).collapse(true); - } - } - - rng.select(true); - - } - this.fireEvent('focus selectionchange'); - } catch (e) { - } - - }, - isFocus:function(){ - return this.selection.isFocus(); - }, - blur:function(){ - var sel = this.selection.getNative(); - if(sel.empty && browser.ie){ - var nativeRng = document.body.createTextRange(); - nativeRng.moveToElementText(document.body); - nativeRng.collapse(true); - nativeRng.select(); - sel.empty() - }else{ - sel.removeAllRanges() - } - - //this.fireEvent('blur selectionchange'); - }, - /** - * 初始化UE事件及部分事件代理 - * @method _initEvents - * @private - */ - _initEvents: function () { - var me = this, - doc = me.document, - win = me.window; - me._proxyDomEvent = utils.bind(me._proxyDomEvent, me); - domUtils.on(doc, ['click', 'contextmenu', 'mousedown', 'keydown', 'keyup', 'keypress', 'mouseup', 'mouseover', 'mouseout', 'selectstart'], me._proxyDomEvent); - domUtils.on(win, ['focus', 'blur'], me._proxyDomEvent); - domUtils.on(me.body,'drop',function(e){ - //阻止ff下默认的弹出新页面打开图片 - if(browser.gecko && e.stopPropagation) { e.stopPropagation(); } - me.fireEvent('contentchange') - }); - domUtils.on(doc, ['mouseup', 'keydown'], function (evt) { - //特殊键不触发selectionchange - if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) { - return; - } - if (evt.button == 2)return; - me._selectionChange(250, evt); - }); - }, - /** - * 触发事件代理 - * @method _proxyDomEvent - * @private - * @return { * } fireEvent的返回值 - * @see UE.EventBase:fireEvent(String) - */ - _proxyDomEvent: function (evt) { - if(this.fireEvent('before' + evt.type.replace(/^on/, '').toLowerCase()) === false){ - return false; - } - if(this.fireEvent(evt.type.replace(/^on/, ''), evt) === false){ - return false; - } - return this.fireEvent('after' + evt.type.replace(/^on/, '').toLowerCase()) - }, - /** - * 变化选区 - * @method _selectionChange - * @private - */ - _selectionChange: function (delay, evt) { - var me = this; - //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1) -// if ( !me.selection.isFocus() ){ -// return; -// } - - - var hackForMouseUp = false; - var mouseX, mouseY; - if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') { - var range = this.selection.getRange(); - if (!range.collapsed) { - hackForMouseUp = true; - mouseX = evt.clientX; - mouseY = evt.clientY; - } - } - clearTimeout(_selectionChangeTimer); - _selectionChangeTimer = setTimeout(function () { - if (!me.selection || !me.selection.getNative()) { - return; - } - //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值. - //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响 - var ieRange; - if (hackForMouseUp && me.selection.getNative().type == 'None') { - ieRange = me.document.body.createTextRange(); - try { - ieRange.moveToPoint(mouseX, mouseY); - } catch (ex) { - ieRange = null; - } - } - var bakGetIERange; - if (ieRange) { - bakGetIERange = me.selection.getIERange; - me.selection.getIERange = function () { - return ieRange; - }; - } - me.selection.cache(); - if (bakGetIERange) { - me.selection.getIERange = bakGetIERange; - } - if (me.selection._cachedRange && me.selection._cachedStartElement) { - me.fireEvent('beforeselectionchange'); - // 第二个参数causeByUi为true代表由用户交互造成的selectionchange. - me.fireEvent('selectionchange', !!evt); - me.fireEvent('afterselectionchange'); - me.selection.clear(); - } - }, delay || 50); - }, - - /** - * 执行编辑命令 - * @method _callCmdFn - * @private - * @param { String } fnName 函数名称 - * @param { * } args 传给命令函数的参数 - * @return { * } 返回命令函数运行的返回值 - */ - _callCmdFn: function (fnName, args) { - var cmdName = args[0].toLowerCase(), - cmd, cmdFn; - cmd = this.commands[cmdName] || UE.commands[cmdName]; - cmdFn = cmd && cmd[fnName]; - //没有querycommandstate或者没有command的都默认返回0 - if ((!cmd || !cmdFn) && fnName == 'queryCommandState') { - return 0; - } else if (cmdFn) { - return cmdFn.apply(this, args); - } - }, - - /** - * 执行编辑命令cmdName,完成富文本编辑效果 - * @method execCommand - * @param { String } cmdName 需要执行的命令 - * @remind 具体命令的使用请参考命令列表 - * @return { * } 返回命令函数运行的返回值 - * @example - * ```javascript - * editor.execCommand(cmdName); - * ``` - */ - execCommand: function (cmdName) { - cmdName = cmdName.toLowerCase(); - var me = this, - result, - cmd = me.commands[cmdName] || UE.commands[cmdName]; - if (!cmd || !cmd.execCommand) { - return null; - } - if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) { - me.__hasEnterExecCommand = true; - if (me.queryCommandState.apply(me,arguments) != -1) { - me.fireEvent('saveScene'); - me.fireEvent.apply(me, ['beforeexeccommand', cmdName].concat(arguments)); - result = this._callCmdFn('execCommand', arguments); - //保存场景时,做了内容对比,再看是否进行contentchange触发,这里多触发了一次,去掉 -// (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange'); - me.fireEvent.apply(me, ['afterexeccommand', cmdName].concat(arguments)); - me.fireEvent('saveScene'); - } - me.__hasEnterExecCommand = false; - } else { - result = this._callCmdFn('execCommand', arguments); - (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange') - } - (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange(); - return result; - }, - - /** - * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态 - * @method queryCommandState - * @param { String } cmdName 需要查询的命令名称 - * @remind 具体命令的使用请参考命令列表 - * @return { Number } number 返回放前命令的状态,返回值三种情况:(-1|0|1) - * @example - * ```javascript - * editor.queryCommandState(cmdName) => (-1|0|1) - * ``` - * @see COMMAND.LIST - */ - queryCommandState: function (cmdName) { - return this._callCmdFn('queryCommandState', arguments); - }, - - /** - * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值 - * @method queryCommandValue - * @param { String } cmdName 需要查询的命令名称 - * @remind 具体命令的使用请参考命令列表 - * @remind 只有部分插件有此方法 - * @return { * } 返回每个命令特定的当前状态值 - * @grammar editor.queryCommandValue(cmdName) => {*} - * @see COMMAND.LIST - */ - queryCommandValue: function (cmdName) { - return this._callCmdFn('queryCommandValue', arguments); - }, - - /** - * 检查编辑区域中是否有内容 - * @method hasContents - * @remind 默认有文本内容,或者有以下节点都不认为是空 - * table,ul,ol,dl,iframe,area,base,col,hr,img,embed,input,link,meta,param - * @return { Boolean } 检查有内容返回true,否则返回false - * @example - * ```javascript - * editor.hasContents() - * ``` - */ - - /** - * 检查编辑区域中是否有内容,若包含参数tags中的节点类型,直接返回true - * @method hasContents - * @param { Array } tags 传入数组判断时用到的节点类型 - * @return { Boolean } 若文档中包含tags数组里对应的tag,返回true,否则返回false - * @example - * ```javascript - * editor.hasContents(['span']); - * ``` - */ - hasContents: function (tags) { - if (tags) { - for (var i = 0, ci; ci = tags[i++];) { - if (this.document.getElementsByTagName(ci).length > 0) { - return true; - } - } - } - if (!domUtils.isEmptyBlock(this.body)) { - return true - } - //随时添加,定义的特殊标签如果存在,不能认为是空 - tags = ['div']; - for (i = 0; ci = tags[i++];) { - var nodes = domUtils.getElementsByTagName(this.document, ci); - for (var n = 0, cn; cn = nodes[n++];) { - if (domUtils.isCustomeNode(cn)) { - return true; - } - } - } - return false; - }, - - /** - * 重置编辑器,可用来做多个tab使用同一个编辑器实例 - * @method reset - * @remind 此方法会清空编辑器内容,清空回退列表,会触发reset事件 - * @example - * ```javascript - * editor.reset() - * ``` - */ - reset: function () { - this.fireEvent('reset'); - }, - - /** - * 设置当前编辑区域可以编辑 - * @method setEnabled - * @example - * ```javascript - * editor.setEnabled() - * ``` - */ - setEnabled: function () { - var me = this, range; - if (me.body.contentEditable == 'false') { - me.body.contentEditable = true; - range = me.selection.getRange(); - //有可能内容丢失了 - try { - range.moveToBookmark(me.lastBk); - delete me.lastBk - } catch (e) { - range.setStartAtFirst(me.body).collapse(true) - } - range.select(true); - if (me.bkqueryCommandState) { - me.queryCommandState = me.bkqueryCommandState; - delete me.bkqueryCommandState; - } - if (me.bkqueryCommandValue) { - me.queryCommandValue = me.bkqueryCommandValue; - delete me.bkqueryCommandValue; - } - me.fireEvent('selectionchange'); - } - }, - enable: function () { - return this.setEnabled(); - }, - - /** 设置当前编辑区域不可编辑 - * @method setDisabled - */ - - /** 设置当前编辑区域不可编辑,except中的命令除外 - * @method setDisabled - * @param { String } except 例外命令的字符串 - * @remind 即使设置了disable,此处配置的例外命令仍然可以执行 - * @example - * ```javascript - * editor.setDisabled('bold'); //禁用工具栏中除加粗之外的所有功能 - * ``` - */ - - /** 设置当前编辑区域不可编辑,except中的命令除外 - * @method setDisabled - * @param { Array } except 例外命令的字符串数组,数组中的命令仍然可以执行 - * @remind 即使设置了disable,此处配置的例外命令仍然可以执行 - * @example - * ```javascript - * editor.setDisabled(['bold','insertimage']); //禁用工具栏中除加粗和插入图片之外的所有功能 - * ``` - */ - setDisabled: function (except) { - var me = this; - except = except ? utils.isArray(except) ? except : [except] : []; - if (me.body.contentEditable == 'true') { - if (!me.lastBk) { - me.lastBk = me.selection.getRange().createBookmark(true); - } - me.body.contentEditable = false; - me.bkqueryCommandState = me.queryCommandState; - me.bkqueryCommandValue = me.queryCommandValue; - me.queryCommandState = function (type) { - if (utils.indexOf(except, type) != -1) { - return me.bkqueryCommandState.apply(me, arguments); - } - return -1; - }; - me.queryCommandValue = function (type) { - if (utils.indexOf(except, type) != -1) { - return me.bkqueryCommandValue.apply(me, arguments); - } - return null; - }; - me.fireEvent('selectionchange'); - } - }, - disable: function (except) { - return this.setDisabled(except); - }, - - /** - * 设置默认内容 - * @method _setDefaultContent - * @private - * @param { String } cont 要存入的内容 - */ - _setDefaultContent: function () { - function clear() { - var me = this; - if (me.document.getElementById('initContent')) { - me.body.innerHTML = '

                  ' + (ie ? '' : '
                  ') + '

                  '; - me.removeListener('firstBeforeExecCommand focus', clear); - setTimeout(function () { - me.focus(); - me._selectionChange(); - }, 0) - } - } - - return function (cont) { - var me = this; - me.body.innerHTML = '

                  ' + cont + '

                  '; - - me.addListener('firstBeforeExecCommand focus', clear); - } - }(), - - /** - * 显示编辑器 - * @method setShow - * @example - * ```javascript - * editor.setShow() - * ``` - */ - setShow: function () { - var me = this, range = me.selection.getRange(); - if (me.container.style.display == 'none') { - //有可能内容丢失了 - try { - range.moveToBookmark(me.lastBk); - delete me.lastBk - } catch (e) { - range.setStartAtFirst(me.body).collapse(true) - } - //ie下focus实效,所以做了个延迟 - setTimeout(function () { - range.select(true); - }, 100); - me.container.style.display = ''; - } - - }, - show: function () { - return this.setShow(); - }, - /** - * 隐藏编辑器 - * @method setHide - * @example - * ```javascript - * editor.setHide() - * ``` - */ - setHide: function () { - var me = this; - if (!me.lastBk) { - me.lastBk = me.selection.getRange().createBookmark(true); - } - me.container.style.display = 'none' - }, - hide: function () { - return this.setHide(); - }, - - /** - * 根据指定的路径,获取对应的语言资源 - * @method getLang - * @param { String } path 路径根据的是lang目录下的语言文件的路径结构 - * @return { Object | String } 根据路径返回语言资源的Json格式对象或者语言字符串 - * @example - * ```javascript - * editor.getLang('contextMenu.delete'); //如果当前是中文,那返回是的是'删除' - * ``` - */ - getLang: function (path) { - var lang = UE.I18N[this.options.lang]; - if (!lang) { - throw Error("not import language file"); - } - path = (path || "").split("."); - for (var i = 0, ci; ci = path[i++];) { - lang = lang[ci]; - if (!lang)break; - } - return lang; - }, - - /** - * 计算编辑器html内容字符串的长度 - * @method getContentLength - * @return { Number } 返回计算的长度 - * @example - * ```javascript - * //编辑器html内容

                  132

                  - * editor.getContentLength() //返回27 - * ``` - */ - /** - * 计算编辑器当前纯文本内容的长度 - * @method getContentLength - * @param { Boolean } ingoneHtml 传入true时,只按照纯文本来计算 - * @return { Number } 返回计算的长度,内容中有hr/img/iframe标签,长度加1 - * @example - * ```javascript - * //编辑器html内容

                  132

                  - * editor.getContentLength() //返回3 - * ``` - */ - getContentLength: function (ingoneHtml, tagNames) { - var count = this.getContent(false,false,true).length; - if (ingoneHtml) { - tagNames = (tagNames || []).concat([ 'hr', 'img', 'iframe']); - count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length; - for (var i = 0, ci; ci = tagNames[i++];) { - count += this.document.getElementsByTagName(ci).length; - } - } - return count; - }, - - /** - * 注册输入过滤规则 - * @method addInputRule - * @param { Function } rule 要添加的过滤规则 - * @example - * ```javascript - * editor.addInputRule(function(root){ - * $.each(root.getNodesByTagName('div'),function(i,node){ - * node.tagName="p"; - * }); - * }); - * ``` - */ - addInputRule: function (rule) { - this.inputRules.push(rule); - }, - - /** - * 执行注册的过滤规则 - * @method filterInputRule - * @param { UE.uNode } root 要过滤的uNode节点 - * @remind 执行editor.setContent方法和执行'inserthtml'命令后,会运行该过滤函数 - * @example - * ```javascript - * editor.filterInputRule(editor.body); - * ``` - * @see UE.Editor:addInputRule - */ - filterInputRule: function (root) { - for (var i = 0, ci; ci = this.inputRules[i++];) { - ci.call(this, root) - } - }, - - /** - * 注册输出过滤规则 - * @method addOutputRule - * @param { Function } rule 要添加的过滤规则 - * @example - * ```javascript - * editor.addOutputRule(function(root){ - * $.each(root.getNodesByTagName('p'),function(i,node){ - * node.tagName="div"; - * }); - * }); - * ``` - */ - addOutputRule: function (rule) { - this.outputRules.push(rule) - }, - - /** - * 根据输出过滤规则,过滤编辑器内容 - * @method filterOutputRule - * @remind 执行editor.getContent方法的时候,会先运行该过滤函数 - * @param { UE.uNode } root 要过滤的uNode节点 - * @example - * ```javascript - * editor.filterOutputRule(editor.body); - * ``` - * @see UE.Editor:addOutputRule - */ - filterOutputRule: function (root) { - for (var i = 0, ci; ci = this.outputRules[i++];) { - ci.call(this, root) - } - }, - - /** - * 根据action名称获取请求的路径 - * @method getActionUrl - * @remind 假如没有设置serverUrl,会根据imageUrl设置默认的controller路径 - * @param { String } action action名称 - * @example - * ```javascript - * editor.getActionUrl('config'); //返回 "/ueditor/php/controller.php?action=config" - * editor.getActionUrl('image'); //返回 "/ueditor/php/controller.php?action=uplaodimage" - * editor.getActionUrl('scrawl'); //返回 "/ueditor/php/controller.php?action=uplaodscrawl" - * editor.getActionUrl('imageManager'); //返回 "/ueditor/php/controller.php?action=listimage" - * ``` - */ - getActionUrl: function(action){ - var actionName = this.getOpt(action) || action, - imageUrl = this.getOpt('imageUrl'), - serverUrl = this.getOpt('serverUrl'); - - if(!serverUrl && imageUrl) { - serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2'); - } - - if(serverUrl) { - serverUrl = serverUrl + (serverUrl.indexOf('?') == -1 ? '?':'&') + 'action=' + (actionName || ''); - return utils.formatUrl(serverUrl); - } else { - return ''; - } - } - }; - utils.inherits(Editor, EventBase); -})(); - - -// core/Editor.defaultoptions.js -//维护编辑器一下默认的不在插件中的配置项 -UE.Editor.defaultOptions = function(editor){ - - var _url = editor.options.UEDITOR_HOME_URL; - return { - isShow: true, - initialContent: '', - initialStyle:'', - autoClearinitialContent: false, - iframeCssUrl: _url + 'themes/iframe.css', - textarea: 'editorValue', - focus: false, - focusInEnd: true, - autoClearEmptyNode: true, - fullscreen: false, - readonly: false, - zIndex: 999, - imagePopup: true, - enterTag: 'p', - customDomain: false, - lang: 'zh-cn', - langPath: _url + 'lang/', - theme: 'default', - themePath: _url + 'themes/', - allHtmlEnabled: false, - scaleEnabled: false, - tableNativeEditInFF: false, - autoSyncData : true, - fileNameFormat: '{time}{rand:6}' - } -}; - -// core/loadconfig.js -(function(){ - - UE.Editor.prototype.loadServerConfig = function(){ - var me = this; - setTimeout(function(){ - try{ - me.options.imageUrl && me.setOpt('serverUrl', me.options.imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2')); - - var configUrl = me.getActionUrl('config'), - isJsonp = utils.isCrossDomainUrl(configUrl); - - /* 发出ajax请求 */ - me._serverConfigLoaded = false; - - configUrl && UE.ajax.request(configUrl,{ - 'method': 'GET', - 'dataType': isJsonp ? 'jsonp':'', - 'onsuccess':function(r){ - try { - var config = isJsonp ? r:eval("("+r.responseText+")"); - utils.extend(me.options, config); - me.fireEvent('serverConfigLoaded'); - me._serverConfigLoaded = true; - } catch (e) { - showErrorMsg(me.getLang('loadconfigFormatError')); - } - }, - 'onerror':function(){ - showErrorMsg(me.getLang('loadconfigHttpError')); - } - }); - } catch(e){ - showErrorMsg(me.getLang('loadconfigError')); - } - }); - - function showErrorMsg(msg) { - console && console.error(msg); - //me.fireEvent('showMessage', { - // 'title': msg, - // 'type': 'error' - //}); - } - }; - - UE.Editor.prototype.isServerConfigLoaded = function(){ - var me = this; - return me._serverConfigLoaded || false; - }; - - UE.Editor.prototype.afterConfigReady = function(handler){ - if (!handler || !utils.isFunction(handler)) return; - var me = this; - var readyHandler = function(){ - handler.apply(me, arguments); - me.removeListener('serverConfigLoaded', readyHandler); - }; - - if (me.isServerConfigLoaded()) { - handler.call(me, 'serverConfigLoaded'); - } else { - me.addListener('serverConfigLoaded', readyHandler); - } - }; - -})(); - - -// core/ajax.js -/** - * @file - * @module UE.ajax - * @since 1.2.6.1 - */ - -/** - * 提供对ajax请求的支持 - * @module UE.ajax - */ -UE.ajax = function() { - - //创建一个ajaxRequest对象 - var fnStr = 'XMLHttpRequest()'; - try { - new ActiveXObject("Msxml2.XMLHTTP"); - fnStr = 'ActiveXObject(\'Msxml2.XMLHTTP\')'; - } catch (e) { - try { - new ActiveXObject("Microsoft.XMLHTTP"); - fnStr = 'ActiveXObject(\'Microsoft.XMLHTTP\')' - } catch (e) { - } - } - var creatAjaxRequest = new Function('return new ' + fnStr); - - - /** - * 将json参数转化成适合ajax提交的参数列表 - * @param json - */ - function json2str(json) { - var strArr = []; - for (var i in json) { - //忽略默认的几个参数 - if(i=="method" || i=="timeout" || i=="async" || i=="dataType" || i=="callback") continue; - //忽略控制 - if(json[i] == undefined || json[i] == null) continue; - //传递过来的对象和函数不在提交之列 - if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) { - strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) ); - } else if (utils.isArray(json[i])) { - //支持传数组内容 - for(var j = 0; j < json[i].length; j++) { - strArr.push( encodeURIComponent(i) + "[]="+encodeURIComponent(json[i][j]) ); - } - } - } - return strArr.join("&"); - } - - function doAjax(url, ajaxOptions) { - var xhr = creatAjaxRequest(), - //是否超时 - timeIsOut = false, - //默认参数 - defaultAjaxOptions = { - method:"POST", - timeout:5000, - async:true, - data:{},//需要传递对象的话只能覆盖 - onsuccess:function() { - }, - onerror:function() { - } - }; - - if (typeof url === "object") { - ajaxOptions = url; - url = ajaxOptions.url; - } - if (!xhr || !url) return; - var ajaxOpts = ajaxOptions ? utils.extend(defaultAjaxOptions,ajaxOptions) : defaultAjaxOptions; - - var submitStr = json2str(ajaxOpts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing" - //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串 - if (!utils.isEmptyObject(ajaxOpts.data)){ - submitStr += (submitStr? "&":"") + json2str(ajaxOpts.data); - } - //超时检测 - var timerID = setTimeout(function() { - if (xhr.readyState != 4) { - timeIsOut = true; - xhr.abort(); - clearTimeout(timerID); - } - }, ajaxOpts.timeout); - - var method = ajaxOpts.method.toUpperCase(); - var str = url + (url.indexOf("?")==-1?"?":"&") + (method=="POST"?"":submitStr+ "&noCache=" + +new Date); - xhr.open(method, str, ajaxOpts.async); - xhr.onreadystatechange = function() { - if (xhr.readyState == 4) { - if (!timeIsOut && xhr.status == 200) { - ajaxOpts.onsuccess(xhr); - } else { - ajaxOpts.onerror(xhr); - } - } - }; - if (method == "POST") { - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - xhr.send(submitStr); - } else { - xhr.send(null); - } - } - - function doJsonp(url, opts) { - - var successhandler = opts.onsuccess || function(){}, - scr = document.createElement('SCRIPT'), - options = opts || {}, - charset = options['charset'], - callbackField = options['jsonp'] || 'callback', - callbackFnName, - timeOut = options['timeOut'] || 0, - timer, - reg = new RegExp('(\\?|&)' + callbackField + '=([^&]*)'), - matches; - - if (utils.isFunction(successhandler)) { - callbackFnName = 'bd__editor__' + Math.floor(Math.random() * 2147483648).toString(36); - window[callbackFnName] = getCallBack(0); - } else if(utils.isString(successhandler)){ - callbackFnName = successhandler; - } else { - if (matches = reg.exec(url)) { - callbackFnName = matches[2]; - } - } - - url = url.replace(reg, '\x241' + callbackField + '=' + callbackFnName); - - if (url.search(reg) < 0) { - url += (url.indexOf('?') < 0 ? '?' : '&') + callbackField + '=' + callbackFnName; - } - - var queryStr = json2str(opts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing" - //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串 - if (!utils.isEmptyObject(opts.data)){ - queryStr += (queryStr? "&":"") + json2str(opts.data); - } - if (queryStr) { - url = url.replace(/\?/, '?' + queryStr + '&'); - } - - scr.onerror = getCallBack(1); - if( timeOut ){ - timer = setTimeout(getCallBack(1), timeOut); - } - createScriptTag(scr, url, charset); - - function createScriptTag(scr, url, charset) { - scr.setAttribute('type', 'text/javascript'); - scr.setAttribute('defer', 'defer'); - charset && scr.setAttribute('charset', charset); - scr.setAttribute('src', url); - document.getElementsByTagName('head')[0].appendChild(scr); - } - - function getCallBack(onTimeOut){ - return function(){ - try { - if(onTimeOut){ - options.onerror && options.onerror(); - }else{ - try{ - clearTimeout(timer); - successhandler.apply(window, arguments); - } catch (e){} - } - } catch (exception) { - options.onerror && options.onerror.call(window, exception); - } finally { - options.oncomplete && options.oncomplete.apply(window, arguments); - scr.parentNode && scr.parentNode.removeChild(scr); - window[callbackFnName] = null; - try { - delete window[callbackFnName]; - }catch(e){} - } - } - } - } - - return { - /** - * 根据给定的参数项,向指定的url发起一个ajax请求。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求 - * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调 - * @method request - * @param { URLString } url ajax请求的url地址 - * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下: - * @example - * ```javascript - * //向sayhello.php发起一个异步的Ajax GET请求, 请求超时时间为10s, 请求完成后执行相应的回调。 - * UE.ajax.requeset( 'sayhello.php', { - * - * //请求方法。可选值: 'GET', 'POST',默认值是'POST' - * method: 'GET', - * - * //超时时间。 默认为5000, 单位是ms - * timeout: 10000, - * - * //是否是异步请求。 true为异步请求, false为同步请求 - * async: true, - * - * //请求携带的数据。如果请求为GET请求, data会经过stringify后附加到请求url之后。 - * data: { - * name: 'ueditor' - * }, - * - * //请求成功后的回调, 该回调接受当前的XMLHttpRequest对象作为参数。 - * onsuccess: function ( xhr ) { - * console.log( xhr.responseText ); - * }, - * - * //请求失败或者超时后的回调。 - * onerror: function ( xhr ) { - * alert( 'Ajax请求失败' ); - * } - * - * } ); - * ``` - */ - - /** - * 根据给定的参数项发起一个ajax请求, 参数项里必须包含一个url地址。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求 - * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调。 - * @method request - * @warning 如果在参数项里未提供一个key为“url”的地址值,则该请求将直接退出。 - * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下: - * @example - * ```javascript - * - * //向sayhello.php发起一个异步的Ajax POST请求, 请求超时时间为5s, 请求完成后不执行任何回调。 - * UE.ajax.requeset( 'sayhello.php', { - * - * //请求的地址, 该项是必须的。 - * url: 'sayhello.php' - * - * } ); - * ``` - */ - request:function(url, opts) { - if (opts && opts.dataType == 'jsonp') { - doJsonp(url, opts); - } else { - doAjax(url, opts); - } - }, - getJSONP:function(url, data, fn) { - var opts = { - 'data': data, - 'oncomplete': fn - }; - doJsonp(url, opts); - } - }; - - -}(); - - -// core/filterword.js -/** - * UE过滤word的静态方法 - * @file - */ - -/** - * UEditor公用空间,UEditor所有的功能都挂载在该空间下 - * @module UE - */ - - -/** - * 根据传入html字符串过滤word - * @module UE - * @since 1.2.6.1 - * @method filterWord - * @param { String } html html字符串 - * @return { String } 已过滤后的结果字符串 - * @example - * ```javascript - * UE.filterWord(html); - * ``` - */ -var filterWord = UE.filterWord = function () { - - //是否是word过来的内容 - function isWordDocument( str ) { - return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test( str ); - } - //去掉小数 - function transUnit( v ) { - v = v.replace( /[\d.]+\w+/g, function ( m ) { - return utils.transUnitToPx(m); - } ); - return v; - } - - function filterPasteWord( str ) { - return str.replace(/[\t\r\n]+/g,' ') - .replace( //ig, "" ) - //转换图片 - .replace(/]*>[\s\S]*?.<\/v:shape>/gi,function(str){ - //opera能自己解析出image所这里直接返回空 - if(browser.opera){ - return ''; - } - try{ - //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中 - if(/Bitmap/i.test(str)){ - return ''; - } - var width = str.match(/width:([ \d.]*p[tx])/i)[1], - height = str.match(/height:([ \d.]*p[tx])/i)[1], - src = str.match(/src=\s*"([^"]*)"/i)[1]; - return ''; - } catch(e){ - return ''; - } - }) - //针对wps添加的多余标签处理 - .replace(/<\/?div[^>]*>/g,'') - //去掉多余的属性 - .replace( /v:\w+=(["']?)[^'"]+\1/g, '' ) - .replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" ) - .replace( /

                  ]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "

                  $1

                  " ) - //去掉多余的属性 - .replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){ - //保留list的标示 - return name == 'class' && val == 'MsoListParagraph' ? str : '' - }) - //清除多余的font/span不能匹配 有可能是空格 - .replace( /<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a,b,c){ - return c.replace(/[\t\r\n ]+/g,' ') - }) - //处理style的问题 - .replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) { - var n = [], - s = style.replace( /^\s+|\s+$/, '' ) - .replace(/'/g,'\'') - .replace( /"/gi, "'" ) - .replace(/[\d.]+(cm|pt)/g,function(str){ - return utils.transUnitToPx(str) - }) - .split( /;\s*/g ); - - for ( var i = 0,v; v = s[i];i++ ) { - - var name, value, - parts = v.split( ":" ); - - if ( parts.length == 2 ) { - name = parts[0].toLowerCase(); - value = parts[1].toLowerCase(); - if(/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g,'').length == 0 - || - /^(margin)\w*/.test(name) && /^0\w+$/.test(value) - ){ - continue; - } - - switch ( name ) { - case "mso-padding-alt": - case "mso-padding-top-alt": - case "mso-padding-right-alt": - case "mso-padding-bottom-alt": - case "mso-padding-left-alt": - case "mso-margin-alt": - case "mso-margin-top-alt": - case "mso-margin-right-alt": - case "mso-margin-bottom-alt": - case "mso-margin-left-alt": - //ie下会出现挤到一起的情况 - //case "mso-table-layout-alt": - case "mso-height": - case "mso-width": - case "mso-vertical-align-alt": - //trace:1819 ff下会解析出padding在table上 - if(!/]/.test(html)) { - return UE.htmlparser(html).children[0] - } else { - return new uNode({ - type:'element', - children:[], - tagName:html - }) - } - }; - uNode.createText = function (data,noTrans) { - return new UE.uNode({ - type:'text', - 'data':noTrans ? data : utils.unhtml(data || '') - }) - }; - function nodeToHtml(node, arr, formatter, current) { - switch (node.type) { - case 'root': - for (var i = 0, ci; ci = node.children[i++];) { - //插入新行 - if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) { - insertLine(arr, current, true); - insertIndent(arr, current) - } - nodeToHtml(ci, arr, formatter, current) - } - break; - case 'text': - isText(node, arr); - break; - case 'element': - isElement(node, arr, formatter, current); - break; - case 'comment': - isComment(node, arr, formatter); - } - return arr; - } - - function isText(node, arr) { - if(node.parentNode.tagName == 'pre'){ - //源码模式下输入html标签,不能做转换处理,直接输出 - arr.push(node.data) - }else{ - arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,'  ')) - } - - } - - function isElement(node, arr, formatter, current) { - var attrhtml = ''; - if (node.attrs) { - attrhtml = []; - var attrs = node.attrs; - for (var a in attrs) { - //这里就针对 - //

                  '

                  - //这里边的\"做转换,要不用innerHTML直接被截断了,属性src - //有可能做的不够 - attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) { - return '"' - }) : utils.unhtml(attrs[a])) + '"' : '')) - } - attrhtml = attrhtml.join(' '); - } - arr.push('<' + node.tagName + - (attrhtml ? ' ' + attrhtml : '') + - (dtd.$empty[node.tagName] ? '\/' : '' ) + '>' - ); - //插入新行 - if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') { - if(node.children && node.children.length){ - current = insertLine(arr, current, true); - insertIndent(arr, current) - } - - } - if (node.children && node.children.length) { - for (var i = 0, ci; ci = node.children[i++];) { - if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) { - insertLine(arr, current); - insertIndent(arr, current) - } - nodeToHtml(ci, arr, formatter, current) - } - } - if (!dtd.$empty[node.tagName]) { - if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') { - - if(node.children && node.children.length){ - current = insertLine(arr, current); - insertIndent(arr, current) - } - } - arr.push('<\/' + node.tagName + '>'); - } - - } - - function isComment(node, arr) { - arr.push(''); - } - - function getNodeById(root, id) { - var node; - if (root.type == 'element' && root.getAttr('id') == id) { - return root; - } - if (root.children && root.children.length) { - for (var i = 0, ci; ci = root.children[i++];) { - if (node = getNodeById(ci, id)) { - return node; - } - } - } - } - - function getNodesByTagName(node, tagName, arr) { - if (node.type == 'element' && node.tagName == tagName) { - arr.push(node); - } - if (node.children && node.children.length) { - for (var i = 0, ci; ci = node.children[i++];) { - getNodesByTagName(ci, tagName, arr) - } - } - } - function nodeTraversal(root,fn){ - if(root.children && root.children.length){ - for(var i= 0,ci;ci=root.children[i];){ - nodeTraversal(ci,fn); - //ci被替换的情况,这里就不再走 fn了 - if(ci.parentNode ){ - if(ci.children && ci.children.length){ - fn(ci) - } - if(ci.parentNode) i++ - } - } - }else{ - fn(root) - } - - } - uNode.prototype = { - - /** - * 当前节点对象,转换成html文本 - * @method toHtml - * @return { String } 返回转换后的html字符串 - * @example - * ```javascript - * node.toHtml(); - * ``` - */ - - /** - * 当前节点对象,转换成html文本 - * @method toHtml - * @param { Boolean } formatter 是否格式化返回值 - * @return { String } 返回转换后的html字符串 - * @example - * ```javascript - * node.toHtml( true ); - * ``` - */ - toHtml:function (formatter) { - var arr = []; - nodeToHtml(this, arr, formatter, 0); - return arr.join('') - }, - - /** - * 获取节点的html内容 - * @method innerHTML - * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 - * @return { String } 返回节点的html内容 - * @example - * ```javascript - * var htmlstr = node.innerHTML(); - * ``` - */ - - /** - * 设置节点的html内容 - * @method innerHTML - * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 - * @param { String } htmlstr 传入要设置的html内容 - * @return { UE.uNode } 返回节点本身 - * @example - * ```javascript - * node.innerHTML('text'); - * ``` - */ - innerHTML:function (htmlstr) { - if (this.type != 'element' || dtd.$empty[this.tagName]) { - return this; - } - if (utils.isString(htmlstr)) { - if(this.children){ - for (var i = 0, ci; ci = this.children[i++];) { - ci.parentNode = null; - } - } - this.children = []; - var tmpRoot = UE.htmlparser(htmlstr); - for (var i = 0, ci; ci = tmpRoot.children[i++];) { - this.children.push(ci); - ci.parentNode = this; - } - return this; - } else { - var tmpRoot = new UE.uNode({ - type:'root', - children:this.children - }); - return tmpRoot.toHtml(); - } - }, - - /** - * 获取节点的纯文本内容 - * @method innerText - * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 - * @return { String } 返回节点的存文本内容 - * @example - * ```javascript - * var textStr = node.innerText(); - * ``` - */ - - /** - * 设置节点的纯文本内容 - * @method innerText - * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 - * @param { String } textStr 传入要设置的文本内容 - * @return { UE.uNode } 返回节点本身 - * @example - * ```javascript - * node.innerText('text'); - * ``` - */ - innerText:function (textStr,noTrans) { - if (this.type != 'element' || dtd.$empty[this.tagName]) { - return this; - } - if (textStr) { - if(this.children){ - for (var i = 0, ci; ci = this.children[i++];) { - ci.parentNode = null; - } - } - this.children = []; - this.appendChild(uNode.createText(textStr,noTrans)); - return this; - } else { - return this.toHtml().replace(/<[^>]+>/g, ''); - } - }, - - /** - * 获取当前对象的data属性 - * @method getData - * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性 - * @example - * ```javascript - * node.getData(); - * ``` - */ - getData:function () { - if (this.type == 'element') - return ''; - return this.data - }, - - /** - * 获取当前节点下的第一个子节点 - * @method firstChild - * @return { UE.uNode } 返回第一个子节点 - * @example - * ```javascript - * node.firstChild(); //返回第一个子节点 - * ``` - */ - firstChild:function () { -// if (this.type != 'element' || dtd.$empty[this.tagName]) { -// return this; -// } - return this.children ? this.children[0] : null; - }, - - /** - * 获取当前节点下的最后一个子节点 - * @method lastChild - * @return { UE.uNode } 返回最后一个子节点 - * @example - * ```javascript - * node.lastChild(); //返回最后一个子节点 - * ``` - */ - lastChild:function () { -// if (this.type != 'element' || dtd.$empty[this.tagName] ) { -// return this; -// } - return this.children ? this.children[this.children.length - 1] : null; - }, - - /** - * 获取和当前节点有相同父亲节点的前一个节点 - * @method previousSibling - * @return { UE.uNode } 返回前一个节点 - * @example - * ```javascript - * node.children[2].previousSibling(); //返回子节点node.children[1] - * ``` - */ - previousSibling : function(){ - var parent = this.parentNode; - for (var i = 0, ci; ci = parent.children[i]; i++) { - if (ci === this) { - return i == 0 ? null : parent.children[i-1]; - } - } - - }, - - /** - * 获取和当前节点有相同父亲节点的后一个节点 - * @method nextSibling - * @return { UE.uNode } 返回后一个节点,找不到返回null - * @example - * ```javascript - * node.children[2].nextSibling(); //如果有,返回子节点node.children[3] - * ``` - */ - nextSibling : function(){ - var parent = this.parentNode; - for (var i = 0, ci; ci = parent.children[i++];) { - if (ci === this) { - return parent.children[i]; - } - } - }, - - /** - * 用新的节点替换当前节点 - * @method replaceChild - * @param { UE.uNode } target 要替换成该节点参数 - * @param { UE.uNode } source 要被替换掉的节点 - * @return { UE.uNode } 返回替换之后的节点对象 - * @example - * ```javascript - * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点 - * ``` - */ - replaceChild:function (target, source) { - if (this.children) { - if(target.parentNode){ - target.parentNode.removeChild(target); - } - for (var i = 0, ci; ci = this.children[i]; i++) { - if (ci === source) { - this.children.splice(i, 1, target); - source.parentNode = null; - target.parentNode = this; - return target; - } - } - } - }, - - /** - * 在节点的子节点列表最后位置插入一个节点 - * @method appendChild - * @param { UE.uNode } node 要插入的节点 - * @return { UE.uNode } 返回刚插入的子节点 - * @example - * ```javascript - * node.appendChild( newNode ); //在node内插入子节点newNode - * ``` - */ - appendChild:function (node) { - if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) { - if (!this.children) { - this.children = [] - } - if(node.parentNode){ - node.parentNode.removeChild(node); - } - for (var i = 0, ci; ci = this.children[i]; i++) { - if (ci === node) { - this.children.splice(i, 1); - break; - } - } - this.children.push(node); - node.parentNode = this; - return node; - } - - - }, - - /** - * 在传入节点的前面插入一个节点 - * @method insertBefore - * @param { UE.uNode } target 要插入的节点 - * @param { UE.uNode } source 在该参数节点前面插入 - * @return { UE.uNode } 返回刚插入的子节点 - * @example - * ```javascript - * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode - * ``` - */ - insertBefore:function (target, source) { - if (this.children) { - if(target.parentNode){ - target.parentNode.removeChild(target); - } - for (var i = 0, ci; ci = this.children[i]; i++) { - if (ci === source) { - this.children.splice(i, 0, target); - target.parentNode = this; - return target; - } - } - - } - }, - - /** - * 在传入节点的后面插入一个节点 - * @method insertAfter - * @param { UE.uNode } target 要插入的节点 - * @param { UE.uNode } source 在该参数节点后面插入 - * @return { UE.uNode } 返回刚插入的子节点 - * @example - * ```javascript - * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode - * ``` - */ - insertAfter:function (target, source) { - if (this.children) { - if(target.parentNode){ - target.parentNode.removeChild(target); - } - for (var i = 0, ci; ci = this.children[i]; i++) { - if (ci === source) { - this.children.splice(i + 1, 0, target); - target.parentNode = this; - return target; - } - - } - } - }, - - /** - * 从当前节点的子节点列表中,移除节点 - * @method removeChild - * @param { UE.uNode } node 要移除的节点引用 - * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置 - * @return { * } 返回刚移除的子节点 - * @example - * ```javascript - * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置 - * ``` - */ - removeChild:function (node,keepChildren) { - if (this.children) { - for (var i = 0, ci; ci = this.children[i]; i++) { - if (ci === node) { - this.children.splice(i, 1); - ci.parentNode = null; - if(keepChildren && ci.children && ci.children.length){ - for(var j= 0,cj;cj=ci.children[j];j++){ - this.children.splice(i+j,0,cj); - cj.parentNode = this; - - } - } - return ci; - } - } - } - }, - - /** - * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值 - * @method getAttr - * @param { String } attrName 要获取的属性名称 - * @return { * } 返回attrs对象下的属性值 - * @example - * ```javascript - * node.getAttr('title'); - * ``` - */ - getAttr:function (attrName) { - return this.attrs && this.attrs[attrName.toLowerCase()] - }, - - /** - * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值 - * @method setAttr - * @param { String } attrName 要设置的属性名称 - * @param { * } attrVal 要设置的属性值,类型视设置的属性而定 - * @return { * } 返回attrs对象下的属性值 - * @example - * ```javascript - * node.setAttr('title','标题'); - * ``` - */ - setAttr:function (attrName, attrVal) { - if (!attrName) { - delete this.attrs; - return; - } - if(!this.attrs){ - this.attrs = {}; - } - if (utils.isObject(attrName)) { - for (var a in attrName) { - if (!attrName[a]) { - delete this.attrs[a] - } else { - this.attrs[a.toLowerCase()] = attrName[a]; - } - } - } else { - if (!attrVal) { - delete this.attrs[attrName] - } else { - this.attrs[attrName.toLowerCase()] = attrVal; - } - - } - }, - - /** - * 获取当前节点在父节点下的位置索引 - * @method getIndex - * @return { Number } 返回索引数值,如果没有父节点,返回-1 - * @example - * ```javascript - * node.getIndex(); - * ``` - */ - getIndex:function(){ - var parent = this.parentNode; - for(var i= 0,ci;ci=parent.children[i];i++){ - if(ci === this){ - return i; - } - } - return -1; - }, - - /** - * 在当前节点下,根据id查找节点 - * @method getNodeById - * @param { String } id 要查找的id - * @return { UE.uNode } 返回找到的节点 - * @example - * ```javascript - * node.getNodeById('textId'); - * ``` - */ - getNodeById:function (id) { - var node; - if (this.children && this.children.length) { - for (var i = 0, ci; ci = this.children[i++];) { - if (node = getNodeById(ci, id)) { - return node; - } - } - } - }, - - /** - * 在当前节点下,根据元素名称查找节点列表 - * @method getNodesByTagName - * @param { String } tagNames 要查找的元素名称 - * @return { Array } 返回找到的节点列表 - * @example - * ```javascript - * node.getNodesByTagName('span'); - * ``` - */ - getNodesByTagName:function (tagNames) { - tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' '); - var arr = [], me = this; - utils.each(tagNames, function (tagName) { - if (me.children && me.children.length) { - for (var i = 0, ci; ci = me.children[i++];) { - getNodesByTagName(ci, tagName, arr) - } - } - }); - return arr; - }, - - /** - * 根据样式名称,获取节点的样式值 - * @method getStyle - * @param { String } name 要获取的样式名称 - * @return { String } 返回样式值 - * @example - * ```javascript - * node.getStyle('font-size'); - * ``` - */ - getStyle:function (name) { - var cssStyle = this.getAttr('style'); - if (!cssStyle) { - return '' - } - var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i'); - var match = cssStyle.match(reg); - if (match && match[0]) { - return match[2] - } - return ''; - }, - - /** - * 给节点设置样式 - * @method setStyle - * @param { String } name 要设置的的样式名称 - * @param { String } val 要设置的的样值 - * @example - * ```javascript - * node.setStyle('font-size', '12px'); - * ``` - */ - setStyle:function (name, val) { - function exec(name, val) { - var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi'); - cssStyle = cssStyle.replace(reg, '$1'); - if (val) { - cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle - } - - } - - var cssStyle = this.getAttr('style'); - if (!cssStyle) { - cssStyle = ''; - } - if (utils.isObject(name)) { - for (var a in name) { - exec(a, name[a]) - } - } else { - exec(name, val) - } - this.setAttr('style', utils.trim(cssStyle)) - }, - - /** - * 传入一个函数,递归遍历当前节点下的所有节点 - * @method traversal - * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数 - * @example - * ```javascript - * traversal(node, function(){ - * console.log(node.type); - * }); - * ``` - */ - traversal:function(fn){ - if(this.children && this.children.length){ - nodeTraversal(this,fn); - } - return this; - } - } -})(); - - -// core/htmlparser.js -/** - * html字符串转换成uNode节点 - * @file - * @module UE - * @since 1.2.6.1 - */ - -/** - * UEditor公用空间,UEditor所有的功能都挂载在该空间下 - * @unfile - * @module UE - */ - -/** - * html字符串转换成uNode节点的静态方法 - * @method htmlparser - * @param { String } htmlstr 要转换的html代码 - * @param { Boolean } ignoreBlank 若设置为true,转换的时候忽略\n\r\t等空白字符 - * @return { uNode } 给定的html片段转换形成的uNode对象 - * @example - * ```javascript - * var root = UE.htmlparser('

                  htmlparser

                  ', true); - * ``` - */ - -var htmlparser = UE.htmlparser = function (htmlstr,ignoreBlank) { - //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 ') - } - html.push('') - } - //禁止指定table-width - return '
                  这样的标签了 - //先去掉了,加上的原因忘了,这里先记录 - var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g, - re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g; - - //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除 - var allowEmptyTags = { - b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1, - sub:1,img:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1 - }; - htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), ''); - if(!ignoreBlank){ - htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n'+(ignoreBlank?'':' ')+']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n'+(ignoreBlank?'':' ')+']*','g'), function(a,b){ - //br暂时单独处理 - if(b && allowEmptyTags[b.toLowerCase()]){ - return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,''); - } - return a.replace(new RegExp('^[\\r\\n'+(ignoreBlank?'':' ')+']+'),'').replace(new RegExp('[\\r\\n'+(ignoreBlank?'':' ')+']+$'),''); - }); - } - - var notTransAttrs = { - 'href':1, - 'src':1 - }; - - var uNode = UE.uNode, - needParentNode = { - 'td':'tr', - 'tr':['tbody','thead','tfoot'], - 'tbody':'table', - 'th':'tr', - 'thead':'table', - 'tfoot':'table', - 'caption':'table', - 'li':['ul', 'ol'], - 'dt':'dl', - 'dd':'dl', - 'option':'select' - }, - needChild = { - 'ol':'li', - 'ul':'li' - }; - - function text(parent, data) { - - if(needChild[parent.tagName]){ - var tmpNode = uNode.createElement(needChild[parent.tagName]); - parent.appendChild(tmpNode); - tmpNode.appendChild(uNode.createText(data)); - parent = tmpNode; - }else{ - - parent.appendChild(uNode.createText(data)); - } - } - - function element(parent, tagName, htmlattr) { - var needParentTag; - if (needParentTag = needParentNode[tagName]) { - var tmpParent = parent,hasParent; - while(tmpParent.type != 'root'){ - if(utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName){ - parent = tmpParent; - hasParent = true; - break; - } - tmpParent = tmpParent.parentNode; - } - if(!hasParent){ - parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag) - } - } - //按dtd处理嵌套 -// if(parent.type != 'root' && !dtd[parent.tagName][tagName]) -// parent = parent.parentNode; - var elm = new uNode({ - parentNode:parent, - type:'element', - tagName:tagName.toLowerCase(), - //是自闭合的处理一下 - children:dtd.$empty[tagName] ? null : [] - }); - //如果属性存在,处理属性 - if (htmlattr) { - var attrs = {}, match; - while (match = re_attr.exec(htmlattr)) { - attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4]) - } - elm.attrs = attrs; - } - //trace:3970 -// //如果parent下不能放elm -// if(dtd.$inline[parent.tagName] && dtd.$block[elm.tagName] && !dtd[parent.tagName][elm.tagName]){ -// parent = parent.parentNode; -// elm.parentNode = parent; -// } - parent.children.push(elm); - //如果是自闭合节点返回父亲节点 - return dtd.$empty[tagName] ? parent : elm - } - - function comment(parent, data) { - parent.children.push(new uNode({ - type:'comment', - data:data, - parentNode:parent - })); - } - - var match, currentIndex = 0, nextIndex = 0; - //设置根节点 - var root = new uNode({ - type:'root', - children:[] - }); - var currentParent = root; - - while (match = re_tag.exec(htmlstr)) { - currentIndex = match.index; - try{ - if (currentIndex > nextIndex) { - //text node - text(currentParent, htmlstr.slice(nextIndex, currentIndex)); - } - if (match[3]) { - - if(dtd.$cdata[currentParent.tagName]){ - text(currentParent, match[0]); - }else{ - //start tag - currentParent = element(currentParent, match[3].toLowerCase(), match[4]); - } - - - } else if (match[1]) { - if(currentParent.type != 'root'){ - if(dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]){ - text(currentParent, match[0]); - }else{ - var tmpParent = currentParent; - while(currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()){ - currentParent = currentParent.parentNode; - if(currentParent.type == 'root'){ - currentParent = tmpParent; - throw 'break' - } - } - //end tag - currentParent = currentParent.parentNode; - } - - } - - } else if (match[2]) { - //comment - comment(currentParent, match[2]) - } - }catch(e){} - - nextIndex = re_tag.lastIndex; - - } - //如果结束是文本,就有可能丢掉,所以这里手动判断一下 - //例如
                • sdfsdfsdf
                • sdfsdfsdfsdf - if (nextIndex < htmlstr.length) { - text(currentParent, htmlstr.slice(nextIndex)); - } - return root; -}; - - -// core/filternode.js -/** - * UE过滤节点的静态方法 - * @file - */ - -/** - * UEditor公用空间,UEditor所有的功能都挂载在该空间下 - * @module UE - */ - - -/** - * 根据传入节点和过滤规则过滤相应节点 - * @module UE - * @since 1.2.6.1 - * @method filterNode - * @param { Object } root 指定root节点 - * @param { Object } rules 过滤规则json对象 - * @example - * ```javascript - * UE.filterNode(root,editor.options.filterRules); - * ``` - */ -var filterNode = UE.filterNode = function () { - function filterNode(node,rules){ - switch (node.type) { - case 'text': - break; - case 'element': - var val; - if(val = rules[node.tagName]){ - if(val === '-'){ - node.parentNode.removeChild(node) - }else if(utils.isFunction(val)){ - var parentNode = node.parentNode, - index = node.getIndex(); - val(node); - if(node.parentNode){ - if(node.children){ - for(var i = 0,ci;ci=node.children[i];){ - filterNode(ci,rules); - if(ci.parentNode){ - i++; - } - } - } - }else{ - for(var i = index,ci;ci=parentNode.children[i];){ - filterNode(ci,rules); - if(ci.parentNode){ - i++; - } - } - } - - - }else{ - var attrs = val['$']; - if(attrs && node.attrs){ - var tmpAttrs = {},tmpVal; - for(var a in attrs){ - tmpVal = node.getAttr(a); - //todo 只先对style单独处理 - if(a == 'style' && utils.isArray(attrs[a])){ - var tmpCssStyle = []; - utils.each(attrs[a],function(v){ - var tmp; - if(tmp = node.getStyle(v)){ - tmpCssStyle.push(v + ':' + tmp); - } - }); - tmpVal = tmpCssStyle.join(';') - } - if(tmpVal){ - tmpAttrs[a] = tmpVal; - } - - } - node.attrs = tmpAttrs; - } - if(node.children){ - for(var i = 0,ci;ci=node.children[i];){ - filterNode(ci,rules); - if(ci.parentNode){ - i++; - } - } - } - } - }else{ - //如果不在名单里扣出子节点并删除该节点,cdata除外 - if(dtd.$cdata[node.tagName]){ - node.parentNode.removeChild(node) - }else{ - var parentNode = node.parentNode, - index = node.getIndex(); - node.parentNode.removeChild(node,true); - for(var i = index,ci;ci=parentNode.children[i];){ - filterNode(ci,rules); - if(ci.parentNode){ - i++; - } - } - } - } - break; - case 'comment': - node.parentNode.removeChild(node) - } - - } - return function(root,rules){ - if(utils.isEmptyObject(rules)){ - return root; - } - var val; - if(val = rules['-']){ - utils.each(val.split(' '),function(k){ - rules[k] = '-' - }) - } - for(var i= 0,ci;ci=root.children[i];){ - filterNode(ci,rules); - if(ci.parentNode){ - i++; - } - } - return root; - } -}(); - -// core/plugin.js -/** - * Created with JetBrains PhpStorm. - * User: campaign - * Date: 10/8/13 - * Time: 6:15 PM - * To change this template use File | Settings | File Templates. - */ -UE.plugin = function(){ - var _plugins = {}; - return { - register : function(pluginName,fn,oldOptionName,afterDisabled){ - if(oldOptionName && utils.isFunction(oldOptionName)){ - afterDisabled = oldOptionName; - oldOptionName = null - } - _plugins[pluginName] = { - optionName : oldOptionName || pluginName, - execFn : fn, - //当插件被禁用时执行 - afterDisabled : afterDisabled - } - }, - load : function(editor){ - utils.each(_plugins,function(plugin){ - var _export = plugin.execFn.call(editor); - if(editor.options[plugin.optionName] !== false){ - if(_export){ - //后边需要再做扩展 - utils.each(_export,function(v,k){ - switch(k.toLowerCase()){ - case 'shortcutkey': - editor.addshortcutkey(v); - break; - case 'bindevents': - utils.each(v,function(fn,eventName){ - editor.addListener(eventName,fn); - }); - break; - case 'bindmultievents': - utils.each(utils.isArray(v) ? v:[v],function(event){ - var types = utils.trim(event.type).split(/\s+/); - utils.each(types,function(eventName){ - editor.addListener(eventName, event.handler); - }); - }); - break; - case 'commands': - utils.each(v,function(execFn,execName){ - editor.commands[execName] = execFn - }); - break; - case 'outputrule': - editor.addOutputRule(v); - break; - case 'inputrule': - editor.addInputRule(v); - break; - case 'defaultoptions': - editor.setOpt(v) - } - }) - } - - }else if(plugin.afterDisabled){ - plugin.afterDisabled.call(editor) - } - - }); - //向下兼容 - utils.each(UE.plugins,function(plugin){ - plugin.call(editor); - }); - }, - run : function(pluginName,editor){ - var plugin = _plugins[pluginName]; - if(plugin){ - plugin.exeFn.call(editor) - } - } - } -}(); - -// core/keymap.js -var keymap = UE.keymap = { - 'Backspace' : 8, - 'Tab' : 9, - 'Enter' : 13, - - 'Shift':16, - 'Control':17, - 'Alt':18, - 'CapsLock':20, - - 'Esc':27, - - 'Spacebar':32, - - 'PageUp':33, - 'PageDown':34, - 'End':35, - 'Home':36, - - 'Left':37, - 'Up':38, - 'Right':39, - 'Down':40, - - 'Insert':45, - - 'Del':46, - - 'NumLock':144, - - 'Cmd':91, - - '=':187, - '-':189, - - "b":66, - 'i':73, - //回退 - 'z':90, - 'y':89, - //粘贴 - 'v' : 86, - 'x' : 88, - - 's' : 83, - - 'n' : 78 -}; - -// core/localstorage.js -//存储媒介封装 -var LocalStorage = UE.LocalStorage = (function () { - - var storage = window.localStorage || getUserData() || null, - LOCAL_FILE = 'localStorage'; - - return { - - saveLocalData: function (key, data) { - - if (storage && data) { - storage.setItem(key, data); - return true; - } - - return false; - - }, - - getLocalData: function (key) { - - if (storage) { - return storage.getItem(key); - } - - return null; - - }, - - removeItem: function (key) { - - storage && storage.removeItem(key); - - } - - }; - - function getUserData() { - - var container = document.createElement("div"); - container.style.display = "none"; - - if (!container.addBehavior) { - return null; - } - - container.addBehavior("#default#userdata"); - - return { - - getItem: function (key) { - - var result = null; - - try { - document.body.appendChild(container); - container.load(LOCAL_FILE); - result = container.getAttribute(key); - document.body.removeChild(container); - } catch (e) { - } - - return result; - - }, - - setItem: function (key, value) { - - document.body.appendChild(container); - container.setAttribute(key, value); - container.save(LOCAL_FILE); - document.body.removeChild(container); - - }, - - //// 暂时没有用到 - //clear: function () { - // - // var expiresTime = new Date(); - // expiresTime.setFullYear(expiresTime.getFullYear() - 1); - // document.body.appendChild(container); - // container.expires = expiresTime.toUTCString(); - // container.save(LOCAL_FILE); - // document.body.removeChild(container); - // - //}, - - removeItem: function (key) { - - document.body.appendChild(container); - container.removeAttribute(key); - container.save(LOCAL_FILE); - document.body.removeChild(container); - - } - - }; - - } - -})(); - -(function () { - - var ROOTKEY = 'ueditor_preference'; - - UE.Editor.prototype.setPreferences = function(key,value){ - var obj = {}; - if (utils.isString(key)) { - obj[ key ] = value; - } else { - obj = key; - } - var data = LocalStorage.getLocalData(ROOTKEY); - if (data && (data = utils.str2json(data))) { - utils.extend(data, obj); - } else { - data = obj; - } - data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data)); - }; - - UE.Editor.prototype.getPreferences = function(key){ - var data = LocalStorage.getLocalData(ROOTKEY); - if (data && (data = utils.str2json(data))) { - return key ? data[key] : data - } - return null; - }; - - UE.Editor.prototype.removePreferences = function (key) { - var data = LocalStorage.getLocalData(ROOTKEY); - if (data && (data = utils.str2json(data))) { - data[key] = undefined; - delete data[key] - } - data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data)); - }; - -})(); - - -// plugins/defaultfilter.js -///import core -///plugin 编辑器默认的过滤转换机制 - -UE.plugins['defaultfilter'] = function () { - var me = this; - me.setOpt({ - 'allowDivTransToP':true, - 'disabledTableInTable':true - }); - //默认的过滤处理 - //进入编辑器的内容处理 - me.addInputRule(function (root) { - var allowDivTransToP = this.options.allowDivTransToP; - var val; - function tdParent(node){ - while(node && node.type == 'element'){ - if(node.tagName == 'td'){ - return true; - } - node = node.parentNode; - } - return false; - } - //进行默认的处理 - root.traversal(function (node) { - if (node.type == 'element') { - if (!dtd.$cdata[node.tagName] && me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) { - if (!node.firstChild()) node.parentNode.removeChild(node); - else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) { - node.parentNode.removeChild(node, true) - } - return; - } - switch (node.tagName) { - case 'style': - case 'script': - node.setAttr({ - cdata_tag: node.tagName, - cdata_data: (node.innerHTML() || ''), - '_ue_custom_node_':'true' - }); - node.tagName = 'div'; - node.innerHTML(''); - break; - case 'a': - if (val = node.getAttr('href')) { - node.setAttr('_href', val) - } - break; - case 'img': - //todo base64暂时去掉,后边做远程图片上传后,干掉这个 - if (val = node.getAttr('src')) { - if (/^data:/.test(val)) { - node.parentNode.removeChild(node); - break; - } - } - node.setAttr('_src', node.getAttr('src')); - break; - case 'span': - if (browser.webkit && (val = node.getStyle('white-space'))) { - if (/nowrap|normal/.test(val)) { - node.setStyle('white-space', ''); - if (me.options.autoClearEmptyNode && utils.isEmptyObject(node.attrs)) { - node.parentNode.removeChild(node, true) - } - } - } - val = node.getAttr('id'); - if(val && /^_baidu_bookmark_/i.test(val)){ - node.parentNode.removeChild(node) - } - break; - case 'p': - if (val = node.getAttr('align')) { - node.setAttr('align'); - node.setStyle('text-align', val) - } - //trace:3431 -// var cssStyle = node.getAttr('style'); -// if (cssStyle) { -// cssStyle = cssStyle.replace(/(margin|padding)[^;]+/g, ''); -// node.setAttr('style', cssStyle) -// -// } - //p标签不允许嵌套 - utils.each(node.children,function(n){ - if(n.type == 'element' && n.tagName == 'p'){ - var next = n.nextSibling(); - node.parentNode.insertAfter(n,node); - var last = n; - while(next){ - var tmp = next.nextSibling(); - node.parentNode.insertAfter(next,last); - last = next; - next = tmp; - } - return false; - } - }); - if (!node.firstChild()) { - node.innerHTML(browser.ie ? ' ' : '
                  ') - } - break; - case 'div': - if(node.getAttr('cdata_tag')){ - break; - } - //针对代码这里不处理插入代码的div - val = node.getAttr('class'); - if(val && /^line number\d+/.test(val)){ - break; - } - if(!allowDivTransToP){ - break; - } - var tmpNode, p = UE.uNode.createElement('p'); - while (tmpNode = node.firstChild()) { - if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) { - p.appendChild(tmpNode); - } else { - if (p.firstChild()) { - node.parentNode.insertBefore(p, node); - p = UE.uNode.createElement('p'); - } else { - node.parentNode.insertBefore(tmpNode, node); - } - } - } - if (p.firstChild()) { - node.parentNode.insertBefore(p, node); - } - node.parentNode.removeChild(node); - break; - case 'dl': - node.tagName = 'ul'; - break; - case 'dt': - case 'dd': - node.tagName = 'li'; - break; - case 'li': - var className = node.getAttr('class'); - if (!className || !/list\-/.test(className)) { - node.setAttr() - } - var tmpNodes = node.getNodesByTagName('ol ul'); - UE.utils.each(tmpNodes, function (n) { - node.parentNode.insertAfter(n, node); - }); - break; - case 'td': - case 'th': - case 'caption': - if(!node.children || !node.children.length){ - node.appendChild(browser.ie11below ? UE.uNode.createText(' ') : UE.uNode.createElement('br')) - } - break; - case 'table': - if(me.options.disabledTableInTable && tdParent(node)){ - node.parentNode.insertBefore(UE.uNode.createText(node.innerText()),node); - node.parentNode.removeChild(node) - } - } - - } -// if(node.type == 'comment'){ -// node.parentNode.removeChild(node); -// } - }) - - }); - - //从编辑器出去的内容处理 - me.addOutputRule(function (root) { - - var val; - root.traversal(function (node) { - if (node.type == 'element') { - - if (me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) { - - if (!node.firstChild()) node.parentNode.removeChild(node); - else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) { - node.parentNode.removeChild(node, true) - } - return; - } - switch (node.tagName) { - case 'div': - if (val = node.getAttr('cdata_tag')) { - node.tagName = val; - node.appendChild(UE.uNode.createText(node.getAttr('cdata_data'))); - node.setAttr({cdata_tag: '', cdata_data: '','_ue_custom_node_':''}); - } - break; - case 'a': - if (val = node.getAttr('_href')) { - node.setAttr({ - 'href': utils.html(val), - '_href': '' - }) - } - break; - break; - case 'span': - val = node.getAttr('id'); - if(val && /^_baidu_bookmark_/i.test(val)){ - node.parentNode.removeChild(node) - } - break; - case 'img': - if (val = node.getAttr('_src')) { - node.setAttr({ - 'src': node.getAttr('_src'), - '_src': '' - }) - } - - - } - } - - }) - - - }); -}; - - -// plugins/inserthtml.js -/** - * 插入html字符串插件 - * @file - * @since 1.2.6.1 - */ - -/** - * 插入html代码 - * @command inserthtml - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } html 插入的html字符串 - * @remaind 插入的标签内容是在当前的选区位置上插入,如果当前是闭合状态,那直接插入内容, 如果当前是选中状态,将先清除当前选中内容后,再做插入 - * @warning 注意:该命令会对当前选区的位置,对插入的内容进行过滤转换处理。 过滤的规则遵循html语意化的原则。 - * @example - * ```javascript - * //xxx[BB]xxx 当前选区为非闭合选区,选中BB这两个文本 - * //执行命令,插入CC - * //插入后的效果 xxxCCxxx - * //

                  xx|xxx

                  当前选区为闭合状态 - * //插入

                  CC

                  - * //结果

                  xx

                  CC

                  xxx

                  - * //

                  xxxx

                  |

                  xxx

                  当前选区在两个p标签之间 - * //插入 xxxx - * //结果

                  xxxx

                  xxxx

                  xxx

                  - * ``` - */ - -UE.commands['inserthtml'] = { - execCommand: function (command,html,notNeedFilter){ - var me = this, - range, - div; - if(!html){ - return; - } - if(me.fireEvent('beforeinserthtml',html) === true){ - return; - } - range = me.selection.getRange(); - div = range.document.createElement( 'div' ); - div.style.display = 'inline'; - - if (!notNeedFilter) { - var root = UE.htmlparser(html); - //如果给了过滤规则就先进行过滤 - if(me.options.filterRules){ - UE.filterNode(root,me.options.filterRules); - } - //执行默认的处理 - me.filterInputRule(root); - html = root.toHtml() - } - div.innerHTML = utils.trim( html ); - - if ( !range.collapsed ) { - var tmpNode = range.startContainer; - if(domUtils.isFillChar(tmpNode)){ - range.setStartBefore(tmpNode) - } - tmpNode = range.endContainer; - if(domUtils.isFillChar(tmpNode)){ - range.setEndAfter(tmpNode) - } - range.txtToElmBoundary(); - //结束边界可能放到了br的前边,要把br包含进来 - // x[xxx]
                  - if(range.endContainer && range.endContainer.nodeType == 1){ - tmpNode = range.endContainer.childNodes[range.endOffset]; - if(tmpNode && domUtils.isBr(tmpNode)){ - range.setEndAfter(tmpNode); - } - } - if(range.startOffset == 0){ - tmpNode = range.startContainer; - if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){ - tmpNode = range.endContainer; - if(range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){ - me.body.innerHTML = '

                  '+(browser.ie ? '' : '
                  ')+'

                  '; - range.setStart(me.body.firstChild,0).collapse(true) - - } - } - } - !range.collapsed && range.deleteContents(); - if(range.startContainer.nodeType == 1){ - var child = range.startContainer.childNodes[range.startOffset],pre; - if(child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)){ - range.setEnd(pre,pre.childNodes.length).collapse(); - while(child.firstChild){ - pre.appendChild(child.firstChild); - } - domUtils.remove(child); - } - } - - } - - - var child,parent,pre,tmp,hadBreak = 0, nextNode; - //如果当前位置选中了fillchar要干掉,要不会产生空行 - if(range.inFillChar()){ - child = range.startContainer; - if(domUtils.isFillChar(child)){ - range.setStartBefore(child).collapse(true); - domUtils.remove(child); - }else if(domUtils.isFillChar(child,true)){ - child.nodeValue = child.nodeValue.replace(fillCharReg,''); - range.startOffset--; - range.collapsed && range.collapse(true) - } - } - //列表单独处理 - var li = domUtils.findParentByTagName(range.startContainer,'li',true); - if(li){ - var next,last; - while(child = div.firstChild){ - //针对hr单独处理一下先 - while(child && (child.nodeType == 3 || !domUtils.isBlockElm(child) || child.tagName=='HR' )){ - next = child.nextSibling; - range.insertNode( child).collapse(); - last = child; - child = next; - - } - if(child){ - if(/^(ol|ul)$/i.test(child.tagName)){ - while(child.firstChild){ - last = child.firstChild; - domUtils.insertAfter(li,child.firstChild); - li = li.nextSibling; - } - domUtils.remove(child) - }else{ - var tmpLi; - next = child.nextSibling; - tmpLi = me.document.createElement('li'); - domUtils.insertAfter(li,tmpLi); - tmpLi.appendChild(child); - last = child; - child = next; - li = tmpLi; - } - } - } - li = domUtils.findParentByTagName(range.startContainer,'li',true); - if(domUtils.isEmptyBlock(li)){ - domUtils.remove(li) - } - if(last){ - - range.setStartAfter(last).collapse(true).select(true) - } - }else{ - while ( child = div.firstChild ) { - if(hadBreak){ - var p = me.document.createElement('p'); - while(child && (child.nodeType == 3 || !dtd.$block[child.tagName])){ - nextNode = child.nextSibling; - p.appendChild(child); - child = nextNode; - } - if(p.firstChild){ - - child = p - } - } - range.insertNode( child ); - nextNode = child.nextSibling; - if ( !hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm( child ) ){ - - parent = domUtils.findParent( child,function ( node ){ return domUtils.isBlockElm( node ); } ); - if ( parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)){ - if(!dtd[parent.tagName][child.nodeName]){ - pre = parent; - }else{ - tmp = child.parentNode; - while (tmp !== parent){ - pre = tmp; - tmp = tmp.parentNode; - - } - } - - - domUtils.breakParent( child, pre || tmp ); - //去掉break后前一个多余的节点

                  |<[p> ==>

                  |

                  - var pre = child.previousSibling; - domUtils.trimWhiteTextNode(pre); - if(!pre.childNodes.length){ - domUtils.remove(pre); - } - //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位 - - if(!browser.ie && - (next = child.nextSibling) && - domUtils.isBlockElm(next) && - next.lastChild && - !domUtils.isBr(next.lastChild)){ - next.appendChild(me.document.createElement('br')); - } - hadBreak = 1; - } - } - var next = child.nextSibling; - if(!div.firstChild && next && domUtils.isBlockElm(next)){ - - range.setStart(next,0).collapse(true); - break; - } - range.setEndAfter( child ).collapse(); - - } - - child = range.startContainer; - - if(nextNode && domUtils.isBr(nextNode)){ - domUtils.remove(nextNode) - } - //用chrome可能有空白展位符 - if(domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)){ - if(nextNode = child.nextSibling){ - domUtils.remove(child); - if(nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]){ - - range.setStart(nextNode,0).collapse(true).shrinkBoundary() - } - }else{ - - try{ - child.innerHTML = browser.ie ? domUtils.fillChar : '
                  '; - }catch(e){ - range.setStartBefore(child); - domUtils.remove(child) - } - - } - - } - //加上true因为在删除表情等时会删两次,第一次是删的fillData - try{ - range.select(true); - }catch(e){} - - } - - - - setTimeout(function(){ - range = me.selection.getRange(); - range.scrollToView(me.autoHeightEnabled,me.autoHeightEnabled ? domUtils.getXY(me.iframe).y:0); - me.fireEvent('afterinserthtml', html); - },200); - } -}; - - -// plugins/autotypeset.js -/** - * 自动排版 - * @file - * @since 1.2.6.1 - */ - -/** - * 对当前编辑器的内容执行自动排版, 排版的行为根据config配置文件里的“autotypeset”选项进行控制。 - * @command autotypeset - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'autotypeset' ); - * ``` - */ - -UE.plugins['autotypeset'] = function(){ - - this.setOpt({'autotypeset': { - mergeEmptyline: true, //合并空行 - removeClass: true, //去掉冗余的class - removeEmptyline: false, //去掉空行 - textAlign:"left", //段落的排版方式,可以是 left,right,center,justify 去掉这个属性表示不执行排版 - imageBlockLine: 'center', //图片的浮动方式,独占一行剧中,左右浮动,默认: center,left,right,none 去掉这个属性表示不执行排版 - pasteFilter: false, //根据规则过滤没事粘贴进来的内容 - clearFontSize: false, //去掉所有的内嵌字号,使用编辑器默认的字号 - clearFontFamily: false, //去掉所有的内嵌字体,使用编辑器默认的字体 - removeEmptyNode: false, // 去掉空节点 - //可以去掉的标签 - removeTagNames: utils.extend({div:1},dtd.$removeEmpty), - indent: false, // 行首缩进 - indentValue : '2em', //行首缩进的大小 - bdc2sb: false, - tobdc: false - }}); - - var me = this, - opt = me.options.autotypeset, - remainClass = { - 'selectTdClass':1, - 'pagebreak':1, - 'anchorclass':1 - }, - remainTag = { - 'li':1 - }, - tags = { - div:1, - p:1, - //trace:2183 这些也认为是行 - blockquote:1,center:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1, - span:1 - }, - highlightCont; - //升级了版本,但配置项目里没有autotypeset - if(!opt){ - return; - } - - readLocalOpts(); - - function isLine(node,notEmpty){ - if(!node || node.nodeType == 3) - return 0; - if(domUtils.isBr(node)) - return 1; - if(node && node.parentNode && tags[node.tagName.toLowerCase()]){ - if(highlightCont && highlightCont.contains(node) - || - node.getAttribute('pagebreak') - ){ - return 0; - } - - return notEmpty ? !domUtils.isEmptyBlock(node) : domUtils.isEmptyBlock(node,new RegExp('[\\s'+domUtils.fillChar - +']','g')); - } - } - - function removeNotAttributeSpan(node){ - if(!node.style.cssText){ - domUtils.removeAttributes(node,['style']); - if(node.tagName.toLowerCase() == 'span' && domUtils.hasNoAttributes(node)){ - domUtils.remove(node,true); - } - } - } - function autotype(type,html){ - - var me = this,cont; - if(html){ - if(!opt.pasteFilter){ - return; - } - cont = me.document.createElement('div'); - cont.innerHTML = html.html; - }else{ - cont = me.document.body; - } - var nodes = domUtils.getElementsByTagName(cont,'*'); - - // 行首缩进,段落方向,段间距,段内间距 - for(var i=0,ci;ci=nodes[i++];){ - - if(me.fireEvent('excludeNodeinautotype',ci) === true){ - continue; - } - //font-size - if(opt.clearFontSize && ci.style.fontSize){ - domUtils.removeStyle(ci,'font-size'); - - removeNotAttributeSpan(ci); - - } - //font-family - if(opt.clearFontFamily && ci.style.fontFamily){ - domUtils.removeStyle(ci,'font-family'); - removeNotAttributeSpan(ci); - } - - if(isLine(ci)){ - //合并空行 - if(opt.mergeEmptyline ){ - var next = ci.nextSibling,tmpNode,isBr = domUtils.isBr(ci); - while(isLine(next)){ - tmpNode = next; - next = tmpNode.nextSibling; - if(isBr && (!next || next && !domUtils.isBr(next))){ - break; - } - domUtils.remove(tmpNode); - } - - } - //去掉空行,保留占位的空行 - if(opt.removeEmptyline && domUtils.inDoc(ci,cont) && !remainTag[ci.parentNode.tagName.toLowerCase()] ){ - if(domUtils.isBr(ci)){ - next = ci.nextSibling; - if(next && !domUtils.isBr(next)){ - continue; - } - } - domUtils.remove(ci); - continue; - - } - - } - if(isLine(ci,true) && ci.tagName != 'SPAN'){ - if(opt.indent){ - ci.style.textIndent = opt.indentValue; - } - if(opt.textAlign){ - ci.style.textAlign = opt.textAlign; - } - // if(opt.lineHeight) - // ci.style.lineHeight = opt.lineHeight + 'cm'; - - } - - //去掉class,保留的class不去掉 - if(opt.removeClass && ci.className && !remainClass[ci.className.toLowerCase()]){ - - if(highlightCont && highlightCont.contains(ci)){ - continue; - } - domUtils.removeAttributes(ci,['class']); - } - - //表情不处理 - if(opt.imageBlockLine && ci.tagName.toLowerCase() == 'img' && !ci.getAttribute('emotion')){ - if(html){ - var img = ci; - switch (opt.imageBlockLine){ - case 'left': - case 'right': - case 'none': - var pN = img.parentNode,tmpNode,pre,next; - while(dtd.$inline[pN.tagName] || pN.tagName == 'A'){ - pN = pN.parentNode; - } - tmpNode = pN; - if(tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode,'text-align') == 'center'){ - if(!domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode,function(node){return !domUtils.isBr(node) && !domUtils.isWhitespace(node)}) == 1){ - pre = tmpNode.previousSibling; - next = tmpNode.nextSibling; - if(pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre)){ - pre.appendChild(tmpNode.firstChild); - while(next.firstChild){ - pre.appendChild(next.firstChild); - } - domUtils.remove(tmpNode); - domUtils.remove(next); - }else{ - domUtils.setStyle(tmpNode,'text-align',''); - } - - - } - - - } - domUtils.setStyle(img,'float', opt.imageBlockLine); - break; - case 'center': - if(me.queryCommandValue('imagefloat') != 'center'){ - pN = img.parentNode; - domUtils.setStyle(img,'float','none'); - tmpNode = img; - while(pN && domUtils.getChildCount(pN,function(node){return !domUtils.isBr(node) && !domUtils.isWhitespace(node)}) == 1 - && (dtd.$inline[pN.tagName] || pN.tagName == 'A')){ - tmpNode = pN; - pN = pN.parentNode; - } - var pNode = me.document.createElement('p'); - domUtils.setAttributes(pNode,{ - - style:'text-align:center' - }); - tmpNode.parentNode.insertBefore(pNode,tmpNode); - pNode.appendChild(tmpNode); - domUtils.setStyle(tmpNode,'float',''); - - } - - - } - } else { - var range = me.selection.getRange(); - range.selectNode(ci).select(); - me.execCommand('imagefloat', opt.imageBlockLine); - } - - } - - //去掉冗余的标签 - if(opt.removeEmptyNode){ - if(opt.removeTagNames[ci.tagName.toLowerCase()] && domUtils.hasNoAttributes(ci) && domUtils.isEmptyBlock(ci)){ - domUtils.remove(ci); - } - } - } - if(opt.tobdc){ - var root = UE.htmlparser(cont.innerHTML); - root.traversal(function(node){ - if(node.type == 'text'){ - node.data = ToDBC(node.data) - } - }); - cont.innerHTML = root.toHtml() - } - if(opt.bdc2sb){ - var root = UE.htmlparser(cont.innerHTML); - root.traversal(function(node){ - if(node.type == 'text'){ - node.data = DBC2SB(node.data) - } - }); - cont.innerHTML = root.toHtml() - } - if(html){ - html.html = cont.innerHTML; - } - } - if(opt.pasteFilter){ - me.addListener('beforepaste',autotype); - } - - function DBC2SB(str) { - var result = ''; - for (var i = 0; i < str.length; i++) { - var code = str.charCodeAt(i); //获取当前字符的unicode编码 - if (code >= 65281 && code <= 65373)//在这个unicode编码范围中的是所有的英文字母已经各种字符 - { - result += String.fromCharCode(str.charCodeAt(i) - 65248); //把全角字符的unicode编码转换为对应半角字符的unicode码 - } else if (code == 12288)//空格 - { - result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32); - } else { - result += str.charAt(i); - } - } - return result; - } - function ToDBC(txtstring) { - txtstring = utils.html(txtstring); - var tmp = ""; - var mark = "";/*用于判断,如果是html尖括里的标记,则不进行全角的转换*/ - for (var i = 0; i < txtstring.length; i++) { - if (txtstring.charCodeAt(i) == 32) { - tmp = tmp + String.fromCharCode(12288); - } - else if (txtstring.charCodeAt(i) < 127) { - tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248); - } - else { - tmp += txtstring.charAt(i); - } - } - return tmp; - } - - function readLocalOpts() { - var cookieOpt = me.getPreferences('autotypeset'); - utils.extend(me.options.autotypeset, cookieOpt); - } - - me.commands['autotypeset'] = { - execCommand:function () { - me.removeListener('beforepaste',autotype); - if(opt.pasteFilter){ - me.addListener('beforepaste',autotype); - } - autotype.call(me) - } - - }; - -}; - - - -// plugins/autosubmit.js -/** - * 快捷键提交 - * @file - * @since 1.2.6.1 - */ - -/** - * 提交表单 - * @command autosubmit - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'autosubmit' ); - * ``` - */ - -UE.plugin.register('autosubmit',function(){ - return { - shortcutkey:{ - "autosubmit":"ctrl+13" //手动提交 - }, - commands:{ - 'autosubmit':{ - execCommand:function () { - var me=this, - form = domUtils.findParentByTagName(me.iframe,"form", false); - if (form){ - if(me.fireEvent("beforesubmit")===false){ - return; - } - me.sync(); - form.submit(); - } - } - } - } - } -}); - -// plugins/background.js -/** - * 背景插件,为UEditor提供设置背景功能 - * @file - * @since 1.2.6.1 - */ -UE.plugin.register('background', function () { - var me = this, - cssRuleId = 'editor_background', - isSetColored, - reg = new RegExp('body[\\s]*\\{(.+)\\}', 'i'); - - function stringToObj(str) { - var obj = {}, styles = str.split(';'); - utils.each(styles, function (v) { - var index = v.indexOf(':'), - key = utils.trim(v.substr(0, index)).toLowerCase(); - key && (obj[key] = utils.trim(v.substr(index + 1) || '')); - }); - return obj; - } - - function setBackground(obj) { - if (obj) { - var styles = []; - for (var name in obj) { - if (obj.hasOwnProperty(name)) { - styles.push(name + ":" + obj[name] + '; '); - } - } - utils.cssRule(cssRuleId, styles.length ? ('body{' + styles.join("") + '}') : '', me.document); - } else { - utils.cssRule(cssRuleId, '', me.document) - } - } - //重写editor.hasContent方法 - - var orgFn = me.hasContents; - me.hasContents = function(){ - if(me.queryCommandValue('background')){ - return true - } - return orgFn.apply(me,arguments); - }; - return { - bindEvents: { - 'getAllHtml': function (type, headHtml) { - var body = this.body, - su = domUtils.getComputedStyle(body, "background-image"), - url = ""; - if (su.indexOf(me.options.imagePath) > 0) { - url = su.substring(su.indexOf(me.options.imagePath), su.length - 1).replace(/"|\(|\)/ig, ""); - } else { - url = su != "none" ? su.replace(/url\("?|"?\)/ig, "") : ""; - } - var html = ' '; - headHtml.push(html); - }, - 'aftersetcontent': function () { - if(isSetColored == false) setBackground(); - } - }, - inputRule: function (root) { - isSetColored = false; - utils.each(root.getNodesByTagName('p'), function (p) { - var styles = p.getAttr('data-background'); - if (styles) { - isSetColored = true; - setBackground(stringToObj(styles)); - p.parentNode.removeChild(p); - } - }) - }, - outputRule: function (root) { - var me = this, - styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg); - if (styles) { - root.appendChild(UE.uNode.createElement('


                  ')); - } - }, - commands: { - 'background': { - execCommand: function (cmd, obj) { - setBackground(obj); - }, - queryCommandValue: function () { - var me = this, - styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg); - return styles ? stringToObj(styles[1]) : null; - }, - notNeedUndo: true - } - } - } -}); - -// plugins/image.js -/** - * 图片插入、排版插件 - * @file - * @since 1.2.6.1 - */ - -/** - * 图片对齐方式 - * @command imagefloat - * @method execCommand - * @remind 值center为独占一行居中 - * @param { String } cmd 命令字符串 - * @param { String } align 对齐方式,可传left、right、none、center - * @remaind center表示图片独占一行 - * @example - * ```javascript - * editor.execCommand( 'imagefloat', 'center' ); - * ``` - */ - -/** - * 如果选区所在位置是图片区域 - * @command imagefloat - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 返回图片对齐方式 - * @example - * ```javascript - * editor.queryCommandValue( 'imagefloat' ); - * ``` - */ - -UE.commands['imagefloat'] = { - execCommand:function (cmd, align) { - var me = this, - range = me.selection.getRange(); - if (!range.collapsed) { - var img = range.getClosedNode(); - if (img && img.tagName == 'IMG') { - switch (align) { - case 'left': - case 'right': - case 'none': - var pN = img.parentNode, tmpNode, pre, next; - while (dtd.$inline[pN.tagName] || pN.tagName == 'A') { - pN = pN.parentNode; - } - tmpNode = pN; - if (tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode, 'text-align') == 'center') { - if (!domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode, function (node) { - return !domUtils.isBr(node) && !domUtils.isWhitespace(node); - }) == 1) { - pre = tmpNode.previousSibling; - next = tmpNode.nextSibling; - if (pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre)) { - pre.appendChild(tmpNode.firstChild); - while (next.firstChild) { - pre.appendChild(next.firstChild); - } - domUtils.remove(tmpNode); - domUtils.remove(next); - } else { - domUtils.setStyle(tmpNode, 'text-align', ''); - } - - - } - - range.selectNode(img).select(); - } - domUtils.setStyle(img, 'float', align == 'none' ? '' : align); - if(align == 'none'){ - domUtils.removeAttributes(img,'align'); - } - - break; - case 'center': - if (me.queryCommandValue('imagefloat') != 'center') { - pN = img.parentNode; - domUtils.setStyle(img, 'float', ''); - domUtils.removeAttributes(img,'align'); - tmpNode = img; - while (pN && domUtils.getChildCount(pN, function (node) { - return !domUtils.isBr(node) && !domUtils.isWhitespace(node); - }) == 1 - && (dtd.$inline[pN.tagName] || pN.tagName == 'A')) { - tmpNode = pN; - pN = pN.parentNode; - } - range.setStartBefore(tmpNode).setCursor(false); - pN = me.document.createElement('div'); - pN.appendChild(tmpNode); - domUtils.setStyle(tmpNode, 'float', ''); - - me.execCommand('insertHtml', '

                  ' + pN.innerHTML + '

                  '); - - tmpNode = me.document.getElementById('_img_parent_tmp'); - tmpNode.removeAttribute('id'); - tmpNode = tmpNode.firstChild; - range.selectNode(tmpNode).select(); - //去掉后边多余的元素 - next = tmpNode.parentNode.nextSibling; - if (next && domUtils.isEmptyNode(next)) { - domUtils.remove(next); - } - - } - - break; - } - - } - } - }, - queryCommandValue:function () { - var range = this.selection.getRange(), - startNode, floatStyle; - if (range.collapsed) { - return 'none'; - } - startNode = range.getClosedNode(); - if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') { - floatStyle = domUtils.getComputedStyle(startNode, 'float') || startNode.getAttribute('align'); - - if (floatStyle == 'none') { - floatStyle = domUtils.getComputedStyle(startNode.parentNode, 'text-align') == 'center' ? 'center' : floatStyle; - } - return { - left:1, - right:1, - center:1 - }[floatStyle] ? floatStyle : 'none'; - } - return 'none'; - - - }, - queryCommandState:function () { - var range = this.selection.getRange(), - startNode; - - if (range.collapsed) return -1; - - startNode = range.getClosedNode(); - if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') { - return 0; - } - return -1; - } -}; - - -/** - * 插入图片 - * @command insertimage - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { Object } opt 属性键值对,这些属性都将被复制到当前插入图片 - * @remind 该命令第二个参数可接受一个图片配置项对象的数组,可以插入多张图片, - * 此时数组的每一个元素都是一个Object类型的图片属性集合。 - * @example - * ```javascript - * editor.execCommand( 'insertimage', { - * src:'a/b/c.jpg', - * width:'100', - * height:'100' - * } ); - * ``` - * @example - * ```javascript - * editor.execCommand( 'insertimage', [{ - * src:'a/b/c.jpg', - * width:'100', - * height:'100' - * },{ - * src:'a/b/d.jpg', - * width:'100', - * height:'100' - * }] ); - * ``` - */ - -UE.commands['insertimage'] = { - execCommand:function (cmd, opt) { - - opt = utils.isArray(opt) ? opt : [opt]; - if (!opt.length) { - return; - } - var me = this, - range = me.selection.getRange(), - img = range.getClosedNode(); - - if(me.fireEvent('beforeinsertimage', opt) === true){ - return; - } - - if (img && /img/i.test(img.tagName) && (img.className != "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1) && !img.getAttribute("word_img")) { - var first = opt.shift(); - var floatStyle = first['floatStyle']; - delete first['floatStyle']; -//// img.style.border = (first.border||0) +"px solid #000"; -//// img.style.margin = (first.margin||0) +"px"; -// img.style.cssText += ';margin:' + (first.margin||0) +"px;" + 'border:' + (first.border||0) +"px solid #000"; - domUtils.setAttributes(img, first); - me.execCommand('imagefloat', floatStyle); - if (opt.length > 0) { - range.setStartAfter(img).setCursor(false, true); - me.execCommand('insertimage', opt); - } - - } else { - var html = [], str = '', ci; - ci = opt[0]; - if (opt.length == 1) { - str = '' + ci.alt + ''; - if (ci['floatStyle'] == 'center') { - str = '

                  ' + str + '

                  '; - } - html.push(str); - - } else { - for (var i = 0; ci = opt[i++];) { - str = '

                  '; - html.push(str); - } - } - - me.execCommand('insertHtml', html.join('')); - } - - me.fireEvent('afterinsertimage', opt) - } -}; - -// plugins/justify.js -/** - * 段落格式 - * @file - * @since 1.2.6.1 - */ - -/** - * 段落对齐方式 - * @command justify - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } align 对齐方式:left => 居左,right => 居右,center => 居中,justify => 两端对齐 - * @example - * ```javascript - * editor.execCommand( 'justify', 'center' ); - * ``` - */ -/** - * 如果选区所在位置是段落区域,返回当前段落对齐方式 - * @command justify - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 返回段落对齐方式 - * @example - * ```javascript - * editor.queryCommandValue( 'justify' ); - * ``` - */ - -UE.plugins['justify']=function(){ - var me=this, - block = domUtils.isBlockElm, - defaultValue = { - left:1, - right:1, - center:1, - justify:1 - }, - doJustify = function (range, style) { - var bookmark = range.createBookmark(), - filterFn = function (node) { - return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node); - }; - - range.enlarge(true); - var bookmark2 = range.createBookmark(), - current = domUtils.getNextDomNode(bookmark2.start, false, filterFn), - tmpRange = range.cloneRange(), - tmpNode; - while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) { - if (current.nodeType == 3 || !block(current)) { - tmpRange.setStartBefore(current); - while (current && current !== bookmark2.end && !block(current)) { - tmpNode = current; - current = domUtils.getNextDomNode(current, false, null, function (node) { - return !block(node); - }); - } - tmpRange.setEndAfter(tmpNode); - var common = tmpRange.getCommonAncestor(); - if (!domUtils.isBody(common) && block(common)) { - domUtils.setStyles(common, utils.isString(style) ? {'text-align':style} : style); - current = common; - } else { - var p = range.document.createElement('p'); - domUtils.setStyles(p, utils.isString(style) ? {'text-align':style} : style); - var frag = tmpRange.extractContents(); - p.appendChild(frag); - tmpRange.insertNode(p); - current = p; - } - current = domUtils.getNextDomNode(current, false, filterFn); - } else { - current = domUtils.getNextDomNode(current, true, filterFn); - } - } - return range.moveToBookmark(bookmark2).moveToBookmark(bookmark); - }; - - UE.commands['justify'] = { - execCommand:function (cmdName, align) { - var range = this.selection.getRange(), - txt; - - //闭合时单独处理 - if (range.collapsed) { - txt = this.document.createTextNode('p'); - range.insertNode(txt); - } - doJustify(range, align); - if (txt) { - range.setStartBefore(txt).collapse(true); - domUtils.remove(txt); - } - - range.select(); - - - return true; - }, - queryCommandValue:function () { - var startNode = this.selection.getStart(), - value = domUtils.getComputedStyle(startNode, 'text-align'); - return defaultValue[value] ? value : 'left'; - }, - queryCommandState:function () { - var start = this.selection.getStart(), - cell = start && domUtils.findParentByTagName(start, ["td", "th","caption"], true); - - return cell? -1:0; - } - - }; -}; - - -// plugins/font.js -/** - * 字体颜色,背景色,字号,字体,下划线,删除线 - * @file - * @since 1.2.6.1 - */ - -/** - * 字体颜色 - * @command forecolor - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } value 色值(必须十六进制) - * @example - * ```javascript - * editor.execCommand( 'forecolor', '#000' ); - * ``` - */ -/** - * 返回选区字体颜色 - * @command forecolor - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 返回字体颜色 - * @example - * ```javascript - * editor.queryCommandValue( 'forecolor' ); - * ``` - */ - -/** - * 字体背景颜色 - * @command backcolor - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } value 色值(必须十六进制) - * @example - * ```javascript - * editor.execCommand( 'backcolor', '#000' ); - * ``` - */ -/** - * 返回选区字体颜色 - * @command backcolor - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 返回字体背景颜色 - * @example - * ```javascript - * editor.queryCommandValue( 'backcolor' ); - * ``` - */ - -/** - * 字体大小 - * @command fontsize - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } value 字体大小 - * @example - * ```javascript - * editor.execCommand( 'fontsize', '14px' ); - * ``` - */ -/** - * 返回选区字体大小 - * @command fontsize - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 返回字体大小 - * @example - * ```javascript - * editor.queryCommandValue( 'fontsize' ); - * ``` - */ - -/** - * 字体样式 - * @command fontfamily - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } value 字体样式 - * @example - * ```javascript - * editor.execCommand( 'fontfamily', '微软雅黑' ); - * ``` - */ -/** - * 返回选区字体样式 - * @command fontfamily - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 返回字体样式 - * @example - * ```javascript - * editor.queryCommandValue( 'fontfamily' ); - * ``` - */ - -/** - * 字体下划线,与删除线互斥 - * @command underline - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'underline' ); - * ``` - */ - -/** - * 字体删除线,与下划线互斥 - * @command strikethrough - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'strikethrough' ); - * ``` - */ - -/** - * 字体边框 - * @command fontborder - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'fontborder' ); - * ``` - */ - -UE.plugins['font'] = function () { - var me = this, - fonts = { - 'forecolor': 'color', - 'backcolor': 'background-color', - 'fontsize': 'font-size', - 'fontfamily': 'font-family', - 'underline': 'text-decoration', - 'strikethrough': 'text-decoration', - 'fontborder': 'border' - }, - needCmd = {'underline': 1, 'strikethrough': 1, 'fontborder': 1}, - needSetChild = { - 'forecolor': 'color', - 'backcolor': 'background-color', - 'fontsize': 'font-size', - 'fontfamily': 'font-family' - - }; - me.setOpt({ - 'fontfamily': [ - { name: 'songti', val: '宋体,SimSun'}, - { name: 'yahei', val: '微软雅黑,Microsoft YaHei'}, - { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'}, - { name: 'heiti', val: '黑体, SimHei'}, - { name: 'lishu', val: '隶书, SimLi'}, - { name: 'andaleMono', val: 'andale mono'}, - { name: 'arial', val: 'arial, helvetica,sans-serif'}, - { name: 'arialBlack', val: 'arial black,avant garde'}, - { name: 'comicSansMs', val: 'comic sans ms'}, - { name: 'impact', val: 'impact,chicago'}, - { name: 'timesNewRoman', val: 'times new roman'} - ], - 'fontsize': [10, 11, 12, 14, 16, 18, 20, 24, 36] - }); - - function mergeWithParent(node){ - var parent; - while(parent = node.parentNode){ - if(parent.tagName == 'SPAN' && domUtils.getChildCount(parent,function(child){ - return !domUtils.isBookmarkNode(child) && !domUtils.isBr(child) - }) == 1) { - parent.style.cssText += node.style.cssText; - domUtils.remove(node,true); - node = parent; - - }else{ - break; - } - } - - } - function mergeChild(rng,cmdName,value){ - if(needSetChild[cmdName]){ - rng.adjustmentBoundary(); - if(!rng.collapsed && rng.startContainer.nodeType == 1){ - var start = rng.startContainer.childNodes[rng.startOffset]; - if(start && domUtils.isTagNode(start,'span')){ - var bk = rng.createBookmark(); - utils.each(domUtils.getElementsByTagName(start, 'span'), function (span) { - if (!span.parentNode || domUtils.isBookmarkNode(span))return; - if(cmdName == 'backcolor' && domUtils.getComputedStyle(span,'background-color').toLowerCase() === value){ - return; - } - domUtils.removeStyle(span,needSetChild[cmdName]); - if(span.style.cssText.replace(/^\s+$/,'').length == 0){ - domUtils.remove(span,true) - } - }); - rng.moveToBookmark(bk) - } - } - } - - } - function mergesibling(rng,cmdName,value) { - var collapsed = rng.collapsed, - bk = rng.createBookmark(), common; - if (collapsed) { - common = bk.start.parentNode; - while (dtd.$inline[common.tagName]) { - common = common.parentNode; - } - } else { - common = domUtils.getCommonAncestor(bk.start, bk.end); - } - utils.each(domUtils.getElementsByTagName(common, 'span'), function (span) { - if (!span.parentNode || domUtils.isBookmarkNode(span))return; - if (/\s*border\s*:\s*none;?\s*/i.test(span.style.cssText)) { - if(/^\s*border\s*:\s*none;?\s*$/.test(span.style.cssText)){ - domUtils.remove(span, true); - }else{ - domUtils.removeStyle(span,'border'); - } - return - } - if (/border/i.test(span.style.cssText) && span.parentNode.tagName == 'SPAN' && /border/i.test(span.parentNode.style.cssText)) { - span.style.cssText = span.style.cssText.replace(/border[^:]*:[^;]+;?/gi, ''); - } - if(!(cmdName=='fontborder' && value=='none')){ - var next = span.nextSibling; - while (next && next.nodeType == 1 && next.tagName == 'SPAN' ) { - if(domUtils.isBookmarkNode(next) && cmdName == 'fontborder') { - span.appendChild(next); - next = span.nextSibling; - continue; - } - if (next.style.cssText == span.style.cssText) { - domUtils.moveChild(next, span); - domUtils.remove(next); - } - if (span.nextSibling === next) - break; - next = span.nextSibling; - } - } - - - mergeWithParent(span); - if(browser.ie && browser.version > 8 ){ - //拷贝父亲们的特别的属性,这里只做背景颜色的处理 - var parent = domUtils.findParent(span,function(n){return n.tagName == 'SPAN' && /background-color/.test(n.style.cssText)}); - if(parent && !/background-color/.test(span.style.cssText)){ - span.style.backgroundColor = parent.style.backgroundColor; - } - } - - }); - rng.moveToBookmark(bk); - mergeChild(rng,cmdName,value) - } - - me.addInputRule(function (root) { - utils.each(root.getNodesByTagName('u s del font strike'), function (node) { - if (node.tagName == 'font') { - var cssStyle = []; - for (var p in node.attrs) { - switch (p) { - case 'size': - cssStyle.push('font-size:' + - ({ - '1':'10', - '2':'12', - '3':'16', - '4':'18', - '5':'24', - '6':'32', - '7':'48' - }[node.attrs[p]] || node.attrs[p]) + 'px'); - break; - case 'color': - cssStyle.push('color:' + node.attrs[p]); - break; - case 'face': - cssStyle.push('font-family:' + node.attrs[p]); - break; - case 'style': - cssStyle.push(node.attrs[p]); - } - } - node.attrs = { - 'style': cssStyle.join(';') - }; - } else { - var val = node.tagName == 'u' ? 'underline' : 'line-through'; - node.attrs = { - 'style': (node.getAttr('style') || '') + 'text-decoration:' + val + ';' - } - } - node.tagName = 'span'; - }); -// utils.each(root.getNodesByTagName('span'), function (node) { -// var val; -// if(val = node.getAttr('class')){ -// if(/fontstrikethrough/.test(val)){ -// node.setStyle('text-decoration','line-through'); -// if(node.attrs['class']){ -// node.attrs['class'] = node.attrs['class'].replace(/fontstrikethrough/,''); -// }else{ -// node.setAttr('class') -// } -// } -// if(/fontborder/.test(val)){ -// node.setStyle('border','1px solid #000'); -// if(node.attrs['class']){ -// node.attrs['class'] = node.attrs['class'].replace(/fontborder/,''); -// }else{ -// node.setAttr('class') -// } -// } -// } -// }); - }); -// me.addOutputRule(function(root){ -// utils.each(root.getNodesByTagName('span'), function (node) { -// var val; -// if(val = node.getStyle('text-decoration')){ -// if(/line-through/.test(val)){ -// if(node.attrs['class']){ -// node.attrs['class'] += ' fontstrikethrough'; -// }else{ -// node.setAttr('class','fontstrikethrough') -// } -// } -// -// node.setStyle('text-decoration') -// } -// if(val = node.getStyle('border')){ -// if(/1px/.test(val) && /solid/.test(val)){ -// if(node.attrs['class']){ -// node.attrs['class'] += ' fontborder'; -// -// }else{ -// node.setAttr('class','fontborder') -// } -// } -// node.setStyle('border') -// -// } -// }); -// }); - for (var p in fonts) { - (function (cmd, style) { - UE.commands[cmd] = { - execCommand: function (cmdName, value) { - value = value || (this.queryCommandState(cmdName) ? 'none' : cmdName == 'underline' ? 'underline' : - cmdName == 'fontborder' ? '1px solid #000' : - 'line-through'); - var me = this, - range = this.selection.getRange(), - text; - - if (value == 'default') { - - if (range.collapsed) { - text = me.document.createTextNode('font'); - range.insertNode(text).select(); - - } - me.execCommand('removeFormat', 'span,a', style); - if (text) { - range.setStartBefore(text).collapse(true); - domUtils.remove(text); - } - mergesibling(range,cmdName,value); - range.select() - } else { - if (!range.collapsed) { - if (needCmd[cmd] && me.queryCommandValue(cmd)) { - me.execCommand('removeFormat', 'span,a', style); - } - range = me.selection.getRange(); - - range.applyInlineStyle('span', {'style': style + ':' + value}); - mergesibling(range, cmdName,value); - range.select(); - } else { - - var span = domUtils.findParentByTagName(range.startContainer, 'span', true); - text = me.document.createTextNode('font'); - if (span && !span.children.length && !span[browser.ie ? 'innerText' : 'textContent'].replace(fillCharReg, '').length) { - //for ie hack when enter - range.insertNode(text); - if (needCmd[cmd]) { - range.selectNode(text).select(); - me.execCommand('removeFormat', 'span,a', style, null); - - span = domUtils.findParentByTagName(text, 'span', true); - range.setStartBefore(text); - - } - span && (span.style.cssText += ';' + style + ':' + value); - range.collapse(true).select(); - - - } else { - range.insertNode(text); - range.selectNode(text).select(); - span = range.document.createElement('span'); - - if (needCmd[cmd]) { - //a标签内的不处理跳过 - if (domUtils.findParentByTagName(text, 'a', true)) { - range.setStartBefore(text).setCursor(); - domUtils.remove(text); - return; - } - me.execCommand('removeFormat', 'span,a', style); - } - - span.style.cssText = style + ':' + value; - - - text.parentNode.insertBefore(span, text); - //修复,span套span 但样式不继承的问题 - if (!browser.ie || browser.ie && browser.version == 9) { - var spanParent = span.parentNode; - while (!domUtils.isBlockElm(spanParent)) { - if (spanParent.tagName == 'SPAN') { - //opera合并style不会加入";" - span.style.cssText = spanParent.style.cssText + ";" + span.style.cssText; - } - spanParent = spanParent.parentNode; - } - } - - - if (opera) { - setTimeout(function () { - range.setStart(span, 0).collapse(true); - mergesibling(range, cmdName,value); - range.select(); - }); - } else { - range.setStart(span, 0).collapse(true); - mergesibling(range,cmdName,value); - range.select(); - } - - //trace:981 - //domUtils.mergeToParent(span) - } - domUtils.remove(text); - } - - - } - return true; - }, - queryCommandValue: function (cmdName) { - var startNode = this.selection.getStart(); - - //trace:946 - if (cmdName == 'underline' || cmdName == 'strikethrough') { - var tmpNode = startNode, value; - while (tmpNode && !domUtils.isBlockElm(tmpNode) && !domUtils.isBody(tmpNode)) { - if (tmpNode.nodeType == 1) { - value = domUtils.getComputedStyle(tmpNode, style); - if (value != 'none') { - return value; - } - } - - tmpNode = tmpNode.parentNode; - } - return 'none'; - } - if (cmdName == 'fontborder') { - var tmp = startNode, val; - while (tmp && dtd.$inline[tmp.tagName]) { - if (val = domUtils.getComputedStyle(tmp, 'border')) { - - if (/1px/.test(val) && /solid/.test(val)) { - return val; - } - } - tmp = tmp.parentNode; - } - return '' - } - - if( cmdName == 'FontSize' ) { - var styleVal = domUtils.getComputedStyle(startNode, style), - tmp = /^([\d\.]+)(\w+)$/.exec( styleVal ); - - if( tmp ) { - - return Math.floor( tmp[1] ) + tmp[2]; - - } - - return styleVal; - - } - - return domUtils.getComputedStyle(startNode, style); - }, - queryCommandState: function (cmdName) { - if (!needCmd[cmdName]) - return 0; - var val = this.queryCommandValue(cmdName); - if (cmdName == 'fontborder') { - return /1px/.test(val) && /solid/.test(val) - } else { - return cmdName == 'underline' ? /underline/.test(val) : /line\-through/.test(val); - - } - - } - }; - })(p, fonts[p]); - } -}; - -// plugins/link.js -/** - * 超链接 - * @file - * @since 1.2.6.1 - */ - -/** - * 插入超链接 - * @command link - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { Object } options 设置自定义属性,例如:url、title、target - * @example - * ```javascript - * editor.execCommand( 'link', '{ - * url:'ueditor.baidu.com', - * title:'ueditor', - * target:'_blank' - * }' ); - * ``` - */ -/** - * 返回当前选中的第一个超链接节点 - * @command link - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { Element } 超链接节点 - * @example - * ```javascript - * editor.queryCommandValue( 'link' ); - * ``` - */ - -/** - * 取消超链接 - * @command unlink - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'unlink'); - * ``` - */ - -UE.plugins['link'] = function(){ - function optimize( range ) { - var start = range.startContainer,end = range.endContainer; - - if ( start = domUtils.findParentByTagName( start, 'a', true ) ) { - range.setStartBefore( start ); - } - if ( end = domUtils.findParentByTagName( end, 'a', true ) ) { - range.setEndAfter( end ); - } - } - - - UE.commands['unlink'] = { - execCommand : function() { - var range = this.selection.getRange(), - bookmark; - if(range.collapsed && !domUtils.findParentByTagName( range.startContainer, 'a', true )){ - return; - } - bookmark = range.createBookmark(); - optimize( range ); - range.removeInlineStyle( 'a' ).moveToBookmark( bookmark ).select(); - }, - queryCommandState : function(){ - return !this.highlight && this.queryCommandValue('link') ? 0 : -1; - } - - }; - function doLink(range,opt,me){ - var rngClone = range.cloneRange(), - link = me.queryCommandValue('link'); - optimize( range = range.adjustmentBoundary() ); - var start = range.startContainer; - if(start.nodeType == 1 && link){ - start = start.childNodes[range.startOffset]; - if(start && start.nodeType == 1 && start.tagName == 'A' && /^(?:https?|ftp|file)\s*:\s*\/\//.test(start[browser.ie?'innerText':'textContent'])){ - start[browser.ie ? 'innerText' : 'textContent'] = utils.html(opt.textValue||opt.href); - - } - } - if( !rngClone.collapsed || link){ - range.removeInlineStyle( 'a' ); - rngClone = range.cloneRange(); - } - - if ( rngClone.collapsed ) { - var a = range.document.createElement( 'a'), - text = ''; - if(opt.textValue){ - - text = utils.html(opt.textValue); - delete opt.textValue; - }else{ - text = utils.html(opt.href); - - } - domUtils.setAttributes( a, opt ); - start = domUtils.findParentByTagName( rngClone.startContainer, 'a', true ); - if(start && domUtils.isInNodeEndBoundary(rngClone,start)){ - range.setStartAfter(start).collapse(true); - - } - a[browser.ie ? 'innerText' : 'textContent'] = text; - range.insertNode(a).selectNode( a ); - } else { - range.applyInlineStyle( 'a', opt ); - - } - } - UE.commands['link'] = { - execCommand : function( cmdName, opt ) { - var range; - opt._href && (opt._href = utils.unhtml(opt._href,/[<">]/g)); - opt.href && (opt.href = utils.unhtml(opt.href,/[<">]/g)); - opt.textValue && (opt.textValue = utils.unhtml(opt.textValue,/[<">]/g)); - doLink(range=this.selection.getRange(),opt,this); - //闭合都不加占位符,如果加了会在a后边多个占位符节点,导致a是图片背景组成的列表,出现空白问题 - range.collapse().select(true); - - }, - queryCommandValue : function() { - var range = this.selection.getRange(), - node; - if ( range.collapsed ) { -// node = this.selection.getStart(); - //在ie下getstart()取值偏上了 - node = range.startContainer; - node = node.nodeType == 1 ? node : node.parentNode; - - if ( node && (node = domUtils.findParentByTagName( node, 'a', true )) && ! domUtils.isInNodeEndBoundary(range,node)) { - - return node; - } - } else { - //trace:1111 如果是

                  xx

                  startContainer是p就会找不到a - range.shrinkBoundary(); - var start = range.startContainer.nodeType == 3 || !range.startContainer.childNodes[range.startOffset] ? range.startContainer : range.startContainer.childNodes[range.startOffset], - end = range.endContainer.nodeType == 3 || range.endOffset == 0 ? range.endContainer : range.endContainer.childNodes[range.endOffset-1], - common = range.getCommonAncestor(); - node = domUtils.findParentByTagName( common, 'a', true ); - if ( !node && common.nodeType == 1){ - - var as = common.getElementsByTagName( 'a' ), - ps,pe; - - for ( var i = 0,ci; ci = as[i++]; ) { - ps = domUtils.getPosition( ci, start ),pe = domUtils.getPosition( ci,end); - if ( (ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS) - && - (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS) - ) { - node = ci; - break; - } - } - } - return node; - } - - }, - queryCommandState : function() { - //判断如果是视频的话连接不可用 - //fix 853 - var img = this.selection.getRange().getClosedNode(), - flag = img && (img.className == "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1); - return flag ? -1 : 0; - } - }; -}; - -// plugins/iframe.js -///import core -///import plugins\inserthtml.js -///commands 插入框架 -///commandsName InsertFrame -///commandsTitle 插入Iframe -///commandsDialog dialogs\insertframe - -UE.plugins['insertframe'] = function() { - var me =this; - function deleteIframe(){ - me._iframe && delete me._iframe; - } - - me.addListener("selectionchange",function(){ - deleteIframe(); - }); - -}; - - - -// plugins/scrawl.js -///import core -///commands 涂鸦 -///commandsName Scrawl -///commandsTitle 涂鸦 -///commandsDialog dialogs\scrawl -UE.commands['scrawl'] = { - queryCommandState : function(){ - return ( browser.ie && browser.version <= 8 ) ? -1 :0; - } -}; - - -// plugins/removeformat.js -/** - * 清除格式 - * @file - * @since 1.2.6.1 - */ - -/** - * 清除文字样式 - * @command removeformat - * @method execCommand - * @param { String } cmd 命令字符串 - * @param {String} tags 以逗号隔开的标签。如:strong - * @param {String} style 样式如:color - * @param {String} attrs 属性如:width - * @example - * ```javascript - * editor.execCommand( 'removeformat', 'strong','color','width' ); - * ``` - */ - -UE.plugins['removeformat'] = function(){ - var me = this; - me.setOpt({ - 'removeFormatTags': 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var', - 'removeFormatAttributes':'class,style,lang,width,height,align,hspace,valign' - }); - me.commands['removeformat'] = { - execCommand : function( cmdName, tags, style, attrs,notIncludeA ) { - - var tagReg = new RegExp( '^(?:' + (tags || this.options.removeFormatTags).replace( /,/g, '|' ) + ')$', 'i' ) , - removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split( ',' ), - range = new dom.Range( this.document ), - bookmark,node,parent, - filter = function( node ) { - return node.nodeType == 1; - }; - - function isRedundantSpan (node) { - if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span'){ - return 0; - } - if (browser.ie) { - //ie 下判断实效,所以只能简单用style来判断 - //return node.style.cssText == '' ? 1 : 0; - var attrs = node.attributes; - if ( attrs.length ) { - for ( var i = 0,l = attrs.length; i - var node = range.startContainer, - tmp, - collapsed = range.collapsed; - while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){ - tmp = node.parentNode; - range.setStartBefore(node); - //trace:937 - //更新结束边界 - if(range.startContainer === range.endContainer){ - range.endOffset--; - } - domUtils.remove(node); - node = tmp; - } - - if(!collapsed){ - node = range.endContainer; - while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){ - tmp = node.parentNode; - range.setEndBefore(node); - domUtils.remove(node); - - node = tmp; - } - - - } - } - - - - range = this.selection.getRange(); - doRemove( range ); - range.select(); - - } - - }; - -}; - - -// plugins/blockquote.js -/** - * 添加引用 - * @file - * @since 1.2.6.1 - */ - -/** - * 添加引用 - * @command blockquote - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'blockquote' ); - * ``` - */ - -/** - * 添加引用 - * @command blockquote - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { Object } attrs 节点属性 - * @example - * ```javascript - * editor.execCommand( 'blockquote',{ - * style: "color: red;" - * } ); - * ``` - */ - - -UE.plugins['blockquote'] = function(){ - var me = this; - function getObj(editor){ - return domUtils.filterNodeList(editor.selection.getStartElementPath(),'blockquote'); - } - me.commands['blockquote'] = { - execCommand : function( cmdName, attrs ) { - var range = this.selection.getRange(), - obj = getObj(this), - blockquote = dtd.blockquote, - bookmark = range.createBookmark(); - - if ( obj ) { - - var start = range.startContainer, - startBlock = domUtils.isBlockElm(start) ? start : domUtils.findParent(start,function(node){return domUtils.isBlockElm(node)}), - - end = range.endContainer, - endBlock = domUtils.isBlockElm(end) ? end : domUtils.findParent(end,function(node){return domUtils.isBlockElm(node)}); - - //处理一下li - startBlock = domUtils.findParentByTagName(startBlock,'li',true) || startBlock; - endBlock = domUtils.findParentByTagName(endBlock,'li',true) || endBlock; - - - if(startBlock.tagName == 'LI' || startBlock.tagName == 'TD' || startBlock === obj || domUtils.isBody(startBlock)){ - domUtils.remove(obj,true); - }else{ - domUtils.breakParent(startBlock,obj); - } - - if(startBlock !== endBlock){ - obj = domUtils.findParentByTagName(endBlock,'blockquote'); - if(obj){ - if(endBlock.tagName == 'LI' || endBlock.tagName == 'TD'|| domUtils.isBody(endBlock)){ - obj.parentNode && domUtils.remove(obj,true); - }else{ - domUtils.breakParent(endBlock,obj); - } - - } - } - - var blockquotes = domUtils.getElementsByTagName(this.document,'blockquote'); - for(var i=0,bi;bi=blockquotes[i++];){ - if(!bi.childNodes.length){ - domUtils.remove(bi); - }else if(domUtils.getPosition(bi,startBlock)&domUtils.POSITION_FOLLOWING && domUtils.getPosition(bi,endBlock)&domUtils.POSITION_PRECEDING){ - domUtils.remove(bi,true); - } - } - - - - - } else { - - var tmpRange = range.cloneRange(), - node = tmpRange.startContainer.nodeType == 1 ? tmpRange.startContainer : tmpRange.startContainer.parentNode, - preNode = node, - doEnd = 1; - - //调整开始 - while ( 1 ) { - if ( domUtils.isBody(node) ) { - if ( preNode !== node ) { - if ( range.collapsed ) { - tmpRange.selectNode( preNode ); - doEnd = 0; - } else { - tmpRange.setStartBefore( preNode ); - } - }else{ - tmpRange.setStart(node,0); - } - - break; - } - if ( !blockquote[node.tagName] ) { - if ( range.collapsed ) { - tmpRange.selectNode( preNode ); - } else{ - tmpRange.setStartBefore( preNode); - } - break; - } - - preNode = node; - node = node.parentNode; - } - - //调整结束 - if ( doEnd ) { - preNode = node = node = tmpRange.endContainer.nodeType == 1 ? tmpRange.endContainer : tmpRange.endContainer.parentNode; - while ( 1 ) { - - if ( domUtils.isBody( node ) ) { - if ( preNode !== node ) { - - tmpRange.setEndAfter( preNode ); - - } else { - tmpRange.setEnd( node, node.childNodes.length ); - } - - break; - } - if ( !blockquote[node.tagName] ) { - tmpRange.setEndAfter( preNode ); - break; - } - - preNode = node; - node = node.parentNode; - } - - } - - - node = range.document.createElement( 'blockquote' ); - domUtils.setAttributes( node, attrs ); - node.appendChild( tmpRange.extractContents() ); - tmpRange.insertNode( node ); - //去除重复的 - var childs = domUtils.getElementsByTagName(node,'blockquote'); - for(var i=0,ci;ci=childs[i++];){ - if(ci.parentNode){ - domUtils.remove(ci,true); - } - } - - } - range.moveToBookmark( bookmark ).select(); - }, - queryCommandState : function() { - return getObj(this) ? 1 : 0; - } - }; -}; - - - -// plugins/convertcase.js -/** - * 大小写转换 - * @file - * @since 1.2.6.1 - */ - -/** - * 把选区内文本变大写,与“tolowercase”命令互斥 - * @command touppercase - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'touppercase' ); - * ``` - */ - -/** - * 把选区内文本变小写,与“touppercase”命令互斥 - * @command tolowercase - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'tolowercase' ); - * ``` - */ -UE.commands['touppercase'] = -UE.commands['tolowercase'] = { - execCommand:function (cmd) { - var me = this; - var rng = me.selection.getRange(); - if(rng.collapsed){ - return rng; - } - var bk = rng.createBookmark(), - bkEnd = bk.end, - filterFn = function( node ) { - return !domUtils.isBr(node) && !domUtils.isWhitespace( node ); - }, - curNode = domUtils.getNextDomNode( bk.start, false, filterFn ); - while ( curNode && (domUtils.getPosition( curNode, bkEnd ) & domUtils.POSITION_PRECEDING) ) { - - if ( curNode.nodeType == 3 ) { - curNode.nodeValue = curNode.nodeValue[cmd == 'touppercase' ? 'toUpperCase' : 'toLowerCase'](); - } - curNode = domUtils.getNextDomNode( curNode, true, filterFn ); - if(curNode === bkEnd){ - break; - } - - } - rng.moveToBookmark(bk).select(); - } -}; - - - -// plugins/indent.js -/** - * 首行缩进 - * @file - * @since 1.2.6.1 - */ - -/** - * 缩进 - * @command indent - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'indent' ); - * ``` - */ -UE.commands['indent'] = { - execCommand : function() { - var me = this,value = me.queryCommandState("indent") ? "0em" : (me.options.indentValue || '2em'); - me.execCommand('Paragraph','p',{style:'text-indent:'+ value}); - }, - queryCommandState : function() { - var pN = domUtils.filterNodeList(this.selection.getStartElementPath(),'p h1 h2 h3 h4 h5 h6'); - return pN && pN.style.textIndent && parseInt(pN.style.textIndent) ? 1 : 0; - } - -}; - - -// plugins/print.js -/** - * 打印 - * @file - * @since 1.2.6.1 - */ - -/** - * 打印 - * @command print - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'print' ); - * ``` - */ -UE.commands['print'] = { - execCommand : function(){ - this.window.print(); - }, - notNeedUndo : 1 -}; - - - -// plugins/preview.js -/** - * 预览 - * @file - * @since 1.2.6.1 - */ - -/** - * 预览 - * @command preview - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'preview' ); - * ``` - */ -UE.commands['preview'] = { - execCommand : function(){ - var w = window.open('', '_blank', ''), - d = w.document; - d.open(); - d.write('
                  '+this.getContent(null,null,true)+'
                  '); - d.close(); - }, - notNeedUndo : 1 -}; - - -// plugins/selectall.js -/** - * 全选 - * @file - * @since 1.2.6.1 - */ - -/** - * 选中所有内容 - * @command selectall - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'selectall' ); - * ``` - */ -UE.plugins['selectall'] = function(){ - var me = this; - me.commands['selectall'] = { - execCommand : function(){ - //去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标 - var me = this,body = me.body, - range = me.selection.getRange(); - range.selectNodeContents(body); - if(domUtils.isEmptyBlock(body)){ - //opera不能自动合并到元素的里边,要手动处理一下 - if(browser.opera && body.firstChild && body.firstChild.nodeType == 1){ - range.setStartAtFirst(body.firstChild); - } - range.collapse(true); - } - range.select(true); - }, - notNeedUndo : 1 - }; - - - //快捷键 - me.addshortcutkey({ - "selectAll" : "ctrl+65" - }); -}; - - -// plugins/paragraph.js -/** - * 段落样式 - * @file - * @since 1.2.6.1 - */ - -/** - * 段落格式 - * @command paragraph - * @method execCommand - * @param { String } cmd 命令字符串 - * @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' - * @param {Object} attrs 标签的属性 - * @example - * ```javascript - * editor.execCommand( 'Paragraph','h1','{ - * class:'test' - * }' ); - * ``` - */ - -/** - * 返回选区内节点标签名 - * @command paragraph - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 节点标签名 - * @example - * ```javascript - * editor.queryCommandValue( 'Paragraph' ); - * ``` - */ - -UE.plugins['paragraph'] = function() { - var me = this, - block = domUtils.isBlockElm, - notExchange = ['TD','LI','PRE'], - - doParagraph = function(range,style,attrs,sourceCmdName){ - var bookmark = range.createBookmark(), - filterFn = function( node ) { - return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace( node ); - }, - para; - - range.enlarge( true ); - var bookmark2 = range.createBookmark(), - current = domUtils.getNextDomNode( bookmark2.start, false, filterFn ), - tmpRange = range.cloneRange(), - tmpNode; - while ( current && !(domUtils.getPosition( current, bookmark2.end ) & domUtils.POSITION_FOLLOWING) ) { - if ( current.nodeType == 3 || !block( current ) ) { - tmpRange.setStartBefore( current ); - while ( current && current !== bookmark2.end && !block( current ) ) { - tmpNode = current; - current = domUtils.getNextDomNode( current, false, null, function( node ) { - return !block( node ); - } ); - } - tmpRange.setEndAfter( tmpNode ); - - para = range.document.createElement( style ); - if(attrs){ - domUtils.setAttributes(para,attrs); - if(sourceCmdName && sourceCmdName == 'customstyle' && attrs.style){ - para.style.cssText = attrs.style; - } - } - para.appendChild( tmpRange.extractContents() ); - //需要内容占位 - if(domUtils.isEmptyNode(para)){ - domUtils.fillChar(range.document,para); - - } - - tmpRange.insertNode( para ); - - var parent = para.parentNode; - //如果para上一级是一个block元素且不是body,td就删除它 - if ( block( parent ) && !domUtils.isBody( para.parentNode ) && utils.indexOf(notExchange,parent.tagName)==-1) { - //存储dir,style - if(!(sourceCmdName && sourceCmdName == 'customstyle')){ - parent.getAttribute('dir') && para.setAttribute('dir',parent.getAttribute('dir')); - //trace:1070 - parent.style.cssText && (para.style.cssText = parent.style.cssText + ';' + para.style.cssText); - //trace:1030 - parent.style.textAlign && !para.style.textAlign && (para.style.textAlign = parent.style.textAlign); - parent.style.textIndent && !para.style.textIndent && (para.style.textIndent = parent.style.textIndent); - parent.style.padding && !para.style.padding && (para.style.padding = parent.style.padding); - } - - //trace:1706 选择的就是h1-6要删除 - if(attrs && /h\d/i.test(parent.tagName) && !/h\d/i.test(para.tagName) ){ - domUtils.setAttributes(parent,attrs); - if(sourceCmdName && sourceCmdName == 'customstyle' && attrs.style){ - parent.style.cssText = attrs.style; - } - domUtils.remove(para,true); - para = parent; - }else{ - domUtils.remove( para.parentNode, true ); - } - - } - if( utils.indexOf(notExchange,parent.tagName)!=-1){ - current = parent; - }else{ - current = para; - } - - - current = domUtils.getNextDomNode( current, false, filterFn ); - } else { - current = domUtils.getNextDomNode( current, true, filterFn ); - } - } - return range.moveToBookmark( bookmark2 ).moveToBookmark( bookmark ); - }; - me.setOpt('paragraph',{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''}); - me.commands['paragraph'] = { - execCommand : function( cmdName, style,attrs,sourceCmdName ) { - var range = this.selection.getRange(); - //闭合时单独处理 - if(range.collapsed){ - var txt = this.document.createTextNode('p'); - range.insertNode(txt); - //去掉冗余的fillchar - if(browser.ie){ - var node = txt.previousSibling; - if(node && domUtils.isWhitespace(node)){ - domUtils.remove(node); - } - node = txt.nextSibling; - if(node && domUtils.isWhitespace(node)){ - domUtils.remove(node); - } - } - - } - range = doParagraph(range,style,attrs,sourceCmdName); - if(txt){ - range.setStartBefore(txt).collapse(true); - pN = txt.parentNode; - - domUtils.remove(txt); - - if(domUtils.isBlockElm(pN)&&domUtils.isEmptyNode(pN)){ - domUtils.fillNode(this.document,pN); - } - - } - - if(browser.gecko && range.collapsed && range.startContainer.nodeType == 1){ - var child = range.startContainer.childNodes[range.startOffset]; - if(child && child.nodeType == 1 && child.tagName.toLowerCase() == style){ - range.setStart(child,0).collapse(true); - } - } - //trace:1097 原来有true,原因忘了,但去了就不能清除多余的占位符了 - range.select(); - - - return true; - }, - queryCommandValue : function() { - var node = domUtils.filterNodeList(this.selection.getStartElementPath(),'p h1 h2 h3 h4 h5 h6'); - return node ? node.tagName.toLowerCase() : ''; - } - }; -}; - - -// plugins/directionality.js -/** - * 设置文字输入的方向的插件 - * @file - * @since 1.2.6.1 - */ -(function() { - var block = domUtils.isBlockElm , - getObj = function(editor){ -// var startNode = editor.selection.getStart(), -// parents; -// if ( startNode ) { -// //查找所有的是block的父亲节点 -// parents = domUtils.findParents( startNode, true, block, true ); -// for ( var i = 0,ci; ci = parents[i++]; ) { -// if ( ci.getAttribute( 'dir' ) ) { -// return ci; -// } -// } -// } - return domUtils.filterNodeList(editor.selection.getStartElementPath(),function(n){return n && n.nodeType == 1 && n.getAttribute('dir')}); - - }, - doDirectionality = function(range,editor,forward){ - - var bookmark, - filterFn = function( node ) { - return node.nodeType == 1 ? !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node); - }, - - obj = getObj( editor ); - - if ( obj && range.collapsed ) { - obj.setAttribute( 'dir', forward ); - return range; - } - bookmark = range.createBookmark(); - range.enlarge( true ); - var bookmark2 = range.createBookmark(), - current = domUtils.getNextDomNode( bookmark2.start, false, filterFn ), - tmpRange = range.cloneRange(), - tmpNode; - while ( current && !(domUtils.getPosition( current, bookmark2.end ) & domUtils.POSITION_FOLLOWING) ) { - if ( current.nodeType == 3 || !block( current ) ) { - tmpRange.setStartBefore( current ); - while ( current && current !== bookmark2.end && !block( current ) ) { - tmpNode = current; - current = domUtils.getNextDomNode( current, false, null, function( node ) { - return !block( node ); - } ); - } - tmpRange.setEndAfter( tmpNode ); - var common = tmpRange.getCommonAncestor(); - if ( !domUtils.isBody( common ) && block( common ) ) { - //遍历到了block节点 - common.setAttribute( 'dir', forward ); - current = common; - } else { - //没有遍历到,添加一个block节点 - var p = range.document.createElement( 'p' ); - p.setAttribute( 'dir', forward ); - var frag = tmpRange.extractContents(); - p.appendChild( frag ); - tmpRange.insertNode( p ); - current = p; - } - - current = domUtils.getNextDomNode( current, false, filterFn ); - } else { - current = domUtils.getNextDomNode( current, true, filterFn ); - } - } - return range.moveToBookmark( bookmark2 ).moveToBookmark( bookmark ); - }; - - /** - * 文字输入方向 - * @command directionality - * @method execCommand - * @param { String } cmdName 命令字符串 - * @param { String } forward 传入'ltr'表示从左向右输入,传入'rtl'表示从右向左输入 - * @example - * ```javascript - * editor.execCommand( 'directionality', 'ltr'); - * ``` - */ - - /** - * 查询当前选区的文字输入方向 - * @command directionality - * @method queryCommandValue - * @param { String } cmdName 命令字符串 - * @return { String } 返回'ltr'表示从左向右输入,返回'rtl'表示从右向左输入 - * @example - * ```javascript - * editor.queryCommandValue( 'directionality'); - * ``` - */ - UE.commands['directionality'] = { - execCommand : function( cmdName,forward ) { - var range = this.selection.getRange(); - //闭合时单独处理 - if(range.collapsed){ - var txt = this.document.createTextNode('d'); - range.insertNode(txt); - } - doDirectionality(range,this,forward); - if(txt){ - range.setStartBefore(txt).collapse(true); - domUtils.remove(txt); - } - - range.select(); - return true; - }, - queryCommandValue : function() { - var node = getObj(this); - return node ? node.getAttribute('dir') : 'ltr'; - } - }; -})(); - - - -// plugins/horizontal.js -/** - * 插入分割线插件 - * @file - * @since 1.2.6.1 - */ - -/** - * 插入分割线 - * @command horizontal - * @method execCommand - * @param { String } cmdName 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'horizontal' ); - * ``` - */ -UE.plugins['horizontal'] = function(){ - var me = this; - me.commands['horizontal'] = { - execCommand : function( cmdName ) { - var me = this; - if(me.queryCommandState(cmdName)!==-1){ - me.execCommand('insertHtml','
                  '); - var range = me.selection.getRange(), - start = range.startContainer; - if(start.nodeType == 1 && !start.childNodes[range.startOffset] ){ - - var tmp; - if(tmp = start.childNodes[range.startOffset - 1]){ - if(tmp.nodeType == 1 && tmp.tagName == 'HR'){ - if(me.options.enterTag == 'p'){ - tmp = me.document.createElement('p'); - range.insertNode(tmp); - range.setStart(tmp,0).setCursor(); - - }else{ - tmp = me.document.createElement('br'); - range.insertNode(tmp); - range.setStartBefore(tmp).setCursor(); - } - } - } - - } - return true; - } - - }, - //边界在table里不能加分隔线 - queryCommandState : function() { - return domUtils.filterNodeList(this.selection.getStartElementPath(),'table') ? -1 : 0; - } - }; -// me.addListener('delkeyup',function(){ -// var rng = this.selection.getRange(); -// if(browser.ie && browser.version > 8){ -// rng.txtToElmBoundary(true); -// if(domUtils.isStartInblock(rng)){ -// var tmpNode = rng.startContainer; -// var pre = tmpNode.previousSibling; -// if(pre && domUtils.isTagNode(pre,'hr')){ -// domUtils.remove(pre); -// rng.select(); -// return; -// } -// } -// } -// if(domUtils.isBody(rng.startContainer)){ -// var hr = rng.startContainer.childNodes[rng.startOffset -1]; -// if(hr && hr.nodeName == 'HR'){ -// var next = hr.nextSibling; -// if(next){ -// rng.setStart(next,0) -// }else if(hr.previousSibling){ -// rng.setStartAtLast(hr.previousSibling) -// }else{ -// var p = this.document.createElement('p'); -// hr.parentNode.insertBefore(p,hr); -// domUtils.fillNode(this.document,p); -// rng.setStart(p,0); -// } -// domUtils.remove(hr); -// rng.setCursor(false,true); -// } -// } -// }) - me.addListener('delkeydown',function(name,evt){ - var rng = this.selection.getRange(); - rng.txtToElmBoundary(true); - if(domUtils.isStartInblock(rng)){ - var tmpNode = rng.startContainer; - var pre = tmpNode.previousSibling; - if(pre && domUtils.isTagNode(pre,'hr')){ - domUtils.remove(pre); - rng.select(); - domUtils.preventDefault(evt); - return true; - - } - } - - }) -}; - - - -// plugins/time.js -/** - * 插入时间和日期 - * @file - * @since 1.2.6.1 - */ - -/** - * 插入时间,默认格式:12:59:59 - * @command time - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'time'); - * ``` - */ - -/** - * 插入日期,默认格式:2013-08-30 - * @command date - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'date'); - * ``` - */ -UE.commands['time'] = UE.commands["date"] = { - execCommand : function(cmd, format){ - var date = new Date; - - function formatTime(date, format) { - var hh = ('0' + date.getHours()).slice(-2), - ii = ('0' + date.getMinutes()).slice(-2), - ss = ('0' + date.getSeconds()).slice(-2); - format = format || 'hh:ii:ss'; - return format.replace(/hh/ig, hh).replace(/ii/ig, ii).replace(/ss/ig, ss); - } - function formatDate(date, format) { - var yyyy = ('000' + date.getFullYear()).slice(-4), - yy = yyyy.slice(-2), - mm = ('0' + (date.getMonth()+1)).slice(-2), - dd = ('0' + date.getDate()).slice(-2); - format = format || 'yyyy-mm-dd'; - return format.replace(/yyyy/ig, yyyy).replace(/yy/ig, yy).replace(/mm/ig, mm).replace(/dd/ig, dd); - } - - this.execCommand('insertHtml',cmd == "time" ? formatTime(date, format):formatDate(date, format) ); - } -}; - - -// plugins/rowspacing.js -/** - * 段前段后间距插件 - * @file - * @since 1.2.6.1 - */ - -/** - * 设置段间距 - * @command rowspacing - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } value 段间距的值,以px为单位 - * @param { String } dir 间距位置,top或bottom,分别表示段前和段后 - * @example - * ```javascript - * editor.execCommand( 'rowspacing', '10', 'top' ); - * ``` - */ - -UE.plugins['rowspacing'] = function(){ - var me = this; - me.setOpt({ - 'rowspacingtop':['5', '10', '15', '20', '25'], - 'rowspacingbottom':['5', '10', '15', '20', '25'] - - }); - me.commands['rowspacing'] = { - execCommand : function( cmdName,value,dir ) { - this.execCommand('paragraph','p',{style:'margin-'+dir+':'+value + 'px'}); - return true; - }, - queryCommandValue : function(cmdName,dir) { - var pN = domUtils.filterNodeList(this.selection.getStartElementPath(),function(node){return domUtils.isBlockElm(node) }), - value; - //trace:1026 - if(pN){ - value = domUtils.getComputedStyle(pN,'margin-'+dir).replace(/[^\d]/g,''); - return !value ? 0 : value; - } - return 0; - - } - }; -}; - - - - -// plugins/lineheight.js -/** - * 设置行内间距 - * @file - * @since 1.2.6.1 - */ -UE.plugins['lineheight'] = function(){ - var me = this; - me.setOpt({'lineheight':['1', '1.5','1.75','2', '3', '4', '5']}); - - /** - * 行距 - * @command lineheight - * @method execCommand - * @param { String } cmdName 命令字符串 - * @param { String } value 传入的行高值, 该值是当前字体的倍数, 例如: 1.5, 1.75 - * @example - * ```javascript - * editor.execCommand( 'lineheight', 1.5); - * ``` - */ - /** - * 查询当前选区内容的行高大小 - * @command lineheight - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 返回当前行高大小 - * @example - * ```javascript - * editor.queryCommandValue( 'lineheight' ); - * ``` - */ - - me.commands['lineheight'] = { - execCommand : function( cmdName,value ) { - this.execCommand('paragraph','p',{style:'line-height:'+ (value == "1" ? "normal" : value + 'em') }); - return true; - }, - queryCommandValue : function() { - var pN = domUtils.filterNodeList(this.selection.getStartElementPath(),function(node){return domUtils.isBlockElm(node)}); - if(pN){ - var value = domUtils.getComputedStyle(pN,'line-height'); - return value == 'normal' ? 1 : value.replace(/[^\d.]*/ig,""); - } - } - }; -}; - - - - -// plugins/insertcode.js -/** - * 插入代码插件 - * @file - * @since 1.2.6.1 - */ - -UE.plugins['insertcode'] = function() { - var me = this; - me.ready(function(){ - utils.cssRule('pre','pre{margin:.5em 0;padding:.4em .6em;border-radius:8px;background:#f8f8f8;}', - me.document) - }); - me.setOpt('insertcode',{ - 'as3':'ActionScript3', - 'bash':'Bash/Shell', - 'cpp':'C/C++', - 'css':'Css', - 'cf':'CodeFunction', - 'c#':'C#', - 'delphi':'Delphi', - 'diff':'Diff', - 'erlang':'Erlang', - 'groovy':'Groovy', - 'html':'Html', - 'java':'Java', - 'jfx':'JavaFx', - 'js':'Javascript', - 'pl':'Perl', - 'php':'Php', - 'plain':'Plain Text', - 'ps':'PowerShell', - 'python':'Python', - 'ruby':'Ruby', - 'scala':'Scala', - 'sql':'Sql', - 'vb':'Vb', - 'xml':'Xml' - }); - - /** - * 插入代码 - * @command insertcode - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } lang 插入代码的语言 - * @example - * ```javascript - * editor.execCommand( 'insertcode', 'javascript' ); - * ``` - */ - - /** - * 如果选区所在位置是插入插入代码区域,返回代码的语言 - * @command insertcode - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 返回代码的语言 - * @example - * ```javascript - * editor.queryCommandValue( 'insertcode' ); - * ``` - */ - - me.commands['insertcode'] = { - execCommand : function(cmd,lang){ - var me = this, - rng = me.selection.getRange(), - pre = domUtils.findParentByTagName(rng.startContainer,'pre',true); - if(pre){ - pre.className = 'brush:'+lang+';toolbar:false;'; - }else{ - var code = ''; - if(rng.collapsed){ - code = browser.ie && browser.ie11below ? (browser.version <= 8 ? ' ':''):'
                  '; - }else{ - var frag = rng.extractContents(); - var div = me.document.createElement('div'); - div.appendChild(frag); - - utils.each(UE.filterNode(UE.htmlparser(div.innerHTML.replace(/[\r\t]/g,'')),me.options.filterTxtRules).children,function(node){ - if(browser.ie && browser.ie11below && browser.version > 8){ - - if(node.type =='element'){ - if(node.tagName == 'br'){ - code += '\n' - }else if(!dtd.$empty[node.tagName]){ - utils.each(node.children,function(cn){ - if(cn.type =='element'){ - if(cn.tagName == 'br'){ - code += '\n' - }else if(!dtd.$empty[node.tagName]){ - code += cn.innerText(); - } - }else{ - code += cn.data - } - }) - if(!/\n$/.test(code)){ - code += '\n'; - } - } - }else{ - code += node.data + '\n' - } - if(!node.nextSibling() && /\n$/.test(code)){ - code = code.replace(/\n$/,''); - } - }else{ - if(browser.ie && browser.ie11below){ - - if(node.type =='element'){ - if(node.tagName == 'br'){ - code += '
                  ' - }else if(!dtd.$empty[node.tagName]){ - utils.each(node.children,function(cn){ - if(cn.type =='element'){ - if(cn.tagName == 'br'){ - code += '
                  ' - }else if(!dtd.$empty[node.tagName]){ - code += cn.innerText(); - } - }else{ - code += cn.data - } - }); - if(!/br>$/.test(code)){ - code += '
                  '; - } - } - }else{ - code += node.data + '
                  ' - } - if(!node.nextSibling() && /
                  $/.test(code)){ - code = code.replace(/
                  $/,''); - } - - }else{ - code += (node.type == 'element' ? (dtd.$empty[node.tagName] ? '' : node.innerText()) : node.data); - if(!/br\/?\s*>$/.test(code)){ - if(!node.nextSibling()) - return; - code += '
                  ' - } - } - - } - - }); - } - me.execCommand('inserthtml','
                  '+code+'
                  ',true); - - pre = me.document.getElementById('coder'); - domUtils.removeAttributes(pre,'id'); - var tmpNode = pre.previousSibling; - - if(tmpNode && (tmpNode.nodeType == 3 && tmpNode.nodeValue.length == 1 && browser.ie && browser.version == 6 || domUtils.isEmptyBlock(tmpNode))){ - - domUtils.remove(tmpNode) - } - var rng = me.selection.getRange(); - if(domUtils.isEmptyBlock(pre)){ - rng.setStart(pre,0).setCursor(false,true) - }else{ - rng.selectNodeContents(pre).select() - } - } - - - - }, - queryCommandValue : function(){ - var path = this.selection.getStartElementPath(); - var lang = ''; - utils.each(path,function(node){ - if(node.nodeName =='PRE'){ - var match = node.className.match(/brush:([^;]+)/); - lang = match && match[1] ? match[1] : ''; - return false; - } - }); - return lang; - } - }; - - me.addInputRule(function(root){ - utils.each(root.getNodesByTagName('pre'),function(pre){ - var brs = pre.getNodesByTagName('br'); - if(brs.length){ - browser.ie && browser.ie11below && browser.version > 8 && utils.each(brs,function(br){ - var txt = UE.uNode.createText('\n'); - br.parentNode.insertBefore(txt,br); - br.parentNode.removeChild(br); - }); - return; - } - if(browser.ie && browser.ie11below && browser.version > 8) - return; - var code = pre.innerText().split(/\n/); - pre.innerHTML(''); - utils.each(code,function(c){ - if(c.length){ - pre.appendChild(UE.uNode.createText(c)); - } - pre.appendChild(UE.uNode.createElement('br')) - }) - }) - }); - me.addOutputRule(function(root){ - utils.each(root.getNodesByTagName('pre'),function(pre){ - var code = ''; - utils.each(pre.children,function(n){ - if(n.type == 'text'){ - //在ie下文本内容有可能末尾带有\n要去掉 - //trace:3396 - code += n.data.replace(/[ ]/g,' ').replace(/\n$/,''); - }else{ - if(n.tagName == 'br'){ - code += '\n' - }else{ - code += (!dtd.$empty[n.tagName] ? '' : n.innerText()); - } - - } - - }); - - pre.innerText(code.replace(/( |\n)+$/,'')) - }) - }); - //不需要判断highlight的command列表 - me.notNeedCodeQuery ={ - help:1, - undo:1, - redo:1, - source:1, - print:1, - searchreplace:1, - fullscreen:1, - preview:1, - insertparagraph:1, - elementpath:1, - insertcode:1, - inserthtml:1, - selectall:1 - }; - //将queyCommamndState重置 - var orgQuery = me.queryCommandState; - me.queryCommandState = function(cmd){ - var me = this; - - if(!me.notNeedCodeQuery[cmd.toLowerCase()] && me.selection && me.queryCommandValue('insertcode')){ - return -1; - } - return UE.Editor.prototype.queryCommandState.apply(this,arguments) - }; - me.addListener('beforeenterkeydown',function(){ - var rng = me.selection.getRange(); - var pre = domUtils.findParentByTagName(rng.startContainer,'pre',true); - if(pre){ - me.fireEvent('saveScene'); - if(!rng.collapsed){ - rng.deleteContents(); - } - if(!browser.ie || browser.ie9above){ - var tmpNode = me.document.createElement('br'),pre; - rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true); - var next = tmpNode.nextSibling; - if(!next && (!browser.ie || browser.version > 10)){ - rng.insertNode(tmpNode.cloneNode(false)); - }else{ - rng.setStartAfter(tmpNode); - } - pre = tmpNode.previousSibling; - var tmp; - while(pre ){ - tmp = pre; - pre = pre.previousSibling; - if(!pre || pre.nodeName == 'BR'){ - pre = tmp; - break; - } - } - if(pre){ - var str = ''; - while(pre && pre.nodeName != 'BR' && new RegExp('^[\\s'+domUtils.fillChar+']*$').test(pre.nodeValue)){ - str += pre.nodeValue; - pre = pre.nextSibling; - } - if(pre.nodeName != 'BR'){ - var match = pre.nodeValue.match(new RegExp('^([\\s'+domUtils.fillChar+']+)')); - if(match && match[1]){ - str += match[1] - } - - } - if(str){ - str = me.document.createTextNode(str); - rng.insertNode(str).setStartAfter(str); - } - } - rng.collapse(true).select(true); - }else{ - if(browser.version > 8){ - - var txt = me.document.createTextNode('\n'); - var start = rng.startContainer; - if(rng.startOffset == 0){ - var preNode = start.previousSibling; - if(preNode){ - rng.insertNode(txt); - var fillchar = me.document.createTextNode(' '); - rng.setStartAfter(txt).insertNode(fillchar).setStart(fillchar,0).collapse(true).select(true) - } - }else{ - rng.insertNode(txt).setStartAfter(txt); - var fillchar = me.document.createTextNode(' '); - start = rng.startContainer.childNodes[rng.startOffset]; - if(start && !/^\n/.test(start.nodeValue)){ - rng.setStartBefore(txt) - } - rng.insertNode(fillchar).setStart(fillchar,0).collapse(true).select(true) - } - - }else{ - var tmpNode = me.document.createElement('br'); - rng.insertNode(tmpNode); - rng.insertNode(me.document.createTextNode(domUtils.fillChar)); - rng.setStartAfter(tmpNode); - pre = tmpNode.previousSibling; - var tmp; - while(pre ){ - tmp = pre; - pre = pre.previousSibling; - if(!pre || pre.nodeName == 'BR'){ - pre = tmp; - break; - } - } - if(pre){ - var str = ''; - while(pre && pre.nodeName != 'BR' && new RegExp('^[ '+domUtils.fillChar+']*$').test(pre.nodeValue)){ - str += pre.nodeValue; - pre = pre.nextSibling; - } - if(pre.nodeName != 'BR'){ - var match = pre.nodeValue.match(new RegExp('^([ '+domUtils.fillChar+']+)')); - if(match && match[1]){ - str += match[1] - } - - } - - str = me.document.createTextNode(str); - rng.insertNode(str).setStartAfter(str); - } - rng.collapse(true).select(); - } - - - } - me.fireEvent('saveScene'); - return true; - } - - - }); - - me.addListener('tabkeydown',function(cmd,evt){ - var rng = me.selection.getRange(); - var pre = domUtils.findParentByTagName(rng.startContainer,'pre',true); - if(pre){ - me.fireEvent('saveScene'); - if(evt.shiftKey){ - - }else{ - if(!rng.collapsed){ - var bk = rng.createBookmark(); - var start = bk.start.previousSibling; - - while(start){ - if(pre.firstChild === start && !domUtils.isBr(start)){ - pre.insertBefore(me.document.createTextNode(' '),start); - - break; - } - if(domUtils.isBr(start)){ - pre.insertBefore(me.document.createTextNode(' '),start.nextSibling); - - break; - } - start = start.previousSibling; - } - var end = bk.end; - start = bk.start.nextSibling; - if(pre.firstChild === bk.start){ - pre.insertBefore(me.document.createTextNode(' '),start.nextSibling) - - } - while(start && start !== end){ - if(domUtils.isBr(start) && start.nextSibling){ - if(start.nextSibling === end){ - break; - } - pre.insertBefore(me.document.createTextNode(' '),start.nextSibling) - } - - start = start.nextSibling; - } - rng.moveToBookmark(bk).select(); - }else{ - var tmpNode = me.document.createTextNode(' '); - rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true).select(true); - } - } - - - me.fireEvent('saveScene'); - return true; - } - - - }); - - - me.addListener('beforeinserthtml',function(evtName,html){ - var me = this, - rng = me.selection.getRange(), - pre = domUtils.findParentByTagName(rng.startContainer,'pre',true); - if(pre){ - if(!rng.collapsed){ - rng.deleteContents() - } - var htmlstr = ''; - if(browser.ie && browser.version > 8){ - - utils.each(UE.filterNode(UE.htmlparser(html),me.options.filterTxtRules).children,function(node){ - if(node.type =='element'){ - if(node.tagName == 'br'){ - htmlstr += '\n' - }else if(!dtd.$empty[node.tagName]){ - utils.each(node.children,function(cn){ - if(cn.type =='element'){ - if(cn.tagName == 'br'){ - htmlstr += '\n' - }else if(!dtd.$empty[node.tagName]){ - htmlstr += cn.innerText(); - } - }else{ - htmlstr += cn.data - } - }) - if(!/\n$/.test(htmlstr)){ - htmlstr += '\n'; - } - } - }else{ - htmlstr += node.data + '\n' - } - if(!node.nextSibling() && /\n$/.test(htmlstr)){ - htmlstr = htmlstr.replace(/\n$/,''); - } - }); - var tmpNode = me.document.createTextNode(utils.html(htmlstr.replace(/ /g,' '))); - rng.insertNode(tmpNode).selectNode(tmpNode).select(); - }else{ - var frag = me.document.createDocumentFragment(); - - utils.each(UE.filterNode(UE.htmlparser(html),me.options.filterTxtRules).children,function(node){ - if(node.type =='element'){ - if(node.tagName == 'br'){ - frag.appendChild(me.document.createElement('br')) - }else if(!dtd.$empty[node.tagName]){ - utils.each(node.children,function(cn){ - if(cn.type =='element'){ - if(cn.tagName == 'br'){ - - frag.appendChild(me.document.createElement('br')) - }else if(!dtd.$empty[node.tagName]){ - frag.appendChild(me.document.createTextNode(utils.html(cn.innerText().replace(/ /g,' ')))); - - } - }else{ - frag.appendChild(me.document.createTextNode(utils.html( cn.data.replace(/ /g,' ')))); - - } - }) - if(frag.lastChild.nodeName != 'BR'){ - frag.appendChild(me.document.createElement('br')) - } - } - }else{ - frag.appendChild(me.document.createTextNode(utils.html( node.data.replace(/ /g,' ')))); - } - if(!node.nextSibling() && frag.lastChild.nodeName == 'BR'){ - frag.removeChild(frag.lastChild) - } - - - }); - rng.insertNode(frag).select(); - - } - - return true; - } - }); - //方向键的处理 - me.addListener('keydown',function(cmd,evt){ - var me = this,keyCode = evt.keyCode || evt.which; - if(keyCode == 40){ - var rng = me.selection.getRange(),pre,start = rng.startContainer; - if(rng.collapsed && (pre = domUtils.findParentByTagName(rng.startContainer,'pre',true)) && !pre.nextSibling){ - var last = pre.lastChild - while(last && last.nodeName == 'BR'){ - last = last.previousSibling; - } - if(last === start || rng.startContainer === pre && rng.startOffset == pre.childNodes.length){ - me.execCommand('insertparagraph'); - domUtils.preventDefault(evt) - } - - } - } - }); - //trace:3395 - me.addListener('delkeydown',function(type,evt){ - var rng = this.selection.getRange(); - rng.txtToElmBoundary(true); - var start = rng.startContainer; - if(domUtils.isTagNode(start,'pre') && rng.collapsed && domUtils.isStartInblock(rng)){ - var p = me.document.createElement('p'); - domUtils.fillNode(me.document,p); - start.parentNode.insertBefore(p,start); - domUtils.remove(start); - rng.setStart(p,0).setCursor(false,true); - domUtils.preventDefault(evt); - return true; - } - }) -}; - - -// plugins/cleardoc.js -/** - * 清空文档插件 - * @file - * @since 1.2.6.1 - */ - -/** - * 清空文档 - * @command cleardoc - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * //editor 是编辑器实例 - * editor.execCommand('cleardoc'); - * ``` - */ - -UE.commands['cleardoc'] = { - execCommand : function( cmdName) { - var me = this, - enterTag = me.options.enterTag, - range = me.selection.getRange(); - if(enterTag == "br"){ - me.body.innerHTML = "
                  "; - range.setStart(me.body,0).setCursor(); - }else{ - me.body.innerHTML = "

                  "+(ie ? "" : "
                  ")+"

                  "; - range.setStart(me.body.firstChild,0).setCursor(false,true); - } - setTimeout(function(){ - me.fireEvent("clearDoc"); - },0); - - } -}; - - - -// plugins/anchor.js -/** - * 锚点插件,为UEditor提供插入锚点支持 - * @file - * @since 1.2.6.1 - */ -UE.plugin.register('anchor', function (){ - - return { - bindEvents:{ - 'ready':function(){ - utils.cssRule('anchor', - '.anchorclass{background: url(\'' - + this.options.themePath - + this.options.theme +'/images/anchor.gif\') no-repeat scroll left center transparent;cursor: auto;display: inline-block;height: 16px;width: 15px;}', - this.document); - } - }, - outputRule: function(root){ - utils.each(root.getNodesByTagName('img'),function(a){ - var val; - if(val = a.getAttr('anchorname')){ - a.tagName = 'a'; - a.setAttr({ - anchorname : '', - name : val, - 'class' : '' - }) - } - }) - }, - inputRule:function(root){ - utils.each(root.getNodesByTagName('a'),function(a){ - var val; - if((val = a.getAttr('name')) && !a.getAttr('href')){ - a.tagName = 'img'; - a.setAttr({ - anchorname :a.getAttr('name'), - 'class' : 'anchorclass' - }); - a.setAttr('name') - - } - }) - - }, - commands:{ - /** - * 插入锚点 - * @command anchor - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } name 锚点名称字符串 - * @example - * ```javascript - * //editor 是编辑器实例 - * editor.execCommand('anchor', 'anchor1'); - * ``` - */ - 'anchor':{ - execCommand:function (cmd, name) { - var range = this.selection.getRange(),img = range.getClosedNode(); - if (img && img.getAttribute('anchorname')) { - if (name) { - img.setAttribute('anchorname', name); - } else { - range.setStartBefore(img).setCursor(); - domUtils.remove(img); - } - } else { - if (name) { - //只在选区的开始插入 - var anchor = this.document.createElement('img'); - range.collapse(true); - domUtils.setAttributes(anchor,{ - 'anchorname':name, - 'class':'anchorclass' - }); - range.insertNode(anchor).setStartAfter(anchor).setCursor(false,true); - } - } - } - } - } - } -}); - - -// plugins/wordcount.js -///import core -///commands 字数统计 -///commandsName WordCount,wordCount -///commandsTitle 字数统计 -/* - * Created by JetBrains WebStorm. - * User: taoqili - * Date: 11-9-7 - * Time: 下午8:18 - * To change this template use File | Settings | File Templates. - */ - -UE.plugins['wordcount'] = function(){ - var me = this; - me.setOpt('wordCount',true); - me.addListener('contentchange',function(){ - me.fireEvent('wordcount'); - }); - var timer; - me.addListener('ready',function(){ - var me = this; - domUtils.on(me.body,"keyup",function(evt){ - var code = evt.keyCode||evt.which, - //忽略的按键,ctr,alt,shift,方向键 - ignores = {"16":1,"18":1,"20":1,"37":1,"38":1,"39":1,"40":1}; - if(code in ignores) return; - clearTimeout(timer); - timer = setTimeout(function(){ - me.fireEvent('wordcount'); - },200) - }) - }); -}; - - -// plugins/pagebreak.js -/** - * 分页功能插件 - * @file - * @since 1.2.6.1 - */ -UE.plugins['pagebreak'] = function () { - var me = this, - notBreakTags = ['td']; - me.setOpt('pageBreakTag','_ueditor_page_break_tag_'); - - function fillNode(node){ - if(domUtils.isEmptyBlock(node)){ - var firstChild = node.firstChild,tmpNode; - - while(firstChild && firstChild.nodeType == 1 && domUtils.isEmptyBlock(firstChild)){ - tmpNode = firstChild; - firstChild = firstChild.firstChild; - } - !tmpNode && (tmpNode = node); - domUtils.fillNode(me.document,tmpNode); - } - } - //分页符样式添加 - - me.ready(function(){ - utils.cssRule('pagebreak','.pagebreak{display:block;clear:both !important;cursor:default !important;width: 100% !important;margin:0;}',me.document); - }); - function isHr(node){ - return node && node.nodeType == 1 && node.tagName == 'HR' && node.className == 'pagebreak'; - } - me.addInputRule(function(root){ - root.traversal(function(node){ - if(node.type == 'text' && node.data == me.options.pageBreakTag){ - var hr = UE.uNode.createElement('
                  '); - node.parentNode.insertBefore(hr,node); - node.parentNode.removeChild(node) - } - }) - }); - me.addOutputRule(function(node){ - utils.each(node.getNodesByTagName('hr'),function(n){ - if(n.getAttr('class') == 'pagebreak'){ - var txt = UE.uNode.createText(me.options.pageBreakTag); - n.parentNode.insertBefore(txt,n); - n.parentNode.removeChild(n); - } - }) - - }); - - /** - * 插入分页符 - * @command pagebreak - * @method execCommand - * @param { String } cmd 命令字符串 - * @remind 在表格中插入分页符会把表格切分成两部分 - * @remind 获取编辑器内的数据时, 编辑器会把分页符转换成“_ueditor_page_break_tag_”字符串, - * 以便于提交数据到服务器端后处理分页。 - * @example - * ```javascript - * editor.execCommand( 'pagebreak'); //插入一个hr标签,带有样式类名pagebreak - * ``` - */ - - me.commands['pagebreak'] = { - execCommand:function () { - var range = me.selection.getRange(),hr = me.document.createElement('hr'); - domUtils.setAttributes(hr,{ - 'class' : 'pagebreak', - noshade:"noshade", - size:"5" - }); - domUtils.unSelectable(hr); - //table单独处理 - var node = domUtils.findParentByTagName(range.startContainer, notBreakTags, true), - - parents = [], pN; - if (node) { - switch (node.tagName) { - case 'TD': - pN = node.parentNode; - if (!pN.previousSibling) { - var table = domUtils.findParentByTagName(pN, 'table'); -// var tableWrapDiv = table.parentNode; -// if(tableWrapDiv && tableWrapDiv.nodeType == 1 -// && tableWrapDiv.tagName == 'DIV' -// && tableWrapDiv.getAttribute('dropdrag') -// ){ -// domUtils.remove(tableWrapDiv,true); -// } - table.parentNode.insertBefore(hr, table); - parents = domUtils.findParents(hr, true); - - } else { - pN.parentNode.insertBefore(hr, pN); - parents = domUtils.findParents(hr); - - } - pN = parents[1]; - if (hr !== pN) { - domUtils.breakParent(hr, pN); - - } - //table要重写绑定一下拖拽 - me.fireEvent('afteradjusttable',me.document); - } - - } else { - - if (!range.collapsed) { - range.deleteContents(); - var start = range.startContainer; - while ( !domUtils.isBody(start) && domUtils.isBlockElm(start) && domUtils.isEmptyNode(start)) { - range.setStartBefore(start).collapse(true); - domUtils.remove(start); - start = range.startContainer; - } - - } - range.insertNode(hr); - - var pN = hr.parentNode, nextNode; - while (!domUtils.isBody(pN)) { - domUtils.breakParent(hr, pN); - nextNode = hr.nextSibling; - if (nextNode && domUtils.isEmptyBlock(nextNode)) { - domUtils.remove(nextNode); - } - pN = hr.parentNode; - } - nextNode = hr.nextSibling; - var pre = hr.previousSibling; - if(isHr(pre)){ - domUtils.remove(pre); - }else{ - pre && fillNode(pre); - } - - if(!nextNode){ - var p = me.document.createElement('p'); - - hr.parentNode.appendChild(p); - domUtils.fillNode(me.document,p); - range.setStart(p,0).collapse(true); - }else{ - if(isHr(nextNode)){ - domUtils.remove(nextNode); - }else{ - fillNode(nextNode); - } - range.setEndAfter(hr).collapse(false); - } - - range.select(true); - - } - - } - }; -}; - -// plugins/wordimage.js -///import core -///commands 本地图片引导上传 -///commandsName WordImage -///commandsTitle 本地图片引导上传 -///commandsDialog dialogs\wordimage - -UE.plugin.register('wordimage',function(){ - var me = this, - images = []; - return { - commands : { - 'wordimage':{ - execCommand:function () { - var images = domUtils.getElementsByTagName(me.body, "img"); - var urlList = []; - for (var i = 0, ci; ci = images[i++];) { - var url = ci.getAttribute("word_img"); - url && urlList.push(url); - } - return urlList; - }, - queryCommandState:function () { - images = domUtils.getElementsByTagName(me.body, "img"); - for (var i = 0, ci; ci = images[i++];) { - if (ci.getAttribute("word_img")) { - return 1; - } - } - return -1; - }, - notNeedUndo:true - } - }, - inputRule : function (root) { - utils.each(root.getNodesByTagName('img'), function (img) { - var attrs = img.attrs, - flag = parseInt(attrs.width) < 128 || parseInt(attrs.height) < 43, - opt = me.options, - src = opt.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif'; - if (attrs['src'] && /^(?:(file:\/+))/.test(attrs['src'])) { - img.setAttr({ - width:attrs.width, - height:attrs.height, - alt:attrs.alt, - word_img: attrs.src, - src:src, - 'style':'background:url(' + ( flag ? opt.themePath + opt.theme + '/images/word.gif' : opt.langPath + opt.lang + '/images/localimage.png') + ') no-repeat center center;border:1px solid #ddd' - }) - } - }) - } - } -}); - -// plugins/dragdrop.js -UE.plugins['dragdrop'] = function (){ - - var me = this; - me.ready(function(){ - domUtils.on(this.body,'dragend',function(){ - var rng = me.selection.getRange(); - var node = rng.getClosedNode()||me.selection.getStart(); - - if(node && node.tagName == 'IMG'){ - - var pre = node.previousSibling,next; - while(next = node.nextSibling){ - if(next.nodeType == 1 && next.tagName == 'SPAN' && !next.firstChild){ - domUtils.remove(next) - }else{ - break; - } - } - - - if((pre && pre.nodeType == 1 && !domUtils.isEmptyBlock(pre) || !pre) && (!next || next && !domUtils.isEmptyBlock(next))){ - if(pre && pre.tagName == 'P' && !domUtils.isEmptyBlock(pre)){ - pre.appendChild(node); - domUtils.moveChild(next,pre); - domUtils.remove(next); - }else if(next && next.tagName == 'P' && !domUtils.isEmptyBlock(next)){ - next.insertBefore(node,next.firstChild); - } - - if(pre && pre.tagName == 'P' && domUtils.isEmptyBlock(pre)){ - domUtils.remove(pre) - } - if(next && next.tagName == 'P' && domUtils.isEmptyBlock(next)){ - domUtils.remove(next) - } - rng.selectNode(node).select(); - me.fireEvent('saveScene'); - - } - - } - - }) - }); - me.addListener('keyup', function(type, evt) { - var keyCode = evt.keyCode || evt.which; - if (keyCode == 13) { - var rng = me.selection.getRange(),node; - if(node = domUtils.findParentByTagName(rng.startContainer,'p',true)){ - if(domUtils.getComputedStyle(node,'text-align') == 'center'){ - domUtils.removeStyle(node,'text-align') - } - } - } - }) -}; - - -// plugins/undo.js -/** - * undo redo - * @file - * @since 1.2.6.1 - */ - -/** - * 撤销上一次执行的命令 - * @command undo - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'undo' ); - * ``` - */ - -/** - * 重做上一次执行的命令 - * @command redo - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'redo' ); - * ``` - */ - -UE.plugins['undo'] = function () { - var saveSceneTimer; - var me = this, - maxUndoCount = me.options.maxUndoCount || 20, - maxInputCount = me.options.maxInputCount || 20, - fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的 - var noNeedFillCharTags = { - ol:1,ul:1,table:1,tbody:1,tr:1,body:1 - }; - var orgState = me.options.autoClearEmptyNode; - function compareAddr(indexA, indexB) { - if (indexA.length != indexB.length) - return 0; - for (var i = 0, l = indexA.length; i < l; i++) { - if (indexA[i] != indexB[i]) - return 0 - } - return 1; - } - - function compareRangeAddress(rngAddrA, rngAddrB) { - if (rngAddrA.collapsed != rngAddrB.collapsed) { - return 0; - } - if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) { - return 0; - } - return 1; - } - - function UndoManager() { - this.list = []; - this.index = 0; - this.hasUndo = false; - this.hasRedo = false; - this.undo = function () { - if (this.hasUndo) { - if (!this.list[this.index - 1] && this.list.length == 1) { - this.reset(); - return; - } - while (this.list[this.index].content == this.list[this.index - 1].content) { - this.index--; - if (this.index == 0) { - return this.restore(0); - } - } - this.restore(--this.index); - } - }; - this.redo = function () { - if (this.hasRedo) { - while (this.list[this.index].content == this.list[this.index + 1].content) { - this.index++; - if (this.index == this.list.length - 1) { - return this.restore(this.index); - } - } - this.restore(++this.index); - } - }; - - this.restore = function () { - var me = this.editor; - var scene = this.list[this.index]; - var root = UE.htmlparser(scene.content.replace(fillchar, '')); - me.options.autoClearEmptyNode = false; - me.filterInputRule(root); - me.options.autoClearEmptyNode = orgState; - //trace:873 - //去掉展位符 - me.document.body.innerHTML = root.toHtml(); - me.fireEvent('afterscencerestore'); - //处理undo后空格不展位的问题 - if (browser.ie) { - utils.each(domUtils.getElementsByTagName(me.document,'td th caption p'),function(node){ - if(domUtils.isEmptyNode(node)){ - domUtils.fillNode(me.document, node); - } - }) - } - - try{ - var rng = new dom.Range(me.document).moveToAddress(scene.address); - rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]); - }catch(e){} - - this.update(); - this.clearKey(); - //不能把自己reset了 - me.fireEvent('reset', true); - }; - - this.getScene = function () { - var me = this.editor; - var rng = me.selection.getRange(), - rngAddress = rng.createAddress(false,true); - me.fireEvent('beforegetscene'); - var root = UE.htmlparser(me.body.innerHTML); - me.options.autoClearEmptyNode = false; - me.filterOutputRule(root); - me.options.autoClearEmptyNode = orgState; - var cont = root.toHtml(); - //trace:3461 - //这个会引起回退时导致空格丢失的情况 -// browser.ie && (cont = cont.replace(/> <').replace(/\s*\s*/g, '>')); - me.fireEvent('aftergetscene'); - - return { - address:rngAddress, - content:cont - } - }; - this.save = function (notCompareRange,notSetCursor) { - clearTimeout(saveSceneTimer); - var currentScene = this.getScene(notSetCursor), - lastScene = this.list[this.index]; - - if(lastScene && lastScene.content != currentScene.content){ - me.trigger('contentchange') - } - //内容相同位置相同不存 - if (lastScene && lastScene.content == currentScene.content && - ( notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address) ) - ) { - return; - } - this.list = this.list.slice(0, this.index + 1); - this.list.push(currentScene); - //如果大于最大数量了,就把最前的剔除 - if (this.list.length > maxUndoCount) { - this.list.shift(); - } - this.index = this.list.length - 1; - this.clearKey(); - //跟新undo/redo状态 - this.update(); - - }; - this.update = function () { - this.hasRedo = !!this.list[this.index + 1]; - this.hasUndo = !!this.list[this.index - 1]; - }; - this.reset = function () { - this.list = []; - this.index = 0; - this.hasUndo = false; - this.hasRedo = false; - this.clearKey(); - }; - this.clearKey = function () { - keycont = 0; - lastKeyCode = null; - }; - } - - me.undoManger = new UndoManager(); - me.undoManger.editor = me; - function saveScene() { - this.undoManger.save(); - } - - me.addListener('saveScene', function () { - var args = Array.prototype.splice.call(arguments,1); - this.undoManger.save.apply(this.undoManger,args); - }); - -// me.addListener('beforeexeccommand', saveScene); -// me.addListener('afterexeccommand', saveScene); - - me.addListener('reset', function (type, exclude) { - if (!exclude) { - this.undoManger.reset(); - } - }); - me.commands['redo'] = me.commands['undo'] = { - execCommand:function (cmdName) { - this.undoManger[cmdName](); - }, - queryCommandState:function (cmdName) { - return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1; - }, - notNeedUndo:1 - }; - - var keys = { - // /*Backspace*/ 8:1, /*Delete*/ 46:1, - /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1, - 37:1, 38:1, 39:1, 40:1 - - }, - keycont = 0, - lastKeyCode; - //输入法状态下不计算字符数 - var inputType = false; - me.addListener('ready', function () { - domUtils.on(this.body, 'compositionstart', function () { - inputType = true; - }); - domUtils.on(this.body, 'compositionend', function () { - inputType = false; - }) - }); - //快捷键 - me.addshortcutkey({ - "Undo":"ctrl+90", //undo - "Redo":"ctrl+89" //redo - - }); - var isCollapsed = true; - me.addListener('keydown', function (type, evt) { - - var me = this; - var keyCode = evt.keyCode || evt.which; - if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) { - if (inputType) - return; - - if(!me.selection.getRange().collapsed){ - me.undoManger.save(false,true); - isCollapsed = false; - return; - } - if (me.undoManger.list.length == 0) { - me.undoManger.save(true); - } - clearTimeout(saveSceneTimer); - function save(cont){ - cont.undoManger.save(false,true); - cont.fireEvent('selectionchange'); - } - saveSceneTimer = setTimeout(function(){ - if(inputType){ - var interalTimer = setInterval(function(){ - if(!inputType){ - save(me); - clearInterval(interalTimer) - } - },300) - return; - } - save(me); - },200); - - lastKeyCode = keyCode; - keycont++; - if (keycont >= maxInputCount ) { - save(me) - } - } - }); - me.addListener('keyup', function (type, evt) { - var keyCode = evt.keyCode || evt.which; - if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) { - if (inputType) - return; - if(!isCollapsed){ - this.undoManger.save(false,true); - isCollapsed = true; - } - } - }); - //扩展实例,添加关闭和开启命令undo - me.stopCmdUndo = function(){ - me.__hasEnterExecCommand = true; - }; - me.startCmdUndo = function(){ - me.__hasEnterExecCommand = false; - } -}; - - -// plugins/copy.js -UE.plugin.register('copy', function () { - - var me = this; - - function initZeroClipboard() { - - ZeroClipboard.config({ - debug: false, - swfPath: me.options.UEDITOR_HOME_URL + 'third-party/zeroclipboard/ZeroClipboard.swf' - }); - - var client = me.zeroclipboard = new ZeroClipboard(); - - // 复制内容 - client.on('copy', function (e) { - var client = e.client, - rng = me.selection.getRange(), - div = document.createElement('div'); - - div.appendChild(rng.cloneContents()); - client.setText(div.innerText || div.textContent); - client.setHtml(div.innerHTML); - rng.select(); - }); - // hover事件传递到target - client.on('mouseover mouseout', function (e) { - var target = e.target; - if (e.type == 'mouseover') { - domUtils.addClass(target, 'edui-state-hover'); - } else if (e.type == 'mouseout') { - domUtils.removeClasses(target, 'edui-state-hover'); - } - }); - // flash加载不成功 - client.on('wrongflash noflash', function () { - ZeroClipboard.destroy(); - }); - } - - return { - bindEvents: { - 'ready': function () { - if (!browser.ie) { - if (window.ZeroClipboard) { - initZeroClipboard(); - } else { - utils.loadFile(document, { - src: me.options.UEDITOR_HOME_URL + "third-party/zeroclipboard/ZeroClipboard.js", - tag: "script", - type: "text/javascript", - defer: "defer" - }, function () { - initZeroClipboard(); - }); - } - } - } - }, - commands: { - 'copy': { - execCommand: function (cmd) { - if (!me.document.execCommand('copy')) { - alert(me.getLang('copymsg')); - } - } - } - } - } -}); - - -// plugins/paste.js -///import core -///import plugins/inserthtml.js -///import plugins/undo.js -///import plugins/serialize.js -///commands 粘贴 -///commandsName PastePlain -///commandsTitle 纯文本粘贴模式 -/** - * @description 粘贴 - * @author zhanyi - */ -UE.plugins['paste'] = function () { - function getClipboardData(callback) { - var doc = this.document; - if (doc.getElementById('baidu_pastebin')) { - return; - } - var range = this.selection.getRange(), - bk = range.createBookmark(), - //创建剪贴的容器div - pastebin = doc.createElement('div'); - pastebin.id = 'baidu_pastebin'; - // Safari 要求div必须有内容,才能粘贴内容进来 - browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar)); - doc.body.appendChild(pastebin); - //trace:717 隐藏的span不能得到top - //bk.start.innerHTML = ' '; - bk.start.style.display = ''; - pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" + - //要在现在光标平行的位置加入,否则会出现跳动的问题 - domUtils.getXY(bk.start).y + 'px'; - - range.selectNodeContents(pastebin).select(true); - - setTimeout(function () { - if (browser.webkit) { - for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) { - if (domUtils.isEmptyNode(pi)) { - domUtils.remove(pi); - } else { - pastebin = pi; - break; - } - } - } - try { - pastebin.parentNode.removeChild(pastebin); - } catch (e) { - } - range.moveToBookmark(bk).select(true); - callback(pastebin); - }, 0); - } - - var me = this; - - me.setOpt({ - retainOnlyLabelPasted : false - }); - - var txtContent, htmlContent, address; - - function getPureHtml(html){ - return html.replace(/<(\/?)([\w\-]+)([^>]*)>/gi, function (a, b, tagName, attrs) { - tagName = tagName.toLowerCase(); - if ({img: 1}[tagName]) { - return a; - } - attrs = attrs.replace(/([\w\-]*?)\s*=\s*(("([^"]*)")|('([^']*)')|([^\s>]+))/gi, function (str, atr, val) { - if ({ - 'src': 1, - 'href': 1, - 'name': 1 - }[atr.toLowerCase()]) { - return atr + '=' + val + ' ' - } - return '' - }); - if ({ - 'span': 1, - 'div': 1 - }[tagName]) { - return '' - } else { - - return '<' + b + tagName + ' ' + utils.trim(attrs) + '>' - } - - }); - } - function filter(div) { - var html; - if (div.firstChild) { - //去掉cut中添加的边界值 - var nodes = domUtils.getElementsByTagName(div, 'span'); - for (var i = 0, ni; ni = nodes[i++];) { - if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') { - domUtils.remove(ni); - } - } - - if (browser.webkit) { - - var brs = div.querySelectorAll('div br'); - for (var i = 0, bi; bi = brs[i++];) { - var pN = bi.parentNode; - if (pN.tagName == 'DIV' && pN.childNodes.length == 1) { - pN.innerHTML = '


                  '; - domUtils.remove(pN); - } - } - var divs = div.querySelectorAll('#baidu_pastebin'); - for (var i = 0, di; di = divs[i++];) { - var tmpP = me.document.createElement('p'); - di.parentNode.insertBefore(tmpP, di); - while (di.firstChild) { - tmpP.appendChild(di.firstChild); - } - domUtils.remove(di); - } - - var metas = div.querySelectorAll('meta'); - for (var i = 0, ci; ci = metas[i++];) { - domUtils.remove(ci); - } - - var brs = div.querySelectorAll('br'); - for (i = 0; ci = brs[i++];) { - if (/^apple-/i.test(ci.className)) { - domUtils.remove(ci); - } - } - } - if (browser.gecko) { - var dirtyNodes = div.querySelectorAll('[_moz_dirty]'); - for (i = 0; ci = dirtyNodes[i++];) { - ci.removeAttribute('_moz_dirty'); - } - } - if (!browser.ie) { - var spans = div.querySelectorAll('span.Apple-style-span'); - for (var i = 0, ci; ci = spans[i++];) { - domUtils.remove(ci, true); - } - } - - //ie下使用innerHTML会产生多余的\r\n字符,也会产生 这里过滤掉 - html = div.innerHTML;//.replace(/>(?:(\s| )*?)<'); - - //过滤word粘贴过来的冗余属性 - html = UE.filterWord(html); - //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签 - var root = UE.htmlparser(html); - //如果给了过滤规则就先进行过滤 - if (me.options.filterRules) { - UE.filterNode(root, me.options.filterRules); - } - //执行默认的处理 - me.filterInputRule(root); - //针对chrome的处理 - if (browser.webkit) { - var br = root.lastChild(); - if (br && br.type == 'element' && br.tagName == 'br') { - root.removeChild(br) - } - utils.each(me.body.querySelectorAll('div'), function (node) { - if (domUtils.isEmptyBlock(node)) { - domUtils.remove(node,true) - } - }) - } - html = {'html': root.toHtml()}; - me.fireEvent('beforepaste', html, root); - //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴 - if(!html.html){ - return; - } - root = UE.htmlparser(html.html,true); - //如果开启了纯文本模式 - if (me.queryCommandState('pasteplain') === 1) { - me.execCommand('insertHtml', UE.filterNode(root, me.options.filterTxtRules).toHtml(), true); - } else { - //文本模式 - UE.filterNode(root, me.options.filterTxtRules); - txtContent = root.toHtml(); - //完全模式 - htmlContent = html.html; - - address = me.selection.getRange().createAddress(true); - me.execCommand('insertHtml', me.getOpt('retainOnlyLabelPasted') === true ? getPureHtml(htmlContent) : htmlContent, true); - } - me.fireEvent("afterpaste", html); - } - } - - me.addListener('pasteTransfer', function (cmd, plainType) { - - if (address && txtContent && htmlContent && txtContent != htmlContent) { - var range = me.selection.getRange(); - range.moveToAddress(address, true); - - if (!range.collapsed) { - - while (!domUtils.isBody(range.startContainer) - ) { - var start = range.startContainer; - if(start.nodeType == 1){ - start = start.childNodes[range.startOffset]; - if(!start){ - range.setStartBefore(range.startContainer); - continue; - } - var pre = start.previousSibling; - - if(pre && pre.nodeType == 3 && new RegExp('^[\n\r\t '+domUtils.fillChar+']*$').test(pre.nodeValue)){ - range.setStartBefore(pre) - } - } - if(range.startOffset == 0){ - range.setStartBefore(range.startContainer); - }else{ - break; - } - - } - while (!domUtils.isBody(range.endContainer) - ) { - var end = range.endContainer; - if(end.nodeType == 1){ - end = end.childNodes[range.endOffset]; - if(!end){ - range.setEndAfter(range.endContainer); - continue; - } - var next = end.nextSibling; - if(next && next.nodeType == 3 && new RegExp('^[\n\r\t'+domUtils.fillChar+']*$').test(next.nodeValue)){ - range.setEndAfter(next) - } - } - if(range.endOffset == range.endContainer[range.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length){ - range.setEndAfter(range.endContainer); - }else{ - break; - } - - } - - } - - range.deleteContents(); - range.select(true); - me.__hasEnterExecCommand = true; - var html = htmlContent; - if (plainType === 2 ) { - html = getPureHtml(html); - } else if (plainType) { - html = txtContent; - } - me.execCommand('inserthtml', html, true); - me.__hasEnterExecCommand = false; - var rng = me.selection.getRange(); - while (!domUtils.isBody(rng.startContainer) && !rng.startOffset && - rng.startContainer[rng.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length - ) { - rng.setStartBefore(rng.startContainer); - } - var tmpAddress = rng.createAddress(true); - address.endAddress = tmpAddress.startAddress; - } - }); - - me.addListener('ready', function () { - domUtils.on(me.body, 'cut', function () { - var range = me.selection.getRange(); - if (!range.collapsed && me.undoManger) { - me.undoManger.save(); - } - }); - - //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理 - domUtils.on(me.body, browser.ie || browser.opera ? 'keydown' : 'paste', function (e) { - if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) { - return; - } - getClipboardData.call(me, function (div) { - filter(div); - }); - }); - - }); - - me.commands['paste'] = { - execCommand: function (cmd) { - if (browser.ie) { - getClipboardData.call(me, function (div) { - filter(div); - }); - me.document.execCommand('paste'); - } else { - alert(me.getLang('pastemsg')); - } - } - } -}; - - - -// plugins/puretxtpaste.js -/** - * 纯文本粘贴插件 - * @file - * @since 1.2.6.1 - */ - -UE.plugins['pasteplain'] = function(){ - var me = this; - me.setOpt({ - 'pasteplain':false, - 'filterTxtRules' : function(){ - function transP(node){ - node.tagName = 'p'; - node.setStyle(); - } - function removeNode(node){ - node.parentNode.removeChild(node,true) - } - return { - //直接删除及其字节点内容 - '-' : 'script style object iframe embed input select', - 'p': {$:{}}, - 'br':{$:{}}, - div: function (node) { - var tmpNode, p = UE.uNode.createElement('p'); - while (tmpNode = node.firstChild()) { - if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) { - p.appendChild(tmpNode); - } else { - if (p.firstChild()) { - node.parentNode.insertBefore(p, node); - p = UE.uNode.createElement('p'); - } else { - node.parentNode.insertBefore(tmpNode, node); - } - } - } - if (p.firstChild()) { - node.parentNode.insertBefore(p, node); - } - node.parentNode.removeChild(node); - }, - ol: removeNode, - ul: removeNode, - dl:removeNode, - dt:removeNode, - dd:removeNode, - 'li':removeNode, - 'caption':transP, - 'th':transP, - 'tr':transP, - 'h1':transP,'h2':transP,'h3':transP,'h4':transP,'h5':transP,'h6':transP, - 'td':function(node){ - //没有内容的td直接删掉 - var txt = !!node.innerText(); - if(txt){ - node.parentNode.insertAfter(UE.uNode.createText('    '),node); - } - node.parentNode.removeChild(node,node.innerText()) - } - } - }() - }); - //暂时这里支持一下老版本的属性 - var pasteplain = me.options.pasteplain; - - /** - * 启用或取消纯文本粘贴模式 - * @command pasteplain - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.queryCommandState( 'pasteplain' ); - * ``` - */ - - /** - * 查询当前是否处于纯文本粘贴模式 - * @command pasteplain - * @method queryCommandState - * @param { String } cmd 命令字符串 - * @return { int } 如果处于纯文本模式,返回1,否则,返回0 - * @example - * ```javascript - * editor.queryCommandState( 'pasteplain' ); - * ``` - */ - me.commands['pasteplain'] = { - queryCommandState: function (){ - return pasteplain ? 1 : 0; - }, - execCommand: function (){ - pasteplain = !pasteplain|0; - }, - notNeedUndo : 1 - }; -}; - -// plugins/list.js -/** - * 有序列表,无序列表插件 - * @file - * @since 1.2.6.1 - */ - -UE.plugins['list'] = function () { - var me = this, - notExchange = { - 'TD':1, - 'PRE':1, - 'BLOCKQUOTE':1 - }; - var customStyle = { - 'cn' : 'cn-1-', - 'cn1' : 'cn-2-', - 'cn2' : 'cn-3-', - 'num': 'num-1-', - 'num1' : 'num-2-', - 'num2' : 'num-3-', - 'dash' : 'dash', - 'dot':'dot' - }; - - me.setOpt( { - 'autoTransWordToList':false, - 'insertorderedlist':{ - 'num':'', - 'num1':'', - 'num2':'', - 'cn':'', - 'cn1':'', - 'cn2':'', - 'decimal':'', - 'lower-alpha':'', - 'lower-roman':'', - 'upper-alpha':'', - 'upper-roman':'' - }, - 'insertunorderedlist':{ - 'circle':'', - 'disc':'', - 'square':'', - 'dash' : '', - 'dot':'' - }, - listDefaultPaddingLeft : '30', - listiconpath : 'http://bs.baidu.com/listicon/', - maxListLevel : -1,//-1不限制 - disablePInList:false - } ); - function listToArray(list){ - var arr = []; - for(var p in list){ - arr.push(p) - } - return arr; - } - var listStyle = { - 'OL':listToArray(me.options.insertorderedlist), - 'UL':listToArray(me.options.insertunorderedlist) - }; - var liiconpath = me.options.listiconpath; - - //根据用户配置,调整customStyle - for(var s in customStyle){ - if(!me.options.insertorderedlist.hasOwnProperty(s) && !me.options.insertunorderedlist.hasOwnProperty(s)){ - delete customStyle[s]; - } - } - - me.ready(function () { - var customCss = []; - for(var p in customStyle){ - if(p == 'dash' || p == 'dot'){ - customCss.push('li.list-' + customStyle[p] + '{background-image:url(' + liiconpath +customStyle[p]+'.gif)}'); - customCss.push('ul.custom_'+p+'{list-style:none;}ul.custom_'+p+' li{background-position:0 3px;background-repeat:no-repeat}'); - }else{ - for(var i= 0;i<99;i++){ - customCss.push('li.list-' + customStyle[p] + i + '{background-image:url(' + liiconpath + 'list-'+customStyle[p] + i + '.gif)}') - } - customCss.push('ol.custom_'+p+'{list-style:none;}ol.custom_'+p+' li{background-position:0 3px;background-repeat:no-repeat}'); - } - switch(p){ - case 'cn': - customCss.push('li.list-'+p+'-paddingleft-1{padding-left:25px}'); - customCss.push('li.list-'+p+'-paddingleft-2{padding-left:40px}'); - customCss.push('li.list-'+p+'-paddingleft-3{padding-left:55px}'); - break; - case 'cn1': - customCss.push('li.list-'+p+'-paddingleft-1{padding-left:30px}'); - customCss.push('li.list-'+p+'-paddingleft-2{padding-left:40px}'); - customCss.push('li.list-'+p+'-paddingleft-3{padding-left:55px}'); - break; - case 'cn2': - customCss.push('li.list-'+p+'-paddingleft-1{padding-left:40px}'); - customCss.push('li.list-'+p+'-paddingleft-2{padding-left:55px}'); - customCss.push('li.list-'+p+'-paddingleft-3{padding-left:68px}'); - break; - case 'num': - case 'num1': - customCss.push('li.list-'+p+'-paddingleft-1{padding-left:25px}'); - break; - case 'num2': - customCss.push('li.list-'+p+'-paddingleft-1{padding-left:35px}'); - customCss.push('li.list-'+p+'-paddingleft-2{padding-left:40px}'); - break; - case 'dash': - customCss.push('li.list-'+p+'-paddingleft{padding-left:35px}'); - break; - case 'dot': - customCss.push('li.list-'+p+'-paddingleft{padding-left:20px}'); - } - } - customCss.push('.list-paddingleft-1{padding-left:0}'); - customCss.push('.list-paddingleft-2{padding-left:'+me.options.listDefaultPaddingLeft+'px}'); - customCss.push('.list-paddingleft-3{padding-left:'+me.options.listDefaultPaddingLeft*2+'px}'); - //如果不给宽度会在自定应样式里出现滚动条 - utils.cssRule('list', 'ol,ul{margin:0;pading:0;'+(browser.ie ? '' : 'width:95%')+'}li{clear:both;}'+customCss.join('\n'), me.document); - }); - //单独处理剪切的问题 - me.ready(function(){ - domUtils.on(me.body,'cut',function(){ - setTimeout(function(){ - var rng = me.selection.getRange(),li; - //trace:3416 - if(!rng.collapsed){ - if(li = domUtils.findParentByTagName(rng.startContainer,'li',true)){ - if(!li.nextSibling && domUtils.isEmptyBlock(li)){ - var pn = li.parentNode,node; - if(node = pn.previousSibling){ - domUtils.remove(pn); - rng.setStartAtLast(node).collapse(true); - rng.select(true); - }else if(node = pn.nextSibling){ - domUtils.remove(pn); - rng.setStartAtFirst(node).collapse(true); - rng.select(true); - }else{ - var tmpNode = me.document.createElement('p'); - domUtils.fillNode(me.document,tmpNode); - pn.parentNode.insertBefore(tmpNode,pn); - domUtils.remove(pn); - rng.setStart(tmpNode,0).collapse(true); - rng.select(true); - } - } - } - } - - }) - }) - }); - - function getStyle(node){ - var cls = node.className; - if(domUtils.hasClass(node,/custom_/)){ - return cls.match(/custom_(\w+)/)[1] - } - return domUtils.getStyle(node, 'list-style-type') - - } - - me.addListener('beforepaste',function(type,html){ - var me = this, - rng = me.selection.getRange(),li; - var root = UE.htmlparser(html.html,true); - if(li = domUtils.findParentByTagName(rng.startContainer,'li',true)){ - var list = li.parentNode,tagName = list.tagName == 'OL' ? 'ul':'ol'; - utils.each(root.getNodesByTagName(tagName),function(n){ - n.tagName = list.tagName; - n.setAttr(); - if(n.parentNode === root){ - type = getStyle(list) || (list.tagName == 'OL' ? 'decimal' : 'disc') - }else{ - var className = n.parentNode.getAttr('class'); - if(className && /custom_/.test(className)){ - type = className.match(/custom_(\w+)/)[1] - }else{ - type = n.parentNode.getStyle('list-style-type'); - } - if(!type){ - type = list.tagName == 'OL' ? 'decimal' : 'disc'; - } - } - var index = utils.indexOf(listStyle[list.tagName], type); - if(n.parentNode !== root) - index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1; - var currentStyle = listStyle[list.tagName][index]; - if(customStyle[currentStyle]){ - n.setAttr('class', 'custom_' + currentStyle) - - }else{ - n.setStyle('list-style-type',currentStyle) - } - }) - - } - - html.html = root.toHtml(); - }); - //导出时,去掉p标签 - me.getOpt('disablePInList') === true && me.addOutputRule(function(root){ - utils.each(root.getNodesByTagName('li'),function(li){ - var newChildrens = [],index=0; - utils.each(li.children,function(n){ - if(n.tagName == 'p'){ - var tmpNode; - while(tmpNode = n.children.pop()) { - newChildrens.splice(index,0,tmpNode); - tmpNode.parentNode = li; - lastNode = tmpNode; - } - tmpNode = newChildrens[newChildrens.length-1]; - if(!tmpNode || tmpNode.type != 'element' || tmpNode.tagName != 'br'){ - var br = UE.uNode.createElement('br'); - br.parentNode = li; - newChildrens.push(br); - } - - index = newChildrens.length; - } - }); - if(newChildrens.length){ - li.children = newChildrens; - } - }); - }); - //进入编辑器的li要套p标签 - me.addInputRule(function(root){ - utils.each(root.getNodesByTagName('li'),function(li){ - var tmpP = UE.uNode.createElement('p'); - for(var i= 0,ci;ci=li.children[i];){ - if(ci.type == 'text' || dtd.p[ci.tagName]){ - tmpP.appendChild(ci); - }else{ - if(tmpP.firstChild()){ - li.insertBefore(tmpP,ci); - tmpP = UE.uNode.createElement('p'); - i = i + 2; - }else{ - i++; - } - - } - } - if(tmpP.firstChild() && !tmpP.parentNode || !li.firstChild()){ - li.appendChild(tmpP); - } - //trace:3357 - //p不能为空 - if (!tmpP.firstChild()) { - tmpP.innerHTML(browser.ie ? ' ' : '
                  ') - } - //去掉末尾的空白 - var p = li.firstChild(); - var lastChild = p.lastChild(); - if(lastChild && lastChild.type == 'text' && /^\s*$/.test(lastChild.data)){ - p.removeChild(lastChild) - } - }); - if(me.options.autoTransWordToList){ - var orderlisttype = { - 'num1':/^\d+\)/, - 'decimal':/^\d+\./, - 'lower-alpha':/^[a-z]+\)/, - 'upper-alpha':/^[A-Z]+\./, - 'cn':/^[\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+[\u3001]/, - 'cn2':/^\([\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+\)/ - }, - unorderlisttype = { - 'square':'n' - }; - function checkListType(content,container){ - var span = container.firstChild(); - if(span && span.type == 'element' && span.tagName == 'span' && /Wingdings|Symbol/.test(span.getStyle('font-family'))){ - for(var p in unorderlisttype){ - if(unorderlisttype[p] == span.data){ - return p - } - } - return 'disc' - } - for(var p in orderlisttype){ - if(orderlisttype[p].test(content)){ - return p; - } - } - - } - utils.each(root.getNodesByTagName('p'),function(node){ - if(node.getAttr('class') != 'MsoListParagraph'){ - return - } - - //word粘贴过来的会带有margin要去掉,但这样也可能会误命中一些央视 - node.setStyle('margin',''); - node.setStyle('margin-left',''); - node.setAttr('class',''); - - function appendLi(list,p,type){ - if(list.tagName == 'ol'){ - if(browser.ie){ - var first = p.firstChild(); - if(first.type =='element' && first.tagName == 'span' && orderlisttype[type].test(first.innerText())){ - p.removeChild(first); - } - }else{ - p.innerHTML(p.innerHTML().replace(orderlisttype[type],'')); - } - }else{ - p.removeChild(p.firstChild()) - } - - var li = UE.uNode.createElement('li'); - li.appendChild(p); - list.appendChild(li); - } - var tmp = node,type,cacheNode = node; - - if(node.parentNode.tagName != 'li' && (type = checkListType(node.innerText(),node))){ - - var list = UE.uNode.createElement(me.options.insertorderedlist.hasOwnProperty(type) ? 'ol' : 'ul'); - if(customStyle[type]){ - list.setAttr('class','custom_'+type) - }else{ - list.setStyle('list-style-type',type) - } - while(node && node.parentNode.tagName != 'li' && checkListType(node.innerText(),node)){ - tmp = node.nextSibling(); - if(!tmp){ - node.parentNode.insertBefore(list,node) - } - appendLi(list,node,type); - node = tmp; - } - if(!list.parentNode && node && node.parentNode){ - node.parentNode.insertBefore(list,node) - } - } - var span = cacheNode.firstChild(); - if(span && span.type == 'element' && span.tagName == 'span' && /^\s*( )+\s*$/.test(span.innerText())){ - span.parentNode.removeChild(span) - } - }) - } - - }); - - //调整索引标签 - me.addListener('contentchange',function(){ - adjustListStyle(me.document) - }); - - function adjustListStyle(doc,ignore){ - utils.each(domUtils.getElementsByTagName(doc,'ol ul'),function(node){ - - if(!domUtils.inDoc(node,doc)) - return; - - var parent = node.parentNode; - if(parent.tagName == node.tagName){ - var nodeStyleType = getStyle(node) || (node.tagName == 'OL' ? 'decimal' : 'disc'), - parentStyleType = getStyle(parent) || (parent.tagName == 'OL' ? 'decimal' : 'disc'); - if(nodeStyleType == parentStyleType){ - var styleIndex = utils.indexOf(listStyle[node.tagName], nodeStyleType); - styleIndex = styleIndex + 1 == listStyle[node.tagName].length ? 0 : styleIndex + 1; - setListStyle(node,listStyle[node.tagName][styleIndex]) - } - - } - var index = 0,type = 2; - if( domUtils.hasClass(node,/custom_/)){ - if(!(/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent,/custom_/))){ - type = 1; - } - }else{ - if(/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent,/custom_/)){ - type = 3; - } - } - - var style = domUtils.getStyle(node, 'list-style-type'); - style && (node.style.cssText = 'list-style-type:' + style); - node.className = utils.trim(node.className.replace(/list-paddingleft-\w+/,'')) + ' list-paddingleft-' + type; - utils.each(domUtils.getElementsByTagName(node,'li'),function(li){ - li.style.cssText && (li.style.cssText = ''); - if(!li.firstChild){ - domUtils.remove(li); - return; - } - if(li.parentNode !== node){ - return; - } - index++; - if(domUtils.hasClass(node,/custom_/) ){ - var paddingLeft = 1,currentStyle = getStyle(node); - if(node.tagName == 'OL'){ - if(currentStyle){ - switch(currentStyle){ - case 'cn' : - case 'cn1': - case 'cn2': - if(index > 10 && (index % 10 == 0 || index > 10 && index < 20)){ - paddingLeft = 2 - }else if(index > 20){ - paddingLeft = 3 - } - break; - case 'num2' : - if(index > 9){ - paddingLeft = 2 - } - } - } - li.className = 'list-'+customStyle[currentStyle]+ index + ' ' + 'list-'+currentStyle+'-paddingleft-' + paddingLeft; - }else{ - li.className = 'list-'+customStyle[currentStyle] + ' ' + 'list-'+currentStyle+'-paddingleft'; - } - }else{ - li.className = li.className.replace(/list-[\w\-]+/gi,''); - } - var className = li.getAttribute('class'); - if(className !== null && !className.replace(/\s/g,'')){ - domUtils.removeAttributes(li,'class') - } - }); - !ignore && adjustList(node,node.tagName.toLowerCase(),getStyle(node)||domUtils.getStyle(node, 'list-style-type'),true); - }) - } - function adjustList(list, tag, style,ignoreEmpty) { - var nextList = list.nextSibling; - if (nextList && nextList.nodeType == 1 && nextList.tagName.toLowerCase() == tag && (getStyle(nextList) || domUtils.getStyle(nextList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) == style) { - domUtils.moveChild(nextList, list); - if (nextList.childNodes.length == 0) { - domUtils.remove(nextList); - } - } - if(nextList && domUtils.isFillChar(nextList)){ - domUtils.remove(nextList); - } - var preList = list.previousSibling; - if (preList && preList.nodeType == 1 && preList.tagName.toLowerCase() == tag && (getStyle(preList) || domUtils.getStyle(preList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) == style) { - domUtils.moveChild(list, preList); - } - if(preList && domUtils.isFillChar(preList)){ - domUtils.remove(preList); - } - !ignoreEmpty && domUtils.isEmptyBlock(list) && domUtils.remove(list); - if(getStyle(list)){ - adjustListStyle(list.ownerDocument,true) - } - } - - function setListStyle(list,style){ - if(customStyle[style]){ - list.className = 'custom_' + style; - } - try{ - domUtils.setStyle(list, 'list-style-type', style); - }catch(e){} - } - function clearEmptySibling(node) { - var tmpNode = node.previousSibling; - if (tmpNode && domUtils.isEmptyBlock(tmpNode)) { - domUtils.remove(tmpNode); - } - tmpNode = node.nextSibling; - if (tmpNode && domUtils.isEmptyBlock(tmpNode)) { - domUtils.remove(tmpNode); - } - } - - me.addListener('keydown', function (type, evt) { - function preventAndSave() { - evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false); - me.fireEvent('contentchange'); - me.undoManger && me.undoManger.save(); - } - function findList(node,filterFn){ - while(node && !domUtils.isBody(node)){ - if(filterFn(node)){ - return null - } - if(node.nodeType == 1 && /[ou]l/i.test(node.tagName)){ - return node; - } - node = node.parentNode; - } - return null; - } - var keyCode = evt.keyCode || evt.which; - if (keyCode == 13 && !evt.shiftKey) {//回车 - var rng = me.selection.getRange(), - parent = domUtils.findParent(rng.startContainer,function(node){return domUtils.isBlockElm(node)},true), - li = domUtils.findParentByTagName(rng.startContainer,'li',true); - if(parent && parent.tagName != 'PRE' && !li){ - var html = parent.innerHTML.replace(new RegExp(domUtils.fillChar, 'g'),''); - if(/^\s*1\s*\.[^\d]/.test(html)){ - parent.innerHTML = html.replace(/^\s*1\s*\./,''); - rng.setStartAtLast(parent).collapse(true).select(); - me.__hasEnterExecCommand = true; - me.execCommand('insertorderedlist'); - me.__hasEnterExecCommand = false; - } - } - var range = me.selection.getRange(), - start = findList(range.startContainer,function (node) { - return node.tagName == 'TABLE'; - }), - end = range.collapsed ? start : findList(range.endContainer,function (node) { - return node.tagName == 'TABLE'; - }); - - if (start && end && start === end) { - - if (!range.collapsed) { - start = domUtils.findParentByTagName(range.startContainer, 'li', true); - end = domUtils.findParentByTagName(range.endContainer, 'li', true); - if (start && end && start === end) { - range.deleteContents(); - li = domUtils.findParentByTagName(range.startContainer, 'li', true); - if (li && domUtils.isEmptyBlock(li)) { - - pre = li.previousSibling; - next = li.nextSibling; - p = me.document.createElement('p'); - - domUtils.fillNode(me.document, p); - parentList = li.parentNode; - if (pre && next) { - range.setStart(next, 0).collapse(true).select(true); - domUtils.remove(li); - - } else { - if (!pre && !next || !pre) { - - parentList.parentNode.insertBefore(p, parentList); - - - } else { - li.parentNode.parentNode.insertBefore(p, parentList.nextSibling); - } - domUtils.remove(li); - if (!parentList.firstChild) { - domUtils.remove(parentList); - } - range.setStart(p, 0).setCursor(); - - - } - preventAndSave(); - return; - - } - } else { - var tmpRange = range.cloneRange(), - bk = tmpRange.collapse(false).createBookmark(); - - range.deleteContents(); - tmpRange.moveToBookmark(bk); - var li = domUtils.findParentByTagName(tmpRange.startContainer, 'li', true); - - clearEmptySibling(li); - tmpRange.select(); - preventAndSave(); - return; - } - } - - - li = domUtils.findParentByTagName(range.startContainer, 'li', true); - - if (li) { - if (domUtils.isEmptyBlock(li)) { - bk = range.createBookmark(); - var parentList = li.parentNode; - if (li !== parentList.lastChild) { - domUtils.breakParent(li, parentList); - clearEmptySibling(li); - } else { - - parentList.parentNode.insertBefore(li, parentList.nextSibling); - if (domUtils.isEmptyNode(parentList)) { - domUtils.remove(parentList); - } - } - //嵌套不处理 - if (!dtd.$list[li.parentNode.tagName]) { - - if (!domUtils.isBlockElm(li.firstChild)) { - p = me.document.createElement('p'); - li.parentNode.insertBefore(p, li); - while (li.firstChild) { - p.appendChild(li.firstChild); - } - domUtils.remove(li); - } else { - domUtils.remove(li, true); - } - } - range.moveToBookmark(bk).select(); - - - } else { - var first = li.firstChild; - if (!first || !domUtils.isBlockElm(first)) { - var p = me.document.createElement('p'); - - !li.firstChild && domUtils.fillNode(me.document, p); - while (li.firstChild) { - - p.appendChild(li.firstChild); - } - li.appendChild(p); - first = p; - } - - var span = me.document.createElement('span'); - - range.insertNode(span); - domUtils.breakParent(span, li); - - var nextLi = span.nextSibling; - first = nextLi.firstChild; - - if (!first) { - p = me.document.createElement('p'); - - domUtils.fillNode(me.document, p); - nextLi.appendChild(p); - first = p; - } - if (domUtils.isEmptyNode(first)) { - first.innerHTML = ''; - domUtils.fillNode(me.document, first); - } - - range.setStart(first, 0).collapse(true).shrinkBoundary().select(); - domUtils.remove(span); - var pre = nextLi.previousSibling; - if (pre && domUtils.isEmptyBlock(pre)) { - pre.innerHTML = '

                  '; - domUtils.fillNode(me.document, pre.firstChild); - } - - } -// } - preventAndSave(); - } - - - } - - - } - if (keyCode == 8) { - //修中ie中li下的问题 - range = me.selection.getRange(); - if (range.collapsed && domUtils.isStartInblock(range)) { - tmpRange = range.cloneRange().trimBoundary(); - li = domUtils.findParentByTagName(range.startContainer, 'li', true); - //要在li的最左边,才能处理 - if (li && domUtils.isStartInblock(tmpRange)) { - start = domUtils.findParentByTagName(range.startContainer, 'p', true); - if (start && start !== li.firstChild) { - var parentList = domUtils.findParentByTagName(start,['ol','ul']); - domUtils.breakParent(start,parentList); - clearEmptySibling(start); - me.fireEvent('contentchange'); - range.setStart(start,0).setCursor(false,true); - me.fireEvent('saveScene'); - domUtils.preventDefault(evt); - return; - } - - if (li && (pre = li.previousSibling)) { - if (keyCode == 46 && li.childNodes.length) { - return; - } - //有可能上边的兄弟节点是个2级菜单,要追加到2级菜单的最后的li - if (dtd.$list[pre.tagName]) { - pre = pre.lastChild; - } - me.undoManger && me.undoManger.save(); - first = li.firstChild; - if (domUtils.isBlockElm(first)) { - if (domUtils.isEmptyNode(first)) { -// range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true); - pre.appendChild(first); - range.setStart(first, 0).setCursor(false, true); - //first不是唯一的节点 - while (li.firstChild) { - pre.appendChild(li.firstChild); - } - } else { - - span = me.document.createElement('span'); - range.insertNode(span); - //判断pre是否是空的节点,如果是


                  类型的空节点,干掉p标签防止它占位 - if (domUtils.isEmptyBlock(pre)) { - pre.innerHTML = ''; - } - domUtils.moveChild(li, pre); - range.setStartBefore(span).collapse(true).select(true); - - domUtils.remove(span); - - } - } else { - if (domUtils.isEmptyNode(li)) { - var p = me.document.createElement('p'); - pre.appendChild(p); - range.setStart(p, 0).setCursor(); -// range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true); - } else { - range.setEnd(pre, pre.childNodes.length).collapse().select(true); - while (li.firstChild) { - pre.appendChild(li.firstChild); - } - } - } - domUtils.remove(li); - me.fireEvent('contentchange'); - me.fireEvent('saveScene'); - domUtils.preventDefault(evt); - return; - - } - //trace:980 - - if (li && !li.previousSibling) { - var parentList = li.parentNode; - var bk = range.createBookmark(); - if(domUtils.isTagNode(parentList.parentNode,'ol ul')){ - parentList.parentNode.insertBefore(li,parentList); - if(domUtils.isEmptyNode(parentList)){ - domUtils.remove(parentList) - } - }else{ - - while(li.firstChild){ - parentList.parentNode.insertBefore(li.firstChild,parentList); - } - - domUtils.remove(li); - if(domUtils.isEmptyNode(parentList)){ - domUtils.remove(parentList) - } - - } - range.moveToBookmark(bk).setCursor(false,true); - me.fireEvent('contentchange'); - me.fireEvent('saveScene'); - domUtils.preventDefault(evt); - return; - - } - - - } - - - } - - } - }); - - me.addListener('keyup',function(type, evt){ - var keyCode = evt.keyCode || evt.which; - if (keyCode == 8) { - var rng = me.selection.getRange(),list; - if(list = domUtils.findParentByTagName(rng.startContainer,['ol', 'ul'],true)){ - adjustList(list,list.tagName.toLowerCase(),getStyle(list)||domUtils.getComputedStyle(list,'list-style-type'),true) - } - } - }); - //处理tab键 - me.addListener('tabkeydown',function(){ - - var range = me.selection.getRange(); - - //控制级数 - function checkLevel(li){ - if(me.options.maxListLevel != -1){ - var level = li.parentNode,levelNum = 0; - while(/[ou]l/i.test(level.tagName)){ - levelNum++; - level = level.parentNode; - } - if(levelNum >= me.options.maxListLevel){ - return true; - } - } - } - //只以开始为准 - //todo 后续改进 - var li = domUtils.findParentByTagName(range.startContainer, 'li', true); - if(li){ - - var bk; - if(range.collapsed){ - if(checkLevel(li)) - return true; - var parentLi = li.parentNode, - list = me.document.createElement(parentLi.tagName), - index = utils.indexOf(listStyle[list.tagName], getStyle(parentLi)||domUtils.getComputedStyle(parentLi, 'list-style-type')); - index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1; - var currentStyle = listStyle[list.tagName][index]; - setListStyle(list,currentStyle); - if(domUtils.isStartInblock(range)){ - me.fireEvent('saveScene'); - bk = range.createBookmark(); - parentLi.insertBefore(list, li); - list.appendChild(li); - adjustList(list,list.tagName.toLowerCase(),currentStyle); - me.fireEvent('contentchange'); - range.moveToBookmark(bk).select(true); - return true; - } - }else{ - me.fireEvent('saveScene'); - bk = range.createBookmark(); - for(var i= 0,closeList,parents = domUtils.findParents(li),ci;ci=parents[i++];){ - if(domUtils.isTagNode(ci,'ol ul')){ - closeList = ci; - break; - } - } - var current = li; - if(bk.end){ - while(current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)){ - if(checkLevel(current)){ - current = domUtils.getNextDomNode(current,false,null,function(node){return node !== closeList}); - continue; - } - var parentLi = current.parentNode, - list = me.document.createElement(parentLi.tagName), - index = utils.indexOf(listStyle[list.tagName], getStyle(parentLi)||domUtils.getComputedStyle(parentLi, 'list-style-type')); - var currentIndex = index + 1 == listStyle[list.tagName].length ? 0 : index + 1; - var currentStyle = listStyle[list.tagName][currentIndex]; - setListStyle(list,currentStyle); - parentLi.insertBefore(list, current); - while(current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)){ - li = current.nextSibling; - list.appendChild(current); - if(!li || domUtils.isTagNode(li,'ol ul')){ - if(li){ - while(li = li.firstChild){ - if(li.tagName == 'LI'){ - break; - } - } - }else{ - li = domUtils.getNextDomNode(current,false,null,function(node){return node !== closeList}); - } - break; - } - current = li; - } - adjustList(list,list.tagName.toLowerCase(),currentStyle); - current = li; - } - } - me.fireEvent('contentchange'); - range.moveToBookmark(bk).select(); - return true; - } - } - - }); - function getLi(start){ - while(start && !domUtils.isBody(start)){ - if(start.nodeName == 'TABLE'){ - return null; - } - if(start.nodeName == 'LI'){ - return start - } - start = start.parentNode; - } - } - - /** - * 有序列表,与“insertunorderedlist”命令互斥 - * @command insertorderedlist - * @method execCommand - * @param { String } command 命令字符串 - * @param { String } style 插入的有序列表类型,值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2 - * @example - * ```javascript - * editor.execCommand( 'insertorderedlist','decimal'); - * ``` - */ - /** - * 查询当前选区内容是否有序列表 - * @command insertorderedlist - * @method queryCommandState - * @param { String } cmd 命令字符串 - * @return { int } 如果当前选区是有序列表返回1,否则返回0 - * @example - * ```javascript - * editor.queryCommandState( 'insertorderedlist' ); - * ``` - */ - /** - * 查询当前选区内容是否有序列表 - * @command insertorderedlist - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @return { String } 返回当前有序列表的类型,值为null或decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2 - * @example - * ```javascript - * editor.queryCommandValue( 'insertorderedlist' ); - * ``` - */ - - /** - * 无序列表,与“insertorderedlist”命令互斥 - * @command insertunorderedlist - * @method execCommand - * @param { String } command 命令字符串 - * @param { String } style 插入的无序列表类型,值为:circle,disc,square,dash,dot - * @example - * ```javascript - * editor.execCommand( 'insertunorderedlist','circle'); - * ``` - */ - /** - * 查询当前是否有word文档粘贴进来的图片 - * @command insertunorderedlist - * @method insertunorderedlist - * @param { String } command 命令字符串 - * @return { int } 如果当前选区是无序列表返回1,否则返回0 - * @example - * ```javascript - * editor.queryCommandState( 'insertunorderedlist' ); - * ``` - */ - /** - * 查询当前选区内容是否有序列表 - * @command insertunorderedlist - * @method queryCommandValue - * @param { String } command 命令字符串 - * @return { String } 返回当前无序列表的类型,值为null或circle,disc,square,dash,dot - * @example - * ```javascript - * editor.queryCommandValue( 'insertunorderedlist' ); - * ``` - */ - - me.commands['insertorderedlist'] = - me.commands['insertunorderedlist'] = { - execCommand:function (command, style) { - - if (!style) { - style = command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc'; - } - var me = this, - range = this.selection.getRange(), - filterFn = function (node) { - return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node); - }, - tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul', - frag = me.document.createDocumentFragment(); - //去掉是因为会出现选到末尾,导致adjustmentBoundary缩到ol/ul的位置 - //range.shrinkBoundary();//.adjustmentBoundary(); - range.adjustmentBoundary().shrinkBoundary(); - var bko = range.createBookmark(true), - start = getLi(me.document.getElementById(bko.start)), - modifyStart = 0, - end = getLi(me.document.getElementById(bko.end)), - modifyEnd = 0, - startParent, endParent, - list, tmp; - - if (start || end) { - start && (startParent = start.parentNode); - if (!bko.end) { - end = start; - } - end && (endParent = end.parentNode); - - if (startParent === endParent) { - while (start !== end) { - tmp = start; - start = start.nextSibling; - if (!domUtils.isBlockElm(tmp.firstChild)) { - var p = me.document.createElement('p'); - while (tmp.firstChild) { - p.appendChild(tmp.firstChild); - } - tmp.appendChild(p); - } - frag.appendChild(tmp); - } - tmp = me.document.createElement('span'); - startParent.insertBefore(tmp, end); - if (!domUtils.isBlockElm(end.firstChild)) { - p = me.document.createElement('p'); - while (end.firstChild) { - p.appendChild(end.firstChild); - } - end.appendChild(p); - } - frag.appendChild(end); - domUtils.breakParent(tmp, startParent); - if (domUtils.isEmptyNode(tmp.previousSibling)) { - domUtils.remove(tmp.previousSibling); - } - if (domUtils.isEmptyNode(tmp.nextSibling)) { - domUtils.remove(tmp.nextSibling) - } - var nodeStyle = getStyle(startParent) || domUtils.getComputedStyle(startParent, 'list-style-type') || (command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc'); - if (startParent.tagName.toLowerCase() == tag && nodeStyle == style) { - for (var i = 0, ci, tmpFrag = me.document.createDocumentFragment(); ci = frag.firstChild;) { - if(domUtils.isTagNode(ci,'ol ul')){ -// 删除时,子列表不处理 -// utils.each(domUtils.getElementsByTagName(ci,'li'),function(li){ -// while(li.firstChild){ -// tmpFrag.appendChild(li.firstChild); -// } -// -// }); - tmpFrag.appendChild(ci); - }else{ - while (ci.firstChild) { - - tmpFrag.appendChild(ci.firstChild); - domUtils.remove(ci); - } - } - - } - tmp.parentNode.insertBefore(tmpFrag, tmp); - } else { - list = me.document.createElement(tag); - setListStyle(list,style); - list.appendChild(frag); - tmp.parentNode.insertBefore(list, tmp); - } - - domUtils.remove(tmp); - list && adjustList(list, tag, style); - range.moveToBookmark(bko).select(); - return; - } - //开始 - if (start) { - while (start) { - tmp = start.nextSibling; - if (domUtils.isTagNode(start, 'ol ul')) { - frag.appendChild(start); - } else { - var tmpfrag = me.document.createDocumentFragment(), - hasBlock = 0; - while (start.firstChild) { - if (domUtils.isBlockElm(start.firstChild)) { - hasBlock = 1; - } - tmpfrag.appendChild(start.firstChild); - } - if (!hasBlock) { - var tmpP = me.document.createElement('p'); - tmpP.appendChild(tmpfrag); - frag.appendChild(tmpP); - } else { - frag.appendChild(tmpfrag); - } - domUtils.remove(start); - } - - start = tmp; - } - startParent.parentNode.insertBefore(frag, startParent.nextSibling); - if (domUtils.isEmptyNode(startParent)) { - range.setStartBefore(startParent); - domUtils.remove(startParent); - } else { - range.setStartAfter(startParent); - } - modifyStart = 1; - } - - if (end && domUtils.inDoc(endParent, me.document)) { - //结束 - start = endParent.firstChild; - while (start && start !== end) { - tmp = start.nextSibling; - if (domUtils.isTagNode(start, 'ol ul')) { - frag.appendChild(start); - } else { - tmpfrag = me.document.createDocumentFragment(); - hasBlock = 0; - while (start.firstChild) { - if (domUtils.isBlockElm(start.firstChild)) { - hasBlock = 1; - } - tmpfrag.appendChild(start.firstChild); - } - if (!hasBlock) { - tmpP = me.document.createElement('p'); - tmpP.appendChild(tmpfrag); - frag.appendChild(tmpP); - } else { - frag.appendChild(tmpfrag); - } - domUtils.remove(start); - } - start = tmp; - } - var tmpDiv = domUtils.createElement(me.document, 'div', { - 'tmpDiv':1 - }); - domUtils.moveChild(end, tmpDiv); - - frag.appendChild(tmpDiv); - domUtils.remove(end); - endParent.parentNode.insertBefore(frag, endParent); - range.setEndBefore(endParent); - if (domUtils.isEmptyNode(endParent)) { - domUtils.remove(endParent); - } - - modifyEnd = 1; - } - - - } - - if (!modifyStart) { - range.setStartBefore(me.document.getElementById(bko.start)); - } - if (bko.end && !modifyEnd) { - range.setEndAfter(me.document.getElementById(bko.end)); - } - range.enlarge(true, function (node) { - return notExchange[node.tagName]; - }); - - frag = me.document.createDocumentFragment(); - - var bk = range.createBookmark(), - current = domUtils.getNextDomNode(bk.start, false, filterFn), - tmpRange = range.cloneRange(), - tmpNode, - block = domUtils.isBlockElm; - - while (current && current !== bk.end && (domUtils.getPosition(current, bk.end) & domUtils.POSITION_PRECEDING)) { - - if (current.nodeType == 3 || dtd.li[current.tagName]) { - if (current.nodeType == 1 && dtd.$list[current.tagName]) { - while (current.firstChild) { - frag.appendChild(current.firstChild); - } - tmpNode = domUtils.getNextDomNode(current, false, filterFn); - domUtils.remove(current); - current = tmpNode; - continue; - - } - tmpNode = current; - tmpRange.setStartBefore(current); - - while (current && current !== bk.end && (!block(current) || domUtils.isBookmarkNode(current) )) { - tmpNode = current; - current = domUtils.getNextDomNode(current, false, null, function (node) { - return !notExchange[node.tagName]; - }); - } - - if (current && block(current)) { - tmp = domUtils.getNextDomNode(tmpNode, false, filterFn); - if (tmp && domUtils.isBookmarkNode(tmp)) { - current = domUtils.getNextDomNode(tmp, false, filterFn); - tmpNode = tmp; - } - } - tmpRange.setEndAfter(tmpNode); - - current = domUtils.getNextDomNode(tmpNode, false, filterFn); - - var li = range.document.createElement('li'); - - li.appendChild(tmpRange.extractContents()); - if(domUtils.isEmptyNode(li)){ - var tmpNode = range.document.createElement('p'); - while(li.firstChild){ - tmpNode.appendChild(li.firstChild) - } - li.appendChild(tmpNode); - } - frag.appendChild(li); - } else { - current = domUtils.getNextDomNode(current, true, filterFn); - } - } - range.moveToBookmark(bk).collapse(true); - list = me.document.createElement(tag); - setListStyle(list,style); - list.appendChild(frag); - range.insertNode(list); - //当前list上下看能否合并 - adjustList(list, tag, style); - //去掉冗余的tmpDiv - for (var i = 0, ci, tmpDivs = domUtils.getElementsByTagName(list, 'div'); ci = tmpDivs[i++];) { - if (ci.getAttribute('tmpDiv')) { - domUtils.remove(ci, true) - } - } - range.moveToBookmark(bko).select(); - - }, - queryCommandState:function (command) { - var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul'; - var path = this.selection.getStartElementPath(); - for(var i= 0,ci;ci = path[i++];){ - if(ci.nodeName == 'TABLE'){ - return 0 - } - if(tag == ci.nodeName.toLowerCase()){ - return 1 - }; - } - return 0; - - }, - queryCommandValue:function (command) { - var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul'; - var path = this.selection.getStartElementPath(), - node; - for(var i= 0,ci;ci = path[i++];){ - if(ci.nodeName == 'TABLE'){ - node = null; - break; - } - if(tag == ci.nodeName.toLowerCase()){ - node = ci; - break; - }; - } - return node ? getStyle(node) || domUtils.getComputedStyle(node, 'list-style-type') : null; - } - }; -}; - - - -// plugins/source.js -/** - * 源码编辑插件 - * @file - * @since 1.2.6.1 - */ - -(function (){ - var sourceEditors = { - textarea: function (editor, holder){ - var textarea = holder.ownerDocument.createElement('textarea'); - textarea.style.cssText = 'position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;'; - // todo: IE下只有onresize属性可用... 很纠结 - if (browser.ie && browser.version < 8) { - textarea.style.width = holder.offsetWidth + 'px'; - textarea.style.height = holder.offsetHeight + 'px'; - holder.onresize = function (){ - textarea.style.width = holder.offsetWidth + 'px'; - textarea.style.height = holder.offsetHeight + 'px'; - }; - } - holder.appendChild(textarea); - return { - setContent: function (content){ - textarea.value = content; - }, - getContent: function (){ - return textarea.value; - }, - select: function (){ - var range; - if (browser.ie) { - range = textarea.createTextRange(); - range.collapse(true); - range.select(); - } else { - //todo: chrome下无法设置焦点 - textarea.setSelectionRange(0, 0); - textarea.focus(); - } - }, - dispose: function (){ - holder.removeChild(textarea); - // todo - holder.onresize = null; - textarea = null; - holder = null; - } - }; - }, - codemirror: function (editor, holder){ - - var codeEditor = window.CodeMirror(holder, { - mode: "text/html", - tabMode: "indent", - lineNumbers: true, - lineWrapping:true - }); - var dom = codeEditor.getWrapperElement(); - dom.style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;'; - codeEditor.getScrollerElement().style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;'; - codeEditor.refresh(); - return { - getCodeMirror:function(){ - return codeEditor; - }, - setContent: function (content){ - codeEditor.setValue(content); - }, - getContent: function (){ - return codeEditor.getValue(); - }, - select: function (){ - codeEditor.focus(); - }, - dispose: function (){ - holder.removeChild(dom); - dom = null; - codeEditor = null; - } - }; - } - }; - - UE.plugins['source'] = function (){ - var me = this; - var opt = this.options; - var sourceMode = false; - var sourceEditor; - var orgSetContent; - opt.sourceEditor = browser.ie ? 'textarea' : (opt.sourceEditor || 'codemirror'); - - me.setOpt({ - sourceEditorFirst:false - }); - function createSourceEditor(holder){ - return sourceEditors[opt.sourceEditor == 'codemirror' && window.CodeMirror ? 'codemirror' : 'textarea'](me, holder); - } - - var bakCssText; - //解决在源码模式下getContent不能得到最新的内容问题 - var oldGetContent, - bakAddress; - - /** - * 切换源码模式和编辑模式 - * @command source - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'source'); - * ``` - */ - - /** - * 查询当前编辑区域的状态是源码模式还是可视化模式 - * @command source - * @method queryCommandState - * @param { String } cmd 命令字符串 - * @return { int } 如果当前是源码编辑模式,返回1,否则返回0 - * @example - * ```javascript - * editor.queryCommandState( 'source' ); - * ``` - */ - - me.commands['source'] = { - execCommand: function (){ - - sourceMode = !sourceMode; - if (sourceMode) { - bakAddress = me.selection.getRange().createAddress(false,true); - me.undoManger && me.undoManger.save(true); - if(browser.gecko){ - me.body.contentEditable = false; - } - - bakCssText = me.iframe.style.cssText; - me.iframe.style.cssText += 'position:absolute;left:-32768px;top:-32768px;'; - - - me.fireEvent('beforegetcontent'); - var root = UE.htmlparser(me.body.innerHTML); - me.filterOutputRule(root); - root.traversal(function (node) { - if (node.type == 'element') { - switch (node.tagName) { - case 'td': - case 'th': - case 'caption': - if(node.children && node.children.length == 1){ - if(node.firstChild().tagName == 'br' ){ - node.removeChild(node.firstChild()) - } - }; - break; - case 'pre': - node.innerText(node.innerText().replace(/ /g,' ')) - - } - } - }); - - me.fireEvent('aftergetcontent'); - - var content = root.toHtml(true); - - sourceEditor = createSourceEditor(me.iframe.parentNode); - - sourceEditor.setContent(content); - - orgSetContent = me.setContent; - - me.setContent = function(html){ - //这里暂时不触发事件,防止报错 - var root = UE.htmlparser(html); - me.filterInputRule(root); - html = root.toHtml(); - sourceEditor.setContent(html); - }; - - setTimeout(function (){ - sourceEditor.select(); - me.addListener('fullscreenchanged', function(){ - try{ - sourceEditor.getCodeMirror().refresh() - }catch(e){} - }); - }); - - //重置getContent,源码模式下取值也能是最新的数据 - oldGetContent = me.getContent; - me.getContent = function (){ - return sourceEditor.getContent() || '

                  ' + (browser.ie ? '' : '
                  ')+'

                  '; - }; - } else { - me.iframe.style.cssText = bakCssText; - var cont = sourceEditor.getContent() || '

                  ' + (browser.ie ? '' : '
                  ')+'

                  '; - //处理掉block节点前后的空格,有可能会误命中,暂时不考虑 - cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>','g'), function(a,b){ - if(b && !dtd.$inlineWithA[b.toLowerCase()]){ - return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g,''); - } - return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g,'') - }); - - me.setContent = orgSetContent; - - me.setContent(cont); - sourceEditor.dispose(); - sourceEditor = null; - //还原getContent方法 - me.getContent = oldGetContent; - var first = me.body.firstChild; - //trace:1106 都删除空了,下边会报错,所以补充一个p占位 - if(!first){ - me.body.innerHTML = '

                  '+(browser.ie?'':'
                  ')+'

                  '; - first = me.body.firstChild; - } - - - //要在ifm为显示时ff才能取到selection,否则报错 - //这里不能比较位置了 - me.undoManger && me.undoManger.save(true); - - if(browser.gecko){ - - var input = document.createElement('input'); - input.style.cssText = 'position:absolute;left:0;top:-32768px'; - - document.body.appendChild(input); - - me.body.contentEditable = false; - setTimeout(function(){ - domUtils.setViewportOffset(input, { left: -32768, top: 0 }); - input.focus(); - setTimeout(function(){ - me.body.contentEditable = true; - me.selection.getRange().moveToAddress(bakAddress).select(true); - domUtils.remove(input); - }); - - }); - }else{ - //ie下有可能报错,比如在代码顶头的情况 - try{ - me.selection.getRange().moveToAddress(bakAddress).select(true); - }catch(e){} - - } - } - this.fireEvent('sourcemodechanged', sourceMode); - }, - queryCommandState: function (){ - return sourceMode|0; - }, - notNeedUndo : 1 - }; - var oldQueryCommandState = me.queryCommandState; - - me.queryCommandState = function (cmdName){ - cmdName = cmdName.toLowerCase(); - if (sourceMode) { - //源码模式下可以开启的命令 - return cmdName in { - 'source' : 1, - 'fullscreen' : 1 - } ? 1 : -1 - } - return oldQueryCommandState.apply(this, arguments); - }; - - if(opt.sourceEditor == "codemirror"){ - - me.addListener("ready",function(){ - utils.loadFile(document,{ - src : opt.codeMirrorJsUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.js", - tag : "script", - type : "text/javascript", - defer : "defer" - },function(){ - if(opt.sourceEditorFirst){ - setTimeout(function(){ - me.execCommand("source"); - },0); - } - }); - utils.loadFile(document,{ - tag : "link", - rel : "stylesheet", - type : "text/css", - href : opt.codeMirrorCssUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.css" - }); - - }); - } - - }; - -})(); - -// plugins/enterkey.js -///import core -///import plugins/undo.js -///commands 设置回车标签p或br -///commandsName EnterKey -///commandsTitle 设置回车标签p或br -/** - * @description 处理回车 - * @author zhanyi - */ -UE.plugins['enterkey'] = function() { - var hTag, - me = this, - tag = me.options.enterTag; - me.addListener('keyup', function(type, evt) { - - var keyCode = evt.keyCode || evt.which; - if (keyCode == 13) { - var range = me.selection.getRange(), - start = range.startContainer, - doSave; - - //修正在h1-h6里边回车后不能嵌套p的问题 - if (!browser.ie) { - - if (/h\d/i.test(hTag)) { - if (browser.gecko) { - var h = domUtils.findParentByTagName(start, [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption','table'], true); - if (!h) { - me.document.execCommand('formatBlock', false, '

                  '); - doSave = 1; - } - } else { - //chrome remove div - if (start.nodeType == 1) { - var tmp = me.document.createTextNode(''),div; - range.insertNode(tmp); - div = domUtils.findParentByTagName(tmp, 'div', true); - if (div) { - var p = me.document.createElement('p'); - while (div.firstChild) { - p.appendChild(div.firstChild); - } - div.parentNode.insertBefore(p, div); - domUtils.remove(div); - range.setStartBefore(tmp).setCursor(); - doSave = 1; - } - domUtils.remove(tmp); - - } - } - - if (me.undoManger && doSave) { - me.undoManger.save(); - } - } - //没有站位符,会出现多行的问题 - browser.opera && range.select(); - }else{ - me.fireEvent('saveScene',true,true) - } - } - }); - - me.addListener('keydown', function(type, evt) { - var keyCode = evt.keyCode || evt.which; - if (keyCode == 13) {//回车 - if(me.fireEvent('beforeenterkeydown')){ - domUtils.preventDefault(evt); - return; - } - me.fireEvent('saveScene',true,true); - hTag = ''; - - - var range = me.selection.getRange(); - - if (!range.collapsed) { - //跨td不能删 - var start = range.startContainer, - end = range.endContainer, - startTd = domUtils.findParentByTagName(start, 'td', true), - endTd = domUtils.findParentByTagName(end, 'td', true); - if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) { - evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false); - return; - } - } - if (tag == 'p') { - - - if (!browser.ie) { - - start = domUtils.findParentByTagName(range.startContainer, ['ol','ul','p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption'], true); - - //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command - //trace:2431 - if (!start && !browser.opera) { - - me.document.execCommand('formatBlock', false, '

                  '); - - if (browser.gecko) { - range = me.selection.getRange(); - start = domUtils.findParentByTagName(range.startContainer, 'p', true); - start && domUtils.removeDirtyAttr(start); - } - - - } else { - hTag = start.tagName; - start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start); - } - - } - - } else { - evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false); - - if (!range.collapsed) { - range.deleteContents(); - start = range.startContainer; - if (start.nodeType == 1 && (start = start.childNodes[range.startOffset])) { - while (start.nodeType == 1) { - if (dtd.$empty[start.tagName]) { - range.setStartBefore(start).setCursor(); - if (me.undoManger) { - me.undoManger.save(); - } - return false; - } - if (!start.firstChild) { - var br = range.document.createElement('br'); - start.appendChild(br); - range.setStart(start, 0).setCursor(); - if (me.undoManger) { - me.undoManger.save(); - } - return false; - } - start = start.firstChild; - } - if (start === range.startContainer.childNodes[range.startOffset]) { - br = range.document.createElement('br'); - range.insertNode(br).setCursor(); - - } else { - range.setStart(start, 0).setCursor(); - } - - - } else { - br = range.document.createElement('br'); - range.insertNode(br).setStartAfter(br).setCursor(); - } - - - } else { - br = range.document.createElement('br'); - range.insertNode(br); - var parent = br.parentNode; - if (parent.lastChild === br) { - br.parentNode.insertBefore(br.cloneNode(true), br); - range.setStartBefore(br); - } else { - range.setStartAfter(br); - } - range.setCursor(); - - } - - } - - } - }); -}; - - -// plugins/keystrokes.js -/* 处理特殊键的兼容性问题 */ -UE.plugins['keystrokes'] = function() { - var me = this; - var collapsed = true; - me.addListener('keydown', function(type, evt) { - var keyCode = evt.keyCode || evt.which, - rng = me.selection.getRange(); - - //处理全选的情况 - if(!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <=90 - || keyCode >= 48 && keyCode <= 57 || - keyCode >= 96 && keyCode <= 111 || { - 13:1, - 8:1, - 46:1 - }[keyCode]) - ){ - - var tmpNode = rng.startContainer; - if(domUtils.isFillChar(tmpNode)){ - rng.setStartBefore(tmpNode) - } - tmpNode = rng.endContainer; - if(domUtils.isFillChar(tmpNode)){ - rng.setEndAfter(tmpNode) - } - rng.txtToElmBoundary(); - //结束边界可能放到了br的前边,要把br包含进来 - // x[xxx]
                  - if(rng.endContainer && rng.endContainer.nodeType == 1){ - tmpNode = rng.endContainer.childNodes[rng.endOffset]; - if(tmpNode && domUtils.isBr(tmpNode)){ - rng.setEndAfter(tmpNode); - } - } - if(rng.startOffset == 0){ - tmpNode = rng.startContainer; - if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){ - tmpNode = rng.endContainer; - if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){ - me.fireEvent('saveScene'); - me.body.innerHTML = '

                  '+(browser.ie ? '' : '
                  ')+'

                  '; - rng.setStart(me.body.firstChild,0).setCursor(false,true); - me._selectionChange(); - return; - } - } - } - } - - //处理backspace - if (keyCode == keymap.Backspace) { - rng = me.selection.getRange(); - collapsed = rng.collapsed; - if(me.fireEvent('delkeydown',evt)){ - return; - } - var start,end; - //避免按两次删除才能生效的问题 - if(rng.collapsed && rng.inFillChar()){ - start = rng.startContainer; - - if(domUtils.isFillChar(start)){ - rng.setStartBefore(start).shrinkBoundary(true).collapse(true); - domUtils.remove(start) - }else{ - start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),''); - rng.startOffset--; - rng.collapse(true).select(true) - } - } - - //解决选中control元素不能删除的问题 - if (start = rng.getClosedNode()) { - me.fireEvent('saveScene'); - rng.setStartBefore(start); - domUtils.remove(start); - rng.setCursor(); - me.fireEvent('saveScene'); - domUtils.preventDefault(evt); - return; - } - //阻止在table上的删除 - if (!browser.ie) { - start = domUtils.findParentByTagName(rng.startContainer, 'table', true); - end = domUtils.findParentByTagName(rng.endContainer, 'table', true); - if (start && !end || !start && end || start !== end) { - evt.preventDefault(); - return; - } - } - - } - //处理tab键的逻辑 - if (keyCode == keymap.Tab) { - //不处理以下标签 - var excludeTagNameForTabKey = { - 'ol' : 1, - 'ul' : 1, - 'table':1 - }; - //处理组件里的tab按下事件 - if(me.fireEvent('tabkeydown',evt)){ - domUtils.preventDefault(evt); - return; - } - var range = me.selection.getRange(); - me.fireEvent('saveScene'); - for (var i = 0,txt = '',tabSize = me.options.tabSize|| 4,tabNode = me.options.tabNode || ' '; i < tabSize; i++) { - txt += tabNode; - } - var span = me.document.createElement('span'); - span.innerHTML = txt + domUtils.fillChar; - if (range.collapsed) { - range.insertNode(span.cloneNode(true).firstChild).setCursor(true); - } else { - var filterFn = function(node) { - return domUtils.isBlockElm(node) && !excludeTagNameForTabKey[node.tagName.toLowerCase()] - - }; - //普通的情况 - start = domUtils.findParent(range.startContainer, filterFn,true); - end = domUtils.findParent(range.endContainer, filterFn,true); - if (start && end && start === end) { - range.deleteContents(); - range.insertNode(span.cloneNode(true).firstChild).setCursor(true); - } else { - var bookmark = range.createBookmark(); - range.enlarge(true); - var bookmark2 = range.createBookmark(), - current = domUtils.getNextDomNode(bookmark2.start, false, filterFn); - while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) { - current.insertBefore(span.cloneNode(true).firstChild, current.firstChild); - current = domUtils.getNextDomNode(current, false, filterFn); - } - range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select(); - } - } - domUtils.preventDefault(evt) - } - //trace:1634 - //ff的del键在容器空的时候,也会删除 - if(browser.gecko && keyCode == 46){ - range = me.selection.getRange(); - if(range.collapsed){ - start = range.startContainer; - if(domUtils.isEmptyBlock(start)){ - var parent = start.parentNode; - while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){ - start = parent; - parent = parent.parentNode; - } - if(start === parent.lastChild) - evt.preventDefault(); - return; - } - } - } - }); - me.addListener('keyup', function(type, evt) { - var keyCode = evt.keyCode || evt.which, - rng,me = this; - if(keyCode == keymap.Backspace){ - if(me.fireEvent('delkeyup')){ - return; - } - rng = me.selection.getRange(); - if(rng.collapsed){ - var tmpNode, - autoClearTagName = ['h1','h2','h3','h4','h5','h6']; - if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){ - if(domUtils.isEmptyBlock(tmpNode)){ - var pre = tmpNode.previousSibling; - if(pre && pre.nodeName != 'TABLE'){ - domUtils.remove(tmpNode); - rng.setStartAtLast(pre).setCursor(false,true); - return; - }else{ - var next = tmpNode.nextSibling; - if(next && next.nodeName != 'TABLE'){ - domUtils.remove(tmpNode); - rng.setStartAtFirst(next).setCursor(false,true); - return; - } - } - } - } - //处理当删除到body时,要重新给p标签展位 - if(domUtils.isBody(rng.startContainer)){ - var tmpNode = domUtils.createElement(me.document,'p',{ - 'innerHTML' : browser.ie ? domUtils.fillChar : '
                  ' - }); - rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true); - } - } - - - //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了 - if( !collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){ - if(browser.ie){ - var span = rng.document.createElement('span'); - rng.insertNode(span).setStartBefore(span).collapse(true); - rng.select(); - domUtils.remove(span) - }else{ - rng.select() - } - - } - } - - - }) -}; - -// plugins/fiximgclick.js -///import core -///commands 修复chrome下图片不能点击的问题,出现八个角可改变大小 -///commandsName FixImgClick -///commandsTitle 修复chrome下图片不能点击的问题,出现八个角可改变大小 -//修复chrome下图片不能点击的问题,出现八个角可改变大小 - -UE.plugins['fiximgclick'] = (function () { - - var elementUpdated = false; - function Scale() { - this.editor = null; - this.resizer = null; - this.cover = null; - this.doc = document; - this.prePos = {x: 0, y: 0}; - this.startPos = {x: 0, y: 0}; - } - - (function () { - var rect = [ - //[left, top, width, height] - [0, 0, -1, -1], - [0, 0, 0, -1], - [0, 0, 1, -1], - [0, 0, -1, 0], - [0, 0, 1, 0], - [0, 0, -1, 1], - [0, 0, 0, 1], - [0, 0, 1, 1] - ]; - - Scale.prototype = { - init: function (editor) { - var me = this; - me.editor = editor; - me.startPos = this.prePos = {x: 0, y: 0}; - me.dragId = -1; - - var hands = [], - cover = me.cover = document.createElement('div'), - resizer = me.resizer = document.createElement('div'); - - cover.id = me.editor.ui.id + '_imagescale_cover'; - cover.style.cssText = 'position:absolute;display:none;z-index:' + (me.editor.options.zIndex) + ';filter:alpha(opacity=0); opacity:0;background:#CCC;'; - domUtils.on(cover, 'mousedown click', function () { - me.hide(); - }); - - for (i = 0; i < 8; i++) { - hands.push(''); - } - resizer.id = me.editor.ui.id + '_imagescale'; - resizer.className = 'edui-editor-imagescale'; - resizer.innerHTML = hands.join(''); - resizer.style.cssText += ';display:none;border:1px solid #3b77ff;z-index:' + (me.editor.options.zIndex) + ';'; - - me.editor.ui.getDom().appendChild(cover); - me.editor.ui.getDom().appendChild(resizer); - - me.initStyle(); - me.initEvents(); - }, - initStyle: function () { - utils.cssRule('imagescale', '.edui-editor-imagescale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;}' + - '.edui-editor-imagescale span{position:absolute;width:6px;height:6px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}' - + '.edui-editor-imagescale .edui-editor-imagescale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}' - + '.edui-editor-imagescale .edui-editor-imagescale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}' - + '.edui-editor-imagescale .edui-editor-imagescale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}' - + '.edui-editor-imagescale .edui-editor-imagescale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}' - + '.edui-editor-imagescale .edui-editor-imagescale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}' - + '.edui-editor-imagescale .edui-editor-imagescale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}' - + '.edui-editor-imagescale .edui-editor-imagescale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}' - + '.edui-editor-imagescale .edui-editor-imagescale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}'); - }, - initEvents: function () { - var me = this; - - me.startPos.x = me.startPos.y = 0; - me.isDraging = false; - }, - _eventHandler: function (e) { - var me = this; - switch (e.type) { - case 'mousedown': - var hand = e.target || e.srcElement, hand; - if (hand.className.indexOf('edui-editor-imagescale-hand') != -1 && me.dragId == -1) { - me.dragId = hand.className.slice(-1); - me.startPos.x = me.prePos.x = e.clientX; - me.startPos.y = me.prePos.y = e.clientY; - domUtils.on(me.doc,'mousemove', me.proxy(me._eventHandler, me)); - } - break; - case 'mousemove': - if (me.dragId != -1) { - me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y}); - me.prePos.x = e.clientX; - me.prePos.y = e.clientY; - elementUpdated = true; - me.updateTargetElement(); - - } - break; - case 'mouseup': - if (me.dragId != -1) { - me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y}); - me.updateTargetElement(); - if (me.target.parentNode) me.attachTo(me.target); - me.dragId = -1; - } - domUtils.un(me.doc,'mousemove', me.proxy(me._eventHandler, me)); - //修复只是点击挪动点,但没有改变大小,不应该触发contentchange - if(elementUpdated){ - elementUpdated = false; - me.editor.fireEvent('contentchange'); - } - - break; - default: - break; - } - }, - updateTargetElement: function () { - var me = this; - domUtils.setStyles(me.target, { - 'width': me.resizer.style.width, - 'height': me.resizer.style.height - }); - me.target.width = parseInt(me.resizer.style.width); - me.target.height = parseInt(me.resizer.style.height); - me.attachTo(me.target); - }, - updateContainerStyle: function (dir, offset) { - var me = this, - dom = me.resizer, tmp; - - if (rect[dir][0] != 0) { - tmp = parseInt(dom.style.left) + offset.x; - dom.style.left = me._validScaledProp('left', tmp) + 'px'; - } - if (rect[dir][1] != 0) { - tmp = parseInt(dom.style.top) + offset.y; - dom.style.top = me._validScaledProp('top', tmp) + 'px'; - } - if (rect[dir][2] != 0) { - tmp = dom.clientWidth + rect[dir][2] * offset.x; - dom.style.width = me._validScaledProp('width', tmp) + 'px'; - } - if (rect[dir][3] != 0) { - tmp = dom.clientHeight + rect[dir][3] * offset.y; - dom.style.height = me._validScaledProp('height', tmp) + 'px'; - } - }, - _validScaledProp: function (prop, value) { - var ele = this.resizer, - wrap = document; - - value = isNaN(value) ? 0 : value; - switch (prop) { - case 'left': - return value < 0 ? 0 : (value + ele.clientWidth) > wrap.clientWidth ? wrap.clientWidth - ele.clientWidth : value; - case 'top': - return value < 0 ? 0 : (value + ele.clientHeight) > wrap.clientHeight ? wrap.clientHeight - ele.clientHeight : value; - case 'width': - return value <= 0 ? 1 : (value + ele.offsetLeft) > wrap.clientWidth ? wrap.clientWidth - ele.offsetLeft : value; - case 'height': - return value <= 0 ? 1 : (value + ele.offsetTop) > wrap.clientHeight ? wrap.clientHeight - ele.offsetTop : value; - } - }, - hideCover: function () { - this.cover.style.display = 'none'; - }, - showCover: function () { - var me = this, - editorPos = domUtils.getXY(me.editor.ui.getDom()), - iframePos = domUtils.getXY(me.editor.iframe); - - domUtils.setStyles(me.cover, { - 'width': me.editor.iframe.offsetWidth + 'px', - 'height': me.editor.iframe.offsetHeight + 'px', - 'top': iframePos.y - editorPos.y + 'px', - 'left': iframePos.x - editorPos.x + 'px', - 'position': 'absolute', - 'display': '' - }) - }, - show: function (targetObj) { - var me = this; - me.resizer.style.display = 'block'; - if(targetObj) me.attachTo(targetObj); - - domUtils.on(this.resizer, 'mousedown', me.proxy(me._eventHandler, me)); - domUtils.on(me.doc, 'mouseup', me.proxy(me._eventHandler, me)); - - me.showCover(); - me.editor.fireEvent('afterscaleshow', me); - me.editor.fireEvent('saveScene'); - }, - hide: function () { - var me = this; - me.hideCover(); - me.resizer.style.display = 'none'; - - domUtils.un(me.resizer, 'mousedown', me.proxy(me._eventHandler, me)); - domUtils.un(me.doc, 'mouseup', me.proxy(me._eventHandler, me)); - me.editor.fireEvent('afterscalehide', me); - }, - proxy: function( fn, context ) { - return function(e) { - return fn.apply( context || this, arguments); - }; - }, - attachTo: function (targetObj) { - var me = this, - target = me.target = targetObj, - resizer = this.resizer, - imgPos = domUtils.getXY(target), - iframePos = domUtils.getXY(me.editor.iframe), - editorPos = domUtils.getXY(resizer.parentNode); - - domUtils.setStyles(resizer, { - 'width': target.width + 'px', - 'height': target.height + 'px', - 'left': iframePos.x + imgPos.x - me.editor.document.body.scrollLeft - editorPos.x - parseInt(resizer.style.borderLeftWidth) + 'px', - 'top': iframePos.y + imgPos.y - me.editor.document.body.scrollTop - editorPos.y - parseInt(resizer.style.borderTopWidth) + 'px' - }); - } - } - })(); - - return function () { - var me = this, - imageScale; - - me.setOpt('imageScaleEnabled', true); - - if ( !browser.ie && me.options.imageScaleEnabled) { - me.addListener('click', function (type, e) { - - var range = me.selection.getRange(), - img = range.getClosedNode(); - - if (img && img.tagName == 'IMG' && me.body.contentEditable!="false") { - - if (img.className.indexOf("edui-faked-music") != -1 || - img.getAttribute("anchorname") || - domUtils.hasClass(img, 'loadingclass') || - domUtils.hasClass(img, 'loaderrorclass')) { return } - - if (!imageScale) { - imageScale = new Scale(); - imageScale.init(me); - me.ui.getDom().appendChild(imageScale.resizer); - - var _keyDownHandler = function (e) { - imageScale.hide(); - if(imageScale.target) me.selection.getRange().selectNode(imageScale.target).select(); - }, _mouseDownHandler = function (e) { - var ele = e.target || e.srcElement; - if (ele && (ele.className===undefined || ele.className.indexOf('edui-editor-imagescale') == -1)) { - _keyDownHandler(e); - } - }, timer; - - me.addListener('afterscaleshow', function (e) { - me.addListener('beforekeydown', _keyDownHandler); - me.addListener('beforemousedown', _mouseDownHandler); - domUtils.on(document, 'keydown', _keyDownHandler); - domUtils.on(document,'mousedown', _mouseDownHandler); - me.selection.getNative().removeAllRanges(); - }); - me.addListener('afterscalehide', function (e) { - me.removeListener('beforekeydown', _keyDownHandler); - me.removeListener('beforemousedown', _mouseDownHandler); - domUtils.un(document, 'keydown', _keyDownHandler); - domUtils.un(document,'mousedown', _mouseDownHandler); - var target = imageScale.target; - if (target.parentNode) { - me.selection.getRange().selectNode(target).select(); - } - }); - //TODO 有iframe的情况,mousedown不能往下传。。 - domUtils.on(imageScale.resizer, 'mousedown', function (e) { - me.selection.getNative().removeAllRanges(); - var ele = e.target || e.srcElement; - if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) { - timer = setTimeout(function () { - imageScale.hide(); - if(imageScale.target) me.selection.getRange().selectNode(ele).select(); - }, 200); - } - }); - domUtils.on(imageScale.resizer, 'mouseup', function (e) { - var ele = e.target || e.srcElement; - if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) { - clearTimeout(timer); - } - }); - } - imageScale.show(img); - } else { - if (imageScale && imageScale.resizer.style.display != 'none') imageScale.hide(); - } - }); - } - - if (browser.webkit) { - me.addListener('click', function (type, e) { - if (e.target.tagName == 'IMG' && me.body.contentEditable!="false") { - var range = new dom.Range(me.document); - range.selectNode(e.target).select(); - } - }); - } - } -})(); - -// plugins/autolink.js -///import core -///commands 为非ie浏览器自动添加a标签 -///commandsName AutoLink -///commandsTitle 自动增加链接 -/** - * @description 为非ie浏览器自动添加a标签 - * @author zhanyi - */ - -UE.plugin.register('autolink',function(){ - var cont = 0; - - return !browser.ie ? { - - bindEvents:{ - 'reset' : function(){ - cont = 0; - }, - 'keydown':function(type, evt) { - var me = this; - var keyCode = evt.keyCode || evt.which; - - if (keyCode == 32 || keyCode == 13) { - - var sel = me.selection.getNative(), - range = sel.getRangeAt(0).cloneRange(), - offset, - charCode; - - var start = range.startContainer; - while (start.nodeType == 1 && range.startOffset > 0) { - start = range.startContainer.childNodes[range.startOffset - 1]; - if (!start){ - break; - } - range.setStart(start, start.nodeType == 1 ? start.childNodes.length : start.nodeValue.length); - range.collapse(true); - start = range.startContainer; - } - - do{ - if (range.startOffset == 0) { - start = range.startContainer.previousSibling; - - while (start && start.nodeType == 1) { - start = start.lastChild; - } - if (!start || domUtils.isFillChar(start)){ - break; - } - offset = start.nodeValue.length; - } else { - start = range.startContainer; - offset = range.startOffset; - } - range.setStart(start, offset - 1); - charCode = range.toString().charCodeAt(0); - } while (charCode != 160 && charCode != 32); - - if (range.toString().replace(new RegExp(domUtils.fillChar, 'g'), '').match(/(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i)) { - while(range.toString().length){ - if(/^(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i.test(range.toString())){ - break; - } - try{ - range.setStart(range.startContainer,range.startOffset+1); - }catch(e){ - //trace:2121 - var start = range.startContainer; - while(!(next = start.nextSibling)){ - if(domUtils.isBody(start)){ - return; - } - start = start.parentNode; - - } - range.setStart(next,0); - - } - - } - //range的开始边界已经在a标签里的不再处理 - if(domUtils.findParentByTagName(range.startContainer,'a',true)){ - return; - } - var a = me.document.createElement('a'),text = me.document.createTextNode(' '),href; - - me.undoManger && me.undoManger.save(); - a.appendChild(range.extractContents()); - a.href = a.innerHTML = a.innerHTML.replace(/<[^>]+>/g,''); - href = a.getAttribute("href").replace(new RegExp(domUtils.fillChar,'g'),''); - href = /^(?:https?:\/\/)/ig.test(href) ? href : "http://"+ href; - a.setAttribute('_src',utils.html(href)); - a.href = utils.html(href); - - range.insertNode(a); - a.parentNode.insertBefore(text, a.nextSibling); - range.setStart(text, 0); - range.collapse(true); - sel.removeAllRanges(); - sel.addRange(range); - me.undoManger && me.undoManger.save(); - } - } - } - } - }:{} - },function(){ - var keyCodes = { - 37:1, 38:1, 39:1, 40:1, - 13:1,32:1 - }; - function checkIsCludeLink(node){ - if(node.nodeType == 3){ - return null - } - if(node.nodeName == 'A'){ - return node; - } - var lastChild = node.lastChild; - - while(lastChild){ - if(lastChild.nodeName == 'A'){ - return lastChild; - } - if(lastChild.nodeType == 3){ - if(domUtils.isWhitespace(lastChild)){ - lastChild = lastChild.previousSibling; - continue; - } - return null - } - lastChild = lastChild.lastChild; - } - } - browser.ie && this.addListener('keyup',function(cmd,evt){ - var me = this,keyCode = evt.keyCode; - if(keyCodes[keyCode]){ - var rng = me.selection.getRange(); - var start = rng.startContainer; - - if(keyCode == 13){ - while(start && !domUtils.isBody(start) && !domUtils.isBlockElm(start)){ - start = start.parentNode; - } - if(start && !domUtils.isBody(start) && start.nodeName == 'P'){ - var pre = start.previousSibling; - if(pre && pre.nodeType == 1){ - var pre = checkIsCludeLink(pre); - if(pre && !pre.getAttribute('_href')){ - domUtils.remove(pre,true); - } - } - } - }else if(keyCode == 32 ){ - if(start.nodeType == 3 && /^\s$/.test(start.nodeValue)){ - start = start.previousSibling; - if(start && start.nodeName == 'A' && !start.getAttribute('_href')){ - domUtils.remove(start,true); - } - } - }else { - start = domUtils.findParentByTagName(start,'a',true); - if(start && !start.getAttribute('_href')){ - var bk = rng.createBookmark(); - - domUtils.remove(start,true); - rng.moveToBookmark(bk).select(true) - } - } - - } - - - }); - } -); - -// plugins/autoheight.js -///import core -///commands 当输入内容超过编辑器高度时,编辑器自动增高 -///commandsName AutoHeight,autoHeightEnabled -///commandsTitle 自动增高 -/** - * @description 自动伸展 - * @author zhanyi - */ -UE.plugins['autoheight'] = function () { - var me = this; - //提供开关,就算加载也可以关闭 - me.autoHeightEnabled = me.options.autoHeightEnabled !== false; - if (!me.autoHeightEnabled) { - return; - } - - var bakOverflow, - lastHeight = 0, - options = me.options, - currentHeight, - timer; - - function adjustHeight() { - var me = this; - clearTimeout(timer); - if(isFullscreen)return; - if (!me.queryCommandState || me.queryCommandState && me.queryCommandState('source') != 1) { - timer = setTimeout(function(){ - - var node = me.body.lastChild; - while(node && node.nodeType != 1){ - node = node.previousSibling; - } - if(node && node.nodeType == 1){ - node.style.clear = 'both'; - currentHeight = Math.max(domUtils.getXY(node).y + node.offsetHeight + 25 ,Math.max(options.minFrameHeight, options.initialFrameHeight)) ; - if (currentHeight != lastHeight) { - if (currentHeight !== parseInt(me.iframe.parentNode.style.height)) { - me.iframe.parentNode.style.height = currentHeight + 'px'; - } - me.body.style.height = currentHeight + 'px'; - lastHeight = currentHeight; - } - domUtils.removeStyle(node,'clear'); - } - - - },50) - } - } - var isFullscreen; - me.addListener('fullscreenchanged',function(cmd,f){ - isFullscreen = f - }); - me.addListener('destroy', function () { - me.removeListener('contentchange afterinserthtml keyup mouseup',adjustHeight) - }); - me.enableAutoHeight = function () { - var me = this; - if (!me.autoHeightEnabled) { - return; - } - var doc = me.document; - me.autoHeightEnabled = true; - bakOverflow = doc.body.style.overflowY; - doc.body.style.overflowY = 'hidden'; - me.addListener('contentchange afterinserthtml keyup mouseup',adjustHeight); - //ff不给事件算得不对 - - setTimeout(function () { - adjustHeight.call(me); - }, browser.gecko ? 100 : 0); - me.fireEvent('autoheightchanged', me.autoHeightEnabled); - }; - me.disableAutoHeight = function () { - - me.body.style.overflowY = bakOverflow || ''; - - me.removeListener('contentchange', adjustHeight); - me.removeListener('keyup', adjustHeight); - me.removeListener('mouseup', adjustHeight); - me.autoHeightEnabled = false; - me.fireEvent('autoheightchanged', me.autoHeightEnabled); - }; - - me.on('setHeight',function(){ - me.disableAutoHeight() - }); - me.addListener('ready', function () { - me.enableAutoHeight(); - //trace:1764 - var timer; - domUtils.on(browser.ie ? me.body : me.document, browser.webkit ? 'dragover' : 'drop', function () { - clearTimeout(timer); - timer = setTimeout(function () { - //trace:3681 - adjustHeight.call(me); - }, 100); - - }); - //修复内容过多时,回到顶部,顶部内容被工具栏遮挡问题 - var lastScrollY; - window.onscroll = function(){ - if(lastScrollY === null){ - lastScrollY = this.scrollY - }else if(this.scrollY == 0 && lastScrollY != 0){ - me.window.scrollTo(0,0); - lastScrollY = null; - } - } - }); - - -}; - - - -// plugins/autofloat.js -///import core -///commands 悬浮工具栏 -///commandsName AutoFloat,autoFloatEnabled -///commandsTitle 悬浮工具栏 -/** - * modified by chengchao01 - * 注意: 引入此功能后,在IE6下会将body的背景图片覆盖掉! - */ -UE.plugins['autofloat'] = function() { - var me = this, - lang = me.getLang(); - me.setOpt({ - topOffset:0 - }); - var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false, - topOffset = me.options.topOffset; - - - //如果不固定toolbar的位置,则直接退出 - if(!optsAutoFloatEnabled){ - return; - } - var uiUtils = UE.ui.uiUtils, - LteIE6 = browser.ie && browser.version <= 6, - quirks = browser.quirks; - - function checkHasUI(){ - if(!UE.ui){ - alert(lang.autofloatMsg); - return 0; - } - return 1; - } - function fixIE6FixedPos(){ - var docStyle = document.body.style; - docStyle.backgroundImage = 'url("about:blank")'; - docStyle.backgroundAttachment = 'fixed'; - } - var bakCssText, - placeHolder = document.createElement('div'), - toolbarBox,orgTop, - getPosition, - flag =true; //ie7模式下需要偏移 - function setFloating(){ - var toobarBoxPos = domUtils.getXY(toolbarBox), - origalFloat = domUtils.getComputedStyle(toolbarBox,'position'), - origalLeft = domUtils.getComputedStyle(toolbarBox,'left'); - toolbarBox.style.width = toolbarBox.offsetWidth + 'px'; - toolbarBox.style.zIndex = me.options.zIndex * 1 + 1; - toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox); - if (LteIE6 || (quirks && browser.ie)) { - if(toolbarBox.style.position != 'absolute'){ - toolbarBox.style.position = 'absolute'; - } - toolbarBox.style.top = (document.body.scrollTop||document.documentElement.scrollTop) - orgTop + topOffset + 'px'; - } else { - if (browser.ie7Compat && flag) { - flag = false; - toolbarBox.style.left = domUtils.getXY(toolbarBox).x - document.documentElement.getBoundingClientRect().left+2 + 'px'; - } - if(toolbarBox.style.position != 'fixed'){ - toolbarBox.style.position = 'fixed'; - toolbarBox.style.top = topOffset +"px"; - ((origalFloat == 'absolute' || origalFloat == 'relative') && parseFloat(origalLeft)) && (toolbarBox.style.left = toobarBoxPos.x + 'px'); - } - } - } - function unsetFloating(){ - flag = true; - if(placeHolder.parentNode){ - placeHolder.parentNode.removeChild(placeHolder); - } - - toolbarBox.style.cssText = bakCssText; - } - - function updateFloating(){ - var rect3 = getPosition(me.container); - var offset=me.options.toolbarTopOffset||0; - if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) { - setFloating(); - }else{ - unsetFloating(); - } - } - var defer_updateFloating = utils.defer(function(){ - updateFloating(); - },browser.ie ? 200 : 100,true); - - me.addListener('destroy',function(){ - domUtils.un(window, ['scroll','resize'], updateFloating); - me.removeListener('keydown', defer_updateFloating); - }); - - me.addListener('ready', function(){ - if(checkHasUI(me)){ - //加载了ui组件,但在new时,没有加载ui,导致编辑器实例上没有ui类,所以这里做判断 - if(!me.ui){ - return; - } - getPosition = uiUtils.getClientRect; - toolbarBox = me.ui.getDom('toolbarbox'); - orgTop = getPosition(toolbarBox).top; - bakCssText = toolbarBox.style.cssText; - placeHolder.style.height = toolbarBox.offsetHeight + 'px'; - if(LteIE6){ - fixIE6FixedPos(); - } - domUtils.on(window, ['scroll','resize'], updateFloating); - me.addListener('keydown', defer_updateFloating); - - me.addListener('beforefullscreenchange', function (t, enabled){ - if (enabled) { - unsetFloating(); - } - }); - me.addListener('fullscreenchanged', function (t, enabled){ - if (!enabled) { - updateFloating(); - } - }); - me.addListener('sourcemodechanged', function (t, enabled){ - setTimeout(function (){ - updateFloating(); - },0); - }); - me.addListener("clearDoc",function(){ - setTimeout(function(){ - updateFloating(); - },0); - - }) - } - }); -}; - - -// plugins/video.js -/** - * video插件, 为UEditor提供视频插入支持 - * @file - * @since 1.2.6.1 - */ - -UE.plugins['video'] = function (){ - var me =this; - - /** - * 创建插入视频字符窜 - * @param url 视频地址 - * @param width 视频宽度 - * @param height 视频高度 - * @param align 视频对齐 - * @param toEmbed 是否以flash代替显示 - * @param addParagraph 是否需要添加P 标签 - */ - function creatInsertStr(url,width,height,id,align,classname,type){ - var str; - switch (type){ - case 'image': - str = '' - break; - case 'embed': - str = ''; - break; - case 'video': - var ext = url.substr(url.lastIndexOf('.') + 1); - if(ext == 'ogv') ext = 'ogg'; - str = '' + - ''; - break; - } - return str; - } - - function switchImgAndVideo(root,img2video){ - utils.each(root.getNodesByTagName(img2video ? 'img' : 'embed video'),function(node){ - var className = node.getAttr('class'); - if(className && className.indexOf('edui-faked-video') != -1){ - var html = creatInsertStr( img2video ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',className,img2video ? 'embed':'image'); - node.parentNode.replaceChild(UE.uNode.createElement(html),node); - } - if(className && className.indexOf('edui-upload-video') != -1){ - var html = creatInsertStr( img2video ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',className,img2video ? 'video':'image'); - node.parentNode.replaceChild(UE.uNode.createElement(html),node); - } - }) - } - - me.addOutputRule(function(root){ - switchImgAndVideo(root,true) - }); - me.addInputRule(function(root){ - switchImgAndVideo(root) - }); - - /** - * 插入视频 - * @command insertvideo - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { Object } videoAttr 键值对对象, 描述一个视频的所有属性 - * @example - * ```javascript - * - * var videoAttr = { - * //视频地址 - * url: 'http://www.youku.com/xxx', - * //视频宽高值, 单位px - * width: 200, - * height: 100 - * }; - * - * //editor 是编辑器实例 - * //向编辑器插入单个视频 - * editor.execCommand( 'insertvideo', videoAttr ); - * ``` - */ - - /** - * 插入视频 - * @command insertvideo - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { Array } videoArr 需要插入的视频的数组, 其中的每一个元素都是一个键值对对象, 描述了一个视频的所有属性 - * @example - * ```javascript - * - * var videoAttr1 = { - * //视频地址 - * url: 'http://www.youku.com/xxx', - * //视频宽高值, 单位px - * width: 200, - * height: 100 - * }, - * videoAttr2 = { - * //视频地址 - * url: 'http://www.youku.com/xxx', - * //视频宽高值, 单位px - * width: 200, - * height: 100 - * } - * - * //editor 是编辑器实例 - * //该方法将会向编辑器内插入两个视频 - * editor.execCommand( 'insertvideo', [ videoAttr1, videoAttr2 ] ); - * ``` - */ - - /** - * 查询当前光标所在处是否是一个视频 - * @command insertvideo - * @method queryCommandState - * @param { String } cmd 需要查询的命令字符串 - * @return { int } 如果当前光标所在处的元素是一个视频对象, 则返回1,否则返回0 - * @example - * ```javascript - * - * //editor 是编辑器实例 - * editor.queryCommandState( 'insertvideo' ); - * ``` - */ - me.commands["insertvideo"] = { - execCommand: function (cmd, videoObjs, type){ - videoObjs = utils.isArray(videoObjs)?videoObjs:[videoObjs]; - var html = [],id = 'tmpVedio', cl; - for(var i=0,vi,len = videoObjs.length;i 0) { - return 0; - } - for (var i in dtd.$isNotEmpty) if (dtd.$isNotEmpty.hasOwnProperty(i)) { - if (node.getElementsByTagName(i).length) { - return 0; - } - } - return 1; - }; - UETable.getWidth = function (cell) { - if (!cell)return 0; - return parseInt(domUtils.getComputedStyle(cell, "width"), 10); - }; - - /** - * 获取单元格或者单元格组的“对齐”状态。 如果当前的检测对象是一个单元格组, 只有在满足所有单元格的 水平和竖直 对齐属性都相同的 - * 条件时才会返回其状态值,否则将返回null; 如果当前只检测了一个单元格, 则直接返回当前单元格的对齐状态; - * @param table cell or table cells , 支持单个单元格dom对象 或者 单元格dom对象数组 - * @return { align: 'left' || 'right' || 'center', valign: 'top' || 'middle' || 'bottom' } 或者 null - */ - UETable.getTableCellAlignState = function ( cells ) { - - !utils.isArray( cells ) && ( cells = [cells] ); - - var result = {}, - status = ['align', 'valign'], - tempStatus = null, - isSame = true;//状态是否相同 - - utils.each( cells, function( cellNode ){ - - utils.each( status, function( currentState ){ - - tempStatus = cellNode.getAttribute( currentState ); - - if( !result[ currentState ] && tempStatus ) { - result[ currentState ] = tempStatus; - } else if( !result[ currentState ] || ( tempStatus !== result[ currentState ] ) ) { - isSame = false; - return false; - } - - } ); - - return isSame; - - }); - - return isSame ? result : null; - - }; - - /** - * 根据当前选区获取相关的table信息 - * @return {Object} - */ - UETable.getTableItemsByRange = function (editor) { - var start = editor.selection.getStart(); - - //ff下会选中bookmark - if( start && start.id && start.id.indexOf('_baidu_bookmark_start_') === 0 && start.nextSibling) { - start = start.nextSibling; - } - - //在table或者td边缘有可能存在选中tr的情况 - var cell = start && domUtils.findParentByTagName(start, ["td", "th"], true), - tr = cell && cell.parentNode, - caption = start && domUtils.findParentByTagName(start, 'caption', true), - table = caption ? caption.parentNode : tr && tr.parentNode.parentNode; - - return { - cell:cell, - tr:tr, - table:table, - caption:caption - } - }; - UETable.getUETableBySelected = function (editor) { - var table = UETable.getTableItemsByRange(editor).table; - if (table && table.ueTable && table.ueTable.selectedTds.length) { - return table.ueTable; - } - return null; - }; - - UETable.getDefaultValue = function (editor, table) { - var borderMap = { - thin:'0px', - medium:'1px', - thick:'2px' - }, - tableBorder, tdPadding, tdBorder, tmpValue; - if (!table) { - table = editor.document.createElement('table'); - table.insertRow(0).insertCell(0).innerHTML = 'xxx'; - editor.body.appendChild(table); - var td = table.getElementsByTagName('td')[0]; - tmpValue = domUtils.getComputedStyle(table, 'border-left-width'); - tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10); - tmpValue = domUtils.getComputedStyle(td, 'padding-left'); - tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10); - tmpValue = domUtils.getComputedStyle(td, 'border-left-width'); - tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10); - domUtils.remove(table); - return { - tableBorder:tableBorder, - tdPadding:tdPadding, - tdBorder:tdBorder - }; - } else { - td = table.getElementsByTagName('td')[0]; - tmpValue = domUtils.getComputedStyle(table, 'border-left-width'); - tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10); - tmpValue = domUtils.getComputedStyle(td, 'padding-left'); - tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10); - tmpValue = domUtils.getComputedStyle(td, 'border-left-width'); - tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10); - return { - tableBorder:tableBorder, - tdPadding:tdPadding, - tdBorder:tdBorder - }; - } - }; - /** - * 根据当前点击的td或者table获取索引对象 - * @param tdOrTable - */ - UETable.getUETable = function (tdOrTable) { - var tag = tdOrTable.tagName.toLowerCase(); - tdOrTable = (tag == "td" || tag == "th" || tag == 'caption') ? domUtils.findParentByTagName(tdOrTable, "table", true) : tdOrTable; - if (!tdOrTable.ueTable) { - tdOrTable.ueTable = new UETable(tdOrTable); - } - return tdOrTable.ueTable; - }; - - UETable.cloneCell = function(cell,ignoreMerge,keepPro){ - if (!cell || utils.isString(cell)) { - return this.table.ownerDocument.createElement(cell || 'td'); - } - var flag = domUtils.hasClass(cell, "selectTdClass"); - flag && domUtils.removeClasses(cell, "selectTdClass"); - var tmpCell = cell.cloneNode(true); - if (ignoreMerge) { - tmpCell.rowSpan = tmpCell.colSpan = 1; - } - //去掉宽高 - !keepPro && domUtils.removeAttributes(tmpCell,'width height'); - !keepPro && domUtils.removeAttributes(tmpCell,'style'); - - tmpCell.style.borderLeftStyle = ""; - tmpCell.style.borderTopStyle = ""; - tmpCell.style.borderLeftColor = cell.style.borderRightColor; - tmpCell.style.borderLeftWidth = cell.style.borderRightWidth; - tmpCell.style.borderTopColor = cell.style.borderBottomColor; - tmpCell.style.borderTopWidth = cell.style.borderBottomWidth; - flag && domUtils.addClass(cell, "selectTdClass"); - return tmpCell; - } - - UETable.prototype = { - getMaxRows:function () { - var rows = this.table.rows, maxLen = 1; - for (var i = 0, row; row = rows[i]; i++) { - var currentMax = 1; - for (var j = 0, cj; cj = row.cells[j++];) { - currentMax = Math.max(cj.rowSpan || 1, currentMax); - } - maxLen = Math.max(currentMax + i, maxLen); - } - return maxLen; - }, - /** - * 获取当前表格的最大列数 - */ - getMaxCols:function () { - var rows = this.table.rows, maxLen = 0, cellRows = {}; - for (var i = 0, row; row = rows[i]; i++) { - var cellsNum = 0; - for (var j = 0, cj; cj = row.cells[j++];) { - cellsNum += (cj.colSpan || 1); - if (cj.rowSpan && cj.rowSpan > 1) { - for (var k = 1; k < cj.rowSpan; k++) { - if (!cellRows['row_' + (i + k)]) { - cellRows['row_' + (i + k)] = (cj.colSpan || 1); - } else { - cellRows['row_' + (i + k)]++ - } - } - - } - } - cellsNum += cellRows['row_' + i] || 0; - maxLen = Math.max(cellsNum, maxLen); - } - return maxLen; - }, - getCellColIndex:function (cell) { - - }, - /** - * 获取当前cell旁边的单元格, - * @param cell - * @param right - */ - getHSideCell:function (cell, right) { - try { - var cellInfo = this.getCellInfo(cell), - previewRowIndex, previewColIndex; - var len = this.selectedTds.length, - range = this.cellsRange; - //首行或者首列没有前置单元格 - if ((!right && (!len ? !cellInfo.colIndex : !range.beginColIndex)) || (right && (!len ? (cellInfo.colIndex == (this.colsNum - 1)) : (range.endColIndex == this.colsNum - 1)))) return null; - - previewRowIndex = !len ? cellInfo.rowIndex : range.beginRowIndex; - previewColIndex = !right ? ( !len ? (cellInfo.colIndex < 1 ? 0 : (cellInfo.colIndex - 1)) : range.beginColIndex - 1) - : ( !len ? cellInfo.colIndex + 1 : range.endColIndex + 1); - return this.getCell(this.indexTable[previewRowIndex][previewColIndex].rowIndex, this.indexTable[previewRowIndex][previewColIndex].cellIndex); - } catch (e) { - showError(e); - } - }, - getTabNextCell:function (cell, preRowIndex) { - var cellInfo = this.getCellInfo(cell), - rowIndex = preRowIndex || cellInfo.rowIndex, - colIndex = cellInfo.colIndex + 1 + (cellInfo.colSpan - 1), - nextCell; - try { - nextCell = this.getCell(this.indexTable[rowIndex][colIndex].rowIndex, this.indexTable[rowIndex][colIndex].cellIndex); - } catch (e) { - try { - rowIndex = rowIndex * 1 + 1; - colIndex = 0; - nextCell = this.getCell(this.indexTable[rowIndex][colIndex].rowIndex, this.indexTable[rowIndex][colIndex].cellIndex); - } catch (e) { - } - } - return nextCell; - - }, - /** - * 获取视觉上的后置单元格 - * @param cell - * @param bottom - */ - getVSideCell:function (cell, bottom, ignoreRange) { - try { - var cellInfo = this.getCellInfo(cell), - nextRowIndex, nextColIndex; - var len = this.selectedTds.length && !ignoreRange, - range = this.cellsRange; - //末行或者末列没有后置单元格 - if ((!bottom && (cellInfo.rowIndex == 0)) || (bottom && (!len ? (cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1) : (range.endRowIndex == this.rowsNum - 1)))) return null; - - nextRowIndex = !bottom ? ( !len ? cellInfo.rowIndex - 1 : range.beginRowIndex - 1) - : ( !len ? (cellInfo.rowIndex + cellInfo.rowSpan) : range.endRowIndex + 1); - nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex; - return this.getCell(this.indexTable[nextRowIndex][nextColIndex].rowIndex, this.indexTable[nextRowIndex][nextColIndex].cellIndex); - } catch (e) { - showError(e); - } - }, - /** - * 获取相同结束位置的单元格,xOrY指代了是获取x轴相同还是y轴相同 - */ - getSameEndPosCells:function (cell, xOrY) { - try { - var flag = (xOrY.toLowerCase() === "x"), - end = domUtils.getXY(cell)[flag ? 'x' : 'y'] + cell["offset" + (flag ? 'Width' : 'Height')], - rows = this.table.rows, - cells = null, returns = []; - for (var i = 0; i < this.rowsNum; i++) { - cells = rows[i].cells; - for (var j = 0, tmpCell; tmpCell = cells[j++];) { - var tmpEnd = domUtils.getXY(tmpCell)[flag ? 'x' : 'y'] + tmpCell["offset" + (flag ? 'Width' : 'Height')]; - //对应行的td已经被上面行rowSpan了 - if (tmpEnd > end && flag) break; - if (cell == tmpCell || end == tmpEnd) { - //只获取单一的单元格 - //todo 仅获取单一单元格在特定情况下会造成returns为空,从而影响后续的拖拽实现,修正这个。需考虑性能 - if (tmpCell[flag ? "colSpan" : "rowSpan"] == 1) { - returns.push(tmpCell); - } - if (flag) break; - } - } - } - return returns; - } catch (e) { - showError(e); - } - }, - setCellContent:function (cell, content) { - cell.innerHTML = content || (browser.ie ? domUtils.fillChar : "
                  "); - }, - cloneCell:UETable.cloneCell, - /** - * 获取跟当前单元格的右边竖线为左边的所有未合并单元格 - */ - getSameStartPosXCells:function (cell) { - try { - var start = domUtils.getXY(cell).x + cell.offsetWidth, - rows = this.table.rows, cells , returns = []; - for (var i = 0; i < this.rowsNum; i++) { - cells = rows[i].cells; - for (var j = 0, tmpCell; tmpCell = cells[j++];) { - var tmpStart = domUtils.getXY(tmpCell).x; - if (tmpStart > start) break; - if (tmpStart == start && tmpCell.colSpan == 1) { - returns.push(tmpCell); - break; - } - } - } - return returns; - } catch (e) { - showError(e); - } - }, - /** - * 更新table对应的索引表 - */ - update:function (table) { - this.table = table || this.table; - this.selectedTds = []; - this.cellsRange = {}; - this.indexTable = []; - var rows = this.table.rows, - rowsNum = this.getMaxRows(), - dNum = rowsNum - rows.length, - colsNum = this.getMaxCols(); - while (dNum--) { - this.table.insertRow(rows.length); - } - this.rowsNum = rowsNum; - this.colsNum = colsNum; - for (var i = 0, len = rows.length; i < len; i++) { - this.indexTable[i] = new Array(colsNum); - } - //填充索引表 - for (var rowIndex = 0, row; row = rows[rowIndex]; rowIndex++) { - for (var cellIndex = 0, cell, cells = row.cells; cell = cells[cellIndex]; cellIndex++) { - //修正整行被rowSpan时导致的行数计算错误 - if (cell.rowSpan > rowsNum) { - cell.rowSpan = rowsNum; - } - var colIndex = cellIndex, - rowSpan = cell.rowSpan || 1, - colSpan = cell.colSpan || 1; - //当已经被上一行rowSpan或者被前一列colSpan了,则跳到下一个单元格进行 - while (this.indexTable[rowIndex][colIndex]) colIndex++; - for (var j = 0; j < rowSpan; j++) { - for (var k = 0; k < colSpan; k++) { - this.indexTable[rowIndex + j][colIndex + k] = { - rowIndex:rowIndex, - cellIndex:cellIndex, - colIndex:colIndex, - rowSpan:rowSpan, - colSpan:colSpan - } - } - } - } - } - //修复残缺td - for (j = 0; j < rowsNum; j++) { - for (k = 0; k < colsNum; k++) { - if (this.indexTable[j][k] === undefined) { - row = rows[j]; - cell = row.cells[row.cells.length - 1]; - cell = cell ? cell.cloneNode(true) : this.table.ownerDocument.createElement("td"); - this.setCellContent(cell); - if (cell.colSpan !== 1)cell.colSpan = 1; - if (cell.rowSpan !== 1)cell.rowSpan = 1; - row.appendChild(cell); - this.indexTable[j][k] = { - rowIndex:j, - cellIndex:cell.cellIndex, - colIndex:k, - rowSpan:1, - colSpan:1 - } - } - } - } - //当框选后删除行或者列后撤销,需要重建选区。 - var tds = domUtils.getElementsByTagName(this.table, "td"), - selectTds = []; - utils.each(tds, function (td) { - if (domUtils.hasClass(td, "selectTdClass")) { - selectTds.push(td); - } - }); - if (selectTds.length) { - var start = selectTds[0], - end = selectTds[selectTds.length - 1], - startInfo = this.getCellInfo(start), - endInfo = this.getCellInfo(end); - this.selectedTds = selectTds; - this.cellsRange = { - beginRowIndex:startInfo.rowIndex, - beginColIndex:startInfo.colIndex, - endRowIndex:endInfo.rowIndex + endInfo.rowSpan - 1, - endColIndex:endInfo.colIndex + endInfo.colSpan - 1 - }; - } - //给第一行设置firstRow的样式名称,在排序图标的样式上使用到 - if(!domUtils.hasClass(this.table.rows[0], "firstRow")) { - domUtils.addClass(this.table.rows[0], "firstRow"); - for(var i = 1; i< this.table.rows.length; i++) { - domUtils.removeClasses(this.table.rows[i], "firstRow"); - } - } - }, - /** - * 获取单元格的索引信息 - */ - getCellInfo:function (cell) { - if (!cell) return; - var cellIndex = cell.cellIndex, - rowIndex = cell.parentNode.rowIndex, - rowInfo = this.indexTable[rowIndex], - numCols = this.colsNum; - for (var colIndex = cellIndex; colIndex < numCols; colIndex++) { - var cellInfo = rowInfo[colIndex]; - if (cellInfo.rowIndex === rowIndex && cellInfo.cellIndex === cellIndex) { - return cellInfo; - } - } - }, - /** - * 根据行列号获取单元格 - */ - getCell:function (rowIndex, cellIndex) { - return rowIndex < this.rowsNum && this.table.rows[rowIndex].cells[cellIndex] || null; - }, - /** - * 删除单元格 - */ - deleteCell:function (cell, rowIndex) { - rowIndex = typeof rowIndex == 'number' ? rowIndex : cell.parentNode.rowIndex; - var row = this.table.rows[rowIndex]; - row.deleteCell(cell.cellIndex); - }, - /** - * 根据始末两个单元格获取被框选的所有单元格范围 - */ - getCellsRange:function (cellA, cellB) { - function checkRange(beginRowIndex, beginColIndex, endRowIndex, endColIndex) { - var tmpBeginRowIndex = beginRowIndex, - tmpBeginColIndex = beginColIndex, - tmpEndRowIndex = endRowIndex, - tmpEndColIndex = endColIndex, - cellInfo, colIndex, rowIndex; - // 通过indexTable检查是否存在超出TableRange上边界的情况 - if (beginRowIndex > 0) { - for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) { - cellInfo = me.indexTable[beginRowIndex][colIndex]; - rowIndex = cellInfo.rowIndex; - if (rowIndex < beginRowIndex) { - tmpBeginRowIndex = Math.min(rowIndex, tmpBeginRowIndex); - } - } - } - // 通过indexTable检查是否存在超出TableRange右边界的情况 - if (endColIndex < me.colsNum) { - for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) { - cellInfo = me.indexTable[rowIndex][endColIndex]; - colIndex = cellInfo.colIndex + cellInfo.colSpan - 1; - if (colIndex > endColIndex) { - tmpEndColIndex = Math.max(colIndex, tmpEndColIndex); - } - } - } - // 检查是否有超出TableRange下边界的情况 - if (endRowIndex < me.rowsNum) { - for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) { - cellInfo = me.indexTable[endRowIndex][colIndex]; - rowIndex = cellInfo.rowIndex + cellInfo.rowSpan - 1; - if (rowIndex > endRowIndex) { - tmpEndRowIndex = Math.max(rowIndex, tmpEndRowIndex); - } - } - } - // 检查是否有超出TableRange左边界的情况 - if (beginColIndex > 0) { - for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) { - cellInfo = me.indexTable[rowIndex][beginColIndex]; - colIndex = cellInfo.colIndex; - if (colIndex < beginColIndex) { - tmpBeginColIndex = Math.min(cellInfo.colIndex, tmpBeginColIndex); - } - } - } - //递归调用直至所有完成所有框选单元格的扩展 - if (tmpBeginRowIndex != beginRowIndex || tmpBeginColIndex != beginColIndex || tmpEndRowIndex != endRowIndex || tmpEndColIndex != endColIndex) { - return checkRange(tmpBeginRowIndex, tmpBeginColIndex, tmpEndRowIndex, tmpEndColIndex); - } else { - // 不需要扩展TableRange的情况 - return { - beginRowIndex:beginRowIndex, - beginColIndex:beginColIndex, - endRowIndex:endRowIndex, - endColIndex:endColIndex - }; - } - } - - try { - var me = this, - cellAInfo = me.getCellInfo(cellA); - if (cellA === cellB) { - return { - beginRowIndex:cellAInfo.rowIndex, - beginColIndex:cellAInfo.colIndex, - endRowIndex:cellAInfo.rowIndex + cellAInfo.rowSpan - 1, - endColIndex:cellAInfo.colIndex + cellAInfo.colSpan - 1 - }; - } - var cellBInfo = me.getCellInfo(cellB); - // 计算TableRange的四个边 - var beginRowIndex = Math.min(cellAInfo.rowIndex, cellBInfo.rowIndex), - beginColIndex = Math.min(cellAInfo.colIndex, cellBInfo.colIndex), - endRowIndex = Math.max(cellAInfo.rowIndex + cellAInfo.rowSpan - 1, cellBInfo.rowIndex + cellBInfo.rowSpan - 1), - endColIndex = Math.max(cellAInfo.colIndex + cellAInfo.colSpan - 1, cellBInfo.colIndex + cellBInfo.colSpan - 1); - - return checkRange(beginRowIndex, beginColIndex, endRowIndex, endColIndex); - } catch (e) { - //throw e; - } - }, - /** - * 依据cellsRange获取对应的单元格集合 - */ - getCells:function (range) { - //每次获取cells之前必须先清除上次的选择,否则会对后续获取操作造成影响 - this.clearSelected(); - var beginRowIndex = range.beginRowIndex, - beginColIndex = range.beginColIndex, - endRowIndex = range.endRowIndex, - endColIndex = range.endColIndex, - cellInfo, rowIndex, colIndex, tdHash = {}, returnTds = []; - for (var i = beginRowIndex; i <= endRowIndex; i++) { - for (var j = beginColIndex; j <= endColIndex; j++) { - cellInfo = this.indexTable[i][j]; - rowIndex = cellInfo.rowIndex; - colIndex = cellInfo.colIndex; - // 如果Cells里已经包含了此Cell则跳过 - var key = rowIndex + '|' + colIndex; - if (tdHash[key]) continue; - tdHash[key] = 1; - if (rowIndex < i || colIndex < j || rowIndex + cellInfo.rowSpan - 1 > endRowIndex || colIndex + cellInfo.colSpan - 1 > endColIndex) { - return null; - } - returnTds.push(this.getCell(rowIndex, cellInfo.cellIndex)); - } - } - return returnTds; - }, - /** - * 清理已经选中的单元格 - */ - clearSelected:function () { - UETable.removeSelectedClass(this.selectedTds); - this.selectedTds = []; - this.cellsRange = {}; - }, - /** - * 根据range设置已经选中的单元格 - */ - setSelected:function (range) { - var cells = this.getCells(range); - UETable.addSelectedClass(cells); - this.selectedTds = cells; - this.cellsRange = range; - }, - isFullRow:function () { - var range = this.cellsRange; - return (range.endColIndex - range.beginColIndex + 1) == this.colsNum; - }, - isFullCol:function () { - var range = this.cellsRange, - table = this.table, - ths = table.getElementsByTagName("th"), - rows = range.endRowIndex - range.beginRowIndex + 1; - return !ths.length ? rows == this.rowsNum : rows == this.rowsNum || (rows == this.rowsNum - 1); - - }, - /** - * 获取视觉上的前置单元格,默认是左边,top传入时 - * @param cell - * @param top - */ - getNextCell:function (cell, bottom, ignoreRange) { - try { - var cellInfo = this.getCellInfo(cell), - nextRowIndex, nextColIndex; - var len = this.selectedTds.length && !ignoreRange, - range = this.cellsRange; - //末行或者末列没有后置单元格 - if ((!bottom && (cellInfo.rowIndex == 0)) || (bottom && (!len ? (cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1) : (range.endRowIndex == this.rowsNum - 1)))) return null; - - nextRowIndex = !bottom ? ( !len ? cellInfo.rowIndex - 1 : range.beginRowIndex - 1) - : ( !len ? (cellInfo.rowIndex + cellInfo.rowSpan) : range.endRowIndex + 1); - nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex; - return this.getCell(this.indexTable[nextRowIndex][nextColIndex].rowIndex, this.indexTable[nextRowIndex][nextColIndex].cellIndex); - } catch (e) { - showError(e); - } - }, - getPreviewCell:function (cell, top) { - try { - var cellInfo = this.getCellInfo(cell), - previewRowIndex, previewColIndex; - var len = this.selectedTds.length, - range = this.cellsRange; - //首行或者首列没有前置单元格 - if ((!top && (!len ? !cellInfo.colIndex : !range.beginColIndex)) || (top && (!len ? (cellInfo.rowIndex > (this.colsNum - 1)) : (range.endColIndex == this.colsNum - 1)))) return null; - - previewRowIndex = !top ? ( !len ? cellInfo.rowIndex : range.beginRowIndex ) - : ( !len ? (cellInfo.rowIndex < 1 ? 0 : (cellInfo.rowIndex - 1)) : range.beginRowIndex); - previewColIndex = !top ? ( !len ? (cellInfo.colIndex < 1 ? 0 : (cellInfo.colIndex - 1)) : range.beginColIndex - 1) - : ( !len ? cellInfo.colIndex : range.endColIndex + 1); - return this.getCell(this.indexTable[previewRowIndex][previewColIndex].rowIndex, this.indexTable[previewRowIndex][previewColIndex].cellIndex); - } catch (e) { - showError(e); - } - }, - /** - * 移动单元格中的内容 - */ - moveContent:function (cellTo, cellFrom) { - if (UETable.isEmptyBlock(cellFrom)) return; - if (UETable.isEmptyBlock(cellTo)) { - cellTo.innerHTML = cellFrom.innerHTML; - return; - } - var child = cellTo.lastChild; - if (child.nodeType == 3 || !dtd.$block[child.tagName]) { - cellTo.appendChild(cellTo.ownerDocument.createElement('br')) - } - while (child = cellFrom.firstChild) { - cellTo.appendChild(child); - } - }, - /** - * 向右合并单元格 - */ - mergeRight:function (cell) { - var cellInfo = this.getCellInfo(cell), - rightColIndex = cellInfo.colIndex + cellInfo.colSpan, - rightCellInfo = this.indexTable[cellInfo.rowIndex][rightColIndex], - rightCell = this.getCell(rightCellInfo.rowIndex, rightCellInfo.cellIndex); - //合并 - cell.colSpan = cellInfo.colSpan + rightCellInfo.colSpan; - //被合并的单元格不应存在宽度属性 - cell.removeAttribute("width"); - //移动内容 - this.moveContent(cell, rightCell); - //删掉被合并的Cell - this.deleteCell(rightCell, rightCellInfo.rowIndex); - this.update(); - }, - /** - * 向下合并单元格 - */ - mergeDown:function (cell) { - var cellInfo = this.getCellInfo(cell), - downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan, - downCellInfo = this.indexTable[downRowIndex][cellInfo.colIndex], - downCell = this.getCell(downCellInfo.rowIndex, downCellInfo.cellIndex); - cell.rowSpan = cellInfo.rowSpan + downCellInfo.rowSpan; - cell.removeAttribute("height"); - this.moveContent(cell, downCell); - this.deleteCell(downCell, downCellInfo.rowIndex); - this.update(); - }, - /** - * 合并整个range中的内容 - */ - mergeRange:function () { - //由于合并操作可以在任意时刻进行,所以无法通过鼠标位置等信息实时生成range,只能通过缓存实例中的cellsRange对象来访问 - var range = this.cellsRange, - leftTopCell = this.getCell(range.beginRowIndex, this.indexTable[range.beginRowIndex][range.beginColIndex].cellIndex); - - if (leftTopCell.tagName == "TH" && range.endRowIndex !== range.beginRowIndex) { - var index = this.indexTable, - info = this.getCellInfo(leftTopCell); - leftTopCell = this.getCell(1, index[1][info.colIndex].cellIndex); - range = this.getCellsRange(leftTopCell, this.getCell(index[this.rowsNum - 1][info.colIndex].rowIndex, index[this.rowsNum - 1][info.colIndex].cellIndex)); - } - - // 删除剩余的Cells - var cells = this.getCells(range); - for(var i= 0,ci;ci=cells[i++];){ - if (ci !== leftTopCell) { - this.moveContent(leftTopCell, ci); - this.deleteCell(ci); - } - } - // 修改左上角Cell的rowSpan和colSpan,并调整宽度属性设置 - leftTopCell.rowSpan = range.endRowIndex - range.beginRowIndex + 1; - leftTopCell.rowSpan > 1 && leftTopCell.removeAttribute("height"); - leftTopCell.colSpan = range.endColIndex - range.beginColIndex + 1; - leftTopCell.colSpan > 1 && leftTopCell.removeAttribute("width"); - if (leftTopCell.rowSpan == this.rowsNum && leftTopCell.colSpan != 1) { - leftTopCell.colSpan = 1; - } - - if (leftTopCell.colSpan == this.colsNum && leftTopCell.rowSpan != 1) { - var rowIndex = leftTopCell.parentNode.rowIndex; - //解决IE下的表格操作问题 - if( this.table.deleteRow ) { - for (var i = rowIndex+ 1, curIndex=rowIndex+ 1, len=leftTopCell.rowSpan; i < len; i++) { - this.table.deleteRow(curIndex); - } - } else { - for (var i = 0, len=leftTopCell.rowSpan - 1; i < len; i++) { - var row = this.table.rows[rowIndex + 1]; - row.parentNode.removeChild(row); - } - } - leftTopCell.rowSpan = 1; - } - this.update(); - }, - /** - * 插入一行单元格 - */ - insertRow:function (rowIndex, sourceCell) { - var numCols = this.colsNum, - table = this.table, - row = table.insertRow(rowIndex), cell, - isInsertTitle = typeof sourceCell == 'string' && sourceCell.toUpperCase() == 'TH'; - - function replaceTdToTh(colIndex, cell, tableRow) { - if (colIndex == 0) { - var tr = tableRow.nextSibling || tableRow.previousSibling, - th = tr.cells[colIndex]; - if (th.tagName == 'TH') { - th = cell.ownerDocument.createElement("th"); - th.appendChild(cell.firstChild); - tableRow.insertBefore(th, cell); - domUtils.remove(cell) - } - }else{ - if (cell.tagName == 'TH') { - var td = cell.ownerDocument.createElement("td"); - td.appendChild(cell.firstChild); - tableRow.insertBefore(td, cell); - domUtils.remove(cell) - } - } - } - - //首行直接插入,无需考虑部分单元格被rowspan的情况 - if (rowIndex == 0 || rowIndex == this.rowsNum) { - for (var colIndex = 0; colIndex < numCols; colIndex++) { - cell = this.cloneCell(sourceCell, true); - this.setCellContent(cell); - cell.getAttribute('vAlign') && cell.setAttribute('vAlign', cell.getAttribute('vAlign')); - row.appendChild(cell); - if(!isInsertTitle) replaceTdToTh(colIndex, cell, row); - } - } else { - var infoRow = this.indexTable[rowIndex], - cellIndex = 0; - for (colIndex = 0; colIndex < numCols; colIndex++) { - var cellInfo = infoRow[colIndex]; - //如果存在某个单元格的rowspan穿过待插入行的位置,则修改该单元格的rowspan即可,无需插入单元格 - if (cellInfo.rowIndex < rowIndex) { - cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex); - cell.rowSpan = cellInfo.rowSpan + 1; - } else { - cell = this.cloneCell(sourceCell, true); - this.setCellContent(cell); - row.appendChild(cell); - } - if(!isInsertTitle) replaceTdToTh(colIndex, cell, row); - } - } - //框选时插入不触发contentchange,需要手动更新索引。 - this.update(); - return row; - }, - /** - * 删除一行单元格 - * @param rowIndex - */ - deleteRow:function (rowIndex) { - var row = this.table.rows[rowIndex], - infoRow = this.indexTable[rowIndex], - colsNum = this.colsNum, - count = 0; //处理计数 - for (var colIndex = 0; colIndex < colsNum;) { - var cellInfo = infoRow[colIndex], - cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex); - if (cell.rowSpan > 1) { - if (cellInfo.rowIndex == rowIndex) { - var clone = cell.cloneNode(true); - clone.rowSpan = cell.rowSpan - 1; - clone.innerHTML = ""; - cell.rowSpan = 1; - var nextRowIndex = rowIndex + 1, - nextRow = this.table.rows[nextRowIndex], - insertCellIndex, - preMerged = this.getPreviewMergedCellsNum(nextRowIndex, colIndex) - count; - if (preMerged < colIndex) { - insertCellIndex = colIndex - preMerged - 1; - //nextRow.insertCell(insertCellIndex); - domUtils.insertAfter(nextRow.cells[insertCellIndex], clone); - } else { - if (nextRow.cells.length) nextRow.insertBefore(clone, nextRow.cells[0]) - } - count += 1; - //cell.parentNode.removeChild(cell); - } - } - colIndex += cell.colSpan || 1; - } - var deleteTds = [], cacheMap = {}; - for (colIndex = 0; colIndex < colsNum; colIndex++) { - var tmpRowIndex = infoRow[colIndex].rowIndex, - tmpCellIndex = infoRow[colIndex].cellIndex, - key = tmpRowIndex + "_" + tmpCellIndex; - if (cacheMap[key])continue; - cacheMap[key] = 1; - cell = this.getCell(tmpRowIndex, tmpCellIndex); - deleteTds.push(cell); - } - var mergeTds = []; - utils.each(deleteTds, function (td) { - if (td.rowSpan == 1) { - td.parentNode.removeChild(td); - } else { - mergeTds.push(td); - } - }); - utils.each(mergeTds, function (td) { - td.rowSpan--; - }); - row.parentNode.removeChild(row); - //浏览器方法本身存在bug,采用自定义方法删除 - //this.table.deleteRow(rowIndex); - this.update(); - }, - insertCol:function (colIndex, sourceCell, defaultValue) { - var rowsNum = this.rowsNum, - rowIndex = 0, - tableRow, cell, - backWidth = parseInt((this.table.offsetWidth - (this.colsNum + 1) * 20 - (this.colsNum + 1)) / (this.colsNum + 1), 10), - isInsertTitleCol = typeof sourceCell == 'string' && sourceCell.toUpperCase() == 'TH'; - - function replaceTdToTh(rowIndex, cell, tableRow) { - if (rowIndex == 0) { - var th = cell.nextSibling || cell.previousSibling; - if (th.tagName == 'TH') { - th = cell.ownerDocument.createElement("th"); - th.appendChild(cell.firstChild); - tableRow.insertBefore(th, cell); - domUtils.remove(cell) - } - }else{ - if (cell.tagName == 'TH') { - var td = cell.ownerDocument.createElement("td"); - td.appendChild(cell.firstChild); - tableRow.insertBefore(td, cell); - domUtils.remove(cell) - } - } - } - - var preCell; - if (colIndex == 0 || colIndex == this.colsNum) { - for (; rowIndex < rowsNum; rowIndex++) { - tableRow = this.table.rows[rowIndex]; - preCell = tableRow.cells[colIndex == 0 ? colIndex : tableRow.cells.length]; - cell = this.cloneCell(sourceCell, true); //tableRow.insertCell(colIndex == 0 ? colIndex : tableRow.cells.length); - this.setCellContent(cell); - cell.setAttribute('vAlign', cell.getAttribute('vAlign')); - preCell && cell.setAttribute('width', preCell.getAttribute('width')); - if (!colIndex) { - tableRow.insertBefore(cell, tableRow.cells[0]); - } else { - domUtils.insertAfter(tableRow.cells[tableRow.cells.length - 1], cell); - } - if(!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow) - } - } else { - for (; rowIndex < rowsNum; rowIndex++) { - var cellInfo = this.indexTable[rowIndex][colIndex]; - if (cellInfo.colIndex < colIndex) { - cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex); - cell.colSpan = cellInfo.colSpan + 1; - } else { - tableRow = this.table.rows[rowIndex]; - preCell = tableRow.cells[cellInfo.cellIndex]; - - cell = this.cloneCell(sourceCell, true);//tableRow.insertCell(cellInfo.cellIndex); - this.setCellContent(cell); - cell.setAttribute('vAlign', cell.getAttribute('vAlign')); - preCell && cell.setAttribute('width', preCell.getAttribute('width')); - //防止IE下报错 - preCell ? tableRow.insertBefore(cell, preCell) : tableRow.appendChild(cell); - } - if(!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow); - } - } - //框选时插入不触发contentchange,需要手动更新索引 - this.update(); - this.updateWidth(backWidth, defaultValue || {tdPadding:10, tdBorder:1}); - }, - updateWidth:function (width, defaultValue) { - var table = this.table, - tmpWidth = UETable.getWidth(table) - defaultValue.tdPadding * 2 - defaultValue.tdBorder + width; - if (tmpWidth < table.ownerDocument.body.offsetWidth) { - table.setAttribute("width", tmpWidth); - return; - } - var tds = domUtils.getElementsByTagName(this.table, "td th"); - utils.each(tds, function (td) { - td.setAttribute("width", width); - }) - }, - deleteCol:function (colIndex) { - var indexTable = this.indexTable, - tableRows = this.table.rows, - backTableWidth = this.table.getAttribute("width"), - backTdWidth = 0, - rowsNum = this.rowsNum, - cacheMap = {}; - for (var rowIndex = 0; rowIndex < rowsNum;) { - var infoRow = indexTable[rowIndex], - cellInfo = infoRow[colIndex], - key = cellInfo.rowIndex + '_' + cellInfo.colIndex; - // 跳过已经处理过的Cell - if (cacheMap[key])continue; - cacheMap[key] = 1; - var cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex); - if (!backTdWidth) backTdWidth = cell && parseInt(cell.offsetWidth / cell.colSpan, 10).toFixed(0); - // 如果Cell的colSpan大于1, 就修改colSpan, 否则就删掉这个Cell - if (cell.colSpan > 1) { - cell.colSpan--; - } else { - tableRows[rowIndex].deleteCell(cellInfo.cellIndex); - } - rowIndex += cellInfo.rowSpan || 1; - } - this.table.setAttribute("width", backTableWidth - backTdWidth); - this.update(); - }, - splitToCells:function (cell) { - var me = this, - cells = this.splitToRows(cell); - utils.each(cells, function (cell) { - me.splitToCols(cell); - }) - }, - splitToRows:function (cell) { - var cellInfo = this.getCellInfo(cell), - rowIndex = cellInfo.rowIndex, - colIndex = cellInfo.colIndex, - results = []; - // 修改Cell的rowSpan - cell.rowSpan = 1; - results.push(cell); - // 补齐单元格 - for (var i = rowIndex, endRow = rowIndex + cellInfo.rowSpan; i < endRow; i++) { - if (i == rowIndex)continue; - var tableRow = this.table.rows[i], - tmpCell = tableRow.insertCell(colIndex - this.getPreviewMergedCellsNum(i, colIndex)); - tmpCell.colSpan = cellInfo.colSpan; - this.setCellContent(tmpCell); - tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign')); - tmpCell.setAttribute('align', cell.getAttribute('align')); - if (cell.style.cssText) { - tmpCell.style.cssText = cell.style.cssText; - } - results.push(tmpCell); - } - this.update(); - return results; - }, - getPreviewMergedCellsNum:function (rowIndex, colIndex) { - var indexRow = this.indexTable[rowIndex], - num = 0; - for (var i = 0; i < colIndex;) { - var colSpan = indexRow[i].colSpan, - tmpRowIndex = indexRow[i].rowIndex; - num += (colSpan - (tmpRowIndex == rowIndex ? 1 : 0)); - i += colSpan; - } - return num; - }, - splitToCols:function (cell) { - var backWidth = (cell.offsetWidth / cell.colSpan - 22).toFixed(0), - - cellInfo = this.getCellInfo(cell), - rowIndex = cellInfo.rowIndex, - colIndex = cellInfo.colIndex, - results = []; - // 修改Cell的rowSpan - cell.colSpan = 1; - cell.setAttribute("width", backWidth); - results.push(cell); - // 补齐单元格 - for (var j = colIndex, endCol = colIndex + cellInfo.colSpan; j < endCol; j++) { - if (j == colIndex)continue; - var tableRow = this.table.rows[rowIndex], - tmpCell = tableRow.insertCell(this.indexTable[rowIndex][j].cellIndex + 1); - tmpCell.rowSpan = cellInfo.rowSpan; - this.setCellContent(tmpCell); - tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign')); - tmpCell.setAttribute('align', cell.getAttribute('align')); - tmpCell.setAttribute('width', backWidth); - if (cell.style.cssText) { - tmpCell.style.cssText = cell.style.cssText; - } - //处理th的情况 - if (cell.tagName == 'TH') { - var th = cell.ownerDocument.createElement('th'); - th.appendChild(tmpCell.firstChild); - th.setAttribute('vAlign', cell.getAttribute('vAlign')); - th.rowSpan = tmpCell.rowSpan; - tableRow.insertBefore(th, tmpCell); - domUtils.remove(tmpCell); - } - results.push(tmpCell); - } - this.update(); - return results; - }, - isLastCell:function (cell, rowsNum, colsNum) { - rowsNum = rowsNum || this.rowsNum; - colsNum = colsNum || this.colsNum; - var cellInfo = this.getCellInfo(cell); - return ((cellInfo.rowIndex + cellInfo.rowSpan) == rowsNum) && - ((cellInfo.colIndex + cellInfo.colSpan) == colsNum); - }, - getLastCell:function (cells) { - cells = cells || this.table.getElementsByTagName("td"); - var firstInfo = this.getCellInfo(cells[0]); - var me = this, last = cells[0], - tr = last.parentNode, - cellsNum = 0, cols = 0, rows; - utils.each(cells, function (cell) { - if (cell.parentNode == tr)cols += cell.colSpan || 1; - cellsNum += cell.rowSpan * cell.colSpan || 1; - }); - rows = cellsNum / cols; - utils.each(cells, function (cell) { - if (me.isLastCell(cell, rows, cols)) { - last = cell; - return false; - } - }); - return last; - - }, - selectRow:function (rowIndex) { - var indexRow = this.indexTable[rowIndex], - start = this.getCell(indexRow[0].rowIndex, indexRow[0].cellIndex), - end = this.getCell(indexRow[this.colsNum - 1].rowIndex, indexRow[this.colsNum - 1].cellIndex), - range = this.getCellsRange(start, end); - this.setSelected(range); - }, - selectTable:function () { - var tds = this.table.getElementsByTagName("td"), - range = this.getCellsRange(tds[0], tds[tds.length - 1]); - this.setSelected(range); - }, - setBackground:function (cells, value) { - if (typeof value === "string") { - utils.each(cells, function (cell) { - cell.style.backgroundColor = value; - }) - } else if (typeof value === "object") { - value = utils.extend({ - repeat:true, - colorList:["#ddd", "#fff"] - }, value); - var rowIndex = this.getCellInfo(cells[0]).rowIndex, - count = 0, - colors = value.colorList, - getColor = function (list, index, repeat) { - return list[index] ? list[index] : repeat ? list[index % list.length] : ""; - }; - for (var i = 0, cell; cell = cells[i++];) { - var cellInfo = this.getCellInfo(cell); - cell.style.backgroundColor = getColor(colors, ((rowIndex + count) == cellInfo.rowIndex) ? count : ++count, value.repeat); - } - } - }, - removeBackground:function (cells) { - utils.each(cells, function (cell) { - cell.style.backgroundColor = ""; - }) - } - - - }; - function showError(e) { - } -})(); - -// plugins/table.cmds.js -/** - * Created with JetBrains PhpStorm. - * User: taoqili - * Date: 13-2-20 - * Time: 下午6:25 - * To change this template use File | Settings | File Templates. - */ -; -(function () { - var UT = UE.UETable, - getTableItemsByRange = function (editor) { - return UT.getTableItemsByRange(editor); - }, - getUETableBySelected = function (editor) { - return UT.getUETableBySelected(editor) - }, - getDefaultValue = function (editor, table) { - return UT.getDefaultValue(editor, table); - }, - getUETable = function (tdOrTable) { - return UT.getUETable(tdOrTable); - }; - - - UE.commands['inserttable'] = { - queryCommandState: function () { - return getTableItemsByRange(this).table ? -1 : 0; - }, - execCommand: function (cmd, opt) { - function createTable(opt, tdWidth) { - var html = [], - rowsNum = opt.numRows, - colsNum = opt.numCols; - for (var r = 0; r < rowsNum; r++) { - html.push(''); - for (var c = 0; c < colsNum; c++) { - html.push('
                • ' + (browser.ie && browser.version < 11 ? domUtils.fillChar : '
                  ') + '
                  ' + html.join('') + '
                  ' - } - - if (!opt) { - opt = utils.extend({}, { - numCols: this.options.defaultCols, - numRows: this.options.defaultRows, - tdvalign: this.options.tdvalign - }) - } - var me = this; - var range = this.selection.getRange(), - start = range.startContainer, - firstParentBlock = domUtils.findParent(start, function (node) { - return domUtils.isBlockElm(node); - }, true) || me.body; - - var defaultValue = getDefaultValue(me), - tableWidth = firstParentBlock.offsetWidth, - tdWidth = Math.floor(tableWidth / opt.numCols - defaultValue.tdPadding * 2 - defaultValue.tdBorder); - - //todo其他属性 - !opt.tdvalign && (opt.tdvalign = me.options.tdvalign); - me.execCommand("inserthtml", createTable(opt, tdWidth)); - } - }; - - UE.commands['insertparagraphbeforetable'] = { - queryCommandState: function () { - return getTableItemsByRange(this).cell ? 0 : -1; - }, - execCommand: function () { - var table = getTableItemsByRange(this).table; - if (table) { - var p = this.document.createElement("p"); - p.innerHTML = browser.ie ? ' ' : '
                  '; - table.parentNode.insertBefore(p, table); - this.selection.getRange().setStart(p, 0).setCursor(); - } - } - }; - - UE.commands['deletetable'] = { - queryCommandState: function () { - var rng = this.selection.getRange(); - return domUtils.findParentByTagName(rng.startContainer, 'table', true) ? 0 : -1; - }, - execCommand: function (cmd, table) { - var rng = this.selection.getRange(); - table = table || domUtils.findParentByTagName(rng.startContainer, 'table', true); - if (table) { - var next = table.nextSibling; - if (!next) { - next = domUtils.createElement(this.document, 'p', { - 'innerHTML': browser.ie ? domUtils.fillChar : '
                  ' - }); - table.parentNode.insertBefore(next, table); - } - domUtils.remove(table); - rng = this.selection.getRange(); - if (next.nodeType == 3) { - rng.setStartBefore(next) - } else { - rng.setStart(next, 0) - } - rng.setCursor(false, true) - this.fireEvent("tablehasdeleted") - - } - - } - }; - UE.commands['cellalign'] = { - queryCommandState: function () { - return getSelectedArr(this).length ? 0 : -1 - }, - execCommand: function (cmd, align) { - var selectedTds = getSelectedArr(this); - if (selectedTds.length) { - for (var i = 0, ci; ci = selectedTds[i++];) { - ci.setAttribute('align', align); - } - } - } - }; - UE.commands['cellvalign'] = { - queryCommandState: function () { - return getSelectedArr(this).length ? 0 : -1; - }, - execCommand: function (cmd, valign) { - var selectedTds = getSelectedArr(this); - if (selectedTds.length) { - for (var i = 0, ci; ci = selectedTds[i++];) { - ci.setAttribute('vAlign', valign); - } - } - } - }; - UE.commands['insertcaption'] = { - queryCommandState: function () { - var table = getTableItemsByRange(this).table; - if (table) { - return table.getElementsByTagName('caption').length == 0 ? 1 : -1; - } - return -1; - }, - execCommand: function () { - var table = getTableItemsByRange(this).table; - if (table) { - var caption = this.document.createElement('caption'); - caption.innerHTML = browser.ie ? domUtils.fillChar : '
                  '; - table.insertBefore(caption, table.firstChild); - var range = this.selection.getRange(); - range.setStart(caption, 0).setCursor(); - } - - } - }; - UE.commands['deletecaption'] = { - queryCommandState: function () { - var rng = this.selection.getRange(), - table = domUtils.findParentByTagName(rng.startContainer, 'table'); - if (table) { - return table.getElementsByTagName('caption').length == 0 ? -1 : 1; - } - return -1; - }, - execCommand: function () { - var rng = this.selection.getRange(), - table = domUtils.findParentByTagName(rng.startContainer, 'table'); - if (table) { - domUtils.remove(table.getElementsByTagName('caption')[0]); - var range = this.selection.getRange(); - range.setStart(table.rows[0].cells[0], 0).setCursor(); - } - - } - }; - UE.commands['inserttitle'] = { - queryCommandState: function () { - var table = getTableItemsByRange(this).table; - if (table) { - var firstRow = table.rows[0]; - return firstRow.cells[firstRow.cells.length-1].tagName.toLowerCase() != 'th' ? 0 : -1 - } - return -1; - }, - execCommand: function () { - var table = getTableItemsByRange(this).table; - if (table) { - getUETable(table).insertRow(0, 'th'); - } - var th = table.getElementsByTagName('th')[0]; - this.selection.getRange().setStart(th, 0).setCursor(false, true); - } - }; - UE.commands['deletetitle'] = { - queryCommandState: function () { - var table = getTableItemsByRange(this).table; - if (table) { - var firstRow = table.rows[0]; - return firstRow.cells[firstRow.cells.length-1].tagName.toLowerCase() == 'th' ? 0 : -1 - } - return -1; - }, - execCommand: function () { - var table = getTableItemsByRange(this).table; - if (table) { - domUtils.remove(table.rows[0]) - } - var td = table.getElementsByTagName('td')[0]; - this.selection.getRange().setStart(td, 0).setCursor(false, true); - } - }; - UE.commands['inserttitlecol'] = { - queryCommandState: function () { - var table = getTableItemsByRange(this).table; - if (table) { - var lastRow = table.rows[table.rows.length-1]; - return lastRow.getElementsByTagName('th').length ? -1 : 0; - } - return -1; - }, - execCommand: function (cmd) { - var table = getTableItemsByRange(this).table; - if (table) { - getUETable(table).insertCol(0, 'th'); - } - resetTdWidth(table, this); - var th = table.getElementsByTagName('th')[0]; - this.selection.getRange().setStart(th, 0).setCursor(false, true); - } - }; - UE.commands['deletetitlecol'] = { - queryCommandState: function () { - var table = getTableItemsByRange(this).table; - if (table) { - var lastRow = table.rows[table.rows.length-1]; - return lastRow.getElementsByTagName('th').length ? 0 : -1; - } - return -1; - }, - execCommand: function () { - var table = getTableItemsByRange(this).table; - if (table) { - for(var i = 0; i< table.rows.length; i++ ){ - domUtils.remove(table.rows[i].children[0]) - } - } - resetTdWidth(table, this); - var td = table.getElementsByTagName('td')[0]; - this.selection.getRange().setStart(td, 0).setCursor(false, true); - } - }; - - UE.commands["mergeright"] = { - queryCommandState: function (cmd) { - var tableItems = getTableItemsByRange(this), - table = tableItems.table, - cell = tableItems.cell; - - if (!table || !cell) return -1; - var ut = getUETable(table); - if (ut.selectedTds.length) return -1; - - var cellInfo = ut.getCellInfo(cell), - rightColIndex = cellInfo.colIndex + cellInfo.colSpan; - if (rightColIndex >= ut.colsNum) return -1; // 如果处于最右边则不能向右合并 - - var rightCellInfo = ut.indexTable[cellInfo.rowIndex][rightColIndex], - rightCell = table.rows[rightCellInfo.rowIndex].cells[rightCellInfo.cellIndex]; - if (!rightCell || cell.tagName != rightCell.tagName) return -1; // TH和TD不能相互合并 - - // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并 - return (rightCellInfo.rowIndex == cellInfo.rowIndex && rightCellInfo.rowSpan == cellInfo.rowSpan) ? 0 : -1; - }, - execCommand: function (cmd) { - var rng = this.selection.getRange(), - bk = rng.createBookmark(true); - var cell = getTableItemsByRange(this).cell, - ut = getUETable(cell); - ut.mergeRight(cell); - rng.moveToBookmark(bk).select(); - } - }; - UE.commands["mergedown"] = { - queryCommandState: function (cmd) { - var tableItems = getTableItemsByRange(this), - table = tableItems.table, - cell = tableItems.cell; - - if (!table || !cell) return -1; - var ut = getUETable(table); - if (ut.selectedTds.length)return -1; - - var cellInfo = ut.getCellInfo(cell), - downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan; - if (downRowIndex >= ut.rowsNum) return -1; // 如果处于最下边则不能向下合并 - - var downCellInfo = ut.indexTable[downRowIndex][cellInfo.colIndex], - downCell = table.rows[downCellInfo.rowIndex].cells[downCellInfo.cellIndex]; - if (!downCell || cell.tagName != downCell.tagName) return -1; // TH和TD不能相互合并 - - // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并 - return (downCellInfo.colIndex == cellInfo.colIndex && downCellInfo.colSpan == cellInfo.colSpan) ? 0 : -1; - }, - execCommand: function () { - var rng = this.selection.getRange(), - bk = rng.createBookmark(true); - var cell = getTableItemsByRange(this).cell, - ut = getUETable(cell); - ut.mergeDown(cell); - rng.moveToBookmark(bk).select(); - } - }; - UE.commands["mergecells"] = { - queryCommandState: function () { - return getUETableBySelected(this) ? 0 : -1; - }, - execCommand: function () { - var ut = getUETableBySelected(this); - if (ut && ut.selectedTds.length) { - var cell = ut.selectedTds[0]; - ut.mergeRange(); - var rng = this.selection.getRange(); - if (domUtils.isEmptyBlock(cell)) { - rng.setStart(cell, 0).collapse(true) - } else { - rng.selectNodeContents(cell) - } - rng.select(); - } - - - } - }; - UE.commands["insertrow"] = { - queryCommandState: function () { - var tableItems = getTableItemsByRange(this), - cell = tableItems.cell; - return cell && (cell.tagName == "TD" || (cell.tagName == 'TH' && tableItems.tr !== tableItems.table.rows[0])) && - getUETable(tableItems.table).rowsNum < this.options.maxRowNum ? 0 : -1; - }, - execCommand: function () { - var rng = this.selection.getRange(), - bk = rng.createBookmark(true); - var tableItems = getTableItemsByRange(this), - cell = tableItems.cell, - table = tableItems.table, - ut = getUETable(table), - cellInfo = ut.getCellInfo(cell); - //ut.insertRow(!ut.selectedTds.length ? cellInfo.rowIndex:ut.cellsRange.beginRowIndex,''); - if (!ut.selectedTds.length) { - ut.insertRow(cellInfo.rowIndex, cell); - } else { - var range = ut.cellsRange; - for (var i = 0, len = range.endRowIndex - range.beginRowIndex + 1; i < len; i++) { - ut.insertRow(range.beginRowIndex, cell); - } - } - rng.moveToBookmark(bk).select(); - if (table.getAttribute("interlaced") === "enabled")this.fireEvent("interlacetable", table); - } - }; - //后插入行 - UE.commands["insertrownext"] = { - queryCommandState: function () { - var tableItems = getTableItemsByRange(this), - cell = tableItems.cell; - return cell && (cell.tagName == "TD") && getUETable(tableItems.table).rowsNum < this.options.maxRowNum ? 0 : -1; - }, - execCommand: function () { - var rng = this.selection.getRange(), - bk = rng.createBookmark(true); - var tableItems = getTableItemsByRange(this), - cell = tableItems.cell, - table = tableItems.table, - ut = getUETable(table), - cellInfo = ut.getCellInfo(cell); - //ut.insertRow(!ut.selectedTds.length? cellInfo.rowIndex + cellInfo.rowSpan : ut.cellsRange.endRowIndex + 1,''); - if (!ut.selectedTds.length) { - ut.insertRow(cellInfo.rowIndex + cellInfo.rowSpan, cell); - } else { - var range = ut.cellsRange; - for (var i = 0, len = range.endRowIndex - range.beginRowIndex + 1; i < len; i++) { - ut.insertRow(range.endRowIndex + 1, cell); - } - } - rng.moveToBookmark(bk).select(); - if (table.getAttribute("interlaced") === "enabled")this.fireEvent("interlacetable", table); - } - }; - UE.commands["deleterow"] = { - queryCommandState: function () { - var tableItems = getTableItemsByRange(this); - return tableItems.cell ? 0 : -1; - }, - execCommand: function () { - var cell = getTableItemsByRange(this).cell, - ut = getUETable(cell), - cellsRange = ut.cellsRange, - cellInfo = ut.getCellInfo(cell), - preCell = ut.getVSideCell(cell), - nextCell = ut.getVSideCell(cell, true), - rng = this.selection.getRange(); - if (utils.isEmptyObject(cellsRange)) { - ut.deleteRow(cellInfo.rowIndex); - } else { - for (var i = cellsRange.beginRowIndex; i < cellsRange.endRowIndex + 1; i++) { - ut.deleteRow(cellsRange.beginRowIndex); - } - } - var table = ut.table; - if (!table.getElementsByTagName('td').length) { - var nextSibling = table.nextSibling; - domUtils.remove(table); - if (nextSibling) { - rng.setStart(nextSibling, 0).setCursor(false, true); - } - } else { - if (cellInfo.rowSpan == 1 || cellInfo.rowSpan == cellsRange.endRowIndex - cellsRange.beginRowIndex + 1) { - if (nextCell || preCell) rng.selectNodeContents(nextCell || preCell).setCursor(false, true); - } else { - var newCell = ut.getCell(cellInfo.rowIndex, ut.indexTable[cellInfo.rowIndex][cellInfo.colIndex].cellIndex); - if (newCell) rng.selectNodeContents(newCell).setCursor(false, true); - } - } - if (table.getAttribute("interlaced") === "enabled")this.fireEvent("interlacetable", table); - } - }; - UE.commands["insertcol"] = { - queryCommandState: function (cmd) { - var tableItems = getTableItemsByRange(this), - cell = tableItems.cell; - return cell && (cell.tagName == "TD" || (cell.tagName == 'TH' && cell !== tableItems.tr.cells[0])) && - getUETable(tableItems.table).colsNum < this.options.maxColNum ? 0 : -1; - }, - execCommand: function (cmd) { - var rng = this.selection.getRange(), - bk = rng.createBookmark(true); - if (this.queryCommandState(cmd) == -1)return; - var cell = getTableItemsByRange(this).cell, - ut = getUETable(cell), - cellInfo = ut.getCellInfo(cell); - - //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex:ut.cellsRange.beginColIndex); - if (!ut.selectedTds.length) { - ut.insertCol(cellInfo.colIndex, cell); - } else { - var range = ut.cellsRange; - for (var i = 0, len = range.endColIndex - range.beginColIndex + 1; i < len; i++) { - ut.insertCol(range.beginColIndex, cell); - } - } - rng.moveToBookmark(bk).select(true); - } - }; - UE.commands["insertcolnext"] = { - queryCommandState: function () { - var tableItems = getTableItemsByRange(this), - cell = tableItems.cell; - return cell && getUETable(tableItems.table).colsNum < this.options.maxColNum ? 0 : -1; - }, - execCommand: function () { - var rng = this.selection.getRange(), - bk = rng.createBookmark(true); - var cell = getTableItemsByRange(this).cell, - ut = getUETable(cell), - cellInfo = ut.getCellInfo(cell); - //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex + cellInfo.colSpan:ut.cellsRange.endColIndex +1); - if (!ut.selectedTds.length) { - ut.insertCol(cellInfo.colIndex + cellInfo.colSpan, cell); - } else { - var range = ut.cellsRange; - for (var i = 0, len = range.endColIndex - range.beginColIndex + 1; i < len; i++) { - ut.insertCol(range.endColIndex + 1, cell); - } - } - rng.moveToBookmark(bk).select(); - } - }; - - UE.commands["deletecol"] = { - queryCommandState: function () { - var tableItems = getTableItemsByRange(this); - return tableItems.cell ? 0 : -1; - }, - execCommand: function () { - var cell = getTableItemsByRange(this).cell, - ut = getUETable(cell), - range = ut.cellsRange, - cellInfo = ut.getCellInfo(cell), - preCell = ut.getHSideCell(cell), - nextCell = ut.getHSideCell(cell, true); - if (utils.isEmptyObject(range)) { - ut.deleteCol(cellInfo.colIndex); - } else { - for (var i = range.beginColIndex; i < range.endColIndex + 1; i++) { - ut.deleteCol(range.beginColIndex); - } - } - var table = ut.table, - rng = this.selection.getRange(); - - if (!table.getElementsByTagName('td').length) { - var nextSibling = table.nextSibling; - domUtils.remove(table); - if (nextSibling) { - rng.setStart(nextSibling, 0).setCursor(false, true); - } - } else { - if (domUtils.inDoc(cell, this.document)) { - rng.setStart(cell, 0).setCursor(false, true); - } else { - if (nextCell && domUtils.inDoc(nextCell, this.document)) { - rng.selectNodeContents(nextCell).setCursor(false, true); - } else { - if (preCell && domUtils.inDoc(preCell, this.document)) { - rng.selectNodeContents(preCell).setCursor(true, true); - } - } - } - } - } - }; - UE.commands["splittocells"] = { - queryCommandState: function () { - var tableItems = getTableItemsByRange(this), - cell = tableItems.cell; - if (!cell) return -1; - var ut = getUETable(tableItems.table); - if (ut.selectedTds.length > 0) return -1; - return cell && (cell.colSpan > 1 || cell.rowSpan > 1) ? 0 : -1; - }, - execCommand: function () { - var rng = this.selection.getRange(), - bk = rng.createBookmark(true); - var cell = getTableItemsByRange(this).cell, - ut = getUETable(cell); - ut.splitToCells(cell); - rng.moveToBookmark(bk).select(); - } - }; - UE.commands["splittorows"] = { - queryCommandState: function () { - var tableItems = getTableItemsByRange(this), - cell = tableItems.cell; - if (!cell) return -1; - var ut = getUETable(tableItems.table); - if (ut.selectedTds.length > 0) return -1; - return cell && cell.rowSpan > 1 ? 0 : -1; - }, - execCommand: function () { - var rng = this.selection.getRange(), - bk = rng.createBookmark(true); - var cell = getTableItemsByRange(this).cell, - ut = getUETable(cell); - ut.splitToRows(cell); - rng.moveToBookmark(bk).select(); - } - }; - UE.commands["splittocols"] = { - queryCommandState: function () { - var tableItems = getTableItemsByRange(this), - cell = tableItems.cell; - if (!cell) return -1; - var ut = getUETable(tableItems.table); - if (ut.selectedTds.length > 0) return -1; - return cell && cell.colSpan > 1 ? 0 : -1; - }, - execCommand: function () { - var rng = this.selection.getRange(), - bk = rng.createBookmark(true); - var cell = getTableItemsByRange(this).cell, - ut = getUETable(cell); - ut.splitToCols(cell); - rng.moveToBookmark(bk).select(); - - } - }; - - UE.commands["adaptbytext"] = - UE.commands["adaptbywindow"] = { - queryCommandState: function () { - return getTableItemsByRange(this).table ? 0 : -1 - }, - execCommand: function (cmd) { - var tableItems = getTableItemsByRange(this), - table = tableItems.table; - if (table) { - if (cmd == 'adaptbywindow') { - resetTdWidth(table, this); - } else { - var cells = domUtils.getElementsByTagName(table, "td th"); - utils.each(cells, function (cell) { - cell.removeAttribute("width"); - }); - table.removeAttribute("width"); - } - } - } - }; - - //平均分配各列 - UE.commands['averagedistributecol'] = { - queryCommandState: function () { - var ut = getUETableBySelected(this); - if (!ut) return -1; - return ut.isFullRow() || ut.isFullCol() ? 0 : -1; - }, - execCommand: function (cmd) { - var me = this, - ut = getUETableBySelected(me); - - function getAverageWidth() { - var tb = ut.table, - averageWidth, sumWidth = 0, colsNum = 0, - tbAttr = getDefaultValue(me, tb); - - if (ut.isFullRow()) { - sumWidth = tb.offsetWidth; - colsNum = ut.colsNum; - } else { - var begin = ut.cellsRange.beginColIndex, - end = ut.cellsRange.endColIndex, - node; - for (var i = begin; i <= end;) { - node = ut.selectedTds[i]; - sumWidth += node.offsetWidth; - i += node.colSpan; - colsNum += 1; - } - } - averageWidth = Math.ceil(sumWidth / colsNum) - tbAttr.tdBorder * 2 - tbAttr.tdPadding * 2; - return averageWidth; - } - - function setAverageWidth(averageWidth) { - utils.each(domUtils.getElementsByTagName(ut.table, "th"), function (node) { - node.setAttribute("width", ""); - }); - var cells = ut.isFullRow() ? domUtils.getElementsByTagName(ut.table, "td") : ut.selectedTds; - - utils.each(cells, function (node) { - if (node.colSpan == 1) { - node.setAttribute("width", averageWidth); - } - }); - } - - if (ut && ut.selectedTds.length) { - setAverageWidth(getAverageWidth()); - } - } - }; - //平均分配各行 - UE.commands['averagedistributerow'] = { - queryCommandState: function () { - var ut = getUETableBySelected(this); - if (!ut) return -1; - if (ut.selectedTds && /th/ig.test(ut.selectedTds[0].tagName)) return -1; - return ut.isFullRow() || ut.isFullCol() ? 0 : -1; - }, - execCommand: function (cmd) { - var me = this, - ut = getUETableBySelected(me); - - function getAverageHeight() { - var averageHeight, rowNum, sumHeight = 0, - tb = ut.table, - tbAttr = getDefaultValue(me, tb), - tdpadding = parseInt(domUtils.getComputedStyle(tb.getElementsByTagName('td')[0], "padding-top")); - - if (ut.isFullCol()) { - var captionArr = domUtils.getElementsByTagName(tb, "caption"), - thArr = domUtils.getElementsByTagName(tb, "th"), - captionHeight, thHeight; - - if (captionArr.length > 0) { - captionHeight = captionArr[0].offsetHeight; - } - if (thArr.length > 0) { - thHeight = thArr[0].offsetHeight; - } - - sumHeight = tb.offsetHeight - (captionHeight || 0) - (thHeight || 0); - rowNum = thArr.length == 0 ? ut.rowsNum : (ut.rowsNum - 1); - } else { - var begin = ut.cellsRange.beginRowIndex, - end = ut.cellsRange.endRowIndex, - count = 0, - trs = domUtils.getElementsByTagName(tb, "tr"); - for (var i = begin; i <= end; i++) { - sumHeight += trs[i].offsetHeight; - count += 1; - } - rowNum = count; - } - //ie8下是混杂模式 - if (browser.ie && browser.version < 9) { - averageHeight = Math.ceil(sumHeight / rowNum); - } else { - averageHeight = Math.ceil(sumHeight / rowNum) - tbAttr.tdBorder * 2 - tdpadding * 2; - } - return averageHeight; - } - - function setAverageHeight(averageHeight) { - var cells = ut.isFullCol() ? domUtils.getElementsByTagName(ut.table, "td") : ut.selectedTds; - utils.each(cells, function (node) { - if (node.rowSpan == 1) { - node.setAttribute("height", averageHeight); - } - }); - } - - if (ut && ut.selectedTds.length) { - setAverageHeight(getAverageHeight()); - } - } - }; - - //单元格对齐方式 - UE.commands['cellalignment'] = { - queryCommandState: function () { - return getTableItemsByRange(this).table ? 0 : -1 - }, - execCommand: function (cmd, data) { - var me = this, - ut = getUETableBySelected(me); - - if (!ut) { - var start = me.selection.getStart(), - cell = start && domUtils.findParentByTagName(start, ["td", "th", "caption"], true); - if (!/caption/ig.test(cell.tagName)) { - domUtils.setAttributes(cell, data); - } else { - cell.style.textAlign = data.align; - cell.style.verticalAlign = data.vAlign; - } - me.selection.getRange().setCursor(true); - } else { - utils.each(ut.selectedTds, function (cell) { - domUtils.setAttributes(cell, data); - }); - } - }, - /** - * 查询当前点击的单元格的对齐状态, 如果当前已经选择了多个单元格, 则会返回所有单元格经过统一协调过后的状态 - * @see UE.UETable.getTableCellAlignState - */ - queryCommandValue: function (cmd) { - - var activeMenuCell = getTableItemsByRange( this).cell; - - if( !activeMenuCell ) { - activeMenuCell = getSelectedArr(this)[0]; - } - - if (!activeMenuCell) { - - return null; - - } else { - - //获取同时选中的其他单元格 - var cells = UE.UETable.getUETable(activeMenuCell).selectedTds; - - !cells.length && ( cells = activeMenuCell ); - - return UE.UETable.getTableCellAlignState(cells); - - } - - } - }; - //表格对齐方式 - UE.commands['tablealignment'] = { - queryCommandState: function () { - if (browser.ie && browser.version < 8) { - return -1; - } - return getTableItemsByRange(this).table ? 0 : -1 - }, - execCommand: function (cmd, value) { - var me = this, - start = me.selection.getStart(), - table = start && domUtils.findParentByTagName(start, ["table"], true); - - if (table) { - table.setAttribute("align",value); - } - } - }; - - //表格属性 - UE.commands['edittable'] = { - queryCommandState: function () { - return getTableItemsByRange(this).table ? 0 : -1 - }, - execCommand: function (cmd, color) { - var rng = this.selection.getRange(), - table = domUtils.findParentByTagName(rng.startContainer, 'table'); - if (table) { - var arr = domUtils.getElementsByTagName(table, "td").concat( - domUtils.getElementsByTagName(table, "th"), - domUtils.getElementsByTagName(table, "caption") - ); - utils.each(arr, function (node) { - node.style.borderColor = color; - }); - } - } - }; - //单元格属性 - UE.commands['edittd'] = { - queryCommandState: function () { - return getTableItemsByRange(this).table ? 0 : -1 - }, - execCommand: function (cmd, bkColor) { - var me = this, - ut = getUETableBySelected(me); - - if (!ut) { - var start = me.selection.getStart(), - cell = start && domUtils.findParentByTagName(start, ["td", "th", "caption"], true); - if (cell) { - cell.style.backgroundColor = bkColor; - } - } else { - utils.each(ut.selectedTds, function (cell) { - cell.style.backgroundColor = bkColor; - }); - } - } - }; - - UE.commands["settablebackground"] = { - queryCommandState: function () { - return getSelectedArr(this).length > 1 ? 0 : -1; - }, - execCommand: function (cmd, value) { - var cells, ut; - cells = getSelectedArr(this); - ut = getUETable(cells[0]); - ut.setBackground(cells, value); - } - }; - - UE.commands["cleartablebackground"] = { - queryCommandState: function () { - var cells = getSelectedArr(this); - if (!cells.length)return -1; - for (var i = 0, cell; cell = cells[i++];) { - if (cell.style.backgroundColor !== "") return 0; - } - return -1; - }, - execCommand: function () { - var cells = getSelectedArr(this), - ut = getUETable(cells[0]); - ut.removeBackground(cells); - } - }; - - UE.commands["interlacetable"] = UE.commands["uninterlacetable"] = { - queryCommandState: function (cmd) { - var table = getTableItemsByRange(this).table; - if (!table) return -1; - var interlaced = table.getAttribute("interlaced"); - if (cmd == "interlacetable") { - //TODO 待定 - //是否需要待定,如果设置,则命令只能单次执行成功,但反射具备toggle效果;否则可以覆盖前次命令,但反射将不存在toggle效果 - return (interlaced === "enabled") ? -1 : 0; - } else { - return (!interlaced || interlaced === "disabled") ? -1 : 0; - } - }, - execCommand: function (cmd, classList) { - var table = getTableItemsByRange(this).table; - if (cmd == "interlacetable") { - table.setAttribute("interlaced", "enabled"); - this.fireEvent("interlacetable", table, classList); - } else { - table.setAttribute("interlaced", "disabled"); - this.fireEvent("uninterlacetable", table); - } - } - }; - UE.commands["setbordervisible"] = { - queryCommandState: function (cmd) { - var table = getTableItemsByRange(this).table; - if (!table) return -1; - return 0; - }, - execCommand: function () { - var table = getTableItemsByRange(this).table; - utils.each(domUtils.getElementsByTagName(table,'td'),function(td){ - td.style.borderWidth = '1px'; - td.style.borderStyle = 'solid'; - }) - } - }; - function resetTdWidth(table, editor) { - var tds = domUtils.getElementsByTagName(table,'td th'); - utils.each(tds, function (td) { - td.removeAttribute("width"); - }); - table.setAttribute('width', getTableWidth(editor, true, getDefaultValue(editor, table))); - var tdsWidths = []; - setTimeout(function () { - utils.each(tds, function (td) { - (td.colSpan == 1) && tdsWidths.push(td.offsetWidth) - }) - utils.each(tds, function (td,i) { - (td.colSpan == 1) && td.setAttribute("width", tdsWidths[i] + ""); - }) - }, 0); - } - - function getTableWidth(editor, needIEHack, defaultValue) { - var body = editor.body; - return body.offsetWidth - (needIEHack ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (editor.options.offsetWidth || 0); - } - - function getSelectedArr(editor) { - var cell = getTableItemsByRange(editor).cell; - if (cell) { - var ut = getUETable(cell); - return ut.selectedTds.length ? ut.selectedTds : [cell]; - } else { - return []; - } - } -})(); - - -// plugins/table.action.js -/** - * Created with JetBrains PhpStorm. - * User: taoqili - * Date: 12-10-12 - * Time: 上午10:05 - * To change this template use File | Settings | File Templates. - */ -UE.plugins['table'] = function () { - var me = this, - tabTimer = null, - //拖动计时器 - tableDragTimer = null, - //双击计时器 - tableResizeTimer = null, - //单元格最小宽度 - cellMinWidth = 5, - isInResizeBuffer = false, - //单元格边框大小 - cellBorderWidth = 5, - //鼠标偏移距离 - offsetOfTableCell = 10, - //记录在有限时间内的点击状态, 共有3个取值, 0, 1, 2。 0代表未初始化, 1代表单击了1次,2代表2次 - singleClickState = 0, - userActionStatus = null, - //双击允许的时间范围 - dblclickTime = 360, - UT = UE.UETable, - getUETable = function (tdOrTable) { - return UT.getUETable(tdOrTable); - }, - getUETableBySelected = function (editor) { - return UT.getUETableBySelected(editor); - }, - getDefaultValue = function (editor, table) { - return UT.getDefaultValue(editor, table); - }, - removeSelectedClass = function (cells) { - return UT.removeSelectedClass(cells); - }; - - function showError(e) { -// throw e; - } - me.ready(function(){ - var me = this; - var orgGetText = me.selection.getText; - me.selection.getText = function(){ - var table = getUETableBySelected(me); - if(table){ - var str = ''; - utils.each(table.selectedTds,function(td){ - str += td[browser.ie?'innerText':'textContent']; - }) - return str; - }else{ - return orgGetText.call(me.selection) - } - - } - }) - - //处理拖动及框选相关方法 - var startTd = null, //鼠标按下时的锚点td - currentTd = null, //当前鼠标经过时的td - onDrag = "", //指示当前拖动状态,其值可为"","h","v" ,分别表示未拖动状态,横向拖动状态,纵向拖动状态,用于鼠标移动过程中的判断 - onBorder = false, //检测鼠标按下时是否处在单元格边缘位置 - dragButton = null, - dragOver = false, - dragLine = null, //模拟的拖动线 - dragTd = null; //发生拖动的目标td - - var mousedown = false, - //todo 判断混乱模式 - needIEHack = true; - - me.setOpt({ - 'maxColNum':20, - 'maxRowNum':100, - 'defaultCols':5, - 'defaultRows':5, - 'tdvalign':'top', - 'cursorpath':me.options.UEDITOR_HOME_URL + "themes/default/images/cursor_", - 'tableDragable':false, - 'classList':["ue-table-interlace-color-single","ue-table-interlace-color-double"] - }); - me.getUETable = getUETable; - var commands = { - 'deletetable':1, - 'inserttable':1, - 'cellvalign':1, - 'insertcaption':1, - 'deletecaption':1, - 'inserttitle':1, - 'deletetitle':1, - "mergeright":1, - "mergedown":1, - "mergecells":1, - "insertrow":1, - "insertrownext":1, - "deleterow":1, - "insertcol":1, - "insertcolnext":1, - "deletecol":1, - "splittocells":1, - "splittorows":1, - "splittocols":1, - "adaptbytext":1, - "adaptbywindow":1, - "adaptbycustomer":1, - "insertparagraph":1, - "insertparagraphbeforetable":1, - "averagedistributecol":1, - "averagedistributerow":1 - }; - me.ready(function () { - utils.cssRule('table', - //选中的td上的样式 - '.selectTdClass{background-color:#edf5fa !important}' + - 'table.noBorderTable td,table.noBorderTable th,table.noBorderTable caption{border:1px dashed #ddd !important}' + - //插入的表格的默认样式 - 'table{margin-bottom:10px;border-collapse:collapse;display:table;}' + - 'td,th{padding: 5px 10px;border: 1px solid #DDD;}' + - 'caption{border:1px dashed #DDD;border-bottom:0;padding:3px;text-align:center;}' + - 'th{border-top:1px solid #BBB;background-color:#F7F7F7;}' + - 'table tr.firstRow th{border-top-width:2px;}' + - '.ue-table-interlace-color-single{ background-color: #fcfcfc; } .ue-table-interlace-color-double{ background-color: #f7faff; }' + - 'td p{margin:0;padding:0;}', me.document); - - var tableCopyList, isFullCol, isFullRow; - //注册del/backspace事件 - me.addListener('keydown', function (cmd, evt) { - var me = this; - var keyCode = evt.keyCode || evt.which; - - if (keyCode == 8) { - - var ut = getUETableBySelected(me); - if (ut && ut.selectedTds.length) { - - if (ut.isFullCol()) { - me.execCommand('deletecol') - } else if (ut.isFullRow()) { - me.execCommand('deleterow') - } else { - me.fireEvent('delcells'); - } - domUtils.preventDefault(evt); - } - - var caption = domUtils.findParentByTagName(me.selection.getStart(), 'caption', true), - range = me.selection.getRange(); - if (range.collapsed && caption && isEmptyBlock(caption)) { - me.fireEvent('saveScene'); - var table = caption.parentNode; - domUtils.remove(caption); - if (table) { - range.setStart(table.rows[0].cells[0], 0).setCursor(false, true); - } - me.fireEvent('saveScene'); - } - - } - - if (keyCode == 46) { - - ut = getUETableBySelected(me); - if (ut) { - me.fireEvent('saveScene'); - for (var i = 0, ci; ci = ut.selectedTds[i++];) { - domUtils.fillNode(me.document, ci) - } - me.fireEvent('saveScene'); - domUtils.preventDefault(evt); - - } - - } - if (keyCode == 13) { - - var rng = me.selection.getRange(), - caption = domUtils.findParentByTagName(rng.startContainer, 'caption', true); - if (caption) { - var table = domUtils.findParentByTagName(caption, 'table'); - if (!rng.collapsed) { - - rng.deleteContents(); - me.fireEvent('saveScene'); - } else { - if (caption) { - rng.setStart(table.rows[0].cells[0], 0).setCursor(false, true); - } - } - domUtils.preventDefault(evt); - return; - } - if (rng.collapsed) { - var table = domUtils.findParentByTagName(rng.startContainer, 'table'); - if (table) { - var cell = table.rows[0].cells[0], - start = domUtils.findParentByTagName(me.selection.getStart(), ['td', 'th'], true), - preNode = table.previousSibling; - if (cell === start && (!preNode || preNode.nodeType == 1 && preNode.tagName == 'TABLE' ) && domUtils.isStartInblock(rng)) { - var first = domUtils.findParent(me.selection.getStart(), function(n){return domUtils.isBlockElm(n)}, true); - if(first && ( /t(h|d)/i.test(first.tagName) || first === start.firstChild )){ - me.execCommand('insertparagraphbeforetable'); - domUtils.preventDefault(evt); - } - - } - } - } - } - - if ((evt.ctrlKey || evt.metaKey) && evt.keyCode == '67') { - tableCopyList = null; - var ut = getUETableBySelected(me); - if (ut) { - var tds = ut.selectedTds; - isFullCol = ut.isFullCol(); - isFullRow = ut.isFullRow(); - tableCopyList = [ - [ut.cloneCell(tds[0],null,true)] - ]; - for (var i = 1, ci; ci = tds[i]; i++) { - if (ci.parentNode !== tds[i - 1].parentNode) { - tableCopyList.push([ut.cloneCell(ci,null,true)]); - } else { - tableCopyList[tableCopyList.length - 1].push(ut.cloneCell(ci,null,true)); - } - - } - } - } - }); - me.addListener("tablehasdeleted",function(){ - toggleDraggableState(this, false, "", null); - if (dragButton)domUtils.remove(dragButton); - }); - - me.addListener('beforepaste', function (cmd, html) { - var me = this; - var rng = me.selection.getRange(); - if (domUtils.findParentByTagName(rng.startContainer, 'caption', true)) { - var div = me.document.createElement("div"); - div.innerHTML = html.html; - //trace:3729 - html.html = div[browser.ie9below ? 'innerText' : 'textContent']; - return; - } - var table = getUETableBySelected(me); - if (tableCopyList) { - me.fireEvent('saveScene'); - var rng = me.selection.getRange(); - var td = domUtils.findParentByTagName(rng.startContainer, ['td', 'th'], true), tmpNode, preNode; - if (td) { - var ut = getUETable(td); - if (isFullRow) { - var rowIndex = ut.getCellInfo(td).rowIndex; - if (td.tagName == 'TH') { - rowIndex++; - } - for (var i = 0, ci; ci = tableCopyList[i++];) { - var tr = ut.insertRow(rowIndex++, "td"); - for (var j = 0, cj; cj = ci[j]; j++) { - var cell = tr.cells[j]; - if (!cell) { - cell = tr.insertCell(j) - } - cell.innerHTML = cj.innerHTML; - cj.getAttribute('width') && cell.setAttribute('width', cj.getAttribute('width')); - cj.getAttribute('vAlign') && cell.setAttribute('vAlign', cj.getAttribute('vAlign')); - cj.getAttribute('align') && cell.setAttribute('align', cj.getAttribute('align')); - cj.style.cssText && (cell.style.cssText = cj.style.cssText) - } - for (var j = 0, cj; cj = tr.cells[j]; j++) { - if (!ci[j]) - break; - cj.innerHTML = ci[j].innerHTML; - ci[j].getAttribute('width') && cj.setAttribute('width', ci[j].getAttribute('width')); - ci[j].getAttribute('vAlign') && cj.setAttribute('vAlign', ci[j].getAttribute('vAlign')); - ci[j].getAttribute('align') && cj.setAttribute('align', ci[j].getAttribute('align')); - ci[j].style.cssText && (cj.style.cssText = ci[j].style.cssText) - } - } - } else { - if (isFullCol) { - cellInfo = ut.getCellInfo(td); - var maxColNum = 0; - for (var j = 0, ci = tableCopyList[0], cj; cj = ci[j++];) { - maxColNum += cj.colSpan || 1; - } - me.__hasEnterExecCommand = true; - for (i = 0; i < maxColNum; i++) { - me.execCommand('insertcol'); - } - me.__hasEnterExecCommand = false; - td = ut.table.rows[0].cells[cellInfo.cellIndex]; - if (td.tagName == 'TH') { - td = ut.table.rows[1].cells[cellInfo.cellIndex]; - } - } - for (var i = 0, ci; ci = tableCopyList[i++];) { - tmpNode = td; - for (var j = 0, cj; cj = ci[j++];) { - if (td) { - td.innerHTML = cj.innerHTML; - //todo 定制处理 - cj.getAttribute('width') && td.setAttribute('width', cj.getAttribute('width')); - cj.getAttribute('vAlign') && td.setAttribute('vAlign', cj.getAttribute('vAlign')); - cj.getAttribute('align') && td.setAttribute('align', cj.getAttribute('align')); - cj.style.cssText && (td.style.cssText = cj.style.cssText); - preNode = td; - td = td.nextSibling; - } else { - var cloneTd = cj.cloneNode(true); - domUtils.removeAttributes(cloneTd, ['class', 'rowSpan', 'colSpan']); - - preNode.parentNode.appendChild(cloneTd) - } - } - td = ut.getNextCell(tmpNode, true, true); - if (!tableCopyList[i]) - break; - if (!td) { - var cellInfo = ut.getCellInfo(tmpNode); - ut.table.insertRow(ut.table.rows.length); - ut.update(); - td = ut.getVSideCell(tmpNode, true); - } - } - } - ut.update(); - } else { - table = me.document.createElement('table'); - for (var i = 0, ci; ci = tableCopyList[i++];) { - var tr = table.insertRow(table.rows.length); - for (var j = 0, cj; cj = ci[j++];) { - cloneTd = UT.cloneCell(cj,null,true); - domUtils.removeAttributes(cloneTd, ['class']); - tr.appendChild(cloneTd) - } - if (j == 2 && cloneTd.rowSpan > 1) { - cloneTd.rowSpan = 1; - } - } - - var defaultValue = getDefaultValue(me), - width = me.body.offsetWidth - - (needIEHack ? parseInt(domUtils.getComputedStyle(me.body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (me.options.offsetWidth || 0); - me.execCommand('insertHTML', '' + table.innerHTML.replace(/>\s*<').replace(/\bth\b/gi, "td") + '
                  ') - } - me.fireEvent('contentchange'); - me.fireEvent('saveScene'); - html.html = ''; - return true; - } else { - var div = me.document.createElement("div"), tables; - div.innerHTML = html.html; - tables = div.getElementsByTagName("table"); - if (domUtils.findParentByTagName(me.selection.getStart(), 'table')) { - utils.each(tables, function (t) { - domUtils.remove(t) - }); - if (domUtils.findParentByTagName(me.selection.getStart(), 'caption', true)) { - div.innerHTML = div[browser.ie ? 'innerText' : 'textContent']; - } - } else { - utils.each(tables, function (table) { - removeStyleSize(table, true); - domUtils.removeAttributes(table, ['style', 'border']); - utils.each(domUtils.getElementsByTagName(table, "td"), function (td) { - if (isEmptyBlock(td)) { - domUtils.fillNode(me.document, td); - } - removeStyleSize(td, true); -// domUtils.removeAttributes(td, ['style']) - }); - }); - } - html.html = div.innerHTML; - } - }); - - me.addListener('afterpaste', function () { - utils.each(domUtils.getElementsByTagName(me.body, "table"), function (table) { - if (table.offsetWidth > me.body.offsetWidth) { - var defaultValue = getDefaultValue(me, table); - table.style.width = me.body.offsetWidth - (needIEHack ? parseInt(domUtils.getComputedStyle(me.body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (me.options.offsetWidth || 0) + 'px' - } - }) - }); - me.addListener('blur', function () { - tableCopyList = null; - }); - var timer; - me.addListener('keydown', function () { - clearTimeout(timer); - timer = setTimeout(function () { - var rng = me.selection.getRange(), - cell = domUtils.findParentByTagName(rng.startContainer, ['th', 'td'], true); - if (cell) { - var table = cell.parentNode.parentNode.parentNode; - if (table.offsetWidth > table.getAttribute("width")) { - cell.style.wordBreak = "break-all"; - } - } - - }, 100); - }); - me.addListener("selectionchange", function () { - toggleDraggableState(me, false, "", null); - }); - - - //内容变化时触发索引更新 - //todo 可否考虑标记检测,如果不涉及表格的变化就不进行索引重建和更新 - me.addListener("contentchange", function () { - var me = this; - //尽可能排除一些不需要更新的状况 - hideDragLine(me); - if (getUETableBySelected(me))return; - var rng = me.selection.getRange(); - var start = rng.startContainer; - start = domUtils.findParentByTagName(start, ['td', 'th'], true); - utils.each(domUtils.getElementsByTagName(me.document, 'table'), function (table) { - if (me.fireEvent("excludetable", table) === true) return; - table.ueTable = new UT(table); - //trace:3742 -// utils.each(domUtils.getElementsByTagName(me.document, 'td'), function (td) { -// -// if (domUtils.isEmptyBlock(td) && td !== start) { -// domUtils.fillNode(me.document, td); -// if (browser.ie && browser.version == 6) { -// td.innerHTML = ' ' -// } -// } -// }); -// utils.each(domUtils.getElementsByTagName(me.document, 'th'), function (th) { -// if (domUtils.isEmptyBlock(th) && th !== start) { -// domUtils.fillNode(me.document, th); -// if (browser.ie && browser.version == 6) { -// th.innerHTML = ' ' -// } -// } -// }); - table.onmouseover = function () { - me.fireEvent('tablemouseover', table); - }; - table.onmousemove = function () { - me.fireEvent('tablemousemove', table); - me.options.tableDragable && toggleDragButton(true, this, me); - utils.defer(function(){ - me.fireEvent('contentchange',50) - },true) - }; - table.onmouseout = function () { - me.fireEvent('tablemouseout', table); - toggleDraggableState(me, false, "", null); - hideDragLine(me); - }; - table.onclick = function (evt) { - evt = me.window.event || evt; - var target = getParentTdOrTh(evt.target || evt.srcElement); - if (!target)return; - var ut = getUETable(target), - table = ut.table, - cellInfo = ut.getCellInfo(target), - cellsRange, - rng = me.selection.getRange(); -// if ("topLeft" == inPosition(table, mouseCoords(evt))) { -// cellsRange = ut.getCellsRange(ut.table.rows[0].cells[0], ut.getLastCell()); -// ut.setSelected(cellsRange); -// return; -// } -// if ("bottomRight" == inPosition(table, mouseCoords(evt))) { -// -// return; -// } - if (inTableSide(table, target, evt, true)) { - var endTdCol = ut.getCell(ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].rowIndex, ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].cellIndex); - if (evt.shiftKey && ut.selectedTds.length) { - if (ut.selectedTds[0] !== endTdCol) { - cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdCol); - ut.setSelected(cellsRange); - } else { - rng && rng.selectNodeContents(endTdCol).select(); - } - } else { - if (target !== endTdCol) { - cellsRange = ut.getCellsRange(target, endTdCol); - ut.setSelected(cellsRange); - } else { - rng && rng.selectNodeContents(endTdCol).select(); - } - } - return; - } - if (inTableSide(table, target, evt)) { - var endTdRow = ut.getCell(ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].rowIndex, ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].cellIndex); - if (evt.shiftKey && ut.selectedTds.length) { - if (ut.selectedTds[0] !== endTdRow) { - cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdRow); - ut.setSelected(cellsRange); - } else { - rng && rng.selectNodeContents(endTdRow).select(); - } - } else { - if (target !== endTdRow) { - cellsRange = ut.getCellsRange(target, endTdRow); - ut.setSelected(cellsRange); - } else { - rng && rng.selectNodeContents(endTdRow).select(); - } - } - } - }; - }); - - switchBorderColor(me, true); - }); - - domUtils.on(me.document, "mousemove", mouseMoveEvent); - - domUtils.on(me.document, "mouseout", function (evt) { - var target = evt.target || evt.srcElement; - if (target.tagName == "TABLE") { - toggleDraggableState(me, false, "", null); - } - }); - /** - * 表格隔行变色 - */ - me.addListener("interlacetable",function(type,table,classList){ - if(!table) return; - var me = this, - rows = table.rows, - len = rows.length, - getClass = function(list,index,repeat){ - return list[index] ? list[index] : repeat ? list[index % list.length]: ""; - }; - for(var i = 0;i 1 ? currentRowIndex : ua.getCellInfo(cell).rowIndex; - var nextCell = ua.getTabNextCell(cell, currentRowIndex); - if (nextCell) { - if (isEmptyBlock(nextCell)) { - range.setStart(nextCell, 0).setCursor(false, true) - } else { - range.selectNodeContents(nextCell).select() - } - } else { - me.fireEvent('saveScene'); - me.__hasEnterExecCommand = true; - this.execCommand('insertrownext'); - me.__hasEnterExecCommand = false; - range = this.selection.getRange(); - range.setStart(table.rows[table.rows.length - 1].cells[0], 0).setCursor(); - me.fireEvent('saveScene'); - } - } - return true; - } - - }); - browser.ie && me.addListener('selectionchange', function () { - toggleDraggableState(this, false, "", null); - }); - me.addListener("keydown", function (type, evt) { - var me = this; - //处理在表格的最后一个输入tab产生新的表格 - var keyCode = evt.keyCode || evt.which; - if (keyCode == 8 || keyCode == 46) { - return; - } - var notCtrlKey = !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey; - notCtrlKey && removeSelectedClass(domUtils.getElementsByTagName(me.body, "td")); - var ut = getUETableBySelected(me); - if (!ut) return; - notCtrlKey && ut.clearSelected(); - }); - - me.addListener("beforegetcontent", function () { - switchBorderColor(this, false); - browser.ie && utils.each(this.document.getElementsByTagName('caption'), function (ci) { - if (domUtils.isEmptyNode(ci)) { - ci.innerHTML = ' ' - } - }); - }); - me.addListener("aftergetcontent", function () { - switchBorderColor(this, true); - }); - me.addListener("getAllHtml", function () { - removeSelectedClass(me.document.getElementsByTagName("td")); - }); - //修正全屏状态下插入的表格宽度在非全屏状态下撑开编辑器的情况 - me.addListener("fullscreenchanged", function (type, fullscreen) { - if (!fullscreen) { - var ratio = this.body.offsetWidth / document.body.offsetWidth, - tables = domUtils.getElementsByTagName(this.body, "table"); - utils.each(tables, function (table) { - if (table.offsetWidth < me.body.offsetWidth) return false; - var tds = domUtils.getElementsByTagName(table, "td"), - backWidths = []; - utils.each(tds, function (td) { - backWidths.push(td.offsetWidth); - }); - for (var i = 0, td; td = tds[i]; i++) { - td.setAttribute("width", Math.floor(backWidths[i] * ratio)); - } - table.setAttribute("width", Math.floor(getTableWidth(me, needIEHack, getDefaultValue(me)))) - }); - } - }); - - //重写execCommand命令,用于处理框选时的处理 - var oldExecCommand = me.execCommand; - me.execCommand = function (cmd, datatat) { - - var me = this, - args = arguments; - - cmd = cmd.toLowerCase(); - var ut = getUETableBySelected(me), tds, - range = new dom.Range(me.document), - cmdFun = me.commands[cmd] || UE.commands[cmd], - result; - if (!cmdFun) return; - if (ut && !commands[cmd] && !cmdFun.notNeedUndo && !me.__hasEnterExecCommand) { - me.__hasEnterExecCommand = true; - me.fireEvent("beforeexeccommand", cmd); - tds = ut.selectedTds; - var lastState = -2, lastValue = -2, value, state; - for (var i = 0, td; td = tds[i]; i++) { - if (isEmptyBlock(td)) { - range.setStart(td, 0).setCursor(false, true) - } else { - range.selectNode(td).select(true); - } - state = me.queryCommandState(cmd); - value = me.queryCommandValue(cmd); - if (state != -1) { - if (lastState !== state || lastValue !== value) { - me._ignoreContentChange = true; - result = oldExecCommand.apply(me, arguments); - me._ignoreContentChange = false; - - } - lastState = me.queryCommandState(cmd); - lastValue = me.queryCommandValue(cmd); - if (domUtils.isEmptyBlock(td)) { - domUtils.fillNode(me.document, td) - } - } - } - range.setStart(tds[0], 0).shrinkBoundary(true).setCursor(false, true); - me.fireEvent('contentchange'); - me.fireEvent("afterexeccommand", cmd); - me.__hasEnterExecCommand = false; - me._selectionChange(); - } else { - result = oldExecCommand.apply(me, arguments); - } - return result; - }; - - - }); - /** - * 删除obj的宽高style,改成属性宽高 - * @param obj - * @param replaceToProperty - */ - function removeStyleSize(obj, replaceToProperty) { - removeStyle(obj, "width", true); - removeStyle(obj, "height", true); - } - - function removeStyle(obj, styleName, replaceToProperty) { - if (obj.style[styleName]) { - replaceToProperty && obj.setAttribute(styleName, parseInt(obj.style[styleName], 10)); - obj.style[styleName] = ""; - } - } - - function getParentTdOrTh(ele) { - if (ele.tagName == "TD" || ele.tagName == "TH") return ele; - var td; - if (td = domUtils.findParentByTagName(ele, "td", true) || domUtils.findParentByTagName(ele, "th", true)) return td; - return null; - } - - function isEmptyBlock(node) { - var reg = new RegExp(domUtils.fillChar, 'g'); - if (node[browser.ie ? 'innerText' : 'textContent'].replace(/^\s*$/, '').replace(reg, '').length > 0) { - return 0; - } - for (var n in dtd.$isNotEmpty) { - if (node.getElementsByTagName(n).length) { - return 0; - } - } - return 1; - } - - - function mouseCoords(evt) { - if (evt.pageX || evt.pageY) { - return { x:evt.pageX, y:evt.pageY }; - } - return { - x:evt.clientX + me.document.body.scrollLeft - me.document.body.clientLeft, - y:evt.clientY + me.document.body.scrollTop - me.document.body.clientTop - }; - } - - function mouseMoveEvent(evt) { - - if( isEditorDisabled() ) { - return; - } - - try { - - //普通状态下鼠标移动 - var target = getParentTdOrTh(evt.target || evt.srcElement), - pos; - - //区分用户的行为是拖动还是双击 - if( isInResizeBuffer ) { - - me.body.style.webkitUserSelect = 'none'; - - if( Math.abs( userActionStatus.x - evt.clientX ) > offsetOfTableCell || Math.abs( userActionStatus.y - evt.clientY ) > offsetOfTableCell ) { - clearTableDragTimer(); - isInResizeBuffer = false; - singleClickState = 0; - //drag action - tableBorderDrag(evt); - } - } - - //修改单元格大小时的鼠标移动 - if (onDrag && dragTd) { - singleClickState = 0; - me.body.style.webkitUserSelect = 'none'; - me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges'](); - pos = mouseCoords(evt); - toggleDraggableState(me, true, onDrag, pos, target); - if (onDrag == "h") { - dragLine.style.left = getPermissionX(dragTd, evt) + "px"; - } else if (onDrag == "v") { - dragLine.style.top = getPermissionY(dragTd, evt) + "px"; - } - return; - } - //当鼠标处于table上时,修改移动过程中的光标状态 - if (target) { - //针对使用table作为容器的组件不触发拖拽效果 - if (me.fireEvent('excludetable', target) === true) - return; - pos = mouseCoords(evt); - var state = getRelation(target, pos), - table = domUtils.findParentByTagName(target, "table", true); - - if (inTableSide(table, target, evt, true)) { - if (me.fireEvent("excludetable", table) === true) return; - me.body.style.cursor = "url(" + me.options.cursorpath + "h.png),pointer"; - } else if (inTableSide(table, target, evt)) { - if (me.fireEvent("excludetable", table) === true) return; - me.body.style.cursor = "url(" + me.options.cursorpath + "v.png),pointer"; - } else { - me.body.style.cursor = "text"; - var curCell = target; - if (/\d/.test(state)) { - state = state.replace(/\d/, ''); - target = getUETable(target).getPreviewCell(target, state == "v"); - } - //位于第一行的顶部或者第一列的左边时不可拖动 - toggleDraggableState(me, target ? !!state : false, target ? state : '', pos, target); - - } - } else { - toggleDragButton(false, table, me); - } - - } catch (e) { - showError(e); - } - } - - var dragButtonTimer; - - function toggleDragButton(show, table, editor) { - if (!show) { - if (dragOver)return; - dragButtonTimer = setTimeout(function () { - !dragOver && dragButton && dragButton.parentNode && dragButton.parentNode.removeChild(dragButton); - }, 2000); - } else { - createDragButton(table, editor); - } - } - - function createDragButton(table, editor) { - var pos = domUtils.getXY(table), - doc = table.ownerDocument; - if (dragButton && dragButton.parentNode)return dragButton; - dragButton = doc.createElement("div"); - dragButton.contentEditable = false; - dragButton.innerHTML = ""; - dragButton.style.cssText = "width:15px;height:15px;background-image:url(" + editor.options.UEDITOR_HOME_URL + "dialogs/table/dragicon.png);position: absolute;cursor:move;top:" + (pos.y - 15) + "px;left:" + (pos.x) + "px;"; - domUtils.unSelectable(dragButton); - dragButton.onmouseover = function (evt) { - dragOver = true; - }; - dragButton.onmouseout = function (evt) { - dragOver = false; - }; - domUtils.on(dragButton, 'click', function (type, evt) { - doClick(evt, this); - }); - domUtils.on(dragButton, 'dblclick', function (type, evt) { - doDblClick(evt); - }); - domUtils.on(dragButton, 'dragstart', function (type, evt) { - domUtils.preventDefault(evt); - }); - var timer; - - function doClick(evt, button) { - // 部分浏览器下需要清理 - clearTimeout(timer); - timer = setTimeout(function () { - editor.fireEvent("tableClicked", table, button); - }, 300); - } - - function doDblClick(evt) { - clearTimeout(timer); - var ut = getUETable(table), - start = table.rows[0].cells[0], - end = ut.getLastCell(), - range = ut.getCellsRange(start, end); - editor.selection.getRange().setStart(start, 0).setCursor(false, true); - ut.setSelected(range); - } - - doc.body.appendChild(dragButton); - } - - -// function inPosition(table, pos) { -// var tablePos = domUtils.getXY(table), -// width = table.offsetWidth, -// height = table.offsetHeight; -// if (pos.x - tablePos.x < 5 && pos.y - tablePos.y < 5) { -// return "topLeft"; -// } else if (tablePos.x + width - pos.x < 5 && tablePos.y + height - pos.y < 5) { -// return "bottomRight"; -// } -// } - - function inTableSide(table, cell, evt, top) { - var pos = mouseCoords(evt), - state = getRelation(cell, pos); - - if (top) { - var caption = table.getElementsByTagName("caption")[0], - capHeight = caption ? caption.offsetHeight : 0; - return (state == "v1") && ((pos.y - domUtils.getXY(table).y - capHeight) < 8); - } else { - return (state == "h1") && ((pos.x - domUtils.getXY(table).x) < 8); - } - } - - /** - * 获取拖动时允许的X轴坐标 - * @param dragTd - * @param evt - */ - function getPermissionX(dragTd, evt) { - var ut = getUETable(dragTd); - if (ut) { - var preTd = ut.getSameEndPosCells(dragTd, "x")[0], - nextTd = ut.getSameStartPosXCells(dragTd)[0], - mouseX = mouseCoords(evt).x, - left = (preTd ? domUtils.getXY(preTd).x : domUtils.getXY(ut.table).x) + 20 , - right = nextTd ? domUtils.getXY(nextTd).x + nextTd.offsetWidth - 20 : (me.body.offsetWidth + 5 || parseInt(domUtils.getComputedStyle(me.body, "width"), 10)); - - left += cellMinWidth; - right -= cellMinWidth; - - return mouseX < left ? left : mouseX > right ? right : mouseX; - } - } - - /** - * 获取拖动时允许的Y轴坐标 - */ - function getPermissionY(dragTd, evt) { - try { - var top = domUtils.getXY(dragTd).y, - mousePosY = mouseCoords(evt).y; - return mousePosY < top ? top : mousePosY; - } catch (e) { - showError(e); - } - } - - /** - * 移动状态切换 - */ - function toggleDraggableState(editor, draggable, dir, mousePos, cell) { - try { - editor.body.style.cursor = dir == "h" ? "col-resize" : dir == "v" ? "row-resize" : "text"; - if (browser.ie) { - if (dir && !mousedown && !getUETableBySelected(editor)) { - getDragLine(editor, editor.document); - showDragLineAt(dir, cell); - } else { - hideDragLine(editor) - } - } - onBorder = draggable; - } catch (e) { - showError(e); - } - } - - /** - * 获取与UETable相关的resize line - * @param uetable UETable对象 - */ - function getResizeLineByUETable() { - - var lineId = '_UETableResizeLine', - line = this.document.getElementById( lineId ); - - if( !line ) { - line = this.document.createElement("div"); - line.id = lineId; - line.contnetEditable = false; - line.setAttribute("unselectable", "on"); - - var styles = { - width: 2*cellBorderWidth + 1 + 'px', - position: 'absolute', - 'z-index': 100000, - cursor: 'col-resize', - background: 'red', - display: 'none' - }; - - //切换状态 - line.onmouseout = function(){ - this.style.display = 'none'; - }; - - utils.extend( line.style, styles ); - - this.document.body.appendChild( line ); - - } - - return line; - - } - - /** - * 更新resize-line - */ - function updateResizeLine( cell, uetable ) { - - var line = getResizeLineByUETable.call( this ), - table = uetable.table, - styles = { - top: domUtils.getXY( table ).y + 'px', - left: domUtils.getXY( cell).x + cell.offsetWidth - cellBorderWidth + 'px', - display: 'block', - height: table.offsetHeight + 'px' - }; - - utils.extend( line.style, styles ); - - } - - /** - * 显示resize-line - */ - function showResizeLine( cell ) { - - var uetable = getUETable( cell ); - - updateResizeLine.call( this, cell, uetable ); - - } - - /** - * 获取鼠标与当前单元格的相对位置 - * @param ele - * @param mousePos - */ - function getRelation(ele, mousePos) { - var elePos = domUtils.getXY(ele); - - if( !elePos ) { - return ''; - } - - if (elePos.x + ele.offsetWidth - mousePos.x < cellBorderWidth) { - return "h"; - } - if (mousePos.x - elePos.x < cellBorderWidth) { - return 'h1' - } - if (elePos.y + ele.offsetHeight - mousePos.y < cellBorderWidth) { - return "v"; - } - if (mousePos.y - elePos.y < cellBorderWidth) { - return 'v1' - } - return ''; - } - - function mouseDownEvent(type, evt) { - - if( isEditorDisabled() ) { - return ; - } - - userActionStatus = { - x: evt.clientX, - y: evt.clientY - }; - - //右键菜单单独处理 - if (evt.button == 2) { - var ut = getUETableBySelected(me), - flag = false; - - if (ut) { - var td = getTargetTd(me, evt); - utils.each(ut.selectedTds, function (ti) { - if (ti === td) { - flag = true; - } - }); - if (!flag) { - removeSelectedClass(domUtils.getElementsByTagName(me.body, "th td")); - ut.clearSelected() - } else { - td = ut.selectedTds[0]; - setTimeout(function () { - me.selection.getRange().setStart(td, 0).setCursor(false, true); - }, 0); - - } - } - } else { - tableClickHander( evt ); - } - - } - - //清除表格的计时器 - function clearTableTimer() { - tabTimer && clearTimeout( tabTimer ); - tabTimer = null; - } - - //双击收缩 - function tableDbclickHandler(evt) { - singleClickState = 0; - evt = evt || me.window.event; - var target = getParentTdOrTh(evt.target || evt.srcElement); - if (target) { - var h; - if (h = getRelation(target, mouseCoords(evt))) { - - hideDragLine( me ); - - if (h == 'h1') { - h = 'h'; - if (inTableSide(domUtils.findParentByTagName(target, "table"), target, evt)) { - me.execCommand('adaptbywindow'); - } else { - target = getUETable(target).getPreviewCell(target); - if (target) { - var rng = me.selection.getRange(); - rng.selectNodeContents(target).setCursor(true, true) - } - } - } - if (h == 'h') { - var ut = getUETable(target), - table = ut.table, - cells = getCellsByMoveBorder( target, table, true ); - - cells = extractArray( cells, 'left' ); - - ut.width = ut.offsetWidth; - - var oldWidth = [], - newWidth = []; - - utils.each( cells, function( cell ){ - - oldWidth.push( cell.offsetWidth ); - - } ); - - utils.each( cells, function( cell ){ - - cell.removeAttribute("width"); - - } ); - - window.setTimeout( function(){ - - //是否允许改变 - var changeable = true; - - utils.each( cells, function( cell, index ){ - - var width = cell.offsetWidth; - - if( width > oldWidth[index] ) { - changeable = false; - return false; - } - - newWidth.push( width ); - - } ); - - var change = changeable ? newWidth : oldWidth; - - utils.each( cells, function( cell, index ){ - - cell.width = change[index] - getTabcellSpace(); - - } ); - - - }, 0 ); - -// minWidth -= cellMinWidth; -// -// table.removeAttribute("width"); -// utils.each(cells, function (cell) { -// cell.style.width = ""; -// cell.width -= minWidth; -// }); - - } - } - } - } - - function tableClickHander( evt ) { - - removeSelectedClass(domUtils.getElementsByTagName(me.body, "td th")); - //trace:3113 - //选中单元格,点击table外部,不会清掉table上挂的ueTable,会引起getUETableBySelected方法返回值 - utils.each(me.document.getElementsByTagName('table'), function (t) { - t.ueTable = null; - }); - startTd = getTargetTd(me, evt); - if( !startTd ) return; - var table = domUtils.findParentByTagName(startTd, "table", true); - ut = getUETable(table); - ut && ut.clearSelected(); - - //判断当前鼠标状态 - if (!onBorder) { - me.document.body.style.webkitUserSelect = ''; - mousedown = true; - me.addListener('mouseover', mouseOverEvent); - } else { - //边框上的动作处理 - borderActionHandler( evt ); - } - - - } - - //处理表格边框上的动作, 这里做延时处理,避免两种动作互相影响 - function borderActionHandler( evt ) { - - if ( browser.ie ) { - evt = reconstruct(evt ); - } - - clearTableDragTimer(); - - //是否正在等待resize的缓冲中 - isInResizeBuffer = true; - - tableDragTimer = setTimeout(function(){ - tableBorderDrag( evt ); - }, dblclickTime); - - } - - function extractArray( originArr, key ) { - - var result = [], - tmp = null; - - for( var i = 0, len = originArr.length; i 0 && singleClickState--; - }, dblclickTime ); - - if( singleClickState === 2 ) { - - singleClickState = 0; - tableDbclickHandler(evt); - return; - - } - - } - - if (evt.button == 2)return; - var me = this; - //清除表格上原生跨选问题 - var range = me.selection.getRange(), - start = domUtils.findParentByTagName(range.startContainer, 'table', true), - end = domUtils.findParentByTagName(range.endContainer, 'table', true); - - if (start || end) { - if (start === end) { - start = domUtils.findParentByTagName(range.startContainer, ['td', 'th', 'caption'], true); - end = domUtils.findParentByTagName(range.endContainer, ['td', 'th', 'caption'], true); - if (start !== end) { - me.selection.clearRange() - } - } else { - me.selection.clearRange() - } - } - mousedown = false; - me.document.body.style.webkitUserSelect = ''; - //拖拽状态下的mouseUP - if ( onDrag && dragTd ) { - - me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges'](); - - singleClickState = 0; - dragLine = me.document.getElementById('ue_tableDragLine'); - - // trace 3973 - if (dragLine) { - var dragTdPos = domUtils.getXY(dragTd), - dragLinePos = domUtils.getXY(dragLine); - - switch (onDrag) { - case "h": - changeColWidth(dragTd, dragLinePos.x - dragTdPos.x); - break; - case "v": - changeRowHeight(dragTd, dragLinePos.y - dragTdPos.y - dragTd.offsetHeight); - break; - default: - } - onDrag = ""; - dragTd = null; - - hideDragLine(me); - me.fireEvent('saveScene'); - return; - } - } - //正常状态下的mouseup - if (!startTd) { - var target = domUtils.findParentByTagName(evt.target || evt.srcElement, "td", true); - if (!target) target = domUtils.findParentByTagName(evt.target || evt.srcElement, "th", true); - if (target && (target.tagName == "TD" || target.tagName == "TH")) { - if (me.fireEvent("excludetable", target) === true) return; - range = new dom.Range(me.document); - range.setStart(target, 0).setCursor(false, true); - } - } else { - var ut = getUETable(startTd), - cell = ut ? ut.selectedTds[0] : null; - if (cell) { - range = new dom.Range(me.document); - if (domUtils.isEmptyBlock(cell)) { - range.setStart(cell, 0).setCursor(false, true); - } else { - range.selectNodeContents(cell).shrinkBoundary().setCursor(false, true); - } - } else { - range = me.selection.getRange().shrinkBoundary(); - if (!range.collapsed) { - var start = domUtils.findParentByTagName(range.startContainer, ['td', 'th'], true), - end = domUtils.findParentByTagName(range.endContainer, ['td', 'th'], true); - //在table里边的不能清除 - if (start && !end || !start && end || start && end && start !== end) { - range.setCursor(false, true); - } - } - } - startTd = null; - me.removeListener('mouseover', mouseOverEvent); - } - me._selectionChange(250, evt); - } - - function mouseOverEvent(type, evt) { - - if( isEditorDisabled() ) { - return; - } - - var me = this, - tar = evt.target || evt.srcElement; - currentTd = domUtils.findParentByTagName(tar, "td", true) || domUtils.findParentByTagName(tar, "th", true); - //需要判断两个TD是否位于同一个表格内 - if (startTd && currentTd && - ((startTd.tagName == "TD" && currentTd.tagName == "TD") || (startTd.tagName == "TH" && currentTd.tagName == "TH")) && - domUtils.findParentByTagName(startTd, 'table') == domUtils.findParentByTagName(currentTd, 'table')) { - var ut = getUETable(currentTd); - if (startTd != currentTd) { - me.document.body.style.webkitUserSelect = 'none'; - me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges'](); - var range = ut.getCellsRange(startTd, currentTd); - ut.setSelected(range); - } else { - me.document.body.style.webkitUserSelect = ''; - ut.clearSelected(); - } - - } - evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false); - } - - function setCellHeight(cell, height, backHeight) { - var lineHight = parseInt(domUtils.getComputedStyle(cell, "line-height"), 10), - tmpHeight = backHeight + height; - height = tmpHeight < lineHight ? lineHight : tmpHeight; - if (cell.style.height) cell.style.height = ""; - cell.rowSpan == 1 ? cell.setAttribute("height", height) : (cell.removeAttribute && cell.removeAttribute("height")); - } - - function getWidth(cell) { - if (!cell)return 0; - return parseInt(domUtils.getComputedStyle(cell, "width"), 10); - } - - function changeColWidth(cell, changeValue) { - - var ut = getUETable(cell); - if (ut) { - - //根据当前移动的边框获取相关的单元格 - var table = ut.table, - cells = getCellsByMoveBorder( cell, table ); - - table.style.width = ""; - table.removeAttribute("width"); - - //修正改变量 - changeValue = correctChangeValue( changeValue, cell, cells ); - - if (cell.nextSibling) { - - var i=0; - - utils.each( cells, function( cellGroup ){ - - cellGroup.left.width = (+cellGroup.left.width)+changeValue; - cellGroup.right && ( cellGroup.right.width = (+cellGroup.right.width)-changeValue ); - - } ); - - } else { - - utils.each( cells, function( cellGroup ){ - cellGroup.left.width -= -changeValue; - } ); - - } - } - - } - - function isEditorDisabled() { - return me.body.contentEditable === "false"; - } - - function changeRowHeight(td, changeValue) { - if (Math.abs(changeValue) < 10) return; - var ut = getUETable(td); - if (ut) { - var cells = ut.getSameEndPosCells(td, "y"), - //备份需要连带变化的td的原始高度,否则后期无法获取正确的值 - backHeight = cells[0] ? cells[0].offsetHeight : 0; - for (var i = 0, cell; cell = cells[i++];) { - setCellHeight(cell, changeValue, backHeight); - } - } - - } - - /** - * 获取调整单元格大小的相关单元格 - * @isContainMergeCell 返回的结果中是否包含发生合并后的单元格 - */ - function getCellsByMoveBorder( cell, table, isContainMergeCell ) { - - if( !table ) { - table = domUtils.findParentByTagName( cell, 'table' ); - } - - if( !table ) { - return null; - } - - //获取到该单元格所在行的序列号 - var index = domUtils.getNodeIndex( cell ), - temp = cell, - rows = table.rows, - colIndex = 0; - - while( temp ) { - //获取到当前单元格在未发生单元格合并时的序列 - if( temp.nodeType === 1 ) { - colIndex += (temp.colSpan || 1); - } - temp = temp.previousSibling; - } - - temp = null; - - //记录想关的单元格 - var borderCells = []; - - utils.each(rows, function( tabRow ){ - - var cells = tabRow.cells, - currIndex = 0; - - utils.each( cells, function( tabCell ){ - - currIndex += (tabCell.colSpan || 1); - - if( currIndex === colIndex ) { - - borderCells.push({ - left: tabCell, - right: tabCell.nextSibling || null - }); - - return false; - - } else if( currIndex > colIndex ) { - - if( isContainMergeCell ) { - borderCells.push({ - left: tabCell - }); - } - - return false; - } - - - } ); - - }); - - return borderCells; - - } - - - /** - * 通过给定的单元格集合获取最小的单元格width - */ - function getMinWidthByTableCells( cells ) { - - var minWidth = Number.MAX_VALUE; - - for( var i = 0, curCell; curCell = cells[ i ] ; i++ ) { - - minWidth = Math.min( minWidth, curCell.width || getTableCellWidth( curCell ) ); - - } - - return minWidth; - - } - - function correctChangeValue( changeValue, relatedCell, cells ) { - - //为单元格的paading预留空间 - changeValue -= getTabcellSpace(); - - if( changeValue < 0 ) { - return 0; - } - - changeValue -= getTableCellWidth( relatedCell ); - - //确定方向 - var direction = changeValue < 0 ? 'left':'right'; - - changeValue = Math.abs(changeValue); - - //只关心非最后一个单元格就可以 - utils.each( cells, function( cellGroup ){ - - var curCell = cellGroup[direction]; - - //为单元格保留最小空间 - if( curCell ) { - changeValue = Math.min( changeValue, getTableCellWidth( curCell )-cellMinWidth ); - } - - - } ); - - - //修正越界 - changeValue = changeValue < 0 ? 0 : changeValue; - - return direction === 'left' ? -changeValue : changeValue; - - } - - function getTableCellWidth( cell ) { - - var width = 0, - //偏移纠正量 - offset = 0, - width = cell.offsetWidth - getTabcellSpace(); - - //最后一个节点纠正一下 - if( !cell.nextSibling ) { - - width -= getTableCellOffset( cell ); - - } - - width = width < 0 ? 0 : width; - - try { - cell.width = width; - } catch(e) { - } - - return width; - - } - - /** - * 获取单元格所在表格的最末单元格的偏移量 - */ - function getTableCellOffset( cell ) { - - tab = domUtils.findParentByTagName( cell, "table", false); - - if( tab.offsetVal === undefined ) { - - var prev = cell.previousSibling; - - if( prev ) { - - //最后一个单元格和前一个单元格的width diff结果 如果恰好为一个border width, 则条件成立 - tab.offsetVal = cell.offsetWidth - prev.offsetWidth === UT.borderWidth ? UT.borderWidth : 0; - - } else { - tab.offsetVal = 0; - } - - } - - return tab.offsetVal; - - } - - function getTabcellSpace() { - - if( UT.tabcellSpace === undefined ) { - - var cell = null, - tab = me.document.createElement("table"), - tbody = me.document.createElement("tbody"), - trow = me.document.createElement("tr"), - tabcell = me.document.createElement("td"), - mirror = null; - - tabcell.style.cssText = 'border: 0;'; - tabcell.width = 1; - - trow.appendChild( tabcell ); - trow.appendChild( mirror = tabcell.cloneNode( false ) ); - - tbody.appendChild( trow ); - - tab.appendChild( tbody ); - - tab.style.cssText = "visibility: hidden;"; - - me.body.appendChild( tab ); - - UT.paddingSpace = tabcell.offsetWidth - 1; - - var tmpTabWidth = tab.offsetWidth; - - tabcell.style.cssText = ''; - mirror.style.cssText = ''; - - UT.borderWidth = ( tab.offsetWidth - tmpTabWidth ) / 3; - - UT.tabcellSpace = UT.paddingSpace + UT.borderWidth; - - me.body.removeChild( tab ); - - } - - getTabcellSpace = function(){ return UT.tabcellSpace; }; - - return UT.tabcellSpace; - - } - - function getDragLine(editor, doc) { - if (mousedown)return; - dragLine = editor.document.createElement("div"); - domUtils.setAttributes(dragLine, { - id:"ue_tableDragLine", - unselectable:'on', - contenteditable:false, - 'onresizestart':'return false', - 'ondragstart':'return false', - 'onselectstart':'return false', - style:"background-color:blue;position:absolute;padding:0;margin:0;background-image:none;border:0px none;opacity:0;filter:alpha(opacity=0)" - }); - editor.body.appendChild(dragLine); - } - - function hideDragLine(editor) { - if (mousedown)return; - var line; - while (line = editor.document.getElementById('ue_tableDragLine')) { - domUtils.remove(line) - } - } - - /** - * 依据state(v|h)在cell位置显示横线 - * @param state - * @param cell - */ - function showDragLineAt(state, cell) { - if (!cell) return; - var table = domUtils.findParentByTagName(cell, "table"), - caption = table.getElementsByTagName('caption'), - width = table.offsetWidth, - height = table.offsetHeight - (caption.length > 0 ? caption[0].offsetHeight : 0), - tablePos = domUtils.getXY(table), - cellPos = domUtils.getXY(cell), css; - switch (state) { - case "h": - css = 'height:' + height + 'px;top:' + (tablePos.y + (caption.length > 0 ? caption[0].offsetHeight : 0)) + 'px;left:' + (cellPos.x + cell.offsetWidth); - dragLine.style.cssText = css + 'px;position: absolute;display:block;background-color:blue;width:1px;border:0; color:blue;opacity:.3;filter:alpha(opacity=30)'; - break; - case "v": - css = 'width:' + width + 'px;left:' + tablePos.x + 'px;top:' + (cellPos.y + cell.offsetHeight ); - //必须加上border:0和color:blue,否则低版ie不支持背景色显示 - dragLine.style.cssText = css + 'px;overflow:hidden;position: absolute;display:block;background-color:blue;height:1px;border:0;color:blue;opacity:.2;filter:alpha(opacity=20)'; - break; - default: - } - } - - /** - * 当表格边框颜色为白色时设置为虚线,true为添加虚线 - * @param editor - * @param flag - */ - function switchBorderColor(editor, flag) { - var tableArr = domUtils.getElementsByTagName(editor.body, "table"), color; - for (var i = 0, node; node = tableArr[i++];) { - var td = domUtils.getElementsByTagName(node, "td"); - if (td[0]) { - if (flag) { - color = (td[0].style.borderColor).replace(/\s/g, ""); - if (/(#ffffff)|(rgb\(255,255,255\))/ig.test(color)) - domUtils.addClass(node, "noBorderTable") - } else { - domUtils.removeClasses(node, "noBorderTable") - } - } - - } - } - - function getTableWidth(editor, needIEHack, defaultValue) { - var body = editor.body; - return body.offsetWidth - (needIEHack ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (editor.options.offsetWidth || 0); - } - - /** - * 获取当前拖动的单元格 - */ - function getTargetTd(editor, evt) { - - var target = domUtils.findParentByTagName(evt.target || evt.srcElement, ["td", "th"], true), - dir = null; - - if( !target ) { - return null; - } - - dir = getRelation( target, mouseCoords( evt ) ); - - //如果有前一个节点, 需要做一个修正, 否则可能会得到一个错误的td - - if( !target ) { - return null; - } - - if( dir === 'h1' && target.previousSibling ) { - - var position = domUtils.getXY( target), - cellWidth = target.offsetWidth; - - if( Math.abs( position.x + cellWidth - evt.clientX ) > cellWidth / 3 ) { - target = target.previousSibling; - } - - } else if( dir === 'v1' && target.parentNode.previousSibling ) { - - var position = domUtils.getXY( target), - cellHeight = target.offsetHeight; - - if( Math.abs( position.y + cellHeight - evt.clientY ) > cellHeight / 3 ) { - target = target.parentNode.previousSibling.firstChild; - } - - } - - - //排除了非td内部以及用于代码高亮部分的td - return target && !(editor.fireEvent("excludetable", target) === true) ? target : null; - } - -}; - - -// plugins/table.sort.js -/** - * Created with JetBrains PhpStorm. - * User: Jinqn - * Date: 13-10-12 - * Time: 上午10:20 - * To change this template use File | Settings | File Templates. - */ - -UE.UETable.prototype.sortTable = function (sortByCellIndex, compareFn) { - var table = this.table, - rows = table.rows, - trArray = [], - flag = rows[0].cells[0].tagName === "TH", - lastRowIndex = 0; - if(this.selectedTds.length){ - var range = this.cellsRange, - len = range.endRowIndex + 1; - for (var i = range.beginRowIndex; i < len; i++) { - trArray[i] = rows[i]; - } - trArray.splice(0,range.beginRowIndex); - lastRowIndex = (range.endRowIndex +1) === this.rowsNum ? 0 : range.endRowIndex +1; - }else{ - for (var i = 0,len = rows.length; i < len; i++) { - trArray[i] = rows[i]; - } - } - - var Fn = { - 'reversecurrent': function(td1,td2){ - return 1; - }, - 'orderbyasc': function(td1,td2){ - var value1 = td1.innerText||td1.textContent, - value2 = td2.innerText||td2.textContent; - return value1.localeCompare(value2); - }, - 'reversebyasc': function(td1,td2){ - var value1 = td1.innerHTML, - value2 = td2.innerHTML; - return value2.localeCompare(value1); - }, - 'orderbynum': function(td1,td2){ - var value1 = td1[browser.ie ? 'innerText':'textContent'].match(/\d+/), - value2 = td2[browser.ie ? 'innerText':'textContent'].match(/\d+/); - if(value1) value1 = +value1[0]; - if(value2) value2 = +value2[0]; - return (value1||0) - (value2||0); - }, - 'reversebynum': function(td1,td2){ - var value1 = td1[browser.ie ? 'innerText':'textContent'].match(/\d+/), - value2 = td2[browser.ie ? 'innerText':'textContent'].match(/\d+/); - if(value1) value1 = +value1[0]; - if(value2) value2 = +value2[0]; - return (value2||0) - (value1||0); - } - }; - - //对表格设置排序的标记data-sort-type - table.setAttribute('data-sort-type', compareFn && typeof compareFn === "string" && Fn[compareFn] ? compareFn:''); - - //th不参与排序 - flag && trArray.splice(0, 1); - trArray = utils.sort(trArray,function (tr1, tr2) { - var result; - if (compareFn && typeof compareFn === "function") { - result = compareFn.call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex]); - } else if (compareFn && typeof compareFn === "number") { - result = 1; - } else if (compareFn && typeof compareFn === "string" && Fn[compareFn]) { - result = Fn[compareFn].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex]); - } else { - result = Fn['orderbyasc'].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex]); - } - return result; - }); - var fragment = table.ownerDocument.createDocumentFragment(); - for (var j = 0, len = trArray.length; j < len; j++) { - fragment.appendChild(trArray[j]); - } - var tbody = table.getElementsByTagName("tbody")[0]; - if(!lastRowIndex){ - tbody.appendChild(fragment); - }else{ - tbody.insertBefore(fragment,rows[lastRowIndex- range.endRowIndex + range.beginRowIndex - 1]) - } -}; - -UE.plugins['tablesort'] = function () { - var me = this, - UT = UE.UETable, - getUETable = function (tdOrTable) { - return UT.getUETable(tdOrTable); - }, - getTableItemsByRange = function (editor) { - return UT.getTableItemsByRange(editor); - }; - - - me.ready(function () { - //添加表格可排序的样式 - utils.cssRule('tablesort', - 'table.sortEnabled tr.firstRow th,table.sortEnabled tr.firstRow td{padding-right:20px;background-repeat: no-repeat;background-position: center right;' + - ' background-image:url(' + me.options.themePath + me.options.theme + '/images/sortable.png);}', - me.document); - - //做单元格合并操作时,清除可排序标识 - me.addListener("afterexeccommand", function (type, cmd) { - if( cmd == 'mergeright' || cmd == 'mergedown' || cmd == 'mergecells') { - this.execCommand('disablesort'); - } - }); - }); - - - - //表格排序 - UE.commands['sorttable'] = { - queryCommandState: function () { - var me = this, - tableItems = getTableItemsByRange(me); - if (!tableItems.cell) return -1; - var table = tableItems.table, - cells = table.getElementsByTagName("td"); - for (var i = 0, cell; cell = cells[i++];) { - if (cell.rowSpan != 1 || cell.colSpan != 1) return -1; - } - return 0; - }, - execCommand: function (cmd, fn) { - var me = this, - range = me.selection.getRange(), - bk = range.createBookmark(true), - tableItems = getTableItemsByRange(me), - cell = tableItems.cell, - ut = getUETable(tableItems.table), - cellInfo = ut.getCellInfo(cell); - ut.sortTable(cellInfo.cellIndex, fn); - range.moveToBookmark(bk); - try{ - range.select(); - }catch(e){} - } - }; - - //设置表格可排序,清除表格可排序 - UE.commands["enablesort"] = UE.commands["disablesort"] = { - queryCommandState: function (cmd) { - var table = getTableItemsByRange(this).table; - if(table && cmd=='enablesort') { - var cells = domUtils.getElementsByTagName(table, 'th td'); - for(var i = 0; i1 || cells[i].getAttribute('rowspan')>1) return -1; - } - } - - return !table ? -1: cmd=='enablesort' ^ table.getAttribute('data-sort')!='sortEnabled' ? -1:0; - }, - execCommand: function (cmd) { - var table = getTableItemsByRange(this).table; - table.setAttribute("data-sort", cmd == "enablesort" ? "sortEnabled" : "sortDisabled"); - cmd == "enablesort" ? domUtils.addClass(table,"sortEnabled"):domUtils.removeClasses(table,"sortEnabled"); - } - }; -}; - - -// plugins/contextmenu.js -///import core -///commands 右键菜单 -///commandsName ContextMenu -///commandsTitle 右键菜单 -/** - * 右键菜单 - * @function - * @name baidu.editor.plugins.contextmenu - * @author zhanyi - */ - -UE.plugins['contextmenu'] = function () { - var me = this; - me.setOpt('enableContextMenu',true); - if(me.getOpt('enableContextMenu') === false){ - return; - } - var lang = me.getLang( "contextMenu" ), - menu, - items = me.options.contextMenu || [ - {label:lang['selectall'], cmdName:'selectall'}, - { - label:lang.cleardoc, - cmdName:'cleardoc', - exec:function () { - if ( confirm( lang.confirmclear ) ) { - this.execCommand( 'cleardoc' ); - } - } - }, - '-', - { - label:lang.unlink, - cmdName:'unlink' - }, - '-', - { - group:lang.paragraph, - icon:'justifyjustify', - subMenu:[ - { - label:lang.justifyleft, - cmdName:'justify', - value:'left' - }, - { - label:lang.justifyright, - cmdName:'justify', - value:'right' - }, - { - label:lang.justifycenter, - cmdName:'justify', - value:'center' - }, - { - label:lang.justifyjustify, - cmdName:'justify', - value:'justify' - } - ] - }, - '-', - { - group:lang.table, - icon:'table', - subMenu:[ - { - label:lang.inserttable, - cmdName:'inserttable' - }, - { - label:lang.deletetable, - cmdName:'deletetable' - }, - '-', - { - label:lang.deleterow, - cmdName:'deleterow' - }, - { - label:lang.deletecol, - cmdName:'deletecol' - }, - { - label:lang.insertcol, - cmdName:'insertcol' - }, - { - label:lang.insertcolnext, - cmdName:'insertcolnext' - }, - { - label:lang.insertrow, - cmdName:'insertrow' - }, - { - label:lang.insertrownext, - cmdName:'insertrownext' - }, - '-', - { - label:lang.insertcaption, - cmdName:'insertcaption' - }, - { - label:lang.deletecaption, - cmdName:'deletecaption' - }, - { - label:lang.inserttitle, - cmdName:'inserttitle' - }, - { - label:lang.deletetitle, - cmdName:'deletetitle' - }, - { - label:lang.inserttitlecol, - cmdName:'inserttitlecol' - }, - { - label:lang.deletetitlecol, - cmdName:'deletetitlecol' - }, - '-', - { - label:lang.mergecells, - cmdName:'mergecells' - }, - { - label:lang.mergeright, - cmdName:'mergeright' - }, - { - label:lang.mergedown, - cmdName:'mergedown' - }, - '-', - { - label:lang.splittorows, - cmdName:'splittorows' - }, - { - label:lang.splittocols, - cmdName:'splittocols' - }, - { - label:lang.splittocells, - cmdName:'splittocells' - }, - '-', - { - label:lang.averageDiseRow, - cmdName:'averagedistributerow' - }, - { - label:lang.averageDisCol, - cmdName:'averagedistributecol' - }, - '-', - { - label:lang.edittd, - cmdName:'edittd', - exec:function () { - if ( UE.ui['edittd'] ) { - new UE.ui['edittd']( this ); - } - this.getDialog('edittd').open(); - } - }, - { - label:lang.edittable, - cmdName:'edittable', - exec:function () { - if ( UE.ui['edittable'] ) { - new UE.ui['edittable']( this ); - } - this.getDialog('edittable').open(); - } - }, - { - label:lang.setbordervisible, - cmdName:'setbordervisible' - } - ] - }, - { - group:lang.tablesort, - icon:'tablesort', - subMenu:[ - { - label:lang.enablesort, - cmdName:'enablesort' - }, - { - label:lang.disablesort, - cmdName:'disablesort' - }, - '-', - { - label:lang.reversecurrent, - cmdName:'sorttable', - value:'reversecurrent' - }, - { - label:lang.orderbyasc, - cmdName:'sorttable', - value:'orderbyasc' - }, - { - label:lang.reversebyasc, - cmdName:'sorttable', - value:'reversebyasc' - }, - { - label:lang.orderbynum, - cmdName:'sorttable', - value:'orderbynum' - }, - { - label:lang.reversebynum, - cmdName:'sorttable', - value:'reversebynum' - } - ] - }, - { - group:lang.borderbk, - icon:'borderBack', - subMenu:[ - { - label:lang.setcolor, - cmdName:"interlacetable", - exec:function(){ - this.execCommand("interlacetable"); - } - }, - { - label:lang.unsetcolor, - cmdName:"uninterlacetable", - exec:function(){ - this.execCommand("uninterlacetable"); - } - }, - { - label:lang.setbackground, - cmdName:"settablebackground", - exec:function(){ - this.execCommand("settablebackground",{repeat:true,colorList:["#bbb","#ccc"]}); - } - }, - { - label:lang.unsetbackground, - cmdName:"cleartablebackground", - exec:function(){ - this.execCommand("cleartablebackground"); - } - }, - { - label:lang.redandblue, - cmdName:"settablebackground", - exec:function(){ - this.execCommand("settablebackground",{repeat:true,colorList:["red","blue"]}); - } - }, - { - label:lang.threecolorgradient, - cmdName:"settablebackground", - exec:function(){ - this.execCommand("settablebackground",{repeat:true,colorList:["#aaa","#bbb","#ccc"]}); - } - } - ] - }, - { - group:lang.aligntd, - icon:'aligntd', - subMenu:[ - { - cmdName:'cellalignment', - value:{align:'left',vAlign:'top'} - }, - { - cmdName:'cellalignment', - value:{align:'center',vAlign:'top'} - }, - { - cmdName:'cellalignment', - value:{align:'right',vAlign:'top'} - }, - { - cmdName:'cellalignment', - value:{align:'left',vAlign:'middle'} - }, - { - cmdName:'cellalignment', - value:{align:'center',vAlign:'middle'} - }, - { - cmdName:'cellalignment', - value:{align:'right',vAlign:'middle'} - }, - { - cmdName:'cellalignment', - value:{align:'left',vAlign:'bottom'} - }, - { - cmdName:'cellalignment', - value:{align:'center',vAlign:'bottom'} - }, - { - cmdName:'cellalignment', - value:{align:'right',vAlign:'bottom'} - } - ] - }, - { - group:lang.aligntable, - icon:'aligntable', - subMenu:[ - { - cmdName:'tablealignment', - className: 'left', - label:lang.tableleft, - value:"left" - }, - { - cmdName:'tablealignment', - className: 'center', - label:lang.tablecenter, - value:"center" - }, - { - cmdName:'tablealignment', - className: 'right', - label:lang.tableright, - value:"right" - } - ] - }, - '-', - { - label:lang.insertparagraphbefore, - cmdName:'insertparagraph', - value:true - }, - { - label:lang.insertparagraphafter, - cmdName:'insertparagraph' - }, - { - label:lang['copy'], - cmdName:'copy' - }, - { - label:lang['paste'], - cmdName:'paste' - } - ]; - if ( !items.length ) { - return; - } - var uiUtils = UE.ui.uiUtils; - - me.addListener( 'contextmenu', function ( type, evt ) { - - var offset = uiUtils.getViewportOffsetByEvent( evt ); - me.fireEvent( 'beforeselectionchange' ); - if ( menu ) { - menu.destroy(); - } - for ( var i = 0, ti, contextItems = []; ti = items[i]; i++ ) { - var last; - (function ( item ) { - if ( item == '-' ) { - if ( (last = contextItems[contextItems.length - 1 ] ) && last !== '-' ) { - contextItems.push( '-' ); - } - } else if ( item.hasOwnProperty( "group" ) ) { - for ( var j = 0, cj, subMenu = []; cj = item.subMenu[j]; j++ ) { - (function ( subItem ) { - if ( subItem == '-' ) { - if ( (last = subMenu[subMenu.length - 1 ] ) && last !== '-' ) { - subMenu.push( '-' ); - }else{ - subMenu.splice(subMenu.length-1); - } - } else { - if ( (me.commands[subItem.cmdName] || UE.commands[subItem.cmdName] || subItem.query) && - (subItem.query ? subItem.query() : me.queryCommandState( subItem.cmdName )) > -1 ) { - subMenu.push( { - 'label':subItem.label || me.getLang( "contextMenu." + subItem.cmdName + (subItem.value || '') )||"", - 'className':'edui-for-' +subItem.cmdName + ( subItem.className ? ( ' edui-for-' + subItem.cmdName + '-' + subItem.className ) : '' ), - onclick:subItem.exec ? function () { - subItem.exec.call( me ); - } : function () { - me.execCommand( subItem.cmdName, subItem.value ); - } - } ); - } - } - })( cj ); - } - if ( subMenu.length ) { - function getLabel(){ - switch (item.icon){ - case "table": - return me.getLang( "contextMenu.table" ); - case "justifyjustify": - return me.getLang( "contextMenu.paragraph" ); - case "aligntd": - return me.getLang("contextMenu.aligntd"); - case "aligntable": - return me.getLang("contextMenu.aligntable"); - case "tablesort": - return lang.tablesort; - case "borderBack": - return lang.borderbk; - default : - return ''; - } - } - contextItems.push( { - //todo 修正成自动获取方式 - 'label':getLabel(), - className:'edui-for-' + item.icon, - 'subMenu':{ - items:subMenu, - editor:me - } - } ); - } - - } else { - //有可能commmand没有加载右键不能出来,或者没有command也想能展示出来添加query方法 - if ( (me.commands[item.cmdName] || UE.commands[item.cmdName] || item.query) && - (item.query ? item.query.call(me) : me.queryCommandState( item.cmdName )) > -1 ) { - - contextItems.push( { - 'label':item.label || me.getLang( "contextMenu." + item.cmdName ), - className:'edui-for-' + (item.icon ? item.icon : item.cmdName + (item.value || '')), - onclick:item.exec ? function () { - item.exec.call( me ); - } : function () { - me.execCommand( item.cmdName, item.value ); - } - } ); - } - - } - - })( ti ); - } - if ( contextItems[contextItems.length - 1] == '-' ) { - contextItems.pop(); - } - - menu = new UE.ui.Menu( { - items:contextItems, - className:"edui-contextmenu", - editor:me - } ); - menu.render(); - menu.showAt( offset ); - - me.fireEvent("aftershowcontextmenu",menu); - - domUtils.preventDefault( evt ); - if ( browser.ie ) { - var ieRange; - try { - ieRange = me.selection.getNative().createRange(); - } catch ( e ) { - return; - } - if ( ieRange.item ) { - var range = new dom.Range( me.document ); - range.selectNode( ieRange.item( 0 ) ).select( true, true ); - } - } - }); - - // 添加复制的flash按钮 - me.addListener('aftershowcontextmenu', function(type, menu) { - if (me.zeroclipboard) { - var items = menu.items; - for (var key in items) { - if (items[key].className == 'edui-for-copy') { - me.zeroclipboard.clip(items[key].getDom()); - } - } - } - }); - -}; - - -// plugins/shortcutmenu.js -///import core -///commands 弹出菜单 -// commandsName popupmenu -///commandsTitle 弹出菜单 -/** - * 弹出菜单 - * @function - * @name baidu.editor.plugins.popupmenu - * @author xuheng - */ - -UE.plugins['shortcutmenu'] = function () { - var me = this, - menu, - items = me.options.shortcutMenu || []; - - if (!items.length) { - return; - } - - me.addListener ('contextmenu mouseup' , function (type , e) { - var me = this, - customEvt = { - type : type , - target : e.target || e.srcElement , - screenX : e.screenX , - screenY : e.screenY , - clientX : e.clientX , - clientY : e.clientY - }; - - setTimeout (function () { - var rng = me.selection.getRange (); - if (rng.collapsed === false || type == "contextmenu") { - - if (!menu) { - menu = new baidu.editor.ui.ShortCutMenu ({ - editor : me , - items : items , - theme : me.options.theme , - className : 'edui-shortcutmenu' - }); - - menu.render (); - me.fireEvent ("afterrendershortcutmenu" , menu); - } - - menu.show (customEvt , !!UE.plugins['contextmenu']); - } - }); - - if (type == 'contextmenu') { - domUtils.preventDefault (e); - if (browser.ie9below) { - var ieRange; - try { - ieRange = me.selection.getNative().createRange(); - } catch (e) { - return; - } - if (ieRange.item) { - var range = new dom.Range (me.document); - range.selectNode (ieRange.item (0)).select (true , true); - - } - } - } - }); - - me.addListener ('keydown' , function (type) { - if (type == "keydown") { - menu && !menu.isHidden && menu.hide (); - } - - }); - -}; - - - - -// plugins/basestyle.js -/** - * B、I、sub、super命令支持 - * @file - * @since 1.2.6.1 - */ - -UE.plugins['basestyle'] = function(){ - - /** - * 字体加粗 - * @command bold - * @param { String } cmd 命令字符串 - * @remind 对已加粗的文本内容执行该命令, 将取消加粗 - * @method execCommand - * @example - * ```javascript - * //editor是编辑器实例 - * //对当前选中的文本内容执行加粗操作 - * //第一次执行, 文本内容加粗 - * editor.execCommand( 'bold' ); - * - * //第二次执行, 文本内容取消加粗 - * editor.execCommand( 'bold' ); - * ``` - */ - - - /** - * 字体倾斜 - * @command italic - * @method execCommand - * @param { String } cmd 命令字符串 - * @remind 对已倾斜的文本内容执行该命令, 将取消倾斜 - * @example - * ```javascript - * //editor是编辑器实例 - * //对当前选中的文本内容执行斜体操作 - * //第一次操作, 文本内容将变成斜体 - * editor.execCommand( 'italic' ); - * - * //再次对同一文本内容执行, 则文本内容将恢复正常 - * editor.execCommand( 'italic' ); - * ``` - */ - - /** - * 下标文本,与“superscript”命令互斥 - * @command subscript - * @method execCommand - * @remind 把选中的文本内容切换成下标文本, 如果当前选中的文本已经是下标, 则该操作会把文本内容还原成正常文本 - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * //editor是编辑器实例 - * //对当前选中的文本内容执行下标操作 - * //第一次操作, 文本内容将变成下标文本 - * editor.execCommand( 'subscript' ); - * - * //再次对同一文本内容执行, 则文本内容将恢复正常 - * editor.execCommand( 'subscript' ); - * ``` - */ - - /** - * 上标文本,与“subscript”命令互斥 - * @command superscript - * @method execCommand - * @remind 把选中的文本内容切换成上标文本, 如果当前选中的文本已经是上标, 则该操作会把文本内容还原成正常文本 - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * //editor是编辑器实例 - * //对当前选中的文本内容执行上标操作 - * //第一次操作, 文本内容将变成上标文本 - * editor.execCommand( 'superscript' ); - * - * //再次对同一文本内容执行, 则文本内容将恢复正常 - * editor.execCommand( 'superscript' ); - * ``` - */ - var basestyles = { - 'bold':['strong','b'], - 'italic':['em','i'], - 'subscript':['sub'], - 'superscript':['sup'] - }, - getObj = function(editor,tagNames){ - return domUtils.filterNodeList(editor.selection.getStartElementPath(),tagNames); - }, - me = this; - //添加快捷键 - me.addshortcutkey({ - "Bold" : "ctrl+66",//^B - "Italic" : "ctrl+73", //^I - "Underline" : "ctrl+85"//^U - }); - me.addInputRule(function(root){ - utils.each(root.getNodesByTagName('b i'),function(node){ - switch (node.tagName){ - case 'b': - node.tagName = 'strong'; - break; - case 'i': - node.tagName = 'em'; - } - }); - }); - for ( var style in basestyles ) { - (function( cmd, tagNames ) { - me.commands[cmd] = { - execCommand : function( cmdName ) { - var range = me.selection.getRange(),obj = getObj(this,tagNames); - if ( range.collapsed ) { - if ( obj ) { - var tmpText = me.document.createTextNode(''); - range.insertNode( tmpText ).removeInlineStyle( tagNames ); - range.setStartBefore(tmpText); - domUtils.remove(tmpText); - } else { - var tmpNode = range.document.createElement( tagNames[0] ); - if(cmdName == 'superscript' || cmdName == 'subscript'){ - tmpText = me.document.createTextNode(''); - range.insertNode(tmpText) - .removeInlineStyle(['sub','sup']) - .setStartBefore(tmpText) - .collapse(true); - } - range.insertNode( tmpNode ).setStart( tmpNode, 0 ); - } - range.collapse( true ); - } else { - if(cmdName == 'superscript' || cmdName == 'subscript'){ - if(!obj || obj.tagName.toLowerCase() != cmdName){ - range.removeInlineStyle(['sub','sup']); - } - } - obj ? range.removeInlineStyle( tagNames ) : range.applyInlineStyle( tagNames[0] ); - } - range.select(); - }, - queryCommandState : function() { - return getObj(this,tagNames) ? 1 : 0; - } - }; - })( style, basestyles[style] ); - } -}; - - - -// plugins/elementpath.js -/** - * 选取路径命令 - * @file - */ -UE.plugins['elementpath'] = function(){ - var currentLevel, - tagNames, - me = this; - me.setOpt('elementPathEnabled',true); - if(!me.options.elementPathEnabled){ - return; - } - me.commands['elementpath'] = { - execCommand : function( cmdName, level ) { - var start = tagNames[level], - range = me.selection.getRange(); - currentLevel = level*1; - range.selectNode(start).select(); - }, - queryCommandValue : function() { - //产生一个副本,不能修改原来的startElementPath; - var parents = [].concat(this.selection.getStartElementPath()).reverse(), - names = []; - tagNames = parents; - for(var i=0,ci;ci=parents[i];i++){ - if(ci.nodeType == 3) { - continue; - } - var name = ci.tagName.toLowerCase(); - if(name == 'img' && ci.getAttribute('anchorname')){ - name = 'anchor'; - } - names[i] = name; - if(currentLevel == i){ - currentLevel = -1; - break; - } - } - return names; - } - }; -}; - - - -// plugins/formatmatch.js -/** - * 格式刷,只格式inline的 - * @file - * @since 1.2.6.1 - */ - -/** - * 格式刷 - * @command formatmatch - * @method execCommand - * @remind 该操作不能复制段落格式 - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * //editor是编辑器实例 - * //获取格式刷 - * editor.execCommand( 'formatmatch' ); - * ``` - */ -UE.plugins['formatmatch'] = function(){ - - var me = this, - list = [],img, - flag = 0; - - me.addListener('reset',function(){ - list = []; - flag = 0; - }); - - function addList(type,evt){ - - if(browser.webkit){ - var target = evt.target.tagName == 'IMG' ? evt.target : null; - } - - function addFormat(range){ - - if(text){ - range.selectNode(text); - } - return range.applyInlineStyle(list[list.length-1].tagName,null,list); - - } - - me.undoManger && me.undoManger.save(); - - var range = me.selection.getRange(), - imgT = target || range.getClosedNode(); - if(img && imgT && imgT.tagName == 'IMG'){ - //trace:964 - - imgT.style.cssText += ';float:' + (img.style.cssFloat || img.style.styleFloat ||'none') + ';display:' + (img.style.display||'inline'); - - img = null; - }else{ - if(!img){ - var collapsed = range.collapsed; - if(collapsed){ - var text = me.document.createTextNode('match'); - range.insertNode(text).select(); - - - } - me.__hasEnterExecCommand = true; - //不能把block上的属性干掉 - //trace:1553 - var removeFormatAttributes = me.options.removeFormatAttributes; - me.options.removeFormatAttributes = ''; - me.execCommand('removeformat'); - me.options.removeFormatAttributes = removeFormatAttributes; - me.__hasEnterExecCommand = false; - //trace:969 - range = me.selection.getRange(); - if(list.length){ - addFormat(range); - } - if(text){ - range.setStartBefore(text).collapse(true); - - } - range.select(); - text && domUtils.remove(text); - } - - } - - - - - me.undoManger && me.undoManger.save(); - me.removeListener('mouseup',addList); - flag = 0; - } - - me.commands['formatmatch'] = { - execCommand : function( cmdName ) { - - if(flag){ - flag = 0; - list = []; - me.removeListener('mouseup',addList); - return; - } - - - - var range = me.selection.getRange(); - img = range.getClosedNode(); - if(!img || img.tagName != 'IMG'){ - range.collapse(true).shrinkBoundary(); - var start = range.startContainer; - list = domUtils.findParents(start,true,function(node){ - return !domUtils.isBlockElm(node) && node.nodeType == 1; - }); - //a不能加入格式刷, 并且克隆节点 - for(var i=0,ci;ci=list[i];i++){ - if(ci.tagName == 'A'){ - list.splice(i,1); - break; - } - } - - } - - me.addListener('mouseup',addList); - flag = 1; - - - }, - queryCommandState : function() { - return flag; - }, - notNeedUndo : 1 - }; -}; - - - -// plugins/searchreplace.js -///import core -///commands 查找替换 -///commandsName SearchReplace -///commandsTitle 查询替换 -///commandsDialog dialogs\searchreplace -/** - * @description 查找替换 - * @author zhanyi - */ - -UE.plugin.register('searchreplace',function(){ - var me = this; - - var _blockElm = {'table':1,'tbody':1,'tr':1,'ol':1,'ul':1}; - - function findTextInString(textContent,opt,currentIndex){ - var str = opt.searchStr; - if(opt.dir == -1){ - textContent = textContent.split('').reverse().join(''); - str = str.split('').reverse().join(''); - currentIndex = textContent.length - currentIndex; - - } - var reg = new RegExp(str,'g' + (opt.casesensitive ? '' : 'i')),match; - - while(match = reg.exec(textContent)){ - if(match.index >= currentIndex){ - return opt.dir == -1 ? textContent.length - match.index - opt.searchStr.length : match.index; - } - } - return -1 - } - function findTextBlockElm(node,currentIndex,opt){ - var textContent,index,methodName = opt.all || opt.dir == 1 ? 'getNextDomNode' : 'getPreDomNode'; - if(domUtils.isBody(node)){ - node = node.firstChild; - } - var first = 1; - while(node){ - textContent = node.nodeType == 3 ? node.nodeValue : node[browser.ie ? 'innerText' : 'textContent']; - index = findTextInString(textContent,opt,currentIndex ); - first = 0; - if(index!=-1){ - return { - 'node':node, - 'index':index - } - } - node = domUtils[methodName](node); - while(node && _blockElm[node.nodeName.toLowerCase()]){ - node = domUtils[methodName](node,true); - } - if(node){ - currentIndex = opt.dir == -1 ? (node.nodeType == 3 ? node.nodeValue : node[browser.ie ? 'innerText' : 'textContent']).length : 0; - } - - } - } - function findNTextInBlockElm(node,index,str){ - var currentIndex = 0, - currentNode = node.firstChild, - currentNodeLength = 0, - result; - while(currentNode){ - if(currentNode.nodeType == 3){ - currentNodeLength = currentNode.nodeValue.replace(/(^[\t\r\n]+)|([\t\r\n]+$)/,'').length; - currentIndex += currentNodeLength; - if(currentIndex >= index){ - return { - 'node':currentNode, - 'index': currentNodeLength - (currentIndex - index) - } - } - }else if(!dtd.$empty[currentNode.tagName]){ - currentNodeLength = currentNode[browser.ie ? 'innerText' : 'textContent'].replace(/(^[\t\r\n]+)|([\t\r\n]+$)/,'').length - currentIndex += currentNodeLength; - if(currentIndex >= index){ - result = findNTextInBlockElm(currentNode,currentNodeLength - (currentIndex - index),str); - if(result){ - return result; - } - } - } - currentNode = domUtils.getNextDomNode(currentNode); - - } - } - - function searchReplace(me,opt){ - - var rng = me.selection.getRange(), - startBlockNode, - searchStr = opt.searchStr, - span = me.document.createElement('span'); - span.innerHTML = '$$ueditor_searchreplace_key$$'; - - rng.shrinkBoundary(true); - - //判断是不是第一次选中 - if(!rng.collapsed){ - rng.select(); - var rngText = me.selection.getText(); - if(new RegExp('^' + opt.searchStr + '$',(opt.casesensitive ? '' : 'i')).test(rngText)){ - if(opt.replaceStr != undefined){ - replaceText(rng,opt.replaceStr); - rng.select(); - return true; - }else{ - rng.collapse(opt.dir == -1) - } - - } - } - - - rng.insertNode(span); - rng.enlargeToBlockElm(true); - startBlockNode = rng.startContainer; - var currentIndex = startBlockNode[browser.ie ? 'innerText' : 'textContent'].indexOf('$$ueditor_searchreplace_key$$'); - rng.setStartBefore(span); - domUtils.remove(span); - var result = findTextBlockElm(startBlockNode,currentIndex,opt); - if(result){ - var rngStart = findNTextInBlockElm(result.node,result.index,searchStr); - var rngEnd = findNTextInBlockElm(result.node,result.index + searchStr.length,searchStr); - rng.setStart(rngStart.node,rngStart.index).setEnd(rngEnd.node,rngEnd.index); - - if(opt.replaceStr !== undefined){ - replaceText(rng,opt.replaceStr) - } - rng.select(); - return true; - }else{ - rng.setCursor() - } - - } - function replaceText(rng,str){ - - str = me.document.createTextNode(str); - rng.deleteContents().insertNode(str); - - } - return { - commands:{ - 'searchreplace':{ - execCommand:function(cmdName,opt){ - utils.extend(opt,{ - all : false, - casesensitive : false, - dir : 1 - },true); - var num = 0; - if(opt.all){ - - var rng = me.selection.getRange(), - first = me.body.firstChild; - if(first && first.nodeType == 1){ - rng.setStart(first,0); - rng.shrinkBoundary(true); - }else if(first.nodeType == 3){ - rng.setStartBefore(first) - } - rng.collapse(true).select(true); - if(opt.replaceStr !== undefined){ - me.fireEvent('saveScene'); - } - while(searchReplace(this,opt)){ - num++; - } - if(num){ - me.fireEvent('saveScene'); - } - }else{ - if(opt.replaceStr !== undefined){ - me.fireEvent('saveScene'); - } - if(searchReplace(this,opt)){ - num++ - } - if(num){ - me.fireEvent('saveScene'); - } - - } - - return num; - }, - notNeedUndo:1 - } - } - } -}); - -// plugins/customstyle.js -/** - * 自定义样式 - * @file - * @since 1.2.6.1 - */ - -/** - * 根据config配置文件里“customstyle”选项的值对匹配的标签执行样式替换。 - * @command customstyle - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand( 'customstyle' ); - * ``` - */ -UE.plugins['customstyle'] = function() { - var me = this; - me.setOpt({ 'customstyle':[ - {tag:'h1',name:'tc', style:'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:center;margin:0 0 20px 0;'}, - {tag:'h1',name:'tl', style:'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:left;margin:0 0 10px 0;'}, - {tag:'span',name:'im', style:'font-size:16px;font-style:italic;font-weight:bold;line-height:18px;'}, - {tag:'span',name:'hi', style:'font-size:16px;font-style:italic;font-weight:bold;color:rgb(51, 153, 204);line-height:18px;'} - ]}); - me.commands['customstyle'] = { - execCommand : function(cmdName, obj) { - var me = this, - tagName = obj.tag, - node = domUtils.findParent(me.selection.getStart(), function(node) { - return node.getAttribute('label'); - }, true), - range,bk,tmpObj = {}; - for (var p in obj) { - if(obj[p]!==undefined) - tmpObj[p] = obj[p]; - } - delete tmpObj.tag; - if (node && node.getAttribute('label') == obj.label) { - range = this.selection.getRange(); - bk = range.createBookmark(); - if (range.collapsed) { - //trace:1732 删掉自定义标签,要有p来回填站位 - if(dtd.$block[node.tagName]){ - var fillNode = me.document.createElement('p'); - domUtils.moveChild(node, fillNode); - node.parentNode.insertBefore(fillNode, node); - domUtils.remove(node); - }else{ - domUtils.remove(node,true); - } - - } else { - - var common = domUtils.getCommonAncestor(bk.start, bk.end), - nodes = domUtils.getElementsByTagName(common, tagName); - if(new RegExp(tagName,'i').test(common.tagName)){ - nodes.push(common); - } - for (var i = 0,ni; ni = nodes[i++];) { - if (ni.getAttribute('label') == obj.label) { - var ps = domUtils.getPosition(ni, bk.start),pe = domUtils.getPosition(ni, bk.end); - if ((ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS) - && - (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS) - ) - if (dtd.$block[tagName]) { - var fillNode = me.document.createElement('p'); - domUtils.moveChild(ni, fillNode); - ni.parentNode.insertBefore(fillNode, ni); - } - domUtils.remove(ni, true); - } - } - node = domUtils.findParent(common, function(node) { - return node.getAttribute('label') == obj.label; - }, true); - if (node) { - - domUtils.remove(node, true); - - } - - } - range.moveToBookmark(bk).select(); - } else { - if (dtd.$block[tagName]) { - this.execCommand('paragraph', tagName, tmpObj,'customstyle'); - range = me.selection.getRange(); - if (!range.collapsed) { - range.collapse(); - node = domUtils.findParent(me.selection.getStart(), function(node) { - return node.getAttribute('label') == obj.label; - }, true); - var pNode = me.document.createElement('p'); - domUtils.insertAfter(node, pNode); - domUtils.fillNode(me.document, pNode); - range.setStart(pNode, 0).setCursor(); - } - } else { - - range = me.selection.getRange(); - if (range.collapsed) { - node = me.document.createElement(tagName); - domUtils.setAttributes(node, tmpObj); - range.insertNode(node).setStart(node, 0).setCursor(); - - return; - } - - bk = range.createBookmark(); - range.applyInlineStyle(tagName, tmpObj).moveToBookmark(bk).select(); - } - } - - }, - queryCommandValue : function() { - var parent = domUtils.filterNodeList( - this.selection.getStartElementPath(), - function(node){return node.getAttribute('label')} - ); - return parent ? parent.getAttribute('label') : ''; - } - }; - //当去掉customstyle是,如果是块元素,用p代替 - me.addListener('keyup', function(type, evt) { - var keyCode = evt.keyCode || evt.which; - - if (keyCode == 32 || keyCode == 13) { - var range = me.selection.getRange(); - if (range.collapsed) { - var node = domUtils.findParent(me.selection.getStart(), function(node) { - return node.getAttribute('label'); - }, true); - if (node && dtd.$block[node.tagName] && domUtils.isEmptyNode(node)) { - var p = me.document.createElement('p'); - domUtils.insertAfter(node, p); - domUtils.fillNode(me.document, p); - domUtils.remove(node); - range.setStart(p, 0).setCursor(); - - - } - } - } - }); -}; - -// plugins/catchremoteimage.js -///import core -///commands 远程图片抓取 -///commandsName catchRemoteImage,catchremoteimageenable -///commandsTitle 远程图片抓取 -/** - * 远程图片抓取,当开启本插件时所有不符合本地域名的图片都将被抓取成为本地服务器上的图片 - */ -UE.plugins['catchremoteimage'] = function () { - var me = this, - ajax = UE.ajax; - - /* 设置默认值 */ - if (me.options.catchRemoteImageEnable === false) return; - me.setOpt({ - catchRemoteImageEnable: false - }); - - me.addListener("afterpaste", function () { - me.fireEvent("catchRemoteImage"); - }); - - me.addListener("catchRemoteImage", function () { - - var catcherLocalDomain = me.getOpt('catcherLocalDomain'), - catcherActionUrl = me.getActionUrl(me.getOpt('catcherActionName')), - catcherUrlPrefix = me.getOpt('catcherUrlPrefix'), - catcherFieldName = me.getOpt('catcherFieldName'); - - var remoteImages = [], - imgs = domUtils.getElementsByTagName(me.document, "img"), - test = function (src, urls) { - if (src.indexOf(location.host) != -1 || /(^\.)|(^\/)/.test(src)) { - return true; - } - if (urls) { - for (var j = 0, url; url = urls[j++];) { - if (src.indexOf(url) !== -1) { - return true; - } - } - } - return false; - }; - - for (var i = 0, ci; ci = imgs[i++];) { - if (ci.getAttribute("word_img")) { - continue; - } - var src = ci.getAttribute("_src") || ci.src || ""; - if (/^(https?|ftp):/i.test(src) && !test(src, catcherLocalDomain)) { - remoteImages.push(src); - } - } - - if (remoteImages.length) { - catchremoteimage(remoteImages, { - //成功抓取 - success: function (r) { - try { - var info = r.state !== undefined ? r:eval("(" + r.responseText + ")"); - } catch (e) { - return; - } - - /* 获取源路径和新路径 */ - var i, j, ci, cj, oldSrc, newSrc, list = info.list; - - for (i = 0; ci = imgs[i++];) { - oldSrc = ci.getAttribute("_src") || ci.src || ""; - for (j = 0; cj = list[j++];) { - if (oldSrc == cj.source && cj.state == "SUCCESS") { //抓取失败时不做替换处理 - newSrc = catcherUrlPrefix + cj.url; - domUtils.setAttributes(ci, { - "src": newSrc, - "_src": newSrc - }); - break; - } - } - } - me.fireEvent('catchremotesuccess') - }, - //回调失败,本次请求超时 - error: function () { - me.fireEvent("catchremoteerror"); - } - }); - } - - function catchremoteimage(imgs, callbacks) { - var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '', - url = utils.formatUrl(catcherActionUrl + (catcherActionUrl.indexOf('?') == -1 ? '?':'&') + params), - isJsonp = utils.isCrossDomainUrl(url), - opt = { - 'method': 'POST', - 'dataType': isJsonp ? 'jsonp':'', - 'timeout': 60000, //单位:毫秒,回调请求超时设置。目标用户如果网速不是很快的话此处建议设置一个较大的数值 - 'onsuccess': callbacks["success"], - 'onerror': callbacks["error"] - }; - opt[catcherFieldName] = imgs; - ajax.request(url, opt); - } - - }); -}; - -// plugins/snapscreen.js -/** - * 截屏插件,为UEditor提供插入支持 - * @file - * @since 1.4.2 - */ -UE.plugin.register('snapscreen', function (){ - - var me = this; - var snapplugin; - - function getLocation(url){ - var search, - a = document.createElement('a'), - params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''; - - a.href = url; - if (browser.ie) { - a.href = a.href; - } - - - search = a.search; - if (params) { - search = search + (search.indexOf('?') == -1 ? '?':'&')+ params; - search = search.replace(/[&]+/ig, '&'); - } - return { - 'port': a.port, - 'hostname': a.hostname, - 'path': a.pathname + search || + a.hash - } - } - - return { - commands:{ - /** - * 字体背景颜色 - * @command snapscreen - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand('snapscreen'); - * ``` - */ - 'snapscreen':{ - execCommand:function (cmd) { - var url, local, res; - var lang = me.getLang("snapScreen_plugin"); - - if(!snapplugin){ - var container = me.container; - var doc = me.container.ownerDocument || me.container.document; - snapplugin = doc.createElement("object"); - try{snapplugin.type = "application/x-pluginbaidusnap";}catch(e){ - return; - } - snapplugin.style.cssText = "position:absolute;left:-9999px;width:0;height:0;"; - snapplugin.setAttribute("width","0"); - snapplugin.setAttribute("height","0"); - container.appendChild(snapplugin); - } - - function onSuccess(rs){ - try{ - rs = eval("("+ rs +")"); - if(rs.state == 'SUCCESS'){ - var opt = me.options; - me.execCommand('insertimage', { - src: opt.snapscreenUrlPrefix + rs.url, - _src: opt.snapscreenUrlPrefix + rs.url, - alt: rs.title || '', - floatStyle: opt.snapscreenImgAlign - }); - } else { - alert(rs.state); - } - }catch(e){ - alert(lang.callBackErrorMsg); - } - } - url = me.getActionUrl(me.getOpt('snapscreenActionName')); - local = getLocation(url); - setTimeout(function () { - try{ - res =snapplugin.saveSnapshot(local.hostname, local.path, local.port); - }catch(e){ - me.ui._dialogs['snapscreenDialog'].open(); - return; - } - - onSuccess(res); - }, 50); - }, - queryCommandState: function(){ - return (navigator.userAgent.indexOf("Windows",0) != -1) ? 0:-1; - } - } - } - } -}); - - -// plugins/insertparagraph.js -/** - * 插入段落 - * @file - * @since 1.2.6.1 - */ - - -/** - * 插入段落 - * @command insertparagraph - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * //editor是编辑器实例 - * editor.execCommand( 'insertparagraph' ); - * ``` - */ - -UE.commands['insertparagraph'] = { - execCommand : function( cmdName,front) { - var me = this, - range = me.selection.getRange(), - start = range.startContainer,tmpNode; - while(start ){ - if(domUtils.isBody(start)){ - break; - } - tmpNode = start; - start = start.parentNode; - } - if(tmpNode){ - var p = me.document.createElement('p'); - if(front){ - tmpNode.parentNode.insertBefore(p,tmpNode) - }else{ - tmpNode.parentNode.insertBefore(p,tmpNode.nextSibling) - } - domUtils.fillNode(me.document,p); - range.setStart(p,0).setCursor(false,true); - } - } -}; - - - -// plugins/webapp.js -/** - * 百度应用 - * @file - * @since 1.2.6.1 - */ - - -/** - * 插入百度应用 - * @command webapp - * @method execCommand - * @remind 需要百度APPKey - * @remind 百度应用主页: http://app.baidu.com/ - * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度, - * height=>应用容器高度,logo=>应用logo,url=>应用地址 - * @example - * ```javascript - * //editor是编辑器实例 - * //在编辑器里插入一个“植物大战僵尸”的APP - * editor.execCommand( 'webapp' , { - * title: '植物大战僵尸', - * width: 560, - * height: 465, - * logo: '应用展示的图片', - * url: '百度应用的地址' - * } ); - * ``` - */ - -//UE.plugins['webapp'] = function () { -// var me = this; -// function createInsertStr( obj, toIframe, addParagraph ) { -// return !toIframe ? -// (addParagraph ? '

                  ' : '') + '' + -// (addParagraph ? '

                  ' : '') -// : -// ''; -// } -// -// function switchImgAndIframe( img2frame ) { -// var tmpdiv, -// nodes = domUtils.getElementsByTagName( me.document, !img2frame ? "iframe" : "img" ); -// for ( var i = 0, node; node = nodes[i++]; ) { -// if ( node.className != "edui-faked-webapp" ){ -// continue; -// } -// tmpdiv = me.document.createElement( "div" ); -// tmpdiv.innerHTML = createInsertStr( img2frame ? {url:node.getAttribute( "_url" ), width:node.width, height:node.height,title:node.title,logo:node.style.backgroundImage.replace("url(","").replace(")","")} : {url:node.getAttribute( "src", 2 ),title:node.title, width:node.width, height:node.height,logo:node.getAttribute("logo_url")}, img2frame ? true : false,false ); -// node.parentNode.replaceChild( tmpdiv.firstChild, node ); -// } -// } -// -// me.addListener( "beforegetcontent", function () { -// switchImgAndIframe( true ); -// } ); -// me.addListener( 'aftersetcontent', function () { -// switchImgAndIframe( false ); -// } ); -// me.addListener( 'aftergetcontent', function ( cmdName ) { -// if ( cmdName == 'aftergetcontent' && me.queryCommandState( 'source' ) ){ -// return; -// } -// switchImgAndIframe( false ); -// } ); -// -// me.commands['webapp'] = { -// execCommand:function ( cmd, obj ) { -// me.execCommand( "inserthtml", createInsertStr( obj, false,true ) ); -// } -// }; -//}; - -UE.plugin.register('webapp', function (){ - var me = this; - function createInsertStr(obj,toEmbed){ - return !toEmbed ? - '' - : - '' - - } - return { - outputRule: function(root){ - utils.each(root.getNodesByTagName('img'),function(node){ - var html; - if(node.getAttr('class') == 'edui-faked-webapp'){ - html = createInsertStr({ - title:node.getAttr('title'), - 'width':node.getAttr('width'), - 'height':node.getAttr('height'), - 'align':node.getAttr('align'), - 'cssfloat':node.getStyle('float'), - 'url':node.getAttr("_url"), - 'logo':node.getAttr('_logo_url') - },true); - var embed = UE.uNode.createElement(html); - node.parentNode.replaceChild(embed,node); - } - }) - }, - inputRule:function(root){ - utils.each(root.getNodesByTagName('iframe'),function(node){ - if(node.getAttr('class') == 'edui-faked-webapp'){ - var img = UE.uNode.createElement(createInsertStr({ - title:node.getAttr('title'), - 'width':node.getAttr('width'), - 'height':node.getAttr('height'), - 'align':node.getAttr('align'), - 'cssfloat':node.getStyle('float'), - 'url':node.getAttr("src"), - 'logo':node.getAttr('logo_url') - })); - node.parentNode.replaceChild(img,node); - } - }) - - }, - commands:{ - /** - * 插入百度应用 - * @command webapp - * @method execCommand - * @remind 需要百度APPKey - * @remind 百度应用主页: http://app.baidu.com/ - * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度, - * height=>应用容器高度,logo=>应用logo,url=>应用地址 - * @example - * ```javascript - * //editor是编辑器实例 - * //在编辑器里插入一个“植物大战僵尸”的APP - * editor.execCommand( 'webapp' , { - * title: '植物大战僵尸', - * width: 560, - * height: 465, - * logo: '应用展示的图片', - * url: '百度应用的地址' - * } ); - * ``` - */ - 'webapp':{ - execCommand:function (cmd, obj) { - - var me = this, - str = createInsertStr(utils.extend(obj,{ - align:'none' - }), false); - me.execCommand("inserthtml",str); - }, - queryCommandState:function () { - var me = this, - img = me.selection.getRange().getClosedNode(), - flag = img && (img.className == "edui-faked-webapp"); - return flag ? 1 : 0; - } - } - } - } -}); - -// plugins/template.js -///import core -///import plugins\inserthtml.js -///import plugins\cleardoc.js -///commands 模板 -///commandsName template -///commandsTitle 模板 -///commandsDialog dialogs\template -UE.plugins['template'] = function () { - UE.commands['template'] = { - execCommand:function (cmd, obj) { - obj.html && this.execCommand("inserthtml", obj.html); - } - }; - this.addListener("click", function (type, evt) { - var el = evt.target || evt.srcElement, - range = this.selection.getRange(); - var tnode = domUtils.findParent(el, function (node) { - if (node.className && domUtils.hasClass(node, "ue_t")) { - return node; - } - }, true); - tnode && range.selectNode(tnode).shrinkBoundary().select(); - }); - this.addListener("keydown", function (type, evt) { - var range = this.selection.getRange(); - if (!range.collapsed) { - if (!evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) { - var tnode = domUtils.findParent(range.startContainer, function (node) { - if (node.className && domUtils.hasClass(node, "ue_t")) { - return node; - } - }, true); - if (tnode) { - domUtils.removeClasses(tnode, ["ue_t"]); - } - } - } - }); -}; - - -// plugins/music.js -/** - * 插入音乐命令 - * @file - */ -UE.plugin.register('music', function (){ - var me = this; - function creatInsertStr(url,width,height,align,cssfloat,toEmbed){ - return !toEmbed ? - '' - : - ''; - } - return { - outputRule: function(root){ - utils.each(root.getNodesByTagName('img'),function(node){ - var html; - if(node.getAttr('class') == 'edui-faked-music'){ - var cssfloat = node.getStyle('float'); - var align = node.getAttr('align'); - html = creatInsertStr(node.getAttr("_url"), node.getAttr('width'), node.getAttr('height'), align, cssfloat, true); - var embed = UE.uNode.createElement(html); - node.parentNode.replaceChild(embed,node); - } - }) - }, - inputRule:function(root){ - utils.each(root.getNodesByTagName('embed'),function(node){ - if(node.getAttr('class') == 'edui-faked-music'){ - var cssfloat = node.getStyle('float'); - var align = node.getAttr('align'); - html = creatInsertStr(node.getAttr("src"), node.getAttr('width'), node.getAttr('height'), align, cssfloat,false); - var img = UE.uNode.createElement(html); - node.parentNode.replaceChild(img,node); - } - }) - - }, - commands:{ - /** - * 插入音乐 - * @command music - * @method execCommand - * @param { Object } musicOptions 插入音乐的参数项, 支持的key有: url=>音乐地址; - * width=>音乐容器宽度;height=>音乐容器高度;align=>音乐文件的对齐方式, 可选值有: left, center, right, none - * @example - * ```javascript - * //editor是编辑器实例 - * //在编辑器里插入一个“植物大战僵尸”的APP - * editor.execCommand( 'music' , { - * width: 400, - * height: 95, - * align: "center", - * url: "音乐地址" - * } ); - * ``` - */ - 'music':{ - execCommand:function (cmd, musicObj) { - var me = this, - str = creatInsertStr(musicObj.url, musicObj.width || 400, musicObj.height || 95, "none", false); - me.execCommand("inserthtml",str); - }, - queryCommandState:function () { - var me = this, - img = me.selection.getRange().getClosedNode(), - flag = img && (img.className == "edui-faked-music"); - return flag ? 1 : 0; - } - } - } - } -}); - -// plugins/autoupload.js -/** - * @description - * 1.拖放文件到编辑区域,自动上传并插入到选区 - * 2.插入粘贴板的图片,自动上传并插入到选区 - * @author Jinqn - * @date 2013-10-14 - */ -UE.plugin.register('autoupload', function (){ - - function sendAndInsertFile(file, editor) { - var me = editor; - //模拟数据 - var fieldName, urlPrefix, maxSize, allowFiles, actionUrl, - loadingHtml, errorHandler, successHandler, - filetype = /image\/\w+/i.test(file.type) ? 'image':'file', - loadingId = 'loading_' + (+new Date()).toString(36); - - fieldName = me.getOpt(filetype + 'FieldName'); - urlPrefix = me.getOpt(filetype + 'UrlPrefix'); - maxSize = me.getOpt(filetype + 'MaxSize'); - allowFiles = me.getOpt(filetype + 'AllowFiles'); - actionUrl = me.getActionUrl(me.getOpt(filetype + 'ActionName')); - errorHandler = function(title) { - var loader = me.document.getElementById(loadingId); - loader && domUtils.remove(loader); - me.fireEvent('showmessage', { - 'id': loadingId, - 'content': title, - 'type': 'error', - 'timeout': 4000 - }); - }; - - if (filetype == 'image') { - loadingHtml = ''; - successHandler = function(data) { - var link = urlPrefix + data.url, - loader = me.document.getElementById(loadingId); - if (loader) { - loader.setAttribute('src', link); - loader.setAttribute('_src', link); - loader.setAttribute('title', data.title || ''); - loader.setAttribute('alt', data.original || ''); - loader.removeAttribute('id'); - domUtils.removeClasses(loader, 'loadingclass'); - } - }; - } else { - loadingHtml = '

                  ' + - '' + - '

                  '; - successHandler = function(data) { - var link = urlPrefix + data.url, - loader = me.document.getElementById(loadingId); - - var rng = me.selection.getRange(), - bk = rng.createBookmark(); - rng.selectNode(loader).select(); - me.execCommand('insertfile', {'url': link}); - rng.moveToBookmark(bk).select(); - }; - } - - /* 插入loading的占位符 */ - me.execCommand('inserthtml', loadingHtml); - - /* 判断后端配置是否没有加载成功 */ - if (!me.getOpt(filetype + 'ActionName')) { - errorHandler(me.getLang('autoupload.errorLoadConfig')); - return; - } - /* 判断文件大小是否超出限制 */ - if(file.size > maxSize) { - errorHandler(me.getLang('autoupload.exceedSizeError')); - return; - } - /* 判断文件格式是否超出允许 */ - var fileext = file.name ? file.name.substr(file.name.lastIndexOf('.')):''; - if ((fileext && filetype != 'image') || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) { - errorHandler(me.getLang('autoupload.exceedTypeError')); - return; - } - - /* 创建Ajax并提交 */ - var xhr = new XMLHttpRequest(), - fd = new FormData(), - params = utils.serializeParam(me.queryCommandValue('serverparam')) || '', - url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?':'&') + params); - - fd.append(fieldName, file, file.name || ('blob.' + file.type.substr('image/'.length))); - fd.append('type', 'ajax'); - xhr.open("post", url, true); - xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - xhr.addEventListener('load', function (e) { - try{ - var json = (new Function("return " + utils.trim(e.target.response)))(); - if (json.state == 'SUCCESS' && json.url) { - successHandler(json); - } else { - errorHandler(json.state); - } - }catch(er){ - errorHandler(me.getLang('autoupload.loadError')); - } - }); - xhr.send(fd); - } - - function getPasteImage(e){ - return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items:null; - } - function getDropImage(e){ - return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files:null; - } - - return { - outputRule: function(root){ - utils.each(root.getNodesByTagName('img'),function(n){ - if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) { - n.parentNode.removeChild(n); - } - }); - utils.each(root.getNodesByTagName('p'),function(n){ - if (/\bloadpara\b/.test(n.getAttr('class'))) { - n.parentNode.removeChild(n); - } - }); - }, - bindEvents:{ - //插入粘贴板的图片,拖放插入图片 - 'ready':function(e){ - var me = this; - if(window.FormData && window.FileReader) { - domUtils.on(me.body, 'paste drop', function(e){ - var hasImg = false, - items; - //获取粘贴板文件列表或者拖放文件列表 - items = e.type == 'paste' ? getPasteImage(e):getDropImage(e); - if(items){ - var len = items.length, - file; - while (len--){ - file = items[len]; - if(file.getAsFile) file = file.getAsFile(); - if(file && file.size > 0) { - sendAndInsertFile(file, me); - hasImg = true; - } - } - hasImg && e.preventDefault(); - } - - }); - //取消拖放图片时出现的文字光标位置提示 - domUtils.on(me.body, 'dragover', function (e) { - if(e.dataTransfer.types[0] == 'Files') { - e.preventDefault(); - } - }); - - //设置loading的样式 - utils.cssRule('loading', - '.loadingclass{display:inline-block;cursor:default;background: url(\'' - + this.options.themePath - + this.options.theme +'/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-left:1px;height: 22px;width: 22px;}\n' + - '.loaderrorclass{display:inline-block;cursor:default;background: url(\'' - + this.options.themePath - + this.options.theme +'/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' + - '}', - this.document); - } - } - } - } -}); - -// plugins/autosave.js -UE.plugin.register('autosave', function (){ - - var me = this, - //无限循环保护 - lastSaveTime = new Date(), - //最小保存间隔时间 - MIN_TIME = 20, - //auto save key - saveKey = null; - - function save ( editor ) { - - var saveData; - - if ( new Date() - lastSaveTime < MIN_TIME ) { - return; - } - - if ( !editor.hasContents() ) { - //这里不能调用命令来删除, 会造成事件死循环 - saveKey && me.removePreferences( saveKey ); - return; - } - - lastSaveTime = new Date(); - - editor._saveFlag = null; - - saveData = me.body.innerHTML; - - if ( editor.fireEvent( "beforeautosave", { - content: saveData - } ) === false ) { - return; - } - - me.setPreferences( saveKey, saveData ); - - editor.fireEvent( "afterautosave", { - content: saveData - } ); - - } - - return { - defaultOptions: { - //默认间隔时间 - saveInterval: 500 - }, - bindEvents:{ - 'ready':function(){ - - var _suffix = "-drafts-data", - key = null; - - if ( me.key ) { - key = me.key + _suffix; - } else { - key = ( me.container.parentNode.id || 'ue-common' ) + _suffix; - } - - //页面地址+编辑器ID 保持唯一 - saveKey = ( location.protocol + location.host + location.pathname ).replace( /[.:\/]/g, '_' ) + key; - - }, - - 'contentchange': function () { - - if ( !saveKey ) { - return; - } - - if ( me._saveFlag ) { - window.clearTimeout( me._saveFlag ); - } - - if ( me.options.saveInterval > 0 ) { - - me._saveFlag = window.setTimeout( function () { - - save( me ); - - }, me.options.saveInterval ); - - } else { - - save(me); - - } - - - } - }, - commands:{ - 'clearlocaldata':{ - execCommand:function (cmd, name) { - if ( saveKey && me.getPreferences( saveKey ) ) { - me.removePreferences( saveKey ) - } - }, - notNeedUndo: true, - ignoreContentChange:true - }, - - 'getlocaldata':{ - execCommand:function (cmd, name) { - return saveKey ? me.getPreferences( saveKey ) || '' : ''; - }, - notNeedUndo: true, - ignoreContentChange:true - }, - - 'drafts':{ - execCommand:function (cmd, name) { - if ( saveKey ) { - me.body.innerHTML = me.getPreferences( saveKey ) || '

                  '+domUtils.fillHtml+'

                  '; - me.focus(true); - } - }, - queryCommandState: function () { - return saveKey ? ( me.getPreferences( saveKey ) === null ? -1 : 0 ) : -1; - }, - notNeedUndo: true, - ignoreContentChange:true - } - } - } - -}); - -// plugins/charts.js -UE.plugin.register('charts', function (){ - - var me = this; - - return { - bindEvents: { - 'chartserror': function () { - } - }, - commands:{ - 'charts': { - execCommand: function ( cmd, data ) { - - var tableNode = domUtils.findParentByTagName(this.selection.getRange().startContainer, 'table', true), - flagText = [], - config = {}; - - if ( !tableNode ) { - return false; - } - - if ( !validData( tableNode ) ) { - me.fireEvent( "chartserror" ); - return false; - } - - config.title = data.title || ''; - config.subTitle = data.subTitle || ''; - config.xTitle = data.xTitle || ''; - config.yTitle = data.yTitle || ''; - config.suffix = data.suffix || ''; - config.tip = data.tip || ''; - //数据对齐方式 - config.dataFormat = data.tableDataFormat || ''; - //图表类型 - config.chartType = data.chartType || 0; - - for ( var key in config ) { - - if ( !config.hasOwnProperty( key ) ) { - continue; - } - - flagText.push( key+":"+config[ key ] ); - - } - - tableNode.setAttribute( "data-chart", flagText.join( ";" ) ); - domUtils.addClass( tableNode, "edui-charts-table" ); - - - - }, - queryCommandState: function ( cmd, name ) { - - var tableNode = domUtils.findParentByTagName(this.selection.getRange().startContainer, 'table', true); - return tableNode && validData( tableNode ) ? 0 : -1; - - } - } - }, - inputRule:function(root){ - utils.each(root.getNodesByTagName('table'),function( tableNode ){ - - if ( tableNode.getAttr("data-chart") !== undefined ) { - tableNode.setAttr("style"); - } - - }) - - }, - outputRule:function(root){ - utils.each(root.getNodesByTagName('table'),function( tableNode ){ - - if ( tableNode.getAttr("data-chart") !== undefined ) { - tableNode.setAttr("style", "display: none;"); - } - - }) - - } - } - - function validData ( table ) { - - var firstRows = null, - cellCount = 0; - - //行数不够 - if ( table.rows.length < 2 ) { - return false; - } - - //列数不够 - if ( table.rows[0].cells.length < 2 ) { - return false; - } - - //第一行所有cell必须是th - firstRows = table.rows[ 0 ].cells; - cellCount = firstRows.length; - - for ( var i = 0, cell; cell = firstRows[ i ]; i++ ) { - - if ( cell.tagName.toLowerCase() !== 'th' ) { - return false; - } - - } - - for ( var i = 1, row; row = table.rows[ i ]; i++ ) { - - //每行单元格数不匹配, 返回false - if ( row.cells.length != cellCount ) { - return false; - } - - //第一列不是th也返回false - if ( row.cells[0].tagName.toLowerCase() !== 'th' ) { - return false; - } - - for ( var j = 1, cell; cell = row.cells[ j ]; j++ ) { - - var value = utils.trim( ( cell.innerText || cell.textContent || '' ) ); - - value = value.replace( new RegExp( UE.dom.domUtils.fillChar, 'g' ), '' ).replace( /^\s+|\s+$/g, '' ); - - //必须是数字 - if ( !/^\d*\.?\d+$/.test( value ) ) { - return false; - } - - } - - } - - return true; - - } - -}); - -// plugins/section.js -/** - * 目录大纲支持插件 - * @file - * @since 1.3.0 - */ -UE.plugin.register('section', function (){ - /* 目录节点对象 */ - function Section(option){ - this.tag = ''; - this.level = -1, - this.dom = null; - this.nextSection = null; - this.previousSection = null; - this.parentSection = null; - this.startAddress = []; - this.endAddress = []; - this.children = []; - } - function getSection(option) { - var section = new Section(); - return utils.extend(section, option); - } - function getNodeFromAddress(startAddress, root) { - var current = root; - for(var i = 0;i < startAddress.length; i++) { - if(!current.childNodes) return null; - current = current.childNodes[startAddress[i]]; - } - return current; - } - - var me = this; - - return { - bindMultiEvents:{ - type: 'aftersetcontent afterscencerestore', - handler: function(){ - me.fireEvent('updateSections'); - } - }, - bindEvents:{ - /* 初始化、拖拽、粘贴、执行setcontent之后 */ - 'ready': function (){ - me.fireEvent('updateSections'); - domUtils.on(me.body, 'drop paste', function(){ - me.fireEvent('updateSections'); - }); - }, - /* 执行paragraph命令之后 */ - 'afterexeccommand': function (type, cmd) { - if(cmd == 'paragraph') { - me.fireEvent('updateSections'); - } - }, - /* 部分键盘操作,触发updateSections事件 */ - 'keyup': function (type, e) { - var me = this, - range = me.selection.getRange(); - if(range.collapsed != true) { - me.fireEvent('updateSections'); - } else { - var keyCode = e.keyCode || e.which; - if(keyCode == 13 || keyCode == 8 || keyCode == 46) { - me.fireEvent('updateSections'); - } - } - } - }, - commands:{ - 'getsections': { - execCommand: function (cmd, levels) { - var levelFn = levels || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; - - for (var i = 0; i < levelFn.length; i++) { - if (typeof levelFn[i] == 'string') { - levelFn[i] = function(fn){ - return function(node){ - return node.tagName == fn.toUpperCase() - }; - }(levelFn[i]); - } else if (typeof levelFn[i] != 'function') { - levelFn[i] = function (node) { - return null; - } - } - } - function getSectionLevel(node) { - for (var i = 0; i < levelFn.length; i++) { - if (levelFn[i](node)) return i; - } - return -1; - } - - var me = this, - Directory = getSection({'level':-1, 'title':'root'}), - previous = Directory; - - function traversal(node, Directory) { - var level, - tmpSection = null, - parent, - child, - children = node.childNodes; - for (var i = 0, len = children.length; i < len; i++) { - child = children[i]; - level = getSectionLevel(child); - if (level >= 0) { - var address = me.selection.getRange().selectNode(child).createAddress(true).startAddress, - current = getSection({ - 'tag': child.tagName, - 'title': child.innerText || child.textContent || '', - 'level': level, - 'dom': child, - 'startAddress': utils.clone(address, []), - 'endAddress': utils.clone(address, []), - 'children': [] - }); - previous.nextSection = current; - current.previousSection = previous; - parent = previous; - while(level <= parent.level){ - parent = parent.parentSection; - } - current.parentSection = parent; - parent.children.push(current); - tmpSection = previous = current; - } else { - child.nodeType === 1 && traversal(child, Directory); - tmpSection && tmpSection.endAddress[tmpSection.endAddress.length - 1] ++; - } - } - } - traversal(me.body, Directory); - return Directory; - }, - notNeedUndo: true - }, - 'movesection': { - execCommand: function (cmd, sourceSection, targetSection, isAfter) { - - var me = this, - targetAddress, - target; - - if(!sourceSection || !targetSection || targetSection.level == -1) return; - - targetAddress = isAfter ? targetSection.endAddress:targetSection.startAddress; - target = getNodeFromAddress(targetAddress, me.body); - - /* 判断目标地址是否被源章节包含 */ - if(!targetAddress || !target || isContainsAddress(sourceSection.startAddress, sourceSection.endAddress, targetAddress)) return; - - var startNode = getNodeFromAddress(sourceSection.startAddress, me.body), - endNode = getNodeFromAddress(sourceSection.endAddress, me.body), - current, - nextNode; - - if(isAfter) { - current = endNode; - while ( current && !(domUtils.getPosition( startNode, current ) & domUtils.POSITION_FOLLOWING) ) { - nextNode = current.previousSibling; - domUtils.insertAfter(target, current); - if(current == startNode) break; - current = nextNode; - } - } else { - current = startNode; - while ( current && !(domUtils.getPosition( current, endNode ) & domUtils.POSITION_FOLLOWING) ) { - nextNode = current.nextSibling; - target.parentNode.insertBefore(current, target); - if(current == endNode) break; - current = nextNode; - } - } - - me.fireEvent('updateSections'); - - /* 获取地址的包含关系 */ - function isContainsAddress(startAddress, endAddress, addressTarget){ - var isAfterStartAddress = false, - isBeforeEndAddress = false; - for(var i = 0; i< startAddress.length; i++){ - if(i >= addressTarget.length) break; - if(addressTarget[i] > startAddress[i]) { - isAfterStartAddress = true; - break; - } else if(addressTarget[i] < startAddress[i]) { - break; - } - } - for(var i = 0; i< endAddress.length; i++){ - if(i >= addressTarget.length) break; - if(addressTarget[i] < startAddress[i]) { - isBeforeEndAddress = true; - break; - } else if(addressTarget[i] > startAddress[i]) { - break; - } - } - return isAfterStartAddress && isBeforeEndAddress; - } - } - }, - 'deletesection': { - execCommand: function (cmd, section, keepChildren) { - var me = this; - - if(!section) return; - - function getNodeFromAddress(startAddress) { - var current = me.body; - for(var i = 0;i < startAddress.length; i++) { - if(!current.childNodes) return null; - current = current.childNodes[startAddress[i]]; - } - return current; - } - - var startNode = getNodeFromAddress(section.startAddress), - endNode = getNodeFromAddress(section.endAddress), - current = startNode, - nextNode; - - if(!keepChildren) { - while ( current && domUtils.inDoc(endNode, me.document) && !(domUtils.getPosition( current, endNode ) & domUtils.POSITION_FOLLOWING) ) { - nextNode = current.nextSibling; - domUtils.remove(current); - current = nextNode; - } - } else { - domUtils.remove(current); - } - - me.fireEvent('updateSections'); - } - }, - 'selectsection': { - execCommand: function (cmd, section) { - if(!section && !section.dom) return false; - var me = this, - range = me.selection.getRange(), - address = { - 'startAddress':utils.clone(section.startAddress, []), - 'endAddress':utils.clone(section.endAddress, []) - }; - address.endAddress[address.endAddress.length - 1]++; - range.moveToAddress(address).select().scrollToView(); - return true; - }, - notNeedUndo: true - }, - 'scrolltosection': { - execCommand: function (cmd, section) { - if(!section && !section.dom) return false; - var me = this, - range = me.selection.getRange(), - address = { - 'startAddress':section.startAddress, - 'endAddress':section.endAddress - }; - address.endAddress[address.endAddress.length - 1]++; - range.moveToAddress(address).scrollToView(); - return true; - }, - notNeedUndo: true - } - } - } -}); - -// plugins/simpleupload.js -/** - * @description - * 简单上传:点击按钮,直接选择文件上传 - * @author Jinqn - * @date 2014-03-31 - */ -UE.plugin.register('simpleupload', function (){ - var me = this, - isLoaded = false, - containerBtn; - - function initUploadBtn(){ - var w = containerBtn.offsetWidth || 20, - h = containerBtn.offsetHeight || 20, - btnIframe = document.createElement('iframe'), - btnStyle = 'display:block;width:' + w + 'px;height:' + h + 'px;overflow:hidden;border:0;margin:0;padding:0;position:absolute;top:0;left:0;filter:alpha(opacity=0);-moz-opacity:0;-khtml-opacity: 0;opacity: 0;cursor:pointer;'; - - domUtils.on(btnIframe, 'load', function(){ - - var timestrap = (+new Date()).toString(36), - wrapper, - btnIframeDoc, - btnIframeBody; - - btnIframeDoc = (btnIframe.contentDocument || btnIframe.contentWindow.document); - btnIframeBody = btnIframeDoc.body; - wrapper = btnIframeDoc.createElement('div'); - - wrapper.innerHTML = '
                  ' + - '' + - '
                  ' + - ''; - - wrapper.className = 'edui-' + me.options.theme; - wrapper.id = me.ui.id + '_iframeupload'; - btnIframeBody.style.cssText = btnStyle; - btnIframeBody.style.width = w + 'px'; - btnIframeBody.style.height = h + 'px'; - btnIframeBody.appendChild(wrapper); - - if (btnIframeBody.parentNode) { - btnIframeBody.parentNode.style.width = w + 'px'; - btnIframeBody.parentNode.style.height = w + 'px'; - } - - var form = btnIframeDoc.getElementById('edui_form_' + timestrap); - var input = btnIframeDoc.getElementById('edui_input_' + timestrap); - var iframe = btnIframeDoc.getElementById('edui_iframe_' + timestrap); - - domUtils.on(input, 'change', function(){ - if(!input.value) return; - var loadingId = 'loading_' + (+new Date()).toString(36); - var params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''; - - var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName')); - var allowFiles = me.getOpt('imageAllowFiles'); - - me.focus(); - me.execCommand('inserthtml', ''); - - function callback(){ - try{ - var link, json, loader, - body = (iframe.contentDocument || iframe.contentWindow.document).body, - result = body.innerText || body.textContent || ''; - json = (new Function("return " + result))(); - link = me.options.imageUrlPrefix + json.url; - if(json.state == 'SUCCESS' && json.url) { - loader = me.document.getElementById(loadingId); - loader.setAttribute('src', link); - loader.setAttribute('_src', link); - loader.setAttribute('title', json.title || ''); - loader.setAttribute('alt', json.original || ''); - loader.removeAttribute('id'); - domUtils.removeClasses(loader, 'loadingclass'); - } else { - showErrorLoader && showErrorLoader(json.state); - } - }catch(er){ - showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError')); - } - form.reset(); - domUtils.un(iframe, 'load', callback); - } - function showErrorLoader(title){ - if(loadingId) { - var loader = me.document.getElementById(loadingId); - loader && domUtils.remove(loader); - me.fireEvent('showmessage', { - 'id': loadingId, - 'content': title, - 'type': 'error', - 'timeout': 4000 - }); - } - } - - /* 判断后端配置是否没有加载成功 */ - if (!me.getOpt('imageActionName')) { - errorHandler(me.getLang('autoupload.errorLoadConfig')); - return; - } - // 判断文件格式是否错误 - var filename = input.value, - fileext = filename ? filename.substr(filename.lastIndexOf('.')):''; - if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) { - showErrorLoader(me.getLang('simpleupload.exceedTypeError')); - return; - } - - domUtils.on(iframe, 'load', callback); - form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?':'&') + params); - form.submit(); - }); - - var stateTimer; - me.addListener('selectionchange', function () { - clearTimeout(stateTimer); - stateTimer = setTimeout(function() { - var state = me.queryCommandState('simpleupload'); - if (state == -1) { - input.disabled = 'disabled'; - } else { - input.disabled = false; - } - }, 400); - }); - isLoaded = true; - }); - - btnIframe.style.cssText = btnStyle; - containerBtn.appendChild(btnIframe); - } - - return { - bindEvents:{ - 'ready': function() { - //设置loading的样式 - utils.cssRule('loading', - '.loadingclass{display:inline-block;cursor:default;background: url(\'' - + this.options.themePath - + this.options.theme +'/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;}\n' + - '.loaderrorclass{display:inline-block;cursor:default;background: url(\'' - + this.options.themePath - + this.options.theme +'/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' + - '}', - this.document); - }, - /* 初始化简单上传按钮 */ - 'simpleuploadbtnready': function(type, container) { - containerBtn = container; - me.afterConfigReady(initUploadBtn); - } - }, - outputRule: function(root){ - utils.each(root.getNodesByTagName('img'),function(n){ - if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) { - n.parentNode.removeChild(n); - } - }); - }, - commands: { - 'simpleupload': { - queryCommandState: function () { - return isLoaded ? 0:-1; - } - } - } - } -}); - -// plugins/serverparam.js -/** - * 服务器提交的额外参数列表设置插件 - * @file - * @since 1.2.6.1 - */ -UE.plugin.register('serverparam', function (){ - - var me = this, - serverParam = {}; - - return { - commands:{ - /** - * 修改服务器提交的额外参数列表,清除所有项 - * @command serverparam - * @method execCommand - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.execCommand('serverparam'); - * editor.queryCommandValue('serverparam'); //返回空 - * ``` - */ - /** - * 修改服务器提交的额外参数列表,删除指定项 - * @command serverparam - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } key 要清除的属性 - * @example - * ```javascript - * editor.execCommand('serverparam', 'name'); //删除属性name - * ``` - */ - /** - * 修改服务器提交的额外参数列表,使用键值添加项 - * @command serverparam - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { String } key 要添加的属性 - * @param { String } value 要添加属性的值 - * @example - * ```javascript - * editor.execCommand('serverparam', 'name', 'hello'); - * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'} - * ``` - */ - /** - * 修改服务器提交的额外参数列表,传入键值对对象添加多项 - * @command serverparam - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { Object } key 传入的键值对对象 - * @example - * ```javascript - * editor.execCommand('serverparam', {'name': 'hello'}); - * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'} - * ``` - */ - /** - * 修改服务器提交的额外参数列表,使用自定义函数添加多项 - * @command serverparam - * @method execCommand - * @param { String } cmd 命令字符串 - * @param { Function } key 自定义获取参数的函数 - * @example - * ```javascript - * editor.execCommand('serverparam', function(editor){ - * return {'key': 'value'}; - * }); - * editor.queryCommandValue('serverparam'); //返回对象 {'key': 'value'} - * ``` - */ - - /** - * 获取服务器提交的额外参数列表 - * @command serverparam - * @method queryCommandValue - * @param { String } cmd 命令字符串 - * @example - * ```javascript - * editor.queryCommandValue( 'serverparam' ); //返回对象 {'key': 'value'} - * ``` - */ - 'serverparam':{ - execCommand:function (cmd, key, value) { - if (key === undefined || key === null) { //不传参数,清空列表 - serverParam = {}; - } else if (utils.isString(key)) { //传入键值 - if(value === undefined || value === null) { - delete serverParam[key]; - } else { - serverParam[key] = value; - } - } else if (utils.isObject(key)) { //传入对象,覆盖列表项 - utils.extend(serverParam, key, true); - } else if (utils.isFunction(key)){ //传入函数,添加列表项 - utils.extend(serverParam, key(), true); - } - }, - queryCommandValue: function(){ - return serverParam || {}; - } - } - } - } -}); - - -// plugins/insertfile.js -/** - * 插入附件 - */ -UE.plugin.register('insertfile', function (){ - - var me = this; - - function getFileIcon(url){ - var ext = url.substr(url.lastIndexOf('.') + 1).toLowerCase(), - maps = { - "rar":"icon_rar.gif", - "zip":"icon_rar.gif", - "tar":"icon_rar.gif", - "gz":"icon_rar.gif", - "bz2":"icon_rar.gif", - "doc":"icon_doc.gif", - "docx":"icon_doc.gif", - "pdf":"icon_pdf.gif", - "mp3":"icon_mp3.gif", - "xls":"icon_xls.gif", - "chm":"icon_chm.gif", - "ppt":"icon_ppt.gif", - "pptx":"icon_ppt.gif", - "avi":"icon_mv.gif", - "rmvb":"icon_mv.gif", - "wmv":"icon_mv.gif", - "flv":"icon_mv.gif", - "swf":"icon_mv.gif", - "rm":"icon_mv.gif", - "exe":"icon_exe.gif", - "psd":"icon_psd.gif", - "txt":"icon_txt.gif", - "jpg":"icon_jpg.gif", - "png":"icon_jpg.gif", - "jpeg":"icon_jpg.gif", - "gif":"icon_jpg.gif", - "ico":"icon_jpg.gif", - "bmp":"icon_jpg.gif" - }; - return maps[ext] ? maps[ext]:maps['txt']; - } - - return { - commands:{ - 'insertfile': { - execCommand: function (command, filelist){ - filelist = utils.isArray(filelist) ? filelist : [filelist]; - - var i, item, icon, title, - html = '', - URL = me.getOpt('UEDITOR_HOME_URL'), - iconDir = URL + (URL.substr(URL.length - 1) == '/' ? '':'/') + 'dialogs/attachment/fileTypeImages/'; - for (i = 0; i < filelist.length; i++) { - item = filelist[i]; - icon = iconDir + getFileIcon(item.url); - title = item.title || item.url.substr(item.url.lastIndexOf('/') + 1); - html += '

                  ' + - '' + - '' + title + '' + - '

                  '; - } - me.execCommand('insertHtml', html); - } - } - } - } -}); - - - - -// ui/ui.js -var baidu = baidu || {}; -baidu.editor = baidu.editor || {}; -UE.ui = baidu.editor.ui = {}; - -// ui/uiutils.js -(function (){ - var browser = baidu.editor.browser, - domUtils = baidu.editor.dom.domUtils; - - var magic = '$EDITORUI'; - var root = window[magic] = {}; - var uidMagic = 'ID' + magic; - var uidCount = 0; - - var uiUtils = baidu.editor.ui.uiUtils = { - uid: function (obj){ - return (obj ? obj[uidMagic] || (obj[uidMagic] = ++ uidCount) : ++ uidCount); - }, - hook: function ( fn, callback ) { - var dg; - if (fn && fn._callbacks) { - dg = fn; - } else { - dg = function (){ - var q; - if (fn) { - q = fn.apply(this, arguments); - } - var callbacks = dg._callbacks; - var k = callbacks.length; - while (k --) { - var r = callbacks[k].apply(this, arguments); - if (q === undefined) { - q = r; - } - } - return q; - }; - dg._callbacks = []; - } - dg._callbacks.push(callback); - return dg; - }, - createElementByHtml: function (html){ - var el = document.createElement('div'); - el.innerHTML = html; - el = el.firstChild; - el.parentNode.removeChild(el); - return el; - }, - getViewportElement: function (){ - return (browser.ie && browser.quirks) ? - document.body : document.documentElement; - }, - getClientRect: function (element){ - var bcr; - //trace IE6下在控制编辑器显隐时可能会报错,catch一下 - try{ - bcr = element.getBoundingClientRect(); - }catch(e){ - bcr={left:0,top:0,height:0,width:0} - } - var rect = { - left: Math.round(bcr.left), - top: Math.round(bcr.top), - height: Math.round(bcr.bottom - bcr.top), - width: Math.round(bcr.right - bcr.left) - }; - var doc; - while ((doc = element.ownerDocument) !== document && - (element = domUtils.getWindow(doc).frameElement)) { - bcr = element.getBoundingClientRect(); - rect.left += bcr.left; - rect.top += bcr.top; - } - rect.bottom = rect.top + rect.height; - rect.right = rect.left + rect.width; - return rect; - }, - getViewportRect: function (){ - var viewportEl = uiUtils.getViewportElement(); - var width = (window.innerWidth || viewportEl.clientWidth) | 0; - var height = (window.innerHeight ||viewportEl.clientHeight) | 0; - return { - left: 0, - top: 0, - height: height, - width: width, - bottom: height, - right: width - }; - }, - setViewportOffset: function (element, offset){ - var rect; - var fixedLayer = uiUtils.getFixedLayer(); - if (element.parentNode === fixedLayer) { - element.style.left = offset.left + 'px'; - element.style.top = offset.top + 'px'; - } else { - domUtils.setViewportOffset(element, offset); - } - }, - getEventOffset: function (evt){ - var el = evt.target || evt.srcElement; - var rect = uiUtils.getClientRect(el); - var offset = uiUtils.getViewportOffsetByEvent(evt); - return { - left: offset.left - rect.left, - top: offset.top - rect.top - }; - }, - getViewportOffsetByEvent: function (evt){ - var el = evt.target || evt.srcElement; - var frameEl = domUtils.getWindow(el).frameElement; - var offset = { - left: evt.clientX, - top: evt.clientY - }; - if (frameEl && el.ownerDocument !== document) { - var rect = uiUtils.getClientRect(frameEl); - offset.left += rect.left; - offset.top += rect.top; - } - return offset; - }, - setGlobal: function (id, obj){ - root[id] = obj; - return magic + '["' + id + '"]'; - }, - unsetGlobal: function (id){ - delete root[id]; - }, - copyAttributes: function (tgt, src){ - var attributes = src.attributes; - var k = attributes.length; - while (k --) { - var attrNode = attributes[k]; - if ( attrNode.nodeName != 'style' && attrNode.nodeName != 'class' && (!browser.ie || attrNode.specified) ) { - tgt.setAttribute(attrNode.nodeName, attrNode.nodeValue); - } - } - if (src.className) { - domUtils.addClass(tgt,src.className); - } - if (src.style.cssText) { - tgt.style.cssText += ';' + src.style.cssText; - } - }, - removeStyle: function (el, styleName){ - if (el.style.removeProperty) { - el.style.removeProperty(styleName); - } else if (el.style.removeAttribute) { - el.style.removeAttribute(styleName); - } else throw ''; - }, - contains: function (elA, elB){ - return elA && elB && (elA === elB ? false : ( - elA.contains ? elA.contains(elB) : - elA.compareDocumentPosition(elB) & 16 - )); - }, - startDrag: function (evt, callbacks,doc){ - var doc = doc || document; - var startX = evt.clientX; - var startY = evt.clientY; - function handleMouseMove(evt){ - var x = evt.clientX - startX; - var y = evt.clientY - startY; - callbacks.ondragmove(x, y,evt); - if (evt.stopPropagation) { - evt.stopPropagation(); - } else { - evt.cancelBubble = true; - } - } - if (doc.addEventListener) { - function handleMouseUp(evt){ - doc.removeEventListener('mousemove', handleMouseMove, true); - doc.removeEventListener('mouseup', handleMouseUp, true); - window.removeEventListener('mouseup', handleMouseUp, true); - callbacks.ondragstop(); - } - doc.addEventListener('mousemove', handleMouseMove, true); - doc.addEventListener('mouseup', handleMouseUp, true); - window.addEventListener('mouseup', handleMouseUp, true); - - evt.preventDefault(); - } else { - var elm = evt.srcElement; - elm.setCapture(); - function releaseCaptrue(){ - elm.releaseCapture(); - elm.detachEvent('onmousemove', handleMouseMove); - elm.detachEvent('onmouseup', releaseCaptrue); - elm.detachEvent('onlosecaptrue', releaseCaptrue); - callbacks.ondragstop(); - } - elm.attachEvent('onmousemove', handleMouseMove); - elm.attachEvent('onmouseup', releaseCaptrue); - elm.attachEvent('onlosecaptrue', releaseCaptrue); - evt.returnValue = false; - } - callbacks.ondragstart(); - }, - getFixedLayer: function (){ - var layer = document.getElementById('edui_fixedlayer'); - if (layer == null) { - layer = document.createElement('div'); - layer.id = 'edui_fixedlayer'; - document.body.appendChild(layer); - if (browser.ie && browser.version <= 8) { - layer.style.position = 'absolute'; - bindFixedLayer(); - setTimeout(updateFixedOffset); - } else { - layer.style.position = 'fixed'; - } - layer.style.left = '0'; - layer.style.top = '0'; - layer.style.width = '0'; - layer.style.height = '0'; - } - return layer; - }, - makeUnselectable: function (element){ - if (browser.opera || (browser.ie && browser.version < 9)) { - element.unselectable = 'on'; - if (element.hasChildNodes()) { - for (var i=0; i
                  '; - } - }; - utils.inherits(Separator, UIBase); - -})(); - - -// ui/mask.js -///import core -///import uicore -(function (){ - var utils = baidu.editor.utils, - domUtils = baidu.editor.dom.domUtils, - UIBase = baidu.editor.ui.UIBase, - uiUtils = baidu.editor.ui.uiUtils; - - var Mask = baidu.editor.ui.Mask = function (options){ - this.initOptions(options); - this.initUIBase(); - }; - Mask.prototype = { - getHtmlTpl: function (){ - return '
                  '; - }, - postRender: function (){ - var me = this; - domUtils.on(window, 'resize', function (){ - setTimeout(function (){ - if (!me.isHidden()) { - me._fill(); - } - }); - }); - }, - show: function (zIndex){ - this._fill(); - this.getDom().style.display = ''; - this.getDom().style.zIndex = zIndex; - }, - hide: function (){ - this.getDom().style.display = 'none'; - this.getDom().style.zIndex = ''; - }, - isHidden: function (){ - return this.getDom().style.display == 'none'; - }, - _onMouseDown: function (){ - return false; - }, - _onClick: function (e, target){ - this.fireEvent('click', e, target); - }, - _fill: function (){ - var el = this.getDom(); - var vpRect = uiUtils.getViewportRect(); - el.style.width = vpRect.width + 'px'; - el.style.height = vpRect.height + 'px'; - } - }; - utils.inherits(Mask, UIBase); -})(); - - -// ui/popup.js -///import core -///import uicore -(function () { - var utils = baidu.editor.utils, - uiUtils = baidu.editor.ui.uiUtils, - domUtils = baidu.editor.dom.domUtils, - UIBase = baidu.editor.ui.UIBase, - Popup = baidu.editor.ui.Popup = function (options){ - this.initOptions(options); - this.initPopup(); - }; - - var allPopups = []; - function closeAllPopup( evt,el ){ - for ( var i = 0; i < allPopups.length; i++ ) { - var pop = allPopups[i]; - if (!pop.isHidden()) { - if (pop.queryAutoHide(el) !== false) { - if(evt&&/scroll/ig.test(evt.type)&&pop.className=="edui-wordpastepop") return; - pop.hide(); - } - } - } - - if(allPopups.length) - pop.editor.fireEvent("afterhidepop"); - } - - Popup.postHide = closeAllPopup; - - var ANCHOR_CLASSES = ['edui-anchor-topleft','edui-anchor-topright', - 'edui-anchor-bottomleft','edui-anchor-bottomright']; - Popup.prototype = { - SHADOW_RADIUS: 5, - content: null, - _hidden: false, - autoRender: true, - canSideLeft: true, - canSideUp: true, - initPopup: function (){ - this.initUIBase(); - allPopups.push( this ); - }, - getHtmlTpl: function (){ - return '
                  ' + - '
                  ' + - ' ' + - '
                  ' + - '
                  ' + - this.getContentHtmlTpl() + - '
                  ' + - '
                  ' + - '
                  '; - }, - getContentHtmlTpl: function (){ - if(this.content){ - if (typeof this.content == 'string') { - return this.content; - } - return this.content.renderHtml(); - }else{ - return '' - } - - }, - _UIBase_postRender: UIBase.prototype.postRender, - postRender: function (){ - - - if (this.content instanceof UIBase) { - this.content.postRender(); - } - - //捕获鼠标滚轮 - if( this.captureWheel && !this.captured ) { - - this.captured = true; - - var winHeight = ( document.documentElement.clientHeight || document.body.clientHeight ) - 80, - _height = this.getDom().offsetHeight, - _top = uiUtils.getClientRect( this.combox.getDom() ).top, - content = this.getDom('content'), - ifr = this.getDom('body').getElementsByTagName('iframe'), - me = this; - - ifr.length && ( ifr = ifr[0] ); - - while( _top + _height > winHeight ) { - _height -= 30; - } - content.style.height = _height + 'px'; - //同步更改iframe高度 - ifr && ( ifr.style.height = _height + 'px' ); - - //阻止在combox上的鼠标滚轮事件, 防止用户的正常操作被误解 - if( window.XMLHttpRequest ) { - - domUtils.on( content, ( 'onmousewheel' in document.body ) ? 'mousewheel' :'DOMMouseScroll' , function(e){ - - if(e.preventDefault) { - e.preventDefault(); - } else { - e.returnValue = false; - } - - if( e.wheelDelta ) { - - content.scrollTop -= ( e.wheelDelta / 120 )*60; - - } else { - - content.scrollTop -= ( e.detail / -3 )*60; - - } - - }); - - } else { - - //ie6 - domUtils.on( this.getDom(), 'mousewheel' , function(e){ - - e.returnValue = false; - - me.getDom('content').scrollTop -= ( e.wheelDelta / 120 )*60; - - }); - - } - - } - this.fireEvent('postRenderAfter'); - this.hide(true); - this._UIBase_postRender(); - }, - _doAutoRender: function (){ - if (!this.getDom() && this.autoRender) { - this.render(); - } - }, - mesureSize: function (){ - var box = this.getDom('content'); - return uiUtils.getClientRect(box); - }, - fitSize: function (){ - if( this.captureWheel && this.sized ) { - return this.__size; - } - this.sized = true; - var popBodyEl = this.getDom('body'); - popBodyEl.style.width = ''; - popBodyEl.style.height = ''; - var size = this.mesureSize(); - if( this.captureWheel ) { - popBodyEl.style.width = -(-20 -size.width) + 'px'; - var height = parseInt( this.getDom('content').style.height, 10 ); - !window.isNaN( height ) && ( size.height = height ); - } else { - popBodyEl.style.width = size.width + 'px'; - } - popBodyEl.style.height = size.height + 'px'; - this.__size = size; - this.captureWheel && (this.getDom('content').style.overflow = 'auto'); - return size; - }, - showAnchor: function ( element, hoz ){ - this.showAnchorRect( uiUtils.getClientRect( element ), hoz ); - }, - showAnchorRect: function ( rect, hoz, adj ){ - this._doAutoRender(); - var vpRect = uiUtils.getViewportRect(); - this.getDom().style.visibility = 'hidden'; - this._show(); - var popSize = this.fitSize(); - - var sideLeft, sideUp, left, top; - if (hoz) { - sideLeft = this.canSideLeft && (rect.right + popSize.width > vpRect.right && rect.left > popSize.width); - sideUp = this.canSideUp && (rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height); - left = (sideLeft ? rect.left - popSize.width : rect.right); - top = (sideUp ? rect.bottom - popSize.height : rect.top); - } else { - sideLeft = this.canSideLeft && (rect.right + popSize.width > vpRect.right && rect.left > popSize.width); - sideUp = this.canSideUp && (rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height); - left = (sideLeft ? rect.right - popSize.width : rect.left); - top = (sideUp ? rect.top - popSize.height : rect.bottom); - } - - var popEl = this.getDom(); - uiUtils.setViewportOffset(popEl, { - left: left, - top: top - }); - domUtils.removeClasses(popEl, ANCHOR_CLASSES); - popEl.className += ' ' + ANCHOR_CLASSES[(sideUp ? 1 : 0) * 2 + (sideLeft ? 1 : 0)]; - if(this.editor){ - popEl.style.zIndex = this.editor.container.style.zIndex * 1 + 10; - baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = popEl.style.zIndex - 1; - } - this.getDom().style.visibility = 'visible'; - - }, - showAt: function (offset) { - var left = offset.left; - var top = offset.top; - var rect = { - left: left, - top: top, - right: left, - bottom: top, - height: 0, - width: 0 - }; - this.showAnchorRect(rect, false, true); - }, - _show: function (){ - if (this._hidden) { - var box = this.getDom(); - box.style.display = ''; - this._hidden = false; -// if (box.setActive) { -// box.setActive(); -// } - this.fireEvent('show'); - } - }, - isHidden: function (){ - return this._hidden; - }, - show: function (){ - this._doAutoRender(); - this._show(); - }, - hide: function (notNofity){ - if (!this._hidden && this.getDom()) { - this.getDom().style.display = 'none'; - this._hidden = true; - if (!notNofity) { - this.fireEvent('hide'); - } - } - }, - queryAutoHide: function (el){ - return !el || !uiUtils.contains(this.getDom(), el); - } - }; - utils.inherits(Popup, UIBase); - - domUtils.on( document, 'mousedown', function ( evt ) { - var el = evt.target || evt.srcElement; - closeAllPopup( evt,el ); - } ); - domUtils.on( window, 'scroll', function (evt,el) { - closeAllPopup( evt,el ); - } ); - -})(); - - -// ui/colorpicker.js -///import core -///import uicore -(function (){ - var utils = baidu.editor.utils, - UIBase = baidu.editor.ui.UIBase, - ColorPicker = baidu.editor.ui.ColorPicker = function (options){ - this.initOptions(options); - this.noColorText = this.noColorText || this.editor.getLang("clearColor"); - this.initUIBase(); - }; - - ColorPicker.prototype = { - getHtmlTpl: function (){ - return genColorPicker(this.noColorText,this.editor); - }, - _onTableClick: function (evt){ - var tgt = evt.target || evt.srcElement; - var color = tgt.getAttribute('data-color'); - if (color) { - this.fireEvent('pickcolor', color); - } - }, - _onTableOver: function (evt){ - var tgt = evt.target || evt.srcElement; - var color = tgt.getAttribute('data-color'); - if (color) { - this.getDom('preview').style.backgroundColor = color; - } - }, - _onTableOut: function (){ - this.getDom('preview').style.backgroundColor = ''; - }, - _onPickNoColor: function (){ - this.fireEvent('picknocolor'); - } - }; - utils.inherits(ColorPicker, UIBase); - - var COLORS = ( - 'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' + - 'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' + - 'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' + - 'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' + - 'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' + - '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' + - 'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(','); - - function genColorPicker(noColorText,editor){ - var html = '
                  ' + - '
                  ' + - '
                  ' + - '
                  '+ noColorText +'
                  ' + - '
                  ' + - '' + - ''+ - ''; - for (var i=0; i':'')+''; - } - html += i<70 ? '':''; - } - html += '
                  '+editor.getLang("themeColor")+'
                  '+editor.getLang("standardColor")+'
                  '; - return html; - } -})(); - - -// ui/tablepicker.js -///import core -///import uicore -(function (){ - var utils = baidu.editor.utils, - uiUtils = baidu.editor.ui.uiUtils, - UIBase = baidu.editor.ui.UIBase; - - var TablePicker = baidu.editor.ui.TablePicker = function (options){ - this.initOptions(options); - this.initTablePicker(); - }; - TablePicker.prototype = { - defaultNumRows: 10, - defaultNumCols: 10, - maxNumRows: 20, - maxNumCols: 20, - numRows: 10, - numCols: 10, - lengthOfCellSide: 22, - initTablePicker: function (){ - this.initUIBase(); - }, - getHtmlTpl: function (){ - var me = this; - return '
                  ' + - '
                  ' + - '
                  ' + - '' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  '; - }, - _UIBase_render: UIBase.prototype.render, - render: function (holder){ - this._UIBase_render(holder); - this.getDom('label').innerHTML = '0'+this.editor.getLang("t_row")+' x 0'+this.editor.getLang("t_col"); - }, - _track: function (numCols, numRows){ - var style = this.getDom('overlay').style; - var sideLen = this.lengthOfCellSide; - style.width = numCols * sideLen + 'px'; - style.height = numRows * sideLen + 'px'; - var label = this.getDom('label'); - label.innerHTML = numCols +this.editor.getLang("t_col")+' x ' + numRows + this.editor.getLang("t_row"); - this.numCols = numCols; - this.numRows = numRows; - }, - _onMouseOver: function (evt, el){ - var rel = evt.relatedTarget || evt.fromElement; - if (!uiUtils.contains(el, rel) && el !== rel) { - this.getDom('label').innerHTML = '0'+this.editor.getLang("t_col")+' x 0'+this.editor.getLang("t_row"); - this.getDom('overlay').style.visibility = ''; - } - }, - _onMouseOut: function (evt, el){ - var rel = evt.relatedTarget || evt.toElement; - if (!uiUtils.contains(el, rel) && el !== rel) { - this.getDom('label').innerHTML = '0'+this.editor.getLang("t_col")+' x 0'+this.editor.getLang("t_row"); - this.getDom('overlay').style.visibility = 'hidden'; - } - }, - _onMouseMove: function (evt, el){ - var style = this.getDom('overlay').style; - var offset = uiUtils.getEventOffset(evt); - var sideLen = this.lengthOfCellSide; - var numCols = Math.ceil(offset.left / sideLen); - var numRows = Math.ceil(offset.top / sideLen); - this._track(numCols, numRows); - }, - _onClick: function (){ - this.fireEvent('picktable', this.numCols, this.numRows); - } - }; - utils.inherits(TablePicker, UIBase); -})(); - - -// ui/stateful.js -(function (){ - var browser = baidu.editor.browser, - domUtils = baidu.editor.dom.domUtils, - uiUtils = baidu.editor.ui.uiUtils; - - var TPL_STATEFUL = 'onmousedown="$$.Stateful_onMouseDown(event, this);"' + - ' onmouseup="$$.Stateful_onMouseUp(event, this);"' + - ( browser.ie ? ( - ' onmouseenter="$$.Stateful_onMouseEnter(event, this);"' + - ' onmouseleave="$$.Stateful_onMouseLeave(event, this);"' ) - : ( - ' onmouseover="$$.Stateful_onMouseOver(event, this);"' + - ' onmouseout="$$.Stateful_onMouseOut(event, this);"' )); - - baidu.editor.ui.Stateful = { - alwalysHoverable: false, - target:null,//目标元素和this指向dom不一样 - Stateful_init: function (){ - this._Stateful_dGetHtmlTpl = this.getHtmlTpl; - this.getHtmlTpl = this.Stateful_getHtmlTpl; - }, - Stateful_getHtmlTpl: function (){ - var tpl = this._Stateful_dGetHtmlTpl(); - // 使用function避免$转义 - return tpl.replace(/stateful/g, function (){ return TPL_STATEFUL; }); - }, - Stateful_onMouseEnter: function (evt, el){ - this.target=el; - if (!this.isDisabled() || this.alwalysHoverable) { - this.addState('hover'); - this.fireEvent('over'); - } - }, - Stateful_onMouseLeave: function (evt, el){ - if (!this.isDisabled() || this.alwalysHoverable) { - this.removeState('hover'); - this.removeState('active'); - this.fireEvent('out'); - } - }, - Stateful_onMouseOver: function (evt, el){ - var rel = evt.relatedTarget; - if (!uiUtils.contains(el, rel) && el !== rel) { - this.Stateful_onMouseEnter(evt, el); - } - }, - Stateful_onMouseOut: function (evt, el){ - var rel = evt.relatedTarget; - if (!uiUtils.contains(el, rel) && el !== rel) { - this.Stateful_onMouseLeave(evt, el); - } - }, - Stateful_onMouseDown: function (evt, el){ - if (!this.isDisabled()) { - this.addState('active'); - } - }, - Stateful_onMouseUp: function (evt, el){ - if (!this.isDisabled()) { - this.removeState('active'); - } - }, - Stateful_postRender: function (){ - if (this.disabled && !this.hasState('disabled')) { - this.addState('disabled'); - } - }, - hasState: function (state){ - return domUtils.hasClass(this.getStateDom(), 'edui-state-' + state); - }, - addState: function (state){ - if (!this.hasState(state)) { - this.getStateDom().className += ' edui-state-' + state; - } - }, - removeState: function (state){ - if (this.hasState(state)) { - domUtils.removeClasses(this.getStateDom(), ['edui-state-' + state]); - } - }, - getStateDom: function (){ - return this.getDom('state'); - }, - isChecked: function (){ - return this.hasState('checked'); - }, - setChecked: function (checked){ - if (!this.isDisabled() && checked) { - this.addState('checked'); - } else { - this.removeState('checked'); - } - }, - isDisabled: function (){ - return this.hasState('disabled'); - }, - setDisabled: function (disabled){ - if (disabled) { - this.removeState('hover'); - this.removeState('checked'); - this.removeState('active'); - this.addState('disabled'); - } else { - this.removeState('disabled'); - } - } - }; -})(); - - -// ui/button.js -///import core -///import uicore -///import ui/stateful.js -(function (){ - var utils = baidu.editor.utils, - UIBase = baidu.editor.ui.UIBase, - Stateful = baidu.editor.ui.Stateful, - Button = baidu.editor.ui.Button = function (options){ - if(options.name){ - var btnName = options.name; - var cssRules = options.cssRules; - if(!options.className){ - options.className = 'edui-for-' + btnName; - } - options.cssRules = '.edui-default .edui-for-'+ btnName +' .edui-icon {'+ cssRules +'}' - } - this.initOptions(options); - this.initButton(); - }; - Button.prototype = { - uiName: 'button', - label: '', - title: '', - showIcon: true, - showText: true, - cssRules:'', - initButton: function (){ - this.initUIBase(); - this.Stateful_init(); - if(this.cssRules){ - utils.cssRule('edui-customize-'+this.name+'-style',this.cssRules); - } - }, - getHtmlTpl: function (){ - return '
                  ' + - '
                  ' + - '
                  ' + - (this.showIcon ? '
                  ' : '') + - (this.showText ? '
                  ' + this.label + '
                  ' : '') + - '
                  ' + - '
                  ' + - '
                  '; - }, - postRender: function (){ - this.Stateful_postRender(); - this.setDisabled(this.disabled) - }, - _onMouseDown: function (e){ - var target = e.target || e.srcElement, - tagName = target && target.tagName && target.tagName.toLowerCase(); - if (tagName == 'input' || tagName == 'object' || tagName == 'object') { - return false; - } - }, - _onClick: function (){ - if (!this.isDisabled()) { - this.fireEvent('click'); - } - }, - setTitle: function(text){ - var label = this.getDom('label'); - label.innerHTML = text; - } - }; - utils.inherits(Button, UIBase); - utils.extend(Button.prototype, Stateful); - -})(); - - -// ui/splitbutton.js -///import core -///import uicore -///import ui/stateful.js -(function (){ - var utils = baidu.editor.utils, - uiUtils = baidu.editor.ui.uiUtils, - domUtils = baidu.editor.dom.domUtils, - UIBase = baidu.editor.ui.UIBase, - Stateful = baidu.editor.ui.Stateful, - SplitButton = baidu.editor.ui.SplitButton = function (options){ - this.initOptions(options); - this.initSplitButton(); - }; - SplitButton.prototype = { - popup: null, - uiName: 'splitbutton', - title: '', - initSplitButton: function (){ - this.initUIBase(); - this.Stateful_init(); - var me = this; - if (this.popup != null) { - var popup = this.popup; - this.popup = null; - this.setPopup(popup); - } - }, - _UIBase_postRender: UIBase.prototype.postRender, - postRender: function (){ - this.Stateful_postRender(); - this._UIBase_postRender(); - }, - setPopup: function (popup){ - if (this.popup === popup) return; - if (this.popup != null) { - this.popup.dispose(); - } - popup.addListener('show', utils.bind(this._onPopupShow, this)); - popup.addListener('hide', utils.bind(this._onPopupHide, this)); - popup.addListener('postrender', utils.bind(function (){ - popup.getDom('body').appendChild( - uiUtils.createElementByHtml('
                  ') - ); - popup.getDom().className += ' ' + this.className; - }, this)); - this.popup = popup; - }, - _onPopupShow: function (){ - this.addState('opened'); - }, - _onPopupHide: function (){ - this.removeState('opened'); - }, - getHtmlTpl: function (){ - return '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  '; - }, - showPopup: function (){ - // 当popup往上弹出的时候,做特殊处理 - var rect = uiUtils.getClientRect(this.getDom()); - rect.top -= this.popup.SHADOW_RADIUS; - rect.height += this.popup.SHADOW_RADIUS; - this.popup.showAnchorRect(rect); - }, - _onArrowClick: function (event, el){ - if (!this.isDisabled()) { - this.showPopup(); - } - }, - _onButtonClick: function (){ - if (!this.isDisabled()) { - this.fireEvent('buttonclick'); - } - } - }; - utils.inherits(SplitButton, UIBase); - utils.extend(SplitButton.prototype, Stateful, true); - -})(); - - -// ui/colorbutton.js -///import core -///import uicore -///import ui/colorpicker.js -///import ui/popup.js -///import ui/splitbutton.js -(function (){ - var utils = baidu.editor.utils, - uiUtils = baidu.editor.ui.uiUtils, - ColorPicker = baidu.editor.ui.ColorPicker, - Popup = baidu.editor.ui.Popup, - SplitButton = baidu.editor.ui.SplitButton, - ColorButton = baidu.editor.ui.ColorButton = function (options){ - this.initOptions(options); - this.initColorButton(); - }; - ColorButton.prototype = { - initColorButton: function (){ - var me = this; - this.popup = new Popup({ - content: new ColorPicker({ - noColorText: me.editor.getLang("clearColor"), - editor:me.editor, - onpickcolor: function (t, color){ - me._onPickColor(color); - }, - onpicknocolor: function (t, color){ - me._onPickNoColor(color); - } - }), - editor:me.editor - }); - this.initSplitButton(); - }, - _SplitButton_postRender: SplitButton.prototype.postRender, - postRender: function (){ - this._SplitButton_postRender(); - this.getDom('button_body').appendChild( - uiUtils.createElementByHtml('
                  ') - ); - this.getDom().className += ' edui-colorbutton'; - }, - setColor: function (color){ - this.getDom('colorlump').style.backgroundColor = color; - this.color = color; - }, - _onPickColor: function (color){ - if (this.fireEvent('pickcolor', color) !== false) { - this.setColor(color); - this.popup.hide(); - } - }, - _onPickNoColor: function (color){ - if (this.fireEvent('picknocolor') !== false) { - this.popup.hide(); - } - } - }; - utils.inherits(ColorButton, SplitButton); - -})(); - - -// ui/tablebutton.js -///import core -///import uicore -///import ui/popup.js -///import ui/tablepicker.js -///import ui/splitbutton.js -(function (){ - var utils = baidu.editor.utils, - Popup = baidu.editor.ui.Popup, - TablePicker = baidu.editor.ui.TablePicker, - SplitButton = baidu.editor.ui.SplitButton, - TableButton = baidu.editor.ui.TableButton = function (options){ - this.initOptions(options); - this.initTableButton(); - }; - TableButton.prototype = { - initTableButton: function (){ - var me = this; - this.popup = new Popup({ - content: new TablePicker({ - editor:me.editor, - onpicktable: function (t, numCols, numRows){ - me._onPickTable(numCols, numRows); - } - }), - 'editor':me.editor - }); - this.initSplitButton(); - }, - _onPickTable: function (numCols, numRows){ - if (this.fireEvent('picktable', numCols, numRows) !== false) { - this.popup.hide(); - } - } - }; - utils.inherits(TableButton, SplitButton); - -})(); - - -// ui/autotypesetpicker.js -///import core -///import uicore -(function () { - var utils = baidu.editor.utils, - UIBase = baidu.editor.ui.UIBase; - - var AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker = function (options) { - this.initOptions(options); - this.initAutoTypeSetPicker(); - }; - AutoTypeSetPicker.prototype = { - initAutoTypeSetPicker:function () { - this.initUIBase(); - }, - getHtmlTpl:function () { - var me = this.editor, - opt = me.options.autotypeset, - lang = me.getLang("autoTypeSet"); - - var textAlignInputName = 'textAlignValue' + me.uid, - imageBlockInputName = 'imageBlockLineValue' + me.uid, - symbolConverInputName = 'symbolConverValue' + me.uid; - - return '
                  ' + - '
                  ' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
                  ' + lang.mergeLine + '' + lang.delLine + '
                  ' + lang.removeFormat + '' + lang.indent + '
                  ' + lang.alignment + '' + - '' + me.getLang("justifyleft") + - '' + me.getLang("justifycenter") + - '' + me.getLang("justifyright") + - '
                  ' + lang.imageFloat + '' + - '' + me.getLang("default") + - '' + me.getLang("justifyleft") + - '' + me.getLang("justifycenter") + - '' + me.getLang("justifyright") + - '
                  ' + lang.removeFontsize + '' + lang.removeFontFamily + '
                  ' + lang.removeHtml + '
                  ' + lang.pasteFilter + '
                  ' + lang.symbol + '' + - '' + lang.bdc2sb + - '' + lang.tobdc + '' + - '
                  ' + - '
                  ' + - '
                  '; - - - }, - _UIBase_render:UIBase.prototype.render - }; - utils.inherits(AutoTypeSetPicker, UIBase); -})(); - - -// ui/autotypesetbutton.js -///import core -///import uicore -///import ui/popup.js -///import ui/autotypesetpicker.js -///import ui/splitbutton.js -(function (){ - var utils = baidu.editor.utils, - Popup = baidu.editor.ui.Popup, - AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker, - SplitButton = baidu.editor.ui.SplitButton, - AutoTypeSetButton = baidu.editor.ui.AutoTypeSetButton = function (options){ - this.initOptions(options); - this.initAutoTypeSetButton(); - }; - function getPara(me){ - - var opt = {}, - cont = me.getDom(), - editorId = me.editor.uid, - inputType = null, - attrName = null, - ipts = domUtils.getElementsByTagName(cont,"input"); - for(var i=ipts.length-1,ipt;ipt=ipts[i--];){ - inputType = ipt.getAttribute("type"); - if(inputType=="checkbox"){ - attrName = ipt.getAttribute("name"); - opt[attrName] && delete opt[attrName]; - if(ipt.checked){ - var attrValue = document.getElementById( attrName + "Value" + editorId ); - if(attrValue){ - if(/input/ig.test(attrValue.tagName)){ - opt[attrName] = attrValue.value; - } else { - var iptChilds = attrValue.getElementsByTagName("input"); - for(var j=iptChilds.length-1,iptchild;iptchild=iptChilds[j--];){ - if(iptchild.checked){ - opt[attrName] = iptchild.value; - break; - } - } - } - } else { - opt[attrName] = true; - } - } else { - opt[attrName] = false; - } - } else { - opt[ipt.getAttribute("value")] = ipt.checked; - } - - } - - var selects = domUtils.getElementsByTagName(cont,"select"); - for(var i=0,si;si=selects[i++];){ - var attr = si.getAttribute('name'); - opt[attr] = opt[attr] ? si.value : ''; - } - - utils.extend(me.editor.options.autotypeset,opt); - - me.editor.setPreferences('autotypeset', opt); - } - - AutoTypeSetButton.prototype = { - initAutoTypeSetButton: function (){ - - var me = this; - this.popup = new Popup({ - //传入配置参数 - content: new AutoTypeSetPicker({editor:me.editor}), - 'editor':me.editor, - hide : function(){ - if (!this._hidden && this.getDom()) { - getPara(this); - this.getDom().style.display = 'none'; - this._hidden = true; - this.fireEvent('hide'); - } - } - }); - var flag = 0; - this.popup.addListener('postRenderAfter',function(){ - var popupUI = this; - if(flag)return; - var cont = this.getDom(), - btn = cont.getElementsByTagName('button')[0]; - - btn.onclick = function(){ - getPara(popupUI); - me.editor.execCommand('autotypeset'); - popupUI.hide() - }; - - domUtils.on(cont, 'click', function(e) { - var target = e.target || e.srcElement, - editorId = me.editor.uid; - if (target && target.tagName == 'INPUT') { - - // 点击图片浮动的checkbox,去除对应的radio - if (target.name == 'imageBlockLine' || target.name == 'textAlign' || target.name == 'symbolConver') { - var checked = target.checked, - radioTd = document.getElementById( target.name + 'Value' + editorId), - radios = radioTd.getElementsByTagName('input'), - defalutSelect = { - 'imageBlockLine': 'none', - 'textAlign': 'left', - 'symbolConver': 'tobdc' - }; - - for (var i = 0; i < radios.length; i++) { - if (checked) { - if (radios[i].value == defalutSelect[target.name]) { - radios[i].checked = 'checked'; - } - } else { - radios[i].checked = false; - } - } - } - // 点击radio,选中对应的checkbox - if (target.name == ('imageBlockLineValue' + editorId) || target.name == ('textAlignValue' + editorId) || target.name == 'bdc') { - var checkboxs = target.parentNode.previousSibling.getElementsByTagName('input'); - checkboxs && (checkboxs[0].checked = true); - } - - getPara(popupUI); - } - }); - - flag = 1; - }); - this.initSplitButton(); - } - }; - utils.inherits(AutoTypeSetButton, SplitButton); - -})(); - - -// ui/cellalignpicker.js -///import core -///import uicore -(function () { - var utils = baidu.editor.utils, - Popup = baidu.editor.ui.Popup, - Stateful = baidu.editor.ui.Stateful, - UIBase = baidu.editor.ui.UIBase; - - /** - * 该参数将新增一个参数: selected, 参数类型为一个Object, 形如{ 'align': 'center', 'valign': 'top' }, 表示单元格的初始 - * 对齐状态为: 竖直居上,水平居中; 其中 align的取值为:'center', 'left', 'right'; valign的取值为: 'top', 'middle', 'bottom' - * @update 2013/4/2 hancong03@baidu.com - */ - var CellAlignPicker = baidu.editor.ui.CellAlignPicker = function (options) { - this.initOptions(options); - this.initSelected(); - this.initCellAlignPicker(); - }; - CellAlignPicker.prototype = { - //初始化选中状态, 该方法将根据传递进来的参数获取到应该选中的对齐方式图标的索引 - initSelected: function(){ - - var status = { - - valign: { - top: 0, - middle: 1, - bottom: 2 - }, - align: { - left: 0, - center: 1, - right: 2 - }, - count: 3 - - }, - result = -1; - - if( this.selected ) { - this.selectedIndex = status.valign[ this.selected.valign ] * status.count + status.align[ this.selected.align ]; - } - - }, - initCellAlignPicker:function () { - this.initUIBase(); - this.Stateful_init(); - }, - getHtmlTpl:function () { - - var alignType = [ 'left', 'center', 'right' ], - COUNT = 9, - tempClassName = null, - tempIndex = -1, - tmpl = []; - - - for( var i= 0; i'); - - tmpl.push( '
                  ' ); - - tempIndex === 2 && tmpl.push(''); - - } - - return '
                  ' + - '
                  ' + - '' + - tmpl.join('') + - '
                  ' + - '
                  ' + - '
                  '; - }, - getStateDom: function (){ - return this.target; - }, - _onClick: function (evt){ - var target= evt.target || evt.srcElement; - if(/icon/.test(target.className)){ - this.items[target.parentNode.getAttribute("index")].onclick(); - Popup.postHide(evt); - } - }, - _UIBase_render:UIBase.prototype.render - }; - utils.inherits(CellAlignPicker, UIBase); - utils.extend(CellAlignPicker.prototype, Stateful,true); -})(); - - - - - -// ui/pastepicker.js -///import core -///import uicore -(function () { - var utils = baidu.editor.utils, - Stateful = baidu.editor.ui.Stateful, - uiUtils = baidu.editor.ui.uiUtils, - UIBase = baidu.editor.ui.UIBase; - - var PastePicker = baidu.editor.ui.PastePicker = function (options) { - this.initOptions(options); - this.initPastePicker(); - }; - PastePicker.prototype = { - initPastePicker:function () { - this.initUIBase(); - this.Stateful_init(); - }, - getHtmlTpl:function () { - return '
                  ' + - '
                  ' + - '
                  ' + this.editor.getLang("pasteOpt") + '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' - }, - getStateDom:function () { - return this.target; - }, - format:function (param) { - this.editor.ui._isTransfer = true; - this.editor.fireEvent('pasteTransfer', param); - }, - _onClick:function (cur) { - var node = domUtils.getNextDomNode(cur), - screenHt = uiUtils.getViewportRect().height, - subPop = uiUtils.getClientRect(node); - - if ((subPop.top + subPop.height) > screenHt) - node.style.top = (-subPop.height - cur.offsetHeight) + "px"; - else - node.style.top = ""; - - if (/hidden/ig.test(domUtils.getComputedStyle(node, "visibility"))) { - node.style.visibility = "visible"; - domUtils.addClass(cur, "edui-state-opened"); - } else { - node.style.visibility = "hidden"; - domUtils.removeClasses(cur, "edui-state-opened") - } - }, - _UIBase_render:UIBase.prototype.render - }; - utils.inherits(PastePicker, UIBase); - utils.extend(PastePicker.prototype, Stateful, true); -})(); - - - - - - -// ui/toolbar.js -(function (){ - var utils = baidu.editor.utils, - uiUtils = baidu.editor.ui.uiUtils, - UIBase = baidu.editor.ui.UIBase, - Toolbar = baidu.editor.ui.Toolbar = function (options){ - this.initOptions(options); - this.initToolbar(); - }; - Toolbar.prototype = { - items: null, - initToolbar: function (){ - this.items = this.items || []; - this.initUIBase(); - }, - add: function (item,index){ - if(index === undefined){ - this.items.push(item); - }else{ - this.items.splice(index,0,item) - } - - }, - getHtmlTpl: function (){ - var buff = []; - for (var i=0; i' + - buff.join('') + - '' - }, - postRender: function (){ - var box = this.getDom(); - for (var i=0; i
                  '; - }, - postRender:function () { - }, - queryAutoHide:function () { - return true; - } - }; - Menu.prototype = { - items:null, - uiName:'menu', - initMenu:function () { - this.items = this.items || []; - this.initPopup(); - this.initItems(); - }, - initItems:function () { - for (var i = 0; i < this.items.length; i++) { - var item = this.items[i]; - if (item == '-') { - this.items[i] = this.getSeparator(); - } else if (!(item instanceof MenuItem)) { - item.editor = this.editor; - item.theme = this.editor.options.theme; - this.items[i] = this.createItem(item); - } - } - }, - getSeparator:function () { - return menuSeparator; - }, - createItem:function (item) { - //新增一个参数menu, 该参数存储了menuItem所对应的menu引用 - item.menu = this; - return new MenuItem(item); - }, - _Popup_getContentHtmlTpl:Popup.prototype.getContentHtmlTpl, - getContentHtmlTpl:function () { - if (this.items.length == 0) { - return this._Popup_getContentHtmlTpl(); - } - var buff = []; - for (var i = 0; i < this.items.length; i++) { - var item = this.items[i]; - buff[i] = item.renderHtml(); - } - return ('
                  ' + buff.join('') + '
                  '); - }, - _Popup_postRender:Popup.prototype.postRender, - postRender:function () { - var me = this; - for (var i = 0; i < this.items.length; i++) { - var item = this.items[i]; - item.ownerMenu = this; - item.postRender(); - } - domUtils.on(this.getDom(), 'mouseover', function (evt) { - evt = evt || event; - var rel = evt.relatedTarget || evt.fromElement; - var el = me.getDom(); - if (!uiUtils.contains(el, rel) && el !== rel) { - me.fireEvent('over'); - } - }); - this._Popup_postRender(); - }, - queryAutoHide:function (el) { - if (el) { - if (uiUtils.contains(this.getDom(), el)) { - return false; - } - for (var i = 0; i < this.items.length; i++) { - var item = this.items[i]; - if (item.queryAutoHide(el) === false) { - return false; - } - } - } - }, - clearItems:function () { - for (var i = 0; i < this.items.length; i++) { - var item = this.items[i]; - clearTimeout(item._showingTimer); - clearTimeout(item._closingTimer); - if (item.subMenu) { - item.subMenu.destroy(); - } - } - this.items = []; - }, - destroy:function () { - if (this.getDom()) { - domUtils.remove(this.getDom()); - } - this.clearItems(); - }, - dispose:function () { - this.destroy(); - } - }; - utils.inherits(Menu, Popup); - - /** - * @update 2013/04/03 hancong03 新增一个参数menu, 该参数存储了menuItem所对应的menu引用 - * @type {Function} - */ - var MenuItem = baidu.editor.ui.MenuItem = function (options) { - this.initOptions(options); - this.initUIBase(); - this.Stateful_init(); - if (this.subMenu && !(this.subMenu instanceof Menu)) { - if (options.className && options.className.indexOf("aligntd") != -1) { - var me = this; - - //获取单元格对齐初始状态 - this.subMenu.selected = this.editor.queryCommandValue( 'cellalignment' ); - - this.subMenu = new Popup({ - content:new CellAlignPicker(this.subMenu), - parentMenu:me, - editor:me.editor, - destroy:function () { - if (this.getDom()) { - domUtils.remove(this.getDom()); - } - } - }); - this.subMenu.addListener("postRenderAfter", function () { - domUtils.on(this.getDom(), "mouseover", function () { - me.addState('opened'); - }); - }); - } else { - this.subMenu = new Menu(this.subMenu); - } - } - }; - MenuItem.prototype = { - label:'', - subMenu:null, - ownerMenu:null, - uiName:'menuitem', - alwalysHoverable:true, - getHtmlTpl:function () { - return '
                  ' + - '
                  ' + - this.renderLabelHtml() + - '
                  ' + - '
                  '; - }, - postRender:function () { - var me = this; - this.addListener('over', function () { - me.ownerMenu.fireEvent('submenuover', me); - if (me.subMenu) { - me.delayShowSubMenu(); - } - }); - if (this.subMenu) { - this.getDom().className += ' edui-hassubmenu'; - this.subMenu.render(); - this.addListener('out', function () { - me.delayHideSubMenu(); - }); - this.subMenu.addListener('over', function () { - clearTimeout(me._closingTimer); - me._closingTimer = null; - me.addState('opened'); - }); - this.ownerMenu.addListener('hide', function () { - me.hideSubMenu(); - }); - this.ownerMenu.addListener('submenuover', function (t, subMenu) { - if (subMenu !== me) { - me.delayHideSubMenu(); - } - }); - this.subMenu._bakQueryAutoHide = this.subMenu.queryAutoHide; - this.subMenu.queryAutoHide = function (el) { - if (el && uiUtils.contains(me.getDom(), el)) { - return false; - } - return this._bakQueryAutoHide(el); - }; - } - this.getDom().style.tabIndex = '-1'; - uiUtils.makeUnselectable(this.getDom()); - this.Stateful_postRender(); - }, - delayShowSubMenu:function () { - var me = this; - if (!me.isDisabled()) { - me.addState('opened'); - clearTimeout(me._showingTimer); - clearTimeout(me._closingTimer); - me._closingTimer = null; - me._showingTimer = setTimeout(function () { - me.showSubMenu(); - }, 250); - } - }, - delayHideSubMenu:function () { - var me = this; - if (!me.isDisabled()) { - me.removeState('opened'); - clearTimeout(me._showingTimer); - if (!me._closingTimer) { - me._closingTimer = setTimeout(function () { - if (!me.hasState('opened')) { - me.hideSubMenu(); - } - me._closingTimer = null; - }, 400); - } - } - }, - renderLabelHtml:function () { - return '
                  ' + - '
                  ' + - '
                  ' + (this.label || '') + '
                  '; - }, - getStateDom:function () { - return this.getDom(); - }, - queryAutoHide:function (el) { - if (this.subMenu && this.hasState('opened')) { - return this.subMenu.queryAutoHide(el); - } - }, - _onClick:function (event, this_) { - if (this.hasState('disabled')) return; - if (this.fireEvent('click', event, this_) !== false) { - if (this.subMenu) { - this.showSubMenu(); - } else { - Popup.postHide(event); - } - } - }, - showSubMenu:function () { - var rect = uiUtils.getClientRect(this.getDom()); - rect.right -= 5; - rect.left += 2; - rect.width -= 7; - rect.top -= 4; - rect.bottom += 4; - rect.height += 8; - this.subMenu.showAnchorRect(rect, true, true); - }, - hideSubMenu:function () { - this.subMenu.hide(); - } - }; - utils.inherits(MenuItem, UIBase); - utils.extend(MenuItem.prototype, Stateful, true); -})(); - - -// ui/combox.js -///import core -///import uicore -///import ui/menu.js -///import ui/splitbutton.js -(function (){ - // todo: menu和item提成通用list - var utils = baidu.editor.utils, - uiUtils = baidu.editor.ui.uiUtils, - Menu = baidu.editor.ui.Menu, - SplitButton = baidu.editor.ui.SplitButton, - Combox = baidu.editor.ui.Combox = function (options){ - this.initOptions(options); - this.initCombox(); - }; - Combox.prototype = { - uiName: 'combox', - onbuttonclick:function () { - this.showPopup(); - }, - initCombox: function (){ - var me = this; - this.items = this.items || []; - for (var i=0; i vpRect.right) { - left = vpRect.right - rect.width; - } - var top = offset.top; - if (top + rect.height > vpRect.bottom) { - top = vpRect.bottom - rect.height; - } - el.style.left = Math.max(left, 0) + 'px'; - el.style.top = Math.max(top, 0) + 'px'; - }, - showAtCenter: function (){ - - var vpRect = uiUtils.getViewportRect(); - - if ( !this.fullscreen ) { - this.getDom().style.display = ''; - var popSize = this.fitSize(); - var titleHeight = this.getDom('titlebar').offsetHeight | 0; - var left = vpRect.width / 2 - popSize.width / 2; - var top = vpRect.height / 2 - (popSize.height - titleHeight) / 2 - titleHeight; - var popEl = this.getDom(); - this.safeSetOffset({ - left: Math.max(left | 0, 0), - top: Math.max(top | 0, 0) - }); - if (!domUtils.hasClass(popEl, 'edui-state-centered')) { - popEl.className += ' edui-state-centered'; - } - } else { - var dialogWrapNode = this.getDom(), - contentNode = this.getDom('content'); - - dialogWrapNode.style.display = "block"; - - var wrapRect = UE.ui.uiUtils.getClientRect( dialogWrapNode ), - contentRect = UE.ui.uiUtils.getClientRect( contentNode ); - dialogWrapNode.style.left = "-100000px"; - - contentNode.style.width = ( vpRect.width - wrapRect.width + contentRect.width ) + "px"; - contentNode.style.height = ( vpRect.height - wrapRect.height + contentRect.height ) + "px"; - - dialogWrapNode.style.width = vpRect.width + "px"; - dialogWrapNode.style.height = vpRect.height + "px"; - dialogWrapNode.style.left = 0; - - //保存环境的overflow值 - this._originalContext = { - html: { - overflowX: document.documentElement.style.overflowX, - overflowY: document.documentElement.style.overflowY - }, - body: { - overflowX: document.body.style.overflowX, - overflowY: document.body.style.overflowY - } - }; - - document.documentElement.style.overflowX = 'hidden'; - document.documentElement.style.overflowY = 'hidden'; - document.body.style.overflowX = 'hidden'; - document.body.style.overflowY = 'hidden'; - - } - - this._show(); - }, - getContentHtml: function (){ - var contentHtml = ''; - if (typeof this.content == 'string') { - contentHtml = this.content; - } else if (this.iframeUrl) { - contentHtml = ''; - } - return contentHtml; - }, - getHtmlTpl: function (){ - var footHtml = ''; - - if (this.buttons) { - var buff = []; - for (var i=0; i' + buff.join('') + '' + - ''; - } - - return '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '' + (this.title || '') + '' + - '
                  ' + - this.closeButton.renderHtml() + - '
                  ' + - '
                  '+ ( this.autoReset ? '' : this.getContentHtml()) +'
                  ' + - footHtml + - '
                  '; - }, - postRender: function (){ - // todo: 保持居中/记住上次关闭位置选项 - if (!this.modalMask.getDom()) { - this.modalMask.render(); - this.modalMask.hide(); - } - if (!this.dragMask.getDom()) { - this.dragMask.render(); - this.dragMask.hide(); - } - var me = this; - this.addListener('show', function (){ - me.modalMask.show(this.getDom().style.zIndex - 2); - }); - this.addListener('hide', function (){ - me.modalMask.hide(); - }); - if (this.buttons) { - for (var i=0; i'; - me.editor.container.style.zIndex && (this.getDom().style.zIndex = me.editor.container.style.zIndex * 1 + 1); - } - } - // canSideUp:false, - // canSideLeft:false - }); - this.onbuttonclick = function(){ - this.showPopup(); - }; - this.initSplitButton(); - } - - }; - - utils.inherits(MultiMenuPop, SplitButton); -})(); - - -// ui/shortcutmenu.js -(function () { - var UI = baidu.editor.ui, - UIBase = UI.UIBase, - uiUtils = UI.uiUtils, - utils = baidu.editor.utils, - domUtils = baidu.editor.dom.domUtils; - - var allMenus = [],//存储所有快捷菜单 - timeID, - isSubMenuShow = false;//是否有子pop显示 - - var ShortCutMenu = UI.ShortCutMenu = function (options) { - this.initOptions (options); - this.initShortCutMenu (); - }; - - ShortCutMenu.postHide = hideAllMenu; - - ShortCutMenu.prototype = { - isHidden : true , - SPACE : 5 , - initShortCutMenu : function () { - this.items = this.items || []; - this.initUIBase (); - this.initItems (); - this.initEvent (); - allMenus.push (this); - } , - initEvent : function () { - var me = this, - doc = me.editor.document; - - domUtils.on (doc , "mousemove" , function (e) { - if (me.isHidden === false) { - //有pop显示就不隐藏快捷菜单 - if (me.getSubMenuMark () || me.eventType == "contextmenu") return; - - - var flag = true, - el = me.getDom (), - wt = el.offsetWidth, - ht = el.offsetHeight, - distanceX = wt / 2 + me.SPACE,//距离中心X标准 - distanceY = ht / 2,//距离中心Y标准 - x = Math.abs (e.screenX - me.left),//离中心距离横坐标 - y = Math.abs (e.screenY - me.top);//离中心距离纵坐标 - - clearTimeout (timeID); - timeID = setTimeout (function () { - if (y > 0 && y < distanceY) { - me.setOpacity (el , "1"); - } else if (y > distanceY && y < distanceY + 70) { - me.setOpacity (el , "0.5"); - flag = false; - } else if (y > distanceY + 70 && y < distanceY + 140) { - me.hide (); - } - - if (flag && x > 0 && x < distanceX) { - me.setOpacity (el , "1") - } else if (x > distanceX && x < distanceX + 70) { - me.setOpacity (el , "0.5") - } else if (x > distanceX + 70 && x < distanceX + 140) { - me.hide (); - } - }); - } - }); - - //ie\ff下 mouseout不准 - if (browser.chrome) { - domUtils.on (doc , "mouseout" , function (e) { - var relatedTgt = e.relatedTarget || e.toElement; - - if (relatedTgt == null || relatedTgt.tagName == "HTML") { - me.hide (); - } - }); - } - - me.editor.addListener ("afterhidepop" , function () { - if (!me.isHidden) { - isSubMenuShow = true; - } - }); - - } , - initItems : function () { - if (utils.isArray (this.items)) { - for (var i = 0, len = this.items.length ; i < len ; i++) { - var item = this.items[i].toLowerCase (); - - if (UI[item]) { - this.items[i] = new UI[item] (this.editor); - this.items[i].className += " edui-shortcutsubmenu "; - } - } - } - } , - setOpacity : function (el , value) { - if (browser.ie && browser.version < 9) { - el.style.filter = "alpha(opacity = " + parseFloat (value) * 100 + ");" - } else { - el.style.opacity = value; - } - } , - getSubMenuMark : function () { - isSubMenuShow = false; - var layerEle = uiUtils.getFixedLayer (); - var list = domUtils.getElementsByTagName (layerEle , "div" , function (node) { - return domUtils.hasClass (node , "edui-shortcutsubmenu edui-popup") - }); - - for (var i = 0, node ; node = list[i++] ;) { - if (node.style.display != "none") { - isSubMenuShow = true; - } - } - return isSubMenuShow; - } , - show : function (e , hasContextmenu) { - var me = this, - offset = {}, - el = this.getDom (), - fixedlayer = uiUtils.getFixedLayer (); - - function setPos (offset) { - if (offset.left < 0) { - offset.left = 0; - } - if (offset.top < 0) { - offset.top = 0; - } - el.style.cssText = "position:absolute;left:" + offset.left + "px;top:" + offset.top + "px;"; - } - - function setPosByCxtMenu (menu) { - if (!menu.tagName) { - menu = menu.getDom (); - } - offset.left = parseInt (menu.style.left); - offset.top = parseInt (menu.style.top); - offset.top -= el.offsetHeight + 15; - setPos (offset); - } - - - me.eventType = e.type; - el.style.cssText = "display:block;left:-9999px"; - - if (e.type == "contextmenu" && hasContextmenu) { - var menu = domUtils.getElementsByTagName (fixedlayer , "div" , "edui-contextmenu")[0]; - if (menu) { - setPosByCxtMenu (menu) - } else { - me.editor.addListener ("aftershowcontextmenu" , function (type , menu) { - setPosByCxtMenu (menu); - }); - } - } else { - offset = uiUtils.getViewportOffsetByEvent (e); - offset.top -= el.offsetHeight + me.SPACE; - offset.left += me.SPACE + 20; - setPos (offset); - me.setOpacity (el , 0.2); - } - - - me.isHidden = false; - me.left = e.screenX + el.offsetWidth / 2 - me.SPACE; - me.top = e.screenY - (el.offsetHeight / 2) - me.SPACE; - - if (me.editor) { - el.style.zIndex = me.editor.container.style.zIndex * 1 + 10; - fixedlayer.style.zIndex = el.style.zIndex - 1; - } - } , - hide : function () { - if (this.getDom ()) { - this.getDom ().style.display = "none"; - } - this.isHidden = true; - } , - postRender : function () { - if (utils.isArray (this.items)) { - for (var i = 0, item ; item = this.items[i++] ;) { - item.postRender (); - } - } - } , - getHtmlTpl : function () { - var buff; - if (utils.isArray (this.items)) { - buff = []; - for (var i = 0 ; i < this.items.length ; i++) { - buff[i] = this.items[i].renderHtml (); - } - buff = buff.join (""); - } else { - buff = this.items; - } - - return '
                  ' + - buff + - '
                  '; - } - }; - - utils.inherits (ShortCutMenu , UIBase); - - function hideAllMenu (e) { - var tgt = e.target || e.srcElement, - cur = domUtils.findParent (tgt , function (node) { - return domUtils.hasClass (node , "edui-shortcutmenu") || domUtils.hasClass (node , "edui-popup"); - } , true); - - if (!cur) { - for (var i = 0, menu ; menu = allMenus[i++] ;) { - menu.hide () - } - } - } - - domUtils.on (document , 'mousedown' , function (e) { - hideAllMenu (e); - }); - - domUtils.on (window , 'scroll' , function (e) { - hideAllMenu (e); - }); - -}) (); - - -// ui/breakline.js -(function (){ - var utils = baidu.editor.utils, - UIBase = baidu.editor.ui.UIBase, - Breakline = baidu.editor.ui.Breakline = function (options){ - this.initOptions(options); - this.initSeparator(); - }; - Breakline.prototype = { - uiName: 'Breakline', - initSeparator: function (){ - this.initUIBase(); - }, - getHtmlTpl: function (){ - return '
                  '; - } - }; - utils.inherits(Breakline, UIBase); - -})(); - - -// ui/message.js -///import core -///import uicore -(function () { - var utils = baidu.editor.utils, - domUtils = baidu.editor.dom.domUtils, - UIBase = baidu.editor.ui.UIBase, - Message = baidu.editor.ui.Message = function (options){ - this.initOptions(options); - this.initMessage(); - }; - - Message.prototype = { - initMessage: function (){ - this.initUIBase(); - }, - getHtmlTpl: function (){ - return '
                  ' + - '
                  ×
                  ' + - '
                  ' + - ' ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  '; - }, - reset: function(opt){ - var me = this; - if (!opt.keepshow) { - clearTimeout(this.timer); - me.timer = setTimeout(function(){ - me.hide(); - }, opt.timeout || 4000); - } - - opt.content !== undefined && me.setContent(opt.content); - opt.type !== undefined && me.setType(opt.type); - - me.show(); - }, - postRender: function(){ - var me = this, - closer = this.getDom('closer'); - closer && domUtils.on(closer, 'click', function(){ - me.hide(); - }); - }, - setContent: function(content){ - this.getDom('content').innerHTML = content; - }, - setType: function(type){ - type = type || 'info'; - var body = this.getDom('body'); - body.className = body.className.replace(/edui-message-type-[\w-]+/, 'edui-message-type-' + type); - }, - getContent: function(){ - return this.getDom('content').innerHTML; - }, - getType: function(){ - var arr = this.getDom('body').match(/edui-message-type-([\w-]+)/); - return arr ? arr[1]:''; - }, - show: function (){ - this.getDom().style.display = 'block'; - }, - hide: function (){ - var dom = this.getDom(); - if (dom) { - dom.style.display = 'none'; - dom.parentNode && dom.parentNode.removeChild(dom); - } - } - }; - - utils.inherits(Message, UIBase); - -})(); - - -// adapter/editorui.js -//ui跟编辑器的适配層 -//那个按钮弹出是dialog,是下拉筐等都是在这个js中配置 -//自己写的ui也要在这里配置,放到baidu.editor.ui下边,当编辑器实例化的时候会根据ueditor.config中的toolbars找到相应的进行实例化 -(function () { - var utils = baidu.editor.utils; - var editorui = baidu.editor.ui; - var _Dialog = editorui.Dialog; - editorui.buttons = {}; - - editorui.Dialog = function (options) { - var dialog = new _Dialog(options); - dialog.addListener('hide', function () { - - if (dialog.editor) { - var editor = dialog.editor; - try { - if (browser.gecko) { - var y = editor.window.scrollY, - x = editor.window.scrollX; - editor.body.focus(); - editor.window.scrollTo(x, y); - } else { - editor.focus(); - } - - - } catch (ex) { - } - } - }); - return dialog; - }; - - var iframeUrlMap = { - 'anchor':'~/dialogs/anchor/anchor.html', - 'insertimage':'~/dialogs/image/image.html', - 'link':'~/dialogs/link/link.html', - 'spechars':'~/dialogs/spechars/spechars.html', - 'searchreplace':'~/dialogs/searchreplace/searchreplace.html', - 'map':'~/dialogs/map/map.html', - 'gmap':'~/dialogs/gmap/gmap.html', - 'insertvideo':'~/dialogs/video/video.html', - 'help':'~/dialogs/help/help.html', - 'preview':'~/dialogs/preview/preview.html', - 'emotion':'~/dialogs/emotion/emotion.html', - 'wordimage':'~/dialogs/wordimage/wordimage.html', - 'attachment':'~/dialogs/attachment/attachment.html', - 'insertframe':'~/dialogs/insertframe/insertframe.html', - 'edittip':'~/dialogs/table/edittip.html', - 'edittable':'~/dialogs/table/edittable.html', - 'edittd':'~/dialogs/table/edittd.html', - 'webapp':'~/dialogs/webapp/webapp.html', - 'snapscreen':'~/dialogs/snapscreen/snapscreen.html', - 'scrawl':'~/dialogs/scrawl/scrawl.html', - 'music':'~/dialogs/music/music.html', - 'template':'~/dialogs/template/template.html', - 'background':'~/dialogs/background/background.html', - 'charts': '~/dialogs/charts/charts.html' - }; - //为工具栏添加按钮,以下都是统一的按钮触发命令,所以写在一起 - var btnCmds = ['undo', 'redo', 'formatmatch', - 'bold', 'italic', 'underline', 'fontborder', 'touppercase', 'tolowercase', - 'strikethrough', 'subscript', 'superscript', 'source', 'indent', 'outdent', - 'blockquote', 'pasteplain', 'pagebreak', - 'selectall', 'print','horizontal', 'removeformat', 'time', 'date', 'unlink', - 'insertparagraphbeforetable', 'insertrow', 'insertcol', 'mergeright', 'mergedown', 'deleterow', - 'deletecol', 'splittorows', 'splittocols', 'splittocells', 'mergecells', 'deletetable', 'drafts']; - - for (var i = 0, ci; ci = btnCmds[i++];) { - ci = ci.toLowerCase(); - editorui[ci] = function (cmd) { - return function (editor) { - var ui = new editorui.Button({ - className:'edui-for-' + cmd, - title:editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '', - onclick:function () { - editor.execCommand(cmd); - }, - theme:editor.options.theme, - showText:false - }); - editorui.buttons[cmd] = ui; - editor.addListener('selectionchange', function (type, causeByUi, uiReady) { - var state = editor.queryCommandState(cmd); - if (state == -1) { - ui.setDisabled(true); - ui.setChecked(false); - } else { - if (!uiReady) { - ui.setDisabled(false); - ui.setChecked(state); - } - } - }); - return ui; - }; - }(ci); - } - - //清除文档 - editorui.cleardoc = function (editor) { - var ui = new editorui.Button({ - className:'edui-for-cleardoc', - title:editor.options.labelMap.cleardoc || editor.getLang("labelMap.cleardoc") || '', - theme:editor.options.theme, - onclick:function () { - if (confirm(editor.getLang("confirmClear"))) { - editor.execCommand('cleardoc'); - } - } - }); - editorui.buttons["cleardoc"] = ui; - editor.addListener('selectionchange', function () { - ui.setDisabled(editor.queryCommandState('cleardoc') == -1); - }); - return ui; - }; - - //排版,图片排版,文字方向 - var typeset = { - 'justify':['left', 'right', 'center', 'justify'], - 'imagefloat':['none', 'left', 'center', 'right'], - 'directionality':['ltr', 'rtl'] - }; - - for (var p in typeset) { - - (function (cmd, val) { - for (var i = 0, ci; ci = val[i++];) { - (function (cmd2) { - editorui[cmd.replace('float', '') + cmd2] = function (editor) { - var ui = new editorui.Button({ - className:'edui-for-' + cmd.replace('float', '') + cmd2, - title:editor.options.labelMap[cmd.replace('float', '') + cmd2] || editor.getLang("labelMap." + cmd.replace('float', '') + cmd2) || '', - theme:editor.options.theme, - onclick:function () { - editor.execCommand(cmd, cmd2); - } - }); - editorui.buttons[cmd] = ui; - editor.addListener('selectionchange', function (type, causeByUi, uiReady) { - ui.setDisabled(editor.queryCommandState(cmd) == -1); - ui.setChecked(editor.queryCommandValue(cmd) == cmd2 && !uiReady); - }); - return ui; - }; - })(ci) - } - })(p, typeset[p]) - } - - //字体颜色和背景颜色 - for (var i = 0, ci; ci = ['backcolor', 'forecolor'][i++];) { - editorui[ci] = function (cmd) { - return function (editor) { - var ui = new editorui.ColorButton({ - className:'edui-for-' + cmd, - color:'default', - title:editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '', - editor:editor, - onpickcolor:function (t, color) { - editor.execCommand(cmd, color); - }, - onpicknocolor:function () { - editor.execCommand(cmd, 'default'); - this.setColor('transparent'); - this.color = 'default'; - }, - onbuttonclick:function () { - editor.execCommand(cmd, this.color); - } - }); - editorui.buttons[cmd] = ui; - editor.addListener('selectionchange', function () { - ui.setDisabled(editor.queryCommandState(cmd) == -1); - }); - return ui; - }; - }(ci); - } - - - var dialogBtns = { - noOk:['searchreplace', 'help', 'spechars', 'webapp','preview'], - ok:['attachment', 'anchor', 'link', 'insertimage', 'map', 'gmap', 'insertframe', 'wordimage', - 'insertvideo', 'insertframe', 'edittip', 'edittable', 'edittd', 'scrawl', 'template', 'music', 'background', 'charts'] - }; - - for (var p in dialogBtns) { - (function (type, vals) { - for (var i = 0, ci; ci = vals[i++];) { - //todo opera下存在问题 - if (browser.opera && ci === "searchreplace") { - continue; - } - (function (cmd) { - editorui[cmd] = function (editor, iframeUrl, title) { - iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd]; - title = editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || ''; - - var dialog; - //没有iframeUrl不创建dialog - if (iframeUrl) { - dialog = new editorui.Dialog(utils.extend({ - iframeUrl:editor.ui.mapUrl(iframeUrl), - editor:editor, - className:'edui-for-' + cmd, - title:title, - holdScroll: cmd === 'insertimage', - fullscreen: /charts|preview/.test(cmd), - closeDialog:editor.getLang("closeDialog") - }, type == 'ok' ? { - buttons:[ - { - className:'edui-okbutton', - label:editor.getLang("ok"), - editor:editor, - onclick:function () { - dialog.close(true); - } - }, - { - className:'edui-cancelbutton', - label:editor.getLang("cancel"), - editor:editor, - onclick:function () { - dialog.close(false); - } - } - ] - } : {})); - - editor.ui._dialogs[cmd + "Dialog"] = dialog; - } - - var ui = new editorui.Button({ - className:'edui-for-' + cmd, - title:title, - onclick:function () { - if (dialog) { - switch (cmd) { - case "wordimage": - var images = editor.execCommand("wordimage"); - if (images && images.length) { - dialog.render(); - dialog.open(); - } - break; - case "scrawl": - if (editor.queryCommandState("scrawl") != -1) { - dialog.render(); - dialog.open(); - } - - break; - default: - dialog.render(); - dialog.open(); - } - } - }, - theme:editor.options.theme, - disabled:(cmd == 'scrawl' && editor.queryCommandState("scrawl") == -1) || ( cmd == 'charts' ) - }); - editorui.buttons[cmd] = ui; - editor.addListener('selectionchange', function () { - //只存在于右键菜单而无工具栏按钮的ui不需要检测状态 - var unNeedCheckState = {'edittable':1}; - if (cmd in unNeedCheckState)return; - - var state = editor.queryCommandState(cmd); - if (ui.getDom()) { - ui.setDisabled(state == -1); - ui.setChecked(state); - } - - }); - - return ui; - }; - })(ci.toLowerCase()) - } - })(p, dialogBtns[p]); - } - - editorui.snapscreen = function (editor, iframeUrl, title) { - title = editor.options.labelMap['snapscreen'] || editor.getLang("labelMap.snapscreen") || ''; - var ui = new editorui.Button({ - className:'edui-for-snapscreen', - title:title, - onclick:function () { - editor.execCommand("snapscreen"); - }, - theme:editor.options.theme - - }); - editorui.buttons['snapscreen'] = ui; - iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})["snapscreen"] || iframeUrlMap["snapscreen"]; - if (iframeUrl) { - var dialog = new editorui.Dialog({ - iframeUrl:editor.ui.mapUrl(iframeUrl), - editor:editor, - className:'edui-for-snapscreen', - title:title, - buttons:[ - { - className:'edui-okbutton', - label:editor.getLang("ok"), - editor:editor, - onclick:function () { - dialog.close(true); - } - }, - { - className:'edui-cancelbutton', - label:editor.getLang("cancel"), - editor:editor, - onclick:function () { - dialog.close(false); - } - } - ] - - }); - dialog.render(); - editor.ui._dialogs["snapscreenDialog"] = dialog; - } - editor.addListener('selectionchange', function () { - ui.setDisabled(editor.queryCommandState('snapscreen') == -1); - }); - return ui; - }; - - editorui.insertcode = function (editor, list, title) { - list = editor.options['insertcode'] || []; - title = editor.options.labelMap['insertcode'] || editor.getLang("labelMap.insertcode") || ''; - // if (!list.length) return; - var items = []; - utils.each(list,function(key,val){ - items.push({ - label:key, - value:val, - theme:editor.options.theme, - renderLabelHtml:function () { - return '
                  ' + (this.label || '') + '
                  '; - } - }); - }); - - var ui = new editorui.Combox({ - editor:editor, - items:items, - onselect:function (t, index) { - editor.execCommand('insertcode', this.items[index].value); - }, - onbuttonclick:function () { - this.showPopup(); - }, - title:title, - initValue:title, - className:'edui-for-insertcode', - indexByValue:function (value) { - if (value) { - for (var i = 0, ci; ci = this.items[i]; i++) { - if (ci.value.indexOf(value) != -1) - return i; - } - } - - return -1; - } - }); - editorui.buttons['insertcode'] = ui; - editor.addListener('selectionchange', function (type, causeByUi, uiReady) { - if (!uiReady) { - var state = editor.queryCommandState('insertcode'); - if (state == -1) { - ui.setDisabled(true); - } else { - ui.setDisabled(false); - var value = editor.queryCommandValue('insertcode'); - if(!value){ - ui.setValue(title); - return; - } - //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号 - value && (value = value.replace(/['"]/g, '').split(',')[0]); - ui.setValue(value); - - } - } - - }); - return ui; - }; - editorui.fontfamily = function (editor, list, title) { - - list = editor.options['fontfamily'] || []; - title = editor.options.labelMap['fontfamily'] || editor.getLang("labelMap.fontfamily") || ''; - if (!list.length) return; - for (var i = 0, ci, items = []; ci = list[i]; i++) { - var langLabel = editor.getLang('fontfamily')[ci.name] || ""; - (function (key, val) { - items.push({ - label:key, - value:val, - theme:editor.options.theme, - renderLabelHtml:function () { - return '
                  ' + (this.label || '') + '
                  '; - } - }); - })(ci.label || langLabel, ci.val) - } - var ui = new editorui.Combox({ - editor:editor, - items:items, - onselect:function (t, index) { - editor.execCommand('FontFamily', this.items[index].value); - }, - onbuttonclick:function () { - this.showPopup(); - }, - title:title, - initValue:title, - className:'edui-for-fontfamily', - indexByValue:function (value) { - if (value) { - for (var i = 0, ci; ci = this.items[i]; i++) { - if (ci.value.indexOf(value) != -1) - return i; - } - } - - return -1; - } - }); - editorui.buttons['fontfamily'] = ui; - editor.addListener('selectionchange', function (type, causeByUi, uiReady) { - if (!uiReady) { - var state = editor.queryCommandState('FontFamily'); - if (state == -1) { - ui.setDisabled(true); - } else { - ui.setDisabled(false); - var value = editor.queryCommandValue('FontFamily'); - //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号 - value && (value = value.replace(/['"]/g, '').split(',')[0]); - ui.setValue(value); - - } - } - - }); - return ui; - }; - - editorui.fontsize = function (editor, list, title) { - title = editor.options.labelMap['fontsize'] || editor.getLang("labelMap.fontsize") || ''; - list = list || editor.options['fontsize'] || []; - if (!list.length) return; - var items = []; - for (var i = 0; i < list.length; i++) { - var size = list[i] + 'px'; - items.push({ - label:size, - value:size, - theme:editor.options.theme, - renderLabelHtml:function () { - return '
                  ' + (this.label || '') + '
                  '; - } - }); - } - var ui = new editorui.Combox({ - editor:editor, - items:items, - title:title, - initValue:title, - onselect:function (t, index) { - editor.execCommand('FontSize', this.items[index].value); - }, - onbuttonclick:function () { - this.showPopup(); - }, - className:'edui-for-fontsize' - }); - editorui.buttons['fontsize'] = ui; - editor.addListener('selectionchange', function (type, causeByUi, uiReady) { - if (!uiReady) { - var state = editor.queryCommandState('FontSize'); - if (state == -1) { - ui.setDisabled(true); - } else { - ui.setDisabled(false); - ui.setValue(editor.queryCommandValue('FontSize')); - } - } - - }); - return ui; - }; - - editorui.paragraph = function (editor, list, title) { - title = editor.options.labelMap['paragraph'] || editor.getLang("labelMap.paragraph") || ''; - list = editor.options['paragraph'] || []; - if (utils.isEmptyObject(list)) return; - var items = []; - for (var i in list) { - items.push({ - value:i, - label:list[i] || editor.getLang("paragraph")[i], - theme:editor.options.theme, - renderLabelHtml:function () { - return '
                  ' + (this.label || '') + '
                  '; - } - }) - } - var ui = new editorui.Combox({ - editor:editor, - items:items, - title:title, - initValue:title, - className:'edui-for-paragraph', - onselect:function (t, index) { - editor.execCommand('Paragraph', this.items[index].value); - }, - onbuttonclick:function () { - this.showPopup(); - } - }); - editorui.buttons['paragraph'] = ui; - editor.addListener('selectionchange', function (type, causeByUi, uiReady) { - if (!uiReady) { - var state = editor.queryCommandState('Paragraph'); - if (state == -1) { - ui.setDisabled(true); - } else { - ui.setDisabled(false); - var value = editor.queryCommandValue('Paragraph'); - var index = ui.indexByValue(value); - if (index != -1) { - ui.setValue(value); - } else { - ui.setValue(ui.initValue); - } - } - } - - }); - return ui; - }; - - - //自定义标题 - editorui.customstyle = function (editor) { - var list = editor.options['customstyle'] || [], - title = editor.options.labelMap['customstyle'] || editor.getLang("labelMap.customstyle") || ''; - if (!list.length)return; - var langCs = editor.getLang('customstyle'); - for (var i = 0, items = [], t; t = list[i++];) { - (function (t) { - var ck = {}; - ck.label = t.label ? t.label : langCs[t.name]; - ck.style = t.style; - ck.className = t.className; - ck.tag = t.tag; - items.push({ - label:ck.label, - value:ck, - theme:editor.options.theme, - renderLabelHtml:function () { - return '
                  ' + '<' + ck.tag + ' ' + (ck.className ? ' class="' + ck.className + '"' : "") - + (ck.style ? ' style="' + ck.style + '"' : "") + '>' + ck.label + "<\/" + ck.tag + ">" - + '
                  '; - } - }); - })(t); - } - - var ui = new editorui.Combox({ - editor:editor, - items:items, - title:title, - initValue:title, - className:'edui-for-customstyle', - onselect:function (t, index) { - editor.execCommand('customstyle', this.items[index].value); - }, - onbuttonclick:function () { - this.showPopup(); - }, - indexByValue:function (value) { - for (var i = 0, ti; ti = this.items[i++];) { - if (ti.label == value) { - return i - 1 - } - } - return -1; - } - }); - editorui.buttons['customstyle'] = ui; - editor.addListener('selectionchange', function (type, causeByUi, uiReady) { - if (!uiReady) { - var state = editor.queryCommandState('customstyle'); - if (state == -1) { - ui.setDisabled(true); - } else { - ui.setDisabled(false); - var value = editor.queryCommandValue('customstyle'); - var index = ui.indexByValue(value); - if (index != -1) { - ui.setValue(value); - } else { - ui.setValue(ui.initValue); - } - } - } - - }); - return ui; - }; - editorui.inserttable = function (editor, iframeUrl, title) { - title = editor.options.labelMap['inserttable'] || editor.getLang("labelMap.inserttable") || ''; - var ui = new editorui.TableButton({ - editor:editor, - title:title, - className:'edui-for-inserttable', - onpicktable:function (t, numCols, numRows) { - editor.execCommand('InsertTable', {numRows:numRows, numCols:numCols, border:1}); - }, - onbuttonclick:function () { - this.showPopup(); - } - }); - editorui.buttons['inserttable'] = ui; - editor.addListener('selectionchange', function () { - ui.setDisabled(editor.queryCommandState('inserttable') == -1); - }); - return ui; - }; - - editorui.lineheight = function (editor) { - var val = editor.options.lineheight || []; - if (!val.length)return; - for (var i = 0, ci, items = []; ci = val[i++];) { - items.push({ - //todo:写死了 - label:ci, - value:ci, - theme:editor.options.theme, - onclick:function () { - editor.execCommand("lineheight", this.value); - } - }) - } - var ui = new editorui.MenuButton({ - editor:editor, - className:'edui-for-lineheight', - title:editor.options.labelMap['lineheight'] || editor.getLang("labelMap.lineheight") || '', - items:items, - onbuttonclick:function () { - var value = editor.queryCommandValue('LineHeight') || this.value; - editor.execCommand("LineHeight", value); - } - }); - editorui.buttons['lineheight'] = ui; - editor.addListener('selectionchange', function () { - var state = editor.queryCommandState('LineHeight'); - if (state == -1) { - ui.setDisabled(true); - } else { - ui.setDisabled(false); - var value = editor.queryCommandValue('LineHeight'); - value && ui.setValue((value + '').replace(/cm/, '')); - ui.setChecked(state) - } - }); - return ui; - }; - - var rowspacings = ['top', 'bottom']; - for (var r = 0, ri; ri = rowspacings[r++];) { - (function (cmd) { - editorui['rowspacing' + cmd] = function (editor) { - var val = editor.options['rowspacing' + cmd] || []; - if (!val.length) return null; - for (var i = 0, ci, items = []; ci = val[i++];) { - items.push({ - label:ci, - value:ci, - theme:editor.options.theme, - onclick:function () { - editor.execCommand("rowspacing", this.value, cmd); - } - }) - } - var ui = new editorui.MenuButton({ - editor:editor, - className:'edui-for-rowspacing' + cmd, - title:editor.options.labelMap['rowspacing' + cmd] || editor.getLang("labelMap.rowspacing" + cmd) || '', - items:items, - onbuttonclick:function () { - var value = editor.queryCommandValue('rowspacing', cmd) || this.value; - editor.execCommand("rowspacing", value, cmd); - } - }); - editorui.buttons[cmd] = ui; - editor.addListener('selectionchange', function () { - var state = editor.queryCommandState('rowspacing', cmd); - if (state == -1) { - ui.setDisabled(true); - } else { - ui.setDisabled(false); - var value = editor.queryCommandValue('rowspacing', cmd); - value && ui.setValue((value + '').replace(/%/, '')); - ui.setChecked(state) - } - }); - return ui; - } - })(ri) - } - //有序,无序列表 - var lists = ['insertorderedlist', 'insertunorderedlist']; - for (var l = 0, cl; cl = lists[l++];) { - (function (cmd) { - editorui[cmd] = function (editor) { - var vals = editor.options[cmd], - _onMenuClick = function () { - editor.execCommand(cmd, this.value); - }, items = []; - for (var i in vals) { - items.push({ - label:vals[i] || editor.getLang()[cmd][i] || "", - value:i, - theme:editor.options.theme, - onclick:_onMenuClick - }) - } - var ui = new editorui.MenuButton({ - editor:editor, - className:'edui-for-' + cmd, - title:editor.getLang("labelMap." + cmd) || '', - 'items':items, - onbuttonclick:function () { - var value = editor.queryCommandValue(cmd) || this.value; - editor.execCommand(cmd, value); - } - }); - editorui.buttons[cmd] = ui; - editor.addListener('selectionchange', function () { - var state = editor.queryCommandState(cmd); - if (state == -1) { - ui.setDisabled(true); - } else { - ui.setDisabled(false); - var value = editor.queryCommandValue(cmd); - ui.setValue(value); - ui.setChecked(state) - } - }); - return ui; - }; - })(cl) - } - - editorui.fullscreen = function (editor, title) { - title = editor.options.labelMap['fullscreen'] || editor.getLang("labelMap.fullscreen") || ''; - var ui = new editorui.Button({ - className:'edui-for-fullscreen', - title:title, - theme:editor.options.theme, - onclick:function () { - if (editor.ui) { - editor.ui.setFullScreen(!editor.ui.isFullScreen()); - } - this.setChecked(editor.ui.isFullScreen()); - } - }); - editorui.buttons['fullscreen'] = ui; - editor.addListener('selectionchange', function () { - var state = editor.queryCommandState('fullscreen'); - ui.setDisabled(state == -1); - ui.setChecked(editor.ui.isFullScreen()); - }); - return ui; - }; - - // 表情 - editorui["emotion"] = function (editor, iframeUrl) { - var cmd = "emotion"; - var ui = new editorui.MultiMenuPop({ - title:editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd + "") || '', - editor:editor, - className:'edui-for-' + cmd, - iframeUrl:editor.ui.mapUrl(iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd]) - }); - editorui.buttons[cmd] = ui; - - editor.addListener('selectionchange', function () { - ui.setDisabled(editor.queryCommandState(cmd) == -1) - }); - return ui; - }; - - editorui.autotypeset = function (editor) { - var ui = new editorui.AutoTypeSetButton({ - editor:editor, - title:editor.options.labelMap['autotypeset'] || editor.getLang("labelMap.autotypeset") || '', - className:'edui-for-autotypeset', - onbuttonclick:function () { - editor.execCommand('autotypeset') - } - }); - editorui.buttons['autotypeset'] = ui; - editor.addListener('selectionchange', function () { - ui.setDisabled(editor.queryCommandState('autotypeset') == -1); - }); - return ui; - }; - - /* 简单上传插件 */ - editorui["simpleupload"] = function (editor) { - var name = 'simpleupload', - ui = new editorui.Button({ - className:'edui-for-' + name, - title:editor.options.labelMap[name] || editor.getLang("labelMap." + name) || '', - onclick:function () {}, - theme:editor.options.theme, - showText:false - }); - editorui.buttons[name] = ui; - editor.addListener('ready', function() { - var b = ui.getDom('body'), - iconSpan = b.children[0]; - editor.fireEvent('simpleuploadbtnready', iconSpan); - }); - editor.addListener('selectionchange', function (type, causeByUi, uiReady) { - var state = editor.queryCommandState(name); - if (state == -1) { - ui.setDisabled(true); - ui.setChecked(false); - } else { - if (!uiReady) { - ui.setDisabled(false); - ui.setChecked(state); - } - } - }); - return ui; - }; - -})(); - - -// adapter/editor.js -///import core -///commands 全屏 -///commandsName FullScreen -///commandsTitle 全屏 -(function () { - var utils = baidu.editor.utils, - uiUtils = baidu.editor.ui.uiUtils, - UIBase = baidu.editor.ui.UIBase, - domUtils = baidu.editor.dom.domUtils; - var nodeStack = []; - - function EditorUI(options) { - this.initOptions(options); - this.initEditorUI(); - } - - EditorUI.prototype = { - uiName:'editor', - initEditorUI:function () { - this.editor.ui = this; - this._dialogs = {}; - this.initUIBase(); - this._initToolbars(); - var editor = this.editor, - me = this; - - editor.addListener('ready', function () { - //提供getDialog方法 - editor.getDialog = function (name) { - return editor.ui._dialogs[name + "Dialog"]; - }; - domUtils.on(editor.window, 'scroll', function (evt) { - baidu.editor.ui.Popup.postHide(evt); - }); - //提供编辑器实时宽高(全屏时宽高不变化) - editor.ui._actualFrameWidth = editor.options.initialFrameWidth; - - UE.browser.ie && UE.browser.version === 6 && editor.container.ownerDocument.execCommand("BackgroundImageCache", false, true); - - //display bottom-bar label based on config - if (editor.options.elementPathEnabled) { - editor.ui.getDom('elementpath').innerHTML = '
                  ' + editor.getLang("elementPathTip") + ':
                  '; - } - if (editor.options.wordCount) { - function countFn() { - setCount(editor,me); - domUtils.un(editor.document, "click", arguments.callee); - } - domUtils.on(editor.document, "click", countFn); - editor.ui.getDom('wordcount').innerHTML = editor.getLang("wordCountTip"); - } - editor.ui._scale(); - if (editor.options.scaleEnabled) { - if (editor.autoHeightEnabled) { - editor.disableAutoHeight(); - } - me.enableScale(); - } else { - me.disableScale(); - } - if (!editor.options.elementPathEnabled && !editor.options.wordCount && !editor.options.scaleEnabled) { - editor.ui.getDom('elementpath').style.display = "none"; - editor.ui.getDom('wordcount').style.display = "none"; - editor.ui.getDom('scale').style.display = "none"; - } - - if (!editor.selection.isFocus())return; - editor.fireEvent('selectionchange', false, true); - - - }); - - editor.addListener('mousedown', function (t, evt) { - var el = evt.target || evt.srcElement; - baidu.editor.ui.Popup.postHide(evt, el); - baidu.editor.ui.ShortCutMenu.postHide(evt); - - }); - editor.addListener("delcells", function () { - if (UE.ui['edittip']) { - new UE.ui['edittip'](editor); - } - editor.getDialog('edittip').open(); - }); - - var pastePop, isPaste = false, timer; - editor.addListener("afterpaste", function () { - if(editor.queryCommandState('pasteplain')) - return; - if(baidu.editor.ui.PastePicker){ - pastePop = new baidu.editor.ui.Popup({ - content:new baidu.editor.ui.PastePicker({editor:editor}), - editor:editor, - className:'edui-wordpastepop' - }); - pastePop.render(); - } - isPaste = true; - }); - - editor.addListener("afterinserthtml", function () { - clearTimeout(timer); - timer = setTimeout(function () { - if (pastePop && (isPaste || editor.ui._isTransfer)) { - if(pastePop.isHidden()){ - var span = domUtils.createElement(editor.document, 'span', { - 'style':"line-height:0px;", - 'innerHTML':'\ufeff' - }), - range = editor.selection.getRange(); - range.insertNode(span); - var tmp= getDomNode(span, 'firstChild', 'previousSibling'); - tmp && pastePop.showAnchor(tmp.nodeType == 3 ? tmp.parentNode : tmp); - domUtils.remove(span); - }else{ - pastePop.show(); - } - delete editor.ui._isTransfer; - isPaste = false; - } - }, 200) - }); - editor.addListener('contextmenu', function (t, evt) { - baidu.editor.ui.Popup.postHide(evt); - }); - editor.addListener('keydown', function (t, evt) { - if (pastePop) pastePop.dispose(evt); - var keyCode = evt.keyCode || evt.which; - if(evt.altKey&&keyCode==90){ - UE.ui.buttons['fullscreen'].onclick(); - } - }); - editor.addListener('wordcount', function (type) { - setCount(this,me); - }); - function setCount(editor,ui) { - editor.setOpt({ - wordCount:true, - maximumWords:10000, - wordCountMsg:editor.options.wordCountMsg || editor.getLang("wordCountMsg"), - wordOverFlowMsg:editor.options.wordOverFlowMsg || editor.getLang("wordOverFlowMsg") - }); - var opt = editor.options, - max = opt.maximumWords, - msg = opt.wordCountMsg , - errMsg = opt.wordOverFlowMsg, - countDom = ui.getDom('wordcount'); - if (!opt.wordCount) { - return; - } - var count = editor.getContentLength(true); - if (count > max) { - countDom.innerHTML = errMsg; - editor.fireEvent("wordcountoverflow"); - } else { - countDom.innerHTML = msg.replace("{#leave}", max - count).replace("{#count}", count); - } - } - - editor.addListener('selectionchange', function () { - if (editor.options.elementPathEnabled) { - me[(editor.queryCommandState('elementpath') == -1 ? 'dis' : 'en') + 'ableElementPath']() - } - if (editor.options.scaleEnabled) { - me[(editor.queryCommandState('scale') == -1 ? 'dis' : 'en') + 'ableScale'](); - - } - }); - var popup = new baidu.editor.ui.Popup({ - editor:editor, - content:'', - className:'edui-bubble', - _onEditButtonClick:function () { - this.hide(); - editor.ui._dialogs.linkDialog.open(); - }, - _onImgEditButtonClick:function (name) { - this.hide(); - editor.ui._dialogs[name] && editor.ui._dialogs[name].open(); - - }, - _onImgSetFloat:function (value) { - this.hide(); - editor.execCommand("imagefloat", value); - - }, - _setIframeAlign:function (value) { - var frame = popup.anchorEl; - var newFrame = frame.cloneNode(true); - switch (value) { - case -2: - newFrame.setAttribute("align", ""); - break; - case -1: - newFrame.setAttribute("align", "left"); - break; - case 1: - newFrame.setAttribute("align", "right"); - break; - } - frame.parentNode.insertBefore(newFrame, frame); - domUtils.remove(frame); - popup.anchorEl = newFrame; - popup.showAnchor(popup.anchorEl); - }, - _updateIframe:function () { - var frame = editor._iframe = popup.anchorEl; - if(domUtils.hasClass(frame, 'ueditor_baidumap')) { - editor.selection.getRange().selectNode(frame).select(); - editor.ui._dialogs.mapDialog.open(); - popup.hide(); - } else { - editor.ui._dialogs.insertframeDialog.open(); - popup.hide(); - } - }, - _onRemoveButtonClick:function (cmdName) { - editor.execCommand(cmdName); - this.hide(); - }, - queryAutoHide:function (el) { - if (el && el.ownerDocument == editor.document) { - if (el.tagName.toLowerCase() == 'img' || domUtils.findParentByTagName(el, 'a', true)) { - return el !== popup.anchorEl; - } - } - return baidu.editor.ui.Popup.prototype.queryAutoHide.call(this, el); - } - }); - popup.render(); - if (editor.options.imagePopup) { - editor.addListener('mouseover', function (t, evt) { - evt = evt || window.event; - var el = evt.target || evt.srcElement; - if (editor.ui._dialogs.insertframeDialog && /iframe/ig.test(el.tagName)) { - var html = popup.formatHtml( - '' + editor.getLang("property") + ': ' + editor.getLang("default") + '  ' + editor.getLang("justifyleft") + '  ' + editor.getLang("justifyright") + '  ' + - ' ' + editor.getLang("modify") + ''); - if (html) { - popup.getDom('content').innerHTML = html; - popup.anchorEl = el; - popup.showAnchor(popup.anchorEl); - } else { - popup.hide(); - } - } - }); - editor.addListener('selectionchange', function (t, causeByUi) { - if (!causeByUi) return; - var html = '', str = "", - img = editor.selection.getRange().getClosedNode(), - dialogs = editor.ui._dialogs; - if (img && img.tagName == 'IMG') { - var dialogName = 'insertimageDialog'; - if (img.className.indexOf("edui-faked-video") != -1 || img.className.indexOf("edui-upload-video") != -1) { - dialogName = "insertvideoDialog" - } - if (img.className.indexOf("edui-faked-webapp") != -1) { - dialogName = "webappDialog" - } - if (img.src.indexOf("http://api.map.baidu.com") != -1) { - dialogName = "mapDialog" - } - if (img.className.indexOf("edui-faked-music") != -1) { - dialogName = "musicDialog" - } - if (img.src.indexOf("http://maps.google.com/maps/api/staticmap") != -1) { - dialogName = "gmapDialog" - } - if (img.getAttribute("anchorname")) { - dialogName = "anchorDialog"; - html = popup.formatHtml( - '' + editor.getLang("property") + ': ' + editor.getLang("modify") + '  ' + - '' + editor.getLang("delete") + ''); - } - if (img.getAttribute("word_img")) { - //todo 放到dialog去做查询 - editor.word_img = [img.getAttribute("word_img")]; - dialogName = "wordimageDialog" - } - if(domUtils.hasClass(img, 'loadingclass') || domUtils.hasClass(img, 'loaderrorclass')) { - dialogName = ""; - } - if (!dialogs[dialogName]) { - return; - } - str = '' + editor.getLang("property") + ': '+ - '' + editor.getLang("default") + '  ' + - '' + editor.getLang("justifyleft") + '  ' + - '' + editor.getLang("justifyright") + '  ' + - '' + editor.getLang("justifycenter") + '  '+ - '' + editor.getLang("modify") + ''; - - !html && (html = popup.formatHtml(str)) - - } - if (editor.ui._dialogs.linkDialog) { - var link = editor.queryCommandValue('link'); - var url; - if (link && (url = (link.getAttribute('_href') || link.getAttribute('href', 2)))) { - var txt = url; - if (url.length > 30) { - txt = url.substring(0, 20) + "..."; - } - if (html) { - html += '
                  ' - } - html += popup.formatHtml( - '' + editor.getLang("anthorMsg") + ': ' + txt + '' + - ' ' + editor.getLang("modify") + '' + - ' ' + editor.getLang("clear") + ''); - popup.showAnchor(link); - } - } - - if (html) { - popup.getDom('content').innerHTML = html; - popup.anchorEl = img || link; - popup.showAnchor(popup.anchorEl); - } else { - popup.hide(); - } - }); - } - - }, - _initToolbars:function () { - var editor = this.editor; - var toolbars = this.toolbars || []; - var toolbarUis = []; - for (var i = 0; i < toolbars.length; i++) { - var toolbar = toolbars[i]; - var toolbarUi = new baidu.editor.ui.Toolbar({theme:editor.options.theme}); - for (var j = 0; j < toolbar.length; j++) { - var toolbarItem = toolbar[j]; - var toolbarItemUi = null; - if (typeof toolbarItem == 'string') { - toolbarItem = toolbarItem.toLowerCase(); - if (toolbarItem == '|') { - toolbarItem = 'Separator'; - } - if(toolbarItem == '||'){ - toolbarItem = 'Breakline'; - } - if (baidu.editor.ui[toolbarItem]) { - toolbarItemUi = new baidu.editor.ui[toolbarItem](editor); - } - - //fullscreen这里单独处理一下,放到首行去 - if (toolbarItem == 'fullscreen') { - if (toolbarUis && toolbarUis[0]) { - toolbarUis[0].items.splice(0, 0, toolbarItemUi); - } else { - toolbarItemUi && toolbarUi.items.splice(0, 0, toolbarItemUi); - } - - continue; - - - } - } else { - toolbarItemUi = toolbarItem; - } - if (toolbarItemUi && toolbarItemUi.id) { - - toolbarUi.add(toolbarItemUi); - } - } - toolbarUis[i] = toolbarUi; - } - - //接受外部定制的UI - - utils.each(UE._customizeUI,function(obj,key){ - var itemUI,index; - if(obj.id && obj.id != editor.key){ - return false; - } - itemUI = obj.execFn.call(editor,editor,key); - if(itemUI){ - index = obj.index; - if(index === undefined){ - index = toolbarUi.items.length; - } - toolbarUi.add(itemUI,index) - } - }); - - this.toolbars = toolbarUis; - }, - getHtmlTpl:function () { - return '
                  ' + - '
                  ' + - (this.toolbars.length ? - '
                  ' + - this.renderToolbarBoxHtml() + - '
                  ' : '') + - '' + - '
                  ' + - '
                  ' + - '
                  ' + - '
                  ' + - //modify wdcount by matao - '
                  ' + - '' + - '' + - '' + - '
                  ' + - '
                  ' + - '
                  '; - }, - showWordImageDialog:function () { - this._dialogs['wordimageDialog'].open(); - }, - renderToolbarBoxHtml:function () { - var buff = []; - for (var i = 0; i < this.toolbars.length; i++) { - buff.push(this.toolbars[i].renderHtml()); - } - return buff.join(''); - }, - setFullScreen:function (fullscreen) { - - var editor = this.editor, - container = editor.container.parentNode.parentNode; - if (this._fullscreen != fullscreen) { - this._fullscreen = fullscreen; - this.editor.fireEvent('beforefullscreenchange', fullscreen); - if (baidu.editor.browser.gecko) { - var bk = editor.selection.getRange().createBookmark(); - } - if (fullscreen) { - while (container.tagName != "BODY") { - var position = baidu.editor.dom.domUtils.getComputedStyle(container, "position"); - nodeStack.push(position); - container.style.position = "static"; - container = container.parentNode; - } - this._bakHtmlOverflow = document.documentElement.style.overflow; - this._bakBodyOverflow = document.body.style.overflow; - this._bakAutoHeight = this.editor.autoHeightEnabled; - this._bakScrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop); - - this._bakEditorContaninerWidth = editor.iframe.parentNode.offsetWidth; - if (this._bakAutoHeight) { - //当全屏时不能执行自动长高 - editor.autoHeightEnabled = false; - this.editor.disableAutoHeight(); - } - - document.documentElement.style.overflow = 'hidden'; - //修复,滚动条不收起的问题 - - window.scrollTo(0,window.scrollY); - this._bakCssText = this.getDom().style.cssText; - this._bakCssText1 = this.getDom('iframeholder').style.cssText; - editor.iframe.parentNode.style.width = ''; - this._updateFullScreen(); - } else { - while (container.tagName != "BODY") { - container.style.position = nodeStack.shift(); - container = container.parentNode; - } - this.getDom().style.cssText = this._bakCssText; - this.getDom('iframeholder').style.cssText = this._bakCssText1; - if (this._bakAutoHeight) { - editor.autoHeightEnabled = true; - this.editor.enableAutoHeight(); - } - - document.documentElement.style.overflow = this._bakHtmlOverflow; - document.body.style.overflow = this._bakBodyOverflow; - editor.iframe.parentNode.style.width = this._bakEditorContaninerWidth + 'px'; - window.scrollTo(0, this._bakScrollTop); - } - if (browser.gecko && editor.body.contentEditable === 'true') { - var input = document.createElement('input'); - document.body.appendChild(input); - editor.body.contentEditable = false; - setTimeout(function () { - input.focus(); - setTimeout(function () { - editor.body.contentEditable = true; - editor.fireEvent('fullscreenchanged', fullscreen); - editor.selection.getRange().moveToBookmark(bk).select(true); - baidu.editor.dom.domUtils.remove(input); - fullscreen && window.scroll(0, 0); - }, 0) - }, 0) - } - - if(editor.body.contentEditable === 'true'){ - this.editor.fireEvent('fullscreenchanged', fullscreen); - this.triggerLayout(); - } - - } - }, - _updateFullScreen:function () { - if (this._fullscreen) { - var vpRect = uiUtils.getViewportRect(); - this.getDom().style.cssText = 'border:0;position:absolute;left:0;top:' + (this.editor.options.topOffset || 0) + 'px;width:' + vpRect.width + 'px;height:' + vpRect.height + 'px;z-index:' + (this.getDom().style.zIndex * 1 + 100); - uiUtils.setViewportOffset(this.getDom(), { left:0, top:this.editor.options.topOffset || 0 }); - this.editor.setHeight(vpRect.height - this.getDom('toolbarbox').offsetHeight - this.getDom('bottombar').offsetHeight - (this.editor.options.topOffset || 0),true); - //不手动调一下,会导致全屏失效 - if(browser.gecko){ - try{ - window.onresize(); - }catch(e){ - - } - - } - } - }, - _updateElementPath:function () { - var bottom = this.getDom('elementpath'), list; - if (this.elementPathEnabled && (list = this.editor.queryCommandValue('elementpath'))) { - - var buff = []; - for (var i = 0, ci; ci = list[i]; i++) { - buff[i] = this.formatHtml('' + ci + ''); - } - bottom.innerHTML = '
                  ' + this.editor.getLang("elementPathTip") + ': ' + buff.join(' > ') + '
                  '; - - } else { - bottom.style.display = 'none' - } - }, - disableElementPath:function () { - var bottom = this.getDom('elementpath'); - bottom.innerHTML = ''; - bottom.style.display = 'none'; - this.elementPathEnabled = false; - - }, - enableElementPath:function () { - var bottom = this.getDom('elementpath'); - bottom.style.display = ''; - this.elementPathEnabled = true; - this._updateElementPath(); - }, - _scale:function () { - var doc = document, - editor = this.editor, - editorHolder = editor.container, - editorDocument = editor.document, - toolbarBox = this.getDom("toolbarbox"), - bottombar = this.getDom("bottombar"), - scale = this.getDom("scale"), - scalelayer = this.getDom("scalelayer"); - - var isMouseMove = false, - position = null, - minEditorHeight = 0, - minEditorWidth = editor.options.minFrameWidth, - pageX = 0, - pageY = 0, - scaleWidth = 0, - scaleHeight = 0; - - function down() { - position = domUtils.getXY(editorHolder); - - if (!minEditorHeight) { - minEditorHeight = editor.options.minFrameHeight + toolbarBox.offsetHeight + bottombar.offsetHeight; - } - - scalelayer.style.cssText = "position:absolute;left:0;display:;top:0;background-color:#41ABFF;opacity:0.4;filter: Alpha(opacity=40);width:" + editorHolder.offsetWidth + "px;height:" - + editorHolder.offsetHeight + "px;z-index:" + (editor.options.zIndex + 1); - - domUtils.on(doc, "mousemove", move); - domUtils.on(editorDocument, "mouseup", up); - domUtils.on(doc, "mouseup", up); - } - - var me = this; - //by xuheng 全屏时关掉缩放 - this.editor.addListener('fullscreenchanged', function (e, fullScreen) { - if (fullScreen) { - me.disableScale(); - - } else { - if (me.editor.options.scaleEnabled) { - me.enableScale(); - var tmpNode = me.editor.document.createElement('span'); - me.editor.body.appendChild(tmpNode); - me.editor.body.style.height = Math.max(domUtils.getXY(tmpNode).y, me.editor.iframe.offsetHeight - 20) + 'px'; - domUtils.remove(tmpNode) - } - } - }); - function move(event) { - clearSelection(); - var e = event || window.event; - pageX = e.pageX || (doc.documentElement.scrollLeft + e.clientX); - pageY = e.pageY || (doc.documentElement.scrollTop + e.clientY); - scaleWidth = pageX - position.x; - scaleHeight = pageY - position.y; - - if (scaleWidth >= minEditorWidth) { - isMouseMove = true; - scalelayer.style.width = scaleWidth + 'px'; - } - if (scaleHeight >= minEditorHeight) { - isMouseMove = true; - scalelayer.style.height = scaleHeight + "px"; - } - } - - function up() { - if (isMouseMove) { - isMouseMove = false; - editor.ui._actualFrameWidth = scalelayer.offsetWidth - 2; - editorHolder.style.width = editor.ui._actualFrameWidth + 'px'; - - editor.setHeight(scalelayer.offsetHeight - bottombar.offsetHeight - toolbarBox.offsetHeight - 2,true); - } - if (scalelayer) { - scalelayer.style.display = "none"; - } - clearSelection(); - domUtils.un(doc, "mousemove", move); - domUtils.un(editorDocument, "mouseup", up); - domUtils.un(doc, "mouseup", up); - } - - function clearSelection() { - if (browser.ie) - doc.selection.clear(); - else - window.getSelection().removeAllRanges(); - } - - this.enableScale = function () { - //trace:2868 - if (editor.queryCommandState("source") == 1) return; - scale.style.display = ""; - this.scaleEnabled = true; - domUtils.on(scale, "mousedown", down); - }; - this.disableScale = function () { - scale.style.display = "none"; - this.scaleEnabled = false; - domUtils.un(scale, "mousedown", down); - }; - }, - isFullScreen:function () { - return this._fullscreen; - }, - postRender:function () { - UIBase.prototype.postRender.call(this); - for (var i = 0; i < this.toolbars.length; i++) { - this.toolbars[i].postRender(); - } - var me = this; - var timerId, - domUtils = baidu.editor.dom.domUtils, - updateFullScreenTime = function () { - clearTimeout(timerId); - timerId = setTimeout(function () { - me._updateFullScreen(); - }); - }; - domUtils.on(window, 'resize', updateFullScreenTime); - - me.addListener('destroy', function () { - domUtils.un(window, 'resize', updateFullScreenTime); - clearTimeout(timerId); - }) - }, - showToolbarMsg:function (msg, flag) { - this.getDom('toolbarmsg_label').innerHTML = msg; - this.getDom('toolbarmsg').style.display = ''; - // - if (!flag) { - var w = this.getDom('upload_dialog'); - w.style.display = 'none'; - } - }, - hideToolbarMsg:function () { - this.getDom('toolbarmsg').style.display = 'none'; - }, - mapUrl:function (url) { - return url ? url.replace('~/', this.editor.options.UEDITOR_HOME_URL || '') : '' - }, - triggerLayout:function () { - var dom = this.getDom(); - if (dom.style.zoom == '1') { - dom.style.zoom = '100%'; - } else { - dom.style.zoom = '1'; - } - } - }; - utils.inherits(EditorUI, baidu.editor.ui.UIBase); - - - var instances = {}; - - - UE.ui.Editor = function (options) { - var editor = new UE.Editor(options); - editor.options.editor = editor; - utils.loadFile(document, { - href:editor.options.themePath + editor.options.theme + "/css/ueditor.css", - tag:"link", - type:"text/css", - rel:"stylesheet" - }); - - var oldRender = editor.render; - editor.render = function (holder) { - if (holder.constructor === String) { - editor.key = holder; - instances[holder] = editor; - } - utils.domReady(function () { - editor.langIsReady ? renderUI() : editor.addListener("langReady", renderUI); - function renderUI() { - editor.setOpt({ - labelMap:editor.options.labelMap || editor.getLang('labelMap') - }); - new EditorUI(editor.options); - if (holder) { - if (holder.constructor === String) { - holder = document.getElementById(holder); - } - holder && holder.getAttribute('name') && ( editor.options.textarea = holder.getAttribute('name')); - if (holder && /script|textarea/ig.test(holder.tagName)) { - var newDiv = document.createElement('div'); - holder.parentNode.insertBefore(newDiv, holder); - var cont = holder.value || holder.innerHTML; - editor.options.initialContent = /^[\t\r\n ]*$/.test(cont) ? editor.options.initialContent : - cont.replace(/>[\n\r\t]+([ ]{4})+/g, '>') - .replace(/[\n\r\t]+([ ]{4})+[\n\r\t]+<'); - holder.className && (newDiv.className = holder.className); - holder.style.cssText && (newDiv.style.cssText = holder.style.cssText); - if (/textarea/i.test(holder.tagName)) { - editor.textarea = holder; - editor.textarea.style.display = 'none'; - - - } else { - holder.parentNode.removeChild(holder); - - - } - if(holder.id){ - newDiv.id = holder.id; - domUtils.removeAttributes(holder,'id'); - } - holder = newDiv; - holder.innerHTML = ''; - } - - } - domUtils.addClass(holder, "edui-" + editor.options.theme); - editor.ui.render(holder); - var opt = editor.options; - //给实例添加一个编辑器的容器引用 - editor.container = editor.ui.getDom(); - var parents = domUtils.findParents(holder,true); - var displays = []; - for(var i = 0 ,ci;ci=parents[i];i++){ - displays[i] = ci.style.display; - ci.style.display = 'block' - } - if (opt.initialFrameWidth) { - opt.minFrameWidth = opt.initialFrameWidth; - } else { - opt.minFrameWidth = opt.initialFrameWidth = holder.offsetWidth; - var styleWidth = holder.style.width; - if(/%$/.test(styleWidth)) { - opt.initialFrameWidth = styleWidth; - } - } - if (opt.initialFrameHeight) { - opt.minFrameHeight = opt.initialFrameHeight; - } else { - opt.initialFrameHeight = opt.minFrameHeight = holder.offsetHeight; - } - for(var i = 0 ,ci;ci=parents[i];i++){ - ci.style.display = displays[i] - } - //编辑器最外容器设置了高度,会导致,编辑器不占位 - //todo 先去掉,没有找到原因 - if(holder.style.height){ - holder.style.height = '' - } - editor.container.style.width = opt.initialFrameWidth + (/%$/.test(opt.initialFrameWidth) ? '' : 'px'); - editor.container.style.zIndex = opt.zIndex; - oldRender.call(editor, editor.ui.getDom('iframeholder')); - editor.fireEvent("afteruiready"); - } - }) - }; - return editor; - }; - - - /** - * @file - * @name UE - * @short UE - * @desc UEditor的顶部命名空间 - */ - /** - * @name getEditor - * @since 1.2.4+ - * @grammar UE.getEditor(id,[opt]) => Editor实例 - * @desc 提供一个全局的方法得到编辑器实例 - * - * * ''id'' 放置编辑器的容器id, 如果容器下的编辑器已经存在,就直接返回 - * * ''opt'' 编辑器的可选参数 - * @example - * UE.getEditor('containerId',{onready:function(){//创建一个编辑器实例 - * this.setContent('hello') - * }}); - * UE.getEditor('containerId'); //返回刚创建的实例 - * - */ - UE.getEditor = function (id, opt) { - var editor = instances[id]; - if (!editor) { - editor = instances[id] = new UE.ui.Editor(opt); - editor.render(id); - } - return editor; - }; - - - UE.delEditor = function (id) { - var editor; - if (editor = instances[id]) { - editor.key && editor.destroy(); - delete instances[id] - } - }; - - UE.registerUI = function(uiName,fn,index,editorId){ - utils.each(uiName.split(/\s+/), function (name) { - UE._customizeUI[name] = { - id : editorId, - execFn:fn, - index:index - }; - }) - - } - -})(); - -// adapter/message.js -UE.registerUI('message', function(editor) { - - var editorui = baidu.editor.ui; - var Message = editorui.Message; - var holder; - var _messageItems = []; - var me = editor; - - me.addListener('ready', function(){ - holder = document.getElementById(me.ui.id + '_message_holder'); - updateHolderPos(); - setTimeout(function(){ - updateHolderPos(); - }, 500); - }); - - me.addListener('showmessage', function(type, opt){ - opt = utils.isString(opt) ? { - 'content': opt - } : opt; - var message = new Message({ - 'timeout': opt.timeout, - 'type': opt.type, - 'content': opt.content, - 'keepshow': opt.keepshow, - 'editor': me - }), - mid = opt.id || ('msg_' + (+new Date()).toString(36)); - message.render(holder); - _messageItems[mid] = message; - message.reset(opt); - updateHolderPos(); - return mid; - }); - - me.addListener('updatemessage',function(type, id, opt){ - opt = utils.isString(opt) ? { - 'content': opt - } : opt; - var message = _messageItems[id]; - message.render(holder); - message && message.reset(opt); - }); - - me.addListener('hidemessage',function(type, id){ - var message = _messageItems[id]; - message && message.hide(); - }); - - function updateHolderPos(){ - var toolbarbox = me.ui.getDom('toolbarbox'); - if (toolbarbox) { - holder.style.top = toolbarbox.offsetHeight + 3 + 'px'; - } - holder.style.zIndex = Math.max(me.options.zIndex, me.iframe.style.zIndex) + 1; - } - -}); - - -// adapter/autosave.js -UE.registerUI('autosave', function(editor) { - var timer = null,uid = null; - editor.on('afterautosave',function(){ - clearTimeout(timer); - - timer = setTimeout(function(){ - if(uid){ - editor.trigger('hidemessage',uid); - } - uid = editor.trigger('showmessage',{ - content : editor.getLang('autosave.success'), - timeout : 2000 - }); - - },2000) - }) - -}); - - - -})(); diff --git a/static/plugs/ueditor/back/ueditor.all.min.js b/static/plugs/ueditor/back/ueditor.all.min.js deleted file mode 100644 index 9fffa8167..000000000 --- a/static/plugs/ueditor/back/ueditor.all.min.js +++ /dev/null @@ -1,709 +0,0 @@ -(function(){function X(d,b,c){var a;b=b.toLowerCase();return(a=d.__allListeners||c&&(d.__allListeners={}))&&(a[b]||c&&(a[b]=[]))}function Y(d,b,c,a,e,h){a=a&&d[b];var g;for(!a&&(a=d[c]);!a&&(g=(g||d).parentNode);){if("BODY"==g.tagName||h&&!h(g))return null;a=g[c]}return a&&e&&!e(a)?Y(a,b,c,!1,e):a}UEDITOR_CONFIG=window.UEDITOR_CONFIG||{};var t=window.baidu||{};window.baidu=t;window.UE=t.editor=window.UE||{};UE.plugins={};UE.commands={};UE.instants={};UE.I18N={};UE._customizeUI={};UE.version="1.4.3"; -var M=UE.dom={},r=UE.browser=function(){var d=navigator.userAgent.toLowerCase(),b=window.opera,c={ie:/(msie\s|trident.*rv:)([\w.]+)/.test(d),opera:!!b&&b.version,webkit:-1a||c.quirks;c.ie9above=8a;c.ie11above=10a}c.gecko&&(e=d.match(/rv:([\d\.]+)/))&&(e=e[1].split("."),a=1E4*e[0]+100*(e[1]||0)+1*(e[2]||0));/chrome\/(\d+\.\d)/i.test(d)&&(c.chrome=+RegExp.$1);/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(d)&&!/chrome/i.test(d)&& -(c.safari=+(RegExp.$1||RegExp.$2));c.opera&&(a=parseFloat(b.version()));c.webkit&&(a=parseFloat(d.match(/ applewebkit\/(\d+)/)[1]));c.version=a;c.isCompatible=!c.mobile&&(c.ie&&6<=a||c.gecko&&10801<=a||c.opera&&9.5<=a||c.air&&1<=a||c.webkit&&522<=a||!1);return c}(),J=r.ie,ma=r.opera,p=UE.utils={each:function(d,b,c){if(null!=d)if(d.length===+d.length)for(var a=0,e=d.length;a=c&&e===b)return a=h,!1});return a},removeItem:function(d,b){for(var c=0,a=d.length;c'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, -function(c,a){return a?c:{"<":"<","&":"&",'"':""",">":">","'":"'"}[c]}):""},html:function(d){return d?d.replace(/&((g|l|quo)t|amp|#39|nbsp);/g,function(b){return{"<":"<","&":"&",""":'"',">":">","'":"'"," ":" "}[b]}):""},cssStyleToDomStyle:function(){var d=document.createElement("div").style,b={"float":void 0!=d.cssFloat?"cssFloat":void 0!=d.styleFloat?"styleFloat":"float"};return function(c){return b[c]||(b[c]=c.toLowerCase().replace(/-./g,function(a){return a.charAt(1).toUpperCase()}))}}(), -loadFile:function(){function d(c,a){try{for(var e=0,h;h=b[e++];)if(h.doc===c&&h.url==(a.src||a.href))return h}catch(g){return null}}var b=[];return function(c,a,e){var h=d(c,a);if(h)h.ready?e&&e():h.funs.push(e);else if(b.push({doc:c,url:a.src||a.href,funs:[e]}),!c.body){e=[];for(var g in a)"tag"!=g&&e.push(g+'="'+a[g]+'"');c.write("<"+a.tag+" "+e.join(" ")+" >")}else if(!a.id||!c.getElementById(a.id)){var l=c.createElement(a.tag);delete a.tag;for(g in a)l.setAttribute(g,a[g]);l.onload= -l.onreadystatechange=function(){if(!this.readyState||/loaded|complete/.test(this.readyState)){h=d(c,a);if(0a?"0"+a:a},b=function(a){/["\\\x00-\x1f]/.test(a)&&(a=a.replace(/["\\\x00-\x1f]/g,function(a){var b=c[a]; -if(b)return b;b=a.charCodeAt();return"\\u00"+Math.floor(b/16).toString(16)+(b%16).toString(16)}));return'"'+a+'"'},c={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};return function(a){switch(typeof a){case "undefined":return"undefined";case "number":return isFinite(a)?String(a):"null";case "string":return b(a);case "boolean":return String(a);default:if(null===a)return"null";if(p.isArray(a)){var e=["["],c=a.length,g,l,k;for(l=0;lr.version?{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder"}:{tabindex:"tabIndex",readonly:"readOnly"},oa=p.listToMap("-webkit-box -moz-box block list-item table table-row-group table-header-group table-footer-group table-row table-column-group table-column table-cell table-caption".split(" ")), -f=M.domUtils={NODE_ELEMENT:1,NODE_DOCUMENT:9,NODE_TEXT:3,NODE_COMMENT:8,NODE_DOCUMENT_FRAGMENT:11,POSITION_IDENTICAL:0,POSITION_DISCONNECTED:1,POSITION_FOLLOWING:2,POSITION_PRECEDING:4,POSITION_IS_CONTAINED:8,POSITION_CONTAINS:16,fillChar:J&&"6"==r.version?"\ufeff":"\u200b",keys:{8:1,46:1,16:1,17:1,18:1,37:1,38:1,39:1,40:1,13:1},getPosition:function(d,b){if(d===b)return 0;var c,a=[d],e=[b];for(c=d;c=c.parentNode;){if(c===b)return 10;a.push(c)}for(c=b;c=c.parentNode;){if(c===d)return 20;e.push(c)}a.reverse(); -e.reverse();if(a[0]!==e[0])return 1;for(c=-1;c++,a[c]===e[c];);d=a[c];for(b=e[c];d=d.nextSibling;)if(d===b)return 4;return 2},getNodeIndex:function(d,b){for(var c=d,a=0;c=c.previousSibling;)b&&3==c.nodeType?c.nodeType!=c.nextSibling.nodeType&&a++:a++;return a},inDoc:function(d,b){return 10==f.getPosition(d,b)},findParent:function(d,b,c){if(d&&!f.isBody(d))for(d=c?d:d.parentNode;d;){if(!b||b(d)||f.isBody(d))return b&&!b(d)&&f.isBody(d)?null:d;d=d.parentNode}return null},findParentByTagName:function(d, -b,c,a){b=p.listToMap(p.isArray(b)?b:[b]);return f.findParent(d,function(e){return b[e.tagName]&&!(a&&a(e))},c)},findParents:function(d,b,c,a){for(b=b&&(c&&c(d)||!c)?[d]:[];d=f.findParent(d,c);)b.push(d);return a?b:b.reverse()},insertAfter:function(d,b){return d.nextSibling?d.parentNode.insertBefore(b,d.nextSibling):d.parentNode.appendChild(b)},remove:function(d,b){var c=d.parentNode,a;if(c){if(b&&d.hasChildNodes())for(;a=d.firstChild;)c.insertBefore(a,d);c.removeChild(d)}return d},getNextDomNode:function(d, -b,c,a){return Y(d,"firstChild","nextSibling",b,c,a)},getPreDomNode:function(d,b,c,a){return Y(d,"lastChild","previousSibling",b,c,a)},isBookmarkNode:function(d){return 1==d.nodeType&&d.id&&/^_baidu_bookmark_/i.test(d.id)},getWindow:function(d){d=d.ownerDocument||d;return d.defaultView||d.parentWindow},getCommonAncestor:function(d,b){if(d===b)return d;for(var c=[d],a=[b],e=d,h=-1;e=e.parentNode;){if(e===b)return e;c.push(e)}for(e=b;e=e.parentNode;){if(e===d)return e;a.push(e)}c.reverse();for(a.reverse();h++, -c[h]===a[h];);return 0==h?null:c[h-1]},clearEmptySibling:function(d,b,c){function a(a,b){for(var g;a&&!f.isBookmarkNode(a)&&(f.isEmptyInlineElement(a)||!(new RegExp("[^\t\n\r"+f.fillChar+"]")).test(a.nodeValue));)g=a[b],f.remove(a),a=g}!b&&a(d.nextSibling,"nextSibling");!c&&a(d.previousSibling,"previousSibling")},split:function(d,b){var c=d.ownerDocument;if(r.ie&&b==d.nodeValue.length){var a=c.createTextNode("");return f.insertAfter(d,a)}a=d.splitText(b);r.ie8&&(c=c.createTextNode(""),f.insertAfter(a, -c),f.remove(c));return a},isWhitespace:function(d){return!(new RegExp("[^ \t\n\r"+f.fillChar+"]")).test(d.nodeValue)},getXY:function(d){for(var b=0,c=0;d.offsetParent;)c+=d.offsetTop,b+=d.offsetLeft,d=d.offsetParent;return{x:b,y:c}},on:function(d,b,c){var a=p.isArray(b)?b:p.trim(b).split(/\s+/),e=a.length;if(e)for(;e--;)if(b=a[e],d.addEventListener)d.addEventListener(b,c,!1);else{c._d||(c._d={els:[]});var h=b+c.toString(),g=p.indexOf(c._d.els,d);c._d[h]&&-1!=g||(-1==g&&c._d.els.push(d),c._d[h]||(c._d[h]= -function(a){return c.call(a.srcElement,a||window.event)}),d.attachEvent("on"+b,c._d[h]))}d=null},un:function(d,b,c){var a=p.isArray(b)?b:p.trim(b).split(/\s+/),e=a.length;if(e)for(;e--;)if(b=a[e],d.removeEventListener)d.removeEventListener(b,c,!1);else{var h=b+c.toString();try{d.detachEvent("on"+b,c._d?c._d[h]:c)}catch(g){}c._d&&c._d[h]&&(b=p.indexOf(c._d.els,d),-1!=b&&c._d.els.splice(b,1),0==c._d.els.length&&delete c._d[h])}},isSameElement:function(d,b){if(d.tagName!=b.tagName)return!1;var c=d.attributes, -a=b.attributes;if(!J&&c.length!=a.length)return!1;for(var e,h,g=0,l=0,k=0;e=c[k++];){if("style"==e.nodeName)if(e.specified&&g++,f.isSameStyle(d,b))continue;else return!1;if(J)if(e.specified)g++,h=a.getNamedItem(e.nodeName);else continue;else h=b.attributes[e.nodeName];if(!h.specified||e.nodeValue!=h.nodeValue)return!1}if(J){for(k=0;h=a[k++];)h.specified&&l++;if(g!=l)return!1}return!0},isSameStyle:function(d,b){var c=d.style.cssText.replace(/( ?; ?)/g,";").replace(/( ?: ?)/g,":"),a=b.style.cssText.replace(/( ?; ?)/g, -";").replace(/( ?: ?)/g,":");if(r.opera){c=d.style;a=b.style;if(c.length!=a.length)return!1;for(var e in c)if(!/^(\d+|csstext)$/i.test(e)&&c[e]!=a[e])return!1;return!0}if(!c||!a)return c==a;c=c.split(";");a=a.split(";");if(c.length!=a.length)return!1;e=0;for(var h;h=c[e++];)if(-1==p.indexOf(a,h))return!1;return!0},isBlockElm:function(d){return 1==d.nodeType&&(w.$block[d.tagName]||oa[f.getComputedStyle(d,"display")])&&!w.$nonChild[d.tagName]},isBody:function(d){return d&&1==d.nodeType&&"body"==d.tagName.toLowerCase()}, -breakParent:function(d,b){var c,a=d,e=d,h,g;do{a=a.parentNode;h?(c=a.cloneNode(!1),c.appendChild(h),h=c,c=a.cloneNode(!1),c.appendChild(g),g=c):(h=a.cloneNode(!1),g=h.cloneNode(!1));for(;c=e.previousSibling;)h.insertBefore(c,h.firstChild);for(;c=e.nextSibling;)g.appendChild(c);e=a}while(b!==a);c=b.parentNode;c.insertBefore(h,b);c.insertBefore(g,b);c.insertBefore(d,g);f.remove(b);return d},isEmptyInlineElement:function(d){if(1!=d.nodeType||!w.$removeEmpty[d.tagName])return 0;for(d=d.firstChild;d;){if(f.isBookmarkNode(d)|| -1==d.nodeType&&!f.isEmptyInlineElement(d)||3==d.nodeType&&!f.isWhitespace(d))return 0;d=d.nextSibling}return 1},trimWhiteTextNode:function(d){function b(b){for(var a;(a=d[b])&&3==a.nodeType&&f.isWhitespace(a);)d.removeChild(a)}b("firstChild");b("lastChild")},mergeChild:function(d,b,c){b=f.getElementsByTagName(d,d.tagName.toLowerCase());for(var a=0,e;e=b[a++];)if(e.parentNode&&!f.isBookmarkNode(e))if("span"==e.tagName.toLowerCase()){if(d===e.parentNode&&(f.trimWhiteTextNode(d),1==d.childNodes.length)){d.style.cssText= -e.style.cssText+";"+d.style.cssText;f.remove(e,!0);continue}e.style.cssText=d.style.cssText+";"+e.style.cssText;if(c){var h=c.style;if(h)for(var h=h.split(";"),g=0,l;l=h[g++];)e.style[p.cssStyleToDomStyle(l.split(":")[0])]=l.split(":")[1]}f.isSameStyle(e,d)&&f.remove(e,!0)}else f.isSameElement(d,e)&&f.remove(e,!0)},getElementsByTagName:function(d,b,c){if(c&&p.isString(c)){var a=c;c=function(e){return f.hasClass(e,a)}}b=p.trim(b).replace(/[ ]{2,}/g," ").split(" ");for(var e=[],h=0,g;g=b[h++];){g=d.getElementsByTagName(g); -for(var l=0,k;k=g[l++];)c&&!c(k)||e.push(k)}return e},mergeToParent:function(d){for(var b=d.parentNode;b&&w.$removeEmpty[b.tagName];){if(b.tagName==d.tagName||"A"==b.tagName){f.trimWhiteTextNode(b);if("SPAN"==b.tagName&&!f.isSameStyle(b,d)||"A"==b.tagName&&"SPAN"==d.tagName)if(1r.version&&"font-size"==b&&!d.style.fontSize&&!w.$empty[d.tagName]&&!w.$nonChild[d.tagName]){var c=d.ownerDocument.createElement("span");c.style.cssText="padding:0;border:0;font-family:simsun;";c.innerHTML=".";d.appendChild(c);var a=c.offsetHeight;d.removeChild(c);c=null;return a+"px"}try{c=f.getStyle(d,b)||(window.getComputedStyle?f.getWindow(d).getComputedStyle(d, -"").getPropertyValue(b):(d.currentStyle||d.style)[p.cssStyleToDomStyle(b)])}catch(e){return""}return p.transUnitToPx(p.fixColor(b,c))},removeClasses:function(d,b){b=p.isArray(b)?b:p.trim(b).replace(/[ ]{2,}/g," ").split(" ");for(var c=0,a,e=d.className;a=b[c++];)e=e.replace(new RegExp("\\b"+a+"\\b"),"");(e=p.trim(e).replace(/[ ]{2,}/g," "))?d.className=e:f.removeAttributes(d,["class"])},addClass:function(d,b){if(d){b=p.trim(b).replace(/[ ]{2,}/g," ").split(" ");for(var c=0,a,e=d.className;a=b[c++];)(new RegExp("\\b"+ -a+"\\b")).test(e)||(e+=" "+a);d.className=p.trim(e)}},hasClass:function(d,b){if(p.isRegExp(b))return b.test(d.className);b=p.trim(b).replace(/[ ]{2,}/g," ").split(" ");for(var c=0,a,e=d.className;a=b[c++];)if(!(new RegExp("\\b"+a+"\\b","i")).test(e))return!1;return c-1==b.length},preventDefault:function(d){d.preventDefault?d.preventDefault():d.returnValue=!1},removeStyle:function(d,b){r.ie?("color"==b&&(b="(^|;)"+b),d.style.cssText=d.style.cssText.replace(new RegExp(b+"[^:]*:[^;]+;?","ig"),"")):d.style.removeProperty? -d.style.removeProperty(b):d.style.removeAttribute(p.cssStyleToDomStyle(b));d.style.cssText||f.removeAttributes(d,["style"])},getStyle:function(d,b){var c=d.style[p.cssStyleToDomStyle(b)];return p.fixColor(b,c)},setStyle:function(d,b,c){d.style[p.cssStyleToDomStyle(b)]=c;p.trim(d.style.cssText)||this.removeAttributes(d,"style")},setStyles:function(d,b){for(var c in b)b.hasOwnProperty(c)&&f.setStyle(d,c,b[c])},removeDirtyAttr:function(d){for(var b=0,c,a=d.getElementsByTagName("*");c=a[b++];)c.removeAttribute("_moz_dirty"); -d.removeAttribute("_moz_dirty")},getChildCount:function(d,b){var c=0,a=d.firstChild;for(b=b||function(){return 1};a;)b(a)&&c++,a=a.nextSibling;return c},isEmptyNode:function(d){return!d.firstChild||0==f.getChildCount(d,function(b){return!f.isBr(b)&&!f.isBookmarkNode(b)&&!f.isWhitespace(b)})},clearSelectedArr:function(d){for(var b;b=d.pop();)f.removeAttributes(b,["class"])},scrollToView:function(d,b,c){var a=function(){var a=b.document,c="CSS1Compat"==a.compatMode;return{width:(c?a.documentElement.clientWidth: -a.body.clientWidth)||0,height:(c?a.documentElement.clientHeight:a.body.clientHeight)||0}}().height;c=-1*a+c+(d.offsetHeight||0);d=f.getXY(d);c+=d.y;d=function(a){if("pageXOffset"in a)return{x:a.pageXOffset||0,y:a.pageYOffset||0};a=a.document;return{x:a.documentElement.scrollLeft||a.body.scrollLeft||0,y:a.documentElement.scrollTop||a.body.scrollTop||0}}(b).y;(c>d||cc?-20:20))},isBr:function(d){return 1==d.nodeType&&"BR"==d.tagName},isFillChar:function(d,b){if(3!=d.nodeType)return!1; -var c=d.nodeValue;return b?(new RegExp("^"+f.fillChar)).test(c):!c.replace(new RegExp(f.fillChar,"g"),"").length},isStartInblock:function(d){d=d.cloneRange();var b=0,c=d.startContainer,a;if(1==c.nodeType&&c.childNodes[d.startOffset])for(var c=c.childNodes[d.startOffset],e=c.previousSibling;e&&f.isFillChar(e);)c=e,e=e.previousSibling;this.isFillChar(c,!0)&&1==d.startOffset&&(d.setStartBefore(c),c=d.startContainer);for(;c&&f.isFillChar(c);)a=c,c=c.previousSibling;a&&(d.setStartBefore(a),c=d.startContainer); -for(1==c.nodeType&&f.isEmptyNode(c)&&1==d.startOffset&&d.setStart(c,0).collapse(!0);!d.startOffset;){c=d.startContainer;if(f.isBlockElm(c)||f.isBody(c)){b=1;break}var e=d.startContainer.previousSibling,h;if(e){for(;e&&f.isFillChar(e);)h=e,e=e.previousSibling;h?d.setStartBefore(h):d.setStartBefore(d.startContainer)}else d.setStartBefore(d.startContainer)}return b&&!f.isBody(d.startContainer)?1:0},isEmptyBlock:function(d,b){if(1!=d.nodeType)return 0;b=b||new RegExp("[ \u00a0\t\r\n"+f.fillChar+"]","g"); -if(0/.test(d.outerHTML):0==d.attributes.length},isCustomeNode:function(d){return 1==d.nodeType&&d.getAttribute("_ue_custom_node_")},isTagNode:function(d,b){return 1==d.nodeType&&(new RegExp("\\b"+d.tagName+"\\b","i")).test(b)},filterNodeList:function(d,b,c){var a=[];if(!p.isFunction(b)){var e=b;b=function(a){return-1!=p.indexOf(p.isArray(e)?e:e.split(" "),a.tagName.toLowerCase())}}p.each(d, -function(e){b(e)&&a.push(e)});return 0==a.length?null:1!=a.length&&c?a:a[0]},isInNodeEndBoundary:function(d,b){var c=d.startContainer;if(3==c.nodeType&&d.startOffset!=c.nodeValue.length||1==c.nodeType&&d.startOffset!=c.childNodes.length)return 0;for(;c!==b;){if(c.nextSibling)return 0;c=c.parentNode}return 1},isBoundaryNode:function(d,b){for(var c;!f.isBody(d);)if(c=d,d=d.parentNode,c!==d[b])return!1;return!0},fillHtml:r.ie11below?" ":"
                  "},Q=new RegExp(f.fillChar,"g");(function(){function d(a){return!a.collapsed&& -1==a.startContainer.nodeType&&a.startContainer===a.endContainer&&1==a.endOffset-a.startOffset}function b(a,g,e,b){1==g.nodeType&&(w.$empty[g.tagName]||w.$nonChild[g.tagName])&&(e=f.getNodeIndex(g)+(a?0:1),g=g.parentNode);a?(b.startContainer=g,b.startOffset=e,b.endContainer||b.collapse(!0)):(b.endContainer=g,b.endOffset=e,b.startContainer||b.collapse(!1));b.collapsed=b.startContainer&&b.endContainer&&b.startContainer===b.endContainer&&b.startOffset==b.endOffset;return b}function c(a,g){var b=a.startContainer, -e=a.endContainer,c=a.startOffset,l=a.endOffset,k=a.document,h=k.createDocumentFragment(),d,p;1==b.nodeType&&(b=b.childNodes[c]||(d=b.appendChild(k.createTextNode(""))));1==e.nodeType&&(e=e.childNodes[l]||(p=e.appendChild(k.createTextNode(""))));if(b===e&&3==b.nodeType)return h.appendChild(k.createTextNode(b.substringData(c,l-c))),g&&(b.deleteData(c,l-c),a.collapse(!0)),h;for(var B,O,r=h,t=f.findParents(b,!0),w=f.findParents(e,!0),A=0;t[A]==w[A];)A++;for(var I=A,E;E=t[I];I++){B=E.nextSibling;E==b? -d||(3==a.startContainer.nodeType?(r.appendChild(k.createTextNode(b.nodeValue.slice(c))),g&&b.deleteData(c,b.nodeValue.length-c)):r.appendChild(g?b:b.cloneNode(!0))):(O=E.cloneNode(!1),r.appendChild(O));for(;B&&B!==e&&B!==w[I];)E=B.nextSibling,r.appendChild(g?B:B.cloneNode(!0)),B=E;r=O}r=h;t[A]||(r.appendChild(t[A-1].cloneNode(!1)),r=r.firstChild);for(I=A;c=w[I];I++){B=c.previousSibling;c==e?p||3!=a.endContainer.nodeType||(r.appendChild(k.createTextNode(e.substringData(0,l))),g&&e.deleteData(0,l)): -(O=c.cloneNode(!1),r.appendChild(O));if(I!=A||!t[A])for(;B&&B!==b;)c=B.previousSibling,r.insertBefore(g?B:B.cloneNode(!0),r.firstChild),B=c;r=O}g&&a.setStartBefore(w[A]?t[A]?w[A]:t[A-1]:w[A-1]).collapse(!0);d&&f.remove(d);p&&f.remove(p);return h}function a(a,g){try{if(l&&f.inDoc(l,a))if(l.nodeValue.replace(Q,"").length)l.nodeValue=l.nodeValue.replace(Q,"");else{var b=l.parentNode;for(f.remove(l);b&&f.isEmptyInlineElement(b)&&(r.safari?!(f.getPosition(b,g)&f.POSITION_CONTAINS):!b.contains(g));)l=b.parentNode, -f.remove(b),b=l}}catch(e){}}function e(a,b){var g;for(a=a[b];a&&f.isFillChar(a);)g=a[b],f.remove(a),a=g}var h=0,g=f.fillChar,l,k=M.Range=function(a){this.startContainer=this.startOffset=this.endContainer=this.endOffset=null;this.document=a;this.collapsed=!0};k.prototype={cloneContents:function(){return this.collapsed?null:c(this,0)},deleteContents:function(){var a;this.collapsed||c(this,1);r.webkit&&(a=this.startContainer,3!=a.nodeType||a.nodeValue.length||(this.setStartBefore(a).collapse(!0),f.remove(a))); -return this},extractContents:function(){return this.collapsed?null:c(this,2)},setStart:function(a,g){return b(!0,a,g,this)},setEnd:function(a,g){return b(!1,a,g,this)},setStartAfter:function(a){return this.setStart(a.parentNode,f.getNodeIndex(a)+1)},setStartBefore:function(a){return this.setStart(a.parentNode,f.getNodeIndex(a))},setEndAfter:function(a){return this.setEnd(a.parentNode,f.getNodeIndex(a)+1)},setEndBefore:function(a){return this.setEnd(a.parentNode,f.getNodeIndex(a))},setStartAtFirst:function(a){return this.setStart(a, -0)},setStartAtLast:function(a){return this.setStart(a,3==a.nodeType?a.nodeValue.length:a.childNodes.length)},setEndAtFirst:function(a){return this.setEnd(a,0)},setEndAtLast:function(a){return this.setEnd(a,3==a.nodeType?a.nodeValue.length:a.childNodes.length)},selectNode:function(a){return this.setStartBefore(a).setEndAfter(a)},selectNodeContents:function(a){return this.setStart(a,0).setEndAtLast(a)},cloneRange:function(){return(new k(this.document)).setStart(this.startContainer,this.startOffset).setEnd(this.endContainer, -this.endOffset)},collapse:function(a){a?(this.endContainer=this.startContainer,this.endOffset=this.startOffset):(this.startContainer=this.endContainer,this.startOffset=this.endOffset);this.collapsed=!0;return this},shrinkBoundary:function(a){function g(a){return 1==a.nodeType&&!f.isBookmarkNode(a)&&!w.$empty[a.tagName]&&!w.$nonChild[a.tagName]}for(var b,e=this.collapsed;1==this.startContainer.nodeType&&(b=this.startContainer.childNodes[this.startOffset])&&g(b);)this.setStart(b,0);if(e)return this.collapse(!0); -if(!a)for(;1==this.endContainer.nodeType&&0=g.nodeValue.length)this.setStartAfter(g);else{var l=f.split(g,b);g===c?this.setEnd(l,this.endOffset-b):g.parentNode===c&&(this.endOffset+=1);this.setStartBefore(l)}if(e)return this.collapse(!0)}a||(b=this.endOffset,c=this.endContainer,3==c.nodeType&&(0==b?this.setEndBefore(c):(b=e.nodeValue.length)a["set"+b.replace(/(\w)/,function(a){return a.toUpperCase()})+"After"](e)}if(a||!this.collapsed)g(this,"start"),g(this,"end");return this},insertNode:function(a){var g=a,b=1;11==a.nodeType&&(g=a.firstChild,b=a.childNodes.length);this.trimBoundary(!0);var e=this.startContainer,c=e.childNodes[this.startOffset];c?e.insertBefore(a,c):e.appendChild(a);g.parentNode===this.endContainer&&(this.endOffset+=b);return this.setStartBefore(g)}, -setCursor:function(a,g){return this.collapse(!a).select(g)},createBookmark:function(a,g){var b,e=this.document.createElement("span");e.style.cssText="display:none;line-height:0px;";e.appendChild(this.document.createTextNode("\u200d"));e.id="_baidu_bookmark_start_"+(g?"":h++);this.collapsed||(b=e.cloneNode(!0),b.id="_baidu_bookmark_end_"+(g?"":h++));this.insertNode(e);b&&this.collapse().insertNode(b).setEndBefore(b);this.setStartAfter(e);return{start:a?e.id:e,end:b?a?b.id:b:null,id:a}},moveToBookmark:function(a){var g= -a.id?this.document.getElementById(a.start):a.start;a=a.end&&a.id?this.document.getElementById(a.end):a.end;this.setStartBefore(g);f.remove(g);a?(this.setEndBefore(a),f.remove(a)):this.collapse(!0);return this},enlarge:function(a,g){var b=f.isBody,e,c,l=this.document.createTextNode("");if(a){c=this.startContainer;1==c.nodeType?c.childNodes[this.startOffset]?e=c=c.childNodes[this.startOffset]:(c.appendChild(l),e=c=l):e=c;for(;;){if(f.isBlockElm(c)){for(c=e;(e=c.previousSibling)&&!f.isBlockElm(e);)c= -e;this.setStartBefore(c);break}e=c;c=c.parentNode}c=this.endContainer;1==c.nodeType?((e=c.childNodes[this.endOffset])?c.insertBefore(l,e):c.appendChild(l),e=c=l):e=c;for(;;){if(f.isBlockElm(c)){for(c=e;(e=c.nextSibling)&&!f.isBlockElm(e);)c=e;this.setEndAfter(c);break}e=c;c=c.parentNode}l.parentNode===this.endContainer&&this.endOffset--;f.remove(l)}if(!this.collapsed){for(;!(0!=this.startOffset||g&&g(this.startContainer)||b(this.startContainer));)this.setStartBefore(this.startContainer);for(;!(this.endOffset!= -(1==this.endContainer.nodeType?this.endContainer.childNodes.length:this.endContainer.nodeValue.length)||g&&g(this.endContainer)||b(this.endContainer));)this.setEndAfter(this.endContainer)}return this},enlargeToBlockElm:function(a){for(;!f.isBlockElm(this.startContainer);)this.setStartBefore(this.startContainer);if(!a)for(;!f.isBlockElm(this.endContainer);)this.setEndAfter(this.endContainer);return this},adjustmentBoundary:function(){if(!this.collapsed){for(;!f.isBody(this.startContainer)&&this.startOffset== -this.startContainer[3==this.startContainer.nodeType?"nodeValue":"childNodes"].length&&this.startContainer[3==this.startContainer.nodeType?"nodeValue":"childNodes"].length;)this.setStartAfter(this.startContainer);for(;!f.isBody(this.endContainer)&&!this.endOffset&&this.endContainer[3==this.endContainer.nodeType?"nodeValue":"childNodes"].length;)this.setEndBefore(this.endContainer)}return this},applyInlineStyle:function(a,g,b){if(this.collapsed)return this;this.trimBoundary().enlarge(!1,function(a){return 1== -a.nodeType&&f.isBlockElm(a)}).adjustmentBoundary();for(var e=this.createBookmark(),c=e.end,l=function(a){return 1==a.nodeType?"br"!=a.tagName.toLowerCase():!f.isWhitespace(a)},k=f.getNextDomNode(e.start,!1,l),h,d,p=this.cloneRange();k&&f.getPosition(k,c)&f.POSITION_PRECEDING;)if(3==k.nodeType||w[a][k.tagName]){p.setStartBefore(k);for(h=k;h&&(3==h.nodeType||w[a][h.tagName])&&h!==c;)d=h,h=f.getNextDomNode(h,1==h.nodeType,null,function(g){return w[a][g.tagName]});var k=p.setEndAfter(d).extractContents(), -B;if(b&&0k&&(k=0);l.push(k);return l}var e={},c=this;e.startAddress=g(!0);a||(e.endAddress=c.collapsed?[].concat(e.startAddress):g());return e},moveToAddress:function(a, -b){function g(a,b){for(var c=e.document.body,k,l,h=0,d,n=a.length;hx)n=u+1;else return{container:g,offset:c(k)}}if(-1== -u){d.moveToElementText(g);d.setEndPoint("StartToStart",a);d=d.text.replace(/(\r\n|\r)/g,"\n").length;l=g.childNodes;if(!d)return k=l[l.length-1],{container:k,offset:k.nodeValue.length};for(c=l.length;0r.version?"":"")+""+(b.iframeCssUrl?"":"")+(b.initialStyle?"":"")+" - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/attachment/attachment.css b/static/plugs/ueditor/dialogs/attachment/attachment.css deleted file mode 100644 index e9d11af25..000000000 --- a/static/plugs/ueditor/dialogs/attachment/attachment.css +++ /dev/null @@ -1,681 +0,0 @@ -@charset "utf-8"; -/* dialog样式 */ -.wrapper { - zoom: 1; - width: 630px; - *width: 626px; - height: 380px; - margin: 0 auto; - padding: 10px; - position: relative; - font-family: sans-serif; -} - -/*tab样式框大小*/ -.tabhead { - float:left; -} -.tabbody { - width: 100%; - height: 346px; - position: relative; - clear: both; -} - -.tabbody .panel { - position: absolute; - width: 0; - height: 0; - background: #fff; - overflow: hidden; - display: none; -} - -.tabbody .panel.focus { - width: 100%; - height: 346px; - display: block; -} - -/* 上传附件 */ -.tabbody #upload.panel { - width: 0; - height: 0; - overflow: hidden; - position: absolute !important; - clip: rect(1px, 1px, 1px, 1px); - background: #fff; - display: block; -} - -.tabbody #upload.panel.focus { - width: 100%; - height: 346px; - display: block; - clip: auto; -} - -#upload .queueList { - margin: 0; - width: 100%; - height: 100%; - position: absolute; - overflow: hidden; -} - -#upload p { - margin: 0; -} - -.element-invisible { - width: 0 !important; - height: 0 !important; - border: 0; - padding: 0; - margin: 0; - overflow: hidden; - position: absolute !important; - clip: rect(1px, 1px, 1px, 1px); -} - -#upload .placeholder { - margin: 10px; - border: 2px dashed #e6e6e6; - *border: 0px dashed #e6e6e6; - height: 172px; - padding-top: 150px; - text-align: center; - background: url(./images/image.png) center 70px no-repeat; - color: #cccccc; - font-size: 18px; - position: relative; - top:0; - *top: 10px; -} - -#upload .placeholder .webuploader-pick { - font-size: 18px; - background: #00b7ee; - border-radius: 3px; - line-height: 44px; - padding: 0 30px; - *width: 120px; - color: #fff; - display: inline-block; - margin: 0 auto 20px auto; - cursor: pointer; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); -} - -#upload .placeholder .webuploader-pick-hover { - background: #00a2d4; -} - - -#filePickerContainer { - text-align: center; -} - -#upload .placeholder .flashTip { - color: #666666; - font-size: 12px; - position: absolute; - width: 100%; - text-align: center; - bottom: 20px; -} - -#upload .placeholder .flashTip a { - color: #0785d1; - text-decoration: none; -} - -#upload .placeholder .flashTip a:hover { - text-decoration: underline; -} - -#upload .placeholder.webuploader-dnd-over { - border-color: #999999; -} - -#upload .filelist { - list-style: none; - margin: 0; - padding: 0; - overflow-x: hidden; - overflow-y: auto; - position: relative; - height: 300px; -} - -#upload .filelist:after { - content: ''; - display: block; - width: 0; - height: 0; - overflow: hidden; - clear: both; -} - -#upload .filelist li { - width: 113px; - height: 113px; - background: url(./images/bg.png); - text-align: center; - margin: 9px 0 0 9px; - *margin: 6px 0 0 6px; - position: relative; - display: block; - float: left; - overflow: hidden; - font-size: 12px; -} - -#upload .filelist li p.log { - position: relative; - top: -45px; -} - -#upload .filelist li p.title { - position: absolute; - top: 0; - left: 0; - width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - top: 5px; - text-indent: 5px; - text-align: left; -} - -#upload .filelist li p.progress { - position: absolute; - width: 100%; - bottom: 0; - left: 0; - height: 8px; - overflow: hidden; - z-index: 50; - margin: 0; - border-radius: 0; - background: none; - -webkit-box-shadow: 0 0 0; -} - -#upload .filelist li p.progress span { - display: none; - overflow: hidden; - width: 0; - height: 100%; - background: #1483d8 url(./images/progress.png) repeat-x; - - -webit-transition: width 200ms linear; - -moz-transition: width 200ms linear; - -o-transition: width 200ms linear; - -ms-transition: width 200ms linear; - transition: width 200ms linear; - - -webkit-animation: progressmove 2s linear infinite; - -moz-animation: progressmove 2s linear infinite; - -o-animation: progressmove 2s linear infinite; - -ms-animation: progressmove 2s linear infinite; - animation: progressmove 2s linear infinite; - - -webkit-transform: translateZ(0); -} - -@-webkit-keyframes progressmove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 17px 0; - } -} - -@-moz-keyframes progressmove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 17px 0; - } -} - -@keyframes progressmove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 17px 0; - } -} - -#upload .filelist li p.imgWrap { - position: relative; - z-index: 2; - line-height: 113px; - vertical-align: middle; - overflow: hidden; - width: 113px; - height: 113px; - - -webkit-transform-origin: 50% 50%; - -moz-transform-origin: 50% 50%; - -o-transform-origin: 50% 50%; - -ms-transform-origin: 50% 50%; - transform-origin: 50% 50%; - - -webit-transition: 200ms ease-out; - -moz-transition: 200ms ease-out; - -o-transition: 200ms ease-out; - -ms-transition: 200ms ease-out; - transition: 200ms ease-out; -} -#upload .filelist li p.imgWrap.notimage { - margin-top: 0; - width: 111px; - height: 111px; - border: 1px #eeeeee solid; -} -#upload .filelist li p.imgWrap.notimage i.file-preview { - margin-top: 15px; -} - -#upload .filelist li img { - width: 100%; -} - -#upload .filelist li p.error { - background: #f43838; - color: #fff; - position: absolute; - bottom: 0; - left: 0; - height: 28px; - line-height: 28px; - width: 100%; - z-index: 100; - display:none; -} - -#upload .filelist li .success { - display: block; - position: absolute; - left: 0; - bottom: 0; - height: 40px; - width: 100%; - z-index: 200; - background: url(./images/success.png) no-repeat right bottom; - background-image: url(./images/success.gif) \9; -} - -#upload .filelist li.filePickerBlock { - width: 113px; - height: 113px; - background: url(./images/image.png) no-repeat center 12px; - border: 1px solid #eeeeee; - border-radius: 0; -} -#upload .filelist li.filePickerBlock div.webuploader-pick { - width: 100%; - height: 100%; - margin: 0; - padding: 0; - opacity: 0; - background: none; - font-size: 0; -} - -#upload .filelist div.file-panel { - position: absolute; - height: 0; - filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#80000000', endColorstr='#80000000') \0; - background: rgba(0, 0, 0, 0.5); - width: 100%; - top: 0; - left: 0; - overflow: hidden; - z-index: 300; -} - -#upload .filelist div.file-panel span { - width: 24px; - height: 24px; - display: inline; - float: right; - text-indent: -9999px; - overflow: hidden; - background: url(./images/icons.png) no-repeat; - background: url(./images/icons.gif) no-repeat \9; - margin: 5px 1px 1px; - cursor: pointer; - -webkit-tap-highlight-color: rgba(0,0,0,0); - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -#upload .filelist div.file-panel span.rotateLeft { - display:none; - background-position: 0 -24px; -} - -#upload .filelist div.file-panel span.rotateLeft:hover { - background-position: 0 0; -} - -#upload .filelist div.file-panel span.rotateRight { - display:none; - background-position: -24px -24px; -} - -#upload .filelist div.file-panel span.rotateRight:hover { - background-position: -24px 0; -} - -#upload .filelist div.file-panel span.cancel { - background-position: -48px -24px; -} - -#upload .filelist div.file-panel span.cancel:hover { - background-position: -48px 0; -} - -#upload .statusBar { - height: 45px; - border-bottom: 1px solid #dadada; - margin: 0 10px; - padding: 0; - line-height: 45px; - vertical-align: middle; - position: relative; -} - -#upload .statusBar .progress { - border: 1px solid #1483d8; - width: 198px; - background: #fff; - height: 18px; - position: absolute; - top: 12px; - display: none; - text-align: center; - line-height: 18px; - color: #6dbfff; - margin: 0 10px 0 0; -} -#upload .statusBar .progress span.percentage { - width: 0; - height: 100%; - left: 0; - top: 0; - background: #1483d8; - position: absolute; -} -#upload .statusBar .progress span.text { - position: relative; - z-index: 10; -} - -#upload .statusBar .info { - display: inline-block; - font-size: 14px; - color: #666666; -} - -#upload .statusBar .btns { - position: absolute; - top: 7px; - right: 0; - line-height: 30px; -} - -#filePickerBtn { - display: inline-block; - float: left; -} -#upload .statusBar .btns .webuploader-pick, -#upload .statusBar .btns .uploadBtn, -#upload .statusBar .btns .uploadBtn.state-uploading, -#upload .statusBar .btns .uploadBtn.state-paused { - background: #ffffff; - border: 1px solid #cfcfcf; - color: #565656; - padding: 0 18px; - display: inline-block; - border-radius: 3px; - margin-left: 10px; - cursor: pointer; - font-size: 14px; - float: left; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -#upload .statusBar .btns .webuploader-pick-hover, -#upload .statusBar .btns .uploadBtn:hover, -#upload .statusBar .btns .uploadBtn.state-uploading:hover, -#upload .statusBar .btns .uploadBtn.state-paused:hover { - background: #f0f0f0; -} - -#upload .statusBar .btns .uploadBtn, -#upload .statusBar .btns .uploadBtn.state-paused{ - background: #00b7ee; - color: #fff; - border-color: transparent; -} -#upload .statusBar .btns .uploadBtn:hover, -#upload .statusBar .btns .uploadBtn.state-paused:hover{ - background: #00a2d4; -} - -#upload .statusBar .btns .uploadBtn.disabled { - pointer-events: none; - filter:alpha(opacity=60); - -moz-opacity:0.6; - -khtml-opacity: 0.6; - opacity: 0.6; -} - - - -/* 图片管理样式 */ -#online { - width: 100%; - height: 336px; - padding: 10px 0 0 0; -} -#online #fileList{ - width: 100%; - height: 100%; - overflow-x: hidden; - overflow-y: auto; - position: relative; -} -#online ul { - display: block; - list-style: none; - margin: 0; - padding: 0; -} -#online li { - float: left; - display: block; - list-style: none; - padding: 0; - width: 113px; - height: 113px; - margin: 0 0 9px 9px; - *margin: 0 0 6px 6px; - background-color: #eee; - overflow: hidden; - cursor: pointer; - position: relative; -} -#online li.clearFloat { - float: none; - clear: both; - display: block; - width:0; - height:0; - margin: 0; - padding: 0; -} -#online li img { - cursor: pointer; -} -#online li div.file-wrapper { - cursor: pointer; - position: absolute; - display: block; - width: 111px; - height: 111px; - border: 1px solid #eee; - background: url("./images/bg.png") repeat; -} -#online li div span.file-title{ - display: block; - padding: 0 3px; - margin: 3px 0 0 0; - font-size: 12px; - height: 13px; - color: #555555; - text-align: center; - width: 107px; - white-space: nowrap; - word-break: break-all; - overflow: hidden; - text-overflow: ellipsis; -} -#online li .icon { - cursor: pointer; - width: 113px; - height: 113px; - position: absolute; - top: 0; - left: 0; - z-index: 2; - border: 0; - background-repeat: no-repeat; -} -#online li .icon:hover { - width: 107px; - height: 107px; - border: 3px solid #1094fa; -} -#online li.selected .icon { - background-image: url(images/success.png); - background-image: url(images/success.gif) \9; - background-position: 75px 75px; -} -#online li.selected .icon:hover { - width: 107px; - height: 107px; - border: 3px solid #1094fa; - background-position: 72px 72px; -} - - -/* 在线文件的文件预览图标 */ -i.file-preview { - display: block; - margin: 10px auto; - width: 70px; - height: 70px; - background-image: url("./images/file-icons.png"); - background-image: url("./images/file-icons.gif") \9; - background-position: -140px center; - background-repeat: no-repeat; -} -i.file-preview.file-type-dir{ - background-position: 0 center; -} -i.file-preview.file-type-file{ - background-position: -140px center; -} -i.file-preview.file-type-filelist{ - background-position: -210px center; -} -i.file-preview.file-type-zip, -i.file-preview.file-type-rar, -i.file-preview.file-type-7z, -i.file-preview.file-type-tar, -i.file-preview.file-type-gz, -i.file-preview.file-type-bz2{ - background-position: -280px center; -} -i.file-preview.file-type-xls, -i.file-preview.file-type-xlsx{ - background-position: -350px center; -} -i.file-preview.file-type-doc, -i.file-preview.file-type-docx{ - background-position: -420px center; -} -i.file-preview.file-type-ppt, -i.file-preview.file-type-pptx{ - background-position: -490px center; -} -i.file-preview.file-type-vsd{ - background-position: -560px center; -} -i.file-preview.file-type-pdf{ - background-position: -630px center; -} -i.file-preview.file-type-txt, -i.file-preview.file-type-md, -i.file-preview.file-type-json, -i.file-preview.file-type-htm, -i.file-preview.file-type-xml, -i.file-preview.file-type-html, -i.file-preview.file-type-js, -i.file-preview.file-type-css, -i.file-preview.file-type-php, -i.file-preview.file-type-jsp, -i.file-preview.file-type-asp{ - background-position: -700px center; -} -i.file-preview.file-type-apk{ - background-position: -770px center; -} -i.file-preview.file-type-exe{ - background-position: -840px center; -} -i.file-preview.file-type-ipa{ - background-position: -910px center; -} -i.file-preview.file-type-mp4, -i.file-preview.file-type-swf, -i.file-preview.file-type-mkv, -i.file-preview.file-type-avi, -i.file-preview.file-type-flv, -i.file-preview.file-type-mov, -i.file-preview.file-type-mpg, -i.file-preview.file-type-mpeg, -i.file-preview.file-type-ogv, -i.file-preview.file-type-webm, -i.file-preview.file-type-rm, -i.file-preview.file-type-rmvb{ - background-position: -980px center; -} -i.file-preview.file-type-ogg, -i.file-preview.file-type-wav, -i.file-preview.file-type-wmv, -i.file-preview.file-type-mid, -i.file-preview.file-type-mp3{ - background-position: -1050px center; -} -i.file-preview.file-type-jpg, -i.file-preview.file-type-jpeg, -i.file-preview.file-type-gif, -i.file-preview.file-type-bmp, -i.file-preview.file-type-png, -i.file-preview.file-type-psd{ - background-position: -140px center; -} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/attachment/attachment.html b/static/plugs/ueditor/dialogs/attachment/attachment.html deleted file mode 100644 index 1a52bb933..000000000 --- a/static/plugs/ueditor/dialogs/attachment/attachment.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - ueditor图片对话框 - - - - - - - - - - - - - - -
                  -
                  - - -
                  -
                  - -
                  -
                  -
                  -
                  - 0% - -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                    -
                  • -
                  -
                  -
                  - - -
                  -
                  -
                  - -
                  -
                  - - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/attachment/attachment.js b/static/plugs/ueditor/dialogs/attachment/attachment.js deleted file mode 100644 index ce3be633d..000000000 --- a/static/plugs/ueditor/dialogs/attachment/attachment.js +++ /dev/null @@ -1,754 +0,0 @@ -/** - * User: Jinqn - * Date: 14-04-08 - * Time: 下午16:34 - * 上传图片对话框逻辑代码,包括tab: 远程图片/上传图片/在线图片/搜索图片 - */ - -(function () { - - var uploadFile, - onlineFile; - - window.onload = function () { - initTabs(); - initButtons(); - }; - - /* 初始化tab标签 */ - function initTabs() { - var tabs = $G('tabhead').children; - for (var i = 0; i < tabs.length; i++) { - domUtils.on(tabs[i], "click", function (e) { - var target = e.target || e.srcElement; - setTabFocus(target.getAttribute('data-content-id')); - }); - } - - setTabFocus('upload'); - } - - /* 初始化tabbody */ - function setTabFocus(id) { - if(!id) return; - var i, bodyId, tabs = $G('tabhead').children; - for (i = 0; i < tabs.length; i++) { - bodyId = tabs[i].getAttribute('data-content-id') - if (bodyId == id) { - domUtils.addClass(tabs[i], 'focus'); - domUtils.addClass($G(bodyId), 'focus'); - } else { - domUtils.removeClasses(tabs[i], 'focus'); - domUtils.removeClasses($G(bodyId), 'focus'); - } - } - switch (id) { - case 'upload': - uploadFile = uploadFile || new UploadFile('queueList'); - break; - case 'online': - onlineFile = onlineFile || new OnlineFile('fileList'); - break; - } - } - - /* 初始化onok事件 */ - function initButtons() { - - dialog.onok = function () { - var list = [], id, tabs = $G('tabhead').children; - for (var i = 0; i < tabs.length; i++) { - if (domUtils.hasClass(tabs[i], 'focus')) { - id = tabs[i].getAttribute('data-content-id'); - break; - } - } - - switch (id) { - case 'upload': - list = uploadFile.getInsertList(); - var count = uploadFile.getQueueCount(); - if (count) { - $('.info', '#queueList').html('' + '还有2个未上传文件'.replace(/[\d]/, count) + ''); - return false; - } - break; - case 'online': - list = onlineFile.getInsertList(); - break; - } - - editor.execCommand('insertfile', list); - }; - } - - - /* 上传附件 */ - function UploadFile(target) { - this.$wrap = target.constructor == String ? $('#' + target) : $(target); - this.init(); - } - UploadFile.prototype = { - init: function () { - this.fileList = []; - this.initContainer(); - this.initUploader(); - }, - initContainer: function () { - this.$queue = this.$wrap.find('.filelist'); - }, - /* 初始化容器 */ - initUploader: function () { - var _this = this, - $ = jQuery, // just in case. Make sure it's not an other libaray. - $wrap = _this.$wrap, - // 图片容器 - $queue = $wrap.find('.filelist'), - // 状态栏,包括进度和控制按钮 - $statusBar = $wrap.find('.statusBar'), - // 文件总体选择信息。 - $info = $statusBar.find('.info'), - // 上传按钮 - $upload = $wrap.find('.uploadBtn'), - // 上传按钮 - $filePickerBtn = $wrap.find('.filePickerBtn'), - // 上传按钮 - $filePickerBlock = $wrap.find('.filePickerBlock'), - // 没选择文件之前的内容。 - $placeHolder = $wrap.find('.placeholder'), - // 总体进度条 - $progress = $statusBar.find('.progress').hide(), - // 添加的文件数量 - fileCount = 0, - // 添加的文件总大小 - fileSize = 0, - // 优化retina, 在retina下这个值是2 - ratio = window.devicePixelRatio || 1, - // 缩略图大小 - thumbnailWidth = 113 * ratio, - thumbnailHeight = 113 * ratio, - // 可能有pedding, ready, uploading, confirm, done. - state = '', - // 所有文件的进度信息,key为file id - percentages = {}, - supportTransition = (function () { - var s = document.createElement('p').style, - r = 'transition' in s || - 'WebkitTransition' in s || - 'MozTransition' in s || - 'msTransition' in s || - 'OTransition' in s; - s = null; - return r; - })(), - // WebUploader实例 - uploader, - actionUrl = editor.getActionUrl(editor.getOpt('fileActionName')), - fileMaxSize = editor.getOpt('fileMaxSize'), - acceptExtensions = (editor.getOpt('fileAllowFiles') || []).join('').replace(/\./g, ',').replace(/^[,]/, '');; - - if (!WebUploader.Uploader.support()) { - $('#filePickerReady').after($('
                  ').html(lang.errorNotSupport)).hide(); - return; - } else if (!editor.getOpt('fileActionName')) { - $('#filePickerReady').after($('
                  ').html(lang.errorLoadConfig)).hide(); - return; - } - - uploader = _this.uploader = WebUploader.create({ - pick: { - id: '#filePickerReady', - label: lang.uploadSelectFile - }, - swf: '../../third-party/webuploader/Uploader.swf', - server: actionUrl, - fileVal: editor.getOpt('fileFieldName'), - duplicate: true, - fileSingleSizeLimit: fileMaxSize, - compress: false - }); - uploader.addButton({ - id: '#filePickerBlock' - }); - uploader.addButton({ - id: '#filePickerBtn', - label: lang.uploadAddFile - }); - - setState('pedding'); - - // 当有文件添加进来时执行,负责view的创建 - function addFile(file) { - var $li = $('
                • ' + - '

                  ' + file.name + '

                  ' + - '

                  ' + - '

                  ' + - '
                • '), - - $btns = $('
                  ' + - '' + lang.uploadDelete + '' + - '' + lang.uploadTurnRight + '' + - '' + lang.uploadTurnLeft + '
                  ').appendTo($li), - $prgress = $li.find('p.progress span'), - $wrap = $li.find('p.imgWrap'), - $info = $('

                  ').hide().appendTo($li), - - showError = function (code) { - switch (code) { - case 'exceed_size': - text = lang.errorExceedSize; - break; - case 'interrupt': - text = lang.errorInterrupt; - break; - case 'http': - text = lang.errorHttp; - break; - case 'not_allow_type': - text = lang.errorFileType; - break; - default: - text = lang.errorUploadRetry; - break; - } - $info.text(text).show(); - }; - - if (file.getStatus() === 'invalid') { - showError(file.statusText); - } else { - $wrap.text(lang.uploadPreview); - if ('|png|jpg|jpeg|bmp|gif|'.indexOf('|'+file.ext.toLowerCase()+'|') == -1) { - $wrap.empty().addClass('notimage').append('' + - '' + file.name + ''); - } else { - if (browser.ie && browser.version <= 7) { - $wrap.text(lang.uploadNoPreview); - } else { - uploader.makeThumb(file, function (error, src) { - if (error || !src) { - $wrap.text(lang.uploadNoPreview); - } else { - var $img = $(''); - $wrap.empty().append($img); - $img.on('error', function () { - $wrap.text(lang.uploadNoPreview); - }); - } - }, thumbnailWidth, thumbnailHeight); - } - } - percentages[ file.id ] = [ file.size, 0 ]; - file.rotation = 0; - - /* 检查文件格式 */ - if (!file.ext || acceptExtensions.indexOf(file.ext.toLowerCase()) == -1) { - showError('not_allow_type'); - uploader.removeFile(file); - } - } - - file.on('statuschange', function (cur, prev) { - if (prev === 'progress') { - $prgress.hide().width(0); - } else if (prev === 'queued') { - $li.off('mouseenter mouseleave'); - $btns.remove(); - } - // 成功 - if (cur === 'error' || cur === 'invalid') { - showError(file.statusText); - percentages[ file.id ][ 1 ] = 1; - } else if (cur === 'interrupt') { - showError('interrupt'); - } else if (cur === 'queued') { - percentages[ file.id ][ 1 ] = 0; - } else if (cur === 'progress') { - $info.hide(); - $prgress.css('display', 'block'); - } else if (cur === 'complete') { - } - - $li.removeClass('state-' + prev).addClass('state-' + cur); - }); - - $li.on('mouseenter', function () { - $btns.stop().animate({height: 30}); - }); - $li.on('mouseleave', function () { - $btns.stop().animate({height: 0}); - }); - - $btns.on('click', 'span', function () { - var index = $(this).index(), - deg; - - switch (index) { - case 0: - uploader.removeFile(file); - return; - case 1: - file.rotation += 90; - break; - case 2: - file.rotation -= 90; - break; - } - - if (supportTransition) { - deg = 'rotate(' + file.rotation + 'deg)'; - $wrap.css({ - '-webkit-transform': deg, - '-mos-transform': deg, - '-o-transform': deg, - 'transform': deg - }); - } else { - $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')'); - } - - }); - - $li.insertBefore($filePickerBlock); - } - - // 负责view的销毁 - function removeFile(file) { - var $li = $('#' + file.id); - delete percentages[ file.id ]; - updateTotalProgress(); - $li.off().find('.file-panel').off().end().remove(); - } - - function updateTotalProgress() { - var loaded = 0, - total = 0, - spans = $progress.children(), - percent; - - $.each(percentages, function (k, v) { - total += v[ 0 ]; - loaded += v[ 0 ] * v[ 1 ]; - }); - - percent = total ? loaded / total : 0; - - spans.eq(0).text(Math.round(percent * 100) + '%'); - spans.eq(1).css('width', Math.round(percent * 100) + '%'); - updateStatus(); - } - - function setState(val, files) { - - if (val != state) { - - var stats = uploader.getStats(); - - $upload.removeClass('state-' + state); - $upload.addClass('state-' + val); - - switch (val) { - - /* 未选择文件 */ - case 'pedding': - $queue.addClass('element-invisible'); - $statusBar.addClass('element-invisible'); - $placeHolder.removeClass('element-invisible'); - $progress.hide(); $info.hide(); - uploader.refresh(); - break; - - /* 可以开始上传 */ - case 'ready': - $placeHolder.addClass('element-invisible'); - $queue.removeClass('element-invisible'); - $statusBar.removeClass('element-invisible'); - $progress.hide(); $info.show(); - $upload.text(lang.uploadStart); - uploader.refresh(); - break; - - /* 上传中 */ - case 'uploading': - $progress.show(); $info.hide(); - $upload.text(lang.uploadPause); - break; - - /* 暂停上传 */ - case 'paused': - $progress.show(); $info.hide(); - $upload.text(lang.uploadContinue); - break; - - case 'confirm': - $progress.show(); $info.hide(); - $upload.text(lang.uploadStart); - - stats = uploader.getStats(); - if (stats.successNum && !stats.uploadFailNum) { - setState('finish'); - return; - } - break; - - case 'finish': - $progress.hide(); $info.show(); - if (stats.uploadFailNum) { - $upload.text(lang.uploadRetry); - } else { - $upload.text(lang.uploadStart); - } - break; - } - - state = val; - updateStatus(); - - } - - if (!_this.getQueueCount()) { - $upload.addClass('disabled') - } else { - $upload.removeClass('disabled') - } - - } - - function updateStatus() { - var text = '', stats; - - if (state === 'ready') { - text = lang.updateStatusReady.replace('_', fileCount).replace('_KB', WebUploader.formatSize(fileSize)); - } else if (state === 'confirm') { - stats = uploader.getStats(); - if (stats.uploadFailNum) { - text = lang.updateStatusConfirm.replace('_', stats.successNum).replace('_', stats.successNum); - } - } else { - stats = uploader.getStats(); - text = lang.updateStatusFinish.replace('_', fileCount). - replace('_KB', WebUploader.formatSize(fileSize)). - replace('_', stats.successNum); - - if (stats.uploadFailNum) { - text += lang.updateStatusError.replace('_', stats.uploadFailNum); - } - } - - $info.html(text); - } - - uploader.on('fileQueued', function (file) { - fileCount++; - fileSize += file.size; - - if (fileCount === 1) { - $placeHolder.addClass('element-invisible'); - $statusBar.show(); - } - - addFile(file); - }); - - uploader.on('fileDequeued', function (file) { - fileCount--; - fileSize -= file.size; - - removeFile(file); - updateTotalProgress(); - }); - - uploader.on('filesQueued', function (file) { - if (!uploader.isInProgress() && (state == 'pedding' || state == 'finish' || state == 'confirm' || state == 'ready')) { - setState('ready'); - } - updateTotalProgress(); - }); - - uploader.on('all', function (type, files) { - switch (type) { - case 'uploadFinished': - setState('confirm', files); - break; - case 'startUpload': - /* 添加额外的GET参数 */ - var params = utils.serializeParam(editor.queryCommandValue('serverparam')) || '', - url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?':'&') + 'encode=utf-8&' + params); - uploader.option('server', url); - setState('uploading', files); - break; - case 'stopUpload': - setState('paused', files); - break; - } - }); - - uploader.on('uploadBeforeSend', function (file, data, header) { - //这里可以通过data对象添加POST参数 - header['X_Requested_With'] = 'XMLHttpRequest'; - }); - - uploader.on('uploadProgress', function (file, percentage) { - var $li = $('#' + file.id), - $percent = $li.find('.progress span'); - - $percent.css('width', percentage * 100 + '%'); - percentages[ file.id ][ 1 ] = percentage; - updateTotalProgress(); - }); - - uploader.on('uploadSuccess', function (file, ret) { - var $file = $('#' + file.id); - try { - var responseText = (ret._raw || ret), - json = utils.str2json(responseText); - if (json.state == 'SUCCESS') { - _this.fileList.push(json); - $file.append(''); - } else { - $file.find('.error').text(json.state).show(); - } - } catch (e) { - $file.find('.error').text(lang.errorServerUpload).show(); - } - }); - - uploader.on('uploadError', function (file, code) { - }); - uploader.on('error', function (code, file) { - if (code == 'Q_TYPE_DENIED' || code == 'F_EXCEED_SIZE') { - addFile(file); - } - }); - uploader.on('uploadComplete', function (file, ret) { - }); - - $upload.on('click', function () { - if ($(this).hasClass('disabled')) { - return false; - } - - if (state === 'ready') { - uploader.upload(); - } else if (state === 'paused') { - uploader.upload(); - } else if (state === 'uploading') { - uploader.stop(); - } - }); - - $upload.addClass('state-' + state); - updateTotalProgress(); - }, - getQueueCount: function () { - var file, i, status, readyFile = 0, files = this.uploader.getFiles(); - for (i = 0; file = files[i++]; ) { - status = file.getStatus(); - if (status == 'queued' || status == 'uploading' || status == 'progress') readyFile++; - } - return readyFile; - }, - getInsertList: function () { - var i, link, data, list = [], - prefix = editor.getOpt('fileUrlPrefix'); - for (i = 0; i < this.fileList.length; i++) { - data = this.fileList[i]; - link = data.url; - list.push({ - title: data.original || link.substr(link.lastIndexOf('/') + 1), - url: prefix + link - }); - } - return list; - } - }; - - - /* 在线附件 */ - function OnlineFile(target) { - this.container = utils.isString(target) ? document.getElementById(target) : target; - this.init(); - } - OnlineFile.prototype = { - init: function () { - this.initContainer(); - this.initEvents(); - this.initData(); - }, - /* 初始化容器 */ - initContainer: function () { - this.container.innerHTML = ''; - this.list = document.createElement('ul'); - this.clearFloat = document.createElement('li'); - - domUtils.addClass(this.list, 'list'); - domUtils.addClass(this.clearFloat, 'clearFloat'); - - this.list.appendChild(this.clearFloat); - this.container.appendChild(this.list); - }, - /* 初始化滚动事件,滚动到地步自动拉取数据 */ - initEvents: function () { - var _this = this; - - /* 滚动拉取图片 */ - domUtils.on($G('fileList'), 'scroll', function(e){ - var panel = this; - if (panel.scrollHeight - (panel.offsetHeight + panel.scrollTop) < 10) { - _this.getFileData(); - } - }); - /* 选中图片 */ - domUtils.on(this.list, 'click', function (e) { - var target = e.target || e.srcElement, - li = target.parentNode; - - if (li.tagName.toLowerCase() == 'li') { - if (domUtils.hasClass(li, 'selected')) { - domUtils.removeClasses(li, 'selected'); - } else { - domUtils.addClass(li, 'selected'); - } - } - }); - }, - /* 初始化第一次的数据 */ - initData: function () { - - /* 拉取数据需要使用的值 */ - this.state = 0; - this.listSize = editor.getOpt('fileManagerListSize'); - this.listIndex = 0; - this.listEnd = false; - - /* 第一次拉取数据 */ - this.getFileData(); - }, - /* 向后台拉取图片列表数据 */ - getFileData: function () { - var _this = this; - - if(!_this.listEnd && !this.isLoadingData) { - this.isLoadingData = true; - ajax.request(editor.getActionUrl(editor.getOpt('fileManagerActionName')), { - timeout: 100000, - data: utils.extend({ - start: this.listIndex, - size: this.listSize - }, editor.queryCommandValue('serverparam')), - method: 'get', - onsuccess: function (r) { - try { - var json = eval('(' + r.responseText + ')'); - if (json.state == 'SUCCESS') { - _this.pushData(json.list); - _this.listIndex = parseInt(json.start) + parseInt(json.list.length); - if(_this.listIndex >= json.total) { - _this.listEnd = true; - } - _this.isLoadingData = false; - } - } catch (e) { - if(r.responseText.indexOf('ue_separate_ue') != -1) { - var list = r.responseText.split(r.responseText); - _this.pushData(list); - _this.listIndex = parseInt(list.length); - _this.listEnd = true; - _this.isLoadingData = false; - } - } - }, - onerror: function () { - _this.isLoadingData = false; - } - }); - } - }, - /* 添加图片到列表界面上 */ - pushData: function (list) { - var i, item, img, filetype, preview, icon, _this = this, - urlPrefix = editor.getOpt('fileManagerUrlPrefix'); - for (i = 0; i < list.length; i++) { - if(list[i] && list[i].url) { - item = document.createElement('li'); - icon = document.createElement('span'); - filetype = list[i].url.substr(list[i].url.lastIndexOf('.') + 1); - - if ( "png|jpg|jpeg|gif|bmp".indexOf(filetype) != -1 ) { - preview = document.createElement('img'); - domUtils.on(preview, 'load', (function(image){ - return function(){ - _this.scale(image, image.parentNode.offsetWidth, image.parentNode.offsetHeight); - }; - })(preview)); - preview.width = 113; - preview.setAttribute('src', urlPrefix + list[i].url + (list[i].url.indexOf('?') == -1 ? '?noCache=':'&noCache=') + (+new Date()).toString(36) ); - } else { - var ic = document.createElement('i'), - textSpan = document.createElement('span'); - textSpan.innerHTML = list[i].url.substr(list[i].url.lastIndexOf('/') + 1); - preview = document.createElement('div'); - preview.appendChild(ic); - preview.appendChild(textSpan); - domUtils.addClass(preview, 'file-wrapper'); - domUtils.addClass(textSpan, 'file-title'); - domUtils.addClass(ic, 'file-type-' + filetype); - domUtils.addClass(ic, 'file-preview'); - } - domUtils.addClass(icon, 'icon'); - item.setAttribute('data-url', urlPrefix + list[i].url); - if (list[i].original) { - item.setAttribute('data-title', list[i].original); - } - - item.appendChild(preview); - item.appendChild(icon); - this.list.insertBefore(item, this.clearFloat); - } - } - }, - /* 改变图片大小 */ - scale: function (img, w, h, type) { - var ow = img.width, - oh = img.height; - - if (type == 'justify') { - if (ow >= oh) { - img.width = w; - img.height = h * oh / ow; - img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px'; - } else { - img.width = w * ow / oh; - img.height = h; - img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px'; - } - } else { - if (ow >= oh) { - img.width = w * ow / oh; - img.height = h; - img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px'; - } else { - img.width = w; - img.height = h * oh / ow; - img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px'; - } - } - }, - getInsertList: function () { - var i, lis = this.list.children, list = []; - for (i = 0; i < lis.length; i++) { - if (domUtils.hasClass(lis[i], 'selected')) { - var url = lis[i].getAttribute('data-url'); - var title = lis[i].getAttribute('data-title') || url.substr(url.lastIndexOf('/') + 1); - list.push({ - title: title, - url: url - }); - } - } - return list; - } - }; - - -})(); \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_chm.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_chm.gif deleted file mode 100644 index 9ca4fb6a2..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_chm.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_default.png b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_default.png deleted file mode 100644 index 50ac1cb16..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_default.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_doc.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_doc.gif deleted file mode 100644 index 206fede4e..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_doc.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_exe.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_exe.gif deleted file mode 100644 index 2e3b7a28e..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_exe.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_jpg.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_jpg.gif deleted file mode 100644 index 5d5dec026..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_jpg.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_mp3.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_mp3.gif deleted file mode 100644 index b351a1f2a..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_mp3.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_mv.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_mv.gif deleted file mode 100644 index 26019b099..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_mv.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_pdf.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_pdf.gif deleted file mode 100644 index bbb65c837..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_pdf.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_ppt.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_ppt.gif deleted file mode 100644 index ccb26fbeb..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_ppt.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_psd.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_psd.gif deleted file mode 100644 index 2e8743a27..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_psd.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_rar.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_rar.gif deleted file mode 100644 index 5359e46d2..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_rar.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_txt.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_txt.gif deleted file mode 100644 index e7b8dd21d..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_txt.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_xls.gif b/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_xls.gif deleted file mode 100644 index e86c1c663..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/fileTypeImages/icon_xls.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/alignicon.gif b/static/plugs/ueditor/dialogs/attachment/images/alignicon.gif deleted file mode 100644 index 005a5ac65..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/alignicon.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/alignicon.png b/static/plugs/ueditor/dialogs/attachment/images/alignicon.png deleted file mode 100644 index 4b6c444b7..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/alignicon.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/bg.png b/static/plugs/ueditor/dialogs/attachment/images/bg.png deleted file mode 100644 index 580be0a01..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/bg.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/file-icons.gif b/static/plugs/ueditor/dialogs/attachment/images/file-icons.gif deleted file mode 100644 index d8c02c27e..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/file-icons.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/file-icons.png b/static/plugs/ueditor/dialogs/attachment/images/file-icons.png deleted file mode 100644 index 3ff82c8c4..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/file-icons.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/icons.gif b/static/plugs/ueditor/dialogs/attachment/images/icons.gif deleted file mode 100644 index 78459dea7..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/icons.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/icons.png b/static/plugs/ueditor/dialogs/attachment/images/icons.png deleted file mode 100644 index 12e470016..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/icons.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/progress.png b/static/plugs/ueditor/dialogs/attachment/images/progress.png deleted file mode 100644 index 717c4865c..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/progress.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/success.gif b/static/plugs/ueditor/dialogs/attachment/images/success.gif deleted file mode 100644 index 8d4f3112b..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/success.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/attachment/images/success.png b/static/plugs/ueditor/dialogs/attachment/images/success.png deleted file mode 100644 index 94f968dc8..000000000 Binary files a/static/plugs/ueditor/dialogs/attachment/images/success.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/background/background.css b/static/plugs/ueditor/dialogs/background/background.css deleted file mode 100644 index 43200aa95..000000000 --- a/static/plugs/ueditor/dialogs/background/background.css +++ /dev/null @@ -1,94 +0,0 @@ -.wrapper{ width: 424px;margin: 10px auto; zoom:1;position: relative} -.tabbody{height:225px;} -.tabbody .panel { position: absolute;width:100%; height:100%;background: #fff; display: none;} -.tabbody .focus { display: block;} - -body{font-size: 12px;color: #888;overflow: hidden;} -input,label{vertical-align:middle} -.clear{clear: both;} -.pl{padding-left: 18px;padding-left: 23px\9;} - -#imageList {width: 420px;height: 215px;margin-top: 10px;overflow: hidden;overflow-y: auto;} -#imageList div {float: left;width: 100px;height: 95px;margin: 5px 10px;} -#imageList img {cursor: pointer;border: 2px solid white;} - -.bgarea{margin: 10px;padding: 5px;height: 84%;border: 1px solid #A8A297;} -.content div{margin: 10px 0 10px 5px;} -.content .iptradio{margin: 0px 5px 5px 0px;} -.txt{width:280px;} - -.wrapcolor{height: 19px;} -div.color{float: left;margin: 0;} -#colorPicker{width: 17px;height: 17px;border: 1px solid #CCC;display: inline-block;border-radius: 3px;box-shadow: 2px 2px 5px #D3D6DA;margin: 0;float: left;} -div.alignment,#custom{margin-left: 23px;margin-left: 28px\9;} -#custom input{height: 15px;min-height: 15px;width:20px;} -#repeatType{width:100px;} - - -/* 图片管理样式 */ -#imgManager { - width: 100%; - height: 225px; -} -#imgManager #imageList{ - width: 100%; - overflow-x: hidden; - overflow-y: auto; -} -#imgManager ul { - display: block; - list-style: none; - margin: 0; - padding: 0; -} -#imgManager li { - float: left; - display: block; - list-style: none; - padding: 0; - width: 113px; - height: 113px; - margin: 9px 0 0 19px; - background-color: #eee; - overflow: hidden; - cursor: pointer; - position: relative; -} -#imgManager li.clearFloat { - float: none; - clear: both; - display: block; - width:0; - height:0; - margin: 0; - padding: 0; -} -#imgManager li img { - cursor: pointer; -} -#imgManager li .icon { - cursor: pointer; - width: 113px; - height: 113px; - position: absolute; - top: 0; - left: 0; - z-index: 2; - border: 0; - background-repeat: no-repeat; -} -#imgManager li .icon:hover { - width: 107px; - height: 107px; - border: 3px solid #1094fa; -} -#imgManager li.selected .icon { - background-image: url(images/success.png); - background-position: 75px 75px; -} -#imgManager li.selected .icon:hover { - width: 107px; - height: 107px; - border: 3px solid #1094fa; - background-position: 72px 72px; -} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/background/background.html b/static/plugs/ueditor/dialogs/background/background.html deleted file mode 100644 index 8def8a734..000000000 --- a/static/plugs/ueditor/dialogs/background/background.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - -
                  -
                  - - -
                  -
                  -
                  -
                  - -
                  -
                  - - -
                  -
                  -
                  - : -
                  -
                  -
                  -
                  -
                  - -
                  -
                  - : -
                  -
                  - :x:px  y:px -
                  -
                  -
                  - -
                  -
                  -
                  -
                  -
                  -
                  - - - diff --git a/static/plugs/ueditor/dialogs/background/background.js b/static/plugs/ueditor/dialogs/background/background.js deleted file mode 100644 index 9d63bade9..000000000 --- a/static/plugs/ueditor/dialogs/background/background.js +++ /dev/null @@ -1,376 +0,0 @@ -(function () { - - var onlineImage, - backupStyle = editor.queryCommandValue('background'); - - window.onload = function () { - initTabs(); - initColorSelector(); - }; - - /* 初始化tab标签 */ - function initTabs(){ - var tabs = $G('tabHeads').children; - for (var i = 0; i < tabs.length; i++) { - domUtils.on(tabs[i], "click", function (e) { - var target = e.target || e.srcElement; - for (var j = 0; j < tabs.length; j++) { - if(tabs[j] == target){ - tabs[j].className = "focus"; - var contentId = tabs[j].getAttribute('data-content-id'); - $G(contentId).style.display = "block"; - if(contentId == 'imgManager') { - initImagePanel(); - } - }else { - tabs[j].className = ""; - $G(tabs[j].getAttribute('data-content-id')).style.display = "none"; - } - } - }); - } - } - - /* 初始化颜色设置 */ - function initColorSelector () { - var obj = editor.queryCommandValue('background'); - if (obj) { - var color = obj['background-color'], - repeat = obj['background-repeat'] || 'repeat', - image = obj['background-image'] || '', - position = obj['background-position'] || 'center center', - pos = position.split(' '), - x = parseInt(pos[0]) || 0, - y = parseInt(pos[1]) || 0; - - if(repeat == 'no-repeat' && (x || y)) repeat = 'self'; - - image = image.match(/url[\s]*\(([^\)]*)\)/); - image = image ? image[1]:''; - updateFormState('colored', color, image, repeat, x, y); - } else { - updateFormState(); - } - - var updateHandler = function () { - updateFormState(); - updateBackground(); - } - domUtils.on($G('nocolorRadio'), 'click', updateBackground); - domUtils.on($G('coloredRadio'), 'click', updateHandler); - domUtils.on($G('url'), 'keyup', function(){ - if($G('url').value && $G('alignment').style.display == "none") { - utils.each($G('repeatType').children, function(item){ - item.selected = ('repeat' == item.getAttribute('value') ? 'selected':false); - }); - } - updateHandler(); - }); - domUtils.on($G('repeatType'), 'change', updateHandler); - domUtils.on($G('x'), 'keyup', updateBackground); - domUtils.on($G('y'), 'keyup', updateBackground); - - initColorPicker(); - } - - /* 初始化颜色选择器 */ - function initColorPicker() { - var me = editor, - cp = $G("colorPicker"); - - /* 生成颜色选择器ui对象 */ - var popup = new UE.ui.Popup({ - content: new UE.ui.ColorPicker({ - noColorText: me.getLang("clearColor"), - editor: me, - onpickcolor: function (t, color) { - updateFormState('colored', color); - updateBackground(); - UE.ui.Popup.postHide(); - }, - onpicknocolor: function (t, color) { - updateFormState('colored', 'transparent'); - updateBackground(); - UE.ui.Popup.postHide(); - } - }), - editor: me, - onhide: function () { - } - }); - - /* 设置颜色选择器 */ - domUtils.on(cp, "click", function () { - popup.showAnchor(this); - }); - domUtils.on(document, 'mousedown', function (evt) { - var el = evt.target || evt.srcElement; - UE.ui.Popup.postHide(el); - }); - domUtils.on(window, 'scroll', function () { - UE.ui.Popup.postHide(); - }); - } - - /* 初始化在线图片列表 */ - function initImagePanel() { - onlineImage = onlineImage || new OnlineImage('imageList'); - } - - /* 更新背景色设置面板 */ - function updateFormState (radio, color, url, align, x, y) { - var nocolorRadio = $G('nocolorRadio'), - coloredRadio = $G('coloredRadio'); - - if(radio) { - nocolorRadio.checked = (radio == 'colored' ? false:'checked'); - coloredRadio.checked = (radio == 'colored' ? 'checked':false); - } - if(color) { - domUtils.setStyle($G("colorPicker"), "background-color", color); - } - - if(url && /^\//.test(url)) { - var a = document.createElement('a'); - a.href = url; - browser.ie && (a.href = a.href); - url = browser.ie ? a.href:(a.protocol + '//' + a.host + a.pathname + a.search + a.hash); - } - - if(url || url === '') { - $G('url').value = url; - } - if(align) { - utils.each($G('repeatType').children, function(item){ - item.selected = (align == item.getAttribute('value') ? 'selected':false); - }); - } - if(x || y) { - $G('x').value = parseInt(x) || 0; - $G('y').value = parseInt(y) || 0; - } - - $G('alignment').style.display = coloredRadio.checked && $G('url').value ? '':'none'; - $G('custom').style.display = coloredRadio.checked && $G('url').value && $G('repeatType').value == 'self' ? '':'none'; - } - - /* 更新背景颜色 */ - function updateBackground () { - if ($G('coloredRadio').checked) { - var color = domUtils.getStyle($G("colorPicker"), "background-color"), - bgimg = $G("url").value, - align = $G("repeatType").value, - backgroundObj = { - "background-repeat": "no-repeat", - "background-position": "center center" - }; - - if (color) backgroundObj["background-color"] = color; - if (bgimg) backgroundObj["background-image"] = 'url(' + bgimg + ')'; - if (align == 'self') { - backgroundObj["background-position"] = $G("x").value + "px " + $G("y").value + "px"; - } else if (align == 'repeat-x' || align == 'repeat-y' || align == 'repeat') { - backgroundObj["background-repeat"] = align; - } - - editor.execCommand('background', backgroundObj); - } else { - editor.execCommand('background', null); - } - } - - - /* 在线图片 */ - function OnlineImage(target) { - this.container = utils.isString(target) ? document.getElementById(target) : target; - this.init(); - } - OnlineImage.prototype = { - init: function () { - this.reset(); - this.initEvents(); - }, - /* 初始化容器 */ - initContainer: function () { - this.container.innerHTML = ''; - this.list = document.createElement('ul'); - this.clearFloat = document.createElement('li'); - - domUtils.addClass(this.list, 'list'); - domUtils.addClass(this.clearFloat, 'clearFloat'); - - this.list.id = 'imageListUl'; - this.list.appendChild(this.clearFloat); - this.container.appendChild(this.list); - }, - /* 初始化滚动事件,滚动到地步自动拉取数据 */ - initEvents: function () { - var _this = this; - - /* 滚动拉取图片 */ - domUtils.on($G('imageList'), 'scroll', function(e){ - var panel = this; - if (panel.scrollHeight - (panel.offsetHeight + panel.scrollTop) < 10) { - _this.getImageData(); - } - }); - /* 选中图片 */ - domUtils.on(this.container, 'click', function (e) { - var target = e.target || e.srcElement, - li = target.parentNode, - nodes = $G('imageListUl').childNodes; - - if (li.tagName.toLowerCase() == 'li') { - updateFormState('nocolor', null, ''); - for (var i = 0, node; node = nodes[i++];) { - if (node == li && !domUtils.hasClass(node, 'selected')) { - domUtils.addClass(node, 'selected'); - updateFormState('colored', null, li.firstChild.getAttribute("_src"), 'repeat'); - } else { - domUtils.removeClasses(node, 'selected'); - } - } - updateBackground(); - } - }); - }, - /* 初始化第一次的数据 */ - initData: function () { - - /* 拉取数据需要使用的值 */ - this.state = 0; - this.listSize = editor.getOpt('imageManagerListSize'); - this.listIndex = 0; - this.listEnd = false; - - /* 第一次拉取数据 */ - this.getImageData(); - }, - /* 重置界面 */ - reset: function() { - this.initContainer(); - this.initData(); - }, - /* 向后台拉取图片列表数据 */ - getImageData: function () { - var _this = this; - - if(!_this.listEnd && !this.isLoadingData) { - this.isLoadingData = true; - var url = editor.getActionUrl(editor.getOpt('imageManagerActionName')), - isJsonp = utils.isCrossDomainUrl(url); - ajax.request(url, { - 'timeout': 100000, - 'dataType': isJsonp ? 'jsonp':'', - 'data': utils.extend({ - start: this.listIndex, - size: this.listSize - }, editor.queryCommandValue('serverparam')), - 'method': 'get', - 'onsuccess': function (r) { - try { - var json = isJsonp ? r:eval('(' + r.responseText + ')'); - if (json.state == 'SUCCESS') { - _this.pushData(json.list); - _this.listIndex = parseInt(json.start) + parseInt(json.list.length); - if(_this.listIndex >= json.total) { - _this.listEnd = true; - } - _this.isLoadingData = false; - } - } catch (e) { - if(r.responseText.indexOf('ue_separate_ue') != -1) { - var list = r.responseText.split(r.responseText); - _this.pushData(list); - _this.listIndex = parseInt(list.length); - _this.listEnd = true; - _this.isLoadingData = false; - } - } - }, - 'onerror': function () { - _this.isLoadingData = false; - } - }); - } - }, - /* 添加图片到列表界面上 */ - pushData: function (list) { - var i, item, img, icon, _this = this, - urlPrefix = editor.getOpt('imageManagerUrlPrefix'); - for (i = 0; i < list.length; i++) { - if(list[i] && list[i].url) { - item = document.createElement('li'); - img = document.createElement('img'); - icon = document.createElement('span'); - - domUtils.on(img, 'load', (function(image){ - return function(){ - _this.scale(image, image.parentNode.offsetWidth, image.parentNode.offsetHeight); - } - })(img)); - img.width = 113; - img.setAttribute('src', urlPrefix + list[i].url + (list[i].url.indexOf('?') == -1 ? '?noCache=':'&noCache=') + (+new Date()).toString(36) ); - img.setAttribute('_src', urlPrefix + list[i].url); - domUtils.addClass(icon, 'icon'); - - item.appendChild(img); - item.appendChild(icon); - this.list.insertBefore(item, this.clearFloat); - } - } - }, - /* 改变图片大小 */ - scale: function (img, w, h, type) { - var ow = img.width, - oh = img.height; - - if (type == 'justify') { - if (ow >= oh) { - img.width = w; - img.height = h * oh / ow; - img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px'; - } else { - img.width = w * ow / oh; - img.height = h; - img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px'; - } - } else { - if (ow >= oh) { - img.width = w * ow / oh; - img.height = h; - img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px'; - } else { - img.width = w; - img.height = h * oh / ow; - img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px'; - } - } - }, - getInsertList: function () { - var i, lis = this.list.children, list = [], align = getAlign(); - for (i = 0; i < lis.length; i++) { - if (domUtils.hasClass(lis[i], 'selected')) { - var img = lis[i].firstChild, - src = img.getAttribute('_src'); - list.push({ - src: src, - _src: src, - floatStyle: align - }); - } - - } - return list; - } - }; - - dialog.onok = function () { - updateBackground(); - editor.fireEvent('saveScene'); - }; - dialog.oncancel = function () { - editor.execCommand('background', backupStyle); - }; - -})(); \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/background/images/bg.png b/static/plugs/ueditor/dialogs/background/images/bg.png deleted file mode 100644 index 580be0a01..000000000 Binary files a/static/plugs/ueditor/dialogs/background/images/bg.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/background/images/success.png b/static/plugs/ueditor/dialogs/background/images/success.png deleted file mode 100644 index 94f968dc8..000000000 Binary files a/static/plugs/ueditor/dialogs/background/images/success.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/charts/chart.config.js b/static/plugs/ueditor/dialogs/charts/chart.config.js deleted file mode 100644 index 678b00deb..000000000 --- a/static/plugs/ueditor/dialogs/charts/chart.config.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 图表配置文件 - * */ - - -//不同类型的配置 -var typeConfig = [ - { - chart: { - type: 'line' - }, - plotOptions: { - line: { - dataLabels: { - enabled: false - }, - enableMouseTracking: true - } - } - }, { - chart: { - type: 'line' - }, - plotOptions: { - line: { - dataLabels: { - enabled: true - }, - enableMouseTracking: false - } - } - }, { - chart: { - type: 'area' - } - }, { - chart: { - type: 'bar' - } - }, { - chart: { - type: 'column' - } - }, { - chart: { - plotBackgroundColor: null, - plotBorderWidth: null, - plotShadow: false - }, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: true, - color: '#000000', - connectorColor: '#000000', - formatter: function() { - return ''+ this.point.name +': '+ ( Math.round( this.point.percentage*100 ) / 100 ) +' %'; - } - } - } - } - } -]; diff --git a/static/plugs/ueditor/dialogs/charts/charts.css b/static/plugs/ueditor/dialogs/charts/charts.css deleted file mode 100644 index ac3c76458..000000000 --- a/static/plugs/ueditor/dialogs/charts/charts.css +++ /dev/null @@ -1,165 +0,0 @@ -html, body { - width: 100%; - height: 100%; - margin: 0; - padding: 0; - overflow-x: hidden; -} - -.main { - width: 100%; - overflow: hidden; -} - -.table-view { - height: 100%; - float: left; - margin: 20px; - width: 40%; -} - -.table-view .table-container { - width: 100%; - margin-bottom: 50px; - overflow: scroll; -} - -.table-view th { - padding: 5px 10px; - background-color: #F7F7F7; -} - -.table-view td { - width: 50px; - text-align: center; - padding:0; -} - -.table-container input { - width: 40px; - padding: 5px; - border: none; - outline: none; -} - -.table-view caption { - font-size: 18px; - text-align: left; -} - -.charts-view { - /*margin-left: 49%!important;*/ - width: 50%; - margin-left: 49%; - height: 400px; -} - -.charts-container { - border-left: 1px solid #c3c3c3; -} - -.charts-format fieldset { - padding-left: 20px; - margin-bottom: 50px; -} - -.charts-format legend { - padding-left: 10px; - padding-right: 10px; -} - -.format-item-container { - padding: 20px; -} - -.format-item-container label { - display: block; - margin: 10px 0; -} - -.charts-format .data-item { - border: 1px solid black; - outline: none; - padding: 2px 3px; -} - -/* 图表类型 */ - -.charts-type { - margin-top: 50px; - height: 300px; -} - -.scroll-view { - border: 1px solid #c3c3c3; - border-left: none; - border-right: none; - overflow: hidden; -} - -.scroll-container { - margin: 20px; - width: 100%; - overflow: hidden; -} - -.scroll-bed { - width: 10000px; - _margin-top: 20px; - -webkit-transition: margin-left .5s ease; - -moz-transition: margin-left .5s ease; - transition: margin-left .5s ease; -} - -.view-box { - display: inline-block; - *display: inline; - *zoom: 1; - margin-right: 20px; - border: 2px solid white; - line-height: 0; - overflow: hidden; - cursor: pointer; -} - -.view-box img { - border: 1px solid #cecece; -} - -.view-box.selected { - border-color: #7274A7; -} - -.button-container { - margin-bottom: 20px; - text-align: center; -} - -.button-container a { - display: inline-block; - width: 100px; - height: 25px; - line-height: 25px; - border: 1px solid #c2ccd1; - margin-right: 30px; - text-decoration: none; - color: black; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - border-radius: 2px; -} - -.button-container a:HOVER { - background: #fcfcfc; -} - -.button-container a:ACTIVE { - border-top-color: #c2ccd1; - box-shadow:inset 0 5px 4px -4px rgba(49, 49, 64, 0.1); -} - -.edui-charts-not-data { - height: 100px; - line-height: 100px; - text-align: center; -} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/charts/charts.html b/static/plugs/ueditor/dialogs/charts/charts.html deleted file mode 100644 index 735274fdd..000000000 --- a/static/plugs/ueditor/dialogs/charts/charts.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - chart - - - - - -
                  -
                  -

                  -
                  -

                  -
                  -
                  -
                  - -
                  - - -
                  -
                  -
                  -
                  - -
                  - - - - -
                  -
                  -
                  - -
                  - -

                  -
                  -
                  -
                  - -
                  - -

                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -

                  -
                  -
                  -
                  -
                  -
                  - - -
                  -
                  -
                  -
                  -
                  - - - - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/charts/charts.js b/static/plugs/ueditor/dialogs/charts/charts.js deleted file mode 100644 index 37344fd12..000000000 --- a/static/plugs/ueditor/dialogs/charts/charts.js +++ /dev/null @@ -1,519 +0,0 @@ -/* - * 图片转换对话框脚本 - **/ - -var tableData = [], - //编辑器页面table - editorTable = null, - chartsConfig = window.typeConfig, - resizeTimer = null, - //初始默认图表类型 - currentChartType = 0; - -window.onload = function () { - - editorTable = domUtils.findParentByTagName( editor.selection.getRange().startContainer, 'table', true); - - //未找到表格, 显示错误页面 - if ( !editorTable ) { - document.body.innerHTML = "
                  未找到数据
                  "; - return; - } - - //初始化图表类型选择 - initChartsTypeView(); - renderTable( editorTable ); - initEvent(); - initUserConfig( editorTable.getAttribute( "data-chart" ) ); - $( "#scrollBed .view-box:eq("+ currentChartType +")" ).trigger( "click" ); - updateViewType( currentChartType ); - - dialog.addListener( "resize", function () { - - if ( resizeTimer != null ) { - window.clearTimeout( resizeTimer ); - } - - resizeTimer = window.setTimeout( function () { - - resizeTimer = null; - - renderCharts(); - - }, 500 ); - - } ); - -}; - -function initChartsTypeView () { - - var contents = []; - - for ( var i = 0, len = chartsConfig.length; i
                  ' ); - - } - - $( "#scrollBed" ).html( contents.join( "" ) ); - -} - -//渲染table, 以便用户修改数据 -function renderTable ( table ) { - - var tableHtml = []; - - //构造数据 - for ( var i = 0, row; row = table.rows[ i ]; i++ ) { - - tableData[ i ] = []; - tableHtml[ i ] = []; - - for ( var j = 0, cell; cell = row.cells[ j ]; j++ ) { - - var value = getCellValue( cell ); - - if ( i > 0 && j > 0 ) { - value = +value; - } - - if ( i === 0 || j === 0 ) { - tableHtml[ i ].push( ''+ value +'' ); - } else { - tableHtml[ i ].push( '' ); - } - - tableData[ i ][ j ] = value; - - } - - tableHtml[ i ] = tableHtml[ i ].join( "" ); - - } - - //draw 表格 - $( "#tableContainer" ).html( ''+ tableHtml.join( "" ) +'
                  ' ); - -} - -/* - * 根据表格已有的图表属性初始化当前图表属性 - */ -function initUserConfig ( config ) { - - var parsedConfig = {}; - - if ( !config ) { - return; - } - - config = config.split( ";" ); - - $.each( config, function ( index, item ) { - - item = item.split( ":" ); - parsedConfig[ item[ 0 ] ] = item[ 1 ]; - - } ); - - setUserConfig( parsedConfig ); - -} - -function initEvent () { - - var cacheValue = null, - //图表类型数 - typeViewCount = chartsConfig.length- 1, - $chartsTypeViewBox = $( '#scrollBed .view-box' ); - - $( ".charts-format" ).delegate( ".format-ctrl", "change", function () { - - renderCharts(); - - } ) - - $( ".table-view" ).delegate( ".data-item", "focus", function () { - - cacheValue = this.value; - - } ).delegate( ".data-item", "blur", function () { - - if ( this.value !== cacheValue ) { - renderCharts(); - } - - cacheValue = null; - - } ); - - $( "#buttonContainer" ).delegate( "a", "click", function (e) { - - e.preventDefault(); - - if ( this.getAttribute( "data-title" ) === 'prev' ) { - - if ( currentChartType > 0 ) { - currentChartType--; - updateViewType( currentChartType ); - } - - } else { - - if ( currentChartType < typeViewCount ) { - currentChartType++; - updateViewType( currentChartType ); - } - - } - - } ); - - //图表类型变化 - $( '#scrollBed' ).delegate( ".view-box", "click", function (e) { - - var index = $( this ).attr( "data-chart-type" ); - $chartsTypeViewBox.removeClass( "selected" ); - $( $chartsTypeViewBox[ index ] ).addClass( "selected" ); - - currentChartType = index | 0; - - //饼图, 禁用部分配置 - if ( currentChartType === chartsConfig.length - 1 ) { - - disableNotPieConfig(); - - //启用完整配置 - } else { - - enableNotPieConfig(); - - } - - renderCharts(); - - } ); - -} - -function renderCharts () { - - var data = collectData(); - - $('#chartsContainer').highcharts( $.extend( {}, chartsConfig[ currentChartType ], { - - credits: { - enabled: false - }, - exporting: { - enabled: false - }, - title: { - text: data.title, - x: -20 //center - }, - subtitle: { - text: data.subTitle, - x: -20 - }, - xAxis: { - title: { - text: data.xTitle - }, - categories: data.categories - }, - yAxis: { - title: { - text: data.yTitle - }, - plotLines: [{ - value: 0, - width: 1, - color: '#808080' - }] - }, - tooltip: { - enabled: true, - valueSuffix: data.suffix - }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 1 - }, - series: data.series - - } )); - -} - -function updateViewType ( index ) { - - $( "#scrollBed" ).css( 'marginLeft', -index*324+'px' ); - -} - -function collectData () { - - var form = document.forms[ 'data-form' ], - data = null; - - if ( currentChartType !== chartsConfig.length - 1 ) { - - data = getSeriesAndCategories(); - $.extend( data, getUserConfig() ); - - //饼图数据格式 - } else { - data = getSeriesForPieChart(); - data.title = form[ 'title' ].value; - data.suffix = form[ 'unit' ].value; - } - - return data; - -} - -/** - * 获取用户配置信息 - */ -function getUserConfig () { - - var form = document.forms[ 'data-form' ], - info = { - title: form[ 'title' ].value, - subTitle: form[ 'sub-title' ].value, - xTitle: form[ 'x-title' ].value, - yTitle: form[ 'y-title' ].value, - suffix: form[ 'unit' ].value, - //数据对齐方式 - tableDataFormat: getTableDataFormat (), - //饼图提示文字 - tip: $( "#tipInput" ).val() - }; - - return info; - -} - -function setUserConfig ( config ) { - - var form = document.forms[ 'data-form' ]; - - config.title && ( form[ 'title' ].value = config.title ); - config.subTitle && ( form[ 'sub-title' ].value = config.subTitle ); - config.xTitle && ( form[ 'x-title' ].value = config.xTitle ); - config.yTitle && ( form[ 'y-title' ].value = config.yTitle ); - config.suffix && ( form[ 'unit' ].value = config.suffix ); - config.dataFormat == "-1" && ( form[ 'charts-format' ][ 1 ].checked = true ); - config.tip && ( form[ 'tip' ].value = config.tip ); - currentChartType = config.chartType || 0; - -} - -function getSeriesAndCategories () { - - var form = document.forms[ 'data-form' ], - series = [], - categories = [], - tmp = [], - tableData = getTableData(); - - //反转数据 - if ( getTableDataFormat() === "-1" ) { - - for ( var i = 0, len = tableData.length; i < len; i++ ) { - - for ( var j = 0, jlen = tableData[ i ].length; j < jlen; j++ ) { - - if ( !tmp[ j ] ) { - tmp[ j ] = []; - } - - tmp[ j ][ i ] = tableData[ i ][ j ]; - - } - - } - - tableData = tmp; - - } - - categories = tableData[0].slice( 1 ); - - for ( var i = 1, data; data = tableData[ i ]; i++ ) { - - series.push( { - name: data[ 0 ], - data: data.slice( 1 ) - } ); - - } - - return { - series: series, - categories: categories - }; - -} - -/* - * 获取数据源数据对齐方式 - */ -function getTableDataFormat () { - - var form = document.forms[ 'data-form' ], - items = form['charts-format']; - - return items[ 0 ].checked ? items[ 0 ].value : items[ 1 ].value; - -} - -/* - * 禁用非饼图类型的配置项 - */ -function disableNotPieConfig() { - - updateConfigItem( 'disable' ); - -} - -/* - * 启用非饼图类型的配置项 - */ -function enableNotPieConfig() { - - updateConfigItem( 'enable' ); - -} - -function updateConfigItem ( value ) { - - var table = $( "#showTable" )[ 0 ], - isDisable = value === 'disable' ? true : false; - - //table中的input处理 - for ( var i = 2 , row; row = table.rows[ i ]; i++ ) { - - for ( var j = 1, cell; cell = row.cells[ j ]; j++ ) { - - $( "input", cell ).attr( "disabled", isDisable ); - - } - - } - - //其他项处理 - $( "input.not-pie-item" ).attr( "disabled", isDisable ); - $( "#tipInput" ).attr( "disabled", !isDisable ) - -} - -/* - * 获取饼图数据 - * 饼图的数据只取第一行的 - **/ -function getSeriesForPieChart () { - - var series = { - type: 'pie', - name: $("#tipInput").val(), - data: [] - }, - tableData = getTableData(); - - - for ( var j = 1, jlen = tableData[ 0 ].length; j < jlen; j++ ) { - - var title = tableData[ 0 ][ j ], - val = tableData[ 1 ][ j ]; - - series.data.push( [ title, val ] ); - - } - - return { - series: [ series ] - }; - -} - -function getTableData () { - - var table = document.getElementById( "showTable" ), - xCount = table.rows[0].cells.length - 1, - values = getTableInputValue(); - - for ( var i = 0, value; value = values[ i ]; i++ ) { - - tableData[ Math.floor( i / xCount ) + 1 ][ i % xCount + 1 ] = values[ i ]; - - } - - return tableData; - -} - -function getTableInputValue () { - - var table = document.getElementById( "showTable" ), - inputs = table.getElementsByTagName( "input" ), - values = []; - - for ( var i = 0, input; input = inputs[ i ]; i++ ) { - values.push( input.value | 0 ); - } - - return values; - -} - -function getCellValue ( cell ) { - - var value = utils.trim( ( cell.innerText || cell.textContent || '' ) ); - - return value.replace( new RegExp( UE.dom.domUtils.fillChar, 'g' ), '' ).replace( /^\s+|\s+$/g, '' ); - -} - - -//dialog确认事件 -dialog.onok = function () { - - //收集信息 - var form = document.forms[ 'data-form' ], - info = getUserConfig(); - - //添加图表类型 - info.chartType = currentChartType; - - //同步表格数据到编辑器 - syncTableData(); - - //执行图表命令 - editor.execCommand( 'charts', info ); - -}; - -/* - * 同步图表编辑视图的表格数据到编辑器里的原始表格 - */ -function syncTableData () { - - var tableData = getTableData(); - - for ( var i = 1, row; row = editorTable.rows[ i ]; i++ ) { - - for ( var j = 1, cell; cell = row.cells[ j ]; j++ ) { - - cell.innerHTML = tableData[ i ] [ j ]; - - } - - } - -} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/charts/images/charts0.png b/static/plugs/ueditor/dialogs/charts/images/charts0.png deleted file mode 100644 index 9485e5ed8..000000000 Binary files a/static/plugs/ueditor/dialogs/charts/images/charts0.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/charts/images/charts1.png b/static/plugs/ueditor/dialogs/charts/images/charts1.png deleted file mode 100644 index b5a003928..000000000 Binary files a/static/plugs/ueditor/dialogs/charts/images/charts1.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/charts/images/charts2.png b/static/plugs/ueditor/dialogs/charts/images/charts2.png deleted file mode 100644 index 7c91a39ff..000000000 Binary files a/static/plugs/ueditor/dialogs/charts/images/charts2.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/charts/images/charts3.png b/static/plugs/ueditor/dialogs/charts/images/charts3.png deleted file mode 100644 index a6bc29bfc..000000000 Binary files a/static/plugs/ueditor/dialogs/charts/images/charts3.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/charts/images/charts4.png b/static/plugs/ueditor/dialogs/charts/images/charts4.png deleted file mode 100644 index 742006adc..000000000 Binary files a/static/plugs/ueditor/dialogs/charts/images/charts4.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/charts/images/charts5.png b/static/plugs/ueditor/dialogs/charts/images/charts5.png deleted file mode 100644 index c49a29609..000000000 Binary files a/static/plugs/ueditor/dialogs/charts/images/charts5.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/emotion/emotion.css b/static/plugs/ueditor/dialogs/emotion/emotion.css deleted file mode 100644 index e0a56ddd2..000000000 --- a/static/plugs/ueditor/dialogs/emotion/emotion.css +++ /dev/null @@ -1,43 +0,0 @@ -.jd img{ - background:transparent url(images/jxface2.gif?v=1.1) no-repeat scroll left top; - cursor:pointer;width:35px;height:35px;display:block; -} -.pp img{ - background:transparent url(images/fface.gif?v=1.1) no-repeat scroll left top; - cursor:pointer;width:25px;height:25px;display:block; -} -.ldw img{ - background:transparent url(images/wface.gif?v=1.1) no-repeat scroll left top; - cursor:pointer;width:35px;height:35px;display:block; -} -.tsj img{ - background:transparent url(images/tface.gif?v=1.1) no-repeat scroll left top; - cursor:pointer;width:35px;height:35px;display:block; -} -.cat img{ - background:transparent url(images/cface.gif?v=1.1) no-repeat scroll left top; - cursor:pointer;width:35px;height:35px;display:block; -} -.bb img{ - background:transparent url(images/bface.gif?v=1.1) no-repeat scroll left top; - cursor:pointer;width:35px;height:35px;display:block; -} -.youa img{ - background:transparent url(images/yface.gif?v=1.1) no-repeat scroll left top; - cursor:pointer;width:35px;height:35px;display:block; -} - -.smileytable td {height: 37px;} -#tabPanel{margin-left:5px;overflow: hidden;} -#tabContent {float:left;background:#FFFFFF;} -#tabContent div{display: none;width:480px;overflow:hidden;} -#tabIconReview.show{left:17px;display:block;} -.menuFocus{background:#ACCD3C;} -.menuDefault{background:#FFFFFF;} -#tabIconReview{position:absolute;left:406px;left:398px \9;top:41px;z-index:65533;width:90px;height:76px;} -img.review{width:90px;height:76px;border:2px solid #9cb945;background:#FFFFFF;background-position:center;background-repeat:no-repeat;} - -.wrapper .tabbody{position:relative;float:left;clear:both;padding:10px;width: 95%;} -.tabbody table{width: 100%;} -.tabbody td{border:1px solid #BAC498;} -.tabbody td span{display: block;zoom:1;padding:0 4px;} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/emotion/emotion.html b/static/plugs/ueditor/dialogs/emotion/emotion.html deleted file mode 100644 index 363d64c68..000000000 --- a/static/plugs/ueditor/dialogs/emotion/emotion.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - -
                  -
                  - - - - - - - -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  - -
                  - - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/emotion/emotion.js b/static/plugs/ueditor/dialogs/emotion/emotion.js deleted file mode 100644 index aa0138082..000000000 --- a/static/plugs/ueditor/dialogs/emotion/emotion.js +++ /dev/null @@ -1,186 +0,0 @@ -window.onload = function () { - editor.setOpt({ - emotionLocalization:false - }); - - emotion.SmileyPath = editor.options.emotionLocalization === true ? 'images/' : "http://img.baidu.com/hi/"; - emotion.SmileyBox = createTabList( emotion.tabNum ); - emotion.tabExist = createArr( emotion.tabNum ); - - initImgName(); - initEvtHandler( "tabHeads" ); -}; - -function initImgName() { - for ( var pro in emotion.SmilmgName ) { - var tempName = emotion.SmilmgName[pro], - tempBox = emotion.SmileyBox[pro], - tempStr = ""; - - if ( tempBox.length ) return; - for ( var i = 1; i <= tempName[1]; i++ ) { - tempStr = tempName[0]; - if ( i < 10 ) tempStr = tempStr + '0'; - tempStr = tempStr + i + '.gif'; - tempBox.push( tempStr ); - } - } -} - -function initEvtHandler( conId ) { - var tabHeads = $G( conId ); - for ( var i = 0, j = 0; i < tabHeads.childNodes.length; i++ ) { - var tabObj = tabHeads.childNodes[i]; - if ( tabObj.nodeType == 1 ) { - domUtils.on( tabObj, "click", (function ( index ) { - return function () { - switchTab( index ); - }; - })( j ) ); - j++; - } - } - switchTab( 0 ); - $G( "tabIconReview" ).style.display = 'none'; -} - -function InsertSmiley( url, evt ) { - var obj = { - src:editor.options.emotionLocalization ? editor.options.UEDITOR_HOME_URL + "dialogs/emotion/" + url : url - }; - obj._src = obj.src; - editor.execCommand( 'insertimage', obj ); - if ( !evt.ctrlKey ) { - dialog.popup.hide(); - } -} - -function switchTab( index ) { - - autoHeight( index ); - if ( emotion.tabExist[index] == 0 ) { - emotion.tabExist[index] = 1; - createTab( 'tab' + index ); - } - //获取呈现元素句柄数组 - var tabHeads = $G( "tabHeads" ).getElementsByTagName( "span" ), - tabBodys = $G( "tabBodys" ).getElementsByTagName( "div" ), - i = 0, L = tabHeads.length; - //隐藏所有呈现元素 - for ( ; i < L; i++ ) { - tabHeads[i].className = ""; - tabBodys[i].style.display = "none"; - } - //显示对应呈现元素 - tabHeads[index].className = "focus"; - tabBodys[index].style.display = "block"; -} - -function autoHeight( index ) { - var iframe = dialog.getDom( "iframe" ), - parent = iframe.parentNode.parentNode; - switch ( index ) { - case 0: - iframe.style.height = "380px"; - parent.style.height = "392px"; - break; - case 1: - iframe.style.height = "220px"; - parent.style.height = "232px"; - break; - case 2: - iframe.style.height = "260px"; - parent.style.height = "272px"; - break; - case 3: - iframe.style.height = "300px"; - parent.style.height = "312px"; - break; - case 4: - iframe.style.height = "140px"; - parent.style.height = "152px"; - break; - case 5: - iframe.style.height = "260px"; - parent.style.height = "272px"; - break; - case 6: - iframe.style.height = "230px"; - parent.style.height = "242px"; - break; - default: - - } -} - - -function createTab( tabName ) { - var faceVersion = "?v=1.1", //版本号 - tab = $G( tabName ), //获取将要生成的Div句柄 - imagePath = emotion.SmileyPath + emotion.imageFolders[tabName], //获取显示表情和预览表情的路径 - positionLine = 11 / 2, //中间数 - iWidth = iHeight = 35, //图片长宽 - iColWidth = 3, //表格剩余空间的显示比例 - tableCss = emotion.imageCss[tabName], - cssOffset = emotion.imageCssOffset[tabName], - textHTML = [''], - i = 0, imgNum = emotion.SmileyBox[tabName].length, imgColNum = 11, faceImage, - sUrl, realUrl, posflag, offset, infor; - - for ( ; i < imgNum; ) { - textHTML.push( '' ); - for ( var j = 0; j < imgColNum; j++, i++ ) { - faceImage = emotion.SmileyBox[tabName][i]; - if ( faceImage ) { - sUrl = imagePath + faceImage + faceVersion; - realUrl = imagePath + faceImage; - posflag = j < positionLine ? 0 : 1; - offset = cssOffset * i * (-1) - 1; - infor = emotion.SmileyInfor[tabName][i]; - - textHTML.push( '' ); - } - textHTML.push( '' ); - } - textHTML.push( '
                  ' ); - textHTML.push( '' ); - textHTML.push( '' ); - textHTML.push( '' ); - } else { - textHTML.push( '' ); - } - textHTML.push( '
                  ' ); - textHTML = textHTML.join( "" ); - tab.innerHTML = textHTML; -} - -function over( td, srcPath, posFlag ) { - td.style.backgroundColor = "#ACCD3C"; - $G( 'faceReview' ).style.backgroundImage = "url(" + srcPath + ")"; - if ( posFlag == 1 ) $G( "tabIconReview" ).className = "show"; - $G( "tabIconReview" ).style.display = 'block'; -} - -function out( td ) { - td.style.backgroundColor = "transparent"; - var tabIconRevew = $G( "tabIconReview" ); - tabIconRevew.className = ""; - tabIconRevew.style.display = 'none'; -} - -function createTabList( tabNum ) { - var obj = {}; - for ( var i = 0; i < tabNum; i++ ) { - obj["tab" + i] = []; - } - return obj; -} - -function createArr( tabNum ) { - var arr = []; - for ( var i = 0; i < tabNum; i++ ) { - arr[i] = 0; - } - return arr; -} - diff --git a/static/plugs/ueditor/dialogs/emotion/images/0.gif b/static/plugs/ueditor/dialogs/emotion/images/0.gif deleted file mode 100644 index 6964168b9..000000000 Binary files a/static/plugs/ueditor/dialogs/emotion/images/0.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/emotion/images/bface.gif b/static/plugs/ueditor/dialogs/emotion/images/bface.gif deleted file mode 100644 index 14fe618ab..000000000 Binary files a/static/plugs/ueditor/dialogs/emotion/images/bface.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/emotion/images/cface.gif b/static/plugs/ueditor/dialogs/emotion/images/cface.gif deleted file mode 100644 index bff947f52..000000000 Binary files a/static/plugs/ueditor/dialogs/emotion/images/cface.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/emotion/images/fface.gif b/static/plugs/ueditor/dialogs/emotion/images/fface.gif deleted file mode 100644 index 0d8a6afeb..000000000 Binary files a/static/plugs/ueditor/dialogs/emotion/images/fface.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/emotion/images/jxface2.gif b/static/plugs/ueditor/dialogs/emotion/images/jxface2.gif deleted file mode 100644 index a959c90f7..000000000 Binary files a/static/plugs/ueditor/dialogs/emotion/images/jxface2.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/emotion/images/neweditor-tab-bg.png b/static/plugs/ueditor/dialogs/emotion/images/neweditor-tab-bg.png deleted file mode 100644 index 8f398b095..000000000 Binary files a/static/plugs/ueditor/dialogs/emotion/images/neweditor-tab-bg.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/emotion/images/tface.gif b/static/plugs/ueditor/dialogs/emotion/images/tface.gif deleted file mode 100644 index 1354f54b9..000000000 Binary files a/static/plugs/ueditor/dialogs/emotion/images/tface.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/emotion/images/wface.gif b/static/plugs/ueditor/dialogs/emotion/images/wface.gif deleted file mode 100644 index 5667160d8..000000000 Binary files a/static/plugs/ueditor/dialogs/emotion/images/wface.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/emotion/images/yface.gif b/static/plugs/ueditor/dialogs/emotion/images/yface.gif deleted file mode 100644 index 51608be0e..000000000 Binary files a/static/plugs/ueditor/dialogs/emotion/images/yface.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/gmap/gmap.html b/static/plugs/ueditor/dialogs/gmap/gmap.html deleted file mode 100644 index 4319a6fad..000000000 --- a/static/plugs/ueditor/dialogs/gmap/gmap.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - -
                  - - - - - - -
                  -
                  -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/help/help.css b/static/plugs/ueditor/dialogs/help/help.css deleted file mode 100644 index 39ade49e5..000000000 --- a/static/plugs/ueditor/dialogs/help/help.css +++ /dev/null @@ -1,7 +0,0 @@ -.wrapper{width: 370px;margin: 10px auto;zoom: 1;} -.tabbody{height: 360px;} -.tabbody .panel{width:100%;height: 360px;position: absolute;background: #fff;} -.tabbody .panel h1{font-size:26px;margin: 5px 0 0 5px;} -.tabbody .panel p{font-size:12px;margin: 5px 0 0 5px;} -.tabbody table{width:90%;line-height: 20px;margin: 5px 0 0 5px;;} -.tabbody table thead{font-weight: bold;line-height: 25px;} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/help/help.html b/static/plugs/ueditor/dialogs/help/help.html deleted file mode 100644 index 0d6f9efcb..000000000 --- a/static/plugs/ueditor/dialogs/help/help.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - 帮助 - - - - - -
                  -
                  - - -
                  -
                  -
                  -

                  UEditor

                  -

                  -

                  -
                  -
                  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                  ctrl+b
                  ctrl+c
                  ctrl+x
                  ctrl+v
                  ctrl+y
                  ctrl+z
                  ctrl+i
                  ctrl+u
                  ctrl+a
                  shift+enter
                  alt+z
                  -
                  -
                  -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/help/help.js b/static/plugs/ueditor/dialogs/help/help.js deleted file mode 100644 index 4ad9e761d..000000000 --- a/static/plugs/ueditor/dialogs/help/help.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Created with JetBrains PhpStorm. - * User: xuheng - * Date: 12-9-26 - * Time: 下午1:06 - * To change this template use File | Settings | File Templates. - */ -/** - * tab点击处理事件 - * @param tabHeads - * @param tabBodys - * @param obj - */ -function clickHandler( tabHeads,tabBodys,obj ) { - //head样式更改 - for ( var k = 0, len = tabHeads.length; k < len; k++ ) { - tabHeads[k].className = ""; - } - obj.className = "focus"; - //body显隐 - var tabSrc = obj.getAttribute( "tabSrc" ); - for ( var j = 0, length = tabBodys.length; j < length; j++ ) { - var body = tabBodys[j], - id = body.getAttribute( "id" ); - body.onclick = function(){ - this.style.zoom = 1; - }; - if ( id != tabSrc ) { - body.style.zIndex = 1; - } else { - body.style.zIndex = 200; - } - } - -} - -/** - * TAB切换 - * @param tabParentId tab的父节点ID或者对象本身 - */ -function switchTab( tabParentId ) { - var tabElements = $G( tabParentId ).children, - tabHeads = tabElements[0].children, - tabBodys = tabElements[1].children; - - for ( var i = 0, length = tabHeads.length; i < length; i++ ) { - var head = tabHeads[i]; - if ( head.className === "focus" )clickHandler(tabHeads,tabBodys, head ); - head.onclick = function () { - clickHandler(tabHeads,tabBodys,this); - } - } -} -switchTab("helptab"); - -document.getElementById('version').innerHTML = parent.UE.version; \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/image/image.css b/static/plugs/ueditor/dialogs/image/image.css deleted file mode 100644 index 33310ac8b..000000000 --- a/static/plugs/ueditor/dialogs/image/image.css +++ /dev/null @@ -1,894 +0,0 @@ -@charset "utf-8"; -/* dialog样式 */ -.wrapper { - zoom: 1; - width: 630px; - *width: 626px; - height: 380px; - margin: 0 auto; - padding: 10px; - position: relative; - font-family: sans-serif; -} - -/*tab样式框大小*/ -.tabhead { - float:left; -} -.tabbody { - width: 100%; - height: 346px; - position: relative; - clear: both; -} - -.tabbody .panel { - position: absolute; - width: 0; - height: 0; - background: #fff; - overflow: hidden; - display: none; -} - -.tabbody .panel.focus { - width: 100%; - height: 346px; - display: block; -} - -/* 图片对齐方式 */ -.alignBar{ - float:right; - margin-top: 5px; - position: relative; -} - -.alignBar .algnLabel{ - float:left; - height: 20px; - line-height: 20px; -} - -.alignBar #alignIcon{ - zoom:1; - _display: inline; - display: inline-block; - position: relative; -} -.alignBar #alignIcon span{ - float: left; - cursor: pointer; - display: block; - width: 19px; - height: 17px; - margin-right: 3px; - margin-left: 3px; - background-image: url(./images/alignicon.jpg); -} -.alignBar #alignIcon .none-align{ - background-position: 0 -18px; -} -.alignBar #alignIcon .left-align{ - background-position: -20px -18px; -} -.alignBar #alignIcon .right-align{ - background-position: -40px -18px; -} -.alignBar #alignIcon .center-align{ - background-position: -60px -18px; -} -.alignBar #alignIcon .none-align.focus{ - background-position: 0 0; -} -.alignBar #alignIcon .left-align.focus{ - background-position: -20px 0; -} -.alignBar #alignIcon .right-align.focus{ - background-position: -40px 0; -} -.alignBar #alignIcon .center-align.focus{ - background-position: -60px 0; -} - - - - -/* 远程图片样式 */ -#remote { - z-index: 200; -} - -#remote .top{ - width: 100%; - margin-top: 25px; -} -#remote .left{ - display: block; - float: left; - width: 300px; - height:10px; -} -#remote .right{ - display: block; - float: right; - width: 300px; - height:10px; -} -#remote .row{ - margin-left: 20px; - clear: both; - height: 40px; -} - -#remote .row label{ - text-align: center; - width: 50px; - zoom:1; - _display: inline; - display:inline-block; - vertical-align: middle; -} -#remote .row label.algnLabel{ - float: left; - -} - -#remote input.text{ - width: 150px; - padding: 3px 6px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -#remote input.text:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); -} -#remote #url{ - width: 500px; - margin-bottom: 2px; -} -#remote #width, -#remote #height{ - width: 20px; - margin-left: 2px; - margin-right: 2px; -} -#remote #border, -#remote #vhSpace, -#remote #title{ - width: 180px; - margin-right: 5px; -} -#remote #lock{ -} -#remote #lockicon{ - zoom: 1; - _display:inline; - display: inline-block; - width: 20px; - height: 20px; - background: url("../../themes/default/images/lock.gif") -13px -13px no-repeat; - vertical-align: middle; -} -#remote #preview{ - clear: both; - width: 260px; - height: 240px; - z-index: 9999; - margin-top: 10px; - background-color: #eee; - overflow: hidden; -} - -/* 上传图片 */ -.tabbody #upload.panel { - width: 0; - height: 0; - overflow: hidden; - position: absolute !important; - clip: rect(1px, 1px, 1px, 1px); - background: #fff; - display: block; -} - -.tabbody #upload.panel.focus { - width: 100%; - height: 346px; - display: block; - clip: auto; -} - -#upload .queueList { - margin: 0; - width: 100%; - height: 100%; - position: absolute; - overflow: hidden; -} - -#upload p { - margin: 0; -} - -.element-invisible { - width: 0 !important; - height: 0 !important; - border: 0; - padding: 0; - margin: 0; - overflow: hidden; - position: absolute !important; - clip: rect(1px, 1px, 1px, 1px); -} - -#upload .placeholder { - margin: 10px; - border: 2px dashed #e6e6e6; - *border: 0px dashed #e6e6e6; - height: 172px; - padding-top: 150px; - text-align: center; - background: url(./images/image.png) center 70px no-repeat; - color: #cccccc; - font-size: 18px; - position: relative; - top:0; - *top: 10px; -} - -#upload .placeholder .webuploader-pick { - font-size: 18px; - background: #00b7ee; - border-radius: 3px; - line-height: 44px; - padding: 0 30px; - *width: 120px; - color: #fff; - display: inline-block; - margin: 0 auto 20px auto; - cursor: pointer; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); -} - -#upload .placeholder .webuploader-pick-hover { - background: #00a2d4; -} - - -#filePickerContainer { - text-align: center; -} - -#upload .placeholder .flashTip { - color: #666666; - font-size: 12px; - position: absolute; - width: 100%; - text-align: center; - bottom: 20px; -} - -#upload .placeholder .flashTip a { - color: #0785d1; - text-decoration: none; -} - -#upload .placeholder .flashTip a:hover { - text-decoration: underline; -} - -#upload .placeholder.webuploader-dnd-over { - border-color: #999999; -} - -#upload .filelist { - list-style: none; - margin: 0; - padding: 0; - overflow-x: hidden; - overflow-y: auto; - position: relative; - height: 300px; -} - -#upload .filelist:after { - content: ''; - display: block; - width: 0; - height: 0; - overflow: hidden; - clear: both; - position: relative; -} - -#upload .filelist li { - width: 113px; - height: 113px; - background: url(./images/bg.png); - text-align: center; - margin: 9px 0 0 9px; - *margin: 6px 0 0 6px; - position: relative; - display: block; - float: left; - overflow: hidden; - font-size: 12px; -} - -#upload .filelist li p.log { - position: relative; - top: -45px; -} - -#upload .filelist li p.title { - position: absolute; - top: 0; - left: 0; - width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - top: 5px; - text-indent: 5px; - text-align: left; -} - -#upload .filelist li p.progress { - position: absolute; - width: 100%; - bottom: 0; - left: 0; - height: 8px; - overflow: hidden; - z-index: 50; - margin: 0; - border-radius: 0; - background: none; - -webkit-box-shadow: 0 0 0; -} - -#upload .filelist li p.progress span { - display: none; - overflow: hidden; - width: 0; - height: 100%; - background: #1483d8 url(./images/progress.png) repeat-x; - - -webit-transition: width 200ms linear; - -moz-transition: width 200ms linear; - -o-transition: width 200ms linear; - -ms-transition: width 200ms linear; - transition: width 200ms linear; - - -webkit-animation: progressmove 2s linear infinite; - -moz-animation: progressmove 2s linear infinite; - -o-animation: progressmove 2s linear infinite; - -ms-animation: progressmove 2s linear infinite; - animation: progressmove 2s linear infinite; - - -webkit-transform: translateZ(0); -} - -@-webkit-keyframes progressmove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 17px 0; - } -} - -@-moz-keyframes progressmove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 17px 0; - } -} - -@keyframes progressmove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 17px 0; - } -} - -#upload .filelist li p.imgWrap { - position: relative; - z-index: 2; - line-height: 113px; - vertical-align: middle; - overflow: hidden; - width: 113px; - height: 113px; - - -webkit-transform-origin: 50% 50%; - -moz-transform-origin: 50% 50%; - -o-transform-origin: 50% 50%; - -ms-transform-origin: 50% 50%; - transform-origin: 50% 50%; - - -webit-transition: 200ms ease-out; - -moz-transition: 200ms ease-out; - -o-transition: 200ms ease-out; - -ms-transition: 200ms ease-out; - transition: 200ms ease-out; -} - -#upload .filelist li img { - width: 100%; -} - -#upload .filelist li p.error { - background: #f43838; - color: #fff; - position: absolute; - bottom: 0; - left: 0; - height: 28px; - line-height: 28px; - width: 100%; - z-index: 100; - display:none; -} - -#upload .filelist li .success { - display: block; - position: absolute; - left: 0; - bottom: 0; - height: 40px; - width: 100%; - z-index: 200; - background: url(./images/success.png) no-repeat right bottom; - background: url(./images/success.gif) no-repeat right bottom \9; -} - -#upload .filelist li.filePickerBlock { - width: 113px; - height: 113px; - background: url(./images/image.png) no-repeat center 12px; - border: 1px solid #eeeeee; - border-radius: 0; -} -#upload .filelist li.filePickerBlock div.webuploader-pick { - width: 100%; - height: 100%; - margin: 0; - padding: 0; - opacity: 0; - background: none; - font-size: 0; -} - -#upload .filelist div.file-panel { - position: absolute; - height: 0; - filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#80000000', endColorstr='#80000000') \0; - background: rgba(0, 0, 0, 0.5); - width: 100%; - top: 0; - left: 0; - overflow: hidden; - z-index: 300; -} - -#upload .filelist div.file-panel span { - width: 24px; - height: 24px; - display: inline; - float: right; - text-indent: -9999px; - overflow: hidden; - background: url(./images/icons.png) no-repeat; - background: url(./images/icons.gif) no-repeat \9; - margin: 5px 1px 1px; - cursor: pointer; - -webkit-tap-highlight-color: rgba(0,0,0,0); - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -#upload .filelist div.file-panel span.rotateLeft { - display:none; - background-position: 0 -24px; -} - -#upload .filelist div.file-panel span.rotateLeft:hover { - background-position: 0 0; -} - -#upload .filelist div.file-panel span.rotateRight { - display:none; - background-position: -24px -24px; -} - -#upload .filelist div.file-panel span.rotateRight:hover { - background-position: -24px 0; -} - -#upload .filelist div.file-panel span.cancel { - background-position: -48px -24px; -} - -#upload .filelist div.file-panel span.cancel:hover { - background-position: -48px 0; -} - -#upload .statusBar { - height: 45px; - border-bottom: 1px solid #dadada; - margin: 0 10px; - padding: 0; - line-height: 45px; - vertical-align: middle; - position: relative; -} - -#upload .statusBar .progress { - border: 1px solid #1483d8; - width: 198px; - background: #fff; - height: 18px; - position: absolute; - top: 12px; - display: none; - text-align: center; - line-height: 18px; - color: #6dbfff; - margin: 0 10px 0 0; -} -#upload .statusBar .progress span.percentage { - width: 0; - height: 100%; - left: 0; - top: 0; - background: #1483d8; - position: absolute; -} -#upload .statusBar .progress span.text { - position: relative; - z-index: 10; -} - -#upload .statusBar .info { - display: inline-block; - font-size: 14px; - color: #666666; -} - -#upload .statusBar .btns { - position: absolute; - top: 7px; - right: 0; - line-height: 30px; -} - -#filePickerBtn { - display: inline-block; - float: left; -} -#upload .statusBar .btns .webuploader-pick, -#upload .statusBar .btns .uploadBtn, -#upload .statusBar .btns .uploadBtn.state-uploading, -#upload .statusBar .btns .uploadBtn.state-paused { - background: #ffffff; - border: 1px solid #cfcfcf; - color: #565656; - padding: 0 18px; - display: inline-block; - border-radius: 3px; - margin-left: 10px; - cursor: pointer; - font-size: 14px; - float: left; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -#upload .statusBar .btns .webuploader-pick-hover, -#upload .statusBar .btns .uploadBtn:hover, -#upload .statusBar .btns .uploadBtn.state-uploading:hover, -#upload .statusBar .btns .uploadBtn.state-paused:hover { - background: #f0f0f0; -} - -#upload .statusBar .btns .uploadBtn, -#upload .statusBar .btns .uploadBtn.state-paused{ - background: #00b7ee; - color: #fff; - border-color: transparent; -} -#upload .statusBar .btns .uploadBtn:hover, -#upload .statusBar .btns .uploadBtn.state-paused:hover{ - background: #00a2d4; -} - -#upload .statusBar .btns .uploadBtn.disabled { - pointer-events: none; - filter:alpha(opacity=60); - -moz-opacity:0.6; - -khtml-opacity: 0.6; - opacity: 0.6; -} - - - -/* 图片管理样式 */ -#online { - width: 100%; - height: 336px; - padding: 10px 0 0 0; -} -#online #imageList{ - width: 100%; - height: 100%; - overflow-x: hidden; - overflow-y: auto; - position: relative; -} -#online ul { - display: block; - list-style: none; - margin: 0; - padding: 0; -} -#online li { - float: left; - display: block; - list-style: none; - padding: 0; - width: 113px; - height: 113px; - margin: 0 0 9px 9px; - *margin: 0 0 6px 6px; - background-color: #eee; - overflow: hidden; - cursor: pointer; - position: relative; -} -#online li.clearFloat { - float: none; - clear: both; - display: block; - width:0; - height:0; - margin: 0; - padding: 0; -} -#online li img { - cursor: pointer; -} -#online li .icon { - cursor: pointer; - width: 113px; - height: 113px; - position: absolute; - top: 0; - left: 0; - z-index: 2; - border: 0; - background-repeat: no-repeat; -} -#online li .icon:hover { - width: 107px; - height: 107px; - border: 3px solid #1094fa; -} -#online li.selected .icon { - background-image: url(images/success.png); - background-image: url(images/success.gif)\9; - background-position: 75px 75px; -} -#online li.selected .icon:hover { - width: 107px; - height: 107px; - border: 3px solid #1094fa; - background-position: 72px 72px; -} - - -/* 图片搜索样式 */ -#search .searchBar { - width: 100%; - height: 30px; - margin: 10px 0 5px 0; - padding: 0; -} - -#search input.text{ - width: 150px; - padding: 3px 6px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -#search input.text:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); -} -#search input.searchTxt { - margin-left:5px; - padding-left: 5px; - background: #FFF; - width: 300px; - *width: 260px; - height: 21px; - line-height: 21px; - float: left; - dislay: block; -} - -#search .searchType { - width: 65px; - height: 28px; - padding:0; - line-height: 28px; - border: 1px solid #d7d7d7; - border-radius: 0; - vertical-align: top; - margin-left: 5px; - float: left; - dislay: block; -} - -#search #searchBtn, -#search #searchReset { - display: inline-block; - margin-bottom: 0; - margin-right: 5px; - padding: 4px 10px; - font-weight: 400; - text-align: center; - vertical-align: middle; - cursor: pointer; - background-image: none; - border: 1px solid transparent; - white-space: nowrap; - font-size: 14px; - border-radius: 4px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - vertical-align: top; - float: right; -} - -#search #searchBtn { - color: white; - border-color: #285e8e; - background-color: #3b97d7; -} -#search #searchReset { - color: #333; - border-color: #ccc; - background-color: #fff; -} -#search #searchBtn:hover { - background-color: #3276b1; -} -#search #searchReset:hover { - background-color: #eee; -} - -#search .msg { - margin-left: 5px; -} - -#search .searchList{ - width: 100%; - height: 300px; - overflow: hidden; - clear: both; -} -#search .searchList ul{ - margin:0; - padding:0; - list-style:none; - clear: both; - width: 100%; - height: 100%; - overflow-x: hidden; - overflow-y: auto; - zoom: 1; - position: relative; -} - -#search .searchList li { - list-style:none; - float: left; - display: block; - width: 115px; - margin: 5px 10px 5px 20px; - *margin: 5px 10px 5px 15px; - padding:0; - font-size: 12px; - box-shadow: 0 1px 3px rgba(0, 0, 0, .3); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, .3); - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, .3); - position: relative; - vertical-align: top; - text-align: center; - overflow: hidden; - cursor: pointer; - filter: alpha(Opacity=100); - -moz-opacity: 1; - opacity: 1; - border: 2px solid #eee; -} - -#search .searchList li.selected { - filter: alpha(Opacity=40); - -moz-opacity: 0.4; - opacity: 0.4; - border: 2px solid #00a0e9; -} - -#search .searchList li p { - background-color: #eee; - margin: 0; - padding: 0; - position: relative; - width:100%; - height:115px; - overflow: hidden; -} - -#search .searchList li p img { - cursor: pointer; - border: 0; -} - -#search .searchList li a { - color: #999; - border-top: 1px solid #F2F2F2; - background: #FAFAFA; - text-align: center; - display: block; - padding: 0 5px; - width: 105px; - height:32px; - line-height:32px; - white-space:nowrap; - text-overflow:ellipsis; - text-decoration: none; - overflow: hidden; - word-break: break-all; -} - -#search .searchList a:hover { - text-decoration: underline; - color: #333; -} -#search .searchList .clearFloat{ - clear: both; -} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/image/image.html b/static/plugs/ueditor/dialogs/image/image.html deleted file mode 100644 index 81f45ad68..000000000 --- a/static/plugs/ueditor/dialogs/image/image.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - ueditor图片对话框 - - - - - - - - - - - - - - -
                  -
                  - - - - -
                  -
                  - - - - - - - - -
                  -
                  - - -
                  -
                  -
                  - - -
                  -
                  -
                  -
                  - -   px -   px - -
                  -
                  - - px -
                  -
                  - - px -
                  -
                  - - -
                  -
                  -
                  -
                  - - -
                  -
                  -
                  -
                  - 0% - -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                    -
                  • -
                  -
                  -
                  - - -
                  -
                  -
                  - - - - -
                  -
                  - - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/image/image.js b/static/plugs/ueditor/dialogs/image/image.js deleted file mode 100644 index 6a0146925..000000000 --- a/static/plugs/ueditor/dialogs/image/image.js +++ /dev/null @@ -1,1139 +0,0 @@ -/** - * User: Jinqn - * Date: 14-04-08 - * Time: 下午16:34 - * 上传图片对话框逻辑代码,包括tab: 远程图片/上传图片/在线图片/搜索图片 - */ - -(function () { - - var remoteImage, - uploadImage, - onlineImage, - searchImage; - - window.onload = function () { - initTabs(); - initAlign(); - initButtons(); - }; - - /* 初始化tab标签 */ - function initTabs() { - var tabs = $G('tabhead').children; - for (var i = 0; i < tabs.length; i++) { - domUtils.on(tabs[i], "click", function (e) { - var target = e.target || e.srcElement; - setTabFocus(target.getAttribute('data-content-id')); - }); - } - - var img = editor.selection.getRange().getClosedNode(); - if (img && img.tagName && img.tagName.toLowerCase() == 'img') { - setTabFocus('remote'); - } else { - setTabFocus('upload'); - } - } - - /* 初始化tabbody */ - function setTabFocus(id) { - if(!id) return; - var i, bodyId, tabs = $G('tabhead').children; - for (i = 0; i < tabs.length; i++) { - bodyId = tabs[i].getAttribute('data-content-id'); - if (bodyId == id) { - domUtils.addClass(tabs[i], 'focus'); - domUtils.addClass($G(bodyId), 'focus'); - } else { - domUtils.removeClasses(tabs[i], 'focus'); - domUtils.removeClasses($G(bodyId), 'focus'); - } - } - switch (id) { - case 'remote': - remoteImage = remoteImage || new RemoteImage(); - break; - case 'upload': - setAlign(editor.getOpt('imageInsertAlign')); - uploadImage = uploadImage || new UploadImage('queueList'); - break; - case 'online': - setAlign(editor.getOpt('imageManagerInsertAlign')); - onlineImage = onlineImage || new OnlineImage('imageList'); - onlineImage.reset(); - break; - case 'search': - setAlign(editor.getOpt('imageManagerInsertAlign')); - searchImage = searchImage || new SearchImage(); - break; - } - } - - /* 初始化onok事件 */ - function initButtons() { - - dialog.onok = function () { - var remote = false, list = [], id, tabs = $G('tabhead').children; - for (var i = 0; i < tabs.length; i++) { - if (domUtils.hasClass(tabs[i], 'focus')) { - id = tabs[i].getAttribute('data-content-id'); - break; - } - } - - switch (id) { - case 'remote': - list = remoteImage.getInsertList(); - break; - case 'upload': - list = uploadImage.getInsertList(); - var count = uploadImage.getQueueCount(); - if (count) { - $('.info', '#queueList').html('' + '还有2个未上传文件'.replace(/[\d]/, count) + ''); - return false; - } - break; - case 'online': - list = onlineImage.getInsertList(); - break; - case 'search': - list = searchImage.getInsertList(); - remote = true; - break; - } - - if(list) { - editor.execCommand('insertimage', list); - remote && editor.fireEvent("catchRemoteImage"); - } - }; - } - - - /* 初始化对其方式的点击事件 */ - function initAlign(){ - /* 点击align图标 */ - domUtils.on($G("alignIcon"), 'click', function(e){ - var target = e.target || e.srcElement; - if(target.className && target.className.indexOf('-align') != -1) { - setAlign(target.getAttribute('data-align')); - } - }); - } - - /* 设置对齐方式 */ - function setAlign(align){ - align = align || 'none'; - var aligns = $G("alignIcon").children; - for(i = 0; i < aligns.length; i++){ - if(aligns[i].getAttribute('data-align') == align) { - domUtils.addClass(aligns[i], 'focus'); - $G("align").value = aligns[i].getAttribute('data-align'); - } else { - domUtils.removeClasses(aligns[i], 'focus'); - } - } - } - /* 获取对齐方式 */ - function getAlign(){ - var align = $G("align").value || 'none'; - return align == 'none' ? '':align; - } - - - /* 在线图片 */ - function RemoteImage(target) { - this.container = utils.isString(target) ? document.getElementById(target) : target; - this.init(); - } - RemoteImage.prototype = { - init: function () { - this.initContainer(); - this.initEvents(); - }, - initContainer: function () { - this.dom = { - 'url': $G('url'), - 'width': $G('width'), - 'height': $G('height'), - 'border': $G('border'), - 'vhSpace': $G('vhSpace'), - 'title': $G('title'), - 'align': $G('align') - }; - var img = editor.selection.getRange().getClosedNode(); - if (img) { - this.setImage(img); - } - }, - initEvents: function () { - var _this = this, - locker = $G('lock'); - - /* 改变url */ - domUtils.on($G("url"), 'keyup', updatePreview); - domUtils.on($G("border"), 'keyup', updatePreview); - domUtils.on($G("title"), 'keyup', updatePreview); - - domUtils.on($G("width"), 'keyup', function(){ - updatePreview(); - if(locker.checked) { - var proportion =locker.getAttribute('data-proportion'); - $G('height').value = Math.round(this.value / proportion); - } else { - _this.updateLocker(); - } - }); - domUtils.on($G("height"), 'keyup', function(){ - updatePreview(); - if(locker.checked) { - var proportion =locker.getAttribute('data-proportion'); - $G('width').value = Math.round(this.value * proportion); - } else { - _this.updateLocker(); - } - }); - domUtils.on($G("lock"), 'change', function(){ - var proportion = parseInt($G("width").value) /parseInt($G("height").value); - locker.setAttribute('data-proportion', proportion); - }); - - function updatePreview(){ - _this.setPreview(); - } - }, - updateLocker: function(){ - var width = $G('width').value, - height = $G('height').value, - locker = $G('lock'); - if(width && height && width == parseInt(width) && height == parseInt(height)) { - locker.disabled = false; - locker.title = ''; - } else { - locker.checked = false; - locker.disabled = 'disabled'; - locker.title = lang.remoteLockError; - } - }, - setImage: function(img){ - /* 不是正常的图片 */ - if (!img.tagName || img.tagName.toLowerCase() != 'img' && !img.getAttribute("src") || !img.src) return; - - var wordImgFlag = img.getAttribute("word_img"), - src = wordImgFlag ? wordImgFlag.replace("&", "&") : (img.getAttribute('_src') || img.getAttribute("src", 2).replace("&", "&")), - align = editor.queryCommandValue("imageFloat"); - - /* 防止onchange事件循环调用 */ - if (src !== $G("url").value) $G("url").value = src; - if(src) { - /* 设置表单内容 */ - $G("width").value = img.width || ''; - $G("height").value = img.height || ''; - $G("border").value = img.getAttribute("border") || '0'; - $G("vhSpace").value = img.getAttribute("vspace") || '0'; - $G("title").value = img.title || img.alt || ''; - setAlign(align); - this.setPreview(); - this.updateLocker(); - } - }, - getData: function(){ - var data = {}; - for(var k in this.dom){ - data[k] = this.dom[k].value; - } - return data; - }, - setPreview: function(){ - var url = $G('url').value, - ow = $G('width').value, - oh = $G('height').value, - border = $G('border').value, - title = $G('title').value, - preview = $G('preview'), - width, - height; - - width = ((!ow || !oh) ? preview.offsetWidth:Math.min(ow, preview.offsetWidth)); - width = width+(border*2) > preview.offsetWidth ? width:(preview.offsetWidth - (border*2)); - height = (!ow || !oh) ? '':width*oh/ow; - - if(url) { - preview.innerHTML = ''; - } - }, - getInsertList: function () { - var data = this.getData(); - if(data['url']) { - return [{ - src: data['url'], - _src: data['url'], - width: data['width'] || '', - height: data['height'] || '', - border: data['border'] || '', - floatStyle: data['align'] || '', - vspace: data['vhSpace'] || '', - title: data['title'] || '', - alt: data['title'] || '', - style: "width:" + data['width'] + "px;height:" + data['height'] + "px;" - }]; - } else { - return []; - } - } - }; - - - - /* 上传图片 */ - function UploadImage(target) { - this.$wrap = target.constructor == String ? $('#' + target) : $(target); - this.init(); - } - UploadImage.prototype = { - init: function () { - this.imageList = []; - this.initContainer(); - this.initUploader(); - }, - initContainer: function () { - this.$queue = this.$wrap.find('.filelist'); - }, - /* 初始化容器 */ - initUploader: function () { - var _this = this, - $ = jQuery, // just in case. Make sure it's not an other libaray. - $wrap = _this.$wrap, - // 图片容器 - $queue = $wrap.find('.filelist'), - // 状态栏,包括进度和控制按钮 - $statusBar = $wrap.find('.statusBar'), - // 文件总体选择信息。 - $info = $statusBar.find('.info'), - // 上传按钮 - $upload = $wrap.find('.uploadBtn'), - // 上传按钮 - $filePickerBtn = $wrap.find('.filePickerBtn'), - // 上传按钮 - $filePickerBlock = $wrap.find('.filePickerBlock'), - // 没选择文件之前的内容。 - $placeHolder = $wrap.find('.placeholder'), - // 总体进度条 - $progress = $statusBar.find('.progress').hide(), - // 添加的文件数量 - fileCount = 0, - // 添加的文件总大小 - fileSize = 0, - // 优化retina, 在retina下这个值是2 - ratio = window.devicePixelRatio || 1, - // 缩略图大小 - thumbnailWidth = 113 * ratio, - thumbnailHeight = 113 * ratio, - // 可能有pedding, ready, uploading, confirm, done. - state = '', - // 所有文件的进度信息,key为file id - percentages = {}, - supportTransition = (function () { - var s = document.createElement('p').style, - r = 'transition' in s || - 'WebkitTransition' in s || - 'MozTransition' in s || - 'msTransition' in s || - 'OTransition' in s; - s = null; - return r; - })(), - // WebUploader实例 - uploader, - actionUrl = editor.getActionUrl(editor.getOpt('imageActionName')), - acceptExtensions = (editor.getOpt('imageAllowFiles') || []).join('').replace(/\./g, ',').replace(/^[,]/, ''), - imageMaxSize = editor.getOpt('imageMaxSize'), - imageCompressBorder = editor.getOpt('imageCompressBorder'); - - if (!WebUploader.Uploader.support()) { - $('#filePickerReady').after($('
                  ').html(lang.errorNotSupport)).hide(); - return; - } else if (!editor.getOpt('imageActionName')) { - $('#filePickerReady').after($('
                  ').html(lang.errorLoadConfig)).hide(); - return; - } - - uploader = _this.uploader = WebUploader.create({ - pick: { - id: '#filePickerReady', - label: lang.uploadSelectFile - }, - accept: { - title: 'Images', - extensions: acceptExtensions, - mimeTypes: 'image/*' - }, - swf: '../../third-party/webuploader/Uploader.swf', - server: actionUrl, - fileVal: editor.getOpt('imageFieldName'), - duplicate: true, - fileSingleSizeLimit: imageMaxSize, // 默认 2 M - compress: editor.getOpt('imageCompressEnable') ? { - width: imageCompressBorder, - height: imageCompressBorder, - // 图片质量,只有type为`image/jpeg`的时候才有效。 - quality: 90, - // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false. - allowMagnify: false, - // 是否允许裁剪。 - crop: false, - // 是否保留头部meta信息。 - preserveHeaders: true - }:false - }); - uploader.addButton({ - id: '#filePickerBlock' - }); - uploader.addButton({ - id: '#filePickerBtn', - label: lang.uploadAddFile - }); - - setState('pedding'); - - // 当有文件添加进来时执行,负责view的创建 - function addFile(file) { - var $li = $('
                • ' + - '

                  ' + file.name + '

                  ' + - '

                  ' + - '

                  ' + - '
                • '), - - $btns = $('
                  ' + - '' + lang.uploadDelete + '' + - '' + lang.uploadTurnRight + '' + - '' + lang.uploadTurnLeft + '
                  ').appendTo($li), - $prgress = $li.find('p.progress span'), - $wrap = $li.find('p.imgWrap'), - $info = $('

                  ').hide().appendTo($li), - - showError = function (code) { - switch (code) { - case 'exceed_size': - text = lang.errorExceedSize; - break; - case 'interrupt': - text = lang.errorInterrupt; - break; - case 'http': - text = lang.errorHttp; - break; - case 'not_allow_type': - text = lang.errorFileType; - break; - default: - text = lang.errorUploadRetry; - break; - } - $info.text(text).show(); - }; - - if (file.getStatus() === 'invalid') { - showError(file.statusText); - } else { - $wrap.text(lang.uploadPreview); - if (browser.ie && browser.version <= 7) { - $wrap.text(lang.uploadNoPreview); - } else { - uploader.makeThumb(file, function (error, src) { - if (error || !src) { - $wrap.text(lang.uploadNoPreview); - } else { - var $img = $(''); - $wrap.empty().append($img); - $img.on('error', function () { - $wrap.text(lang.uploadNoPreview); - }); - } - }, thumbnailWidth, thumbnailHeight); - } - percentages[ file.id ] = [ file.size, 0 ]; - file.rotation = 0; - - /* 检查文件格式 */ - if (!file.ext || acceptExtensions.indexOf(file.ext.toLowerCase()) == -1) { - showError('not_allow_type'); - uploader.removeFile(file); - } - } - - file.on('statuschange', function (cur, prev) { - if (prev === 'progress') { - $prgress.hide().width(0); - } else if (prev === 'queued') { - $li.off('mouseenter mouseleave'); - $btns.remove(); - } - // 成功 - if (cur === 'error' || cur === 'invalid') { - showError(file.statusText); - percentages[ file.id ][ 1 ] = 1; - } else if (cur === 'interrupt') { - showError('interrupt'); - } else if (cur === 'queued') { - percentages[ file.id ][ 1 ] = 0; - } else if (cur === 'progress') { - $info.hide(); - $prgress.css('display', 'block'); - } else if (cur === 'complete') { - } - - $li.removeClass('state-' + prev).addClass('state-' + cur); - }); - - $li.on('mouseenter', function () { - $btns.stop().animate({height: 30}); - }); - $li.on('mouseleave', function () { - $btns.stop().animate({height: 0}); - }); - - $btns.on('click', 'span', function () { - var index = $(this).index(), - deg; - - switch (index) { - case 0: - uploader.removeFile(file); - return; - case 1: - file.rotation += 90; - break; - case 2: - file.rotation -= 90; - break; - } - - if (supportTransition) { - deg = 'rotate(' + file.rotation + 'deg)'; - $wrap.css({ - '-webkit-transform': deg, - '-mos-transform': deg, - '-o-transform': deg, - 'transform': deg - }); - } else { - $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')'); - } - - }); - - $li.insertBefore($filePickerBlock); - } - - // 负责view的销毁 - function removeFile(file) { - var $li = $('#' + file.id); - delete percentages[ file.id ]; - updateTotalProgress(); - $li.off().find('.file-panel').off().end().remove(); - } - - function updateTotalProgress() { - var loaded = 0, - total = 0, - spans = $progress.children(), - percent; - - $.each(percentages, function (k, v) { - total += v[ 0 ]; - loaded += v[ 0 ] * v[ 1 ]; - }); - - percent = total ? loaded / total : 0; - - spans.eq(0).text(Math.round(percent * 100) + '%'); - spans.eq(1).css('width', Math.round(percent * 100) + '%'); - updateStatus(); - } - - function setState(val, files) { - - if (val != state) { - - var stats = uploader.getStats(); - - $upload.removeClass('state-' + state); - $upload.addClass('state-' + val); - - switch (val) { - - /* 未选择文件 */ - case 'pedding': - $queue.addClass('element-invisible'); - $statusBar.addClass('element-invisible'); - $placeHolder.removeClass('element-invisible'); - $progress.hide(); $info.hide(); - uploader.refresh(); - break; - - /* 可以开始上传 */ - case 'ready': - $placeHolder.addClass('element-invisible'); - $queue.removeClass('element-invisible'); - $statusBar.removeClass('element-invisible'); - $progress.hide(); $info.show(); - $upload.text(lang.uploadStart); - uploader.refresh(); - break; - - /* 上传中 */ - case 'uploading': - $progress.show(); $info.hide(); - $upload.text(lang.uploadPause); - break; - - /* 暂停上传 */ - case 'paused': - $progress.show(); $info.hide(); - $upload.text(lang.uploadContinue); - break; - - case 'confirm': - $progress.show(); $info.hide(); - $upload.text(lang.uploadStart); - - stats = uploader.getStats(); - if (stats.successNum && !stats.uploadFailNum) { - setState('finish'); - return; - } - break; - - case 'finish': - $progress.hide(); $info.show(); - if (stats.uploadFailNum) { - $upload.text(lang.uploadRetry); - } else { - $upload.text(lang.uploadStart); - } - break; - } - - state = val; - updateStatus(); - - } - - if (!_this.getQueueCount()) { - $upload.addClass('disabled') - } else { - $upload.removeClass('disabled') - } - - } - - function updateStatus() { - var text = '', stats; - - if (state === 'ready') { - text = lang.updateStatusReady.replace('_', fileCount).replace('_KB', WebUploader.formatSize(fileSize)); - } else if (state === 'confirm') { - stats = uploader.getStats(); - if (stats.uploadFailNum) { - text = lang.updateStatusConfirm.replace('_', stats.successNum).replace('_', stats.successNum); - } - } else { - stats = uploader.getStats(); - text = lang.updateStatusFinish.replace('_', fileCount). - replace('_KB', WebUploader.formatSize(fileSize)). - replace('_', stats.successNum); - - if (stats.uploadFailNum) { - text += lang.updateStatusError.replace('_', stats.uploadFailNum); - } - } - - $info.html(text); - } - - uploader.on('fileQueued', function (file) { - fileCount++; - fileSize += file.size; - - if (fileCount === 1) { - $placeHolder.addClass('element-invisible'); - $statusBar.show(); - } - - addFile(file); - }); - - uploader.on('fileDequeued', function (file) { - fileCount--; - fileSize -= file.size; - - removeFile(file); - updateTotalProgress(); - }); - - uploader.on('filesQueued', function (file) { - if (!uploader.isInProgress() && (state == 'pedding' || state == 'finish' || state == 'confirm' || state == 'ready')) { - setState('ready'); - } - updateTotalProgress(); - }); - - uploader.on('all', function (type, files) { - switch (type) { - case 'uploadFinished': - setState('confirm', files); - break; - case 'startUpload': - /* 添加额外的GET参数 */ - var params = utils.serializeParam(editor.queryCommandValue('serverparam')) || '', - url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?':'&') + 'encode=utf-8&' + params); - uploader.option('server', url); - setState('uploading', files); - break; - case 'stopUpload': - setState('paused', files); - break; - } - }); - - uploader.on('uploadBeforeSend', function (file, data, header) { - //这里可以通过data对象添加POST参数 - header['X_Requested_With'] = 'XMLHttpRequest'; - }); - - uploader.on('uploadProgress', function (file, percentage) { - var $li = $('#' + file.id), - $percent = $li.find('.progress span'); - - $percent.css('width', percentage * 100 + '%'); - percentages[ file.id ][ 1 ] = percentage; - updateTotalProgress(); - }); - - uploader.on('uploadSuccess', function (file, ret) { - var $file = $('#' + file.id); - try { - var responseText = (ret._raw || ret), - json = utils.str2json(responseText); - if (json.state == 'SUCCESS') { - _this.imageList.push(json); - $file.append(''); - } else { - $file.find('.error').text(json.state).show(); - } - } catch (e) { - $file.find('.error').text(lang.errorServerUpload).show(); - } - }); - - uploader.on('uploadError', function (file, code) { - }); - uploader.on('error', function (code, file) { - if (code == 'Q_TYPE_DENIED' || code == 'F_EXCEED_SIZE') { - addFile(file); - } - }); - uploader.on('uploadComplete', function (file, ret) { - }); - - $upload.on('click', function () { - if ($(this).hasClass('disabled')) { - return false; - } - - if (state === 'ready') { - uploader.upload(); - } else if (state === 'paused') { - uploader.upload(); - } else if (state === 'uploading') { - uploader.stop(); - } - }); - - $upload.addClass('state-' + state); - updateTotalProgress(); - }, - getQueueCount: function () { - var file, i, status, readyFile = 0, files = this.uploader.getFiles(); - for (i = 0; file = files[i++]; ) { - status = file.getStatus(); - if (status == 'queued' || status == 'uploading' || status == 'progress') readyFile++; - } - return readyFile; - }, - destroy: function () { - this.$wrap.remove(); - }, - getInsertList: function () { - var i, data, list = [], - align = getAlign(), - prefix = editor.getOpt('imageUrlPrefix'); - for (i = 0; i < this.imageList.length; i++) { - data = this.imageList[i]; - list.push({ - src: prefix + data.url, - _src: prefix + data.url, - title: data.title, - alt: data.original, - floatStyle: align - }); - } - return list; - } - }; - - - /* 在线图片 */ - function OnlineImage(target) { - this.container = utils.isString(target) ? document.getElementById(target) : target; - this.init(); - } - OnlineImage.prototype = { - init: function () { - this.reset(); - this.initEvents(); - }, - /* 初始化容器 */ - initContainer: function () { - this.container.innerHTML = ''; - this.list = document.createElement('ul'); - this.clearFloat = document.createElement('li'); - - domUtils.addClass(this.list, 'list'); - domUtils.addClass(this.clearFloat, 'clearFloat'); - - this.list.appendChild(this.clearFloat); - this.container.appendChild(this.list); - }, - /* 初始化滚动事件,滚动到地步自动拉取数据 */ - initEvents: function () { - var _this = this; - - /* 滚动拉取图片 */ - domUtils.on($G('imageList'), 'scroll', function(e){ - var panel = this; - if (panel.scrollHeight - (panel.offsetHeight + panel.scrollTop) < 10) { - _this.getImageData(); - } - }); - /* 选中图片 */ - domUtils.on(this.container, 'click', function (e) { - var target = e.target || e.srcElement, - li = target.parentNode; - - if (li.tagName.toLowerCase() == 'li') { - if (domUtils.hasClass(li, 'selected')) { - domUtils.removeClasses(li, 'selected'); - } else { - domUtils.addClass(li, 'selected'); - } - } - }); - }, - /* 初始化第一次的数据 */ - initData: function () { - - /* 拉取数据需要使用的值 */ - this.state = 0; - this.listSize = editor.getOpt('imageManagerListSize'); - this.listIndex = 0; - this.listEnd = false; - - /* 第一次拉取数据 */ - this.getImageData(); - }, - /* 重置界面 */ - reset: function() { - this.initContainer(); - this.initData(); - }, - /* 向后台拉取图片列表数据 */ - getImageData: function () { - var _this = this; - - if(!_this.listEnd && !this.isLoadingData) { - this.isLoadingData = true; - var url = editor.getActionUrl(editor.getOpt('imageManagerActionName')), - isJsonp = utils.isCrossDomainUrl(url); - ajax.request(url, { - 'timeout': 100000, - 'dataType': isJsonp ? 'jsonp':'', - 'data': utils.extend({ - start: this.listIndex, - size: this.listSize - }, editor.queryCommandValue('serverparam')), - 'method': 'get', - 'onsuccess': function (r) { - try { - var json = isJsonp ? r:eval('(' + r.responseText + ')'); - if (json.state == 'SUCCESS') { - _this.pushData(json.list); - _this.listIndex = parseInt(json.start) + parseInt(json.list.length); - if(_this.listIndex >= json.total) { - _this.listEnd = true; - } - _this.isLoadingData = false; - } - } catch (e) { - if(r.responseText.indexOf('ue_separate_ue') != -1) { - var list = r.responseText.split(r.responseText); - _this.pushData(list); - _this.listIndex = parseInt(list.length); - _this.listEnd = true; - _this.isLoadingData = false; - } - } - }, - 'onerror': function () { - _this.isLoadingData = false; - } - }); - } - }, - /* 添加图片到列表界面上 */ - pushData: function (list) { - var i, item, img, icon, _this = this, - urlPrefix = editor.getOpt('imageManagerUrlPrefix'); - for (i = 0; i < list.length; i++) { - if(list[i] && list[i].url) { - item = document.createElement('li'); - img = document.createElement('img'); - icon = document.createElement('span'); - - domUtils.on(img, 'load', (function(image){ - return function(){ - _this.scale(image, image.parentNode.offsetWidth, image.parentNode.offsetHeight); - } - })(img)); - img.width = 113; - img.setAttribute('src', urlPrefix + list[i].url + (list[i].url.indexOf('?') == -1 ? '?noCache=':'&noCache=') + (+new Date()).toString(36) ); - img.setAttribute('_src', urlPrefix + list[i].url); - domUtils.addClass(icon, 'icon'); - - item.appendChild(img); - item.appendChild(icon); - this.list.insertBefore(item, this.clearFloat); - } - } - }, - /* 改变图片大小 */ - scale: function (img, w, h, type) { - var ow = img.width, - oh = img.height; - - if (type == 'justify') { - if (ow >= oh) { - img.width = w; - img.height = h * oh / ow; - img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px'; - } else { - img.width = w * ow / oh; - img.height = h; - img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px'; - } - } else { - if (ow >= oh) { - img.width = w * ow / oh; - img.height = h; - img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px'; - } else { - img.width = w; - img.height = h * oh / ow; - img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px'; - } - } - }, - getInsertList: function () { - var i, lis = this.list.children, list = [], align = getAlign(); - for (i = 0; i < lis.length; i++) { - if (domUtils.hasClass(lis[i], 'selected')) { - var img = lis[i].firstChild, - src = img.getAttribute('_src'); - list.push({ - src: src, - _src: src, - alt: src.substr(src.lastIndexOf('/') + 1), - floatStyle: align - }); - } - - } - return list; - } - }; - - /*搜索图片 */ - function SearchImage() { - this.init(); - } - SearchImage.prototype = { - init: function () { - this.initEvents(); - }, - initEvents: function(){ - var _this = this; - - /* 点击搜索按钮 */ - domUtils.on($G('searchBtn'), 'click', function(){ - var key = $G('searchTxt').value; - if(key && key != lang.searchRemind) { - _this.getImageData(); - } - }); - /* 点击清除妞 */ - domUtils.on($G('searchReset'), 'click', function(){ - $G('searchTxt').value = lang.searchRemind; - $G('searchListUl').innerHTML = ''; - $G('searchType').selectedIndex = 0; - }); - /* 搜索框聚焦 */ - domUtils.on($G('searchTxt'), 'focus', function(){ - var key = $G('searchTxt').value; - if(key && key == lang.searchRemind) { - $G('searchTxt').value = ''; - } - }); - /* 搜索框回车键搜索 */ - domUtils.on($G('searchTxt'), 'keydown', function(e){ - var keyCode = e.keyCode || e.which; - if (keyCode == 13) { - $G('searchBtn').click(); - } - }); - - /* 选中图片 */ - domUtils.on($G('searchList'), 'click', function(e){ - var target = e.target || e.srcElement, - li = target.parentNode.parentNode; - - if (li.tagName.toLowerCase() == 'li') { - if (domUtils.hasClass(li, 'selected')) { - domUtils.removeClasses(li, 'selected'); - } else { - domUtils.addClass(li, 'selected'); - } - } - }); - }, - encodeToGb2312:function (str){ - if(!str) return ''; - var strOut = "", - z = 'D2BBB6A18140C6DF814181428143CDF2D5C9C8FDC9CFCFC2D8A2B2BBD3EB8144D8A4B3F38145D7A8C7D2D8A7CAC08146C7F0B1FBD2B5B4D4B6ABCBBFD8A9814781488149B6AA814AC1BDD1CF814BC9A5D8AD814CB8F6D1BEE3DCD6D0814D814EB7E1814FB4AE8150C1D98151D8BC8152CDE8B5A4CEAAD6F78153C0F6BED9D8AF815481558156C4CB8157BEC38158D8B1C3B4D2E58159D6AECEDAD5A7BAF5B7A6C0D6815AC6B9C5D2C7C7815BB9D4815CB3CBD2D2815D815ED8BFBEC5C6F2D2B2CFB0CFE7815F816081618162CAE981638164D8C081658166816781688169816AC2F2C2D2816BC8E9816C816D816E816F817081718172817381748175C7AC8176817781788179817A817B817CC1CB817DD3E8D5F9817ECAC2B6FED8A1D3DABFF78180D4C6BBA5D8C1CEE5BEAE81818182D8A88183D1C7D0A9818481858186D8BDD9EFCDF6BFBA8187BDBBBAA5D2E0B2FABAE0C4B68188CFEDBEA9CDA4C1C18189818A818BC7D7D9F1818CD9F4818D818E818F8190C8CBD8E9819181928193D2DACAB2C8CAD8ECD8EAD8C6BDF6C6CDB3F08194D8EBBDF1BDE98195C8D4B4D381968197C2D88198B2D6D7D0CACBCBFBD5CCB8B6CFC98199819A819BD9DAD8F0C7AA819CD8EE819DB4FAC1EED2D4819E819FD8ED81A0D2C7D8EFC3C781A181A281A3D1F681A4D6D9D8F281A5D8F5BCFEBCDB81A681A781A8C8CE81A9B7DD81AAB7C281ABC6F381AC81AD81AE81AF81B081B181B2D8F8D2C181B381B4CEE9BCBFB7FCB7A5D0DD81B581B681B781B881B9D6DAD3C5BBEFBBE1D8F181BA81BBC9A1CEB0B4AB81BCD8F381BDC9CBD8F6C2D7D8F781BE81BFCEB1D8F981C081C181C2B2AEB9C081C3D9A381C4B0E981C5C1E681C6C9EC81C7CBC581C8CBC6D9A481C981CA81CB81CC81CDB5E881CE81CFB5AB81D081D181D281D381D481D5CEBBB5CDD7A1D7F4D3D381D6CCE581D7BACE81D8D9A2D9DCD3E0D8FDB7F0D7F7D8FED8FAD9A1C4E381D981DAD3B6D8F4D9DD81DBD8FB81DCC5E581DD81DEC0D081DF81E0D1F0B0DB81E181E2BCD1D9A681E3D9A581E481E581E681E7D9ACD9AE81E8D9ABCAB981E981EA81EBD9A9D6B681EC81ED81EEB3DED9A881EFC0FD81F0CACC81F1D9AA81F2D9A781F381F4D9B081F581F6B6B181F781F881F9B9A981FAD2C081FB81FCCFC081FD81FEC2C28240BDC4D5ECB2E0C7C8BFEBD9AD8241D9AF8242CEEABAEE82438244824582468247C7D682488249824A824B824C824D824E824F8250B1E3825182528253B4D9B6EDD9B48254825582568257BFA182588259825AD9DEC7CEC0FED9B8825B825C825D825E825FCBD7B7FD8260D9B58261D9B7B1A3D3E1D9B98262D0C58263D9B682648265D9B18266D9B2C1A9D9B382678268BCF3D0DEB8A98269BEE3826AD9BD826B826C826D826ED9BA826FB0B3827082718272D9C28273827482758276827782788279827A827B827C827D827E8280D9C4B1B68281D9BF82828283B5B98284BEF3828582868287CCC8BAF2D2D08288D9C38289828ABDE8828BB3AB828C828D828ED9C5BEEB828FD9C6D9BBC4DF8290D9BED9C1D9C0829182928293829482958296829782988299829A829BD5AE829CD6B5829DC7E3829E829F82A082A1D9C882A282A382A4BCD9D9CA82A582A682A7D9BC82A8D9CBC6AB82A982AA82AB82AC82ADD9C982AE82AF82B082B1D7F682B2CDA382B382B482B582B682B782B882B982BABDA182BB82BC82BD82BE82BF82C0D9CC82C182C282C382C482C582C682C782C882C9C5BCCDB582CA82CB82CCD9CD82CD82CED9C7B3A5BFFE82CF82D082D182D2B8B582D382D4C0FC82D582D682D782D8B0F882D982DA82DB82DC82DD82DE82DF82E082E182E282E382E482E582E682E782E882E982EA82EB82EC82EDB4F682EED9CE82EFD9CFB4A2D9D082F082F1B4DF82F282F382F482F582F6B0C182F782F882F982FA82FB82FC82FDD9D1C9B582FE8340834183428343834483458346834783488349834A834B834C834D834E834F83508351CFF1835283538354835583568357D9D283588359835AC1C5835B835C835D835E835F836083618362836383648365D9D6C9AE8366836783688369D9D5D9D4D9D7836A836B836C836DCBDB836EBDA9836F8370837183728373C6A7837483758376837783788379837A837B837C837DD9D3D9D8837E83808381D9D9838283838384838583868387C8E583888389838A838B838C838D838E838F839083918392839383948395C0DC8396839783988399839A839B839C839D839E839F83A083A183A283A383A483A583A683A783A883A983AA83AB83AC83AD83AE83AF83B083B183B2B6F9D8A3D4CA83B3D4AAD0D6B3E4D5D783B4CFC8B9E283B5BFCB83B6C3E283B783B883B9B6D283BA83BBCDC3D9EED9F083BC83BD83BEB5B383BFB6B583C083C183C283C383C4BEA483C583C6C8EB83C783C8C8AB83C983CAB0CBB9ABC1F9D9E283CBC0BCB9B283CCB9D8D0CBB1F8C6E4BEDFB5E4D7C883CDD1F8BCE6CADE83CE83CFBCBDD9E6D8E783D083D1C4DA83D283D3B8D4C8BD83D483D5B2E1D4D983D683D783D883D9C3B083DA83DBC3E1DAA2C8DF83DCD0B483DDBEFCC5A983DE83DF83E0B9DA83E1DAA383E2D4A9DAA483E383E483E583E683E7D9FBB6AC83E883E9B7EBB1F9D9FCB3E5BEF683EABFF6D2B1C0E483EB83EC83EDB6B3D9FED9FD83EE83EFBEBB83F083F183F2C6E083F3D7BCDAA183F4C1B983F5B5F2C1E883F683F7BCF583F8B4D583F983FA83FB83FC83FD83FE844084418442C1DD8443C4FD84448445BCB8B7B284468447B7EF84488449844A844B844C844DD9EC844EC6BE844FBFADBBCB84508451B5CA8452DBC9D0D78453CDB9B0BCB3F6BBF7DBCABAAF8454D4E4B5B6B5F3D8D6C8D084558456B7D6C7D0D8D78457BFAF84588459DBBBD8D8845A845BD0CCBBAE845C845D845EEBBEC1D0C1F5D4F2B8D5B4B4845FB3F584608461C9BE846284638464C5D0846584668467C5D9C0FB8468B1F08469D8D9B9CE846AB5BD846B846CD8DA846D846ED6C6CBA2C8AFC9B2B4CCBFCC846FB9F48470D8DBD8DCB6E7BCC1CCEA847184728473847484758476CFF78477D8DDC7B084788479B9D0BDA3847A847BCCDE847CC6CA847D847E848084818482D8E08483D8DE84848485D8DF848684878488B0FE8489BEE7848ACAA3BCF4848B848C848D848EB8B1848F8490B8EE849184928493849484958496849784988499849AD8E2849BBDCB849CD8E4D8E3849D849E849F84A084A1C5FC84A284A384A484A584A684A784A8D8E584A984AAD8E684AB84AC84AD84AE84AF84B084B1C1A684B2C8B0B0ECB9A6BCD3CEF1DBBDC1D384B384B484B584B6B6AFD6FAC5ACBDD9DBBEDBBF84B784B884B9C0F8BEA2C0CD84BA84BB84BC84BD84BE84BF84C084C184C284C3DBC0CAC684C484C584C6B2AA84C784C884C9D3C284CAC3E384CBD1AB84CC84CD84CE84CFDBC284D0C0D584D184D284D3DBC384D4BFB184D584D684D784D884D984DAC4BC84DB84DC84DD84DEC7DA84DF84E084E184E284E384E484E584E684E784E884E9DBC484EA84EB84EC84ED84EE84EF84F084F1D9E8C9D784F284F384F4B9B4CEF0D4C884F584F684F784F8B0FCB4D284F9D0D984FA84FB84FC84FDD9E984FEDECBD9EB8540854185428543D8B0BBAFB1B18544B3D7D8CE85458546D4D185478548BDB3BFEF8549CFBB854A854BD8D0854C854D854EB7CB854F85508551D8D185528553855485558556855785588559855A855BC6A5C7F8D2BD855C855DD8D2C4E4855ECAAE855FC7A78560D8A68561C9FDCEE7BBDCB0EB856285638564BBAAD0AD8565B1B0D7E4D7BF8566B5A5C2F4C4CF85678568B2A98569B2B7856AB1E5DFB2D5BCBFA8C2ACD8D5C2B1856BD8D4CED4856CDAE0856DCEC0856E856FD8B4C3AED3A1CEA38570BCB4C8B4C2D18571BEEDD0B68572DAE18573857485758576C7E485778578B3A78579B6F2CCFCC0FA857A857BC0F7857CD1B9D1E1D8C7857D857E85808581858285838584B2DE85858586C0E58587BAF185888589D8C8858AD4AD858B858CCFE1D8C9858DD8CACFC3858EB3F8BEC7858F859085918592D8CB8593859485958596859785988599DBCC859A859B859C859DC8A5859E859F85A0CFD885A1C8FEB2CE85A285A385A485A585A6D3D6B2E6BCB0D3D1CBABB7B485A785A885A9B7A285AA85ABCAE585ACC8A1CADCB1E4D0F085ADC5D185AE85AF85B0DBC5B5FE85B185B2BFDAB9C5BEE4C1ED85B3DFB6DFB5D6BBBDD0D5D9B0C8B6A3BFC9CCA8DFB3CAB7D3D285B4D8CFD2B6BAC5CBBECCBE85B5DFB7B5F0DFB485B685B785B8D3F585B9B3D4B8F785BADFBA85BBBACFBCAAB5F585BCCDACC3FBBAF3C0F4CDC2CFF2DFB8CFC585BDC2C0DFB9C2F085BE85BF85C0BEFD85C1C1DFCDCCD2F7B7CDDFC185C2DFC485C385C4B7F1B0C9B6D6B7D485C5BAACCCFDBFD4CBB1C6F485C6D6A8DFC585C7CEE2B3B385C885C9CEFCB4B585CACEC7BAF085CBCEE185CCD1BD85CD85CEDFC085CF85D0B4F485D1B3CA85D2B8E6DFBB85D385D485D585D6C4C585D7DFBCDFBDDFBEC5BBDFBFDFC2D4B1DFC385D8C7BACED885D985DA85DB85DC85DDC4D885DEDFCA85DFDFCF85E0D6DC85E185E285E385E485E585E685E785E8DFC9DFDACEB685E9BAC7DFCEDFC8C5DE85EA85EBC9EBBAF4C3FC85EC85EDBED785EEDFC685EFDFCD85F0C5D885F185F285F385F4D5A6BACD85F5BECCD3BDB8C085F6D6E485F7DFC7B9BEBFA785F885F9C1FCDFCBDFCC85FADFD085FB85FC85FD85FE8640DFDBDFE58641DFD7DFD6D7C9DFE3DFE4E5EBD2A7DFD28642BFA98643D4DB8644BFC8DFD4864586468647CFCC86488649DFDD864AD1CA864BDFDEB0A7C6B7DFD3864CBAE5864DB6DFCDDBB9FED4D5864E864FDFDFCFECB0A5DFE7DFD1D1C6DFD5DFD8DFD9DFDC8650BBA98651DFE0DFE18652DFE2DFE6DFE8D3B486538654865586568657B8E7C5B6DFEAC9DAC1A8C4C486588659BFDECFF8865A865B865CD5DCDFEE865D865E865F866086618662B2B88663BADFDFEC8664DBC18665D1E48666866786688669CBF4B4BD866AB0A6866B866C866D866E866FDFF1CCC6DFF286708671DFED867286738674867586768677DFE986788679867A867BDFEB867CDFEFDFF0BBBD867D867EDFF386808681DFF48682BBA38683CADBCEA8E0A7B3AA8684E0A6868586868687E0A186888689868A868BDFFE868CCDD9DFFC868DDFFA868EBFD0D7C4868FC9CC86908691DFF8B0A186928693869486958696DFFD869786988699869ADFFBE0A2869B869C869D869E869FE0A886A086A186A286A3B7C886A486A5C6A1C9B6C0B2DFF586A686A7C5BE86A8D8C4DFF9C4F686A986AA86AB86AC86AD86AEE0A3E0A4E0A5D0A586AF86B0E0B4CCE486B1E0B186B2BFA6E0AFCEB9E0ABC9C686B386B4C0AEE0AEBAEDBAB0E0A986B586B686B7DFF686B8E0B386B986BAE0B886BB86BC86BDB4ADE0B986BE86BFCFB2BAC886C0E0B086C186C286C386C486C586C686C7D0FA86C886C986CA86CB86CC86CD86CE86CF86D0E0AC86D1D4FB86D2DFF786D3C5E786D4E0AD86D5D3F786D6E0B6E0B786D786D886D986DA86DBE0C4D0E186DC86DD86DEE0BC86DF86E0E0C9E0CA86E186E286E3E0BEE0AAC9A4E0C186E4E0B286E586E686E786E886E9CAC8E0C386EAE0B586EBCECB86ECCBC3E0CDE0C6E0C286EDE0CB86EEE0BAE0BFE0C086EF86F0E0C586F186F2E0C7E0C886F3E0CC86F4E0BB86F586F686F786F886F9CBD4E0D586FAE0D6E0D286FB86FC86FD86FE87408741E0D0BCCE87428743E0D18744B8C2D8C587458746874787488749874A874B874CD0EA874D874EC2EF874F8750E0CFE0BD875187528753E0D4E0D387548755E0D78756875787588759E0DCE0D8875A875B875CD6F6B3B0875DD7EC875ECBBB875F8760E0DA8761CEFB876287638764BAD987658766876787688769876A876B876C876D876E876F8770E0E1E0DDD2AD87718772877387748775E0E287768777E0DBE0D9E0DF87788779E0E0877A877B877C877D877EE0DE8780E0E4878187828783C6F7D8ACD4EBE0E6CAC98784878587868787E0E587888789878A878BB8C1878C878D878E878FE0E7E0E887908791879287938794879587968797E0E9E0E387988799879A879B879C879D879EBABFCCE7879F87A087A1E0EA87A287A387A487A587A687A787A887A987AA87AB87AC87AD87AE87AF87B0CFF987B187B287B387B487B587B687B787B887B987BA87BBE0EB87BC87BD87BE87BF87C087C187C2C8C287C387C487C587C6BDC087C787C887C987CA87CB87CC87CD87CE87CF87D087D187D287D3C4D287D487D587D687D787D887D987DA87DB87DCE0EC87DD87DEE0ED87DF87E0C7F4CBC487E1E0EEBBD8D8B6D2F2E0EFCDC587E2B6DA87E387E487E587E687E787E8E0F187E9D4B087EA87EBC0A7B4D187EC87EDCEA7E0F087EE87EF87F0E0F2B9CC87F187F2B9FACDBCE0F387F387F487F5C6D4E0F487F6D4B287F7C8A6E0F6E0F587F887F987FA87FB87FC87FD87FE8840884188428843884488458846884788488849E0F7884A884BCDC1884C884D884ECAA5884F885088518852D4DADBD7DBD98853DBD8B9E7DBDCDBDDB5D888548855DBDA8856885788588859885ADBDBB3A1DBDF885B885CBBF8885DD6B7885EDBE0885F886088618862BEF988638864B7BB8865DBD0CCAEBFB2BBB5D7F8BFD38866886788688869886ABFE9886B886CBCE1CCB3DBDEB0D3CEEBB7D8D7B9C6C2886D886EC0A4886FCCB98870DBE7DBE1C6BADBE38871DBE88872C5F7887388748875DBEA88768877DBE9BFC088788879887ADBE6DBE5887B887C887D887E8880B4B9C0ACC2A2DBE2DBE48881888288838884D0CDDBED88858886888788888889C0DDDBF2888A888B888C888D888E888F8890B6E28891889288938894DBF3DBD2B9B8D4ABDBEC8895BFD1DBF08896DBD18897B5E68898DBEBBFE58899889A889BDBEE889CDBF1889D889E889FDBF988A088A188A288A388A488A588A688A788A8B9A1B0A388A988AA88AB88AC88AD88AE88AFC2F188B088B1B3C7DBEF88B288B3DBF888B4C6D2DBF488B588B6DBF5DBF7DBF688B788B8DBFE88B9D3F2B2BA88BA88BB88BCDBFD88BD88BE88BF88C088C188C288C388C4DCA488C5DBFB88C688C788C888C9DBFA88CA88CB88CCDBFCC5E0BBF988CD88CEDCA388CF88D0DCA588D1CCC388D288D388D4B6D1DDC088D588D688D7DCA188D8DCA288D988DA88DBC7B588DC88DD88DEB6E988DF88E088E1DCA788E288E388E488E5DCA688E6DCA9B1A488E788E8B5CC88E988EA88EB88EC88EDBFB088EE88EF88F088F188F2D1DF88F388F488F588F6B6C288F788F888F988FA88FB88FC88FD88FE894089418942894389448945DCA88946894789488949894A894B894CCBFAEBF3894D894E894FCBDC89508951CBFE895289538954CCC189558956895789588959C8FB895A895B895C895D895E895FDCAA89608961896289638964CCEEDCAB89658966896789688969896A896B896C896D896E896F897089718972897389748975DBD38976DCAFDCAC8977BEB38978CAFB8979897A897BDCAD897C897D897E89808981898289838984C9CAC4B989858986898789888989C7BDDCAE898A898B898CD4F6D0E6898D898E898F89908991899289938994C4ABB6D589958996899789988999899A899B899C899D899E899F89A089A189A289A389A489A589A6DBD489A789A889A989AAB1DA89AB89AC89ADDBD589AE89AF89B089B189B289B389B489B589B689B789B8DBD689B989BA89BBBABE89BC89BD89BE89BF89C089C189C289C389C489C589C689C789C889C9C8C089CA89CB89CC89CD89CE89CFCABFC8C989D0D7B389D1C9F989D289D3BFC789D489D5BAF889D689D7D2BC89D889D989DA89DB89DC89DD89DE89DFE2BA89E0B4A689E189E2B1B889E389E489E589E689E7B8B489E8CFC489E989EA89EB89ECD9E7CFA6CDE289ED89EED9EDB6E089EFD2B989F089F1B9BB89F289F389F489F5E2B9E2B789F6B4F389F7CCECCCABB7F289F8D8B2D1EBBABB89F9CAA789FA89FBCDB789FC89FDD2C4BFE4BCD0B6E189FEDEC58A408A418A428A43DEC6DBBC8A44D1D98A458A46C6E6C4CEB7EE8A47B7DC8A488A49BFFCD7E08A4AC6F58A4B8A4CB1BCDEC8BDB1CCD7DECA8A4DDEC98A4E8A4F8A508A518A52B5EC8A53C9DD8A548A55B0C28A568A578A588A598A5A8A5B8A5C8A5D8A5E8A5F8A608A618A62C5AEC5AB8A63C4CC8A64BCE9CBFD8A658A668A67BAC38A688A698A6AE5F9C8E7E5FACDFD8A6BD7B1B8BEC2E88A6CC8D18A6D8A6EE5FB8A6F8A708A718A72B6CABCCB8A738A74D1FDE6A18A75C3EE8A768A778A788A79E6A48A7A8A7B8A7C8A7DE5FEE6A5CDD78A7E8A80B7C1E5FCE5FDE6A38A818A82C4DDE6A88A838A84E6A78A858A868A878A888A898A8AC3C38A8BC6DE8A8C8A8DE6AA8A8E8A8F8A908A918A928A938A94C4B78A958A968A97E6A2CABC8A988A998A9A8A9BBDE3B9C3E6A6D0D5CEAF8A9C8A9DE6A9E6B08A9ED2A68A9FBDAAE6AD8AA08AA18AA28AA38AA4E6AF8AA5C0D18AA68AA7D2CC8AA88AA98AAABCA78AAB8AAC8AAD8AAE8AAF8AB08AB18AB28AB38AB48AB58AB6E6B18AB7D2F68AB88AB98ABAD7CB8ABBCDFE8ABCCDDEC2A6E6ABE6ACBDBFE6AEE6B38ABD8ABEE6B28ABF8AC08AC18AC2E6B68AC3E6B88AC48AC58AC68AC7C4EF8AC88AC98ACAC4C88ACB8ACCBEEAC9EF8ACD8ACEE6B78ACFB6F08AD08AD18AD2C3E48AD38AD48AD58AD68AD78AD88AD9D3E9E6B48ADAE6B58ADBC8A28ADC8ADD8ADE8ADF8AE0E6BD8AE18AE28AE3E6B98AE48AE58AE68AE78AE8C6C58AE98AEACDF1E6BB8AEB8AEC8AED8AEE8AEF8AF08AF18AF28AF38AF4E6BC8AF58AF68AF78AF8BBE98AF98AFA8AFB8AFC8AFD8AFE8B40E6BE8B418B428B438B44E6BA8B458B46C0B78B478B488B498B4A8B4B8B4C8B4D8B4E8B4FD3A4E6BFC9F4E6C38B508B51E6C48B528B538B548B55D0F68B568B578B588B598B5A8B5B8B5C8B5D8B5E8B5F8B608B618B628B638B648B658B668B67C3BD8B688B698B6A8B6B8B6C8B6D8B6EC3C4E6C28B6F8B708B718B728B738B748B758B768B778B788B798B7A8B7B8B7CE6C18B7D8B7E8B808B818B828B838B84E6C7CFB18B85EBF48B868B87E6CA8B888B898B8A8B8B8B8CE6C58B8D8B8EBCDEC9A98B8F8B908B918B928B938B94BCB58B958B96CFD38B978B988B998B9A8B9BE6C88B9CE6C98B9DE6CE8B9EE6D08B9F8BA08BA1E6D18BA28BA38BA4E6CBB5D58BA5E6CC8BA68BA7E6CF8BA88BA9C4DB8BAAE6C68BAB8BAC8BAD8BAE8BAFE6CD8BB08BB18BB28BB38BB48BB58BB68BB78BB88BB98BBA8BBB8BBC8BBD8BBE8BBF8BC08BC18BC28BC38BC48BC58BC6E6D28BC78BC88BC98BCA8BCB8BCC8BCD8BCE8BCF8BD08BD18BD2E6D4E6D38BD38BD48BD58BD68BD78BD88BD98BDA8BDB8BDC8BDD8BDE8BDF8BE08BE18BE28BE38BE48BE58BE68BE78BE88BE98BEA8BEB8BECE6D58BEDD9F88BEE8BEFE6D68BF08BF18BF28BF38BF48BF58BF68BF7E6D78BF88BF98BFA8BFB8BFC8BFD8BFE8C408C418C428C438C448C458C468C47D7D3E6DD8C48E6DEBFD7D4D08C49D7D6B4E6CBEFE6DAD8C3D7CED0A28C4AC3CF8C4B8C4CE6DFBCBEB9C2E6DBD1A78C4D8C4EBAA2C2CF8C4FD8AB8C508C518C52CAEBE5EE8C53E6DC8C54B7F58C558C568C578C58C8E68C598C5AC4F58C5B8C5CE5B2C4FE8C5DCBFCE5B3D5AC8C5ED3EECAD8B0B28C5FCBCECDEA8C608C61BAEA8C628C638C64E5B58C65E5B48C66D7DAB9D9D6E6B6A8CDF0D2CBB1A6CAB58C67B3E8C9F3BFCDD0FBCAD2E5B6BBC28C688C698C6ACFDCB9AC8C6B8C6C8C6D8C6ED4D78C6F8C70BAA6D1E7CFFCBCD28C71E5B7C8DD8C728C738C74BFEDB1F6CBDE8C758C76BCC58C77BCC4D2FAC3DCBFDC8C788C798C7A8C7BB8BB8C7C8C7D8C7EC3C28C80BAAED4A28C818C828C838C848C858C868C878C888C89C7DEC4AFB2EC8C8AB9D18C8B8C8CE5BBC1C88C8D8C8ED5AF8C8F8C908C918C928C93E5BC8C94E5BE8C958C968C978C988C998C9A8C9BB4E7B6D4CBC2D1B0B5BC8C9C8C9DCAD98C9EB7E28C9F8CA0C9E48CA1BDAB8CA28CA3CEBED7F08CA48CA58CA68CA7D0A18CA8C9D98CA98CAAB6FBE6D8BCE28CABB3BE8CACC9D08CADE6D9B3A28CAE8CAF8CB08CB1DECC8CB2D3C8DECD8CB3D2A28CB48CB58CB68CB7DECE8CB88CB98CBA8CBBBECD8CBC8CBDDECF8CBE8CBF8CC0CAACD2FCB3DFE5EAC4E1BEA1CEB2C4F2BED6C6A8B2E38CC18CC2BED38CC38CC4C7FCCCEBBDECCEDD8CC58CC6CABAC6C1E5ECD0BC8CC78CC88CC9D5B98CCA8CCB8CCCE5ED8CCD8CCE8CCF8CD0CAF48CD1CDC0C2C58CD2E5EF8CD3C2C4E5F08CD48CD58CD68CD78CD88CD98CDAE5F8CDCD8CDBC9BD8CDC8CDD8CDE8CDF8CE08CE18CE2D2D9E1A88CE38CE48CE58CE6D3EC8CE7CBEAC6F18CE88CE98CEA8CEB8CECE1AC8CED8CEE8CEFE1A7E1A98CF08CF1E1AAE1AF8CF28CF3B2ED8CF4E1ABB8DAE1ADE1AEE1B0B5BAE1B18CF58CF68CF78CF88CF9E1B3E1B88CFA8CFB8CFC8CFD8CFED1D28D40E1B6E1B5C1EB8D418D428D43E1B78D44D4C08D45E1B28D46E1BAB0B68D478D488D498D4AE1B48D4BBFF98D4CE1B98D4D8D4EE1BB8D4F8D508D518D528D538D54E1BE8D558D568D578D588D598D5AE1BC8D5B8D5C8D5D8D5E8D5F8D60D6C58D618D628D638D648D658D668D67CFBF8D688D69E1BDE1BFC2CD8D6AB6EB8D6BD3F88D6C8D6DC7CD8D6E8D6FB7E58D708D718D728D738D748D758D768D778D788D79BEFE8D7A8D7B8D7C8D7D8D7E8D80E1C0E1C18D818D82E1C7B3E78D838D848D858D868D878D88C6E98D898D8A8D8B8D8C8D8DB4DE8D8ED1C28D8F8D908D918D92E1C88D938D94E1C68D958D968D978D988D99E1C58D9AE1C3E1C28D9BB1C08D9C8D9D8D9ED5B8E1C48D9F8DA08DA18DA28DA3E1CB8DA48DA58DA68DA78DA88DA98DAA8DABE1CCE1CA8DAC8DAD8DAE8DAF8DB08DB18DB28DB3EFFA8DB48DB5E1D3E1D2C7B68DB68DB78DB88DB98DBA8DBB8DBC8DBD8DBE8DBF8DC0E1C98DC18DC2E1CE8DC3E1D08DC48DC58DC68DC78DC88DC98DCA8DCB8DCC8DCD8DCEE1D48DCFE1D1E1CD8DD08DD1E1CF8DD28DD38DD48DD5E1D58DD68DD78DD88DD98DDA8DDB8DDC8DDD8DDE8DDF8DE08DE18DE2E1D68DE38DE48DE58DE68DE78DE88DE98DEA8DEB8DEC8DED8DEE8DEF8DF08DF18DF28DF38DF48DF58DF68DF78DF8E1D78DF98DFA8DFBE1D88DFC8DFD8DFE8E408E418E428E438E448E458E468E478E488E498E4A8E4B8E4C8E4D8E4E8E4F8E508E518E528E538E548E55E1DA8E568E578E588E598E5A8E5B8E5C8E5D8E5E8E5F8E608E618E62E1DB8E638E648E658E668E678E688E69CEA18E6A8E6B8E6C8E6D8E6E8E6F8E708E718E728E738E748E758E76E7DD8E77B4A8D6DD8E788E79D1B2B3B28E7A8E7BB9A4D7F3C7C9BEDEB9AE8E7CCED78E7D8E7EB2EEDBCF8E80BCBAD2D1CBC8B0CD8E818E82CFEF8E838E848E858E868E87D9E3BDED8E888E89B1D2CAD0B2BC8E8ACBA7B7AB8E8BCAA68E8C8E8D8E8ECFA38E8F8E90E0F8D5CAE0FB8E918E92E0FAC5C1CCFB8E93C1B1E0F9D6E3B2AFD6C4B5DB8E948E958E968E978E988E998E9A8E9BB4F8D6A18E9C8E9D8E9E8E9F8EA0CFAFB0EF8EA18EA2E0FC8EA38EA48EA58EA68EA7E1A1B3A38EA88EA9E0FDE0FEC3B18EAA8EAB8EAC8EADC3DD8EAEE1A2B7F98EAF8EB08EB18EB28EB38EB4BBCF8EB58EB68EB78EB88EB98EBA8EBBE1A3C4BB8EBC8EBD8EBE8EBF8EC0E1A48EC18EC2E1A58EC38EC4E1A6B4B18EC58EC68EC78EC88EC98ECA8ECB8ECC8ECD8ECE8ECF8ED08ED18ED28ED3B8C9C6BDC4EA8ED4B2A28ED5D0D28ED6E7DBBBC3D3D7D3C48ED7B9E3E2CF8ED88ED98EDAD7AF8EDBC7ECB1D38EDC8EDDB4B2E2D18EDE8EDF8EE0D0F2C2AEE2D08EE1BFE2D3A6B5D7E2D2B5EA8EE2C3EDB8FD8EE3B8AE8EE4C5D3B7CFE2D48EE58EE68EE78EE8E2D3B6C8D7F98EE98EEA8EEB8EEC8EEDCDA58EEE8EEF8EF08EF18EF2E2D88EF3E2D6CAFCBFB5D3B9E2D58EF48EF58EF68EF7E2D78EF88EF98EFA8EFB8EFC8EFD8EFE8F408F418F42C1AEC0C88F438F448F458F468F478F48E2DBE2DAC0AA8F498F4AC1CE8F4B8F4C8F4D8F4EE2DC8F4F8F508F518F528F538F548F558F568F578F588F598F5AE2DD8F5BE2DE8F5C8F5D8F5E8F5F8F608F618F628F638F64DBC88F65D1D3CDA28F668F67BDA88F688F698F6ADEC3D8A5BFAADBCDD2ECC6FAC5AA8F6B8F6C8F6DDEC48F6EB1D7DFAE8F6F8F708F71CABD8F72DFB18F73B9AD8F74D2FD8F75B8A5BAEB8F768F77B3DA8F788F798F7AB5DCD5C58F7B8F7C8F7D8F7EC3D6CFD2BBA18F80E5F3E5F28F818F82E5F48F83CDE48F84C8F58F858F868F878F888F898F8A8F8BB5AFC7BF8F8CE5F68F8D8F8E8F8FECB08F908F918F928F938F948F958F968F978F988F998F9A8F9B8F9C8F9D8F9EE5E68F9FB9E9B5B18FA0C2BCE5E8E5E7E5E98FA18FA28FA38FA4D2CD8FA58FA68FA7E1EAD0CE8FA8CDAE8FA9D1E58FAA8FABB2CAB1EB8FACB1F2C5ED8FAD8FAED5C3D3B08FAFE1DC8FB08FB18FB2E1DD8FB3D2DB8FB4B3B9B1CB8FB58FB68FB7CDF9D5F7E1DE8FB8BEB6B4FD8FB9E1DFBADCE1E0BBB2C2C9E1E18FBA8FBB8FBCD0EC8FBDCDBD8FBE8FBFE1E28FC0B5C3C5C7E1E38FC18FC2E1E48FC38FC48FC58FC6D3F98FC78FC88FC98FCA8FCB8FCCE1E58FCDD1AD8FCE8FCFE1E6CEA28FD08FD18FD28FD38FD48FD5E1E78FD6B5C28FD78FD88FD98FDAE1E8BBD58FDB8FDC8FDD8FDE8FDFD0C4E2E0B1D8D2E48FE08FE1E2E18FE28FE3BCC9C8CC8FE4E2E3ECFEECFDDFAF8FE58FE68FE7E2E2D6BECDFCC3A68FE88FE98FEAE3C38FEB8FECD6D2E2E78FED8FEEE2E88FEF8FF0D3C78FF18FF2E2ECBFEC8FF3E2EDE2E58FF48FF5B3C08FF68FF78FF8C4EE8FF98FFAE2EE8FFB8FFCD0C38FFDBAF6E2E9B7DEBBB3CCACCBCBE2E4E2E6E2EAE2EB8FFE90409041E2F790429043E2F4D4F5E2F390449045C5AD9046D5FAC5C2B2C090479048E2EF9049E2F2C1AFCBBC904A904BB5A1E2F9904C904D904EBCB1E2F1D0D4D4B9E2F5B9D6E2F6904F90509051C7D390529053905490559056E2F0905790589059905A905BD7DCEDA1905C905DE2F8905EEDA5E2FECAD1905F906090619062906390649065C1B59066BBD090679068BFD69069BAE3906A906BCBA1906C906D906EEDA6EDA3906F9070EDA29071907290739074BBD6EDA7D0F490759076EDA4BADEB6F7E3A1B6B2CCF1B9A79077CFA2C7A190789079BFD2907A907BB6F1907CE2FAE2FBE2FDE2FCC4D5E3A2907DD3C1907E90809081E3A7C7C49082908390849085CFA490869087E3A9BAB790889089908A908BE3A8908CBBDA908DE3A3908E908F9090E3A4E3AA9091E3A69092CEF2D3C690939094BBBC90959096D4C39097C4FA90989099EDA8D0FCE3A5909AC3F5909BE3ADB1AF909CE3B2909D909E909FBCC290A090A1E3ACB5BF90A290A390A490A590A690A790A890A9C7E9E3B090AA90AB90ACBEAACDEF90AD90AE90AF90B090B1BBF390B290B390B4CCE890B590B6E3AF90B7E3B190B8CFA7E3AE90B9CEA9BBDD90BA90BB90BC90BD90BEB5EBBEE5B2D2B3CD90BFB1B9E3ABB2D1B5ACB9DFB6E890C090C1CFEBE3B790C2BBCC90C390C4C8C7D0CA90C590C690C790C890C9E3B8B3EE90CA90CB90CC90CDEDA990CED3FAD3E490CF90D090D1EDAAE3B9D2E290D290D390D490D590D6E3B590D790D890D990DAD3DE90DB90DC90DD90DEB8D0E3B390DF90E0E3B6B7DF90E1E3B4C0A290E290E390E4E3BA90E590E690E790E890E990EA90EB90EC90ED90EE90EF90F090F190F290F390F490F590F690F7D4B890F890F990FA90FB90FC90FD90FE9140B4C89141E3BB9142BBC59143C9F791449145C9E5914691479148C4BD9149914A914B914C914D914E914FEDAB9150915191529153C2FD9154915591569157BBDBBFAE91589159915A915B915C915D915ECEBF915F916091619162E3BC9163BFB6916491659166916791689169916A916B916C916D916E916F9170917191729173917491759176B1EF91779178D4F79179917A917B917C917DE3BE917E9180918191829183918491859186EDAD918791889189918A918B918C918D918E918FE3BFBAA9EDAC91909191E3BD91929193919491959196919791989199919A919BE3C0919C919D919E919F91A091A1BAB691A291A391A4B6AE91A591A691A791A891A9D0B891AAB0C3EDAE91AB91AC91AD91AE91AFEDAFC0C191B0E3C191B191B291B391B491B591B691B791B891B991BA91BB91BC91BD91BE91BF91C091C1C5B391C291C391C491C591C691C791C891C991CA91CB91CC91CD91CE91CFE3C291D091D191D291D391D491D591D691D791D8DCB291D991DA91DB91DC91DD91DEEDB091DFB8EA91E0CEECEAA7D0E7CAF9C8D6CFB7B3C9CED2BDE491E191E2E3DEBBF2EAA8D5BD91E3C6DDEAA991E491E591E6EAAA91E7EAACEAAB91E8EAAEEAAD91E991EA91EB91ECBDD891EDEAAF91EEC2BE91EF91F091F191F2B4C1B4F791F391F4BBA791F591F691F791F891F9ECE6ECE5B7BFCBF9B1E291FAECE791FB91FC91FDC9C8ECE8ECE991FECAD6DED0B2C5D4FA92409241C6CBB0C7B4F2C8D3924292439244CDD092459246BFB8924792489249924A924B924C924DBFDB924E924FC7A4D6B49250C0A9DED1C9A8D1EFC5A4B0E7B3B6C8C592519252B0E292539254B7F692559256C5FA92579258B6F39259D5D2B3D0BCBC925A925B925CB3AD925D925E925F9260BEF1B0D1926192629263926492659266D2D6CAE3D7A59267CDB6B6B6BFB9D5DB9268B8A7C5D79269926A926BDED2BFD9C2D5C7C0926CBBA4B1A8926D926EC5EA926F9270C5FBCCA79271927292739274B1A7927592769277B5D692789279927AC4A8927BDED3D1BAB3E9927CC3F2927D927EB7F79280D6F4B5A3B2F0C4B4C4E9C0ADDED49281B0E8C5C4C1E09282B9D59283BEDCCDD8B0CE9284CDCFDED6BED0D7BEDED5D5D0B0DD92859286C4E292879288C2A3BCF09289D3B5C0B9C5A1B2A6D4F1928A928BC0A8CAC3DED7D5FC928CB9B0928DC8ADCBA9928EDED9BFBD928F929092919292C6B4D7A7CAB0C4C39293B3D6B9D29294929592969297D6B8EAFCB0B492989299929A929BBFE6929C929DCCF4929E929F92A092A1CDDA92A292A392A4D6BFC2CE92A5CECECCA2D0AEC4D3B5B2DED8D5F5BCB7BBD392A692A7B0A492A8C5B2B4EC92A992AA92ABD5F192AC92ADEAFD92AE92AF92B092B192B292B3DEDACDA692B492B5CDEC92B692B792B892B9CEE6DEDC92BACDB1C0A692BB92BCD7BD92BDDEDBB0C6BAB4C9D3C4F3BEE892BE92BF92C092C1B2B692C292C392C492C592C692C792C892C9C0CCCBF092CABCF1BBBBB5B792CB92CC92CDC5F592CEDEE692CF92D092D1DEE3BEDD92D292D3DEDF92D492D592D692D7B4B7BDDD92D892D9DEE0C4ED92DA92DB92DC92DDCFC692DEB5E092DF92E092E192E2B6DECADAB5F4DEE592E3D5C692E4DEE1CCCDC6FE92E5C5C592E692E792E8D2B492E9BEF292EA92EB92EC92ED92EE92EF92F0C2D392F1CCBDB3B892F2BDD392F3BFD8CDC6D1DAB4EB92F4DEE4DEDDDEE792F5EAFE92F692F7C2B0DEE292F892F9D6C0B5A792FAB2F492FBDEE892FCDEF292FD92FE934093419342DEED9343DEF193449345C8E0934693479348D7E1DEEFC3E8CCE19349B2E5934A934B934CD2BE934D934E934F9350935193529353DEEE9354DEEBCED59355B4A79356935793589359935ABFABBEBE935B935CBDD2935D935E935F9360DEE99361D4AE9362DEDE9363DEEA9364936593669367C0BF9368DEECB2F3B8E9C2A79369936ABDC1936B936C936D936E936FDEF5DEF893709371B2ABB4A493729373B4EAC9A6937493759376937793789379DEF6CBD1937AB8E3937BDEF7DEFA937C937D937E9380DEF9938193829383CCC29384B0E1B4EE93859386938793889389938AE5BA938B938C938D938E938FD0AF93909391B2EB9392EBA19393DEF493949395C9E3DEF3B0DAD2A1B1F79396CCAF939793989399939A939B939C939DDEF0939ECBA4939F93A093A1D5AA93A293A393A493A593A6DEFB93A793A893A993AA93AB93AC93AD93AEB4DD93AFC4A693B093B193B2DEFD93B393B493B593B693B793B893B993BA93BB93BCC3FEC4A1DFA193BD93BE93BF93C093C193C293C3C1CC93C4DEFCBEEF93C5C6B293C693C793C893C993CA93CB93CC93CD93CEB3C5C8F693CF93D0CBBADEFE93D193D2DFA493D393D493D593D6D7B293D793D893D993DA93DBB3B793DC93DD93DE93DFC1C393E093E1C7CBB2A5B4E993E2D7AB93E393E493E593E6C4EC93E7DFA2DFA393E8DFA593E9BAB393EA93EB93ECDFA693EDC0DE93EE93EFC9C393F093F193F293F393F493F593F6B2D9C7E693F7DFA793F8C7DC93F993FA93FB93FCDFA8EBA293FD93FE944094419442CBD3944394449445DFAA9446DFA99447B2C194489449944A944B944C944D944E944F9450945194529453945494559456945794589459945A945B945C945D945E945F9460C5CA94619462946394649465946694679468DFAB9469946A946B946C946D946E946F9470D4DC94719472947394749475C8C19476947794789479947A947B947C947D947E948094819482DFAC94839484948594869487BEF094889489DFADD6A7948A948B948C948DEAB7EBB6CAD5948ED8FCB8C4948FB9A594909491B7C5D5FE94929493949494959496B9CA94979498D0A7F4CD9499949AB5D0949B949CC3F4949DBEC8949E949F94A0EBB7B0BD94A194A2BDCC94A3C1B294A4B1D6B3A894A594A694A7B8D2C9A294A894A9B6D894AA94AB94AC94ADEBB8BEB494AE94AF94B0CAFD94B1C7C394B2D5FB94B394B4B7F394B594B694B794B894B994BA94BB94BC94BD94BE94BF94C094C194C294C3CEC494C494C594C6D5ABB1F394C794C894C9ECB3B0DF94CAECB594CB94CC94CDB6B794CEC1CF94CFF5FAD0B194D094D1D5E594D2CED394D394D4BDEFB3E294D5B8AB94D6D5B694D7EDBD94D8B6CF94D9CBB9D0C294DA94DB94DC94DD94DE94DF94E094E1B7BD94E294E3ECB6CAA994E494E594E6C5D494E7ECB9ECB8C2C3ECB794E894E994EA94EBD0FDECBA94ECECBBD7E594ED94EEECBC94EF94F094F1ECBDC6EC94F294F394F494F594F694F794F894F9CEDE94FABCC894FB94FCC8D5B5A9BEC9D6BCD4E794FD94FED1AED0F1EAB8EAB9EABABAB59540954195429543CAB1BFF595449545CDFA9546954795489549954AEAC0954BB0BAEABE954C954DC0A5954E954F9550EABB9551B2FD9552C3F7BBE8955395549555D2D7CEF4EABF955695579558EABC9559955A955BEAC3955CD0C7D3B3955D955E955F9560B4BA9561C3C1D7F29562956395649565D5D19566CAC79567EAC595689569EAC4EAC7EAC6956A956B956C956D956ED6E7956FCFD495709571EACB9572BBCE9573957495759576957795789579BDFAC9CE957A957BEACC957C957DC9B9CFFEEACAD4CEEACDEACF957E9580CDED9581958295839584EAC99585EACE95869587CEEE9588BBDE9589B3BF958A958B958C958D958EC6D5BEB0CEFA958F95909591C7E79592BEA7EAD095939594D6C7959595969597C1C095989599959AD4DD959BEAD1959C959DCFBE959E959F95A095A1EAD295A295A395A495A5CAEE95A695A795A895A9C5AFB0B595AA95AB95AC95AD95AEEAD495AF95B095B195B295B395B495B595B695B7EAD3F4DF95B895B995BA95BB95BCC4BA95BD95BE95BF95C095C1B1A995C295C395C495C5E5DF95C695C795C895C9EAD595CA95CB95CC95CD95CE95CF95D095D195D295D395D495D595D695D795D895D995DA95DB95DC95DD95DE95DF95E095E195E295E3CAEF95E4EAD6EAD7C6D895E595E695E795E895E995EA95EB95ECEAD895ED95EEEAD995EF95F095F195F295F395F4D4BB95F5C7FAD2B7B8FC95F695F7EAC295F8B2DC95F995FAC2FC95FBD4F8CCE6D7EE95FC95FD95FE9640964196429643D4C2D3D0EBC3C5F39644B7FE96459646EBD4964796489649CBB7EBDE964AC0CA964B964C964DCDFB964EB3AF964FC6DA965096519652965396549655EBFC9656C4BE9657CEB4C4A9B1BED4FD9658CAF59659D6EC965A965BC6D3B6E4965C965D965E965FBBFA96609661D0E096629663C9B19664D4D3C8A896659666B8CB9667E8BEC9BC96689669E8BB966AC0EED0D3B2C4B4E5966BE8BC966C966DD5C8966E966F967096719672B6C59673E8BDCAF8B8DCCCF5967496759676C0B496779678D1EEE8BFE8C29679967ABABC967BB1ADBDDC967CEABDE8C3967DE8C6967EE8CB9680968196829683E8CC9684CBC9B0E59685BCAB96869687B9B996889689E8C1968ACDF7968BE8CA968C968D968E968FCEF69690969196929693D5ED9694C1D6E8C49695C3B69696B9FBD6A6E8C8969796989699CAE0D4E6969AE8C0969BE8C5E8C7969CC7B9B7E3969DE8C9969EBFDDE8D2969F96A0E8D796A1E8D5BCDCBCCFE8DB96A296A396A496A596A696A796A896A9E8DE96AAE8DAB1FA96AB96AC96AD96AE96AF96B096B196B296B396B4B0D8C4B3B8CCC6E2C8BEC8E196B596B696B7E8CFE8D4E8D696B8B9F1E8D8D7F596B9C4FB96BAE8DC96BB96BCB2E996BD96BE96BFE8D196C096C1BCED96C296C3BFC2E8CDD6F996C4C1F8B2F196C596C696C796C896C996CA96CB96CCE8DF96CDCAC1E8D996CE96CF96D096D1D5A496D2B1EAD5BBE8CEE8D0B6B0E8D396D3E8DDC0B896D4CAF796D5CBA896D696D7C6DCC0F596D896D996DA96DB96DCE8E996DD96DE96DFD0A396E096E196E296E396E496E596E6E8F2D6EA96E796E896E996EA96EB96EC96EDE8E0E8E196EE96EF96F0D1F9BACBB8F996F196F2B8F1D4D4E8EF96F3E8EEE8ECB9F0CCD2E8E6CEA6BFF296F4B0B8E8F1E8F096F5D7C096F6E8E496F7CDA9C9A396F8BBB8BDDBE8EA96F996FA96FB96FC96FD96FE9740974197429743E8E2E8E3E8E5B5B5E8E7C7C5E8EBE8EDBDB0D7AE9744E8F897459746974797489749974A974B974CE8F5974DCDB0E8F6974E974F9750975197529753975497559756C1BA9757E8E89758C3B7B0F09759975A975B975C975D975E975F9760E8F4976197629763E8F7976497659766B9A3976797689769976A976B976C976D976E976F9770C9D2977197729773C3CECEE0C0E69774977597769777CBF39778CCDDD0B59779977ACAE1977BE8F3977C977D977E9780978197829783978497859786BCEC9787E8F997889789978A978B978C978DC3DE978EC6E5978FB9F79790979197929793B0F497949795D7D897969797BCAC9798C5EF9799979A979B979C979DCCC4979E979FE9A697A097A197A297A397A497A597A697A797A897A9C9AD97AAE9A2C0E297AB97AC97ADBFC397AE97AF97B0E8FEB9D797B1E8FB97B297B397B497B5E9A497B697B797B8D2CE97B997BA97BB97BC97BDE9A397BED6B2D7B597BFE9A797C0BDB797C197C297C397C497C597C697C797C897C997CA97CB97CCE8FCE8FD97CD97CE97CFE9A197D097D197D297D397D497D597D697D7CDD697D897D9D2AC97DA97DB97DCE9B297DD97DE97DF97E0E9A997E197E297E3B4AA97E4B4BB97E597E6E9AB97E797E897E997EA97EB97EC97ED97EE97EF97F097F197F297F397F497F597F697F7D0A897F897F9E9A597FA97FBB3FE97FC97FDE9ACC0E397FEE9AA98409841E9B998429843E9B89844984598469847E9AE98489849E8FA984A984BE9A8984C984D984E984F9850BFACE9B1E9BA98519852C2A5985398549855E9AF9856B8C59857E9AD9858D3DCE9B4E9B5E9B79859985A985BE9C7985C985D985E985F98609861C0C6E9C598629863E9B098649865E9BBB0F19866986798689869986A986B986C986D986E986FE9BCD5A598709871E9BE9872E9BF987398749875E9C198769877C1F198789879C8B6987A987B987CE9BD987D987E988098819882E9C29883988498859886988798889889988AE9C3988BE9B3988CE9B6988DBBB1988E988F9890E9C0989198929893989498959896BCF7989798989899E9C4E9C6989A989B989C989D989E989F98A098A198A298A398A498A5E9CA98A698A798A898A9E9CE98AA98AB98AC98AD98AE98AF98B098B198B298B3B2DB98B4E9C898B598B698B798B898B998BA98BB98BC98BD98BEB7AE98BF98C098C198C298C398C498C598C698C798C898C998CAE9CBE9CC98CB98CC98CD98CE98CF98D0D5C198D1C4A398D298D398D498D598D698D7E9D898D8BAE198D998DA98DB98DCE9C998DDD3A398DE98DF98E0E9D498E198E298E398E498E598E698E7E9D7E9D098E898E998EA98EB98ECE9CF98ED98EEC7C198EF98F098F198F298F398F498F598F6E9D298F798F898F998FA98FB98FC98FDE9D9B3C898FEE9D399409941994299439944CFF0994599469947E9CD99489949994A994B994C994D994E994F995099519952B3F79953995499559956995799589959E9D6995A995BE9DA995C995D995ECCB4995F99609961CFAD99629963996499659966996799689969996AE9D5996BE9DCE9DB996C996D996E996F9970E9DE99719972997399749975997699779978E9D19979997A997B997C997D997E99809981E9DD9982E9DFC3CA9983998499859986998799889989998A998B998C998D998E998F9990999199929993999499959996999799989999999A999B999C999D999E999F99A099A199A299A399A499A599A699A799A899A999AA99AB99AC99AD99AE99AF99B099B199B299B399B499B599B699B799B899B999BA99BB99BC99BD99BE99BF99C099C199C299C399C499C599C699C799C899C999CA99CB99CC99CD99CE99CF99D099D199D299D399D499D599D699D799D899D999DA99DB99DC99DD99DE99DF99E099E199E299E399E499E599E699E799E899E999EA99EB99EC99ED99EE99EF99F099F199F299F399F499F5C7B7B4CEBBB6D0C0ECA399F699F7C5B799F899F999FA99FB99FC99FD99FE9A409A419A42D3FB9A439A449A459A46ECA49A47ECA5C6DB9A489A499A4ABFEE9A4B9A4C9A4D9A4EECA69A4F9A50ECA7D0AA9A51C7B89A529A53B8E89A549A559A569A579A589A599A5A9A5B9A5C9A5D9A5E9A5FECA89A609A619A629A639A649A659A669A67D6B9D5FDB4CBB2BDCEE4C6E79A689A69CDE19A6A9A6B9A6C9A6D9A6E9A6F9A709A719A729A739A749A759A769A77B4F59A78CBC0BCDF9A799A7A9A7B9A7CE9E2E9E3D1EAE9E59A7DB4F9E9E49A7ED1B3CAE2B2D09A80E9E89A819A829A839A84E9E6E9E79A859A86D6B39A879A889A89E9E9E9EA9A8A9A8B9A8C9A8D9A8EE9EB9A8F9A909A919A929A939A949A959A96E9EC9A979A989A999A9A9A9B9A9C9A9D9A9EECAFC5B9B6CE9A9FD2F39AA09AA19AA29AA39AA49AA59AA6B5EE9AA7BBD9ECB19AA89AA9D2E39AAA9AAB9AAC9AAD9AAECEE39AAFC4B89AB0C3BF9AB19AB2B6BED8B9B1C8B1CFB1D1C5FE9AB3B1D09AB4C3AB9AB59AB69AB79AB89AB9D5B19ABA9ABB9ABC9ABD9ABE9ABF9AC09AC1EBA4BAC19AC29AC39AC4CCBA9AC59AC69AC7EBA59AC8EBA79AC99ACA9ACBEBA89ACC9ACD9ACEEBA69ACF9AD09AD19AD29AD39AD49AD5EBA9EBABEBAA9AD69AD79AD89AD99ADAEBAC9ADBCACFD8B5C3F19ADCC3A5C6F8EBADC4CA9ADDEBAEEBAFEBB0B7D59ADE9ADF9AE0B7FA9AE1EBB1C7E29AE2EBB39AE3BAA4D1F5B0B1EBB2EBB49AE49AE59AE6B5AAC2C8C7E89AE7EBB59AE8CBAEE3DF9AE99AEAD3C09AEB9AEC9AED9AEED9DB9AEF9AF0CDA1D6ADC7F39AF19AF29AF3D9E0BBE39AF4BABAE3E29AF59AF69AF79AF89AF9CFAB9AFA9AFB9AFCE3E0C9C79AFDBAB99AFE9B409B41D1B4E3E1C8EAB9AFBDADB3D8CEDB9B429B43CCC09B449B459B46E3E8E3E9CDF49B479B489B499B4A9B4BCCAD9B4CBCB39B4DE3EA9B4EE3EB9B4F9B50D0DA9B519B529B53C6FBB7DA9B549B55C7DFD2CACED69B56E3E4E3EC9B57C9F2B3C19B589B59E3E79B5A9B5BC6E3E3E59B5C9B5DEDB3E3E69B5E9B5F9B609B61C9B39B62C5E69B639B649B65B9B59B66C3BB9B67E3E3C5BDC1A4C2D9B2D79B68E3EDBBA6C4AD9B69E3F0BEDA9B6A9B6BE3FBE3F5BAD39B6C9B6D9B6E9B6FB7D0D3CD9B70D6CED5D3B9C1D5B4D1D89B719B729B739B74D0B9C7F69B759B769B77C8AAB2B49B78C3DA9B799B7A9B7BE3EE9B7C9B7DE3FCE3EFB7A8E3F7E3F49B7E9B809B81B7BA9B829B83C5A29B84E3F6C5DDB2A8C6FC9B85C4E09B869B87D7A29B88C0E1E3F99B899B8AE3FAE3FDCCA9E3F39B8BD3BE9B8CB1C3EDB4E3F1E3F29B8DE3F8D0BAC6C3D4F3E3FE9B8E9B8FBDE09B909B91E4A79B929B93E4A69B949B959B96D1F3E4A39B97E4A99B989B999B9AC8F79B9B9B9C9B9D9B9ECFB49B9FE4A8E4AEC2E59BA09BA1B6B49BA29BA39BA49BA59BA69BA7BDF29BA8E4A29BA99BAABAE9E4AA9BAB9BACE4AC9BAD9BAEB6FDD6DEE4B29BAFE4AD9BB09BB19BB2E4A19BB3BBEECDDDC7A2C5C99BB49BB5C1F79BB6E4A49BB7C7B3BDACBDBDE4A59BB8D7C7B2E29BB9E4ABBCC3E4AF9BBABBEBE4B0C5A8E4B19BBB9BBC9BBD9BBED5E3BFA39BBFE4BA9BC0E4B79BC1E4BB9BC29BC3E4BD9BC49BC5C6D69BC69BC7BAC6C0CB9BC89BC99BCAB8A1E4B49BCB9BCC9BCD9BCED4A19BCF9BD0BAA3BDFE9BD19BD29BD3E4BC9BD49BD59BD69BD79BD8CDBF9BD99BDAC4F99BDB9BDCCFFBC9E69BDD9BDED3BF9BDFCFD19BE09BE1E4B39BE2E4B8E4B9CCE99BE39BE49BE59BE69BE7CCCE9BE8C0D4E4B5C1B0E4B6CED09BE9BBC1B5D39BEAC8F3BDA7D5C7C9ACB8A2E4CA9BEB9BECE4CCD1C49BED9BEED2BA9BEF9BF0BAAD9BF19BF2BAD49BF39BF49BF59BF69BF79BF8E4C3B5ED9BF99BFA9BFBD7CDE4C0CFFDE4BF9BFC9BFD9BFEC1DCCCCA9C409C419C429C43CAE79C449C459C469C47C4D79C48CCD4E4C89C499C4A9C4BE4C7E4C19C4CE4C4B5AD9C4D9C4ED3D99C4FE4C69C509C519C529C53D2F9B4E39C54BBB49C559C56C9EE9C57B4BE9C589C599C5ABBEC9C5BD1CD9C5CCCEDEDB59C5D9C5E9C5F9C609C619C629C639C64C7E59C659C669C679C68D4A89C69E4CBD7D5E4C29C6ABDA5E4C59C6B9C6CD3E69C6DE4C9C9F89C6E9C6FE4BE9C709C71D3E59C729C73C7FEB6C99C74D4FCB2B3E4D79C759C769C77CEC29C78E4CD9C79CEBC9C7AB8DB9C7B9C7CE4D69C7DBFCA9C7E9C809C81D3CE9C82C3EC9C839C849C859C869C879C889C899C8AC5C8E4D89C8B9C8C9C8D9C8E9C8F9C909C919C92CDC4E4CF9C939C949C959C96E4D4E4D59C97BAFE9C98CFE69C999C9AD5BF9C9B9C9C9C9DE4D29C9E9C9F9CA09CA19CA29CA39CA49CA59CA69CA79CA8E4D09CA99CAAE4CE9CAB9CAC9CAD9CAE9CAF9CB09CB19CB29CB39CB49CB59CB69CB79CB89CB9CDE5CAAA9CBA9CBB9CBCC0A39CBDBDA6E4D39CBE9CBFB8C89CC09CC19CC29CC39CC4E4E7D4B49CC59CC69CC79CC89CC99CCA9CCBE4DB9CCC9CCD9CCEC1EF9CCF9CD0E4E99CD19CD2D2E79CD39CD4E4DF9CD5E4E09CD69CD7CFAA9CD89CD99CDA9CDBCBDD9CDCE4DAE4D19CDDE4E59CDEC8DCE4E39CDF9CE0C4E7E4E29CE1E4E19CE29CE39CE4B3FCE4E89CE59CE69CE79CE8B5E19CE99CEA9CEBD7CC9CEC9CED9CEEE4E69CEFBBAC9CF0D7D2CCCFEBF89CF1E4E49CF29CF3B9F69CF49CF59CF6D6CDE4D9E4DCC2FAE4DE9CF7C2CBC0C4C2D09CF8B1F5CCB29CF99CFA9CFB9CFC9CFD9CFE9D409D419D429D43B5CE9D449D459D469D47E4EF9D489D499D4A9D4B9D4C9D4D9D4E9D4FC6AF9D509D519D52C6E19D539D54E4F59D559D569D579D589D59C2A99D5A9D5B9D5CC0ECD1DDE4EE9D5D9D5E9D5F9D609D619D629D639D649D659D66C4AE9D679D689D69E4ED9D6A9D6B9D6C9D6DE4F6E4F4C2FE9D6EE4DD9D6FE4F09D70CAFE9D71D5C49D729D73E4F19D749D759D769D779D789D799D7AD1FA9D7B9D7C9D7D9D7E9D809D819D82E4EBE4EC9D839D849D85E4F29D86CEAB9D879D889D899D8A9D8B9D8C9D8D9D8E9D8F9D90C5CB9D919D929D93C7B19D94C2BA9D959D969D97E4EA9D989D999D9AC1CA9D9B9D9C9D9D9D9E9D9F9DA0CCB6B3B19DA19DA29DA3E4FB9DA4E4F39DA59DA69DA7E4FA9DA8E4FD9DA9E4FC9DAA9DAB9DAC9DAD9DAE9DAF9DB0B3CE9DB19DB29DB3B3BAE4F79DB49DB5E4F9E4F8C5EC9DB69DB79DB89DB99DBA9DBB9DBC9DBD9DBE9DBF9DC09DC19DC2C0BD9DC39DC49DC59DC6D4E89DC79DC89DC99DCA9DCBE5A29DCC9DCD9DCE9DCF9DD09DD19DD29DD39DD49DD59DD6B0C49DD79DD8E5A49DD99DDAE5A39DDB9DDC9DDD9DDE9DDF9DE0BCA49DE1E5A59DE29DE39DE49DE59DE69DE7E5A19DE89DE99DEA9DEB9DEC9DED9DEEE4FEB1F49DEF9DF09DF19DF29DF39DF49DF59DF69DF79DF89DF9E5A89DFAE5A9E5A69DFB9DFC9DFD9DFE9E409E419E429E439E449E459E469E47E5A7E5AA9E489E499E4A9E4B9E4C9E4D9E4E9E4F9E509E519E529E539E549E559E569E579E589E599E5A9E5B9E5C9E5D9E5E9E5F9E609E619E629E639E649E659E669E679E68C6D99E699E6A9E6B9E6C9E6D9E6E9E6F9E70E5ABE5AD9E719E729E739E749E759E769E77E5AC9E789E799E7A9E7B9E7C9E7D9E7E9E809E819E829E839E849E859E869E879E889E89E5AF9E8A9E8B9E8CE5AE9E8D9E8E9E8F9E909E919E929E939E949E959E969E979E989E999E9A9E9B9E9C9E9D9E9EB9E09E9F9EA0E5B09EA19EA29EA39EA49EA59EA69EA79EA89EA99EAA9EAB9EAC9EAD9EAEE5B19EAF9EB09EB19EB29EB39EB49EB59EB69EB79EB89EB99EBABBF0ECE1C3F09EBBB5C6BBD29EBC9EBD9EBE9EBFC1E9D4EE9EC0BEC49EC19EC29EC3D7C69EC4D4D6B2D3ECBE9EC59EC69EC79EC8EAC19EC99ECA9ECBC2AFB4B69ECC9ECD9ECED1D79ECF9ED09ED1B3B49ED2C8B2BFBBECC09ED39ED4D6CB9ED59ED6ECBFECC19ED79ED89ED99EDA9EDB9EDC9EDD9EDE9EDF9EE09EE19EE29EE3ECC5BEE6CCBFC5DABEBC9EE4ECC69EE5B1FE9EE69EE79EE8ECC4D5A8B5E39EE9ECC2C1B6B3E39EEA9EEBECC3CBB8C0C3CCFE9EEC9EED9EEE9EEFC1D29EF0ECC89EF19EF29EF39EF49EF59EF69EF79EF89EF99EFA9EFB9EFC9EFDBAE6C0D39EFED6F29F409F419F42D1CC9F439F449F459F46BFBE9F47B7B3C9D5ECC7BBE29F48CCCCBDFDC8C89F49CFA99F4A9F4B9F4C9F4D9F4E9F4F9F50CDE99F51C5EB9F529F539F54B7E99F559F569F579F589F599F5A9F5B9F5C9F5D9F5E9F5FD1C9BAB89F609F619F629F639F64ECC99F659F66ECCA9F67BBC0ECCB9F68ECE2B1BAB7D99F699F6A9F6B9F6C9F6D9F6E9F6F9F709F719F729F73BDB99F749F759F769F779F789F799F7A9F7BECCCD1E6ECCD9F7C9F7D9F7E9F80C8BB9F819F829F839F849F859F869F879F889F899F8A9F8B9F8C9F8D9F8EECD19F8F9F909F919F92ECD39F93BBCD9F94BCE59F959F969F979F989F999F9A9F9B9F9C9F9D9F9E9F9F9FA09FA1ECCF9FA2C9B79FA39FA49FA59FA69FA7C3BA9FA8ECE3D5D5ECD09FA99FAA9FAB9FAC9FADD6F39FAE9FAF9FB0ECD2ECCE9FB19FB29FB39FB4ECD49FB5ECD59FB69FB7C9BF9FB89FB99FBA9FBB9FBC9FBDCFA89FBE9FBF9FC09FC19FC2D0DC9FC39FC49FC59FC6D1AC9FC79FC89FC99FCAC8DB9FCB9FCC9FCDECD6CEF59FCE9FCF9FD09FD19FD2CAECECDA9FD39FD49FD59FD69FD79FD89FD9ECD99FDA9FDB9FDCB0BE9FDD9FDE9FDF9FE09FE19FE2ECD79FE3ECD89FE49FE59FE6ECE49FE79FE89FE99FEA9FEB9FEC9FED9FEE9FEFC8BC9FF09FF19FF29FF39FF49FF59FF69FF79FF89FF9C1C79FFA9FFB9FFC9FFD9FFEECDCD1E0A040A041A042A043A044A045A046A047A048A049ECDBA04AA04BA04CA04DD4EFA04EECDDA04FA050A051A052A053A054DBC6A055A056A057A058A059A05AA05BA05CA05DA05EECDEA05FA060A061A062A063A064A065A066A067A068A069A06AB1ACA06BA06CA06DA06EA06FA070A071A072A073A074A075A076A077A078A079A07AA07BA07CA07DA07EA080A081ECDFA082A083A084A085A086A087A088A089A08AA08BECE0A08CD7A6A08DC5C0A08EA08FA090EBBCB0AEA091A092A093BEF4B8B8D2AFB0D6B5F9A094D8B3A095CBACA096E3DDA097A098A099A09AA09BA09CA09DC6ACB0E6A09EA09FA0A0C5C6EBB9A0A1A0A2A0A3A0A4EBBAA0A5A0A6A0A7EBBBA0A8A0A9D1C0A0AAC5A3A0ABEAF2A0ACC4B2A0ADC4B5C0CEA0AEA0AFA0B0EAF3C4C1A0B1CEEFA0B2A0B3A0B4A0B5EAF0EAF4A0B6A0B7C9FCA0B8A0B9C7A3A0BAA0BBA0BCCCD8CEFEA0BDA0BEA0BFEAF5EAF6CFACC0E7A0C0A0C1EAF7A0C2A0C3A0C4A0C5A0C6B6BFEAF8A0C7EAF9A0C8EAFAA0C9A0CAEAFBA0CBA0CCA0CDA0CEA0CFA0D0A0D1A0D2A0D3A0D4A0D5A0D6EAF1A0D7A0D8A0D9A0DAA0DBA0DCA0DDA0DEA0DFA0E0A0E1A0E2C8AEE1EBA0E3B7B8E1ECA0E4A0E5A0E6E1EDA0E7D7B4E1EEE1EFD3CCA0E8A0E9A0EAA0EBA0ECA0EDA0EEE1F1BFF1E1F0B5D2A0EFA0F0A0F1B1B7A0F2A0F3A0F4A0F5E1F3E1F2A0F6BAFCA0F7E1F4A0F8A0F9A0FAA0FBB9B7A0FCBED1A0FDA0FEAA40AA41C4FCAA42BADDBDC6AA43AA44AA45AA46AA47AA48E1F5E1F7AA49AA4AB6C0CFC1CAA8E1F6D5F8D3FCE1F8E1FCE1F9AA4BAA4CE1FAC0EAAA4DE1FEE2A1C0C7AA4EAA4FAA50AA51E1FBAA52E1FDAA53AA54AA55AA56AA57AA58E2A5AA59AA5AAA5BC1D4AA5CAA5DAA5EAA5FE2A3AA60E2A8B2FEE2A2AA61AA62AA63C3CDB2C2E2A7E2A6AA64AA65E2A4E2A9AA66AA67E2ABAA68AA69AA6AD0C9D6EDC3A8E2ACAA6BCFD7AA6CAA6DE2AEAA6EAA6FBAEFAA70AA71E9E0E2ADE2AAAA72AA73AA74AA75BBABD4B3AA76AA77AA78AA79AA7AAA7BAA7CAA7DAA7EAA80AA81AA82AA83E2B0AA84AA85E2AFAA86E9E1AA87AA88AA89AA8AE2B1AA8BAA8CAA8DAA8EAA8FAA90AA91AA92E2B2AA93AA94AA95AA96AA97AA98AA99AA9AAA9BAA9CAA9DE2B3CCA1AA9EE2B4AA9FAAA0AB40AB41AB42AB43AB44AB45AB46AB47AB48AB49AB4AAB4BE2B5AB4CAB4DAB4EAB4FAB50D0FEAB51AB52C2CAAB53D3F1AB54CDF5AB55AB56E7E0AB57AB58E7E1AB59AB5AAB5BAB5CBEC1AB5DAB5EAB5FAB60C2EAAB61AB62AB63E7E4AB64AB65E7E3AB66AB67AB68AB69AB6AAB6BCDE6AB6CC3B5AB6DAB6EE7E2BBB7CFD6AB6FC1E1E7E9AB70AB71AB72E7E8AB73AB74E7F4B2A3AB75AB76AB77AB78E7EAAB79E7E6AB7AAB7BAB7CAB7DAB7EE7ECE7EBC9BAAB80AB81D5E4AB82E7E5B7A9E7E7AB83AB84AB85AB86AB87AB88AB89E7EEAB8AAB8BAB8CAB8DE7F3AB8ED6E9AB8FAB90AB91AB92E7EDAB93E7F2AB94E7F1AB95AB96AB97B0E0AB98AB99AB9AAB9BE7F5AB9CAB9DAB9EAB9FABA0AC40AC41AC42AC43AC44AC45AC46AC47AC48AC49AC4AC7F2AC4BC0C5C0EDAC4CAC4DC1F0E7F0AC4EAC4FAC50AC51E7F6CBF6AC52AC53AC54AC55AC56AC57AC58AC59AC5AE8A2E8A1AC5BAC5CAC5DAC5EAC5FAC60D7C1AC61AC62E7FAE7F9AC63E7FBAC64E7F7AC65E7FEAC66E7FDAC67E7FCAC68AC69C1D5C7D9C5FDC5C3AC6AAC6BAC6CAC6DAC6EC7EDAC6FAC70AC71AC72E8A3AC73AC74AC75AC76AC77AC78AC79AC7AAC7BAC7CAC7DAC7EAC80AC81AC82AC83AC84AC85AC86E8A6AC87E8A5AC88E8A7BAF7E7F8E8A4AC89C8F0C9AAAC8AAC8BAC8CAC8DAC8EAC8FAC90AC91AC92AC93AC94AC95AC96E8A9AC97AC98B9E5AC99AC9AAC9BAC9CAC9DD1FEE8A8AC9EAC9FACA0AD40AD41AD42E8AAAD43E8ADE8AEAD44C1A7AD45AD46AD47E8AFAD48AD49AD4AE8B0AD4BAD4CE8ACAD4DE8B4AD4EAD4FAD50AD51AD52AD53AD54AD55AD56AD57AD58E8ABAD59E8B1AD5AAD5BAD5CAD5DAD5EAD5FAD60AD61E8B5E8B2E8B3AD62AD63AD64AD65AD66AD67AD68AD69AD6AAD6BAD6CAD6DAD6EAD6FAD70AD71E8B7AD72AD73AD74AD75AD76AD77AD78AD79AD7AAD7BAD7CAD7DAD7EAD80AD81AD82AD83AD84AD85AD86AD87AD88AD89E8B6AD8AAD8BAD8CAD8DAD8EAD8FAD90AD91AD92B9CFAD93F0ACAD94F0ADAD95C6B0B0EAC8BFAD96CDDFAD97AD98AD99AD9AAD9BAD9CAD9DCECDEAB1AD9EAD9FADA0AE40EAB2AE41C6BFB4C9AE42AE43AE44AE45AE46AE47AE48EAB3AE49AE4AAE4BAE4CD5E7AE4DAE4EAE4FAE50AE51AE52AE53AE54DDF9AE55EAB4AE56EAB5AE57EAB6AE58AE59AE5AAE5BB8CADFB0C9F5AE5CCCF0AE5DAE5EC9FAAE5FAE60AE61AE62AE63C9FBAE64AE65D3C3CBA6AE66B8A6F0AEB1C2AE67E5B8CCEFD3C9BCD7C9EAAE68B5E7AE69C4D0B5E9AE6AEEAEBBADAE6BAE6CE7DEAE6DEEAFAE6EAE6FAE70AE71B3A9AE72AE73EEB2AE74AE75EEB1BDE7AE76EEB0CEB7AE77AE78AE79AE7AC5CFAE7BAE7CAE7DAE7EC1F4DBCEEEB3D0F3AE80AE81AE82AE83AE84AE85AE86AE87C2D4C6E8AE88AE89AE8AB7ACAE8BAE8CAE8DAE8EAE8FAE90AE91EEB4AE92B3EBAE93AE94AE95BBFBEEB5AE96AE97AE98AE99AE9AE7DCAE9BAE9CAE9DEEB6AE9EAE9FBDAEAEA0AF40AF41AF42F1E2AF43AF44AF45CAE8AF46D2C9F0DAAF47F0DBAF48F0DCC1C6AF49B8EDBECEAF4AAF4BF0DEAF4CC5B1F0DDD1F1AF4DF0E0B0CCBDEAAF4EAF4FAF50AF51AF52D2DFF0DFAF53B4AFB7E8F0E6F0E5C6A3F0E1F0E2B4C3AF54AF55F0E3D5EEAF56AF57CCDBBED2BCB2AF58AF59AF5AF0E8F0E7F0E4B2A1AF5BD6A2D3B8BEB7C8ACAF5CAF5DF0EAAF5EAF5FAF60AF61D1F7AF62D6CCBADBF0E9AF63B6BBAF64AF65CDB4AF66AF67C6A6AF68AF69AF6AC1A1F0EBF0EEAF6BF0EDF0F0F0ECAF6CBBBEF0EFAF6DAF6EAF6FAF70CCB5F0F2AF71AF72B3D5AF73AF74AF75AF76B1D4AF77AF78F0F3AF79AF7AF0F4F0F6B4E1AF7BF0F1AF7CF0F7AF7DAF7EAF80AF81F0FAAF82F0F8AF83AF84AF85F0F5AF86AF87AF88AF89F0FDAF8AF0F9F0FCF0FEAF8BF1A1AF8CAF8DAF8ECEC1F1A4AF8FF1A3AF90C1F6F0FBCADDAF91AF92B4F1B1F1CCB1AF93F1A6AF94AF95F1A7AF96AF97F1ACD5CEF1A9AF98AF99C8B3AF9AAF9BAF9CF1A2AF9DF1ABF1A8F1A5AF9EAF9FF1AAAFA0B040B041B042B043B044B045B046B0A9F1ADB047B048B049B04AB04BB04CF1AFB04DF1B1B04EB04FB050B051B052F1B0B053F1AEB054B055B056B057D1A2B058B059B05AB05BB05CB05DB05EF1B2B05FB060B061F1B3B062B063B064B065B066B067B068B069B9EFB06AB06BB5C7B06CB0D7B0D9B06DB06EB06FD4EDB070B5C4B071BDD4BBCAF0A7B072B073B8DEB074B075F0A8B076B077B0A8B078F0A9B079B07ACDEEB07BB07CF0AAB07DB07EB080B081B082B083B084B085B086B087F0ABB088B089B08AB08BB08CB08DB08EB08FB090C6A4B091B092D6E5F1E4B093F1E5B094B095B096B097B098B099B09AB09BB09CB09DC3F3B09EB09FD3DBB0A0B140D6D1C5E8B141D3AFB142D2E6B143B144EEC1B0BBD5B5D1CEBCE0BAD0B145BFF8B146B8C7B5C1C5CCB147B148CAA2B149B14AB14BC3CBB14CB14DB14EB14FB150EEC2B151B152B153B154B155B156B157B158C4BFB6A2B159EDECC3A4B15AD6B1B15BB15CB15DCFE0EDEFB15EB15FC5CEB160B6DCB161B162CAA1B163B164EDEDB165B166EDF0EDF1C3BCB167BFB4B168EDEEB169B16AB16BB16CB16DB16EB16FB170B171B172B173EDF4EDF2B174B175B176B177D5E6C3DFB178EDF3B179B17AB17BEDF6B17CD5A3D1A3B17DB17EB180EDF5B181C3D0B182B183B184B185B186EDF7BFF4BEECEDF8B187CCF7B188D1DBB189B18AB18BD7C5D5F6B18CEDFCB18DB18EB18FEDFBB190B191B192B193B194B195B196B197EDF9EDFAB198B199B19AB19BB19CB19DB19EB19FEDFDBEA6B1A0B240B241B242B243CBAFEEA1B6BDB244EEA2C4C0B245EDFEB246B247BDDEB2C7B248B249B24AB24BB24CB24DB24EB24FB250B251B252B253B6C3B254B255B256EEA5D8BAEEA3EEA6B257B258B259C3E9B3F2B25AB25BB25CB25DB25EB25FEEA7EEA4CFB9B260B261EEA8C2F7B262B263B264B265B266B267B268B269B26AB26BB26CB26DEEA9EEAAB26EDEABB26FB270C6B3B271C7C6B272D6F5B5C9B273CBB2B274B275B276EEABB277B278CDABB279EEACB27AB27BB27CB27DB27ED5B0B280EEADB281F6C4B282B283B284B285B286B287B288B289B28AB28BB28CB28DB28EDBC7B28FB290B291B292B293B294B295B296B297B4A3B298B299B29AC3ACF1E6B29BB29CB29DB29EB29FCAB8D2D3B2A0D6AAB340EFF2B341BED8B342BDC3EFF3B6CCB0ABB343B344B345B346CAAFB347B348EDB6B349EDB7B34AB34BB34CB34DCEF9B7AFBFF3EDB8C2EBC9B0B34EB34FB350B351B352B353EDB9B354B355C6F6BFB3B356B357B358EDBCC5F8B359D1D0B35AD7A9EDBAEDBBB35BD1E2B35CEDBFEDC0B35DEDC4B35EB35FB360EDC8B361EDC6EDCED5E8B362EDC9B363B364EDC7EDBEB365B366C5E9B367B368B369C6C6B36AB36BC9E9D4D2EDC1EDC2EDC3EDC5B36CC0F9B36DB4A1B36EB36FB370B371B9E8B372EDD0B373B374B375B376EDD1B377EDCAB378EDCFB379CEF8B37AB37BCBB6EDCCEDCDB37CB37DB37EB380B381CFF5B382B383B384B385B386B387B388B389B38AB38BB38CB38DEDD2C1F2D3B2EDCBC8B7B38EB38FB390B391B392B393B394B395BCEFB396B397B398B399C5F0B39AB39BB39CB39DB39EB39FB3A0B440B441B442EDD6B443B5EFB444B445C2B5B0ADCBE9B446B447B1AEB448EDD4B449B44AB44BCDEBB5E2B44CEDD5EDD3EDD7B44DB44EB5FAB44FEDD8B450EDD9B451EDDCB452B1CCB453B454B455B456B457B458B459B45AC5F6BCEEEDDACCBCB2EAB45BB45CB45DB45EEDDBB45FB460B461B462C4EBB463B464B4C5B465B466B467B0F5B468B469B46AEDDFC0DAB4E8B46BB46CB46DB46EC5CDB46FB470B471EDDDBFC4B472B473B474EDDEB475B476B477B478B479B47AB47BB47CB47DB47EB480B481B482B483C4A5B484B485B486EDE0B487B488B489B48AB48BEDE1B48CEDE3B48DB48EC1D7B48FB490BBC7B491B492B493B494B495B496BDB8B497B498B499EDE2B49AB49BB49CB49DB49EB49FB4A0B540B541B542B543B544B545EDE4B546B547B548B549B54AB54BB54CB54DB54EB54FEDE6B550B551B552B553B554EDE5B555B556B557B558B559B55AB55BB55CB55DB55EB55FB560B561B562B563EDE7B564B565B566B567B568CABEECEAC0F1B569C9E7B56AECEBC6EEB56BB56CB56DB56EECECB56FC6EDECEDB570B571B572B573B574B575B576B577B578ECF0B579B57AD7E6ECF3B57BB57CECF1ECEEECEFD7A3C9F1CBEEECF4B57DECF2B57EB580CFE9B581ECF6C6B1B582B583B584B585BCC0B586ECF5B587B588B589B58AB58BB58CB58DB5BBBBF6B58EECF7B58FB590B591B592B593D9F7BDFBB594B595C2BBECF8B596B597B598B599ECF9B59AB59BB59CB59DB8A3B59EB59FB5A0B640B641B642B643B644B645B646ECFAB647B648B649B64AB64BB64CB64DB64EB64FB650B651B652ECFBB653B654B655B656B657B658B659B65AB65BB65CB65DECFCB65EB65FB660B661B662D3EDD8AEC0EBB663C7DDBACCB664D0E3CBBDB665CDBAB666B667B8D1B668B669B1FCB66AC7EFB66BD6D6B66CB66DB66EBFC6C3EBB66FB670EFF5B671B672C3D8B673B674B675B676B677B678D7E2B679B67AB67BEFF7B3D3B67CC7D8D1EDB67DD6C8B67EEFF8B680EFF6B681BBFDB3C6B682B683B684B685B686B687B688BDD5B689B68AD2C6B68BBBE0B68CB68DCFA1B68EEFFCEFFBB68FB690EFF9B691B692B693B694B3CCB695C9D4CBB0B696B697B698B699B69AEFFEB69BB69CB0DEB69DB69ED6C9B69FB6A0B740EFFDB741B3EDB742B743F6D5B744B745B746B747B748B749B74AB74BB74CB74DB74EB74FB750B751B752CEC8B753B754B755F0A2B756F0A1B757B5BEBCDABBFCB758B8E5B759B75AB75BB75CB75DB75EC4C2B75FB760B761B762B763B764B765B766B767B768F0A3B769B76AB76BB76CB76DCBEBB76EB76FB770B771B772B773B774B775B776B777B778B779B77AB77BB77CB77DB77EB780B781B782B783B784B785B786F0A6B787B788B789D1A8B78ABEBFC7EEF1B6F1B7BFD5B78BB78CB78DB78EB4A9F1B8CDBBB78FC7D4D5ADB790F1B9B791F1BAB792B793B794B795C7CFB796B797B798D2A4D6CFB799B79AF1BBBDD1B4B0BEBDB79BB79CB79DB4DCCED1B79EBFDFF1BDB79FB7A0B840B841BFFAF1BCB842F1BFB843B844B845F1BEF1C0B846B847B848B849B84AF1C1B84BB84CB84DB84EB84FB850B851B852B853B854B855C1FEB856B857B858B859B85AB85BB85CB85DB85EB85FB860C1A2B861B862B863B864B865B866B867B868B869B86ACAFAB86BB86CD5BEB86DB86EB86FB870BEBABEB9D5C2B871B872BFA2B873CDAFF1B5B874B875B876B877B878B879BDDFB87AB6CBB87BB87CB87DB87EB880B881B882B883B884D6F1F3C3B885B886F3C4B887B8CDB888B889B88AF3C6F3C7B88BB0CAB88CF3C5B88DF3C9CBF1B88EB88FB890F3CBB891D0A6B892B893B1CAF3C8B894B895B896F3CFB897B5D1B898B899F3D7B89AF3D2B89BB89CB89DF3D4F3D3B7FBB89EB1BFB89FF3CEF3CAB5DAB8A0F3D0B940B941F3D1B942F3D5B943B944B945B946F3CDB947BCE3B948C1FDB949F3D6B94AB94BB94CB94DB94EB94FF3DAB950F3CCB951B5C8B952BDEEF3DCB953B954B7A4BFF0D6FECDB2B955B4F0B956B2DFB957F3D8B958F3D9C9B8B959F3DDB95AB95BF3DEB95CF3E1B95DB95EB95FB960B961B962B963B964B965B966B967F3DFB968B969F3E3F3E2B96AB96BF3DBB96CBFEAB96DB3EFB96EF3E0B96FB970C7A9B971BCF2B972B973B974B975F3EBB976B977B978B979B97AB97BB97CB9BFB97DB97EF3E4B980B981B982B2ADBBFEB983CBE3B984B985B986B987F3EDF3E9B988B989B98AB9DCF3EEB98BB98CB98DF3E5F3E6F3EAC2E1F3ECF3EFF3E8BCFDB98EB98FB990CFE4B991B992F3F0B993B994B995F3E7B996B997B998B999B99AB99BB99CB99DF3F2B99EB99FB9A0BA40D7ADC6AABA41BA42BA43BA44F3F3BA45BA46BA47BA48F3F1BA49C2A8BA4ABA4BBA4CBA4DBA4EB8DDF3F5BA4FBA50F3F4BA51BA52BA53B4DBBA54BA55BA56F3F6F3F7BA57BA58BA59F3F8BA5ABA5BBA5CC0BABA5DBA5EC0E9BA5FBA60BA61BA62BA63C5F1BA64BA65BA66BA67F3FBBA68F3FABA69BA6ABA6BBA6CBA6DBA6EBA6FBA70B4D8BA71BA72BA73F3FEF3F9BA74BA75F3FCBA76BA77BA78BA79BA7ABA7BF3FDBA7CBA7DBA7EBA80BA81BA82BA83BA84F4A1BA85BA86BA87BA88BA89BA8AF4A3BBC9BA8BBA8CF4A2BA8DBA8EBA8FBA90BA91BA92BA93BA94BA95BA96BA97BA98BA99F4A4BA9ABA9BBA9CBA9DBA9EBA9FB2BEF4A6F4A5BAA0BB40BB41BB42BB43BB44BB45BB46BB47BB48BB49BCAEBB4ABB4BBB4CBB4DBB4EBB4FBB50BB51BB52BB53BB54BB55BB56BB57BB58BB59BB5ABB5BBB5CBB5DBB5EBB5FBB60BB61BB62BB63BB64BB65BB66BB67BB68BB69BB6ABB6BBB6CBB6DBB6EC3D7D9E1BB6FBB70BB71BB72BB73BB74C0E0F4CCD7D1BB75BB76BB77BB78BB79BB7ABB7BBB7CBB7DBB7EBB80B7DBBB81BB82BB83BB84BB85BB86BB87F4CEC1A3BB88BB89C6C9BB8AB4D6D5B3BB8BBB8CBB8DF4D0F4CFF4D1CBDABB8EBB8FF4D2BB90D4C1D6E0BB91BB92BB93BB94B7E0BB95BB96BB97C1B8BB98BB99C1BBF4D3BEACBB9ABB9BBB9CBB9DBB9EB4E2BB9FBBA0F4D4F4D5BEABBC40BC41F4D6BC42BC43BC44F4DBBC45F4D7F4DABC46BAFDBC47F4D8F4D9BC48BC49BC4ABC4BBC4CBC4DBC4EB8E2CCC7F4DCBC4FB2DABC50BC51C3D3BC52BC53D4E3BFB7BC54BC55BC56BC57BC58BC59BC5AF4DDBC5BBC5CBC5DBC5EBC5FBC60C5B4BC61BC62BC63BC64BC65BC66BC67BC68F4E9BC69BC6ACFB5BC6BBC6CBC6DBC6EBC6FBC70BC71BC72BC73BC74BC75BC76BC77BC78CEC9BC79BC7ABC7BBC7CBC7DBC7EBC80BC81BC82BC83BC84BC85BC86BC87BC88BC89BC8ABC8BBC8CBC8DBC8ECBD8BC8FCBF7BC90BC91BC92BC93BDF4BC94BC95BC96D7CFBC97BC98BC99C0DBBC9ABC9BBC9CBC9DBC9EBC9FBCA0BD40BD41BD42BD43BD44BD45BD46BD47BD48BD49BD4ABD4BBD4CBD4DBD4EBD4FBD50BD51BD52BD53BD54BD55BD56BD57BD58BD59BD5ABD5BBD5CBD5DBD5EBD5FBD60BD61BD62BD63BD64BD65BD66BD67BD68BD69BD6ABD6BBD6CBD6DBD6EBD6FBD70BD71BD72BD73BD74BD75BD76D0F5BD77BD78BD79BD7ABD7BBD7CBD7DBD7EF4EABD80BD81BD82BD83BD84BD85BD86BD87BD88BD89BD8ABD8BBD8CBD8DBD8EBD8FBD90BD91BD92BD93BD94BD95BD96BD97BD98BD99BD9ABD9BBD9CBD9DBD9EBD9FBDA0BE40BE41BE42BE43BE44BE45BE46BE47BE48BE49BE4ABE4BBE4CF4EBBE4DBE4EBE4FBE50BE51BE52BE53F4ECBE54BE55BE56BE57BE58BE59BE5ABE5BBE5CBE5DBE5EBE5FBE60BE61BE62BE63BE64BE65BE66BE67BE68BE69BE6ABE6BBE6CBE6DBE6EBE6FBE70BE71BE72BE73BE74BE75BE76BE77BE78BE79BE7ABE7BBE7CBE7DBE7EBE80BE81BE82BE83BE84BE85BE86BE87BE88BE89BE8ABE8BBE8CBE8DBE8EBE8FBE90BE91BE92BE93BE94BE95BE96BE97BE98BE99BE9ABE9BBE9CBE9DBE9EBE9FBEA0BF40BF41BF42BF43BF44BF45BF46BF47BF48BF49BF4ABF4BBF4CBF4DBF4EBF4FBF50BF51BF52BF53BF54BF55BF56BF57BF58BF59BF5ABF5BBF5CBF5DBF5EBF5FBF60BF61BF62BF63BF64BF65BF66BF67BF68BF69BF6ABF6BBF6CBF6DBF6EBF6FBF70BF71BF72BF73BF74BF75BF76BF77BF78BF79BF7ABF7BBF7CBF7DBF7EBF80F7E3BF81BF82BF83BF84BF85B7B1BF86BF87BF88BF89BF8AF4EDBF8BBF8CBF8DBF8EBF8FBF90BF91BF92BF93BF94BF95BF96BF97BF98BF99BF9ABF9BBF9CBF9DBF9EBF9FBFA0C040C041C042C043C044C045C046C047C048C049C04AC04BC04CC04DC04EC04FC050C051C052C053C054C055C056C057C058C059C05AC05BC05CC05DC05EC05FC060C061C062C063D7EBC064C065C066C067C068C069C06AC06BC06CC06DC06EC06FC070C071C072C073C074C075C076C077C078C079C07AC07BF4EEC07CC07DC07EE6F9BEC0E6FABAECE6FBCFCBE6FCD4BCBCB6E6FDE6FEBCCDC8D2CEB3E7A1C080B4BFE7A2C9B4B8D9C4C9C081D7DDC2DAB7D7D6BDCEC6B7C4C082C083C5A6E7A3CFDFE7A4E7A5E7A6C1B7D7E9C9F0CFB8D6AFD6D5E7A7B0EDE7A8E7A9C9DCD2EFBEADE7AAB0F3C8DEBDE1E7ABC8C6C084E7ACBBE6B8F8D1A4E7ADC2E7BEF8BDCACDB3E7AEE7AFBEEED0E5C085CBE7CCD0BCCCE7B0BCA8D0F7E7B1C086D0F8E7B2E7B3B4C2E7B4E7B5C9FECEACC3E0E7B7B1C1B3F1C087E7B8E7B9D7DBD5C0E7BAC2CCD7BAE7BBE7BCE7BDBCEAC3E5C0C2E7BEE7BFBCA9C088E7C0E7C1E7B6B6D0E7C2C089E7C3E7C4BBBAB5DEC2C6B1E0E7C5D4B5E7C6B8BFE7C8E7C7B7ECC08AE7C9B2F8E7CAE7CBE7CCE7CDE7CEE7CFE7D0D3A7CBF5E7D1E7D2E7D3E7D4C9C9E7D5E7D6E7D7E7D8E7D9BDC9E7DAF3BEC08BB8D7C08CC8B1C08DC08EC08FC090C091C092C093F3BFC094F3C0F3C1C095C096C097C098C099C09AC09BC09CC09DC09EB9DECDF8C09FC0A0D8E8BAB1C140C2DEEEB7C141B7A3C142C143C144C145EEB9C146EEB8B0D5C147C148C149C14AC14BEEBBD5D6D7EFC14CC14DC14ED6C3C14FC150EEBDCAF0C151EEBCC152C153C154C155EEBEC156C157C158C159EEC0C15AC15BEEBFC15CC15DC15EC15FC160C161C162C163D1F2C164C7BCC165C3C0C166C167C168C169C16AB8E1C16BC16CC16DC16EC16FC1E7C170C171F4C6D0DFF4C7C172CFDBC173C174C8BAC175C176F4C8C177C178C179C17AC17BC17CC17DF4C9F4CAC17EF4CBC180C181C182C183C184D9FAB8FEC185C186E5F1D3F0C187F4E0C188CECCC189C18AC18BB3E1C18CC18DC18EC18FF1B4C190D2EEC191F4E1C192C193C194C195C196CFE8F4E2C197C198C7CCC199C19AC19BC19CC19DC19EB5D4B4E4F4E4C19FC1A0C240F4E3F4E5C241C242F4E6C243C244C245C246F4E7C247BAB2B0BFC248F4E8C249C24AC24BC24CC24DC24EC24FB7ADD2EDC250C251C252D2ABC0CFC253BFBCEBA3D5DFEAC8C254C255C256C257F1F3B6F8CBA3C258C259C4CDC25AF1E7C25BF1E8B8FBF1E9BAC4D4C5B0D2C25CC25DF1EAC25EC25FC260F1EBC261F1ECC262C263F1EDF1EEF1EFF1F1F1F0C5D5C264C265C266C267C268C269F1F2C26AB6FAC26BF1F4D2AEDEC7CBCAC26CC26DB3DCC26EB5A2C26FB9A2C270C271C4F4F1F5C272C273F1F6C274C275C276C1C4C1FBD6B0F1F7C277C278C279C27AF1F8C27BC1AAC27CC27DC27EC6B8C280BEDBC281C282C283C284C285C286C287C288C289C28AC28BC28CC28DC28EF1F9B4CFC28FC290C291C292C293C294F1FAC295C296C297C298C299C29AC29BC29CC29DC29EC29FC2A0C340EDB2EDB1C341C342CBE0D2DEC343CBC1D5D8C344C8E2C345C0DFBCA1C346C347C348C349C34AC34BEBC1C34CC34DD0A4C34ED6E2C34FB6C7B8D8EBC0B8CEC350EBBFB3A6B9C9D6ABC351B7F4B7CAC352C353C354BCE7B7BEEBC6C355EBC7B0B9BFCFC356EBC5D3FDC357EBC8C358C359EBC9C35AC35BB7CEC35CEBC2EBC4C9F6D6D7D5CDD0B2EBCFCEB8EBD0C35DB5A8C35EC35FC360C361C362B1B3EBD2CCA5C363C364C365C366C367C368C369C5D6EBD3C36AEBD1C5DFEBCECAA4EBD5B0FBC36BC36CBAFAC36DC36ED8B7F1E3C36FEBCAEBCBEBCCEBCDEBD6E6C0EBD9C370BFE8D2C8EBD7EBDCB8ECEBD8C371BDBAC372D0D8C373B0B7C374EBDDC4DCC375C376C377C378D6ACC379C37AC37BB4E0C37CC37DC2F6BCB9C37EC380EBDAEBDBD4E0C6EAC4D4EBDFC5A7D9F5C381B2B1C382EBE4C383BDC5C384C385C386EBE2C387C388C389C38AC38BC38CC38DC38EC38FC390C391C392C393EBE3C394C395B8ACC396CDD1EBE5C397C398C399EBE1C39AC1B3C39BC39CC39DC39EC39FC6A2C3A0C440C441C442C443C444C445CCF3C446EBE6C447C0B0D2B8EBE7C448C449C44AB8AFB8ADC44BEBE8C7BBCDF3C44CC44DC44EEBEAEBEBC44FC450C451C452C453EBEDC454C455C456C457D0C8C458EBF2C459EBEEC45AC45BC45CEBF1C8F9C45DD1FCEBECC45EC45FEBE9C460C461C462C463B8B9CFD9C4E5EBEFEBF0CCDACDC8B0F2C464EBF6C465C466C467C468C469EBF5C46AB2B2C46BC46CC46DC46EB8E0C46FEBF7C470C471C472C473C474C475B1ECC476C477CCC5C4A4CFA5C478C479C47AC47BC47CEBF9C47DC47EECA2C480C5F2C481EBFAC482C483C484C485C486C487C488C489C9C5C48AC48BC48CC48DC48EC48FE2DFEBFEC490C491C492C493CDCEECA1B1DBD3B7C494C495D2DCC496C497C498EBFDC499EBFBC49AC49BC49CC49DC49EC49FC4A0C540C541C542C543C544C545C546C547C548C549C54AC54BC54CC54DC54EB3BCC54FC550C551EAB0C552C553D7D4C554F4ABB3F4C555C556C557C558C559D6C1D6C2C55AC55BC55CC55DC55EC55FD5E9BECAC560F4A7C561D2A8F4A8F4A9C562F4AABECBD3DFC563C564C565C566C567C9E0C9E1C568C569F3C2C56ACAE6C56BCCF2C56CC56DC56EC56FC570C571E2B6CBB4C572CEE8D6DBC573F4ADF4AEF4AFC574C575C576C577F4B2C578BABDF4B3B0E3F4B0C579F4B1BDA2B2D5C57AF4B6F4B7B6E6B2B0CFCFF4B4B4ACC57BF4B5C57CC57DF4B8C57EC580C581C582C583F4B9C584C585CDA7C586F4BAC587F4BBC588C589C58AF4BCC58BC58CC58DC58EC58FC590C591C592CBD2C593F4BDC594C595C596C597F4BEC598C599C59AC59BC59CC59DC59EC59FF4BFC5A0C640C641C642C643F4DEC1BCBCE8C644C9ABD1DEE5F5C645C646C647C648DCB3D2D5C649C64ADCB4B0ACDCB5C64BC64CBDDAC64DDCB9C64EC64FC650D8C2C651DCB7D3F3C652C9D6DCBADCB6C653DCBBC3A2C654C655C656C657DCBCDCC5DCBDC658C659CEDFD6A5C65ADCCFC65BDCCDC65CC65DDCD2BDE6C2ABC65EDCB8DCCBDCCEDCBEB7D2B0C5DCC7D0BEDCC1BBA8C65FB7BCDCCCC660C661DCC6DCBFC7DBC662C663C664D1BFDCC0C665C666DCCAC667C668DCD0C669C66ACEADDCC2C66BDCC3DCC8DCC9B2D4DCD1CBD5C66CD4B7DCDBDCDFCCA6DCE6C66DC3E7DCDCC66EC66FBFC1DCD9C670B0FAB9B6DCE5DCD3C671DCC4DCD6C8F4BFE0C672C673C674C675C9BBC676C677C678B1BDC679D3A2C67AC67BDCDAC67CC67DDCD5C67EC6BBC680DCDEC681C682C683C684C685D7C2C3AFB7B6C7D1C3A9DCE2DCD8DCEBDCD4C686C687DCDDC688BEA5DCD7C689DCE0C68AC68BDCE3DCE4C68CDCF8C68DC68EDCE1DDA2DCE7C68FC690C691C692C693C694C695C696C697C698BCEBB4C4C699C69AC3A3B2E7DCFAC69BDCF2C69CDCEFC69DDCFCDCEED2F0B2E8C69EC8D7C8E3DCFBC69FDCEDC6A0C740C741DCF7C742C743DCF5C744C745BEA3DCF4C746B2DDC747C748C749C74AC74BDCF3BCF6DCE8BBC4C74CC0F3C74DC74EC74FC750C751BCD4DCE9DCEAC752DCF1DCF6DCF9B5B4C753C8D9BBE7DCFEDCFDD3ABDDA1DDA3DDA5D2F1DDA4DDA6DDA7D2A9C754C755C756C757C758C759C75ABAC9DDA9C75BC75CDDB6DDB1DDB4C75DC75EC75FC760C761C762C763DDB0C6CEC764C765C0F2C766C767C768C769C9AFC76AC76BC76CDCECDDAEC76DC76EC76FC770DDB7C771C772DCF0DDAFC773DDB8C774DDACC775C776C777C778C779C77AC77BDDB9DDB3DDADC4AAC77CC77DC77EC780DDA8C0B3C1ABDDAADDABC781DDB2BBF1DDB5D3A8DDBAC782DDBBC3A7C783C784DDD2DDBCC785C786C787DDD1C788B9BDC789C78ABED5C78BBEFAC78CC78DBACAC78EC78FC790C791DDCAC792DDC5C793DDBFC794C795C796B2CBDDC3C797DDCBB2A4DDD5C798C799C79ADDBEC79BC79CC79DC6D0DDD0C79EC79FC7A0C840C841DDD4C1E2B7C6C842C843C844C845C846DDCEDDCFC847C848C849DDC4C84AC84BC84CDDBDC84DDDCDCCD1C84EDDC9C84FC850C851C852DDC2C3C8C6BCCEAEDDCCC853DDC8C854C855C856C857C858C859DDC1C85AC85BC85CDDC6C2DCC85DC85EC85FC860C861C862D3A9D3AADDD3CFF4C8F8C863C864C865C866C867C868C869C86ADDE6C86BC86CC86DC86EC86FC870DDC7C871C872C873DDE0C2E4C874C875C876C877C878C879C87AC87BDDE1C87CC87DC87EC880C881C882C883C884C885C886DDD7C887C888C889C88AC88BD6F8C88CDDD9DDD8B8F0DDD6C88DC88EC88FC890C6CFC891B6ADC892C893C894C895C896DDE2C897BAF9D4E1DDE7C898C899C89AB4D0C89BDDDAC89CBFFBDDE3C89DDDDFC89EDDDDC89FC8A0C940C941C942C943C944B5D9C945C946C947C948DDDBDDDCDDDEC949BDAFDDE4C94ADDE5C94BC94CC94DC94EC94FC950C951C952DDF5C953C3C9C954C955CBE2C956C957C958C959DDF2C95AC95BC95CC95DC95EC95FC960C961C962C963C964C965C966D8E1C967C968C6D1C969DDF4C96AC96BC96CD5F4DDF3DDF0C96DC96EDDECC96FDDEFC970DDE8C971C972D0EEC973C974C975C976C8D8DDEEC977C978DDE9C979C97ADDEACBF2C97BDDEDC97CC97DB1CDC97EC980C981C982C983C984C0B6C985BCBBDDF1C986C987DDF7C988DDF6DDEBC989C98AC98BC98CC98DC5EEC98EC98FC990DDFBC991C992C993C994C995C996C997C998C999C99AC99BDEA4C99CC99DDEA3C99EC99FC9A0CA40CA41CA42CA43CA44CA45CA46CA47CA48DDF8CA49CA4ACA4BCA4CC3EFCA4DC2FBCA4ECA4FCA50D5E1CA51CA52CEB5CA53CA54CA55CA56DDFDCA57B2CCCA58CA59CA5ACA5BCA5CCA5DCA5ECA5FCA60C4E8CADFCA61CA62CA63CA64CA65CA66CA67CA68CA69CA6AC7BEDDFADDFCDDFEDEA2B0AAB1CECA6BCA6CCA6DCA6ECA6FDEACCA70CA71CA72CA73DEA6BDB6C8EFCA74CA75CA76CA77CA78CA79CA7ACA7BCA7CCA7DCA7EDEA1CA80CA81DEA5CA82CA83CA84CA85DEA9CA86CA87CA88CA89CA8ADEA8CA8BCA8CCA8DDEA7CA8ECA8FCA90CA91CA92CA93CA94CA95CA96DEADCA97D4CCCA98CA99CA9ACA9BDEB3DEAADEAECA9CCA9DC0D9CA9ECA9FCAA0CB40CB41B1A1DEB6CB42DEB1CB43CB44CB45CB46CB47CB48CB49DEB2CB4ACB4BCB4CCB4DCB4ECB4FCB50CB51CB52CB53CB54D1A6DEB5CB55CB56CB57CB58CB59CB5ACB5BDEAFCB5CCB5DCB5EDEB0CB5FD0BDCB60CB61CB62DEB4CAEDDEB9CB63CB64CB65CB66CB67CB68DEB8CB69DEB7CB6ACB6BCB6CCB6DCB6ECB6FCB70DEBBCB71CB72CB73CB74CB75CB76CB77BDE5CB78CB79CB7ACB7BCB7CB2D8C3EACB7DCB7EDEBACB80C5BACB81CB82CB83CB84CB85CB86DEBCCB87CB88CB89CB8ACB8BCB8CCB8DCCD9CB8ECB8FCB90CB91B7AACB92CB93CB94CB95CB96CB97CB98CB99CB9ACB9BCB9CCB9DCB9ECB9FCBA0CC40CC41D4E5CC42CC43CC44DEBDCC45CC46CC47CC48CC49DEBFCC4ACC4BCC4CCC4DCC4ECC4FCC50CC51CC52CC53CC54C4A2CC55CC56CC57CC58DEC1CC59CC5ACC5BCC5CCC5DCC5ECC5FCC60CC61CC62CC63CC64CC65CC66CC67CC68DEBECC69DEC0CC6ACC6BCC6CCC6DCC6ECC6FCC70CC71CC72CC73CC74CC75CC76CC77D5BACC78CC79CC7ADEC2CC7BCC7CCC7DCC7ECC80CC81CC82CC83CC84CC85CC86CC87CC88CC89CC8ACC8BF2AEBBA2C2B2C5B0C2C7CC8CCC8DF2AFCC8ECC8FCC90CC91CC92D0E9CC93CC94CC95D3DDCC96CC97CC98EBBDCC99CC9ACC9BCC9CCC9DCC9ECC9FCCA0B3E6F2B0CD40F2B1CD41CD42CAADCD43CD44CD45CD46CD47CD48CD49BAE7F2B3F2B5F2B4CBE4CFBAF2B2CAB4D2CFC2ECCD4ACD4BCD4CCD4DCD4ECD4FCD50CEC3F2B8B0F6F2B7CD51CD52CD53CD54CD55F2BECD56B2CFCD57CD58CD59CD5ACD5BCD5CD1C1F2BACD5DCD5ECD5FCD60CD61F2BCD4E9CD62CD63F2BBF2B6F2BFF2BDCD64F2B9CD65CD66F2C7F2C4F2C6CD67CD68F2CAF2C2F2C0CD69CD6ACD6BF2C5CD6CCD6DCD6ECD6FCD70D6FBCD71CD72CD73F2C1CD74C7F9C9DFCD75F2C8B9C6B5B0CD76CD77F2C3F2C9F2D0F2D6CD78CD79BBD7CD7ACD7BCD7CF2D5CDDCCD7DD6EBCD7ECD80F2D2F2D4CD81CD82CD83CD84B8F2CD85CD86CD87CD88F2CBCD89CD8ACD8BF2CEC2F9CD8CD5DDF2CCF2CDF2CFF2D3CD8DCD8ECD8FF2D9D3BCCD90CD91CD92CD93B6EACD94CAF1CD95B7E4F2D7CD96CD97CD98F2D8F2DAF2DDF2DBCD99CD9AF2DCCD9BCD9CCD9DCD9ED1D1F2D1CD9FCDC9CDA0CECFD6A9CE40F2E3CE41C3DBCE42F2E0CE43CE44C0AFF2ECF2DECE45F2E1CE46CE47CE48F2E8CE49CE4ACE4BCE4CF2E2CE4DCE4EF2E7CE4FCE50F2E6CE51CE52F2E9CE53CE54CE55F2DFCE56CE57F2E4F2EACE58CE59CE5ACE5BCE5CCE5DCE5ED3ACF2E5B2F5CE5FCE60F2F2CE61D0ABCE62CE63CE64CE65F2F5CE66CE67CE68BBC8CE69F2F9CE6ACE6BCE6CCE6DCE6ECE6FF2F0CE70CE71F2F6F2F8F2FACE72CE73CE74CE75CE76CE77CE78CE79F2F3CE7AF2F1CE7BCE7CCE7DBAFBCE7EB5FBCE80CE81CE82CE83F2EFF2F7F2EDF2EECE84CE85CE86F2EBF3A6CE87F3A3CE88CE89F3A2CE8ACE8BF2F4CE8CC8DACE8DCE8ECE8FCE90CE91F2FBCE92CE93CE94F3A5CE95CE96CE97CE98CE99CE9ACE9BC3F8CE9CCE9DCE9ECE9FCEA0CF40CF41CF42F2FDCF43CF44F3A7F3A9F3A4CF45F2FCCF46CF47CF48F3ABCF49F3AACF4ACF4BCF4CCF4DC2DDCF4ECF4FF3AECF50CF51F3B0CF52CF53CF54CF55CF56F3A1CF57CF58CF59F3B1F3ACCF5ACF5BCF5CCF5DCF5EF3AFF2FEF3ADCF5FCF60CF61CF62CF63CF64CF65F3B2CF66CF67CF68CF69F3B4CF6ACF6BCF6CCF6DF3A8CF6ECF6FCF70CF71F3B3CF72CF73CF74F3B5CF75CF76CF77CF78CF79CF7ACF7BCF7CCF7DCF7ED0B7CF80CF81CF82CF83F3B8CF84CF85CF86CF87D9F9CF88CF89CF8ACF8BCF8CCF8DF3B9CF8ECF8FCF90CF91CF92CF93CF94CF95F3B7CF96C8E4F3B6CF97CF98CF99CF9AF3BACF9BCF9CCF9DCF9ECF9FF3BBB4C0CFA0D040D041D042D043D044D045D046D047D048D049D04AD04BD04CD04DEEC3D04ED04FD050D051D052D053F3BCD054D055F3BDD056D057D058D1AAD059D05AD05BF4ACD0C6D05CD05DD05ED05FD060D061D0D0D1DCD062D063D064D065D066D067CFCED068D069BDD6D06AD1C3D06BD06CD06DD06ED06FD070D071BAE2E1E9D2C2F1C2B2B9D072D073B1EDF1C3D074C9C0B3C4D075D9F2D076CBA5D077F1C4D078D079D07AD07BD6D4D07CD07DD07ED080D081F1C5F4C0F1C6D082D4ACF1C7D083B0C0F4C1D084D085F4C2D086D087B4FCD088C5DBD089D08AD08BD08CCCBBD08DD08ED08FD0E4D090D091D092D093D094CDE0D095D096D097D098D099F1C8D09AD9F3D09BD09CD09DD09ED09FD0A0B1BBD140CFAED141D142D143B8A4D144D145D146D147D148F1CAD149D14AD14BD14CF1CBD14DD14ED14FD150B2C3C1D1D151D152D7B0F1C9D153D154F1CCD155D156D157D158F1CED159D15AD15BD9F6D15CD2E1D4A3D15DD15EF4C3C8B9D15FD160D161D162D163F4C4D164D165F1CDF1CFBFE3F1D0D166D167F1D4D168D169D16AD16BD16CD16DD16EF1D6F1D1D16FC9D1C5E1D170D171D172C2E3B9FCD173D174F1D3D175F1D5D176D177D178B9D3D179D17AD17BD17CD17DD17ED180F1DBD181D182D183D184D185BAD6D186B0FDF1D9D187D188D189D18AD18BF1D8F1D2F1DAD18CD18DD18ED18FD190F1D7D191D192D193C8ECD194D195D196D197CDCAF1DDD198D199D19AD19BE5BDD19CD19DD19EF1DCD19FF1DED1A0D240D241D242D243D244D245D246D247D248F1DFD249D24ACFE5D24BD24CD24DD24ED24FD250D251D252D253D254D255D256D257D258D259D25AD25BD25CD25DD25ED25FD260D261D262D263F4C5BDF3D264D265D266D267D268D269F1E0D26AD26BD26CD26DD26ED26FD270D271D272D273D274D275D276D277D278D279D27AD27BD27CD27DF1E1D27ED280D281CEF7D282D2AAD283F1FBD284D285B8B2D286D287D288D289D28AD28BD28CD28DD28ED28FD290D291D292D293D294D295D296D297D298D299D29AD29BD29CD29DD29ED29FD2A0D340D341D342D343D344D345D346D347D348D349D34AD34BD34CD34DD34ED34FD350D351D352D353D354D355D356D357D358D359D35AD35BD35CD35DD35EBCFBB9DBD35FB9E6C3D9CAD3EAE8C0C0BEF5EAE9EAEAEAEBD360EAECEAEDEAEEEAEFBDC7D361D362D363F5FBD364D365D366F5FDD367F5FED368F5FCD369D36AD36BD36CBDE2D36DF6A1B4A5D36ED36FD370D371F6A2D372D373D374F6A3D375D376D377ECB2D378D379D37AD37BD37CD37DD37ED380D381D382D383D384D1D4D385D386D387D388D389D38AD9EAD38BD38CD38DD38ED38FD390D391D392D393D394D395D396D397D398D399D39AD39BD39CD39DD39ED39FD3A0D440D441D442D443D444D445D446D447D448D449D44AD44BD44CD44DD44ED44FD450D451D452D453D454D455D456D457D458D459D45AD45BD45CD45DD45ED45FF6A4D460D461D462D463D464D465D466D467D468EEBAD469D46AD46BD46CD46DD46ED46FD470D471D472D473D474D475D476D477D478D479D47AD47BD47CD47DD47ED480D481D482D483D484D485D486D487D488D489D48AD48BD48CD48DD48ED48FD490D491D492D493D494D495D496D497D498D499D5B2D49AD49BD49CD49DD49ED49FD4A0D540D541D542D543D544D545D546D547D3FECCDCD548D549D54AD54BD54CD54DD54ED54FCAC4D550D551D552D553D554D555D556D557D558D559D55AD55BD55CD55DD55ED55FD560D561D562D563D564D565D566D567D568D569D56AD56BD56CD56DD56ED56FD570D571D572D573D574D575D576D577D578D579D57AD57BD57CD57DD57ED580D581D582D583D584D585D586D587D588D589D58AD58BD58CD58DD58ED58FD590D591D592D593D594D595D596D597D598D599D59AD59BD59CD59DD59ED59FD5A0D640D641D642D643D644D645D646D647D648D649D64AD64BD64CD64DD64ED64FD650D651D652D653D654D655D656D657D658D659D65AD65BD65CD65DD65ED65FD660D661D662E5C0D663D664D665D666D667D668D669D66AD66BD66CD66DD66ED66FD670D671D672D673D674D675D676D677D678D679D67AD67BD67CD67DD67ED680D681F6A5D682D683D684D685D686D687D688D689D68AD68BD68CD68DD68ED68FD690D691D692D693D694D695D696D697D698D699D69AD69BD69CD69DD69ED69FD6A0D740D741D742D743D744D745D746D747D748D749D74AD74BD74CD74DD74ED74FD750D751D752D753D754D755D756D757D758D759D75AD75BD75CD75DD75ED75FBEAFD760D761D762D763D764C6A9D765D766D767D768D769D76AD76BD76CD76DD76ED76FD770D771D772D773D774D775D776D777D778D779D77AD77BD77CD77DD77ED780D781D782D783D784D785D786D787D788D789D78AD78BD78CD78DD78ED78FD790D791D792D793D794D795D796D797D798DAA5BCC6B6A9B8BCC8CFBCA5DAA6DAA7CCD6C8C3DAA8C6FDD799D1B5D2E9D1B6BCC7D79ABDB2BBE4DAA9DAAAD1C8DAABD0EDB6EFC2DBD79BCBCFB7EDC9E8B7C3BEF7D6A4DAACDAADC6C0D7E7CAB6D79CD5A9CBDFD5EFDAAED6DFB4CADAB0DAAFD79DD2EBDAB1DAB2DAB3CAD4DAB4CAABDAB5DAB6B3CFD6EFDAB7BBB0B5AEDAB8DAB9B9EED1AFD2E8DABAB8C3CFEAB2EFDABBDABCD79EBDEBCEDCD3EFDABDCEF3DABED3D5BBE5DABFCBB5CBD0DAC0C7EBD6EEDAC1C5B5B6C1DAC2B7CCBFCEDAC3DAC4CBADDAC5B5F7DAC6C1C2D7BBDAC7CCB8D79FD2EAC4B1DAC8B5FDBBD1DAC9D0B3DACADACBCEBDDACCDACDDACEB2F7DAD1DACFD1E8DAD0C3D5DAD2D7A0DAD3DAD4DAD5D0BBD2A5B0F9DAD6C7ABDAD7BDF7C3A1DAD8DAD9C3FDCCB7DADADADBC0BEC6D7DADCDADDC7B4DADEDADFB9C8D840D841D842D843D844D845D846D847D848BBEDD849D84AD84BD84CB6B9F4F8D84DF4F9D84ED84FCDE3D850D851D852D853D854D855D856D857F5B9D858D859D85AD85BEBE0D85CD85DD85ED85FD860D861CFF3BBBFD862D863D864D865D866D867D868BAC0D4A5D869D86AD86BD86CD86DD86ED86FE1D9D870D871D872D873F5F4B1AAB2F2D874D875D876D877D878D879D87AF5F5D87BD87CF5F7D87DD87ED880BAD1F5F6D881C3B2D882D883D884D885D886D887D888F5F9D889D88AD88BF5F8D88CD88DD88ED88FD890D891D892D893D894D895D896D897D898D899D89AD89BD89CD89DD89ED89FD8A0D940D941D942D943D944D945D946D947D948D949D94AD94BD94CD94DD94ED94FD950D951D952D953D954D955D956D957D958D959D95AD95BD95CD95DD95ED95FD960D961D962D963D964D965D966D967D968D969D96AD96BD96CD96DD96ED96FD970D971D972D973D974D975D976D977D978D979D97AD97BD97CD97DD97ED980D981D982D983D984D985D986D987D988D989D98AD98BD98CD98DD98ED98FD990D991D992D993D994D995D996D997D998D999D99AD99BD99CD99DD99ED99FD9A0DA40DA41DA42DA43DA44DA45DA46DA47DA48DA49DA4ADA4BDA4CDA4DDA4EB1B4D5EAB8BADA4FB9B1B2C6D4F0CFCDB0DCD5CBBBF5D6CAB7B7CCB0C6B6B1E1B9BAD6FCB9E1B7A1BCFAEADAEADBCCF9B9F3EADCB4FBC3B3B7D1BAD8EADDD4F4EADEBCD6BBDFEADFC1DEC2B8D4DFD7CAEAE0EAE1EAE4EAE2EAE3C9DEB8B3B6C4EAE5CAEAC9CDB4CDDA50DA51E2D9C5E2EAE6C0B5DA52D7B8EAE7D7ACC8FCD8D3D8CDD4DEDA53D4F9C9C4D3AEB8D3B3E0DA54C9E2F4F6DA55DA56DA57BAD5DA58F4F7DA59DA5AD7DFDA5BDA5CF4F1B8B0D5D4B8CFC6F0DA5DDA5EDA5FDA60DA61DA62DA63DA64DA65B3C3DA66DA67F4F2B3ACDA68DA69DA6ADA6BD4BDC7F7DA6CDA6DDA6EDA6FDA70F4F4DA71DA72F4F3DA73DA74DA75DA76DA77DA78DA79DA7ADA7BDA7CCCCBDA7DDA7EDA80C8A4DA81DA82DA83DA84DA85DA86DA87DA88DA89DA8ADA8BDA8CDA8DF4F5DA8ED7E3C5BFF5C0DA8FDA90F5BBDA91F5C3DA92F5C2DA93D6BAF5C1DA94DA95DA96D4BEF5C4DA97F5CCDA98DA99DA9ADA9BB0CFB5F8DA9CF5C9F5CADA9DC5DCDA9EDA9FDAA0DB40F5C5F5C6DB41DB42F5C7F5CBDB43BEE0F5C8B8FADB44DB45DB46F5D0F5D3DB47DB48DB49BFE7DB4AB9F2F5BCF5CDDB4BDB4CC2B7DB4DDB4EDB4FCCF8DB50BCF9DB51F5CEF5CFF5D1B6E5F5D2DB52F5D5DB53DB54DB55DB56DB57DB58DB59F5BDDB5ADB5BDB5CF5D4D3BBDB5DB3ECDB5EDB5FCCA4DB60DB61DB62DB63F5D6DB64DB65DB66DB67DB68DB69DB6ADB6BF5D7BEE1F5D8DB6CDB6DCCDFF5DBDB6EDB6FDB70DB71DB72B2C8D7D9DB73F5D9DB74F5DAF5DCDB75F5E2DB76DB77DB78F5E0DB79DB7ADB7BF5DFF5DDDB7CDB7DF5E1DB7EDB80F5DEF5E4F5E5DB81CCE3DB82DB83E5BFB5B8F5E3F5E8CCA3DB84DB85DB86DB87DB88F5E6F5E7DB89DB8ADB8BDB8CDB8DDB8EF5BEDB8FDB90DB91DB92DB93DB94DB95DB96DB97DB98DB99DB9AB1C4DB9BDB9CF5BFDB9DDB9EB5C5B2E4DB9FF5ECF5E9DBA0B6D7DC40F5EDDC41F5EADC42DC43DC44DC45DC46F5EBDC47DC48B4DADC49D4EADC4ADC4BDC4CF5EEDC4DB3F9DC4EDC4FDC50DC51DC52DC53DC54F5EFF5F1DC55DC56DC57F5F0DC58DC59DC5ADC5BDC5CDC5DDC5EF5F2DC5FF5F3DC60DC61DC62DC63DC64DC65DC66DC67DC68DC69DC6ADC6BC9EDB9AADC6CDC6DC7FBDC6EDC6FB6E3DC70DC71DC72DC73DC74DC75DC76CCC9DC77DC78DC79DC7ADC7BDC7CDC7DDC7EDC80DC81DC82DC83DC84DC85DC86DC87DC88DC89DC8AEAA6DC8BDC8CDC8DDC8EDC8FDC90DC91DC92DC93DC94DC95DC96DC97DC98DC99DC9ADC9BDC9CDC9DDC9EDC9FDCA0DD40DD41DD42DD43DD44DD45DD46DD47DD48DD49DD4ADD4BDD4CDD4DDD4EDD4FDD50DD51DD52DD53DD54DD55DD56DD57DD58DD59DD5ADD5BDD5CDD5DDD5EDD5FDD60DD61DD62DD63DD64DD65DD66DD67DD68DD69DD6ADD6BDD6CDD6DDD6EDD6FDD70DD71DD72DD73DD74DD75DD76DD77DD78DD79DD7ADD7BDD7CDD7DDD7EDD80DD81DD82DD83DD84DD85DD86DD87DD88DD89DD8ADD8BDD8CDD8DDD8EDD8FDD90DD91DD92DD93DD94DD95DD96DD97DD98DD99DD9ADD9BDD9CDD9DDD9EDD9FDDA0DE40DE41DE42DE43DE44DE45DE46DE47DE48DE49DE4ADE4BDE4CDE4DDE4EDE4FDE50DE51DE52DE53DE54DE55DE56DE57DE58DE59DE5ADE5BDE5CDE5DDE5EDE5FDE60B3B5D4FEB9ECD0F9DE61E9EDD7AAE9EEC2D6C8EDBAE4E9EFE9F0E9F1D6E1E9F2E9F3E9F5E9F4E9F6E9F7C7E1E9F8D4D8E9F9BDCEDE62E9FAE9FBBDCFE9FCB8A8C1BEE9FDB1B2BBD4B9F5E9FEDE63EAA1EAA2EAA3B7F8BCADDE64CAE4E0CED4AFCFBDD5B7EAA4D5DEEAA5D0C1B9BCDE65B4C7B1D9DE66DE67DE68C0B1DE69DE6ADE6BDE6CB1E6B1E7DE6DB1E8DE6EDE6FDE70DE71B3BDC8E8DE72DE73DE74DE75E5C1DE76DE77B1DFDE78DE79DE7AC1C9B4EFDE7BDE7CC7A8D3D8DE7DC6F9D1B8DE7EB9FDC2F5DE80DE81DE82DE83DE84D3ADDE85D4CBBDFCDE86E5C2B7B5E5C3DE87DE88BBB9D5E2DE89BDF8D4B6CEA5C1ACB3D9DE8ADE8BCCF6DE8CE5C6E5C4E5C8DE8DE5CAE5C7B5CFC6C8DE8EB5FCE5C5DE8FCAF6DE90DE91E5C9DE92DE93DE94C3D4B1C5BCA3DE95DE96DE97D7B7DE98DE99CDCBCBCDCACACCD3E5CCE5CBC4E6DE9ADE9BD1A1D1B7E5CDDE9CE5D0DE9DCDB8D6F0E5CFB5DDDE9ECDBEDE9FE5D1B6BADEA0DF40CDA8B9E4DF41CAC5B3D1CBD9D4ECE5D2B7EADF42DF43DF44E5CEDF45DF46DF47DF48DF49DF4AE5D5B4FEE5D6DF4BDF4CDF4DDF4EDF4FE5D3E5D4DF50D2DDDF51DF52C2DFB1C6DF53D3E2DF54DF55B6DDCBECDF56E5D7DF57DF58D3F6DF59DF5ADF5BDF5CDF5DB1E9DF5EB6F4E5DAE5D8E5D9B5C0DF5FDF60DF61D2C5E5DCDF62DF63E5DEDF64DF65DF66DF67DF68DF69E5DDC7B2DF6AD2A3DF6BDF6CE5DBDF6DDF6EDF6FDF70D4E2D5DADF71DF72DF73DF74DF75E5E0D7F1DF76DF77DF78DF79DF7ADF7BDF7CE5E1DF7DB1DCD1FBDF7EE5E2E5E4DF80DF81DF82DF83E5E3DF84DF85E5E5DF86DF87DF88DF89DF8AD2D8DF8BB5CBDF8CE7DFDF8DDAF5DF8EDAF8DF8FDAF6DF90DAF7DF91DF92DF93DAFAD0CFC4C7DF94DF95B0EEDF96DF97DF98D0B0DF99DAF9DF9AD3CABAAADBA2C7F1DF9BDAFCDAFBC9DBDAFDDF9CDBA1D7DEDAFEC1DADF9DDF9EDBA5DF9FDFA0D3F4E040E041DBA7DBA4E042DBA8E043E044BDBCE045E046E047C0C9DBA3DBA6D6A3E048DBA9E049E04AE04BDBADE04CE04DE04EDBAEDBACBAC2E04FE050E051BFA4DBABE052E053E054DBAAD4C7B2BFE055E056DBAFE057B9F9E058DBB0E059E05AE05BE05CB3BBE05DE05EE05FB5A6E060E061E062E063B6BCDBB1E064E065E066B6F5E067DBB2E068E069E06AE06BE06CE06DE06EE06FE070E071E072E073E074E075E076E077E078E079E07AE07BB1C9E07CE07DE07EE080DBB4E081E082E083DBB3DBB5E084E085E086E087E088E089E08AE08BE08CE08DE08EDBB7E08FDBB6E090E091E092E093E094E095E096DBB8E097E098E099E09AE09BE09CE09DE09EE09FDBB9E0A0E140DBBAE141E142D3CFF4FAC7F5D7C3C5E4F4FCF4FDF4FBE143BEC6E144E145E146E147D0EFE148E149B7D3E14AE14BD4CDCCAAE14CE14DF5A2F5A1BAA8F4FECBD6E14EE14FE150F5A4C0D2E151B3EAE152CDAAF5A5F5A3BDB4F5A8E153F5A9BDCDC3B8BFE1CBE1F5AAE154E155E156F5A6F5A7C4F0E157E158E159E15AE15BF5ACE15CB4BCE15DD7EDE15EB4D7F5ABF5AEE15FE160F5ADF5AFD0D1E161E162E163E164E165E166E167C3D1C8A9E168E169E16AE16BE16CE16DF5B0F5B1E16EE16FE170E171E172E173F5B2E174E175F5B3F5B4F5B5E176E177E178E179F5B7F5B6E17AE17BE17CE17DF5B8E17EE180E181E182E183E184E185E186E187E188E189E18AB2C9E18BD3D4CACDE18CC0EFD6D8D2B0C1BFE18DBDF0E18EE18FE190E191E192E193E194E195E196E197B8AAE198E199E19AE19BE19CE19DE19EE19FE1A0E240E241E242E243E244E245E246E247E248E249E24AE24BE24CE24DE24EE24FE250E251E252E253E254E255E256E257E258E259E25AE25BE25CE25DE25EE25FE260E261E262E263E264E265E266E267E268E269E26AE26BE26CE26DE26EE26FE270E271E272E273E274E275E276E277E278E279E27AE27BE27CE27DE27EE280E281E282E283E284E285E286E287E288E289E28AE28BE28CE28DE28EE28FE290E291E292E293E294E295E296E297E298E299E29AE29BE29CE29DE29EE29FE2A0E340E341E342E343E344E345E346E347E348E349E34AE34BE34CE34DE34EE34FE350E351E352E353E354E355E356E357E358E359E35AE35BE35CE35DE35EE35FE360E361E362E363E364E365E366E367E368E369E36AE36BE36CE36DBCF8E36EE36FE370E371E372E373E374E375E376E377E378E379E37AE37BE37CE37DE37EE380E381E382E383E384E385E386E387F6C6E388E389E38AE38BE38CE38DE38EE38FE390E391E392E393E394E395E396E397E398E399E39AE39BE39CE39DE39EE39FE3A0E440E441E442E443E444E445F6C7E446E447E448E449E44AE44BE44CE44DE44EE44FE450E451E452E453E454E455E456E457E458E459E45AE45BE45CE45DE45EF6C8E45FE460E461E462E463E464E465E466E467E468E469E46AE46BE46CE46DE46EE46FE470E471E472E473E474E475E476E477E478E479E47AE47BE47CE47DE47EE480E481E482E483E484E485E486E487E488E489E48AE48BE48CE48DE48EE48FE490E491E492E493E494E495E496E497E498E499E49AE49BE49CE49DE49EE49FE4A0E540E541E542E543E544E545E546E547E548E549E54AE54BE54CE54DE54EE54FE550E551E552E553E554E555E556E557E558E559E55AE55BE55CE55DE55EE55FE560E561E562E563E564E565E566E567E568E569E56AE56BE56CE56DE56EE56FE570E571E572E573F6C9E574E575E576E577E578E579E57AE57BE57CE57DE57EE580E581E582E583E584E585E586E587E588E589E58AE58BE58CE58DE58EE58FE590E591E592E593E594E595E596E597E598E599E59AE59BE59CE59DE59EE59FF6CAE5A0E640E641E642E643E644E645E646E647E648E649E64AE64BE64CE64DE64EE64FE650E651E652E653E654E655E656E657E658E659E65AE65BE65CE65DE65EE65FE660E661E662F6CCE663E664E665E666E667E668E669E66AE66BE66CE66DE66EE66FE670E671E672E673E674E675E676E677E678E679E67AE67BE67CE67DE67EE680E681E682E683E684E685E686E687E688E689E68AE68BE68CE68DE68EE68FE690E691E692E693E694E695E696E697E698E699E69AE69BE69CE69DF6CBE69EE69FE6A0E740E741E742E743E744E745E746E747F7E9E748E749E74AE74BE74CE74DE74EE74FE750E751E752E753E754E755E756E757E758E759E75AE75BE75CE75DE75EE75FE760E761E762E763E764E765E766E767E768E769E76AE76BE76CE76DE76EE76FE770E771E772E773E774E775E776E777E778E779E77AE77BE77CE77DE77EE780E781E782E783E784E785E786E787E788E789E78AE78BE78CE78DE78EE78FE790E791E792E793E794E795E796E797E798E799E79AE79BE79CE79DE79EE79FE7A0E840E841E842E843E844E845E846E847E848E849E84AE84BE84CE84DE84EF6CDE84FE850E851E852E853E854E855E856E857E858E859E85AE85BE85CE85DE85EE85FE860E861E862E863E864E865E866E867E868E869E86AE86BE86CE86DE86EE86FE870E871E872E873E874E875E876E877E878E879E87AF6CEE87BE87CE87DE87EE880E881E882E883E884E885E886E887E888E889E88AE88BE88CE88DE88EE88FE890E891E892E893E894EEC4EEC5EEC6D5EBB6A4EEC8EEC7EEC9EECAC7A5EECBEECCE895B7B0B5F6EECDEECFE896EECEE897B8C6EED0EED1EED2B6DBB3AED6D3C4C6B1B5B8D6EED3EED4D4BFC7D5BEFBCED9B9B3EED6EED5EED8EED7C5A5EED9EEDAC7AEEEDBC7AFEEDCB2A7EEDDEEDEEEDFEEE0EEE1D7EAEEE2EEE3BCD8EEE4D3CBCCFAB2ACC1E5EEE5C7A6C3ADE898EEE6EEE7EEE8EEE9EEEAEEEBEEECE899EEEDEEEEEEEFE89AE89BEEF0EEF1EEF2EEF4EEF3E89CEEF5CDADC2C1EEF6EEF7EEF8D5A1EEF9CFB3EEFAEEFBE89DEEFCEEFDEFA1EEFEEFA2B8F5C3FAEFA3EFA4BDC2D2BFB2F9EFA5EFA6EFA7D2F8EFA8D6FDEFA9C6CCE89EEFAAEFABC1B4EFACCFFACBF8EFAEEFADB3FAB9F8EFAFEFB0D0E2EFB1EFB2B7E6D0BFEFB3EFB4EFB5C8F1CCE0EFB6EFB7EFB8EFB9EFBAD5E0EFBBB4EDC3AAEFBCE89FEFBDEFBEEFBFE8A0CEFDEFC0C2E0B4B8D7B6BDF5E940CFC7EFC3EFC1EFC2EFC4B6A7BCFCBEE2C3CCEFC5EFC6E941EFC7EFCFEFC8EFC9EFCAC7C2EFF1B6CDEFCBE942EFCCEFCDB6C6C3BEEFCEE943EFD0EFD1EFD2D5F2E944EFD3C4F7E945EFD4C4F8EFD5EFD6B8E4B0F7EFD7EFD8EFD9E946EFDAEFDBEFDCEFDDE947EFDEBEB5EFE1EFDFEFE0E948EFE2EFE3C1CDEFE4EFE5EFE6EFE7EFE8EFE9EFEAEFEBEFECC0D8E949EFEDC1ADEFEEEFEFEFF0E94AE94BCFE2E94CE94DE94EE94FE950E951E952E953B3A4E954E955E956E957E958E959E95AE95BE95CE95DE95EE95FE960E961E962E963E964E965E966E967E968E969E96AE96BE96CE96DE96EE96FE970E971E972E973E974E975E976E977E978E979E97AE97BE97CE97DE97EE980E981E982E983E984E985E986E987E988E989E98AE98BE98CE98DE98EE98FE990E991E992E993E994E995E996E997E998E999E99AE99BE99CE99DE99EE99FE9A0EA40EA41EA42EA43EA44EA45EA46EA47EA48EA49EA4AEA4BEA4CEA4DEA4EEA4FEA50EA51EA52EA53EA54EA55EA56EA57EA58EA59EA5AEA5BC3C5E3C5C9C1E3C6EA5CB1D5CECAB4B3C8F2E3C7CFD0E3C8BCE4E3C9E3CAC3C6D5A2C4D6B9EBCEC5E3CBC3F6E3CCEA5DB7A7B8F3BAD2E3CDE3CED4C4E3CFEA5EE3D0D1CBE3D1E3D2E3D3E3D4D1D6E3D5B2FBC0BBE3D6EA5FC0ABE3D7E3D8E3D9EA60E3DAE3DBEA61B8B7DAE2EA62B6D3EA63DAE4DAE3EA64EA65EA66EA67EA68EA69EA6ADAE6EA6BEA6CEA6DC8EEEA6EEA6FDAE5B7C0D1F4D2F5D5F3BDD7EA70EA71EA72EA73D7E8DAE8DAE7EA74B0A2CDD3EA75DAE9EA76B8BDBCCAC2BDC2A4B3C2DAEAEA77C2AAC4B0BDB5EA78EA79CFDEEA7AEA7BEA7CDAEBC9C2EA7DEA7EEA80EA81EA82B1DDEA83EA84EA85DAECEA86B6B8D4BAEA87B3FDEA88EA89DAEDD4C9CFD5C5E3EA8ADAEEEA8BEA8CEA8DEA8EEA8FDAEFEA90DAF0C1EACCD5CFDDEA91EA92EA93EA94EA95EA96EA97EA98EA99EA9AEA9BEA9CEA9DD3E7C2A1EA9EDAF1EA9FEAA0CBE5EB40DAF2EB41CBE6D2FEEB42EB43EB44B8F4EB45EB46DAF3B0AFCFB6EB47EB48D5CFEB49EB4AEB4BEB4CEB4DEB4EEB4FEB50EB51EB52CBEDEB53EB54EB55EB56EB57EB58EB59EB5ADAF4EB5BEB5CE3C4EB5DEB5EC1A5EB5FEB60F6BFEB61EB62F6C0F6C1C4D1EB63C8B8D1E3EB64EB65D0DBD1C5BCAFB9CDEB66EFF4EB67EB68B4C6D3BAF6C2B3FBEB69EB6AF6C3EB6BEB6CB5F1EB6DEB6EEB6FEB70EB71EB72EB73EB74EB75EB76F6C5EB77EB78EB79EB7AEB7BEB7CEB7DD3EAF6A7D1A9EB7EEB80EB81EB82F6A9EB83EB84EB85F6A8EB86EB87C1E3C0D7EB88B1A2EB89EB8AEB8BEB8CCEEDEB8DD0E8F6ABEB8EEB8FCFF6EB90F6AAD5F0F6ACC3B9EB91EB92EB93BBF4F6AEF6ADEB94EB95EB96C4DEEB97EB98C1D8EB99EB9AEB9BEB9CEB9DCBAAEB9ECFBCEB9FEBA0EC40EC41EC42EC43EC44EC45EC46EC47EC48F6AFEC49EC4AF6B0EC4BEC4CF6B1EC4DC2B6EC4EEC4FEC50EC51EC52B0D4C5F9EC53EC54EC55EC56F6B2EC57EC58EC59EC5AEC5BEC5CEC5DEC5EEC5FEC60EC61EC62EC63EC64EC65EC66EC67EC68EC69C7E0F6A6EC6AEC6BBEB8EC6CEC6DBEB2EC6EB5E5EC6FEC70B7C7EC71BFBFC3D2C3E6EC72EC73D8CCEC74EC75EC76B8EFEC77EC78EC79EC7AEC7BEC7CEC7DEC7EEC80BDF9D1A5EC81B0D0EC82EC83EC84EC85EC86F7B0EC87EC88EC89EC8AEC8BEC8CEC8DEC8EF7B1EC8FEC90EC91EC92EC93D0ACEC94B0B0EC95EC96EC97F7B2F7B3EC98F7B4EC99EC9AEC9BC7CAEC9CEC9DEC9EEC9FECA0ED40ED41BECFED42ED43F7B7ED44ED45ED46ED47ED48ED49ED4AF7B6ED4BB1DEED4CF7B5ED4DED4EF7B8ED4FF7B9ED50ED51ED52ED53ED54ED55ED56ED57ED58ED59ED5AED5BED5CED5DED5EED5FED60ED61ED62ED63ED64ED65ED66ED67ED68ED69ED6AED6BED6CED6DED6EED6FED70ED71ED72ED73ED74ED75ED76ED77ED78ED79ED7AED7BED7CED7DED7EED80ED81CEA4C8CDED82BAABE8B8E8B9E8BABEC2ED83ED84ED85ED86ED87D2F4ED88D4CFC9D8ED89ED8AED8BED8CED8DED8EED8FED90ED91ED92ED93ED94ED95ED96ED97ED98ED99ED9AED9BED9CED9DED9EED9FEDA0EE40EE41EE42EE43EE44EE45EE46EE47EE48EE49EE4AEE4BEE4CEE4DEE4EEE4FEE50EE51EE52EE53EE54EE55EE56EE57EE58EE59EE5AEE5BEE5CEE5DEE5EEE5FEE60EE61EE62EE63EE64EE65EE66EE67EE68EE69EE6AEE6BEE6CEE6DEE6EEE6FEE70EE71EE72EE73EE74EE75EE76EE77EE78EE79EE7AEE7BEE7CEE7DEE7EEE80EE81EE82EE83EE84EE85EE86EE87EE88EE89EE8AEE8BEE8CEE8DEE8EEE8FEE90EE91EE92EE93EE94EE95EE96EE97EE98EE99EE9AEE9BEE9CEE9DEE9EEE9FEEA0EF40EF41EF42EF43EF44EF45D2B3B6A5C7EAF1FCCFEECBB3D0EBE7EFCDE7B9CBB6D9F1FDB0E4CBCCF1FED4A4C2ADC1ECC6C4BEB1F2A1BCD5EF46F2A2F2A3EF47F2A4D2C3C6B5EF48CDC7F2A5EF49D3B1BFC5CCE2EF4AF2A6F2A7D1D5B6EEF2A8F2A9B5DFF2AAF2ABEF4BB2FCF2ACF2ADC8A7EF4CEF4DEF4EEF4FEF50EF51EF52EF53EF54EF55EF56EF57EF58EF59EF5AEF5BEF5CEF5DEF5EEF5FEF60EF61EF62EF63EF64EF65EF66EF67EF68EF69EF6AEF6BEF6CEF6DEF6EEF6FEF70EF71B7E7EF72EF73ECA9ECAAECABEF74ECACEF75EF76C6AEECADECAEEF77EF78EF79B7C9CAB3EF7AEF7BEF7CEF7DEF7EEF80EF81E2B8F7CFEF82EF83EF84EF85EF86EF87EF88EF89EF8AEF8BEF8CEF8DEF8EEF8FEF90EF91EF92EF93EF94EF95EF96EF97EF98EF99EF9AEF9BEF9CEF9DEF9EEF9FEFA0F040F041F042F043F044F7D0F045F046B2CDF047F048F049F04AF04BF04CF04DF04EF04FF050F051F052F053F054F055F056F057F058F059F05AF05BF05CF05DF05EF05FF060F061F062F063F7D1F064F065F066F067F068F069F06AF06BF06CF06DF06EF06FF070F071F072F073F074F075F076F077F078F079F07AF07BF07CF07DF07EF080F081F082F083F084F085F086F087F088F089F7D3F7D2F08AF08BF08CF08DF08EF08FF090F091F092F093F094F095F096E2BBF097BCA2F098E2BCE2BDE2BEE2BFE2C0E2C1B7B9D2FBBDA4CACEB1A5CBC7F099E2C2B6FCC8C4E2C3F09AF09BBDC8F09CB1FDE2C4F09DB6F6E2C5C4D9F09EF09FE2C6CFDAB9DDE2C7C0A1F0A0E2C8B2F6F140E2C9F141C1F3E2CAE2CBC2F8E2CCE2CDE2CECAD7D8B8D9E5CFE3F142F143F144F145F146F147F148F149F14AF14BF14CF0A5F14DF14EDCB0F14FF150F151F152F153F154F155F156F157F158F159F15AF15BF15CF15DF15EF15FF160F161F162F163F164F165F166F167F168F169F16AF16BF16CF16DF16EF16FF170F171F172F173F174F175F176F177F178F179F17AF17BF17CF17DF17EF180F181F182F183F184F185F186F187F188F189F18AF18BF18CF18DF18EF18FF190F191F192F193F194F195F196F197F198F199F19AF19BF19CF19DF19EF19FF1A0F240F241F242F243F244F245F246F247F248F249F24AF24BF24CF24DF24EF24FF250F251F252F253F254F255F256F257F258F259F25AF25BF25CF25DF25EF25FF260F261F262F263F264F265F266F267F268F269F26AF26BF26CF26DF26EF26FF270F271F272F273F274F275F276F277F278F279F27AF27BF27CF27DF27EF280F281F282F283F284F285F286F287F288F289F28AF28BF28CF28DF28EF28FF290F291F292F293F294F295F296F297F298F299F29AF29BF29CF29DF29EF29FF2A0F340F341F342F343F344F345F346F347F348F349F34AF34BF34CF34DF34EF34FF350F351C2EDD4A6CDD4D1B1B3DBC7FDF352B2B5C2BFE6E0CABBE6E1E6E2BED4E6E3D7A4CDD5E6E5BCDDE6E4E6E6E6E7C2EEF353BDBEE6E8C2E6BAA7E6E9F354E6EAB3D2D1E9F355F356BFA5E6EBC6EFE6ECE6EDF357F358E6EEC6ADE6EFF359C9A7E6F0E6F1E6F2E5B9E6F3E6F4C2E2E6F5E6F6D6E8E6F7F35AE6F8B9C7F35BF35CF35DF35EF35FF360F361F7BBF7BAF362F363F364F365F7BEF7BCBAA1F366F7BFF367F7C0F368F369F36AF7C2F7C1F7C4F36BF36CF7C3F36DF36EF36FF370F371F7C5F7C6F372F373F374F375F7C7F376CBE8F377F378F379F37AB8DFF37BF37CF37DF37EF380F381F7D4F382F7D5F383F384F385F386F7D6F387F388F389F38AF7D8F38BF7DAF38CF7D7F38DF38EF38FF390F391F392F393F394F395F7DBF396F7D9F397F398F399F39AF39BF39CF39DD7D7F39EF39FF3A0F440F7DCF441F442F443F444F445F446F7DDF447F448F449F7DEF44AF44BF44CF44DF44EF44FF450F451F452F453F454F7DFF455F456F457F7E0F458F459F45AF45BF45CF45DF45EF45FF460F461F462DBCBF463F464D8AAF465F466F467F468F469F46AF46BF46CE5F7B9EDF46DF46EF46FF470BFFDBBEAF7C9C6C7F7C8F471F7CAF7CCF7CBF472F473F474F7CDF475CEBAF476F7CEF477F478C4A7F479F47AF47BF47CF47DF47EF480F481F482F483F484F485F486F487F488F489F48AF48BF48CF48DF48EF48FF490F491F492F493F494F495F496F497F498F499F49AF49BF49CF49DF49EF49FF4A0F540F541F542F543F544F545F546F547F548F549F54AF54BF54CF54DF54EF54FF550F551F552F553F554F555F556F557F558F559F55AF55BF55CF55DF55EF55FF560F561F562F563F564F565F566F567F568F569F56AF56BF56CF56DF56EF56FF570F571F572F573F574F575F576F577F578F579F57AF57BF57CF57DF57EF580F581F582F583F584F585F586F587F588F589F58AF58BF58CF58DF58EF58FF590F591F592F593F594F595F596F597F598F599F59AF59BF59CF59DF59EF59FF5A0F640F641F642F643F644F645F646F647F648F649F64AF64BF64CF64DF64EF64FF650F651F652F653F654F655F656F657F658F659F65AF65BF65CF65DF65EF65FF660F661F662F663F664F665F666F667F668F669F66AF66BF66CF66DF66EF66FF670F671F672F673F674F675F676F677F678F679F67AF67BF67CF67DF67EF680F681F682F683F684F685F686F687F688F689F68AF68BF68CF68DF68EF68FF690F691F692F693F694F695F696F697F698F699F69AF69BF69CF69DF69EF69FF6A0F740F741F742F743F744F745F746F747F748F749F74AF74BF74CF74DF74EF74FF750F751F752F753F754F755F756F757F758F759F75AF75BF75CF75DF75EF75FF760F761F762F763F764F765F766F767F768F769F76AF76BF76CF76DF76EF76FF770F771F772F773F774F775F776F777F778F779F77AF77BF77CF77DF77EF780D3E3F781F782F6CFF783C2B3F6D0F784F785F6D1F6D2F6D3F6D4F786F787F6D6F788B1ABF6D7F789F6D8F6D9F6DAF78AF6DBF6DCF78BF78CF78DF78EF6DDF6DECFCAF78FF6DFF6E0F6E1F6E2F6E3F6E4C0F0F6E5F6E6F6E7F6E8F6E9F790F6EAF791F6EBF6ECF792F6EDF6EEF6EFF6F0F6F1F6F2F6F3F6F4BEA8F793F6F5F6F6F6F7F6F8F794F795F796F797F798C8FAF6F9F6FAF6FBF6FCF799F79AF6FDF6FEF7A1F7A2F7A3F7A4F7A5F79BF79CF7A6F7A7F7A8B1EEF7A9F7AAF7ABF79DF79EF7ACF7ADC1DBF7AEF79FF7A0F7AFF840F841F842F843F844F845F846F847F848F849F84AF84BF84CF84DF84EF84FF850F851F852F853F854F855F856F857F858F859F85AF85BF85CF85DF85EF85FF860F861F862F863F864F865F866F867F868F869F86AF86BF86CF86DF86EF86FF870F871F872F873F874F875F876F877F878F879F87AF87BF87CF87DF87EF880F881F882F883F884F885F886F887F888F889F88AF88BF88CF88DF88EF88FF890F891F892F893F894F895F896F897F898F899F89AF89BF89CF89DF89EF89FF8A0F940F941F942F943F944F945F946F947F948F949F94AF94BF94CF94DF94EF94FF950F951F952F953F954F955F956F957F958F959F95AF95BF95CF95DF95EF95FF960F961F962F963F964F965F966F967F968F969F96AF96BF96CF96DF96EF96FF970F971F972F973F974F975F976F977F978F979F97AF97BF97CF97DF97EF980F981F982F983F984F985F986F987F988F989F98AF98BF98CF98DF98EF98FF990F991F992F993F994F995F996F997F998F999F99AF99BF99CF99DF99EF99FF9A0FA40FA41FA42FA43FA44FA45FA46FA47FA48FA49FA4AFA4BFA4CFA4DFA4EFA4FFA50FA51FA52FA53FA54FA55FA56FA57FA58FA59FA5AFA5BFA5CFA5DFA5EFA5FFA60FA61FA62FA63FA64FA65FA66FA67FA68FA69FA6AFA6BFA6CFA6DFA6EFA6FFA70FA71FA72FA73FA74FA75FA76FA77FA78FA79FA7AFA7BFA7CFA7DFA7EFA80FA81FA82FA83FA84FA85FA86FA87FA88FA89FA8AFA8BFA8CFA8DFA8EFA8FFA90FA91FA92FA93FA94FA95FA96FA97FA98FA99FA9AFA9BFA9CFA9DFA9EFA9FFAA0FB40FB41FB42FB43FB44FB45FB46FB47FB48FB49FB4AFB4BFB4CFB4DFB4EFB4FFB50FB51FB52FB53FB54FB55FB56FB57FB58FB59FB5AFB5BC4F1F0AFBCA6F0B0C3F9FB5CC5B8D1BBFB5DF0B1F0B2F0B3F0B4F0B5D1BCFB5ED1ECFB5FF0B7F0B6D4A7FB60CDD2F0B8F0BAF0B9F0BBF0BCFB61FB62B8EBF0BDBAE8FB63F0BEF0BFBEE9F0C0B6ECF0C1F0C2F0C3F0C4C8B5F0C5F0C6FB64F0C7C5F4FB65F0C8FB66FB67FB68F0C9FB69F0CAF7BDFB6AF0CBF0CCF0CDFB6BF0CEFB6CFB6DFB6EFB6FF0CFBAD7FB70F0D0F0D1F0D2F0D3F0D4F0D5F0D6F0D8FB71FB72D3A5F0D7FB73F0D9FB74FB75FB76FB77FB78FB79FB7AFB7BFB7CFB7DF5BAC2B9FB7EFB80F7E4FB81FB82FB83FB84F7E5F7E6FB85FB86F7E7FB87FB88FB89FB8AFB8BFB8CF7E8C2B4FB8DFB8EFB8FFB90FB91FB92FB93FB94FB95F7EAFB96F7EBFB97FB98FB99FB9AFB9BFB9CC2F3FB9DFB9EFB9FFBA0FC40FC41FC42FC43FC44FC45FC46FC47FC48F4F0FC49FC4AFC4BF4EFFC4CFC4DC2E9FC4EF7E1F7E2FC4FFC50FC51FC52FC53BBC6FC54FC55FC56FC57D9E4FC58FC59FC5ACAF2C0E8F0A4FC5BBADAFC5CFC5DC7ADFC5EFC5FFC60C4ACFC61FC62F7ECF7EDF7EEFC63F7F0F7EFFC64F7F1FC65FC66F7F4FC67F7F3FC68F7F2F7F5FC69FC6AFC6BFC6CF7F6FC6DFC6EFC6FFC70FC71FC72FC73FC74FC75EDE9FC76EDEAEDEBFC77F6BCFC78FC79FC7AFC7BFC7CFC7DFC7EFC80FC81FC82FC83FC84F6BDFC85F6BEB6A6FC86D8BEFC87FC88B9C4FC89FC8AFC8BD8BBFC8CDCB1FC8DFC8EFC8FFC90FC91FC92CAF3FC93F7F7FC94FC95FC96FC97FC98FC99FC9AFC9BFC9CF7F8FC9DFC9EF7F9FC9FFCA0FD40FD41FD42FD43FD44F7FBFD45F7FAFD46B1C7FD47F7FCF7FDFD48FD49FD4AFD4BFD4CF7FEFD4DFD4EFD4FFD50FD51FD52FD53FD54FD55FD56FD57C6EBECB4FD58FD59FD5AFD5BFD5CFD5DFD5EFD5FFD60FD61FD62FD63FD64FD65FD66FD67FD68FD69FD6AFD6BFD6CFD6DFD6EFD6FFD70FD71FD72FD73FD74FD75FD76FD77FD78FD79FD7AFD7BFD7CFD7DFD7EFD80FD81FD82FD83FD84FD85B3DDF6B3FD86FD87F6B4C1E4F6B5F6B6F6B7F6B8F6B9F6BAC8A3F6BBFD88FD89FD8AFD8BFD8CFD8DFD8EFD8FFD90FD91FD92FD93C1FAB9A8EDE8FD94FD95FD96B9EAD9DFFD97FD98FD99FD9AFD9'; - - for (var i = 0; i < str.length; i++) { - var c = str.charAt(i), - code = str.charCodeAt(i); - if (c == " ") strOut += "+"; - else if (code >= 19968 && code <= 40869) { - var index = code - 19968; - strOut += "%" + z.substr(index * 4, 2) + "%" + z.substr(index * 4 + 2, 2); - } else { - strOut += "%" + str.charCodeAt(i).toString(16); - } - } - return strOut; - }, - /* 改变图片大小 */ - scale: function (img, w, h) { - var ow = img.width, - oh = img.height; - - if (ow >= oh) { - img.width = w * ow / oh; - img.height = h; - img.style.marginLeft = '-' + parseInt((img.width - w) / 2) + 'px'; - } else { - img.width = w; - img.height = h * oh / ow; - img.style.marginTop = '-' + parseInt((img.height - h) / 2) + 'px'; - } - }, - getImageData: function(){ - var _this = this, - key = $G('searchTxt').value, - type = $G('searchType').value, - keepOriginName = editor.options.keepOriginName ? "1" : "0", - url = "http://image.baidu.com/i?ct=201326592&cl=2&lm=-1&st=-1&tn=baiduimagejson&istype=2&rn=32&fm=index&pv=&word=" + _this.encodeToGb2312(key) + type + "&keeporiginname=" + keepOriginName + "&" + +new Date; - - $G('searchListUl').innerHTML = lang.searchLoading; - ajax.request(url, { - 'dataType': 'jsonp', - 'charset': 'GB18030', - 'onsuccess':function(json){ - var list = []; - if(json && json.data) { - for(var i = 0; i < json.data.length; i++) { - if(json.data[i].objURL) { - list.push({ - title: json.data[i].fromPageTitleEnc, - src: json.data[i].objURL, - url: json.data[i].fromURL - }); - } - } - } - _this.setList(list); - }, - 'onerror':function(){ - $G('searchListUl').innerHTML = lang.searchRetry; - } - }); - }, - /* 添加图片到列表界面上 */ - setList: function (list) { - var i, item, p, img, link, _this = this, - listUl = $G('searchListUl'); - - listUl.innerHTML = ''; - if(list.length) { - for (i = 0; i < list.length; i++) { - item = document.createElement('li'); - p = document.createElement('p'); - img = document.createElement('img'); - link = document.createElement('a'); - - img.onload = function () { - _this.scale(this, 113, 113); - }; - img.width = 113; - img.setAttribute('src', list[i].src); - - link.href = list[i].url; - link.target = '_blank'; - link.title = list[i].title; - link.innerHTML = list[i].title; - - p.appendChild(img); - item.appendChild(p); - item.appendChild(link); - listUl.appendChild(item); - } - } else { - listUl.innerHTML = lang.searchRetry; - } - }, - getInsertList: function () { - var child, - src, - align = getAlign(), - list = [], - items = $G('searchListUl').children; - for(var i = 0; i < items.length; i++) { - child = items[i].firstChild && items[i].firstChild.firstChild; - if(child.tagName && child.tagName.toLowerCase() == 'img' && domUtils.hasClass(items[i], 'selected')) { - src = child.src; - list.push({ - src: src, - _src: src, - alt: src.substr(src.lastIndexOf('/') + 1), - floatStyle: align - }); - } - } - return list; - } - }; - -})(); diff --git a/static/plugs/ueditor/dialogs/image/images/alignicon.jpg b/static/plugs/ueditor/dialogs/image/images/alignicon.jpg deleted file mode 100644 index 754755b1b..000000000 Binary files a/static/plugs/ueditor/dialogs/image/images/alignicon.jpg and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/image/images/bg.png b/static/plugs/ueditor/dialogs/image/images/bg.png deleted file mode 100644 index 580be0a01..000000000 Binary files a/static/plugs/ueditor/dialogs/image/images/bg.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/image/images/icons.gif b/static/plugs/ueditor/dialogs/image/images/icons.gif deleted file mode 100644 index 78459dea7..000000000 Binary files a/static/plugs/ueditor/dialogs/image/images/icons.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/image/images/icons.png b/static/plugs/ueditor/dialogs/image/images/icons.png deleted file mode 100644 index 12e470016..000000000 Binary files a/static/plugs/ueditor/dialogs/image/images/icons.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/image/images/image.png b/static/plugs/ueditor/dialogs/image/images/image.png deleted file mode 100644 index 19699f6a9..000000000 Binary files a/static/plugs/ueditor/dialogs/image/images/image.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/image/images/progress.png b/static/plugs/ueditor/dialogs/image/images/progress.png deleted file mode 100644 index 717c4865c..000000000 Binary files a/static/plugs/ueditor/dialogs/image/images/progress.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/image/images/success.gif b/static/plugs/ueditor/dialogs/image/images/success.gif deleted file mode 100644 index 8d4f3112b..000000000 Binary files a/static/plugs/ueditor/dialogs/image/images/success.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/image/images/success.png b/static/plugs/ueditor/dialogs/image/images/success.png deleted file mode 100644 index 94f968dc8..000000000 Binary files a/static/plugs/ueditor/dialogs/image/images/success.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/insertframe/insertframe.html b/static/plugs/ueditor/dialogs/insertframe/insertframe.html deleted file mode 100644 index 8271f0b1d..000000000 --- a/static/plugs/ueditor/dialogs/insertframe/insertframe.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - -
                  - - - - - - - - - - - - - - - - - - - -
                  - - -
                  px
                  px
                  - -
                  -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/internal.js b/static/plugs/ueditor/dialogs/internal.js deleted file mode 100644 index 1f6413b39..000000000 --- a/static/plugs/ueditor/dialogs/internal.js +++ /dev/null @@ -1,81 +0,0 @@ -(function () { - var parent = window.parent; - //dialog对象 - dialog = parent.$EDITORUI[window.frameElement.id.replace( /_iframe$/, '' )]; - //当前打开dialog的编辑器实例 - editor = dialog.editor; - - UE = parent.UE; - - domUtils = UE.dom.domUtils; - - utils = UE.utils; - - browser = UE.browser; - - ajax = UE.ajax; - - $G = function ( id ) { - return document.getElementById( id ) - }; - //focus元素 - $focus = function ( node ) { - setTimeout( function () { - if ( browser.ie ) { - var r = node.createTextRange(); - r.collapse( false ); - r.select(); - } else { - node.focus() - } - }, 0 ) - }; - utils.loadFile(document,{ - href:editor.options.themePath + editor.options.theme + "/dialogbase.css?cache="+Math.random(), - tag:"link", - type:"text/css", - rel:"stylesheet" - }); - lang = editor.getLang(dialog.className.split( "-" )[2]); - if(lang){ - domUtils.on(window,'load',function () { - - var langImgPath = editor.options.langPath + editor.options.lang + "/images/"; - //针对静态资源 - for ( var i in lang["static"] ) { - var dom = $G( i ); - if(!dom) continue; - var tagName = dom.tagName, - content = lang["static"][i]; - if(content.src){ - //clone - content = utils.extend({},content,false); - content.src = langImgPath + content.src; - } - if(content.style){ - content = utils.extend({},content,false); - content.style = content.style.replace(/url\s*\(/g,"url(" + langImgPath) - } - switch ( tagName.toLowerCase() ) { - case "var": - dom.parentNode.replaceChild( document.createTextNode( content ), dom ); - break; - case "select": - var ops = dom.options; - for ( var j = 0, oj; oj = ops[j]; ) { - oj.innerHTML = content.options[j++]; - } - for ( var p in content ) { - p != "options" && dom.setAttribute( p, content[p] ); - } - break; - default : - domUtils.setAttributes( dom, content); - } - } - } ); - } - - -})(); - diff --git a/static/plugs/ueditor/dialogs/link/link.html b/static/plugs/ueditor/dialogs/link/link.html deleted file mode 100644 index 317a65079..000000000 --- a/static/plugs/ueditor/dialogs/link/link.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                  - - -
                  - - - diff --git a/static/plugs/ueditor/dialogs/map/map.html b/static/plugs/ueditor/dialogs/map/map.html deleted file mode 100644 index 9857f2f69..000000000 --- a/static/plugs/ueditor/dialogs/map/map.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - - -
                  - - - - - - - - - -
                  ::
                  -
                  - -
                  - - - - - diff --git a/static/plugs/ueditor/dialogs/map/show.html b/static/plugs/ueditor/dialogs/map/show.html deleted file mode 100644 index 329cfebfb..000000000 --- a/static/plugs/ueditor/dialogs/map/show.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - 百度地图API自定义地图 - - - - - - - -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/music/music.css b/static/plugs/ueditor/dialogs/music/music.css deleted file mode 100644 index 82d6118b8..000000000 --- a/static/plugs/ueditor/dialogs/music/music.css +++ /dev/null @@ -1,30 +0,0 @@ -.wrapper{margin: 5px 10px;} - -.searchBar{height:30px;padding:7px 0 3px;text-align:center;} -.searchBtn{font-size:13px;height:24px;} - -.resultBar{width:460px;margin:5px auto;border: 1px solid #CCC;border-radius: 5px;box-shadow: 2px 2px 5px #D3D6DA;overflow: hidden;} - -.listPanel{overflow: hidden;} -.panelon{display:block;} -.paneloff{display:none} - -.page{width:220px;margin:20px auto;overflow: hidden;} -.pageon{float:right;width:24px;line-height:24px;height:24px;margin-right: 5px;background: none;border: none;color: #000;font-weight: bold;text-align:center} -.pageoff{float:right;width:24px;line-height:24px;height:24px;cursor:pointer;background-color: #fff; - border: 1px solid #E7ECF0;color: #2D64B3;margin-right: 5px;text-decoration: none;text-align:center;} - -.m-box{width:460px;} -.m-m{float: left;line-height: 20px;height: 20px;} -.m-h{height:24px;line-height:24px;padding-left: 46px;background-color:#FAFAFA;border-bottom: 1px solid #DAD8D8;font-weight: bold;font-size: 12px;color: #333;} -.m-l{float:left;width:40px; } -.m-t{float:left;width:140px;} -.m-s{float:left;width:110px;} -.m-z{float:left;width:100px;} -.m-try-t{float: left;width: 60px;;} - -.m-try{float:left;width:20px;height:20px;background:url('http://static.tieba.baidu.com/tb/editor/images/try_music.gif') no-repeat ;} -.m-trying{float:left;width:20px;height:20px;background:url('http://static.tieba.baidu.com/tb/editor/images/stop_music.gif') no-repeat ;} - -.loading{width:95px;height:7px;font-size:7px;margin:60px auto;background:url(http://static.tieba.baidu.com/tb/editor/images/loading.gif) no-repeat} -.empty{width:300px;height:40px;padding:2px;margin:50px auto;line-height:40px; color:#006699;text-align:center;} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/music/music.html b/static/plugs/ueditor/dialogs/music/music.html deleted file mode 100644 index 7dad7a7c4..000000000 --- a/static/plugs/ueditor/dialogs/music/music.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - 插入音乐 - - - - -
                  - -
                  - -
                  -
                  -
                  -
                  - - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/music/music.js b/static/plugs/ueditor/dialogs/music/music.js deleted file mode 100644 index e43ea54bf..000000000 --- a/static/plugs/ueditor/dialogs/music/music.js +++ /dev/null @@ -1,192 +0,0 @@ -function Music() { - this.init(); -} -(function () { - var pages = [], - panels = [], - selectedItem = null; - Music.prototype = { - total:70, - pageSize:10, - dataUrl:"http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.search.common", - playerUrl:"http://box.baidu.com/widget/flash/bdspacesong.swf", - - init:function () { - var me = this; - domUtils.on($G("J_searchName"), "keyup", function (event) { - var e = window.event || event; - if (e.keyCode == 13) { - me.dosearch(); - } - }); - domUtils.on($G("J_searchBtn"), "click", function () { - me.dosearch(); - }); - }, - callback:function (data) { - var me = this; - me.data = data.song_list; - setTimeout(function () { - $G('J_resultBar').innerHTML = me._renderTemplate(data.song_list); - }, 300); - }, - dosearch:function () { - var me = this; - selectedItem = null; - var key = $G('J_searchName').value; - if (utils.trim(key) == "")return false; - key = encodeURIComponent(key); - me._sent(key); - }, - doselect:function (i) { - var me = this; - if (typeof i == 'object') { - selectedItem = i; - } else if (typeof i == 'number') { - selectedItem = me.data[i]; - } - }, - onpageclick:function (id) { - var me = this; - for (var i = 0; i < pages.length; i++) { - $G(pages[i]).className = 'pageoff'; - $G(panels[i]).className = 'paneloff'; - } - $G('page' + id).className = 'pageon'; - $G('panel' + id).className = 'panelon'; - }, - listenTest:function (elem) { - var me = this, - view = $G('J_preview'), - is_play_action = (elem.className == 'm-try'), - old_trying = me._getTryingElem(); - - if (old_trying) { - old_trying.className = 'm-try'; - view.innerHTML = ''; - } - if (is_play_action) { - elem.className = 'm-trying'; - view.innerHTML = me._buildMusicHtml(me._getUrl(true)); - } - }, - _sent:function (param) { - var me = this; - $G('J_resultBar').innerHTML = '
                  '; - - utils.loadFile(document, { - src:me.dataUrl + '&query=' + param + '&page_size=' + me.total + '&callback=music.callback&.r=' + Math.random(), - tag:"script", - type:"text/javascript", - defer:"defer" - }); - }, - _removeHtml:function (str) { - var reg = /<\s*\/?\s*[^>]*\s*>/gi; - return str.replace(reg, ""); - }, - _getUrl:function (isTryListen) { - var me = this; - var param = 'from=tiebasongwidget&url=&name=' + encodeURIComponent(me._removeHtml(selectedItem.title)) + '&artist=' - + encodeURIComponent(me._removeHtml(selectedItem.author)) + '&extra=' - + encodeURIComponent(me._removeHtml(selectedItem.album_title)) - + '&autoPlay='+isTryListen+'' + '&loop=true'; - return me.playerUrl + "?" + param; - }, - _getTryingElem:function () { - var s = $G('J_listPanel').getElementsByTagName('span'); - - for (var i = 0; i < s.length; i++) { - if (s[i].className == 'm-trying') - return s[i]; - } - return null; - }, - _buildMusicHtml:function (playerUrl) { - var html = ' 12) - return s.substring(0, 5) + '...'; - if (!s) s = " "; - return s; - }, - _rebuildData:function (data) { - var me = this, - newData = [], - d = me.pageSize, - itembox; - for (var i = 0; i < data.length; i++) { - if ((i + d) % d == 0) { - itembox = []; - newData.push(itembox) - } - itembox.push(data[i]); - } - return newData; - }, - _renderTemplate:function (data) { - var me = this; - if (data.length == 0)return '
                  ' + lang.emptyTxt + '
                  '; - data = me._rebuildData(data); - var s = [], p = [], t = []; - s.push('
                  '); - p.push('
                  '); - for (var i = 0, tmpList; tmpList = data[i++];) { - panels.push('panel' + i); - pages.push('page' + i); - if (i == 1) { - s.push('
                  '); - if (data.length != 1) { - t.push('
                  ' + (i ) + '
                  '); - } - } else { - s.push('
                  '); - t.push('
                  ' + (i ) + '
                  '); - } - s.push('
                  '); - s.push('
                  ' + lang.chapter + '' + lang.singer - + '' + lang.special + '' + lang.listenTest + '
                  '); - for (var j = 0, tmpObj; tmpObj = tmpList[j++];) { - s.push(''); - } - s.push('
                  '); - s.push('
                  '); - } - t.reverse(); - p.push(t.join('')); - s.push('
                  '); - p.push('
                  '); - return s.join('') + p.join(''); - }, - exec:function () { - var me = this; - if (selectedItem == null) return; - $G('J_preview').innerHTML = ""; - editor.execCommand('music', { - url:me._getUrl(false), - width:400, - height:95 - }); - } - }; -})(); - - - diff --git a/static/plugs/ueditor/dialogs/preview/preview.html b/static/plugs/ueditor/dialogs/preview/preview.html deleted file mode 100644 index f6b433bcf..000000000 --- a/static/plugs/ueditor/dialogs/preview/preview.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - -
                  - -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/scrawl/images/addimg.png b/static/plugs/ueditor/dialogs/scrawl/images/addimg.png deleted file mode 100644 index 03a87135b..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/addimg.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/brush.png b/static/plugs/ueditor/dialogs/scrawl/images/brush.png deleted file mode 100644 index efa6fdb01..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/brush.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/delimg.png b/static/plugs/ueditor/dialogs/scrawl/images/delimg.png deleted file mode 100644 index 5a892e40a..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/delimg.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/delimgH.png b/static/plugs/ueditor/dialogs/scrawl/images/delimgH.png deleted file mode 100644 index 2f0c5c9de..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/delimgH.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/empty.png b/static/plugs/ueditor/dialogs/scrawl/images/empty.png deleted file mode 100644 index 037519625..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/empty.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/emptyH.png b/static/plugs/ueditor/dialogs/scrawl/images/emptyH.png deleted file mode 100644 index 838ca7231..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/emptyH.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/eraser.png b/static/plugs/ueditor/dialogs/scrawl/images/eraser.png deleted file mode 100644 index 63e87cecb..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/eraser.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/redo.png b/static/plugs/ueditor/dialogs/scrawl/images/redo.png deleted file mode 100644 index 12cd9bbef..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/redo.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/redoH.png b/static/plugs/ueditor/dialogs/scrawl/images/redoH.png deleted file mode 100644 index d9f33d38a..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/redoH.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/scale.png b/static/plugs/ueditor/dialogs/scrawl/images/scale.png deleted file mode 100644 index 935a3f3e1..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/scale.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/scaleH.png b/static/plugs/ueditor/dialogs/scrawl/images/scaleH.png deleted file mode 100644 index 72e64a9d0..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/scaleH.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/size.png b/static/plugs/ueditor/dialogs/scrawl/images/size.png deleted file mode 100644 index 836684505..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/size.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/undo.png b/static/plugs/ueditor/dialogs/scrawl/images/undo.png deleted file mode 100644 index 084c7cc73..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/undo.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/images/undoH.png b/static/plugs/ueditor/dialogs/scrawl/images/undoH.png deleted file mode 100644 index fde7eb3c2..000000000 Binary files a/static/plugs/ueditor/dialogs/scrawl/images/undoH.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/scrawl/scrawl.css b/static/plugs/ueditor/dialogs/scrawl/scrawl.css deleted file mode 100644 index 4ea30c6e6..000000000 --- a/static/plugs/ueditor/dialogs/scrawl/scrawl.css +++ /dev/null @@ -1,72 +0,0 @@ -/*common -*/ -body{margin: 0;} -table{width:100%;} -table td{padding:2px 4px;vertical-align: middle;} -a{text-decoration: none;} -em{font-style: normal;} -.border_style1{border: 1px solid #ccc;border-radius: 5px;box-shadow:2px 2px 5px #d3d6da;} -/*module -*/ -.main{margin: 8px;overflow: hidden;} - -.hot{float:left;height:335px;} -.drawBoard{position: relative; cursor: crosshair;} -.brushBorad{position: absolute;left:0;top:0;z-index: 998;} -.picBoard{border: none;text-align: center;line-height: 300px;cursor: default;} -.operateBar{margin-top:10px;font-size:12px;text-align: center;} -.operateBar span{margin-left: 10px;} - -.drawToolbar{float:right;width:110px;height:300px;overflow: hidden;} -.colorBar{margin-top:10px;font-size: 12px;text-align: center;} -.colorBar a{display:block;width: 10px;height: 10px;border:1px solid #1006F1;border-radius: 3px; box-shadow:2px 2px 5px #d3d6da;opacity: 0.3} -.sectionBar{margin-top:15px;font-size: 12px;text-align: center;} -.sectionBar a{display:inline-block;width:10px;height:12px;color: #888;text-indent: -999px;opacity: 0.3} -.size1{background: url('images/size.png') 1px center no-repeat ;} -.size2{background: url('images/size.png') -10px center no-repeat;} -.size3{background: url('images/size.png') -22px center no-repeat;} -.size4{background: url('images/size.png') -35px center no-repeat;} - -.addImgH{position: relative;} -.addImgH_form{position: absolute;left: 18px;top: -1px;width: 75px;height: 21px;opacity: 0;cursor: pointer;} -.addImgH_form input{width: 100%;} -/*scrawl遮罩层 -*/ -.maskLayerNull{display: none;} -.maskLayer{position: absolute;top:0;left:0;width: 100%; height: 100%;opacity: 0.7; - background-color: #fff;text-align:center;font-weight:bold;line-height:300px;z-index: 1000;} -/*btn state -*/ -.previousStepH .icon{display: inline-block;width:16px;height:16px;background-image: url('images/undoH.png');cursor: pointer;} -.previousStepH .text{color:#888;cursor:pointer;} -.previousStep .icon{display: inline-block;width:16px;height:16px;background-image: url('images/undo.png');cursor:default;} -.previousStep .text{color:#ccc;cursor:default;} - -.nextStepH .icon{display: inline-block;width:16px;height:16px;background-image: url('images/redoH.png');cursor: pointer;} -.nextStepH .text{color:#888;cursor:pointer;} -.nextStep .icon{display: inline-block;width:16px;height:16px;background-image: url('images/redo.png');cursor:default;} -.nextStep .text{color:#ccc;cursor:default;} - -.clearBoardH .icon{display: inline-block;width:16px;height:16px;background-image: url('images/emptyH.png');cursor: pointer;} -.clearBoardH .text{color:#888;cursor:pointer;} -.clearBoard .icon{display: inline-block;width:16px;height:16px;background-image: url('images/empty.png');cursor:default;} -.clearBoard .text{color:#ccc;cursor:default;} - -.scaleBoardH .icon{display: inline-block;width:16px;height:16px;background-image: url('images/scaleH.png');cursor: pointer;} -.scaleBoardH .text{color:#888;cursor:pointer;} -.scaleBoard .icon{display: inline-block;width:16px;height:16px;background-image: url('images/scale.png');cursor:default;} -.scaleBoard .text{color:#ccc;cursor:default;} - -.removeImgH .icon{display: inline-block;width:16px;height:16px;background-image: url('images/delimgH.png');cursor: pointer;} -.removeImgH .text{color:#888;cursor:pointer;} -.removeImg .icon{display: inline-block;width:16px;height:16px;background-image: url('images/delimg.png');cursor:default;} -.removeImg .text{color:#ccc;cursor:default;} - -.addImgH .icon{vertical-align:top;display: inline-block;width:16px;height:16px;background-image: url('images/addimg.png')} -.addImgH .text{color:#888;cursor:pointer;} -/*icon -*/ -.brushIcon{display: inline-block;width:16px;height:16px;background-image: url('images/brush.png')} -.eraserIcon{display: inline-block;width:16px;height:16px;background-image: url('images/eraser.png')} - - diff --git a/static/plugs/ueditor/dialogs/scrawl/scrawl.html b/static/plugs/ueditor/dialogs/scrawl/scrawl.html deleted file mode 100644 index 13f6ab13f..000000000 --- a/static/plugs/ueditor/dialogs/scrawl/scrawl.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - -
                  -
                  -
                  - -
                  -
                  -
                  - - - - - - - - - - - - - - - - -
                  -
                  -
                  -
                  -
                  - - 1 - 3 - 5 - 7 -
                  -
                  - - 1 - 3 - 5 - 7 -
                  -
                  -
                  - - -
                  - -
                  - -
                  -
                  -
                  - - - - -
                  -
                  -
                  -
                  - - - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/scrawl/scrawl.js b/static/plugs/ueditor/dialogs/scrawl/scrawl.js deleted file mode 100644 index cadb0cb2f..000000000 --- a/static/plugs/ueditor/dialogs/scrawl/scrawl.js +++ /dev/null @@ -1,671 +0,0 @@ -/** - * Created with JetBrains PhpStorm. - * User: xuheng - * Date: 12-5-22 - * Time: 上午11:38 - * To change this template use File | Settings | File Templates. - */ -var scrawl = function (options) { - options && this.initOptions(options); -}; -(function () { - var canvas = $G("J_brushBoard"), - context = canvas.getContext('2d'), - drawStep = [], //undo redo存储 - drawStepIndex = 0; //undo redo指针 - - scrawl.prototype = { - isScrawl:false, //是否涂鸦 - brushWidth:-1, //画笔粗细 - brushColor:"", //画笔颜色 - - initOptions:function (options) { - var me = this; - me.originalState(options);//初始页面状态 - me._buildToolbarColor(options.colorList);//动态生成颜色选择集合 - - me._addBoardListener(options.saveNum);//添加画板处理 - me._addOPerateListener(options.saveNum);//添加undo redo clearBoard处理 - me._addColorBarListener();//添加颜色选择处理 - me._addBrushBarListener();//添加画笔大小处理 - me._addEraserBarListener();//添加橡皮大小处理 - me._addAddImgListener();//添加增添背景图片处理 - me._addRemoveImgListenter();//删除背景图片处理 - me._addScalePicListenter();//添加缩放处理 - me._addClearSelectionListenter();//添加清楚选中状态处理 - - me._originalColorSelect(options.drawBrushColor);//初始化颜色选中 - me._originalBrushSelect(options.drawBrushSize);//初始化画笔选中 - me._clearSelection();//清楚选中状态 - }, - - originalState:function (options) { - var me = this; - - me.brushWidth = options.drawBrushSize;//同步画笔粗细 - me.brushColor = options.drawBrushColor;//同步画笔颜色 - - context.lineWidth = me.brushWidth;//初始画笔大小 - context.strokeStyle = me.brushColor;//初始画笔颜色 - context.fillStyle = "transparent";//初始画布背景颜色 - context.lineCap = "round";//去除锯齿 - context.fill(); - }, - _buildToolbarColor:function (colorList) { - var tmp = null, arr = []; - arr.push(""); - for (var i = 0, color; color = colorList[i++];) { - if ((i - 1) % 5 == 0) { - if (i != 1) { - arr.push(""); - } - arr.push(""); - } - tmp = '#' + color; - arr.push(""); - } - arr.push("
                  "); - $G("J_colorBar").innerHTML = arr.join(""); - }, - - _addBoardListener:function (saveNum) { - var me = this, - margin = 0, - startX = -1, - startY = -1, - isMouseDown = false, - isMouseMove = false, - isMouseUp = false, - buttonPress = 0, button, flag = ''; - - margin = parseInt(domUtils.getComputedStyle($G("J_wrap"), "margin-left")); - drawStep.push(context.getImageData(0, 0, context.canvas.width, context.canvas.height)); - drawStepIndex += 1; - - domUtils.on(canvas, ["mousedown", "mousemove", "mouseup", "mouseout"], function (e) { - button = browser.webkit ? e.which : buttonPress; - switch (e.type) { - case 'mousedown': - buttonPress = 1; - flag = 1; - isMouseDown = true; - isMouseUp = false; - isMouseMove = false; - me.isScrawl = true; - startX = e.clientX - margin;//10为外边距总和 - startY = e.clientY - margin; - context.beginPath(); - break; - case 'mousemove' : - if (!flag && button == 0) { - return; - } - if (!flag && button) { - startX = e.clientX - margin;//10为外边距总和 - startY = e.clientY - margin; - context.beginPath(); - flag = 1; - } - if (isMouseUp || !isMouseDown) { - return; - } - var endX = e.clientX - margin, - endY = e.clientY - margin; - - context.moveTo(startX, startY); - context.lineTo(endX, endY); - context.stroke(); - startX = endX; - startY = endY; - isMouseMove = true; - break; - case 'mouseup': - buttonPress = 0; - if (!isMouseDown)return; - if (!isMouseMove) { - context.arc(startX, startY, context.lineWidth, 0, Math.PI * 2, false); - context.fillStyle = context.strokeStyle; - context.fill(); - } - context.closePath(); - me._saveOPerate(saveNum); - isMouseDown = false; - isMouseMove = false; - isMouseUp = true; - startX = -1; - startY = -1; - break; - case 'mouseout': - flag = ''; - buttonPress = 0; - if (button == 1) return; - context.closePath(); - break; - } - }); - }, - _addOPerateListener:function (saveNum) { - var me = this; - domUtils.on($G("J_previousStep"), "click", function () { - if (drawStepIndex > 1) { - drawStepIndex -= 1; - context.clearRect(0, 0, context.canvas.width, context.canvas.height); - context.putImageData(drawStep[drawStepIndex - 1], 0, 0); - me.btn2Highlight("J_nextStep"); - drawStepIndex == 1 && me.btn2disable("J_previousStep"); - } - }); - domUtils.on($G("J_nextStep"), "click", function () { - if (drawStepIndex > 0 && drawStepIndex < drawStep.length) { - context.clearRect(0, 0, context.canvas.width, context.canvas.height); - context.putImageData(drawStep[drawStepIndex], 0, 0); - drawStepIndex += 1; - me.btn2Highlight("J_previousStep"); - drawStepIndex == drawStep.length && me.btn2disable("J_nextStep"); - } - }); - domUtils.on($G("J_clearBoard"), "click", function () { - context.clearRect(0, 0, context.canvas.width, context.canvas.height); - drawStep = []; - me._saveOPerate(saveNum); - drawStepIndex = 1; - me.isScrawl = false; - me.btn2disable("J_previousStep"); - me.btn2disable("J_nextStep"); - me.btn2disable("J_clearBoard"); - }); - }, - _addColorBarListener:function () { - var me = this; - domUtils.on($G("J_colorBar"), "click", function (e) { - var target = me.getTarget(e), - color = target.title; - if (!!color) { - me._addColorSelect(target); - - me.brushColor = color; - context.globalCompositeOperation = "source-over"; - context.lineWidth = me.brushWidth; - context.strokeStyle = color; - } - }); - }, - _addBrushBarListener:function () { - var me = this; - domUtils.on($G("J_brushBar"), "click", function (e) { - var target = me.getTarget(e), - size = browser.ie ? target.innerText : target.text; - if (!!size) { - me._addBESelect(target); - - context.globalCompositeOperation = "source-over"; - context.lineWidth = parseInt(size); - context.strokeStyle = me.brushColor; - me.brushWidth = context.lineWidth; - } - }); - }, - _addEraserBarListener:function () { - var me = this; - domUtils.on($G("J_eraserBar"), "click", function (e) { - var target = me.getTarget(e), - size = browser.ie ? target.innerText : target.text; - if (!!size) { - me._addBESelect(target); - - context.lineWidth = parseInt(size); - context.globalCompositeOperation = "destination-out"; - context.strokeStyle = "#FFF"; - } - }); - }, - _addAddImgListener:function () { - var file = $G("J_imgTxt"); - if (!window.FileReader) { - $G("J_addImg").style.display = 'none'; - $G("J_removeImg").style.display = 'none'; - $G("J_sacleBoard").style.display = 'none'; - } - domUtils.on(file, "change", function (e) { - var frm = file.parentNode; - addMaskLayer(lang.backgroundUploading); - - var target = e.target || e.srcElement, - reader = new FileReader(); - reader.onload = function(evt){ - var target = evt.target || evt.srcElement; - ue_callback(target.result, 'SUCCESS'); - }; - reader.readAsDataURL(target.files[0]); - frm.reset(); - }); - }, - _addRemoveImgListenter:function () { - var me = this; - domUtils.on($G("J_removeImg"), "click", function () { - $G("J_picBoard").innerHTML = ""; - me.btn2disable("J_removeImg"); - me.btn2disable("J_sacleBoard"); - }); - }, - _addScalePicListenter:function () { - domUtils.on($G("J_sacleBoard"), "click", function () { - var picBoard = $G("J_picBoard"), - scaleCon = $G("J_scaleCon"), - img = picBoard.children[0]; - - if (img) { - if (!scaleCon) { - picBoard.style.cssText = "position:relative;z-index:999;"+picBoard.style.cssText; - img.style.cssText = "position: absolute;top:" + (canvas.height - img.height) / 2 + "px;left:" + (canvas.width - img.width) / 2 + "px;"; - var scale = new ScaleBoy(); - picBoard.appendChild(scale.init()); - scale.startScale(img); - } else { - if (scaleCon.style.visibility == "visible") { - scaleCon.style.visibility = "hidden"; - picBoard.style.position = ""; - picBoard.style.zIndex = ""; - } else { - scaleCon.style.visibility = "visible"; - picBoard.style.cssText += "position:relative;z-index:999"; - } - } - } - }); - }, - _addClearSelectionListenter:function () { - var doc = document; - domUtils.on(doc, 'mousemove', function (e) { - if (browser.ie && browser.version < 11) - doc.selection.clear(); - else - window.getSelection().removeAllRanges(); - }); - }, - _clearSelection:function () { - var list = ["J_operateBar", "J_colorBar", "J_brushBar", "J_eraserBar", "J_picBoard"]; - for (var i = 0, group; group = list[i++];) { - domUtils.unSelectable($G(group)); - } - }, - - _saveOPerate:function (saveNum) { - var me = this; - if (drawStep.length <= saveNum) { - if(drawStepIndex"); - } - scale.innerHTML = arr.join(""); - return scale; - } - - var rect = [ - //[left, top, width, height] - [1, 1, -1, -1], - [0, 1, 0, -1], - [0, 1, 1, -1], - [1, 0, -1, 0], - [0, 0, 1, 0], - [1, 0, -1, 1], - [0, 0, 0, 1], - [0, 0, 1, 1] - ]; - ScaleBoy.prototype = { - init:function () { - _appendStyle(); - var me = this, - scale = me.dom = _getDom(); - - me.scaleMousemove.fp = me; - domUtils.on(scale, 'mousedown', function (e) { - var target = e.target || e.srcElement; - me.start = {x:e.clientX, y:e.clientY}; - if (target.className.indexOf('hand') != -1) { - me.dir = target.className.replace('hand', ''); - } - domUtils.on(document.body, 'mousemove', me.scaleMousemove); - e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true; - }); - domUtils.on(document.body, 'mouseup', function (e) { - if (me.start) { - domUtils.un(document.body, 'mousemove', me.scaleMousemove); - if (me.moved) { - me.updateScaledElement({position:{x:scale.style.left, y:scale.style.top}, size:{w:scale.style.width, h:scale.style.height}}); - } - delete me.start; - delete me.moved; - delete me.dir; - } - }); - return scale; - }, - startScale:function (objElement) { - var me = this, Idom = me.dom; - - Idom.style.cssText = 'visibility:visible;top:' + objElement.style.top + ';left:' + objElement.style.left + ';width:' + objElement.offsetWidth + 'px;height:' + objElement.offsetHeight + 'px;'; - me.scalingElement = objElement; - }, - updateScaledElement:function (objStyle) { - var cur = this.scalingElement, - pos = objStyle.position, - size = objStyle.size; - if (pos) { - typeof pos.x != 'undefined' && (cur.style.left = pos.x); - typeof pos.y != 'undefined' && (cur.style.top = pos.y); - } - if (size) { - size.w && (cur.style.width = size.w); - size.h && (cur.style.height = size.h); - } - }, - updateStyleByDir:function (dir, offset) { - var me = this, - dom = me.dom, tmp; - - rect['def'] = [1, 1, 0, 0]; - if (rect[dir][0] != 0) { - tmp = parseInt(dom.style.left) + offset.x; - dom.style.left = me._validScaledProp('left', tmp) + 'px'; - } - if (rect[dir][1] != 0) { - tmp = parseInt(dom.style.top) + offset.y; - dom.style.top = me._validScaledProp('top', tmp) + 'px'; - } - if (rect[dir][2] != 0) { - tmp = dom.clientWidth + rect[dir][2] * offset.x; - dom.style.width = me._validScaledProp('width', tmp) + 'px'; - } - if (rect[dir][3] != 0) { - tmp = dom.clientHeight + rect[dir][3] * offset.y; - dom.style.height = me._validScaledProp('height', tmp) + 'px'; - } - if (dir === 'def') { - me.updateScaledElement({position:{x:dom.style.left, y:dom.style.top}}); - } - }, - scaleMousemove:function (e) { - var me = arguments.callee.fp, - start = me.start, - dir = me.dir || 'def', - offset = {x:e.clientX - start.x, y:e.clientY - start.y}; - - me.updateStyleByDir(dir, offset); - arguments.callee.fp.start = {x:e.clientX, y:e.clientY}; - arguments.callee.fp.moved = 1; - }, - _validScaledProp:function (prop, value) { - var ele = this.dom, - wrap = $G("J_picBoard"); - - value = isNaN(value) ? 0 : value; - switch (prop) { - case 'left': - return value < 0 ? 0 : (value + ele.clientWidth) > wrap.clientWidth ? wrap.clientWidth - ele.clientWidth : value; - case 'top': - return value < 0 ? 0 : (value + ele.clientHeight) > wrap.clientHeight ? wrap.clientHeight - ele.clientHeight : value; - case 'width': - return value <= 0 ? 1 : (value + ele.offsetLeft) > wrap.clientWidth ? wrap.clientWidth - ele.offsetLeft : value; - case 'height': - return value <= 0 ? 1 : (value + ele.offsetTop) > wrap.clientHeight ? wrap.clientHeight - ele.offsetTop : value; - } - } - }; -})(); - -//后台回调 -function ue_callback(url, state) { - var doc = document, - picBorard = $G("J_picBoard"), - img = doc.createElement("img"); - - //图片缩放 - function scale(img, max, oWidth, oHeight) { - var width = 0, height = 0, percent, ow = img.width || oWidth, oh = img.height || oHeight; - if (ow > max || oh > max) { - if (ow >= oh) { - if (width = ow - max) { - percent = (width / ow).toFixed(2); - img.height = oh - oh * percent; - img.width = max; - } - } else { - if (height = oh - max) { - percent = (height / oh).toFixed(2); - img.width = ow - ow * percent; - img.height = max; - } - } - } - } - - //移除遮罩层 - removeMaskLayer(); - //状态响应 - if (state == "SUCCESS") { - picBorard.innerHTML = ""; - img.onload = function () { - scale(this, 300); - picBorard.appendChild(img); - - var obj = new scrawl(); - obj.btn2Highlight("J_removeImg"); - //trace 2457 - obj.btn2Highlight("J_sacleBoard"); - }; - img.src = url; - } else { - alert(state); - } -} -//去掉遮罩层 -function removeMaskLayer() { - var maskLayer = $G("J_maskLayer"); - maskLayer.className = "maskLayerNull"; - maskLayer.innerHTML = ""; - dialog.buttons[0].setDisabled(false); -} -//添加遮罩层 -function addMaskLayer(html) { - var maskLayer = $G("J_maskLayer"); - dialog.buttons[0].setDisabled(true); - maskLayer.className = "maskLayer"; - maskLayer.innerHTML = html; -} -//执行确认按钮方法 -function exec(scrawlObj) { - if (scrawlObj.isScrawl) { - addMaskLayer(lang.scrawlUpLoading); - var base64 = scrawlObj.getCanvasData(); - if (!!base64) { - var options = { - timeout:100000, - onsuccess:function (xhr) { - if (!scrawlObj.isCancelScrawl) { - var responseObj; - responseObj = eval("(" + xhr.responseText + ")"); - if (responseObj.state == "SUCCESS") { - var imgObj = {}, - url = editor.options.scrawlUrlPrefix + responseObj.url; - imgObj.src = url; - imgObj._src = url; - imgObj.alt = responseObj.original || ''; - imgObj.title = responseObj.title || ''; - editor.execCommand("insertImage", imgObj); - dialog.close(); - } else { - alert(responseObj.state); - } - - } - }, - onerror:function () { - alert(lang.imageError); - dialog.close(); - } - }; - options[editor.getOpt('scrawlFieldName')] = base64; - - var actionUrl = editor.getActionUrl(editor.getOpt('scrawlActionName')), - params = utils.serializeParam(editor.queryCommandValue('serverparam')) || '', - url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?':'&') + params); - ajax.request(url, options); - } - } else { - addMaskLayer(lang.noScarwl + "   "); - } -} - diff --git a/static/plugs/ueditor/dialogs/searchreplace/searchreplace.html b/static/plugs/ueditor/dialogs/searchreplace/searchreplace.html deleted file mode 100644 index b3eaaa5c9..000000000 --- a/static/plugs/ueditor/dialogs/searchreplace/searchreplace.html +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - -
                  - -
                  -
                  - - - - - - - - - - - - - - - - - - - - - - -
                  :
                  - -
                  - - -
                  -   -
                  - -
                  -
                  -
                  - - - - - - - - - - - - - - - - - - - - - - - - - - -
                  :
                  :
                  - -
                  - - - - -
                  -   -
                  - -
                  -
                  -
                  -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/searchreplace/searchreplace.js b/static/plugs/ueditor/dialogs/searchreplace/searchreplace.js deleted file mode 100644 index b55bb7ae7..000000000 --- a/static/plugs/ueditor/dialogs/searchreplace/searchreplace.js +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Created with JetBrains PhpStorm. - * User: xuheng - * Date: 12-9-26 - * Time: 下午12:29 - * To change this template use File | Settings | File Templates. - */ - -//清空上次查选的痕迹 -editor.firstForSR = 0; -editor.currentRangeForSR = null; -//给tab注册切换事件 -/** - * tab点击处理事件 - * @param tabHeads - * @param tabBodys - * @param obj - */ -function clickHandler( tabHeads,tabBodys,obj ) { - //head样式更改 - for ( var k = 0, len = tabHeads.length; k < len; k++ ) { - tabHeads[k].className = ""; - } - obj.className = "focus"; - //body显隐 - var tabSrc = obj.getAttribute( "tabSrc" ); - for ( var j = 0, length = tabBodys.length; j < length; j++ ) { - var body = tabBodys[j], - id = body.getAttribute( "id" ); - if ( id != tabSrc ) { - body.style.zIndex = 1; - } else { - body.style.zIndex = 200; - } - } - -} - -/** - * TAB切换 - * @param tabParentId tab的父节点ID或者对象本身 - */ -function switchTab( tabParentId ) { - var tabElements = $G( tabParentId ).children, - tabHeads = tabElements[0].children, - tabBodys = tabElements[1].children; - - for ( var i = 0, length = tabHeads.length; i < length; i++ ) { - var head = tabHeads[i]; - if ( head.className === "focus" )clickHandler(tabHeads,tabBodys, head ); - head.onclick = function () { - clickHandler(tabHeads,tabBodys,this); - } - } -} -$G('searchtab').onmousedown = function(){ - $G('search-msg').innerHTML = ''; - $G('replace-msg').innerHTML = '' -} -//是否区分大小写 -function getMatchCase(id) { - return $G(id).checked ? true : false; -} -//查找 -$G("nextFindBtn").onclick = function (txt, dir, mcase) { - var findtxt = $G("findtxt").value, obj; - if (!findtxt) { - return false; - } - obj = { - searchStr:findtxt, - dir:1, - casesensitive:getMatchCase("matchCase") - }; - if (!frCommond(obj)) { - var bk = editor.selection.getRange().createBookmark(); - $G('search-msg').innerHTML = lang.getEnd; - editor.selection.getRange().moveToBookmark(bk).select(); - - - } -}; -$G("nextReplaceBtn").onclick = function (txt, dir, mcase) { - var findtxt = $G("findtxt1").value, obj; - if (!findtxt) { - return false; - } - obj = { - searchStr:findtxt, - dir:1, - casesensitive:getMatchCase("matchCase1") - }; - frCommond(obj); -}; -$G("preFindBtn").onclick = function (txt, dir, mcase) { - var findtxt = $G("findtxt").value, obj; - if (!findtxt) { - return false; - } - obj = { - searchStr:findtxt, - dir:-1, - casesensitive:getMatchCase("matchCase") - }; - if (!frCommond(obj)) { - $G('search-msg').innerHTML = lang.getStart; - } -}; -$G("preReplaceBtn").onclick = function (txt, dir, mcase) { - var findtxt = $G("findtxt1").value, obj; - if (!findtxt) { - return false; - } - obj = { - searchStr:findtxt, - dir:-1, - casesensitive:getMatchCase("matchCase1") - }; - frCommond(obj); -}; -//替换 -$G("repalceBtn").onclick = function () { - var findtxt = $G("findtxt1").value.replace(/^\s|\s$/g, ""), obj, - replacetxt = $G("replacetxt").value.replace(/^\s|\s$/g, ""); - if (!findtxt) { - return false; - } - if (findtxt == replacetxt || (!getMatchCase("matchCase1") && findtxt.toLowerCase() == replacetxt.toLowerCase())) { - return false; - } - obj = { - searchStr:findtxt, - dir:1, - casesensitive:getMatchCase("matchCase1"), - replaceStr:replacetxt - }; - frCommond(obj); -}; -//全部替换 -$G("repalceAllBtn").onclick = function () { - var findtxt = $G("findtxt1").value.replace(/^\s|\s$/g, ""), obj, - replacetxt = $G("replacetxt").value.replace(/^\s|\s$/g, ""); - if (!findtxt) { - return false; - } - if (findtxt == replacetxt || (!getMatchCase("matchCase1") && findtxt.toLowerCase() == replacetxt.toLowerCase())) { - return false; - } - obj = { - searchStr:findtxt, - casesensitive:getMatchCase("matchCase1"), - replaceStr:replacetxt, - all:true - }; - var num = frCommond(obj); - if (num) { - $G('replace-msg').innerHTML = lang.countMsg.replace("{#count}", num); - } -}; -//执行 -var frCommond = function (obj) { - return editor.execCommand("searchreplace", obj); -}; -switchTab("searchtab"); \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/snapscreen/snapscreen.html b/static/plugs/ueditor/dialogs/snapscreen/snapscreen.html deleted file mode 100644 index f3593268a..000000000 --- a/static/plugs/ueditor/dialogs/snapscreen/snapscreen.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - -
                  -

                  -
                  -
                  -
                  -
                  -
                  -
                  - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/spechars/spechars.html b/static/plugs/ueditor/dialogs/spechars/spechars.html deleted file mode 100644 index d92f5150d..000000000 --- a/static/plugs/ueditor/dialogs/spechars/spechars.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - -
                  -
                  -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/spechars/spechars.js b/static/plugs/ueditor/dialogs/spechars/spechars.js deleted file mode 100644 index bd3b36d33..000000000 --- a/static/plugs/ueditor/dialogs/spechars/spechars.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Created with JetBrains PhpStorm. - * User: xuheng - * Date: 12-9-26 - * Time: 下午1:09 - * To change this template use File | Settings | File Templates. - */ -var charsContent = [ - { name:"tsfh", title:lang.tsfh, content:toArray("、,。,·,ˉ,ˇ,¨,〃,々,—,~,‖,…,‘,’,“,”,〔,〕,〈,〉,《,》,「,」,『,』,〖,〗,【,】,±,×,÷,∶,∧,∨,∑,∏,∪,∩,∈,∷,√,⊥,∥,∠,⌒,⊙,∫,∮,≡,≌,≈,∽,∝,≠,≮,≯,≤,≥,∞,∵,∴,♂,♀,°,′,″,℃,$,¤,¢,£,‰,§,№,☆,★,○,●,◎,◇,◆,□,■,△,▲,※,→,←,↑,↓,〓,〡,〢,〣,〤,〥,〦,〧,〨,〩,㊣,㎎,㎏,㎜,㎝,㎞,㎡,㏄,㏎,㏑,㏒,㏕,︰,¬,¦,℡,ˊ,ˋ,˙,–,―,‥,‵,℅,℉,↖,↗,↘,↙,∕,∟,∣,≒,≦,≧,⊿,═,║,╒,╓,╔,╕,╖,╗,╘,╙,╚,╛,╜,╝,╞,╟,╠,╡,╢,╣,╤,╥,╦,╧,╨,╩,╪,╫,╬,╭,╮,╯,╰,╱,╲,╳,▁,▂,▃,▄,▅,▆,▇,�,█,▉,▊,▋,▌,▍,▎,▏,▓,▔,▕,▼,▽,◢,◣,◤,◥,☉,⊕,〒,〝,〞")}, - { name:"lmsz", title:lang.lmsz, content:toArray("ⅰ,ⅱ,ⅲ,ⅳ,ⅴ,ⅵ,ⅶ,ⅷ,ⅸ,ⅹ,Ⅰ,Ⅱ,Ⅲ,Ⅳ,Ⅴ,Ⅵ,Ⅶ,Ⅷ,Ⅸ,Ⅹ,Ⅺ,Ⅻ")}, - { name:"szfh", title:lang.szfh, content:toArray("⒈,⒉,⒊,⒋,⒌,⒍,⒎,⒏,⒐,⒑,⒒,⒓,⒔,⒕,⒖,⒗,⒘,⒙,⒚,⒛,⑴,⑵,⑶,⑷,⑸,⑹,⑺,⑻,⑼,⑽,⑾,⑿,⒀,⒁,⒂,⒃,⒄,⒅,⒆,⒇,①,②,③,④,⑤,⑥,⑦,⑧,⑨,⑩,㈠,㈡,㈢,㈣,㈤,㈥,㈦,㈧,㈨,㈩")}, - { name:"rwfh", title:lang.rwfh, content:toArray("ぁ,あ,ぃ,い,ぅ,う,ぇ,え,ぉ,お,か,が,き,ぎ,く,ぐ,け,げ,こ,ご,さ,ざ,し,じ,す,ず,せ,ぜ,そ,ぞ,た,だ,ち,ぢ,っ,つ,づ,て,で,と,ど,な,に,ぬ,ね,の,は,ば,ぱ,ひ,び,ぴ,ふ,ぶ,ぷ,へ,べ,ぺ,ほ,ぼ,ぽ,ま,み,む,め,も,ゃ,や,ゅ,ゆ,ょ,よ,ら,り,る,れ,ろ,ゎ,わ,ゐ,ゑ,を,ん,ァ,ア,ィ,イ,ゥ,ウ,ェ,エ,ォ,オ,カ,ガ,キ,ギ,ク,グ,ケ,ゲ,コ,ゴ,サ,ザ,シ,ジ,ス,ズ,セ,ゼ,ソ,ゾ,タ,ダ,チ,ヂ,ッ,ツ,ヅ,テ,デ,ト,ド,ナ,ニ,ヌ,ネ,ノ,ハ,バ,パ,ヒ,ビ,ピ,フ,ブ,プ,ヘ,ベ,ペ,ホ,ボ,ポ,マ,ミ,ム,メ,モ,ャ,ヤ,ュ,ユ,ョ,ヨ,ラ,リ,ル,レ,ロ,ヮ,ワ,ヰ,ヱ,ヲ,ン,ヴ,ヵ,ヶ")}, - { name:"xlzm", title:lang.xlzm, content:toArray("Α,Β,Γ,Δ,Ε,Ζ,Η,Θ,Ι,Κ,Λ,Μ,Ν,Ξ,Ο,Π,Ρ,Σ,Τ,Υ,Φ,Χ,Ψ,Ω,α,β,γ,δ,ε,ζ,η,θ,ι,κ,λ,μ,ν,ξ,ο,π,ρ,σ,τ,υ,φ,χ,ψ,ω")}, - { name:"ewzm", title:lang.ewzm, content:toArray("А,Б,В,Г,Д,Е,Ё,Ж,З,И,Й,К,Л,М,Н,О,П,Р,С,Т,У,Ф,Х,Ц,Ч,Ш,Щ,Ъ,Ы,Ь,Э,Ю,Я,а,б,в,г,д,е,ё,ж,з,и,й,к,л,м,н,о,п,р,с,т,у,ф,х,ц,ч,ш,щ,ъ,ы,ь,э,ю,я")}, - { name:"pyzm", title:lang.pyzm, content:toArray("ā,á,ǎ,à,ē,é,ě,è,ī,í,ǐ,ì,ō,ó,ǒ,ò,ū,ú,ǔ,ù,ǖ,ǘ,ǚ,ǜ,ü")}, - { name:"yyyb", title:lang.yyyb, content:toArray("i:,i,e,æ,ʌ,ə:,ə,u:,u,ɔ:,ɔ,a:,ei,ai,ɔi,əu,au,iə,εə,uə,p,t,k,b,d,g,f,s,ʃ,θ,h,v,z,ʒ,ð,tʃ,tr,ts,dʒ,dr,dz,m,n,ŋ,l,r,w,j,")}, - { name:"zyzf", title:lang.zyzf, content:toArray("ㄅ,ㄆ,ㄇ,ㄈ,ㄉ,ㄊ,ㄋ,ㄌ,ㄍ,ㄎ,ㄏ,ㄐ,ㄑ,ㄒ,ㄓ,ㄔ,ㄕ,ㄖ,ㄗ,ㄘ,ㄙ,ㄚ,ㄛ,ㄜ,ㄝ,ㄞ,ㄟ,ㄠ,ㄡ,ㄢ,ㄣ,ㄤ,ㄥ,ㄦ,ㄧ,ㄨ")} -]; -(function createTab(content) { - for (var i = 0, ci; ci = content[i++];) { - var span = document.createElement("span"); - span.setAttribute("tabSrc", ci.name); - span.innerHTML = ci.title; - if (i == 1)span.className = "focus"; - domUtils.on(span, "click", function () { - var tmps = $G("tabHeads").children; - for (var k = 0, sk; sk = tmps[k++];) { - sk.className = ""; - } - tmps = $G("tabBodys").children; - for (var k = 0, sk; sk = tmps[k++];) { - sk.style.display = "none"; - } - this.className = "focus"; - $G(this.getAttribute("tabSrc")).style.display = ""; - }); - $G("tabHeads").appendChild(span); - domUtils.insertAfter(span, document.createTextNode("\n")); - var div = document.createElement("div"); - div.id = ci.name; - div.style.display = (i == 1) ? "" : "none"; - var cons = ci.content; - for (var j = 0, con; con = cons[j++];) { - var charSpan = document.createElement("span"); - charSpan.innerHTML = con; - domUtils.on(charSpan, "click", function () { - editor.execCommand("insertHTML", this.innerHTML); - dialog.close(); - }); - div.appendChild(charSpan); - } - $G("tabBodys").appendChild(div); - } -})(charsContent); -function toArray(str) { - return str.split(","); -} diff --git a/static/plugs/ueditor/dialogs/table/dragicon.png b/static/plugs/ueditor/dialogs/table/dragicon.png deleted file mode 100644 index f26203bf3..000000000 Binary files a/static/plugs/ueditor/dialogs/table/dragicon.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/table/edittable.css b/static/plugs/ueditor/dialogs/table/edittable.css deleted file mode 100644 index c6f9396c9..000000000 --- a/static/plugs/ueditor/dialogs/table/edittable.css +++ /dev/null @@ -1,84 +0,0 @@ -body{ - overflow: hidden; - width: 540px; -} -.wrapper { - margin: 10px auto 0; - font-size: 12px; - overflow: hidden; - width: 520px; - height: 315px; -} - -.clear { - clear: both; -} - -.wrapper .left { - float: left; - margin-left: 10px;; -} - -.wrapper .right { - float: right; - border-left: 2px dotted #EDEDED; - padding-left: 15px; -} - -.section { - margin-bottom: 15px; - width: 240px; - overflow: hidden; -} - -.section h3 { - font-weight: bold; - padding: 5px 0; - margin-bottom: 10px; - border-bottom: 1px solid #EDEDED; - font-size: 12px; -} - -.section ul { - list-style: none; - overflow: hidden; - clear: both; - -} - -.section li { - float: left; - width: 120px;; -} - -.section .tone { - width: 80px;; -} - -.section .preview { - width: 220px; -} - -.section .preview table { - text-align: center; - vertical-align: middle; - color: #666; -} - -.section .preview caption { - font-weight: bold; -} - -.section .preview td { - border-width: 1px; - border-style: solid; - height: 22px; -} - -.section .preview th { - border-style: solid; - border-color: #DDD; - border-width: 2px 1px 1px 1px; - height: 22px; - background-color: #F7F7F7; -} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/table/edittable.html b/static/plugs/ueditor/dialogs/table/edittable.html deleted file mode 100644 index 3c412fb82..000000000 --- a/static/plugs/ueditor/dialogs/table/edittable.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - -
                  -
                  -
                  -

                  -
                    -
                  • - -
                  • -
                  • - -
                  • -
                  -
                    -
                  • - -
                  • -
                  • - -
                  • -
                  -
                  -
                  -
                  -

                  -
                    -
                  • - -
                  • -
                  • - -
                  • -
                  -
                  -
                  -
                  -

                  -
                    -
                  • - - -
                  • -
                  -
                  -
                  -
                  -
                  -
                  -

                  -
                  -
                  -
                  -
                  -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/table/edittable.js b/static/plugs/ueditor/dialogs/table/edittable.js deleted file mode 100644 index 11dbee7c5..000000000 --- a/static/plugs/ueditor/dialogs/table/edittable.js +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Created with JetBrains PhpStorm. - * User: xuheng - * Date: 12-12-19 - * Time: 下午4:55 - * To change this template use File | Settings | File Templates. - */ -(function () { - var title = $G("J_title"), - titleCol = $G("J_titleCol"), - caption = $G("J_caption"), - sorttable = $G("J_sorttable"), - autoSizeContent = $G("J_autoSizeContent"), - autoSizePage = $G("J_autoSizePage"), - tone = $G("J_tone"), - me, - preview = $G("J_preview"); - - var editTable = function () { - me = this; - me.init(); - }; - editTable.prototype = { - init:function () { - var colorPiker = new UE.ui.ColorPicker({ - editor:editor - }), - colorPop = new UE.ui.Popup({ - editor:editor, - content:colorPiker - }); - - title.checked = editor.queryCommandState("inserttitle") == -1; - titleCol.checked = editor.queryCommandState("inserttitlecol") == -1; - caption.checked = editor.queryCommandState("insertcaption") == -1; - sorttable.checked = editor.queryCommandState("enablesort") == 1; - - var enablesortState = editor.queryCommandState("enablesort"), - disablesortState = editor.queryCommandState("disablesort"); - - sorttable.checked = !!(enablesortState < 0 && disablesortState >=0); - sorttable.disabled = !!(enablesortState < 0 && disablesortState < 0); - sorttable.title = enablesortState < 0 && disablesortState < 0 ? lang.errorMsg:''; - - me.createTable(title.checked, titleCol.checked, caption.checked); - me.setAutoSize(); - me.setColor(me.getColor()); - - domUtils.on(title, "click", me.titleHanler); - domUtils.on(titleCol, "click", me.titleColHanler); - domUtils.on(caption, "click", me.captionHanler); - domUtils.on(sorttable, "click", me.sorttableHanler); - domUtils.on(autoSizeContent, "click", me.autoSizeContentHanler); - domUtils.on(autoSizePage, "click", me.autoSizePageHanler); - - domUtils.on(tone, "click", function () { - colorPop.showAnchor(tone); - }); - domUtils.on(document, 'mousedown', function () { - colorPop.hide(); - }); - colorPiker.addListener("pickcolor", function () { - me.setColor(arguments[1]); - colorPop.hide(); - }); - colorPiker.addListener("picknocolor", function () { - me.setColor(""); - colorPop.hide(); - }); - }, - - createTable:function (hasTitle, hasTitleCol, hasCaption) { - var arr = [], - sortSpan = '^'; - arr.push(""); - if (hasCaption) { - arr.push("") - } - if (hasTitle) { - arr.push(""); - if(hasTitleCol) { arr.push(""); } - for (var j = 0; j < 5; j++) { - arr.push(""); - } - arr.push(""); - } - for (var i = 0; i < 6; i++) { - arr.push(""); - if(hasTitleCol) { arr.push("") } - for (var k = 0; k < 5; k++) { - arr.push("") - } - arr.push(""); - } - arr.push("
                  " + lang.captionName + "
                  " + lang.titleName + "" + lang.titleName + "
                  " + lang.titleName + "" + lang.cellsName + "
                  "); - preview.innerHTML = arr.join(""); - this.updateSortSpan(); - }, - titleHanler:function () { - var example = $G("J_example"), - frg=document.createDocumentFragment(), - color = domUtils.getComputedStyle(domUtils.getElementsByTagName(example, "td")[0], "border-color"), - colCount = example.rows[0].children.length; - - if (title.checked) { - example.insertRow(0); - for (var i = 0, node; i < colCount; i++) { - node = document.createElement("th"); - node.innerHTML = lang.titleName; - frg.appendChild(node); - } - example.rows[0].appendChild(frg); - - } else { - domUtils.remove(example.rows[0]); - } - me.setColor(color); - me.updateSortSpan(); - }, - titleColHanler:function () { - var example = $G("J_example"), - color = domUtils.getComputedStyle(domUtils.getElementsByTagName(example, "td")[0], "border-color"), - colArr = example.rows, - colCount = colArr.length; - - if (titleCol.checked) { - for (var i = 0, node; i < colCount; i++) { - node = document.createElement("th"); - node.innerHTML = lang.titleName; - colArr[i].insertBefore(node, colArr[i].children[0]); - } - } else { - for (var i = 0; i < colCount; i++) { - domUtils.remove(colArr[i].children[0]); - } - } - me.setColor(color); - me.updateSortSpan(); - }, - captionHanler:function () { - var example = $G("J_example"); - if (caption.checked) { - var row = document.createElement('caption'); - row.innerHTML = lang.captionName; - example.insertBefore(row, example.firstChild); - } else { - domUtils.remove(domUtils.getElementsByTagName(example, 'caption')[0]); - } - }, - sorttableHanler:function(){ - me.updateSortSpan(); - }, - autoSizeContentHanler:function () { - var example = $G("J_example"); - example.removeAttribute("width"); - }, - autoSizePageHanler:function () { - var example = $G("J_example"); - var tds = example.getElementsByTagName(example, "td"); - utils.each(tds, function (td) { - td.removeAttribute("width"); - }); - example.setAttribute('width', '100%'); - }, - updateSortSpan: function(){ - var example = $G("J_example"), - row = example.rows[0]; - - var spans = domUtils.getElementsByTagName(example,"span"); - utils.each(spans,function(span){ - span.parentNode.removeChild(span); - }); - if (sorttable.checked) { - utils.each(row.cells, function(cell, i){ - var span = document.createElement("span"); - span.innerHTML = "^"; - cell.appendChild(span); - }); - } - }, - getColor:function () { - var start = editor.selection.getStart(), color, - cell = domUtils.findParentByTagName(start, ["td", "th", "caption"], true); - color = cell && domUtils.getComputedStyle(cell, "border-color"); - if (!color) color = "#DDDDDD"; - return color; - }, - setColor:function (color) { - var example = $G("J_example"), - arr = domUtils.getElementsByTagName(example, "td").concat( - domUtils.getElementsByTagName(example, "th"), - domUtils.getElementsByTagName(example, "caption") - ); - - tone.value = color; - utils.each(arr, function (node) { - node.style.borderColor = color; - }); - - }, - setAutoSize:function () { - var me = this; - autoSizePage.checked = true; - me.autoSizePageHanler(); - } - }; - - new editTable; - - dialog.onok = function () { - editor.__hasEnterExecCommand = true; - - var checks = { - title:"inserttitle deletetitle", - titleCol:"inserttitlecol deletetitlecol", - caption:"insertcaption deletecaption", - sorttable:"enablesort disablesort" - }; - editor.fireEvent('saveScene'); - for(var i in checks){ - var cmds = checks[i].split(" "), - input = $G("J_" + i); - if(input["checked"]){ - editor.queryCommandState(cmds[0])!=-1 &&editor.execCommand(cmds[0]); - }else{ - editor.queryCommandState(cmds[1])!=-1 &&editor.execCommand(cmds[1]); - } - } - - editor.execCommand("edittable", tone.value); - autoSizeContent.checked ?editor.execCommand('adaptbytext') : ""; - autoSizePage.checked ? editor.execCommand("adaptbywindow") : ""; - editor.fireEvent('saveScene'); - - editor.__hasEnterExecCommand = false; - }; -})(); \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/table/edittd.html b/static/plugs/ueditor/dialogs/table/edittd.html deleted file mode 100644 index 49a52f719..000000000 --- a/static/plugs/ueditor/dialogs/table/edittd.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - -
                  - - -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/table/edittip.html b/static/plugs/ueditor/dialogs/table/edittip.html deleted file mode 100644 index 954f7bb66..000000000 --- a/static/plugs/ueditor/dialogs/table/edittip.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - 表格删除提示 - - - - -
                  -
                  - -
                  -
                  - -
                  -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/template/config.js b/static/plugs/ueditor/dialogs/template/config.js deleted file mode 100644 index 42e474b47..000000000 --- a/static/plugs/ueditor/dialogs/template/config.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Created with JetBrains PhpStorm. - * User: xuheng - * Date: 12-8-8 - * Time: 下午2:00 - * To change this template use File | Settings | File Templates. - */ -var templates = [ - { - "pre":"pre0.png", - 'title':lang.blank, - 'preHtml':'

                   欢迎使用UEditor!

                  ', - "html":'

                  欢迎使用UEditor!

                  ' - - }, - { - "pre":"pre1.png", - 'title':lang.blog, - 'preHtml':'

                  深入理解Range

                  UEditor二次开发

                  什么是Range

                  对于“插入”选项卡上的库,在设计时都充分考虑了其中的项与文档整体外观的协调性。


                  Range能干什么

                  在“开始”选项卡上,通过从快速样式库中为所选文本选择一种外观,您可以方便地更改文档中所选文本的格式。

                  ', - "html":'

                  [键入文档标题]

                  [键入文档副标题]

                  [标题 1]

                  对于“插入”选项卡上的库,在设计时都充分考虑了其中的项与文档整体外观的协调性。 您可以使用这些库来插入表格、页眉、页脚、列表、封面以及其他文档构建基块。 您创建的图片、图表或关系图也将与当前的文档外观协调一致。

                  [标题 2]

                  在“开始”选项卡上,通过从快速样式库中为所选文本选择一种外观,您可以方便地更改文档中所选文本的格式。 您还可以使用“开始”选项卡上的其他控件来直接设置文本格式。大多数控件都允许您选择是使用当前主题外观,还是使用某种直接指定的格式。

                  [标题 3]

                  对于“插入”选项卡上的库,在设计时都充分考虑了其中的项与文档整体外观的协调性。 您可以使用这些库来插入表格、页眉、页脚、列表、封面以及其他文档构建基块。 您创建的图片、图表或关系图也将与当前的文档外观协调一致。


                  ' - - }, - { - "pre":"pre2.png", - 'title':lang.resume, - 'preHtml':'

                  WEB前端开发简历


                  联系电话:[键入您的电话]

                  电子邮件:[键入您的电子邮件地址]

                  家庭住址:[键入您的地址]

                  目标职位

                  WEB前端研发工程师

                  学历

                  1. [起止时间] [学校名称] [所学专业] [所获学位]

                  工作经验


                  ', - "html":'

                  [此处键入简历标题]


                  【此处插入照片】


                  联系电话:[键入您的电话]


                  电子邮件:[键入您的电子邮件地址]


                  家庭住址:[键入您的地址]


                  目标职位

                  [此处键入您的期望职位]

                  学历

                  1. [键入起止时间] [键入学校名称] [键入所学专业] [键入所获学位]

                  2. [键入起止时间] [键入学校名称] [键入所学专业] [键入所获学位]

                  工作经验

                  1. [键入起止时间] [键入公司名称] [键入职位名称]

                    1. [键入负责项目] [键入项目简介]

                    2. [键入负责项目] [键入项目简介]

                  2. [键入起止时间] [键入公司名称] [键入职位名称]

                    1. [键入负责项目] [键入项目简介]

                  掌握技能

                   [这里可以键入您所掌握的技能]

                  ' - - }, - { - "pre":"pre3.png", - 'title':lang.richText, - 'preHtml':'

                  [此处键入文章标题]

                  图文混排方法

                  图片居左,文字围绕图片排版

                  方法:在文字前面插入图片,设置居左对齐,然后即可在右边输入多行文


                  还有没有什么其他的环绕方式呢?这里是居右环绕


                  欢迎大家多多尝试,为UEditor提供更多高质量模板!

                  ', - "html":'


                  [此处键入文章标题]

                  图文混排方法

                  1. 图片居左,文字围绕图片排版

                  方法:在文字前面插入图片,设置居左对齐,然后即可在右边输入多行文本


                  2. 图片居右,文字围绕图片排版

                  方法:在文字前面插入图片,设置居右对齐,然后即可在左边输入多行文本


                  3. 图片居中环绕排版

                  方法:亲,这个真心没有办法。。。



                  还有没有什么其他的环绕方式呢?这里是居右环绕


                  欢迎大家多多尝试,为UEditor提供更多高质量模板!


                  占位


                  占位


                  占位


                  占位


                  占位



                  ' - }, - { - "pre":"pre4.png", - 'title':lang.sciPapers, - 'preHtml':'

                  [键入文章标题]

                  摘要:这里可以输入很长很长很长很长很长很长很长很长很差的摘要

                  标题 1

                  这里可以输入很多内容,可以图文混排,可以有列表等。

                  标题 2

                  1. 列表 1

                  2. 列表 2

                    1. 多级列表 1

                    2. 多级列表 2

                  3. 列表 3

                  标题 3

                  来个文字图文混排的


                  ', - 'html':'

                  [键入文章标题]

                  摘要:这里可以输入很长很长很长很长很长很长很长很长很差的摘要

                  标题 1

                  这里可以输入很多内容,可以图文混排,可以有列表等。

                  标题 2

                  来个列表瞅瞅:

                  1. 列表 1

                  2. 列表 2

                    1. 多级列表 1

                    2. 多级列表 2

                  3. 列表 3

                  标题 3

                  来个文字图文混排的

                  这里可以多行

                  右边是图片

                  绝对没有问题的,不信你也可以试试看


                  ' - } -]; \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/template/images/bg.gif b/static/plugs/ueditor/dialogs/template/images/bg.gif deleted file mode 100644 index 8c1d10ad1..000000000 Binary files a/static/plugs/ueditor/dialogs/template/images/bg.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/template/images/pre0.png b/static/plugs/ueditor/dialogs/template/images/pre0.png deleted file mode 100644 index 8f3c16ab1..000000000 Binary files a/static/plugs/ueditor/dialogs/template/images/pre0.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/template/images/pre1.png b/static/plugs/ueditor/dialogs/template/images/pre1.png deleted file mode 100644 index 5a03f9699..000000000 Binary files a/static/plugs/ueditor/dialogs/template/images/pre1.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/template/images/pre2.png b/static/plugs/ueditor/dialogs/template/images/pre2.png deleted file mode 100644 index 5a55672c1..000000000 Binary files a/static/plugs/ueditor/dialogs/template/images/pre2.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/template/images/pre3.png b/static/plugs/ueditor/dialogs/template/images/pre3.png deleted file mode 100644 index d852d29f1..000000000 Binary files a/static/plugs/ueditor/dialogs/template/images/pre3.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/template/images/pre4.png b/static/plugs/ueditor/dialogs/template/images/pre4.png deleted file mode 100644 index 0d7bc72ab..000000000 Binary files a/static/plugs/ueditor/dialogs/template/images/pre4.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/template/template.css b/static/plugs/ueditor/dialogs/template/template.css deleted file mode 100644 index 2658bd2f1..000000000 --- a/static/plugs/ueditor/dialogs/template/template.css +++ /dev/null @@ -1,18 +0,0 @@ -.wrap{ padding: 5px;font-size: 14px;} -.left{width:425px;float: left;} -.right{width:160px;border: 1px solid #ccc;float: right;padding: 5px;margin-right: 5px;} -.right .pre{height: 332px;overflow-y: auto;} -.right .preitem{border: white 1px solid;margin: 5px 0;padding: 2px 0;} -.right .preitem:hover{background-color: lemonChiffon;cursor: pointer;border: #ccc 1px solid;} -.right .preitem img{display: block;margin: 0 auto;width:100px;} -.clear{clear: both;} -.top{height:26px;line-height: 26px;padding: 5px;} -.bottom{height:320px;width:100%;margin: 0 auto;} -.transparent{ background: url("images/bg.gif") repeat;} -.bottom table tr td{border:1px dashed #ccc;} -#colorPicker{width: 17px;height: 17px;border: 1px solid #CCC;display: inline-block;border-radius: 3px;box-shadow: 2px 2px 5px #D3D6DA;} -.border_style1{padding:2px;border: 1px solid #ccc;border-radius: 5px;box-shadow:2px 2px 5px #d3d6da;} -p{margin: 5px 0} -table{clear:both;margin-bottom:10px;border-collapse:collapse;word-break:break-all;} -li{clear:both} -ol{padding-left:40px; } \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/template/template.html b/static/plugs/ueditor/dialogs/template/template.html deleted file mode 100644 index 9d71eced6..000000000 --- a/static/plugs/ueditor/dialogs/template/template.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - -
                  -
                  -
                  - -
                  -
                  -
                  -
                  - -
                  -
                  -
                  -
                  - - - - diff --git a/static/plugs/ueditor/dialogs/template/template.js b/static/plugs/ueditor/dialogs/template/template.js deleted file mode 100644 index 0f1cdad5e..000000000 --- a/static/plugs/ueditor/dialogs/template/template.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Created with JetBrains PhpStorm. - * User: xuheng - * Date: 12-8-8 - * Time: 下午2:09 - * To change this template use File | Settings | File Templates. - */ -(function () { - var me = editor, - preview = $G( "preview" ), - preitem = $G( "preitem" ), - tmps = templates, - currentTmp; - var initPre = function () { - var str = ""; - for ( var i = 0, tmp; tmp = tmps[i++]; ) { - str += '
                  '; - } - preitem.innerHTML = str; - }; - var pre = function ( n ) { - var tmp = tmps[n - 1]; - currentTmp = tmp; - clearItem(); - domUtils.setStyles( preitem.childNodes[n - 1], { - "background-color":"lemonChiffon", - "border":"#ccc 1px solid" - } ); - preview.innerHTML = tmp.preHtml ? tmp.preHtml : ""; - }; - var clearItem = function () { - var items = preitem.children; - for ( var i = 0, item; item = items[i++]; ) { - domUtils.setStyles( item, { - "background-color":"", - "border":"white 1px solid" - } ); - } - }; - dialog.onok = function () { - if ( !$G( "issave" ).checked ){ - me.execCommand( "cleardoc" ); - } - var obj = { - html:currentTmp && currentTmp.html - }; - me.execCommand( "template", obj ); - }; - initPre(); - window.pre = pre; - pre(2) - -})(); \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/video/images/bg.png b/static/plugs/ueditor/dialogs/video/images/bg.png deleted file mode 100644 index 580be0a01..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/bg.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/center_focus.jpg b/static/plugs/ueditor/dialogs/video/images/center_focus.jpg deleted file mode 100644 index 262b02916..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/center_focus.jpg and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/file-icons.gif b/static/plugs/ueditor/dialogs/video/images/file-icons.gif deleted file mode 100644 index d8c02c27e..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/file-icons.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/file-icons.png b/static/plugs/ueditor/dialogs/video/images/file-icons.png deleted file mode 100644 index 3ff82c8c4..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/file-icons.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/icons.gif b/static/plugs/ueditor/dialogs/video/images/icons.gif deleted file mode 100644 index 78459dea7..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/icons.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/icons.png b/static/plugs/ueditor/dialogs/video/images/icons.png deleted file mode 100644 index 12e470016..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/icons.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/image.png b/static/plugs/ueditor/dialogs/video/images/image.png deleted file mode 100644 index 19699f6a9..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/image.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/left_focus.jpg b/static/plugs/ueditor/dialogs/video/images/left_focus.jpg deleted file mode 100644 index 7886d276d..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/left_focus.jpg and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/none_focus.jpg b/static/plugs/ueditor/dialogs/video/images/none_focus.jpg deleted file mode 100644 index 7c768dcb4..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/none_focus.jpg and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/progress.png b/static/plugs/ueditor/dialogs/video/images/progress.png deleted file mode 100644 index 717c4865c..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/progress.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/right_focus.jpg b/static/plugs/ueditor/dialogs/video/images/right_focus.jpg deleted file mode 100644 index 173e10d2d..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/right_focus.jpg and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/success.gif b/static/plugs/ueditor/dialogs/video/images/success.gif deleted file mode 100644 index 8d4f3112b..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/success.gif and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/images/success.png b/static/plugs/ueditor/dialogs/video/images/success.png deleted file mode 100644 index 94f968dc8..000000000 Binary files a/static/plugs/ueditor/dialogs/video/images/success.png and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/video/video.css b/static/plugs/ueditor/dialogs/video/video.css deleted file mode 100644 index 8a17284e1..000000000 --- a/static/plugs/ueditor/dialogs/video/video.css +++ /dev/null @@ -1,635 +0,0 @@ -@charset "utf-8"; -.wrapper{ width: 570px;_width:575px;margin: 10px auto; zoom:1;position: relative} -.tabbody{height: 335px;} -.tabbody .panel { - position: absolute; - width: 0; - height: 0; - background: #fff; - overflow: hidden; - display: none; -} -.tabbody .panel.focus { - width: 100%; - height: 335px; - display: block; -} - -.tabbody .panel table td{vertical-align: middle;} -#videoUrl { - width: 490px; - height: 21px; - line-height: 21px; - margin: 8px 5px; - background: #FFF; - border: 1px solid #d7d7d7; -} -#videoSearchTxt{margin-left:15px;background: #FFF;width:200px;height:21px;line-height:21px;border: 1px solid #d7d7d7;} -#searchList{width: 570px;overflow: auto;zoom:1;height: 270px;} -#searchList div{float: left;width: 120px;height: 135px;margin: 5px 15px;} -#searchList img{margin: 2px 8px;cursor: pointer;border: 2px solid #fff} /*不用缩略图*/ -#searchList p{margin-left: 10px;} -#videoType{ - width: 65px; - height: 23px; - line-height: 22px; - border: 1px solid #d7d7d7; -} -#videoSearchBtn,#videoSearchReset{ - /*width: 80px;*/ - height: 25px; - line-height: 25px; - background: #eee; - border: 1px solid #d7d7d7; - cursor: pointer; - padding: 0 5px; -} - - - -#preview{position: relative;width: 420px;padding:0;overflow: hidden; margin-left: 10px; _margin-left:5px; height: 280px;background-color: #ddd;float: left} -#preview .previewMsg {position:absolute;top:0;margin:0;padding:0;height:280px;width:100%;background-color: #666;} -#preview .previewMsg span{display:block;margin: 125px auto 0 auto;text-align:center;font-size:18px;color:#fff;} -#preview .previewVideo {position:absolute;top:0;margin:0;padding:0;height:280px;width:100%;} -.edui-video-wrapper fieldset{ - border: 1px solid #ddd; - padding-left: 5px; - margin-bottom: 20px; - padding-bottom: 5px; - width: 115px; -} - -#videoInfo {width: 120px;float: left;margin-left: 10px;_margin-left:7px;} -fieldset{ - border: 1px solid #ddd; - padding-left: 5px; - margin-bottom: 20px; - padding-bottom: 5px; - width: 115px; -} -fieldset legend{font-weight: bold;} -fieldset p{line-height: 30px;} -fieldset input.txt{ - width: 65px; - height: 21px; - line-height: 21px; - margin: 8px 5px; - background: #FFF; - border: 1px solid #d7d7d7; -} -label.url{font-weight: bold;margin-left: 5px;color: #06c;} -#videoFloat div{cursor:pointer;opacity: 0.5;filter: alpha(opacity = 50);margin:9px;_margin:5px;width:38px;height:36px;float:left;} -#videoFloat .focus{opacity: 1;filter: alpha(opacity = 100)} -span.view{display: inline-block;width: 30px;float: right;cursor: pointer;color: blue} - - - - -/* upload video */ -.tabbody #upload.panel { - width: 0; - height: 0; - overflow: hidden; - position: absolute !important; - clip: rect(1px, 1px, 1px, 1px); - background: #fff; - display: block; -} -.tabbody #upload.panel.focus { - width: 100%; - height: 335px; - display: block; - clip: auto; -} -#upload_alignment div{cursor:pointer;opacity: 0.5;filter: alpha(opacity = 50);margin:9px;_margin:5px;width:38px;height:36px;float:left;} -#upload_alignment .focus{opacity: 1;filter: alpha(opacity = 100)} -#upload_left { width:427px; float:left; } -#upload_left .controller { height: 30px; clear: both; } -#uploadVideoInfo{margin-top:10px;float:right;padding-right:8px;} - -#upload .queueList { - margin: 0; -} - -#upload p { - margin: 0; -} - -.element-invisible { - width: 0 !important; - height: 0 !important; - border: 0; - padding: 0; - margin: 0; - overflow: hidden; - position: absolute !important; - clip: rect(1px, 1px, 1px, 1px); -} - -#upload .placeholder { - margin: 10px; - margin-right:0; - border: 2px dashed #e6e6e6; - *border: 0px dashed #e6e6e6; - height: 161px; - padding-top: 150px; - text-align: center; - width: 97%; - float: left; - background: url(./images/image.png) center 70px no-repeat; - color: #cccccc; - font-size: 18px; - position: relative; - top:0; - *margin-left: 0; - *left: 10px; -} - -#upload .placeholder .webuploader-pick { - font-size: 18px; - background: #00b7ee; - border-radius: 3px; - line-height: 44px; - padding: 0 30px; - *width: 120px; - color: #fff; - display: inline-block; - margin: 0 auto 20px auto; - cursor: pointer; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); -} - -#upload .placeholder .webuploader-pick-hover { - background: #00a2d4; -} - - -#filePickerContainer { - text-align: center; -} - -#upload .placeholder .flashTip { - color: #666666; - font-size: 12px; - position: absolute; - width: 100%; - text-align: center; - bottom: 20px; -} - -#upload .placeholder .flashTip a { - color: #0785d1; - text-decoration: none; -} - -#upload .placeholder .flashTip a:hover { - text-decoration: underline; -} - -#upload .placeholder.webuploader-dnd-over { - border-color: #999999; -} - -#upload .filelist { - list-style: none; - margin: 0; - padding: 0; - overflow-x: hidden; - overflow-y: auto; - position: relative; - height: 285px; -} - -#upload .filelist:after { - content: ''; - display: block; - width: 0; - height: 0; - overflow: hidden; - clear: both; -} - -#upload .filelist li { - width: 113px; - height: 113px; - background: url(./images/bg.png); - text-align: center; - margin: 15px 0 0 20px; - *margin: 15px 0 0 15px; - position: relative; - display: block; - float: left; - overflow: hidden; - font-size: 12px; -} - -#upload .filelist li p.log { - position: relative; - top: -45px; -} - -#upload .filelist li p.title { - position: absolute; - top: 0; - left: 0; - width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - top: 5px; - text-indent: 5px; - text-align: left; -} - -#upload .filelist li p.progress { - position: absolute; - width: 100%; - bottom: 0; - left: 0; - height: 8px; - overflow: hidden; - z-index: 50; - margin: 0; - border-radius: 0; - background: none; - -webkit-box-shadow: 0 0 0; -} - -#upload .filelist li p.progress span { - display: none; - overflow: hidden; - width: 0; - height: 100%; - background: #1483d8 url(./images/progress.png) repeat-x; - - -webit-transition: width 200ms linear; - -moz-transition: width 200ms linear; - -o-transition: width 200ms linear; - -ms-transition: width 200ms linear; - transition: width 200ms linear; - - -webkit-animation: progressmove 2s linear infinite; - -moz-animation: progressmove 2s linear infinite; - -o-animation: progressmove 2s linear infinite; - -ms-animation: progressmove 2s linear infinite; - animation: progressmove 2s linear infinite; - - -webkit-transform: translateZ(0); -} - -@-webkit-keyframes progressmove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 17px 0; - } -} - -@-moz-keyframes progressmove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 17px 0; - } -} - -@keyframes progressmove { - 0% { - background-position: 0 0; - } - 100% { - background-position: 17px 0; - } -} - -#upload .filelist li p.imgWrap { - position: relative; - z-index: 2; - line-height: 113px; - vertical-align: middle; - overflow: hidden; - width: 113px; - height: 113px; - - -webkit-transform-origin: 50% 50%; - -moz-transform-origin: 50% 50%; - -o-transform-origin: 50% 50%; - -ms-transform-origin: 50% 50%; - transform-origin: 50% 50%; - - -webit-transition: 200ms ease-out; - -moz-transition: 200ms ease-out; - -o-transition: 200ms ease-out; - -ms-transition: 200ms ease-out; - transition: 200ms ease-out; -} -#upload .filelist li p.imgWrap.notimage { - margin-top: 0; - width: 111px; - height: 111px; - border: 1px #eeeeee solid; -} -#upload .filelist li p.imgWrap.notimage i.file-preview { - margin-top: 15px; -} - -#upload .filelist li img { - width: 100%; -} - -#upload .filelist li p.error { - background: #f43838; - color: #fff; - position: absolute; - bottom: 0; - left: 0; - height: 28px; - line-height: 28px; - width: 100%; - z-index: 100; - display:none; -} - -#upload .filelist li .success { - display: block; - position: absolute; - left: 0; - bottom: 0; - height: 40px; - width: 100%; - z-index: 200; - background: url(./images/success.png) no-repeat right bottom; - background-image: url(./images/success.gif) \9; -} - -#upload .filelist li.filePickerBlock { - width: 113px; - height: 113px; - background: url(./images/image.png) no-repeat center 12px; - border: 1px solid #eeeeee; - border-radius: 0; -} -#upload .filelist li.filePickerBlock div.webuploader-pick { - width: 100%; - height: 100%; - margin: 0; - padding: 0; - opacity: 0; - background: none; - font-size: 0; -} - -#upload .filelist div.file-panel { - position: absolute; - height: 0; - filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#80000000', endColorstr='#80000000') \0; - background: rgba(0, 0, 0, 0.5); - width: 100%; - top: 0; - left: 0; - overflow: hidden; - z-index: 300; -} - -#upload .filelist div.file-panel span { - width: 24px; - height: 24px; - display: inline; - float: right; - text-indent: -9999px; - overflow: hidden; - background: url(./images/icons.png) no-repeat; - background: url(./images/icons.gif) no-repeat \9; - margin: 5px 1px 1px; - cursor: pointer; - -webkit-tap-highlight-color: rgba(0,0,0,0); - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -#upload .filelist div.file-panel span.rotateLeft { - display:none; - background-position: 0 -24px; -} - -#upload .filelist div.file-panel span.rotateLeft:hover { - background-position: 0 0; -} - -#upload .filelist div.file-panel span.rotateRight { - display:none; - background-position: -24px -24px; -} - -#upload .filelist div.file-panel span.rotateRight:hover { - background-position: -24px 0; -} - -#upload .filelist div.file-panel span.cancel { - background-position: -48px -24px; -} - -#upload .filelist div.file-panel span.cancel:hover { - background-position: -48px 0; -} - -#upload .statusBar { - height: 45px; - border-bottom: 1px solid #dadada; - margin: 0 10px; - padding: 0; - line-height: 45px; - vertical-align: middle; - position: relative; -} - -#upload .statusBar .progress { - border: 1px solid #1483d8; - width: 198px; - background: #fff; - height: 18px; - position: absolute; - top: 12px; - display: none; - text-align: center; - line-height: 18px; - color: #6dbfff; - margin: 0 10px 0 0; -} -#upload .statusBar .progress span.percentage { - width: 0; - height: 100%; - left: 0; - top: 0; - background: #1483d8; - position: absolute; -} -#upload .statusBar .progress span.text { - position: relative; - z-index: 10; -} - -#upload .statusBar .info { - display: inline-block; - font-size: 14px; - color: #666666; -} - -#upload .statusBar .btns { - position: absolute; - top: 7px; - right: 0; - line-height: 30px; -} - -#filePickerBtn { - display: inline-block; - float: left; -} -#upload .statusBar .btns .webuploader-pick, -#upload .statusBar .btns .uploadBtn, -#upload .statusBar .btns .uploadBtn.state-uploading, -#upload .statusBar .btns .uploadBtn.state-paused { - background: #ffffff; - border: 1px solid #cfcfcf; - color: #565656; - padding: 0 18px; - display: inline-block; - border-radius: 3px; - margin-left: 10px; - cursor: pointer; - font-size: 14px; - float: left; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -#upload .statusBar .btns .webuploader-pick-hover, -#upload .statusBar .btns .uploadBtn:hover, -#upload .statusBar .btns .uploadBtn.state-uploading:hover, -#upload .statusBar .btns .uploadBtn.state-paused:hover { - background: #f0f0f0; -} - -#upload .statusBar .btns .uploadBtn, -#upload .statusBar .btns .uploadBtn.state-paused{ - background: #00b7ee; - color: #fff; - border-color: transparent; -} -#upload .statusBar .btns .uploadBtn:hover, -#upload .statusBar .btns .uploadBtn.state-paused:hover{ - background: #00a2d4; -} - -#upload .statusBar .btns .uploadBtn.disabled { - pointer-events: none; - filter:alpha(opacity=60); - -moz-opacity:0.6; - -khtml-opacity: 0.6; - opacity: 0.6; -} - - -/* 在线文件的文件预览图标 */ -i.file-preview { - display: block; - margin: 10px auto; - width: 70px; - height: 70px; - background-image: url("./images/file-icons.png"); - background-image: url("./images/file-icons.gif") \9; - background-position: -140px center; - background-repeat: no-repeat; -} -i.file-preview.file-type-dir{ - background-position: 0 center; -} -i.file-preview.file-type-file{ - background-position: -140px center; -} -i.file-preview.file-type-filelist{ - background-position: -210px center; -} -i.file-preview.file-type-zip, -i.file-preview.file-type-rar, -i.file-preview.file-type-7z, -i.file-preview.file-type-tar, -i.file-preview.file-type-gz, -i.file-preview.file-type-bz2{ - background-position: -280px center; -} -i.file-preview.file-type-xls, -i.file-preview.file-type-xlsx{ - background-position: -350px center; -} -i.file-preview.file-type-doc, -i.file-preview.file-type-docx{ - background-position: -420px center; -} -i.file-preview.file-type-ppt, -i.file-preview.file-type-pptx{ - background-position: -490px center; -} -i.file-preview.file-type-vsd{ - background-position: -560px center; -} -i.file-preview.file-type-pdf{ - background-position: -630px center; -} -i.file-preview.file-type-txt, -i.file-preview.file-type-md, -i.file-preview.file-type-json, -i.file-preview.file-type-htm, -i.file-preview.file-type-xml, -i.file-preview.file-type-html, -i.file-preview.file-type-js, -i.file-preview.file-type-css, -i.file-preview.file-type-php, -i.file-preview.file-type-jsp, -i.file-preview.file-type-asp{ - background-position: -700px center; -} -i.file-preview.file-type-apk{ - background-position: -770px center; -} -i.file-preview.file-type-exe{ - background-position: -840px center; -} -i.file-preview.file-type-ipa{ - background-position: -910px center; -} -i.file-preview.file-type-mp4, -i.file-preview.file-type-swf, -i.file-preview.file-type-mkv, -i.file-preview.file-type-avi, -i.file-preview.file-type-flv, -i.file-preview.file-type-mov, -i.file-preview.file-type-mpg, -i.file-preview.file-type-mpeg, -i.file-preview.file-type-ogv, -i.file-preview.file-type-webm, -i.file-preview.file-type-rm, -i.file-preview.file-type-rmvb{ - background-position: -980px center; -} -i.file-preview.file-type-ogg, -i.file-preview.file-type-wav, -i.file-preview.file-type-wmv, -i.file-preview.file-type-mid, -i.file-preview.file-type-mp3{ - background-position: -1050px center; -} -i.file-preview.file-type-jpg, -i.file-preview.file-type-jpeg, -i.file-preview.file-type-gif, -i.file-preview.file-type-bmp, -i.file-preview.file-type-png, -i.file-preview.file-type-psd{ - background-position: -140px center; -} \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/video/video.html b/static/plugs/ueditor/dialogs/video/video.html deleted file mode 100644 index 14f10f455..000000000 --- a/static/plugs/ueditor/dialogs/video/video.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - -
                  -
                  -
                  - - -
                  -
                  -
                  -
                  -
                  -
                  -
                  - - - - -
                  -
                  -
                  - -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  - 0% - -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                    -
                  • -
                  -
                  -
                  -
                  -
                  - - - - -
                  -
                  -
                  - -
                  -
                  -
                  -
                  -
                  -
                  -
                  - - - - - - - - - - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/video/video.js b/static/plugs/ueditor/dialogs/video/video.js deleted file mode 100644 index 58553e3c5..000000000 --- a/static/plugs/ueditor/dialogs/video/video.js +++ /dev/null @@ -1,789 +0,0 @@ -/** - * Created by JetBrains PhpStorm. - * User: taoqili - * Date: 12-2-20 - * Time: 上午11:19 - * To change this template use File | Settings | File Templates. - */ - -(function(){ - - var video = {}, - uploadVideoList = [], - isModifyUploadVideo = false, - uploadFile; - - window.onload = function(){ - $focus($G("videoUrl")); - initTabs(); - initVideo(); - initUpload(); - }; - - /* 初始化tab标签 */ - function initTabs(){ - var tabs = $G('tabHeads').children; - for (var i = 0; i < tabs.length; i++) { - domUtils.on(tabs[i], "click", function (e) { - var j, bodyId, target = e.target || e.srcElement; - for (j = 0; j < tabs.length; j++) { - bodyId = tabs[j].getAttribute('data-content-id'); - if(tabs[j] == target){ - domUtils.addClass(tabs[j], 'focus'); - domUtils.addClass($G(bodyId), 'focus'); - }else { - domUtils.removeClasses(tabs[j], 'focus'); - domUtils.removeClasses($G(bodyId), 'focus'); - } - } - }); - } - } - - function initVideo(){ - createAlignButton( ["videoFloat", "upload_alignment"] ); - addUrlChangeListener($G("videoUrl")); - addOkListener(); - - //编辑视频时初始化相关信息 - (function(){ - var img = editor.selection.getRange().getClosedNode(),url; - if(img && img.className){ - var hasFakedClass = (img.className == "edui-faked-video"), - hasUploadClass = img.className.indexOf("edui-upload-video")!=-1; - if(hasFakedClass || hasUploadClass) { - $G("videoUrl").value = url = img.getAttribute("_url"); - $G("videoWidth").value = img.width; - $G("videoHeight").value = img.height; - var align = domUtils.getComputedStyle(img,"float"), - parentAlign = domUtils.getComputedStyle(img.parentNode,"text-align"); - updateAlignButton(parentAlign==="center"?"center":align); - } - if(hasUploadClass) { - isModifyUploadVideo = true; - } - } - createPreviewVideo(url); - })(); - } - - /** - * 监听确认和取消两个按钮事件,用户执行插入或者清空正在播放的视频实例操作 - */ - function addOkListener(){ - dialog.onok = function(){ - $G("preview").innerHTML = ""; - var currentTab = findFocus("tabHeads","tabSrc"); - switch(currentTab){ - case "video": - return insertSingle(); - break; - case "videoSearch": - return insertSearch("searchList"); - break; - case "upload": - return insertUpload(); - break; - } - }; - dialog.oncancel = function(){ - $G("preview").innerHTML = ""; - }; - } - - /** - * 依据传入的align值更新按钮信息 - * @param align - */ - function updateAlignButton( align ) { - var aligns = $G( "videoFloat" ).children; - for ( var i = 0, ci; ci = aligns[i++]; ) { - if ( ci.getAttribute( "name" ) == align ) { - if ( ci.className !="focus" ) { - ci.className = "focus"; - } - } else { - if ( ci.className =="focus" ) { - ci.className = ""; - } - } - } - } - - /** - * 将单个视频信息插入编辑器中 - */ - function insertSingle(){ - var width = $G("videoWidth"), - height = $G("videoHeight"), - url=$G('videoUrl').value, - align = findFocus("videoFloat","name"); - if(!url) return false; - if ( !checkNum( [width, height] ) ) return false; - editor.execCommand('insertvideo', { - url: convert_url(url), - width: width.value, - height: height.value, - align: align - }, isModifyUploadVideo ? 'upload':null); - } - - /** - * 将元素id下的所有代表视频的图片插入编辑器中 - * @param id - */ - function insertSearch(id){ - var imgs = domUtils.getElementsByTagName($G(id),"img"), - videoObjs=[]; - for(var i=0,img; img=imgs[i++];){ - if(img.getAttribute("selected")){ - videoObjs.push({ - url:img.getAttribute("ue_video_url"), - width:420, - height:280, - align:"none" - }); - } - } - editor.execCommand('insertvideo',videoObjs); - } - - /** - * 找到id下具有focus类的节点并返回该节点下的某个属性 - * @param id - * @param returnProperty - */ - function findFocus( id, returnProperty ) { - var tabs = $G( id ).children, - property; - for ( var i = 0, ci; ci = tabs[i++]; ) { - if ( ci.className=="focus" ) { - property = ci.getAttribute( returnProperty ); - break; - } - } - return property; - } - function convert_url(url){ - if ( !url ) return ''; - url = utils.trim(url) - .replace(/v\.youku\.com\/v_show\/id_([\w\-=]+)\.html/i, 'player.youku.com/player.php/sid/$1/v.swf') - .replace(/(www\.)?youtube\.com\/watch\?v=([\w\-]+)/i, "www.youtube.com/v/$2") - .replace(/youtu.be\/(\w+)$/i, "www.youtube.com/v/$1") - .replace(/v\.ku6\.com\/.+\/([\w\.]+)\.html.*$/i, "player.ku6.com/refer/$1/v.swf") - .replace(/www\.56\.com\/u\d+\/v_([\w\-]+)\.html/i, "player.56.com/v_$1.swf") - .replace(/www.56.com\/w\d+\/play_album\-aid\-\d+_vid\-([^.]+)\.html/i, "player.56.com/v_$1.swf") - .replace(/v\.pps\.tv\/play_([\w]+)\.html.*$/i, "player.pps.tv/player/sid/$1/v.swf") - .replace(/www\.letv\.com\/ptv\/vplay\/([\d]+)\.html.*$/i, "i7.imgs.letv.com/player/swfPlayer.swf?id=$1&autoplay=0") - .replace(/www\.tudou\.com\/programs\/view\/([\w\-]+)\/?/i, "www.tudou.com/v/$1") - .replace(/v\.qq\.com\/cover\/[\w]+\/[\w]+\/([\w]+)\.html/i, "static.video.qq.com/TPout.swf?vid=$1") - .replace(/v\.qq\.com\/.+[\?\&]vid=([^&]+).*$/i, "static.video.qq.com/TPout.swf?vid=$1") - .replace(/my\.tv\.sohu\.com\/[\w]+\/[\d]+\/([\d]+)\.shtml.*$/i, "share.vrs.sohu.com/my/v.swf&id=$1"); - - return url; - } - - /** - * 检测传入的所有input框中输入的长宽是否是正数 - * @param nodes input框集合, - */ - function checkNum( nodes ) { - for ( var i = 0, ci; ci = nodes[i++]; ) { - var value = ci.value; - if ( !isNumber( value ) && value) { - alert( lang.numError ); - ci.value = ""; - ci.focus(); - return false; - } - } - return true; - } - - /** - * 数字判断 - * @param value - */ - function isNumber( value ) { - return /(0|^[1-9]\d*$)/.test( value ); - } - - /** - * 创建图片浮动选择按钮 - * @param ids - */ - function createAlignButton( ids ) { - for ( var i = 0, ci; ci = ids[i++]; ) { - var floatContainer = $G( ci ), - nameMaps = {"none":lang['default'], "left":lang.floatLeft, "right":lang.floatRight, "center":lang.block}; - for ( var j in nameMaps ) { - var div = document.createElement( "div" ); - div.setAttribute( "name", j ); - if ( j == "none" ) div.className="focus"; - div.style.cssText = "background:url(images/" + j + "_focus.jpg);"; - div.setAttribute( "title", nameMaps[j] ); - floatContainer.appendChild( div ); - } - switchSelect( ci ); - } - } - - /** - * 选择切换 - * @param selectParentId - */ - function switchSelect( selectParentId ) { - var selects = $G( selectParentId ).children; - for ( var i = 0, ci; ci = selects[i++]; ) { - domUtils.on( ci, "click", function () { - for ( var j = 0, cj; cj = selects[j++]; ) { - cj.className = ""; - cj.removeAttribute && cj.removeAttribute( "class" ); - } - this.className = "focus"; - } ) - } - } - - /** - * 监听url改变事件 - * @param url - */ - function addUrlChangeListener(url){ - if (browser.ie) { - url.onpropertychange = function () { - createPreviewVideo( this.value ); - } - } else { - url.addEventListener( "input", function () { - createPreviewVideo( this.value ); - }, false ); - } - } - - /** - * 根据url生成视频预览 - * @param url - */ - function createPreviewVideo(url){ - if ( !url )return; - - var conUrl = convert_url(url); - - $G("preview").innerHTML = '
                  '+lang.urlError+'
                  '+ - '' + - ''; - } - - - /* 插入上传视频 */ - function insertUpload(){ - var videoObjs=[], - uploadDir = editor.getOpt('videoUrlPrefix'), - width = $G('upload_width').value || 420, - height = $G('upload_height').value || 280, - align = findFocus("upload_alignment","name") || 'none'; - for(var key in uploadVideoList) { - var file = uploadVideoList[key]; - videoObjs.push({ - url: uploadDir + file.url, - width:width, - height:height, - align:align - }); - } - - var count = uploadFile.getQueueCount(); - if (count) { - $('.info', '#queueList').html('' + '还有2个未上传文件'.replace(/[\d]/, count) + ''); - return false; - } else { - editor.execCommand('insertvideo', videoObjs, 'upload'); - } - } - - /*初始化上传标签*/ - function initUpload(){ - uploadFile = new UploadFile('queueList'); - } - - - /* 上传附件 */ - function UploadFile(target) { - this.$wrap = target.constructor == String ? $('#' + target) : $(target); - this.init(); - } - UploadFile.prototype = { - init: function () { - this.fileList = []; - this.initContainer(); - this.initUploader(); - }, - initContainer: function () { - this.$queue = this.$wrap.find('.filelist'); - }, - /* 初始化容器 */ - initUploader: function () { - var _this = this, - $ = jQuery, // just in case. Make sure it's not an other libaray. - $wrap = _this.$wrap, - // 图片容器 - $queue = $wrap.find('.filelist'), - // 状态栏,包括进度和控制按钮 - $statusBar = $wrap.find('.statusBar'), - // 文件总体选择信息。 - $info = $statusBar.find('.info'), - // 上传按钮 - $upload = $wrap.find('.uploadBtn'), - // 上传按钮 - $filePickerBtn = $wrap.find('.filePickerBtn'), - // 上传按钮 - $filePickerBlock = $wrap.find('.filePickerBlock'), - // 没选择文件之前的内容。 - $placeHolder = $wrap.find('.placeholder'), - // 总体进度条 - $progress = $statusBar.find('.progress').hide(), - // 添加的文件数量 - fileCount = 0, - // 添加的文件总大小 - fileSize = 0, - // 优化retina, 在retina下这个值是2 - ratio = window.devicePixelRatio || 1, - // 缩略图大小 - thumbnailWidth = 113 * ratio, - thumbnailHeight = 113 * ratio, - // 可能有pedding, ready, uploading, confirm, done. - state = '', - // 所有文件的进度信息,key为file id - percentages = {}, - supportTransition = (function () { - var s = document.createElement('p').style, - r = 'transition' in s || - 'WebkitTransition' in s || - 'MozTransition' in s || - 'msTransition' in s || - 'OTransition' in s; - s = null; - return r; - })(), - // WebUploader实例 - uploader, - actionUrl = editor.getActionUrl(editor.getOpt('videoActionName')), - fileMaxSize = editor.getOpt('videoMaxSize'), - acceptExtensions = (editor.getOpt('videoAllowFiles') || []).join('').replace(/\./g, ',').replace(/^[,]/, '');; - - if (!WebUploader.Uploader.support()) { - $('#filePickerReady').after($('
                  ').html(lang.errorNotSupport)).hide(); - return; - } else if (!editor.getOpt('videoActionName')) { - $('#filePickerReady').after($('
                  ').html(lang.errorLoadConfig)).hide(); - return; - } - - uploader = _this.uploader = WebUploader.create({ - pick: { - id: '#filePickerReady', - label: lang.uploadSelectFile - }, - swf: '../../third-party/webuploader/Uploader.swf', - server: actionUrl, - fileVal: editor.getOpt('videoFieldName'), - duplicate: true, - fileSingleSizeLimit: fileMaxSize, - compress: false - }); - uploader.addButton({ - id: '#filePickerBlock' - }); - uploader.addButton({ - id: '#filePickerBtn', - label: lang.uploadAddFile - }); - - setState('pedding'); - - // 当有文件添加进来时执行,负责view的创建 - function addFile(file) { - var $li = $('
                • ' + - '

                  ' + file.name + '

                  ' + - '

                  ' + - '

                  ' + - '
                • '), - - $btns = $('
                  ' + - '' + lang.uploadDelete + '' + - '' + lang.uploadTurnRight + '' + - '' + lang.uploadTurnLeft + '
                  ').appendTo($li), - $prgress = $li.find('p.progress span'), - $wrap = $li.find('p.imgWrap'), - $info = $('

                  ').hide().appendTo($li), - - showError = function (code) { - switch (code) { - case 'exceed_size': - text = lang.errorExceedSize; - break; - case 'interrupt': - text = lang.errorInterrupt; - break; - case 'http': - text = lang.errorHttp; - break; - case 'not_allow_type': - text = lang.errorFileType; - break; - default: - text = lang.errorUploadRetry; - break; - } - $info.text(text).show(); - }; - - if (file.getStatus() === 'invalid') { - showError(file.statusText); - } else { - $wrap.text(lang.uploadPreview); - if ('|png|jpg|jpeg|bmp|gif|'.indexOf('|'+file.ext.toLowerCase()+'|') == -1) { - $wrap.empty().addClass('notimage').append('' + - '' + file.name + ''); - } else { - if (browser.ie && browser.version <= 7) { - $wrap.text(lang.uploadNoPreview); - } else { - uploader.makeThumb(file, function (error, src) { - if (error || !src || (/^data:/.test(src) && browser.ie && browser.version <= 7)) { - $wrap.text(lang.uploadNoPreview); - } else { - var $img = $(''); - $wrap.empty().append($img); - $img.on('error', function () { - $wrap.text(lang.uploadNoPreview); - }); - } - }, thumbnailWidth, thumbnailHeight); - } - } - percentages[ file.id ] = [ file.size, 0 ]; - file.rotation = 0; - - /* 检查文件格式 */ - if (!file.ext || acceptExtensions.indexOf(file.ext.toLowerCase()) == -1) { - showError('not_allow_type'); - uploader.removeFile(file); - } - } - - file.on('statuschange', function (cur, prev) { - if (prev === 'progress') { - $prgress.hide().width(0); - } else if (prev === 'queued') { - $li.off('mouseenter mouseleave'); - $btns.remove(); - } - // 成功 - if (cur === 'error' || cur === 'invalid') { - showError(file.statusText); - percentages[ file.id ][ 1 ] = 1; - } else if (cur === 'interrupt') { - showError('interrupt'); - } else if (cur === 'queued') { - percentages[ file.id ][ 1 ] = 0; - } else if (cur === 'progress') { - $info.hide(); - $prgress.css('display', 'block'); - } else if (cur === 'complete') { - } - - $li.removeClass('state-' + prev).addClass('state-' + cur); - }); - - $li.on('mouseenter', function () { - $btns.stop().animate({height: 30}); - }); - $li.on('mouseleave', function () { - $btns.stop().animate({height: 0}); - }); - - $btns.on('click', 'span', function () { - var index = $(this).index(), - deg; - - switch (index) { - case 0: - uploader.removeFile(file); - return; - case 1: - file.rotation += 90; - break; - case 2: - file.rotation -= 90; - break; - } - - if (supportTransition) { - deg = 'rotate(' + file.rotation + 'deg)'; - $wrap.css({ - '-webkit-transform': deg, - '-mos-transform': deg, - '-o-transform': deg, - 'transform': deg - }); - } else { - $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')'); - } - - }); - - $li.insertBefore($filePickerBlock); - } - - // 负责view的销毁 - function removeFile(file) { - var $li = $('#' + file.id); - delete percentages[ file.id ]; - updateTotalProgress(); - $li.off().find('.file-panel').off().end().remove(); - } - - function updateTotalProgress() { - var loaded = 0, - total = 0, - spans = $progress.children(), - percent; - - $.each(percentages, function (k, v) { - total += v[ 0 ]; - loaded += v[ 0 ] * v[ 1 ]; - }); - - percent = total ? loaded / total : 0; - - spans.eq(0).text(Math.round(percent * 100) + '%'); - spans.eq(1).css('width', Math.round(percent * 100) + '%'); - updateStatus(); - } - - function setState(val, files) { - - if (val != state) { - - var stats = uploader.getStats(); - - $upload.removeClass('state-' + state); - $upload.addClass('state-' + val); - - switch (val) { - - /* 未选择文件 */ - case 'pedding': - $queue.addClass('element-invisible'); - $statusBar.addClass('element-invisible'); - $placeHolder.removeClass('element-invisible'); - $progress.hide(); $info.hide(); - uploader.refresh(); - break; - - /* 可以开始上传 */ - case 'ready': - $placeHolder.addClass('element-invisible'); - $queue.removeClass('element-invisible'); - $statusBar.removeClass('element-invisible'); - $progress.hide(); $info.show(); - $upload.text(lang.uploadStart); - uploader.refresh(); - break; - - /* 上传中 */ - case 'uploading': - $progress.show(); $info.hide(); - $upload.text(lang.uploadPause); - break; - - /* 暂停上传 */ - case 'paused': - $progress.show(); $info.hide(); - $upload.text(lang.uploadContinue); - break; - - case 'confirm': - $progress.show(); $info.hide(); - $upload.text(lang.uploadStart); - - stats = uploader.getStats(); - if (stats.successNum && !stats.uploadFailNum) { - setState('finish'); - return; - } - break; - - case 'finish': - $progress.hide(); $info.show(); - if (stats.uploadFailNum) { - $upload.text(lang.uploadRetry); - } else { - $upload.text(lang.uploadStart); - } - break; - } - - state = val; - updateStatus(); - - } - - if (!_this.getQueueCount()) { - $upload.addClass('disabled') - } else { - $upload.removeClass('disabled') - } - - } - - function updateStatus() { - var text = '', stats; - - if (state === 'ready') { - text = lang.updateStatusReady.replace('_', fileCount).replace('_KB', WebUploader.formatSize(fileSize)); - } else if (state === 'confirm') { - stats = uploader.getStats(); - if (stats.uploadFailNum) { - text = lang.updateStatusConfirm.replace('_', stats.successNum).replace('_', stats.successNum); - } - } else { - stats = uploader.getStats(); - text = lang.updateStatusFinish.replace('_', fileCount). - replace('_KB', WebUploader.formatSize(fileSize)). - replace('_', stats.successNum); - - if (stats.uploadFailNum) { - text += lang.updateStatusError.replace('_', stats.uploadFailNum); - } - } - - $info.html(text); - } - - uploader.on('fileQueued', function (file) { - fileCount++; - fileSize += file.size; - - if (fileCount === 1) { - $placeHolder.addClass('element-invisible'); - $statusBar.show(); - } - - addFile(file); - }); - - uploader.on('fileDequeued', function (file) { - fileCount--; - fileSize -= file.size; - - removeFile(file); - updateTotalProgress(); - }); - - uploader.on('filesQueued', function (file) { - if (!uploader.isInProgress() && (state == 'pedding' || state == 'finish' || state == 'confirm' || state == 'ready')) { - setState('ready'); - } - updateTotalProgress(); - }); - - uploader.on('all', function (type, files) { - switch (type) { - case 'uploadFinished': - setState('confirm', files); - break; - case 'startUpload': - /* 添加额外的GET参数 */ - var params = utils.serializeParam(editor.queryCommandValue('serverparam')) || '', - url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?':'&') + 'encode=utf-8&' + params); - uploader.option('server', url); - setState('uploading', files); - break; - case 'stopUpload': - setState('paused', files); - break; - } - }); - - uploader.on('uploadBeforeSend', function (file, data, header) { - //这里可以通过data对象添加POST参数 - header['X_Requested_With'] = 'XMLHttpRequest'; - }); - - uploader.on('uploadProgress', function (file, percentage) { - var $li = $('#' + file.id), - $percent = $li.find('.progress span'); - - $percent.css('width', percentage * 100 + '%'); - percentages[ file.id ][ 1 ] = percentage; - updateTotalProgress(); - }); - - uploader.on('uploadSuccess', function (file, ret) { - var $file = $('#' + file.id); - try { - var responseText = (ret._raw || ret), - json = utils.str2json(responseText); - if (json.state == 'SUCCESS') { - uploadVideoList.push({ - 'url': json.url, - 'type': json.type, - 'original':json.original - }); - $file.append(''); - } else { - $file.find('.error').text(json.state).show(); - } - } catch (e) { - $file.find('.error').text(lang.errorServerUpload).show(); - } - }); - - uploader.on('uploadError', function (file, code) { - }); - uploader.on('error', function (code, file) { - if (code == 'Q_TYPE_DENIED' || code == 'F_EXCEED_SIZE') { - addFile(file); - } - }); - uploader.on('uploadComplete', function (file, ret) { - }); - - $upload.on('click', function () { - if ($(this).hasClass('disabled')) { - return false; - } - - if (state === 'ready') { - uploader.upload(); - } else if (state === 'paused') { - uploader.upload(); - } else if (state === 'uploading') { - uploader.stop(); - } - }); - - $upload.addClass('state-' + state); - updateTotalProgress(); - }, - getQueueCount: function () { - var file, i, status, readyFile = 0, files = this.uploader.getFiles(); - for (i = 0; file = files[i++]; ) { - status = file.getStatus(); - if (status == 'queued' || status == 'uploading' || status == 'progress') readyFile++; - } - return readyFile; - }, - refresh: function(){ - this.uploader.refresh(); - } - }; - -})(); \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/webapp/webapp.html b/static/plugs/ueditor/dialogs/webapp/webapp.html deleted file mode 100644 index 73f8dc23f..000000000 --- a/static/plugs/ueditor/dialogs/webapp/webapp.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - -
                  -
                  -
                  - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/wordimage/fClipboard_ueditor.swf b/static/plugs/ueditor/dialogs/wordimage/fClipboard_ueditor.swf deleted file mode 100644 index ac5d27f81..000000000 Binary files a/static/plugs/ueditor/dialogs/wordimage/fClipboard_ueditor.swf and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/wordimage/imageUploader.swf b/static/plugs/ueditor/dialogs/wordimage/imageUploader.swf deleted file mode 100644 index 2a554cadb..000000000 Binary files a/static/plugs/ueditor/dialogs/wordimage/imageUploader.swf and /dev/null differ diff --git a/static/plugs/ueditor/dialogs/wordimage/tangram.js b/static/plugs/ueditor/dialogs/wordimage/tangram.js deleted file mode 100644 index 2ebd8fd3d..000000000 --- a/static/plugs/ueditor/dialogs/wordimage/tangram.js +++ /dev/null @@ -1,1495 +0,0 @@ -// Copyright (c) 2009, Baidu Inc. All rights reserved. -// -// Licensed under the BSD License -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http:// tangram.baidu.com/license.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - /** - * @namespace T Tangram七巧板 - * @name T - * @version 1.6.0 -*/ - -/** - * 声明baidu包 - * @author: allstar, erik, meizz, berg - */ -var T, - baidu = T = baidu || {version: "1.5.0"}; -baidu.guid = "$BAIDU$"; -baidu.$$ = window[baidu.guid] = window[baidu.guid] || {global:{}}; - -/** - * 使用flash资源封装的一些功能 - * @namespace baidu.flash - */ -baidu.flash = baidu.flash || {}; - -/** - * 操作dom的方法 - * @namespace baidu.dom - */ -baidu.dom = baidu.dom || {}; - - -/** - * 从文档中获取指定的DOM元素 - * @name baidu.dom.g - * @function - * @grammar baidu.dom.g(id) - * @param {string|HTMLElement} id 元素的id或DOM元素. - * @shortcut g,T.G - * @meta standard - * @see baidu.dom.q - * - * @return {HTMLElement|null} 获取的元素,查找不到时返回null,如果参数不合法,直接返回参数. - */ -baidu.dom.g = function(id) { - if (!id) return null; - if ('string' == typeof id || id instanceof String) { - return document.getElementById(id); - } else if (id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) { - return id; - } - return null; -}; -baidu.g = baidu.G = baidu.dom.g; - - -/** - * 操作数组的方法 - * @namespace baidu.array - */ - -baidu.array = baidu.array || {}; - - -/** - * 遍历数组中所有元素 - * @name baidu.array.each - * @function - * @grammar baidu.array.each(source, iterator[, thisObject]) - * @param {Array} source 需要遍历的数组 - * @param {Function} iterator 对每个数组元素进行调用的函数,该函数有两个参数,第一个为数组元素,第二个为数组索引值,function (item, index)。 - * @param {Object} [thisObject] 函数调用时的this指针,如果没有此参数,默认是当前遍历的数组 - * @remark - * each方法不支持对Object的遍历,对Object的遍历使用baidu.object.each 。 - * @shortcut each - * @meta standard - * - * @returns {Array} 遍历的数组 - */ - -baidu.each = baidu.array.forEach = baidu.array.each = function (source, iterator, thisObject) { - var returnValue, item, i, len = source.length; - - if ('function' == typeof iterator) { - for (i = 0; i < len; i++) { - item = source[i]; - returnValue = iterator.call(thisObject || source, item, i); - - if (returnValue === false) { - break; - } - } - } - return source; -}; - -/** - * 对语言层面的封装,包括类型判断、模块扩展、继承基类以及对象自定义事件的支持。 - * @namespace baidu.lang - */ -baidu.lang = baidu.lang || {}; - - -/** - * 判断目标参数是否为function或Function实例 - * @name baidu.lang.isFunction - * @function - * @grammar baidu.lang.isFunction(source) - * @param {Any} source 目标参数 - * @version 1.2 - * @see baidu.lang.isString,baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate - * @meta standard - * @returns {boolean} 类型判断结果 - */ -baidu.lang.isFunction = function (source) { - return '[object Function]' == Object.prototype.toString.call(source); -}; - -/** - * 判断目标参数是否string类型或String对象 - * @name baidu.lang.isString - * @function - * @grammar baidu.lang.isString(source) - * @param {Any} source 目标参数 - * @shortcut isString - * @meta standard - * @see baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate - * - * @returns {boolean} 类型判断结果 - */ -baidu.lang.isString = function (source) { - return '[object String]' == Object.prototype.toString.call(source); -}; -baidu.isString = baidu.lang.isString; - - -/** - * 判断浏览器类型和特性的属性 - * @namespace baidu.browser - */ -baidu.browser = baidu.browser || {}; - - -/** - * 判断是否为opera浏览器 - * @property opera opera版本号 - * @grammar baidu.browser.opera - * @meta standard - * @see baidu.browser.ie,baidu.browser.firefox,baidu.browser.safari,baidu.browser.chrome - * @returns {Number} opera版本号 - */ - -/** - * opera 从10开始不是用opera后面的字符串进行版本的判断 - * 在Browser identification最后添加Version + 数字进行版本标识 - * opera后面的数字保持在9.80不变 - */ -baidu.browser.opera = /opera(\/| )(\d+(\.\d+)?)(.+?(version\/(\d+(\.\d+)?)))?/i.test(navigator.userAgent) ? + ( RegExp["\x246"] || RegExp["\x242"] ) : undefined; - - -/** - * 在目标元素的指定位置插入HTML代码 - * @name baidu.dom.insertHTML - * @function - * @grammar baidu.dom.insertHTML(element, position, html) - * @param {HTMLElement|string} element 目标元素或目标元素的id - * @param {string} position 插入html的位置信息,取值为beforeBegin,afterBegin,beforeEnd,afterEnd - * @param {string} html 要插入的html - * @remark - * - * 对于position参数,大小写不敏感
                  - * 参数的意思:beforeBegin<span>afterBegin this is span! beforeEnd</span> afterEnd
                  - * 此外,如果使用本函数插入带有script标签的HTML字符串,script标签对应的脚本将不会被执行。 - * - * @shortcut insertHTML - * @meta standard - * - * @returns {HTMLElement} 目标元素 - */ -baidu.dom.insertHTML = function (element, position, html) { - element = baidu.dom.g(element); - var range,begin; - if (element.insertAdjacentHTML && !baidu.browser.opera) { - element.insertAdjacentHTML(position, html); - } else { - range = element.ownerDocument.createRange(); - position = position.toUpperCase(); - if (position == 'AFTERBEGIN' || position == 'BEFOREEND') { - range.selectNodeContents(element); - range.collapse(position == 'AFTERBEGIN'); - } else { - begin = position == 'BEFOREBEGIN'; - range[begin ? 'setStartBefore' : 'setEndAfter'](element); - range.collapse(begin); - } - range.insertNode(range.createContextualFragment(html)); - } - return element; -}; - -baidu.insertHTML = baidu.dom.insertHTML; - -/** - * 操作flash对象的方法,包括创建flash对象、获取flash对象以及判断flash插件的版本号 - * @namespace baidu.swf - */ -baidu.swf = baidu.swf || {}; - - -/** - * 浏览器支持的flash插件版本 - * @property version 浏览器支持的flash插件版本 - * @grammar baidu.swf.version - * @return {String} 版本号 - * @meta standard - */ -baidu.swf.version = (function () { - var n = navigator; - if (n.plugins && n.mimeTypes.length) { - var plugin = n.plugins["Shockwave Flash"]; - if (plugin && plugin.description) { - return plugin.description - .replace(/([a-zA-Z]|\s)+/, "") - .replace(/(\s)+r/, ".") + ".0"; - } - } else if (window.ActiveXObject && !window.opera) { - for (var i = 12; i >= 2; i--) { - try { - var c = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.' + i); - if (c) { - var version = c.GetVariable("$version"); - return version.replace(/WIN/g,'').replace(/,/g,'.'); - } - } catch(e) {} - } - } -})(); - -/** - * 操作字符串的方法 - * @namespace baidu.string - */ -baidu.string = baidu.string || {}; - - -/** - * 对目标字符串进行html编码 - * @name baidu.string.encodeHTML - * @function - * @grammar baidu.string.encodeHTML(source) - * @param {string} source 目标字符串 - * @remark - * 编码字符有5个:&<>"' - * @shortcut encodeHTML - * @meta standard - * @see baidu.string.decodeHTML - * - * @returns {string} html编码后的字符串 - */ -baidu.string.encodeHTML = function (source) { - return String(source) - .replace(/&/g,'&') - .replace(//g,'>') - .replace(/"/g, """) - .replace(/'/g, "'"); -}; - -baidu.encodeHTML = baidu.string.encodeHTML; - -/** - * 创建flash对象的html字符串 - * @name baidu.swf.createHTML - * @function - * @grammar baidu.swf.createHTML(options) - * - * @param {Object} options 创建flash的选项参数 - * @param {string} options.id 要创建的flash的标识 - * @param {string} options.url flash文件的url - * @param {String} options.errorMessage 未安装flash player或flash player版本号过低时的提示 - * @param {string} options.ver 最低需要的flash player版本号 - * @param {string} options.width flash的宽度 - * @param {string} options.height flash的高度 - * @param {string} options.align flash的对齐方式,允许值:middle/left/right/top/bottom - * @param {string} options.base 设置用于解析swf文件中的所有相对路径语句的基本目录或URL - * @param {string} options.bgcolor swf文件的背景色 - * @param {string} options.salign 设置缩放的swf文件在由width和height设置定义的区域内的位置。允许值:l/r/t/b/tl/tr/bl/br - * @param {boolean} options.menu 是否显示右键菜单,允许值:true/false - * @param {boolean} options.loop 播放到最后一帧时是否重新播放,允许值: true/false - * @param {boolean} options.play flash是否在浏览器加载时就开始播放。允许值:true/false - * @param {string} options.quality 设置flash播放的画质,允许值:low/medium/high/autolow/autohigh/best - * @param {string} options.scale 设置flash内容如何缩放来适应设置的宽高。允许值:showall/noborder/exactfit - * @param {string} options.wmode 设置flash的显示模式。允许值:window/opaque/transparent - * @param {string} options.allowscriptaccess 设置flash与页面的通信权限。允许值:always/never/sameDomain - * @param {string} options.allownetworking 设置swf文件中允许使用的网络API。允许值:all/internal/none - * @param {boolean} options.allowfullscreen 是否允许flash全屏。允许值:true/false - * @param {boolean} options.seamlesstabbing 允许设置执行无缝跳格,从而使用户能跳出flash应用程序。该参数只能在安装Flash7及更高版本的Windows中使用。允许值:true/false - * @param {boolean} options.devicefont 设置静态文本对象是否以设备字体呈现。允许值:true/false - * @param {boolean} options.swliveconnect 第一次加载flash时浏览器是否应启动Java。允许值:true/false - * @param {Object} options.vars 要传递给flash的参数,支持JSON或string类型。 - * - * @see baidu.swf.create - * @meta standard - * @returns {string} flash对象的html字符串 - */ -baidu.swf.createHTML = function (options) { - options = options || {}; - var version = baidu.swf.version, - needVersion = options['ver'] || '6.0.0', - vUnit1, vUnit2, i, k, len, item, tmpOpt = {}, - encodeHTML = baidu.string.encodeHTML; - for (k in options) { - tmpOpt[k] = options[k]; - } - options = tmpOpt; - if (version) { - version = version.split('.'); - needVersion = needVersion.split('.'); - for (i = 0; i < 3; i++) { - vUnit1 = parseInt(version[i], 10); - vUnit2 = parseInt(needVersion[i], 10); - if (vUnit2 < vUnit1) { - break; - } else if (vUnit2 > vUnit1) { - return ''; - } - } - } else { - return ''; - } - - var vars = options['vars'], - objProperties = ['classid', 'codebase', 'id', 'width', 'height', 'align']; - options['align'] = options['align'] || 'middle'; - options['classid'] = 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000'; - options['codebase'] = 'http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0'; - options['movie'] = options['url'] || ''; - delete options['vars']; - delete options['url']; - if ('string' == typeof vars) { - options['flashvars'] = vars; - } else { - var fvars = []; - for (k in vars) { - item = vars[k]; - fvars.push(k + "=" + encodeURIComponent(item)); - } - options['flashvars'] = fvars.join('&'); - } - var str = [''); - var params = { - 'wmode' : 1, - 'scale' : 1, - 'quality' : 1, - 'play' : 1, - 'loop' : 1, - 'menu' : 1, - 'salign' : 1, - 'bgcolor' : 1, - 'base' : 1, - 'allowscriptaccess' : 1, - 'allownetworking' : 1, - 'allowfullscreen' : 1, - 'seamlesstabbing' : 1, - 'devicefont' : 1, - 'swliveconnect' : 1, - 'flashvars' : 1, - 'movie' : 1 - }; - - for (k in options) { - item = options[k]; - k = k.toLowerCase(); - if (params[k] && (item || item === false || item === 0)) { - str.push(''); - } - } - options['src'] = options['movie']; - options['name'] = options['id']; - delete options['id']; - delete options['movie']; - delete options['classid']; - delete options['codebase']; - options['type'] = 'application/x-shockwave-flash'; - options['pluginspage'] = 'http://www.macromedia.com/go/getflashplayer'; - str.push(''); - - return str.join(''); -}; - - -/** - * 在页面中创建一个flash对象 - * @name baidu.swf.create - * @function - * @grammar baidu.swf.create(options[, container]) - * - * @param {Object} options 创建flash的选项参数 - * @param {string} options.id 要创建的flash的标识 - * @param {string} options.url flash文件的url - * @param {String} options.errorMessage 未安装flash player或flash player版本号过低时的提示 - * @param {string} options.ver 最低需要的flash player版本号 - * @param {string} options.width flash的宽度 - * @param {string} options.height flash的高度 - * @param {string} options.align flash的对齐方式,允许值:middle/left/right/top/bottom - * @param {string} options.base 设置用于解析swf文件中的所有相对路径语句的基本目录或URL - * @param {string} options.bgcolor swf文件的背景色 - * @param {string} options.salign 设置缩放的swf文件在由width和height设置定义的区域内的位置。允许值:l/r/t/b/tl/tr/bl/br - * @param {boolean} options.menu 是否显示右键菜单,允许值:true/false - * @param {boolean} options.loop 播放到最后一帧时是否重新播放,允许值: true/false - * @param {boolean} options.play flash是否在浏览器加载时就开始播放。允许值:true/false - * @param {string} options.quality 设置flash播放的画质,允许值:low/medium/high/autolow/autohigh/best - * @param {string} options.scale 设置flash内容如何缩放来适应设置的宽高。允许值:showall/noborder/exactfit - * @param {string} options.wmode 设置flash的显示模式。允许值:window/opaque/transparent - * @param {string} options.allowscriptaccess 设置flash与页面的通信权限。允许值:always/never/sameDomain - * @param {string} options.allownetworking 设置swf文件中允许使用的网络API。允许值:all/internal/none - * @param {boolean} options.allowfullscreen 是否允许flash全屏。允许值:true/false - * @param {boolean} options.seamlesstabbing 允许设置执行无缝跳格,从而使用户能跳出flash应用程序。该参数只能在安装Flash7及更高版本的Windows中使用。允许值:true/false - * @param {boolean} options.devicefont 设置静态文本对象是否以设备字体呈现。允许值:true/false - * @param {boolean} options.swliveconnect 第一次加载flash时浏览器是否应启动Java。允许值:true/false - * @param {Object} options.vars 要传递给flash的参数,支持JSON或string类型。 - * - * @param {HTMLElement|string} [container] flash对象的父容器元素,不传递该参数时在当前代码位置创建flash对象。 - * @meta standard - * @see baidu.swf.createHTML,baidu.swf.getMovie - */ -baidu.swf.create = function (options, target) { - options = options || {}; - var html = baidu.swf.createHTML(options) - || options['errorMessage'] - || ''; - - if (target && 'string' == typeof target) { - target = document.getElementById(target); - } - baidu.dom.insertHTML( target || document.body ,'beforeEnd',html ); -}; -/** - * 判断是否为ie浏览器 - * @name baidu.browser.ie - * @field - * @grammar baidu.browser.ie - * @returns {Number} IE版本号 - */ -baidu.browser.ie = baidu.ie = /msie (\d+\.\d+)/i.test(navigator.userAgent) ? (document.documentMode || + RegExp['\x241']) : undefined; - -/** - * 移除数组中的项 - * @name baidu.array.remove - * @function - * @grammar baidu.array.remove(source, match) - * @param {Array} source 需要移除项的数组 - * @param {Any} match 要移除的项 - * @meta standard - * @see baidu.array.removeAt - * - * @returns {Array} 移除后的数组 - */ -baidu.array.remove = function (source, match) { - var len = source.length; - - while (len--) { - if (len in source && source[len] === match) { - source.splice(len, 1); - } - } - return source; -}; - -/** - * 判断目标参数是否Array对象 - * @name baidu.lang.isArray - * @function - * @grammar baidu.lang.isArray(source) - * @param {Any} source 目标参数 - * @meta standard - * @see baidu.lang.isString,baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate - * - * @returns {boolean} 类型判断结果 - */ -baidu.lang.isArray = function (source) { - return '[object Array]' == Object.prototype.toString.call(source); -}; - - - -/** - * 将一个变量转换成array - * @name baidu.lang.toArray - * @function - * @grammar baidu.lang.toArray(source) - * @param {mix} source 需要转换成array的变量 - * @version 1.3 - * @meta standard - * @returns {array} 转换后的array - */ -baidu.lang.toArray = function (source) { - if (source === null || source === undefined) - return []; - if (baidu.lang.isArray(source)) - return source; - if (typeof source.length !== 'number' || typeof source === 'string' || baidu.lang.isFunction(source)) { - return [source]; - } - if (source.item) { - var l = source.length, array = new Array(l); - while (l--) - array[l] = source[l]; - return array; - } - - return [].slice.call(source); -}; - -/** - * 获得flash对象的实例 - * @name baidu.swf.getMovie - * @function - * @grammar baidu.swf.getMovie(name) - * @param {string} name flash对象的名称 - * @see baidu.swf.create - * @meta standard - * @returns {HTMLElement} flash对象的实例 - */ -baidu.swf.getMovie = function (name) { - var movie = document[name], ret; - return baidu.browser.ie == 9 ? - movie && movie.length ? - (ret = baidu.array.remove(baidu.lang.toArray(movie),function(item){ - return item.tagName.toLowerCase() != "embed"; - })).length == 1 ? ret[0] : ret - : movie - : movie || window[name]; -}; - - -baidu.flash._Base = (function(){ - - var prefix = 'bd__flash__'; - - /** - * 创建一个随机的字符串 - * @private - * @return {String} - */ - function _createString(){ - return prefix + Math.floor(Math.random() * 2147483648).toString(36); - }; - - /** - * 检查flash状态 - * @private - * @param {Object} target flash对象 - * @return {Boolean} - */ - function _checkReady(target){ - if(typeof target !== 'undefined' && typeof target.flashInit !== 'undefined' && target.flashInit()){ - return true; - }else{ - return false; - } - }; - - /** - * 调用之前进行压栈的函数 - * @private - * @param {Array} callQueue 调用队列 - * @param {Object} target flash对象 - * @return {Null} - */ - function _callFn(callQueue, target){ - var result = null; - - callQueue = callQueue.reverse(); - baidu.each(callQueue, function(item){ - result = target.call(item.fnName, item.params); - item.callBack(result); - }); - }; - - /** - * 为传入的匿名函数创建函数名 - * @private - * @param {String|Function} fun 传入的匿名函数或者函数名 - * @return {String} - */ - function _createFunName(fun){ - var name = ''; - - if(baidu.lang.isFunction(fun)){ - name = _createString(); - window[name] = function(){ - fun.apply(window, arguments); - }; - - return name; - }else if(baidu.lang.isString){ - return fun; - } - }; - - /** - * 绘制flash - * @private - * @param {Object} options 创建参数 - * @return {Object} - */ - function _render(options){ - if(!options.id){ - options.id = _createString(); - } - - var container = options.container || ''; - delete(options.container); - - baidu.swf.create(options, container); - - return baidu.swf.getMovie(options.id); - }; - - return function(options, callBack){ - var me = this, - autoRender = (typeof options.autoRender !== 'undefined' ? options.autoRender : true), - createOptions = options.createOptions || {}, - target = null, - isReady = false, - callQueue = [], - timeHandle = null, - callBack = callBack || []; - - /** - * 将flash文件绘制到页面上 - * @public - * @return {Null} - */ - me.render = function(){ - target = _render(createOptions); - - if(callBack.length > 0){ - baidu.each(callBack, function(funName, index){ - callBack[index] = _createFunName(options[funName] || new Function()); - }); - } - me.call('setJSFuncName', [callBack]); - }; - - /** - * 返回flash状态 - * @return {Boolean} - */ - me.isReady = function(){ - return isReady; - }; - - /** - * 调用flash接口的统一入口 - * @param {String} fnName 调用的函数名 - * @param {Array} params 传入的参数组成的数组,若不许要参数,需传入空数组 - * @param {Function} [callBack] 异步调用后将返回值作为参数的调用回调函数,如无返回值,可以不传入此参数 - * @return {Null} - */ - me.call = function(fnName, params, callBack){ - if(!fnName) return null; - callBack = callBack || new Function(); - - var result = null; - - if(isReady){ - result = target.call(fnName, params); - callBack(result); - }else{ - callQueue.push({ - fnName: fnName, - params: params, - callBack: callBack - }); - - (!timeHandle) && (timeHandle = setInterval(_check, 200)); - } - }; - - /** - * 为传入的匿名函数创建函数名 - * @public - * @param {String|Function} fun 传入的匿名函数或者函数名 - * @return {String} - */ - me.createFunName = function(fun){ - return _createFunName(fun); - }; - - /** - * 检查flash是否ready, 并进行调用 - * @private - * @return {Null} - */ - function _check(){ - if(_checkReady(target)){ - clearInterval(timeHandle); - timeHandle = null; - _call(); - - isReady = true; - } - }; - - /** - * 调用之前进行压栈的函数 - * @private - * @return {Null} - */ - function _call(){ - _callFn(callQueue, target); - callQueue = []; - } - - autoRender && me.render(); - }; -})(); - - - -/** - * 创建flash based imageUploader - * @class - * @grammar baidu.flash.imageUploader(options) - * @param {Object} createOptions 创建flash时需要的参数,请参照baidu.swf.create文档 - * @config {Object} vars 创建imageUploader时所需要的参数 - * @config {Number} vars.gridWidth 每一个预览图片所占的宽度,应该为flash寛的整除 - * @config {Number} vars.gridHeight 每一个预览图片所占的高度,应该为flash高的整除 - * @config {Number} vars.picWidth 单张预览图片的宽度 - * @config {Number} vars.picHeight 单张预览图片的高度 - * @config {String} vars.uploadDataFieldName POST请求中图片数据的key,默认值'picdata' - * @config {String} vars.picDescFieldName POST请求中图片描述的key,默认值'picDesc' - * @config {Number} vars.maxSize 文件的最大体积,单位'MB' - * @config {Number} vars.compressSize 上传前如果图片体积超过该值,会先压缩 - * @config {Number} vars.maxNum:32 最大上传多少个文件 - * @config {Number} vars.compressLength 能接受的最大边长,超过该值会等比压缩 - * @config {String} vars.url 上传的url地址 - * @config {Number} vars.mode mode == 0时,是使用滚动条,mode == 1时,拉伸flash, 默认值为0 - * @see baidu.swf.createHTML - * @param {String} backgroundUrl 背景图片路径 - * @param {String} listBacgroundkUrl 布局控件背景 - * @param {String} buttonUrl 按钮图片不背景 - * @param {String|Function} selectFileCallback 选择文件的回调 - * @param {String|Function} exceedFileCallback文件超出限制的最大体积时的回调 - * @param {String|Function} deleteFileCallback 删除文件的回调 - * @param {String|Function} startUploadCallback 开始上传某个文件时的回调 - * @param {String|Function} uploadCompleteCallback 某个文件上传完成的回调 - * @param {String|Function} uploadErrorCallback 某个文件上传失败的回调 - * @param {String|Function} allCompleteCallback 全部上传完成时的回调 - * @param {String|Function} changeFlashHeight 改变Flash的高度,mode==1的时候才有用 - */ -baidu.flash.imageUploader = baidu.flash.imageUploader || function(options){ - - var me = this, - options = options || {}, - _flash = new baidu.flash._Base(options, [ - 'selectFileCallback', - 'exceedFileCallback', - 'deleteFileCallback', - 'startUploadCallback', - 'uploadCompleteCallback', - 'uploadErrorCallback', - 'allCompleteCallback', - 'changeFlashHeight' - ]); - /** - * 开始或回复上传图片 - * @public - * @return {Null} - */ - me.upload = function(){ - _flash.call('upload'); - }; - - /** - * 暂停上传图片 - * @public - * @return {Null} - */ - me.pause = function(){ - _flash.call('pause'); - }; - me.addCustomizedParams = function(index,obj){ - _flash.call('addCustomizedParams',[index,obj]); - } -}; - -/** - * 操作原生对象的方法 - * @namespace baidu.object - */ -baidu.object = baidu.object || {}; - - -/** - * 将源对象的所有属性拷贝到目标对象中 - * @author erik - * @name baidu.object.extend - * @function - * @grammar baidu.object.extend(target, source) - * @param {Object} target 目标对象 - * @param {Object} source 源对象 - * @see baidu.array.merge - * @remark - * -1.目标对象中,与源对象key相同的成员将会被覆盖。
                  -2.源对象的prototype成员不会拷贝。 - - * @shortcut extend - * @meta standard - * - * @returns {Object} 目标对象 - */ -baidu.extend = -baidu.object.extend = function (target, source) { - for (var p in source) { - if (source.hasOwnProperty(p)) { - target[p] = source[p]; - } - } - - return target; -}; - - - - - -/** - * 创建flash based fileUploader - * @class - * @grammar baidu.flash.fileUploader(options) - * @param {Object} options - * @config {Object} createOptions 创建flash时需要的参数,请参照baidu.swf.create文档 - * @config {String} createOptions.width - * @config {String} createOptions.height - * @config {Number} maxNum 最大可选文件数 - * @config {Function|String} selectFile - * @config {Function|String} exceedMaxSize - * @config {Function|String} deleteFile - * @config {Function|String} uploadStart - * @config {Function|String} uploadComplete - * @config {Function|String} uploadError - * @config {Function|String} uploadProgress - */ -baidu.flash.fileUploader = baidu.flash.fileUploader || function(options){ - var me = this, - options = options || {}; - - options.createOptions = baidu.extend({ - wmod: 'transparent' - },options.createOptions || {}); - - var _flash = new baidu.flash._Base(options, [ - 'selectFile', - 'exceedMaxSize', - 'deleteFile', - 'uploadStart', - 'uploadComplete', - 'uploadError', - 'uploadProgress' - ]); - - _flash.call('setMaxNum', options.maxNum ? [options.maxNum] : [1]); - - /** - * 设置当鼠标移动到flash上时,是否变成手型 - * @public - * @param {Boolean} isCursor - * @return {Null} - */ - me.setHandCursor = function(isCursor){ - _flash.call('setHandCursor', [isCursor || false]); - }; - - /** - * 设置鼠标相应函数名 - * @param {String|Function} fun - */ - me.setMSFunName = function(fun){ - _flash.call('setMSFunName',[_flash.createFunName(fun)]); - }; - - /** - * 执行上传操作 - * @param {String} url 上传的url - * @param {String} fieldName 上传的表单字段名 - * @param {Object} postData 键值对,上传的POST数据 - * @param {Number|Array|null|-1} [index]上传的文件序列 - * Int值上传该文件 - * Array一次串行上传该序列文件 - * -1/null上传所有文件 - * @return {Null} - */ - me.upload = function(url, fieldName, postData, index){ - - if(typeof url !== 'string' || typeof fieldName !== 'string') return null; - if(typeof index === 'undefined') index = -1; - - _flash.call('upload', [url, fieldName, postData, index]); - }; - - /** - * 取消上传操作 - * @public - * @param {Number|-1} index - */ - me.cancel = function(index){ - if(typeof index === 'undefined') index = -1; - _flash.call('cancel', [index]); - }; - - /** - * 删除文件 - * @public - * @param {Number|Array} [index] 要删除的index,不传则全部删除 - * @param {Function} callBack - * */ - me.deleteFile = function(index, callBack){ - - var callBackAll = function(list){ - callBack && callBack(list); - }; - - if(typeof index === 'undefined'){ - _flash.call('deleteFilesAll', [], callBackAll); - return; - }; - - if(typeof index === 'Number') index = [index]; - index.sort(function(a,b){ - return b-a; - }); - baidu.each(index, function(item){ - _flash.call('deleteFileBy', item, callBackAll); - }); - }; - - /** - * 添加文件类型,支持macType - * @public - * @param {Object|Array[Object]} type {description:String, extention:String} - * @return {Null}; - */ - me.addFileType = function(type){ - var type = type || [[]]; - - if(type instanceof Array) type = [type]; - else type = [[type]]; - _flash.call('addFileTypes', type); - }; - - /** - * 设置文件类型,支持macType - * @public - * @param {Object|Array[Object]} type {description:String, extention:String} - * @return {Null}; - */ - me.setFileType = function(type){ - var type = type || [[]]; - - if(type instanceof Array) type = [type]; - else type = [[type]]; - _flash.call('setFileTypes', type); - }; - - /** - * 设置可选文件的数量限制 - * @public - * @param {Number} num - * @return {Null} - */ - me.setMaxNum = function(num){ - _flash.call('setMaxNum', [num]); - }; - - /** - * 设置可选文件大小限制,以兆M为单位 - * @public - * @param {Number} num,0为无限制 - * @return {Null} - */ - me.setMaxSize = function(num){ - _flash.call('setMaxSize', [num]); - }; - - /** - * @public - */ - me.getFileAll = function(callBack){ - _flash.call('getFileAll', [], callBack); - }; - - /** - * @public - * @param {Number} index - * @param {Function} [callBack] - */ - me.getFileByIndex = function(index, callBack){ - _flash.call('getFileByIndex', [], callBack); - }; - - /** - * @public - * @param {Number} index - * @param {function} [callBack] - */ - me.getStatusByIndex = function(index, callBack){ - _flash.call('getStatusByIndex', [], callBack); - }; -}; - -/** - * 使用动态script标签请求服务器资源,包括由服务器端的回调和浏览器端的回调 - * @namespace baidu.sio - */ -baidu.sio = baidu.sio || {}; - -/** - * - * @param {HTMLElement} src script节点 - * @param {String} url script节点的地址 - * @param {String} [charset] 编码 - */ -baidu.sio._createScriptTag = function(scr, url, charset){ - scr.setAttribute('type', 'text/javascript'); - charset && scr.setAttribute('charset', charset); - scr.setAttribute('src', url); - document.getElementsByTagName('head')[0].appendChild(scr); -}; - -/** - * 删除script的属性,再删除script标签,以解决修复内存泄漏的问题 - * - * @param {HTMLElement} src script节点 - */ -baidu.sio._removeScriptTag = function(scr){ - if (scr.clearAttributes) { - scr.clearAttributes(); - } else { - for (var attr in scr) { - if (scr.hasOwnProperty(attr)) { - delete scr[attr]; - } - } - } - if(scr && scr.parentNode){ - scr.parentNode.removeChild(scr); - } - scr = null; -}; - - -/** - * 通过script标签加载数据,加载完成由浏览器端触发回调 - * @name baidu.sio.callByBrowser - * @function - * @grammar baidu.sio.callByBrowser(url, opt_callback, opt_options) - * @param {string} url 加载数据的url - * @param {Function|string} opt_callback 数据加载结束时调用的函数或函数名 - * @param {Object} opt_options 其他可选项 - * @config {String} [charset] script的字符集 - * @config {Integer} [timeOut] 超时时间,超过这个时间将不再响应本请求,并触发onfailure函数 - * @config {Function} [onfailure] timeOut设定后才生效,到达超时时间时触发本函数 - * @remark - * 1、与callByServer不同,callback参数只支持Function类型,不支持string。 - * 2、如果请求了一个不存在的页面,callback函数在IE/opera下也会被调用,因此使用者需要在onsuccess函数中判断数据是否正确加载。 - * @meta standard - * @see baidu.sio.callByServer - */ -baidu.sio.callByBrowser = function (url, opt_callback, opt_options) { - var scr = document.createElement("SCRIPT"), - scriptLoaded = 0, - options = opt_options || {}, - charset = options['charset'], - callback = opt_callback || function(){}, - timeOut = options['timeOut'] || 0, - timer; - scr.onload = scr.onreadystatechange = function () { - if (scriptLoaded) { - return; - } - - var readyState = scr.readyState; - if ('undefined' == typeof readyState - || readyState == "loaded" - || readyState == "complete") { - scriptLoaded = 1; - try { - callback(); - clearTimeout(timer); - } finally { - scr.onload = scr.onreadystatechange = null; - baidu.sio._removeScriptTag(scr); - } - } - }; - - if( timeOut ){ - timer = setTimeout(function(){ - scr.onload = scr.onreadystatechange = null; - baidu.sio._removeScriptTag(scr); - options.onfailure && options.onfailure(); - }, timeOut); - } - - baidu.sio._createScriptTag(scr, url, charset); -}; - -/** - * 通过script标签加载数据,加载完成由服务器端触发回调 - * @name baidu.sio.callByServer - * @function - * @grammar baidu.sio.callByServer(url, callback[, opt_options]) - * @param {string} url 加载数据的url. - * @param {Function|string} callback 服务器端调用的函数或函数名。如果没有指定本参数,将在URL中寻找options['queryField']做为callback的方法名. - * @param {Object} opt_options 加载数据时的选项. - * @config {string} [charset] script的字符集 - * @config {string} [queryField] 服务器端callback请求字段名,默认为callback - * @config {Integer} [timeOut] 超时时间(单位:ms),超过这个时间将不再响应本请求,并触发onfailure函数 - * @config {Function} [onfailure] timeOut设定后才生效,到达超时时间时触发本函数 - * @remark - * 如果url中已经包含key为“options['queryField']”的query项,将会被替换成callback中参数传递或自动生成的函数名。 - * @meta standard - * @see baidu.sio.callByBrowser - */ -baidu.sio.callByServer = /**@function*/function(url, callback, opt_options) { - var scr = document.createElement('SCRIPT'), - prefix = 'bd__cbs__', - callbackName, - callbackImpl, - options = opt_options || {}, - charset = options['charset'], - queryField = options['queryField'] || 'callback', - timeOut = options['timeOut'] || 0, - timer, - reg = new RegExp('(\\?|&)' + queryField + '=([^&]*)'), - matches; - - if (baidu.lang.isFunction(callback)) { - callbackName = prefix + Math.floor(Math.random() * 2147483648).toString(36); - window[callbackName] = getCallBack(0); - } else if(baidu.lang.isString(callback)){ - callbackName = callback; - } else { - if (matches = reg.exec(url)) { - callbackName = matches[2]; - } - } - - if( timeOut ){ - timer = setTimeout(getCallBack(1), timeOut); - } - url = url.replace(reg, '\x241' + queryField + '=' + callbackName); - - if (url.search(reg) < 0) { - url += (url.indexOf('?') < 0 ? '?' : '&') + queryField + '=' + callbackName; - } - baidu.sio._createScriptTag(scr, url, charset); - - /* - * 返回一个函数,用于立即(挂在window上)或者超时(挂在setTimeout中)时执行 - */ - function getCallBack(onTimeOut){ - /*global callbackName, callback, scr, options;*/ - return function(){ - try { - if( onTimeOut ){ - options.onfailure && options.onfailure(); - }else{ - callback.apply(window, arguments); - clearTimeout(timer); - } - window[callbackName] = null; - delete window[callbackName]; - } catch (exception) { - } finally { - baidu.sio._removeScriptTag(scr); - } - } - } -}; - -/** - * 通过请求一个图片的方式令服务器存储一条日志 - * @function - * @grammar baidu.sio.log(url) - * @param {string} url 要发送的地址. - * @author: int08h,leeight - */ -baidu.sio.log = function(url) { - var img = new Image(), - key = 'tangram_sio_log_' + Math.floor(Math.random() * - 2147483648).toString(36); - window[key] = img; - - img.onload = img.onerror = img.onabort = function() { - img.onload = img.onerror = img.onabort = null; - - window[key] = null; - img = null; - }; - img.src = url; -}; - - - -/* - * Tangram - * Copyright 2009 Baidu Inc. All rights reserved. - * - * path: baidu/json.js - * author: erik - * version: 1.1.0 - * date: 2009/12/02 - */ - - -/** - * 操作json对象的方法 - * @namespace baidu.json - */ -baidu.json = baidu.json || {}; -/* - * Tangram - * Copyright 2009 Baidu Inc. All rights reserved. - * - * path: baidu/json/parse.js - * author: erik, berg - * version: 1.2 - * date: 2009/11/23 - */ - - - -/** - * 将字符串解析成json对象。注:不会自动祛除空格 - * @name baidu.json.parse - * @function - * @grammar baidu.json.parse(data) - * @param {string} source 需要解析的字符串 - * @remark - * 该方法的实现与ecma-262第五版中规定的JSON.parse不同,暂时只支持传入一个参数。后续会进行功能丰富。 - * @meta standard - * @see baidu.json.stringify,baidu.json.decode - * - * @returns {JSON} 解析结果json对象 - */ -baidu.json.parse = function (data) { - //2010/12/09:更新至不使用原生parse,不检测用户输入是否正确 - return (new Function("return (" + data + ")"))(); -}; -/* - * Tangram - * Copyright 2009 Baidu Inc. All rights reserved. - * - * path: baidu/json/decode.js - * author: erik, cat - * version: 1.3.4 - * date: 2010/12/23 - */ - - - -/** - * 将字符串解析成json对象,为过时接口,今后会被baidu.json.parse代替 - * @name baidu.json.decode - * @function - * @grammar baidu.json.decode(source) - * @param {string} source 需要解析的字符串 - * @meta out - * @see baidu.json.encode,baidu.json.parse - * - * @returns {JSON} 解析结果json对象 - */ -baidu.json.decode = baidu.json.parse; -/* - * Tangram - * Copyright 2009 Baidu Inc. All rights reserved. - * - * path: baidu/json/stringify.js - * author: erik - * version: 1.1.0 - * date: 2010/01/11 - */ - - - -/** - * 将json对象序列化 - * @name baidu.json.stringify - * @function - * @grammar baidu.json.stringify(value) - * @param {JSON} value 需要序列化的json对象 - * @remark - * 该方法的实现与ecma-262第五版中规定的JSON.stringify不同,暂时只支持传入一个参数。后续会进行功能丰富。 - * @meta standard - * @see baidu.json.parse,baidu.json.encode - * - * @returns {string} 序列化后的字符串 - */ -baidu.json.stringify = (function () { - /** - * 字符串处理时需要转义的字符表 - * @private - */ - var escapeMap = { - "\b": '\\b', - "\t": '\\t', - "\n": '\\n', - "\f": '\\f', - "\r": '\\r', - '"' : '\\"', - "\\": '\\\\' - }; - - /** - * 字符串序列化 - * @private - */ - function encodeString(source) { - if (/["\\\x00-\x1f]/.test(source)) { - source = source.replace( - /["\\\x00-\x1f]/g, - function (match) { - var c = escapeMap[match]; - if (c) { - return c; - } - c = match.charCodeAt(); - return "\\u00" - + Math.floor(c / 16).toString(16) - + (c % 16).toString(16); - }); - } - return '"' + source + '"'; - } - - /** - * 数组序列化 - * @private - */ - function encodeArray(source) { - var result = ["["], - l = source.length, - preComma, i, item; - - for (i = 0; i < l; i++) { - item = source[i]; - - switch (typeof item) { - case "undefined": - case "function": - case "unknown": - break; - default: - if(preComma) { - result.push(','); - } - result.push(baidu.json.stringify(item)); - preComma = 1; - } - } - result.push("]"); - return result.join(""); - } - - /** - * 处理日期序列化时的补零 - * @private - */ - function pad(source) { - return source < 10 ? '0' + source : source; - } - - /** - * 日期序列化 - * @private - */ - function encodeDate(source){ - return '"' + source.getFullYear() + "-" - + pad(source.getMonth() + 1) + "-" - + pad(source.getDate()) + "T" - + pad(source.getHours()) + ":" - + pad(source.getMinutes()) + ":" - + pad(source.getSeconds()) + '"'; - } - - return function (value) { - switch (typeof value) { - case 'undefined': - return 'undefined'; - - case 'number': - return isFinite(value) ? String(value) : "null"; - - case 'string': - return encodeString(value); - - case 'boolean': - return String(value); - - default: - if (value === null) { - return 'null'; - } else if (value instanceof Array) { - return encodeArray(value); - } else if (value instanceof Date) { - return encodeDate(value); - } else { - var result = ['{'], - encode = baidu.json.stringify, - preComma, - item; - - for (var key in value) { - if (Object.prototype.hasOwnProperty.call(value, key)) { - item = value[key]; - switch (typeof item) { - case 'undefined': - case 'unknown': - case 'function': - break; - default: - if (preComma) { - result.push(','); - } - preComma = 1; - result.push(encode(key) + ':' + encode(item)); - } - } - } - result.push('}'); - return result.join(''); - } - } - }; -})(); -/* - * Tangram - * Copyright 2009 Baidu Inc. All rights reserved. - * - * path: baidu/json/encode.js - * author: erik, cat - * version: 1.3.4 - * date: 2010/12/23 - */ - - - -/** - * 将json对象序列化,为过时接口,今后会被baidu.json.stringify代替 - * @name baidu.json.encode - * @function - * @grammar baidu.json.encode(value) - * @param {JSON} value 需要序列化的json对象 - * @meta out - * @see baidu.json.decode,baidu.json.stringify - * - * @returns {string} 序列化后的字符串 - */ -baidu.json.encode = baidu.json.stringify; diff --git a/static/plugs/ueditor/dialogs/wordimage/wordimage.html b/static/plugs/ueditor/dialogs/wordimage/wordimage.html deleted file mode 100644 index f8c09052a..000000000 --- a/static/plugs/ueditor/dialogs/wordimage/wordimage.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - -
                  -
                  - -
                  -
                  -
                  -
                  -
                  - -
                  - : -
                  -
                  -
                  - - - - - - \ No newline at end of file diff --git a/static/plugs/ueditor/dialogs/wordimage/wordimage.js b/static/plugs/ueditor/dialogs/wordimage/wordimage.js deleted file mode 100644 index e3602639e..000000000 --- a/static/plugs/ueditor/dialogs/wordimage/wordimage.js +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Created by JetBrains PhpStorm. - * User: taoqili - * Date: 12-1-30 - * Time: 下午12:50 - * To change this template use File | Settings | File Templates. - */ - - - -var wordImage = {}; -//(function(){ -var g = baidu.g, - flashObj,flashContainer; - -wordImage.init = function(opt, callbacks) { - showLocalPath("localPath"); - //createCopyButton("clipboard","localPath"); - createFlashUploader(opt, callbacks); - addUploadListener(); - addOkListener(); -}; - -function hideFlash(){ - flashObj = null; - flashContainer.innerHTML = ""; -} -function addOkListener() { - dialog.onok = function() { - if (!imageUrls.length) return; - var urlPrefix = editor.getOpt('imageUrlPrefix'), - images = domUtils.getElementsByTagName(editor.document,"img"); - editor.fireEvent('saveScene'); - for (var i = 0,img; img = images[i++];) { - var src = img.getAttribute("word_img"); - if (!src) continue; - for (var j = 0,url; url = imageUrls[j++];) { - if (src.indexOf(url.original.replace(" ","")) != -1) { - img.src = urlPrefix + url.url; - img.setAttribute("_src", urlPrefix + url.url); //同时修改"_src"属性 - img.setAttribute("title",url.title); - domUtils.removeAttributes(img, ["word_img","style","width","height"]); - editor.fireEvent("selectionchange"); - break; - } - } - } - editor.fireEvent('saveScene'); - hideFlash(); - }; - dialog.oncancel = function(){ - hideFlash(); - } -} - -/** - * 绑定开始上传事件 - */ -function addUploadListener() { - g("upload").onclick = function () { - flashObj.upload(); - this.style.display = "none"; - }; -} - -function showLocalPath(id) { - //单张编辑 - var img = editor.selection.getRange().getClosedNode(); - var images = editor.execCommand('wordimage'); - if(images.length==1 || img && img.tagName == 'IMG'){ - g(id).value = images[0]; - return; - } - var path = images[0]; - var leftSlashIndex = path.lastIndexOf("/")||0, //不同版本的doc和浏览器都可能影响到这个符号,故直接判断两种 - rightSlashIndex = path.lastIndexOf("\\")||0, - separater = leftSlashIndex > rightSlashIndex ? "/":"\\" ; - - path = path.substring(0, path.lastIndexOf(separater)+1); - g(id).value = path; -} - -function createFlashUploader(opt, callbacks) { - //由于lang.flashI18n是静态属性,不可以直接进行修改,否则会影响到后续内容 - var i18n = utils.extend({},lang.flashI18n); - //处理图片资源地址的编码,补全等问题 - for(var i in i18n){ - if(!(i in {"lang":1,"uploadingTF":1,"imageTF":1,"textEncoding":1}) && i18n[i]){ - i18n[i] = encodeURIComponent(editor.options.langPath + editor.options.lang + "/images/" + i18n[i]); - } - } - opt = utils.extend(opt,i18n,false); - var option = { - createOptions:{ - id:'flash', - url:opt.flashUrl, - width:opt.width, - height:opt.height, - errorMessage:lang.flashError, - wmode:browser.safari ? 'transparent' : 'window', - ver:'10.0.0', - vars:opt, - container:opt.container - } - }; - - option = extendProperty(callbacks, option); - flashObj = new baidu.flash.imageUploader(option); - flashContainer = $G(opt.container); -} - -function extendProperty(fromObj, toObj) { - for (var i in fromObj) { - if (!toObj[i]) { - toObj[i] = fromObj[i]; - } - } - return toObj; -} - -//})(); - -function getPasteData(id) { - baidu.g("msg").innerHTML = lang.copySuccess + "
                  "; - setTimeout(function() { - baidu.g("msg").innerHTML = ""; - }, 5000); - return baidu.g(id).value; -} - -function createCopyButton(id, dataFrom) { - baidu.swf.create({ - id:"copyFlash", - url:"fClipboard_ueditor.swf", - width:"58", - height:"25", - errorMessage:"", - bgColor:"#CBCBCB", - wmode:"transparent", - ver:"10.0.0", - vars:{ - tid:dataFrom - } - }, id - ); - - var clipboard = baidu.swf.getMovie("copyFlash"); - var clipinterval = setInterval(function() { - if (clipboard && clipboard.flashInit) { - clearInterval(clipinterval); - clipboard.setHandCursor(true); - clipboard.setContentFuncName("getPasteData"); - //clipboard.setMEFuncName("mouseEventHandler"); - } - }, 500); -} -createCopyButton("clipboard", "localPath"); \ No newline at end of file diff --git a/static/plugs/ueditor/index.html b/static/plugs/ueditor/index.html deleted file mode 100644 index f02b43c91..000000000 --- a/static/plugs/ueditor/index.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - 完整demo - - - - - - - - - - -
                  -

                  完整demo

                  - -
                  -
                  -
                  - - - - - - - - - - - -
                  -
                  - - - - - - - -
                  - -
                  - - -
                  - -
                  -
                  - - -
                  - - - - \ No newline at end of file diff --git a/static/plugs/ueditor/lang/en/en.js b/static/plugs/ueditor/lang/en/en.js deleted file mode 100644 index c2bdbbbc9..000000000 --- a/static/plugs/ueditor/lang/en/en.js +++ /dev/null @@ -1,684 +0,0 @@ -/** - * Created with JetBrains PhpStorm. - * User: taoqili - * Date: 12-6-12 - * Time: 下午6:57 - * To change this template use File | Settings | File Templates. - */ -UE.I18N['en'] = { - 'labelMap':{ - 'anchor':'Anchor', 'undo':'Undo', 'redo':'Redo', 'bold':'Bold', 'indent':'Indent', 'snapscreen':'SnapScreen', - 'italic':'Italic', 'underline':'Underline', 'strikethrough':'Strikethrough', 'subscript':'SubScript','fontborder':'text border', - 'superscript':'SuperScript', 'formatmatch':'Format Match', 'source':'Source', 'blockquote':'BlockQuote', - 'pasteplain':'PastePlain', 'selectall':'SelectAll', 'print':'Print', 'preview':'Preview', - 'horizontal':'Horizontal', 'removeformat':'RemoveFormat', 'time':'Time', 'date':'Date', - 'unlink':'Unlink', 'insertrow':'InsertRow', 'insertcol':'InsertCol', 'mergeright':'MergeRight', 'mergedown':'MergeDown', - 'deleterow':'DeleteRow', 'deletecol':'DeleteCol', 'splittorows':'SplitToRows','insertcode':'insert code', - 'splittocols':'SplitToCols', 'splittocells':'SplitToCells','deletecaption':'DeleteCaption','inserttitle':'InsertTitle', - 'mergecells':'MergeCells', 'deletetable':'DeleteTable', 'cleardoc':'Clear', 'insertparagraphbeforetable':"InsertParagraphBeforeTable", - 'fontfamily':'FontFamily', 'fontsize':'FontSize', 'paragraph':'Paragraph','simpleupload':'Single Image','insertimage':'Multi Image','edittable':'Edit Table', 'edittd':'Edit Td','link':'Link', - 'emotion':'Emotion', 'spechars':'Spechars', 'searchreplace':'SearchReplace', 'map':'BaiduMap', 'gmap':'GoogleMap', - 'insertvideo':'Video', 'help':'Help', 'justifyleft':'JustifyLeft', 'justifyright':'JustifyRight', 'justifycenter':'JustifyCenter', - 'justifyjustify':'Justify', 'forecolor':'FontColor', 'backcolor':'BackColor', 'insertorderedlist':'OL', - 'insertunorderedlist':'UL', 'fullscreen':'FullScreen', 'directionalityltr':'EnterFromLeft', 'directionalityrtl':'EnterFromRight', - 'rowspacingtop':'RowSpacingTop', 'rowspacingbottom':'RowSpacingBottom', 'pagebreak':'PageBreak', 'insertframe':'Iframe', 'imagenone':'Default', - 'imageleft':'ImageLeft', 'imageright':'ImageRight', 'attachment':'Attachment', 'imagecenter':'ImageCenter', 'wordimage':'WordImage', - 'lineheight':'LineHeight','edittip':'EditTip','customstyle':'CustomStyle', 'scrawl':'Scrawl', 'autotypeset':'AutoTypeset', - 'webapp':'WebAPP', 'touppercase':'UpperCase', 'tolowercase':'LowerCase','template':'Template','background':'Background','inserttable':'InsertTable', - 'music':'Music', 'charts': 'charts','drafts': 'Load from Drafts' - }, - 'insertorderedlist':{ - 'num':'1,2,3...', - 'num1':'1),2),3)...', - 'num2':'(1),(2),(3)...', - 'cn':'一,二,三....', - 'cn1':'一),二),三)....', - 'cn2':'(一),(二),(三)....', - 'decimal':'1,2,3...', - 'lower-alpha':'a,b,c...', - 'lower-roman':'i,ii,iii...', - 'upper-alpha':'A,B,C...', - 'upper-roman':'I,II,III...' - }, - 'insertunorderedlist':{ - 'circle':'○ Circle', - 'disc':'● Circle dot', - 'square':'■ Rectangle ', - 'dash' :'- Dash', - 'dot' : '。dot' - }, - 'paragraph':{'p':'Paragraph', 'h1':'Title 1', 'h2':'Title 2', 'h3':'Title 3', 'h4':'Title 4', 'h5':'Title 5', 'h6':'Title 6'}, - 'fontfamily':{ - 'songti':'Sim Sun', - 'kaiti':'Sim Kai', - 'heiti':'Sim Hei', - 'lishu':'Sim Li', - 'yahei': 'Microsoft YaHei', - 'andaleMono':'Andale Mono', - 'arial': 'Arial', - 'arialBlack':'Arial Black', - 'comicSansMs':'Comic Sans MS', - 'impact':'Impact', - 'timesNewRoman':'Times New Roman' - }, - 'customstyle':{ - 'tc':'Title center', - 'tl':'Title left', - 'im':'Important', - 'hi':'Highlight' - }, - 'autoupload': { - 'exceedSizeError': 'File Size Exceed', - 'exceedTypeError': 'File Type Not Allow', - 'jsonEncodeError': 'Server Return Format Error', - 'loading':"loading...", - 'loadError':"load error", - 'errorLoadConfig': 'Server config not loaded, upload can not work.', - }, - 'simpleupload':{ - 'exceedSizeError': 'File Size Exceed', - 'exceedTypeError': 'File Type Not Allow', - 'jsonEncodeError': 'Server Return Format Error', - 'loading':"loading...", - 'loadError':"load error", - 'errorLoadConfig': 'Server config not loaded, upload can not work.', - }, - 'elementPathTip':"Path", - 'wordCountTip':"Word Count", - 'wordCountMsg':'{#count} characters entered,{#leave} left. ', - 'wordOverFlowMsg':'The number of characters has exceeded allowable maximum values, the server may refuse to save!', - 'ok':"OK", - 'cancel':"Cancel", - 'closeDialog':"closeDialog", - 'tableDrag':"You must import the file uiUtils.js before drag! ", - 'autofloatMsg':"The plugin AutoFloat depends on EditorUI!", - 'loadconfigError': 'Get server config error.', - 'loadconfigFormatError': 'Server config format error.', - 'loadconfigHttpError': 'Get server config http error.', - 'snapScreen_plugin':{ - 'browserMsg':"Only IE supported!", - 'callBackErrorMsg':"The callback data is wrong,please check the config!", - 'uploadErrorMsg':"Upload error,please check your server environment! " - }, - 'insertcode':{ - 'as3':'ActionScript 3', - 'bash':'Bash/Shell', - 'cpp':'C/C++', - 'css':'CSS', - 'cf':'ColdFusion', - 'c#':'C#', - 'delphi':'Delphi', - 'diff':'Diff', - 'erlang':'Erlang', - 'groovy':'Groovy', - 'html':'HTML', - 'java':'Java', - 'jfx':'JavaFX', - 'js':'JavaScript', - 'pl':'Perl', - 'php':'PHP', - 'plain':'Plain Text', - 'ps':'PowerShell', - 'python':'Python', - 'ruby':'Ruby', - 'scala':'Scala', - 'sql':'SQL', - 'vb':'Visual Basic', - 'xml':'XML' - }, - 'confirmClear':"Do you confirm to clear the Document?", - 'contextMenu':{ - 'delete':"Delete", - 'selectall':"Select all", - 'deletecode':"Delete Code", - 'cleardoc':"Clear Document", - 'confirmclear':"Do you confirm to clear the Document?", - 'unlink':"Unlink", - 'paragraph':"Paragraph", - 'edittable':"Table property", - 'aligncell':'Align cell', - 'aligntable':'Table alignment', - 'tableleft':'Left float', - 'tablecenter':'Center', - 'tableright':'Right float', - 'aligntd':'Cell alignment', - 'edittd':"Cell property", - 'setbordervisible':'set table edge visible', - 'table':"Table", - 'justifyleft':'Justify Left', - 'justifyright':'Justify Right', - 'justifycenter':'Justify Center', - 'justifyjustify':'Default', - 'deletetable':"Delete table", - 'insertparagraphbefore':"InsertedBeforeLine", - 'insertparagraphafter':'InsertedAfterLine', - 'inserttable':'Insert table', - 'insertcaption':'Insert caption', - 'deletecaption':'Delete Caption', - 'inserttitle':'Insert Title', - 'deletetitle':'Delete Title', - 'inserttitlecol':'Insert Title Col', - 'deletetitlecol':'Delete Title Col', - 'averageDiseRow':'AverageDise Row', - 'averageDisCol':'AverageDis Col', - 'deleterow':"Delete row", - 'deletecol':"Delete col", - 'insertrow':"Insert row", - 'insertcol':"Insert col", - 'insertrownext':'Insert Row Next', - 'insertcolnext':'Insert Col Next', - 'mergeright':"Merge right", - 'mergeleft':"Merge left", - 'mergedown':"Merge down", - 'mergecells':"Merge cells", - 'splittocells':"Split to cells", - 'splittocols':"Split to Cols", - 'splittorows':"Split to Rows", - 'tablesort':'Table sorting', - 'enablesort':'Sorting Enable', - 'disablesort':'Sorting Disable', - 'reversecurrent':'Reverse current', - 'orderbyasc':'Order By ASCII', - 'reversebyasc':'Reverse By ASCII', - 'orderbynum':'Order By Num', - 'reversebynum':'Reverse By Num', - 'borderbk':'Border shading', - 'setcolor':'interlaced color', - 'unsetcolor':'Cancel interlacedcolor', - 'setbackground':'Background interlaced', - 'unsetbackground':'Cancel Bk interlaced', - 'redandblue':'Blue and red', - 'threecolorgradient':'Three-color gradient', - 'copy':"Copy(Ctrl + c)", - 'copymsg':"Browser does not support. Please use 'Ctrl + c' instead!", - 'paste':"Paste(Ctrl + v)", - 'pastemsg':"Browser does not support. Please use 'Ctrl + v' instead!" - }, - 'copymsg': "Browser does not support. Please use 'Ctrl + c' instead!", - 'pastemsg': "Browser does not support. Please use 'Ctrl + v' instead!", - 'anthorMsg':"Link", - 'clearColor':'Clear', - 'standardColor':'Standard color', - 'themeColor':'Theme color', - 'property':'Property', - 'default':'Default', - 'modify':'Modify', - 'justifyleft':'Justify Left', - 'justifyright':'Justify Right', - 'justifycenter':'Justify Center', - 'justify':'Default', - 'clear':'Clear', - 'anchorMsg':'Anchor', - 'delete':'Delete', - 'clickToUpload':"Click to upload", - 'unset':'Language hasn\'t been set!', - 't_row':'row', - 't_col':'col', - 'pasteOpt':'Paste Option', - 'pasteSourceFormat':"Keep Source Formatting", - 'tagFormat':'Keep tag', - 'pasteTextFormat':'Keep Text only', - 'more':'More', - 'autoTypeSet':{ - 'mergeLine':"Merge empty line", - 'delLine':"Del empty line", - 'removeFormat':"Remove format", - 'indent':"Indent", - 'alignment':"Alignment", - 'imageFloat':"Image float", - 'removeFontsize':"Remove font size", - 'removeFontFamily':"Remove fontFamily", - 'removeHtml':"Remove redundant HTML code", - 'pasteFilter':"Paste filter", - 'run':"Done", - 'symbol':'Symbol Conversion', - 'bdc2sb':'Full-width to Half-width', - 'tobdc':'Half-width to Full-width' - }, - - 'background':{ - 'static':{ - 'lang_background_normal':'Normal', - 'lang_background_local':'Online', - 'lang_background_set':'Background Set', - 'lang_background_none':'No Background', - 'lang_background_colored':'Colored Background', - 'lang_background_color':'Color Set', - 'lang_background_netimg':'Net-Image', - 'lang_background_align':'Align Type', - 'lang_background_position':'Position', - 'repeatType':{'options':["Center", "Repeat-x", "Repeat-y", "Tile","Custom"]} - }, - 'noUploadImage':"No pictures has been uploaded!", - 'toggleSelect':'Change the active state by click!\n Image Size: ' - }, - //===============dialog i18N======================= - 'insertimage':{ - 'static':{ - 'lang_tab_remote':"Insert", - 'lang_tab_upload':"Local", - 'lang_tab_online':"Manager", - 'lang_tab_search':"Search", - 'lang_input_url':"Address:", - 'lang_input_size':"Size:", - 'lang_input_width':"Width", - 'lang_input_height':"Height", - 'lang_input_border':"Border:", - 'lang_input_vhspace':"Margins:", - 'lang_input_title':"Title:", - 'lang_input_align':'Image Float Style:', - 'lang_imgLoading':"Loading...", - 'lang_start_upload':"Start Upload", - 'lock':{'title':"Lock rate"}, - 'searchType':{'title':"ImageType", 'options':["News", "Wallpaper", "emotions", "photo"]}, - 'searchTxt':{'value':"Enter the search keyword!"}, - 'searchBtn':{'value':"Search"}, - 'searchReset':{'value':"Clear"}, - 'noneAlign':{'title':'None Float'}, - 'leftAlign':{'title':'Left Float'}, - 'rightAlign':{'title':'Right Float'}, - 'centerAlign':{'title':'Center In A Line'} - }, - 'uploadSelectFile':'Select File', - 'uploadAddFile':'Add File', - 'uploadStart':'Start Upload', - 'uploadPause':'Pause Upload', - 'uploadContinue':'Continue Upload', - 'uploadRetry':'Retry Upload', - 'uploadDelete':'Delete', - 'uploadTurnLeft':'Turn Left', - 'uploadTurnRight':'Turn Right', - 'uploadPreview':'Doing Preview', - 'uploadNoPreview':'Can Not Preview', - 'updateStatusReady': 'Selected _ pictures, total _KB.', - 'updateStatusConfirm': '_ uploaded successfully and _ upload failed', - 'updateStatusFinish': 'Total _ pictures (_KB), _ uploaded successfully', - 'updateStatusError': ' and _ upload failed', - 'errorNotSupport': 'WebUploader does not support the browser you are using. Please upgrade your browser or flash player', - 'errorLoadConfig': 'Server config not loaded, upload can not work.', - 'errorExceedSize':'File Size Exceed', - 'errorFileType':'File Type Not Allow', - 'errorInterrupt':'File Upload Interrupted', - 'errorUploadRetry':'Upload Error, Please Retry.', - 'errorHttp':'Http Error', - 'errorServerUpload':'Server Result Error.', - 'remoteLockError':"Cannot Lock the Proportion between width and height", - 'numError':"Please enter the correct Num. e.g 123,400", - 'imageUrlError':"The image format may be wrong!", - 'imageLoadError':"Error,please check the network or URL!", - 'searchRemind':"Enter the search keyword!", - 'searchLoading':"Image is loading,please wait...", - 'searchRetry':" Sorry,can't find the image,please try again!" - }, - 'attachment':{ - 'static':{ - 'lang_tab_upload': 'Upload', - 'lang_tab_online': 'Online', - 'lang_start_upload':"Start upload", - 'lang_drop_remind':"You can drop files here, a single maximum of 300 files" - }, - 'uploadSelectFile':'Select File', - 'uploadAddFile':'Add File', - 'uploadStart':'Start Upload', - 'uploadPause':'Pause Upload', - 'uploadContinue':'Continue Upload', - 'uploadRetry':'Retry Upload', - 'uploadDelete':'Delete', - 'uploadTurnLeft':'Turn Left', - 'uploadTurnRight':'Turn Right', - 'uploadPreview':'Doing Preview', - 'updateStatusReady': 'Selected _ files, total _KB.', - 'updateStatusConfirm': '_ uploaded successfully and _ upload failed', - 'updateStatusFinish': 'Total _ files (_KB), _ uploaded successfully', - 'updateStatusError': ' and _ upload failed', - 'errorNotSupport': 'WebUploader does not support the browser you are using. Please upgrade your browser or flash player', - 'errorLoadConfig': 'Server config not loaded, upload can not work.', - 'errorExceedSize':'File Size Exceed', - 'errorFileType':'File Type Not Allow', - 'errorInterrupt':'File Upload Interrupted', - 'errorUploadRetry':'Upload Error, Please Retry.', - 'errorHttp':'Http Error', - 'errorServerUpload':'Server Result Error.' - }, - - 'insertvideo':{ - 'static':{ - 'lang_tab_insertV':"Video", - 'lang_tab_searchV':"Search", - 'lang_tab_uploadV':"Upload", - 'lang_video_url':" URL ", - 'lang_video_size':"Video Size", - 'lang_videoW':"Width", - 'lang_videoH':"Height", - 'lang_alignment':"Alignment", - 'videoSearchTxt':{'value':"Enter the search keyword!"}, - 'videoType':{'options':["All", "Hot", "Entertainment", "Funny", "Sports", "Science", "variety"]}, - 'videoSearchBtn':{'value':"Search in Baidu"}, - 'videoSearchReset':{'value':"Clear result"}, - - 'lang_input_fileStatus':' No file uploaded!', - 'startUpload':{'style':"background:url(upload.png) no-repeat;"}, - - 'lang_upload_size':"Video Size", - 'lang_upload_width':"Width", - 'lang_upload_height':"Height", - 'lang_upload_alignment':"Alignment", - 'lang_format_advice':"Recommends mp4 format." - }, - 'numError':"Please enter the correct Num. e.g 123,400", - 'floatLeft':"Float left", - 'floatRight':"Float right", - 'default':"Default", - 'block':"Display in block", - 'urlError':"The video url format may be wrong!", - 'loading':"  The video is loading, please wait…", - 'clickToSelect':"Click to select", - 'goToSource':'Visit source video ', - 'noVideo':"    Sorry,can't find the video,please try again!", - - 'browseFiles':'Open files', - 'uploadSuccess':'Upload Successful!', - 'delSuccessFile':'Remove from the success of the queue', - 'delFailSaveFile':'Remove the save failed file', - 'statusPrompt':' file(s) uploaded! ', - 'flashVersionError':'The current Flash version is too low, please update FlashPlayer,then try again!', - 'flashLoadingError':'The Flash failed loading! Please check the path or network state', - 'fileUploadReady':'Wait for uploading...', - 'delUploadQueue':'Remove from the uploading queue ', - 'limitPrompt1':'Can not choose more than single', - 'limitPrompt2':'file(s)!Please choose again!', - 'delFailFile':'Remove failure file', - 'fileSizeLimit':'File size exceeds the limit!', - 'emptyFile':'Can not upload an empty file!', - 'fileTypeError':'File type error!', - 'unknownError':'Unknown error!', - 'fileUploading':'Uploading,please wait...', - 'cancelUpload':'Cancel upload', - 'netError':'Network error', - 'failUpload':'Upload failed', - 'serverIOError':'Server IO error!', - 'noAuthority':'No Permission!', - 'fileNumLimit':'Upload limit to the number', - 'failCheck':'Authentication fails, the upload is skipped!', - 'fileCanceling':'Cancel, please wait...', - 'stopUploading':'Upload has stopped...', - - 'uploadSelectFile':'Select File', - 'uploadAddFile':'Add File', - 'uploadStart':'Start Upload', - 'uploadPause':'Pause Upload', - 'uploadContinue':'Continue Upload', - 'uploadRetry':'Retry Upload', - 'uploadDelete':'Delete', - 'uploadTurnLeft':'Turn Left', - 'uploadTurnRight':'Turn Right', - 'uploadPreview':'Doing Preview', - 'updateStatusReady': 'Selected _ files, total _KB.', - 'updateStatusConfirm': '_ uploaded successfully and _ upload failed', - 'updateStatusFinish': 'Total _ files (_KB), _ uploaded successfully', - 'updateStatusError': ' and _ upload failed', - 'errorNotSupport': 'WebUploader does not support the browser you are using. Please upgrade your browser or flash player', - 'errorLoadConfig': 'Server config not loaded, upload can not work.', - 'errorExceedSize':'File Size Exceed', - 'errorFileType':'File Type Not Allow', - 'errorInterrupt':'File Upload Interrupted', - 'errorUploadRetry':'Upload Error, Please Retry.', - 'errorHttp':'Http Error', - 'errorServerUpload':'Server Result Error.' - }, - 'webapp':{ - 'tip1':"This function provided by Baidu APP,please apply for baidu APPKey webmaster first!", - 'tip2':"And then open the file ueditor.config.js to set it! ", - 'applyFor':"APPLY FOR", - 'anthorApi':"Baidu API" - }, - 'template':{ - 'static':{ - 'lang_template_bkcolor':'Background Color', - 'lang_template_clear' : 'Keep Content', - 'lang_template_select':'Select Template' - }, - 'blank':"Blank", - 'blog':"Blog", - 'resume':"Resume", - 'richText':"Rich Text", - 'scrPapers':"Scientific Papers" - }, - scrawl:{ - 'static':{ - 'lang_input_previousStep':"Previous", - 'lang_input_nextsStep':"Next", - 'lang_input_clear':'Clear', - 'lang_input_addPic':'AddImage', - 'lang_input_ScalePic':'ScaleImage', - 'lang_input_removePic':'RemoveImage', - 'J_imgTxt':{title:'Add background image'} - }, - 'noScarwl':"No paint, a white paper...", - 'scrawlUpLoading':"Image is uploading, please wait...", - 'continueBtn':"Try again", - 'imageError':"Image failed to load!", - 'backgroundUploading':'Image is uploading,please wait...' - }, - 'music':{ - 'static':{ - 'lang_input_tips':"Input singer/song/album, search you interested in music!", - 'J_searchBtn':{value:'Search songs'} - }, - 'emptyTxt':'Not search to the relevant music results, please change a keyword try.', - 'chapter':'Songs', - 'singer':'Singer', - 'special':'Album', - 'listenTest':'Audition' - }, - anchor:{ - 'static':{ - 'lang_input_anchorName':'Anchor Name:' - } - }, - 'charts':{ - 'static':{ - 'lang_data_source':'Data source:', - 'lang_chart_format': 'Chart format:', - 'lang_data_align': 'Align', - 'lang_chart_align_same': 'Consistent with the X-axis Y-axis', - 'lang_chart_align_reverse': 'X-axis Y-axis opposite', - 'lang_chart_title': 'Title', - 'lang_chart_main_title': 'main title:', - 'lang_chart_sub_title': 'sub title:', - 'lang_chart_x_title': 'X-axis title:', - 'lang_chart_y_title': 'Y-axis title:', - 'lang_chart_tip': 'Prompt', - 'lang_cahrt_tip_prefix': 'prefix:', - 'lang_cahrt_tip_description': '仅饼图有效, 当鼠标移动到饼图中相应的块上时,提示框内的文字的前缀', - 'lang_chart_data_unit': 'Unit', - 'lang_chart_data_unit_title': 'unit:', - 'lang_chart_data_unit_description': '显示在每个数据点上的数据的单位, 比如: 温度的单位 ℃', - 'lang_chart_type': 'Chart type:', - 'lang_prev_btn': 'Previous', - 'lang_next_btn': 'Next' - } - }, - emotion:{ - 'static':{ - 'lang_input_choice':'Choice', - 'lang_input_Tuzki':'Tuzki', - 'lang_input_lvdouwa':'LvDouWa', - 'lang_input_BOBO':'BOBO', - 'lang_input_babyCat':'BabyCat', - 'lang_input_bubble':'Bubble', - 'lang_input_youa':'YouA' - } - }, - gmap:{ - 'static':{ - 'lang_input_address':'Address:', - 'lang_input_search':'Search', - 'address':{value:"Beijing"} - }, - searchError:'Unable to locate the address!' - }, - help:{ - 'static':{ - 'lang_input_about':'About', - 'lang_input_shortcuts':'Shortcuts', - 'lang_input_introduction':"UEditor is developed by Baidu Co.ltd. It is lightweight, customizable , focusing on user experience and etc. , UEditor is based on open source BSD license , allowing free use and redistribution.", - 'lang_Txt_shortcuts':'Shortcuts', - 'lang_Txt_func':'Function', - 'lang_Txt_bold':'Bold', - 'lang_Txt_copy':'Copy', - 'lang_Txt_cut':'Cut', - 'lang_Txt_Paste':'Paste', - 'lang_Txt_undo':'Undo', - 'lang_Txt_redo':'Redo', - 'lang_Txt_italic':'Italic', - 'lang_Txt_underline':'Underline', - 'lang_Txt_selectAll':'Select All', - 'lang_Txt_visualEnter':'Submit', - 'lang_Txt_fullscreen':'Fullscreen' - } - }, - insertframe:{ - 'static':{ - 'lang_input_address':'Address:', - 'lang_input_width':'Width:', - 'lang_input_height':'height:', - 'lang_input_isScroll':'Enable scrollbars:', - 'lang_input_frameborder':'Show frame border:', - 'lang_input_alignMode':'Alignment:', - 'align':{title:"Alignment", options:["Default", "Left", "Right", "Center"]} - }, - 'enterAddress':'Please enter an address!' - }, - link:{ - 'static':{ - 'lang_input_text':'Text:', - 'lang_input_url':'URL:', - 'lang_input_title':'Title:', - 'lang_input_target':'open in new window:' - }, - 'validLink':'Supports only effective when a link is selected', - 'httpPrompt':'The hyperlink you enter should start with "http|https|ftp://"!' - }, - map:{ - 'static':{ - lang_city:"City", - lang_address:"Address", - city:{value:"Beijing"}, - lang_search:"Search", - lang_dynamicmap:"Dynamic map" - }, - cityMsg:"Please enter the city name!", - errorMsg:"Can't find the place!" - }, - searchreplace:{ - 'static':{ - lang_tab_search:"Search", - lang_tab_replace:"Replace", - lang_search1:"Search", - lang_search2:"Search", - lang_replace:"Replace", - lang_searchReg:'Support regular expression ,which starts and ends with a slash ,for example "/expression/"', - lang_searchReg1:'Support regular expression ,which starts and ends with a slash ,for example "/expression/"', - lang_case_sensitive1:"Case sense", - lang_case_sensitive2:"Case sense", - nextFindBtn:{value:"Next"}, - preFindBtn:{value:"Preview"}, - nextReplaceBtn:{value:"Next"}, - preReplaceBtn:{value:"Preview"}, - repalceBtn:{value:"Replace"}, - repalceAllBtn:{value:"Replace all"} - }, - getEnd:"Has the search to the bottom!", - getStart:"Has the search to the top!", - countMsg:"Altogether replaced {#count} character(s)!" - }, - snapscreen:{ - 'static':{ - lang_showMsg:"You should install the UEditor screenshots program first!", - lang_download:"Download!", - lang_step1:"Step1:Download the program and then run it", - lang_step2:"Step2:After complete install,try to click the button again" - } - }, - spechars:{ - 'static':{}, - tsfh:"Special", - lmsz:"Roman", - szfh:"Numeral", - rwfh:"Japanese", - xlzm:"The Greek", - ewzm:"Russian", - pyzm:"Phonetic", - yyyb:"English", - zyzf:"Others" - }, - 'edittable':{ - 'static':{ - 'lang_tableStyle':'Table style', - 'lang_insertCaption':'Add table header row', - 'lang_insertTitle':'Add table title row', - 'lang_insertTitleCol':'Add table title col', - 'lang_tableSize':'Automatically adjust table size', - 'lang_autoSizeContent':'Adaptive by form text', - 'lang_orderbycontent':"Table of contents sortable", - 'lang_autoSizePage':'Page width adaptive', - 'lang_example':'Example', - 'lang_borderStyle':'Table Border', - 'lang_color':'Color:' - }, - captionName:'Caption', - titleName:'Title', - cellsName:'text', - errorMsg:'There are merged cells, can not sort.' - }, - 'edittip':{ - 'static':{ - lang_delRow:'Delete entire row', - lang_delCol:'Delete entire col' - } - }, - 'edittd':{ - 'static':{ - lang_tdBkColor:'Background Color:' - } - }, - 'formula':{ - 'static':{ - } - }, - wordimage:{ - 'static':{ - lang_resave:"The re-save step", - uploadBtn:{src:"upload.png", alt:"Upload"}, - clipboard:{style:"background: url(copy.png) -153px -1px no-repeat;"}, - lang_step:" 1. Click top button to copy the url and then open the dialog to paste it. 2. Open after choose photos uploaded process." - }, - fileType:"Image", - flashError:"Flash initialization failed!", - netError:"Network error! Please try again!", - copySuccess:"URL has been copied!", - - 'flashI18n':{ - lang:encodeURI( '{"UploadingState":"totalNum: ${a},uploadComplete: ${b}", "BeforeUpload":"waitingNum: ${a}", "ExceedSize":"Size exceed${a}", "ErrorInPreview":"Preview failed", "DefaultDescription":"Description", "LoadingImage":"Loading..."}' ), - uploadingTF:encodeURI( '{"font":"Arial", "size":12, "color":"0x000", "bold":"true", "italic":"false", "underline":"false"}' ), - imageTF:encodeURI( '{"font":"Arial", "size":11, "color":"red", "bold":"false", "italic":"false", "underline":"false"}' ), - textEncoding:"utf-8", - addImageSkinURL:"addImage.png", - allDeleteBtnUpSkinURL:"allDeleteBtnUpSkin.png", - allDeleteBtnHoverSkinURL:"allDeleteBtnHoverSkin.png", - rotateLeftBtnEnableSkinURL:"rotateLeftEnable.png", - rotateLeftBtnDisableSkinURL:"rotateLeftDisable.png", - rotateRightBtnEnableSkinURL:"rotateRightEnable.png", - rotateRightBtnDisableSkinURL:"rotateRightDisable.png", - deleteBtnEnableSkinURL:"deleteEnable.png", - deleteBtnDisableSkinURL:"deleteDisable.png", - backgroundURL:'', - listBackgroundURL:'', - buttonURL:'button.png' - } - }, - 'autosave': { - 'success':'Local conservation success' - } -}; diff --git a/static/plugs/ueditor/lang/en/images/addimage.png b/static/plugs/ueditor/lang/en/images/addimage.png deleted file mode 100644 index 3a2fd1712..000000000 Binary files a/static/plugs/ueditor/lang/en/images/addimage.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/alldeletebtnhoverskin.png b/static/plugs/ueditor/lang/en/images/alldeletebtnhoverskin.png deleted file mode 100644 index 355eeabbd..000000000 Binary files a/static/plugs/ueditor/lang/en/images/alldeletebtnhoverskin.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/alldeletebtnupskin.png b/static/plugs/ueditor/lang/en/images/alldeletebtnupskin.png deleted file mode 100644 index 61658ce6f..000000000 Binary files a/static/plugs/ueditor/lang/en/images/alldeletebtnupskin.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/background.png b/static/plugs/ueditor/lang/en/images/background.png deleted file mode 100644 index d5bf5fdd8..000000000 Binary files a/static/plugs/ueditor/lang/en/images/background.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/button.png b/static/plugs/ueditor/lang/en/images/button.png deleted file mode 100644 index 098874cb1..000000000 Binary files a/static/plugs/ueditor/lang/en/images/button.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/copy.png b/static/plugs/ueditor/lang/en/images/copy.png deleted file mode 100644 index f982e8bcb..000000000 Binary files a/static/plugs/ueditor/lang/en/images/copy.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/deletedisable.png b/static/plugs/ueditor/lang/en/images/deletedisable.png deleted file mode 100644 index c8ee75094..000000000 Binary files a/static/plugs/ueditor/lang/en/images/deletedisable.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/deleteenable.png b/static/plugs/ueditor/lang/en/images/deleteenable.png deleted file mode 100644 index 26acc8835..000000000 Binary files a/static/plugs/ueditor/lang/en/images/deleteenable.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/listbackground.png b/static/plugs/ueditor/lang/en/images/listbackground.png deleted file mode 100644 index 4f82ccd88..000000000 Binary files a/static/plugs/ueditor/lang/en/images/listbackground.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/localimage.png b/static/plugs/ueditor/lang/en/images/localimage.png deleted file mode 100644 index 12c8e6aef..000000000 Binary files a/static/plugs/ueditor/lang/en/images/localimage.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/music.png b/static/plugs/ueditor/lang/en/images/music.png deleted file mode 100644 index 2f495fe92..000000000 Binary files a/static/plugs/ueditor/lang/en/images/music.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/rotateleftdisable.png b/static/plugs/ueditor/lang/en/images/rotateleftdisable.png deleted file mode 100644 index 741526e0d..000000000 Binary files a/static/plugs/ueditor/lang/en/images/rotateleftdisable.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/rotateleftenable.png b/static/plugs/ueditor/lang/en/images/rotateleftenable.png deleted file mode 100644 index e164ddbd6..000000000 Binary files a/static/plugs/ueditor/lang/en/images/rotateleftenable.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/rotaterightdisable.png b/static/plugs/ueditor/lang/en/images/rotaterightdisable.png deleted file mode 100644 index 5a78c2606..000000000 Binary files a/static/plugs/ueditor/lang/en/images/rotaterightdisable.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/rotaterightenable.png b/static/plugs/ueditor/lang/en/images/rotaterightenable.png deleted file mode 100644 index d768531fc..000000000 Binary files a/static/plugs/ueditor/lang/en/images/rotaterightenable.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/en/images/upload.png b/static/plugs/ueditor/lang/en/images/upload.png deleted file mode 100644 index 7bb15b3d6..000000000 Binary files a/static/plugs/ueditor/lang/en/images/upload.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/zh-cn/images/copy.png b/static/plugs/ueditor/lang/zh-cn/images/copy.png deleted file mode 100644 index b2536aac7..000000000 Binary files a/static/plugs/ueditor/lang/zh-cn/images/copy.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/zh-cn/images/localimage.png b/static/plugs/ueditor/lang/zh-cn/images/localimage.png deleted file mode 100644 index 7303c3643..000000000 Binary files a/static/plugs/ueditor/lang/zh-cn/images/localimage.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/zh-cn/images/music.png b/static/plugs/ueditor/lang/zh-cn/images/music.png deleted file mode 100644 index 354edebc3..000000000 Binary files a/static/plugs/ueditor/lang/zh-cn/images/music.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/zh-cn/images/upload.png b/static/plugs/ueditor/lang/zh-cn/images/upload.png deleted file mode 100644 index 08d4d9268..000000000 Binary files a/static/plugs/ueditor/lang/zh-cn/images/upload.png and /dev/null differ diff --git a/static/plugs/ueditor/lang/zh-cn/zh-cn.js b/static/plugs/ueditor/lang/zh-cn/zh-cn.js deleted file mode 100644 index 10ff15162..000000000 --- a/static/plugs/ueditor/lang/zh-cn/zh-cn.js +++ /dev/null @@ -1,669 +0,0 @@ -/** - * Created with JetBrains PhpStorm. - * User: taoqili - * Date: 12-6-12 - * Time: 下午5:02 - * To change this template use File | Settings | File Templates. - */ -UE.I18N['zh-cn'] = { - 'labelMap':{ - 'anchor':'锚点', 'undo':'撤销', 'redo':'重做', 'bold':'加粗', 'indent':'首行缩进', 'snapscreen':'截图', - 'italic':'斜体', 'underline':'下划线', 'strikethrough':'删除线', 'subscript':'下标','fontborder':'字符边框', - 'superscript':'上标', 'formatmatch':'格式刷', 'source':'源代码', 'blockquote':'引用', - 'pasteplain':'纯文本粘贴模式', 'selectall':'全选', 'print':'打印', 'preview':'预览', - 'horizontal':'分隔线', 'removeformat':'清除格式', 'time':'时间', 'date':'日期', - 'unlink':'取消链接', 'insertrow':'前插入行', 'insertcol':'前插入列', 'mergeright':'右合并单元格', 'mergedown':'下合并单元格', - 'deleterow':'删除行', 'deletecol':'删除列', 'splittorows':'拆分成行', - 'splittocols':'拆分成列', 'splittocells':'完全拆分单元格','deletecaption':'删除表格标题','inserttitle':'插入标题', - 'mergecells':'合并多个单元格', 'deletetable':'删除表格', 'cleardoc':'清空文档','insertparagraphbeforetable':"表格前插入行",'insertcode':'代码语言', - 'fontfamily':'字体', 'fontsize':'字号', 'paragraph':'段落格式', 'simpleupload':'单图上传', 'insertimage':'多图上传','edittable':'表格属性','edittd':'单元格属性', 'link':'超链接', - 'emotion':'表情', 'spechars':'特殊字符', 'searchreplace':'查询替换', 'map':'Baidu地图', 'gmap':'Google地图', - 'insertvideo':'视频', 'help':'帮助', 'justifyleft':'居左对齐', 'justifyright':'居右对齐', 'justifycenter':'居中对齐', - 'justifyjustify':'两端对齐', 'forecolor':'字体颜色', 'backcolor':'背景色', 'insertorderedlist':'有序列表', - 'insertunorderedlist':'无序列表', 'fullscreen':'全屏', 'directionalityltr':'从左向右输入', 'directionalityrtl':'从右向左输入', - 'rowspacingtop':'段前距', 'rowspacingbottom':'段后距', 'pagebreak':'分页', 'insertframe':'插入Iframe', 'imagenone':'默认', - 'imageleft':'左浮动', 'imageright':'右浮动', 'attachment':'附件', 'imagecenter':'居中', 'wordimage':'图片转存', - 'lineheight':'行间距','edittip' :'编辑提示','customstyle':'自定义标题', 'autotypeset':'自动排版', - 'webapp':'百度应用','touppercase':'字母大写', 'tolowercase':'字母小写','background':'背景','template':'模板','scrawl':'涂鸦', - 'music':'音乐','inserttable':'插入表格','drafts': '从草稿箱加载', 'charts': '图表' - }, - 'insertorderedlist':{ - 'num':'1,2,3...', - 'num1':'1),2),3)...', - 'num2':'(1),(2),(3)...', - 'cn':'一,二,三....', - 'cn1':'一),二),三)....', - 'cn2':'(一),(二),(三)....', - 'decimal':'1,2,3...', - 'lower-alpha':'a,b,c...', - 'lower-roman':'i,ii,iii...', - 'upper-alpha':'A,B,C...', - 'upper-roman':'I,II,III...' - }, - 'insertunorderedlist':{ - 'circle':'○ 大圆圈', - 'disc':'● 小黑点', - 'square':'■ 小方块 ', - 'dash' :'— 破折号', - 'dot':' 。 小圆圈' - }, - 'paragraph':{'p':'段落', 'h1':'标题 1', 'h2':'标题 2', 'h3':'标题 3', 'h4':'标题 4', 'h5':'标题 5', 'h6':'标题 6'}, - 'fontfamily':{ - 'songti':'宋体', - 'kaiti':'楷体', - 'heiti':'黑体', - 'lishu':'隶书', - 'yahei':'微软雅黑', - 'andaleMono':'andale mono', - 'arial': 'arial', - 'arialBlack':'arial black', - 'comicSansMs':'comic sans ms', - 'impact':'impact', - 'timesNewRoman':'times new roman' - }, - 'customstyle':{ - 'tc':'标题居中', - 'tl':'标题居左', - 'im':'强调', - 'hi':'明显强调' - }, - 'autoupload': { - 'exceedSizeError': '文件大小超出限制', - 'exceedTypeError': '文件格式不允许', - 'jsonEncodeError': '服务器返回格式错误', - 'loading':"正在上传...", - 'loadError':"上传错误", - 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!' - }, - 'simpleupload':{ - 'exceedSizeError': '文件大小超出限制', - 'exceedTypeError': '文件格式不允许', - 'jsonEncodeError': '服务器返回格式错误', - 'loading':"正在上传...", - 'loadError':"上传错误", - 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!' - }, - 'elementPathTip':"元素路径", - 'wordCountTip':"字数统计", - 'wordCountMsg':'当前已输入{#count}个字符, 您还可以输入{#leave}个字符。 ', - 'wordOverFlowMsg':'字数超出最大允许值,服务器可能拒绝保存!', - 'ok':"确认", - 'cancel':"取消", - 'closeDialog':"关闭对话框", - 'tableDrag':"表格拖动必须引入uiUtils.js文件!", - 'autofloatMsg':"工具栏浮动依赖编辑器UI,您首先需要引入UI文件!", - 'loadconfigError': '获取后台配置项请求出错,上传功能将不能正常使用!', - 'loadconfigFormatError': '后台配置项返回格式出错,上传功能将不能正常使用!', - 'loadconfigHttpError': '请求后台配置项http错误,上传功能将不能正常使用!', - 'snapScreen_plugin':{ - 'browserMsg':"仅支持IE浏览器!", - 'callBackErrorMsg':"服务器返回数据有误,请检查配置项之后重试。", - 'uploadErrorMsg':"截图上传失败,请检查服务器端环境! " - }, - 'insertcode':{ - 'as3':'ActionScript 3', - 'bash':'Bash/Shell', - 'cpp':'C/C++', - 'css':'CSS', - 'cf':'ColdFusion', - 'c#':'C#', - 'delphi':'Delphi', - 'diff':'Diff', - 'erlang':'Erlang', - 'groovy':'Groovy', - 'html':'HTML', - 'java':'Java', - 'jfx':'JavaFX', - 'js':'JavaScript', - 'pl':'Perl', - 'php':'PHP', - 'plain':'Plain Text', - 'ps':'PowerShell', - 'python':'Python', - 'ruby':'Ruby', - 'scala':'Scala', - 'sql':'SQL', - 'vb':'Visual Basic', - 'xml':'XML' - }, - 'confirmClear':"确定清空当前文档么?", - 'contextMenu':{ - 'delete':"删除", - 'selectall':"全选", - 'deletecode':"删除代码", - 'cleardoc':"清空文档", - 'confirmclear':"确定清空当前文档么?", - 'unlink':"删除超链接", - 'paragraph':"段落格式", - 'edittable':"表格属性", - 'aligntd':"单元格对齐方式", - 'aligntable':'表格对齐方式', - 'tableleft':'左浮动', - 'tablecenter':'居中显示', - 'tableright':'右浮动', - 'edittd':"单元格属性", - 'setbordervisible':'设置表格边线可见', - 'justifyleft':'左对齐', - 'justifyright':'右对齐', - 'justifycenter':'居中对齐', - 'justifyjustify':'两端对齐', - 'table':"表格", - 'inserttable':'插入表格', - 'deletetable':"删除表格", - 'insertparagraphbefore':"前插入段落", - 'insertparagraphafter':'后插入段落', - 'deleterow':"删除当前行", - 'deletecol':"删除当前列", - 'insertrow':"前插入行", - 'insertcol':"左插入列", - 'insertrownext':'后插入行', - 'insertcolnext':'右插入列', - 'insertcaption':'插入表格名称', - 'deletecaption':'删除表格名称', - 'inserttitle':'插入表格标题行', - 'deletetitle':'删除表格标题行', - 'inserttitlecol':'插入表格标题列', - 'deletetitlecol':'删除表格标题列', - 'averageDiseRow':'平均分布各行', - 'averageDisCol':'平均分布各列', - 'mergeright':"向右合并", - 'mergeleft':"向左合并", - 'mergedown':"向下合并", - 'mergecells':"合并单元格", - 'splittocells':"完全拆分单元格", - 'splittocols':"拆分成列", - 'splittorows':"拆分成行", - 'tablesort':'表格排序', - 'enablesort':'设置表格可排序', - 'disablesort':'取消表格可排序', - 'reversecurrent':'逆序当前', - 'orderbyasc':'按ASCII字符升序', - 'reversebyasc':'按ASCII字符降序', - 'orderbynum':'按数值大小升序', - 'reversebynum':'按数值大小降序', - 'borderbk':'边框底纹', - 'setcolor':'表格隔行变色', - 'unsetcolor':'取消表格隔行变色', - 'setbackground':'选区背景隔行', - 'unsetbackground':'取消选区背景', - 'redandblue':'红蓝相间', - 'threecolorgradient':'三色渐变', - 'copy':"复制(Ctrl + c)", - 'copymsg': "浏览器不支持,请使用 'Ctrl + c'", - 'paste':"粘贴(Ctrl + v)", - 'pastemsg': "浏览器不支持,请使用 'Ctrl + v'" - }, - 'copymsg': "浏览器不支持,请使用 'Ctrl + c'", - 'pastemsg': "浏览器不支持,请使用 'Ctrl + v'", - 'anthorMsg':"链接", - 'clearColor':'清空颜色', - 'standardColor':'标准颜色', - 'themeColor':'主题颜色', - 'property':'属性', - 'default':'默认', - 'modify':'修改', - 'justifyleft':'左对齐', - 'justifyright':'右对齐', - 'justifycenter':'居中', - 'justify':'默认', - 'clear':'清除', - 'anchorMsg':'锚点', - 'delete':'删除', - 'clickToUpload':"点击上传", - 'unset':'尚未设置语言文件', - 't_row':'行', - 't_col':'列', - 'more':'更多', - 'pasteOpt':'粘贴选项', - 'pasteSourceFormat':"保留源格式", - 'tagFormat':'只保留标签', - 'pasteTextFormat':'只保留文本', - 'autoTypeSet':{ - 'mergeLine':"合并空行", - 'delLine':"清除空行", - 'removeFormat':"清除格式", - 'indent':"首行缩进", - 'alignment':"对齐方式", - 'imageFloat':"图片浮动", - 'removeFontsize':"清除字号", - 'removeFontFamily':"清除字体", - 'removeHtml':"清除冗余HTML代码", - 'pasteFilter':"粘贴过滤", - 'run':"执行", - 'symbol':'符号转换', - 'bdc2sb':'全角转半角', - 'tobdc':'半角转全角' - }, - - 'background':{ - 'static':{ - 'lang_background_normal':'背景设置', - 'lang_background_local':'在线图片', - 'lang_background_set':'选项', - 'lang_background_none':'无背景色', - 'lang_background_colored':'有背景色', - 'lang_background_color':'颜色设置', - 'lang_background_netimg':'网络图片', - 'lang_background_align':'对齐方式', - 'lang_background_position':'精确定位', - 'repeatType':{'options':["居中", "横向重复", "纵向重复", "平铺","自定义"]} - - }, - 'noUploadImage':"当前未上传过任何图片!", - 'toggleSelect':"单击可切换选中状态\n原图尺寸: " - }, - //===============dialog i18N======================= - 'insertimage':{ - 'static':{ - 'lang_tab_remote':"插入图片", //节点 - 'lang_tab_upload':"本地上传", - 'lang_tab_online':"在线管理", - 'lang_tab_search':"图片搜索", - 'lang_input_url':"地 址:", - 'lang_input_size':"大 小:", - 'lang_input_width':"宽度", - 'lang_input_height':"高度", - 'lang_input_border':"边 框:", - 'lang_input_vhspace':"边 距:", - 'lang_input_title':"描 述:", - 'lang_input_align':'图片浮动方式:', - 'lang_imgLoading':" 图片加载中……", - 'lang_start_upload':"开始上传", - 'lock':{'title':"锁定宽高比例"}, //属性 - 'searchType':{'title':"图片类型", 'options':["新闻", "壁纸", "表情", "头像"]}, //select的option - 'searchTxt':{'value':"请输入搜索关键词"}, - 'searchBtn':{'value':"百度一下"}, - 'searchReset':{'value':"清空搜索"}, - 'noneAlign':{'title':'无浮动'}, - 'leftAlign':{'title':'左浮动'}, - 'rightAlign':{'title':'右浮动'}, - 'centerAlign':{'title':'居中独占一行'} - }, - 'uploadSelectFile':'点击选择图片', - 'uploadAddFile':'继续添加', - 'uploadStart':'开始上传', - 'uploadPause':'暂停上传', - 'uploadContinue':'继续上传', - 'uploadRetry':'重试上传', - 'uploadDelete':'删除', - 'uploadTurnLeft':'向左旋转', - 'uploadTurnRight':'向右旋转', - 'uploadPreview':'预览中', - 'uploadNoPreview':'不能预览', - 'updateStatusReady': '选中_张图片,共_KB。', - 'updateStatusConfirm': '已成功上传_张照片,_张照片上传失败', - 'updateStatusFinish': '共_张(_KB),_张上传成功', - 'updateStatusError': ',_张上传失败。', - 'errorNotSupport': 'WebUploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器。', - 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!', - 'errorExceedSize':'文件大小超出', - 'errorFileType':'文件格式不允许', - 'errorInterrupt':'文件传输中断', - 'errorUploadRetry':'上传失败,请重试', - 'errorHttp':'http请求错误', - 'errorServerUpload':'服务器返回出错', - 'remoteLockError':"宽高不正确,不能所定比例", - 'numError':"请输入正确的长度或者宽度值!例如:123,400", - 'imageUrlError':"不允许的图片格式或者图片域!", - 'imageLoadError':"图片加载失败!请检查链接地址或网络状态!", - 'searchRemind':"请输入搜索关键词", - 'searchLoading':"图片加载中,请稍后……", - 'searchRetry':" :( ,抱歉,没有找到图片!请重试一次!" - }, - 'attachment':{ - 'static':{ - 'lang_tab_upload': '上传附件', - 'lang_tab_online': '在线附件', - 'lang_start_upload':"开始上传", - 'lang_drop_remind':"可以将文件拖到这里,单次最多可选100个文件" - }, - 'uploadSelectFile':'点击选择文件', - 'uploadAddFile':'继续添加', - 'uploadStart':'开始上传', - 'uploadPause':'暂停上传', - 'uploadContinue':'继续上传', - 'uploadRetry':'重试上传', - 'uploadDelete':'删除', - 'uploadTurnLeft':'向左旋转', - 'uploadTurnRight':'向右旋转', - 'uploadPreview':'预览中', - 'updateStatusReady': '选中_个文件,共_KB。', - 'updateStatusConfirm': '已成功上传_个文件,_个文件上传失败', - 'updateStatusFinish': '共_个(_KB),_个上传成功', - 'updateStatusError': ',_张上传失败。', - 'errorNotSupport': 'WebUploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器。', - 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!', - 'errorExceedSize':'文件大小超出', - 'errorFileType':'文件格式不允许', - 'errorInterrupt':'文件传输中断', - 'errorUploadRetry':'上传失败,请重试', - 'errorHttp':'http请求错误', - 'errorServerUpload':'服务器返回出错' - }, - 'insertvideo':{ - 'static':{ - 'lang_tab_insertV':"插入视频", - 'lang_tab_searchV':"搜索视频", - 'lang_tab_uploadV':"上传视频", - 'lang_video_url':"视频网址", - 'lang_video_size':"视频尺寸", - 'lang_videoW':"宽度", - 'lang_videoH':"高度", - 'lang_alignment':"对齐方式", - 'videoSearchTxt':{'value':"请输入搜索关键字!"}, - 'videoType':{'options':["全部", "热门", "娱乐", "搞笑", "体育", "科技", "综艺"]}, - 'videoSearchBtn':{'value':"百度一下"}, - 'videoSearchReset':{'value':"清空结果"}, - - 'lang_input_fileStatus':' 当前未上传文件', - 'startUpload':{'style':"background:url(upload.png) no-repeat;"}, - - 'lang_upload_size':"视频尺寸", - 'lang_upload_width':"宽度", - 'lang_upload_height':"高度", - 'lang_upload_alignment':"对齐方式", - 'lang_format_advice':"建议使用mp4格式." - - }, - 'numError':"请输入正确的数值,如123,400", - 'floatLeft':"左浮动", - 'floatRight':"右浮动", - '"default"':"默认", - 'block':"独占一行", - 'urlError':"输入的视频地址有误,请检查后再试!", - 'loading':"  视频加载中,请等待……", - 'clickToSelect':"点击选中", - 'goToSource':'访问源视频', - 'noVideo':"    抱歉,找不到对应的视频,请重试!", - - 'browseFiles':'浏览文件', - 'uploadSuccess':'上传成功!', - 'delSuccessFile':'从成功队列中移除', - 'delFailSaveFile':'移除保存失败文件', - 'statusPrompt':' 个文件已上传! ', - 'flashVersionError':'当前Flash版本过低,请更新FlashPlayer后重试!', - 'flashLoadingError':'Flash加载失败!请检查路径或网络状态', - 'fileUploadReady':'等待上传……', - 'delUploadQueue':'从上传队列中移除', - 'limitPrompt1':'单次不能选择超过', - 'limitPrompt2':'个文件!请重新选择!', - 'delFailFile':'移除失败文件', - 'fileSizeLimit':'文件大小超出限制!', - 'emptyFile':'空文件无法上传!', - 'fileTypeError':'文件类型不允许!', - 'unknownError':'未知错误!', - 'fileUploading':'上传中,请等待……', - 'cancelUpload':'取消上传', - 'netError':'网络错误', - 'failUpload':'上传失败!', - 'serverIOError':'服务器IO错误!', - 'noAuthority':'无权限!', - 'fileNumLimit':'上传个数限制', - 'failCheck':'验证失败,本次上传被跳过!', - 'fileCanceling':'取消中,请等待……', - 'stopUploading':'上传已停止……', - - 'uploadSelectFile':'点击选择文件', - 'uploadAddFile':'继续添加', - 'uploadStart':'开始上传', - 'uploadPause':'暂停上传', - 'uploadContinue':'继续上传', - 'uploadRetry':'重试上传', - 'uploadDelete':'删除', - 'uploadTurnLeft':'向左旋转', - 'uploadTurnRight':'向右旋转', - 'uploadPreview':'预览中', - 'updateStatusReady': '选中_个文件,共_KB。', - 'updateStatusConfirm': '成功上传_个,_个失败', - 'updateStatusFinish': '共_个(_KB),_个成功上传', - 'updateStatusError': ',_张上传失败。', - 'errorNotSupport': 'WebUploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器。', - 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!', - 'errorExceedSize':'文件大小超出', - 'errorFileType':'文件格式不允许', - 'errorInterrupt':'文件传输中断', - 'errorUploadRetry':'上传失败,请重试', - 'errorHttp':'http请求错误', - 'errorServerUpload':'服务器返回出错' - }, - 'webapp':{ - 'tip1':"本功能由百度APP提供,如看到此页面,请各位站长首先申请百度APPKey!", - 'tip2':"申请完成之后请至ueditor.config.js中配置获得的appkey! ", - 'applyFor':"点此申请", - 'anthorApi':"百度API" - }, - 'template':{ - 'static':{ - 'lang_template_bkcolor':'背景颜色', - 'lang_template_clear' : '保留原有内容', - 'lang_template_select' : '选择模板' - }, - 'blank':"空白文档", - 'blog':"博客文章", - 'resume':"个人简历", - 'richText':"图文混排", - 'sciPapers':"科技论文" - - - }, - 'scrawl':{ - 'static':{ - 'lang_input_previousStep':"上一步", - 'lang_input_nextsStep':"下一步", - 'lang_input_clear':'清空', - 'lang_input_addPic':'添加背景', - 'lang_input_ScalePic':'缩放背景', - 'lang_input_removePic':'删除背景', - 'J_imgTxt':{title:'添加背景图片'} - }, - 'noScarwl':"尚未作画,白纸一张~", - 'scrawlUpLoading':"涂鸦上传中,别急哦~", - 'continueBtn':"继续", - 'imageError':"糟糕,图片读取失败了!", - 'backgroundUploading':'背景图片上传中,别急哦~' - }, - 'music':{ - 'static':{ - 'lang_input_tips':"输入歌手/歌曲/专辑,搜索您感兴趣的音乐!", - 'J_searchBtn':{value:'搜索歌曲'} - }, - 'emptyTxt':'未搜索到相关音乐结果,请换一个关键词试试。', - 'chapter':'歌曲', - 'singer':'歌手', - 'special':'专辑', - 'listenTest':'试听' - }, - 'anchor':{ - 'static':{ - 'lang_input_anchorName':'锚点名字:' - } - }, - 'charts':{ - 'static':{ - 'lang_data_source':'数据源:', - 'lang_chart_format': '图表格式:', - 'lang_data_align': '数据对齐方式', - 'lang_chart_align_same': '数据源与图表X轴Y轴一致', - 'lang_chart_align_reverse': '数据源与图表X轴Y轴相反', - 'lang_chart_title': '图表标题', - 'lang_chart_main_title': '主标题:', - 'lang_chart_sub_title': '子标题:', - 'lang_chart_x_title': 'X轴标题:', - 'lang_chart_y_title': 'Y轴标题:', - 'lang_chart_tip': '提示文字', - 'lang_cahrt_tip_prefix': '提示文字前缀:', - 'lang_cahrt_tip_description': '仅饼图有效, 当鼠标移动到饼图中相应的块上时,提示框内的文字的前缀', - 'lang_chart_data_unit': '数据单位', - 'lang_chart_data_unit_title': '单位:', - 'lang_chart_data_unit_description': '显示在每个数据点上的数据的单位, 比如: 温度的单位 ℃', - 'lang_chart_type': '图表类型:', - 'lang_prev_btn': '上一个', - 'lang_next_btn': '下一个' - } - }, - 'emotion':{ - 'static':{ - 'lang_input_choice':'精选', - 'lang_input_Tuzki':'兔斯基', - 'lang_input_BOBO':'BOBO', - 'lang_input_lvdouwa':'绿豆蛙', - 'lang_input_babyCat':'baby猫', - 'lang_input_bubble':'泡泡', - 'lang_input_youa':'有啊' - } - }, - 'gmap':{ - 'static':{ - 'lang_input_address':'地址', - 'lang_input_search':'搜索', - 'address':{value:"北京"} - }, - searchError:'无法定位到该地址!' - }, - 'help':{ - 'static':{ - 'lang_input_about':'关于UEditor', - 'lang_input_shortcuts':'快捷键', - 'lang_input_introduction':'UEditor是由百度web前端研发部开发的所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点。开源基于BSD协议,允许自由使用和修改代码。', - 'lang_Txt_shortcuts':'快捷键', - 'lang_Txt_func':'功能', - 'lang_Txt_bold':'给选中字设置为加粗', - 'lang_Txt_copy':'复制选中内容', - 'lang_Txt_cut':'剪切选中内容', - 'lang_Txt_Paste':'粘贴', - 'lang_Txt_undo':'重新执行上次操作', - 'lang_Txt_redo':'撤销上一次操作', - 'lang_Txt_italic':'给选中字设置为斜体', - 'lang_Txt_underline':'给选中字加下划线', - 'lang_Txt_selectAll':'全部选中', - 'lang_Txt_visualEnter':'软回车', - 'lang_Txt_fullscreen':'全屏' - } - }, - 'insertframe':{ - 'static':{ - 'lang_input_address':'地址:', - 'lang_input_width':'宽度:', - 'lang_input_height':'高度:', - 'lang_input_isScroll':'允许滚动条:', - 'lang_input_frameborder':'显示框架边框:', - 'lang_input_alignMode':'对齐方式:', - 'align':{title:"对齐方式", options:["默认", "左对齐", "右对齐", "居中"]} - }, - 'enterAddress':'请输入地址!' - }, - 'link':{ - 'static':{ - 'lang_input_text':'文本内容:', - 'lang_input_url':'链接地址:', - 'lang_input_title':'标题:', - 'lang_input_target':'是否在新窗口打开:' - }, - 'validLink':'只支持选中一个链接时生效', - 'httpPrompt':'您输入的超链接中不包含http等协议名称,默认将为您添加http://前缀' - }, - 'map':{ - 'static':{ - lang_city:"城市", - lang_address:"地址", - city:{value:"北京"}, - lang_search:"搜索", - lang_dynamicmap:"插入动态地图" - }, - cityMsg:"请选择城市", - errorMsg:"抱歉,找不到该位置!" - }, - 'searchreplace':{ - 'static':{ - lang_tab_search:"查找", - lang_tab_replace:"替换", - lang_search1:"查找", - lang_search2:"查找", - lang_replace:"替换", - lang_searchReg:'支持正则表达式,添加前后斜杠标示为正则表达式,例如“/表达式/”', - lang_searchReg1:'支持正则表达式,添加前后斜杠标示为正则表达式,例如“/表达式/”', - lang_case_sensitive1:"区分大小写", - lang_case_sensitive2:"区分大小写", - nextFindBtn:{value:"下一个"}, - preFindBtn:{value:"上一个"}, - nextReplaceBtn:{value:"下一个"}, - preReplaceBtn:{value:"上一个"}, - repalceBtn:{value:"替换"}, - repalceAllBtn:{value:"全部替换"} - }, - getEnd:"已经搜索到文章末尾!", - getStart:"已经搜索到文章头部", - countMsg:"总共替换了{#count}处!" - }, - 'snapscreen':{ - 'static':{ - lang_showMsg:"截图功能需要首先安装UEditor截图插件! ", - lang_download:"点此下载", - lang_step1:"第一步,下载UEditor截图插件并运行安装。", - lang_step2:"第二步,插件安装完成后即可使用,如不生效,请重启浏览器后再试!" - } - }, - 'spechars':{ - 'static':{}, - tsfh:"特殊字符", - lmsz:"罗马字符", - szfh:"数学字符", - rwfh:"日文字符", - xlzm:"希腊字母", - ewzm:"俄文字符", - pyzm:"拼音字母", - yyyb:"英语音标", - zyzf:"其他" - }, - 'edittable':{ - 'static':{ - 'lang_tableStyle':'表格样式', - 'lang_insertCaption':'添加表格名称行', - 'lang_insertTitle':'添加表格标题行', - 'lang_insertTitleCol':'添加表格标题列', - 'lang_orderbycontent':"使表格内容可排序", - 'lang_tableSize':'自动调整表格尺寸', - 'lang_autoSizeContent':'按表格文字自适应', - 'lang_autoSizePage':'按页面宽度自适应', - 'lang_example':'示例', - 'lang_borderStyle':'表格边框', - 'lang_color':'颜色:' - }, - captionName:'表格名称', - titleName:'标题', - cellsName:'内容', - errorMsg:'有合并单元格,不可排序' - }, - 'edittip':{ - 'static':{ - lang_delRow:'删除整行', - lang_delCol:'删除整列' - } - }, - 'edittd':{ - 'static':{ - lang_tdBkColor:'背景颜色:' - } - }, - 'formula':{ - 'static':{ - } - }, - 'wordimage':{ - 'static':{ - lang_resave:"转存步骤", - uploadBtn:{src:"upload.png",alt:"上传"}, - clipboard:{style:"background: url(copy.png) -153px -1px no-repeat;"}, - lang_step:"1、点击顶部复制按钮,将地址复制到剪贴板;2、点击添加照片按钮,在弹出的对话框中使用Ctrl+V粘贴地址;3、点击打开后选择图片上传流程。" - }, - 'fileType':"图片", - 'flashError':"FLASH初始化失败,请检查FLASH插件是否正确安装!", - 'netError':"网络连接错误,请重试!", - 'copySuccess':"图片地址已经复制!", - 'flashI18n':{} //留空默认中文 - }, - 'autosave': { - 'saving':'保存中...', - 'success':'本地保存成功' - } -}; diff --git a/static/plugs/ueditor/php/Uploader.class.php b/static/plugs/ueditor/php/Uploader.class.php deleted file mode 100644 index 9b14a90d1..000000000 --- a/static/plugs/ueditor/php/Uploader.class.php +++ /dev/null @@ -1,372 +0,0 @@ - "临时文件错误", - "ERROR_TMP_FILE_NOT_FOUND" => "找不到临时文件", - "ERROR_SIZE_EXCEED" => "文件大小超出网站限制", - "ERROR_TYPE_NOT_ALLOWED" => "文件类型不允许", - "ERROR_CREATE_DIR" => "目录创建失败", - "ERROR_DIR_NOT_WRITEABLE" => "目录没有写权限", - "ERROR_FILE_MOVE" => "文件保存时出错", - "ERROR_FILE_NOT_FOUND" => "找不到上传文件", - "ERROR_WRITE_CONTENT" => "写入文件内容错误", - "ERROR_UNKNOWN" => "未知错误", - "ERROR_DEAD_LINK" => "链接不可用", - "ERROR_HTTP_LINK" => "链接不是http链接", - "ERROR_HTTP_CONTENTTYPE" => "链接contentType不正确", - "INVALID_URL" => "非法 URL", - "INVALID_IP" => "非法 IP" - ); - - /** - * 构造函数 - * @param string $fileField 表单名称 - * @param array $config 配置项 - * @param bool $base64 是否解析base64编码,可省略。若开启,则$fileField代表的是base64编码的字符串表单名 - */ - public function __construct($fileField, $config, $type = "upload") - { - $this->fileField = $fileField; - $this->config = $config; - $this->type = $type; - if ($type == "remote") { - $this->saveRemote(); - } else if($type == "base64") { - $this->upBase64(); - } else { - $this->upFile(); - } - - $this->stateMap['ERROR_TYPE_NOT_ALLOWED'] = iconv('unicode', 'utf-8', $this->stateMap['ERROR_TYPE_NOT_ALLOWED']); - } - - /** - * 上传文件的主处理方法 - * @return mixed - */ - private function upFile() - { - $file = $this->file = $_FILES[$this->fileField]; - if (!$file) { - $this->stateInfo = $this->getStateInfo("ERROR_FILE_NOT_FOUND"); - return; - } - if ($this->file['error']) { - $this->stateInfo = $this->getStateInfo($file['error']); - return; - } else if (!file_exists($file['tmp_name'])) { - $this->stateInfo = $this->getStateInfo("ERROR_TMP_FILE_NOT_FOUND"); - return; - } else if (!is_uploaded_file($file['tmp_name'])) { - $this->stateInfo = $this->getStateInfo("ERROR_TMPFILE"); - return; - } - - $this->oriName = $file['name']; - $this->fileSize = $file['size']; - $this->fileType = $this->getFileExt(); - $this->fullName = $this->getFullName(); - $this->filePath = $this->getFilePath(); - $this->fileName = $this->getFileName(); - $dirname = dirname($this->filePath); - - //检查文件大小是否超出限制 - if (!$this->checkSize()) { - $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED"); - return; - } - - //检查是否不允许的文件格式 - if (!$this->checkType()) { - $this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED"); - return; - } - - //创建目录失败 - if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) { - $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR"); - return; - } else if (!is_writeable($dirname)) { - $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE"); - return; - } - - //移动文件 - if (!(move_uploaded_file($file["tmp_name"], $this->filePath) && file_exists($this->filePath))) { //移动失败 - $this->stateInfo = $this->getStateInfo("ERROR_FILE_MOVE"); - } else { //移动成功 - $this->stateInfo = $this->stateMap[0]; - } - } - - /** - * 处理base64编码的图片上传 - * @return mixed - */ - private function upBase64() - { - $base64Data = $_POST[$this->fileField]; - $img = base64_decode($base64Data); - - $this->oriName = $this->config['oriName']; - $this->fileSize = strlen($img); - $this->fileType = $this->getFileExt(); - $this->fullName = $this->getFullName(); - $this->filePath = $this->getFilePath(); - $this->fileName = $this->getFileName(); - $dirname = dirname($this->filePath); - - //检查文件大小是否超出限制 - if (!$this->checkSize()) { - $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED"); - return; - } - - //创建目录失败 - if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) { - $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR"); - return; - } else if (!is_writeable($dirname)) { - $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE"); - return; - } - - //移动文件 - if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败 - $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT"); - } else { //移动成功 - $this->stateInfo = $this->stateMap[0]; - } - - } - - /** - * 拉取远程图片 - * @return mixed - */ - private function saveRemote() - { - $local_url = htmlspecialchars($this->fileField); - $local_url = str_replace("&", "&", $local_url); - - //http开头验证 - if (strpos($local_url, "http") !== 0) { - $this->stateInfo = $this->getStateInfo("ERROR_HTTP_LINK"); - return; - } - - preg_match('/(^https*:\/\/[^:\/]+)/', $local_url, $matches); - $host_with_protocol = count($matches) > 1 ? $matches[1] : ''; - - // 判断是否是合法 url - if (!filter_var($host_with_protocol, FILTER_VALIDATE_URL)) { - $this->stateInfo = $this->getStateInfo("INVALID_URL"); - return; - } - - preg_match('/^https*:\/\/(.+)/', $host_with_protocol, $matches); - $host_without_protocol = count($matches) > 1 ? $matches[1] : ''; - - // 此时提取出来的可能是 ip 也有可能是域名,先获取 ip - $ip = gethostbyname($host_without_protocol); - // 判断是否是私有 ip - if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) { - $this->stateInfo = $this->getStateInfo("INVALID_IP"); - return; - } - - //获取请求头并检测死链 - $heads = get_headers($local_url, 1); - if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) { - $this->stateInfo = $this->getStateInfo("ERROR_DEAD_LINK"); - return; - } - //格式验证(扩展名验证和Content-Type验证) - $fileType = strtolower(strrchr($local_url, '.')); - if (!in_array($fileType, $this->config['allowFiles']) || !isset($heads['Content-Type']) || !stristr($heads['Content-Type'], "image")) { - $this->stateInfo = $this->getStateInfo("ERROR_HTTP_CONTENTTYPE"); - return; - } - - //打开输出缓冲区并获取远程图片 - ob_start(); - $context = stream_context_create( - array('http' => array( - 'follow_location' => false // don't follow redirects - )) - ); - readfile($local_url, false, $context); - $img = ob_get_contents(); - ob_end_clean(); - preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/", $local_url, $m); - - $this->oriName = $m ? $m[1]:""; - $this->fileSize = strlen($img); - $this->fileType = $this->getFileExt(); - $this->fullName = $this->getFullName(); - $this->filePath = $this->getFilePath(); - $this->fileName = $this->getFileName(); - $dirname = dirname($this->filePath); - - //检查文件大小是否超出限制 - if (!$this->checkSize()) { - $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED"); - return; - } - - //创建目录失败 - if (!file_exists($dirname) && !mkdir($dirname, 0777, true)) { - $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR"); - return; - } else if (!is_writeable($dirname)) { - $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE"); - return; - } - - //移动文件 - if (!(file_put_contents($this->filePath, $img) && file_exists($this->filePath))) { //移动失败 - $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT"); - } else { //移动成功 - $this->stateInfo = $this->stateMap[0]; - } - - } - - /** - * 上传错误检查 - * @param $errCode - * @return string - */ - private function getStateInfo($errCode) - { - return !$this->stateMap[$errCode] ? $this->stateMap["ERROR_UNKNOWN"] : $this->stateMap[$errCode]; - } - - /** - * 获取文件扩展名 - * @return string - */ - private function getFileExt() - { - return strtolower(strrchr($this->oriName, '.')); - } - - /** - * 重命名文件 - * @return string - */ - private function getFullName() - { - //替换日期事件 - $t = time(); - $d = explode('-', date("Y-y-m-d-H-i-s")); - $format = $this->config["pathFormat"]; - $format = str_replace("{yyyy}", $d[0], $format); - $format = str_replace("{yy}", $d[1], $format); - $format = str_replace("{mm}", $d[2], $format); - $format = str_replace("{dd}", $d[3], $format); - $format = str_replace("{hh}", $d[4], $format); - $format = str_replace("{ii}", $d[5], $format); - $format = str_replace("{ss}", $d[6], $format); - $format = str_replace("{time}", $t, $format); - - //过滤文件名的非法自负,并替换文件名 - $oriName = substr($this->oriName, 0, strrpos($this->oriName, '.')); - $oriName = preg_replace("/[\|\?\"\<\>\/\*\\\\]+/", '', $oriName); - $format = str_replace("{filename}", $oriName, $format); - - //替换随机字符串 - $randNum = rand(1, 10000000000) . rand(1, 10000000000); - if (preg_match("/\{rand\:([\d]*)\}/i", $format, $matches)) { - $format = preg_replace("/\{rand\:[\d]*\}/i", substr($randNum, 0, $matches[1]), $format); - } - - $ext = $this->getFileExt(); - return $format . $ext; - } - - /** - * 获取文件名 - * @return string - */ - private function getFileName () { - return substr($this->filePath, strrpos($this->filePath, '/') + 1); - } - - /** - * 获取文件完整路径 - * @return string - */ - private function getFilePath() - { - $fullname = $this->fullName; - $rootPath = $_SERVER['DOCUMENT_ROOT']; - - if (substr($fullname, 0, 1) != '/') { - $fullname = '/' . $fullname; - } - - return $rootPath . $fullname; - } - - /** - * 文件类型检测 - * @return bool - */ - private function checkType() - { - return in_array($this->getFileExt(), $this->config["allowFiles"]); - } - - /** - * 文件大小检测 - * @return bool - */ - private function checkSize() - { - return $this->fileSize <= ($this->config["maxSize"]); - } - - /** - * 获取当前上传成功文件的各项信息 - * @return array - */ - public function getFileInfo() - { - return array( - "state" => $this->stateInfo, - "url" => $this->fullName, - "title" => $this->fileName, - "original" => $this->oriName, - "type" => $this->fileType, - "size" => $this->fileSize - ); - } - -} \ No newline at end of file diff --git a/static/plugs/ueditor/php/action_crawler.php b/static/plugs/ueditor/php/action_crawler.php deleted file mode 100644 index aa2af7a17..000000000 --- a/static/plugs/ueditor/php/action_crawler.php +++ /dev/null @@ -1,44 +0,0 @@ - $CONFIG['catcherPathFormat'], - "maxSize" => $CONFIG['catcherMaxSize'], - "allowFiles" => $CONFIG['catcherAllowFiles'], - "oriName" => "remote.png" -); -$fieldName = $CONFIG['catcherFieldName']; - -/* 抓取远程图片 */ -$list = array(); -if (isset($_POST[$fieldName])) { - $source = $_POST[$fieldName]; -} else { - $source = $_GET[$fieldName]; -} -foreach ($source as $local_url) { - $item = new Uploader($local_url, $config, "remote"); - $info = $item->getFileInfo(); - array_push($list, array( - "state" => $info["state"], - "url" => $info["url"], - "size" => $info["size"], - "title" => htmlspecialchars($info["title"]), - "original" => htmlspecialchars($info["original"]), - "source" => htmlspecialchars($local_url) - )); -} - -/* 返回抓取数据 */ -return json_encode(array( - 'state'=> count($list) ? 'SUCCESS':'ERROR', - 'list'=> $list -)); \ No newline at end of file diff --git a/static/plugs/ueditor/php/action_list.php b/static/plugs/ueditor/php/action_list.php deleted file mode 100644 index bf9cd62c1..000000000 --- a/static/plugs/ueditor/php/action_list.php +++ /dev/null @@ -1,92 +0,0 @@ - "no match file", - "list" => array(), - "start" => $start, - "total" => count($files) - )); -} - -/* 获取指定范围的列表 */ -$len = count($files); -for ($i = min($end, $len) - 1, $list = array(); $i < $len && $i >= 0 && $i >= $start; $i--){ - $list[] = $files[$i]; -} -//倒序 -//for ($i = $end, $list = array(); $i < $len && $i < $end; $i++){ -// $list[] = $files[$i]; -//} - -/* 返回数据 */ -$result = json_encode(array( - "state" => "SUCCESS", - "list" => $list, - "start" => $start, - "total" => count($files) -)); - -return $result; - - -/** - * 遍历获取目录下的指定类型的文件 - * @param $path - * @param array $files - * @return array - */ -function getfiles($path, $allowFiles, &$files = array()) -{ - if (!is_dir($path)) return null; - if(substr($path, strlen($path) - 1) != '/') $path .= '/'; - $handle = opendir($path); - while (false !== ($file = readdir($handle))) { - if ($file != '.' && $file != '..') { - $path2 = $path . $file; - if (is_dir($path2)) { - getfiles($path2, $allowFiles, $files); - } else { - if (preg_match("/\.(".$allowFiles.")$/i", $file)) { - $files[] = array( - 'url'=> substr($path2, strlen($_SERVER['DOCUMENT_ROOT'])), - 'mtime'=> filemtime($path2) - ); - } - } - } - } - return $files; -} \ No newline at end of file diff --git a/static/plugs/ueditor/php/action_upload.php b/static/plugs/ueditor/php/action_upload.php deleted file mode 100644 index d55b6591a..000000000 --- a/static/plugs/ueditor/php/action_upload.php +++ /dev/null @@ -1,66 +0,0 @@ - $CONFIG['imagePathFormat'], - "maxSize" => $CONFIG['imageMaxSize'], - "allowFiles" => $CONFIG['imageAllowFiles'] - ); - $fieldName = $CONFIG['imageFieldName']; - break; - case 'uploadscrawl': - $config = array( - "pathFormat" => $CONFIG['scrawlPathFormat'], - "maxSize" => $CONFIG['scrawlMaxSize'], - "allowFiles" => $CONFIG['scrawlAllowFiles'], - "oriName" => "scrawl.png" - ); - $fieldName = $CONFIG['scrawlFieldName']; - $base64 = "base64"; - break; - case 'uploadvideo': - $config = array( - "pathFormat" => $CONFIG['videoPathFormat'], - "maxSize" => $CONFIG['videoMaxSize'], - "allowFiles" => $CONFIG['videoAllowFiles'] - ); - $fieldName = $CONFIG['videoFieldName']; - break; - case 'uploadfile': - default: - $config = array( - "pathFormat" => $CONFIG['filePathFormat'], - "maxSize" => $CONFIG['fileMaxSize'], - "allowFiles" => $CONFIG['fileAllowFiles'] - ); - $fieldName = $CONFIG['fileFieldName']; - break; -} - -/* 生成上传实例对象并完成上传 */ -$up = new Uploader($fieldName, $config, $base64); - -/** - * 得到上传文件所对应的各个参数,数组结构 - * array( - * "state" => "", //上传状态,上传成功时必须返回"SUCCESS" - * "url" => "", //返回的地址 - * "title" => "", //新文件名 - * "original" => "", //原始文件名 - * "type" => "" //文件类型 - * "size" => "", //文件大小 - * ) - */ - -/* 返回数据 */ -return json_encode($up->getFileInfo()); diff --git a/static/plugs/ueditor/php/config.json b/static/plugs/ueditor/php/config.json deleted file mode 100644 index 5f124fcf0..000000000 --- a/static/plugs/ueditor/php/config.json +++ /dev/null @@ -1,174 +0,0 @@ -{ - "imageActionName": "uploadimage", - "imageFieldName": "upfile", - "imageMaxSize": 2048000, - "imageAllowFiles": [ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp" - ], - "imageCompressEnable": true, - "imageCompressBorder": 1600, - "imageInsertAlign": "none", - "imageUrlPrefix": "", - "imagePathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", - "scrawlActionName": "uploadscrawl", - "scrawlFieldName": "upfile", - "scrawlPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", - "scrawlMaxSize": 2048000, - "scrawlUrlPrefix": "", - "scrawlInsertAlign": "none", - "snapscreenActionName": "uploadimage", - "snapscreenPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", - "snapscreenUrlPrefix": "", - "snapscreenInsertAlign": "none", - "catcherLocalDomain": [ - "127.0.0.1", - "localhost", - "img.baidu.com" - ], - "catcherActionName": "catchimage", - "catcherFieldName": "source", - "catcherPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", - "catcherUrlPrefix": "", - "catcherMaxSize": 2048000, - "catcherAllowFiles": [ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp" - ], - "videoActionName": "uploadvideo", - "videoFieldName": "upfile", - "videoPathFormat": "/ueditor/php/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}", - "videoUrlPrefix": "", - "videoMaxSize": 102400000, - "videoAllowFiles": [ - ".flv", - ".swf", - ".mkv", - ".avi", - ".rm", - ".rmvb", - ".mpeg", - ".mpg", - ".ogg", - ".ogv", - ".mov", - ".wmv", - ".mp4", - ".webm", - ".mp3", - ".wav", - ".mid" - ], - "fileActionName": "uploadfile", - "fileFieldName": "upfile", - "filePathFormat": "/ueditor/php/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", - "fileUrlPrefix": "", - "fileMaxSize": 51200000, - "fileAllowFiles": [ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".flv", - ".swf", - ".mkv", - ".avi", - ".rm", - ".rmvb", - ".mpeg", - ".mpg", - ".ogg", - ".ogv", - ".mov", - ".wmv", - ".mp4", - ".webm", - ".mp3", - ".wav", - ".mid", - ".rar", - ".zip", - ".tar", - ".gz", - ".7z", - ".bz2", - ".cab", - ".iso", - ".doc", - ".docx", - ".xls", - ".xlsx", - ".ppt", - ".pptx", - ".pdf", - ".txt", - ".md", - ".xml" - ], - "imageManagerActionName": "listimage", - "imageManagerListPath": "/ueditor/php/upload/image/", - "imageManagerListSize": 20, - "imageManagerUrlPrefix": "", - "imageManagerInsertAlign": "none", - "imageManagerAllowFiles": [ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp" - ], - "fileManagerActionName": "listfile", - "fileManagerListPath": "/ueditor/php/upload/file/", - "fileManagerUrlPrefix": "", - "fileManagerListSize": 20, - "fileManagerAllowFiles": [ - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".flv", - ".swf", - ".mkv", - ".avi", - ".rm", - ".rmvb", - ".mpeg", - ".mpg", - ".ogg", - ".ogv", - ".mov", - ".wmv", - ".mp4", - ".webm", - ".mp3", - ".wav", - ".mid", - ".rar", - ".zip", - ".tar", - ".gz", - ".7z", - ".bz2", - ".cab", - ".iso", - ".doc", - ".docx", - ".xls", - ".xlsx", - ".ppt", - ".pptx", - ".pdf", - ".txt", - ".md", - ".xml" - ] - -} \ No newline at end of file diff --git a/static/plugs/ueditor/php/controller.php b/static/plugs/ueditor/php/controller.php deleted file mode 100644 index feac890ce..000000000 --- a/static/plugs/ueditor/php/controller.php +++ /dev/null @@ -1,59 +0,0 @@ - '请求地址出错' - )); - break; -} - -/* 输出结果 */ -if (isset($_GET["callback"])) { - if (preg_match("/^[\w_]+$/", $_GET["callback"])) { - echo htmlspecialchars($_GET["callback"]) . '(' . $result . ')'; - } else { - echo json_encode(array( - 'state'=> 'callback参数不合法' - )); - } -} else { - echo $result; -} \ No newline at end of file diff --git a/static/plugs/ueditor/themes/default/css/ueditor.css b/static/plugs/ueditor/themes/default/css/ueditor.css deleted file mode 100644 index 37b7edf44..000000000 --- a/static/plugs/ueditor/themes/default/css/ueditor.css +++ /dev/null @@ -1,1903 +0,0 @@ -/*基础UI构建 -*/ -/* common layer */ -.edui-default .edui-box { - border: none; - padding: 0; - margin: 0; - overflow: hidden; -} - -.edui-default a.edui-box { - display: block; - text-decoration: none; - color: black; -} - -.edui-default a.edui-box:hover { - text-decoration: none; -} - -.edui-default a.edui-box:active { - text-decoration: none; -} - -.edui-default table.edui-box { - border-collapse: collapse; -} - -.edui-default ul.edui-box { - list-style-type: none; -} - -div.edui-box { - position: relative; - display: -moz-inline-box !important; - display: inline-block !important; - vertical-align: top; -} - -.edui-default .edui-clearfix { - zoom: 1 -} - -.edui-default .edui-clearfix:after { - content: '\20'; - display: block; - clear: both; -} - - * html div.edui-box { - display: inline !important; -} - -*:first-child+html div.edui-box { - display: inline !important; -} - -/* control layout */ -.edui-default .edui-button-body, .edui-splitbutton-body, .edui-menubutton-body, .edui-combox-body { - position: relative; -} - -.edui-default .edui-popup { - position: absolute; - -webkit-user-select: none; - -moz-user-select: none; -} - -.edui-default .edui-popup .edui-shadow { - position: absolute; - z-index: -1; -} - -.edui-default .edui-popup .edui-bordereraser { - position: absolute; - overflow: hidden; -} - -.edui-default .edui-tablepicker .edui-canvas { - position: relative; -} - -.edui-default .edui-tablepicker .edui-canvas .edui-overlay { - position: absolute; -} - -.edui-default .edui-dialog-modalmask, .edui-dialog-dragmask { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; -} - -.edui-default .edui-toolbar { - position: relative; -} - -/* - * default theme - */ -.edui-default .edui-label { - cursor: default; -} - -.edui-default span.edui-clickable { - color: blue; - cursor: pointer; - text-decoration: underline; -} - -.edui-default span.edui-unclickable { - color: gray; - cursor: default; -} -/* 工具栏 */ -.edui-default .edui-toolbar { - cursor: default; - -webkit-user-select: none; - -moz-user-select: none; - padding: 1px; - overflow: hidden; /*全屏下单独一行不占位*/ - zoom: 1; - width:auto; - height:auto; -} - -.edui-default .edui-toolbar .edui-button, -.edui-default .edui-toolbar .edui-splitbutton, -.edui-default .edui-toolbar .edui-menubutton, -.edui-default .edui-toolbar .edui-combox { - margin: 1px; -} -/*UI工具栏、编辑区域、底部*/ -.edui-default .edui-editor { - border: 1px solid #d4d4d4; - background-color: white; - position: relative; - overflow: visible; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.edui-editor div{ - width:auto; - height:auto; -} -.edui-default .edui-editor-toolbarbox { - position: relative; - zoom: 1; - -webkit-box-shadow:0 1px 4px rgba(204, 204, 204, 0.6); - -moz-box-shadow:0 1px 4px rgba(204, 204, 204, 0.6); - box-shadow:0 1px 4px rgba(204, 204, 204, 0.6); - border-top-left-radius:2px; - border-top-right-radius:2px; -} - -.edui-default .edui-editor-toolbarboxouter { - border-bottom: 1px solid #d4d4d4; - background-color: #fafafa; - background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); - background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); - background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); - background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); - background-repeat: repeat-x; - /*border: 1px solid #d4d4d4;*/ - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); - *zoom: 1; - -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); -} - -.edui-default .edui-editor-toolbarboxinner { - padding: 2px; -} - -.edui-default .edui-editor-iframeholder { - position: relative; - /*for fix ie6 toolbarmsg under iframe bug. relative -> static */ - /*_position: static !important;* -} - -.edui-default .edui-editor-iframeholder textarea { - font-family: consolas, "Courier New", "lucida console", monospace; - font-size: 12px; - line-height: 18px; -} - -.edui-default .edui-editor-bottombar { - /*border-top: 1px solid #ccc;*/ - /*height: 20px;*/ - /*width: 40%;*/ - /*float: left;*/ - /*overflow: hidden;*/ -} - -.edui-default .edui-editor-bottomContainer { - overflow: hidden; -} - -.edui-default .edui-editor-bottomContainer table { - width: 100%; - height: 0; - overflow: hidden; - border-spacing: 0; -} - -.edui-default .edui-editor-bottomContainer td { - white-space: nowrap; - border-top: 1px solid #ccc; - line-height: 20px; - font-size: 12px; - font-family: Arial, Helvetica, Tahoma, Verdana, Sans-Serif; -} - -.edui-default .edui-editor-wordcount { - text-align: right; - margin-right: 5px; - color: #aaa; -} -.edui-default .edui-editor-scale { - width: 12px; -} -.edui-default .edui-editor-scale .edui-editor-icon { - float: right; - width: 100%; - height: 12px; - margin-top: 10px; - background: url(../images/scale.png) no-repeat; - cursor: se-resize; -} -.edui-default .edui-editor-breadcrumb { - margin: 2px 0 0 3px; -} - -.edui-default .edui-editor-breadcrumb span { - cursor: pointer; - text-decoration: underline; - color: blue; -} - -.edui-default .edui-toolbar .edui-for-fullscreen { - float: right; -} - -.edui-default .edui-bubble .edui-popup-content { - border: 1px solid #DCAC6C; - background-color: #fff6d9; - padding: 5px; - font-size: 10pt; - font-family: "宋体"; -} - -.edui-default .edui-bubble .edui-shadow { - /*box-shadow: 1px 1px 3px #818181;*/ - /*-webkit-box-shadow: 2px 2px 3px #818181;*/ - /*-moz-box-shadow: 2px 2px 3px #818181;*/ - /*filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius = '2', MakeShadow = 'true', ShadowOpacity = '0.5');*/ -} - -.edui-default .edui-editor-toolbarmsg { - background-color: #FFF6D9; - border-bottom: 1px solid #ccc; - position: absolute; - bottom: -25px; - left: 0; - z-index: 1009; - width: 99.9%; -} - -.edui-default .edui-editor-toolbarmsg-upload { - font-size: 14px; - color: blue; - width: 100px; - height: 16px; - line-height: 16px; - cursor: pointer; - position: absolute; - top: 5px; - left: 350px; -} - -.edui-default .edui-editor-toolbarmsg-label { - font-size: 12px; - line-height: 16px; - padding: 4px; -} - -.edui-default .edui-editor-toolbarmsg-close { - float: right; - width: 20px; - height: 16px; - line-height: 16px; - cursor: pointer; - color: red; -} -/*可选中菜单按钮*/ -.edui-default .edui-list .edui-bordereraser { - display: none; -} - -.edui-default .edui-listitem { - padding: 1px; - white-space: nowrap; -} - -.edui-default .edui-list .edui-state-hover { - position: relative; - background-color: #fff5d4; - border: 1px solid #dcac6c; - padding: 0; -} - -.edui-default .edui-for-fontfamily .edui-listitem-label { - min-width: 130px; - _width: 120px; - font-size: 12px; - height: 22px; - line-height: 22px; - padding-left: 5px; -} -.edui-default .edui-for-insertcode .edui-listitem-label { - min-width: 120px; - _width: 120px; - font-size: 12px; - height: 22px; - line-height: 22px; - padding-left: 5px; -} -.edui-default .edui-for-underline .edui-listitem-label { - min-width: 120px; - _width: 120px; - padding: 3px 5px; - font-size: 12px; -} - -.edui-default .edui-for-fontsize .edui-listitem-label { - min-width: 120px; - _width: 120px; - padding: 3px 5px; - -} - -.edui-default .edui-for-paragraph .edui-listitem-label { - min-width: 200px; - _width: 200px; - padding: 2px 5px; -} - -.edui-default .edui-for-rowspacingtop .edui-listitem-label, -.edui-default .edui-for-rowspacingbottom .edui-listitem-label { - min-width: 53px; - _width: 53px; - padding: 2px 5px; -} - -.edui-default .edui-for-lineheight .edui-listitem-label { - min-width: 53px; - _width: 53px; - padding: 2px 5px; -} - -.edui-default .edui-for-customstyle .edui-listitem-label { - min-width: 200px; - _width: 200px; - width: 200px !important; - padding: 2px 5px; -} -/* 可选中按钮弹出菜单*/ -.edui-default .edui-menu { - z-index: 3000; -} - -.edui-default .edui-menu .edui-popup-content { - padding: 3px; -} - -.edui-default .edui-menu-body { - _width: 150px; - min-width: 170px; - background: url("../images/sparator_v.png") repeat-y 25px; -} - -.edui-default .edui-menuitem-body { -} - -.edui-default .edui-menuitem { - height: 20px; - cursor: default; - vertical-align: top; -} - -.edui-default .edui-menuitem .edui-icon { - width: 20px !important; - height: 20px !important; - background: url(../images/icons.png) 0 -4000px; - background: url(../images/icons.gif) 0 -4000px\9; -} - -.edui-default .edui-menuitem .edui-label { - font-size: 12px; - line-height: 20px; - height: 20px; - padding-left: 10px; -} - -.edui-default .edui-state-checked .edui-menuitem-body { - background: url("../images/icons-all.gif") no-repeat 6px -205px; -} - -.edui-default .edui-state-disabled .edui-menuitem-label { - color: gray; -} - - -/*不可选中菜单按钮 */ -.edui-default .edui-toolbar .edui-combox-body .edui-button-body { - width: 60px; - font-size: 12px; - height: 20px; - line-height: 20px; - padding-left: 5px; - white-space: nowrap; - margin: 0 3px 0 0; -} - -.edui-default .edui-toolbar .edui-combox-body .edui-arrow { - background: url(../images/icons.png) -741px 0; - _background: url(../images/icons.gif) -741px 0; - height: 20px; - width: 9px; -} - -.edui-default .edui-toolbar .edui-combox .edui-combox-body { - border: 1px solid #CCC; - background-color: white; - border-radius: 2px; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; -} - -.edui-default .edui-toolbar .edui-combox-body .edui-splitborder { - display: none; -} - -.edui-default .edui-toolbar .edui-combox-body .edui-arrow { - border-left: 1px solid #CCC; -} - -.edui-default .edui-toolbar .edui-state-hover .edui-combox-body { - background-color: #fff5d4; - border: 1px solid #dcac6c; -} - -.edui-default .edui-toolbar .edui-state-hover .edui-combox-body .edui-arrow { - border-left: 1px solid #dcac6c; -} - -.edui-default .edui-toolbar .edui-state-checked .edui-combox-body { - background-color: #FFE69F; - border: 1px solid #DCAC6C; -} - -.edui-toolbar .edui-state-checked .edui-combox-body .edui-arrow { - border-left: 1px solid #DCAC6C; -} - -.edui-toolbar .edui-state-disabled .edui-combox-body { - background-color: #F0F0EE; - opacity: 0.3; - filter: alpha(opacity = 30); -} - -.edui-toolbar .edui-state-opened .edui-combox-body { - background-color: white; - border: 1px solid gray; -} -/*普通按钮样式及状态*/ -.edui-default .edui-toolbar .edui-button .edui-icon, -.edui-default .edui-toolbar .edui-menubutton .edui-icon, -.edui-default .edui-toolbar .edui-splitbutton .edui-icon { - height: 20px !important; - width: 20px !important; - background-image: url(../images/icons.png); - background-image: url(../images/icons.gif) \9; -} - -.edui-default .edui-toolbar .edui-button .edui-button-wrap { - padding: 1px; - position: relative; -} - -.edui-default .edui-toolbar .edui-button .edui-state-hover .edui-button-wrap { - background-color: #fff5d4; - padding: 0; - border: 1px solid #dcac6c; -} - -.edui-default .edui-toolbar .edui-button .edui-state-checked .edui-button-wrap { - background-color: #ffe69f; - padding: 0; - border: 1px solid #dcac6c; - border-radius: 2px; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; -} - -.edui-default .edui-toolbar .edui-button .edui-state-active .edui-button-wrap { - background-color: #ffffff; - padding: 0; - border: 1px solid gray; -} -.edui-default .edui-toolbar .edui-state-disabled .edui-label { - color: #ccc; -} -.edui-default .edui-toolbar .edui-state-disabled .edui-icon { - opacity: 0.3; - filter: alpha(opacity = 30); -} - -/* toolbar icons */ -.edui-default .edui-for-undo .edui-icon { - background-position: -160px 0; -} - -.edui-default .edui-for-redo .edui-icon { - background-position: -100px 0; -} - -.edui-default .edui-for-bold .edui-icon { - background-position: 0 0; -} - -.edui-default .edui-for-italic .edui-icon { - background-position: -60px 0; -} - -.edui-default .edui-for-fontborder .edui-icon { - background-position:-160px -40px; -} -.edui-default .edui-for-underline .edui-icon { - background-position: -140px 0; -} - -.edui-default .edui-for-strikethrough .edui-icon { - background-position: -120px 0; -} - -.edui-default .edui-for-subscript .edui-icon { - background-position: -600px 0; -} - -.edui-default .edui-for-superscript .edui-icon { - background-position: -620px 0; -} - -.edui-default .edui-for-blockquote .edui-icon { - background-position: -220px 0; -} - -.edui-default .edui-for-forecolor .edui-icon { - background-position: -720px 0; -} - -.edui-default .edui-for-backcolor .edui-icon { - background-position: -760px 0; -} - -.edui-default .edui-for-inserttable .edui-icon { - background-position: -580px -20px; -} - -.edui-default .edui-for-autotypeset .edui-icon { - background-position: -640px -40px; -} - -.edui-default .edui-for-justifyleft .edui-icon { - background-position: -460px 0; -} - -.edui-default .edui-for-justifycenter .edui-icon { - background-position: -420px 0; -} - -.edui-default .edui-for-justifyright .edui-icon { - background-position: -480px 0; -} - -.edui-default .edui-for-justifyjustify .edui-icon { - background-position: -440px 0; -} - -.edui-default .edui-for-insertorderedlist .edui-icon { - background-position: -80px 0; -} - -.edui-default .edui-for-insertunorderedlist .edui-icon { - background-position: -20px 0; -} - -.edui-default .edui-for-lineheight .edui-icon { - background-position: -725px -40px; -} - -.edui-default .edui-for-rowspacingbottom .edui-icon { - background-position: -745px -40px; -} - -.edui-default .edui-for-rowspacingtop .edui-icon { - background-position: -765px -40px; -} - -.edui-default .edui-for-horizontal .edui-icon { - background-position: -360px 0; -} - -.edui-default .edui-for-link .edui-icon { - background-position: -500px 0; -} - -.edui-default .edui-for-code .edui-icon { - background-position: -440px -40px; -} - -.edui-default .edui-for-insertimage .edui-icon { - background-position: -726px -77px; -} - -.edui-default .edui-for-insertframe .edui-icon { - background-position: -240px -40px; -} - -.edui-default .edui-for-emoticon .edui-icon { - background-position: -60px -20px; -} - -.edui-default .edui-for-spechars .edui-icon { - background-position: -240px 0; -} - -.edui-default .edui-for-help .edui-icon { - background-position: -340px 0; -} - -.edui-default .edui-for-print .edui-icon { - background-position: -440px -20px; -} - -.edui-default .edui-for-preview .edui-icon { - background-position: -420px -20px; -} - -.edui-default .edui-for-selectall .edui-icon { - background-position: -400px -20px; -} - -.edui-default .edui-for-searchreplace .edui-icon { - background-position: -520px -20px; -} - -.edui-default .edui-for-map .edui-icon { - background-position: -40px -40px; -} - -.edui-default .edui-for-gmap .edui-icon { - background-position: -260px -40px; -} - -.edui-default .edui-for-insertvideo .edui-icon { - background-position: -320px -20px; -} - -.edui-default .edui-for-time .edui-icon { - background-position: -160px -20px; -} - -.edui-default .edui-for-date .edui-icon { - background-position: -140px -20px; -} - -.edui-default .edui-for-cut .edui-icon { - background-position: -680px 0; -} - -.edui-default .edui-for-copy .edui-icon { - background-position: -700px 0; -} - -.edui-default .edui-for-paste .edui-icon { - background-position: -560px 0; -} - -.edui-default .edui-for-formatmatch .edui-icon { - background-position: -40px 0; -} - -.edui-default .edui-for-pasteplain .edui-icon { - background-position: -360px -20px; -} - -.edui-default .edui-for-directionalityltr .edui-icon { - background-position: -20px -20px; -} - -.edui-default .edui-for-directionalityrtl .edui-icon { - background-position: -40px -20px; -} - -.edui-default .edui-for-source .edui-icon { - background-position: -261px -0px; -} - -.edui-default .edui-for-removeformat .edui-icon { - background-position: -580px 0; -} - -.edui-default .edui-for-unlink .edui-icon { - background-position: -640px 0; -} - -.edui-default .edui-for-touppercase .edui-icon { - background-position: -786px 0; -} - -.edui-default .edui-for-tolowercase .edui-icon { - background-position: -806px 0; -} - -.edui-default .edui-for-insertrow .edui-icon { - background-position: -478px -76px; -} - -.edui-default .edui-for-insertrownext .edui-icon { - background-position: -498px -76px; -} - -.edui-default .edui-for-insertcol .edui-icon { - background-position: -455px -76px; -} - -.edui-default .edui-for-insertcolnext .edui-icon { - background-position: -429px -76px; -} - -.edui-default .edui-for-mergeright .edui-icon { - background-position: -60px -40px; -} - -.edui-default .edui-for-mergedown .edui-icon { - background-position: -80px -40px; -} - -.edui-default .edui-for-splittorows .edui-icon { - background-position: -100px -40px; -} - -.edui-default .edui-for-splittocols .edui-icon { - background-position: -120px -40px; -} - -.edui-default .edui-for-insertparagraphbeforetable .edui-icon { - background-position: -140px -40px; -} - -.edui-default .edui-for-deleterow .edui-icon { - background-position: -660px -20px; -} - -.edui-default .edui-for-deletecol .edui-icon { - background-position: -640px -20px; -} - -.edui-default .edui-for-splittocells .edui-icon { - background-position: -800px -20px; -} - -.edui-default .edui-for-mergecells .edui-icon { - background-position: -760px -20px; -} - -.edui-default .edui-for-deletetable .edui-icon { - background-position: -620px -20px; -} - -.edui-default .edui-for-cleardoc .edui-icon { - background-position: -520px 0; -} - -.edui-default .edui-for-fullscreen .edui-icon { - background-position: -100px -20px; -} - -.edui-default .edui-for-anchor .edui-icon { - background-position: -200px 0; -} - -.edui-default .edui-for-pagebreak .edui-icon { - background-position: -460px -40px; -} - -.edui-default .edui-for-imagenone .edui-icon { - background-position: -480px -40px; -} - -.edui-default .edui-for-imageleft .edui-icon { - background-position: -500px -40px; -} - -.edui-default .edui-for-wordimage .edui-icon { - background-position: -660px -40px; -} - -.edui-default .edui-for-imageright .edui-icon { - background-position: -520px -40px; -} - -.edui-default .edui-for-imagecenter .edui-icon { - background-position: -540px -40px; -} - -.edui-default .edui-for-indent .edui-icon { - background-position: -400px 0; -} - -.edui-default .edui-for-outdent .edui-icon { - background-position: -540px 0; -} - -.edui-default .edui-for-webapp .edui-icon { - background-position: -601px -40px -} - -.edui-default .edui-for-table .edui-icon { - background-position: -580px -20px; -} - -.edui-default .edui-for-edittable .edui-icon { - background-position: -420px -40px; -} - -.edui-default .edui-for-template .edui-icon { - background-position: -339px -40px; -} - -.edui-default .edui-for-delete .edui-icon { - background-position: -360px -40px; -} - -.edui-default .edui-for-attachment .edui-icon { - background-position: -620px -40px; -} - -.edui-default .edui-for-edittd .edui-icon { - background-position: -700px -40px; -} - -.edui-default .edui-for-snapscreen .edui-icon { - background-position: -581px -40px -} - -.edui-default .edui-for-scrawl .edui-icon { - background-position: -801px -41px -} - -.edui-default .edui-for-background .edui-icon { - background-position: -680px -40px; -} - -.edui-default .edui-for-music .edui-icon { - background-position: -18px -40px -} - -.edui-default .edui-for-formula .edui-icon { - background-position: -200px -40px -} - -.edui-default .edui-for-aligntd .edui-icon { - background-position: -236px -76px; -} - -.edui-default .edui-for-insertparagraphtrue .edui-icon { - background-position: -625px -76px; -} - -.edui-default .edui-for-insertparagraph .edui-icon { - background-position: -602px -76px; -} - -.edui-default .edui-for-insertcaption .edui-icon { - background-position: -336px -76px; -} - -.edui-default .edui-for-deletecaption .edui-icon { - background-position: -362px -76px; -} - -.edui-default .edui-for-inserttitle .edui-icon { - background-position: -286px -76px; -} - -.edui-default .edui-for-deletetitle .edui-icon { - background-position: -311px -76px; -} - -.edui-default .edui-for-aligntable .edui-icon { - background-position: -440px 0; -} - -.edui-default .edui-for-tablealignment-left .edui-icon { - background-position: -460px 0; -} - -.edui-default .edui-for-tablealignment-center .edui-icon { - background-position: -420px 0; -} - -.edui-default .edui-for-tablealignment-right .edui-icon { - background-position: -480px 0; -} - -.edui-default .edui-for-drafts .edui-icon { - background-position: -560px 0; -} - -.edui-default .edui-for-charts .edui-icon { - background: url( ../images/charts.png ) no-repeat 2px 3px!important; -} - -.edui-default .edui-for-inserttitlecol .edui-icon { - background-position: -673px -76px; -} - -.edui-default .edui-for-deletetitlecol .edui-icon { - background-position: -698px -76px; -} - -.edui-default .edui-for-simpleupload .edui-icon { - background-position: -380px 0px; -} -/*splitbutton*/ -.edui-default .edui-toolbar .edui-splitbutton-body .edui-arrow, -.edui-default .edui-toolbar .edui-menubutton-body .edui-arrow { - background: url(../images/icons.png) -741px 0; - _background: url(../images/icons.gif) -741px 0; - height: 20px; - width: 9px; -} - -.edui-default .edui-toolbar .edui-splitbutton .edui-splitbutton-body, -.edui-default .edui-toolbar .edui-menubutton .edui-menubutton-body { - padding: 1px; -} - -.edui-default .edui-toolbar .edui-splitborder { - width: 1px; - height: 20px; -} - -.edui-default .edui-toolbar .edui-state-hover .edui-splitborder { - width: 1px; - border-left: 0px solid #dcac6c; -} - -.edui-default .edui-toolbar .edui-state-active .edui-splitborder { - width: 0; - border-left: 1px solid gray; -} - -.edui-default .edui-toolbar .edui-state-opened .edui-splitborder { - width: 1px; - border: 0; -} - -.edui-default .edui-toolbar .edui-splitbutton .edui-state-hover .edui-splitbutton-body, -.edui-default .edui-toolbar .edui-menubutton .edui-state-hover .edui-menubutton-body { - background-color: #fff5d4; - border: 1px solid #dcac6c; - padding: 0; -} - -.edui-default .edui-toolbar .edui-splitbutton .edui-state-checked .edui-splitbutton-body, -.edui-default .edui-toolbar .edui-menubutton .edui-state-checked .edui-menubutton-body { - background-color: #FFE69F; - border: 1px solid #DCAC6C; - padding: 0; -} - -.edui-default .edui-toolbar .edui-splitbutton .edui-state-active .edui-splitbutton-body, -.edui-default .edui-toolbar .edui-menubutton .edui-state-active .edui-menubutton-body { - background-color: #ffffff; - border: 1px solid gray; - padding: 0; -} - -.edui-default .edui-state-disabled .edui-arrow { - opacity: 0.3; - _filter: alpha(opacity = 30); -} - -.edui-default .edui-toolbar .edui-splitbutton .edui-state-opened .edui-splitbutton-body, -.edui-default .edui-toolbar .edui-menubutton .edui-state-opened .edui-menubutton-body { - background-color: white; - border: 1px solid gray; - padding: 0; -} - -.edui-default .edui-for-insertorderedlist .edui-bordereraser, -.edui-default .edui-for-lineheight .edui-bordereraser, -.edui-default .edui-for-rowspacingtop .edui-bordereraser, -.edui-default .edui-for-rowspacingbottom .edui-bordereraser, -.edui-default .edui-for-insertunorderedlist .edui-bordereraser { - background-color: white; -} - -/* 解决嵌套导致的图标问题 */ -.edui-default .edui-for-insertorderedlist .edui-popup-body .edui-icon, -.edui-default .edui-for-lineheight .edui-popup-body .edui-icon, -.edui-default .edui-for-rowspacingtop .edui-popup-body .edui-icon, -.edui-default .edui-for-rowspacingbottom .edui-popup-body .edui-icon, -.edui-default .edui-for-insertunorderedlist .edui-popup-body .edui-icon { - /*background-position: 0 -40px;*/ - background-image: none ; -} - -/* 弹出菜单 */ -.edui-default .edui-popup { - z-index: 3000; - background-color: #ffffff; - width:auto; - height:auto; - -} - -.edui-default .edui-popup .edui-shadow { - left: 0; - top: 0; - width: 100%; - height: 100%; -} - -.edui-default .edui-popup-content { - border:1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - *border-right-width: 2px; - *border-bottom-width: 2px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2); - box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; - padding: 5px; - background:#ffffff; -} - -.edui-default .edui-popup .edui-bordereraser { - background-color: white; - height: 3px; -} - -.edui-default .edui-menu .edui-bordereraser { - height: 3px; -} - -.edui-default .edui-anchor-topleft .edui-bordereraser { - left: 1px; - top: -2px; -} - -.edui-default .edui-anchor-topright .edui-bordereraser { - right: 1px; - top: -2px; -} - -.edui-default .edui-anchor-bottomleft .edui-bordereraser { - left: 0; - bottom: -6px; - height: 7px; - border-left: 1px solid gray; - border-right: 1px solid gray; -} - -.edui-default .edui-anchor-bottomright .edui-bordereraser { - right: 0; - bottom: -6px; - height: 7px; - border-left: 1px solid gray; - border-right: 1px solid gray; -} - -.edui-popup div{ - width:auto; - height:auto; -} -.edui-default .edui-editor-messageholder { - display: block; - width: 150px; - height: auto; - border: 0; - margin: 0; - padding: 0; - position: absolute; - top: 28px; - right: 3px; -} - -.edui-default .edui-message{ - min-height: 10px; - text-shadow: 0 1px 0 rgba(255,255,255,0.5); - padding: 0; - margin-bottom: 3px; - position: relative; -} -.edui-default .edui-message-body{ - border-radius: 3px; - padding: 8px 15px 8px 8px; - color: #c09853; - background-color: #fcf8e3; - border: 1px solid #fbeed5; -} -.edui-default .edui-message-type-info{ - color: #3a87ad; - background-color: #d9edf7; - border-color: #bce8f1 -} -.edui-default .edui-message-type-success{ - color: #468847; - background-color: #dff0d8; - border-color: #d6e9c6 -} -.edui-default .edui-message-type-danger, -.edui-default .edui-message-type-error{ - color: #b94a48; - background-color: #f2dede; - border-color: #eed3d7 -} -.edui-default .edui-message .edui-message-closer { - display: block; - width: 16px; - height: 16px; - line-height: 16px; - position: absolute; - top: 0; - right: 0; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - float: right; - font-size: 20px; - font-weight: bold; - color: #999; - text-shadow: 0 1px 0 #fff; - font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; -} -.edui-default .edui-message .edui-message-content { - font-size: 10pt; - word-wrap: break-word; - word-break: normal; -} -/* 弹出对话框按钮和对话框大小 */ -.edui-default .edui-dialog { - z-index: 2000; - position: absolute; - -} - -.edui-dialog div{ - width:auto; -} - -.edui-default .edui-dialog-wrap { - margin-right: 6px; - margin-bottom: 6px; -} - -.edui-default .edui-dialog-fullscreen-flag { - margin-right: 0; - margin-bottom: 0; -} - -.edui-default .edui-dialog-body { - position: relative; - padding:2px 0 0 2px; - _zoom: 1; -} - -.edui-default .edui-dialog-fullscreen-flag .edui-dialog-body { - padding: 0; -} - -.edui-default .edui-dialog-shadow { - position: absolute; - z-index: -1; - left: 0; - top: 0; - width: 100%; - height: 100%; - background-color: #ffffff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - *border-right-width: 2px; - *border-bottom-width: 2px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; -} - -.edui-default .edui-dialog-foot { - background-color: white; -} - -.edui-default .edui-dialog-titlebar { - height: 26px; - border-bottom: 1px solid #c6c6c6; - background: url(../images/dialog-title-bg.png) repeat-x bottom; - position: relative; - cursor: move; -} -.edui-default .edui-dialog-caption { - font-weight: bold; - font-size: 12px; - line-height: 26px; - padding-left: 5px; -} - -.edui-default .edui-dialog-draghandle { - height: 26px; -} - -.edui-default .edui-dialog-closebutton { - position: absolute !important; - right: 5px; - top: 3px; -} - -.edui-default .edui-dialog-closebutton .edui-button-body { - height: 20px; - width: 20px; - cursor: pointer; - background: url("../images/icons-all.gif") no-repeat 0 -59px; -} - -.edui-default .edui-dialog-closebutton .edui-state-hover .edui-button-body { - background: url("../images/icons-all.gif") no-repeat 0 -89px; -} - -.edui-default .edui-dialog-foot { - height: 40px; -} - -.edui-default .edui-dialog-buttons { - position: absolute; - right: 0; -} - -.edui-default .edui-dialog-buttons .edui-button { - margin-right: 10px; -} - -.edui-default .edui-dialog-buttons .edui-button .edui-button-body { - background: url("../images/icons-all.gif") no-repeat; - height: 24px; - width: 96px; - font-size: 12px; - line-height: 24px; - text-align: center; - cursor: default; -} - -.edui-default .edui-dialog-buttons .edui-button .edui-state-hover .edui-button-body { - background: url("../images/icons-all.gif") no-repeat 0 -30px; -} - -.edui-default .edui-dialog iframe { - border: 0; - padding: 0; - margin: 0; - vertical-align: top; -} - -.edui-default .edui-dialog-modalmask { - opacity: 0.3; - filter: alpha(opacity = 30); - background-color: #ccc; - position: absolute; - /*z-index: 1999;*/ -} - -.edui-default .edui-dialog-dragmask { - position: absolute; - /*z-index: 2001;*/ - background-color: transparent; - cursor: move; -} - -.edui-default .edui-dialog-content { - position: relative; -} - -.edui-default .dialogcontmask { - cursor: move; - visibility: hidden; - display: block; - position: absolute; - width: 100%; - height: 100%; - opacity: 0; - filter: alpha(opacity = 0); -} - -/*link-dialog*/ -.edui-default .edui-for-link .edui-dialog-content { - width: 420px; - height: 200px; - overflow: hidden; -} -/*background-dialog*/ -.edui-default .edui-for-background .edui-dialog-content { - width: 440px; - height: 280px; - overflow: hidden; -} - -/*template-dialog*/ -.edui-default .edui-for-template .edui-dialog-content { - width: 630px; - height: 390px; - overflow: hidden; -} - -/*scrawl-dialog*/ -.edui-default .edui-for-scrawl .edui-dialog-content { - width: 515px; - *width: 506px; - height: 360px; -} - -/*spechars-dialog*/ -.edui-default .edui-for-spechars .edui-dialog-content { - width: 620px; - height: 500px; - *width: 630px; - *height: 570px; -} - -/*image-dialog*/ -.edui-default .edui-for-insertimage .edui-dialog-content { - width: 650px; - height: 400px; - overflow: hidden; -} -/*webapp-dialog*/ -.edui-default .edui-for-webapp .edui-dialog-content { - width: 560px; - _width: 565px; - height: 450px; - overflow: hidden; -} - -/*image-insertframe*/ -.edui-default .edui-for-insertframe .edui-dialog-content { - width: 350px; - height: 200px; - overflow: hidden; -} - -/*wordImage-dialog*/ -.edui-default .edui-for-wordimage .edui-dialog-content { - width: 620px; - height: 380px; - overflow: hidden; -} - -/*attachment-dialog*/ -.edui-default .edui-for-attachment .edui-dialog-content { - width: 650px; - height: 400px; - overflow: hidden; -} - - -/*map-dialog*/ -.edui-default .edui-for-map .edui-dialog-content { - width: 550px; - height: 400px; -} - -/*gmap-dialog*/ -.edui-default .edui-for-gmap .edui-dialog-content { - width: 550px; - height: 400px; -} - -/*video-dialog*/ -.edui-default .edui-for-insertvideo .edui-dialog-content { - width: 590px; - height: 390px; -} - -/*anchor-dialog*/ -.edui-default .edui-for-anchor .edui-dialog-content { - width: 320px; - height: 60px; - overflow: hidden; -} - -/*searchreplace-dialog*/ -.edui-default .edui-for-searchreplace .edui-dialog-content { - width: 400px; - height: 220px; -} - -/*help-dialog*/ -.edui-default .edui-for-help .edui-dialog-content { - width: 400px; - height: 420px; -} - -/*edittable-dialog*/ -.edui-default .edui-for-edittable .edui-dialog-content { - width: 540px; - _width:590px; - height: 335px; -} - -/*edittip-dialog*/ -.edui-default .edui-for-edittip .edui-dialog-content { - width: 225px; - height: 60px; -} - -/*edittd-dialog*/ -.edui-default .edui-for-edittd .edui-dialog-content { - width: 240px; - height: 50px; -} -/*snapscreen-dialog*/ -.edui-default .edui-for-snapscreen .edui-dialog-content { - width: 400px; - height: 220px; -} - -/*music-dialog*/ -.edui-default .edui-for-music .edui-dialog-content { - width: 515px; - height: 360px; -} - -/*段落弹出菜单*/ -.edui-default .edui-for-paragraph .edui-listitem-label { - font-family: Tahoma, Verdana, Arial, Helvetica; -} - -.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-p { - font-size: 22px; - line-height: 27px; -} - -.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h1 { - font-weight: bolder; - font-size: 32px; - line-height: 36px; -} - -.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h2 { - font-weight: bolder; - font-size: 27px; - line-height: 29px; -} - -.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h3 { - font-weight: bolder; - font-size: 19px; - line-height: 23px; -} - -.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h4 { - font-weight: bolder; - font-size: 16px; - line-height: 19px -} - -.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h5 { - font-weight: bolder; - font-size: 13px; - line-height: 16px; -} - -.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h6 { - font-weight: bolder; - font-size: 12px; - line-height: 14px; -} -/* 表格弹出菜单 */ -.edui-default .edui-for-inserttable .edui-splitborder { - display: none -} -.edui-default .edui-for-inserttable .edui-splitbutton-body .edui-arrow { - width: 0 -} -.edui-default .edui-toolbar .edui-for-inserttable .edui-state-active .edui-splitborder{ - border-left: 1px solid transparent; -} -.edui-default .edui-tablepicker .edui-infoarea { - height: 14px; - line-height: 14px; - font-size: 12px; - width: 220px; - margin-bottom: 3px; - clear: both; -} - -.edui-default .edui-tablepicker .edui-infoarea .edui-label { - float: left; -} - -.edui-default .edui-dialog-buttons .edui-label { - line-height: 24px; -} - -.edui-default .edui-tablepicker .edui-infoarea .edui-clickable { - float: right; -} - -.edui-default .edui-tablepicker .edui-pickarea { - background: url("../images/unhighlighted.gif") repeat; - height: 220px; - width: 220px; -} - -.edui-default .edui-tablepicker .edui-pickarea .edui-overlay { - background: url("../images/highlighted.gif") repeat; -} - -/* 颜色弹出菜单 */ -.edui-default .edui-colorpicker-topbar { - height: 27px; - width: 200px; - /*border-bottom: 1px gray dashed;*/ -} - -.edui-default .edui-colorpicker-preview { - height: 20px; - border: 1px inset black; - margin-left: 1px; - width: 128px; - float: left; -} - -.edui-default .edui-colorpicker-nocolor { - float: right; - margin-right: 1px; - font-size: 12px; - line-height: 14px; - height: 14px; - border: 1px solid #333; - padding: 3px 5px; - cursor: pointer; -} - -.edui-default .edui-colorpicker-tablefirstrow { - height: 30px; -} - -.edui-default .edui-colorpicker-colorcell { - width: 14px; - height: 14px; - display: block; - margin: 0; - cursor: pointer; -} - -.edui-default .edui-colorpicker-colorcell:hover { - width: 14px; - height: 14px; - margin: 0; -} -.edui-default .edui-colorpicker-advbtn{ - display: block; - text-align: center; - cursor: pointer; - height:20px; -} -.arrow_down{ - background: white url('../images/arrow_down.png') no-repeat center; -} -.arrow_up{ - background: white url('../images/arrow_up.png') no-repeat center; -} -/*高级的样式*/ -.edui-colorpicker-adv{ - position: relative; - overflow: hidden; - height: 180px; - display: none; -} -.edui-colorpicker-plant, .edui-colorpicker-hue { - border: solid 1px #666; -} -.edui-colorpicker-pad { - width: 150px; - height: 150px; - left: 14px; - top: 13px; - position: absolute; - background: red; - overflow: hidden; - cursor: crosshair; -} -.edui-colorpicker-cover{ - position: absolute; - top: 0; - left: 0; - width: 150px; - height: 150px; - background: url("../images/tangram-colorpicker.png") -160px -200px; -} -.edui-colorpicker-padDot{ - position: absolute; - top: 0; - left: 0; - width: 11px; - height: 11px; - overflow: hidden; - background: url(../images/tangram-colorpicker.png) 0px -200px repeat-x; - z-index: 1000; - -} -.edui-colorpicker-sliderMain { - position: absolute; - left: 171px; - top: 13px; - width: 19px; - height: 152px; - background: url(../images/tangram-colorpicker.png) -179px -12px no-repeat; - -} -.edui-colorpicker-slider { - width: 100%; - height: 100%; - cursor: pointer; -} -.edui-colorpicker-thumb{ - position: absolute; - top: 0; - cursor: pointer; - height: 3px; - left: -1px; - right: -1px; - border: 1px solid black; - background: white; - opacity: .8; -} -/*自动排版弹出菜单*/ -.edui-default .edui-autotypesetpicker .edui-autotypesetpicker-body { - font-size: 12px; - margin-bottom: 3px; - clear: both; -} - -.edui-default .edui-autotypesetpicker-body table { - border-collapse: separate; - border-spacing: 2px; -} - -.edui-default .edui-autotypesetpicker-body td { - font-size: 12px; - word-wrap:break-word; -} - -.edui-default .edui-autotypesetpicker-body td input { - margin: 3px 3px 3px 4px; - *margin: 1px 0 0 0; -} -/*自动排版弹出菜单*/ -.edui-default .edui-cellalignpicker .edui-cellalignpicker-body { - width: 70px; - font-size: 12px; - cursor: default; -} - -.edui-default .edui-cellalignpicker-body table { - border-collapse: separate; - border-spacing: 0; -} -.edui-default .edui-cellalignpicker-body td{ - padding: 1px; -} -.edui-default .edui-cellalignpicker-body .edui-icon{ - height: 20px; - width: 20px; - padding: 1px; - background-image: url(../images/table-cell-align.png); -} - -.edui-default .edui-cellalignpicker-body .edui-left{ - background-position: 0 0; -} - -.edui-default .edui-cellalignpicker-body .edui-center{ - background-position: -25px 0; -} -.edui-default .edui-cellalignpicker-body .edui-right{ - background-position: -51px 0; -} - -.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-left{ - background-position: -73px 0; -} - -.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-center{ - background-position: -98px 0; -} - -.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-right{ - background-position: -124px 0; -} - -.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-left { - background-position: -146px 0; - background-color: #f1f4f5; -} - -.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-center { - background-position: -245px 0; -} - -.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-right { - background-position: -271px 0; -} -/*分隔线*/ -.edui-default .edui-toolbar .edui-separator { - width: 2px; - height: 20px; - margin: 2px 4px 2px 3px; - background: url(../images/icons.png) -181px 0; - background: url(../images/icons.gif) -181px 0 \9; -} - -/*颜色按钮 */ -.edui-default .edui-toolbar .edui-colorbutton .edui-colorlump { - position: absolute; - overflow: hidden; - bottom: 1px; - left: 1px; - width: 18px; - height: 4px; -} -/*表情按钮及弹出菜单*/ -/*去除了表情的下拉箭头*/ -.edui-default .edui-for-emotion .edui-icon { - background-position: -60px -20px; -} -.edui-default .edui-for-emotion .edui-popup-content iframe -{ - width: 514px; - height: 380px; - overflow: hidden; -} -.edui-default .edui-for-emotion .edui-popup-content -{ - position: relative; - z-index: 555 -} - -.edui-default .edui-for-emotion .edui-splitborder { - display: none -} - -.edui-default .edui-for-emotion .edui-splitbutton-body .edui-arrow -{ - width: 0 -} -.edui-default .edui-toolbar .edui-for-emotion .edui-state-active .edui-splitborder -{ - border-left: 1px solid transparent; -} -/*contextmenu*/ -.edui-default .edui-hassubmenu .edui-arrow { - height: 20px; - width: 20px; - float: right; - background: url("../images/icons-all.gif") no-repeat 10px -233px; -} - -.edui-default .edui-menu-body .edui-menuitem { - padding: 1px; -} - -.edui-default .edui-menuseparator { - margin: 2px 0; - height: 1px; - overflow: hidden; -} - -.edui-default .edui-menuseparator-inner { - border-bottom: 1px solid #e2e3e3; - margin-left: 29px; - margin-right: 1px; -} - -.edui-default .edui-menu-body .edui-state-hover { - padding: 0 !important; - background-color: #fff5d4; - border: 1px solid #dcac6c; -} -/*弹出菜单*/ -.edui-default .edui-shortcutmenu { - padding: 2px; - width: 190px; - height: 50px; - background-color: #fff; - border: 1px solid #ccc; - border-radius: 5px; -} - -/*粘贴弹出菜单*/ -.edui-default .edui-wordpastepop .edui-popup-content{ - border: none; - padding: 0; - width: 54px; - height: 21px; -} -.edui-default .edui-pasteicon { - width: 100%; - height: 100%; - background-image: url('../images/wordpaste.png'); - background-position: 0 0; -} - -.edui-default .edui-pasteicon.edui-state-opened { - background-position: 0 -34px; -} - -.edui-default .edui-pastecontainer { - position: relative; - visibility: hidden; - width: 97px; - background: #fff; - border: 1px solid #ccc; -} - -.edui-default .edui-pastecontainer .edui-title { - font-weight: bold; - background: #F8F8FF; - height: 25px; - line-height: 25px; - font-size: 12px; - padding-left: 5px; -} - -.edui-default .edui-pastecontainer .edui-button { - overflow: hidden; - margin: 3px 0; -} - -.edui-default .edui-pastecontainer .edui-button .edui-richtxticon, -.edui-default .edui-pastecontainer .edui-button .edui-tagicon, -.edui-default .edui-pastecontainer .edui-button .edui-plaintxticon{ - float: left; - cursor: pointer; - width: 29px; - height: 29px; - margin-left: 5px; - background-image: url('../images/wordpaste.png'); - background-repeat: no-repeat; -} -.edui-default .edui-pastecontainer .edui-button .edui-richtxticon { - margin-left: 0; - background-position: -109px 0; -} -.edui-default .edui-pastecontainer .edui-button .edui-tagicon { - background-position: -148px 1px; -} - -.edui-default .edui-pastecontainer .edui-button .edui-plaintxticon { - background-position: -72px 0; -} - -.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-richtxticon { - background-position: -109px -34px; -} -.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-tagicon{ - background-position: -148px -34px; -} -.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-plaintxticon{ - background-position: -72px -34px; -} \ No newline at end of file diff --git a/static/plugs/ueditor/themes/default/css/ueditor.min.css b/static/plugs/ueditor/themes/default/css/ueditor.min.css deleted file mode 100644 index 63d297c7f..000000000 --- a/static/plugs/ueditor/themes/default/css/ueditor.min.css +++ /dev/null @@ -1,8 +0,0 @@ -/*! - * UEditor - * version: ueditor - * build: Tue Aug 25 2015 15:23:01 GMT+0800 (CST) - */ - - -.edui-default .edui-box{border:0;padding:0;margin:0;overflow:hidden}.edui-default a.edui-box{display:block;text-decoration:none;color:#000}.edui-default a.edui-box:hover{text-decoration:none}.edui-default a.edui-box:active{text-decoration:none}.edui-default table.edui-box{border-collapse:collapse}.edui-default ul.edui-box{list-style-type:none}div.edui-box{position:relative;display:-moz-inline-box!important;display:inline-block!important;vertical-align:top}.edui-default .edui-clearfix{zoom:1}.edui-default .edui-clearfix:after{content:'\20';display:block;clear:both}* html div.edui-box{display:inline!important}:first-child+html div.edui-box{display:inline!important}.edui-default .edui-button-body,.edui-splitbutton-body,.edui-menubutton-body,.edui-combox-body{position:relative}.edui-default .edui-popup{position:absolute;-webkit-user-select:none;-moz-user-select:none}.edui-default .edui-popup .edui-shadow{position:absolute;z-index:-1}.edui-default .edui-popup .edui-bordereraser{position:absolute;overflow:hidden}.edui-default .edui-tablepicker .edui-canvas{position:relative}.edui-default .edui-tablepicker .edui-canvas .edui-overlay{position:absolute}.edui-default .edui-dialog-modalmask,.edui-dialog-dragmask{position:absolute;left:0;top:0;width:100%;height:100%}.edui-default .edui-toolbar{position:relative}.edui-default .edui-label{cursor:default}.edui-default span.edui-clickable{color:#00f;cursor:pointer;text-decoration:underline}.edui-default span.edui-unclickable{color:gray;cursor:default}.edui-default .edui-toolbar{cursor:default;-webkit-user-select:none;-moz-user-select:none;padding:1px;overflow:hidden;zoom:1;width:auto;height:auto}.edui-default .edui-toolbar .edui-button,.edui-default .edui-toolbar .edui-splitbutton,.edui-default .edui-toolbar .edui-menubutton,.edui-default .edui-toolbar .edui-combox{margin:1px}.edui-default .edui-editor{border:1px solid #d4d4d4;background-color:#fff;position:relative;overflow:visible;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.edui-editor div{width:auto;height:auto}.edui-default .edui-editor-toolbarbox{position:relative;zoom:1;-webkit-box-shadow:0 1px 4px rgba(204,204,204,.6);-moz-box-shadow:0 1px 4px rgba(204,204,204,.6);box-shadow:0 1px 4px rgba(204,204,204,.6);border-top-left-radius:2px;border-top-right-radius:2px}.edui-default .edui-editor-toolbarboxouter{border-bottom:1px solid #d4d4d4;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,.065);box-shadow:0 1px 4px rgba(0,0,0,.065)}.edui-default .edui-editor-toolbarboxinner{padding:2px}.edui-default .edui-editor-iframeholder{position:relative}.edui-default .edui-editor-bottomContainer{overflow:hidden}.edui-default .edui-editor-bottomContainer table{width:100%;height:0;overflow:hidden;border-spacing:0}.edui-default .edui-editor-bottomContainer td{white-space:nowrap;border-top:1px solid #ccc;line-height:20px;font-size:12px;font-family:Arial,Helvetica,Tahoma,Verdana,Sans-Serif}.edui-default .edui-editor-wordcount{text-align:right;margin-right:5px;color:#aaa}.edui-default .edui-editor-scale{width:12px}.edui-default .edui-editor-scale .edui-editor-icon{float:right;width:100%;height:12px;margin-top:10px;background:url(../images/scale.png) no-repeat;cursor:se-resize}.edui-default .edui-editor-breadcrumb{margin:2px 0 0 3px}.edui-default .edui-editor-breadcrumb span{cursor:pointer;text-decoration:underline;color:#00f}.edui-default .edui-toolbar .edui-for-fullscreen{float:right}.edui-default .edui-bubble .edui-popup-content{border:1px solid #DCAC6C;background-color:#fff6d9;padding:5px;font-size:10pt;font-family:"宋体"}.edui-default .edui-bubble .edui-shadow{}.edui-default .edui-editor-toolbarmsg{background-color:#FFF6D9;border-bottom:1px solid #ccc;position:absolute;bottom:-25px;left:0;z-index:1009;width:99.9%}.edui-default .edui-editor-toolbarmsg-upload{font-size:14px;color:#00f;width:100px;height:16px;line-height:16px;cursor:pointer;position:absolute;top:5px;left:350px}.edui-default .edui-editor-toolbarmsg-label{font-size:12px;line-height:16px;padding:4px}.edui-default .edui-editor-toolbarmsg-close{float:right;width:20px;height:16px;line-height:16px;cursor:pointer;color:red}.edui-default .edui-list .edui-bordereraser{display:none}.edui-default .edui-listitem{padding:1px;white-space:nowrap}.edui-default .edui-list .edui-state-hover{position:relative;background-color:#fff5d4;border:1px solid #dcac6c;padding:0}.edui-default .edui-for-fontfamily .edui-listitem-label{min-width:130px;_width:120px;font-size:12px;height:22px;line-height:22px;padding-left:5px}.edui-default .edui-for-insertcode .edui-listitem-label{min-width:120px;_width:120px;font-size:12px;height:22px;line-height:22px;padding-left:5px}.edui-default .edui-for-underline .edui-listitem-label{min-width:120px;_width:120px;padding:3px 5px;font-size:12px}.edui-default .edui-for-fontsize .edui-listitem-label{min-width:120px;_width:120px;padding:3px 5px}.edui-default .edui-for-paragraph .edui-listitem-label{min-width:200px;_width:200px;padding:2px 5px}.edui-default .edui-for-rowspacingtop .edui-listitem-label,.edui-default .edui-for-rowspacingbottom .edui-listitem-label{min-width:53px;_width:53px;padding:2px 5px}.edui-default .edui-for-lineheight .edui-listitem-label{min-width:53px;_width:53px;padding:2px 5px}.edui-default .edui-for-customstyle .edui-listitem-label{min-width:200px;_width:200px;width:200px!important;padding:2px 5px}.edui-default .edui-menu{z-index:3000}.edui-default .edui-menu .edui-popup-content{padding:3px}.edui-default .edui-menu-body{_width:150px;min-width:170px;background:url(../images/sparator_v.png) repeat-y 25px}.edui-default .edui-menuitem-body{}.edui-default .edui-menuitem{height:20px;cursor:default;vertical-align:top}.edui-default .edui-menuitem .edui-icon{width:20px!important;height:20px!important;background:url(../images/icons.png) 0 -4000px;background:url(../images/icons.gif) 0 -4000px\9}.edui-default .edui-menuitem .edui-label{font-size:12px;line-height:20px;height:20px;padding-left:10px}.edui-default .edui-state-checked .edui-menuitem-body{background:url(../images/icons-all.gif) no-repeat 6px -205px}.edui-default .edui-state-disabled .edui-menuitem-label{color:gray}.edui-default .edui-toolbar .edui-combox-body .edui-button-body{width:60px;font-size:12px;height:20px;line-height:20px;padding-left:5px;white-space:nowrap;margin:0 3px 0 0}.edui-default .edui-toolbar .edui-combox-body .edui-arrow{background:url(../images/icons.png) -741px 0;_background:url(../images/icons.gif) -741px 0;height:20px;width:9px}.edui-default .edui-toolbar .edui-combox .edui-combox-body{border:1px solid #CCC;background-color:#fff;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px}.edui-default .edui-toolbar .edui-combox-body .edui-splitborder{display:none}.edui-default .edui-toolbar .edui-combox-body .edui-arrow{border-left:1px solid #CCC}.edui-default .edui-toolbar .edui-state-hover .edui-combox-body{background-color:#fff5d4;border:1px solid #dcac6c}.edui-default .edui-toolbar .edui-state-hover .edui-combox-body .edui-arrow{border-left:1px solid #dcac6c}.edui-default .edui-toolbar .edui-state-checked .edui-combox-body{background-color:#FFE69F;border:1px solid #DCAC6C}.edui-toolbar .edui-state-checked .edui-combox-body .edui-arrow{border-left:1px solid #DCAC6C}.edui-toolbar .edui-state-disabled .edui-combox-body{background-color:#F0F0EE;opacity:.3;filter:alpha(opacity=30)}.edui-toolbar .edui-state-opened .edui-combox-body{background-color:#fff;border:1px solid gray}.edui-default .edui-toolbar .edui-button .edui-icon,.edui-default .edui-toolbar .edui-menubutton .edui-icon,.edui-default .edui-toolbar .edui-splitbutton .edui-icon{height:20px!important;width:20px!important;background-image:url(../images/icons.png);background-image:url(../images/icons.gif) \9}.edui-default .edui-toolbar .edui-button .edui-button-wrap{padding:1px;position:relative}.edui-default .edui-toolbar .edui-button .edui-state-hover .edui-button-wrap{background-color:#fff5d4;padding:0;border:1px solid #dcac6c}.edui-default .edui-toolbar .edui-button .edui-state-checked .edui-button-wrap{background-color:#ffe69f;padding:0;border:1px solid #dcac6c;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px}.edui-default .edui-toolbar .edui-button .edui-state-active .edui-button-wrap{background-color:#fff;padding:0;border:1px solid gray}.edui-default .edui-toolbar .edui-state-disabled .edui-label{color:#ccc}.edui-default .edui-toolbar .edui-state-disabled .edui-icon{opacity:.3;filter:alpha(opacity=30)}.edui-default .edui-for-undo .edui-icon{background-position:-160px 0}.edui-default .edui-for-redo .edui-icon{background-position:-100px 0}.edui-default .edui-for-bold .edui-icon{background-position:0 0}.edui-default .edui-for-italic .edui-icon{background-position:-60px 0}.edui-default .edui-for-fontborder .edui-icon{background-position:-160px -40px}.edui-default .edui-for-underline .edui-icon{background-position:-140px 0}.edui-default .edui-for-strikethrough .edui-icon{background-position:-120px 0}.edui-default .edui-for-subscript .edui-icon{background-position:-600px 0}.edui-default .edui-for-superscript .edui-icon{background-position:-620px 0}.edui-default .edui-for-blockquote .edui-icon{background-position:-220px 0}.edui-default .edui-for-forecolor .edui-icon{background-position:-720px 0}.edui-default .edui-for-backcolor .edui-icon{background-position:-760px 0}.edui-default .edui-for-inserttable .edui-icon{background-position:-580px -20px}.edui-default .edui-for-autotypeset .edui-icon{background-position:-640px -40px}.edui-default .edui-for-justifyleft .edui-icon{background-position:-460px 0}.edui-default .edui-for-justifycenter .edui-icon{background-position:-420px 0}.edui-default .edui-for-justifyright .edui-icon{background-position:-480px 0}.edui-default .edui-for-justifyjustify .edui-icon{background-position:-440px 0}.edui-default .edui-for-insertorderedlist .edui-icon{background-position:-80px 0}.edui-default .edui-for-insertunorderedlist .edui-icon{background-position:-20px 0}.edui-default .edui-for-lineheight .edui-icon{background-position:-725px -40px}.edui-default .edui-for-rowspacingbottom .edui-icon{background-position:-745px -40px}.edui-default .edui-for-rowspacingtop .edui-icon{background-position:-765px -40px}.edui-default .edui-for-horizontal .edui-icon{background-position:-360px 0}.edui-default .edui-for-link .edui-icon{background-position:-500px 0}.edui-default .edui-for-code .edui-icon{background-position:-440px -40px}.edui-default .edui-for-insertimage .edui-icon{background-position:-726px -77px}.edui-default .edui-for-insertframe .edui-icon{background-position:-240px -40px}.edui-default .edui-for-emoticon .edui-icon{background-position:-60px -20px}.edui-default .edui-for-spechars .edui-icon{background-position:-240px 0}.edui-default .edui-for-help .edui-icon{background-position:-340px 0}.edui-default .edui-for-print .edui-icon{background-position:-440px -20px}.edui-default .edui-for-preview .edui-icon{background-position:-420px -20px}.edui-default .edui-for-selectall .edui-icon{background-position:-400px -20px}.edui-default .edui-for-searchreplace .edui-icon{background-position:-520px -20px}.edui-default .edui-for-map .edui-icon{background-position:-40px -40px}.edui-default .edui-for-gmap .edui-icon{background-position:-260px -40px}.edui-default .edui-for-insertvideo .edui-icon{background-position:-320px -20px}.edui-default .edui-for-time .edui-icon{background-position:-160px -20px}.edui-default .edui-for-date .edui-icon{background-position:-140px -20px}.edui-default .edui-for-cut .edui-icon{background-position:-680px 0}.edui-default .edui-for-copy .edui-icon{background-position:-700px 0}.edui-default .edui-for-paste .edui-icon{background-position:-560px 0}.edui-default .edui-for-formatmatch .edui-icon{background-position:-40px 0}.edui-default .edui-for-pasteplain .edui-icon{background-position:-360px -20px}.edui-default .edui-for-directionalityltr .edui-icon{background-position:-20px -20px}.edui-default .edui-for-directionalityrtl .edui-icon{background-position:-40px -20px}.edui-default .edui-for-source .edui-icon{background-position:-261px -0px}.edui-default .edui-for-removeformat .edui-icon{background-position:-580px 0}.edui-default .edui-for-unlink .edui-icon{background-position:-640px 0}.edui-default .edui-for-touppercase .edui-icon{background-position:-786px 0}.edui-default .edui-for-tolowercase .edui-icon{background-position:-806px 0}.edui-default .edui-for-insertrow .edui-icon{background-position:-478px -76px}.edui-default .edui-for-insertrownext .edui-icon{background-position:-498px -76px}.edui-default .edui-for-insertcol .edui-icon{background-position:-455px -76px}.edui-default .edui-for-insertcolnext .edui-icon{background-position:-429px -76px}.edui-default .edui-for-mergeright .edui-icon{background-position:-60px -40px}.edui-default .edui-for-mergedown .edui-icon{background-position:-80px -40px}.edui-default .edui-for-splittorows .edui-icon{background-position:-100px -40px}.edui-default .edui-for-splittocols .edui-icon{background-position:-120px -40px}.edui-default .edui-for-insertparagraphbeforetable .edui-icon{background-position:-140px -40px}.edui-default .edui-for-deleterow .edui-icon{background-position:-660px -20px}.edui-default .edui-for-deletecol .edui-icon{background-position:-640px -20px}.edui-default .edui-for-splittocells .edui-icon{background-position:-800px -20px}.edui-default .edui-for-mergecells .edui-icon{background-position:-760px -20px}.edui-default .edui-for-deletetable .edui-icon{background-position:-620px -20px}.edui-default .edui-for-cleardoc .edui-icon{background-position:-520px 0}.edui-default .edui-for-fullscreen .edui-icon{background-position:-100px -20px}.edui-default .edui-for-anchor .edui-icon{background-position:-200px 0}.edui-default .edui-for-pagebreak .edui-icon{background-position:-460px -40px}.edui-default .edui-for-imagenone .edui-icon{background-position:-480px -40px}.edui-default .edui-for-imageleft .edui-icon{background-position:-500px -40px}.edui-default .edui-for-wordimage .edui-icon{background-position:-660px -40px}.edui-default .edui-for-imageright .edui-icon{background-position:-520px -40px}.edui-default .edui-for-imagecenter .edui-icon{background-position:-540px -40px}.edui-default .edui-for-indent .edui-icon{background-position:-400px 0}.edui-default .edui-for-outdent .edui-icon{background-position:-540px 0}.edui-default .edui-for-webapp .edui-icon{background-position:-601px -40px}.edui-default .edui-for-table .edui-icon{background-position:-580px -20px}.edui-default .edui-for-edittable .edui-icon{background-position:-420px -40px}.edui-default .edui-for-template .edui-icon{background-position:-339px -40px}.edui-default .edui-for-delete .edui-icon{background-position:-360px -40px}.edui-default .edui-for-attachment .edui-icon{background-position:-620px -40px}.edui-default .edui-for-edittd .edui-icon{background-position:-700px -40px}.edui-default .edui-for-snapscreen .edui-icon{background-position:-581px -40px}.edui-default .edui-for-scrawl .edui-icon{background-position:-801px -41px}.edui-default .edui-for-background .edui-icon{background-position:-680px -40px}.edui-default .edui-for-music .edui-icon{background-position:-18px -40px}.edui-default .edui-for-formula .edui-icon{background-position:-200px -40px}.edui-default .edui-for-aligntd .edui-icon{background-position:-236px -76px}.edui-default .edui-for-insertparagraphtrue .edui-icon{background-position:-625px -76px}.edui-default .edui-for-insertparagraph .edui-icon{background-position:-602px -76px}.edui-default .edui-for-insertcaption .edui-icon{background-position:-336px -76px}.edui-default .edui-for-deletecaption .edui-icon{background-position:-362px -76px}.edui-default .edui-for-inserttitle .edui-icon{background-position:-286px -76px}.edui-default .edui-for-deletetitle .edui-icon{background-position:-311px -76px}.edui-default .edui-for-aligntable .edui-icon{background-position:-440px 0}.edui-default .edui-for-tablealignment-left .edui-icon{background-position:-460px 0}.edui-default .edui-for-tablealignment-center .edui-icon{background-position:-420px 0}.edui-default .edui-for-tablealignment-right .edui-icon{background-position:-480px 0}.edui-default .edui-for-drafts .edui-icon{background-position:-560px 0}.edui-default .edui-for-charts .edui-icon{background:url( ../images/charts.png ) no-repeat 2px 3px!important}.edui-default .edui-for-inserttitlecol .edui-icon{background-position:-673px -76px}.edui-default .edui-for-deletetitlecol .edui-icon{background-position:-698px -76px}.edui-default .edui-for-simpleupload .edui-icon{background-position:-380px 0}.edui-default .edui-toolbar .edui-splitbutton-body .edui-arrow,.edui-default .edui-toolbar .edui-menubutton-body .edui-arrow{background:url(../images/icons.png) -741px 0;_background:url(../images/icons.gif) -741px 0;height:20px;width:9px}.edui-default .edui-toolbar .edui-splitbutton .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-menubutton-body{padding:1px}.edui-default .edui-toolbar .edui-splitborder{width:1px;height:20px}.edui-default .edui-toolbar .edui-state-hover .edui-splitborder{width:1px;border-left:0 solid #dcac6c}.edui-default .edui-toolbar .edui-state-active .edui-splitborder{width:0;border-left:1px solid gray}.edui-default .edui-toolbar .edui-state-opened .edui-splitborder{width:1px;border:0}.edui-default .edui-toolbar .edui-splitbutton .edui-state-hover .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-hover .edui-menubutton-body{background-color:#fff5d4;border:1px solid #dcac6c;padding:0}.edui-default .edui-toolbar .edui-splitbutton .edui-state-checked .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-checked .edui-menubutton-body{background-color:#FFE69F;border:1px solid #DCAC6C;padding:0}.edui-default .edui-toolbar .edui-splitbutton .edui-state-active .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-active .edui-menubutton-body{background-color:#fff;border:1px solid gray;padding:0}.edui-default .edui-state-disabled .edui-arrow{opacity:.3;_filter:alpha(opacity=30)}.edui-default .edui-toolbar .edui-splitbutton .edui-state-opened .edui-splitbutton-body,.edui-default .edui-toolbar .edui-menubutton .edui-state-opened .edui-menubutton-body{background-color:#fff;border:1px solid gray;padding:0}.edui-default .edui-for-insertorderedlist .edui-bordereraser,.edui-default .edui-for-lineheight .edui-bordereraser,.edui-default .edui-for-rowspacingtop .edui-bordereraser,.edui-default .edui-for-rowspacingbottom .edui-bordereraser,.edui-default .edui-for-insertunorderedlist .edui-bordereraser{background-color:#fff}.edui-default .edui-for-insertorderedlist .edui-popup-body .edui-icon,.edui-default .edui-for-lineheight .edui-popup-body .edui-icon,.edui-default .edui-for-rowspacingtop .edui-popup-body .edui-icon,.edui-default .edui-for-rowspacingbottom .edui-popup-body .edui-icon,.edui-default .edui-for-insertunorderedlist .edui-popup-body .edui-icon{background-image:none}.edui-default .edui-popup{z-index:3000;background-color:#fff;width:auto;height:auto}.edui-default .edui-popup .edui-shadow{left:0;top:0;width:100%;height:100%}.edui-default .edui-popup-content{border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 4px rgba(0,0,0,.2);-moz-box-shadow:0 3px 4px rgba(0,0,0,.2);box-shadow:0 3px 4px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;padding:5px;background:#fff}.edui-default .edui-popup .edui-bordereraser{background-color:#fff;height:3px}.edui-default .edui-menu .edui-bordereraser{height:3px}.edui-default .edui-anchor-topleft .edui-bordereraser{left:1px;top:-2px}.edui-default .edui-anchor-topright .edui-bordereraser{right:1px;top:-2px}.edui-default .edui-anchor-bottomleft .edui-bordereraser{left:0;bottom:-6px;height:7px;border-left:1px solid gray;border-right:1px solid gray}.edui-default .edui-anchor-bottomright .edui-bordereraser{right:0;bottom:-6px;height:7px;border-left:1px solid gray;border-right:1px solid gray}.edui-popup div{width:auto;height:auto}.edui-default .edui-editor-messageholder{display:block;width:150px;height:auto;border:0;margin:0;padding:0;position:absolute;top:28px;right:3px}.edui-default .edui-message{min-height:10px;text-shadow:0 1px 0 rgba(255,255,255,.5);padding:0;margin-bottom:3px;position:relative}.edui-default .edui-message-body{border-radius:3px;padding:8px 15px 8px 8px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5}.edui-default .edui-message-type-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.edui-default .edui-message-type-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.edui-default .edui-message-type-danger,.edui-default .edui-message-type-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.edui-default .edui-message .edui-message-closer{display:block;width:16px;height:16px;line-height:16px;position:absolute;top:0;right:0;padding:0;cursor:pointer;background:transparent;border:0;float:right;font-size:20px;font-weight:700;color:#999;text-shadow:0 1px 0 #fff;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.edui-default .edui-message .edui-message-content{font-size:10pt;word-wrap:break-word;word-break:normal}.edui-default .edui-dialog{z-index:2000;position:absolute}.edui-dialog div{width:auto}.edui-default .edui-dialog-wrap{margin-right:6px;margin-bottom:6px}.edui-default .edui-dialog-fullscreen-flag{margin-right:0;margin-bottom:0}.edui-default .edui-dialog-body{position:relative;padding:2px 0 0 2px;_zoom:1}.edui-default .edui-dialog-fullscreen-flag .edui-dialog-body{padding:0}.edui-default .edui-dialog-shadow{position:absolute;z-index:-1;left:0;top:0;width:100%;height:100%;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.edui-default .edui-dialog-foot{background-color:#fff}.edui-default .edui-dialog-titlebar{height:26px;border-bottom:1px solid #c6c6c6;background:url(../images/dialog-title-bg.png) repeat-x bottom;position:relative;cursor:move}.edui-default .edui-dialog-caption{font-weight:700;font-size:12px;line-height:26px;padding-left:5px}.edui-default .edui-dialog-draghandle{height:26px}.edui-default .edui-dialog-closebutton{position:absolute!important;right:5px;top:3px}.edui-default .edui-dialog-closebutton .edui-button-body{height:20px;width:20px;cursor:pointer;background:url(../images/icons-all.gif) no-repeat 0 -59px}.edui-default .edui-dialog-closebutton .edui-state-hover .edui-button-body{background:url(../images/icons-all.gif) no-repeat 0 -89px}.edui-default .edui-dialog-foot{height:40px}.edui-default .edui-dialog-buttons{position:absolute;right:0}.edui-default .edui-dialog-buttons .edui-button{margin-right:10px}.edui-default .edui-dialog-buttons .edui-button .edui-button-body{background:url(../images/icons-all.gif) no-repeat;height:24px;width:96px;font-size:12px;line-height:24px;text-align:center;cursor:default}.edui-default .edui-dialog-buttons .edui-button .edui-state-hover .edui-button-body{background:url(../images/icons-all.gif) no-repeat 0 -30px}.edui-default .edui-dialog iframe{border:0;padding:0;margin:0;vertical-align:top}.edui-default .edui-dialog-modalmask{opacity:.3;filter:alpha(opacity=30);background-color:#ccc;position:absolute}.edui-default .edui-dialog-dragmask{position:absolute;background-color:transparent;cursor:move}.edui-default .edui-dialog-content{position:relative}.edui-default .dialogcontmask{cursor:move;visibility:hidden;display:block;position:absolute;width:100%;height:100%;opacity:0;filter:alpha(opacity=0)}.edui-default .edui-for-link .edui-dialog-content{width:420px;height:200px;overflow:hidden}.edui-default .edui-for-background .edui-dialog-content{width:440px;height:280px;overflow:hidden}.edui-default .edui-for-template .edui-dialog-content{width:630px;height:390px;overflow:hidden}.edui-default .edui-for-scrawl .edui-dialog-content{width:515px;*width:506px;height:360px}.edui-default .edui-for-spechars .edui-dialog-content{width:620px;height:500px;*width:630px;*height:570px}.edui-default .edui-for-insertimage .edui-dialog-content{width:650px;height:400px;overflow:hidden}.edui-default .edui-for-webapp .edui-dialog-content{width:560px;_width:565px;height:450px;overflow:hidden}.edui-default .edui-for-insertframe .edui-dialog-content{width:350px;height:200px;overflow:hidden}.edui-default .edui-for-wordimage .edui-dialog-content{width:620px;height:380px;overflow:hidden}.edui-default .edui-for-attachment .edui-dialog-content{width:650px;height:400px;overflow:hidden}.edui-default .edui-for-map .edui-dialog-content{width:550px;height:400px}.edui-default .edui-for-gmap .edui-dialog-content{width:550px;height:400px}.edui-default .edui-for-insertvideo .edui-dialog-content{width:590px;height:390px}.edui-default .edui-for-anchor .edui-dialog-content{width:320px;height:60px;overflow:hidden}.edui-default .edui-for-searchreplace .edui-dialog-content{width:400px;height:220px}.edui-default .edui-for-help .edui-dialog-content{width:400px;height:420px}.edui-default .edui-for-edittable .edui-dialog-content{width:540px;_width:590px;height:335px}.edui-default .edui-for-edittip .edui-dialog-content{width:225px;height:60px}.edui-default .edui-for-edittd .edui-dialog-content{width:240px;height:50px}.edui-default .edui-for-snapscreen .edui-dialog-content{width:400px;height:220px}.edui-default .edui-for-music .edui-dialog-content{width:515px;height:360px}.edui-default .edui-for-paragraph .edui-listitem-label{font-family:Tahoma,Verdana,Arial,Helvetica}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-p{font-size:22px;line-height:27px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h1{font-weight:bolder;font-size:32px;line-height:36px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h2{font-weight:bolder;font-size:27px;line-height:29px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h3{font-weight:bolder;font-size:19px;line-height:23px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h4{font-weight:bolder;font-size:16px;line-height:19px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h5{font-weight:bolder;font-size:13px;line-height:16px}.edui-default .edui-for-paragraph .edui-listitem-label .edui-for-h6{font-weight:bolder;font-size:12px;line-height:14px}.edui-default .edui-for-inserttable .edui-splitborder{display:none}.edui-default .edui-for-inserttable .edui-splitbutton-body .edui-arrow{width:0}.edui-default .edui-toolbar .edui-for-inserttable .edui-state-active .edui-splitborder{border-left:1px solid transparent}.edui-default .edui-tablepicker .edui-infoarea{height:14px;line-height:14px;font-size:12px;width:220px;margin-bottom:3px;clear:both}.edui-default .edui-tablepicker .edui-infoarea .edui-label{float:left}.edui-default .edui-dialog-buttons .edui-label{line-height:24px}.edui-default .edui-tablepicker .edui-infoarea .edui-clickable{float:right}.edui-default .edui-tablepicker .edui-pickarea{background:url(../images/unhighlighted.gif) repeat;height:220px;width:220px}.edui-default .edui-tablepicker .edui-pickarea .edui-overlay{background:url(../images/highlighted.gif) repeat}.edui-default .edui-colorpicker-topbar{height:27px;width:200px}.edui-default .edui-colorpicker-preview{height:20px;border:1px inset #000;margin-left:1px;width:128px;float:left}.edui-default .edui-colorpicker-nocolor{float:right;margin-right:1px;font-size:12px;line-height:14px;height:14px;border:1px solid #333;padding:3px 5px;cursor:pointer}.edui-default .edui-colorpicker-tablefirstrow{height:30px}.edui-default .edui-colorpicker-colorcell{width:14px;height:14px;display:block;margin:0;cursor:pointer}.edui-default .edui-colorpicker-colorcell:hover{width:14px;height:14px;margin:0}.edui-default .edui-colorpicker-advbtn{display:block;text-align:center;cursor:pointer;height:20px}.arrow_down{background:#fff url(../images/arrow_down.png) no-repeat center}.arrow_up{background:#fff url(../images/arrow_up.png) no-repeat center}.edui-colorpicker-adv{position:relative;overflow:hidden;height:180px;display:none}.edui-colorpicker-plant,.edui-colorpicker-hue{border:solid 1px #666}.edui-colorpicker-pad{width:150px;height:150px;left:14px;top:13px;position:absolute;background:red;overflow:hidden;cursor:crosshair}.edui-colorpicker-cover{position:absolute;top:0;left:0;width:150px;height:150px;background:url(../images/tangram-colorpicker.png) -160px -200px}.edui-colorpicker-padDot{position:absolute;top:0;left:0;width:11px;height:11px;overflow:hidden;background:url(../images/tangram-colorpicker.png) 0 -200px repeat-x;z-index:1000}.edui-colorpicker-sliderMain{position:absolute;left:171px;top:13px;width:19px;height:152px;background:url(../images/tangram-colorpicker.png) -179px -12px no-repeat}.edui-colorpicker-slider{width:100%;height:100%;cursor:pointer}.edui-colorpicker-thumb{position:absolute;top:0;cursor:pointer;height:3px;left:-1px;right:-1px;border:1px solid #000;background:#fff;opacity:.8}.edui-default .edui-autotypesetpicker .edui-autotypesetpicker-body{font-size:12px;margin-bottom:3px;clear:both}.edui-default .edui-autotypesetpicker-body table{border-collapse:separate;border-spacing:2px}.edui-default .edui-autotypesetpicker-body td{font-size:12px;word-wrap:break-word}.edui-default .edui-autotypesetpicker-body td input{margin:3px 3px 3px 4px;*margin:1px 0 0}.edui-default .edui-cellalignpicker .edui-cellalignpicker-body{width:70px;font-size:12px;cursor:default}.edui-default .edui-cellalignpicker-body table{border-collapse:separate;border-spacing:0}.edui-default .edui-cellalignpicker-body td{padding:1px}.edui-default .edui-cellalignpicker-body .edui-icon{height:20px;width:20px;padding:1px;background-image:url(../images/table-cell-align.png)}.edui-default .edui-cellalignpicker-body .edui-left{background-position:0 0}.edui-default .edui-cellalignpicker-body .edui-center{background-position:-25px 0}.edui-default .edui-cellalignpicker-body .edui-right{background-position:-51px 0}.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-left{background-position:-73px 0}.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-center{background-position:-98px 0}.edui-default .edui-cellalignpicker-body td.edui-state-hover .edui-right{background-position:-124px 0}.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-left{background-position:-146px 0;background-color:#f1f4f5}.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-center{background-position:-245px 0}.edui-default .edui-cellalignpicker-body td.edui-cellalign-selected .edui-right{background-position:-271px 0}.edui-default .edui-toolbar .edui-separator{width:2px;height:20px;margin:2px 4px 2px 3px;background:url(../images/icons.png) -181px 0;background:url(../images/icons.gif) -181px 0 \9}.edui-default .edui-toolbar .edui-colorbutton .edui-colorlump{position:absolute;overflow:hidden;bottom:1px;left:1px;width:18px;height:4px}.edui-default .edui-for-emotion .edui-icon{background-position:-60px -20px}.edui-default .edui-for-emotion .edui-popup-content iframe{width:514px;height:380px;overflow:hidden}.edui-default .edui-for-emotion .edui-popup-content{position:relative;z-index:555}.edui-default .edui-for-emotion .edui-splitborder{display:none}.edui-default .edui-for-emotion .edui-splitbutton-body .edui-arrow{width:0}.edui-default .edui-toolbar .edui-for-emotion .edui-state-active .edui-splitborder{border-left:1px solid transparent}.edui-default .edui-hassubmenu .edui-arrow{height:20px;width:20px;float:right;background:url(../images/icons-all.gif) no-repeat 10px -233px}.edui-default .edui-menu-body .edui-menuitem{padding:1px}.edui-default .edui-menuseparator{margin:2px 0;height:1px;overflow:hidden}.edui-default .edui-menuseparator-inner{border-bottom:1px solid #e2e3e3;margin-left:29px;margin-right:1px}.edui-default .edui-menu-body .edui-state-hover{padding:0!important;background-color:#fff5d4;border:1px solid #dcac6c}.edui-default .edui-shortcutmenu{padding:2px;width:190px;height:50px;background-color:#fff;border:1px solid #ccc;border-radius:5px}.edui-default .edui-wordpastepop .edui-popup-content{border:0;padding:0;width:54px;height:21px}.edui-default .edui-pasteicon{width:100%;height:100%;background-image:url(../images/wordpaste.png);background-position:0 0}.edui-default .edui-pasteicon.edui-state-opened{background-position:0 -34px}.edui-default .edui-pastecontainer{position:relative;visibility:hidden;width:97px;background:#fff;border:1px solid #ccc}.edui-default .edui-pastecontainer .edui-title{font-weight:700;background:#F8F8FF;height:25px;line-height:25px;font-size:12px;padding-left:5px}.edui-default .edui-pastecontainer .edui-button{overflow:hidden;margin:3px 0}.edui-default .edui-pastecontainer .edui-button .edui-richtxticon,.edui-default .edui-pastecontainer .edui-button .edui-tagicon,.edui-default .edui-pastecontainer .edui-button .edui-plaintxticon{float:left;cursor:pointer;width:29px;height:29px;margin-left:5px;background-image:url(../images/wordpaste.png);background-repeat:no-repeat}.edui-default .edui-pastecontainer .edui-button .edui-richtxticon{margin-left:0;background-position:-109px 0}.edui-default .edui-pastecontainer .edui-button .edui-tagicon{background-position:-148px 1px}.edui-default .edui-pastecontainer .edui-button .edui-plaintxticon{background-position:-72px 0}.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-richtxticon{background-position:-109px -34px}.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-tagicon{background-position:-148px -34px}.edui-default .edui-pastecontainer .edui-button .edui-state-hover .edui-plaintxticon{background-position:-72px -34px} \ No newline at end of file diff --git a/static/plugs/ueditor/themes/default/dialogbase.css b/static/plugs/ueditor/themes/default/dialogbase.css deleted file mode 100644 index ea712666a..000000000 --- a/static/plugs/ueditor/themes/default/dialogbase.css +++ /dev/null @@ -1,100 +0,0 @@ -/*弹出对话框页面样式组件 -*/ - -/*reset -*/ -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, font, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td { - margin: 0; - padding: 0; - outline: 0; - font-size: 100%; -} - -body { - line-height: 1; -} - -ol, ul { - list-style: none; -} - -blockquote, q { - quotes: none; -} - -ins { - text-decoration: none; -} - -del { - text-decoration: line-through; -} - -table { - border-collapse: collapse; - border-spacing: 0; -} - -/*module -*/ -body { - background-color: #fff; - font: 12px/1.5 sans-serif, "宋体", "Arial Narrow", HELVETICA; - color: #646464; -} - -/*tab*/ -.tabhead { - position: relative; - z-index: 10; -} - -.tabhead span { - display: inline-block; - padding: 0 5px; - height: 30px; - border: 1px solid #ccc; - background: url("images/dialog-title-bg.png") repeat-x; - text-align: center; - line-height: 30px; - cursor: pointer; - *margin-right: 5px; -} - -.tabhead span.focus { - height: 31px; - border-bottom: none; - background: #fff; -} - -.tabbody { - position: relative; - top: -1px; - margin: 0 auto; - border: 1px solid #ccc; -} - -/*button*/ -a.button { - display: block; - text-align: center; - line-height: 24px; - text-decoration: none; - height: 24px; - width: 95px; - border: 0; - color: #838383; - background: url(../../themes/default/images/icons-all.gif) no-repeat; -} - -a.button:hover { - background-position: 0 -30px; -} \ No newline at end of file diff --git a/static/plugs/ueditor/themes/default/images/anchor.gif b/static/plugs/ueditor/themes/default/images/anchor.gif deleted file mode 100644 index 5aa797b22..000000000 Binary files a/static/plugs/ueditor/themes/default/images/anchor.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/arrow.png b/static/plugs/ueditor/themes/default/images/arrow.png deleted file mode 100644 index d9008866b..000000000 Binary files a/static/plugs/ueditor/themes/default/images/arrow.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/arrow_down.png b/static/plugs/ueditor/themes/default/images/arrow_down.png deleted file mode 100644 index e9257e83b..000000000 Binary files a/static/plugs/ueditor/themes/default/images/arrow_down.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/arrow_up.png b/static/plugs/ueditor/themes/default/images/arrow_up.png deleted file mode 100644 index 74277af1e..000000000 Binary files a/static/plugs/ueditor/themes/default/images/arrow_up.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/button-bg.gif b/static/plugs/ueditor/themes/default/images/button-bg.gif deleted file mode 100644 index ec7fa2eab..000000000 Binary files a/static/plugs/ueditor/themes/default/images/button-bg.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/cancelbutton.gif b/static/plugs/ueditor/themes/default/images/cancelbutton.gif deleted file mode 100644 index df4bc2c06..000000000 Binary files a/static/plugs/ueditor/themes/default/images/cancelbutton.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/charts.png b/static/plugs/ueditor/themes/default/images/charts.png deleted file mode 100644 index 713965cc4..000000000 Binary files a/static/plugs/ueditor/themes/default/images/charts.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/cursor_h.gif b/static/plugs/ueditor/themes/default/images/cursor_h.gif deleted file mode 100644 index d7c3e7e9e..000000000 Binary files a/static/plugs/ueditor/themes/default/images/cursor_h.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/cursor_h.png b/static/plugs/ueditor/themes/default/images/cursor_h.png deleted file mode 100644 index 2088fc240..000000000 Binary files a/static/plugs/ueditor/themes/default/images/cursor_h.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/cursor_v.gif b/static/plugs/ueditor/themes/default/images/cursor_v.gif deleted file mode 100644 index bb508db55..000000000 Binary files a/static/plugs/ueditor/themes/default/images/cursor_v.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/cursor_v.png b/static/plugs/ueditor/themes/default/images/cursor_v.png deleted file mode 100644 index 6f39ca3d8..000000000 Binary files a/static/plugs/ueditor/themes/default/images/cursor_v.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/dialog-title-bg.png b/static/plugs/ueditor/themes/default/images/dialog-title-bg.png deleted file mode 100644 index f744f267f..000000000 Binary files a/static/plugs/ueditor/themes/default/images/dialog-title-bg.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/filescan.png b/static/plugs/ueditor/themes/default/images/filescan.png deleted file mode 100644 index 1d2715886..000000000 Binary files a/static/plugs/ueditor/themes/default/images/filescan.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/highlighted.gif b/static/plugs/ueditor/themes/default/images/highlighted.gif deleted file mode 100644 index 9272b4915..000000000 Binary files a/static/plugs/ueditor/themes/default/images/highlighted.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/icons-all.gif b/static/plugs/ueditor/themes/default/images/icons-all.gif deleted file mode 100644 index 21915e59d..000000000 Binary files a/static/plugs/ueditor/themes/default/images/icons-all.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/icons.gif b/static/plugs/ueditor/themes/default/images/icons.gif deleted file mode 100644 index 7abd30a1c..000000000 Binary files a/static/plugs/ueditor/themes/default/images/icons.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/icons.png b/static/plugs/ueditor/themes/default/images/icons.png deleted file mode 100644 index c015e3aac..000000000 Binary files a/static/plugs/ueditor/themes/default/images/icons.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/loaderror.png b/static/plugs/ueditor/themes/default/images/loaderror.png deleted file mode 100644 index 35ff33364..000000000 Binary files a/static/plugs/ueditor/themes/default/images/loaderror.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/loading.gif b/static/plugs/ueditor/themes/default/images/loading.gif deleted file mode 100644 index b713e27df..000000000 Binary files a/static/plugs/ueditor/themes/default/images/loading.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/lock.gif b/static/plugs/ueditor/themes/default/images/lock.gif deleted file mode 100644 index b4e6d7822..000000000 Binary files a/static/plugs/ueditor/themes/default/images/lock.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/neweditor-tab-bg.png b/static/plugs/ueditor/themes/default/images/neweditor-tab-bg.png deleted file mode 100644 index 8f398b095..000000000 Binary files a/static/plugs/ueditor/themes/default/images/neweditor-tab-bg.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/pagebreak.gif b/static/plugs/ueditor/themes/default/images/pagebreak.gif deleted file mode 100644 index 8d1cffd64..000000000 Binary files a/static/plugs/ueditor/themes/default/images/pagebreak.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/scale.png b/static/plugs/ueditor/themes/default/images/scale.png deleted file mode 100644 index f45adb585..000000000 Binary files a/static/plugs/ueditor/themes/default/images/scale.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/sortable.png b/static/plugs/ueditor/themes/default/images/sortable.png deleted file mode 100644 index 1bca64969..000000000 Binary files a/static/plugs/ueditor/themes/default/images/sortable.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/spacer.gif b/static/plugs/ueditor/themes/default/images/spacer.gif deleted file mode 100644 index 5bfd67a2d..000000000 Binary files a/static/plugs/ueditor/themes/default/images/spacer.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/sparator_v.png b/static/plugs/ueditor/themes/default/images/sparator_v.png deleted file mode 100644 index 8cf5662da..000000000 Binary files a/static/plugs/ueditor/themes/default/images/sparator_v.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/table-cell-align.png b/static/plugs/ueditor/themes/default/images/table-cell-align.png deleted file mode 100644 index ddf42853e..000000000 Binary files a/static/plugs/ueditor/themes/default/images/table-cell-align.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/tangram-colorpicker.png b/static/plugs/ueditor/themes/default/images/tangram-colorpicker.png deleted file mode 100644 index 738e500cf..000000000 Binary files a/static/plugs/ueditor/themes/default/images/tangram-colorpicker.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/toolbar_bg.png b/static/plugs/ueditor/themes/default/images/toolbar_bg.png deleted file mode 100644 index 7ab685f42..000000000 Binary files a/static/plugs/ueditor/themes/default/images/toolbar_bg.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/unhighlighted.gif b/static/plugs/ueditor/themes/default/images/unhighlighted.gif deleted file mode 100644 index 7ad0b67ae..000000000 Binary files a/static/plugs/ueditor/themes/default/images/unhighlighted.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/upload.png b/static/plugs/ueditor/themes/default/images/upload.png deleted file mode 100644 index 08d4d9268..000000000 Binary files a/static/plugs/ueditor/themes/default/images/upload.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/videologo.gif b/static/plugs/ueditor/themes/default/images/videologo.gif deleted file mode 100644 index 555af7417..000000000 Binary files a/static/plugs/ueditor/themes/default/images/videologo.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/word.gif b/static/plugs/ueditor/themes/default/images/word.gif deleted file mode 100644 index 9ef5d09b7..000000000 Binary files a/static/plugs/ueditor/themes/default/images/word.gif and /dev/null differ diff --git a/static/plugs/ueditor/themes/default/images/wordpaste.png b/static/plugs/ueditor/themes/default/images/wordpaste.png deleted file mode 100644 index 936775810..000000000 Binary files a/static/plugs/ueditor/themes/default/images/wordpaste.png and /dev/null differ diff --git a/static/plugs/ueditor/themes/iframe.css b/static/plugs/ueditor/themes/iframe.css deleted file mode 100644 index 32e72222c..000000000 --- a/static/plugs/ueditor/themes/iframe.css +++ /dev/null @@ -1 +0,0 @@ -/*可以在这里添加你自己的css*/ diff --git a/static/plugs/ueditor/third-party/SyntaxHighlighter/shCore.js b/static/plugs/ueditor/third-party/SyntaxHighlighter/shCore.js deleted file mode 100644 index 324918425..000000000 --- a/static/plugs/ueditor/third-party/SyntaxHighlighter/shCore.js +++ /dev/null @@ -1,3655 +0,0 @@ -// XRegExp 1.5.1 -// (c) 2007-2012 Steven Levithan -// MIT License -// -// Provides an augmented, extensible, cross-browser implementation of regular expressions, -// including support for additional syntax, flags, and methods - -var XRegExp; - -if (XRegExp) { - // Avoid running twice, since that would break references to native globals - throw Error("can't load XRegExp twice in the same frame"); -} - -// Run within an anonymous function to protect variables and avoid new globals -(function (undefined) { - - //--------------------------------- - // Constructor - //--------------------------------- - - // Accepts a pattern and flags; returns a new, extended `RegExp` object. Differs from a native - // regular expression in that additional syntax and flags are supported and cross-browser - // syntax inconsistencies are ameliorated. `XRegExp(/regex/)` clones an existing regex and - // converts to type XRegExp - XRegExp = function (pattern, flags) { - var output = [], - currScope = XRegExp.OUTSIDE_CLASS, - pos = 0, - context, tokenResult, match, chr, regex; - - if (XRegExp.isRegExp(pattern)) { - if (flags !== undefined) - throw TypeError("can't supply flags when constructing one RegExp from another"); - return clone(pattern); - } - // Tokens become part of the regex construction process, so protect against infinite - // recursion when an XRegExp is constructed within a token handler or trigger - if (isInsideConstructor) - throw Error("can't call the XRegExp constructor within token definition functions"); - - flags = flags || ""; - context = { // `this` object for custom tokens - hasNamedCapture: false, - captureNames: [], - hasFlag: function (flag) {return flags.indexOf(flag) > -1;}, - setFlag: function (flag) {flags += flag;} - }; - - while (pos < pattern.length) { - // Check for custom tokens at the current position - tokenResult = runTokens(pattern, pos, currScope, context); - - if (tokenResult) { - output.push(tokenResult.output); - pos += (tokenResult.match[0].length || 1); - } else { - // Check for native multicharacter metasequences (excluding character classes) at - // the current position - if (match = nativ.exec.call(nativeTokens[currScope], pattern.slice(pos))) { - output.push(match[0]); - pos += match[0].length; - } else { - chr = pattern.charAt(pos); - if (chr === "[") - currScope = XRegExp.INSIDE_CLASS; - else if (chr === "]") - currScope = XRegExp.OUTSIDE_CLASS; - // Advance position one character - output.push(chr); - pos++; - } - } - } - - regex = RegExp(output.join(""), nativ.replace.call(flags, flagClip, "")); - regex._xregexp = { - source: pattern, - captureNames: context.hasNamedCapture ? context.captureNames : null - }; - return regex; - }; - - - //--------------------------------- - // Public properties - //--------------------------------- - - XRegExp.version = "1.5.1"; - - // Token scope bitflags - XRegExp.INSIDE_CLASS = 1; - XRegExp.OUTSIDE_CLASS = 2; - - - //--------------------------------- - // Private variables - //--------------------------------- - - var replacementToken = /\$(?:(\d\d?|[$&`'])|{([$\w]+)})/g, - flagClip = /[^gimy]+|([\s\S])(?=[\s\S]*\1)/g, // Nonnative and duplicate flags - quantifier = /^(?:[?*+]|{\d+(?:,\d*)?})\??/, - isInsideConstructor = false, - tokens = [], - // Copy native globals for reference ("native" is an ES3 reserved keyword) - nativ = { - exec: RegExp.prototype.exec, - test: RegExp.prototype.test, - match: String.prototype.match, - replace: String.prototype.replace, - split: String.prototype.split - }, - compliantExecNpcg = nativ.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups - compliantLastIndexIncrement = function () { - var x = /^/g; - nativ.test.call(x, ""); - return !x.lastIndex; - }(), - hasNativeY = RegExp.prototype.sticky !== undefined, - nativeTokens = {}; - - // `nativeTokens` match native multicharacter metasequences only (including deprecated octals, - // excluding character classes) - nativeTokens[XRegExp.INSIDE_CLASS] = /^(?:\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S]))/; - nativeTokens[XRegExp.OUTSIDE_CLASS] = /^(?:\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??)/; - - - //--------------------------------- - // Public methods - //--------------------------------- - - // Lets you extend or change XRegExp syntax and create custom flags. This is used internally by - // the XRegExp library and can be used to create XRegExp plugins. This function is intended for - // users with advanced knowledge of JavaScript's regular expression syntax and behavior. It can - // be disabled by `XRegExp.freezeTokens` - XRegExp.addToken = function (regex, handler, scope, trigger) { - tokens.push({ - pattern: clone(regex, "g" + (hasNativeY ? "y" : "")), - handler: handler, - scope: scope || XRegExp.OUTSIDE_CLASS, - trigger: trigger || null - }); - }; - - // Accepts a pattern and flags; returns an extended `RegExp` object. If the pattern and flag - // combination has previously been cached, the cached copy is returned; otherwise the newly - // created regex is cached - XRegExp.cache = function (pattern, flags) { - var key = pattern + "/" + (flags || ""); - return XRegExp.cache[key] || (XRegExp.cache[key] = XRegExp(pattern, flags)); - }; - - // Accepts a `RegExp` instance; returns a copy with the `/g` flag set. The copy has a fresh - // `lastIndex` (set to zero). If you want to copy a regex without forcing the `global` - // property, use `XRegExp(regex)`. Do not use `RegExp(regex)` because it will not preserve - // special properties required for named capture - XRegExp.copyAsGlobal = function (regex) { - return clone(regex, "g"); - }; - - // Accepts a string; returns the string with regex metacharacters escaped. The returned string - // can safely be used at any point within a regex to match the provided literal string. Escaped - // characters are [ ] { } ( ) * + ? - . , \ ^ $ | # and whitespace - XRegExp.escape = function (str) { - return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); - }; - - // Accepts a string to search, regex to search with, position to start the search within the - // string (default: 0), and an optional Boolean indicating whether matches must start at-or- - // after the position or at the specified position only. This function ignores the `lastIndex` - // of the provided regex in its own handling, but updates the property for compatibility - XRegExp.execAt = function (str, regex, pos, anchored) { - var r2 = clone(regex, "g" + ((anchored && hasNativeY) ? "y" : "")), - match; - r2.lastIndex = pos = pos || 0; - match = r2.exec(str); // Run the altered `exec` (required for `lastIndex` fix, etc.) - if (anchored && match && match.index !== pos) - match = null; - if (regex.global) - regex.lastIndex = match ? r2.lastIndex : 0; - return match; - }; - - // Breaks the unrestorable link to XRegExp's private list of tokens, thereby preventing - // syntax and flag changes. Should be run after XRegExp and any plugins are loaded - XRegExp.freezeTokens = function () { - XRegExp.addToken = function () { - throw Error("can't run addToken after freezeTokens"); - }; - }; - - // Accepts any value; returns a Boolean indicating whether the argument is a `RegExp` object. - // Note that this is also `true` for regex literals and regexes created by the `XRegExp` - // constructor. This works correctly for variables created in another frame, when `instanceof` - // and `constructor` checks would fail to work as intended - XRegExp.isRegExp = function (o) { - return Object.prototype.toString.call(o) === "[object RegExp]"; - }; - - // Executes `callback` once per match within `str`. Provides a simpler and cleaner way to - // iterate over regex matches compared to the traditional approaches of subverting - // `String.prototype.replace` or repeatedly calling `exec` within a `while` loop - XRegExp.iterate = function (str, regex, callback, context) { - var r2 = clone(regex, "g"), - i = -1, match; - while (match = r2.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.) - if (regex.global) - regex.lastIndex = r2.lastIndex; // Doing this to follow expectations if `lastIndex` is checked within `callback` - callback.call(context, match, ++i, str, regex); - if (r2.lastIndex === match.index) - r2.lastIndex++; - } - if (regex.global) - regex.lastIndex = 0; - }; - - // Accepts a string and an array of regexes; returns the result of using each successive regex - // to search within the matches of the previous regex. The array of regexes can also contain - // objects with `regex` and `backref` properties, in which case the named or numbered back- - // references specified are passed forward to the next regex or returned. E.g.: - // var xregexpImgFileNames = XRegExp.matchChain(html, [ - // {regex: /]+)>/i, backref: 1}, // tag attributes - // {regex: XRegExp('(?ix) \\s src=" (? [^"]+ )'), backref: "src"}, // src attribute values - // {regex: XRegExp("^http://xregexp\\.com(/[^#?]+)", "i"), backref: 1}, // xregexp.com paths - // /[^\/]+$/ // filenames (strip directory paths) - // ]); - XRegExp.matchChain = function (str, chain) { - return function recurseChain (values, level) { - var item = chain[level].regex ? chain[level] : {regex: chain[level]}, - regex = clone(item.regex, "g"), - matches = [], i; - for (i = 0; i < values.length; i++) { - XRegExp.iterate(values[i], regex, function (match) { - matches.push(item.backref ? (match[item.backref] || "") : match[0]); - }); - } - return ((level === chain.length - 1) || !matches.length) ? - matches : recurseChain(matches, level + 1); - }([str], 0); - }; - - - //--------------------------------- - // New RegExp prototype methods - //--------------------------------- - - // Accepts a context object and arguments array; returns the result of calling `exec` with the - // first value in the arguments array. the context is ignored but is accepted for congruity - // with `Function.prototype.apply` - RegExp.prototype.apply = function (context, args) { - return this.exec(args[0]); - }; - - // Accepts a context object and string; returns the result of calling `exec` with the provided - // string. the context is ignored but is accepted for congruity with `Function.prototype.call` - RegExp.prototype.call = function (context, str) { - return this.exec(str); - }; - - - //--------------------------------- - // Overriden native methods - //--------------------------------- - - // Adds named capture support (with backreferences returned as `result.name`), and fixes two - // cross-browser issues per ES3: - // - Captured values for nonparticipating capturing groups should be returned as `undefined`, - // rather than the empty string. - // - `lastIndex` should not be incremented after zero-length matches. - RegExp.prototype.exec = function (str) { - var match, name, r2, origLastIndex; - if (!this.global) - origLastIndex = this.lastIndex; - match = nativ.exec.apply(this, arguments); - if (match) { - // Fix browsers whose `exec` methods don't consistently return `undefined` for - // nonparticipating capturing groups - if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) { - r2 = RegExp(this.source, nativ.replace.call(getNativeFlags(this), "g", "")); - // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed - // matching due to characters outside the match - nativ.replace.call((str + "").slice(match.index), r2, function () { - for (var i = 1; i < arguments.length - 2; i++) { - if (arguments[i] === undefined) - match[i] = undefined; - } - }); - } - // Attach named capture properties - if (this._xregexp && this._xregexp.captureNames) { - for (var i = 1; i < match.length; i++) { - name = this._xregexp.captureNames[i - 1]; - if (name) - match[name] = match[i]; - } - } - // Fix browsers that increment `lastIndex` after zero-length matches - if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index)) - this.lastIndex--; - } - if (!this.global) - this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows) - return match; - }; - - // Fix browser bugs in native method - RegExp.prototype.test = function (str) { - // Use the native `exec` to skip some processing overhead, even though the altered - // `exec` would take care of the `lastIndex` fixes - var match, origLastIndex; - if (!this.global) - origLastIndex = this.lastIndex; - match = nativ.exec.call(this, str); - // Fix browsers that increment `lastIndex` after zero-length matches - if (match && !compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index)) - this.lastIndex--; - if (!this.global) - this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows) - return !!match; - }; - - // Adds named capture support and fixes browser bugs in native method - String.prototype.match = function (regex) { - if (!XRegExp.isRegExp(regex)) - regex = RegExp(regex); // Native `RegExp` - if (regex.global) { - var result = nativ.match.apply(this, arguments); - regex.lastIndex = 0; // Fix IE bug - return result; - } - return regex.exec(this); // Run the altered `exec` - }; - - // Adds support for `${n}` tokens for named and numbered backreferences in replacement text, - // and provides named backreferences to replacement functions as `arguments[0].name`. Also - // fixes cross-browser differences in replacement text syntax when performing a replacement - // using a nonregex search value, and the value of replacement regexes' `lastIndex` property - // during replacement iterations. Note that this doesn't support SpiderMonkey's proprietary - // third (`flags`) parameter - String.prototype.replace = function (search, replacement) { - var isRegex = XRegExp.isRegExp(search), - captureNames, result, str, origLastIndex; - - // There are too many combinations of search/replacement types/values and browser bugs that - // preclude passing to native `replace`, so don't try - //if (...) - // return nativ.replace.apply(this, arguments); - - if (isRegex) { - if (search._xregexp) - captureNames = search._xregexp.captureNames; // Array or `null` - if (!search.global) - origLastIndex = search.lastIndex; - } else { - search = search + ""; // Type conversion - } - - if (Object.prototype.toString.call(replacement) === "[object Function]") { - result = nativ.replace.call(this + "", search, function () { - if (captureNames) { - // Change the `arguments[0]` string primitive to a String object which can store properties - arguments[0] = new String(arguments[0]); - // Store named backreferences on `arguments[0]` - for (var i = 0; i < captureNames.length; i++) { - if (captureNames[i]) - arguments[0][captureNames[i]] = arguments[i + 1]; - } - } - // Update `lastIndex` before calling `replacement` (fix browsers) - if (isRegex && search.global) - search.lastIndex = arguments[arguments.length - 2] + arguments[0].length; - return replacement.apply(null, arguments); - }); - } else { - str = this + ""; // Type conversion, so `args[args.length - 1]` will be a string (given nonstring `this`) - result = nativ.replace.call(str, search, function () { - var args = arguments; // Keep this function's `arguments` available through closure - return nativ.replace.call(replacement + "", replacementToken, function ($0, $1, $2) { - // Numbered backreference (without delimiters) or special variable - if ($1) { - switch ($1) { - case "$": return "$"; - case "&": return args[0]; - case "`": return args[args.length - 1].slice(0, args[args.length - 2]); - case "'": return args[args.length - 1].slice(args[args.length - 2] + args[0].length); - // Numbered backreference - default: - // What does "$10" mean? - // - Backreference 10, if 10 or more capturing groups exist - // - Backreference 1 followed by "0", if 1-9 capturing groups exist - // - Otherwise, it's the string "$10" - // Also note: - // - Backreferences cannot be more than two digits (enforced by `replacementToken`) - // - "$01" is equivalent to "$1" if a capturing group exists, otherwise it's the string "$01" - // - There is no "$0" token ("$&" is the entire match) - var literalNumbers = ""; - $1 = +$1; // Type conversion; drop leading zero - if (!$1) // `$1` was "0" or "00" - return $0; - while ($1 > args.length - 3) { - literalNumbers = String.prototype.slice.call($1, -1) + literalNumbers; - $1 = Math.floor($1 / 10); // Drop the last digit - } - return ($1 ? args[$1] || "" : "$") + literalNumbers; - } - // Named backreference or delimited numbered backreference - } else { - // What does "${n}" mean? - // - Backreference to numbered capture n. Two differences from "$n": - // - n can be more than two digits - // - Backreference 0 is allowed, and is the entire match - // - Backreference to named capture n, if it exists and is not a number overridden by numbered capture - // - Otherwise, it's the string "${n}" - var n = +$2; // Type conversion; drop leading zeros - if (n <= args.length - 3) - return args[n]; - n = captureNames ? indexOf(captureNames, $2) : -1; - return n > -1 ? args[n + 1] : $0; - } - }); - }); - } - - if (isRegex) { - if (search.global) - search.lastIndex = 0; // Fix IE, Safari bug (last tested IE 9.0.5, Safari 5.1.2 on Windows) - else - search.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows) - } - - return result; - }; - - // A consistent cross-browser, ES3 compliant `split` - String.prototype.split = function (s /* separator */, limit) { - // If separator `s` is not a regex, use the native `split` - if (!XRegExp.isRegExp(s)) - return nativ.split.apply(this, arguments); - - var str = this + "", // Type conversion - output = [], - lastLastIndex = 0, - match, lastLength; - - // Behavior for `limit`: if it's... - // - `undefined`: No limit - // - `NaN` or zero: Return an empty array - // - A positive number: Use `Math.floor(limit)` - // - A negative number: No limit - // - Other: Type-convert, then use the above rules - if (limit === undefined || +limit < 0) { - limit = Infinity; - } else { - limit = Math.floor(+limit); - if (!limit) - return []; - } - - // This is required if not `s.global`, and it avoids needing to set `s.lastIndex` to zero - // and restore it to its original value when we're done using the regex - s = XRegExp.copyAsGlobal(s); - - while (match = s.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.) - if (s.lastIndex > lastLastIndex) { - output.push(str.slice(lastLastIndex, match.index)); - - if (match.length > 1 && match.index < str.length) - Array.prototype.push.apply(output, match.slice(1)); - - lastLength = match[0].length; - lastLastIndex = s.lastIndex; - - if (output.length >= limit) - break; - } - - if (s.lastIndex === match.index) - s.lastIndex++; - } - - if (lastLastIndex === str.length) { - if (!nativ.test.call(s, "") || lastLength) - output.push(""); - } else { - output.push(str.slice(lastLastIndex)); - } - - return output.length > limit ? output.slice(0, limit) : output; - }; - - - //--------------------------------- - // Private helper functions - //--------------------------------- - - // Supporting function for `XRegExp`, `XRegExp.copyAsGlobal`, etc. Returns a copy of a `RegExp` - // instance with a fresh `lastIndex` (set to zero), preserving properties required for named - // capture. Also allows adding new flags in the process of copying the regex - function clone (regex, additionalFlags) { - if (!XRegExp.isRegExp(regex)) - throw TypeError("type RegExp expected"); - var x = regex._xregexp; - regex = XRegExp(regex.source, getNativeFlags(regex) + (additionalFlags || "")); - if (x) { - regex._xregexp = { - source: x.source, - captureNames: x.captureNames ? x.captureNames.slice(0) : null - }; - } - return regex; - } - - function getNativeFlags (regex) { - return (regex.global ? "g" : "") + - (regex.ignoreCase ? "i" : "") + - (regex.multiline ? "m" : "") + - (regex.extended ? "x" : "") + // Proposed for ES4; included in AS3 - (regex.sticky ? "y" : ""); - } - - function runTokens (pattern, index, scope, context) { - var i = tokens.length, - result, match, t; - // Protect against constructing XRegExps within token handler and trigger functions - isInsideConstructor = true; - // Must reset `isInsideConstructor`, even if a `trigger` or `handler` throws - try { - while (i--) { // Run in reverse order - t = tokens[i]; - if ((scope & t.scope) && (!t.trigger || t.trigger.call(context))) { - t.pattern.lastIndex = index; - match = t.pattern.exec(pattern); // Running the altered `exec` here allows use of named backreferences, etc. - if (match && match.index === index) { - result = { - output: t.handler.call(context, match, scope), - match: match - }; - break; - } - } - } - } catch (err) { - throw err; - } finally { - isInsideConstructor = false; - } - return result; - } - - function indexOf (array, item, from) { - if (Array.prototype.indexOf) // Use the native array method if available - return array.indexOf(item, from); - for (var i = from || 0; i < array.length; i++) { - if (array[i] === item) - return i; - } - return -1; - } - - - //--------------------------------- - // Built-in tokens - //--------------------------------- - - // Augment XRegExp's regular expression syntax and flags. Note that when adding tokens, the - // third (`scope`) argument defaults to `XRegExp.OUTSIDE_CLASS` - - // Comment pattern: (?# ) - XRegExp.addToken( - /\(\?#[^)]*\)/, - function (match) { - // Keep tokens separated unless the following token is a quantifier - return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)"; - } - ); - - // Capturing group (match the opening parenthesis only). - // Required for support of named capturing groups - XRegExp.addToken( - /\((?!\?)/, - function () { - this.captureNames.push(null); - return "("; - } - ); - - // Named capturing group (match the opening delimiter only): (? - XRegExp.addToken( - /\(\?<([$\w]+)>/, - function (match) { - this.captureNames.push(match[1]); - this.hasNamedCapture = true; - return "("; - } - ); - - // Named backreference: \k - XRegExp.addToken( - /\\k<([\w$]+)>/, - function (match) { - var index = indexOf(this.captureNames, match[1]); - // Keep backreferences separate from subsequent literal numbers. Preserve back- - // references to named groups that are undefined at this point as literal strings - return index > -1 ? - "\\" + (index + 1) + (isNaN(match.input.charAt(match.index + match[0].length)) ? "" : "(?:)") : - match[0]; - } - ); - - // Empty character class: [] or [^] - XRegExp.addToken( - /\[\^?]/, - function (match) { - // For cross-browser compatibility with ES3, convert [] to \b\B and [^] to [\s\S]. - // (?!) should work like \b\B, but is unreliable in Firefox - return match[0] === "[]" ? "\\b\\B" : "[\\s\\S]"; - } - ); - - // Mode modifier at the start of the pattern only, with any combination of flags imsx: (?imsx) - // Does not support x(?i), (?-i), (?i-m), (?i: ), (?i)(?m), etc. - XRegExp.addToken( - /^\(\?([imsx]+)\)/, - function (match) { - this.setFlag(match[1]); - return ""; - } - ); - - // Whitespace and comments, in free-spacing (aka extended) mode only - XRegExp.addToken( - /(?:\s+|#.*)+/, - function (match) { - // Keep tokens separated unless the following token is a quantifier - return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)"; - }, - XRegExp.OUTSIDE_CLASS, - function () {return this.hasFlag("x");} - ); - - // Dot, in dotall (aka singleline) mode only - XRegExp.addToken( - /\./, - function () {return "[\\s\\S]";}, - XRegExp.OUTSIDE_CLASS, - function () {return this.hasFlag("s");} - ); - - - //--------------------------------- - // Backward compatibility - //--------------------------------- - - // Uncomment the following block for compatibility with XRegExp 1.0-1.2: - /* - XRegExp.matchWithinChain = XRegExp.matchChain; - RegExp.prototype.addFlags = function (s) {return clone(this, s);}; - RegExp.prototype.execAll = function (s) {var r = []; XRegExp.iterate(s, this, function (m) {r.push(m);}); return r;}; - RegExp.prototype.forEachExec = function (s, f, c) {return XRegExp.iterate(s, this, f, c);}; - RegExp.prototype.validate = function (s) {var r = RegExp("^(?:" + this.source + ")$(?!\\s)", getNativeFlags(this)); if (this.global) this.lastIndex = 0; return s.search(r) === 0;}; - */ - -})(); - -// -// Begin anonymous function. This is used to contain local scope variables without polutting global scope. -// -if (typeof(SyntaxHighlighter) == 'undefined') var SyntaxHighlighter = function() { - -// CommonJS - if (typeof(require) != 'undefined' && typeof(XRegExp) == 'undefined') - { - XRegExp = require('XRegExp').XRegExp; - } - -// Shortcut object which will be assigned to the SyntaxHighlighter variable. -// This is a shorthand for local reference in order to avoid long namespace -// references to SyntaxHighlighter.whatever... - var sh = { - defaults : { - /** Additional CSS class names to be added to highlighter elements. */ - 'class-name' : '', - - /** First line number. */ - 'first-line' : 1, - - /** - * Pads line numbers. Possible values are: - * - * false - don't pad line numbers. - * true - automaticaly pad numbers with minimum required number of leading zeroes. - * [int] - length up to which pad line numbers. - */ - 'pad-line-numbers' : false, - - /** Lines to highlight. */ - 'highlight' : false, - - /** Title to be displayed above the code block. */ - 'title' : null, - - /** Enables or disables smart tabs. */ - 'smart-tabs' : true, - - /** Gets or sets tab size. */ - 'tab-size' : 4, - - /** Enables or disables gutter. */ - 'gutter' : true, - - /** Enables or disables toolbar. */ - 'toolbar' : true, - - /** Enables quick code copy and paste from double click. */ - 'quick-code' : true, - - /** Forces code view to be collapsed. */ - 'collapse' : false, - - /** Enables or disables automatic links. */ - 'auto-links' : false, - - /** Gets or sets light mode. Equavalent to turning off gutter and toolbar. */ - 'light' : false, - - 'unindent' : true, - - 'html-script' : false - }, - - config : { - space : ' ', - - /** Enables use of ', '12599', '2017-05-16 17:50:23', '1', '47,49', '0', null, '2015-11-13 15:14:22'); - --- ---------------------------- --- Table structure for wechat_fans --- ---------------------------- -DROP TABLE IF EXISTS `wechat_fans`; -CREATE TABLE `wechat_fans` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '粉丝表ID', - `appid` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '公众号Appid', - `groupid` bigint(20) unsigned DEFAULT NULL COMMENT '分组ID', - `tagid_list` varchar(100) CHARACTER SET utf8 DEFAULT '' COMMENT '标签id', - `is_back` tinyint(1) unsigned DEFAULT '0' COMMENT '是否为黑名单用户', - `subscribe` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '用户是否订阅该公众号,0:未关注,1:已关注', - `openid` char(100) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '用户的标识,对当前公众号唯一', - `spread_openid` char(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '推荐人OPENID', - `spread_at` datetime DEFAULT NULL, - `nickname` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '用户的昵称', - `sex` tinyint(1) unsigned DEFAULT NULL COMMENT '用户的性别,值为1时是男性,值为2时是女性,值为0时是未知', - `country` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '用户所在国家', - `province` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '用户所在省份', - `city` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '用户所在城市', - `language` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '用户的语言,简体中文为zh_CN', - `headimgurl` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT '用户头像', - `subscribe_time` bigint(20) unsigned DEFAULT NULL COMMENT '用户关注时间', - `subscribe_at` datetime DEFAULT NULL COMMENT '关注时间', - `unionid` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT 'unionid', - `remark` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '备注', - `expires_in` bigint(20) unsigned DEFAULT '0' COMMENT '有效时间', - `refresh_token` varchar(200) CHARACTER SET utf8 DEFAULT NULL COMMENT '刷新token', - `access_token` varchar(200) CHARACTER SET utf8 DEFAULT NULL COMMENT '访问token', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `index_wechat_fans_spread_openid` (`spread_openid`) USING BTREE, - KEY `index_wechat_fans_openid` (`openid`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=9427 DEFAULT CHARSET=sjis COMMENT='微信粉丝'; - --- ---------------------------- --- Table structure for wechat_fans_tags --- ---------------------------- -DROP TABLE IF EXISTS `wechat_fans_tags`; -CREATE TABLE `wechat_fans_tags` ( - `id` bigint(20) unsigned NOT NULL COMMENT '标签ID', - `appid` char(50) DEFAULT NULL COMMENT '公众号APPID', - `name` varchar(35) DEFAULT NULL COMMENT '标签名称', - `count` int(11) unsigned DEFAULT NULL COMMENT '总数', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期', - KEY `index_wechat_fans_tags_id` (`id`) USING BTREE, - KEY `index_wechat_fans_tags_appid` (`appid`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信会员标签'; - --- ---------------------------- --- Table structure for wechat_keys --- ---------------------------- -DROP TABLE IF EXISTS `wechat_keys`; -CREATE TABLE `wechat_keys` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `appid` char(50) DEFAULT NULL COMMENT '公众号APPID', - `type` varchar(20) DEFAULT NULL COMMENT '类型,text 文件消息,image 图片消息,news 图文消息', - `keys` varchar(100) DEFAULT NULL COMMENT '关键字', - `content` text COMMENT '文本内容', - `image_url` varchar(255) DEFAULT NULL COMMENT '图片链接', - `voice_url` varchar(255) DEFAULT NULL COMMENT '语音链接', - `music_title` varchar(100) DEFAULT NULL COMMENT '音乐标题', - `music_url` varchar(255) DEFAULT NULL COMMENT '音乐链接', - `music_image` varchar(255) DEFAULT NULL COMMENT '音乐缩略图链接', - `music_desc` varchar(255) DEFAULT NULL COMMENT '音乐描述', - `video_title` varchar(100) DEFAULT NULL COMMENT '视频标题', - `video_url` varchar(255) DEFAULT NULL COMMENT '视频URL', - `video_desc` varchar(255) DEFAULT NULL COMMENT '视频描述', - `news_id` bigint(20) unsigned DEFAULT NULL COMMENT '图文ID', - `sort` bigint(20) unsigned DEFAULT '0' COMMENT '排序字段', - `status` tinyint(1) unsigned DEFAULT '1' COMMENT '0 禁用,1 启用', - `create_by` bigint(20) unsigned DEFAULT NULL COMMENT '创建人', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COMMENT=' 微信关键字'; - --- ---------------------------- --- Table structure for wechat_menu --- ---------------------------- -DROP TABLE IF EXISTS `wechat_menu`; -CREATE TABLE `wechat_menu` ( - `id` bigint(16) unsigned NOT NULL AUTO_INCREMENT, - `index` bigint(20) DEFAULT NULL, - `pindex` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '父id', - `type` varchar(24) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '菜单类型 null主菜单 link链接 keys关键字', - `name` varchar(256) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '菜单名称', - `content` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文字内容', - `sort` bigint(20) unsigned DEFAULT '0' COMMENT '排序', - `status` tinyint(1) unsigned DEFAULT '1' COMMENT '状态(0禁用1启用)', - `create_by` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '创建人', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `wechat_menu_pid` (`pindex`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=1507 DEFAULT CHARSET=utf8; - --- ---------------------------- --- Records of wechat_menu --- ---------------------------- -INSERT INTO `wechat_menu` VALUES ('1502', '1', '0', 'text', '关键字', '2234123413', '0', '1', '0', '2017-04-27 14:49:14'); -INSERT INTO `wechat_menu` VALUES ('1503', '11', '1', 'keys', '图片', '图片', '0', '1', '0', '2017-04-27 14:49:14'); -INSERT INTO `wechat_menu` VALUES ('1504', '12', '1', 'keys', '音乐', '音乐', '1', '1', '0', '2017-04-27 14:49:14'); -INSERT INTO `wechat_menu` VALUES ('1505', '2', '0', 'event', '事件类', 'pic_weixin', '1', '1', '0', '2017-04-27 14:49:14'); -INSERT INTO `wechat_menu` VALUES ('1506', '3', '0', 'view', '微信支付', 'index/wap/payjs', '2', '1', '0', '2017-04-27 14:49:14'); - --- ---------------------------- --- Table structure for wechat_news --- ---------------------------- -DROP TABLE IF EXISTS `wechat_news`; -CREATE TABLE `wechat_news` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `media_id` varchar(100) DEFAULT NULL COMMENT '永久素材MediaID', - `local_url` varchar(300) DEFAULT NULL COMMENT '永久素材显示URL', - `article_id` varchar(60) DEFAULT NULL COMMENT '关联图文ID,用,号做分割', - `is_deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '是否删除', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建人', - PRIMARY KEY (`id`), - KEY `index_wechat_new_artcle_id` (`article_id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 COMMENT='微信图文表'; - --- ---------------------------- --- Table structure for wechat_news_article --- ---------------------------- -DROP TABLE IF EXISTS `wechat_news_article`; -CREATE TABLE `wechat_news_article` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `title` varchar(50) DEFAULT NULL COMMENT '素材标题', - `local_url` varchar(300) DEFAULT NULL COMMENT '永久素材显示URL', - `show_cover_pic` tinyint(4) unsigned DEFAULT '0' COMMENT '是否显示封面 0不显示,1 显示', - `author` varchar(20) DEFAULT NULL COMMENT '作者', - `digest` varchar(300) DEFAULT NULL COMMENT '摘要内容', - `content` longtext COMMENT '图文内容', - `content_source_url` varchar(200) DEFAULT NULL COMMENT '图文消息原文地址', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建人', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=96 DEFAULT CHARSET=utf8 COMMENT='微信素材表'; - --- ---------------------------- --- Table structure for wechat_news_image --- ---------------------------- -DROP TABLE IF EXISTS `wechat_news_image`; -CREATE TABLE `wechat_news_image` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `appid` varchar(200) DEFAULT NULL COMMENT '公众号ID', - `md5` varchar(32) DEFAULT NULL COMMENT '文件md5', - `media_id` varchar(100) DEFAULT NULL COMMENT '永久素材MediaID', - `local_url` varchar(300) DEFAULT NULL COMMENT '本地文件链接', - `media_url` varchar(300) DEFAULT NULL COMMENT '远程图片链接', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='微信服务器图片'; - --- ---------------------------- --- Table structure for wechat_news_media --- ---------------------------- -DROP TABLE IF EXISTS `wechat_news_media`; -CREATE TABLE `wechat_news_media` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `appid` varchar(200) DEFAULT NULL COMMENT '公众号ID', - `md5` varchar(32) DEFAULT NULL COMMENT '文件md5', - `type` varchar(20) DEFAULT NULL COMMENT '媒体类型', - `media_id` varchar(100) DEFAULT NULL COMMENT '永久素材MediaID', - `local_url` varchar(300) DEFAULT NULL COMMENT '本地文件链接', - `media_url` varchar(300) DEFAULT NULL COMMENT '远程图片链接', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 COMMENT='微信素材表'; - --- ---------------------------- --- Table structure for wechat_pay_notify --- ---------------------------- -DROP TABLE IF EXISTS `wechat_pay_notify`; -CREATE TABLE `wechat_pay_notify` ( - `id` int(20) NOT NULL AUTO_INCREMENT, - `appid` varchar(50) DEFAULT NULL COMMENT '公众号Appid', - `bank_type` varchar(50) DEFAULT NULL COMMENT '银行类型', - `cash_fee` bigint(20) DEFAULT NULL COMMENT '现金价', - `fee_type` char(20) DEFAULT NULL COMMENT '币种,1人民币', - `is_subscribe` char(1) DEFAULT 'N' COMMENT '是否关注,Y为关注,N为未关注', - `mch_id` varchar(50) DEFAULT NULL COMMENT '商户MCH_ID', - `nonce_str` varchar(32) DEFAULT NULL COMMENT '随机串', - `openid` varchar(50) DEFAULT NULL COMMENT '微信用户openid', - `out_trade_no` varchar(50) DEFAULT NULL COMMENT '支付平台退款交易号', - `sign` varchar(100) DEFAULT NULL COMMENT '签名', - `time_end` datetime DEFAULT NULL COMMENT '结束时间', - `result_code` varchar(10) DEFAULT NULL, - `return_code` varchar(10) DEFAULT NULL, - `total_fee` varchar(11) DEFAULT NULL COMMENT '支付总金额,单位为分', - `trade_type` varchar(20) DEFAULT NULL COMMENT '支付方式', - `transaction_id` varchar(64) DEFAULT NULL COMMENT '订单号', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '本地系统时间', - PRIMARY KEY (`id`), - KEY `index_wechat_pay_notify_openid` (`openid`) USING BTREE, - KEY `index_wechat_pay_notify_out_trade_no` (`out_trade_no`) USING BTREE, - KEY `index_wechat_pay_notify_transaction_id` (`transaction_id`) USING BTREE -) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='支付日志表'; - --- ---------------------------- --- Table structure for wechat_pay_prepayid --- ---------------------------- -DROP TABLE IF EXISTS `wechat_pay_prepayid`; -CREATE TABLE `wechat_pay_prepayid` ( - `id` int(20) NOT NULL AUTO_INCREMENT, - `appid` char(50) DEFAULT NULL COMMENT '公众号APPID', - `from` char(32) DEFAULT 'shop' COMMENT '支付来源', - `fee` bigint(20) unsigned DEFAULT NULL COMMENT '支付费用(分)', - `trade_type` varchar(20) DEFAULT NULL COMMENT '订单类型', - `order_no` varchar(50) DEFAULT NULL COMMENT '内部订单号', - `out_trade_no` varchar(50) DEFAULT NULL COMMENT '外部订单号', - `prepayid` varchar(500) DEFAULT NULL COMMENT '预支付码', - `expires_in` bigint(20) unsigned DEFAULT NULL COMMENT '有效时间', - `transaction_id` varchar(64) DEFAULT NULL COMMENT '微信平台订单号', - `is_pay` tinyint(1) unsigned DEFAULT '0' COMMENT '1已支付,0未支退款', - `pay_at` datetime DEFAULT NULL COMMENT '支付时间', - `is_refund` tinyint(1) unsigned DEFAULT '0' COMMENT '是否退款,退款单号(T+原来订单)', - `refund_at` datetime DEFAULT NULL COMMENT '退款时间', - `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '本地系统时间', - PRIMARY KEY (`id`), - KEY `index_wechat_pay_prepayid_outer_no` (`out_trade_no`) USING BTREE, - KEY `index_wechat_pay_prepayid_order_no` (`order_no`) USING BTREE, - KEY `index_wechat_pay_is_pay` (`is_pay`) USING BTREE, - KEY `index_wechat_pay_is_refund` (`is_refund`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=146 DEFAULT CHARSET=utf8 COMMENT='支付订单号对应表'; \ No newline at end of file diff --git a/thinkphp/.gitignore b/thinkphp/.gitignore index 7e31ef510..f7775ba41 100644 --- a/thinkphp/.gitignore +++ b/thinkphp/.gitignore @@ -1,4 +1,8 @@ -/composer.lock /vendor -.idea +composer.phar +composer.lock .DS_Store +Thumbs.db +/phpunit.xml +/.idea +/.vscode \ No newline at end of file diff --git a/thinkphp/.travis.yml b/thinkphp/.travis.yml deleted file mode 100644 index f74ffca11..000000000 --- a/thinkphp/.travis.yml +++ /dev/null @@ -1,47 +0,0 @@ -sudo: false - -language: php - -services: - - memcached - - mongodb - - mysql - - postgresql - - redis-server - -matrix: - fast_finish: true - include: - - php: 5.4 - - php: 5.5 - - php: 5.6 - - php: 7.0 - - php: hhvm - allow_failures: - - php: hhvm - -cache: - directories: - - $HOME/.composer/cache - -before_install: - - composer self-update - - mysql -e "create database IF NOT EXISTS test;" -uroot - - psql -c 'DROP DATABASE IF EXISTS test;' -U postgres - - psql -c 'create database test;' -U postgres - -install: - - ./tests/script/install.sh - -script: - ## LINT - - find . -path ./vendor -prune -o -type f -name \*.php -exec php -l {} \; - ## PHP Copy/Paste Detector - - vendor/bin/phpcpd --verbose --exclude vendor ./ || true - ## PHPLOC - - vendor/bin/phploc --exclude vendor ./ - ## PHPUNIT - - vendor/bin/phpunit --coverage-clover=coverage.xml --configuration=phpunit.xml - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/thinkphp/LICENSE.txt b/thinkphp/LICENSE.txt index 574a39c40..774fa76fd 100644 --- a/thinkphp/LICENSE.txt +++ b/thinkphp/LICENSE.txt @@ -1,6 +1,6 @@ ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 -版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn) +版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn) All rights reserved。 ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 diff --git a/thinkphp/README.md b/thinkphp/README.md index 4b0eb838f..b2a791b8b 100644 --- a/thinkphp/README.md +++ b/thinkphp/README.md @@ -1,103 +1,82 @@ -ThinkPHP 5.0 +![](https://box.kancloud.cn/5a0aaa69a5ff42657b5c4715f3d49221) + +ThinkPHP 5.1(LTS) —— 12载初心,你值得信赖的PHP框架 =============== -[![StyleCI](https://styleci.io/repos/48530411/shield?style=flat&branch=master)](https://styleci.io/repos/48530411) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/top-think/framework/badges/quality-score.png?b=5.1)](https://scrutinizer-ci.com/g/top-think/framework/?branch=5.1) [![Build Status](https://travis-ci.org/top-think/framework.svg?branch=master)](https://travis-ci.org/top-think/framework) -[![codecov.io](http://codecov.io/github/top-think/framework/coverage.svg?branch=master)](http://codecov.io/github/github/top-think/framework?branch=master) [![Total Downloads](https://poser.pugx.org/topthink/framework/downloads)](https://packagist.org/packages/topthink/framework) [![Latest Stable Version](https://poser.pugx.org/topthink/framework/v/stable)](https://packagist.org/packages/topthink/framework) -[![Latest Unstable Version](https://poser.pugx.org/topthink/framework/v/unstable)](https://packagist.org/packages/topthink/framework) +[![PHP Version](https://img.shields.io/badge/php-%3E%3D5.6-8892BF.svg)](http://www.php.net/) [![License](https://poser.pugx.org/topthink/framework/license)](https://packagist.org/packages/topthink/framework) -ThinkPHP5在保持快速开发和大道至简的核心理念不变的同时,PHP版本要求提升到5.4,优化核心,减少依赖,基于全新的架构思想和命名空间实现,是ThinkPHP突破原有框架思路的颠覆之作,其主要特性包括: +ThinkPHP5.1对底层架构做了进一步的改进,减少依赖,其主要特性包括: - + 基于命名空间和众多PHP新特性 - + 核心功能组件化 - + 强化路由功能 - + 更灵活的控制器 - + 重构的模型和数据库类 - + 配置文件可分离 - + 重写的自动验证和完成 - + 简化扩展机制 - + API支持完善 - + 改进的Log类 - + 命令行访问支持 - + REST支持 - + 引导文件支持 - + 方便的自动生成定义 - + 真正惰性加载 - + 分布式环境支持 - + 支持Composer - + 支持MongoDb + + 采用容器统一管理对象 + + 支持Facade + + 更易用的路由 + + 注解路由支持 + + 路由跨域请求支持 + + 验证类增强 + + 配置和路由目录独立 + + 取消系统常量 + + 类库别名机制 + + 模型和数据库增强 + + 依赖注入完善 + + 支持PSR-3日志规范 + + 中间件支持(`V5.1.6+`) + + 支持`Swoole`/`Workerman`运行(`V5.1.18+`) -> ThinkPHP5的运行环境要求PHP5.4以上。 +官方已经正式宣布`5.1.27`版本为LTS版本。 -详细开发文档参考 [ThinkPHP5完全开发手册](http://www.kancloud.cn/manual/thinkphp5) 以及[ThinkPHP5入门系列教程](http://www.kancloud.cn/special/thinkphp5_quickstart) +### 废除的功能: -## 目录结构 + + 聚合模型 + + 内置控制器扩展类 + + 模型自动验证 -初始的目录结构如下: +> ThinkPHP5.1的运行环境要求PHP5.6+。 + + +## 安装 + +使用composer安装 ~~~ -www WEB部署目录(或者子目录) -├─application 应用目录 -│ ├─common 公共模块目录(可以更改) -│ ├─module_name 模块目录 -│ │ ├─config.php 模块配置文件 -│ │ ├─common.php 模块函数文件 -│ │ ├─controller 控制器目录 -│ │ ├─model 模型目录 -│ │ ├─view 视图目录 -│ │ └─ ... 更多类库目录 -│ │ -│ ├─command.php 命令行工具配置文件 -│ ├─common.php 公共函数文件 -│ ├─config.php 公共配置文件 -│ ├─route.php 路由配置文件 -│ ├─tags.php 应用行为扩展定义文件 -│ └─database.php 数据库配置文件 -│ -├─public WEB目录(对外访问目录) -│ ├─index.php 入口文件 -│ ├─router.php 快速测试文件 -│ └─.htaccess 用于apache的重写 -│ -├─thinkphp 框架系统目录 -│ ├─lang 语言文件目录 -│ ├─library 框架类库目录 -│ │ ├─think Think类库包目录 -│ │ └─traits 系统Trait目录 -│ │ -│ ├─tpl 系统模板目录 -│ ├─base.php 基础定义文件 -│ ├─console.php 控制台入口文件 -│ ├─convention.php 框架惯例配置文件 -│ ├─helper.php 助手函数文件 -│ ├─phpunit.xml phpunit配置文件 -│ └─start.php 框架入口文件 -│ -├─extend 扩展类库目录 -├─runtime 应用的运行时目录(可写,可定制) -├─vendor 第三方类库目录(Composer依赖库) -├─build.php 自动生成定义文件(参考) -├─composer.json composer 定义文件 -├─LICENSE.txt 授权说明文件 -├─README.md README 文件 -├─think 命令行入口文件 +composer create-project topthink/think tp ~~~ -> router.php用于php自带webserver支持,可用于快速测试 -> 切换到public目录后,启动命令:php -S localhost:8888 router.php -> 上面的目录结构和名称是可以改变的,这取决于你的入口文件和配置参数。 +启动服务 + +~~~ +cd tp +php think run +~~~ + +然后就可以在浏览器中访问 + +~~~ +http://localhost:8000 +~~~ + +更新框架 +~~~ +composer update topthink/framework +~~~ + + +## 在线手册 + ++ [完全开发手册](https://www.kancloud.cn/manual/thinkphp5_1/content) ++ [升级指导](https://www.kancloud.cn/manual/thinkphp5_1/354155) ## 命名规范 -ThinkPHP5的命名规范遵循PSR-2规范以及PSR-4自动加载规范。 +`ThinkPHP5.1`遵循PSR-2命名规范和PSR-4自动加载规范。 ## 参与开发 -注册并登录 Github 帐号, fork 本项目并进行改动。 -更多细节参阅 [CONTRIBUTING.md](CONTRIBUTING.md) +请参阅 [ThinkPHP5 核心框架包](https://github.com/top-think/framework)。 ## 版权信息 @@ -105,7 +84,7 @@ ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 本项目包含的第三方源码和二进制文件之版权信息另行标注。 -版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn) +版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn) All rights reserved。 diff --git a/thinkphp/base.php b/thinkphp/base.php index 9744f01df..d7238cc6a 100644 --- a/thinkphp/base.php +++ b/thinkphp/base.php @@ -2,62 +2,51 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- - -define('THINK_VERSION', '5.0.9'); -define('THINK_START_TIME', microtime(true)); -define('THINK_START_MEM', memory_get_usage()); -define('EXT', '.php'); -define('DS', DIRECTORY_SEPARATOR); -defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS); -define('LIB_PATH', THINK_PATH . 'library' . DS); -define('CORE_PATH', LIB_PATH . 'think' . DS); -define('TRAIT_PATH', LIB_PATH . 'traits' . DS); -defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . DS); -defined('ROOT_PATH') or define('ROOT_PATH', dirname(realpath(APP_PATH)) . DS); -defined('EXTEND_PATH') or define('EXTEND_PATH', ROOT_PATH . 'extend' . DS); -defined('VENDOR_PATH') or define('VENDOR_PATH', ROOT_PATH . 'vendor' . DS); -defined('RUNTIME_PATH') or define('RUNTIME_PATH', ROOT_PATH . 'runtime' . DS); -defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH . 'log' . DS); -defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH . 'cache' . DS); -defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS); -defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目录 -defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件后缀 -defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 环境变量的配置前缀 - -// 环境常量 -define('IS_CLI', PHP_SAPI == 'cli' ? true : false); -define('IS_WIN', strpos(PHP_OS, 'WIN') !== false); +namespace think; // 载入Loader类 -require CORE_PATH . 'Loader.php'; - -// 加载环境变量配置文件 -if (is_file(ROOT_PATH . '.env')) { - $env = parse_ini_file(ROOT_PATH . '.env', true); - foreach ($env as $key => $val) { - $name = ENV_PREFIX . strtoupper($key); - if (is_array($val)) { - foreach ($val as $k => $v) { - $item = $name . '_' . strtoupper($k); - putenv("$item=$v"); - } - } else { - putenv("$name=$val"); - } - } -} +require __DIR__ . '/library/think/Loader.php'; // 注册自动加载 -\think\Loader::register(); +Loader::register(); // 注册错误和异常处理机制 -\think\Error::register(); +Error::register(); -// 加载惯例配置文件 -\think\Config::set(include THINK_PATH . 'convention' . EXT); +// 实现日志接口 +if (interface_exists('Psr\Log\LoggerInterface')) { + interface LoggerInterface extends \Psr\Log\LoggerInterface + {} +} else { + interface LoggerInterface + {} +} + +// 注册类库别名 +Loader::addClassAlias([ + 'App' => facade\App::class, + 'Build' => facade\Build::class, + 'Cache' => facade\Cache::class, + 'Config' => facade\Config::class, + 'Cookie' => facade\Cookie::class, + 'Db' => Db::class, + 'Debug' => facade\Debug::class, + 'Env' => facade\Env::class, + 'Facade' => Facade::class, + 'Hook' => facade\Hook::class, + 'Lang' => facade\Lang::class, + 'Log' => facade\Log::class, + 'Request' => facade\Request::class, + 'Response' => facade\Response::class, + 'Route' => facade\Route::class, + 'Session' => facade\Session::class, + 'Url' => facade\Url::class, + 'Validate' => facade\Validate::class, + 'View' => facade\View::class, +]); diff --git a/thinkphp/codecov.yml b/thinkphp/codecov.yml deleted file mode 100644 index bef9d643a..000000000 --- a/thinkphp/codecov.yml +++ /dev/null @@ -1,12 +0,0 @@ -comment: - layout: header, changes, diff -coverage: - ignore: - - base.php - - helper.php - - convention.php - - lang/zh-cn.php - - start.php - - console.php - status: - patch: false diff --git a/thinkphp/composer.json b/thinkphp/composer.json index c546e1142..cc4fca912 100644 --- a/thinkphp/composer.json +++ b/thinkphp/composer.json @@ -13,23 +13,23 @@ { "name": "liu21st", "email": "liu21st@gmail.com" + }, + { + "name": "yunwuxin", + "email": "448901948@qq.com" } ], "require": { - "php": ">=5.4.0", - "topthink/think-installer": "~1.0" + "php": ">=5.6.0", + "topthink/think-installer": "2.*" }, "require-dev": { - "phpunit/phpunit": "4.8.*", + "phpunit/phpunit": "^5.0|^6.0", "johnkary/phpunit-speedtrap": "^1.0", "mikey179/vfsStream": "~1.6", "phploc/phploc": "2.*", "sebastian/phpcpd": "2.*", + "squizlabs/php_codesniffer": "2.*", "phpdocumentor/reflection-docblock": "^2.0" - }, - "autoload": { - "psr-4": { - "think\\": "library/think" - } } } diff --git a/thinkphp/console.php b/thinkphp/console.php deleted file mode 100644 index 578e4a7c4..000000000 --- a/thinkphp/console.php +++ /dev/null @@ -1,20 +0,0 @@ - -// +---------------------------------------------------------------------- - -namespace think; - -// ThinkPHP 引导文件 -// 加载基础文件 -require __DIR__ . '/base.php'; - -// 执行应用 -App::initCommon(); -Console::init(); diff --git a/thinkphp/convention.php b/thinkphp/convention.php index c22a7bac5..1d85e56ef 100644 --- a/thinkphp/convention.php +++ b/thinkphp/convention.php @@ -4,115 +4,157 @@ return [ // +---------------------------------------------------------------------- // | 应用设置 // +---------------------------------------------------------------------- + 'app' => [ + // 应用名称 + 'app_name' => '', + // 应用地址 + 'app_host' => '', + // 应用调试模式 + 'app_debug' => false, + // 应用Trace + 'app_trace' => false, + // 应用模式状态 + 'app_status' => '', + // 是否HTTPS + 'is_https' => false, + // 入口自动绑定模块 + 'auto_bind_module' => false, + // 注册的根命名空间 + 'root_namespace' => [], + // 默认输出类型 + 'default_return_type' => 'html', + // 默认AJAX 数据返回格式,可选json xml ... + 'default_ajax_return' => 'json', + // 默认JSONP格式返回的处理方法 + 'default_jsonp_handler' => 'jsonpReturn', + // 默认JSONP处理方法 + 'var_jsonp_handler' => 'callback', + // 默认时区 + 'default_timezone' => 'Asia/Shanghai', + // 是否开启多语言 + 'lang_switch_on' => false, + // 默认验证器 + 'default_validate' => '', + // 默认语言 + 'default_lang' => 'zh-cn', - // 应用调试模式 - 'app_debug' => true, - // 应用Trace - 'app_trace' => false, - // 应用模式状态 - 'app_status' => '', - // 是否支持多模块 - 'app_multi_module' => true, - // 入口自动绑定模块 - 'auto_bind_module' => false, - // 注册的根命名空间 - 'root_namespace' => [], - // 扩展函数文件 - 'extra_file_list' => [THINK_PATH . 'helper' . EXT], - // 默认输出类型 - 'default_return_type' => 'html', - // 默认AJAX 数据返回格式,可选json xml ... - 'default_ajax_return' => 'json', - // 默认JSONP格式返回的处理方法 - 'default_jsonp_handler' => 'jsonpReturn', - // 默认JSONP处理方法 - 'var_jsonp_handler' => 'callback', - // 默认时区 - 'default_timezone' => 'PRC', - // 是否开启多语言 - 'lang_switch_on' => false, - // 默认全局过滤方法 用逗号分隔多个 - 'default_filter' => '', - // 默认语言 - 'default_lang' => 'zh-cn', - // 应用类库后缀 - 'class_suffix' => false, - // 控制器类后缀 - 'controller_suffix' => false, + // +---------------------------------------------------------------------- + // | 模块设置 + // +---------------------------------------------------------------------- - // +---------------------------------------------------------------------- - // | 模块设置 - // +---------------------------------------------------------------------- + // 自动搜索控制器 + 'controller_auto_search' => false, + // 操作方法前缀 + 'use_action_prefix' => false, + // 操作方法后缀 + 'action_suffix' => '', + // 默认的空控制器名 + 'empty_controller' => 'Error', + // 默认的空模块名 + 'empty_module' => '', + // 默认模块名 + 'default_module' => 'index', + // 是否支持多模块 + 'app_multi_module' => true, + // 禁止访问模块 + 'deny_module_list' => ['common'], + // 默认控制器名 + 'default_controller' => 'Index', + // 默认操作名 + 'default_action' => 'index', + // 是否自动转换URL中的控制器和操作名 + 'url_convert' => true, + // 默认的访问控制器层 + 'url_controller_layer' => 'controller', + // 应用类库后缀 + 'class_suffix' => false, + // 控制器类后缀 + 'controller_suffix' => false, - // 默认模块名 - 'default_module' => 'index', - // 禁止访问模块 - 'deny_module_list' => ['common'], - // 默认控制器名 - 'default_controller' => 'Index', - // 默认操作名 - 'default_action' => 'index', - // 默认验证器 - 'default_validate' => '', - // 默认的空控制器名 - 'empty_controller' => 'Error', - // 操作方法前缀 - 'use_action_prefix' => false, - // 操作方法后缀 - 'action_suffix' => '', - // 自动搜索控制器 - 'controller_auto_search' => false, + // +---------------------------------------------------------------------- + // | URL请求设置 + // +---------------------------------------------------------------------- - // +---------------------------------------------------------------------- - // | URL设置 - // +---------------------------------------------------------------------- + // 默认全局过滤方法 用逗号分隔多个 + 'default_filter' => '', + // PATHINFO变量名 用于兼容模式 + 'var_pathinfo' => 's', + // 兼容PATH_INFO获取 + 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], + // HTTPS代理标识 + 'https_agent_name' => '', + // IP代理获取标识 + 'http_agent_ip' => 'HTTP_X_REAL_IP', + // URL伪静态后缀 + 'url_html_suffix' => 'html', + // 域名根,如thinkphp.cn + 'url_domain_root' => '', + // 表单请求类型伪装变量 + 'var_method' => '_method', + // 表单ajax伪装变量 + 'var_ajax' => '_ajax', + // 表单pjax伪装变量 + 'var_pjax' => '_pjax', + // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 + 'request_cache' => false, + // 请求缓存有效期 + 'request_cache_expire' => null, + // 全局请求缓存排除规则 + 'request_cache_except' => [], - // PATHINFO变量名 用于兼容模式 - 'var_pathinfo' => 's', - // 兼容PATH_INFO获取 - 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], - // pathinfo分隔符 - 'pathinfo_depr' => '/', - // URL伪静态后缀 - 'url_html_suffix' => 'html', - // URL普通方式参数 用于自动生成 - 'url_common_param' => false, - // URL参数方式 0 按名称成对解析 1 按顺序解析 - 'url_param_type' => 0, - // 是否开启路由 - 'url_route_on' => true, - // 路由配置文件(支持配置多个) - 'route_config_file' => ['route'], - // 路由使用完整匹配 - 'route_complete_match' => false, - // 是否强制使用路由 - 'url_route_must' => false, - // 域名部署 - 'url_domain_deploy' => false, - // 域名根,如thinkphp.cn - 'url_domain_root' => '', - // 是否自动转换URL中的控制器和操作名 - 'url_convert' => true, - // 默认的访问控制器层 - 'url_controller_layer' => 'controller', - // 表单请求类型伪装变量 - 'var_method' => '_method', - // 表单ajax伪装变量 - 'var_ajax' => '_ajax', - // 表单pjax伪装变量 - 'var_pjax' => '_pjax', - // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 - 'request_cache' => false, - // 请求缓存有效期 - 'request_cache_expire' => null, - // 全局请求缓存排除规则 - 'request_cache_except' => [], + // +---------------------------------------------------------------------- + // | 路由设置 + // +---------------------------------------------------------------------- + + // pathinfo分隔符 + 'pathinfo_depr' => '/', + // URL普通方式参数 用于自动生成 + 'url_common_param' => false, + // URL参数方式 0 按名称成对解析 1 按顺序解析 + 'url_param_type' => 0, + // 是否开启路由延迟解析 + 'url_lazy_route' => false, + // 是否强制使用路由 + 'url_route_must' => false, + // 合并路由规则 + 'route_rule_merge' => false, + // 路由是否完全匹配 + 'route_complete_match' => false, + // 使用注解路由 + 'route_annotation' => false, + // 默认的路由变量规则 + 'default_route_pattern' => '\w+', + // 是否开启路由缓存 + 'route_check_cache' => false, + // 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5 + 'route_check_cache_key' => '', + // 路由缓存的设置 + 'route_cache_option' => [], + + // +---------------------------------------------------------------------- + // | 异常及错误设置 + // +---------------------------------------------------------------------- + + // 默认跳转页面对应的模板文件 + 'dispatch_success_tmpl' => __DIR__ . '/tpl/dispatch_jump.tpl', + 'dispatch_error_tmpl' => __DIR__ . '/tpl/dispatch_jump.tpl', + // 异常页面的模板文件 + 'exception_tmpl' => __DIR__ . '/tpl/think_exception.tpl', + // 错误显示信息,非调试模式有效 + 'error_message' => '页面错误!请稍后再试~', + // 显示错误信息 + 'show_error_msg' => false, + // 异常处理handle类 留空使用 \think\exception\Handle + 'exception_handle' => '', + ], // +---------------------------------------------------------------------- // | 模板设置 // +---------------------------------------------------------------------- - 'template' => [ + 'template' => [ + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 + 'auto_rule' => 1, // 模板引擎类型 支持 php think 支持扩展 'type' => 'Think', // 视图基础目录,配置目录为所有模块的视图起始目录 @@ -122,7 +164,7 @@ return [ // 模板后缀 'view_suffix' => 'html', // 模板文件名分隔符 - 'view_depr' => DS, + 'view_depr' => DIRECTORY_SEPARATOR, // 模板引擎普通标签开始标记 'tpl_begin' => '{', // 模板引擎普通标签结束标记 @@ -133,56 +175,42 @@ return [ 'taglib_end' => '}', ], - // 视图输出字符串内容替换 - 'view_replace_str' => [], - // 默认跳转页面对应的模板文件 - 'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', - 'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', - - // +---------------------------------------------------------------------- - // | 异常及错误设置 - // +---------------------------------------------------------------------- - - // 异常页面的模板文件 - 'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl', - - // 错误显示信息,非调试模式有效 - 'error_message' => '页面错误!请稍后再试~', - // 显示错误信息 - 'show_error_msg' => false, - // 异常处理handle类 留空使用 \think\exception\Handle - 'exception_handle' => '', - // +---------------------------------------------------------------------- // | 日志设置 // +---------------------------------------------------------------------- - 'log' => [ + 'log' => [ // 日志记录方式,内置 file socket 支持扩展 - 'type' => 'File', + 'type' => 'File', // 日志保存目录 - 'path' => LOG_PATH, + //'path' => LOG_PATH, // 日志记录级别 - 'level' => [], + 'level' => [], + // 是否记录trace信息到日志 + 'record_trace' => false, + // 是否JSON格式记录 + 'json' => false, ], // +---------------------------------------------------------------------- // | Trace设置 开启 app_trace 后 有效 // +---------------------------------------------------------------------- - 'trace' => [ + + 'trace' => [ // 内置Html Console 支持扩展 'type' => 'Html', + 'file' => __DIR__ . '/tpl/page_trace.tpl', ], // +---------------------------------------------------------------------- // | 缓存设置 // +---------------------------------------------------------------------- - 'cache' => [ + 'cache' => [ // 驱动方式 'type' => 'File', // 缓存保存目录 - 'path' => CACHE_PATH, + //'path' => CACHE_PATH, // 缓存前缀 'prefix' => '', // 缓存有效期 0表示永久缓存 @@ -193,7 +221,7 @@ return [ // | 会话设置 // +---------------------------------------------------------------------- - 'session' => [ + 'session' => [ 'id' => '', // SESSION_ID的提交变量,解决flash上传跨域 'var_session_id' => '', @@ -210,7 +238,8 @@ return [ // +---------------------------------------------------------------------- // | Cookie设置 // +---------------------------------------------------------------------- - 'cookie' => [ + + 'cookie' => [ // cookie 名称前缀 'prefix' => '', // cookie 保存时间 @@ -231,7 +260,7 @@ return [ // | 数据库设置 // +---------------------------------------------------------------------- - 'database' => [ + 'database' => [ // 数据库类型 'type' => 'mysql', // 数据库连接DSN配置 @@ -272,13 +301,27 @@ return [ 'datetime_format' => 'Y-m-d H:i:s', // 是否需要进行SQL性能分析 'sql_explain' => false, + // 查询对象 + 'query' => '\\think\\db\\Query', ], //分页配置 - 'paginate' => [ + 'paginate' => [ 'type' => 'bootstrap', 'var_page' => 'page', 'list_rows' => 15, ], + //控制台配置 + 'console' => [ + 'name' => 'Think Console', + 'version' => '0.1', + 'user' => null, + 'auto_path' => '', + ], + + // 中间件配置 + 'middleware' => [ + 'default_namespace' => 'app\\http\\middleware\\', + ], ]; diff --git a/thinkphp/helper.php b/thinkphp/helper.php index a23b67942..fc2ca8c85 100644 --- a/thinkphp/helper.php +++ b/thinkphp/helper.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,205 +13,42 @@ // ThinkPHP 助手函数 //------------------------- -use think\Cache; -use think\Config; -use think\Cookie; +use think\Container; use think\Db; -use think\Debug; use think\exception\HttpException; use think\exception\HttpResponseException; -use think\Lang; -use think\Loader; -use think\Log; -use think\Model; -use think\Request; +use think\facade\Cache; +use think\facade\Config; +use think\facade\Cookie; +use think\facade\Debug; +use think\facade\Env; +use think\facade\Hook; +use think\facade\Lang; +use think\facade\Log; +use think\facade\Request; +use think\facade\Route; +use think\facade\Session; +use think\facade\Url; use think\Response; -use think\Session; -use think\Url; -use think\View; +use think\route\RuleItem; -if (!function_exists('load_trait')) { +if (!function_exists('abort')) { /** - * 快速导入Traits PHP5.5以上无需调用 - * @param string $class trait库 - * @param string $ext 类库后缀 - * @return boolean + * 抛出HTTP异常 + * @param integer|Response $code 状态码 或者 Response对象实例 + * @param string $message 错误信息 + * @param array $header 参数 */ - function load_trait($class, $ext = EXT) + function abort($code, $message = null, $header = []) { - return Loader::import($class, TRAIT_PATH, $ext); - } -} - -if (!function_exists('exception')) { - /** - * 抛出异常处理 - * - * @param string $msg 异常消息 - * @param integer $code 异常代码 默认为0 - * @param string $exception 异常类 - * - * @throws Exception - */ - function exception($msg, $code = 0, $exception = '') - { - $e = $exception ?: '\think\Exception'; - throw new $e($msg, $code); - } -} - -if (!function_exists('debug')) { - /** - * 记录时间(微秒)和内存使用情况 - * @param string $start 开始标签 - * @param string $end 结束标签 - * @param integer|string $dec 小数位 如果是m 表示统计内存占用 - * @return mixed - */ - function debug($start, $end = '', $dec = 6) - { - if ('' == $end) { - Debug::remark($start); + if ($code instanceof Response) { + throw new HttpResponseException($code); } else { - return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec); + throw new HttpException($code, $message, null, $header); } } } -if (!function_exists('lang')) { - /** - * 获取语言变量值 - * @param string $name 语言变量名 - * @param array $vars 动态变量值 - * @param string $lang 语言 - * @return mixed - */ - function lang($name, $vars = [], $lang = '') - { - return Lang::get($name, $vars, $lang); - } -} - -if (!function_exists('config')) { - /** - * 获取和设置配置参数 - * @param string|array $name 参数名 - * @param mixed $value 参数值 - * @param string $range 作用域 - * @return mixed - */ - function config($name = '', $value = null, $range = '') - { - if (is_null($value) && is_string($name)) { - return 0 === strpos($name, '?') ? Config::has(substr($name, 1), $range) : Config::get($name, $range); - } else { - return Config::set($name, $value, $range); - } - } -} - -if (!function_exists('input')) { - /** - * 获取输入数据 支持默认值和过滤 - * @param string $key 获取的变量名 - * @param mixed $default 默认值 - * @param string $filter 过滤方法 - * @return mixed - */ - function input($key = '', $default = null, $filter = '') - { - if (0 === strpos($key, '?')) { - $key = substr($key, 1); - $has = true; - } - if ($pos = strpos($key, '.')) { - // 指定参数来源 - list($method, $key) = explode('.', $key, 2); - if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) { - $key = $method . '.' . $key; - $method = 'param'; - } - } else { - // 默认为自动判断 - $method = 'param'; - } - if (isset($has)) { - return request()->has($key, $method, $default); - } else { - return request()->$method($key, $default, $filter); - } - } -} - -if (!function_exists('widget')) { - /** - * 渲染输出Widget - * @param string $name Widget名称 - * @param array $data 传入的参数 - * @return mixed - */ - function widget($name, $data = []) - { - return Loader::action($name, $data, 'widget'); - } -} - -if (!function_exists('model')) { - /** - * 实例化Model - * @param string $name Model名称 - * @param string $layer 业务层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @return \think\Model - */ - function model($name = '', $layer = 'model', $appendSuffix = false) - { - return Loader::model($name, $layer, $appendSuffix); - } -} - -if (!function_exists('validate')) { - /** - * 实例化验证器 - * @param string $name 验证器名称 - * @param string $layer 业务层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @return \think\Validate - */ - function validate($name = '', $layer = 'validate', $appendSuffix = false) - { - return Loader::validate($name, $layer, $appendSuffix); - } -} - -if (!function_exists('db')) { - /** - * 实例化数据库类 - * @param string $name 操作的数据表名称(不含前缀) - * @param array|string $config 数据库配置参数 - * @param bool $force 是否强制重新连接 - * @return \think\db\Query - */ - function db($name = '', $config = [], $force = false) - { - return Db::connect($config, $force)->name($name); - } -} - -if (!function_exists('controller')) { - /** - * 实例化控制器 格式:[模块/]控制器 - * @param string $name 资源地址 - * @param string $layer 控制层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @return \think\Controller - */ - function controller($name, $layer = 'controller', $appendSuffix = false) - { - return Loader::controller($name, $layer, $appendSuffix); - } -} - if (!function_exists('action')) { /** * 调用模块的操作方法 参数格式 [模块/控制器/]操作 @@ -223,92 +60,186 @@ if (!function_exists('action')) { */ function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) { - return Loader::action($url, $vars, $layer, $appendSuffix); + return app()->action($url, $vars, $layer, $appendSuffix); } } -if (!function_exists('import')) { +if (!function_exists('app')) { /** - * 导入所需的类库 同java的Import 本函数有缓存功能 - * @param string $class 类库命名空间字符串 - * @param string $baseUrl 起始路径 - * @param string $ext 导入的文件扩展名 - * @return boolean + * 快速获取容器中的实例 支持依赖注入 + * @param string $name 类名或标识 默认获取当前应用实例 + * @param array $args 参数 + * @param bool $newInstance 是否每次创建新的实例 + * @return mixed|\think\App */ - function import($class, $baseUrl = '', $ext = EXT) + function app($name = 'think\App', $args = [], $newInstance = false) { - return Loader::import($class, $baseUrl, $ext); + return Container::get($name, $args, $newInstance); } } -if (!function_exists('vendor')) { +if (!function_exists('behavior')) { /** - * 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面 - * @param string $class 类库 - * @param string $ext 类库后缀 - * @return boolean - */ - function vendor($class, $ext = EXT) - { - return Loader::import($class, VENDOR_PATH, $ext); - } -} - -if (!function_exists('dump')) { - /** - * 浏览器友好的变量输出 - * @param mixed $var 变量 - * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 - * @param string $label 标签 默认为空 - * @return void|string - */ - function dump($var, $echo = true, $label = null) - { - return Debug::dump($var, $echo, $label); - } -} - -if (!function_exists('url')) { - /** - * Url生成 - * @param string $url 路由地址 - * @param string|array $vars 变量 - * @param bool|string $suffix 生成的URL后缀 - * @param bool|string $domain 域名 - * @return string - */ - function url($url = '', $vars = '', $suffix = true, $domain = false) - { - return Url::build($url, $vars, $suffix, $domain); - } -} - -if (!function_exists('session')) { - /** - * Session管理 - * @param string|array $name session名称,如果为数组表示进行session设置 - * @param mixed $value session值 - * @param string $prefix 前缀 + * 执行某个行为(run方法) 支持依赖注入 + * @param mixed $behavior 行为类名或者别名 + * @param mixed $args 参数 * @return mixed */ - function session($name, $value = '', $prefix = null) + function behavior($behavior, $args = null) { - if (is_array($name)) { - // 初始化 - Session::init($name); - } elseif (is_null($name)) { - // 清除 - Session::clear('' === $value ? null : $value); - } elseif ('' === $value) { - // 判断或获取 - return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix); - } elseif (is_null($value)) { - // 删除 - return Session::delete($name, $prefix); - } else { - // 设置 - return Session::set($name, $value, $prefix); + return Hook::exec($behavior, $args); + } +} + +if (!function_exists('bind')) { + /** + * 绑定一个类到容器 + * @access public + * @param string $abstract 类标识、接口 + * @param mixed $concrete 要绑定的类、闭包或者实例 + * @return Container + */ + function bind($abstract, $concrete = null) + { + return Container::getInstance()->bindTo($abstract, $concrete); + } +} + +if (!function_exists('cache')) { + /** + * 缓存管理 + * @param mixed $name 缓存名称,如果为数组表示进行缓存设置 + * @param mixed $value 缓存值 + * @param mixed $options 缓存参数 + * @param string $tag 缓存标签 + * @return mixed + */ + function cache($name, $value = '', $options = null, $tag = null) + { + if (is_array($options)) { + // 缓存操作的同时初始化 + Cache::connect($options); + } elseif (is_array($name)) { + // 缓存初始化 + return Cache::connect($name); } + + if ('' === $value) { + // 获取缓存 + return 0 === strpos($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name); + } elseif (is_null($value)) { + // 删除缓存 + return Cache::rm($name); + } + + // 缓存数据 + if (is_array($options)) { + $expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间 + } else { + $expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间 + } + + if (is_null($tag)) { + return Cache::set($name, $value, $expire); + } else { + return Cache::tag($tag)->set($name, $value, $expire); + } + } +} + +if (!function_exists('call')) { + /** + * 调用反射执行callable 支持依赖注入 + * @param mixed $callable 支持闭包等callable写法 + * @param array $args 参数 + * @return mixed + */ + function call($callable, $args = []) + { + return Container::getInstance()->invoke($callable, $args); + } +} + +if (!function_exists('class_basename')) { + /** + * 获取类名(不包含命名空间) + * + * @param string|object $class + * @return string + */ + function class_basename($class) + { + $class = is_object($class) ? get_class($class) : $class; + return basename(str_replace('\\', '/', $class)); + } +} + +if (!function_exists('class_uses_recursive')) { + /** + *获取一个类里所有用到的trait,包括父类的 + * + * @param $class + * @return array + */ + function class_uses_recursive($class) + { + if (is_object($class)) { + $class = get_class($class); + } + + $results = []; + $classes = array_merge([$class => $class], class_parents($class)); + foreach ($classes as $class) { + $results += trait_uses_recursive($class); + } + + return array_unique($results); + } +} + +if (!function_exists('config')) { + /** + * 获取和设置配置参数 + * @param string|array $name 参数名 + * @param mixed $value 参数值 + * @return mixed + */ + function config($name = '', $value = null) + { + if (is_null($value) && is_string($name)) { + if ('.' == substr($name, -1)) { + return Config::pull(substr($name, 0, -1)); + } + + return 0 === strpos($name, '?') ? Config::has(substr($name, 1)) : Config::get($name); + } else { + return Config::set($name, $value); + } + } +} + +if (!function_exists('container')) { + /** + * 获取容器对象实例 + * @return Container + */ + function container() + { + return Container::getInstance(); + } +} + +if (!function_exists('controller')) { + /** + * 实例化控制器 格式:[模块/]控制器 + * @param string $name 资源地址 + * @param string $layer 控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return \think\Controller + */ + function controller($name, $layer = 'controller', $appendSuffix = false) + { + return app()->controller($name, $layer, $appendSuffix); } } @@ -330,7 +261,7 @@ if (!function_exists('cookie')) { Cookie::clear($value); } elseif ('' === $value) { // 获取 - return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name, $option); + return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name); } elseif (is_null($value)) { // 删除 return Cookie::delete($name); @@ -341,109 +272,144 @@ if (!function_exists('cookie')) { } } -if (!function_exists('cache')) { +if (!function_exists('db')) { /** - * 缓存管理 - * @param mixed $name 缓存名称,如果为数组表示进行缓存设置 - * @param mixed $value 缓存值 - * @param mixed $options 缓存参数 - * @param string $tag 缓存标签 + * 实例化数据库类 + * @param string $name 操作的数据表名称(不含前缀) + * @param array|string $config 数据库配置参数 + * @param bool $force 是否强制重新连接 + * @return \think\db\Query + */ + function db($name = '', $config = [], $force = true) + { + return Db::connect($config, $force)->name($name); + } +} + +if (!function_exists('debug')) { + /** + * 记录时间(微秒)和内存使用情况 + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer|string $dec 小数位 如果是m 表示统计内存占用 * @return mixed */ - function cache($name, $value = '', $options = null, $tag = null) + function debug($start, $end = '', $dec = 6) { - if (is_array($options)) { - // 缓存操作的同时初始化 - $cache = Cache::connect($options); - } elseif (is_array($name)) { - // 缓存初始化 - return Cache::connect($name); + if ('' == $end) { + Debug::remark($start); } else { - $cache = Cache::init(); + return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec); + } + } +} + +if (!function_exists('download')) { + /** + * 获取\think\response\Download对象实例 + * @param string $filename 要下载的文件 + * @param string $name 显示文件名 + * @param bool $content 是否为内容 + * @param integer $expire 有效期(秒) + * @return \think\response\Download + */ + function download($filename, $name = '', $content = false, $expire = 360, $openinBrowser = false) + { + return Response::create($filename, 'download')->name($name)->isContent($content)->expire($expire)->openinBrowser($openinBrowser); + } +} + +if (!function_exists('dump')) { + /** + * 浏览器友好的变量输出 + * @param mixed $var 变量 + * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 + * @param string $label 标签 默认为空 + * @return void|string + */ + function dump($var, $echo = true, $label = null) + { + return Debug::dump($var, $echo, $label); + } +} + +if (!function_exists('env')) { + /** + * 获取环境变量值 + * @access public + * @param string $name 环境变量名(支持二级 .号分割) + * @param string $default 默认值 + * @return mixed + */ + function env($name = null, $default = null) + { + return Env::get($name, $default); + } +} + +if (!function_exists('exception')) { + /** + * 抛出异常处理 + * + * @param string $msg 异常消息 + * @param integer $code 异常代码 默认为0 + * @param string $exception 异常类 + * + * @throws Exception + */ + function exception($msg, $code = 0, $exception = '') + { + $e = $exception ?: '\think\Exception'; + throw new $e($msg, $code); + } +} + +if (!function_exists('halt')) { + /** + * 调试变量并且中断输出 + * @param mixed $var 调试变量或者信息 + */ + function halt($var) + { + dump($var); + + throw new HttpResponseException(new Response); + } +} + +if (!function_exists('input')) { + /** + * 获取输入数据 支持默认值和过滤 + * @param string $key 获取的变量名 + * @param mixed $default 默认值 + * @param string $filter 过滤方法 + * @return mixed + */ + function input($key = '', $default = null, $filter = '') + { + if (0 === strpos($key, '?')) { + $key = substr($key, 1); + $has = true; } - if (is_null($name)) { - return $cache->clear($value); - } elseif ('' === $value) { - // 获取缓存 - return 0 === strpos($name, '?') ? $cache->has(substr($name, 1)) : $cache->get($name); - } elseif (is_null($value)) { - // 删除缓存 - return $cache->rm($name); - } elseif (0 === strpos($name, '?') && '' !== $value) { - $expire = is_numeric($options) ? $options : null; - return $cache->remember(substr($name, 1), $value, $expire); - } else { - // 缓存数据 - if (is_array($options)) { - $expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间 + if ($pos = strpos($key, '.')) { + // 指定参数来源 + $method = substr($key, 0, $pos); + if (in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) { + $key = substr($key, $pos + 1); } else { - $expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间 + $method = 'param'; } - if (is_null($tag)) { - return $cache->set($name, $value, $expire); - } else { - return $cache->tag($tag)->set($name, $value, $expire); - } - } - } -} - -if (!function_exists('trace')) { - /** - * 记录日志信息 - * @param mixed $log log信息 支持字符串和数组 - * @param string $level 日志级别 - * @return void|array - */ - function trace($log = '[think]', $level = 'log') - { - if ('[think]' === $log) { - return Log::getLog(); } else { - Log::record($log, $level); + // 默认为自动判断 + $method = 'param'; } - } -} -if (!function_exists('request')) { - /** - * 获取当前Request对象实例 - * @return Request - */ - function request() - { - return Request::instance(); - } -} - -if (!function_exists('response')) { - /** - * 创建普通 Response 对象实例 - * @param mixed $data 输出数据 - * @param int|string $code 状态码 - * @param array $header 头信息 - * @param string $type - * @return Response - */ - function response($data = [], $code = 200, $header = [], $type = 'html') - { - return Response::create($data, $type, $code, $header); - } -} - -if (!function_exists('view')) { - /** - * 渲染模板输出 - * @param string $template 模板文件 - * @param array $vars 模板变量 - * @param array $replace 模板替换 - * @param integer $code 状态码 - * @return \think\response\View - */ - function view($template = '', $vars = [], $replace = [], $code = 200) - { - return Response::create($template, 'view', $code)->replace($replace)->assign($vars); + if (isset($has)) { + return request()->has($key, $method, $default); + } else { + return request()->$method($key, $default, $filter); + } } } @@ -477,6 +443,253 @@ if (!function_exists('jsonp')) { } } +if (!function_exists('lang')) { + /** + * 获取语言变量值 + * @param string $name 语言变量名 + * @param array $vars 动态变量值 + * @param string $lang 语言 + * @return mixed + */ + function lang($name, $vars = [], $lang = '') + { + return Lang::get($name, $vars, $lang); + } +} + +if (!function_exists('model')) { + /** + * 实例化Model + * @param string $name Model名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return \think\Model + */ + function model($name = '', $layer = 'model', $appendSuffix = false) + { + return app()->model($name, $layer, $appendSuffix); + } +} + +if (!function_exists('parse_name')) { + /** + * 字符串命名风格转换 + * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 + * @param string $name 字符串 + * @param integer $type 转换类型 + * @param bool $ucfirst 首字母是否大写(驼峰规则) + * @return string + */ + function parse_name($name, $type = 0, $ucfirst = true) + { + if ($type) { + $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { + return strtoupper($match[1]); + }, $name); + + return $ucfirst ? ucfirst($name) : lcfirst($name); + } else { + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); + } + } +} + +if (!function_exists('redirect')) { + /** + * 获取\think\response\Redirect对象实例 + * @param mixed $url 重定向地址 支持Url::build方法的地址 + * @param array|integer $params 额外参数 + * @param integer $code 状态码 + * @return \think\response\Redirect + */ + function redirect($url = [], $params = [], $code = 302) + { + if (is_integer($params)) { + $code = $params; + $params = []; + } + + return Response::create($url, 'redirect', $code)->params($params); + } +} + +if (!function_exists('request')) { + /** + * 获取当前Request对象实例 + * @return Request + */ + function request() + { + return app('request'); + } +} + +if (!function_exists('response')) { + /** + * 创建普通 Response 对象实例 + * @param mixed $data 输出数据 + * @param int|string $code 状态码 + * @param array $header 头信息 + * @param string $type + * @return Response + */ + function response($data = '', $code = 200, $header = [], $type = 'html') + { + return Response::create($data, $type, $code, $header); + } +} + +if (!function_exists('route')) { + /** + * 路由注册 + * @param string $rule 路由规则 + * @param mixed $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem + */ + function route($rule, $route, $option = [], $pattern = []) + { + return Route::rule($rule, $route, '*', $option, $pattern); + } +} + +if (!function_exists('session')) { + /** + * Session管理 + * @param string|array $name session名称,如果为数组表示进行session设置 + * @param mixed $value session值 + * @param string $prefix 前缀 + * @return mixed + */ + function session($name, $value = '', $prefix = null) + { + if (is_array($name)) { + // 初始化 + Session::init($name); + } elseif (is_null($name)) { + // 清除 + Session::clear($value); + } elseif ('' === $value) { + // 判断或获取 + return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix); + } elseif (is_null($value)) { + // 删除 + return Session::delete($name, $prefix); + } else { + // 设置 + return Session::set($name, $value, $prefix); + } + } +} + +if (!function_exists('token')) { + /** + * 生成表单令牌 + * @param string $name 令牌名称 + * @param mixed $type 令牌生成方法 + * @return string + */ + function token($name = '__token__', $type = 'md5') + { + $token = Request::token($name, $type); + + return ''; + } +} + +if (!function_exists('trace')) { + /** + * 记录日志信息 + * @param mixed $log log信息 支持字符串和数组 + * @param string $level 日志级别 + * @return array|void + */ + function trace($log = '[think]', $level = 'log') + { + if ('[think]' === $log) { + return Log::getLog(); + } else { + Log::record($log, $level); + } + } +} + +if (!function_exists('trait_uses_recursive')) { + /** + * 获取一个trait里所有引用到的trait + * + * @param string $trait + * @return array + */ + function trait_uses_recursive($trait) + { + $traits = class_uses($trait); + foreach ($traits as $trait) { + $traits += trait_uses_recursive($trait); + } + + return $traits; + } +} + +if (!function_exists('url')) { + /** + * Url生成 + * @param string $url 路由地址 + * @param string|array $vars 变量 + * @param bool|string $suffix 生成的URL后缀 + * @param bool|string $domain 域名 + * @return string + */ + function url($url = '', $vars = '', $suffix = true, $domain = false) + { + return Url::build($url, $vars, $suffix, $domain); + } +} + +if (!function_exists('validate')) { + /** + * 实例化验证器 + * @param string $name 验证器名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return \think\Validate + */ + function validate($name = '', $layer = 'validate', $appendSuffix = false) + { + return app()->validate($name, $layer, $appendSuffix); + } +} + +if (!function_exists('view')) { + /** + * 渲染模板输出 + * @param string $template 模板文件 + * @param array $vars 模板变量 + * @param integer $code 状态码 + * @param callable $filter 内容过滤 + * @return \think\response\View + */ + function view($template = '', $vars = [], $code = 200, $filter = null) + { + return Response::create($template, 'view', $code)->assign($vars)->filter($filter); + } +} + +if (!function_exists('widget')) { + /** + * 渲染输出Widget + * @param string $name Widget名称 + * @param array $data 传入的参数 + * @return mixed + */ + function widget($name, $data = []) + { + return app()->action($name, $data, 'widget'); + } +} + if (!function_exists('xml')) { /** * 获取\think\response\Xml对象实例 @@ -492,98 +705,16 @@ if (!function_exists('xml')) { } } -if (!function_exists('redirect')) { +if (!function_exists('yaconf')) { /** - * 获取\think\response\Redirect对象实例 - * @param mixed $url 重定向地址 支持Url::build方法的地址 - * @param array|integer $params 额外参数 - * @param integer $code 状态码 - * @param array $with 隐式传参 - * @return \think\response\Redirect + * 获取yaconf配置 + * + * @param string $name 配置参数名 + * @param mixed $default 默认值 + * @return mixed */ - function redirect($url = [], $params = [], $code = 302, $with = []) + function yaconf($name, $default = null) { - if (is_integer($params)) { - $code = $params; - $params = []; - } - return Response::create($url, 'redirect', $code)->params($params)->with($with); - } -} - -if (!function_exists('abort')) { - /** - * 抛出HTTP异常 - * @param integer|Response $code 状态码 或者 Response对象实例 - * @param string $message 错误信息 - * @param array $header 参数 - */ - function abort($code, $message = null, $header = []) - { - if ($code instanceof Response) { - throw new HttpResponseException($code); - } else { - throw new HttpException($code, $message, null, $header); - } - } -} - -if (!function_exists('halt')) { - /** - * 调试变量并且中断输出 - * @param mixed $var 调试变量或者信息 - */ - function halt($var) - { - dump($var); - throw new HttpResponseException(new Response); - } -} - -if (!function_exists('token')) { - /** - * 生成表单令牌 - * @param string $name 令牌名称 - * @param mixed $type 令牌生成方法 - * @return string - */ - function token($name = '__token__', $type = 'md5') - { - $token = Request::instance()->token($name, $type); - return ''; - } -} - -if (!function_exists('load_relation')) { - /** - * 延迟预载入关联查询 - * @param mixed $resultSet 数据集 - * @param mixed $relation 关联 - * @return array - */ - function load_relation($resultSet, $relation) - { - $item = current($resultSet); - if ($item instanceof Model) { - $item->eagerlyResultSet($resultSet, $relation); - } - return $resultSet; - } -} - -if (!function_exists('collection')) { - /** - * 数组转换为数据集对象 - * @param array $resultSet 数据集数组 - * @return \think\model\Collection|\think\Collection - */ - function collection($resultSet) - { - $item = current($resultSet); - if ($item instanceof Model) { - return \think\model\Collection::make($resultSet); - } else { - return \think\Collection::make($resultSet); - } + return Config::yaconf($name, $default); } } diff --git a/thinkphp/lang/zh-cn.php b/thinkphp/lang/zh-cn.php index c6c5a50f5..1e0508204 100644 --- a/thinkphp/lang/zh-cn.php +++ b/thinkphp/lang/zh-cn.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,57 +12,133 @@ // 核心中文语言包 return [ // 系统错误提示 - 'Undefined variable' => '未定义变量', - 'Undefined index' => '未定义数组索引', - 'Undefined offset' => '未定义数组下标', - 'Parse error' => '语法解析错误', - 'Type error' => '类型错误', - 'Fatal error' => '致命错误', - 'syntax error' => '语法错误', + 'Undefined variable' => '未定义变量', + 'Undefined index' => '未定义数组索引', + 'Undefined offset' => '未定义数组下标', + 'Parse error' => '语法解析错误', + 'Type error' => '类型错误', + 'Fatal error' => '致命错误', + 'syntax error' => '语法错误', // 框架核心错误提示 - 'dispatch type not support' => '不支持的调度类型', - 'method param miss' => '方法参数错误', - 'method not exists' => '方法不存在', - 'module not exists' => '模块不存在', - 'controller not exists' => '控制器不存在', - 'class not exists' => '类不存在', - 'property not exists' => '类的属性不存在', - 'template not exists' => '模板文件不存在', - 'illegal controller name' => '非法的控制器名称', - 'illegal action name' => '非法的操作名称', - 'url suffix deny' => '禁止的URL后缀访问', - 'Route Not Found' => '当前访问路由未定义', - 'Underfined db type' => '未定义数据库类型', - 'variable type error' => '变量类型错误', - 'PSR-4 error' => 'PSR-4 规范错误', - 'not support total' => '简洁模式下不能获取数据总数', - 'not support last' => '简洁模式下不能获取最后一页', - 'error session handler' => '错误的SESSION处理器类', - 'not allow php tag' => '模板不允许使用PHP语法', - 'not support' => '不支持', - 'redisd master' => 'Redisd 主服务器错误', - 'redisd slave' => 'Redisd 从服务器错误', - 'must run at sae' => '必须在SAE运行', - 'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务', - 'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务', - 'fields not exists' => '数据表字段不存在', - 'where express error' => '查询表达式错误', - 'no data to update' => '没有任何数据需要更新', - 'miss data to insert' => '缺少需要写入的数据', - 'miss complex primary data' => '缺少复合主键数据', - 'miss update condition' => '缺少更新条件', - 'model data Not Found' => '模型数据不存在', - 'table data not Found' => '表数据不存在', - 'delete without condition' => '没有条件不会执行删除操作', - 'miss relation data' => '缺少关联表数据', - 'tag attr must' => '模板标签属性必须', - 'tag error' => '模板标签错误', - 'cache write error' => '缓存写入失败', - 'sae mc write error' => 'SAE mc 写入错误', - 'route name not exists' => '路由标识不存在(或参数不够)', - 'invalid request' => '非法请求', - 'bind attr has exists' => '模型的属性已经存在', - 'relation data not exists' => '关联数据不存在', - 'relation not support' => '关联不支持', + 'dispatch type not support' => '不支持的调度类型', + 'method param miss' => '方法参数错误', + 'method not exists' => '方法不存在', + 'function not exists' => '函数不存在', + 'file not exists' => '文件不存在', + 'module not exists' => '模块不存在', + 'controller not exists' => '控制器不存在', + 'class not exists' => '类不存在', + 'property not exists' => '类的属性不存在', + 'template not exists' => '模板文件不存在', + 'illegal controller name' => '非法的控制器名称', + 'illegal action name' => '非法的操作名称', + 'url suffix deny' => '禁止的URL后缀访问', + 'Route Not Found' => '当前访问路由未定义或不匹配', + 'Undefined db type' => '未定义数据库类型', + 'variable type error' => '变量类型错误', + 'PSR-4 error' => 'PSR-4 规范错误', + 'not support total' => '简洁模式下不能获取数据总数', + 'not support last' => '简洁模式下不能获取最后一页', + 'error session handler' => '错误的SESSION处理器类', + 'not allow php tag' => '模板不允许使用PHP语法', + 'not support' => '不支持', + 'redisd master' => 'Redisd 主服务器错误', + 'redisd slave' => 'Redisd 从服务器错误', + 'must run at sae' => '必须在SAE运行', + 'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务', + 'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务', + 'fields not exists' => '数据表字段不存在', + 'where express error' => '查询表达式错误', + 'order express error' => '排序表达式错误', + 'no data to update' => '没有任何数据需要更新', + 'miss data to insert' => '缺少需要写入的数据', + 'not support data' => '不支持的数据表达式', + 'miss complex primary data' => '缺少复合主键数据', + 'miss update condition' => '缺少更新条件', + 'model data Not Found' => '模型数据不存在', + 'table data not Found' => '表数据不存在', + 'delete without condition' => '没有条件不会执行删除操作', + 'miss relation data' => '缺少关联表数据', + 'tag attr must' => '模板标签属性必须', + 'tag error' => '模板标签错误', + 'cache write error' => '缓存写入失败', + 'sae mc write error' => 'SAE mc 写入错误', + 'route name not exists' => '路由标识不存在(或参数不够)', + 'invalid request' => '非法请求', + 'bind attr has exists' => '模型的属性已经存在', + 'relation data not exists' => '关联数据不存在', + 'relation not support' => '关联不支持', + 'chunk not support order' => 'Chunk不支持调用order方法', + 'route pattern error' => '路由变量规则定义错误', + 'route behavior will not support' => '路由行为废弃(使用中间件替代)', + 'closure not support cache(true)' => '使用闭包查询不支持cache(true),请指定缓存Key', + + // 上传错误信息 + 'unknown upload error' => '未知上传错误!', + 'file write error' => '文件写入失败!', + 'upload temp dir not found' => '找不到临时文件夹!', + 'no file to uploaded' => '没有文件被上传!', + 'only the portion of file is uploaded' => '文件只有部分被上传!', + 'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!', + 'upload write error' => '文件上传保存错误!', + 'has the same filename: {:filename}' => '存在同名文件:{:filename}', + 'upload illegal files' => '非法上传文件', + 'illegal image files' => '非法图片文件', + 'extensions to upload is not allowed' => '上传文件后缀不允许', + 'mimetype to upload is not allowed' => '上传文件MIME类型不允许!', + 'filesize not match' => '上传文件大小不符!', + 'directory {:path} creation failed' => '目录 {:path} 创建失败!', + + 'The middleware must return Response instance' => '中间件方法必须返回Response对象实例', + 'The queue was exhausted, with no response returned' => '中间件队列为空', + // Validate Error Message + ':attribute require' => ':attribute不能为空', + ':attribute must' => ':attribute必须', + ':attribute must be numeric' => ':attribute必须是数字', + ':attribute must be integer' => ':attribute必须是整数', + ':attribute must be float' => ':attribute必须是浮点数', + ':attribute must be bool' => ':attribute必须是布尔值', + ':attribute not a valid email address' => ':attribute格式不符', + ':attribute not a valid mobile' => ':attribute格式不符', + ':attribute must be a array' => ':attribute必须是数组', + ':attribute must be yes,on or 1' => ':attribute必须是yes、on或者1', + ':attribute not a valid datetime' => ':attribute不是一个有效的日期或时间格式', + ':attribute not a valid file' => ':attribute不是有效的上传文件', + ':attribute not a valid image' => ':attribute不是有效的图像文件', + ':attribute must be alpha' => ':attribute只能是字母', + ':attribute must be alpha-numeric' => ':attribute只能是字母和数字', + ':attribute must be alpha-numeric, dash, underscore' => ':attribute只能是字母、数字和下划线_及破折号-', + ':attribute not a valid domain or ip' => ':attribute不是有效的域名或者IP', + ':attribute must be chinese' => ':attribute只能是汉字', + ':attribute must be chinese or alpha' => ':attribute只能是汉字、字母', + ':attribute must be chinese,alpha-numeric' => ':attribute只能是汉字、字母和数字', + ':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-', + ':attribute not a valid url' => ':attribute不是有效的URL地址', + ':attribute not a valid ip' => ':attribute不是有效的IP地址', + ':attribute must be dateFormat of :rule' => ':attribute必须使用日期格式 :rule', + ':attribute must be in :rule' => ':attribute必须在 :rule 范围内', + ':attribute be notin :rule' => ':attribute不能在 :rule 范围内', + ':attribute must between :1 - :2' => ':attribute只能在 :1 - :2 之间', + ':attribute not between :1 - :2' => ':attribute不能在 :1 - :2 之间', + 'size of :attribute must be :rule' => ':attribute长度不符合要求 :rule', + 'max size of :attribute must be :rule' => ':attribute长度不能超过 :rule', + 'min size of :attribute must be :rule' => ':attribute长度不能小于 :rule', + ':attribute cannot be less than :rule' => ':attribute日期不能小于 :rule', + ':attribute cannot exceed :rule' => ':attribute日期不能超过 :rule', + ':attribute not within :rule' => '不在有效期内 :rule', + 'access IP is not allowed' => '不允许的IP访问', + 'access IP denied' => '禁止的IP访问', + ':attribute out of accord with :2' => ':attribute和确认字段:2不一致', + ':attribute cannot be same with :2' => ':attribute和比较字段:2不能相同', + ':attribute must greater than or equal :rule' => ':attribute必须大于等于 :rule', + ':attribute must greater than :rule' => ':attribute必须大于 :rule', + ':attribute must less than or equal :rule' => ':attribute必须小于等于 :rule', + ':attribute must less than :rule' => ':attribute必须小于 :rule', + ':attribute must equal :rule' => ':attribute必须等于 :rule', + ':attribute has exists' => ':attribute已存在', + ':attribute not conform to the rules' => ':attribute不符合指定规则', + 'invalid Request method' => '无效的请求类型', + 'invalid token' => '令牌数据无效', + 'not conform to the rules' => '规则错误', ]; diff --git a/thinkphp/library/think/App.php b/thinkphp/library/think/App.php index f6ed5fdf7..6e713240f 100644 --- a/thinkphp/library/think/App.php +++ b/thinkphp/library/think/App.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,578 +11,971 @@ namespace think; -use think\exception\HttpException; +use think\exception\ClassNotFoundException; use think\exception\HttpResponseException; -use think\exception\RouteNotFoundException; +use think\route\Dispatch; /** * App 应用管理 - * @author liu21st */ -class App +class App extends Container { + const VERSION = '5.1.34 LTS'; + /** - * @var bool 是否初始化过 + * 当前模块路径 + * @var string */ - protected static $init = false; + protected $modulePath; /** - * @var string 当前模块路径 + * 应用调试模式 + * @var bool */ - public static $modulePath; + protected $appDebug = true; /** - * @var bool 应用调试模式 + * 应用开始时间 + * @var float */ - public static $debug = true; + protected $beginTime; /** - * @var string 应用类库命名空间 + * 应用内存初始占用 + * @var integer */ - public static $namespace = 'app'; + protected $beginMem; /** - * @var bool 应用类库后缀 + * 应用类库命名空间 + * @var string */ - public static $suffix = false; + protected $namespace = 'app'; /** - * @var bool 应用路由检测 + * 应用类库后缀 + * @var bool */ - protected static $routeCheck; + protected $suffix = false; /** - * @var bool 严格路由检测 + * 严格路由检测 + * @var bool */ - protected static $routeMust; - - protected static $dispatch; - protected static $file = []; + protected $routeMust; /** - * 执行应用程序 + * 应用类库目录 + * @var string + */ + protected $appPath; + + /** + * 框架目录 + * @var string + */ + protected $thinkPath; + + /** + * 应用根目录 + * @var string + */ + protected $rootPath; + + /** + * 运行时目录 + * @var string + */ + protected $runtimePath; + + /** + * 配置目录 + * @var string + */ + protected $configPath; + + /** + * 路由目录 + * @var string + */ + protected $routePath; + + /** + * 配置后缀 + * @var string + */ + protected $configExt; + + /** + * 应用调度实例 + * @var Dispatch + */ + protected $dispatch; + + /** + * 绑定模块(控制器) + * @var string + */ + protected $bindModule; + + /** + * 初始化 + * @var bool + */ + protected $initialized = false; + + public function __construct($appPath = '') + { + $this->thinkPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR; + $this->path($appPath); + } + + /** + * 绑定模块或者控制器 * @access public - * @param Request $request Request对象 - * @return Response - * @throws Exception + * @param string $bind + * @return $this */ - public static function run(Request $request = null) + public function bind($bind) { - is_null($request) && $request = Request::instance(); - - try { - $config = self::initCommon(); - if (defined('BIND_MODULE')) { - // 模块/控制器绑定 - BIND_MODULE && Route::bind(BIND_MODULE); - } elseif ($config['auto_bind_module']) { - // 入口自动绑定 - $name = pathinfo($request->baseFile(), PATHINFO_FILENAME); - if ($name && 'index' != $name && is_dir(APP_PATH . $name)) { - Route::bind($name); - } - } - - $request->filter($config['default_filter']); - - // 默认语言 - Lang::range($config['default_lang']); - if ($config['lang_switch_on']) { - // 开启多语言机制 检测当前语言 - Lang::detect(); - } - $request->langset(Lang::range()); - - // 加载系统语言包 - Lang::load([ - THINK_PATH . 'lang' . DS . $request->langset() . EXT, - APP_PATH . 'lang' . DS . $request->langset() . EXT, - ]); - - // 获取应用调度信息 - $dispatch = self::$dispatch; - if (empty($dispatch)) { - // 进行URL路由检测 - $dispatch = self::routeCheck($request, $config); - } - // 记录当前调度信息 - $request->dispatch($dispatch); - - // 记录路由和请求信息 - if (self::$debug) { - Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info'); - Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info'); - Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info'); - } - - // 监听app_begin - Hook::listen('app_begin', $dispatch); - // 请求缓存检查 - $request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']); - - $data = self::exec($dispatch, $config); - } catch (HttpResponseException $exception) { - $data = $exception->getResponse(); - } - - // 清空类的实例化 - Loader::clearInstance(); - - // 输出数据到客户端 - if ($data instanceof Response) { - $response = $data; - } elseif (!is_null($data)) { - // 默认自动识别响应输出类型 - $isAjax = $request->isAjax(); - $type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); - $response = Response::create($data, $type); - } else { - $response = Response::create(); - } - - // 监听app_end - Hook::listen('app_end', $response); - - return $response; + $this->bindModule = $bind; + return $this; } /** - * 设置当前请求的调度信息 + * 设置应用类库目录 * @access public - * @param array|string $dispatch 调度信息 - * @param string $type 调度类型 - * @return void + * @param string $path 路径 + * @return $this */ - public static function dispatch($dispatch, $type = 'module') + public function path($path) { - self::$dispatch = ['type' => $type, $type => $dispatch]; - } + $this->appPath = $path ? realpath($path) . DIRECTORY_SEPARATOR : $this->getAppPath(); - /** - * 执行函数或者闭包方法 支持参数调用 - * @access public - * @param string|array|\Closure $function 函数或者闭包 - * @param array $vars 变量 - * @return mixed - */ - public static function invokeFunction($function, $vars = []) - { - $reflect = new \ReflectionFunction($function); - $args = self::bindParams($reflect, $vars); - // 记录执行信息 - self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); - return $reflect->invokeArgs($args); - } - - /** - * 调用反射执行类的方法 支持参数绑定 - * @access public - * @param string|array $method 方法 - * @param array $vars 变量 - * @return mixed - */ - public static function invokeMethod($method, $vars = []) - { - if (is_array($method)) { - $class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]); - $reflect = new \ReflectionMethod($class, $method[1]); - } else { - // 静态方法 - $reflect = new \ReflectionMethod($method); - } - $args = self::bindParams($reflect, $vars); - - self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info'); - return $reflect->invokeArgs(isset($class) ? $class : null, $args); - } - - /** - * 调用反射执行类的实例化 支持依赖注入 - * @access public - * @param string $class 类名 - * @param array $vars 变量 - * @return mixed - */ - public static function invokeClass($class, $vars = []) - { - $reflect = new \ReflectionClass($class); - $constructor = $reflect->getConstructor(); - if ($constructor) { - $args = self::bindParams($constructor, $vars); - } else { - $args = []; - } - return $reflect->newInstanceArgs($args); - } - - /** - * 绑定参数 - * @access private - * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类 - * @param array $vars 变量 - * @return array - */ - private static function bindParams($reflect, $vars = []) - { - if (empty($vars)) { - // 自动获取请求变量 - if (Config::get('url_param_type')) { - $vars = Request::instance()->route(); - } else { - $vars = Request::instance()->param(); - } - } - $args = []; - if ($reflect->getNumberOfParameters() > 0) { - // 判断数组类型 数字数组时按顺序绑定参数 - reset($vars); - $type = key($vars) === 0 ? 1 : 0; - $params = $reflect->getParameters(); - foreach ($params as $param) { - $args[] = self::getParamValue($param, $vars, $type); - } - } - return $args; - } - - /** - * 获取参数值 - * @access private - * @param \ReflectionParameter $param - * @param array $vars 变量 - * @param string $type - * @return array - */ - private static function getParamValue($param, &$vars, $type) - { - $name = $param->getName(); - $class = $param->getClass(); - if ($class) { - $className = $class->getName(); - $bind = Request::instance()->$name; - if ($bind instanceof $className) { - $result = $bind; - } else { - if (method_exists($className, 'invoke')) { - $method = new \ReflectionMethod($className, 'invoke'); - if ($method->isPublic() && $method->isStatic()) { - return $className::invoke(Request::instance()); - } - } - $result = method_exists($className, 'instance') ? $className::instance() : new $className; - } - } elseif (1 == $type && !empty($vars)) { - $result = array_shift($vars); - } elseif (0 == $type && isset($vars[$name])) { - $result = $vars[$name]; - } elseif ($param->isDefaultValueAvailable()) { - $result = $param->getDefaultValue(); - } else { - throw new \InvalidArgumentException('method param miss:' . $name); - } - return $result; - } - - protected static function exec($dispatch, $config) - { - switch ($dispatch['type']) { - case 'redirect': - // 执行重定向跳转 - $data = Response::create($dispatch['url'], 'redirect')->code($dispatch['status']); - break; - case 'module': - // 模块/控制器/操作 - $data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null); - break; - case 'controller': - // 执行控制器操作 - $vars = array_merge(Request::instance()->param(), $dispatch['var']); - $data = Loader::action($dispatch['controller'], $vars, $config['url_controller_layer'], $config['controller_suffix']); - break; - case 'method': - // 执行回调方法 - $vars = array_merge(Request::instance()->param(), $dispatch['var']); - $data = self::invokeMethod($dispatch['method'], $vars); - break; - case 'function': - // 执行闭包 - $data = self::invokeFunction($dispatch['function']); - break; - case 'response': - $data = $dispatch['response']; - break; - default: - throw new \InvalidArgumentException('dispatch type not support'); - } - return $data; - } - - /** - * 执行模块 - * @access public - * @param array $result 模块/控制器/操作 - * @param array $config 配置参数 - * @param bool $convert 是否自动转换控制器和操作名 - * @return mixed - */ - public static function module($result, $config, $convert = null) - { - if (is_string($result)) { - $result = explode('/', $result); - } - $request = Request::instance(); - if ($config['app_multi_module']) { - // 多模块部署 - $module = strip_tags(strtolower($result[0] ?: $config['default_module'])); - $bind = Route::getBind('module'); - $available = false; - if ($bind) { - // 绑定模块 - list($bindModule) = explode('/', $bind); - if (empty($result[0])) { - $module = $bindModule; - $available = true; - } elseif ($module == $bindModule) { - $available = true; - } - } elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) { - $available = true; - } - - // 模块初始化 - if ($module && $available) { - // 初始化模块 - $request->module($module); - $config = self::init($module); - // 模块请求缓存检查 - $request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']); - } else { - throw new HttpException(404, 'module not exists:' . $module); - } - } else { - // 单一模块部署 - $module = ''; - $request->module($module); - } - // 当前模块路径 - App::$modulePath = APP_PATH . ($module ? $module . DS : ''); - - // 是否自动转换控制器和操作名 - $convert = is_bool($convert) ? $convert : $config['url_convert']; - // 获取控制器名 - $controller = strip_tags($result[1] ?: $config['default_controller']); - $controller = $convert ? strtolower($controller) : $controller; - - // 获取操作名 - $actionName = strip_tags($result[2] ?: $config['default_action']); - $actionName = $convert ? strtolower($actionName) : $actionName; - - // 设置当前请求的控制器、操作 - $request->controller(Loader::parseName($controller, 1))->action($actionName); - - // 监听module_init - Hook::listen('module_init', $request); - - $instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']); - if (is_null($instance)) { - throw new HttpException(404, 'controller not exists:' . Loader::parseName($controller, 1)); - } - // 获取当前操作名 - $action = $actionName . $config['action_suffix']; - - $vars = []; - if (is_callable([$instance, $action])) { - // 执行操作方法 - $call = [$instance, $action]; - } elseif (is_callable([$instance, '_empty'])) { - // 空操作 - $call = [$instance, '_empty']; - $vars = [$actionName]; - } else { - // 操作不存在 - throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()'); - } - - Hook::listen('action_begin', $call); - - return self::invokeMethod($call, $vars); + return $this; } /** * 初始化应用 + * @access public + * @return void */ - public static function initCommon() + public function initialize() { - if (empty(self::$init)) { - if (defined('APP_NAMESPACE')) { - self::$namespace = APP_NAMESPACE; - } - Loader::addNamespace(self::$namespace, APP_PATH); - - // 初始化应用 - $config = self::init(); - self::$suffix = $config['class_suffix']; - - // 应用调试模式 - self::$debug = Env::get('app_debug', Config::get('app_debug')); - if (!self::$debug) { - ini_set('display_errors', 'Off'); - } elseif (!IS_CLI) { - //重新申请一块比较大的buffer - if (ob_get_level() > 0) { - $output = ob_get_clean(); - } - ob_start(); - if (!empty($output)) { - echo $output; - } - } - - if (!empty($config['root_namespace'])) { - Loader::addNamespace($config['root_namespace']); - } - - // 加载额外文件 - if (!empty($config['extra_file_list'])) { - foreach ($config['extra_file_list'] as $file) { - $file = strpos($file, '.') ? $file : APP_PATH . $file . EXT; - if (is_file($file) && !isset(self::$file[$file])) { - include $file; - self::$file[$file] = true; - } - } - } - - // 设置系统时区 - date_default_timezone_set($config['default_timezone']); - - // 监听app_init - Hook::listen('app_init'); - - self::$init = true; + if ($this->initialized) { + return; } - return Config::get(); + + $this->initialized = true; + $this->beginTime = microtime(true); + $this->beginMem = memory_get_usage(); + + $this->rootPath = dirname($this->appPath) . DIRECTORY_SEPARATOR; + $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR; + $this->routePath = $this->rootPath . 'route' . DIRECTORY_SEPARATOR; + $this->configPath = $this->rootPath . 'config' . DIRECTORY_SEPARATOR; + + static::setInstance($this); + + $this->instance('app', $this); + + $this->configExt = $this->env->get('config_ext', '.php'); + + // 加载惯例配置文件 + $this->config->set(include $this->thinkPath . 'convention.php'); + + // 设置路径环境变量 + $this->env->set([ + 'think_path' => $this->thinkPath, + 'root_path' => $this->rootPath, + 'app_path' => $this->appPath, + 'config_path' => $this->configPath, + 'route_path' => $this->routePath, + 'runtime_path' => $this->runtimePath, + 'extend_path' => $this->rootPath . 'extend' . DIRECTORY_SEPARATOR, + 'vendor_path' => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR, + ]); + + // 加载环境变量配置文件 + if (is_file($this->rootPath . '.env')) { + $this->env->load($this->rootPath . '.env'); + } + + $this->namespace = $this->env->get('app_namespace', $this->namespace); + $this->env->set('app_namespace', $this->namespace); + + // 注册应用命名空间 + Loader::addNamespace($this->namespace, $this->appPath); + + // 初始化应用 + $this->init(); + + // 开启类名后缀 + $this->suffix = $this->config('app.class_suffix'); + + // 应用调试模式 + $this->appDebug = $this->env->get('app_debug', $this->config('app.app_debug')); + $this->env->set('app_debug', $this->appDebug); + + if (!$this->appDebug) { + ini_set('display_errors', 'Off'); + } elseif (PHP_SAPI != 'cli') { + //重新申请一块比较大的buffer + if (ob_get_level() > 0) { + $output = ob_get_clean(); + } + ob_start(); + if (!empty($output)) { + echo $output; + } + } + + // 注册异常处理类 + if ($this->config('app.exception_handle')) { + Error::setExceptionHandler($this->config('app.exception_handle')); + } + + // 注册根命名空间 + if (!empty($this->config('app.root_namespace'))) { + Loader::addNamespace($this->config('app.root_namespace')); + } + + // 加载composer autofile文件 + Loader::loadComposerAutoloadFiles(); + + // 注册类库别名 + Loader::addClassAlias($this->config->pull('alias')); + + // 数据库配置初始化 + Db::init($this->config->pull('database')); + + // 设置系统时区 + date_default_timezone_set($this->config('app.default_timezone')); + + // 读取语言包 + $this->loadLangPack(); + + // 路由初始化 + $this->routeInit(); } /** * 初始化应用或模块 * @access public - * @param string $module 模块名 - * @return array + * @param string $module 模块名 + * @return void */ - private static function init($module = '') + public function init($module = '') { // 定位模块目录 - $module = $module ? $module . DS : ''; + $module = $module ? $module . DIRECTORY_SEPARATOR : ''; + $path = $this->appPath . $module; // 加载初始化文件 - if (is_file(APP_PATH . $module . 'init' . EXT)) { - include APP_PATH . $module . 'init' . EXT; - } elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) { - include RUNTIME_PATH . $module . 'init' . EXT; + if (is_file($path . 'init.php')) { + include $path . 'init.php'; + } elseif (is_file($this->runtimePath . $module . 'init.php')) { + include $this->runtimePath . $module . 'init.php'; } else { - $path = APP_PATH . $module; - // 加载模块配置 - $config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT); - // 读取数据库配置文件 - $filename = CONF_PATH . $module . 'database' . CONF_EXT; - Config::load($filename, 'database'); - // 读取扩展配置文件 - if (is_dir(CONF_PATH . $module . 'extra')) { - $dir = CONF_PATH . $module . 'extra'; - $files = scandir($dir); - foreach ($files as $file) { - if (strpos($file, CONF_EXT)) { - $filename = $dir . DS . $file; - Config::load($filename, pathinfo($file, PATHINFO_FILENAME)); - } + // 加载行为扩展文件 + if (is_file($path . 'tags.php')) { + $tags = include $path . 'tags.php'; + if (is_array($tags)) { + $this->hook->import($tags); } } - // 加载应用状态配置 - if ($config['app_status']) { - $config = Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT); - } - - // 加载行为扩展文件 - if (is_file(CONF_PATH . $module . 'tags' . EXT)) { - Hook::import(include CONF_PATH . $module . 'tags' . EXT); - } - // 加载公共文件 - if (is_file($path . 'common' . EXT)) { - include $path . 'common' . EXT; + if (is_file($path . 'common.php')) { + include_once $path . 'common.php'; } - // 加载当前模块语言包 - if ($module) { - Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT); + if ('' == $module) { + // 加载系统助手函数 + include $this->thinkPath . 'helper.php'; + } + + // 加载中间件 + if (is_file($path . 'middleware.php')) { + $middleware = include $path . 'middleware.php'; + if (is_array($middleware)) { + $this->middleware->import($middleware); + } + } + + // 注册服务的容器对象实例 + if (is_file($path . 'provider.php')) { + $provider = include $path . 'provider.php'; + if (is_array($provider)) { + $this->bindTo($provider); + } + } + + // 自动读取配置文件 + if (is_dir($path . 'config')) { + $dir = $path . 'config' . DIRECTORY_SEPARATOR; + } elseif (is_dir($this->configPath . $module)) { + $dir = $this->configPath . $module; + } + + $files = isset($dir) ? scandir($dir) : []; + + foreach ($files as $file) { + if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) { + $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME)); + } + } + } + + $this->setModulePath($path); + + if ($module) { + // 对容器中的对象实例进行配置更新 + $this->containerConfigUpdate($module); + } + } + + protected function containerConfigUpdate($module) + { + $config = $this->config->get(); + + // 注册异常处理类 + if ($config['app']['exception_handle']) { + Error::setExceptionHandler($config['app']['exception_handle']); + } + + Db::init($config['database']); + $this->middleware->setConfig($config['middleware']); + $this->route->setConfig($config['app']); + $this->request->init($config['app']); + $this->cookie->init($config['cookie']); + $this->view->init($config['template']); + $this->log->init($config['log']); + $this->session->setConfig($config['session']); + $this->debug->setConfig($config['trace']); + $this->cache->init($config['cache'], true); + + // 加载当前模块语言包 + $this->lang->load($this->appPath . $module . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php'); + + // 模块请求缓存检查 + $this->checkRequestCache( + $config['app']['request_cache'], + $config['app']['request_cache_expire'], + $config['app']['request_cache_except'] + ); + } + + /** + * 执行应用程序 + * @access public + * @return Response + * @throws Exception + */ + public function run() + { + try { + // 初始化应用 + $this->initialize(); + + // 监听app_init + $this->hook->listen('app_init'); + + if ($this->bindModule) { + // 模块/控制器绑定 + $this->route->bind($this->bindModule); + } elseif ($this->config('app.auto_bind_module')) { + // 入口自动绑定 + $name = pathinfo($this->request->baseFile(), PATHINFO_FILENAME); + if ($name && 'index' != $name && is_dir($this->appPath . $name)) { + $this->route->bind($name); + } + } + + // 监听app_dispatch + $this->hook->listen('app_dispatch'); + + $dispatch = $this->dispatch; + + if (empty($dispatch)) { + // 路由检测 + $dispatch = $this->routeCheck()->init(); + } + + // 记录当前调度信息 + $this->request->dispatch($dispatch); + + // 记录路由和请求信息 + if ($this->appDebug) { + $this->log('[ ROUTE ] ' . var_export($this->request->routeInfo(), true)); + $this->log('[ HEADER ] ' . var_export($this->request->header(), true)); + $this->log('[ PARAM ] ' . var_export($this->request->param(), true)); + } + + // 监听app_begin + $this->hook->listen('app_begin'); + + // 请求缓存检查 + $this->checkRequestCache( + $this->config('request_cache'), + $this->config('request_cache_expire'), + $this->config('request_cache_except') + ); + + $data = null; + } catch (HttpResponseException $exception) { + $dispatch = null; + $data = $exception->getResponse(); + } + + $this->middleware->add(function (Request $request, $next) use ($dispatch, $data) { + return is_null($data) ? $dispatch->run() : $data; + }); + + $response = $this->middleware->dispatch($this->request); + + // 监听app_end + $this->hook->listen('app_end', $response); + + return $response; + } + + protected function getRouteCacheKey() + { + if ($this->config->get('route_check_cache_key')) { + $closure = $this->config->get('route_check_cache_key'); + $routeKey = $closure($this->request); + } else { + $routeKey = md5($this->request->baseUrl(true) . ':' . $this->request->method()); + } + + return $routeKey; + } + + protected function loadLangPack() + { + // 读取默认语言 + $this->lang->range($this->config('app.default_lang')); + + if ($this->config('app.lang_switch_on')) { + // 开启多语言机制 检测当前语言 + $this->lang->detect(); + } + + $this->request->setLangset($this->lang->range()); + + // 加载系统语言包 + $this->lang->load([ + $this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php', + $this->appPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php', + ]); + } + + /** + * 设置当前地址的请求缓存 + * @access public + * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id + * @param mixed $expire 缓存有效期 + * @param array $except 缓存排除 + * @param string $tag 缓存标签 + * @return void + */ + public function checkRequestCache($key, $expire = null, $except = [], $tag = null) + { + $cache = $this->request->cache($key, $expire, $except, $tag); + + if ($cache) { + $this->setResponseCache($cache); + } + } + + public function setResponseCache($cache) + { + list($key, $expire, $tag) = $cache; + + if (strtotime($this->request->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $this->request->server('REQUEST_TIME')) { + // 读取缓存 + $response = Response::create()->code(304); + throw new HttpResponseException($response); + } elseif ($this->cache->has($key)) { + list($content, $header) = $this->cache->get($key); + + $response = Response::create($content)->header($header); + throw new HttpResponseException($response); + } + } + + /** + * 设置当前请求的调度信息 + * @access public + * @param Dispatch $dispatch 调度信息 + * @return $this + */ + public function dispatch(Dispatch $dispatch) + { + $this->dispatch = $dispatch; + return $this; + } + + /** + * 记录调试信息 + * @access public + * @param mixed $msg 调试信息 + * @param string $type 信息类型 + * @return void + */ + public function log($msg, $type = 'info') + { + $this->appDebug && $this->log->record($msg, $type); + } + + /** + * 获取配置参数 为空则获取所有配置 + * @access public + * @param string $name 配置参数名(支持二级配置 .号分割) + * @return mixed + */ + public function config($name = '') + { + return $this->config->get($name); + } + + /** + * 路由初始化 导入路由定义规则 + * @access public + * @return void + */ + public function routeInit() + { + // 路由检测 + $files = scandir($this->routePath); + foreach ($files as $file) { + if (strpos($file, '.php')) { + $filename = $this->routePath . $file; + // 导入路由配置 + $rules = include $filename; + if (is_array($rules)) { + $this->route->import($rules); + } + } + } + + if ($this->route->config('route_annotation')) { + // 自动生成路由定义 + if ($this->appDebug) { + $suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix'); + $this->build->buildRoute($suffix); + } + + $filename = $this->runtimePath . 'build_route.php'; + + if (is_file($filename)) { + include $filename; } } - return Config::get(); } /** * URL路由检测(根据PATH_INFO) * @access public - * @param \think\Request $request - * @param array $config - * @return array - * @throws \think\Exception + * @return Dispatch */ - public static function routeCheck($request, array $config) + public function routeCheck() { - $path = $request->path(); - $depr = $config['pathinfo_depr']; - $result = false; - // 路由检测 - $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on']; - if ($check) { - // 开启路由 - if (is_file(RUNTIME_PATH . 'route.php')) { - // 读取路由缓存 - $rules = include RUNTIME_PATH . 'route.php'; - if (is_array($rules)) { - Route::rules($rules); - } - } else { - $files = $config['route_config_file']; - foreach ($files as $file) { - if (is_file(CONF_PATH . $file . CONF_EXT)) { - // 导入路由配置 - $rules = include CONF_PATH . $file . CONF_EXT; - if (is_array($rules)) { - Route::import($rules); - } - } - } - } + // 检测路由缓存 + if (!$this->appDebug && $this->config->get('route_check_cache')) { + $routeKey = $this->getRouteCacheKey(); + $option = $this->config->get('route_cache_option'); - // 路由检测(根据路由定义返回不同的URL调度) - $result = Route::check($request, $path, $depr, $config['url_domain_deploy']); - $must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must']; - if ($must && false === $result) { - // 路由无效 - throw new RouteNotFoundException(); + if ($option && $this->cache->connect($option)->has($routeKey)) { + return $this->cache->connect($option)->get($routeKey); + } elseif ($this->cache->has($routeKey)) { + return $this->cache->get($routeKey); } } - if (false === $result) { - // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索 - $result = Route::parseUrl($path, $depr, $config['controller_auto_search']); + + // 获取应用调度信息 + $path = $this->request->path(); + + // 是否强制路由模式 + $must = !is_null($this->routeMust) ? $this->routeMust : $this->route->config('url_route_must'); + + // 路由检测 返回一个Dispatch对象 + $dispatch = $this->route->check($path, $must); + + if (!empty($routeKey)) { + try { + if ($option) { + $this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch); + } else { + $this->cache->tag('route_cache')->set($routeKey, $dispatch); + } + } catch (\Exception $e) { + // 存在闭包的时候缓存无效 + } } - return $result; + + return $dispatch; } /** * 设置应用的路由检测机制 * @access public - * @param bool $route 是否需要检测路由 * @param bool $must 是否强制检测路由 + * @return $this + */ + public function routeMust($must = false) + { + $this->routeMust = $must; + return $this; + } + + /** + * 解析模块和类名 + * @access protected + * @param string $name 资源地址 + * @param string $layer 验证层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return array + */ + protected function parseModuleAndClass($name, $layer, $appendSuffix) + { + if (false !== strpos($name, '\\')) { + $class = $name; + $module = $this->request->module(); + } else { + if (strpos($name, '/')) { + list($module, $name) = explode('/', $name, 2); + } else { + $module = $this->request->module(); + } + + $class = $this->parseClass($module, $layer, $name, $appendSuffix); + } + + return [$module, $class]; + } + + /** + * 实例化应用类库 + * @access public + * @param string $name 类名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $common 公共模块名 + * @return object + * @throws ClassNotFoundException + */ + public function create($name, $layer, $appendSuffix = false, $common = 'common') + { + $guid = $name . $layer; + + if ($this->__isset($guid)) { + return $this->__get($guid); + } + + list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix); + + if (class_exists($class)) { + $object = $this->__get($class); + } else { + $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); + if (class_exists($class)) { + $object = $this->__get($class); + } else { + throw new ClassNotFoundException('class not exists:' . $class, $class); + } + } + + $this->__set($guid, $class); + + return $object; + } + + /** + * 实例化(分层)模型 + * @access public + * @param string $name Model名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $common 公共模块名 + * @return Model + * @throws ClassNotFoundException + */ + public function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common') + { + return $this->create($name, $layer, $appendSuffix, $common); + } + + /** + * 实例化(分层)控制器 格式:[模块名/]控制器名 + * @access public + * @param string $name 资源地址 + * @param string $layer 控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $empty 空控制器名称 + * @return object + * @throws ClassNotFoundException + */ + public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '') + { + list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix); + + if (class_exists($class)) { + return $this->make($class, true); + } elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) { + return $this->make($emptyClass, true); + } + + throw new ClassNotFoundException('class not exists:' . $class, $class); + } + + /** + * 实例化验证类 格式:[模块名/]验证器名 + * @access public + * @param string $name 资源地址 + * @param string $layer 验证层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $common 公共模块名 + * @return Validate + * @throws ClassNotFoundException + */ + public function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common') + { + $name = $name ?: $this->config('default_validate'); + + if (empty($name)) { + return new Validate; + } + + return $this->create($name, $layer, $appendSuffix, $common); + } + + /** + * 数据库初始化 + * @access public + * @param mixed $config 数据库配置 + * @param bool|string $name 连接标识 true 强制重新连接 + * @return \think\db\Query + */ + public function db($config = [], $name = false) + { + return Db::connect($config, $name); + } + + /** + * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作 + * @access public + * @param string $url 调用地址 + * @param string|array $vars 调用参数 支持字符串和数组 + * @param string $layer 要调用的控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return mixed + * @throws ClassNotFoundException + */ + public function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) + { + $info = pathinfo($url); + $action = $info['basename']; + $module = '.' != $info['dirname'] ? $info['dirname'] : $this->request->controller(); + $class = $this->controller($module, $layer, $appendSuffix); + + if (is_scalar($vars)) { + if (strpos($vars, '=')) { + parse_str($vars, $vars); + } else { + $vars = [$vars]; + } + } + + return $this->invokeMethod([$class, $action . $this->config('action_suffix')], $vars); + } + + /** + * 解析应用类的类名 + * @access public + * @param string $module 模块名 + * @param string $layer 层名 controller model ... + * @param string $name 类名 + * @param bool $appendSuffix + * @return string + */ + public function parseClass($module, $layer, $name, $appendSuffix = false) + { + $name = str_replace(['/', '.'], '\\', $name); + $array = explode('\\', $name); + $class = Loader::parseName(array_pop($array), 1) . ($this->suffix || $appendSuffix ? ucfirst($layer) : ''); + $path = $array ? implode('\\', $array) . '\\' : ''; + + return $this->namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class; + } + + /** + * 获取框架版本 + * @access public + * @return string + */ + public function version() + { + return static::VERSION; + } + + /** + * 是否为调试模式 + * @access public + * @return bool + */ + public function isDebug() + { + return $this->appDebug; + } + + /** + * 获取模块路径 + * @access public + * @return string + */ + public function getModulePath() + { + return $this->modulePath; + } + + /** + * 设置模块路径 + * @access public + * @param string $path 路径 * @return void */ - public static function route($route, $must = false) + public function setModulePath($path) { - self::$routeCheck = $route; - self::$routeMust = $must; + $this->modulePath = $path; + $this->env->set('module_path', $path); } + + /** + * 获取应用根目录 + * @access public + * @return string + */ + public function getRootPath() + { + return $this->rootPath; + } + + /** + * 获取应用类库目录 + * @access public + * @return string + */ + public function getAppPath() + { + if (is_null($this->appPath)) { + $this->appPath = Loader::getRootPath() . 'application' . DIRECTORY_SEPARATOR; + } + + return $this->appPath; + } + + /** + * 获取应用运行时目录 + * @access public + * @return string + */ + public function getRuntimePath() + { + return $this->runtimePath; + } + + /** + * 获取核心框架目录 + * @access public + * @return string + */ + public function getThinkPath() + { + return $this->thinkPath; + } + + /** + * 获取路由目录 + * @access public + * @return string + */ + public function getRoutePath() + { + return $this->routePath; + } + + /** + * 获取应用配置目录 + * @access public + * @return string + */ + public function getConfigPath() + { + return $this->configPath; + } + + /** + * 获取配置后缀 + * @access public + * @return string + */ + public function getConfigExt() + { + return $this->configExt; + } + + /** + * 获取应用类库命名空间 + * @access public + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * 设置应用类库命名空间 + * @access public + * @param string $namespace 命名空间名称 + * @return $this + */ + public function setNamespace($namespace) + { + $this->namespace = $namespace; + return $this; + } + + /** + * 是否启用类库后缀 + * @access public + * @return bool + */ + public function getSuffix() + { + return $this->suffix; + } + + /** + * 获取应用开启时间 + * @access public + * @return float + */ + public function getBeginTime() + { + return $this->beginTime; + } + + /** + * 获取应用初始内存占用 + * @access public + * @return integer + */ + public function getBeginMem() + { + return $this->beginMem; + } + } diff --git a/thinkphp/library/think/Build.php b/thinkphp/library/think/Build.php index 13b7bfdc0..7a531d74c 100644 --- a/thinkphp/library/think/Build.php +++ b/thinkphp/library/think/Build.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,35 +13,56 @@ namespace think; class Build { + /** + * 应用对象 + * @var App + */ + protected $app; + + /** + * 应用目录 + * @var string + */ + protected $basePath; + + public function __construct(App $app) + { + $this->app = $app; + $this->basePath = $this->app->getAppPath(); + } + /** * 根据传入的build资料创建目录和文件 - * @access protected + * @access public * @param array $build build列表 * @param string $namespace 应用类库命名空间 * @param bool $suffix 类库后缀 * @return void */ - public static function run(array $build = [], $namespace = 'app', $suffix = false) + public function run(array $build = [], $namespace = 'app', $suffix = false) { // 锁定 - $lockfile = APP_PATH . 'build.lock'; + $lockfile = $this->basePath . 'build.lock'; + if (is_writable($lockfile)) { return; } elseif (!touch($lockfile)) { - throw new Exception('应用目录[' . APP_PATH . ']不可写,目录无法自动生成!
                  请手动生成项目目录~', 10006); + throw new Exception('应用目录[' . $this->basePath . ']不可写,目录无法自动生成!
                  请手动生成项目目录~', 10006); } + foreach ($build as $module => $list) { if ('__dir__' == $module) { // 创建目录列表 - self::buildDir($list); + $this->buildDir($list); } elseif ('__file__' == $module) { // 创建文件列表 - self::buildFile($list); + $this->buildFile($list); } else { // 创建模块 - self::module($module, $list, $namespace, $suffix); + $this->module($module, $list, $namespace, $suffix); } } + // 解除锁定 unlink($lockfile); } @@ -52,13 +73,10 @@ class Build * @param array $list 目录列表 * @return void */ - protected static function buildDir($list) + protected function buildDir($list) { foreach ($list as $dir) { - if (!is_dir(APP_PATH . $dir)) { - // 创建目录 - mkdir(APP_PATH . $dir, 0755, true); - } + $this->checkDirBuild($this->basePath . $dir); } } @@ -68,15 +86,16 @@ class Build * @param array $list 文件列表 * @return void */ - protected static function buildFile($list) + protected function buildFile($list) { foreach ($list as $file) { - if (!is_dir(APP_PATH . dirname($file))) { + if (!is_dir($this->basePath . dirname($file))) { // 创建目录 - mkdir(APP_PATH . dirname($file), 0755, true); + mkdir($this->basePath . dirname($file), 0755, true); } - if (!is_file(APP_PATH . $file)) { - file_put_contents(APP_PATH . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "basePath . $file)) { + file_put_contents($this->basePath . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "basePath . $module)) { // 创建模块目录 - mkdir(APP_PATH . $module); + mkdir($this->basePath . $module); } - if (basename(RUNTIME_PATH) != $module) { + + if (basename($this->app->getRuntimePath()) != $module) { // 创建配置文件和公共文件 - self::buildCommon($module); + $this->buildCommon($module); // 创建模块的默认页面 - self::buildHello($module, $namespace, $suffix); + $this->buildHello($module, $namespace, $suffix); } + if (empty($list)) { // 创建默认的模块目录和文件 $list = [ - '__file__' => ['config.php', 'common.php'], - '__dir__' => ['controller', 'model', 'view'], + '__file__' => ['common.php'], + '__dir__' => ['controller', 'model', 'view', 'config'], ]; } + // 创建子目录和文件 foreach ($list as $path => $file) { - $modulePath = APP_PATH . $module . DS; + $modulePath = $this->basePath . $module . DIRECTORY_SEPARATOR; if ('__dir__' == $path) { // 生成子目录 foreach ($file as $dir) { - if (!is_dir($modulePath . $dir)) { - // 创建目录 - mkdir($modulePath . $dir, 0755, true); - } + $this->checkDirBuild($modulePath . $dir); } } elseif ('__file__' == $path) { // 生成(空白)文件 @@ -132,7 +152,7 @@ class Build // 生成相关MVC文件 foreach ($file as $val) { $val = trim($val); - $filename = $modulePath . $path . DS . $val . ($suffix ? ucfirst($path) : '') . EXT; + $filename = $modulePath . $path . DIRECTORY_SEPARATOR . $val . ($suffix ? ucfirst($path) : '') . '.php'; $space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path; $class = $val . ($suffix ? ucfirst($path) : ''); switch ($path) { @@ -143,11 +163,8 @@ class Build $content = "checkDirBuild(dirname($filename)); $content = ''; break; default: @@ -164,41 +181,235 @@ class Build } /** - * 创建模块的欢迎页面 + * 根据注释自动生成路由规则 * @access public + * @param bool $suffix 类库后缀 + * @param string $layer 控制器层目录名 + * @return string + */ + public function buildRoute($suffix = false, $layer = '') + { + $namespace = $this->app->getNameSpace(); + $content = 'app->config('app.url_controller_layer'); + } + + if ($this->app->config('app.app_multi_module')) { + $modules = glob($this->basePath . '*', GLOB_ONLYDIR); + + foreach ($modules as $module) { + $module = basename($module); + + if (in_array($module, $this->app->config('app.deny_module_list'))) { + continue; + } + + $path = $this->basePath . $module . DIRECTORY_SEPARATOR . $layer . DIRECTORY_SEPARATOR; + $content .= $this->buildDirRoute($path, $namespace, $module, $suffix, $layer); + } + } else { + $path = $this->basePath . $layer . DIRECTORY_SEPARATOR; + $content .= $this->buildDirRoute($path, $namespace, '', $suffix, $layer); + } + + $filename = $this->app->getRuntimePath() . 'build_route.php'; + file_put_contents($filename, $content); + + return $filename; + } + + /** + * 生成子目录控制器类的路由规则 + * @access protected + * @param string $path 控制器目录 + * @param string $namespace 应用命名空间 + * @param string $module 模块 + * @param bool $suffix 类库后缀 + * @param string $layer 控制器层目录名 + * @return string + */ + protected function buildDirRoute($path, $namespace, $module, $suffix, $layer) + { + $content = ''; + $controllers = glob($path . '*.php'); + + foreach ($controllers as $controller) { + $controller = basename($controller, '.php'); + + $class = new \ReflectionClass($namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $controller); + + if (strpos($layer, '\\')) { + // 多级控制器 + $level = str_replace(DIRECTORY_SEPARATOR, '.', substr($layer, 11)); + $controller = $level . '.' . $controller; + $length = strlen(strstr($layer, '\\', true)); + } else { + $length = strlen($layer); + } + + if ($suffix) { + $controller = substr($controller, 0, -$length); + } + + $content .= $this->getControllerRoute($class, $module, $controller); + } + + $subDir = glob($path . '*', GLOB_ONLYDIR); + + foreach ($subDir as $dir) { + $content .= $this->buildDirRoute($dir . DIRECTORY_SEPARATOR, $namespace, $module, $suffix, $layer . '\\' . basename($dir)); + } + + return $content; + } + + /** + * 生成控制器类的路由规则 + * @access protected + * @param string $class 控制器完整类名 + * @param string $module 模块名 + * @param string $controller 控制器名 + * @return string + */ + protected function getControllerRoute($class, $module, $controller) + { + $content = ''; + $comment = $class->getDocComment(); + + if (false !== strpos($comment, '@route(')) { + $comment = $this->parseRouteComment($comment); + $route = ($module ? $module . '/' : '') . $controller; + $comment = preg_replace('/route\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::resource(\1,\'' . $route . '\')', $comment); + $content .= PHP_EOL . $comment; + } elseif (false !== strpos($comment, '@alias(')) { + $comment = $this->parseRouteComment($comment, '@alias('); + $route = ($module ? $module . '/' : '') . $controller; + $comment = preg_replace('/alias\(\s?([\'\"][\-\_\/\w]+[\'\"])\s?\)/is', 'Route::alias(\1,\'' . $route . '\')', $comment); + $content .= PHP_EOL . $comment; + } + + $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC); + + foreach ($methods as $method) { + $comment = $this->getMethodRouteComment($module, $controller, $method); + if ($comment) { + $content .= PHP_EOL . $comment; + } + } + + return $content; + } + + /** + * 解析路由注释 + * @access protected + * @param string $comment + * @param string $tag + * @return string + */ + protected function parseRouteComment($comment, $tag = '@route(') + { + $comment = substr($comment, 3, -2); + $comment = explode(PHP_EOL, substr(strstr(trim($comment), $tag), 1)); + $comment = array_map(function ($item) {return trim(trim($item), ' \t*');}, $comment); + + if (count($comment) > 1) { + $key = array_search('', $comment); + $comment = array_slice($comment, 0, false === $key ? 1 : $key); + } + + $comment = implode(PHP_EOL . "\t", $comment) . ';'; + + if (strpos($comment, '{')) { + $comment = preg_replace_callback('/\{\s?.*?\s?\}/s', function ($matches) { + return false !== strpos($matches[0], '"') ? '[' . substr(var_export(json_decode($matches[0], true), true), 7, -1) . ']' : $matches[0]; + }, $comment); + } + return $comment; + } + + /** + * 获取方法的路由注释 + * @access protected + * @param string $module 模块 + * @param string $controller 控制器名 + * @param \ReflectMethod $reflectMethod + * @return string|void + */ + protected function getMethodRouteComment($module, $controller, $reflectMethod) + { + $comment = $reflectMethod->getDocComment(); + + if (false !== strpos($comment, '@route(')) { + $comment = $this->parseRouteComment($comment); + $action = $reflectMethod->getName(); + + if ($suffix = $this->app->config('app.action_suffix')) { + $action = substr($action, 0, -strlen($suffix)); + } + + $route = ($module ? $module . '/' : '') . $controller . '/' . $action; + $comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\,?\s?[\'\"]?(\w+?)[\'\"]?\s?\)/is', 'Route::\2(\1,\'' . $route . '\')', $comment); + $comment = preg_replace('/route\s?\(\s?([\'\"][\-\_\/\:\<\>\?\$\[\]\w]+[\'\"])\s?\)/is', 'Route::rule(\1,\'' . $route . '\')', $comment); + + return $comment; + } + } + + /** + * 创建模块的欢迎页面 + * @access protected * @param string $module 模块名 * @param string $namespace 应用类库命名空间 * @param bool $suffix 类库后缀 * @return void */ - protected static function buildHello($module, $namespace, $suffix = false) + protected function buildHello($module, $namespace, $suffix = false) { - $filename = APP_PATH . ($module ? $module . DS : '') . 'controller' . DS . 'Index' . ($suffix ? 'Controller' : '') . EXT; + $filename = $this->basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . ($suffix ? 'Controller' : '') . '.php'; if (!is_file($filename)) { - $content = file_get_contents(THINK_PATH . 'tpl' . DS . 'default_index.tpl'); + $content = file_get_contents($this->app->getThinkPath() . 'tpl' . DIRECTORY_SEPARATOR . 'default_index.tpl'); $content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content); - if (!is_dir(dirname($filename))) { - mkdir(dirname($filename), 0755, true); - } + $this->checkDirBuild(dirname($filename)); + file_put_contents($filename, $content); } } /** * 创建模块的公共文件 - * @access public + * @access protected * @param string $module 模块名 * @return void */ - protected static function buildCommon($module) + protected function buildCommon($module) { - $filename = CONF_PATH . ($module ? $module . DS : '') . 'config.php'; + $filename = $this->app->getConfigPath() . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'app.php'; + $this->checkDirBuild(dirname($filename)); + if (!is_file($filename)) { file_put_contents($filename, "basePath . ($module ? $module . DIRECTORY_SEPARATOR : '') . 'common.php'; + if (!is_file($filename)) { file_put_contents($filename, "config = $config; + $this->init($config); + } /** * 连接缓存 * @access public - * @param array $options 配置数组 - * @param bool|string $name 缓存连接标识 true 强制重新连接 + * @param array $options 配置数组 + * @param bool|string $name 缓存连接标识 true 强制重新连接 * @return Driver */ - public static function connect(array $options = [], $name = false) + public function connect(array $options = [], $name = false) { - $type = !empty($options['type']) ? $options['type'] : 'File'; if (false === $name) { $name = md5(serialize($options)); } - if (true === $name || !isset(self::$instance[$name])) { - $class = false !== strpos($type, '\\') ? $type : '\\think\\cache\\driver\\' . ucwords($type); + if (true === $name || !isset($this->instance[$name])) { + $type = !empty($options['type']) ? $options['type'] : 'File'; - // 记录初始化信息 - App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info'); if (true === $name) { - return new $class($options); - } else { - self::$instance[$name] = new $class($options); + $name = md5(serialize($options)); } + + $this->instance[$name] = Loader::factory($type, '\\think\\cache\\driver\\', $options); } - return self::$instance[$name]; + + return $this->instance[$name]; } /** * 自动初始化缓存 * @access public - * @param array $options 配置数组 + * @param array $options 配置数组 + * @param bool $force 强制更新 * @return Driver */ - public static function init(array $options = []) + public function init(array $options = [], $force = false) { - if (is_null(self::$handler)) { - // 自动初始化缓存 - if (!empty($options)) { - $connect = self::connect($options); - } elseif ('complex' == Config::get('cache.type')) { - $connect = self::connect(Config::get('cache.default')); - } else { - $connect = self::connect(Config::get('cache')); + if (is_null($this->handler) || $force) { + + if ('complex' == $options['type']) { + $default = $options['default']; + $options = isset($options[$default['type']]) ? $options[$default['type']] : $default; } - self::$handler = $connect; + + $this->handler = $this->connect($options); } - return self::$handler; + + return $this->handler; + } + + public static function __make(Config $config) + { + return new static($config->pull('cache')); + } + + public function getConfig() + { + return $this->config; + } + + public function setConfig(array $config) + { + $this->config = array_merge($this->config, $config); } /** * 切换缓存类型 需要配置 cache.type 为 complex * @access public - * @param string $name 缓存标识 + * @param string $name 缓存标识 * @return Driver */ - public static function store($name = '') + public function store($name = '') { - if ('' !== $name && 'complex' == Config::get('cache.type')) { - return self::connect(Config::get('cache.' . $name), strtolower($name)); + if ('' !== $name && 'complex' == $this->config['type']) { + return $this->connect($this->config[$name], strtolower($name)); } - return self::init(); + + return $this->init(); } - /** - * 判断缓存是否存在 - * @access public - * @param string $name 缓存变量名 - * @return bool - */ - public static function has($name) + public function __call($method, $args) { - self::$readTimes++; - return self::init()->has($name); - } - - /** - * 读取缓存 - * @access public - * @param string $name 缓存标识 - * @param mixed $default 默认值 - * @return mixed - */ - public static function get($name, $default = false) - { - self::$readTimes++; - return self::init()->get($name, $default); - } - - /** - * 写入缓存 - * @access public - * @param string $name 缓存标识 - * @param mixed $value 存储数据 - * @param int|null $expire 有效时间 0为永久 - * @return boolean - */ - public static function set($name, $value, $expire = null) - { - self::$writeTimes++; - return self::init()->set($name, $value, $expire); - } - - /** - * 自增缓存(针对数值缓存) - * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 - * @return false|int - */ - public static function inc($name, $step = 1) - { - self::$writeTimes++; - return self::init()->inc($name, $step); - } - - /** - * 自减缓存(针对数值缓存) - * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 - * @return false|int - */ - public static function dec($name, $step = 1) - { - self::$writeTimes++; - return self::init()->dec($name, $step); - } - - /** - * 删除缓存 - * @access public - * @param string $name 缓存标识 - * @return boolean - */ - public static function rm($name) - { - self::$writeTimes++; - return self::init()->rm($name); - } - - /** - * 清除缓存 - * @access public - * @param string $tag 标签名 - * @return boolean - */ - public static function clear($tag = null) - { - self::$writeTimes++; - return self::init()->clear($tag); - } - - /** - * 读取缓存并删除 - * @access public - * @param string $name 缓存变量名 - * @return mixed - */ - public static function pull($name) - { - self::$readTimes++; - self::$writeTimes++; - return self::init()->pull($name); - } - - /** - * 如果不存在则写入缓存 - * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param int $expire 有效时间 0为永久 - * @return mixed - */ - public static function remember($name, $value, $expire = null) - { - self::$readTimes++; - return self::init()->remember($name, $value, $expire); - } - - /** - * 缓存标签 - * @access public - * @param string $name 标签名 - * @param string|array $keys 缓存标识 - * @param bool $overlay 是否覆盖 - * @return Driver - */ - public static function tag($name, $keys = null, $overlay = false) - { - return self::init()->tag($name, $keys, $overlay); + return call_user_func_array([$this->init(), $method], $args); } } diff --git a/thinkphp/library/think/Collection.php b/thinkphp/library/think/Collection.php index 41b427598..d58c8999b 100644 --- a/thinkphp/library/think/Collection.php +++ b/thinkphp/library/think/Collection.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -19,6 +19,10 @@ use JsonSerializable; class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { + /** + * 数据集数据 + * @var array + */ protected $items = []; public function __construct($items = []) @@ -33,6 +37,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 是否为空 + * @access public * @return bool */ public function isEmpty() @@ -55,6 +60,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 合并数组 * + * @access public * @param mixed $items * @return static */ @@ -63,20 +69,10 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria return new static(array_merge($this->items, $this->convertToArray($items))); } - /** - * 比较数组,返回差集 - * - * @param mixed $items - * @return static - */ - public function diff($items) - { - return new static(array_diff($this->items, $this->convertToArray($items))); - } - /** * 交换数组中的键和值 * + * @access public * @return static */ public function flip() @@ -85,29 +81,113 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria } /** - * 比较数组,返回交集 + * 按指定键整理数据 * - * @param mixed $items + * @access public + * @param mixed $items 数据 + * @param string $indexKey 键名 + * @return array + */ + public function dictionary($items = null, &$indexKey = null) + { + if ($items instanceof self || $items instanceof Paginator) { + $items = $items->all(); + } + + $items = is_null($items) ? $this->items : $items; + + if ($items && empty($indexKey)) { + $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk(); + } + + if (isset($indexKey) && is_string($indexKey)) { + return array_column($items, null, $indexKey); + } + + return $items; + } + + /** + * 比较数组,返回差集 + * + * @access public + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 * @return static */ - public function intersect($items) + public function diff($items, $indexKey = null) { - return new static(array_intersect($this->items, $this->convertToArray($items))); + if ($this->isEmpty() || is_scalar($this->items[0])) { + return new static(array_diff($this->items, $this->convertToArray($items))); + } + + $diff = []; + $dictionary = $this->dictionary($items, $indexKey); + + if (is_string($indexKey)) { + foreach ($this->items as $item) { + if (!isset($dictionary[$item[$indexKey]])) { + $diff[] = $item; + } + } + } + + return new static($diff); + } + + /** + * 比较数组,返回交集 + * + * @access public + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 + * @return static + */ + public function intersect($items, $indexKey = null) + { + if ($this->isEmpty() || is_scalar($this->items[0])) { + return new static(array_diff($this->items, $this->convertToArray($items))); + } + + $intersect = []; + $dictionary = $this->dictionary($items, $indexKey); + + if (is_string($indexKey)) { + foreach ($this->items as $item) { + if (isset($dictionary[$item[$indexKey]])) { + $intersect[] = $item; + } + } + } + + return new static($intersect); } /** * 返回数组中所有的键名 * - * @return static + * @access public + * @return array */ public function keys() { - return new static(array_keys($this->items)); + $current = current($this->items); + + if (is_scalar($current)) { + $array = $this->items; + } elseif (is_array($current)) { + $array = $current; + } else { + $array = $current->toArray(); + } + + return array_keys($array); } /** * 删除数组的最后一个元素(出栈) * + * @access public * @return mixed */ public function pop() @@ -118,6 +198,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 通过使用用户自定义函数,以字符串返回数组 * + * @access public * @param callable $callback * @param mixed $initial * @return mixed @@ -130,6 +211,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 以相反的顺序返回数组。 * + * @access public * @return static */ public function reverse() @@ -140,6 +222,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 删除数组中首个元素,并返回被删除元素的值 * + * @access public * @return mixed */ public function shift() @@ -147,9 +230,26 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria return array_shift($this->items); } + /** + * 在数组结尾插入一个元素 + * @access public + * @param mixed $value + * @param mixed $key + * @return void + */ + public function push($value, $key = null) + { + if (is_null($key)) { + $this->items[] = $value; + } else { + $this->items[$key] = $value; + } + } + /** * 把一个数组分割为新的数组块. * + * @access public * @param int $size * @param bool $preserveKeys * @return static @@ -167,9 +267,10 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 在数组开头插入一个元素 - * @param mixed $value - * @param null $key - * @return int + * @access public + * @param mixed $value + * @param mixed $key + * @return void */ public function unshift($value, $key = null) { @@ -183,23 +284,40 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 给每个元素执行个回调 * + * @access public * @param callable $callback * @return $this */ public function each(callable $callback) { foreach ($this->items as $key => $item) { - if ($callback($item, $key) === false) { + $result = $callback($item, $key); + + if (false === $result) { break; + } elseif (!is_object($item)) { + $this->items[$key] = $result; } } return $this; } + /** + * 用回调函数处理数组中的元素 + * @access public + * @param callable|null $callback + * @return static + */ + public function map(callable $callback) + { + return new static(array_map($callback, $this->items)); + } + /** * 用回调函数过滤数组中的元素 - * @param callable|null $callback + * @access public + * @param callable|null $callback * @return static */ public function filter(callable $callback = null) @@ -212,46 +330,83 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria } /** - * 返回数组中指定的一列 - * @param $column_key - * @param null $index_key - * @return array + * 根据字段条件过滤数组中的元素 + * @access public + * @param string $field 字段名 + * @param mixed $operator 操作符 + * @param mixed $value 数据 + * @return static */ - public function column($column_key, $index_key = null) + public function where($field, $operator, $value = null) { - if (function_exists('array_column')) { - return array_column($this->items, $column_key, $index_key); + if (is_null($value)) { + $value = $operator; + $operator = '='; } - $result = []; - foreach ($this->items as $row) { - $key = $value = null; - $keySet = $valueSet = false; - if (null !== $index_key && array_key_exists($index_key, $row)) { - $keySet = true; - $key = (string) $row[$index_key]; + return $this->filter(function ($data) use ($field, $operator, $value) { + if (strpos($field, '.')) { + list($field, $relation) = explode('.', $field); + + $result = isset($data[$field][$relation]) ? $data[$field][$relation] : null; + } else { + $result = isset($data[$field]) ? $data[$field] : null; } - if (null === $column_key) { - $valueSet = true; - $value = $row; - } elseif (is_array($row) && array_key_exists($column_key, $row)) { - $valueSet = true; - $value = $row[$column_key]; + + switch ($operator) { + case '===': + return $result === $value; + case '!==': + return $result !== $value; + case '!=': + case '<>': + return $result != $value; + case '>': + return $result > $value; + case '>=': + return $result >= $value; + case '<': + return $result < $value; + case '<=': + return $result <= $value; + case 'like': + return is_string($result) && false !== strpos($result, $value); + case 'not like': + return is_string($result) && false === strpos($result, $value); + case 'in': + return is_scalar($result) && in_array($result, $value, true); + case 'not in': + return is_scalar($result) && !in_array($result, $value, true); + case 'between': + list($min, $max) = is_string($value) ? explode(',', $value) : $value; + return is_scalar($result) && $result >= $min && $result <= $max; + case 'not between': + list($min, $max) = is_string($value) ? explode(',', $value) : $value; + return is_scalar($result) && $result > $max || $result < $min; + case '==': + case '=': + default: + return $result == $value; } - if ($valueSet) { - if ($keySet) { - $result[$key] = $value; - } else { - $result[] = $value; - } - } - } - return $result; + }); + } + + /** + * 返回数据中指定的一列 + * @access public + * @param mixed $columnKey 键名 + * @param mixed $indexKey 作为索引值的列 + * @return array + */ + public function column($columnKey, $indexKey = null) + { + return array_column($this->items, $columnKey, $indexKey); } /** * 对数组排序 * + * @access public * @param callable|null $callback * @return static */ @@ -259,21 +414,42 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria { $items = $this->items; - $callback ? uasort($items, $callback) : uasort($items, function ($a, $b) { + $callback = $callback ?: function ($a, $b) { + return $a == $b ? 0 : (($a < $b) ? -1 : 1); - if ($a == $b) { - return 0; - } + }; - return ($a < $b) ? -1 : 1; - }); + uasort($items, $callback); return new static($items); } + /** + * 指定字段排序 + * @access public + * @param string $field 排序字段 + * @param string $order 排序 + * @param bool $intSort 是否为数字排序 + * @return $this + */ + public function order($field, $order = null, $intSort = true) + { + return $this->sort(function ($a, $b) use ($field, $order, $intSort) { + $fieldA = isset($a[$field]) ? $a[$field] : null; + $fieldB = isset($b[$field]) ? $b[$field] : null; + + if ($intSort) { + return 'desc' == strtolower($order) ? $fieldB >= $fieldA : $fieldA >= $fieldB; + } else { + return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB); + } + }); + } + /** * 将数组打乱 * + * @access public * @return static */ public function shuffle() @@ -288,6 +464,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 截取数组 * + * @access public * @param int $offset * @param int $length * @param bool $preserveKeys @@ -344,7 +521,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 转换当前数据集为JSON字符串 * @access public - * @param integer $options json参数 + * @param integer $options json参数 * @return string */ public function toJson($options = JSON_UNESCAPED_UNICODE) @@ -360,6 +537,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria /** * 转换成数组 * + * @access public * @param mixed $items * @return array */ @@ -368,6 +546,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria if ($items instanceof self) { return $items->all(); } + return (array) $items; } } diff --git a/thinkphp/library/think/Config.php b/thinkphp/library/think/Config.php index 2b8084349..bec6222ad 100644 --- a/thinkphp/library/think/Config.php +++ b/thinkphp/library/think/Config.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,160 +11,388 @@ namespace think; -class Config -{ - // 配置参数 - private static $config = []; - // 参数作用域 - private static $range = '_sys_'; +use Yaconf; - // 设定配置参数的作用域 - public static function range($range) +class Config implements \ArrayAccess +{ + /** + * 配置参数 + * @var array + */ + protected $config = []; + + /** + * 配置前缀 + * @var string + */ + protected $prefix = 'app'; + + /** + * 配置文件目录 + * @var string + */ + protected $path; + + /** + * 配置文件后缀 + * @var string + */ + protected $ext; + + /** + * 是否支持Yaconf + * @var bool + */ + protected $yaconf; + + /** + * 构造方法 + * @access public + */ + public function __construct($path = '', $ext = '.php') { - self::$range = $range; - if (!isset(self::$config[$range])) { - self::$config[$range] = []; + $this->path = $path; + $this->ext = $ext; + $this->yaconf = class_exists('Yaconf'); + } + + public static function __make(App $app) + { + $path = $app->getConfigPath(); + $ext = $app->getConfigExt(); + return new static($path, $ext); + } + + /** + * 设置开启Yaconf + * @access public + * @param bool|string $yaconf 是否使用Yaconf + * @return void + */ + public function setYaconf($yaconf) + { + if ($this->yaconf) { + $this->yaconf = $yaconf; } } + /** + * 设置配置参数默认前缀 + * @access public + * @param string $prefix 前缀 + * @return void + */ + public function setDefaultPrefix($prefix) + { + $this->prefix = $prefix; + } + /** * 解析配置文件或内容 - * @param string $config 配置文件路径或内容 - * @param string $type 配置解析类型 - * @param string $name 配置名(如设置即表示二级配置) - * @param string $range 作用域 + * @access public + * @param string $config 配置文件路径或内容 + * @param string $type 配置解析类型 + * @param string $name 配置名(如设置即表示二级配置) * @return mixed */ - public static function parse($config, $type = '', $name = '', $range = '') + public function parse($config, $type = '', $name = '') { - $range = $range ?: self::$range; if (empty($type)) { $type = pathinfo($config, PATHINFO_EXTENSION); } - $class = false !== strpos($type, '\\') ? $type : '\\think\\config\\driver\\' . ucwords($type); - return self::set((new $class())->parse($config), $name, $range); + + $object = Loader::factory($type, '\\think\\config\\driver\\', $config); + + return $this->set($object->parse(), $name); } /** - * 加载配置文件(PHP格式) - * @param string $file 配置文件名 - * @param string $name 配置名(如设置即表示二级配置) - * @param string $range 作用域 + * 加载配置文件(多种格式) + * @access public + * @param string $file 配置文件名 + * @param string $name 一级配置名 * @return mixed */ - public static function load($file, $name = '', $range = '') + public function load($file, $name = '') { - $range = $range ?: self::$range; - if (!isset(self::$config[$range])) { - self::$config[$range] = []; - } if (is_file($file)) { - $name = strtolower($name); - $type = pathinfo($file, PATHINFO_EXTENSION); - if ('php' == $type) { - return self::set(include $file, $name, $range); - } elseif ('yaml' == $type && function_exists('yaml_parse_file')) { - return self::set(yaml_parse_file($file), $name, $range); - } else { - return self::parse($file, $type, $name, $range); - } - } else { - return self::$config[$range]; + $filename = $file; + } elseif (is_file($this->path . $file . $this->ext)) { + $filename = $this->path . $file . $this->ext; } + + if (isset($filename)) { + return $this->loadFile($filename, $name); + } elseif ($this->yaconf && Yaconf::has($file)) { + return $this->set(Yaconf::get($file), $name); + } + + return $this->config; + } + + /** + * 获取实际的yaconf配置参数 + * @access protected + * @param string $name 配置参数名 + * @return string + */ + protected function getYaconfName($name) + { + if ($this->yaconf && is_string($this->yaconf)) { + return $this->yaconf . '.' . $name; + } + + return $name; + } + + /** + * 获取yaconf配置 + * @access public + * @param string $name 配置参数名 + * @param mixed $default 默认值 + * @return mixed + */ + public function yaconf($name, $default = null) + { + if ($this->yaconf) { + $yaconfName = $this->getYaconfName($name); + + if (Yaconf::has($yaconfName)) { + return Yaconf::get($yaconfName); + } + } + + return $default; + } + + protected function loadFile($file, $name) + { + $name = strtolower($name); + $type = pathinfo($file, PATHINFO_EXTENSION); + + if ('php' == $type) { + return $this->set(include $file, $name); + } elseif ('yaml' == $type && function_exists('yaml_parse_file')) { + return $this->set(yaml_parse_file($file), $name); + } + + return $this->parse($file, $type, $name); } /** * 检测配置是否存在 - * @param string $name 配置参数名(支持二级配置 .号分割) - * @param string $range 作用域 + * @access public + * @param string $name 配置参数名(支持多级配置 .号分割) * @return bool */ - public static function has($name, $range = '') + public function has($name) { - $range = $range ?: self::$range; - - if (!strpos($name, '.')) { - return isset(self::$config[$range][strtolower($name)]); - } else { - // 二维数组设置和获取支持 - $name = explode('.', $name, 2); - return isset(self::$config[$range][strtolower($name[0])][$name[1]]); + if (false === strpos($name, '.')) { + $name = $this->prefix . '.' . $name; } + + return !is_null($this->get($name)); + } + + /** + * 获取一级配置 + * @access public + * @param string $name 一级配置名 + * @return array + */ + public function pull($name) + { + $name = strtolower($name); + + if ($this->yaconf) { + $yaconfName = $this->getYaconfName($name); + + if (Yaconf::has($yaconfName)) { + $config = Yaconf::get($yaconfName); + return isset($this->config[$name]) ? array_merge($this->config[$name], $config) : $config; + } + } + + return isset($this->config[$name]) ? $this->config[$name] : []; } /** * 获取配置参数 为空则获取所有配置 - * @param string $name 配置参数名(支持二级配置 .号分割) - * @param string $range 作用域 + * @access public + * @param string $name 配置参数名(支持多级配置 .号分割) + * @param mixed $default 默认值 * @return mixed */ - public static function get($name = null, $range = '') + public function get($name = null, $default = null) { - $range = $range ?: self::$range; - // 无参数时获取所有 - if (empty($name) && isset(self::$config[$range])) { - return self::$config[$range]; + if ($name && false === strpos($name, '.')) { + $name = $this->prefix . '.' . $name; } - if (!strpos($name, '.')) { - $name = strtolower($name); - return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null; - } else { - // 二维数组设置和获取支持 - $name = explode('.', $name, 2); - $name[0] = strtolower($name[0]); - return isset(self::$config[$range][$name[0]][$name[1]]) ? self::$config[$range][$name[0]][$name[1]] : null; + // 无参数时获取所有 + if (empty($name)) { + return $this->config; } + + if ('.' == substr($name, -1)) { + return $this->pull(substr($name, 0, -1)); + } + + if ($this->yaconf) { + $yaconfName = $this->getYaconfName($name); + + if (Yaconf::has($yaconfName)) { + return Yaconf::get($yaconfName); + } + } + + $name = explode('.', $name); + $name[0] = strtolower($name[0]); + $config = $this->config; + + // 按.拆分成多维数组进行判断 + foreach ($name as $val) { + if (isset($config[$val])) { + $config = $config[$val]; + } else { + return $default; + } + } + + return $config; } /** * 设置配置参数 name为数组则为批量设置 - * @param string|array $name 配置参数名(支持二级配置 .号分割) - * @param mixed $value 配置值 - * @param string $range 作用域 + * @access public + * @param string|array $name 配置参数名(支持三级配置 .号分割) + * @param mixed $value 配置值 * @return mixed */ - public static function set($name, $value = null, $range = '') + public function set($name, $value = null) { - $range = $range ?: self::$range; - if (!isset(self::$config[$range])) { - self::$config[$range] = []; - } if (is_string($name)) { - if (!strpos($name, '.')) { - self::$config[$range][strtolower($name)] = $value; - } else { - // 二维数组设置和获取支持 - $name = explode('.', $name, 2); - self::$config[$range][strtolower($name[0])][$name[1]] = $value; + if (false === strpos($name, '.')) { + $name = $this->prefix . '.' . $name; } - return; + + $name = explode('.', $name, 3); + + if (count($name) == 2) { + $this->config[strtolower($name[0])][$name[1]] = $value; + } else { + $this->config[strtolower($name[0])][$name[1]][$name[2]] = $value; + } + + return $value; } elseif (is_array($name)) { // 批量设置 if (!empty($value)) { - self::$config[$range][$value] = isset(self::$config[$range][$value]) ? - array_merge(self::$config[$range][$value], $name) : - self::$config[$range][$value] = $name; - return self::$config[$range][$value]; + if (isset($this->config[$value])) { + $result = array_merge($this->config[$value], $name); + } else { + $result = $name; + } + + $this->config[$value] = $result; } else { - return self::$config[$range] = array_merge(self::$config[$range], array_change_key_case($name)); + $result = $this->config = array_merge($this->config, $name); } } else { // 为空直接返回 已有配置 - return self::$config[$range]; + $result = $this->config; + } + + return $result; + } + + /** + * 移除配置 + * @access public + * @param string $name 配置参数名(支持三级配置 .号分割) + * @return void + */ + public function remove($name) + { + if (false === strpos($name, '.')) { + $name = $this->prefix . '.' . $name; + } + + $name = explode('.', $name, 3); + + if (count($name) == 2) { + unset($this->config[strtolower($name[0])][$name[1]]); + } else { + unset($this->config[strtolower($name[0])][$name[1]][$name[2]]); } } /** * 重置配置参数 + * @access public + * @param string $prefix 配置前缀名 + * @return void */ - public static function reset($range = '') + public function reset($prefix = '') { - $range = $range ?: self::$range; - if (true === $range) { - self::$config = []; + if ('' === $prefix) { + $this->config = []; } else { - self::$config[$range] = []; + $this->config[$prefix] = []; } } + + /** + * 设置配置 + * @access public + * @param string $name 参数名 + * @param mixed $value 值 + */ + public function __set($name, $value) + { + return $this->set($name, $value); + } + + /** + * 获取配置参数 + * @access public + * @param string $name 参数名 + * @return mixed + */ + public function __get($name) + { + return $this->get($name); + } + + /** + * 检测是否存在参数 + * @access public + * @param string $name 参数名 + * @return bool + */ + public function __isset($name) + { + return $this->has($name); + } + + // ArrayAccess + public function offsetSet($name, $value) + { + $this->set($name, $value); + } + + public function offsetExists($name) + { + return $this->has($name); + } + + public function offsetUnset($name) + { + $this->remove($name); + } + + public function offsetGet($name) + { + return $this->get($name); + } } diff --git a/thinkphp/library/think/Console.php b/thinkphp/library/think/Console.php index 1d97ab2b7..d12129072 100644 --- a/thinkphp/library/think/Console.php +++ b/thinkphp/library/think/Console.php @@ -35,50 +35,81 @@ class Console private $defaultCommand; private static $defaultCommands = [ - "think\\console\\command\\Help", - "think\\console\\command\\Lists", - "think\\console\\command\\Build", - "think\\console\\command\\Clear", - "think\\console\\command\\make\\Controller", - "think\\console\\command\\make\\Model", - "think\\console\\command\\optimize\\Autoload", - "think\\console\\command\\optimize\\Config", - "think\\console\\command\\optimize\\Route", - "think\\console\\command\\optimize\\Schema", + 'help' => "think\\console\\command\\Help", + 'list' => "think\\console\\command\\Lists", + 'build' => "think\\console\\command\\Build", + 'clear' => "think\\console\\command\\Clear", + 'make:command' => "think\\console\\command\\make\\Command", + 'make:controller' => "think\\console\\command\\make\\Controller", + 'make:model' => "think\\console\\command\\make\\Model", + 'make:middleware' => "think\\console\\command\\make\\Middleware", + 'make:validate' => "think\\console\\command\\make\\Validate", + 'optimize:autoload' => "think\\console\\command\\optimize\\Autoload", + 'optimize:config' => "think\\console\\command\\optimize\\Config", + 'optimize:schema' => "think\\console\\command\\optimize\\Schema", + 'optimize:route' => "think\\console\\command\\optimize\\Route", + 'run' => "think\\console\\command\\RunServer", + 'version' => "think\\console\\command\\Version", + 'route:list' => "think\\console\\command\\RouteList", ]; - public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + /** + * Console constructor. + * @access public + * @param string $name 名称 + * @param string $version 版本 + * @param null|string $user 执行用户 + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', $user = null) { $this->name = $name; $this->version = $version; + if ($user) { + $this->setUser($user); + } + $this->defaultCommand = 'list'; $this->definition = $this->getDefaultInputDefinition(); + } - foreach ($this->getDefaultCommands() as $command) { - $this->add($command); + /** + * 设置执行用户 + * @param $user + */ + public function setUser($user) + { + if (DIRECTORY_SEPARATOR == '\\') { + return; + } + + $user = posix_getpwnam($user); + if ($user) { + posix_setuid($user['uid']); + posix_setgid($user['gid']); } } + /** + * 初始化 Console + * @access public + * @param bool $run 是否运行 Console + * @return int|Console + */ public static function init($run = true) { static $console; + if (!$console) { - // 实例化console - $console = new self('Think Console', '0.1'); - // 读取指令集 - if (is_file(CONF_PATH . 'command' . EXT)) { - $commands = include CONF_PATH . 'command' . EXT; - if (is_array($commands)) { - foreach ($commands as $command) { - if (class_exists($command) && is_subclass_of($command, "\\think\\console\\Command")) { - // 注册指令 - $console->add(new $command()); - } - } - } - } + $config = Container::get('config')->pull('console'); + $console = new self($config['name'], $config['version'], $config['user']); + + $commands = $console->getDefinedCommands($config); + + // 添加指令集 + $console->addCommands($commands); } + if ($run) { // 运行 return $console->run(); @@ -88,9 +119,50 @@ class Console } /** - * @param $command - * @param array $parameters - * @param string $driver + * @access public + * @param array $config + * @return array + */ + public function getDefinedCommands(array $config = []) + { + $commands = self::$defaultCommands; + + if (!empty($config['auto_path']) && is_dir($config['auto_path'])) { + // 自动加载指令类 + $files = scandir($config['auto_path']); + + if (count($files) > 2) { + $beforeClass = get_declared_classes(); + + foreach ($files as $file) { + if (pathinfo($file, PATHINFO_EXTENSION) == 'php') { + include $config['auto_path'] . $file; + } + } + + $afterClass = get_declared_classes(); + $commands = array_merge($commands, array_diff($afterClass, $beforeClass)); + } + } + + $file = Container::get('env')->get('app_path') . 'command.php'; + + if (is_file($file)) { + $appCommands = include $file; + + if (is_array($appCommands)) { + $commands = array_merge($commands, $appCommands); + } + } + + return $commands; + } + + /** + * @access public + * @param string $command + * @param array $parameters + * @param string $driver * @return Output|Buffer */ public static function call($command, array $parameters = [], $driver = 'buffer') @@ -110,6 +182,7 @@ class Console /** * 执行当前的指令 + * @access public * @return int * @throws \Exception * @api @@ -154,8 +227,9 @@ class Console /** * 执行指令 - * @param Input $input - * @param Output $output + * @access public + * @param Input $input + * @param Output $output * @return int */ public function doRun(Input $input, Output $output) @@ -191,7 +265,8 @@ class Console /** * 设置输入参数定义 - * @param InputDefinition $definition + * @access public + * @param InputDefinition $definition */ public function setDefinition(InputDefinition $definition) { @@ -200,6 +275,7 @@ class Console /** * 获取输入参数定义 + * @access public * @return InputDefinition The InputDefinition instance */ public function getDefinition() @@ -209,6 +285,7 @@ class Console /** * Gets the help message. + * @access public * @return string A help message. */ public function getHelp() @@ -218,7 +295,8 @@ class Console /** * 是否捕获异常 - * @param bool $boolean + * @access public + * @param bool $boolean * @api */ public function setCatchExceptions($boolean) @@ -228,7 +306,8 @@ class Console /** * 是否自动退出 - * @param bool $boolean + * @access public + * @param bool $boolean * @api */ public function setAutoExit($boolean) @@ -238,6 +317,7 @@ class Console /** * 获取名称 + * @access public * @return string */ public function getName() @@ -247,7 +327,8 @@ class Console /** * 设置名称 - * @param string $name + * @access public + * @param string $name */ public function setName($name) { @@ -256,6 +337,7 @@ class Console /** * 获取版本 + * @access public * @return string * @api */ @@ -266,7 +348,8 @@ class Console /** * 设置版本 - * @param string $version + * @access public + * @param string $version */ public function setVersion($version) { @@ -275,6 +358,7 @@ class Console /** * 获取完整的版本号 + * @access public * @return string */ public function getLongVersion() @@ -287,8 +371,9 @@ class Console } /** - * 注册一个指令 - * @param string $name + * 注册一个指令 (便于动态创建指令) + * @access public + * @param string $name 指令名 * @return Command */ public function register($name) @@ -297,23 +382,38 @@ class Console } /** - * 添加指令 - * @param Command[] $commands + * 添加指令集 + * @access public + * @param array $commands */ public function addCommands(array $commands) { - foreach ($commands as $command) { - $this->add($command); + foreach ($commands as $key => $command) { + if (is_subclass_of($command, "\\think\\console\\Command")) { + // 注册指令 + $this->add($command, is_numeric($key) ? '' : $key); + } } } /** - * 添加一个指令 - * @param Command $command - * @return Command + * 注册一个指令(对象) + * @access public + * @param mixed $command 指令对象或者指令类名 + * @param string $name 指令名 留空则自动获取 + * @return mixed */ - public function add(Command $command) + public function add($command, $name) { + if ($name) { + $this->commands[$name] = $command; + return; + } + + if (is_string($command)) { + $command = new $command(); + } + $command->setConsole($this); if (!$command->isEnabled()) { @@ -336,7 +436,8 @@ class Console /** * 获取指令 - * @param string $name 指令名称 + * @access public + * @param string $name 指令名称 * @return Command * @throws \InvalidArgumentException */ @@ -348,6 +449,12 @@ class Console $command = $this->commands[$name]; + if (is_string($command)) { + $command = new $command(); + } + + $command->setConsole($this); + if ($this->wantHelps) { $this->wantHelps = false; @@ -363,7 +470,8 @@ class Console /** * 某个指令是否存在 - * @param string $name 指令名称 + * @access public + * @param string $name 指令名称 * @return bool */ public function has($name) @@ -373,6 +481,7 @@ class Console /** * 获取所有的命名空间 + * @access public * @return array */ public function getNamespaces() @@ -391,7 +500,8 @@ class Console /** * 查找注册命名空间中的名称或缩写。 - * @param string $namespace + * @access public + * @param string $namespace * @return string * @throws \InvalidArgumentException */ @@ -429,16 +539,19 @@ class Console /** * 查找指令 - * @param string $name 名称或者别名 + * @access public + * @param string $name 名称或者别名 * @return Command * @throws \InvalidArgumentException */ public function find($name) { $allCommands = array_keys($this->commands); - $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { + + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]) . '[^:]*'; }, $name); + $commands = preg_grep('{^' . $expr . '}', $allCommands); if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) { @@ -460,15 +573,6 @@ class Console throw new \InvalidArgumentException($message); } - if (count($commands) > 1) { - $commandList = $this->commands; - $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { - $commandName = $commandList[$nameOrAlias]->getName(); - - return $commandName === $nameOrAlias || !in_array($commandName, $commands); - }); - } - $exact = in_array($name, $commands, true); if (count($commands) > 1 && !$exact) { $suggestions = $this->getAbbreviationSuggestions(array_values($commands)); @@ -481,7 +585,8 @@ class Console /** * 获取所有的指令 - * @param string $namespace 命名空间 + * @access public + * @param string $namespace 命名空间 * @return Command[] * @api */ @@ -503,7 +608,8 @@ class Console /** * 获取可能的指令名 - * @param array $names + * @access public + * @param array $names * @return array */ public static function getAbbreviations($names) @@ -521,8 +627,9 @@ class Console /** * 配置基于用户的参数和选项的输入和输出实例。 - * @param Input $input 输入实例 - * @param Output $output 输出实例 + * @access protected + * @param Input $input 输入实例 + * @param Output $output 输出实例 */ protected function configureIO(Input $input, Output $output) { @@ -551,9 +658,10 @@ class Console /** * 执行指令 - * @param Command $command 指令实例 - * @param Input $input 输入实例 - * @param Output $output 输出实例 + * @access protected + * @param Command $command 指令实例 + * @param Input $input 输入实例 + * @param Output $output 输出实例 * @return int * @throws \Exception */ @@ -564,7 +672,8 @@ class Console /** * 获取指令的基础名称 - * @param Input $input + * @access protected + * @param Input $input * @return string */ protected function getCommandName(Input $input) @@ -574,6 +683,7 @@ class Console /** * 获取默认输入定义 + * @access protected * @return InputDefinition */ protected function getDefaultInputDefinition() @@ -590,23 +700,6 @@ class Console ]); } - /** - * 设置默认命令 - * @return Command[] An array of default Command instances - */ - protected function getDefaultCommands() - { - $defaultCommands = []; - - foreach (self::$defaultCommands as $classname) { - if (class_exists($classname) && is_subclass_of($classname, "think\\console\\Command")) { - $defaultCommands[] = new $classname(); - } - } - - return $defaultCommands; - } - public static function addDefaultCommands(array $classnames) { self::$defaultCommands = array_merge(self::$defaultCommands, $classnames); @@ -614,7 +707,8 @@ class Console /** * 获取可能的建议 - * @param array $abbrevs + * @access private + * @param array $abbrevs * @return string */ private function getAbbreviationSuggestions($abbrevs) @@ -624,8 +718,9 @@ class Console /** * 返回命名空间部分 - * @param string $name 指令 - * @param string $limit 部分的命名空间的最大数量 + * @access public + * @param string $name 指令 + * @param string $limit 部分的命名空间的最大数量 * @return string */ public function extractNamespace($name, $limit = null) @@ -638,8 +733,9 @@ class Console /** * 查找可替代的建议 - * @param string $name - * @param array|\Traversable $collection + * @access private + * @param string $name + * @param array|\Traversable $collection * @return array */ private function findAlternatives($name, $collection) @@ -688,7 +784,8 @@ class Console /** * 设置默认的指令 - * @param string $commandName The Command name + * @access public + * @param string $commandName The Command name */ public function setDefaultCommand($commandName) { @@ -697,7 +794,8 @@ class Console /** * 返回所有的命名空间 - * @param string $name + * @access private + * @param string $name * @return array */ private function extractAllNamespaces($name) @@ -716,4 +814,11 @@ class Console return $namespaces; } + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['commands'], $data['definition']); + + return $data; + } } diff --git a/thinkphp/library/think/Container.php b/thinkphp/library/think/Container.php new file mode 100644 index 000000000..cd7954c06 --- /dev/null +++ b/thinkphp/library/think/Container.php @@ -0,0 +1,568 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use ArrayAccess; +use ArrayIterator; +use Closure; +use Countable; +use InvalidArgumentException; +use IteratorAggregate; +use ReflectionClass; +use ReflectionException; +use ReflectionFunction; +use ReflectionMethod; +use think\exception\ClassNotFoundException; + +/** + * @package think + * @property Build $build + * @property Cache $cache + * @property Config $config + * @property Cookie $cookie + * @property Debug $debug + * @property Env $env + * @property Hook $hook + * @property Lang $lang + * @property Middleware $middleware + * @property Request $request + * @property Response $response + * @property Route $route + * @property Session $session + * @property Template $template + * @property Url $url + * @property Validate $validate + * @property View $view + * @property route\RuleName $rule_name + * @property Log $log + */ +class Container implements ArrayAccess, IteratorAggregate, Countable +{ + /** + * 容器对象实例 + * @var Container + */ + protected static $instance; + + /** + * 容器中的对象实例 + * @var array + */ + protected $instances = []; + + /** + * 容器绑定标识 + * @var array + */ + protected $bind = [ + 'app' => App::class, + 'build' => Build::class, + 'cache' => Cache::class, + 'config' => Config::class, + 'cookie' => Cookie::class, + 'debug' => Debug::class, + 'env' => Env::class, + 'hook' => Hook::class, + 'lang' => Lang::class, + 'log' => Log::class, + 'middleware' => Middleware::class, + 'request' => Request::class, + 'response' => Response::class, + 'route' => Route::class, + 'session' => Session::class, + 'template' => Template::class, + 'url' => Url::class, + 'validate' => Validate::class, + 'view' => View::class, + 'rule_name' => route\RuleName::class, + // 接口依赖注入 + 'think\LoggerInterface' => Log::class, + ]; + + /** + * 容器标识别名 + * @var array + */ + protected $name = []; + + /** + * 获取当前容器的实例(单例) + * @access public + * @return static + */ + public static function getInstance() + { + if (is_null(static::$instance)) { + static::$instance = new static; + } + + return static::$instance; + } + + /** + * 设置当前容器的实例 + * @access public + * @param object $instance + * @return void + */ + public static function setInstance($instance) + { + static::$instance = $instance; + } + + /** + * 获取容器中的对象实例 + * @access public + * @param string $abstract 类名或者标识 + * @param array|true $vars 变量 + * @param bool $newInstance 是否每次创建新的实例 + * @return object + */ + public static function get($abstract, $vars = [], $newInstance = false) + { + return static::getInstance()->make($abstract, $vars, $newInstance); + } + + /** + * 绑定一个类、闭包、实例、接口实现到容器 + * @access public + * @param string $abstract 类标识、接口 + * @param mixed $concrete 要绑定的类、闭包或者实例 + * @return Container + */ + public static function set($abstract, $concrete = null) + { + return static::getInstance()->bindTo($abstract, $concrete); + } + + /** + * 移除容器中的对象实例 + * @access public + * @param string $abstract 类标识、接口 + * @return void + */ + public static function remove($abstract) + { + return static::getInstance()->delete($abstract); + } + + /** + * 清除容器中的对象实例 + * @access public + * @return void + */ + public static function clear() + { + return static::getInstance()->flush(); + } + + /** + * 绑定一个类、闭包、实例、接口实现到容器 + * @access public + * @param string|array $abstract 类标识、接口 + * @param mixed $concrete 要绑定的类、闭包或者实例 + * @return $this + */ + public function bindTo($abstract, $concrete = null) + { + if (is_array($abstract)) { + $this->bind = array_merge($this->bind, $abstract); + } elseif ($concrete instanceof Closure) { + $this->bind[$abstract] = $concrete; + } elseif (is_object($concrete)) { + if (isset($this->bind[$abstract])) { + $abstract = $this->bind[$abstract]; + } + $this->instances[$abstract] = $concrete; + } else { + $this->bind[$abstract] = $concrete; + } + + return $this; + } + + /** + * 绑定一个类实例当容器 + * @access public + * @param string $abstract 类名或者标识 + * @param object|\Closure $instance 类的实例 + * @return $this + */ + public function instance($abstract, $instance) + { + if ($instance instanceof \Closure) { + $this->bind[$abstract] = $instance; + } else { + if (isset($this->bind[$abstract])) { + $abstract = $this->bind[$abstract]; + } + + $this->instances[$abstract] = $instance; + } + + return $this; + } + + /** + * 判断容器中是否存在类及标识 + * @access public + * @param string $abstract 类名或者标识 + * @return bool + */ + public function bound($abstract) + { + return isset($this->bind[$abstract]) || isset($this->instances[$abstract]); + } + + /** + * 判断容器中是否存在对象实例 + * @access public + * @param string $abstract 类名或者标识 + * @return bool + */ + public function exists($abstract) + { + if (isset($this->bind[$abstract])) { + $abstract = $this->bind[$abstract]; + } + + return isset($this->instances[$abstract]); + } + + /** + * 判断容器中是否存在类及标识 + * @access public + * @param string $name 类名或者标识 + * @return bool + */ + public function has($name) + { + return $this->bound($name); + } + + /** + * 创建类的实例 + * @access public + * @param string $abstract 类名或者标识 + * @param array|true $vars 变量 + * @param bool $newInstance 是否每次创建新的实例 + * @return object + */ + public function make($abstract, $vars = [], $newInstance = false) + { + if (true === $vars) { + // 总是创建新的实例化对象 + $newInstance = true; + $vars = []; + } + + $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract; + + if (isset($this->instances[$abstract]) && !$newInstance) { + return $this->instances[$abstract]; + } + + if (isset($this->bind[$abstract])) { + $concrete = $this->bind[$abstract]; + + if ($concrete instanceof Closure) { + $object = $this->invokeFunction($concrete, $vars); + } else { + $this->name[$abstract] = $concrete; + return $this->make($concrete, $vars, $newInstance); + } + } else { + $object = $this->invokeClass($abstract, $vars); + } + + if (!$newInstance) { + $this->instances[$abstract] = $object; + } + + return $object; + } + + /** + * 删除容器中的对象实例 + * @access public + * @param string|array $abstract 类名或者标识 + * @return void + */ + public function delete($abstract) + { + foreach ((array) $abstract as $name) { + $name = isset($this->name[$name]) ? $this->name[$name] : $name; + + if (isset($this->instances[$name])) { + unset($this->instances[$name]); + } + } + } + + /** + * 获取容器中的对象实例 + * @access public + * @return array + */ + public function all() + { + return $this->instances; + } + + /** + * 清除容器中的对象实例 + * @access public + * @return void + */ + public function flush() + { + $this->instances = []; + $this->bind = []; + $this->name = []; + } + + /** + * 执行函数或者闭包方法 支持参数调用 + * @access public + * @param mixed $function 函数或者闭包 + * @param array $vars 参数 + * @return mixed + */ + public function invokeFunction($function, $vars = []) + { + try { + $reflect = new ReflectionFunction($function); + + $args = $this->bindParams($reflect, $vars); + + return call_user_func_array($function, $args); + } catch (ReflectionException $e) { + throw new Exception('function not exists: ' . $function . '()'); + } + } + + /** + * 调用反射执行类的方法 支持参数绑定 + * @access public + * @param mixed $method 方法 + * @param array $vars 参数 + * @return mixed + */ + public function invokeMethod($method, $vars = []) + { + try { + if (is_array($method)) { + $class = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]); + $reflect = new ReflectionMethod($class, $method[1]); + } else { + // 静态方法 + $reflect = new ReflectionMethod($method); + } + + $args = $this->bindParams($reflect, $vars); + + return $reflect->invokeArgs(isset($class) ? $class : null, $args); + } catch (ReflectionException $e) { + if (is_array($method) && is_object($method[0])) { + $method[0] = get_class($method[0]); + } + + throw new Exception('method not exists: ' . (is_array($method) ? $method[0] . '::' . $method[1] : $method) . '()'); + } + } + + /** + * 调用反射执行类的方法 支持参数绑定 + * @access public + * @param object $instance 对象实例 + * @param mixed $reflect 反射类 + * @param array $vars 参数 + * @return mixed + */ + public function invokeReflectMethod($instance, $reflect, $vars = []) + { + $args = $this->bindParams($reflect, $vars); + + return $reflect->invokeArgs($instance, $args); + } + + /** + * 调用反射执行callable 支持参数绑定 + * @access public + * @param mixed $callable + * @param array $vars 参数 + * @return mixed + */ + public function invoke($callable, $vars = []) + { + if ($callable instanceof Closure) { + return $this->invokeFunction($callable, $vars); + } + + return $this->invokeMethod($callable, $vars); + } + + /** + * 调用反射执行类的实例化 支持依赖注入 + * @access public + * @param string $class 类名 + * @param array $vars 参数 + * @return mixed + */ + public function invokeClass($class, $vars = []) + { + try { + $reflect = new ReflectionClass($class); + + if ($reflect->hasMethod('__make')) { + $method = new ReflectionMethod($class, '__make'); + + if ($method->isPublic() && $method->isStatic()) { + $args = $this->bindParams($method, $vars); + return $method->invokeArgs(null, $args); + } + } + + $constructor = $reflect->getConstructor(); + + $args = $constructor ? $this->bindParams($constructor, $vars) : []; + + return $reflect->newInstanceArgs($args); + + } catch (ReflectionException $e) { + throw new ClassNotFoundException('class not exists: ' . $class, $class); + } + } + + /** + * 绑定参数 + * @access protected + * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类 + * @param array $vars 参数 + * @return array + */ + protected function bindParams($reflect, $vars = []) + { + if ($reflect->getNumberOfParameters() == 0) { + return []; + } + + // 判断数组类型 数字数组时按顺序绑定参数 + reset($vars); + $type = key($vars) === 0 ? 1 : 0; + $params = $reflect->getParameters(); + + foreach ($params as $param) { + $name = $param->getName(); + $lowerName = Loader::parseName($name); + $class = $param->getClass(); + + if ($class) { + $args[] = $this->getObjectParam($class->getName(), $vars); + } elseif (1 == $type && !empty($vars)) { + $args[] = array_shift($vars); + } elseif (0 == $type && isset($vars[$name])) { + $args[] = $vars[$name]; + } elseif (0 == $type && isset($vars[$lowerName])) { + $args[] = $vars[$lowerName]; + } elseif ($param->isDefaultValueAvailable()) { + $args[] = $param->getDefaultValue(); + } else { + throw new InvalidArgumentException('method param miss:' . $name); + } + } + + return $args; + } + + /** + * 获取对象类型的参数值 + * @access protected + * @param string $className 类名 + * @param array $vars 参数 + * @return mixed + */ + protected function getObjectParam($className, &$vars) + { + $array = $vars; + $value = array_shift($array); + + if ($value instanceof $className) { + $result = $value; + array_shift($vars); + } else { + $result = $this->make($className); + } + + return $result; + } + + public function __set($name, $value) + { + $this->bindTo($name, $value); + } + + public function __get($name) + { + return $this->make($name); + } + + public function __isset($name) + { + return $this->bound($name); + } + + public function __unset($name) + { + $this->delete($name); + } + + public function offsetExists($key) + { + return $this->__isset($key); + } + + public function offsetGet($key) + { + return $this->__get($key); + } + + public function offsetSet($key, $value) + { + $this->__set($key, $value); + } + + public function offsetUnset($key) + { + $this->__unset($key); + } + + //Countable + public function count() + { + return count($this->instances); + } + + //IteratorAggregate + public function getIterator() + { + return new ArrayIterator($this->instances); + } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['instances'], $data['instance']); + + return $data; + } +} diff --git a/thinkphp/library/think/Controller.php b/thinkphp/library/think/Controller.php index 7aba39cd1..d16a1ed50 100644 --- a/thinkphp/library/think/Controller.php +++ b/thinkphp/library/think/Controller.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,70 +11,111 @@ namespace think; -\think\Loader::import('controller/Jump', TRAIT_PATH, EXT); - use think\exception\ValidateException; +use traits\controller\Jump; class Controller { - use \traits\controller\Jump; + use Jump; /** - * @var \think\View 视图类实例 + * 视图类实例 + * @var \think\View */ protected $view; + /** - * @var \think\Request Request实例 + * Request实例 + * @var \think\Request */ protected $request; - // 验证失败是否抛出异常 + + /** + * 验证失败是否抛出异常 + * @var bool + */ protected $failException = false; - // 是否批量验证 + + /** + * 是否批量验证 + * @var bool + */ protected $batchValidate = false; /** - * 前置操作方法列表 + * 前置操作方法列表(即将废弃) * @var array $beforeActionList - * @access protected */ protected $beforeActionList = []; + /** + * 控制器中间件 + * @var array + */ + protected $middleware = []; + /** * 构造方法 - * @param Request $request Request对象 * @access public */ - public function __construct(Request $request = null) + public function __construct(App $app = null) { - if (is_null($request)) { - $request = Request::instance(); - } - $this->view = View::instance(Config::get('template'), Config::get('view_replace_str')); - $this->request = $request; + $this->app = $app ?: Container::get('app'); + $this->request = $this->app['request']; + $this->view = $this->app['view']; // 控制器初始化 - $this->_initialize(); + $this->initialize(); - // 前置操作方法 - if ($this->beforeActionList) { - foreach ($this->beforeActionList as $method => $options) { - is_numeric($method) ? - $this->beforeAction($options) : - $this->beforeAction($method, $options); - } + $this->registerMiddleware(); + + // 前置操作方法 即将废弃 + foreach ((array) $this->beforeActionList as $method => $options) { + is_numeric($method) ? + $this->beforeAction($options) : + $this->beforeAction($method, $options); } } // 初始化 - protected function _initialize() + protected function initialize() + {} + + // 注册控制器中间件 + public function registerMiddleware() { + foreach ($this->middleware as $key => $val) { + if (!is_int($key)) { + $only = $except = null; + + if (isset($val['only'])) { + $only = array_map(function ($item) { + return strtolower($item); + }, $val['only']); + } elseif (isset($val['except'])) { + $except = array_map(function ($item) { + return strtolower($item); + }, $val['except']); + } + + if (isset($only) && !in_array($this->request->action(), $only)) { + continue; + } elseif (isset($except) && in_array($this->request->action(), $except)) { + continue; + } else { + $val = $key; + } + } + + $this->app['middleware']->controller($val); + } } /** * 前置操作 * @access protected - * @param string $method 前置操作方法名 - * @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]] + * @param string $method 前置操作方法名 + * @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]] */ protected function beforeAction($method, $options = []) { @@ -82,14 +123,24 @@ class Controller if (is_string($options['only'])) { $options['only'] = explode(',', $options['only']); } - if (!in_array($this->request->action(), $options['only'])) { + + $only = array_map(function ($val) { + return strtolower($val); + }, $options['only']); + + if (!in_array($this->request->action(), $only)) { return; } } elseif (isset($options['except'])) { if (is_string($options['except'])) { $options['except'] = explode(',', $options['except']); } - if (in_array($this->request->action(), $options['except'])) { + + $except = array_map(function ($val) { + return strtolower($val); + }, $options['except']); + + if (in_array($this->request->action(), $except)) { return; } } @@ -100,92 +151,109 @@ class Controller /** * 加载模板输出 * @access protected - * @param string $template 模板文件名 - * @param array $vars 模板输出变量 - * @param array $replace 模板替换 - * @param array $config 模板参数 + * @param string $template 模板文件名 + * @param array $vars 模板输出变量 + * @param array $config 模板参数 * @return mixed */ - protected function fetch($template = '', $vars = [], $replace = [], $config = []) + protected function fetch($template = '', $vars = [], $config = []) { - return $this->view->fetch($template, $vars, $replace, $config); + return $this->view->fetch($template, $vars, $config); } /** * 渲染内容输出 * @access protected - * @param string $content 模板内容 - * @param array $vars 模板输出变量 - * @param array $replace 替换内容 - * @param array $config 模板参数 + * @param string $content 模板内容 + * @param array $vars 模板输出变量 + * @param array $config 模板参数 * @return mixed */ - protected function display($content = '', $vars = [], $replace = [], $config = []) + protected function display($content = '', $vars = [], $config = []) { - return $this->view->display($content, $vars, $replace, $config); + return $this->view->display($content, $vars, $config); } /** * 模板变量赋值 * @access protected - * @param mixed $name 要显示的模板变量 - * @param mixed $value 变量的值 - * @return void + * @param mixed $name 要显示的模板变量 + * @param mixed $value 变量的值 + * @return $this */ protected function assign($name, $value = '') { $this->view->assign($name, $value); + + return $this; + } + + /** + * 视图过滤 + * @access protected + * @param Callable $filter 过滤方法或闭包 + * @return $this + */ + protected function filter($filter) + { + $this->view->filter($filter); + + return $this; } /** * 初始化模板引擎 * @access protected - * @param array|string $engine 引擎参数 - * @return void + * @param array|string $engine 引擎参数 + * @return $this */ protected function engine($engine) { $this->view->engine($engine); + + return $this; } /** * 设置验证失败后是否抛出异常 * @access protected - * @param bool $fail 是否抛出异常 + * @param bool $fail 是否抛出异常 * @return $this */ protected function validateFailException($fail = true) { $this->failException = $fail; + return $this; } /** * 验证数据 * @access protected - * @param array $data 数据 - * @param string|array $validate 验证器名或者验证规则数组 - * @param array $message 提示信息 - * @param bool $batch 是否批量验证 - * @param mixed $callback 回调方法(闭包) + * @param array $data 数据 + * @param string|array $validate 验证器名或者验证规则数组 + * @param array $message 提示信息 + * @param bool $batch 是否批量验证 + * @param mixed $callback 回调方法(闭包) * @return array|string|true * @throws ValidateException */ protected function validate($data, $validate, $message = [], $batch = false, $callback = null) { if (is_array($validate)) { - $v = Loader::validate(); + $v = $this->app->validate(); $v->rule($validate); } else { if (strpos($validate, '.')) { // 支持场景 list($validate, $scene) = explode('.', $validate); } - $v = Loader::validate($validate); + $v = $this->app->validate($validate); if (!empty($scene)) { $v->scene($scene); } } + // 是否批量验证 if ($batch || $this->batchValidate) { $v->batch(true); @@ -202,11 +270,18 @@ class Controller if (!$v->check($data)) { if ($this->failException) { throw new ValidateException($v->getError()); - } else { - return $v->getError(); } - } else { - return true; + return $v->getError(); } + + return true; + } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app'], $data['request']); + + return $data; } } diff --git a/thinkphp/library/think/Cookie.php b/thinkphp/library/think/Cookie.php index 3205fcd9a..6a9fb1ee5 100644 --- a/thinkphp/library/think/Cookie.php +++ b/thinkphp/library/think/Cookie.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,7 +13,11 @@ namespace think; class Cookie { - protected static $config = [ + /** + * 配置参数 + * @var array + */ + protected $config = [ // cookie 名称前缀 'prefix' => '', // cookie 保存时间 @@ -25,56 +29,66 @@ class Cookie // cookie 启用安全传输 'secure' => false, // httponly设置 - 'httponly' => '', + 'httponly' => false, // 是否使用 setcookie 'setcookie' => true, ]; - protected static $init; + /** + * 构造方法 + * @access public + */ + public function __construct(array $config = []) + { + $this->init($config); + } /** * Cookie初始化 - * @param array $config + * @access public + * @param array $config * @return void */ - public static function init(array $config = []) + public function init(array $config = []) { - if (empty($config)) { - $config = Config::get('cookie'); - } - self::$config = array_merge(self::$config, array_change_key_case($config)); - if (!empty(self::$config['httponly'])) { + $this->config = array_merge($this->config, array_change_key_case($config)); + + if (!empty($this->config['httponly']) && PHP_SESSION_ACTIVE != session_status()) { ini_set('session.cookie_httponly', 1); } - self::$init = true; + } + + public static function __make(Config $config) + { + return new static($config->pull('cookie')); } /** * 设置或者获取cookie作用域(前缀) - * @param string $prefix + * @access public + * @param string $prefix * @return string|void */ - public static function prefix($prefix = '') + public function prefix($prefix = '') { if (empty($prefix)) { - return self::$config['prefix']; + return $this->config['prefix']; } - self::$config['prefix'] = $prefix; + + $this->config['prefix'] = $prefix; } /** * Cookie 设置、获取、删除 * - * @param string $name cookie名称 - * @param mixed $value cookie值 - * @param mixed $option 可选参数 可能会是 null|integer|string - * - * @return mixed - * @internal param mixed $options cookie参数 + * @access public + * @param string $name cookie名称 + * @param mixed $value cookie值 + * @param mixed $option 可选参数 可能会是 null|integer|string + * @return void */ - public static function set($name, $value = '', $option = null) + public function set($name, $value = '', $option = null) { - !isset(self::$init) && self::init(); // 参数设置(会覆盖黙认设置) if (!is_null($option)) { if (is_numeric($option)) { @@ -82,67 +96,90 @@ class Cookie } elseif (is_string($option)) { parse_str($option, $option); } - $config = array_merge(self::$config, array_change_key_case($option)); + + $config = array_merge($this->config, array_change_key_case($option)); } else { - $config = self::$config; + $config = $this->config; } + $name = $config['prefix'] . $name; + // 设置cookie if (is_array($value)) { - array_walk_recursive($value, 'self::jsonFormatProtect', 'encode'); + array_walk_recursive($value, [$this, 'jsonFormatProtect'], 'encode'); $value = 'think:' . json_encode($value); } + $expire = !empty($config['expire']) ? $_SERVER['REQUEST_TIME'] + intval($config['expire']) : 0; + if ($config['setcookie']) { - setcookie($name, $value, $expire, $config['path'], $config['domain'], $config['secure'], $config['httponly']); + $this->setCookie($name, $value, $expire, $config); } + $_COOKIE[$name] = $value; } /** - * 永久保存Cookie数据 - * @param string $name cookie名称 - * @param mixed $value cookie值 - * @param mixed $option 可选参数 可能会是 null|integer|string + * Cookie 设置保存 + * + * @access public + * @param string $name cookie名称 + * @param mixed $value cookie值 + * @param array $option 可选参数 * @return void */ - public static function forever($name, $value = '', $option = null) + protected function setCookie($name, $value, $expire, $option = []) + { + setcookie($name, $value, $expire, $option['path'], $option['domain'], $option['secure'], $option['httponly']); + } + + /** + * 永久保存Cookie数据 + * @access public + * @param string $name cookie名称 + * @param mixed $value cookie值 + * @param mixed $option 可选参数 可能会是 null|integer|string + * @return void + */ + public function forever($name, $value = '', $option = null) { if (is_null($option) || is_numeric($option)) { $option = []; } + $option['expire'] = 315360000; - self::set($name, $value, $option); + + $this->set($name, $value, $option); } /** * 判断Cookie数据 - * @param string $name cookie名称 - * @param string|null $prefix cookie前缀 + * @access public + * @param string $name cookie名称 + * @param string|null $prefix cookie前缀 * @return bool */ - public static function has($name, $prefix = null) + public function has($name, $prefix = null) { - !isset(self::$init) && self::init(); - $prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; + $prefix = !is_null($prefix) ? $prefix : $this->config['prefix']; $name = $prefix . $name; + return isset($_COOKIE[$name]); } /** * Cookie获取 - * @param string $name cookie名称 - * @param string|null $prefix cookie前缀 + * @access public + * @param string $name cookie名称 留空获取全部 + * @param string|null $prefix cookie前缀 * @return mixed */ - public static function get($name = '', $prefix = null) + public function get($name = '', $prefix = null) { - !isset(self::$init) && self::init(); - $prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; + $prefix = !is_null($prefix) ? $prefix : $this->config['prefix']; $key = $prefix . $name; if ('' == $name) { - // 获取全部 if ($prefix) { $value = []; foreach ($_COOKIE as $k => $val) { @@ -155,66 +192,73 @@ class Cookie } } elseif (isset($_COOKIE[$key])) { $value = $_COOKIE[$key]; + if (0 === strpos($value, 'think:')) { $value = substr($value, 6); $value = json_decode($value, true); - array_walk_recursive($value, 'self::jsonFormatProtect', 'decode'); + array_walk_recursive($value, [$this, 'jsonFormatProtect'], 'decode'); } } else { $value = null; } + return $value; } /** * Cookie删除 - * @param string $name cookie名称 - * @param string|null $prefix cookie前缀 - * @return mixed + * @access public + * @param string $name cookie名称 + * @param string|null $prefix cookie前缀 + * @return void */ - public static function delete($name, $prefix = null) + public function delete($name, $prefix = null) { - !isset(self::$init) && self::init(); - $config = self::$config; + $config = $this->config; $prefix = !is_null($prefix) ? $prefix : $config['prefix']; $name = $prefix . $name; + if ($config['setcookie']) { - setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']); + $this->setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, $config); } + // 删除指定cookie unset($_COOKIE[$name]); } /** * Cookie清空 - * @param string|null $prefix cookie前缀 - * @return mixed + * @access public + * @param string|null $prefix cookie前缀 + * @return void */ - public static function clear($prefix = null) + public function clear($prefix = null) { // 清除指定前缀的所有cookie if (empty($_COOKIE)) { return; } - !isset(self::$init) && self::init(); + // 要删除的cookie前缀,不指定则删除config设置的指定前缀 - $config = self::$config; + $config = $this->config; $prefix = !is_null($prefix) ? $prefix : $config['prefix']; + if ($prefix) { // 如果前缀为空字符串将不作处理直接返回 foreach ($_COOKIE as $key => $val) { if (0 === strpos($key, $prefix)) { if ($config['setcookie']) { - setcookie($key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']); + $this->setcookie($key, '', $_SERVER['REQUEST_TIME'] - 3600, $config); } unset($_COOKIE[$key]); } } } + return; } - private static function jsonFormatProtect(&$val, $key, $type = 'encode') + private function jsonFormatProtect(&$val, $key, $type = 'encode') { if (!empty($val) && true !== $val) { $val = 'decode' == $type ? urldecode($val) : urlencode($val); diff --git a/thinkphp/library/think/Db.php b/thinkphp/library/think/Db.php index 3e613f7a2..9280eac0e 100644 --- a/thinkphp/library/think/Db.php +++ b/thinkphp/library/think/Db.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,22 +12,31 @@ namespace think; use think\db\Connection; -use think\db\Query; /** * Class Db * @package think - * @method Query table(string $table) static 指定数据表(含前缀) - * @method Query name(string $name) static 指定数据表(不含前缀) - * @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 - * @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 - * @method Query union(mixed $union, boolean $all = false) static UNION查询 - * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT - * @method Query order(mixed $field, string $order = null) static 查询ORDER - * @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 + * @method \think\db\Query master() static 从主服务器读取数据 + * @method \think\db\Query readMaster(bool $all = false) static 后续从主服务器读取数据 + * @method \think\db\Query table(string $table) static 指定数据表(含前缀) + * @method \think\db\Query name(string $name) static 指定数据表(不含前缀) + * @method \think\db\Expression raw(string $value) static 使用表达式设置数据 + * @method \think\db\Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 + * @method \think\db\Query whereRaw(string $where, array $bind = []) static 表达式查询 + * @method \think\db\Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询 + * @method \think\db\Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询 + * @method \think\db\Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 + * @method \think\db\Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 + * @method \think\db\Query field(mixed $field, boolean $except = false) static 指定查询字段 + * @method \think\db\Query fieldRaw(string $field, array $bind = []) static 指定查询字段 + * @method \think\db\Query union(mixed $union, boolean $all = false) static UNION查询 + * @method \think\db\Query limit(mixed $offset, integer $length = null) static 查询LIMIT + * @method \think\db\Query order(mixed $field, string $order = null) static 查询ORDER + * @method \think\db\Query orderRaw(string $field, array $bind = []) static 查询ORDER + * @method \think\db\Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 + * @method \think\db\Query withAttr(string $name,callable $callback = null) static 使用获取器获取数据 * @method mixed value(string $field) static 获取某个字段的值 * @method array column(string $field, string $key = '') static 获取某个列的值 - * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 * @method mixed find(mixed $data = null) static 查询单个记录 * @method mixed select(mixed $data = null) static 查询多个记录 * @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录 @@ -36,96 +45,132 @@ use think\db\Query; * @method integer update(array $data) static 更新记录 * @method integer delete(mixed $data = null) static 删除记录 * @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据 + * @method \Generator cursor(mixed $data = null) static 使用游标查找记录 * @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询 * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行 - * @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询 + * @method \think\Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询 * @method mixed transaction(callable $callback) static 执行数据库事务 * @method void startTrans() static 启动事务 * @method void commit() static 用于非自动提交状态下面的查询提交 * @method void rollback() static 事务回滚 * @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句 - * @method string quote(string $str) static SQL指令安全过滤 - * @method string getLastInsID($sequence = null) static 获取最近插入的ID + * @method string getLastInsID(string $sequence = null) static 获取最近插入的ID */ class Db { - // 数据库连接实例 - private static $instance = []; - // 查询次数 + /** + * 当前数据库连接对象 + * @var Connection + */ + protected static $connection; + + /** + * 数据库配置 + * @var array + */ + protected static $config = []; + + /** + * 查询次数 + * @var integer + */ public static $queryTimes = 0; - // 执行次数 + + /** + * 执行次数 + * @var integer + */ public static $executeTimes = 0; /** - * 数据库初始化 并取得数据库类实例 - * @static + * 配置 * @access public - * @param mixed $config 连接配置 - * @param bool|string $name 连接标识 true 强制重新连接 - * @return Connection + * @param mixed $config + * @return void + */ + public static function init($config = []) + { + self::$config = $config; + + if (empty($config['query'])) { + self::$config['query'] = '\\think\\db\\Query'; + } + } + + /** + * 获取数据库配置 + * @access public + * @param string $config 配置名称 + * @return mixed + */ + public static function getConfig($name = '') + { + if ('' === $name) { + return self::$config; + } + + return isset(self::$config[$name]) ? self::$config[$name] : null; + } + + /** + * 切换数据库连接 + * @access public + * @param mixed $config 连接配置 + * @param bool|string $name 连接标识 true 强制重新连接 + * @param string $query 查询对象类名 + * @return mixed 返回查询对象实例 * @throws Exception */ - public static function connect($config = [], $name = false) + public static function connect($config = [], $name = false, $query = '') { - if (false === $name) { - $name = md5(serialize($config)); - } - if (true === $name || !isset(self::$instance[$name])) { - // 解析连接参数 支持数组和字符串 - $options = self::parseConfig($config); - if (empty($options['type'])) { - throw new \InvalidArgumentException('Underfined db type'); - } - $class = false !== strpos($options['type'], '\\') ? $options['type'] : '\\think\\db\\connector\\' . ucwords($options['type']); - // 记录初始化信息 - if (App::$debug) { - Log::record('[ DB ] INIT ' . $options['type'], 'info'); - } - if (true === $name) { - return new $class($options); - } else { - self::$instance[$name] = new $class($options); - } - } - return self::$instance[$name]; + // 解析配置参数 + $options = self::parseConfig($config ?: self::$config); + + $query = $query ?: $options['query']; + + // 创建数据库连接对象实例 + self::$connection = Connection::instance($options, $name); + + return new $query(self::$connection); } /** * 数据库连接参数解析 - * @static * @access private - * @param mixed $config + * @param mixed $config * @return array */ private static function parseConfig($config) { - if (empty($config)) { - $config = Config::get('database'); - } elseif (is_string($config) && false === strpos($config, '/')) { + if (is_string($config) && false === strpos($config, '/')) { // 支持读取配置参数 - $config = Config::get($config); + $config = isset(self::$config[$config]) ? self::$config[$config] : self::$config; } - if (is_string($config)) { - return self::parseDsn($config); - } else { - return $config; + + $result = is_string($config) ? self::parseDsnConfig($config) : $config; + + if (empty($result['query'])) { + $result['query'] = self::$config['query']; } + + return $result; } /** * DSN解析 * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8 - * @static * @access private - * @param string $dsnStr + * @param string $dsnStr * @return array */ - private static function parseDsn($dsnStr) + private static function parseDsnConfig($dsnStr) { $info = parse_url($dsnStr); + if (!$info) { return []; } + $dsn = [ 'type' => $info['scheme'], 'username' => isset($info['user']) ? $info['user'] : '', @@ -141,13 +186,12 @@ class Db } else { $dsn['params'] = []; } + return $dsn; } - // 调用驱动类的方法 - public static function __callStatic($method, $params) + public static function __callStatic($method, $args) { - // 自动初始化数据库 - return call_user_func_array([self::connect(), $method], $params); + return call_user_func_array([static::connect(), $method], $args); } } diff --git a/thinkphp/library/think/Debug.php b/thinkphp/library/think/Debug.php index 844dfb89f..776e17873 100644 --- a/thinkphp/library/think/Debug.php +++ b/thinkphp/library/think/Debug.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,161 +11,224 @@ namespace think; -use think\exception\ClassNotFoundException; +use think\model\Collection as ModelCollection; use think\response\Redirect; class Debug { - // 区间时间信息 - protected static $info = []; - // 区间内存信息 - protected static $mem = []; + /** + * 配置参数 + * @var array + */ + protected $config = []; + + /** + * 区间时间信息 + * @var array + */ + protected $info = []; + + /** + * 区间内存信息 + * @var array + */ + protected $mem = []; + + /** + * 应用对象 + * @var App + */ + protected $app; + + public function __construct(App $app, array $config = []) + { + $this->app = $app; + $this->config = $config; + } + + public static function __make(App $app, Config $config) + { + return new static($app, $config->pull('trace')); + } + + public function setConfig(array $config) + { + $this->config = array_merge($this->config, $config); + } /** * 记录时间(微秒)和内存使用情况 - * @param string $name 标记位置 - * @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存 - * @return mixed + * @access public + * @param string $name 标记位置 + * @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存 + * @return void */ - public static function remark($name, $value = '') + public function remark($name, $value = '') { // 记录时间和内存使用 - self::$info[$name] = is_float($value) ? $value : microtime(true); + $this->info[$name] = is_float($value) ? $value : microtime(true); + if ('time' != $value) { - self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage(); - self::$mem['peak'][$name] = memory_get_peak_usage(); + $this->mem['mem'][$name] = is_float($value) ? $value : memory_get_usage(); + $this->mem['peak'][$name] = memory_get_peak_usage(); } } /** * 统计某个区间的时间(微秒)使用情况 - * @param string $start 开始标签 - * @param string $end 结束标签 - * @param integer|string $dec 小数位 + * @access public + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer|string $dec 小数位 * @return integer */ - public static function getRangeTime($start, $end, $dec = 6) + public function getRangeTime($start, $end, $dec = 6) { - if (!isset(self::$info[$end])) { - self::$info[$end] = microtime(true); + if (!isset($this->info[$end])) { + $this->info[$end] = microtime(true); } - return number_format((self::$info[$end] - self::$info[$start]), $dec); + + return number_format(($this->info[$end] - $this->info[$start]), $dec); } /** * 统计从开始到统计时的时间(微秒)使用情况 - * @param integer|string $dec 小数位 + * @access public + * @param integer|string $dec 小数位 * @return integer */ - public static function getUseTime($dec = 6) + public function getUseTime($dec = 6) { - return number_format((microtime(true) - THINK_START_TIME), $dec); + return number_format((microtime(true) - $this->app->getBeginTime()), $dec); } /** * 获取当前访问的吞吐率情况 + * @access public * @return string */ - public static function getThroughputRate() + public function getThroughputRate() { - return number_format(1 / self::getUseTime(), 2) . 'req/s'; + return number_format(1 / $this->getUseTime(), 2) . 'req/s'; } /** * 记录区间的内存使用情况 - * @param string $start 开始标签 - * @param string $end 结束标签 - * @param integer|string $dec 小数位 + * @access public + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer|string $dec 小数位 * @return string */ - public static function getRangeMem($start, $end, $dec = 2) + public function getRangeMem($start, $end, $dec = 2) { - if (!isset(self::$mem['mem'][$end])) { - self::$mem['mem'][$end] = memory_get_usage(); + if (!isset($this->mem['mem'][$end])) { + $this->mem['mem'][$end] = memory_get_usage(); } - $size = self::$mem['mem'][$end] - self::$mem['mem'][$start]; + + $size = $this->mem['mem'][$end] - $this->mem['mem'][$start]; $a = ['B', 'KB', 'MB', 'GB', 'TB']; $pos = 0; + while ($size >= 1024) { $size /= 1024; $pos++; } + return round($size, $dec) . " " . $a[$pos]; } /** * 统计从开始到统计时的内存使用情况 - * @param integer|string $dec 小数位 + * @access public + * @param integer|string $dec 小数位 * @return string */ - public static function getUseMem($dec = 2) + public function getUseMem($dec = 2) { - $size = memory_get_usage() - THINK_START_MEM; + $size = memory_get_usage() - $this->app->getBeginMem(); $a = ['B', 'KB', 'MB', 'GB', 'TB']; $pos = 0; + while ($size >= 1024) { $size /= 1024; $pos++; } + return round($size, $dec) . " " . $a[$pos]; } /** * 统计区间的内存峰值情况 - * @param string $start 开始标签 - * @param string $end 结束标签 - * @param integer|string $dec 小数位 - * @return mixed + * @access public + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer|string $dec 小数位 + * @return string */ - public static function getMemPeak($start, $end, $dec = 2) + public function getMemPeak($start, $end, $dec = 2) { - if (!isset(self::$mem['peak'][$end])) { - self::$mem['peak'][$end] = memory_get_peak_usage(); + if (!isset($this->mem['peak'][$end])) { + $this->mem['peak'][$end] = memory_get_peak_usage(); } - $size = self::$mem['peak'][$end] - self::$mem['peak'][$start]; + + $size = $this->mem['peak'][$end] - $this->mem['peak'][$start]; $a = ['B', 'KB', 'MB', 'GB', 'TB']; $pos = 0; + while ($size >= 1024) { $size /= 1024; $pos++; } + return round($size, $dec) . " " . $a[$pos]; } /** * 获取文件加载信息 - * @param bool $detail 是否显示详细 + * @access public + * @param bool $detail 是否显示详细 * @return integer|array */ - public static function getFile($detail = false) + public function getFile($detail = false) { if ($detail) { $files = get_included_files(); $info = []; + foreach ($files as $key => $file) { $info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )'; } + return $info; } + return count(get_included_files()); } /** * 浏览器友好的变量输出 - * @param mixed $var 变量 - * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 - * @param string $label 标签 默认为空 - * @param integer $flags htmlspecialchars flags + * @access public + * @param mixed $var 变量 + * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 + * @param string $label 标签 默认为空 + * @param integer $flags htmlspecialchars flags * @return void|string */ - public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE) + public function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE) { $label = (null === $label) ? '' : rtrim($label) . ':'; + if ($var instanceof Model || $var instanceof ModelCollection) { + $var = $var->toArray(); + } + ob_start(); var_dump($var); + $output = ob_get_clean(); $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output); - if (IS_CLI) { + + if (PHP_SAPI == 'cli') { $output = PHP_EOL . $label . $output . PHP_EOL; } else { if (!extension_loaded('xdebug')) { @@ -176,28 +239,23 @@ class Debug if ($echo) { echo($output); return; - } else { - return $output; } + return $output; } - public static function inject(Response $response, &$content) + public function inject(Response $response, &$content) { - $config = Config::get('trace'); - $type = isset($config['type']) ? $config['type'] : 'Html'; - $request = Request::instance(); - $class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type); + $config = $this->config; + $type = isset($config['type']) ? $config['type'] : 'Html'; + unset($config['type']); - if (class_exists($class)) { - $trace = new $class($config); - } else { - throw new ClassNotFoundException('class not exists:' . $class, $class); - } + + $trace = Loader::factory($type, '\\think\\debug\\', $config); if ($response instanceof Redirect) { //TODO 记录 } else { - $output = $trace->output($response, Log::getLog()); + $output = $trace->output($response, $this->app['log']->getLog()); if (is_string($output)) { // trace调试信息注入 $pos = strripos($content, ''); @@ -209,4 +267,12 @@ class Debug } } } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/Env.php b/thinkphp/library/think/Env.php index 6f31841d5..eaeee943e 100644 --- a/thinkphp/library/think/Env.php +++ b/thinkphp/library/think/Env.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,19 +13,101 @@ namespace think; class Env { + /** + * 环境变量数据 + * @var array + */ + protected $data = []; + + public function __construct() + { + $this->data = $_ENV; + } + + /** + * 读取环境变量定义文件 + * @access public + * @param string $file 环境变量定义文件 + * @return void + */ + public function load($file) + { + $env = parse_ini_file($file, true); + $this->set($env); + } + /** * 获取环境变量值 - * @param string $name 环境变量名(支持二级 .号分割) - * @param string $default 默认值 + * @access public + * @param string $name 环境变量名 + * @param mixed $default 默认值 * @return mixed */ - public static function get($name, $default = null) + public function get($name = null, $default = null, $php_prefix = true) { - $result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name))); - if (false !== $result) { - return $result; - } else { + if (is_null($name)) { + return $this->data; + } + + $name = strtoupper(str_replace('.', '_', $name)); + + if (isset($this->data[$name])) { + return $this->data[$name]; + } + + return $this->getEnv($name, $default, $php_prefix); + } + + protected function getEnv($name, $default = null, $php_prefix = true) + { + if ($php_prefix) { + $name = 'PHP_' . $name; + } + + $result = getenv($name); + + if (false === $result) { return $default; } + + if ('false' === $result) { + $result = false; + } elseif ('true' === $result) { + $result = true; + } + + if (!isset($this->data[$name])) { + $this->data[$name] = $result; + } + + return $result; + } + + /** + * 设置环境变量值 + * @access public + * @param string|array $env 环境变量 + * @param mixed $value 值 + * @return void + */ + public function set($env, $value = null) + { + if (is_array($env)) { + $env = array_change_key_case($env, CASE_UPPER); + + foreach ($env as $key => $val) { + if (is_array($val)) { + foreach ($val as $k => $v) { + $this->data[$key . '_' . strtoupper($k)] = $v; + } + } else { + $this->data[$key] = $val; + } + } + } else { + $name = strtoupper(str_replace('.', '_', $env)); + + $this->data[$name] = $value; + } } } diff --git a/thinkphp/library/think/Error.php b/thinkphp/library/think/Error.php index c17292bf5..ea3328eec 100644 --- a/thinkphp/library/think/Error.php +++ b/thinkphp/library/think/Error.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -18,8 +18,15 @@ use think\exception\ThrowableError; class Error { + /** + * 配置参数 + * @var array + */ + protected static $exceptionHandler; + /** * 注册异常处理 + * @access public * @return void */ public static function register() @@ -32,6 +39,7 @@ class Error /** * Exception Handler + * @access public * @param \Exception|\Throwable $e */ public static function appException($e) @@ -41,7 +49,8 @@ class Error } self::getExceptionHandler()->report($e); - if (IS_CLI) { + + if (PHP_SAPI == 'cli') { self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e); } else { self::getExceptionHandler()->render($e)->send(); @@ -50,26 +59,27 @@ class Error /** * Error Handler + * @access public * @param integer $errno 错误编号 * @param integer $errstr 详细错误信息 * @param string $errfile 出错的文件 * @param integer $errline 出错行号 - * @param array $errcontext * @throws ErrorException */ - public static function appError($errno, $errstr, $errfile = '', $errline = 0, $errcontext = []) + public static function appError($errno, $errstr, $errfile = '', $errline = 0) { - $exception = new ErrorException($errno, $errstr, $errfile, $errline, $errcontext); + $exception = new ErrorException($errno, $errstr, $errfile, $errline); if (error_reporting() & $errno) { // 将错误信息托管至 think\exception\ErrorException throw $exception; - } else { - self::getExceptionHandler()->report($exception); } + + self::getExceptionHandler()->report($exception); } /** * Shutdown Handler + * @access public */ public static function appShutdown() { @@ -81,12 +91,13 @@ class Error } // 写入日志 - Log::save(); + Container::get('log')->save(); } /** * 确定错误类型是否致命 * + * @access protected * @param int $type * @return bool */ @@ -95,23 +106,42 @@ class Error return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]); } + /** + * 设置异常处理类 + * + * @access public + * @param mixed $handle + * @return void + */ + public static function setExceptionHandler($handle) + { + self::$exceptionHandler = $handle; + } + /** * Get an instance of the exception handler. * + * @access public * @return Handle */ public static function getExceptionHandler() { static $handle; + if (!$handle) { // 异常处理handle - $class = Config::get('exception_handle'); - if ($class && class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle")) { + $class = self::$exceptionHandler; + + if ($class && is_string($class) && class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle")) { $handle = new $class; } else { $handle = new Handle; + if ($class instanceof \Closure) { + $handle->setRender($class); + } } } + return $handle; } } diff --git a/thinkphp/library/think/Exception.php b/thinkphp/library/think/Exception.php index 034c85b64..414a090ad 100644 --- a/thinkphp/library/think/Exception.php +++ b/thinkphp/library/think/Exception.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -33,8 +33,9 @@ class Exception extends \Exception * key1 value1 * key2 value2 * - * @param string $label 数据分类,用于异常页面显示 - * @param array $data 需要显示的数据,必须为关联数组 + * @access protected + * @param string $label 数据分类,用于异常页面显示 + * @param array $data 需要显示的数据,必须为关联数组 */ final protected function setData($label, array $data) { @@ -44,11 +45,12 @@ class Exception extends \Exception /** * 获取异常额外Debug数据 * 主要用于输出到异常页面便于调试 + * @access public * @return array 由setData设置的Debug数据 */ final public function getData() { return $this->data; } - + } diff --git a/thinkphp/library/think/Facade.php b/thinkphp/library/think/Facade.php new file mode 100644 index 000000000..ac5ae28bc --- /dev/null +++ b/thinkphp/library/think/Facade.php @@ -0,0 +1,125 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Facade +{ + /** + * 绑定对象 + * @var array + */ + protected static $bind = []; + + /** + * 始终创建新的对象实例 + * @var bool + */ + protected static $alwaysNewInstance; + + /** + * 绑定类的静态代理 + * @static + * @access public + * @param string|array $name 类标识 + * @param string $class 类名 + * @return object + */ + public static function bind($name, $class = null) + { + if (__CLASS__ != static::class) { + return self::__callStatic('bind', func_get_args()); + } + + if (is_array($name)) { + self::$bind = array_merge(self::$bind, $name); + } else { + self::$bind[$name] = $class; + } + } + + /** + * 创建Facade实例 + * @static + * @access protected + * @param string $class 类名或标识 + * @param array $args 变量 + * @param bool $newInstance 是否每次创建新的实例 + * @return object + */ + protected static function createFacade($class = '', $args = [], $newInstance = false) + { + $class = $class ?: static::class; + + $facadeClass = static::getFacadeClass(); + + if ($facadeClass) { + $class = $facadeClass; + } elseif (isset(self::$bind[$class])) { + $class = self::$bind[$class]; + } + + if (static::$alwaysNewInstance) { + $newInstance = true; + } + + return Container::getInstance()->make($class, $args, $newInstance); + } + + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + {} + + /** + * 带参数实例化当前Facade类 + * @access public + * @return mixed + */ + public static function instance(...$args) + { + if (__CLASS__ != static::class) { + return self::createFacade('', $args); + } + } + + /** + * 调用类的实例 + * @access public + * @param string $class 类名或者标识 + * @param array|true $args 变量 + * @param bool $newInstance 是否每次创建新的实例 + * @return mixed + */ + public static function make($class, $args = [], $newInstance = false) + { + if (__CLASS__ != static::class) { + return self::__callStatic('make', func_get_args()); + } + + if (true === $args) { + // 总是创建新的实例化对象 + $newInstance = true; + $args = []; + } + + return self::createFacade($class, $args, $newInstance); + } + + // 调用实际类的方法 + public static function __callStatic($method, $params) + { + return call_user_func_array([static::createFacade(), $method], $params); + } +} diff --git a/thinkphp/library/think/File.php b/thinkphp/library/think/File.php index 636a3430a..b24b77708 100644 --- a/thinkphp/library/think/File.php +++ b/thinkphp/library/think/File.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -20,51 +20,85 @@ class File extends SplFileObject * @var string */ private $error = ''; - // 当前完整文件名 + + /** + * 当前完整文件名 + * @var string + */ protected $filename; - // 上传文件名 + + /** + * 上传文件名 + * @var string + */ protected $saveName; - // 文件上传命名规则 + + /** + * 上传文件命名规则 + * @var string + */ protected $rule = 'date'; - // 文件上传验证规则 + + /** + * 上传文件验证规则 + * @var array + */ protected $validate = []; - // 单元测试 + + /** + * 是否单元测试 + * @var bool + */ protected $isTest; - // 上传文件信息 - protected $info; - // 文件hash信息 + + /** + * 上传文件信息 + * @var array + */ + protected $info = []; + + /** + * 文件hash规则 + * @var array + */ protected $hash = []; public function __construct($filename, $mode = 'r') { parent::__construct($filename, $mode); + $this->filename = $this->getRealPath() ?: $this->getPathname(); } /** * 是否测试 + * @access public * @param bool $test 是否测试 * @return $this */ public function isTest($test = false) { $this->isTest = $test; + return $this; } /** * 设置上传信息 + * @access public * @param array $info 上传文件信息 * @return $this */ public function setUploadInfo($info) { $this->info = $info; + return $this; } /** * 获取上传文件的信息 + * @access public * @param string $name * @return array|string */ @@ -75,6 +109,7 @@ class File extends SplFileObject /** * 获取上传文件的文件名 + * @access public * @return string */ public function getSaveName() @@ -84,29 +119,35 @@ class File extends SplFileObject /** * 设置上传文件的保存文件名 + * @access public * @param string $saveName * @return $this */ public function setSaveName($saveName) { $this->saveName = $saveName; + return $this; } /** * 获取文件的哈希散列值 - * @return $string + * @access public + * @param string $type + * @return string */ public function hash($type = 'sha1') { if (!isset($this->hash[$type])) { $this->hash[$type] = hash_file($type, $this->filename); } + return $this->hash[$type]; } /** * 检查目录是否可写 + * @access protected * @param string $path 目录 * @return boolean */ @@ -118,46 +159,53 @@ class File extends SplFileObject if (mkdir($path, 0755, true)) { return true; - } else { - $this->error = "目录 {$path} 创建失败!"; - return false; } + + $this->error = ['directory {:path} creation failed', ['path' => $path]]; + return false; } /** * 获取文件类型信息 + * @access public * @return string */ public function getMime() { $finfo = finfo_open(FILEINFO_MIME_TYPE); + return finfo_file($finfo, $this->filename); } /** * 设置文件的命名规则 + * @access public * @param string $rule 文件命名规则 * @return $this */ public function rule($rule) { $this->rule = $rule; + return $this; } /** * 设置上传文件的验证规则 + * @access public * @param array $rule 验证规则 * @return $this */ public function validate($rule = []) { $this->validate = $rule; + return $this; } /** * 检测是否合法的上传文件 + * @access public * @return bool */ public function isValid() @@ -165,11 +213,13 @@ class File extends SplFileObject if ($this->isTest) { return is_file($this->filename); } + return is_uploaded_file($this->filename); } /** * 检测上传文件 + * @access public * @param array $rule 验证规则 * @return bool */ @@ -177,27 +227,10 @@ class File extends SplFileObject { $rule = $rule ?: $this->validate; - /* 检查文件大小 */ - if (isset($rule['size']) && !$this->checkSize($rule['size'])) { - $this->error = '上传文件大小不符!'; - return false; - } - - /* 检查文件Mime类型 */ - if (isset($rule['type']) && !$this->checkMime($rule['type'])) { - $this->error = '上传文件MIME类型不允许!'; - return false; - } - - /* 检查文件后缀 */ - if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) { - $this->error = '上传文件后缀不允许'; - return false; - } - - /* 检查图像文件 */ - if (!$this->checkImg()) { - $this->error = '非法图像文件!'; + if ((isset($rule['size']) && !$this->checkSize($rule['size'])) + || (isset($rule['type']) && !$this->checkMime($rule['type'])) + || (isset($rule['ext']) && !$this->checkExt($rule['ext'])) + || !$this->checkImg()) { return false; } @@ -206,6 +239,7 @@ class File extends SplFileObject /** * 检测上传文件后缀 + * @access public * @param array|string $ext 允许后缀 * @return bool */ @@ -214,24 +248,32 @@ class File extends SplFileObject if (is_string($ext)) { $ext = explode(',', $ext); } + $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); + if (!in_array($extension, $ext)) { + $this->error = 'extensions to upload is not allowed'; return false; } + return true; } /** * 检测图像文件 + * @access public * @return bool */ public function checkImg() { $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); + /* 对图像文件进行严格检测 */ - if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6])) { + if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13])) { + $this->error = 'illegal image files'; return false; } + return true; } @@ -240,27 +282,35 @@ class File extends SplFileObject { if (function_exists('exif_imagetype')) { return exif_imagetype($image); - } else { + } + + try { $info = getimagesize($image); - return $info[2]; + return $info ? $info[2] : false; + } catch (\Exception $e) { + return false; } } /** * 检测上传文件大小 + * @access public * @param integer $size 最大大小 * @return bool */ public function checkSize($size) { - if ($this->getSize() > $size) { + if ($this->getSize() > (int) $size) { + $this->error = 'filesize not match'; return false; } + return true; } /** * 检测上传文件类型 + * @access public * @param array|string $mime 允许类型 * @return bool */ @@ -269,20 +319,25 @@ class File extends SplFileObject if (is_string($mime)) { $mime = explode(',', $mime); } + if (!in_array(strtolower($this->getMime()), $mime)) { + $this->error = 'mimetype to upload is not allowed'; return false; } + return true; } /** * 移动文件 + * @access public * @param string $path 保存路径 * @param string|bool $savename 保存的文件名 默认自动生成 * @param boolean $replace 同名文件是否覆盖 - * @return false|File false-失败 否则返回File实例 + * @param bool $autoAppendExt 自动补充扩展名 + * @return false|File false-失败 否则返回File实例 */ - public function move($path, $savename = true, $replace = true) + public function move($path, $savename = true, $replace = true, $autoAppendExt = true) { // 文件上传失败,捕获错误代码 if (!empty($this->info['error'])) { @@ -292,7 +347,7 @@ class File extends SplFileObject // 检测合法性 if (!$this->isValid()) { - $this->error = '非法上传文件'; + $this->error = 'upload illegal files'; return false; } @@ -300,9 +355,10 @@ class File extends SplFileObject if (!$this->check()) { return false; } - $path = rtrim($path, DS) . DS; + + $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; // 文件保存命名规则 - $saveName = $this->buildSaveName($savename); + $saveName = $this->buildSaveName($savename, $autoAppendExt); $filename = $path . $saveName; // 检测目录 @@ -312,7 +368,7 @@ class File extends SplFileObject /* 不覆盖同名文件 */ if (!$replace && is_file($filename)) { - $this->error = '存在同名文件' . $filename; + $this->error = ['has the same filename: {:filename}', ['filename' => $filename]]; return false; } @@ -320,87 +376,117 @@ class File extends SplFileObject if ($this->isTest) { rename($this->filename, $filename); } elseif (!move_uploaded_file($this->filename, $filename)) { - $this->error = '文件上传保存错误!'; + $this->error = 'upload write error'; return false; } + // 返回 File对象实例 $file = new self($filename); $file->setSaveName($saveName); $file->setUploadInfo($this->info); + return $file; } /** * 获取保存文件名 + * @access protected * @param string|bool $savename 保存的文件名 默认自动生成 + * @param bool $autoAppendExt 自动补充扩展名 * @return string */ - protected function buildSaveName($savename) + protected function buildSaveName($savename, $autoAppendExt = true) { if (true === $savename) { // 自动生成文件名 - if ($this->rule instanceof \Closure) { - $savename = call_user_func_array($this->rule, [$this]); - } else { - switch ($this->rule) { - case 'date': - $savename = date('Ymd') . DS . md5(microtime(true)); - break; - default: - if (in_array($this->rule, hash_algos())) { - $hash = $this->hash($this->rule); - $savename = substr($hash, 0, 2) . DS . substr($hash, 2); - } elseif (is_callable($this->rule)) { - $savename = call_user_func($this->rule); - } else { - $savename = date('Ymd') . DS . md5(microtime(true)); - } - } - } - } elseif ('' === $savename) { + $savename = $this->autoBuildName(); + } elseif ('' === $savename || false === $savename) { + // 保留原文件名 $savename = $this->getInfo('name'); } - if (!strpos($savename, '.')) { + + if ($autoAppendExt && false === strpos($savename, '.')) { $savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION); } + + return $savename; + } + + /** + * 自动生成文件名 + * @access protected + * @return string + */ + protected function autoBuildName() + { + if ($this->rule instanceof \Closure) { + $savename = call_user_func_array($this->rule, [$this]); + } else { + switch ($this->rule) { + case 'date': + $savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true)); + break; + default: + if (in_array($this->rule, hash_algos())) { + $hash = $this->hash($this->rule); + $savename = substr($hash, 0, 2) . DIRECTORY_SEPARATOR . substr($hash, 2); + } elseif (is_callable($this->rule)) { + $savename = call_user_func($this->rule); + } else { + $savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true)); + } + } + } + return $savename; } /** * 获取错误代码信息 - * @param int $errorNo 错误号 + * @access private + * @param int $errorNo 错误号 */ private function error($errorNo) { switch ($errorNo) { case 1: case 2: - $this->error = '上传文件大小超过了最大值!'; + $this->error = 'upload File size exceeds the maximum value'; break; case 3: - $this->error = '文件只有部分被上传!'; + $this->error = 'only the portion of file is uploaded'; break; case 4: - $this->error = '没有文件被上传!'; + $this->error = 'no file to uploaded'; break; case 6: - $this->error = '找不到临时文件夹!'; + $this->error = 'upload temp dir not found'; break; case 7: - $this->error = '文件写入失败!'; + $this->error = 'file write error'; break; default: - $this->error = '未知上传错误!'; + $this->error = 'unknown upload error'; } } /** - * 获取错误信息 - * @return mixed + * 获取错误信息(支持多语言) + * @access public + * @return string */ public function getError() { - return $this->error; + $lang = Container::get('lang'); + + if (is_array($this->error)) { + list($msg, $vars) = $this->error; + } else { + $msg = $this->error; + $vars = []; + } + + return $lang->has($msg) ? $lang->get($msg, $vars) : $msg; } public function __call($method, $args) diff --git a/thinkphp/library/think/Hook.php b/thinkphp/library/think/Hook.php index f06196e4d..1d011410e 100644 --- a/thinkphp/library/think/Hook.php +++ b/thinkphp/library/think/Hook.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,124 +13,208 @@ namespace think; class Hook { + /** + * 钩子行为定义 + * @var array + */ + private $tags = []; - private static $tags = []; + /** + * 绑定行为列表 + * @var array + */ + protected $bind = []; + + /** + * 入口方法名称 + * @var string + */ + private static $portal = 'run'; + + /** + * 应用对象 + * @var App + */ + protected $app; + + public function __construct(App $app) + { + $this->app = $app; + } + + /** + * 指定入口方法名称 + * @access public + * @param string $name 方法名 + * @return $this + */ + public function portal($name) + { + self::$portal = $name; + return $this; + } + + /** + * 指定行为标识 便于调用 + * @access public + * @param string|array $name 行为标识 + * @param mixed $behavior 行为 + * @return $this + */ + public function alias($name, $behavior = null) + { + if (is_array($name)) { + $this->bind = array_merge($this->bind, $name); + } else { + $this->bind[$name] = $behavior; + } + + return $this; + } /** * 动态添加行为扩展到某个标签 - * @param string $tag 标签名称 - * @param mixed $behavior 行为名称 - * @param bool $first 是否放到开头执行 + * @access public + * @param string $tag 标签名称 + * @param mixed $behavior 行为名称 + * @param bool $first 是否放到开头执行 * @return void */ - public static function add($tag, $behavior, $first = false) + public function add($tag, $behavior, $first = false) { - isset(self::$tags[$tag]) || self::$tags[$tag] = []; + isset($this->tags[$tag]) || $this->tags[$tag] = []; + if (is_array($behavior) && !is_callable($behavior)) { - if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) { - unset($behavior['_overlay']); - self::$tags[$tag] = array_merge(self::$tags[$tag], $behavior); + if (!array_key_exists('_overlay', $behavior)) { + $this->tags[$tag] = array_merge($this->tags[$tag], $behavior); } else { unset($behavior['_overlay']); - self::$tags[$tag] = $behavior; + $this->tags[$tag] = $behavior; } } elseif ($first) { - array_unshift(self::$tags[$tag], $behavior); + array_unshift($this->tags[$tag], $behavior); } else { - self::$tags[$tag][] = $behavior; + $this->tags[$tag][] = $behavior; } } /** * 批量导入插件 - * @param array $tags 插件信息 - * @param boolean $recursive 是否递归合并 + * @access public + * @param array $tags 插件信息 + * @param bool $recursive 是否递归合并 + * @return void */ - public static function import(array $tags, $recursive = true) + public function import(array $tags, $recursive = true) { if ($recursive) { foreach ($tags as $tag => $behavior) { - self::add($tag, $behavior); + $this->add($tag, $behavior); } } else { - self::$tags = $tags + self::$tags; + $this->tags = $tags + $this->tags; } } /** * 获取插件信息 - * @param string $tag 插件位置 留空获取全部 + * @access public + * @param string $tag 插件位置 留空获取全部 * @return array */ - public static function get($tag = '') + public function get($tag = '') { if (empty($tag)) { //获取全部的插件信息 - return self::$tags; - } else { - return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : []; + return $this->tags; } + + return array_key_exists($tag, $this->tags) ? $this->tags[$tag] : []; } /** * 监听标签的行为 - * @param string $tag 标签名称 - * @param mixed $params 传入参数 - * @param mixed $extra 额外参数 - * @param bool $once 只获取一个有效返回值 + * @access public + * @param string $tag 标签名称 + * @param mixed $params 传入参数 + * @param bool $once 只获取一个有效返回值 * @return mixed */ - public static function listen($tag, &$params = null, $extra = null, $once = false) + public function listen($tag, $params = null, $once = false) { $results = []; - $tags = static::get($tag); + $tags = $this->get($tag); + foreach ($tags as $key => $name) { - $results[$key] = self::exec($name, $tag, $params, $extra); - if (false === $results[$key]) { - // 如果返回false 则中断行为执行 - break; - } elseif (!is_null($results[$key]) && $once) { + $results[$key] = $this->execTag($name, $tag, $params); + + if (false === $results[$key] || (!is_null($results[$key]) && $once)) { break; } } + return $once ? end($results) : $results; } /** - * 执行某个行为 - * @param mixed $class 要执行的行为 - * @param string $tag 方法名(标签名) - * @param Mixed $params 传人的参数 - * @param mixed $extra 额外参数 + * 执行行为 + * @access public + * @param mixed $class 行为 + * @param mixed $params 参数 * @return mixed */ - public static function exec($class, $tag = '', &$params = null, $extra = null) + public function exec($class, $params = null) { - App::$debug && Debug::remark('behavior_start', 'time'); - $method = Loader::parseName($tag, 1, false); - if ($class instanceof \Closure) { - $result = call_user_func_array($class, [ & $params, $extra]); - $class = 'Closure'; - } elseif (is_array($class)) { - list($class, $method) = $class; - - $result = (new $class())->$method($params, $extra); - $class = $class . '->' . $method; - } elseif (is_object($class)) { - $result = $class->$method($params, $extra); - $class = get_class($class); - } elseif (strpos($class, '::')) { - $result = call_user_func_array($class, [ & $params, $extra]); + if ($class instanceof \Closure || is_array($class)) { + $method = $class; } else { - $obj = new $class(); - $method = ($tag && is_callable([$obj, $method])) ? $method : 'run'; - $result = $obj->$method($params, $extra); + if (isset($this->bind[$class])) { + $class = $this->bind[$class]; + } + $method = [$class, self::$portal]; } - if (App::$debug) { - Debug::remark('behavior_end', 'time'); - Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info'); + + return $this->app->invoke($method, [$params]); + } + + /** + * 执行某个标签的行为 + * @access protected + * @param mixed $class 要执行的行为 + * @param string $tag 方法名(标签名) + * @param mixed $params 参数 + * @return mixed + */ + protected function execTag($class, $tag = '', $params = null) + { + $method = Loader::parseName($tag, 1, false); + + if ($class instanceof \Closure) { + $call = $class; + $class = 'Closure'; + } elseif (is_array($class) || strpos($class, '::')) { + $call = $class; + } else { + $obj = Container::get($class); + + if (!is_callable([$obj, $method])) { + $method = self::$portal; + } + + $call = [$class, $method]; + $class = $class . '->' . $method; } + + $result = $this->app->invoke($call, [$params]); + return $result; } + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/Lang.php b/thinkphp/library/think/Lang.php index 2df3767cc..be7979f89 100644 --- a/thinkphp/library/think/Lang.php +++ b/thinkphp/library/think/Lang.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,116 +13,160 @@ namespace think; class Lang { - // 语言数据 - private static $lang = []; - // 语言作用域 - private static $range = 'zh-cn'; - // 语言自动侦测的变量 - protected static $langDetectVar = 'lang'; - // 语言Cookie变量 - protected static $langCookieVar = 'think_var'; - // 语言Cookie的过期时间 - protected static $langCookieExpire = 3600; - // 允许语言列表 - protected static $allowLangList = []; - // Accept-Language转义为对应语言包名称 系统默认配置 - protected static $acceptLanguage = [ + /** + * 多语言信息 + * @var array + */ + private $lang = []; + + /** + * 当前语言 + * @var string + */ + private $range = 'zh-cn'; + + /** + * 多语言自动侦测变量名 + * @var string + */ + protected $langDetectVar = 'lang'; + + /** + * 多语言cookie变量 + * @var string + */ + protected $langCookieVar = 'think_var'; + + /** + * 允许的多语言列表 + * @var array + */ + protected $allowLangList = []; + + /** + * Accept-Language转义为对应语言包名称 系统默认配置 + * @var string + */ + protected $acceptLanguage = [ 'zh-hans-cn' => 'zh-cn', ]; + /** + * 应用对象 + * @var App + */ + protected $app; + + public function __construct(App $app) + { + $this->app = $app; + } + // 设定当前的语言 - public static function range($range = '') + public function range($range = '') { if ('' == $range) { - return self::$range; + return $this->range; } else { - self::$range = $range; + $this->range = $range; } - return self::$range; } /** * 设置语言定义(不区分大小写) - * @param string|array $name 语言变量 - * @param string $value 语言值 - * @param string $range 语言作用域 + * @access public + * @param string|array $name 语言变量 + * @param string $value 语言值 + * @param string $range 语言作用域 * @return mixed */ - public static function set($name, $value = null, $range = '') + public function set($name, $value = null, $range = '') { - $range = $range ?: self::$range; + $range = $range ?: $this->range; // 批量定义 - if (!isset(self::$lang[$range])) { - self::$lang[$range] = []; + if (!isset($this->lang[$range])) { + $this->lang[$range] = []; } + if (is_array($name)) { - return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range]; - } else { - return self::$lang[$range][strtolower($name)] = $value; + return $this->lang[$range] = array_change_key_case($name) + $this->lang[$range]; } + + return $this->lang[$range][strtolower($name)] = $value; } /** * 加载语言定义(不区分大小写) - * @param string $file 语言文件 - * @param string $range 语言作用域 - * @return mixed + * @access public + * @param string|array $file 语言文件 + * @param string $range 语言作用域 + * @return array */ - public static function load($file, $range = '') + public function load($file, $range = '') { - $range = $range ?: self::$range; - if (!isset(self::$lang[$range])) { - self::$lang[$range] = []; + $range = $range ?: $this->range; + if (!isset($this->lang[$range])) { + $this->lang[$range] = []; } + // 批量定义 if (is_string($file)) { $file = [$file]; } + $lang = []; + foreach ($file as $_file) { if (is_file($_file)) { // 记录加载信息 - App::$debug && Log::record('[ LANG ] ' . $_file, 'info'); + $this->app->log('[ LANG ] ' . $_file); $_lang = include $_file; if (is_array($_lang)) { $lang = array_change_key_case($_lang) + $lang; } } } + if (!empty($lang)) { - self::$lang[$range] = $lang + self::$lang[$range]; + $this->lang[$range] = $lang + $this->lang[$range]; } - return self::$lang[$range]; + + return $this->lang[$range]; } /** * 获取语言定义(不区分大小写) - * @param string|null $name 语言变量 - * @param string $range 语言作用域 - * @return mixed + * @access public + * @param string|null $name 语言变量 + * @param string $range 语言作用域 + * @return bool */ - public static function has($name, $range = '') + public function has($name, $range = '') { - $range = $range ?: self::$range; - return isset(self::$lang[$range][strtolower($name)]); + $range = $range ?: $this->range; + + return isset($this->lang[$range][strtolower($name)]); } /** * 获取语言定义(不区分大小写) - * @param string|null $name 语言变量 - * @param array $vars 变量替换 - * @param string $range 语言作用域 + * @access public + * @param string|null $name 语言变量 + * @param array $vars 变量替换 + * @param string $range 语言作用域 * @return mixed */ - public static function get($name = null, $vars = [], $range = '') + public function get($name = null, $vars = [], $range = '') { - $range = $range ?: self::$range; + $range = $range ?: $this->range; + // 空参数返回所有定义 - if (empty($name)) { - return self::$lang[$range]; + if (is_null($name)) { + return $this->lang[$range]; } + $key = strtolower($name); - $value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name; + $value = isset($this->lang[$range][$key]) ? $this->lang[$range][$key] : $name; // 变量解析 if (!empty($vars) && is_array($vars)) { @@ -143,78 +187,98 @@ class Lang } $value = str_replace($replace, $vars, $value); } - } + return $value; } /** * 自动侦测设置获取语言选择 + * @access public * @return string */ - public static function detect() + public function detect() { // 自动侦测设置获取语言选择 $langSet = ''; - if (isset($_GET[self::$langDetectVar])) { + if (isset($_GET[$this->langDetectVar])) { // url中设置了语言变量 - $langSet = strtolower($_GET[self::$langDetectVar]); + $langSet = strtolower($_GET[$this->langDetectVar]); + } elseif (isset($_COOKIE[$this->langCookieVar])) { + // Cookie中设置了语言变量 + $langSet = strtolower($_COOKIE[$this->langCookieVar]); } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { // 自动侦测浏览器语言 preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches); - $langSet = strtolower($matches[1]); - $acceptLangs = Config::get('header_accept_lang'); - if (isset($acceptLangs[$langSet])) { - $langSet = $acceptLangs[$langSet]; - } elseif (isset(self::$acceptLanguage[$langSet])) { - $langSet = self::$acceptLanguage[$langSet]; + $langSet = strtolower($matches[1]); + if (isset($this->acceptLanguage[$langSet])) { + $langSet = $this->acceptLanguage[$langSet]; } } - if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) { + + if (empty($this->allowLangList) || in_array($langSet, $this->allowLangList)) { // 合法的语言 - self::$range = $langSet ?: self::$range; + $this->range = $langSet ?: $this->range; } - return self::$range; + + return $this->range; + } + + /** + * 设置当前语言到Cookie + * @access public + * @param string $lang 语言 + * @return void + */ + public function saveToCookie($lang = null) + { + $range = $lang ?: $this->range; + + $_COOKIE[$this->langCookieVar] = $range; } /** * 设置语言自动侦测的变量 - * @param string $var 变量名称 + * @access public + * @param string $var 变量名称 * @return void */ - public static function setLangDetectVar($var) + public function setLangDetectVar($var) { - self::$langDetectVar = $var; + $this->langDetectVar = $var; } /** * 设置语言的cookie保存变量 - * @param string $var 变量名称 + * @access public + * @param string $var 变量名称 * @return void */ - public static function setLangCookieVar($var) + public function setLangCookieVar($var) { - self::$langCookieVar = $var; - } - - /** - * 设置语言的cookie的过期时间 - * @param string $expire 过期时间 - * @return void - */ - public static function setLangCookieExpire($expire) - { - self::$langCookieExpire = $expire; + $this->langCookieVar = $var; } /** * 设置允许的语言列表 - * @param array $list 语言列表 + * @access public + * @param array $list 语言列表 * @return void */ - public static function setAllowLangList($list) + public function setAllowLangList(array $list) { - self::$allowLangList = $list; + $this->allowLangList = $list; + } + + /** + * 设置转义的语言列表 + * @access public + * @param array $list 语言列表 + * @return void + */ + public function setAcceptLanguage(array $list) + { + $this->acceptLanguage = array_merge($this->acceptLanguage, $list); } } diff --git a/thinkphp/library/think/Loader.php b/thinkphp/library/think/Loader.php index a0727fef5..d807db640 100644 --- a/thinkphp/library/think/Loader.php +++ b/thinkphp/library/think/Loader.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -15,43 +15,117 @@ use think\exception\ClassNotFoundException; class Loader { - protected static $instance = []; - // 类名映射 - protected static $map = []; + /** + * 类名映射信息 + * @var array + */ + protected static $classMap = []; - // 命名空间别名 - protected static $namespaceAlias = []; + /** + * 类库别名 + * @var array + */ + protected static $classAlias = []; - // PSR-4 + /** + * PSR-4 + * @var array + */ private static $prefixLengthsPsr4 = []; private static $prefixDirsPsr4 = []; private static $fallbackDirsPsr4 = []; - // PSR-0 + /** + * PSR-0 + * @var array + */ private static $prefixesPsr0 = []; private static $fallbackDirsPsr0 = []; - // 自动加载的文件 - private static $autoloadFiles = []; + /** + * 需要加载的文件 + * @var array + */ + private static $files = []; + + /** + * Composer安装路径 + * @var string + */ + private static $composerPath; + + // 获取应用根目录 + public static function getRootPath() + { + if ('cli' == PHP_SAPI) { + $scriptName = realpath($_SERVER['argv'][0]); + } else { + $scriptName = $_SERVER['SCRIPT_FILENAME']; + } + + $path = realpath(dirname($scriptName)); + + if (!is_file($path . DIRECTORY_SEPARATOR . 'think')) { + $path = dirname($path); + } + + return $path . DIRECTORY_SEPARATOR; + } + + // 注册自动加载机制 + public static function register($autoload = '') + { + // 注册系统自动加载 + spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true); + + $rootPath = self::getRootPath(); + + self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR; + + // Composer自动加载支持 + if (is_dir(self::$composerPath)) { + if (is_file(self::$composerPath . 'autoload_static.php')) { + require self::$composerPath . 'autoload_static.php'; + + $declaredClass = get_declared_classes(); + $composerClass = array_pop($declaredClass); + + foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) { + if (property_exists($composerClass, $attr)) { + self::${$attr} = $composerClass::${$attr}; + } + } + } else { + self::registerComposerLoader(self::$composerPath); + } + } + + // 注册命名空间定义 + self::addNamespace([ + 'think' => __DIR__, + 'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits', + ]); + + // 加载类库映射文件 + if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) { + self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')); + } + + // 自动加载extend目录 + self::addAutoLoadDir($rootPath . 'extend'); + } // 自动加载 public static function autoload($class) { - // 检测命名空间别名 - if (!empty(self::$namespaceAlias)) { - $namespace = dirname($class); - if (isset(self::$namespaceAlias[$namespace])) { - $original = self::$namespaceAlias[$namespace] . '\\' . basename($class); - if (class_exists($original)) { - return class_alias($original, $class, false); - } - } + if (isset(self::$classAlias[$class])) { + return class_alias(self::$classAlias[$class], $class); } if ($file = self::findFile($class)) { // Win环境严格区分大小写 - if (IS_WIN && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) { + if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) { return false; } @@ -62,25 +136,26 @@ class Loader /** * 查找文件 - * @param $class - * @return bool + * @access private + * @param string $class + * @return string|false */ private static function findFile($class) { - if (!empty(self::$map[$class])) { + if (!empty(self::$classMap[$class])) { // 类库映射 - return self::$map[$class]; + return self::$classMap[$class]; } // 查找 PSR-4 - $logicalPathPsr4 = strtr($class, '\\', DS) . EXT; + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php'; $first = $class[0]; if (isset(self::$prefixLengthsPsr4[$first])) { foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { foreach (self::$prefixDirsPsr4[$prefix] as $dir) { - if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } @@ -90,7 +165,7 @@ class Loader // 查找 PSR-4 fallback dirs foreach (self::$fallbackDirsPsr4 as $dir) { - if (is_file($file = $dir . DS . $logicalPathPsr4)) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } @@ -99,17 +174,17 @@ class Loader if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) - . strtr(substr($logicalPathPsr4, $pos + 1), '_', DS); + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DS) . EXT; + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php'; } if (isset(self::$prefixesPsr0[$first])) { foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { - if (is_file($file = $dir . DS . $logicalPathPsr0)) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } @@ -119,21 +194,21 @@ class Loader // 查找 PSR-0 fallback dirs foreach (self::$fallbackDirsPsr0 as $dir) { - if (is_file($file = $dir . DS . $logicalPathPsr0)) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } - return self::$map[$class] = false; + return self::$classMap[$class] = false; } // 注册classmap public static function addClassMap($class, $map = '') { if (is_array($class)) { - self::$map = array_merge(self::$map, $class); + self::$classMap = array_merge(self::$classMap, $class); } else { - self::$map[$class] = $map; + self::$classMap[$class] = $map; } } @@ -142,10 +217,10 @@ class Loader { if (is_array($namespace)) { foreach ($namespace as $prefix => $paths) { - self::addPsr4($prefix . '\\', rtrim($paths, DS), true); + self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true); } } else { - self::addPsr4($namespace . '\\', rtrim($path, DS), true); + self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true); } } @@ -174,6 +249,7 @@ class Loader return; } + if ($prepend) { self::$prefixesPsr0[$first][$prefix] = array_merge( (array) $paths, @@ -209,6 +285,7 @@ class Loader if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } + self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length; self::$prefixDirsPsr4[$prefix] = (array) $paths; } elseif ($prepend) { @@ -226,290 +303,70 @@ class Loader } } - // 注册命名空间别名 - public static function addNamespaceAlias($namespace, $original = '') + // 注册自动加载类库目录 + public static function addAutoLoadDir($path) { - if (is_array($namespace)) { - self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace); - } else { - self::$namespaceAlias[$namespace] = $original; - } + self::$fallbackDirsPsr4[] = $path; } - // 注册自动加载机制 - public static function register($autoload = '') + // 注册类别名 + public static function addClassAlias($alias, $class = null) { - // 注册系统自动加载 - spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true); - // 注册命名空间定义 - self::addNamespace([ - 'think' => LIB_PATH . 'think' . DS, - 'behavior' => LIB_PATH . 'behavior' . DS, - 'traits' => LIB_PATH . 'traits' . DS, - ]); - // 加载类库映射文件 - if (is_file(RUNTIME_PATH . 'classmap' . EXT)) { - self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT)); + if (is_array($alias)) { + self::$classAlias = array_merge(self::$classAlias, $alias); + } else { + self::$classAlias[$alias] = $class; } - - // Composer自动加载支持 - if (is_dir(VENDOR_PATH . 'composer')) { - self::registerComposerLoader(); - } - - // 自动加载extend目录 - self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS); } // 注册composer自动加载 - private static function registerComposerLoader() + public static function registerComposerLoader($composerPath) { - if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) { - $map = require VENDOR_PATH . 'composer/autoload_namespaces.php'; + if (is_file($composerPath . 'autoload_namespaces.php')) { + $map = require $composerPath . 'autoload_namespaces.php'; foreach ($map as $namespace => $path) { self::addPsr0($namespace, $path); } } - if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) { - $map = require VENDOR_PATH . 'composer/autoload_psr4.php'; + if (is_file($composerPath . 'autoload_psr4.php')) { + $map = require $composerPath . 'autoload_psr4.php'; foreach ($map as $namespace => $path) { self::addPsr4($namespace, $path); } } - if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) { - $classMap = require VENDOR_PATH . 'composer/autoload_classmap.php'; + if (is_file($composerPath . 'autoload_classmap.php')) { + $classMap = require $composerPath . 'autoload_classmap.php'; if ($classMap) { self::addClassMap($classMap); } } - if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) { - $includeFiles = require VENDOR_PATH . 'composer/autoload_files.php'; - foreach ($includeFiles as $fileIdentifier => $file) { - if (empty(self::$autoloadFiles[$fileIdentifier])) { - __require_file($file); - self::$autoloadFiles[$fileIdentifier] = true; - } - } + if (is_file($composerPath . 'autoload_files.php')) { + self::$files = require $composerPath . 'autoload_files.php'; } } - /** - * 导入所需的类库 同java的Import 本函数有缓存功能 - * @param string $class 类库命名空间字符串 - * @param string $baseUrl 起始路径 - * @param string $ext 导入的文件扩展名 - * @return boolean - */ - public static function import($class, $baseUrl = '', $ext = EXT) + // 加载composer autofile文件 + public static function loadComposerAutoloadFiles() { - static $_file = []; - $key = $class . $baseUrl; - $class = str_replace(['.', '#'], [DS, '.'], $class); - if (isset($_file[$key])) { - return true; - } + foreach (self::$files as $fileIdentifier => $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + __require_file($file); - if (empty($baseUrl)) { - list($name, $class) = explode(DS, $class, 2); - - if (isset(self::$prefixDirsPsr4[$name . '\\'])) { - // 注册的命名空间 - $baseUrl = self::$prefixDirsPsr4[$name . '\\']; - } elseif ('@' == $name) { - //加载当前模块应用类库 - $baseUrl = App::$modulePath; - } elseif (is_dir(EXTEND_PATH . $name)) { - $baseUrl = EXTEND_PATH . $name . DS; - } else { - // 加载其它模块的类库 - $baseUrl = APP_PATH . $name . DS; + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; } - } elseif (substr($baseUrl, -1) != DS) { - $baseUrl .= DS; - } - // 如果类存在 则导入类库文件 - if (is_array($baseUrl)) { - foreach ($baseUrl as $path) { - $filename = $path . DS . $class . $ext; - if (is_file($filename)) { - break; - } - } - } else { - $filename = $baseUrl . $class . $ext; - } - - if (!empty($filename) && is_file($filename)) { - // 开启调试模式Win环境严格区分大小写 - if (IS_WIN && pathinfo($filename, PATHINFO_FILENAME) != pathinfo(realpath($filename), PATHINFO_FILENAME)) { - return false; - } - __include_file($filename); - $_file[$key] = true; - return true; - } - return false; - } - - /** - * 实例化(分层)模型 - * @param string $name Model名称 - * @param string $layer 业务层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @param string $common 公共模块名 - * @return Object - * @throws ClassNotFoundException - */ - public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common') - { - $guid = $name . $layer; - if (isset(self::$instance[$guid])) { - return self::$instance[$guid]; - } - if (false !== strpos($name, '\\')) { - $class = $name; - $module = Request::instance()->module(); - } else { - if (strpos($name, '/')) { - list($module, $name) = explode('/', $name, 2); - } else { - $module = Request::instance()->module(); - } - $class = self::parseClass($module, $layer, $name, $appendSuffix); - } - if (class_exists($class)) { - $model = new $class(); - } else { - $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); - if (class_exists($class)) { - $model = new $class(); - } else { - throw new ClassNotFoundException('class not exists:' . $class, $class); - } - } - self::$instance[$guid] = $model; - return $model; - } - - /** - * 实例化(分层)控制器 格式:[模块名/]控制器名 - * @param string $name 资源地址 - * @param string $layer 控制层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @param string $empty 空控制器名称 - * @return Object|false - * @throws ClassNotFoundException - */ - public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '') - { - if (false !== strpos($name, '\\')) { - $class = $name; - $module = Request::instance()->module(); - } else { - if (strpos($name, '/')) { - list($module, $name) = explode('/', $name); - } else { - $module = Request::instance()->module(); - } - $class = self::parseClass($module, $layer, $name, $appendSuffix); - } - if (class_exists($class)) { - return App::invokeClass($class); - } elseif ($empty && class_exists($emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix))) { - return new $emptyClass(Request::instance()); - } - } - - /** - * 实例化验证类 格式:[模块名/]验证器名 - * @param string $name 资源地址 - * @param string $layer 验证层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @param string $common 公共模块名 - * @return Object|false - * @throws ClassNotFoundException - */ - public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common') - { - $name = $name ?: Config::get('default_validate'); - if (empty($name)) { - return new Validate; - } - $guid = $name . $layer; - if (isset(self::$instance[$guid])) { - return self::$instance[$guid]; - } - if (false !== strpos($name, '\\')) { - $class = $name; - $module = Request::instance()->module(); - } else { - if (strpos($name, '/')) { - list($module, $name) = explode('/', $name); - } else { - $module = Request::instance()->module(); - } - $class = self::parseClass($module, $layer, $name, $appendSuffix); - } - if (class_exists($class)) { - $validate = new $class; - } else { - $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); - if (class_exists($class)) { - $validate = new $class; - } else { - throw new ClassNotFoundException('class not exists:' . $class, $class); - } - } - self::$instance[$guid] = $validate; - return $validate; - } - - /** - * 数据库初始化 并取得数据库类实例 - * @param mixed $config 数据库配置 - * @param bool|string $name 连接标识 true 强制重新连接 - * @return \think\db\Connection - */ - public static function db($config = [], $name = false) - { - return Db::connect($config, $name); - } - - /** - * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作 - * @param string $url 调用地址 - * @param string|array $vars 调用参数 支持字符串和数组 - * @param string $layer 要调用的控制层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @return mixed - */ - public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) - { - $info = pathinfo($url); - $action = $info['basename']; - $module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller(); - $class = self::controller($module, $layer, $appendSuffix); - if ($class) { - if (is_scalar($vars)) { - if (strpos($vars, '=')) { - parse_str($vars, $vars); - } else { - $vars = [$vars]; - } - } - return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars); } } /** * 字符串命名风格转换 * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 - * @param string $name 字符串 - * @param integer $type 转换类型 - * @param bool $ucfirst 首字母是否大写(驼峰规则) + * @access public + * @param string $name 字符串 + * @param integer $type 转换类型 + * @param bool $ucfirst 首字母是否大写(驼峰规则) * @return string */ public static function parseName($name, $type = 0, $ucfirst = true) @@ -519,35 +376,27 @@ class Loader return strtoupper($match[1]); }, $name); return $ucfirst ? ucfirst($name) : lcfirst($name); - } else { - return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } + + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } /** - * 解析应用类的类名 - * @param string $module 模块名 - * @param string $layer 层名 controller model ... - * @param string $name 类名 - * @param bool $appendSuffix - * @return string + * 创建工厂对象实例 + * @access public + * @param string $name 工厂类名 + * @param string $namespace 默认命名空间 + * @return mixed */ - public static function parseClass($module, $layer, $name, $appendSuffix = false) + public static function factory($name, $namespace = '', ...$args) { - $name = str_replace(['/', '.'], '\\', $name); - $array = explode('\\', $name); - $class = self::parseName(array_pop($array), 1) . (App::$suffix || $appendSuffix ? ucfirst($layer) : ''); - $path = $array ? implode('\\', $array) . '\\' : ''; - return App::$namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class; - } + $class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name); - /** - * 初始化类的实例 - * @return void - */ - public static function clearInstance() - { - self::$instance = []; + if (class_exists($class)) { + return Container::getInstance()->invokeClass($class, $args); + } else { + throw new ClassNotFoundException('class not exists:' . $class, $class); + } } } diff --git a/thinkphp/library/think/Log.php b/thinkphp/library/think/Log.php index 5a6588809..1a3749626 100644 --- a/thinkphp/library/think/Log.php +++ b/thinkphp/library/think/Log.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,203 +11,377 @@ namespace think; -use think\exception\ClassNotFoundException; - -/** - * Class Log - * @package think - * - * @method void log($msg) static - * @method void error($msg) static - * @method void info($msg) static - * @method void sql($msg) static - * @method void notice($msg) static - * @method void alert($msg) static - */ -class Log +class Log implements LoggerInterface { - const LOG = 'log'; - const ERROR = 'error'; - const INFO = 'info'; - const SQL = 'sql'; - const NOTICE = 'notice'; - const ALERT = 'alert'; - const DEBUG = 'debug'; + const EMERGENCY = 'emergency'; + const ALERT = 'alert'; + const CRITICAL = 'critical'; + const ERROR = 'error'; + const WARNING = 'warning'; + const NOTICE = 'notice'; + const INFO = 'info'; + const DEBUG = 'debug'; + const SQL = 'sql'; - // 日志信息 - protected static $log = []; - // 配置参数 - protected static $config = []; - // 日志类型 - protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug']; - // 日志写入驱动 - protected static $driver; + /** + * 日志信息 + * @var array + */ + protected $log = []; - // 当前日志授权key - protected static $key; + /** + * 配置参数 + * @var array + */ + protected $config = []; + + /** + * 日志写入驱动 + * @var object + */ + protected $driver; + + /** + * 日志授权key + * @var string + */ + protected $key; + + /** + * 是否允许日志写入 + * @var bool + */ + protected $allowWrite = true; + + /** + * 应用对象 + * @var App + */ + protected $app; + + public function __construct(App $app) + { + $this->app = $app; + } + + public static function __make(App $app, Config $config) + { + return (new static($app))->init($config->pull('log')); + } /** * 日志初始化 - * @param array $config + * @access public + * @param array $config + * @return $this */ - public static function init($config = []) + public function init($config = []) { - $type = isset($config['type']) ? $config['type'] : 'File'; - $class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type); - self::$config = $config; + $type = isset($config['type']) ? $config['type'] : 'File'; + + $this->config = $config; + unset($config['type']); - if (class_exists($class)) { - self::$driver = new $class($config); - } else { - throw new ClassNotFoundException('class not exists:' . $class, $class); + + if (!empty($config['close'])) { + $this->allowWrite = false; } - // 记录初始化信息 - App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info'); + + $this->driver = Loader::factory($type, '\\think\\log\\driver\\', $config); + + return $this; } /** * 获取日志信息 - * @param string $type 信息类型 + * @access public + * @param string $type 信息类型 * @return array */ - public static function getLog($type = '') + public function getLog($type = '') { - return $type ? self::$log[$type] : self::$log; + return $type ? $this->log[$type] : $this->log; } /** - * 记录调试信息 - * @param mixed $msg 调试信息 - * @param string $type 信息类型 - * @return void + * 记录日志信息 + * @access public + * @param mixed $msg 日志信息 + * @param string $type 日志级别 + * @param array $context 替换内容 + * @return $this */ - public static function record($msg, $type = 'log') + public function record($msg, $type = 'info', array $context = []) { - self::$log[$type][] = $msg; - if (IS_CLI) { - // 命令行下面日志写入改进 - self::save(); + if (!$this->allowWrite) { + return; } + + if (is_string($msg) && !empty($context)) { + $replace = []; + foreach ($context as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + $msg = strtr($msg, $replace); + } + + if (PHP_SAPI == 'cli') { + // 命令行日志实时写入 + $this->write($msg, $type, true); + } else { + $this->log[$type][] = $msg; + } + + return $this; } /** * 清空日志信息 - * @return void + * @access public + * @return $this */ - public static function clear() + public function clear() { - self::$log = []; + $this->log = []; + + return $this; } /** * 当前日志记录的授权key - * @param string $key 授权key - * @return void + * @access public + * @param string $key 授权key + * @return $this */ - public static function key($key) + public function key($key) { - self::$key = $key; + $this->key = $key; + + return $this; } /** * 检查日志写入权限 - * @param array $config 当前日志配置参数 + * @access public + * @param array $config 当前日志配置参数 * @return bool */ - public static function check($config) + public function check($config) { - if (self::$key && !empty($config['allow_key']) && !in_array(self::$key, $config['allow_key'])) { + if ($this->key && !empty($config['allow_key']) && !in_array($this->key, $config['allow_key'])) { return false; } + return true; } + /** + * 关闭本次请求日志写入 + * @access public + * @return $this + */ + public function close() + { + $this->allowWrite = false; + $this->log = []; + + return $this; + } + /** * 保存调试信息 + * @access public * @return bool */ - public static function save() + public function save() { - if (!empty(self::$log)) { - if (is_null(self::$driver)) { - self::init(Config::get('log')); - } - - if (!self::check(self::$config)) { - // 检测日志写入权限 - return false; - } - - if (empty(self::$config['level'])) { - // 获取全部日志 - $log = self::$log; - if (!App::$debug && isset($log['debug'])) { - unset($log['debug']); - } - } else { - // 记录允许级别 - $log = []; - foreach (self::$config['level'] as $level) { - if (isset(self::$log[$level])) { - $log[$level] = self::$log[$level]; - } - } - } - - $result = self::$driver->save($log); - if ($result) { - self::$log = []; - } - - return $result; + if (empty($this->log) || !$this->allowWrite) { + return true; } - return true; + + if (!$this->check($this->config)) { + // 检测日志写入权限 + return false; + } + + $log = []; + + foreach ($this->log as $level => $info) { + if (!$this->app->isDebug() && 'debug' == $level) { + continue; + } + + if (empty($this->config['level']) || in_array($level, $this->config['level'])) { + $log[$level] = $info; + + $this->app['hook']->listen('log_level', [$level, $info]); + } + } + + $result = $this->driver->save($log, true); + + if ($result) { + $this->log = []; + } + + return $result; } /** * 实时写入日志信息 并支持行为 - * @param mixed $msg 调试信息 - * @param string $type 信息类型 - * @param bool $force 是否强制写入 + * @access public + * @param mixed $msg 调试信息 + * @param string $type 日志级别 + * @param bool $force 是否强制写入 * @return bool */ - public static function write($msg, $type = 'log', $force = false) + public function write($msg, $type = 'info', $force = false) { - $log = self::$log; // 封装日志信息 - if (true === $force || empty(self::$config['level'])) { - $log[$type][] = $msg; - } elseif (in_array($type, self::$config['level'])) { + if (empty($this->config['level'])) { + $force = true; + } + + if (true === $force || in_array($type, $this->config['level'])) { $log[$type][] = $msg; } else { return false; } // 监听log_write - Hook::listen('log_write', $log); - if (is_null(self::$driver)) { - self::init(Config::get('log')); - } + $this->app['hook']->listen('log_write', $log); + // 写入日志 - $result = self::$driver->save($log); - if ($result) { - self::$log = []; - } - return $result; + return $this->driver->save($log, false); } /** - * 静态调用 - * @param $method - * @param $args - * @return mixed + * 记录日志信息 + * @access public + * @param string $level 日志级别 + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void */ - public static function __callStatic($method, $args) + public function log($level, $message, array $context = []) { - if (in_array($method, self::$type)) { - array_push($args, $method); - return call_user_func_array('\\think\\Log::record', $args); - } + $this->record($message, $level, $context); } + /** + * 记录emergency信息 + * @access public + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void + */ + public function emergency($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * 记录警报信息 + * @access public + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void + */ + public function alert($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * 记录紧急情况 + * @access public + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void + */ + public function critical($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * 记录错误信息 + * @access public + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void + */ + public function error($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * 记录warning信息 + * @access public + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void + */ + public function warning($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * 记录notice信息 + * @access public + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void + */ + public function notice($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * 记录一般信息 + * @access public + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void + */ + public function info($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * 记录调试信息 + * @access public + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void + */ + public function debug($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + /** + * 记录sql信息 + * @access public + * @param mixed $message 日志信息 + * @param array $context 替换内容 + * @return void + */ + public function sql($message, array $context = []) + { + $this->log(__FUNCTION__, $message, $context); + } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/Middleware.php b/thinkphp/library/think/Middleware.php new file mode 100644 index 000000000..d3f43606d --- /dev/null +++ b/thinkphp/library/think/Middleware.php @@ -0,0 +1,205 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use InvalidArgumentException; +use LogicException; +use think\exception\HttpResponseException; + +class Middleware +{ + protected $queue = []; + protected $app; + protected $config = [ + 'default_namespace' => 'app\\http\\middleware\\', + ]; + + public function __construct(App $app, array $config = []) + { + $this->app = $app; + $this->config = array_merge($this->config, $config); + } + + public static function __make(App $app, Config $config) + { + return new static($app, $config->pull('middleware')); + } + + public function setConfig(array $config) + { + $this->config = array_merge($this->config, $config); + } + + /** + * 导入中间件 + * @access public + * @param array $middlewares + * @param string $type 中间件类型 + */ + public function import(array $middlewares = [], $type = 'route') + { + foreach ($middlewares as $middleware) { + $this->add($middleware, $type); + } + } + + /** + * 注册中间件 + * @access public + * @param mixed $middleware + * @param string $type 中间件类型 + */ + public function add($middleware, $type = 'route') + { + if (is_null($middleware)) { + return; + } + + $middleware = $this->buildMiddleware($middleware, $type); + + if ($middleware) { + $this->queue[$type][] = $middleware; + } + } + + /** + * 注册控制器中间件 + * @access public + * @param mixed $middleware + */ + public function controller($middleware) + { + return $this->add($middleware, 'controller'); + } + + /** + * 移除中间件 + * @access public + * @param mixed $middleware + * @param string $type 中间件类型 + */ + public function unshift($middleware, $type = 'route') + { + if (is_null($middleware)) { + return; + } + + $middleware = $this->buildMiddleware($middleware, $type); + + if ($middleware) { + array_unshift($this->queue[$type], $middleware); + } + } + + /** + * 获取注册的中间件 + * @access public + * @param string $type 中间件类型 + */ + public function all($type = 'route') + { + return $this->queue[$type] ?: []; + } + + /** + * 清除中间件 + * @access public + */ + public function clear() + { + $this->queue = []; + } + + /** + * 中间件调度 + * @access public + * @param Request $request + * @param string $type 中间件类型 + */ + public function dispatch(Request $request, $type = 'route') + { + return call_user_func($this->resolve($type), $request); + } + + /** + * 解析中间件 + * @access protected + * @param mixed $middleware + * @param string $type 中间件类型 + */ + protected function buildMiddleware($middleware, $type = 'route') + { + if (is_array($middleware)) { + list($middleware, $param) = $middleware; + } + + if ($middleware instanceof \Closure) { + return [$middleware, isset($param) ? $param : null]; + } + + if (!is_string($middleware)) { + throw new InvalidArgumentException('The middleware is invalid'); + } + + if (false === strpos($middleware, '\\')) { + if (isset($this->config[$middleware])) { + $middleware = $this->config[$middleware]; + } else { + $middleware = $this->config['default_namespace'] . $middleware; + } + } + + if (is_array($middleware)) { + return $this->import($middleware, $type); + } + + if (strpos($middleware, ':')) { + list($middleware, $param) = explode(':', $middleware, 2); + } + + return [[$this->app->make($middleware), 'handle'], isset($param) ? $param : null]; + } + + protected function resolve($type = 'route') + { + return function (Request $request) use ($type) { + + $middleware = array_shift($this->queue[$type]); + + if (null === $middleware) { + throw new InvalidArgumentException('The queue was exhausted, with no response returned'); + } + + list($call, $param) = $middleware; + + try { + $response = call_user_func_array($call, [$request, $this->resolve($type), $param]); + } catch (HttpResponseException $exception) { + $response = $exception->getResponse(); + } + + if (!$response instanceof Response) { + throw new LogicException('The middleware must return Response instance'); + } + + return $response; + }; + } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } +} diff --git a/thinkphp/library/think/Model.php b/thinkphp/library/think/Model.php index 55e7da8e4..b22ee5bcc 100644 --- a/thinkphp/library/think/Model.php +++ b/thinkphp/library/think/Model.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,106 +13,154 @@ namespace think; use InvalidArgumentException; use think\db\Query; -use think\Exception\ValidateException; -use think\model\Collection as ModelCollection; -use think\model\Relation; -use think\model\relation\BelongsTo; -use think\model\relation\BelongsToMany; -use think\model\relation\HasMany; -use think\model\relation\HasManyThrough; -use think\model\relation\HasOne; -use think\model\relation\MorphMany; -use think\model\relation\MorphOne; -use think\model\relation\MorphTo; /** * Class Model * @package think * @mixin Query + * @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 + * @method Query whereRaw(string $where, array $bind = []) static 表达式查询 + * @method Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询 + * @method Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询 + * @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 + * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 + * @method Query with(mixed $with) static 关联预载入 + * @method Query count(string $field) static Count统计查询 + * @method Query min(string $field) static Min统计查询 + * @method Query max(string $field) static Max统计查询 + * @method Query sum(string $field) static SUM统计查询 + * @method Query avg(string $field) static Avg统计查询 + * @method Query field(mixed $field, boolean $except = false) static 指定查询字段 + * @method Query fieldRaw(string $field, array $bind = []) static 指定查询字段 + * @method Query union(mixed $union, boolean $all = false) static UNION查询 + * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT + * @method Query order(mixed $field, string $order = null) static 查询ORDER + * @method Query orderRaw(string $field, array $bind = []) static 查询ORDER + * @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 + * @method mixed value(string $field) static 获取某个字段的值 + * @method array column(string $field, string $key = '') static 获取某个列的值 + * @method mixed find(mixed $data = null) static 查询单个记录 + * @method mixed select(mixed $data = null) static 查询多个记录 + * @method mixed get(mixed $data = null,mixed $with =[],bool $cache= false) static 查询单个记录 支持关联预载入 + * @method mixed getOrFail(mixed $data = null,mixed $with =[],bool $cache= false) static 查询单个记录 不存在则抛出异常 + * @method mixed findOrEmpty(mixed $data = null,mixed $with =[],bool $cache= false) static 查询单个记录 不存在则返回空模型 + * @method mixed all(mixed $data = null,mixed $with =[],bool $cache= false) static 查询多个记录 支持关联预载入 + * @method \think\Model withAttr(array $name,\Closure $closure) 动态定义获取器 */ abstract class Model implements \JsonSerializable, \ArrayAccess { - // 数据库查询对象池 - protected static $links = []; - // 数据库配置 - protected $connection = []; - // 父关联模型对象 - protected $parent; - // 数据库查询对象 - protected $query; - // 当前模型名称 - protected $name; - // 数据表名称 - protected $table; - // 当前类名称 - protected $class; - // 回调事件 - private static $event = []; - // 错误信息 - protected $error; - // 字段验证规则 - protected $validate; - // 数据表主键 复合主键使用数组定义 不设置则自动获取 - protected $pk; - // 数据表字段信息 留空则自动获取 - protected $field = []; - // 只读字段 - protected $readonly = []; - // 显示属性 - protected $visible = []; - // 隐藏属性 - protected $hidden = []; - // 追加属性 - protected $append = []; - // 数据信息 - protected $data = []; - // 原始数据 - protected $origin = []; - // 关联模型 - protected $relation = []; + use model\concern\Attribute; + use model\concern\RelationShip; + use model\concern\ModelEvent; + use model\concern\TimeStamp; + use model\concern\Conversion; - // 保存自动完成列表 + /** + * 是否存在数据 + * @var bool + */ + private $exists = false; + + /** + * 是否Replace + * @var bool + */ + private $replace = false; + + /** + * 是否强制更新所有数据 + * @var bool + */ + private $force = false; + + /** + * 更新条件 + * @var array + */ + private $updateWhere; + + /** + * 数据库配置信息 + * @var array|string + */ + protected $connection = []; + + /** + * 数据库查询对象类名 + * @var string + */ + protected $query; + + /** + * 模型名称 + * @var string + */ + protected $name; + + /** + * 数据表名称 + * @var string + */ + protected $table; + + /** + * 写入自动完成定义 + * @var array + */ protected $auto = []; - // 新增自动完成列表 + + /** + * 新增自动完成定义 + * @var array + */ protected $insert = []; - // 更新自动完成列表 + + /** + * 更新自动完成定义 + * @var array + */ protected $update = []; - // 是否需要自动写入时间戳 如果设置为字符串 则表示时间字段的类型 - protected $autoWriteTimestamp; - // 创建时间字段 - protected $createTime = 'create_time'; - // 更新时间字段 - protected $updateTime = 'update_time'; - // 时间字段取出后的默认时间格式 - protected $dateFormat; - // 字段类型或者格式转换 - protected $type = []; - // 是否为更新数据 - protected $isUpdate = false; - // 更新条件 - protected $updateWhere; - // 验证失败是否抛出异常 - protected $failException = false; - // 全局查询范围 - protected $useGlobalScope = true; - // 是否采用批量验证 - protected $batchValidate = false; - // 查询数据集对象 - protected $resultSetType; - // 关联自动写入 - protected $relationWrite; /** * 初始化过的模型. - * * @var array */ protected static $initialized = []; /** - * 构造方法 + * 是否从主库读取(主从分布式有效) + * @var array + */ + protected static $readMaster; + + /** + * 查询对象实例 + * @var Query + */ + protected $queryInstance; + + /** + * 错误信息 + * @var mixed + */ + protected $error; + + /** + * 软删除字段默认值 + * @var mixed + */ + protected $defaultSoftDelete; + + /** + * 全局查询范围 + * @var array + */ + protected $globalScope = []; + + /** + * 架构函数 * @access public - * @param array|object $data 数据 + * @param array|object $data 数据 */ public function __construct($data = []) { @@ -121,17 +169,26 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } else { $this->data = $data; } + + if ($this->disuse) { + // 废弃字段 + foreach ((array) $this->disuse as $key) { + if (array_key_exists($key, $this->data)) { + unset($this->data[$key]); + } + } + } + // 记录原始数据 $this->origin = $this->data; - // 当前类名 - $this->class = get_called_class(); + $config = Db::getConfig(); if (empty($this->name)) { // 当前模型名 - $name = str_replace('\\', '/', $this->class); + $name = str_replace('\\', '/', static::class); $this->name = basename($name); - if (Config::get('class_suffix')) { + if (Container::get('config')->get('class_suffix')) { $suffix = basename(dirname($name)); $this->name = substr($this->name, 0, -strlen($suffix)); } @@ -139,21 +196,70 @@ abstract class Model implements \JsonSerializable, \ArrayAccess if (is_null($this->autoWriteTimestamp)) { // 自动写入时间戳 - $this->autoWriteTimestamp = $this->getQuery()->getConfig('auto_timestamp'); + $this->autoWriteTimestamp = $config['auto_timestamp']; } if (is_null($this->dateFormat)) { // 设置时间戳格式 - $this->dateFormat = $this->getQuery()->getConfig('datetime_format'); + $this->dateFormat = $config['datetime_format']; } if (is_null($this->resultSetType)) { - $this->resultSetType = $this->getQuery()->getConfig('resultset_type'); + $this->resultSetType = $config['resultset_type']; } + + if (!empty($this->connection) && is_array($this->connection)) { + // 设置模型的数据库连接 + $this->connection = array_merge($config, $this->connection); + } + + if ($this->observerClass) { + // 注册模型观察者 + static::observe($this->observerClass); + } + // 执行初始化操作 $this->initialize(); } + /** + * 获取当前模型名称 + * @access public + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * 是否从主库读取数据(主从分布有效) + * @access public + * @param bool $all 是否所有模型有效 + * @return $this + */ + public function readMaster($all = false) + { + $model = $all ? '*' : static::class; + + static::$readMaster[$model] = true; + + return $this; + } + + /** + * 创建新的模型实例 + * @access public + * @param array|object $data 数据 + * @param bool $isUpdate 是否为更新 + * @param mixed $where 更新条件 + * @return Model + */ + public function newInstance($data = [], $isUpdate = false, $where = null) + { + return (new static($data))->isUpdate($isUpdate, $where); + } + /** * 创建模型的查询对象 * @access protected @@ -161,27 +267,20 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ protected function buildQuery() { - // 合并数据库配置 - if (!empty($this->connection)) { - if (is_array($this->connection)) { - $connection = array_merge(Config::get('database'), $this->connection); - } else { - $connection = $this->connection; - } - } else { - $connection = []; - } - - $con = Db::connect($connection); // 设置当前模型 确保查询返回模型对象 - $queryClass = $this->query ?: $con->getConfig('query'); - $query = new $queryClass($con, $this->class); + $query = Db::connect($this->connection, false, $this->query); + $query->model($this) + ->name($this->name) + ->json($this->json, $this->jsonAssoc) + ->setJsonFieldType($this->jsonType); + + if (isset(static::$readMaster['*']) || isset(static::$readMaster[static::class])) { + $query->master(true); + } // 设置当前数据表和模型名 if (!empty($this->table)) { - $query->setTable($this->table); - } else { - $query->name($this->name); + $query->table($this->table); } if (!empty($this->pk)) { @@ -192,39 +291,47 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } /** - * 获取当前模型的查询对象 + * 获取当前模型的数据库查询对象 * @access public - * @param bool $buildNewQuery 创建新的查询对象 - * @return Query + * @param Query $query 查询对象实例 + * @return $this */ - public function getQuery($buildNewQuery = false) + public function setQuery($query) { - if ($buildNewQuery) { - return $this->buildQuery(); - } elseif (!isset(self::$links[$this->class])) { - // 创建模型查询对象 - self::$links[$this->class] = $this->buildQuery(); - } - - return self::$links[$this->class]; + $this->queryInstance = $query; + return $this; } /** * 获取当前模型的数据库查询对象 * @access public - * @param bool $useBaseQuery 是否调用全局查询范围 - * @param bool $buildNewQuery 创建新的查询对象 + * @param bool|array $useBaseQuery 是否调用全局查询范围(或者指定查询范围名称) * @return Query */ - public function db($useBaseQuery = true, $buildNewQuery = true) + public function db($useBaseQuery = true) { - $query = $this->getQuery($buildNewQuery); + if ($this->queryInstance) { + return $this->queryInstance; + } + + $query = $this->buildQuery(); + + // 软删除 + if (property_exists($this, 'withTrashed') && !$this->withTrashed) { + $this->withNoTrashed($query); + } // 全局作用域 - if ($useBaseQuery && method_exists($this, 'base')) { + if (true === $useBaseQuery && method_exists($this, 'base')) { call_user_func_array([$this, 'base'], [ & $query]); } + $globalScope = is_array($useBaseQuery) && $useBaseQuery ? $useBaseQuery : $this->globalScope; + + if ($globalScope && false !== $useBaseQuery) { + $query->scope($globalScope); + } + // 返回当前模型的数据库查询对象 return $query; } @@ -236,9 +343,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ protected function initialize() { - $class = get_class($this); - if (!isset(static::$initialized[$class])) { - static::$initialized[$class] = true; + if (!isset(static::$initialized[static::class])) { + static::$initialized[static::class] = true; static::init(); } } @@ -249,970 +355,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess * @return void */ protected static function init() - { - } - - /** - * 设置父关联对象 - * @access public - * @param Model $model 模型对象 - * @return $this - */ - public function setParent($model) - { - $this->parent = $model; - - return $this; - } - - /** - * 获取父关联对象 - * @access public - * @return Model - */ - public function getParent() - { - return $this->parent; - } - - /** - * 设置数据对象值 - * @access public - * @param mixed $data 数据或者属性名 - * @param mixed $value 值 - * @return $this - */ - public function data($data, $value = null) - { - if (is_string($data)) { - $this->data[$data] = $value; - } else { - // 清空数据 - $this->data = []; - if (is_object($data)) { - $data = get_object_vars($data); - } - if (true === $value) { - // 数据对象赋值 - foreach ($data as $key => $value) { - $this->setAttr($key, $value, $data); - } - } else { - $this->data = $data; - } - } - return $this; - } - - /** - * 获取对象原始数据 如果不存在指定字段返回false - * @access public - * @param string $name 字段名 留空获取全部 - * @return mixed - * @throws InvalidArgumentException - */ - public function getData($name = null) - { - if (is_null($name)) { - return $this->data; - } elseif (array_key_exists($name, $this->data)) { - return $this->data[$name]; - } elseif (array_key_exists($name, $this->relation)) { - return $this->relation[$name]; - } else { - throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name); - } - } - - /** - * 修改器 设置数据对象值 - * @access public - * @param string $name 属性名 - * @param mixed $value 属性值 - * @param array $data 数据 - * @return $this - */ - public function setAttr($name, $value, $data = []) - { - if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { - // 自动写入的时间戳字段 - $value = $this->autoWriteTimestamp($name); - } else { - // 检测修改器 - $method = 'set' . Loader::parseName($name, 1) . 'Attr'; - if (method_exists($this, $method)) { - $value = $this->$method($value, array_merge($this->data, $data)); - } elseif (isset($this->type[$name])) { - // 类型转换 - $value = $this->writeTransform($value, $this->type[$name]); - } - } - - // 设置数据对象属性 - $this->data[$name] = $value; - return $this; - } - - /** - * 获取当前模型的关联模型数据 - * @access public - * @param string $name 关联方法名 - * @return mixed - */ - public function getRelation($name = null) - { - if (is_null($name)) { - return $this->relation; - } elseif (array_key_exists($name, $this->relation)) { - return $this->relation[$name]; - } else { - return; - } - } - - /** - * 设置关联数据对象值 - * @access public - * @param string $name 属性名 - * @param mixed $value 属性值 - * @return $this - */ - public function setRelation($name, $value) - { - $this->relation[$name] = $value; - return $this; - } - - /** - * 自动写入时间戳 - * @access public - * @param string $name 时间戳字段 - * @return mixed - */ - protected function autoWriteTimestamp($name) - { - if (isset($this->type[$name])) { - $type = $this->type[$name]; - if (strpos($type, ':')) { - list($type, $param) = explode(':', $type, 2); - } - switch ($type) { - case 'datetime': - case 'date': - $format = !empty($param) ? $param : $this->dateFormat; - $value = $this->formatDateTime(time(), $format); - break; - case 'timestamp': - case 'integer': - default: - $value = time(); - break; - } - } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ - 'datetime', - 'date', - 'timestamp', - ]) - ) { - $value = $this->formatDateTime(time(), $this->dateFormat); - } else { - $value = $this->formatDateTime(time(), $this->dateFormat, true); - } - return $value; - } - - /** - * 时间日期字段格式化处理 - * @access public - * @param mixed $time 时间日期表达式 - * @param mixed $format 日期格式 - * @param bool $timestamp 是否进行时间戳转换 - * @return mixed - */ - protected function formatDateTime($time, $format, $timestamp = false) - { - if (false !== strpos($format, '\\')) { - $time = new $format($time); - } elseif (!$timestamp && false !== $format) { - $time = date($format, $time); - } - return $time; - } - - /** - * 数据写入 类型转换 - * @access public - * @param mixed $value 值 - * @param string|array $type 要转换的类型 - * @return mixed - */ - protected function writeTransform($value, $type) - { - if (is_null($value)) { - return; - } - - if (is_array($type)) { - list($type, $param) = $type; - } elseif (strpos($type, ':')) { - list($type, $param) = explode(':', $type, 2); - } - switch ($type) { - case 'integer': - $value = (int) $value; - break; - case 'float': - if (empty($param)) { - $value = (float) $value; - } else { - $value = (float) number_format($value, $param, '.', ''); - } - break; - case 'boolean': - $value = (bool) $value; - break; - case 'timestamp': - if (!is_numeric($value)) { - $value = strtotime($value); - } - break; - case 'datetime': - $format = !empty($param) ? $param : $this->dateFormat; - $value = is_numeric($value) ? $value : strtotime($value); - $value = $this->formatDateTime($value, $format); - break; - case 'object': - if (is_object($value)) { - $value = json_encode($value, JSON_FORCE_OBJECT); - } - break; - case 'array': - $value = (array) $value; - case 'json': - $option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE; - $value = json_encode($value, $option); - break; - case 'serialize': - $value = serialize($value); - break; - - } - return $value; - } - - /** - * 获取器 获取数据对象的值 - * @access public - * @param string $name 名称 - * @return mixed - * @throws InvalidArgumentException - */ - public function getAttr($name) - { - try { - $notFound = false; - $value = $this->getData($name); - } catch (InvalidArgumentException $e) { - $notFound = true; - $value = null; - } - - // 检测属性获取器 - $method = 'get' . Loader::parseName($name, 1) . 'Attr'; - if (method_exists($this, $method)) { - $value = $this->$method($value, $this->data); - } elseif (isset($this->type[$name])) { - // 类型转换 - $value = $this->readTransform($value, $this->type[$name]); - } elseif (in_array($name, [$this->createTime, $this->updateTime])) { - if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ - 'datetime', - 'date', - 'timestamp', - ]) - ) { - $value = $this->formatDateTime(strtotime($value), $this->dateFormat); - } else { - $value = $this->formatDateTime($value, $this->dateFormat); - } - } elseif ($notFound) { - $relation = Loader::parseName($name, 1, false); - if (method_exists($this, $relation)) { - $modelRelation = $this->$relation(); - // 不存在该字段 获取关联数据 - $value = $this->getRelationData($modelRelation); - // 保存关联对象值 - $this->relation[$name] = $value; - } else { - throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name); - } - } - return $value; - } - - /** - * 获取关联模型数据 - * @access public - * @param Relation $modelRelation 模型关联对象 - * @return mixed - */ - protected function getRelationData(Relation $modelRelation) - { - if ($this->parent && get_class($this->parent) == $modelRelation->getModel()) { - $value = $this->parent; - } else { - // 首先获取关联数据 - $value = $modelRelation->getRelation(); - } - return $value; - } - - /** - * 数据读取 类型转换 - * @access public - * @param mixed $value 值 - * @param string|array $type 要转换的类型 - * @return mixed - */ - protected function readTransform($value, $type) - { - if (is_null($value)) { - return; - } - - if (is_array($type)) { - list($type, $param) = $type; - } elseif (strpos($type, ':')) { - list($type, $param) = explode(':', $type, 2); - } - switch ($type) { - case 'integer': - $value = (int) $value; - break; - case 'float': - if (empty($param)) { - $value = (float) $value; - } else { - $value = (float) number_format($value, $param, '.', ''); - } - break; - case 'boolean': - $value = (bool) $value; - break; - case 'timestamp': - if (!is_null($value)) { - $format = !empty($param) ? $param : $this->dateFormat; - $value = $this->formatDateTime($value, $format); - } - break; - case 'datetime': - if (!is_null($value)) { - $format = !empty($param) ? $param : $this->dateFormat; - $value = $this->formatDateTime(strtotime($value), $format); - } - break; - case 'json': - $value = json_decode($value, true); - break; - case 'array': - $value = empty($value) ? [] : json_decode($value, true); - break; - case 'object': - $value = empty($value) ? new \stdClass() : json_decode($value); - break; - case 'serialize': - $value = unserialize($value); - break; - default: - if (false !== strpos($type, '\\')) { - // 对象类型 - $value = new $type($value); - } - } - return $value; - } - - /** - * 设置需要追加的输出属性 - * @access public - * @param array $append 属性列表 - * @param bool $override 是否覆盖 - * @return $this - */ - public function append($append = [], $override = false) - { - $this->append = $override ? $append : array_merge($this->append, $append); - return $this; - } - - /** - * 设置附加关联对象的属性 - * @access public - * @param string $relation 关联方法 - * @param string|array $append 追加属性名 - * @return $this - * @throws Exception - */ - public function appendRelationAttr($relation, $append) - { - if (is_string($append)) { - $append = explode(',', $append); - } - - $relation = Loader::parseName($relation, 1, false); - - // 获取关联数据 - if (isset($this->relation[$relation])) { - $model = $this->relation[$relation]; - } else { - $model = $this->getRelationData($this->$relation()); - } - - if ($model instanceof Model) { - foreach ($append as $key => $attr) { - $key = is_numeric($key) ? $attr : $key; - if (isset($this->data[$key])) { - throw new Exception('bind attr has exists:' . $key); - } else { - $this->data[$key] = $model->$attr; - } - } - } - return $this; - } - - /** - * 设置需要隐藏的输出属性 - * @access public - * @param array $hidden 属性列表 - * @param bool $override 是否覆盖 - * @return $this - */ - public function hidden($hidden = [], $override = false) - { - $this->hidden = $override ? $hidden : array_merge($this->hidden, $hidden); - return $this; - } - - /** - * 设置需要输出的属性 - * @access public - * @param array $visible - * @param bool $override 是否覆盖 - * @return $this - */ - public function visible($visible = [], $override = false) - { - $this->visible = $override ? $visible : array_merge($this->visible, $visible); - return $this; - } - - /** - * 解析隐藏及显示属性 - * @access protected - * @param array $attrs 属性 - * @param array $result 结果集 - * @param bool $visible - * @return array - */ - protected function parseAttr($attrs, &$result, $visible = true) - { - $array = []; - foreach ($attrs as $key => $val) { - if (is_array($val)) { - if ($visible) { - $array[] = $key; - } - $result[$key] = $val; - } elseif (strpos($val, '.')) { - list($key, $name) = explode('.', $val); - if ($visible) { - $array[] = $key; - } - $result[$key][] = $name; - } else { - $array[] = $val; - } - } - return $array; - } - - /** - * 转换子模型对象 - * @access protected - * @param Model|ModelCollection $model - * @param $visible - * @param $hidden - * @param $key - * @return array - */ - protected function subToArray($model, $visible, $hidden, $key) - { - // 关联模型对象 - if (isset($visible[$key])) { - $model->visible($visible[$key]); - } elseif (isset($hidden[$key])) { - $model->hidden($hidden[$key]); - } - return $model->toArray(); - } - - /** - * 转换当前模型对象为数组 - * @access public - * @return array - */ - public function toArray() - { - $item = []; - $visible = []; - $hidden = []; - - $data = array_merge($this->data, $this->relation); - - // 过滤属性 - if (!empty($this->visible)) { - $array = $this->parseAttr($this->visible, $visible); - $data = array_intersect_key($data, array_flip($array)); - } elseif (!empty($this->hidden)) { - $array = $this->parseAttr($this->hidden, $hidden, false); - $data = array_diff_key($data, array_flip($array)); - } - - foreach ($data as $key => $val) { - if ($val instanceof Model || $val instanceof ModelCollection) { - // 关联模型对象 - $item[$key] = $this->subToArray($val, $visible, $hidden, $key); - } elseif (is_array($val) && reset($val) instanceof Model) { - // 关联模型数据集 - $arr = []; - foreach ($val as $k => $value) { - $arr[$k] = $this->subToArray($value, $visible, $hidden, $key); - } - $item[$key] = $arr; - } else { - // 模型属性 - $item[$key] = $this->getAttr($key); - } - } - // 追加属性(必须定义获取器) - if (!empty($this->append)) { - foreach ($this->append as $key => $name) { - if (is_array($name)) { - // 追加关联对象属性 - $relation = $this->getAttr($key); - $item[$key] = $relation->append($name)->toArray(); - } elseif (strpos($name, '.')) { - list($key, $attr) = explode('.', $name); - // 追加关联对象属性 - $relation = $this->getAttr($key); - $item[$key] = $relation->append([$attr])->toArray(); - } else { - $item[$name] = $this->getAttr($name); - } - } - } - return !empty($item) ? $item : []; - } - - /** - * 转换当前模型对象为JSON字符串 - * @access public - * @param integer $options json参数 - * @return string - */ - public function toJson($options = JSON_UNESCAPED_UNICODE) - { - return json_encode($this->toArray(), $options); - } - - /** - * 转换当前模型数据集为数据集对象 - * @access public - * @param array|\think\Collection $collection 数据集 - * @return \think\Collection - */ - public function toCollection($collection) - { - if ($this->resultSetType) { - if ('collection' == $this->resultSetType) { - $collection = new ModelCollection($collection); - } elseif (false !== strpos($this->resultSetType, '\\')) { - $class = $this->resultSetType; - $collection = new $class($collection); - } - } - return $collection; - } - - /** - * 关联数据一起更新 - * @access public - * @param mixed $relation 关联 - * @return $this - */ - public function together($relation) - { - if (is_string($relation)) { - $relation = explode(',', $relation); - } - $this->relationWrite = $relation; - return $this; - } - - /** - * 获取模型对象的主键 - * @access public - * @param string $name 模型名 - * @return mixed - */ - public function getPk($name = '') - { - if (!empty($name)) { - $table = $this->getQuery()->getTable($name); - return $this->getQuery()->getPk($table); - } elseif (empty($this->pk)) { - $this->pk = $this->getQuery()->getPk(); - } - return $this->pk; - } - - /** - * 判断一个字段名是否为主键字段 - * @access public - * @param string $key 名称 - * @return bool - */ - protected function isPk($key) - { - $pk = $this->getPk(); - if (is_string($pk) && $pk == $key) { - return true; - } elseif (is_array($pk) && in_array($key, $pk)) { - return true; - } - return false; - } - - /** - * 保存当前数据对象 - * @access public - * @param array $data 数据 - * @param array $where 更新条件 - * @param string $sequence 自增序列名 - * @return integer|false - */ - public function save($data = [], $where = [], $sequence = null) - { - if (!empty($data)) { - // 数据自动验证 - if (!$this->validateData($data)) { - return false; - } - // 数据对象赋值 - foreach ($data as $key => $value) { - $this->setAttr($key, $value, $data); - } - if (!empty($where)) { - $this->isUpdate = true; - } - } - - // 自动关联写入 - if (!empty($this->relationWrite)) { - $relation = []; - foreach ($this->relationWrite as $key => $name) { - if (is_array($name)) { - if (key($name) === 0) { - $relation[$key] = []; - foreach ($name as $val) { - if (isset($this->data[$val])) { - $relation[$key][$val] = $this->data[$val]; - unset($this->data[$val]); - } - } - } else { - $relation[$key] = $name; - } - } elseif (isset($this->relation[$name])) { - $relation[$name] = $this->relation[$name]; - } elseif (isset($this->data[$name])) { - $relation[$name] = $this->data[$name]; - unset($this->data[$name]); - } - } - } - - // 数据自动完成 - $this->autoCompleteData($this->auto); - - // 事件回调 - if (false === $this->trigger('before_write', $this)) { - return false; - } - $pk = $this->getPk(); - if ($this->isUpdate) { - // 检测字段 - $this->checkAllowField($this->data, array_merge($this->auto, $this->update)); - - // 自动更新 - $this->autoCompleteData($this->update); - - // 获取有更新的数据 - $data = $this->getChangedData(); - - // 事件回调 - if (false === $this->trigger('before_update', $this)) { - return false; - } - - if (empty($data) || (count($data) == 1 && is_string($pk) && isset($data[$pk]))) { - // 关联更新 - if (isset($relation)) { - $this->autoRelationUpdate($relation); - } - return 0; - } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { - // 自动写入更新时间 - $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); - } - - if (empty($where) && !empty($this->updateWhere)) { - $where = $this->updateWhere; - } - - // 保留主键数据 - foreach ($this->data as $key => $val) { - if ($this->isPk($key)) { - $data[$key] = $val; - } - } - - if (is_string($pk) && isset($data[$pk])) { - if (!isset($where[$pk])) { - unset($where); - $where[$pk] = $data[$pk]; - } - unset($data[$pk]); - } - - // 模型更新 - $result = $this->getQuery()->where($where)->update($data); - - // 关联更新 - if (isset($relation)) { - $this->autoRelationUpdate($relation); - } - - // 更新回调 - $this->trigger('after_update', $this); - - } else { - // 检测字段 - $this->checkAllowField($this->data, array_merge($this->auto, $this->insert)); - - // 自动写入 - $this->autoCompleteData($this->insert); - - // 自动写入创建时间和更新时间 - if ($this->autoWriteTimestamp) { - if ($this->createTime && !isset($this->data[$this->createTime])) { - $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime); - } - if ($this->updateTime && !isset($this->data[$this->updateTime])) { - $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); - } - } - - if (false === $this->trigger('before_insert', $this)) { - return false; - } - - $result = $this->getQuery()->insert($this->data); - - // 获取自动增长主键 - if ($result && is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) { - $insertId = $this->getQuery()->getLastInsID($sequence); - if ($insertId) { - $this->data[$pk] = $insertId; - } - } - - // 关联写入 - if (isset($relation)) { - foreach ($relation as $name => $val) { - $method = Loader::parseName($name, 1, false); - $this->$method()->save($val); - } - } - - // 标记为更新 - $this->isUpdate = true; - - // 新增回调 - $this->trigger('after_insert', $this); - } - // 写入回调 - $this->trigger('after_write', $this); - - // 重新记录原始数据 - $this->origin = $this->data; - - return $result; - } - - protected function checkAllowField(&$data, $auto = []) - { - if (!empty($this->field)) { - if (true === $this->field) { - $this->field = $this->getQuery()->getTableInfo('', 'fields'); - $field = $this->field; - } else { - $field = array_merge($this->field, $auto); - } - - foreach ($data as $key => $val) { - if (!in_array($key, $field)) { - unset($data[$key]); - } - } - } - } - - protected function autoRelationUpdate($relation) - { - foreach ($relation as $name => $val) { - if ($val instanceof Model) { - $val->save(); - } else { - unset($this->data[$name]); - $model = $this->getAttr($name); - if ($model instanceof Model) { - $model->save($val); - } - } - } - } - - /** - * 获取变化的数据 并排除只读数据 - * @access public - * @return array - */ - public function getChangedData() - { - $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) { - if ((empty($b) || empty($b)) && $a !== $b) { - return 1; - } - return is_object($a) || $a != $b ? 1 : 0; - }); - - if (!empty($this->readonly)) { - // 只读字段不允许更新 - foreach ($this->readonly as $key => $field) { - if (isset($data[$field])) { - unset($data[$field]); - } - } - } - - return $data; - } - - /** - * 保存多个数据到当前数据对象 - * @access public - * @param array $dataSet 数据 - * @param boolean $replace 是否自动识别更新和写入 - * @return array|false - * @throws \Exception - */ - public function saveAll($dataSet, $replace = true) - { - if ($this->validate) { - // 数据批量验证 - $validate = $this->validate; - foreach ($dataSet as $data) { - if (!$this->validateData($data, $validate)) { - return false; - } - } - } - - $result = []; - $db = $this->getQuery(); - $db->startTrans(); - try { - $pk = $this->getPk(); - if (is_string($pk) && $replace) { - $auto = true; - } - foreach ($dataSet as $key => $data) { - if (!empty($auto) && isset($data[$pk])) { - $result[$key] = self::update($data, [], $this->field); - } else { - $result[$key] = self::create($data, $this->field); - } - } - $db->commit(); - return $result; - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } - } - - /** - * 设置允许写入的字段 - * @access public - * @param mixed $field 允许写入的字段 如果为true只允许写入数据表字段 - * @return $this - */ - public function allowField($field) - { - if (is_string($field)) { - $field = explode(',', $field); - } - $this->field = $field; - return $this; - } - - /** - * 设置只读字段 - * @access public - * @param mixed $field 只读字段 - * @return $this - */ - public function readonly($field) - { - if (is_string($field)) { - $field = explode(',', $field); - } - $this->readonly = $field; - return $this; - } - - /** - * 是否为更新数据 - * @access public - * @param bool $update - * @param mixed $where - * @return $this - */ - public function isUpdate($update = true, $where = null) - { - $this->isUpdate = $update; - if (!empty($where)) { - $this->updateWhere = $where; - } - return $this; - } + {} /** * 数据自动完成 - * @access public - * @param array $auto 要自动更新的字段列表 + * @access protected + * @param array $auto 要自动更新的字段列表 * @return void */ protected function autoCompleteData($auto = []) @@ -1234,139 +382,600 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } /** - * 删除当前的记录 + * 更新是否强制写入数据 而不做比较 * @access public - * @return integer + * @param bool $force + * @return $this */ - public function delete() + public function force($force = true) { - if (false === $this->trigger('before_delete', $this)) { + $this->force = $force; + return $this; + } + + /** + * 判断force + * @access public + * @return bool + */ + public function isForce() + { + return $this->force; + } + + /** + * 新增数据是否使用Replace + * @access public + * @param bool $replace + * @return $this + */ + public function replace($replace = true) + { + $this->replace = $replace; + return $this; + } + + /** + * 设置数据是否存在 + * @access public + * @param bool $exists + * @return $this + */ + public function exists($exists) + { + $this->exists = $exists; + return $this; + } + + /** + * 判断数据是否存在数据库 + * @access public + * @return bool + */ + public function isExists() + { + return $this->exists; + } + + /** + * 保存当前数据对象 + * @access public + * @param array $data 数据 + * @param array $where 更新条件 + * @param string $sequence 自增序列名 + * @return bool + */ + public function save($data = [], $where = [], $sequence = null) + { + if (is_string($data)) { + $sequence = $data; + $data = []; + } + + if (!$this->checkBeforeSave($data, $where)) { return false; } + $result = $this->exists ? $this->updateData($where) : $this->insertData($sequence); + + if (false === $result) { + return false; + } + + // 写入回调 + $this->trigger('after_write'); + + // 重新记录原始数据 + $this->origin = $this->data; + $this->set = []; + + return true; + } + + /** + * 写入之前检查数据 + * @access protected + * @param array $data 数据 + * @param array $where 保存条件 + * @return bool + */ + protected function checkBeforeSave($data, $where) + { + if (!empty($data)) { + // 数据对象赋值 + foreach ($data as $key => $value) { + $this->setAttr($key, $value, $data); + } + + if (!empty($where)) { + $this->exists = true; + $this->updateWhere = $where; + } + } + + // 数据自动完成 + $this->autoCompleteData($this->auto); + + // 事件回调 + if (false === $this->trigger('before_write')) { + return false; + } + + return true; + } + + /** + * 检查数据是否允许写入 + * @access protected + * @param array $append 自动完成的字段列表 + * @return array + */ + protected function checkAllowFields(array $append = []) + { + // 检测字段 + if (empty($this->field) || true === $this->field) { + $query = $this->db(false); + $table = $this->table ?: $query->getTable(); + + $this->field = $query->getConnection()->getTableFields($table); + + $field = $this->field; + } else { + $field = array_merge($this->field, $append); + + if ($this->autoWriteTimestamp) { + array_push($field, $this->createTime, $this->updateTime); + } + } + + if ($this->disuse) { + // 废弃字段 + $field = array_diff($field, (array) $this->disuse); + } + + return $field; + } + + /** + * 更新写入数据 + * @access protected + * @param mixed $where 更新条件 + * @return bool + */ + protected function updateData($where) + { + // 自动更新 + $this->autoCompleteData($this->update); + + // 事件回调 + if (false === $this->trigger('before_update')) { + return false; + } + + // 获取有更新的数据 + $data = $this->getChangedData(); + + if (empty($data)) { + // 关联更新 + if (!empty($this->relationWrite)) { + $this->autoRelationUpdate(); + } + + return false; + } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { + // 自动写入更新时间 + $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + + $this->data[$this->updateTime] = $data[$this->updateTime]; + } + + if (empty($where) && !empty($this->updateWhere)) { + $where = $this->updateWhere; + } + + // 检查允许字段 + $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->update)); + + // 保留主键数据 + foreach ($this->data as $key => $val) { + if ($this->isPk($key)) { + $data[$key] = $val; + } + } + + $pk = $this->getPk(); + $array = []; + + foreach ((array) $pk as $key) { + if (isset($data[$key])) { + $array[] = [$key, '=', $data[$key]]; + unset($data[$key]); + } + } + + if (!empty($array)) { + $where = $array; + } + + foreach ((array) $this->relationWrite as $name => $val) { + if (is_array($val)) { + foreach ($val as $key) { + if (isset($data[$key])) { + unset($data[$key]); + } + } + } + } + + // 模型更新 + $db = $this->db(false); + $db->startTrans(); + + try { + $db->where($where) + ->strict(false) + ->field($allowFields) + ->update($data); + + // 关联更新 + if (!empty($this->relationWrite)) { + $this->autoRelationUpdate(); + } + + $db->commit(); + + // 更新回调 + $this->trigger('after_update'); + + return true; + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + + /** + * 新增写入数据 + * @access protected + * @param string $sequence 自增序列名 + * @return bool + */ + protected function insertData($sequence) + { + // 自动写入 + $this->autoCompleteData($this->insert); + + // 时间戳自动写入 + $this->checkTimeStampWrite(); + + if (false === $this->trigger('before_insert')) { + return false; + } + + // 检查允许字段 + $allowFields = $this->checkAllowFields(array_merge($this->auto, $this->insert)); + + $db = $this->db(false); + $db->startTrans(); + + try { + $result = $db->strict(false) + ->field($allowFields) + ->insert($this->data, $this->replace, false, $sequence); + + // 获取自动增长主键 + if ($result && $insertId = $db->getLastInsID($sequence)) { + $pk = $this->getPk(); + + foreach ((array) $pk as $key) { + if (!isset($this->data[$key]) || '' == $this->data[$key]) { + $this->data[$key] = $insertId; + } + } + } + + // 关联写入 + if (!empty($this->relationWrite)) { + $this->autoRelationInsert(); + } + + $db->commit(); + + // 标记为更新 + $this->exists = true; + + // 新增回调 + $this->trigger('after_insert'); + + return true; + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + + /** + * 字段值(延迟)增长 + * @access public + * @param string $field 字段名 + * @param integer $step 增长值 + * @param integer $lazyTime 延时时间(s) + * @return bool + * @throws Exception + */ + public function setInc($field, $step = 1, $lazyTime = 0) + { + // 读取更新条件 + $where = $this->getWhere(); + + // 事件回调 + if (false === $this->trigger('before_update')) { + return false; + } + + $result = $this->db(false) + ->where($where) + ->setInc($field, $step, $lazyTime); + + if (true !== $result) { + $this->data[$field] += $step; + } + + // 更新回调 + $this->trigger('after_update'); + + return true; + } + + /** + * 字段值(延迟)减少 + * @access public + * @param string $field 字段名 + * @param integer $step 减少值 + * @param integer $lazyTime 延时时间(s) + * @return bool + * @throws Exception + */ + public function setDec($field, $step = 1, $lazyTime = 0) + { + // 读取更新条件 + $where = $this->getWhere(); + + // 事件回调 + if (false === $this->trigger('before_update')) { + return false; + } + + $result = $this->db(false) + ->where($where) + ->setDec($field, $step, $lazyTime); + + if (true !== $result) { + $this->data[$field] -= $step; + } + + // 更新回调 + $this->trigger('after_update'); + + return true; + } + + /** + * 获取当前的更新条件 + * @access protected + * @return mixed + */ + protected function getWhere() + { // 删除条件 $pk = $this->getPk(); + if (is_string($pk) && isset($this->data[$pk])) { - $where = [$pk => $this->data[$pk]]; + $where[] = [$pk, '=', $this->data[$pk]]; } elseif (!empty($this->updateWhere)) { $where = $this->updateWhere; } else { $where = null; } - // 删除当前模型数据 - $result = $this->getQuery()->where($where)->delete(); + return $where; + } - // 关联删除 - if (!empty($this->relationWrite)) { - foreach ($this->relationWrite as $key => $name) { - $name = is_numeric($key) ? $name : $key; - $model = $this->getAttr($name); - if ($model instanceof Model) { - $model->delete(); + /** + * 保存多个数据到当前数据对象 + * @access public + * @param array $dataSet 数据 + * @param boolean $replace 是否自动识别更新和写入 + * @return Collection + * @throws \Exception + */ + public function saveAll($dataSet, $replace = true) + { + $db = $this->db(false); + $db->startTrans(); + + try { + $pk = $this->getPk(); + + if (is_string($pk) && $replace) { + $auto = true; + } + + $result = []; + + foreach ($dataSet as $key => $data) { + if ($this->exists || (!empty($auto) && isset($data[$pk]))) { + $result[$key] = self::update($data, [], $this->field); + } else { + $result[$key] = self::create($data, $this->field, $this->replace); } } + + $db->commit(); + + return $this->toCollection($result); + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + + /** + * 是否为更新数据 + * @access public + * @param mixed $update + * @param mixed $where + * @return $this + */ + public function isUpdate($update = true, $where = null) + { + if (is_bool($update)) { + $this->exists = $update; + + if (!empty($where)) { + $this->updateWhere = $where; + } + } else { + $this->exists = true; + $this->updateWhere = $update; } - $this->trigger('after_delete', $this); - // 清空原始数据 - $this->origin = []; + return $this; + } - return $result; + /** + * 删除当前的记录 + * @access public + * @return bool + */ + public function delete() + { + if (!$this->exists || false === $this->trigger('before_delete')) { + return false; + } + + // 读取更新条件 + $where = $this->getWhere(); + + $db = $this->db(false); + $db->startTrans(); + + try { + // 删除当前模型数据 + $db->where($where)->delete(); + + // 关联删除 + if (!empty($this->relationWrite)) { + $this->autoRelationDelete(); + } + + $db->commit(); + + $this->trigger('after_delete'); + + $this->exists = false; + + return true; + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } } /** * 设置自动完成的字段( 规则通过修改器定义) * @access public - * @param array $fields 需要自动完成的字段 + * @param array $fields 需要自动完成的字段 * @return $this */ public function auto($fields) { $this->auto = $fields; + return $this; } /** - * 设置字段验证 + * 写入数据 * @access public - * @param array|string|bool $rule 验证规则 true表示自动读取验证器类 - * @param array $msg 提示信息 - * @param bool $batch 批量验证 - * @return $this + * @param array $data 数据数组 + * @param array|true $field 允许字段 + * @param bool $replace 使用Replace + * @return static */ - public function validate($rule = true, $msg = [], $batch = false) + public static function create($data = [], $field = null, $replace = false) { - if (is_array($rule)) { - $this->validate = [ - 'rule' => $rule, - 'msg' => $msg, - ]; - } else { - $this->validate = true === $rule ? $this->name : $rule; + $model = new static(); + + if (!empty($field)) { + $model->allowField($field); } - $this->batchValidate = $batch; - return $this; + + $model->isUpdate(false)->replace($replace)->save($data, []); + + return $model; } /** - * 设置验证失败后是否抛出异常 + * 更新数据 * @access public - * @param bool $fail 是否抛出异常 - * @return $this + * @param array $data 数据数组 + * @param array $where 更新条件 + * @param array|true $field 允许字段 + * @return static */ - public function validateFailException($fail = true) + public static function update($data = [], $where = [], $field = null) { - $this->failException = $fail; - return $this; + $model = new static(); + + if (!empty($field)) { + $model->allowField($field); + } + + $model->isUpdate(true)->save($data, $where); + + return $model; } /** - * 自动验证数据 - * @access protected - * @param array $data 验证数据 - * @param mixed $rule 验证规则 - * @param bool $batch 批量验证 + * 删除记录 + * @access public + * @param mixed $data 主键列表 支持闭包查询条件 * @return bool */ - protected function validateData($data, $rule = null, $batch = null) + public static function destroy($data) { - $info = is_null($rule) ? $this->validate : $rule; - - if (!empty($info)) { - if (is_array($info)) { - $validate = Loader::validate(); - $validate->rule($info['rule']); - $validate->message($info['msg']); - } else { - $name = is_string($info) ? $info : $this->name; - if (strpos($name, '.')) { - list($name, $scene) = explode('.', $name); - } - $validate = Loader::validate($name); - if (!empty($scene)) { - $validate->scene($scene); - } - } - $batch = is_null($batch) ? $this->batchValidate : $batch; - - if (!$validate->batch($batch)->check($data)) { - $this->error = $validate->getError(); - if ($this->failException) { - throw new ValidateException($this->error); - } else { - return false; - } - } - $this->validate = null; + if (empty($data) && 0 !== $data) { + return false; } + + $model = new static(); + + $query = $model->db(); + + if (is_array($data) && key($data) !== 0) { + $query->where($data); + $data = null; + } elseif ($data instanceof \Closure) { + $data($query); + $data = null; + } + + $resultSet = $query->select($data); + + if ($resultSet) { + foreach ($resultSet as $data) { + $data->delete(); + } + } + return true; } /** - * 返回模型的错误信息 + * 获取错误信息 * @access public - * @return string|array + * @return mixed */ public function getError() { @@ -1374,607 +983,26 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } /** - * 注册回调方法 - * @access public - * @param string $event 事件名 - * @param callable $callback 回调方法 - * @param bool $override 是否覆盖 - * @return void + * 解序列化后处理 */ - public static function event($event, $callback, $override = false) + public function __wakeup() { - $class = get_called_class(); - if ($override) { - self::$event[$class][$event] = []; - } - self::$event[$class][$event][] = $callback; + $this->initialize(); } - /** - * 触发事件 - * @access protected - * @param string $event 事件名 - * @param mixed $params 传入参数(引用) - * @return bool - */ - protected function trigger($event, &$params) + public function __debugInfo() { - if (isset(self::$event[$this->class][$event])) { - foreach (self::$event[$this->class][$event] as $callback) { - if (is_callable($callback)) { - $result = call_user_func_array($callback, [ & $params]); - if (false === $result) { - return false; - } - } - } - } - return true; - } - - /** - * 写入数据 - * @access public - * @param array $data 数据数组 - * @param array|true $field 允许字段 - * @return $this - */ - public static function create($data = [], $field = null) - { - $model = new static(); - if (!empty($field)) { - $model->allowField($field); - } - $model->isUpdate(false)->save($data, []); - return $model; - } - - /** - * 更新数据 - * @access public - * @param array $data 数据数组 - * @param array $where 更新条件 - * @param array|true $field 允许字段 - * @return $this - */ - public static function update($data = [], $where = [], $field = null) - { - $model = new static(); - if (!empty($field)) { - $model->allowField($field); - } - $result = $model->isUpdate(true)->save($data, $where); - return $model; - } - - /** - * 查找单条记录 - * @access public - * @param mixed $data 主键值或者查询条件(闭包) - * @param array|string $with 关联预查询 - * @param bool $cache 是否缓存 - * @return static|null - * @throws exception\DbException - */ - public static function get($data, $with = [], $cache = false) - { - if (is_null($data)) { - return; - } - - if (true === $with || is_int($with)) { - $cache = $with; - $with = []; - } - $query = static::parseQuery($data, $with, $cache); - return $query->find($data); - } - - /** - * 查找所有记录 - * @access public - * @param mixed $data 主键列表或者查询条件(闭包) - * @param array|string $with 关联预查询 - * @param bool $cache 是否缓存 - * @return static[]|false - * @throws exception\DbException - */ - public static function all($data = null, $with = [], $cache = false) - { - if (true === $with || is_int($with)) { - $cache = $with; - $with = []; - } - $query = static::parseQuery($data, $with, $cache); - return $query->select($data); - } - - /** - * 分析查询表达式 - * @access public - * @param mixed $data 主键列表或者查询条件(闭包) - * @param string $with 关联预查询 - * @param bool $cache 是否缓存 - * @return Query - */ - protected static function parseQuery(&$data, $with, $cache) - { - $result = self::with($with)->cache($cache); - if (is_array($data) && key($data) !== 0) { - $result = $result->where($data); - $data = null; - } elseif ($data instanceof \Closure) { - call_user_func_array($data, [ & $result]); - $data = null; - } elseif ($data instanceof Query) { - $result = $data->with($with)->cache($cache); - $data = null; - } - return $result; - } - - /** - * 删除记录 - * @access public - * @param mixed $data 主键列表 支持闭包查询条件 - * @return integer 成功删除的记录数 - */ - public static function destroy($data) - { - $model = new static(); - $query = $model->db(); - if (is_array($data) && key($data) !== 0) { - $query->where($data); - $data = null; - } elseif ($data instanceof \Closure) { - call_user_func_array($data, [ & $query]); - $data = null; - } elseif (empty($data) && 0 !== $data) { - return 0; - } - $resultSet = $query->select($data); - $count = 0; - if ($resultSet) { - foreach ($resultSet as $data) { - $result = $data->delete(); - $count += $result; - } - } - return $count; - } - - /** - * 命名范围 - * @access public - * @param string|array|\Closure $name 命名范围名称 逗号分隔 - * @internal mixed ...$params 参数调用 - * @return Query - */ - public static function scope($name) - { - $model = new static(); - $query = $model->db(); - $params = func_get_args(); - array_unshift($params, $query); - if ($name instanceof \Closure) { - call_user_func_array($name, $params); - } elseif (is_string($name)) { - $name = explode(',', $name); - } - if (is_array($name)) { - foreach ($name as $scope) { - $method = 'scope' . trim($scope); - if (method_exists($model, $method)) { - call_user_func_array([$model, $method], $params); - } - } - } - return $query; - } - - /** - * 设置是否使用全局查询范围 - * @param bool $use 是否启用全局查询范围 - * @access public - * @return Model - */ - public static function useGlobalScope($use) - { - $model = new static(); - return $model->db($use); - } - - /** - * 根据关联条件查询当前模型 - * @access public - * @param string $relation 关联方法名 - * @param mixed $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @return Relation|Query - */ - public static function has($relation, $operator = '>=', $count = 1, $id = '*') - { - $relation = (new static())->$relation(); - if (is_array($operator) || $operator instanceof \Closure) { - return $relation->hasWhere($operator); - } - return $relation->has($operator, $count, $id); - } - - /** - * 根据关联条件查询当前模型 - * @access public - * @param string $relation 关联方法名 - * @param mixed $where 查询条件(数组或者闭包) - * @return Relation|Query - */ - public static function hasWhere($relation, $where = []) - { - return (new static())->$relation()->hasWhere($where); - } - - /** - * 解析模型的完整命名空间 - * @access public - * @param string $model 模型名(或者完整类名) - * @return string - */ - protected function parseModel($model) - { - if (false === strpos($model, '\\')) { - $path = explode('\\', get_called_class()); - array_pop($path); - array_push($path, Loader::parseName($model, 1)); - $model = implode('\\', $path); - } - return $model; - } - - /** - * 查询当前模型的关联数据 - * @access public - * @param string|array $relations 关联名 - * @return $this - */ - public function relationQuery($relations) - { - if (is_string($relations)) { - $relations = explode(',', $relations); - } - - foreach ($relations as $key => $relation) { - $subRelation = ''; - $closure = null; - if ($relation instanceof \Closure) { - // 支持闭包查询过滤关联条件 - $closure = $relation; - $relation = $key; - } - if (is_array($relation)) { - $subRelation = $relation; - $relation = $key; - } elseif (strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation, 2); - } - $method = Loader::parseName($relation, 1, false); - $this->data[$relation] = $this->$method()->getRelation($subRelation, $closure); - } - return $this; - } - - /** - * 预载入关联查询 返回数据集 - * @access public - * @param array $resultSet 数据集 - * @param string $relation 关联名 - * @return array - */ - public function eagerlyResultSet(&$resultSet, $relation) - { - $relations = is_string($relation) ? explode(',', $relation) : $relation; - foreach ($relations as $key => $relation) { - $subRelation = ''; - $closure = false; - if ($relation instanceof \Closure) { - $closure = $relation; - $relation = $key; - } - if (is_array($relation)) { - $subRelation = $relation; - $relation = $key; - } elseif (strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation, 2); - } - $relation = Loader::parseName($relation, 1, false); - $this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure); - } - } - - /** - * 预载入关联查询 返回模型对象 - * @access public - * @param Model $result 数据对象 - * @param string $relation 关联名 - * @return Model - */ - public function eagerlyResult(&$result, $relation) - { - $relations = is_string($relation) ? explode(',', $relation) : $relation; - - foreach ($relations as $key => $relation) { - $subRelation = ''; - $closure = false; - if ($relation instanceof \Closure) { - $closure = $relation; - $relation = $key; - } - if (is_array($relation)) { - $subRelation = $relation; - $relation = $key; - } elseif (strpos($relation, '.')) { - list($relation, $subRelation) = explode('.', $relation, 2); - } - $relation = Loader::parseName($relation, 1, false); - $this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure); - } - } - - /** - * 关联统计 - * @access public - * @param Model $result 数据对象 - * @param string|array $relation 关联名 - * @return void - */ - public function relationCount(&$result, $relation) - { - $relations = is_string($relation) ? explode(',', $relation) : $relation; - - foreach ($relations as $key => $relation) { - $closure = false; - if ($relation instanceof \Closure) { - $closure = $relation; - $relation = $key; - } elseif (is_string($key)) { - $name = $relation; - $relation = $key; - } - $relation = Loader::parseName($relation, 1, false); - $count = $this->$relation()->relationCount($result, $closure); - if (!isset($name)) { - $name = Loader::parseName($relation) . '_count'; - } - $result->setAttr($name, $count); - } - } - - /** - * 获取模型的默认外键名 - * @access public - * @param string $name 模型名 - * @return string - */ - protected function getForeignKey($name) - { - if (strpos($name, '\\')) { - $name = basename(str_replace('\\', '/', $name)); - } - return Loader::parseName($name) . '_id'; - } - - /** - * HAS ONE 关联定义 - * @access public - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @param array $alias 别名定义(已经废弃) - * @param string $joinType JOIN类型 - * @return HasOne - */ - public function hasOne($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER') - { - // 记录当前关联信息 - $model = $this->parseModel($model); - $localKey = $localKey ?: $this->getPk(); - $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); - return new HasOne($this, $model, $foreignKey, $localKey, $joinType); - } - - /** - * BELONGS TO 关联定义 - * @access public - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @param array $alias 别名定义(已经废弃) - * @param string $joinType JOIN类型 - * @return BelongsTo - */ - public function belongsTo($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER') - { - // 记录当前关联信息 - $model = $this->parseModel($model); - $foreignKey = $foreignKey ?: $this->getForeignKey($model); - $localKey = $localKey ?: (new $model)->getPk(); - $trace = debug_backtrace(false, 2); - $relation = Loader::parseName($trace[1]['function']); - return new BelongsTo($this, $model, $foreignKey, $localKey, $joinType, $relation); - } - - /** - * HAS MANY 关联定义 - * @access public - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @return HasMany - */ - public function hasMany($model, $foreignKey = '', $localKey = '') - { - // 记录当前关联信息 - $model = $this->parseModel($model); - $localKey = $localKey ?: $this->getPk(); - $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); - return new HasMany($this, $model, $foreignKey, $localKey); - } - - /** - * HAS MANY 远程关联定义 - * @access public - * @param string $model 模型名 - * @param string $through 中间模型名 - * @param string $foreignKey 关联外键 - * @param string $throughKey 关联外键 - * @param string $localKey 关联主键 - * @return HasManyThrough - */ - public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '') - { - // 记录当前关联信息 - $model = $this->parseModel($model); - $through = $this->parseModel($through); - $localKey = $localKey ?: $this->getPk(); - $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); - $throughKey = $throughKey ?: $this->getForeignKey($through); - return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey); - } - - /** - * BELONGS TO MANY 关联定义 - * @access public - * @param string $model 模型名 - * @param string $table 中间表名 - * @param string $foreignKey 关联外键 - * @param string $localKey 当前模型关联键 - * @return BelongsToMany - */ - public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '') - { - // 记录当前关联信息 - $model = $this->parseModel($model); - $name = Loader::parseName(basename(str_replace('\\', '/', $model))); - $table = $table ?: $this->getQuery()->getTable(Loader::parseName($this->name) . '_' . $name); - $foreignKey = $foreignKey ?: $name . '_id'; - $localKey = $localKey ?: $this->getForeignKey($this->name); - return new BelongsToMany($this, $model, $table, $foreignKey, $localKey); - } - - /** - * MORPH MANY 关联定义 - * @access public - * @param string $model 模型名 - * @param string|array $morph 多态字段信息 - * @param string $type 多态类型 - * @return MorphMany - */ - public function morphMany($model, $morph = null, $type = '') - { - // 记录当前关联信息 - $model = $this->parseModel($model); - if (is_null($morph)) { - $trace = debug_backtrace(false, 2); - $morph = Loader::parseName($trace[1]['function']); - } - $type = $type ?: Loader::parseName($this->name); - if (is_array($morph)) { - list($morphType, $foreignKey) = $morph; - } else { - $morphType = $morph . '_type'; - $foreignKey = $morph . '_id'; - } - return new MorphMany($this, $model, $foreignKey, $morphType, $type); - } - - /** - * MORPH One 关联定义 - * @access public - * @param string $model 模型名 - * @param string|array $morph 多态字段信息 - * @param string $type 多态类型 - * @return MorphOne - */ - public function morphOne($model, $morph = null, $type = '') - { - // 记录当前关联信息 - $model = $this->parseModel($model); - if (is_null($morph)) { - $trace = debug_backtrace(false, 2); - $morph = Loader::parseName($trace[1]['function']); - } - $type = $type ?: Loader::parseName($this->name); - if (is_array($morph)) { - list($morphType, $foreignKey) = $morph; - } else { - $morphType = $morph . '_type'; - $foreignKey = $morph . '_id'; - } - return new MorphOne($this, $model, $foreignKey, $morphType, $type); - } - - /** - * MORPH TO 关联定义 - * @access public - * @param string|array $morph 多态字段信息 - * @param array $alias 多态别名定义 - * @return MorphTo - */ - public function morphTo($morph = null, $alias = []) - { - $trace = debug_backtrace(false, 2); - $relation = Loader::parseName($trace[1]['function']); - - if (is_null($morph)) { - $morph = $relation; - } - // 记录当前关联信息 - if (is_array($morph)) { - list($morphType, $foreignKey) = $morph; - } else { - $morphType = $morph . '_type'; - $foreignKey = $morph . '_id'; - } - return new MorphTo($this, $morphType, $foreignKey, $alias, $relation); - } - - public function __call($method, $args) - { - $query = $this->db(true, false); - - if (method_exists($this, 'scope' . $method)) { - // 动态调用命名范围 - $method = 'scope' . $method; - array_unshift($args, $query); - call_user_func_array([$this, $method], $args); - return $this; - } else { - return call_user_func_array([$query, $method], $args); - } - } - - public static function __callStatic($method, $args) - { - $model = new static(); - $query = $model->db(); - - if (method_exists($model, 'scope' . $method)) { - // 动态调用命名范围 - $method = 'scope' . $method; - array_unshift($args, $query); - - call_user_func_array([$model, $method], $args); - return $query; - } else { - return call_user_func_array([$query, $method], $args); - } + return [ + 'data' => $this->data, + 'relation' => $this->relation, + ]; } /** * 修改器 设置数据对象的值 * @access public - * @param string $name 名称 - * @param mixed $value 值 + * @param string $name 名称 + * @param mixed $value 值 * @return void */ public function __set($name, $value) @@ -1985,7 +1013,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 获取器 获取数据对象的值 * @access public - * @param string $name 名称 + * @param string $name 名称 * @return mixed */ public function __get($name) @@ -1996,28 +1024,22 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 检测数据对象的值 * @access public - * @param string $name 名称 + * @param string $name 名称 * @return boolean */ public function __isset($name) { try { - if (array_key_exists($name, $this->data) || array_key_exists($name, $this->relation)) { - return true; - } else { - $this->getAttr($name); - return true; - } + return !is_null($this->getAttr($name)); } catch (InvalidArgumentException $e) { return false; } - } /** * 销毁数据对象的值 * @access public - * @param string $name 名称 + * @param string $name 名称 * @return void */ public function __unset($name) @@ -2025,17 +1047,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess unset($this->data[$name], $this->relation[$name]); } - public function __toString() - { - return $this->toJson(); - } - - // JsonSerializable - public function jsonSerialize() - { - return $this->toArray(); - } - // ArrayAccess public function offsetSet($name, $value) { @@ -2058,56 +1069,31 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } /** - * 解序列化后处理 + * 设置是否使用全局查询范围 + * @access public + * @param bool|array $use 是否启用全局查询范围(或者用数组指定查询范围名称) + * @return Query */ - public function __wakeup() + public static function useGlobalScope($use) { - $this->initialize(); + $model = new static(); + + return $model->db($use); } - /** - * 模型事件快捷方法 - * @param $callback - * @param bool $override - */ - protected static function beforeInsert($callback, $override = false) + public function __call($method, $args) { - self::event('before_insert', $callback, $override); + if ('withattr' == strtolower($method)) { + return call_user_func_array([$this, 'withAttribute'], $args); + } + + return call_user_func_array([$this->db(), $method], $args); } - protected static function afterInsert($callback, $override = false) + public static function __callStatic($method, $args) { - self::event('after_insert', $callback, $override); - } + $model = new static(); - protected static function beforeUpdate($callback, $override = false) - { - self::event('before_update', $callback, $override); + return call_user_func_array([$model->db(), $method], $args); } - - protected static function afterUpdate($callback, $override = false) - { - self::event('after_update', $callback, $override); - } - - protected static function beforeWrite($callback, $override = false) - { - self::event('before_write', $callback, $override); - } - - protected static function afterWrite($callback, $override = false) - { - self::event('after_write', $callback, $override); - } - - protected static function beforeDelete($callback, $override = false) - { - self::event('before_delete', $callback, $override); - } - - protected static function afterDelete($callback, $override = false) - { - self::event('after_delete', $callback, $override); - } - } diff --git a/thinkphp/library/think/Paginator.php b/thinkphp/library/think/Paginator.php index 5a8fa20e0..bbe63e2e3 100644 --- a/thinkphp/library/think/Paginator.php +++ b/thinkphp/library/think/Paginator.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -20,28 +20,52 @@ use Traversable; abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { - /** @var bool 是否为简洁模式 */ + /** + * 是否简洁模式 + * @var bool + */ protected $simple = false; - /** @var Collection 数据集 */ + /** + * 数据集 + * @var Collection + */ protected $items; - /** @var integer 当前页 */ + /** + * 当前页 + * @var integer + */ protected $currentPage; - /** @var integer 最后一页 */ + /** + * 最后一页 + * @var integer + */ protected $lastPage; - /** @var integer|null 数据总数 */ + /** + * 数据总数 + * @var integer|null + */ protected $total; - /** @var integer 每页的数量 */ + /** + * 每页数量 + * @var integer + */ protected $listRows; - /** @var bool 是否有下一页 */ + /** + * 是否有下一页 + * @var bool + */ protected $hasMore; - /** @var array 一些配置 */ + /** + * 分页配置 + * @var array + */ protected $options = [ 'var_page' => 'page', 'path' => '/', @@ -76,11 +100,12 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J } /** + * @access public * @param $items * @param $listRows * @param null $currentPage - * @param bool $simple * @param null $total + * @param bool $simple * @param array $options * @return Paginator */ @@ -101,7 +126,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 获取页码对应的链接 * - * @param $page + * @access protected + * @param $page * @return string */ protected function url($page) @@ -117,25 +143,29 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J $parameters = []; $path = str_replace('[PAGE]', $page, $this->options['path']); } + if (count($this->options['query']) > 0) { $parameters = array_merge($this->options['query'], $parameters); } + $url = $path; if (!empty($parameters)) { - $url .= '?' . urldecode(http_build_query($parameters, null, '&')); + $url .= '?' . http_build_query($parameters, null, '&'); } + return $url . $this->buildFragment(); } /** * 自动获取当前页码 - * @param string $varPage - * @param int $default + * @access public + * @param string $varPage + * @param int $default * @return int */ public static function getCurrentPage($varPage = 'page', $default = 1) { - $page = Request::instance()->request($varPage); + $page = Container::get('request')->param($varPage); if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) { return $page; @@ -146,11 +176,12 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 自动获取当前的path + * @access public * @return string */ public static function getCurrentPath() { - return Request::instance()->baseUrl(); + return Container::get('request')->baseUrl(); } public function total() @@ -158,6 +189,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J if ($this->simple) { throw new \DomainException('not support total'); } + return $this->total; } @@ -176,11 +208,13 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J if ($this->simple) { throw new \DomainException('not support last'); } + return $this->lastPage; } /** * 数据是否足够分页 + * @access public * @return boolean */ public function hasPages() @@ -191,6 +225,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 创建一组分页链接 * + * @access public * @param int $start * @param int $end * @return array @@ -209,18 +244,21 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 设置URL锚点 * + * @access public * @param string|null $fragment * @return $this */ public function fragment($fragment) { $this->options['fragment'] = $fragment; + return $this; } /** * 添加URL参数 * + * @access public * @param array|string $key * @param string|null $value * @return $this @@ -245,6 +283,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 构造锚点字符串 * + * @access public * @return string */ protected function buildFragment() @@ -254,6 +293,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 渲染分页html + * @access public * @return mixed */ abstract public function render(); @@ -276,14 +316,19 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * 给每个元素执行个回调 * + * @access public * @param callable $callback * @return $this */ public function each(callable $callback) { foreach ($this->items as $key => $item) { - if ($callback($item, $key) === false) { + $result = $callback($item, $key); + + if (false === $result) { break; + } elseif (!is_object($item)) { + $this->items[$key] = $result; } } @@ -292,6 +337,7 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Retrieve an external iterator + * @access public * @return Traversable An instance of an object implementing Iterator or * Traversable */ @@ -302,7 +348,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Whether a offset exists - * @param mixed $offset + * @access public + * @param mixed $offset * @return bool */ public function offsetExists($offset) @@ -312,7 +359,8 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Offset to retrieve - * @param mixed $offset + * @access public + * @param mixed $offset * @return mixed */ public function offsetGet($offset) @@ -322,8 +370,9 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Offset to set - * @param mixed $offset - * @param mixed $value + * @access public + * @param mixed $offset + * @param mixed $value */ public function offsetSet($offset, $value) { @@ -332,9 +381,10 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J /** * Offset to unset - * @param mixed $offset + * @access public + * @param mixed $offset * @return void - * @since 5.0.0 + * @since 5.0.0 */ public function offsetUnset($offset) { @@ -381,7 +431,15 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J public function __call($name, $arguments) { - return call_user_func_array([$this->getCollection(), $name], $arguments); + $collection = $this->getCollection(); + + $result = call_user_func_array([$collection, $name], $arguments); + + if ($result === $collection) { + return $this; + } + + return $result; } } diff --git a/thinkphp/library/think/Process.php b/thinkphp/library/think/Process.php index 6f3faa315..3b574db49 100644 --- a/thinkphp/library/think/Process.php +++ b/thinkphp/library/think/Process.php @@ -114,12 +114,13 @@ class Process /** * 构造方法 - * @param string $commandline 指令 - * @param string|null $cwd 工作目录 - * @param array|null $env 环境变量 - * @param string|null $input 输入 - * @param int|float|null $timeout 超时时间 - * @param array $options proc_open的选项 + * @access public + * @param string $commandline 指令 + * @param string|null $cwd 工作目录 + * @param array|null $env 环境变量 + * @param string|null $input 输入 + * @param int|float|null $timeout 超时时间 + * @param array $options proc_open的选项 * @throws \RuntimeException * @api */ @@ -132,7 +133,7 @@ class Process $this->commandline = $commandline; $this->cwd = $cwd; - if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || '\\' === DS)) { + if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || '\\' === DIRECTORY_SEPARATOR)) { $this->cwd = getcwd(); } if (null !== $env) { @@ -141,10 +142,10 @@ class Process $this->input = $input; $this->setTimeout($timeout); - $this->useFileHandles = '\\' === DS; + $this->useFileHandles = '\\' === DIRECTORY_SEPARATOR; $this->pty = false; $this->enhanceWindowsCompatibility = true; - $this->enhanceSigchildCompatibility = '\\' !== DS && $this->isSigchildEnabled(); + $this->enhanceSigchildCompatibility = '\\' !== DIRECTORY_SEPARATOR && $this->isSigchildEnabled(); $this->options = array_replace([ 'suppress_errors' => true, 'binary_pipes' => true, @@ -163,7 +164,8 @@ class Process /** * 运行指令 - * @param callback|null $callback + * @access public + * @param callback|null $callback * @return int */ public function run($callback = null) @@ -175,7 +177,8 @@ class Process /** * 运行指令 - * @param callable|null $callback + * @access public + * @param callable|null $callback * @return self * @throws \RuntimeException * @throws ProcessFailedException @@ -195,7 +198,8 @@ class Process /** * 启动进程并写到 STDIN 输入后返回。 - * @param callable|null $callback + * @access public + * @param callable|null $callback * @throws \RuntimeException * @throws \RuntimeException * @throws \LogicException @@ -216,7 +220,7 @@ class Process $commandline = $this->commandline; - if ('\\' === DS && $this->enhanceWindowsCompatibility) { + if ('\\' === DIRECTORY_SEPARATOR && $this->enhanceWindowsCompatibility) { $commandline = 'cmd /V:ON /E:ON /C "(' . $commandline . ')'; foreach ($this->processPipes->getFiles() as $offset => $filename) { $commandline .= ' ' . $offset . '>' . Utils::escapeArgument($filename); @@ -245,7 +249,8 @@ class Process /** * 重启进程 - * @param callable|null $callback + * @access public + * @param callable|null $callback * @return Process * @throws \RuntimeException * @throws \RuntimeException @@ -264,7 +269,8 @@ class Process /** * 等待要终止的进程 - * @param callable|null $callback + * @access public + * @param callable|null $callback * @return int */ public function wait($callback = null) @@ -278,8 +284,8 @@ class Process do { $this->checkTimeout(); - $running = '\\' === DS ? $this->isRunning() : $this->processPipes->areOpen(); - $close = '\\' !== DS || !$running; + $running = '\\' === DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen(); + $close = '\\' !== DIRECTORY_SEPARATOR || !$running; $this->readPipes(true, $close); } while ($running); @@ -296,6 +302,7 @@ class Process /** * 获取PID + * @access public * @return int|null * @throws \RuntimeException */ @@ -312,7 +319,8 @@ class Process /** * 将一个 POSIX 信号发送到进程中 - * @param int $signal + * @access public + * @param int $signal * @return Process */ public function signal($signal) @@ -324,6 +332,7 @@ class Process /** * 禁用从底层过程获取输出和错误输出。 + * @access public * @return Process */ public function disableOutput() @@ -342,6 +351,7 @@ class Process /** * 开启从底层过程获取输出和错误输出。 + * @access public * @return Process * @throws \RuntimeException */ @@ -358,6 +368,7 @@ class Process /** * 输出是否禁用 + * @access public * @return bool */ public function isOutputDisabled() @@ -367,9 +378,9 @@ class Process /** * 获取当前的输出管道 + * @access public * @return string * @throws \LogicException - * @throws \LogicException * @api */ public function getOutput() @@ -380,13 +391,14 @@ class Process $this->requireProcessIsStarted(__FUNCTION__); - $this->readPipes(false, '\\' === DS ? !$this->processInformation['running'] : true); + $this->readPipes(false, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true); return $this->stdout; } /** * 以增量方式返回的输出结果。 + * @access public * @return string */ public function getIncrementalOutput() @@ -408,6 +420,7 @@ class Process /** * 清空输出 + * @access public * @return Process */ public function clearOutput() @@ -420,6 +433,7 @@ class Process /** * 返回当前的错误输出的过程 (STDERR)。 + * @access public * @return string */ public function getErrorOutput() @@ -430,13 +444,14 @@ class Process $this->requireProcessIsStarted(__FUNCTION__); - $this->readPipes(false, '\\' === DS ? !$this->processInformation['running'] : true); + $this->readPipes(false, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true); return $this->stderr; } /** * 以增量方式返回 errorOutput + * @access public * @return string */ public function getIncrementalErrorOutput() @@ -458,6 +473,7 @@ class Process /** * 清空 errorOutput + * @access public * @return Process */ public function clearErrorOutput() @@ -470,6 +486,7 @@ class Process /** * 获取退出码 + * @access public * @return null|int */ public function getExitCode() @@ -485,6 +502,7 @@ class Process /** * 获取退出文本 + * @access public * @return null|string */ public function getExitCodeText() @@ -498,6 +516,7 @@ class Process /** * 检查是否成功 + * @access public * @return bool */ public function isSuccessful() @@ -507,6 +526,7 @@ class Process /** * 是否未捕获的信号已被终止子进程 + * @access public * @return bool */ public function hasBeenSignaled() @@ -524,6 +544,7 @@ class Process /** * 返回导致子进程终止其执行的数。 + * @access public * @return int */ public function getTermSignal() @@ -541,6 +562,7 @@ class Process /** * 检查子进程信号是否已停止 + * @access public * @return bool */ public function hasBeenStopped() @@ -554,6 +576,7 @@ class Process /** * 返回导致子进程停止其执行的数。 + * @access public * @return int */ public function getStopSignal() @@ -567,6 +590,7 @@ class Process /** * 检查是否正在运行 + * @access public * @return bool */ public function isRunning() @@ -582,6 +606,7 @@ class Process /** * 检查是否已开始 + * @access public * @return bool */ public function isStarted() @@ -591,6 +616,7 @@ class Process /** * 检查是否已终止 + * @access public * @return bool */ public function isTerminated() @@ -602,6 +628,7 @@ class Process /** * 获取当前的状态 + * @access public * @return string */ public function getStatus() @@ -613,11 +640,12 @@ class Process /** * 终止进程 + * @access public */ public function stop() { if ($this->isRunning()) { - if ('\\' === DS && !$this->isSigchildEnabled()) { + if ('\\' === DIRECTORY_SEPARATOR && !$this->isSigchildEnabled()) { exec(sprintf('taskkill /F /T /PID %d 2>&1', $this->getPid()), $output, $exitCode); if ($exitCode > 0) { throw new \RuntimeException('Unable to kill the process'); @@ -642,6 +670,7 @@ class Process /** * 添加一行输出 + * @access public * @param string $line */ public function addOutput($line) @@ -652,6 +681,7 @@ class Process /** * 添加一行错误输出 + * @access public * @param string $line */ public function addErrorOutput($line) @@ -662,6 +692,7 @@ class Process /** * 获取被执行的指令 + * @access public * @return string */ public function getCommandLine() @@ -671,6 +702,7 @@ class Process /** * 设置指令 + * @access public * @param string $commandline * @return self */ @@ -683,6 +715,7 @@ class Process /** * 获取超时时间 + * @access public * @return float|null */ public function getTimeout() @@ -692,6 +725,7 @@ class Process /** * 获取idle超时时间 + * @access public * @return float|null */ public function getIdleTimeout() @@ -701,7 +735,8 @@ class Process /** * 设置超时时间 - * @param int|float|null $timeout + * @access public + * @param int|float|null $timeout * @return self */ public function setTimeout($timeout) @@ -713,7 +748,8 @@ class Process /** * 设置idle超时时间 - * @param int|float|null $timeout + * @access public + * @param int|float|null $timeout * @return self */ public function setIdleTimeout($timeout) @@ -729,12 +765,13 @@ class Process /** * 设置TTY - * @param bool $tty + * @access public + * @param bool $tty * @return self */ public function setTty($tty) { - if ('\\' === DS && $tty) { + if ('\\' === DIRECTORY_SEPARATOR && $tty) { throw new \RuntimeException('TTY mode is not supported on Windows platform.'); } if ($tty && (!file_exists('/dev/tty') || !is_readable('/dev/tty'))) { @@ -748,6 +785,7 @@ class Process /** * 检查是否是tty模式 + * @access public * @return bool */ public function isTty() @@ -757,7 +795,8 @@ class Process /** * 设置pty模式 - * @param bool $bool + * @access public + * @param bool $bool * @return self */ public function setPty($bool) @@ -769,6 +808,7 @@ class Process /** * 是否是pty模式 + * @access public * @return bool */ public function isPty() @@ -778,6 +818,7 @@ class Process /** * 获取工作目录 + * @access public * @return string|null */ public function getWorkingDirectory() @@ -791,7 +832,8 @@ class Process /** * 设置工作目录 - * @param string $cwd + * @access public + * @param string $cwd * @return self */ public function setWorkingDirectory($cwd) @@ -803,6 +845,7 @@ class Process /** * 获取环境变量 + * @access public * @return array */ public function getEnv() @@ -812,7 +855,8 @@ class Process /** * 设置环境变量 - * @param array $env + * @access public + * @param array $env * @return self */ public function setEnv(array $env) @@ -831,6 +875,7 @@ class Process /** * 获取输入 + * @access public * @return null|string */ public function getInput() @@ -840,7 +885,8 @@ class Process /** * 设置输入 - * @param mixed $input + * @access public + * @param mixed $input * @return self */ public function setInput($input) @@ -856,6 +902,7 @@ class Process /** * 获取proc_open的选项 + * @access public * @return array */ public function getOptions() @@ -865,7 +912,8 @@ class Process /** * 设置proc_open的选项 - * @param array $options + * @access public + * @param array $options * @return self */ public function setOptions(array $options) @@ -877,6 +925,7 @@ class Process /** * 是否兼容windows + * @access public * @return bool */ public function getEnhanceWindowsCompatibility() @@ -886,7 +935,8 @@ class Process /** * 设置是否兼容windows - * @param bool $enhance + * @access public + * @param bool $enhance * @return self */ public function setEnhanceWindowsCompatibility($enhance) @@ -898,6 +948,7 @@ class Process /** * 返回是否 sigchild 兼容模式激活 + * @access public * @return bool */ public function getEnhanceSigchildCompatibility() @@ -907,7 +958,8 @@ class Process /** * 激活 sigchild 兼容性模式。 - * @param bool $enhance + * @access public + * @param bool $enhance * @return self */ public function setEnhanceSigchildCompatibility($enhance) @@ -941,6 +993,7 @@ class Process /** * 是否支持pty + * @access public * @return bool */ public static function isPtySupported() @@ -951,7 +1004,7 @@ class Process return $result; } - if ('\\' === DS) { + if ('\\' === DIRECTORY_SEPARATOR) { return $result = false; } @@ -967,11 +1020,12 @@ class Process /** * 创建所需的 proc_open 的描述符 + * @access private * @return array */ private function getDescriptors() { - if ('\\' === DS) { + if ('\\' === DIRECTORY_SEPARATOR) { $this->processPipes = WindowsPipes::create($this, $this->input); } else { $this->processPipes = UnixPipes::create($this, $this->input); @@ -990,7 +1044,8 @@ class Process /** * 建立 wait () 使用的回调。 - * @param callable|null $callback + * @access protected + * @param callable|null $callback * @return callable */ protected function buildCallback($callback) @@ -1013,6 +1068,7 @@ class Process /** * 更新状态 + * @access protected * @param bool $blocking */ protected function updateStatus($blocking) @@ -1024,7 +1080,7 @@ class Process $this->processInformation = proc_get_status($this->process); $this->captureExitCode(); - $this->readPipes($blocking, '\\' === DS ? !$this->processInformation['running'] : true); + $this->readPipes($blocking, '\\' === DIRECTORY_SEPARATOR ? !$this->processInformation['running'] : true); if (!$this->processInformation['running']) { $this->close(); @@ -1033,6 +1089,7 @@ class Process /** * 是否开启 '--enable-sigchild' + * @access protected * @return bool */ protected function isSigchildEnabled() @@ -1053,7 +1110,8 @@ class Process /** * 验证是否超时 - * @param int|float|null $timeout + * @access private + * @param int|float|null $timeout * @return float|null */ private function validateTimeout($timeout) @@ -1071,8 +1129,9 @@ class Process /** * 读取pipes - * @param bool $blocking - * @param bool $close + * @access private + * @param bool $blocking + * @param bool $close */ private function readPipes($blocking, $close) { @@ -1100,6 +1159,7 @@ class Process /** * 关闭资源 + * @access private * @return int 退出码 */ private function close() @@ -1146,8 +1206,9 @@ class Process /** * 将一个 POSIX 信号发送到进程中。 - * @param int $signal - * @param bool $throwException + * @access private + * @param int $signal + * @param bool $throwException * @return bool */ private function doSignal($signal, $throwException) @@ -1183,7 +1244,8 @@ class Process /** * 确保进程已经开启 - * @param string $functionName + * @access private + * @param string $functionName */ private function requireProcessIsStarted($functionName) { @@ -1194,7 +1256,8 @@ class Process /** * 确保进程已经终止 - * @param string $functionName + * @access private + * @param string $functionName */ private function requireProcessIsTerminated($functionName) { diff --git a/thinkphp/library/think/Request.php b/thinkphp/library/think/Request.php index ffd001f27..f6529d03c 100644 --- a/thinkphp/library/think/Request.php +++ b/thinkphp/library/think/Request.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,81 +11,215 @@ namespace think; +use think\facade\Cookie; +use think\facade\Session; + class Request { /** - * @var object 对象实例 + * 配置参数 + * @var array */ - protected static $instance; + protected $config = [ + // 表单请求类型伪装变量 + 'var_method' => '_method', + // 表单ajax伪装变量 + 'var_ajax' => '_ajax', + // 表单pjax伪装变量 + 'var_pjax' => '_pjax', + // PATHINFO变量名 用于兼容模式 + 'var_pathinfo' => 's', + // 兼容PATH_INFO获取 + 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], + // 默认全局过滤方法 用逗号分隔多个 + 'default_filter' => '', + // 域名根,如thinkphp.cn + 'url_domain_root' => '', + // HTTPS代理标识 + 'https_agent_name' => '', + // IP代理获取标识 + 'http_agent_ip' => 'HTTP_X_REAL_IP', + // URL伪静态后缀 + 'url_html_suffix' => 'html', + ]; - protected $method; /** - * @var string 域名(含协议和端口) + * 请求类型 + * @var string + */ + protected $method; + + /** + * 主机名(含端口) + * @var string + */ + protected $host; + + /** + * 域名(含协议及端口) + * @var string */ protected $domain; /** - * @var string URL地址 + * 子域名 + * @var string + */ + protected $subDomain; + + /** + * 泛域名 + * @var string + */ + protected $panDomain; + + /** + * 当前URL地址 + * @var string */ protected $url; /** - * @var string 基础URL + * 基础URL + * @var string */ protected $baseUrl; /** - * @var string 当前执行的文件 + * 当前执行的文件 + * @var string */ protected $baseFile; /** - * @var string 访问的ROOT地址 + * 访问的ROOT地址 + * @var string */ protected $root; /** - * @var string pathinfo + * pathinfo + * @var string */ protected $pathinfo; /** - * @var string pathinfo(不含后缀) + * pathinfo(不含后缀) + * @var string */ protected $path; /** - * @var array 当前路由信息 + * 当前路由信息 + * @var array */ protected $routeInfo = []; /** - * @var array 当前调度信息 + * 当前调度信息 + * @var \think\route\Dispatch + */ + protected $dispatch; + + /** + * 当前模块名 + * @var string */ - protected $dispatch = []; protected $module; + + /** + * 当前控制器名 + * @var string + */ protected $controller; + + /** + * 当前操作名 + * @var string + */ protected $action; - // 当前语言集 + + /** + * 当前语言集 + * @var string + */ protected $langset; /** - * @var array 请求参数 + * 当前请求参数 + * @var array */ - protected $param = []; - protected $get = []; - protected $post = []; - protected $request = []; - protected $route = []; - protected $put; - protected $session = []; - protected $file = []; - protected $cookie = []; - protected $server = []; - protected $header = []; + protected $param = []; /** - * @var array 资源类型 + * 当前GET参数 + * @var array + */ + protected $get = []; + + /** + * 当前POST参数 + * @var array + */ + protected $post = []; + + /** + * 当前REQUEST参数 + * @var array + */ + protected $request = []; + + /** + * 当前ROUTE参数 + * @var array + */ + protected $route = []; + + /** + * 当前PUT参数 + * @var array + */ + protected $put; + + /** + * 当前SESSION参数 + * @var array + */ + protected $session = []; + + /** + * 当前FILE参数 + * @var array + */ + protected $file = []; + + /** + * 当前COOKIE参数 + * @var array + */ + protected $cookie = []; + + /** + * 当前SERVER参数 + * @var array + */ + protected $server = []; + + /** + * 当前ENV参数 + * @var array + */ + protected $env = []; + + /** + * 当前HEADER参数 + * @var array + */ + protected $header = []; + + /** + * 资源类型定义 + * @var array */ protected $mimeType = [ 'xml' => 'application/xml,text/xml,application/x-xml', @@ -102,101 +236,143 @@ class Request 'html' => 'text/html,application/xhtml+xml,*/*', ]; + /** + * 当前请求内容 + * @var string + */ protected $content; - // 全局过滤规则 + /** + * 全局过滤规则 + * @var array + */ protected $filter; - // Hook扩展方法 - protected static $hook = []; - // 绑定的属性 - protected $bind = []; - // php://input + + /** + * 扩展方法 + * @var array + */ + protected $hook = []; + + /** + * php://input内容 + * @var string + */ protected $input; - // 请求缓存 + + /** + * 请求缓存 + * @var array + */ protected $cache; - // 缓存是否检查 + + /** + * 缓存是否检查 + * @var bool + */ protected $isCheckCache; /** - * 构造函数 - * @access protected - * @param array $options 参数 + * 请求安全Key + * @var string */ - protected function __construct($options = []) + protected $secureKey; + + /** + * 是否合并Param + * @var bool + */ + protected $mergeParam = false; + + /** + * 架构函数 + * @access public + * @param array $options 参数 + */ + public function __construct(array $options = []) { - foreach ($options as $name => $item) { - if (property_exists($this, $name)) { - $this->$name = $item; - } - } - if (is_null($this->filter)) { - $this->filter = Config::get('default_filter'); - } + $this->init($options); + // 保存 php://input $this->input = file_get_contents('php://input'); } + public function init(array $options = []) + { + $this->config = array_merge($this->config, $options); + + if (is_null($this->filter) && !empty($this->config['default_filter'])) { + $this->filter = $this->config['default_filter']; + } + } + + public function config($name = null) + { + if (is_null($name)) { + return $this->config; + } + return isset($this->config[$name]) ? $this->config[$name] : null; + } + + public static function __make(App $app, Config $config) + { + $request = new static($config->pull('app')); + + $request->server = $_SERVER; + $request->env = $app['env']->get(); + + return $request; + } + public function __call($method, $args) { - if (array_key_exists($method, self::$hook)) { + if (array_key_exists($method, $this->hook)) { array_unshift($args, $this); - return call_user_func_array(self::$hook[$method], $args); - } else { - throw new Exception('method not exists:' . __CLASS__ . '->' . $method); + return call_user_func_array($this->hook[$method], $args); } + + throw new Exception('method not exists:' . static::class . '->' . $method); } /** * Hook 方法注入 * @access public - * @param string|array $method 方法名 - * @param mixed $callback callable + * @param string|array $method 方法名 + * @param mixed $callback callable * @return void */ - public static function hook($method, $callback = null) + public function hook($method, $callback = null) { if (is_array($method)) { - self::$hook = array_merge(self::$hook, $method); + $this->hook = array_merge($this->hook, $method); } else { - self::$hook[$method] = $callback; + $this->hook[$method] = $callback; } } - /** - * 初始化 - * @access public - * @param array $options 参数 - * @return \think\Request - */ - public static function instance($options = []) - { - if (is_null(self::$instance)) { - self::$instance = new static($options); - } - return self::$instance; - } - /** * 创建一个URL请求 * @access public - * @param string $uri URL地址 - * @param string $method 请求类型 - * @param array $params 请求参数 - * @param array $cookie - * @param array $files - * @param array $server - * @param string $content + * @param string $uri URL地址 + * @param string $method 请求类型 + * @param array $params 请求参数 + * @param array $cookie + * @param array $files + * @param array $server + * @param string $content * @return \think\Request */ - public static function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = [], $content = null) + public function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = [], $content = null) { $server['PATH_INFO'] = ''; $server['REQUEST_METHOD'] = strtoupper($method); $info = parse_url($uri); + if (isset($info['host'])) { $server['SERVER_NAME'] = $info['host']; $server['HTTP_HOST'] = $info['host']; } + if (isset($info['scheme'])) { if ('https' === $info['scheme']) { $server['HTTPS'] = 'on'; @@ -206,27 +382,34 @@ class Request $server['SERVER_PORT'] = 80; } } + if (isset($info['port'])) { $server['SERVER_PORT'] = $info['port']; $server['HTTP_HOST'] = $server['HTTP_HOST'] . ':' . $info['port']; } + if (isset($info['user'])) { $server['PHP_AUTH_USER'] = $info['user']; } + if (isset($info['pass'])) { $server['PHP_AUTH_PW'] = $info['pass']; } + if (!isset($info['path'])) { $info['path'] = '/'; } - $options = []; + + $options = []; + $queryString = ''; + $options[strtolower($method)] = $params; - $queryString = ''; + if (isset($info['query'])) { parse_str(html_entity_decode($info['query']), $query); if (!empty($params)) { $params = array_replace($query, $params); - $queryString = http_build_query($query, '', '&'); + $queryString = http_build_query($params, '', '&'); } else { $params = $query; $queryString = $info['query']; @@ -234,6 +417,7 @@ class Request } elseif (!empty($params)) { $queryString = http_build_query($params, '', '&'); } + if ($queryString) { parse_str($queryString, $get); $options['get'] = isset($options['get']) ? array_merge($get, $options['get']) : $get; @@ -251,123 +435,239 @@ class Request $options['method'] = $server['REQUEST_METHOD']; $options['domain'] = isset($info['scheme']) ? $info['scheme'] . '://' . $server['HTTP_HOST'] : ''; $options['content'] = $content; - self::$instance = new self($options); - return self::$instance; - } - /** - * 设置或获取当前包含协议的域名 - * @access public - * @param string $domain 域名 - * @return string - */ - public function domain($domain = null) - { - if (!is_null($domain)) { - $this->domain = $domain; - return $this; - } elseif (!$this->domain) { - $this->domain = $this->scheme() . '://' . $this->host(); + $request = new static(); + foreach ($options as $name => $item) { + if (property_exists($request, $name)) { + $request->$name = $item; + } } - return $this->domain; + + return $request; } /** - * 设置或获取当前完整URL 包括QUERY_STRING + * 获取当前包含协议、端口的域名 * @access public - * @param string|true $url URL地址 true 带域名获取 + * @param bool $port 是否需要去除端口号 * @return string */ - public function url($url = null) + public function domain($port = false) { - if (!is_null($url) && true !== $url) { - $this->url = $url; - return $this; - } elseif (!$this->url) { - if (IS_CLI) { + return $this->scheme() . '://' . $this->host($port); + } + + /** + * 获取当前根域名 + * @access public + * @return string + */ + public function rootDomain() + { + $root = $this->config['url_domain_root']; + + if (!$root) { + $item = explode('.', $this->host(true)); + $count = count($item); + $root = $count > 1 ? $item[$count - 2] . '.' . $item[$count - 1] : $item[0]; + } + + return $root; + } + + /** + * 获取当前子域名 + * @access public + * @return string + */ + public function subDomain() + { + if (is_null($this->subDomain)) { + // 获取当前主域名 + $rootDomain = $this->config['url_domain_root']; + + if ($rootDomain) { + // 配置域名根 例如 thinkphp.cn 163.com.cn 如果是国家级域名 com.cn net.cn 之类的域名需要配置 + $domain = explode('.', rtrim(stristr($this->host(true), $rootDomain, true), '.')); + } else { + $domain = explode('.', $this->host(true), -2); + } + + $this->subDomain = implode('.', $domain); + } + + return $this->subDomain; + } + + /** + * 设置当前泛域名的值 + * @access public + * @param string $domain 域名 + * @return $this + */ + public function setPanDomain($domain) + { + $this->panDomain = $domain; + return $this; + } + + /** + * 获取当前泛域名的值 + * @access public + * @return string + */ + public function panDomain() + { + return $this->panDomain; + } + + /** + * 设置当前完整URL 包括QUERY_STRING + * @access public + * @param string $url URL + * @return $this + */ + public function setUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * 获取当前完整URL 包括QUERY_STRING + * @access public + * @param bool $complete 是否包含域名 + * @return string + */ + public function url($complete = false) + { + if (!$this->url) { + if ($this->isCli()) { $this->url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; - } elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) { - $this->url = $_SERVER['HTTP_X_REWRITE_URL']; - } elseif (isset($_SERVER['REQUEST_URI'])) { - $this->url = $_SERVER['REQUEST_URI']; - } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { - $this->url = $_SERVER['ORIG_PATH_INFO'] . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''); + } elseif ($this->server('HTTP_X_REWRITE_URL')) { + $this->url = $this->server('HTTP_X_REWRITE_URL'); + } elseif ($this->server('REQUEST_URI')) { + $this->url = $this->server('REQUEST_URI'); + } elseif ($this->server('ORIG_PATH_INFO')) { + $this->url = $this->server('ORIG_PATH_INFO') . (!empty($this->server('QUERY_STRING')) ? '?' . $this->server('QUERY_STRING') : ''); } else { $this->url = ''; } } - return true === $url ? $this->domain() . $this->url : $this->url; + + return $complete ? $this->domain() . $this->url : $this->url; } /** - * 设置或获取当前URL 不含QUERY_STRING + * 设置当前完整URL 不包括QUERY_STRING * @access public - * @param string $url URL地址 - * @return string + * @param string $url URL + * @return $this */ - public function baseUrl($url = null) + public function setBaseUrl($url) { - if (!is_null($url) && true !== $url) { - $this->baseUrl = $url; - return $this; - } elseif (!$this->baseUrl) { + $this->baseUrl = $url; + return $this; + } + + /** + * 获取当前URL 不含QUERY_STRING + * @access public + * @param bool $domain 是否包含域名 + * @return string|$this + */ + public function baseUrl($domain = false) + { + if (!$this->baseUrl) { $str = $this->url(); $this->baseUrl = strpos($str, '?') ? strstr($str, '?', true) : $str; } - return true === $url ? $this->domain() . $this->baseUrl : $this->baseUrl; + + return $domain ? $this->domain() . $this->baseUrl : $this->baseUrl; } /** * 设置或获取当前执行的文件 SCRIPT_NAME * @access public - * @param string $file 当前执行的文件 - * @return string + * @param bool $domain 是否包含域名 + * @return string|$this */ - public function baseFile($file = null) + public function baseFile($domain = false) { - if (!is_null($file) && true !== $file) { - $this->baseFile = $file; - return $this; - } elseif (!$this->baseFile) { + if (!$this->baseFile) { $url = ''; - if (!IS_CLI) { - $script_name = basename($_SERVER['SCRIPT_FILENAME']); - if (basename($_SERVER['SCRIPT_NAME']) === $script_name) { - $url = $_SERVER['SCRIPT_NAME']; - } elseif (basename($_SERVER['PHP_SELF']) === $script_name) { - $url = $_SERVER['PHP_SELF']; - } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $script_name) { - $url = $_SERVER['ORIG_SCRIPT_NAME']; - } elseif (($pos = strpos($_SERVER['PHP_SELF'], '/' . $script_name)) !== false) { - $url = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $script_name; - } elseif (isset($_SERVER['DOCUMENT_ROOT']) && strpos($_SERVER['SCRIPT_FILENAME'], $_SERVER['DOCUMENT_ROOT']) === 0) { - $url = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME'])); + if (!$this->isCli()) { + $script_name = basename($this->server('SCRIPT_FILENAME')); + if (basename($this->server('SCRIPT_NAME')) === $script_name) { + $url = $this->server('SCRIPT_NAME'); + } elseif (basename($this->server('PHP_SELF')) === $script_name) { + $url = $this->server('PHP_SELF'); + } elseif (basename($this->server('ORIG_SCRIPT_NAME')) === $script_name) { + $url = $this->server('ORIG_SCRIPT_NAME'); + } elseif (($pos = strpos($this->server('PHP_SELF'), '/' . $script_name)) !== false) { + $url = substr($this->server('SCRIPT_NAME'), 0, $pos) . '/' . $script_name; + } elseif ($this->server('DOCUMENT_ROOT') && strpos($this->server('SCRIPT_FILENAME'), $this->server('DOCUMENT_ROOT')) === 0) { + $url = str_replace('\\', '/', str_replace($this->server('DOCUMENT_ROOT'), '', $this->server('SCRIPT_FILENAME'))); } } $this->baseFile = $url; } - return true === $file ? $this->domain() . $this->baseFile : $this->baseFile; + + return $domain ? $this->domain() . $this->baseFile : $this->baseFile; } /** - * 设置或获取URL访问根地址 + * 设置URL访问根地址 * @access public - * @param string $url URL地址 - * @return string + * @param string $url URL地址 + * @return string|$this */ - public function root($url = null) + public function setRoot($url = null) { - if (!is_null($url) && true !== $url) { - $this->root = $url; - return $this; - } elseif (!$this->root) { + $this->root = $url; + return $this; + } + + /** + * 获取URL访问根地址 + * @access public + * @param bool $domain 是否包含域名 + * @return string|$this + */ + public function root($domain = false) + { + if (!$this->root) { $file = $this->baseFile(); if ($file && 0 !== strpos($this->url(), $file)) { $file = str_replace('\\', '/', dirname($file)); } $this->root = rtrim($file, '/'); } - return true === $url ? $this->domain() . $this->root : $this->root; + + return $domain ? $this->domain() . $this->root : $this->root; + } + + /** + * 获取URL访问根目录 + * @access public + * @return string + */ + public function rootUrl() + { + $base = $this->root(); + $root = strpos($base, '.') ? ltrim(dirname($base), DIRECTORY_SEPARATOR) : $base; + + if ('' != $root) { + $root = '/' . ltrim($root, '/'); + } + + return $root; + } + + public function setPathinfo($pathinfo) + { + $this->pathinfo = $pathinfo; + return $this; } /** @@ -378,27 +678,33 @@ class Request public function pathinfo() { if (is_null($this->pathinfo)) { - if (isset($_GET[Config::get('var_pathinfo')])) { + if (isset($_GET[$this->config['var_pathinfo']])) { // 判断URL里面是否有兼容模式参数 - $_SERVER['PATH_INFO'] = $_GET[Config::get('var_pathinfo')]; - unset($_GET[Config::get('var_pathinfo')]); - } elseif (IS_CLI) { + $pathinfo = $_GET[$this->config['var_pathinfo']]; + unset($_GET[$this->config['var_pathinfo']]); + } elseif ($this->isCli()) { // CLI模式下 index.php module/controller/action/params/... - $_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; + $pathinfo = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; + } elseif ('cli-server' == PHP_SAPI) { + $pathinfo = strpos($this->server('REQUEST_URI'), '?') ? strstr($this->server('REQUEST_URI'), '?', true) : $this->server('REQUEST_URI'); + } elseif ($this->server('PATH_INFO')) { + $pathinfo = $this->server('PATH_INFO'); } // 分析PATHINFO信息 - if (!isset($_SERVER['PATH_INFO'])) { - foreach (Config::get('pathinfo_fetch') as $type) { - if (!empty($_SERVER[$type])) { - $_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ? - substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type]; + if (!isset($pathinfo)) { + foreach ($this->config['pathinfo_fetch'] as $type) { + if ($this->server($type)) { + $pathinfo = (0 === strpos($this->server($type), $this->server('SCRIPT_NAME'))) ? + substr($this->server($type), strlen($this->server('SCRIPT_NAME'))) : $this->server($type); break; } } } - $this->pathinfo = empty($_SERVER['PATH_INFO']) ? '/' : ltrim($_SERVER['PATH_INFO'], '/'); + + $this->pathinfo = empty($pathinfo) || '/' == $pathinfo ? '' : ltrim($pathinfo, '/'); } + return $this->pathinfo; } @@ -410,8 +716,9 @@ class Request public function path() { if (is_null($this->path)) { - $suffix = Config::get('url_html_suffix'); + $suffix = $this->config['url_html_suffix']; $pathinfo = $this->pathinfo(); + if (false === $suffix) { // 禁止伪静态访问 $this->path = $pathinfo; @@ -423,6 +730,7 @@ class Request $this->path = preg_replace('/\.' . $this->ext() . '$/i', '', $pathinfo); } } + return $this->path; } @@ -439,12 +747,12 @@ class Request /** * 获取当前请求的时间 * @access public - * @param bool $float 是否使用浮点类型 + * @param bool $float 是否使用浮点类型 * @return integer|float */ public function time($float = false) { - return $float ? $_SERVER['REQUEST_TIME_FLOAT'] : $_SERVER['REQUEST_TIME']; + return $float ? $this->server('REQUEST_TIME_FLOAT') : $this->server('REQUEST_TIME'); } /** @@ -455,6 +763,7 @@ class Request public function type() { $accept = $this->server('HTTP_ACCEPT'); + if (empty($accept)) { return false; } @@ -467,14 +776,15 @@ class Request } } } + return false; } /** * 设置资源类型 * @access public - * @param string|array $type 资源类型名 - * @param string $val 资源类型 + * @param string|array $type 资源类型名 + * @param string $val 资源类型 * @return void */ public function mimeType($type, $val = '') @@ -489,24 +799,31 @@ class Request /** * 当前的请求类型 * @access public - * @param bool $method true 获取原始请求类型 + * @param bool $origin 是否获取原始请求类型 * @return string */ - public function method($method = false) + public function method($origin = false) { - if (true === $method) { + if ($origin) { // 获取原始请求类型 - return IS_CLI ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']); + return $this->server('REQUEST_METHOD') ?: 'GET'; } elseif (!$this->method) { - if (isset($_POST[Config::get('var_method')])) { - $this->method = strtoupper($_POST[Config::get('var_method')]); - $this->{$this->method}($_POST); - } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { - $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); + if (isset($_POST[$this->config['var_method']])) { + $method = strtolower($_POST[$this->config['var_method']]); + if (in_array($method, ['get', 'post', 'put', 'patch', 'delete'])) { + $this->method = strtoupper($method); + $this->{$method} = $_POST; + } else { + $this->method = 'POST'; + } + unset($_POST[$this->config['var_method']]); + } elseif ($this->server('HTTP_X_HTTP_METHOD_OVERRIDE')) { + $this->method = strtoupper($this->server('HTTP_X_HTTP_METHOD_OVERRIDE')); } else { - $this->method = IS_CLI ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']); + $this->method = $this->server('REQUEST_METHOD') ?: 'GET'; } } + return $this->method; } @@ -601,17 +918,18 @@ class Request } /** - * 获取获取当前请求的参数 + * 获取当前请求的参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param mixed $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function param($name = '', $default = null, $filter = '') { - if (empty($this->param)) { + if (!$this->mergeParam) { $method = $this->method(true); + // 自动获取请求变量 switch ($method) { case 'POST': @@ -625,41 +943,55 @@ class Request default: $vars = []; } + // 当前请求参数和URL地址中的参数合并 - $this->param = array_merge($this->get(false), $vars, $this->route(false)); + $this->param = array_merge($this->param, $this->get(false), $vars, $this->route(false)); + + $this->mergeParam = true; } + if (true === $name) { // 获取包含文件上传信息的数组 $file = $this->file(); $data = is_array($file) ? array_merge($this->param, $file) : $this->param; + return $this->input($data, '', $default, $filter); } + return $this->input($this->param, $name, $default, $filter); } /** - * 设置获取获取路由参数 + * 设置路由变量 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param array $route 路由变量 + * @return $this + */ + public function setRouteVars(array $route) + { + $this->route = array_merge($this->route, $route); + return $this; + } + + /** + * 获取路由参数 + * @access public + * @param string|false $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function route($name = '', $default = null, $filter = '') { - if (is_array($name)) { - $this->param = []; - return $this->route = array_merge($this->route, $name); - } return $this->input($this->route, $name, $default, $filter); } /** - * 设置获取获取GET参数 + * 获取GET参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|false $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function get($name = '', $default = null, $filter = '') @@ -667,70 +999,62 @@ class Request if (empty($this->get)) { $this->get = $_GET; } - if (is_array($name)) { - $this->param = []; - return $this->get = array_merge($this->get, $name); - } + return $this->input($this->get, $name, $default, $filter); } /** - * 设置获取获取POST参数 + * 获取POST参数 * @access public - * @param string $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|false $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function post($name = '', $default = null, $filter = '') { if (empty($this->post)) { - $content = $this->input; - if (empty($_POST) && false !== strpos($this->contentType(), 'application/json')) { - $this->post = (array) json_decode($content, true); - } else { - $this->post = $_POST; - } - } - if (is_array($name)) { - $this->param = []; - return $this->post = array_merge($this->post, $name); + $this->post = !empty($_POST) ? $_POST : $this->getInputData($this->input); } + return $this->input($this->post, $name, $default, $filter); } /** - * 设置获取获取PUT参数 + * 获取PUT参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|false $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function put($name = '', $default = null, $filter = '') { if (is_null($this->put)) { - $content = $this->input; - if (false !== strpos($this->contentType(), 'application/json')) { - $this->put = (array) json_decode($content, true); - } else { - parse_str($content, $this->put); - } - } - if (is_array($name)) { - $this->param = []; - return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name); + $this->put = $this->getInputData($this->input); } return $this->input($this->put, $name, $default, $filter); } + protected function getInputData($content) + { + if (false !== strpos($this->contentType(), 'application/json') || 0 === strpos($content, '{"')) { + return (array) json_decode($content, true); + } elseif (strpos($content, '=')) { + parse_str($content, $data); + return $data; + } + + return []; + } + /** - * 设置获取获取DELETE参数 + * 获取DELETE参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|false $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function delete($name = '', $default = null, $filter = '') @@ -739,11 +1063,11 @@ class Request } /** - * 设置获取获取PATCH参数 + * 获取PATCH参数 * @access public - * @param string|array $name 变量名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤方法 + * @param string|false $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function patch($name = '', $default = null, $filter = '') @@ -753,9 +1077,10 @@ class Request /** * 获取request变量 - * @param string $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @access public + * @param string|false $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function request($name = '', $default = null, $filter = '') @@ -763,38 +1088,38 @@ class Request if (empty($this->request)) { $this->request = $_REQUEST; } - if (is_array($name)) { - $this->param = []; - return $this->request = array_merge($this->request, $name); - } + return $this->input($this->request, $name, $default, $filter); } /** * 获取session数据 * @access public - * @param string|array $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @param string $name 数据名称 + * @param string $default 默认值 * @return mixed */ - public function session($name = '', $default = null, $filter = '') + public function session($name = '', $default = null) { if (empty($this->session)) { $this->session = Session::get(); } - if (is_array($name)) { - return $this->session = array_merge($this->session, $name); + + if ('' === $name) { + return $this->session; } - return $this->input($this->session, $name, $default, $filter); + + $data = $this->getData($this->session, $name); + + return is_null($data) ? $default : $data; } /** * 获取cookie参数 * @access public - * @param string|array $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @param string $name 变量名 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 * @return mixed */ public function cookie($name = '', $default = null, $filter = '') @@ -802,9 +1127,8 @@ class Request if (empty($this->cookie)) { $this->cookie = Cookie::get(); } - if (is_array($name)) { - return $this->cookie = array_merge($this->cookie, $name); - } elseif (!empty($name)) { + + if (!empty($name)) { $data = Cookie::has($name) ? Cookie::get($name) : $default; } else { $data = $this->cookie; @@ -819,32 +1143,32 @@ class Request } else { $this->filterValue($data, $name, $filter); } + return $data; } /** * 获取server参数 * @access public - * @param string|array $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @param string $name 数据名称 + * @param string $default 默认值 * @return mixed */ - public function server($name = '', $default = null, $filter = '') + public function server($name = '', $default = null) { - if (empty($this->server)) { - $this->server = $_SERVER; + if (empty($name)) { + return $this->server; + } else { + $name = strtoupper($name); } - if (is_array($name)) { - return $this->server = array_merge($this->server, $name); - } - return $this->input($this->server, false === $name ? false : strtoupper($name), $default, $filter); + + return isset($this->server[$name]) ? $this->server[$name] : $default; } /** * 获取上传的文件信息 * @access public - * @param string|array $name 名称 + * @param string $name 名称 * @return null|array|\think\File */ public function file($name = '') @@ -852,43 +1176,16 @@ class Request if (empty($this->file)) { $this->file = isset($_FILES) ? $_FILES : []; } - if (is_array($name)) { - return $this->file = array_merge($this->file, $name); - } + $files = $this->file; if (!empty($files)) { - // 处理上传文件 - $array = []; - foreach ($files as $key => $file) { - if (is_array($file['name'])) { - $item = []; - $keys = array_keys($file); - $count = count($file['name']); - for ($i = 0; $i < $count; $i++) { - if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) { - continue; - } - $temp['key'] = $key; - foreach ($keys as $_key) { - $temp[$_key] = $file[$_key][$i]; - } - $item[] = (new File($temp['tmp_name']))->setUploadInfo($temp); - } - $array[$key] = $item; - } else { - if ($file instanceof File) { - $array[$key] = $file; - } else { - if (empty($file['tmp_name']) || !is_file($file['tmp_name'])) { - continue; - } - $array[$key] = (new File($file['tmp_name']))->setUploadInfo($file); - } - } - } if (strpos($name, '.')) { list($name, $sub) = explode('.', $name); } + + // 处理上传文件 + $array = $this->dealUploadFile($files, $name); + if ('' === $name) { // 获取全部文件 return $array; @@ -898,33 +1195,96 @@ class Request return $array[$name]; } } + return; } + protected function dealUploadFile($files, $name) + { + $array = []; + foreach ($files as $key => $file) { + if ($file instanceof File) { + $array[$key] = $file; + } elseif (is_array($file['name'])) { + $item = []; + $keys = array_keys($file); + $count = count($file['name']); + + for ($i = 0; $i < $count; $i++) { + if ($file['error'][$i] > 0) { + if ($name == $key) { + $this->throwUploadFileError($file['error'][$i]); + } else { + continue; + } + } + + $temp['key'] = $key; + + foreach ($keys as $_key) { + $temp[$_key] = $file[$_key][$i]; + } + + $item[] = (new File($temp['tmp_name']))->setUploadInfo($temp); + } + + $array[$key] = $item; + } else { + if ($file['error'] > 0) { + if ($key == $name) { + $this->throwUploadFileError($file['error']); + } else { + continue; + } + } + + $array[$key] = (new File($file['tmp_name']))->setUploadInfo($file); + } + } + + return $array; + } + + protected function throwUploadFileError($error) + { + static $fileUploadErrors = [ + 1 => 'upload File size exceeds the maximum value', + 2 => 'upload File size exceeds the maximum value', + 3 => 'only the portion of file is uploaded', + 4 => 'no file to uploaded', + 6 => 'upload temp dir not found', + 7 => 'file write error', + ]; + + $msg = $fileUploadErrors[$error]; + + throw new Exception($msg); + } + /** * 获取环境变量 - * @param string|array $name 数据名称 - * @param string $default 默认值 - * @param string|array $filter 过滤方法 + * @access public + * @param string $name 数据名称 + * @param string $default 默认值 * @return mixed */ - public function env($name = '', $default = null, $filter = '') + public function env($name = '', $default = null) { - if (empty($this->env)) { - $this->env = $_ENV; + if (empty($name)) { + return $this->env; + } else { + $name = strtoupper($name); } - if (is_array($name)) { - return $this->env = array_merge($this->env, $name); - } - return $this->input($this->env, false === $name ? false : strtoupper($name), $default, $filter); + + return isset($this->env[$name]) ? $this->env[$name] : $default; } /** - * 设置或者获取当前的Header + * 获取当前的Header * @access public - * @param string|array $name header名称 - * @param string $default 默认值 - * @return string + * @param string $name header名称 + * @param string $default 默认值 + * @return string|array */ public function header($name = '', $default = null) { @@ -933,7 +1293,7 @@ class Request if (function_exists('apache_request_headers') && $result = apache_request_headers()) { $header = $result; } else { - $server = $this->server ?: $_SERVER; + $server = $this->server; foreach ($server as $key => $val) { if (0 === strpos($key, 'HTTP_')) { $key = str_replace('_', '-', strtolower(substr($key, 5))); @@ -949,22 +1309,39 @@ class Request } $this->header = array_change_key_case($header); } - if (is_array($name)) { - return $this->header = array_merge($this->header, $name); - } + if ('' === $name) { return $this->header; } + $name = str_replace('_', '-', strtolower($name)); + return isset($this->header[$name]) ? $this->header[$name] : $default; } + /** + * 递归重置数组指针 + * @access public + * @param array $data 数据源 + * @return void + */ + public function arrayReset(array &$data) + { + foreach ($data as &$value) { + if (is_array($value)) { + $this->arrayReset($value); + } + } + reset($data); + } + /** * 获取变量 支持过滤和默认值 - * @param array $data 数据源 - * @param string|false $name 字段名 - * @param mixed $default 默认值 - * @param string|array $filter 过滤函数 + * @access public + * @param array $data 数据源 + * @param string|false $name 字段名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤函数 * @return mixed */ public function input($data = [], $name = '', $default = null, $filter = '') @@ -973,23 +1350,20 @@ class Request // 获取原始数据 return $data; } + $name = (string) $name; if ('' != $name) { // 解析name if (strpos($name, '/')) { list($name, $type) = explode('/', $name); - } else { - $type = 's'; } - // 按.拆分成多维数组进行判断 - foreach (explode('.', $name) as $val) { - if (isset($data[$val])) { - $data = $data[$val]; - } else { - // 无输入数据,返回默认值 - return $default; - } + + $data = $this->getData($data, $name); + + if (is_null($data)) { + return $default; } + if (is_object($data)) { return $data; } @@ -1000,7 +1374,10 @@ class Request if (is_array($data)) { array_walk_recursive($data, [$this, 'filterValue'], $filter); - reset($data); + if (version_compare(PHP_VERSION, '7.1.0', '<')) { + // 恢复PHP版本低于 7.1 时 array_walk_recursive 中消耗的内部指针 + $this->arrayReset($data); + } } else { $this->filterValue($data, $name, $filter); } @@ -1009,21 +1386,43 @@ class Request // 强制类型转换 $this->typeCast($data, $type); } + + return $data; + } + + /** + * 获取数据 + * @access public + * @param array $data 数据源 + * @param string|false $name 字段名 + * @return mixed + */ + protected function getData(array $data, $name) + { + foreach (explode('.', $name) as $val) { + if (isset($data[$val])) { + $data = $data[$val]; + } else { + return; + } + } + return $data; } /** * 设置或获取当前的过滤规则 - * @param mixed $filter 过滤规则 + * @access public + * @param mixed $filter 过滤规则 * @return mixed */ public function filter($filter = null) { if (is_null($filter)) { return $this->filter; - } else { - $this->filter = $filter; } + + $this->filter = $filter; } protected function getFilter($filter, $default) @@ -1040,19 +1439,22 @@ class Request } $filter[] = $default; + return $filter; } /** * 递归过滤给定的值 - * @param mixed $value 键值 - * @param mixed $key 键名 - * @param array $filters 过滤方法+默认值 + * @access public + * @param mixed $value 键值 + * @param mixed $key 键名 + * @param array $filters 过滤方法+默认值 * @return mixed */ private function filterValue(&$value, $key, $filters) { $default = array_pop($filters); + foreach ($filters as $filter) { if (is_callable($filter)) { // 调用函数或者方法过滤 @@ -1076,27 +1478,15 @@ class Request } } } - return $this->filterExp($value); - } - /** - * 过滤表单中的表达式 - * @param string $value - * @return void - */ - public function filterExp(&$value) - { - // 过滤查询特殊字符 - if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i', $value)) { - $value .= ' '; - } - // TODO 其他安全过滤 + return $value; } /** * 强制类型转换 - * @param string $data - * @param string $type + * @access public + * @param string $data + * @param string $type * @return mixed */ private function typeCast(&$data, $type) @@ -1120,30 +1510,35 @@ class Request break; // 字符串 case 's': - default: if (is_scalar($data)) { $data = (string) $data; } else { throw new \InvalidArgumentException('variable type error:' . gettype($data)); } + break; } } /** * 是否存在某个请求参数 * @access public - * @param string $name 变量名 - * @param string $type 变量类型 - * @param bool $checkEmpty 是否检测空值 + * @param string $name 变量名 + * @param string $type 变量类型 + * @param bool $checkEmpty 是否检测空值 * @return mixed */ public function has($name, $type = 'param', $checkEmpty = false) { + if (!in_array($type, ['param', 'get', 'post', 'request', 'put', 'patch', 'file', 'session', 'cookie', 'env', 'header', 'route'])) { + return false; + } + if (empty($this->$type)) { $param = $this->$type(); } else { $param = $this->$type; } + // 按.拆分成多维数组进行判断 foreach (explode('.', $name) as $val) { if (isset($param[$val])) { @@ -1152,36 +1547,50 @@ class Request return false; } } + return ($checkEmpty && '' === $param) ? false : true; } /** * 获取指定的参数 * @access public - * @param string|array $name 变量名 - * @param string $type 变量类型 + * @param string|array $name 变量名 + * @param string $type 变量类型 * @return mixed */ public function only($name, $type = 'param') { $param = $this->$type(); + if (is_string($name)) { $name = explode(',', $name); } + $item = []; - foreach ($name as $key) { + foreach ($name as $key => $val) { + + if (is_int($key)) { + $default = null; + $key = $val; + } else { + $default = $val; + } + if (isset($param[$key])) { $item[$key] = $param[$key]; + } elseif (isset($default)) { + $item[$key] = $default; } } + return $item; } /** * 排除指定参数获取 * @access public - * @param string|array $name 变量名 - * @param string $type 变量类型 + * @param string|array $name 变量名 + * @param string $type 变量类型 * @return mixed */ public function except($name, $type = 'param') @@ -1190,11 +1599,13 @@ class Request if (is_string($name)) { $name = explode(',', $name); } + foreach ($name as $key) { if (isset($param[$key])) { unset($param[$key]); } } + return $param; } @@ -1205,85 +1616,110 @@ class Request */ public function isSsl() { - $server = array_merge($_SERVER, $this->server); - if (isset($server['HTTPS']) && ('1' == $server['HTTPS'] || 'on' == strtolower($server['HTTPS']))) { + if ($this->server('HTTPS') && ('1' == $this->server('HTTPS') || 'on' == strtolower($this->server('HTTPS')))) { return true; - } elseif (isset($server['REQUEST_SCHEME']) && 'https' == $server['REQUEST_SCHEME']) { + } elseif ('https' == $this->server('REQUEST_SCHEME')) { return true; - } elseif (isset($server['SERVER_PORT']) && ('443' == $server['SERVER_PORT'])) { + } elseif ('443' == $this->server('SERVER_PORT')) { return true; - } elseif (isset($server['HTTP_X_FORWARDED_PROTO']) && 'https' == $server['HTTP_X_FORWARDED_PROTO']) { + } elseif ('https' == $this->server('HTTP_X_FORWARDED_PROTO')) { + return true; + } elseif ($this->config['https_agent_name'] && $this->server($this->config['https_agent_name'])) { return true; } + return false; } /** * 当前是否Ajax请求 * @access public - * @param bool $ajax true 获取原始ajax请求 + * @param bool $ajax true 获取原始ajax请求 * @return bool */ public function isAjax($ajax = false) { - $value = $this->server('HTTP_X_REQUESTED_WITH', '', 'strtolower'); - $result = ('xmlhttprequest' == $value) ? true : false; + $value = $this->server('HTTP_X_REQUESTED_WITH'); + $result = 'xmlhttprequest' == strtolower($value) ? true : false; + if (true === $ajax) { return $result; - } else { - return $this->param(Config::get('var_ajax')) ? true : $result; } + + $result = $this->param($this->config['var_ajax']) ? true : $result; + $this->mergeParam = false; + return $result; } /** * 当前是否Pjax请求 * @access public - * @param bool $pjax true 获取原始pjax请求 + * @param bool $pjax true 获取原始pjax请求 * @return bool */ public function isPjax($pjax = false) { $result = !is_null($this->server('HTTP_X_PJAX')) ? true : false; + if (true === $pjax) { return $result; - } else { - return $this->param(Config::get('var_pjax')) ? true : $result; } + + $result = $this->param($this->config['var_pjax']) ? true : $result; + $this->mergeParam = false; + return $result; } /** * 获取客户端IP地址 - * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字 - * @param boolean $adv 是否进行高级模式获取(有可能被伪装) + * @access public + * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字 + * @param boolean $adv 是否进行高级模式获取(有可能被伪装) * @return mixed */ - public function ip($type = 0, $adv = false) + public function ip($type = 0, $adv = true) { $type = $type ? 1 : 0; static $ip = null; + if (null !== $ip) { return $ip[$type]; } - if ($adv) { - if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); + $httpAgentIp = $this->config['http_agent_ip']; + + if ($httpAgentIp && $this->server($httpAgentIp)) { + $ip = $this->server($httpAgentIp); + } elseif ($adv) { + if ($this->server('HTTP_X_FORWARDED_FOR')) { + $arr = explode(',', $this->server('HTTP_X_FORWARDED_FOR')); $pos = array_search('unknown', $arr); if (false !== $pos) { unset($arr[$pos]); } $ip = trim(current($arr)); - } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) { - $ip = $_SERVER['HTTP_CLIENT_IP']; - } elseif (isset($_SERVER['REMOTE_ADDR'])) { - $ip = $_SERVER['REMOTE_ADDR']; + } elseif ($this->server('HTTP_CLIENT_IP')) { + $ip = $this->server('HTTP_CLIENT_IP'); + } elseif ($this->server('REMOTE_ADDR')) { + $ip = $this->server('REMOTE_ADDR'); } - } elseif (isset($_SERVER['REMOTE_ADDR'])) { - $ip = $_SERVER['REMOTE_ADDR']; + } elseif ($this->server('REMOTE_ADDR')) { + $ip = $this->server('REMOTE_ADDR'); } + + // IP地址类型 + $ip_mode = (strpos($ip, ':') === false) ? 'ipv4' : 'ipv6'; + // IP地址合法验证 - $long = sprintf("%u", ip2long($ip)); - $ip = $long ? [$ip, $long] : ['0.0.0.0', 0]; + if (filter_var($ip, FILTER_VALIDATE_IP) !== $ip) { + $ip = ('ipv4' === $ip_mode) ? '0.0.0.0' : '::'; + } + + // 如果是ipv4地址,则直接使用ip2long返回int类型ip;如果是ipv6地址,暂时不支持,直接返回0 + $long_ip = ('ipv4' === $ip_mode) ? sprintf("%u", ip2long($ip)) : 0; + + $ip = [$ip, $long_ip]; + return $ip[$type]; } @@ -1294,17 +1730,17 @@ class Request */ public function isMobile() { - if (isset($_SERVER['HTTP_VIA']) && stristr($_SERVER['HTTP_VIA'], "wap")) { + if ($this->server('HTTP_VIA') && stristr($this->server('HTTP_VIA'), "wap")) { return true; - } elseif (isset($_SERVER['HTTP_ACCEPT']) && strpos(strtoupper($_SERVER['HTTP_ACCEPT']), "VND.WAP.WML")) { + } elseif ($this->server('HTTP_ACCEPT') && strpos(strtoupper($this->server('HTTP_ACCEPT')), "VND.WAP.WML")) { return true; - } elseif (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE'])) { + } elseif ($this->server('HTTP_X_WAP_PROFILE') || $this->server('HTTP_PROFILE')) { return true; - } elseif (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(blackberry|configuration\/cldc|hp |hp-|htc |htc_|htc-|iemobile|kindle|midp|mmp|motorola|mobile|nokia|opera mini|opera |Googlebot-Mobile|YahooSeeker\/M1A1-R2D2|android|iphone|ipod|mobi|palm|palmos|pocket|portalmmm|ppc;|smartphone|sonyericsson|sqh|spv|symbian|treo|up.browser|up.link|vodafone|windows ce|xda |xda_)/i', $_SERVER['HTTP_USER_AGENT'])) { + } elseif ($this->server('HTTP_USER_AGENT') && preg_match('/(blackberry|configuration\/cldc|hp |hp-|htc |htc_|htc-|iemobile|kindle|midp|mmp|motorola|mobile|nokia|opera mini|opera |Googlebot-Mobile|YahooSeeker\/M1A1-R2D2|android|iphone|ipod|mobi|palm|palmos|pocket|portalmmm|ppc;|smartphone|sonyericsson|sqh|spv|symbian|treo|up.browser|up.link|vodafone|windows ce|xda |xda_)/i', $this->server('HTTP_USER_AGENT'))) { return true; - } else { - return false; } + + return false; } /** @@ -1327,14 +1763,32 @@ class Request return $this->server('QUERY_STRING'); } + /** + * 设置当前请求的host(包含端口) + * @access public + * @param string $host 主机名(含端口) + * @return $this + */ + public function setHost($host) + { + $this->host = $host; + + return $this; + } + /** * 当前请求的host * @access public + * @param bool $strict true 仅仅获取HOST * @return string */ - public function host() + public function host($strict = false) { - return $this->server('HTTP_HOST'); + if (!$this->host) { + $this->host = $this->server('HTTP_X_REAL_HOST') ?: $this->server('HTTP_HOST'); + } + + return true === $strict && strpos($this->host, ':') ? strstr($this->host, ':', true) : $this->host; } /** @@ -1350,7 +1804,7 @@ class Request /** * 当前请求 SERVER_PROTOCOL * @access public - * @return integer + * @return string */ public function protocol() { @@ -1375,6 +1829,7 @@ class Request public function contentType() { $contentType = $this->server('CONTENT_TYPE'); + if ($contentType) { if (strpos($contentType, ';')) { list($type) = explode(';', $contentType); @@ -1383,100 +1838,144 @@ class Request } return trim($type); } + return ''; } /** * 获取当前请求的路由信息 * @access public - * @param array $route 路由名称 + * @param array $route 路由名称 * @return array */ - public function routeInfo($route = []) + public function routeInfo(array $route = []) { if (!empty($route)) { $this->routeInfo = $route; - } else { - return $this->routeInfo; } + + return $this->routeInfo; } /** * 设置或者获取当前请求的调度信息 * @access public - * @param array $dispatch 调度信息 - * @return array + * @param \think\route\Dispatch $dispatch 调度信息 + * @return \think\route\Dispatch */ public function dispatch($dispatch = null) { if (!is_null($dispatch)) { $this->dispatch = $dispatch; } + return $this->dispatch; } /** - * 设置或者获取当前的模块名 + * 获取当前请求的安全Key * @access public - * @param string $module 模块名 - * @return string|Request + * @return string */ - public function module($module = null) + public function secureKey() { - if (!is_null($module)) { - $this->module = $module; - return $this; - } else { - return $this->module ?: ''; + if (is_null($this->secureKey)) { + $this->secureKey = uniqid('', true); } + + return $this->secureKey; } /** - * 设置或者获取当前的控制器名 + * 设置当前的模块名 * @access public - * @param string $controller 控制器名 - * @return string|Request + * @param string $module 模块名 + * @return $this */ - public function controller($controller = null) + public function setModule($module) { - if (!is_null($controller)) { - $this->controller = $controller; - return $this; - } else { - return $this->controller ?: ''; - } + $this->module = $module; + return $this; } /** - * 设置或者获取当前的操作名 + * 设置当前的控制器名 * @access public - * @param string $action 操作名 - * @return string|Request + * @param string $controller 控制器名 + * @return $this */ - public function action($action = null) + public function setController($controller) { - if (!is_null($action)) { - $this->action = $action; - return $this; - } else { - return $this->action ?: ''; - } + $this->controller = $controller; + return $this; } /** - * 设置或者获取当前的语言 + * 设置当前的操作名 * @access public - * @param string $lang 语言名 - * @return string|Request + * @param string $action 操作名 + * @return $this */ - public function langset($lang = null) + public function setAction($action) { - if (!is_null($lang)) { - $this->langset = $lang; - return $this; - } else { - return $this->langset ?: ''; - } + $this->action = $action; + return $this; + } + + /** + * 获取当前的模块名 + * @access public + * @return string + */ + public function module() + { + return $this->module ?: ''; + } + + /** + * 获取当前的控制器名 + * @access public + * @param bool $convert 转换为小写 + * @return string + */ + public function controller($convert = false) + { + $name = $this->controller ?: ''; + return $convert ? strtolower($name) : $name; + } + + /** + * 获取当前的操作名 + * @access public + * @param bool $convert 转换为驼峰 + * @return string + */ + public function action($convert = false) + { + $name = $this->action ?: ''; + return $convert ? $name : strtolower($name); + } + + /** + * 设置当前的语言 + * @access public + * @param string $lang 语言名 + * @return $this + */ + public function setLangset($lang) + { + $this->langset = $lang; + return $this; + } + + /** + * 获取当前的语言 + * @access public + * @return string + */ + public function langset() + { + return $this->langset ?: ''; } /** @@ -1489,6 +1988,7 @@ class Request if (is_null($this->content)) { $this->content = $this->input; } + return $this->content; } @@ -1505,87 +2005,90 @@ class Request /** * 生成请求令牌 * @access public - * @param string $name 令牌名称 - * @param mixed $type 令牌生成方法 + * @param string $name 令牌名称 + * @param mixed $type 令牌生成方法 * @return string */ - public function token($name = '__token__', $type = 'md5') + public function token($name = '__token__', $type = null) { $type = is_callable($type) ? $type : 'md5'; - $token = call_user_func($type, $_SERVER['REQUEST_TIME_FLOAT']); + $token = call_user_func($type, $this->server('REQUEST_TIME_FLOAT')); + if ($this->isAjax()) { header($name . ': ' . $token); } - Session::set($name, $token); + + facade\Session::set($name, $token); + return $token; } /** * 设置当前地址的请求缓存 * @access public - * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id - * @param mixed $expire 缓存有效期 - * @param array $except 缓存排除 - * @return void + * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id + * @param mixed $expire 缓存有效期 + * @param array $except 缓存排除 + * @param string $tag 缓存标签 + * @return mixed */ - public function cache($key, $expire = null, $except = []) + public function cache($key, $expire = null, $except = [], $tag = null) { - if (false !== $key && $this->isGet() && !$this->isCheckCache) { - // 标记请求缓存检查 - $this->isCheckCache = true; - if (false === $expire) { - // 关闭当前缓存 + if (!is_array($except)) { + $tag = $except; + $except = []; + } + + if (false === $key || !$this->isGet() || $this->isCheckCache || false === $expire) { + // 关闭当前缓存 + return; + } + + // 标记请求缓存检查 + $this->isCheckCache = true; + + foreach ($except as $rule) { + if (0 === stripos($this->url(), $rule)) { return; } - if ($key instanceof \Closure) { - $key = call_user_func_array($key, [$this]); - } elseif (true === $key) { - foreach ($except as $rule) { - if (0 === strpos($this->url(), $rule)) { - return; - } - } - // 自动缓存功能 - $key = '__URL__'; - } elseif (strpos($key, '|')) { - list($key, $fun) = explode('|', $key); - } - // 特殊规则替换 - if (false !== strpos($key, '__')) { - $key = str_replace(['__MODULE__', '__CONTROLLER__', '__ACTION__', '__URL__', ''], [$this->module, $this->controller, $this->action, md5($this->url(true))], $key); - } + } - if (false !== strpos($key, ':')) { - $param = $this->param(); - foreach ($param as $item => $val) { - if (is_string($val) && false !== strpos($key, ':' . $item)) { - $key = str_replace(':' . $item, $val, $key); - } - } - } elseif (strpos($key, ']')) { - if ('[' . $this->ext() . ']' == $key) { - // 缓存某个后缀的请求 - $key = md5($this->url()); - } else { - return; - } - } - if (isset($fun)) { - $key = $fun($key); - } + if ($key instanceof \Closure) { + $key = call_user_func_array($key, [$this]); + } elseif (true === $key) { + // 自动缓存功能 + $key = '__URL__'; + } elseif (strpos($key, '|')) { + list($key, $fun) = explode('|', $key); + } - if (strtotime($this->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $_SERVER['REQUEST_TIME']) { - // 读取缓存 - $response = Response::create()->code(304); - throw new \think\exception\HttpResponseException($response); - } elseif (Cache::has($key)) { - list($content, $header) = Cache::get($key); - $response = Response::create($content)->header($header); - throw new \think\exception\HttpResponseException($response); + // 特殊规则替换 + if (false !== strpos($key, '__')) { + $key = str_replace(['__MODULE__', '__CONTROLLER__', '__ACTION__', '__URL__'], [$this->module, $this->controller, $this->action, md5($this->url(true))], $key); + } + + if (false !== strpos($key, ':')) { + $param = $this->param(); + foreach ($param as $item => $val) { + if (is_string($val) && false !== strpos($key, ':' . $item)) { + $key = str_replace(':' . $item, $val, $key); + } + } + } elseif (strpos($key, ']')) { + if ('[' . $this->ext() . ']' == $key) { + // 缓存某个后缀的请求 + $key = md5($this->url()); } else { - $this->cache = [$key, $expire]; + return; } } + + if (isset($fun)) { + $key = $fun($key); + } + + $this->cache = [$key, $expire, $tag]; + return $this->cache; } /** @@ -1599,33 +2102,151 @@ class Request } /** - * 设置当前请求绑定的对象实例 + * 设置GET数据 * @access public - * @param string $name 绑定的对象标识 - * @param mixed $obj 绑定的对象实例 - * @return mixed + * @param array $get 数据 + * @return $this */ - public function bind($name, $obj = null) + public function withGet(array $get) { - if (is_array($name)) { - $this->bind = array_merge($this->bind, $name); - } else { - $this->bind[$name] = $obj; - } + $this->get = $get; + return $this; } + /** + * 设置POST数据 + * @access public + * @param array $post 数据 + * @return $this + */ + public function withPost(array $post) + { + $this->post = $post; + return $this; + } + + /** + * 设置php://input数据 + * @access public + * @param string $input RAW数据 + * @return $this + */ + public function withInput($input) + { + $this->input = $input; + return $this; + } + + /** + * 设置文件上传数据 + * @access public + * @param array $files 上传信息 + * @return $this + */ + public function withFiles(array $files) + { + $this->file = $files; + return $this; + } + + /** + * 设置COOKIE数据 + * @access public + * @param array $cookie 数据 + * @return $this + */ + public function withCookie(array $cookie) + { + $this->cookie = $cookie; + return $this; + } + + /** + * 设置SERVER数据 + * @access public + * @param array $server 数据 + * @return $this + */ + public function withServer(array $server) + { + $this->server = array_change_key_case($server, CASE_UPPER); + return $this; + } + + /** + * 设置HEADER数据 + * @access public + * @param array $header 数据 + * @return $this + */ + public function withHeader(array $header) + { + $this->header = array_change_key_case($header); + return $this; + } + + /** + * 设置ENV数据 + * @access public + * @param array $env 数据 + * @return $this + */ + public function withEnv(array $env) + { + $this->env = $env; + return $this; + } + + /** + * 设置ROUTE变量 + * @access public + * @param array $route 数据 + * @return $this + */ + public function withRoute(array $route) + { + $this->route = $route; + return $this; + } + + /** + * 设置请求数据 + * @access public + * @param string $name 参数名 + * @param mixed $value 值 + */ public function __set($name, $value) { - $this->bind[$name] = $value; + return $this->param[$name] = $value; } + /** + * 获取请求数据的值 + * @access public + * @param string $name 参数名 + * @return mixed + */ public function __get($name) { - return isset($this->bind[$name]) ? $this->bind[$name] : null; + return $this->param($name); } + /** + * 检测请求数据的值 + * @access public + * @param string $name 名称 + * @return boolean + */ public function __isset($name) { - return isset($this->bind[$name]); + return isset($this->param[$name]); + } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['dispatch'], $data['config']); + + return $data; } } diff --git a/thinkphp/library/think/Response.php b/thinkphp/library/think/Response.php index 96737dcd2..5fa5402ab 100644 --- a/thinkphp/library/think/Response.php +++ b/thinkphp/library/think/Response.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,99 +11,135 @@ namespace think; -use think\response\Json as JsonResponse; -use think\response\Jsonp as JsonpResponse; use think\response\Redirect as RedirectResponse; -use think\response\View as ViewResponse; -use think\response\Xml as XmlResponse; class Response { - // 原始数据 + /** + * 原始数据 + * @var mixed + */ protected $data; - // 当前的contentType + /** + * 应用对象实例 + * @var App + */ + protected $app; + + /** + * 当前contentType + * @var string + */ protected $contentType = 'text/html'; - // 字符集 + /** + * 字符集 + * @var string + */ protected $charset = 'utf-8'; - //状态 + /** + * 状态码 + * @var integer + */ protected $code = 200; - // 输出参数 + /** + * 是否允许请求缓存 + * @var bool + */ + protected $allowCache = true; + + /** + * 输出参数 + * @var array + */ protected $options = []; - // header参数 + + /** + * header参数 + * @var array + */ protected $header = []; + /** + * 输出内容 + * @var string + */ protected $content = null; /** - * 构造函数 - * @access public - * @param mixed $data 输出数据 - * @param int $code - * @param array $header - * @param array $options 输出参数 + * 架构函数 + * @access public + * @param mixed $data 输出数据 + * @param int $code + * @param array $header + * @param array $options 输出参数 */ public function __construct($data = '', $code = 200, array $header = [], $options = []) { $this->data($data); + if (!empty($options)) { $this->options = array_merge($this->options, $options); } + $this->contentType($this->contentType, $this->charset); - $this->header = array_merge($this->header, $header); + $this->code = $code; + $this->app = Container::get('app'); + $this->header = array_merge($this->header, $header); } /** * 创建Response对象 * @access public - * @param mixed $data 输出数据 - * @param string $type 输出类型 - * @param int $code - * @param array $header - * @param array $options 输出参数 - * @return Response|JsonResponse|ViewResponse|XmlResponse|RedirectResponse|JsonpResponse + * @param mixed $data 输出数据 + * @param string $type 输出类型 + * @param int $code + * @param array $header + * @param array $options 输出参数 + * @return Response */ public static function create($data = '', $type = '', $code = 200, array $header = [], $options = []) { - $type = empty($type) ? 'null' : strtolower($type); + $class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst(strtolower($type)); - $class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst($type); if (class_exists($class)) { - $response = new $class($data, $code, $header, $options); - } else { - $response = new static($data, $code, $header, $options); + return new $class($data, $code, $header, $options); } - return $response; + return new static($data, $code, $header, $options); } /** * 发送数据到客户端 * @access public - * @return mixed + * @return void * @throws \InvalidArgumentException */ public function send() { + // 监听response_send + $this->app['hook']->listen('response_send', $this); + // 处理输出数据 $data = $this->getContent(); // Trace调试注入 - if (Env::get('app_trace', Config::get('app_trace'))) { - Debug::inject($this, $data); + if ('cli' != PHP_SAPI && $this->app['env']->get('app_trace', $this->app->config('app.app_trace'))) { + $this->app['debug']->inject($this, $data); } - if (200 == $this->code) { - $cache = Request::instance()->getCache(); + if (200 == $this->code && $this->allowCache) { + $cache = $this->app['request']->getCache(); if ($cache) { $this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate'; $this->header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT'; $this->header['Expires'] = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT'; - Cache::set($cache[0], [$data, $this->header], $cache[1]); + + $this->app['cache']->tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]); } } @@ -112,15 +148,11 @@ class Response http_response_code($this->code); // 发送头部信息 foreach ($this->header as $name => $val) { - if (is_null($val)) { - header($name); - } else { - header($name . ':' . $val); - } + header($name . (!is_null($val) ? ':' . $val : '')); } } - echo $data; + $this->sendData($data); if (function_exists('fastcgi_finish_request')) { // 提高页面响应 @@ -128,18 +160,18 @@ class Response } // 监听response_end - Hook::listen('response_end', $this); + $this->app['hook']->listen('response_end', $this); // 清空当次请求有效的数据 if (!($this instanceof RedirectResponse)) { - Session::flush(); + $this->app['session']->flush(); } } /** * 处理数据 * @access protected - * @param mixed $data 要处理的数据 + * @param mixed $data 要处理的数据 * @return mixed */ protected function output($data) @@ -147,35 +179,61 @@ class Response return $data; } + /** + * 输出数据 + * @access protected + * @param string $data 要处理的数据 + * @return void + */ + protected function sendData($data) + { + echo $data; + } + /** * 输出的参数 * @access public - * @param mixed $options 输出参数 + * @param mixed $options 输出参数 * @return $this */ public function options($options = []) { $this->options = array_merge($this->options, $options); + return $this; } /** * 输出数据设置 * @access public - * @param mixed $data 输出数据 + * @param mixed $data 输出数据 * @return $this */ public function data($data) { $this->data = $data; + + return $this; + } + + /** + * 是否允许请求缓存 + * @access public + * @param bool $cache 允许请求缓存 + * @return $this + */ + public function allowCache($cache) + { + $this->allowCache = $cache; + return $this; } /** * 设置响应头 * @access public - * @param string|array $name 参数名 - * @param string $value 参数值 + * @param string|array $name 参数名 + * @param string $value 参数值 * @return $this */ public function header($name, $value = null) @@ -185,12 +243,14 @@ class Response } else { $this->header[$name] = $value; } + return $this; } /** * 设置页面输出内容 - * @param $content + * @access public + * @param mixed $content * @return $this */ public function content($content) @@ -210,87 +270,114 @@ class Response /** * 发送HTTP状态 - * @param integer $code 状态码 + * @access public + * @param integer $code 状态码 * @return $this */ public function code($code) { $this->code = $code; + return $this; } /** * LastModified - * @param string $time + * @access public + * @param string $time * @return $this */ public function lastModified($time) { $this->header['Last-Modified'] = $time; + return $this; } /** * Expires - * @param string $time + * @access public + * @param string $time * @return $this */ public function expires($time) { $this->header['Expires'] = $time; + return $this; } /** * ETag - * @param string $eTag + * @access public + * @param string $eTag * @return $this */ public function eTag($eTag) { $this->header['ETag'] = $eTag; + return $this; } /** * 页面缓存控制 - * @param string $cache 状态码 + * @access public + * @param string $cache 缓存设置 * @return $this */ public function cacheControl($cache) { $this->header['Cache-control'] = $cache; + + return $this; + } + + /** + * 设置页面不做任何缓存 + * @access public + * @return $this + */ + public function noCache() + { + $this->header['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'; + $this->header['Pragma'] = 'no-cache'; + return $this; } /** * 页面输出类型 - * @param string $contentType 输出类型 - * @param string $charset 输出编码 + * @access public + * @param string $contentType 输出类型 + * @param string $charset 输出编码 * @return $this */ public function contentType($contentType, $charset = 'utf-8') { $this->header['Content-Type'] = $contentType . '; charset=' . $charset; + return $this; } /** * 获取头部信息 - * @param string $name 头部名称 + * @access public + * @param string $name 头部名称 * @return mixed */ public function getHeader($name = '') { if (!empty($name)) { return isset($this->header[$name]) ? $this->header[$name] : null; - } else { - return $this->header; } + + return $this->header; } /** * 获取原始数据 + * @access public * @return mixed */ public function getData() @@ -300,6 +387,7 @@ class Response /** * 获取输出数据 + * @access public * @return mixed */ public function getContent() @@ -317,15 +405,25 @@ class Response $this->content = (string) $content; } + return $this->content; } /** * 获取状态码 + * @access public * @return integer */ public function getCode() { return $this->code; } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; + } } diff --git a/thinkphp/library/think/Route.php b/thinkphp/library/think/Route.php index a5b193274..44af9871b 100644 --- a/thinkphp/library/think/Route.php +++ b/thinkphp/library/think/Route.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,39 +11,35 @@ namespace think; -use think\exception\HttpException; +use think\exception\RouteNotFoundException; +use think\route\AliasRule; +use think\route\dispatch\Url as UrlDispatch; +use think\route\Domain; +use think\route\Resource; +use think\route\RuleGroup; +use think\route\RuleItem; class Route { - // 路由规则 - private static $rules = [ - 'get' => [], - 'post' => [], - 'put' => [], - 'delete' => [], - 'patch' => [], - 'head' => [], - 'options' => [], - '*' => [], - 'alias' => [], - 'domain' => [], - 'pattern' => [], - 'name' => [], - ]; - - // REST路由操作方法定义 - private static $rest = [ + /** + * REST定义 + * @var array + */ + protected $rest = [ 'index' => ['get', '', 'index'], 'create' => ['get', '/create', 'create'], - 'edit' => ['get', '/:id/edit', 'edit'], - 'read' => ['get', '/:id', 'read'], + 'edit' => ['get', '//edit', 'edit'], + 'read' => ['get', '/', 'read'], 'save' => ['post', '', 'save'], - 'update' => ['put', '/:id', 'update'], - 'delete' => ['delete', '/:id', 'delete'], + 'update' => ['put', '/', 'update'], + 'delete' => ['delete', '/', 'delete'], ]; - // 不同请求类型的方法前缀 - private static $methodPrefix = [ + /** + * 请求方法前缀定义 + * @var array + */ + protected $methodPrefix = [ 'get' => 'get', 'post' => 'post', 'put' => 'put', @@ -51,170 +47,483 @@ class Route 'patch' => 'patch', ]; - // 子域名 - private static $subDomain = ''; - // 域名绑定 - private static $bind = []; - // 当前分组信息 - private static $group = []; - // 当前子域名绑定 - private static $domainBind; - private static $domainRule; - // 当前域名 - private static $domain; - // 当前路由执行过程中的参数 - private static $option = []; + /** + * 应用对象 + * @var App + */ + protected $app; + + /** + * 请求对象 + * @var Request + */ + protected $request; + + /** + * 当前HOST + * @var string + */ + protected $host; + + /** + * 当前域名 + * @var string + */ + protected $domain; + + /** + * 当前分组对象 + * @var RuleGroup + */ + protected $group; + + /** + * 配置参数 + * @var array + */ + protected $config = []; + + /** + * 路由绑定 + * @var array + */ + protected $bind = []; + + /** + * 域名对象 + * @var array + */ + protected $domains = []; + + /** + * 跨域路由规则 + * @var RuleGroup + */ + protected $cross; + + /** + * 路由别名 + * @var array + */ + protected $alias = []; + + /** + * 路由是否延迟解析 + * @var bool + */ + protected $lazy = true; + + /** + * 路由是否测试模式 + * @var bool + */ + protected $isTest; + + /** + * (分组)路由规则是否合并解析 + * @var bool + */ + protected $mergeRuleRegex = true; + + /** + * 路由解析自动搜索多级控制器 + * @var bool + */ + protected $autoSearchController = true; + + public function __construct(App $app, array $config = []) + { + $this->app = $app; + $this->request = $app['request']; + $this->config = $config; + + $this->host = $this->request->host(true) ?: $config['app_host']; + + $this->setDefaultDomain(); + } + + public function config($name = null) + { + if (is_null($name)) { + return $this->config; + } + + return isset($this->config[$name]) ? $this->config[$name] : null; + } + + /** + * 配置 + * @access public + * @param array $config + * @return void + */ + public function setConfig(array $config = []) + { + $this->config = array_merge($this->config, array_change_key_case($config)); + } + + public static function __make(App $app, Config $config) + { + $config = $config->pull('app'); + $route = new static($app, $config); + + $route->lazy($config['url_lazy_route']) + ->autoSearchController($config['controller_auto_search']) + ->mergeRuleRegex($config['route_rule_merge']); + + return $route; + } + + /** + * 设置路由的请求对象实例 + * @access public + * @param Request $request 请求对象实例 + * @return void + */ + public function setRequest($request) + { + $this->request = $request; + } + + /** + * 设置路由域名及分组(包括资源路由)是否延迟解析 + * @access public + * @param bool $lazy 路由是否延迟解析 + * @return $this + */ + public function lazy($lazy = true) + { + $this->lazy = $lazy; + return $this; + } + + /** + * 设置路由为测试模式 + * @access public + * @param bool $test 路由是否测试模式 + * @return void + */ + public function setTestMode($test) + { + $this->isTest = $test; + } + + /** + * 检查路由是否为测试模式 + * @access public + * @return bool + */ + public function isTest() + { + return $this->isTest; + } + + /** + * 设置路由域名及分组(包括资源路由)是否合并解析 + * @access public + * @param bool $merge 路由是否合并解析 + * @return $this + */ + public function mergeRuleRegex($merge = true) + { + $this->mergeRuleRegex = $merge; + $this->group->mergeRuleRegex($merge); + + return $this; + } + + /** + * 设置路由自动解析是否搜索多级控制器 + * @access public + * @param bool $auto 是否自动搜索多级控制器 + * @return $this + */ + public function autoSearchController($auto = true) + { + $this->autoSearchController = $auto; + return $this; + } + + /** + * 初始化默认域名 + * @access protected + * @return void + */ + protected function setDefaultDomain() + { + // 默认域名 + $this->domain = $this->host; + + // 注册默认域名 + $domain = new Domain($this, $this->host); + + $this->domains[$this->host] = $domain; + + // 默认分组 + $this->group = $domain; + } + + /** + * 设置当前域名 + * @access public + * @param RuleGroup $group 域名 + * @return void + */ + public function setGroup(RuleGroup $group) + { + $this->group = $group; + } + + /** + * 获取当前分组 + * @access public + * @return RuleGroup + */ + public function getGroup() + { + return $this->group; + } /** * 注册变量规则 * @access public - * @param string|array $name 变量名 - * @param string $rule 变量规则 - * @return void + * @param string|array $name 变量名 + * @param string $rule 变量规则 + * @return $this */ - public static function pattern($name = null, $rule = '') + public function pattern($name, $rule = '') { - if (is_array($name)) { - self::$rules['pattern'] = array_merge(self::$rules['pattern'], $name); - } else { - self::$rules['pattern'][$name] = $rule; - } + $this->group->pattern($name, $rule); + + return $this; } /** - * 注册子域名部署规则 + * 注册路由参数 * @access public - * @param string|array $domain 子域名 - * @param mixed $rule 路由规则 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string|array $name 参数名 + * @param mixed $value 值 + * @return $this */ - public static function domain($domain, $rule = '', $option = [], $pattern = []) + public function option($name, $value = '') { - if (is_array($domain)) { - foreach ($domain as $key => $item) { - self::domain($key, $item, $option, $pattern); - } - } elseif ($rule instanceof \Closure) { - // 执行闭包 - self::setDomain($domain); - call_user_func_array($rule, []); - self::setDomain(null); - } elseif (is_array($rule)) { - self::setDomain($domain); - self::group('', function () use ($rule) { - // 动态注册域名的路由规则 - self::registerRules($rule); - }, $option, $pattern); - self::setDomain(null); - } else { - self::$rules['domain'][$domain]['[bind]'] = [$rule, $option, $pattern]; - } + $this->group->option($name, $value); + + return $this; } - private static function setDomain($domain) + /** + * 注册域名路由 + * @access public + * @param string|array $name 子域名 + * @param mixed $rule 路由规则 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return Domain + */ + public function domain($name, $rule = '', $option = [], $pattern = []) { - self::$domain = $domain; + // 支持多个域名使用相同路由规则 + $domainName = is_array($name) ? array_shift($name) : $name; + + if ('*' != $domainName && false === strpos($domainName, '.')) { + $domainName .= '.' . $this->request->rootDomain(); + } + + if (!isset($this->domains[$domainName])) { + $domain = (new Domain($this, $domainName, $rule, $option, $pattern)) + ->lazy($this->lazy) + ->mergeRuleRegex($this->mergeRuleRegex); + + $this->domains[$domainName] = $domain; + } else { + $domain = $this->domains[$domainName]; + $domain->parseGroupRule($rule); + } + + if (is_array($name) && !empty($name)) { + $root = $this->request->rootDomain(); + foreach ($name as $item) { + if (false === strpos($item, '.')) { + $item .= '.' . $root; + } + + $this->domains[$item] = $domainName; + } + } + + // 返回域名对象 + return $domain; + } + + /** + * 获取域名 + * @access public + * @return array + */ + public function getDomains() + { + return $this->domains; } /** * 设置路由绑定 * @access public - * @param mixed $bind 绑定信息 - * @param string $type 绑定类型 默认为module 支持 namespace class controller - * @return mixed + * @param string $bind 绑定信息 + * @param string $domain 域名 + * @return $this */ - public static function bind($bind, $type = 'module') + public function bind($bind, $domain = null) { - self::$bind = ['type' => $type, $type => $bind]; - } + $domain = is_null($domain) ? $this->domain : $domain; - /** - * 设置或者获取路由标识 - * @access public - * @param string|array $name 路由命名标识 数组表示批量设置 - * @param array $value 路由地址及变量信息 - * @return array - */ - public static function name($name = '', $value = null) - { - if (is_array($name)) { - return self::$rules['name'] = $name; - } elseif ('' === $name) { - return self::$rules['name']; - } elseif (!is_null($value)) { - self::$rules['name'][strtolower($name)][] = $value; - } else { - $name = strtolower($name); - return isset(self::$rules['name'][$name]) ? self::$rules['name'][$name] : null; - } + $this->bind[$domain] = $bind; + + return $this; } /** * 读取路由绑定 * @access public - * @param string $type 绑定类型 + * @param string $domain 域名 + * @return string|null + */ + public function getBind($domain = null) + { + if (is_null($domain)) { + $domain = $this->domain; + } elseif (true === $domain) { + return $this->bind; + } elseif (false === strpos($domain, '.')) { + $domain .= '.' . $this->request->rootDomain(); + } + + $subDomain = $this->request->subDomain(); + + if (strpos($subDomain, '.')) { + $name = '*' . strstr($subDomain, '.'); + } + + if (isset($this->bind[$domain])) { + $result = $this->bind[$domain]; + } elseif (isset($name) && isset($this->bind[$name])) { + $result = $this->bind[$name]; + } elseif (!empty($subDomain) && isset($this->bind['*'])) { + $result = $this->bind['*']; + } else { + $result = null; + } + + return $result; + } + + /** + * 读取路由标识 + * @access public + * @param string $name 路由标识 + * @param string $domain 域名 * @return mixed */ - public static function getBind($type) + public function getName($name = null, $domain = null, $method = '*') { - return isset(self::$bind[$type]) ? self::$bind[$type] : null; + return $this->app['rule_name']->get($name, $domain, $method); + } + + /** + * 读取路由 + * @access public + * @param string $rule 路由规则 + * @param string $domain 域名 + * @return array + */ + public function getRule($rule, $domain = null) + { + if (is_null($domain)) { + $domain = $this->domain; + } + + return $this->app['rule_name']->getRule($rule, $domain); + } + + /** + * 读取路由 + * @access public + * @param string $domain 域名 + * @return array + */ + public function getRuleList($domain = null) + { + return $this->app['rule_name']->getRuleList($domain); + } + + /** + * 批量导入路由标识 + * @access public + * @param array $name 路由标识 + * @return $this + */ + public function setName($name) + { + $this->app['rule_name']->import($name); + return $this; } /** * 导入配置文件的路由规则 * @access public - * @param array $rule 路由规则 - * @param string $type 请求类型 + * @param array $rules 路由规则 + * @param string $type 请求类型 * @return void */ - public static function import(array $rule, $type = '*') + public function import(array $rules, $type = '*') { // 检查域名部署 - if (isset($rule['__domain__'])) { - self::domain($rule['__domain__']); - unset($rule['__domain__']); + if (isset($rules['__domain__'])) { + foreach ($rules['__domain__'] as $key => $rule) { + $this->domain($key, $rule); + } + unset($rules['__domain__']); } // 检查变量规则 - if (isset($rule['__pattern__'])) { - self::pattern($rule['__pattern__']); - unset($rule['__pattern__']); + if (isset($rules['__pattern__'])) { + $this->pattern($rules['__pattern__']); + unset($rules['__pattern__']); } // 检查路由别名 - if (isset($rule['__alias__'])) { - self::alias($rule['__alias__']); - unset($rule['__alias__']); + if (isset($rules['__alias__'])) { + foreach ($rules['__alias__'] as $key => $val) { + $this->alias($key, $val); + } + unset($rules['__alias__']); } // 检查资源路由 - if (isset($rule['__rest__'])) { - self::resource($rule['__rest__']); - unset($rule['__rest__']); + if (isset($rules['__rest__'])) { + foreach ($rules['__rest__'] as $key => $rule) { + $this->resource($key, $rule); + } + unset($rules['__rest__']); } - self::registerRules($rule, strtolower($type)); - } - - // 批量注册路由 - protected static function registerRules($rules, $type = '*') - { + // 检查路由规则(包含分组) foreach ($rules as $key => $val) { if (is_numeric($key)) { $key = array_shift($val); } + if (empty($val)) { continue; } + if (is_string($key) && 0 === strpos($key, '[')) { $key = substr($key, 1, -1); - self::group($key, $val); + $this->group($key, $val); } elseif (is_array($val)) { - self::setRule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []); + $this->rule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []); } else { - self::setRule($key, $val, $type); + $this->rule($key, $val, $type); } } } @@ -222,1372 +531,460 @@ class Route /** * 注册路由规则 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param string $type 请求类型 - * @param array $option 路由参数 - * @param array $pattern 变量规则 + * @param string $rule 路由规则 + * @param mixed $route 路由地址 + * @param string $method 请求类型 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem + */ + public function rule($rule, $route, $method = '*', array $option = [], array $pattern = []) + { + return $this->group->addRule($rule, $route, $method, $option, $pattern); + } + + /** + * 设置跨域有效路由规则 + * @access public + * @param Rule $rule 路由规则 + * @param string $method 请求类型 + * @return $this + */ + public function setCrossDomainRule($rule, $method = '*') + { + if (!isset($this->cross)) { + $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex); + } + + $this->cross->addRuleItem($rule, $method); + + return $this; + } + + /** + * 批量注册路由规则 + * @access public + * @param array $rules 路由规则 + * @param string $method 请求类型 + * @param array $option 路由参数 + * @param array $pattern 变量规则 * @return void */ - public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = []) + public function rules($rules, $method = '*', array $option = [], array $pattern = []) { - $group = self::getGroup('name'); - - if (!is_null($group)) { - // 路由分组 - $option = array_merge(self::getGroup('option'), $option); - $pattern = array_merge(self::getGroup('pattern'), $pattern); - } - - $type = strtolower($type); - - if (strpos($type, '|')) { - $option['method'] = $type; - $type = '*'; - } - if (is_array($rule) && empty($route)) { - foreach ($rule as $key => $val) { - if (is_numeric($key)) { - $key = array_shift($val); - } - if (is_array($val)) { - $route = $val[0]; - $option1 = array_merge($option, $val[1]); - $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []); - } else { - $route = $val; - } - self::setRule($key, $route, $type, isset($option1) ? $option1 : $option, isset($pattern1) ? $pattern1 : $pattern, $group); - } - } else { - self::setRule($rule, $route, $type, $option, $pattern, $group); - } - - } - - /** - * 设置路由规则 - * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param string $type 请求类型 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @param string $group 所属分组 - * @return void - */ - protected static function setRule($rule, $route, $type = '*', $option = [], $pattern = [], $group = '') - { - if (is_array($rule)) { - $name = $rule[0]; - $rule = $rule[1]; - } elseif (is_string($route)) { - $name = $route; - } - if (!isset($option['complete_match'])) { - if (Config::get('route_complete_match')) { - $option['complete_match'] = true; - } elseif ('$' == substr($rule, -1, 1)) { - // 是否完整匹配 - $option['complete_match'] = true; - } - } elseif (empty($option['complete_match']) && '$' == substr($rule, -1, 1)) { - // 是否完整匹配 - $option['complete_match'] = true; - } - - if ('$' == substr($rule, -1, 1)) { - $rule = substr($rule, 0, -1); - } - - if ('/' != $rule || $group) { - $rule = trim($rule, '/'); - } - $vars = self::parseVar($rule); - if (isset($name)) { - $key = $group ? $group . ($rule ? '/' . $rule : '') : $rule; - $suffix = isset($option['ext']) ? $option['ext'] : null; - self::name($name, [$key, $vars, self::$domain, $suffix]); - } - if (isset($option['modular'])) { - $route = $option['modular'] . '/' . $route; - } - if ($group) { - if ('*' != $type) { - $option['method'] = $type; - } - if (self::$domain) { - self::$rules['domain'][self::$domain]['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern]; - } else { - self::$rules['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern]; - } - } else { - if ('*' != $type && isset(self::$rules['*'][$rule])) { - unset(self::$rules['*'][$rule]); - } - if (self::$domain) { - self::$rules['domain'][self::$domain][$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern]; - } else { - self::$rules[$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern]; - } - if ('*' == $type) { - // 注册路由快捷方式 - foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) { - if (self::$domain) { - self::$rules['domain'][self::$domain][$method][$rule] = true; - } else { - self::$rules[$method][$rule] = true; - } - } - } - } - } - - /** - * 设置当前执行的参数信息 - * @access public - * @param array $options 参数信息 - * @return mixed - */ - protected static function setOption($options = []) - { - self::$option[] = $options; - } - - /** - * 获取当前执行的所有参数信息 - * @access public - * @return array - */ - public static function getOption() - { - return self::$option; - } - - /** - * 获取当前的分组信息 - * @access public - * @param string $type 分组信息名称 name option pattern - * @return mixed - */ - public static function getGroup($type) - { - if (isset(self::$group[$type])) { - return self::$group[$type]; - } else { - return 'name' == $type ? null : []; - } - } - - /** - * 设置当前的路由分组 - * @access public - * @param string $name 分组名称 - * @param array $option 分组路由参数 - * @param array $pattern 分组变量规则 - * @return void - */ - public static function setGroup($name, $option = [], $pattern = []) - { - self::$group['name'] = $name; - self::$group['option'] = $option ?: []; - self::$group['pattern'] = $pattern ?: []; + $this->group->addRules($rules, $method, $option, $pattern); } /** * 注册路由分组 * @access public - * @param string|array $name 分组名称或者参数 - * @param array|\Closure $routes 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string|array $name 分组名称或者参数 + * @param array|\Closure $route 分组路由 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleGroup */ - public static function group($name, $routes, $option = [], $pattern = []) + public function group($name, $route, array $option = [], array $pattern = []) { if (is_array($name)) { $option = $name; $name = isset($option['name']) ? $option['name'] : ''; } - // 分组 - $currentGroup = self::getGroup('name'); - if ($currentGroup) { - $name = $currentGroup . ($name ? '/' . ltrim($name, '/') : ''); - } - if (!empty($name)) { - if ($routes instanceof \Closure) { - $currentOption = self::getGroup('option'); - $currentPattern = self::getGroup('pattern'); - self::setGroup($name, array_merge($currentOption, $option), array_merge($currentPattern, $pattern)); - call_user_func_array($routes, []); - self::setGroup($currentGroup, $currentOption, $currentPattern); - if ($currentGroup != $name) { - self::$rules['*'][$name]['route'] = ''; - self::$rules['*'][$name]['var'] = self::parseVar($name); - self::$rules['*'][$name]['option'] = $option; - self::$rules['*'][$name]['pattern'] = $pattern; - } - } else { - $item = []; - foreach ($routes as $key => $val) { - if (is_numeric($key)) { - $key = array_shift($val); - } - if (is_array($val)) { - $route = $val[0]; - $option1 = array_merge($option, isset($val[1]) ? $val[1] : []); - $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []); - } else { - $route = $val; - } - $options = isset($option1) ? $option1 : $option; - $patterns = isset($pattern1) ? $pattern1 : $pattern; - if ('$' == substr($key, -1, 1)) { - // 是否完整匹配 - $options['complete_match'] = true; - $key = substr($key, 0, -1); - } - $key = trim($key, '/'); - $vars = self::parseVar($key); - $item[] = ['rule' => $key, 'route' => $route, 'var' => $vars, 'option' => $options, 'pattern' => $patterns]; - // 设置路由标识 - $suffix = isset($options['ext']) ? $options['ext'] : null; - self::name($route, [$name . ($key ? '/' . $key : ''), $vars, self::$domain, $suffix]); - } - self::$rules['*'][$name] = ['rule' => $item, 'route' => '', 'var' => [], 'option' => $option, 'pattern' => $pattern]; - } - - foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) { - if (!isset(self::$rules[$method][$name])) { - self::$rules[$method][$name] = true; - } elseif (is_array(self::$rules[$method][$name])) { - self::$rules[$method][$name] = array_merge(self::$rules['*'][$name], self::$rules[$method][$name]); - } - } - - } elseif ($routes instanceof \Closure) { - // 闭包注册 - $currentOption = self::getGroup('option'); - $currentPattern = self::getGroup('pattern'); - self::setGroup('', array_merge($currentOption, $option), array_merge($currentPattern, $pattern)); - call_user_func_array($routes, []); - self::setGroup($currentGroup, $currentOption, $currentPattern); - } else { - // 批量注册路由 - self::rule($routes, '', '*', $option, $pattern); - } + return (new RuleGroup($this, $this->group, $name, $route, $option, $pattern)) + ->lazy($this->lazy) + ->mergeRuleRegex($this->mergeRuleRegex); } /** * 注册路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string $rule 路由规则 + * @param mixed $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem */ - public static function any($rule, $route = '', $option = [], $pattern = []) + public function any($rule, $route = '', array $option = [], array $pattern = []) { - self::rule($rule, $route, '*', $option, $pattern); + return $this->rule($rule, $route, '*', $option, $pattern); } /** * 注册GET路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string $rule 路由规则 + * @param mixed $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem */ - public static function get($rule, $route = '', $option = [], $pattern = []) + public function get($rule, $route = '', array $option = [], array $pattern = []) { - self::rule($rule, $route, 'GET', $option, $pattern); + return $this->rule($rule, $route, 'GET', $option, $pattern); } /** * 注册POST路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string $rule 路由规则 + * @param mixed $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem */ - public static function post($rule, $route = '', $option = [], $pattern = []) + public function post($rule, $route = '', array $option = [], array $pattern = []) { - self::rule($rule, $route, 'POST', $option, $pattern); + return $this->rule($rule, $route, 'POST', $option, $pattern); } /** * 注册PUT路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string $rule 路由规则 + * @param mixed $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem */ - public static function put($rule, $route = '', $option = [], $pattern = []) + public function put($rule, $route = '', array $option = [], array $pattern = []) { - self::rule($rule, $route, 'PUT', $option, $pattern); + return $this->rule($rule, $route, 'PUT', $option, $pattern); } /** * 注册DELETE路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string $rule 路由规则 + * @param mixed $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem */ - public static function delete($rule, $route = '', $option = [], $pattern = []) + public function delete($rule, $route = '', array $option = [], array $pattern = []) { - self::rule($rule, $route, 'DELETE', $option, $pattern); + return $this->rule($rule, $route, 'DELETE', $option, $pattern); } /** * 注册PATCH路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string $rule 路由规则 + * @param mixed $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem */ - public static function patch($rule, $route = '', $option = [], $pattern = []) + public function patch($rule, $route = '', array $option = [], array $pattern = []) { - self::rule($rule, $route, 'PATCH', $option, $pattern); + return $this->rule($rule, $route, 'PATCH', $option, $pattern); } /** * 注册资源路由 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return Resource */ - public static function resource($rule, $route = '', $option = [], $pattern = []) + public function resource($rule, $route = '', array $option = [], array $pattern = []) { - if (is_array($rule)) { - foreach ($rule as $key => $val) { - if (is_array($val)) { - list($val, $option, $pattern) = array_pad($val, 3, []); - } - self::resource($key, $val, $option, $pattern); - } - } else { - if (strpos($rule, '.')) { - // 注册嵌套资源路由 - $array = explode('.', $rule); - $last = array_pop($array); - $item = []; - foreach ($array as $val) { - $item[] = $val . '/:' . (isset($option['var'][$val]) ? $option['var'][$val] : $val . '_id'); - } - $rule = implode('/', $item) . '/' . $last; - } - // 注册资源路由 - foreach (self::$rest as $key => $val) { - if ((isset($option['only']) && !in_array($key, $option['only'])) - || (isset($option['except']) && in_array($key, $option['except']))) { - continue; - } - if (isset($last) && strpos($val[1], ':id') && isset($option['var'][$last])) { - $val[1] = str_replace(':id', ':' . $option['var'][$last], $val[1]); - } elseif (strpos($val[1], ':id') && isset($option['var'][$rule])) { - $val[1] = str_replace(':id', ':' . $option['var'][$rule], $val[1]); - } - $item = ltrim($rule . $val[1], '/'); - $option['rest'] = $key; - self::rule($item . '$', $route . '/' . $val[2], $val[0], $option, $pattern); - } - } + return (new Resource($this, $this->group, $rule, $route, $option, $pattern, $this->rest)) + ->lazy($this->lazy); } /** - * 注册控制器路由 操作方法对应不同的请求后缀 + * 注册控制器路由 操作方法对应不同的请求前缀 * @access public - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @param array $pattern 变量规则 - * @return void + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleGroup */ - public static function controller($rule, $route = '', $option = [], $pattern = []) + public function controller($rule, $route = '', array $option = [], array $pattern = []) { - foreach (self::$methodPrefix as $type => $val) { - self::$type($rule . '/:action', $route . '/' . $val . ':action', $option, $pattern); + $group = new RuleGroup($this, $this->group, $rule, null, $option, $pattern); + + foreach ($this->methodPrefix as $type => $val) { + $group->addRule('', $val . '', $type); } + + return $group->prefix($route . '/'); + } + + /** + * 注册视图路由 + * @access public + * @param string|array $rule 路由规则 + * @param string $template 路由模板地址 + * @param array $vars 模板变量 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem + */ + public function view($rule, $template = '', array $vars = [], array $option = [], array $pattern = []) + { + return $this->rule($rule, $template, 'GET', $option, $pattern)->view($vars); + } + + /** + * 注册重定向路由 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $status 状态码 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return RuleItem + */ + public function redirect($rule, $route = '', $status = 301, array $option = [], array $pattern = []) + { + return $this->rule($rule, $route, '*', $option, $pattern)->redirect()->status($status); } /** * 注册别名路由 * @access public - * @param string|array $rule 路由别名 - * @param string $route 路由地址 - * @param array $option 路由参数 - * @return void + * @param string $rule 路由别名 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @return AliasRule */ - public static function alias($rule = null, $route = '', $option = []) + public function alias($rule, $route, array $option = []) { - if (is_array($rule)) { - self::$rules['alias'] = array_merge(self::$rules['alias'], $rule); - } else { - self::$rules['alias'][$rule] = $option ? [$route, $option] : $route; + $aliasRule = new AliasRule($this, $this->group, $rule, $route, $option); + + $this->alias[$rule] = $aliasRule; + + return $aliasRule; + } + + /** + * 获取别名路由定义 + * @access public + * @param string $name 路由别名 + * @return string|array|null + */ + public function getAlias($name = null) + { + if (is_null($name)) { + return $this->alias; } + + return isset($this->alias[$name]) ? $this->alias[$name] : null; } /** * 设置不同请求类型下面的方法前缀 * @access public - * @param string $method 请求类型 - * @param string $prefix 类型前缀 - * @return void + * @param string|array $method 请求类型 + * @param string $prefix 类型前缀 + * @return $this */ - public static function setMethodPrefix($method, $prefix = '') + public function setMethodPrefix($method, $prefix = '') { if (is_array($method)) { - self::$methodPrefix = array_merge(self::$methodPrefix, array_change_key_case($method)); + $this->methodPrefix = array_merge($this->methodPrefix, array_change_key_case($method)); } else { - self::$methodPrefix[strtolower($method)] = $prefix; + $this->methodPrefix[strtolower($method)] = $prefix; } + + return $this; + } + + /** + * 获取请求类型的方法前缀 + * @access public + * @param string $method 请求类型 + * @param string $prefix 类型前缀 + * @return string|null + */ + public function getMethodPrefix($method) + { + $method = strtolower($method); + + return isset($this->methodPrefix[$method]) ? $this->methodPrefix[$method] : null; } /** * rest方法定义和修改 * @access public - * @param string $name 方法名称 - * @param array|bool $resource 资源 - * @return void + * @param string $name 方法名称 + * @param array|bool $resource 资源 + * @return $this */ - public static function rest($name, $resource = []) + public function rest($name, $resource = []) { if (is_array($name)) { - self::$rest = $resource ? $name : array_merge(self::$rest, $name); + $this->rest = $resource ? $name : array_merge($this->rest, $name); } else { - self::$rest[$name] = $resource; + $this->rest[$name] = $resource; } + + return $this; + } + + /** + * 获取rest方法定义的参数 + * @access public + * @param string $name 方法名称 + * @return array|null + */ + public function getRest($name = null) + { + if (is_null($name)) { + return $this->rest; + } + + return isset($this->rest[$name]) ? $this->rest[$name] : null; } /** * 注册未匹配路由规则后的处理 * @access public - * @param string $route 路由地址 - * @param string $method 请求类型 - * @param array $option 路由参数 - * @return void + * @param string $route 路由地址 + * @param string $method 请求类型 + * @param array $option 路由参数 + * @return RuleItem */ - public static function miss($route, $method = '*', $option = []) + public function miss($route, $method = '*', array $option = []) { - self::rule('__miss__', $route, $method, $option, []); + return $this->group->addMissRule($route, $method, $option); } /** * 注册一个自动解析的URL路由 * @access public - * @param string $route 路由地址 - * @return void + * @param string $route 路由地址 + * @return RuleItem */ - public static function auto($route) + public function auto($route) { - self::rule('__auto__', $route, '*', [], []); - } - - /** - * 获取或者批量设置路由定义 - * @access public - * @param mixed $rules 请求类型或者路由定义数组 - * @return array - */ - public static function rules($rules = '') - { - if (is_array($rules)) { - self::$rules = $rules; - } elseif ($rules) { - return true === $rules ? self::$rules : self::$rules[strtolower($rules)]; - } else { - $rules = self::$rules; - unset($rules['pattern'], $rules['alias'], $rules['domain'], $rules['name']); - return $rules; - } - } - - /** - * 检测子域名部署 - * @access public - * @param Request $request Request请求对象 - * @param array $currentRules 当前路由规则 - * @param string $method 请求类型 - * @return void - */ - public static function checkDomain($request, &$currentRules, $method = 'get') - { - // 域名规则 - $rules = self::$rules['domain']; - // 开启子域名部署 支持二级和三级域名 - if (!empty($rules)) { - $host = $request->host(); - if (isset($rules[$host])) { - // 完整域名或者IP配置 - $item = $rules[$host]; - } else { - $rootDomain = Config::get('url_domain_root'); - if ($rootDomain) { - // 配置域名根 例如 thinkphp.cn 163.com.cn 如果是国家级域名 com.cn net.cn 之类的域名需要配置 - $domain = explode('.', rtrim(stristr($host, $rootDomain, true), '.')); - } else { - $domain = explode('.', $host, -2); - } - // 子域名配置 - if (!empty($domain)) { - // 当前子域名 - $subDomain = implode('.', $domain); - self::$subDomain = $subDomain; - $domain2 = array_pop($domain); - if ($domain) { - // 存在三级域名 - $domain3 = array_pop($domain); - } - if ($subDomain && isset($rules[$subDomain])) { - // 子域名配置 - $item = $rules[$subDomain]; - } elseif (isset($rules['*.' . $domain2]) && !empty($domain3)) { - // 泛三级域名 - $item = $rules['*.' . $domain2]; - $panDomain = $domain3; - } elseif (isset($rules['*']) && !empty($domain2)) { - // 泛二级域名 - if ('www' != $domain2) { - $item = $rules['*']; - $panDomain = $domain2; - } - } - } - } - if (!empty($item)) { - if (isset($panDomain)) { - // 保存当前泛域名 - $request->route(['__domain__' => $panDomain]); - } - if (isset($item['[bind]'])) { - // 解析子域名部署规则 - list($rule, $option, $pattern) = $item['[bind]']; - if (!empty($option['https']) && !$request->isSsl()) { - // https检测 - throw new HttpException(404, 'must use https request:' . $host); - } - - if (strpos($rule, '?')) { - // 传入其它参数 - $array = parse_url($rule); - $result = $array['path']; - parse_str($array['query'], $params); - if (isset($panDomain)) { - $pos = array_search('*', $params); - if (false !== $pos) { - // 泛域名作为参数 - $params[$pos] = $panDomain; - } - } - $_GET = array_merge($_GET, $params); - } else { - $result = $rule; - } - - if (0 === strpos($result, '\\')) { - // 绑定到命名空间 例如 \app\index\behavior - self::$bind = ['type' => 'namespace', 'namespace' => $result]; - } elseif (0 === strpos($result, '@')) { - // 绑定到类 例如 @app\index\controller\User - self::$bind = ['type' => 'class', 'class' => substr($result, 1)]; - } else { - // 绑定到模块/控制器 例如 index/user - self::$bind = ['type' => 'module', 'module' => $result]; - } - self::$domainBind = true; - } else { - self::$domainRule = $item; - $currentRules = isset($item[$method]) ? $item[$method] : $item['*']; - } - } - } + return $this->group->addAutoRule($route); } /** * 检测URL路由 * @access public - * @param Request $request Request请求对象 - * @param string $url URL地址 - * @param string $depr URL分隔符 - * @param bool $checkDomain 是否检测域名规则 - * @return false|array + * @param string $url URL地址 + * @param bool $must 是否强制路由 + * @return Dispatch + * @throws RouteNotFoundException */ - public static function check($request, $url, $depr = '/', $checkDomain = false) + public function check($url, $must = false) { - // 分隔符替换 确保路由定义使用统一的分隔符 - $url = str_replace($depr, '|', $url); + // 自动检测域名路由 + $domain = $this->checkDomain(); + $url = str_replace($this->config['pathinfo_depr'], '|', $url); - if (isset(self::$rules['alias'][$url]) || isset(self::$rules['alias'][strstr($url, '|', true)])) { - // 检测路由别名 - $result = self::checkRouteAlias($request, $url, $depr); - if (false !== $result) { - return $result; - } - } - $method = strtolower($request->method()); - // 获取当前请求类型的路由规则 - $rules = isset(self::$rules[$method]) ? self::$rules[$method] : []; - // 检测域名部署 - if ($checkDomain) { - self::checkDomain($request, $rules, $method); - } - // 检测URL绑定 - $return = self::checkUrlBind($url, $rules, $depr); - if (false !== $return) { - return $return; - } - if ('|' != $url) { - $url = rtrim($url, '|'); - } - $item = str_replace('|', '/', $url); - if (isset($rules[$item])) { - // 静态路由规则检测 - $rule = $rules[$item]; - if (true === $rule) { - $rule = self::getRouteExpress($item); - } - if (!empty($rule['route']) && self::checkOption($rule['option'], $request)) { - self::setOption($rule['option']); - return self::parseRule($item, $rule['route'], $url, $rule['option']); - } + $completeMatch = $this->config['route_complete_match']; + + $result = $domain->check($this->request, $url, $completeMatch); + + if (false === $result && !empty($this->cross)) { + // 检测跨域路由 + $result = $this->cross->check($this->request, $url, $completeMatch); } - // 路由规则检测 - if (!empty($rules)) { - return self::checkRoute($request, $rules, $url, $depr); + if (false !== $result) { + // 路由匹配 + return $result; + } elseif ($must) { + // 强制路由不匹配则抛出异常 + throw new RouteNotFoundException(); } - return false; - } - private static function getRouteExpress($key) - { - return self::$domainRule ? self::$domainRule['*'][$key] : self::$rules['*'][$key]; + // 默认路由解析 + return new UrlDispatch($this->request, $this->group, $url, [ + 'auto_search' => $this->autoSearchController, + ]); } /** - * 检测路由规则 - * @access private - * @param Request $request - * @param array $rules 路由规则 - * @param string $url URL地址 - * @param string $depr URL分割符 - * @param string $group 路由分组名 - * @param array $options 路由参数(分组) - * @return mixed + * 检测域名的路由规则 + * @access protected + * @return Domain */ - private static function checkRoute($request, $rules, $url, $depr = '/', $group = '', $options = []) + protected function checkDomain() { - foreach ($rules as $key => $item) { - if (true === $item) { - $item = self::getRouteExpress($key); - } - if (!isset($item['rule'])) { - continue; - } - $rule = $item['rule']; - $route = $item['route']; - $vars = $item['var']; - $option = $item['option']; - $pattern = $item['pattern']; + // 获取当前子域名 + $subDomain = $this->request->subDomain(); - // 检查参数有效性 - if (!self::checkOption($option, $request)) { - continue; + $item = false; + + if ($subDomain && count($this->domains) > 1) { + $domain = explode('.', $subDomain); + $domain2 = array_pop($domain); + + if ($domain) { + // 存在三级域名 + $domain3 = array_pop($domain); } - if (isset($option['ext'])) { - // 路由ext参数 优先于系统配置的URL伪静态后缀参数 - $url = preg_replace('/\.' . $request->ext() . '$/i', '', $url); + if ($subDomain && isset($this->domains[$subDomain])) { + // 子域名配置 + $item = $this->domains[$subDomain]; + } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) { + // 泛三级域名 + $item = $this->domains['*.' . $domain2]; + $panDomain = $domain3; + } elseif (isset($this->domains['*']) && !empty($domain2)) { + // 泛二级域名 + if ('www' != $domain2) { + $item = $this->domains['*']; + $panDomain = $domain2; + } } - if (is_array($rule)) { - // 分组路由 - $pos = strpos(str_replace('<', ':', $key), ':'); - if (false !== $pos) { - $str = substr($key, 0, $pos); - } else { - $str = $key; - } - if (is_string($str) && $str && 0 !== stripos(str_replace('|', '/', $url), $str)) { - continue; - } - self::setOption($option); - $result = self::checkRoute($request, $rule, $url, $depr, $key, $option); - if (false !== $result) { - return $result; - } - } elseif ($route) { - if ('__miss__' == $rule || '__auto__' == $rule) { - // 指定特殊路由 - $var = trim($rule, '__'); - ${$var} = $item; - continue; - } - if ($group) { - $rule = $group . ($rule ? '/' . ltrim($rule, '/') : ''); - } - - self::setOption($option); - if (isset($options['bind_model']) && isset($option['bind_model'])) { - $option['bind_model'] = array_merge($options['bind_model'], $option['bind_model']); - } - $result = self::checkRule($rule, $route, $url, $pattern, $option, $depr); - if (false !== $result) { - return $result; - } + if (isset($panDomain)) { + // 保存当前泛域名 + $this->request->setPanDomain($panDomain); } } - if (isset($auto)) { - // 自动解析URL地址 - return self::parseUrl($auto['route'] . '/' . $url, $depr); - } elseif (isset($miss)) { - // 未匹配所有路由的路由规则处理 - return self::parseRule('', $miss['route'], $url, $miss['option']); + + if (false === $item) { + // 检测当前完整域名 + $item = $this->domains[$this->host]; } - return false; + + if (is_string($item)) { + $item = $this->domains[$item]; + } + + return $item; } /** - * 检测路由别名 - * @access private - * @param Request $request - * @param string $url URL地址 - * @param string $depr URL分隔符 - * @return mixed - */ - private static function checkRouteAlias($request, $url, $depr) - { - $array = explode('|', $url); - $alias = array_shift($array); - $item = self::$rules['alias'][$alias]; - - if (is_array($item)) { - list($rule, $option) = $item; - $action = $array[0]; - if (isset($option['allow']) && !in_array($action, explode(',', $option['allow']))) { - // 允许操作 - return false; - } elseif (isset($option['except']) && in_array($action, explode(',', $option['except']))) { - // 排除操作 - return false; - } - if (isset($option['method'][$action])) { - $option['method'] = $option['method'][$action]; - } - } else { - $rule = $item; - } - $bind = implode('|', $array); - // 参数有效性检查 - if (isset($option) && !self::checkOption($option, $request)) { - // 路由不匹配 - return false; - } elseif (0 === strpos($rule, '\\')) { - // 路由到类 - return self::bindToClass($bind, substr($rule, 1), $depr); - } elseif (0 === strpos($rule, '@')) { - // 路由到控制器类 - return self::bindToController($bind, substr($rule, 1), $depr); - } else { - // 路由到模块/控制器 - return self::bindToModule($bind, $rule, $depr); - } - } - - /** - * 检测URL绑定 - * @access private - * @param string $url URL地址 - * @param array $rules 路由规则 - * @param string $depr URL分隔符 - * @return mixed - */ - private static function checkUrlBind(&$url, &$rules, $depr = '/') - { - if (!empty(self::$bind)) { - $type = self::$bind['type']; - $bind = self::$bind[$type]; - // 记录绑定信息 - App::$debug && Log::record('[ BIND ] ' . var_export($bind, true), 'info'); - // 如果有URL绑定 则进行绑定检测 - switch ($type) { - case 'class': - // 绑定到类 - return self::bindToClass($url, $bind, $depr); - case 'controller': - // 绑定到控制器类 - return self::bindToController($url, $bind, $depr); - case 'namespace': - // 绑定到命名空间 - return self::bindToNamespace($url, $bind, $depr); - } - } - return false; - } - - /** - * 绑定到类 + * 清空路由规则 * @access public - * @param string $url URL地址 - * @param string $class 类名(带命名空间) - * @param string $depr URL分隔符 - * @return array - */ - public static function bindToClass($url, $class, $depr = '/') - { - $url = str_replace($depr, '|', $url); - $array = explode('|', $url, 2); - $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); - if (!empty($array[1])) { - self::parseUrlParams($array[1]); - } - return ['type' => 'method', 'method' => [$class, $action], 'var' => []]; - } - - /** - * 绑定到命名空间 - * @access public - * @param string $url URL地址 - * @param string $namespace 命名空间 - * @param string $depr URL分隔符 - * @return array - */ - public static function bindToNamespace($url, $namespace, $depr = '/') - { - $url = str_replace($depr, '|', $url); - $array = explode('|', $url, 3); - $class = !empty($array[0]) ? $array[0] : Config::get('default_controller'); - $method = !empty($array[1]) ? $array[1] : Config::get('default_action'); - if (!empty($array[2])) { - self::parseUrlParams($array[2]); - } - return ['type' => 'method', 'method' => [$namespace . '\\' . Loader::parseName($class, 1), $method], 'var' => []]; - } - - /** - * 绑定到控制器类 - * @access public - * @param string $url URL地址 - * @param string $controller 控制器名 (支持带模块名 index/user ) - * @param string $depr URL分隔符 - * @return array - */ - public static function bindToController($url, $controller, $depr = '/') - { - $url = str_replace($depr, '|', $url); - $array = explode('|', $url, 2); - $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); - if (!empty($array[1])) { - self::parseUrlParams($array[1]); - } - return ['type' => 'controller', 'controller' => $controller . '/' . $action, 'var' => []]; - } - - /** - * 绑定到模块/控制器 - * @access public - * @param string $url URL地址 - * @param string $controller 控制器类名(带命名空间) - * @param string $depr URL分隔符 - * @return array - */ - public static function bindToModule($url, $controller, $depr = '/') - { - $url = str_replace($depr, '|', $url); - $array = explode('|', $url, 2); - $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); - if (!empty($array[1])) { - self::parseUrlParams($array[1]); - } - return ['type' => 'module', 'module' => $controller . '/' . $action]; - } - - /** - * 路由参数有效性检查 - * @access private - * @param array $option 路由参数 - * @param Request $request Request对象 - * @return bool - */ - private static function checkOption($option, $request) - { - if ((isset($option['method']) && is_string($option['method']) && false === stripos($option['method'], $request->method())) - || (isset($option['ajax']) && $option['ajax'] && !$request->isAjax()) // Ajax检测 - || (isset($option['ajax']) && !$option['ajax'] && $request->isAjax()) // 非Ajax检测 - || (isset($option['pjax']) && $option['pjax'] && !$request->isPjax()) // Pjax检测 - || (isset($option['pjax']) && !$option['pjax'] && $request->isPjax()) // 非Pjax检测 - || (isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|')) // 伪静态后缀检测 - || (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', '|' . $request->ext() . '|')) - || (isset($option['domain']) && !in_array($option['domain'], [$_SERVER['HTTP_HOST'], self::$subDomain])) // 域名检测 - || (isset($option['https']) && $option['https'] && !$request->isSsl()) // https检测 - || (isset($option['https']) && !$option['https'] && $request->isSsl()) // https检测 - || (!empty($option['before_behavior']) && false === Hook::exec($option['before_behavior'])) // 行为检测 - || (!empty($option['callback']) && is_callable($option['callback']) && false === call_user_func($option['callback'])) // 自定义检测 - ) { - return false; - } - return true; - } - - /** - * 检测路由规则 - * @access private - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param string $url URL地址 - * @param array $pattern 变量规则 - * @param array $option 路由参数 - * @param string $depr URL分隔符(全局) - * @return array|false - */ - private static function checkRule($rule, $route, $url, $pattern, $option, $depr) - { - // 检查完整规则定义 - if (isset($pattern['__url__']) && !preg_match('/^' . $pattern['__url__'] . '/', str_replace('|', $depr, $url))) { - return false; - } - // 检查路由的参数分隔符 - if (isset($option['param_depr'])) { - $url = str_replace(['|', $option['param_depr']], [$depr, '|'], $url); - } - - $len1 = substr_count($url, '|'); - $len2 = substr_count($rule, '/'); - // 多余参数是否合并 - $merge = !empty($option['merge_extra_vars']); - if ($merge && $len1 > $len2) { - $url = str_replace('|', $depr, $url); - $url = implode('|', explode($depr, $url, $len2 + 1)); - } - - if ($len1 >= $len2 || strpos($rule, '[')) { - if (!empty($option['complete_match'])) { - // 完整匹配 - if (!$merge && $len1 != $len2 && (false === strpos($rule, '[') || $len1 > $len2 || $len1 < $len2 - substr_count($rule, '['))) { - return false; - } - } - $pattern = array_merge(self::$rules['pattern'], $pattern); - if (false !== $match = self::match($url, $rule, $pattern)) { - // 匹配到路由规则 - return self::parseRule($rule, $route, $url, $option, $match); - } - } - return false; - } - - /** - * 解析模块的URL地址 [模块/控制器/操作?]参数1=值1&参数2=值2... - * @access public - * @param string $url URL地址 - * @param string $depr URL分隔符 - * @param bool $autoSearch 是否自动深度搜索控制器 - * @return array - */ - public static function parseUrl($url, $depr = '/', $autoSearch = false) - { - - if (isset(self::$bind['module'])) { - $bind = str_replace('/', $depr, self::$bind['module']); - // 如果有模块/控制器绑定 - $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr); - } - $url = str_replace($depr, '|', $url); - list($path, $var) = self::parseUrlPath($url); - $route = [null, null, null]; - if (isset($path)) { - // 解析模块 - $module = Config::get('app_multi_module') ? array_shift($path) : null; - if ($autoSearch) { - // 自动搜索控制器 - $dir = APP_PATH . ($module ? $module . DS : '') . Config::get('url_controller_layer'); - $suffix = App::$suffix || Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : ''; - $item = []; - $find = false; - foreach ($path as $val) { - $item[] = $val; - $file = $dir . DS . str_replace('.', DS, $val) . $suffix . EXT; - $file = pathinfo($file, PATHINFO_DIRNAME) . DS . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . EXT; - if (is_file($file)) { - $find = true; - break; - } else { - $dir .= DS . Loader::parseName($val); - } - } - if ($find) { - $controller = implode('.', $item); - $path = array_slice($path, count($item)); - } else { - $controller = array_shift($path); - } - } else { - // 解析控制器 - $controller = !empty($path) ? array_shift($path) : null; - } - // 解析操作 - $action = !empty($path) ? array_shift($path) : null; - // 解析额外参数 - self::parseUrlParams(empty($path) ? '' : implode('|', $path)); - // 封装路由 - $route = [$module, $controller, $action]; - // 检查地址是否被定义过路由 - $name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action); - $name2 = ''; - if (empty($module) || isset($bind) && $module == $bind) { - $name2 = strtolower(Loader::parseName($controller, 1) . '/' . $action); - } - - if (isset(self::$rules['name'][$name]) || isset(self::$rules['name'][$name2])) { - throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url)); - } - } - return ['type' => 'module', 'module' => $route]; - } - - /** - * 解析URL的pathinfo参数和变量 - * @access private - * @param string $url URL地址 - * @return array - */ - private static function parseUrlPath($url) - { - // 分隔符替换 确保路由定义使用统一的分隔符 - $url = str_replace('|', '/', $url); - $url = trim($url, '/'); - $var = []; - if (false !== strpos($url, '?')) { - // [模块/控制器/操作?]参数1=值1&参数2=值2... - $info = parse_url($url); - $path = explode('/', $info['path']); - parse_str($info['query'], $var); - } elseif (strpos($url, '/')) { - // [模块/控制器/操作] - $path = explode('/', $url); - } else { - $path = [$url]; - } - return [$path, $var]; - } - - /** - * 检测URL和规则路由是否匹配 - * @access private - * @param string $url URL地址 - * @param string $rule 路由规则 - * @param array $pattern 变量规则 - * @return array|false - */ - private static function match($url, $rule, $pattern) - { - $m2 = explode('/', $rule); - $m1 = explode('|', $url); - - $var = []; - foreach ($m2 as $key => $val) { - // val中定义了多个变量 - if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) { - $value = []; - $replace = []; - foreach ($matches[1] as $name) { - if (strpos($name, '?')) { - $name = substr($name, 0, -1); - $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')?'; - } else { - $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')'; - } - $value[] = $name; - } - $val = str_replace($matches[0], $replace, $val); - if (preg_match('/^' . $val . '$/', isset($m1[$key]) ? $m1[$key] : '', $match)) { - array_shift($match); - foreach ($value as $k => $name) { - if (isset($match[$k])) { - $var[$name] = $match[$k]; - } - } - continue; - } else { - return false; - } - } - - if (0 === strpos($val, '[:')) { - // 可选参数 - $val = substr($val, 1, -1); - $optional = true; - } else { - $optional = false; - } - if (0 === strpos($val, ':')) { - // URL变量 - $name = substr($val, 1); - if (!$optional && !isset($m1[$key])) { - return false; - } - if (isset($m1[$key]) && isset($pattern[$name])) { - // 检查变量规则 - if ($pattern[$name] instanceof \Closure) { - $result = call_user_func_array($pattern[$name], [$m1[$key]]); - if (false === $result) { - return false; - } - } elseif (!preg_match('/^' . $pattern[$name] . '$/', $m1[$key])) { - return false; - } - } - $var[$name] = isset($m1[$key]) ? $m1[$key] : ''; - } elseif (!isset($m1[$key]) || 0 !== strcasecmp($val, $m1[$key])) { - return false; - } - } - // 成功匹配后返回URL中的动态变量数组 - return $var; - } - - /** - * 解析规则路由 - * @access private - * @param string $rule 路由规则 - * @param string $route 路由地址 - * @param string $pathinfo URL地址 - * @param array $option 路由参数 - * @param array $matches 匹配的变量 - * @return array - */ - private static function parseRule($rule, $route, $pathinfo, $option = [], $matches = []) - { - $request = Request::instance(); - // 解析路由规则 - if ($rule) { - $rule = explode('/', $rule); - // 获取URL地址中的参数 - $paths = explode('|', $pathinfo); - foreach ($rule as $item) { - $fun = ''; - if (0 === strpos($item, '[:')) { - $item = substr($item, 1, -1); - } - if (0 === strpos($item, ':')) { - $var = substr($item, 1); - $matches[$var] = array_shift($paths); - } else { - // 过滤URL中的静态变量 - array_shift($paths); - } - } - } else { - $paths = explode('|', $pathinfo); - } - - // 获取路由地址规则 - if (is_string($route) && isset($option['prefix'])) { - // 路由地址前缀 - $route = $option['prefix'] . $route; - } - // 替换路由地址中的变量 - if (is_string($route) && !empty($matches)) { - foreach ($matches as $key => $val) { - if (false !== strpos($route, ':' . $key)) { - $route = str_replace(':' . $key, $val, $route); - } - } - } - - // 绑定模型数据 - if (isset($option['bind_model'])) { - $bind = []; - foreach ($option['bind_model'] as $key => $val) { - if ($val instanceof \Closure) { - $result = call_user_func_array($val, [$matches]); - } else { - if (is_array($val)) { - $fields = explode('&', $val[1]); - $model = $val[0]; - $exception = isset($val[2]) ? $val[2] : true; - } else { - $fields = ['id']; - $model = $val; - $exception = true; - } - $where = []; - $match = true; - foreach ($fields as $field) { - if (!isset($matches[$field])) { - $match = false; - break; - } else { - $where[$field] = $matches[$field]; - } - } - if ($match) { - $query = strpos($model, '\\') ? $model::where($where) : Loader::model($model)->where($where); - $result = $query->failException($exception)->find(); - } - } - if (!empty($result)) { - $bind[$key] = $result; - } - } - $request->bind($bind); - } - - // 解析额外参数 - self::parseUrlParams(empty($paths) ? '' : implode('|', $paths), $matches); - // 记录匹配的路由信息 - $request->routeInfo(['rule' => $rule, 'route' => $route, 'option' => $option, 'var' => $matches]); - - // 检测路由after行为 - if (!empty($option['after_behavior'])) { - if ($option['after_behavior'] instanceof \Closure) { - $result = call_user_func_array($option['after_behavior'], []); - } else { - foreach ((array) $option['after_behavior'] as $behavior) { - $result = Hook::exec($behavior, ''); - if (!is_null($result)) { - break; - } - } - } - // 路由规则重定向 - if ($result instanceof Response) { - return ['type' => 'response', 'response' => $result]; - } elseif (is_array($result)) { - return $result; - } - } - - if ($route instanceof \Closure) { - // 执行闭包 - $result = ['type' => 'function', 'function' => $route]; - } elseif (0 === strpos($route, '/') || strpos($route, '://')) { - // 路由到重定向地址 - $result = ['type' => 'redirect', 'url' => $route, 'status' => isset($option['status']) ? $option['status'] : 301]; - } elseif (false !== strpos($route, '\\')) { - // 路由到方法 - list($path, $var) = self::parseUrlPath($route); - $route = str_replace('/', '@', implode('/', $path)); - $method = strpos($route, '@') ? explode('@', $route) : $route; - $result = ['type' => 'method', 'method' => $method, 'var' => $var]; - } elseif (0 === strpos($route, '@')) { - // 路由到控制器 - $route = substr($route, 1); - list($route, $var) = self::parseUrlPath($route); - $result = ['type' => 'controller', 'controller' => implode('/', $route), 'var' => $var]; - $request->action(array_pop($route)); - $request->controller($route ? array_pop($route) : Config::get('default_controller')); - $request->module($route ? array_pop($route) : Config::get('default_module')); - App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : ''); - } else { - // 路由到模块/控制器/操作 - $result = self::parseModule($route); - } - // 开启请求缓存 - if ($request->isGet() && isset($option['cache'])) { - $cache = $option['cache']; - if (is_array($cache)) { - list($key, $expire) = $cache; - } else { - $key = str_replace('|', '/', $pathinfo); - $expire = $cache; - } - $request->cache($key, $expire); - } - return $result; - } - - /** - * 解析URL地址为 模块/控制器/操作 - * @access private - * @param string $url URL地址 - * @return array - */ - private static function parseModule($url) - { - list($path, $var) = self::parseUrlPath($url); - $action = array_pop($path); - $controller = !empty($path) ? array_pop($path) : null; - $module = Config::get('app_multi_module') && !empty($path) ? array_pop($path) : null; - $method = Request::instance()->method(); - if (Config::get('use_action_prefix') && !empty(self::$methodPrefix[$method])) { - // 操作方法前缀支持 - $action = 0 !== strpos($action, self::$methodPrefix[$method]) ? self::$methodPrefix[$method] . $action : $action; - } - // 设置当前请求的路由变量 - Request::instance()->route($var); - // 路由到模块/控制器/操作 - return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => false]; - } - - /** - * 解析URL地址中的参数Request对象 - * @access private - * @param string $rule 路由规则 - * @param array $var 变量 * @return void */ - private static function parseUrlParams($url, &$var = []) + public function clear() { - if ($url) { - if (Config::get('url_param_type')) { - $var += explode('|', $url); - } else { - preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) { - $var[$match[1]] = strip_tags($match[2]); - }, $url); - } - } - // 设置当前请求的参数 - Request::instance()->route($var); + $this->app['rule_name']->clear(); + $this->group->clear(); } - // 分析路由规则中的变量 - private static function parseVar($rule) + /** + * 设置全局的路由分组参数 + * @access public + * @param string $method 方法名 + * @param array $args 调用参数 + * @return RuleGroup + */ + public function __call($method, $args) { - // 提取路由规则中的变量 - $var = []; - foreach (explode('/', $rule) as $val) { - $optional = false; - if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) { - foreach ($matches[1] as $name) { - if (strpos($name, '?')) { - $name = substr($name, 0, -1); - $optional = true; - } else { - $optional = false; - } - $var[$name] = $optional ? 2 : 1; - } - } + return call_user_func_array([$this->group, $method], $args); + } - if (0 === strpos($val, '[:')) { - // 可选参数 - $optional = true; - $val = substr($val, 1, -1); - } - if (0 === strpos($val, ':')) { - // URL变量 - $name = substr($val, 1); - $var[$name] = $optional ? 2 : 1; - } - } - return $var; + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app'], $data['request']); + + return $data; } } diff --git a/thinkphp/library/think/Session.php b/thinkphp/library/think/Session.php index d5a6a5a5a..63ee7a03e 100644 --- a/thinkphp/library/think/Session.php +++ b/thinkphp/library/think/Session.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -15,36 +15,115 @@ use think\exception\ClassNotFoundException; class Session { - protected static $prefix = ''; - protected static $init = null; + /** + * 配置参数 + * @var array + */ + protected $config = []; + + /** + * 前缀 + * @var string + */ + protected $prefix = ''; + + /** + * 是否初始化 + * @var bool + */ + protected $init = null; + + /** + * 锁驱动 + * @var object + */ + protected $lockDriver = null; + + /** + * 锁key + * @var string + */ + protected $sessKey = 'PHPSESSID'; + + /** + * 锁超时时间 + * @var integer + */ + protected $lockTimeout = 3; + + /** + * 是否启用锁机制 + * @var bool + */ + protected $lock = false; + + public function __construct(array $config = []) + { + $this->config = $config; + } /** * 设置或者获取session作用域(前缀) - * @param string $prefix + * @access public + * @param string $prefix * @return string|void */ - public static function prefix($prefix = '') + public function prefix($prefix = '') { + empty($this->init) && $this->boot(); + if (empty($prefix) && null !== $prefix) { - return self::$prefix; + return $this->prefix; } else { - self::$prefix = $prefix; + $this->prefix = $prefix; + } + } + + public static function __make(Config $config) + { + return new static($config->pull('session')); + } + + /** + * 配置 + * @access public + * @param array $config + * @return void + */ + public function setConfig(array $config = []) + { + $this->config = array_merge($this->config, array_change_key_case($config)); + + if (isset($config['prefix'])) { + $this->prefix = $config['prefix']; + } + + if (isset($config['use_lock'])) { + $this->lock = $config['use_lock']; } } + /** + * 设置已经初始化 + * @access public + * @return void + */ + public function inited() + { + $this->init = true; + } + /** * session初始化 - * @param array $config + * @access public + * @param array $config * @return void * @throws \think\Exception */ - public static function init(array $config = []) + public function init(array $config = []) { - if (empty($config)) { - $config = Config::get('session'); - } - // 记录初始化信息 - App::$debug && Log::record('[ SESSION ] INIT ' . var_export($config, true), 'info'); + $config = $config ?: $this->config; + $isDoStart = false; if (isset($config['use_trans_sid'])) { ini_set('session.use_trans_sid', $config['use_trans_sid'] ? 1 : 0); @@ -56,42 +135,57 @@ class Session $isDoStart = true; } - if (isset($config['prefix']) && (self::$prefix === '' || self::$prefix === null)) { - self::$prefix = $config['prefix']; + if (isset($config['prefix'])) { + $this->prefix = $config['prefix']; } + + if (isset($config['use_lock'])) { + $this->lock = $config['use_lock']; + } + if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) { session_id($_REQUEST[$config['var_session_id']]); } elseif (isset($config['id']) && !empty($config['id'])) { session_id($config['id']); } + if (isset($config['name'])) { session_name($config['name']); } + if (isset($config['path'])) { session_save_path($config['path']); } + if (isset($config['domain'])) { ini_set('session.cookie_domain', $config['domain']); } + if (isset($config['expire'])) { ini_set('session.gc_maxlifetime', $config['expire']); ini_set('session.cookie_lifetime', $config['expire']); } + if (isset($config['secure'])) { ini_set('session.cookie_secure', $config['secure']); } + if (isset($config['httponly'])) { ini_set('session.cookie_httponly', $config['httponly']); } + if (isset($config['use_cookies'])) { ini_set('session.use_cookies', $config['use_cookies'] ? 1 : 0); } + if (isset($config['cache_limiter'])) { session_cache_limiter($config['cache_limiter']); } + if (isset($config['cache_expire'])) { session_cache_expire($config['cache_expire']); } + if (!empty($config['type'])) { // 读取session驱动 $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']); @@ -101,42 +195,51 @@ class Session throw new ClassNotFoundException('error session handler:' . $class, $class); } } + if ($isDoStart) { - session_start(); - self::$init = true; + $this->start(); } else { - self::$init = false; + $this->init = false; } + + return $this; } /** * session自动启动或者初始化 + * @access public * @return void */ - public static function boot() + public function boot() { - if (is_null(self::$init)) { - self::init(); - } elseif (false === self::$init) { + if (is_null($this->init)) { + $this->init(); + } + + if (false === $this->init) { if (PHP_SESSION_ACTIVE != session_status()) { - session_start(); + $this->start(); } - self::$init = true; + $this->init = true; } } /** * session设置 - * @param string $name session名称 - * @param mixed $value session值 - * @param string|null $prefix 作用域(前缀) + * @access public + * @param string $name session名称 + * @param mixed $value session值 + * @param string|null $prefix 作用域(前缀) * @return void */ - public static function set($name, $value = '', $prefix = null) + public function set($name, $value, $prefix = null) { - empty(self::$init) && self::boot(); + $this->lock(); + + empty($this->init) && $this->boot(); + + $prefix = !is_null($prefix) ? $prefix : $this->prefix; - $prefix = !is_null($prefix) ? $prefix : self::$prefix; if (strpos($name, '.')) { // 二维数组赋值 list($name1, $name2) = explode('.', $name); @@ -150,51 +253,130 @@ class Session } else { $_SESSION[$name] = $value; } + + $this->unlock(); } /** * session获取 - * @param string $name session名称 - * @param string|null $prefix 作用域(前缀) + * @access public + * @param string $name session名称 + * @param string|null $prefix 作用域(前缀) * @return mixed */ - public static function get($name = '', $prefix = null) + public function get($name = '', $prefix = null) { - empty(self::$init) && self::boot(); - $prefix = !is_null($prefix) ? $prefix : self::$prefix; - if ('' == $name) { - // 获取全部的session - $value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION; - } elseif ($prefix) { - // 获取session - if (strpos($name, '.')) { - list($name1, $name2) = explode('.', $name); - $value = isset($_SESSION[$prefix][$name1][$name2]) ? $_SESSION[$prefix][$name1][$name2] : null; - } else { - $value = isset($_SESSION[$prefix][$name]) ? $_SESSION[$prefix][$name] : null; - } - } else { - if (strpos($name, '.')) { - list($name1, $name2) = explode('.', $name); - $value = isset($_SESSION[$name1][$name2]) ? $_SESSION[$name1][$name2] : null; - } else { - $value = isset($_SESSION[$name]) ? $_SESSION[$name] : null; + $this->lock(); + + empty($this->init) && $this->boot(); + + $prefix = !is_null($prefix) ? $prefix : $this->prefix; + + $value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION; + + if ('' != $name) { + $name = explode('.', $name); + + foreach ($name as $val) { + if (isset($value[$val])) { + $value = $value[$val]; + } else { + $value = null; + break; + } } } + + $this->unlock(); + return $value; } + /** + * session 读写锁驱动实例化 + */ + protected function initDriver() + { + $config = $this->config; + + if (!empty($config['type']) && isset($config['use_lock']) && $config['use_lock']) { + // 读取session驱动 + $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']); + + // 检查驱动类及类中是否存在 lock 和 unlock 函数 + if (class_exists($class) && method_exists($class, 'lock') && method_exists($class, 'unlock')) { + $this->lockDriver = new $class($config); + } + } + + // 通过cookie获得session_id + if (isset($config['name']) && $config['name']) { + $this->sessKey = $config['name']; + } + + if (isset($config['lock_timeout']) && $config['lock_timeout'] > 0) { + $this->lockTimeout = $config['lock_timeout']; + } + } + + /** + * session 读写加锁 + * @access protected + * @return void + */ + protected function lock() + { + if (empty($this->lock)) { + return; + } + + $this->initDriver(); + + if (null !== $this->lockDriver && method_exists($this->lockDriver, 'lock')) { + $t = time(); + // 使用 session_id 作为互斥条件,即只对同一 session_id 的会话互斥。第一次请求没有 session_id + $sessID = isset($_COOKIE[$this->sessKey]) ? $_COOKIE[$this->sessKey] : ''; + + do { + if (time() - $t > $this->lockTimeout) { + $this->unlock(); + } + } while (!$this->lockDriver->lock($sessID, $this->lockTimeout)); + } + } + + /** + * session 读写解锁 + * @access protected + * @return void + */ + protected function unlock() + { + if (empty($this->lock)) { + return; + } + + $this->pause(); + + if ($this->lockDriver && method_exists($this->lockDriver, 'unlock')) { + $sessID = isset($_COOKIE[$this->sessKey]) ? $_COOKIE[$this->sessKey] : ''; + $this->lockDriver->unlock($sessID); + } + } + /** * session获取并删除 - * @param string $name session名称 - * @param string|null $prefix 作用域(前缀) + * @access public + * @param string $name session名称 + * @param string|null $prefix 作用域(前缀) * @return mixed */ - public static function pull($name, $prefix = null) + public function pull($name, $prefix = null) { - $result = self::get($name, $prefix); + $result = $this->get($name, $prefix); + if ($result) { - self::delete($name, $prefix); + $this->delete($name, $prefix); return $result; } else { return; @@ -203,53 +385,63 @@ class Session /** * session设置 下一次请求有效 - * @param string $name session名称 - * @param mixed $value session值 - * @param string|null $prefix 作用域(前缀) + * @access public + * @param string $name session名称 + * @param mixed $value session值 + * @param string|null $prefix 作用域(前缀) * @return void */ - public static function flash($name, $value) + public function flash($name, $value) { - self::set($name, $value); - if (!self::has('__flash__.__time__')) { - self::set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']); + $this->set($name, $value); + + if (!$this->has('__flash__.__time__')) { + $this->set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']); } - self::push('__flash__', $name); + + $this->push('__flash__', $name); } /** * 清空当前请求的session数据 + * @access public * @return void */ - public static function flush() + public function flush() { - if (self::$init) { - $item = self::get('__flash__'); + if (!$this->init) { + return; + } - if (!empty($item)) { - $time = $item['__time__']; - if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) { - unset($item['__time__']); - self::delete($item); - self::set('__flash__', []); - } + $item = $this->get('__flash__'); + + if (!empty($item)) { + $time = $item['__time__']; + + if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) { + unset($item['__time__']); + $this->delete($item); + $this->set('__flash__', []); } } } /** * 删除session数据 - * @param string|array $name session名称 - * @param string|null $prefix 作用域(前缀) + * @access public + * @param string|array $name session名称 + * @param string|null $prefix 作用域(前缀) * @return void */ - public static function delete($name, $prefix = null) + public function delete($name, $prefix = null) { - empty(self::$init) && self::boot(); - $prefix = !is_null($prefix) ? $prefix : self::$prefix; + empty($this->init) && $this->boot(); + + $prefix = !is_null($prefix) ? $prefix : $this->prefix; + if (is_array($name)) { foreach ($name as $key) { - self::delete($key, $prefix); + $this->delete($key, $prefix); } } elseif (strpos($name, '.')) { list($name1, $name2) = explode('.', $name); @@ -269,13 +461,15 @@ class Session /** * 清空session数据 - * @param string|null $prefix 作用域(前缀) + * @access public + * @param string|null $prefix 作用域(前缀) * @return void */ - public static function clear($prefix = null) + public function clear($prefix = null) { - empty(self::$init) && self::boot(); - $prefix = !is_null($prefix) ? $prefix : self::$prefix; + empty($this->init) && $this->boot(); + $prefix = !is_null($prefix) ? $prefix : $this->prefix; + if ($prefix) { unset($_SESSION[$prefix]); } else { @@ -285,81 +479,101 @@ class Session /** * 判断session数据 - * @param string $name session名称 - * @param string|null $prefix + * @access public + * @param string $name session名称 + * @param string|null $prefix * @return bool */ - public static function has($name, $prefix = null) + public function has($name, $prefix = null) { - empty(self::$init) && self::boot(); - $prefix = !is_null($prefix) ? $prefix : self::$prefix; - if (strpos($name, '.')) { - // 支持数组 - list($name1, $name2) = explode('.', $name); - return $prefix ? isset($_SESSION[$prefix][$name1][$name2]) : isset($_SESSION[$name1][$name2]); - } else { - return $prefix ? isset($_SESSION[$prefix][$name]) : isset($_SESSION[$name]); + empty($this->init) && $this->boot(); + + $prefix = !is_null($prefix) ? $prefix : $this->prefix; + $value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION; + + $name = explode('.', $name); + + foreach ($name as $val) { + if (!isset($value[$val])) { + return false; + } else { + $value = $value[$val]; + } } + + return true; } /** * 添加数据到一个session数组 + * @access public * @param string $key * @param mixed $value * @return void */ - public static function push($key, $value) + public function push($key, $value) { - $array = self::get($key); + $array = $this->get($key); + if (is_null($array)) { $array = []; } + $array[] = $value; - self::set($key, $array); + + $this->set($key, $array); } /** * 启动session + * @access public * @return void */ - public static function start() + public function start() { session_start(); - self::$init = true; + + $this->init = true; } /** * 销毁session + * @access public * @return void */ - public static function destroy() + public function destroy() { if (!empty($_SESSION)) { $_SESSION = []; } + session_unset(); session_destroy(); - self::$init = null; + + $this->init = null; + $this->lockDriver = null; } /** * 重新生成session_id - * @param bool $delete 是否删除关联会话文件 + * @access public + * @param bool $delete 是否删除关联会话文件 * @return void */ - private static function regenerate($delete = false) + public function regenerate($delete = false) { session_regenerate_id($delete); } /** * 暂停session + * @access public * @return void */ - public static function pause() + public function pause() { // 暂停session session_write_close(); - self::$init = false; + $this->init = false; } } diff --git a/thinkphp/library/think/Template.php b/thinkphp/library/think/Template.php index 4abf21ce7..2855cbcb2 100644 --- a/thinkphp/library/think/Template.php +++ b/thinkphp/library/think/Template.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -20,14 +20,22 @@ use think\exception\TemplateNotFoundException; */ class Template { - // 模板变量 + protected $app; + /** + * 模板变量 + * @var array + */ protected $data = []; - // 引擎配置 + + /** + * 模板配置参数 + * @var array + */ protected $config = [ 'view_path' => '', // 模板路径 'view_base' => '', 'view_suffix' => 'html', // 默认模板文件后缀 - 'view_depr' => DS, + 'view_depr' => DIRECTORY_SEPARATOR, 'cache_suffix' => 'php', // 默认模板缓存后缀 'tpl_deny_func_list' => 'echo,exit', // 模板引擎禁用函数 'tpl_deny_php' => false, // 默认模板引擎是否禁用PHP原生代码 @@ -50,50 +58,62 @@ class Template 'cache_id' => '', // 模板缓存ID 'tpl_replace_string' => [], 'tpl_var_identify' => 'array', // .语法变量识别,array|object|'', 为空时自动识别 + 'default_filter' => 'htmlentities', // 默认过滤方法 用于普通标签输出 ]; - private $literal = []; - private $includeFile = []; // 记录所有模板包含的文件路径及更新时间 + /** + * 保留内容信息 + * @var array + */ + private $literal = []; + + /** + * 模板包含信息 + * @var array + */ + private $includeFile = []; + + /** + * 模板存储对象 + * @var object + */ protected $storage; /** - * 构造函数 + * 架构函数 * @access public + * @param array $config */ - public function __construct(array $config = []) + public function __construct(App $app, array $config = []) { - $this->config['cache_path'] = TEMP_PATH; - $this->config = array_merge($this->config, $config); - $this->config['taglib_begin'] = $this->stripPreg($this->config['taglib_begin']); - $this->config['taglib_end'] = $this->stripPreg($this->config['taglib_end']); - $this->config['tpl_begin'] = $this->stripPreg($this->config['tpl_begin']); - $this->config['tpl_end'] = $this->stripPreg($this->config['tpl_end']); + $this->app = $app; + $this->config['cache_path'] = $app->getRuntimePath() . 'temp/'; + $this->config = array_merge($this->config, $config); + + $this->config['taglib_begin_origin'] = $this->config['taglib_begin']; + $this->config['taglib_end_origin'] = $this->config['taglib_end']; + + $this->config['taglib_begin'] = preg_quote($this->config['taglib_begin'], '/'); + $this->config['taglib_end'] = preg_quote($this->config['taglib_end'], '/'); + $this->config['tpl_begin'] = preg_quote($this->config['tpl_begin'], '/'); + $this->config['tpl_end'] = preg_quote($this->config['tpl_end'], '/'); // 初始化模板编译存储器 - $type = $this->config['compile_type'] ? $this->config['compile_type'] : 'File'; - $class = false !== strpos($type, '\\') ? $type : '\\think\\template\\driver\\' . ucwords($type); - $this->storage = new $class(); + $type = $this->config['compile_type'] ? $this->config['compile_type'] : 'File'; + + $this->storage = Loader::factory($type, '\\think\\template\\driver\\', null); } - /** - * 字符串替换 避免正则混淆 - * @access private - * @param string $str - * @return string - */ - private function stripPreg($str) + public static function __make(Config $config) { - return str_replace( - ['{', '}', '(', ')', '|', '[', ']', '-', '+', '*', '.', '^', '?'], - ['\{', '\}', '\(', '\)', '\|', '\[', '\]', '\-', '\+', '\*', '\.', '\^', '\?'], - $str); + return new static($config->pull('template')); } /** * 模板变量赋值 * @access public - * @param mixed $name - * @param mixed $value + * @param mixed $name + * @param mixed $value * @return void */ public function assign($name, $value = '') @@ -108,8 +128,8 @@ class Template /** * 模板引擎参数赋值 * @access public - * @param mixed $name - * @param mixed $value + * @param mixed $name + * @param mixed $value */ public function __set($name, $value) { @@ -119,7 +139,7 @@ class Template /** * 模板引擎配置项 * @access public - * @param array|string $config + * @param array|string $config * @return void|array */ public function config($config) @@ -128,8 +148,6 @@ class Template $this->config = array_merge($this->config, $config); } elseif (isset($this->config[$config])) { return $this->config[$config]; - } else { - return; } } @@ -143,26 +161,28 @@ class Template { if ('' == $name) { return $this->data; - } else { - $data = $this->data; - foreach (explode('.', $name) as $key => $val) { - if (isset($data[$val])) { - $data = $data[$val]; - } else { - $data = null; - break; - } - } - return $data; } + + $data = $this->data; + + foreach (explode('.', $name) as $key => $val) { + if (isset($data[$val])) { + $data = $data[$val]; + } else { + $data = null; + break; + } + } + + return $data; } /** * 渲染模板文件 * @access public - * @param string $template 模板文件 - * @param array $vars 模板变量 - * @param array $config 模板参数 + * @param string $template 模板文件 + * @param array $vars 模板变量 + * @param array $config 模板参数 * @return void */ public function fetch($template, $vars = [], $config = []) @@ -170,36 +190,49 @@ class Template if ($vars) { $this->data = $vars; } + if ($config) { $this->config($config); } + + $cache = $this->app['cache']; + if (!empty($this->config['cache_id']) && $this->config['display_cache']) { // 读取渲染缓存 - $cacheContent = Cache::get($this->config['cache_id']); + $cacheContent = $cache->get($this->config['cache_id']); + if (false !== $cacheContent) { echo $cacheContent; return; } } + $template = $this->parseTemplateFile($template); + if ($template) { - $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($template) . '.' . ltrim($this->config['cache_suffix'], '.'); + $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($this->config['layout_on'] . $this->config['layout_name'] . $template) . '.' . ltrim($this->config['cache_suffix'], '.'); + if (!$this->checkCache($cacheFile)) { // 缓存无效 重新模板编译 $content = file_get_contents($template); $this->compiler($content, $cacheFile); } + // 页面缓存 ob_start(); ob_implicit_flush(0); + // 读取编译存储 $this->storage->read($cacheFile, $this->data); + // 获取并清空缓存 $content = ob_get_clean(); + if (!empty($this->config['cache_id']) && $this->config['display_cache']) { // 缓存页面输出 - Cache::set($this->config['cache_id'], $content, $this->config['cache_time']); + $cache->set($this->config['cache_id'], $content, $this->config['cache_time']); } + echo $content; } } @@ -207,9 +240,9 @@ class Template /** * 渲染模板内容 * @access public - * @param string $content 模板内容 - * @param array $vars 模板变量 - * @param array $config 模板参数 + * @param string $content 模板内容 + * @param array $vars 模板变量 + * @param array $config 模板参数 * @return void */ public function display($content, $vars = [], $config = []) @@ -217,14 +250,18 @@ class Template if ($vars) { $this->data = $vars; } + if ($config) { $this->config($config); } + $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($content) . '.' . ltrim($this->config['cache_suffix'], '.'); + if (!$this->checkCache($cacheFile)) { // 缓存无效 模板编译 $this->compiler($content, $cacheFile); } + // 读取编译存储 $this->storage->read($cacheFile, $this->data); } @@ -232,8 +269,8 @@ class Template /** * 设置布局 * @access public - * @param mixed $name 布局模板名称 false 则关闭布局 - * @param string $replace 布局模板内容替换标识 + * @param mixed $name 布局模板名称 false 则关闭布局 + * @param string $replace 布局模板内容替换标识 * @return object */ public function layout($name, $replace = '') @@ -244,14 +281,17 @@ class Template } else { // 开启布局 $this->config['layout_on'] = true; + // 名称必须为字符串 if (is_string($name)) { $this->config['layout_name'] = $name; } + if (!empty($replace)) { $this->config['layout_item'] = $replace; } } + return $this; } @@ -259,32 +299,28 @@ class Template * 检查编译缓存是否有效 * 如果无效则需要重新编译 * @access private - * @param string $cacheFile 缓存文件名 + * @param string $cacheFile 缓存文件名 * @return boolean */ private function checkCache($cacheFile) { - // 未开启缓存功能 - if (!$this->config['tpl_cache']) { - return false; - } - // 缓存文件不存在 - if (!is_file($cacheFile)) { - return false; - } - // 读取缓存文件失败 - if (!$handle = @fopen($cacheFile, "r")) { + if (!$this->config['tpl_cache'] || !is_file($cacheFile) || !$handle = @fopen($cacheFile, "r")) { return false; } + // 读取第一行 preg_match('/\/\*(.+?)\*\//', fgets($handle), $matches); + if (!isset($matches[1])) { return false; } + $includeFile = unserialize($matches[1]); + if (!is_array($includeFile)) { return false; } + // 检查模板文件是否有更新 foreach ($includeFile as $path => $time) { if (is_file($path) && filemtime($path) > $time) { @@ -292,6 +328,7 @@ class Template return false; } } + // 检查编译存储是否有效 return $this->storage->check($cacheFile, $this->config['cache_time']); } @@ -299,23 +336,24 @@ class Template /** * 检查编译缓存是否存在 * @access public - * @param string $cacheId 缓存的id + * @param string $cacheId 缓存的id * @return boolean */ public function isCache($cacheId) { if ($cacheId && $this->config['display_cache']) { // 缓存页面输出 - return Cache::has($cacheId); + return $this->app['cache']->has($cacheId); } + return false; } /** * 编译模板文件内容 * @access private - * @param string $content 模板内容 - * @param string $cacheFile 缓存文件名 + * @param string $content 模板内容 + * @param string $cacheFile 缓存文件名 * @return void */ private function compiler(&$content, $cacheFile) @@ -328,6 +366,7 @@ class Template } else { // 读取布局模板 $layoutFile = $this->parseTemplateFile($this->config['layout_name']); + if ($layoutFile) { // 替换布局的主体内容 $content = str_replace($this->config['layout_item'], $content, file_get_contents($layoutFile)); @@ -339,30 +378,34 @@ class Template // 模板解析 $this->parse($content); + if ($this->config['strip_space']) { /* 去除html空格与换行 */ $find = ['~>\s+<~', '~>(\s+\n|\r)~']; $replace = ['><', '>']; $content = preg_replace($find, $replace, $content); } + // 优化生成的php代码 - $content = preg_replace('/\?>\s*<\?php\s(?!echo\b)/s', '', $content); + $content = preg_replace('/\?>\s*<\?php\s(?!echo\b|\bend)/s', '', $content); + // 模板过滤输出 $replace = $this->config['tpl_replace_string']; $content = str_replace(array_keys($replace), array_values($replace), $content); + // 添加安全代码及模板引用记录 - $content = 'includeFile) . '*/ ?>' . "\n" . $content; + $content = 'includeFile) . '*/ ?>' . "\n" . $content; // 编译存储 $this->storage->write($cacheFile, $content); + $this->includeFile = []; - return; } /** * 模板解析入口 * 支持普通标签和TagLib解析 支持自定义标签库 * @access public - * @param string $content 要解析的模板内容 + * @param string $content 要解析的模板内容 * @return void */ public function parse(&$content) @@ -371,16 +414,22 @@ class Template if (empty($content)) { return; } + // 替换literal标签内容 $this->parseLiteral($content); + // 解析继承 $this->parseExtend($content); + // 解析布局 $this->parseLayout($content); + // 检查include语法 $this->parseInclude($content); + // 替换包含文件中literal标签内容 $this->parseLiteral($content); + // 检查PHP语法 $this->parsePhp($content); @@ -391,6 +440,7 @@ class Template // 当TAGLIB_LOAD配置为true时才会进行检测 if ($this->config['taglib_load']) { $tagLibs = $this->getIncludeTagLib($content); + if (!empty($tagLibs)) { // 对导入的TagLib进行解析 foreach ($tagLibs as $tagLibName) { @@ -398,30 +448,34 @@ class Template } } } + // 预先加载的标签库 无需在每个模板中使用taglib标签加载 但必须使用标签库XML前缀 if ($this->config['taglib_pre_load']) { $tagLibs = explode(',', $this->config['taglib_pre_load']); + foreach ($tagLibs as $tag) { $this->parseTagLib($tag, $content); } } + // 内置标签库 无需使用taglib标签导入就可以使用 并且不需使用标签库XML前缀 $tagLibs = explode(',', $this->config['taglib_build_in']); + foreach ($tagLibs as $tag) { $this->parseTagLib($tag, $content, true); } + // 解析普通模板标签 {$tagName} $this->parseTag($content); // 还原被替换的Literal标签 $this->parseLiteral($content, true); - return; } /** * 检查PHP语法 * @access private - * @param string $content 要解析的模板内容 + * @param string $content 要解析的模板内容 * @return void * @throws \think\Exception */ @@ -429,17 +483,17 @@ class Template { // 短标签的情况要将' . "\n", $content); + // PHP语法检查 if ($this->config['tpl_deny_php'] && false !== strpos($content, 'parseAttr($matches[0]); + if (!$this->config['layout_on'] || $this->config['layout_name'] != $array['name']) { // 读取布局模板 $layoutFile = $this->parseTemplateFile($array['name']); + if ($layoutFile) { $replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item']; // 替换布局的主体内容 @@ -462,7 +518,6 @@ class Template } else { $content = str_replace('{__NOLAYOUT__}', '', $content); } - return; } /** @@ -480,15 +535,19 @@ class Template $array = $this->parseAttr($match[0]); $file = $array['file']; unset($array['file']); + // 分析模板文件名并读取内容 $parseStr = $this->parseTemplateName($file); + foreach ($array as $k => $v) { // 以$开头字符串转换成模板变量 if (0 === strpos($v, '$')) { $v = $this->get(substr($v, 1)); } + $parseStr = str_replace('[' . $k . ']', $v, $parseStr); } + $content = str_replace($match[0], $parseStr, $content); // 再次对包含文件进行模板分析 $func($parseStr); @@ -496,9 +555,9 @@ class Template unset($matches); } }; + // 替换模板中的include标签 $func($content); - return; } /** @@ -512,21 +571,26 @@ class Template $regex = $this->getRegex('extend'); $array = $blocks = $baseBlocks = []; $extend = ''; - $func = function ($template) use (&$func, &$regex, &$array, &$extend, &$blocks, &$baseBlocks) { + + $func = function ($template) use (&$func, &$regex, &$array, &$extend, &$blocks, &$baseBlocks) { if (preg_match($regex, $template, $matches)) { if (!isset($array[$matches['name']])) { $array[$matches['name']] = 1; // 读取继承模板 $extend = $this->parseTemplateName($matches['name']); + // 递归检查继承 $func($extend); + // 取得block标签内容 $blocks = array_merge($blocks, $this->parseBlock($template)); + return; } } else { // 取得顶层模板block标签内容 $baseBlocks = $this->parseBlock($template, true); + if (empty($extend)) { // 无extend标签但有block标签的情况 $extend = $template; @@ -535,28 +599,35 @@ class Template }; $func($content); + if (!empty($extend)) { if ($baseBlocks) { $children = []; foreach ($baseBlocks as $name => $val) { $replace = $val['content']; + if (!empty($children[$name])) { // 如果包含有子block标签 foreach ($children[$name] as $key) { $replace = str_replace($baseBlocks[$key]['begin'] . $baseBlocks[$key]['content'] . $baseBlocks[$key]['end'], $blocks[$key]['content'], $replace); } } + if (isset($blocks[$name])) { // 带有{__block__}表示与所继承模板的相应标签合并,而不是覆盖 $replace = str_replace(['{__BLOCK__}', '{__block__}'], $replace, $blocks[$name]['content']); + if (!empty($val['parent'])) { // 如果不是最顶层的block标签 $parent = $val['parent']; + if (isset($blocks[$parent])) { $blocks[$parent]['content'] = str_replace($blocks[$name]['begin'] . $blocks[$name]['content'] . $blocks[$name]['end'], $replace, $blocks[$parent]['content']); } + $blocks[$name]['content'] = $replace; $children[$parent][] = $name; + continue; } } elseif (!empty($val['parent'])) { @@ -564,16 +635,17 @@ class Template $children[$val['parent']][] = $name; $blocks[$name] = $val; } + if (!$val['parent']) { // 替换模板中的顶级block标签 $extend = str_replace($val['begin'] . $val['content'] . $val['end'], $replace, $extend); } } } + $content = $extend; unset($blocks, $baseBlocks); } - return; } /** @@ -586,9 +658,11 @@ class Template private function parseLiteral(&$content, $restore = false) { $regex = $this->getRegex($restore ? 'restoreliteral' : 'literal'); + if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) { if (!$restore) { $count = count($this->literal); + // 替换literal标签 foreach ($matches as $match) { $this->literal[] = substr($match[0], strlen($match[1]), -strlen($match[2])); @@ -600,12 +674,13 @@ class Template foreach ($matches as $match) { $content = str_replace($match[0], $this->literal[$match[1]], $content); } + // 清空literal记录 $this->literal = []; } + unset($matches); } - return; } /** @@ -619,20 +694,24 @@ class Template { $regex = $this->getRegex('block'); $result = []; + if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { $right = $keys = []; + foreach ($matches as $match) { if (empty($match['name'][0])) { if (count($right) > 0) { - $tag = array_pop($right); - $start = $tag['offset'] + strlen($tag['tag']); - $length = $match[0][1] - $start; + $tag = array_pop($right); + $start = $tag['offset'] + strlen($tag['tag']); + $length = $match[0][1] - $start; + $result[$tag['name']] = [ 'begin' => $tag['tag'], 'content' => substr($content, $start, $length), 'end' => $match[0][0], 'parent' => count($right) ? end($right)['name'] : '', ]; + $keys[$tag['name']] = $match[0][1]; } } else { @@ -644,12 +723,15 @@ class Template ]; } } + unset($right, $matches); + if ($sort) { // 按block标签结束符在模板中的位置排序 array_multisort($keys, $result); } } + return $result; } @@ -666,9 +748,9 @@ class Template if (preg_match($this->getRegex('taglib'), $content, $matches)) { // 替换TagLib标签 $content = str_replace($matches[0], '', $content); + return explode(',', $matches['name']); } - return; } /** @@ -688,9 +770,10 @@ class Template } else { $className = '\\think\\template\\taglib\\' . ucwords($tagLib); } + $tLib = new $className($this); + $tLib->parseTag($content, $hide ? '' : $tagLib); - return; } /** @@ -704,17 +787,19 @@ class Template { $regex = '/\s+(?>(?P[\w-]+)\s*)=(?>\s*)([\"\'])(?P(?:(?!\\2).)*)\\2/is'; $array = []; + if (preg_match_all($regex, $str, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $array[$match['name']] = $match['value']; } unset($matches); } + if (!empty($name) && isset($array[$name])) { return $array[$name]; - } else { - return $array; } + + return $array; } /** @@ -727,10 +812,12 @@ class Template private function parseTag(&$content) { $regex = $this->getRegex('tag'); + if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $str = stripslashes($match[1]); $flag = substr($str, 0, 1); + switch ($flag) { case '$': // 解析模板变量 格式 {$varName} @@ -738,20 +825,24 @@ class Template if (false !== $pos = strpos($str, '?')) { $array = preg_split('/([!=]={1,2}|(?<]={0,1})/', substr($str, 0, $pos), 2, PREG_SPLIT_DELIM_CAPTURE); $name = $array[0]; + $this->parseVar($name); - $this->parseVarFunction($name); + //$this->parseVarFunction($name); $str = trim(substr($str, $pos + 1)); $this->parseVar($str); $first = substr($str, 0, 1); + if (strpos($name, ')')) { // $name为对象或是自动识别,或者含有函数 if (isset($array[1])) { $this->parseVar($array[2]); $name .= $array[1] . $array[2]; } + switch ($first) { case '?': + $this->parseVarFunction($name); $str = ''; break; case '=': @@ -762,32 +853,45 @@ class Template } } else { if (isset($array[1])) { + $express = true; $this->parseVar($array[2]); - $_name = ' && ' . $name . $array[1] . $array[2]; + $express = $name . $array[1] . $array[2]; } else { - $_name = ''; + $express = false; } + + if (in_array($first, ['?', '=', ':'])) { + $str = trim(substr($str, 1)); + if ('$' == substr($str, 0, 1)) { + $str = $this->parseVarFunction($str); + } + } + // $name为数组 switch ($first) { case '?': // {$varname??'xxx'} $varname有定义则输出$varname,否则输出xxx - $str = ''; + $str = 'parseVarFunction($name) . ' : ' . $str . '; ?>'; break; case '=': // {$varname?='xxx'} $varname为真时才输出xxx - $str = ''; + $str = ''; break; case ':': // {$varname?:'xxx'} $varname为真时输出$varname,否则输出xxx - $str = ''; + $str = 'parseVarFunction($name) . ' : ' . $str . '; ?>'; break; default: if (strpos($str, ':')) { // {$varname ? 'a' : 'b'} $varname为真时输出a,否则输出b - $str = ''; - } else { - $str = ''; + $array = explode(':', $str, 2); + + $array[0] = '$' == substr(trim($array[0]), 0, 1) ? $this->parseVarFunction($array[0]) : $array[0]; + $array[1] = '$' == substr(trim($array[1]), 0, 1) ? $this->parseVarFunction($array[1]) : $array[1]; + + $str = implode(' : ', $array); } + $str = ''; } } } else { @@ -826,11 +930,12 @@ class Template $str = $this->config['tpl_begin'] . $str . $this->config['tpl_end']; break; } + $content = str_replace($match[0], $str, $content); } + unset($matches); } - return; } /** @@ -843,10 +948,13 @@ class Template public function parseVar(&$varStr) { $varStr = trim($varStr); + if (preg_match_all('/\$[a-zA-Z_](?>\w*)(?:[:\.][0-9a-zA-Z_](?>\w*))+/', $varStr, $matches, PREG_OFFSET_CAPTURE)) { static $_varParseList = []; + while ($matches[0]) { $match = array_pop($matches[0]); + //如果已经解析过该变量字串,则直接返回变量值 if (isset($_varParseList[$match[0]])) { $parseStr = $_varParseList[$match[0]]; @@ -854,6 +962,7 @@ class Template if (strpos($match[0], '.')) { $vars = explode('.', $match[0]); $first = array_shift($vars); + if ('$Think' == $first) { // 所有以Think.打头的以特殊变量对待 无需模板赋值就可以输出 $parseStr = $this->parseThinkVar($vars); @@ -868,7 +977,8 @@ class Template } else { $params = ''; } - $parseStr = '\think\Request::instance()->' . $method . '(' . $params . ')'; + + $parseStr = 'app(\'request\')->' . $method . '(' . $params . ')'; } else { switch ($this->config['tpl_var_identify']) { case 'array': // 识别为数组 @@ -884,45 +994,81 @@ class Template } else { $parseStr = str_replace(':', '->', $match[0]); } + $_varParseList[$match[0]] = $parseStr; } + $varStr = substr_replace($varStr, $parseStr, $match[1], strlen($match[0])); } unset($matches); } - return; } /** * 对模板中使用了函数的变量进行解析 * 格式 {$varname|function1|function2=arg1,arg2} * @access public - * @param string $varStr 变量字符串 + * @param string $varStr 变量字符串 + * @param bool $autoescape 自动转义 * @return void */ - public function parseVarFunction(&$varStr) + public function parseVarFunction(&$varStr, $autoescape = true) { - if (false == strpos($varStr, '|')) { - return; + if (!$autoescape && false === strpos($varStr, '|')) { + return $varStr; + } elseif ($autoescape && !preg_match('/\|(\s)?raw(\||\s)?/i', $varStr)) { + $varStr .= '|' . $this->config['default_filter']; } + static $_varFunctionList = []; - $_key = md5($varStr); + + $_key = md5($varStr); + //如果已经解析过该变量字串,则直接返回变量值 if (isset($_varFunctionList[$_key])) { $varStr = $_varFunctionList[$_key]; } else { $varArray = explode('|', $varStr); + // 取得变量名称 - $name = array_shift($varArray); + $name = trim(array_shift($varArray)); + // 对变量使用函数 $length = count($varArray); + // 取得模板禁止使用函数列表 $template_deny_funs = explode(',', $this->config['tpl_deny_func_list']); + for ($i = 0; $i < $length; $i++) { $args = explode('=', $varArray[$i], 2); + // 模板函数过滤 $fun = trim($args[0]); - switch ($fun) { + if (in_array($fun, $template_deny_funs)) { + continue; + } + + switch (strtolower($fun)) { + case 'raw': + break; + case 'date': + $name = 'date(' . $args[1] . ',!is_numeric(' . $name . ')? strtotime(' . $name . ') : ' . $name . ')'; + break; + case 'first': + $name = 'current(' . $name . ')'; + break; + case 'last': + $name = 'end(' . $name . ')'; + break; + case 'upper': + $name = 'strtoupper(' . $name . ')'; + break; + case 'lower': + $name = 'strtolower(' . $name . ')'; + break; + case 'format': + $name = 'sprintf(' . $args[1] . ',' . $name . ')'; + break; case 'default': // 特殊模板函数 if (false === strpos($name, '(')) { $name = '(isset(' . $name . ') && (' . $name . ' !== \'\')?' . $name . ':' . $args[1] . ')'; @@ -931,26 +1077,25 @@ class Template } break; default: // 通用模板函数 - if (!in_array($fun, $template_deny_funs)) { - if (isset($args[1])) { - if (strstr($args[1], '###')) { - $args[1] = str_replace('###', $name, $args[1]); - $name = "$fun($args[1])"; - } else { - $name = "$fun($name,$args[1])"; - } + if (isset($args[1])) { + if (strstr($args[1], '###')) { + $args[1] = str_replace('###', $name, $args[1]); + $name = "$fun($args[1])"; } else { - if (!empty($args[0])) { - $name = "$fun($name)"; - } + $name = "$fun($name,$args[1])"; + } + } else { + if (!empty($args[0])) { + $name = "$fun($name)"; } } } } + $_varFunctionList[$_key] = $name; $varStr = $name; } - return; + return $varStr; } /** @@ -964,37 +1109,38 @@ class Template { $type = strtoupper(trim(array_shift($vars))); $param = implode('.', $vars); + if ($vars) { switch ($type) { case 'SERVER': - $parseStr = '\\think\\Request::instance()->server(\'' . $param . '\')'; + $parseStr = 'app(\'request\')->server(\'' . $param . '\')'; break; case 'GET': - $parseStr = '\\think\\Request::instance()->get(\'' . $param . '\')'; + $parseStr = 'app(\'request\')->get(\'' . $param . '\')'; break; case 'POST': - $parseStr = '\\think\\Request::instance()->post(\'' . $param . '\')'; + $parseStr = 'app(\'request\')->post(\'' . $param . '\')'; break; case 'COOKIE': - $parseStr = '\\think\\Cookie::get(\'' . $param . '\')'; + $parseStr = 'app(\'cookie\')->get(\'' . $param . '\')'; break; case 'SESSION': - $parseStr = '\\think\\Session::get(\'' . $param . '\')'; + $parseStr = 'app(\'session\')->get(\'' . $param . '\')'; break; case 'ENV': - $parseStr = '\\think\\Request::instance()->env(\'' . $param . '\')'; + $parseStr = 'app(\'request\')->env(\'' . $param . '\')'; break; case 'REQUEST': - $parseStr = '\\think\\Request::instance()->request(\'' . $param . '\')'; + $parseStr = 'app(\'request\')->request(\'' . $param . '\')'; break; case 'CONST': $parseStr = strtoupper($param); break; case 'LANG': - $parseStr = '\\think\\Lang::get(\'' . $param . '\')'; + $parseStr = 'app(\'lang\')->get(\'' . $param . '\')'; break; case 'CONFIG': - $parseStr = '\\think\\Config::get(\'' . $param . '\')'; + $parseStr = 'app(\'config\')->get(\'' . $param . '\')'; break; default: $parseStr = '\'\''; @@ -1006,7 +1152,7 @@ class Template $parseStr = "date('Y-m-d g:i a',time())"; break; case 'VERSION': - $parseStr = 'THINK_VERSION'; + $parseStr = 'app()->version()'; break; case 'LDELIM': $parseStr = '\'' . ltrim($this->config['tpl_begin'], '\\') . '\''; @@ -1022,6 +1168,7 @@ class Template } } } + return $parseStr; } @@ -1035,20 +1182,25 @@ class Template { $array = explode(',', $templateName); $parseStr = ''; + foreach ($array as $templateName) { if (empty($templateName)) { continue; } + if (0 === strpos($templateName, '$')) { //支持加载变量文件名 $templateName = $this->get(substr($templateName, 1)); } + $template = $this->parseTemplateFile($templateName); + if ($template) { // 获取模板文件内容 $parseStr .= file_get_contents($template); } } + return $parseStr; } @@ -1064,27 +1216,31 @@ class Template if (strpos($template, '@')) { list($module, $template) = explode('@', $template); } + if (0 !== strpos($template, '/')) { $template = str_replace(['/', ':'], $this->config['view_depr'], $template); } else { $template = str_replace(['/', ':'], $this->config['view_depr'], substr($template, 1)); } + if ($this->config['view_base']) { - $module = isset($module) ? $module : Request::instance()->module(); - $path = $this->config['view_base'] . ($module ? $module . DS : ''); + $module = isset($module) ? $module : $this->app['request']->module(); + $path = $this->config['view_base'] . ($module ? $module . DIRECTORY_SEPARATOR : ''); } else { - $path = isset($module) ? APP_PATH . $module . DS . basename($this->config['view_path']) . DS : $this->config['view_path']; + $path = isset($module) ? $this->app->getAppPath() . $module . DIRECTORY_SEPARATOR . basename($this->config['view_path']) . DIRECTORY_SEPARATOR : $this->config['view_path']; } + $template = $path . $template . '.' . ltrim($this->config['view_suffix'], '.'); } if (is_file($template)) { // 记录模板文件的更新时间 $this->includeFile[$template] = filemtime($template); + return $template; - } else { - throw new TemplateNotFoundException('template not exists:' . $template, $template); } + + throw new TemplateNotFoundException('template not exists:' . $template, $template); } /** @@ -1099,6 +1255,7 @@ class Template if ('tag' == $tagName) { $begin = $this->config['tpl_begin']; $end = $this->config['tpl_end']; + if (strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1) { $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>[^' . $end . ']*))' . $end; } else { @@ -1108,12 +1265,13 @@ class Template $begin = $this->config['taglib_begin']; $end = $this->config['taglib_end']; $single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false; + switch ($tagName) { case 'block': if ($single) { - $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end; + $regex = $begin . '(?:' . $tagName . '\b\s+(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end; } else { - $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end; + $regex = $begin . '(?:' . $tagName . '\b\s+(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end; } break; case 'literal': @@ -1139,13 +1297,22 @@ class Template $name = 'name'; } if ($single) { - $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end; + $regex = $begin . $tagName . '\b\s+(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end; } else { - $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end; + $regex = $begin . $tagName . '\b\s+(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end; } break; } } + return '/' . $regex . '/is'; } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app'], $data['storage']); + + return $data; + } } diff --git a/thinkphp/library/think/Url.php b/thinkphp/library/think/Url.php index 8e716a5b5..a339f8de2 100644 --- a/thinkphp/library/think/Url.php +++ b/thinkphp/library/think/Url.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,39 +13,88 @@ namespace think; class Url { - // 生成URL地址的root - protected static $root; - protected static $bindCheck; + /** + * 配置参数 + * @var array + */ + protected $config = []; + + /** + * ROOT地址 + * @var string + */ + protected $root; + + /** + * 绑定检查 + * @var bool + */ + protected $bindCheck; + + /** + * 应用对象 + * @var App + */ + protected $app; + + public function __construct(App $app, array $config = []) + { + $this->app = $app; + $this->config = $config; + + if (is_file($app->getRuntimePath() . 'route.php')) { + // 读取路由映射文件 + $app['route']->setName(include $app->getRuntimePath() . 'route.php'); + } + } + + /** + * 初始化 + * @access public + * @param array $config + * @return void + */ + public function init(array $config = []) + { + $this->config = array_merge($this->config, array_change_key_case($config)); + } + + public static function __make(App $app, Config $config) + { + return new static($app, $config->pull('app')); + } /** * URL生成 支持路由反射 - * @param string $url 路由地址 - * @param string|array $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2'] - * @param string|bool $suffix 伪静态后缀,默认为true表示获取配置值 - * @param boolean|string $domain 是否显示域名 或者直接传入域名 + * @access public + * @param string $url 路由地址 + * @param string|array $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2'] + * @param string|bool $suffix 伪静态后缀,默认为true表示获取配置值 + * @param boolean|string $domain 是否显示域名 或者直接传入域名 * @return string */ - public static function build($url = '', $vars = '', $suffix = true, $domain = false) + public function build($url = '', $vars = '', $suffix = true, $domain = false) { - if (false === $domain && Route::rules('domain')) { - $domain = true; - } // 解析URL if (0 === strpos($url, '[') && $pos = strpos($url, ']')) { // [name] 表示使用路由命名标识生成URL $name = substr($url, 1, $pos - 1); $url = 'name' . substr($url, $pos + 1); } + if (false === strpos($url, '://') && 0 !== strpos($url, '/')) { $info = parse_url($url); $url = !empty($info['path']) ? $info['path'] : ''; + if (isset($info['fragment'])) { // 解析锚点 $anchor = $info['fragment']; + if (false !== strpos($anchor, '?')) { // 解析参数 list($anchor, $info['query']) = explode('?', $anchor, 2); } + if (false !== strpos($anchor, '@')) { // 解析域名 list($anchor, $domain) = explode('@', $anchor, 2); @@ -63,23 +112,26 @@ class Url } if ($url) { - $rule = Route::name(isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '')); + $checkName = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : ''); + $checkDomain = $domain && is_string($domain) ? $domain : null; + + $rule = $this->app['route']->getName($checkName, $checkDomain); + if (is_null($rule) && isset($info['query'])) { - $rule = Route::name($url); + $rule = $this->app['route']->getName($url); // 解析地址里面参数 合并到vars parse_str($info['query'], $params); $vars = array_merge($params, $vars); unset($info['query']); } } - if (!empty($rule) && $match = self::getRuleUrl($rule, $vars)) { + + if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) { // 匹配路由命名标识 $url = $match[0]; - // 替换可选分隔符 - $url = preg_replace(['/(\W)\?$/', '/(\W)\?/'], ['', '\1'], $url); - if (!empty($match[1])) { - $domain = $match[1]; - } + + $domain = $match[1]; + if (!is_null($match[2])) { $suffix = $match[2]; } @@ -87,14 +139,14 @@ class Url throw new \InvalidArgumentException('route name not exists:' . $name); } else { // 检查别名路由 - $alias = Route::rules('alias'); + $alias = $this->app['route']->getAlias(); $matchAlias = false; + if ($alias) { // 别名路由解析 - foreach ($alias as $key => $val) { - if (is_array($val)) { - $val = $val[0]; - } + foreach ($alias as $key => $item) { + $val = $item->getRoute(); + if (0 === strpos($url, $val)) { $url = $key . substr($url, strlen($val)); $matchAlias = true; @@ -102,10 +154,31 @@ class Url } } } + if (!$matchAlias) { // 路由标识不存在 直接解析 - $url = self::parseUrl($url, $domain); + $url = $this->parseUrl($url); } + + // 检测URL绑定 + if (!$this->bindCheck) { + $bind = $this->app['route']->getBind($domain && is_string($domain) ? $domain : null); + + if ($bind && 0 === strpos($url, $bind)) { + $url = substr($url, strlen($bind) + 1); + } else { + $binds = $this->app['route']->getBind(true); + + foreach ($binds as $key => $val) { + if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) { + $url = substr($url, strlen($val) + 1); + $domain = $key; + break; + } + } + } + } + if (isset($info['query'])) { // 解析地址里面参数 合并到vars parse_str($info['query'], $params); @@ -113,32 +186,29 @@ class Url } } - // 检测URL绑定 - if (!self::$bindCheck) { - $type = Route::getBind('type'); - if ($type) { - $bind = Route::getBind($type); - if (0 === strpos($url, $bind)) { - $url = substr($url, strlen($bind) + 1); - } - } - } // 还原URL分隔符 - $depr = Config::get('pathinfo_depr'); + $depr = $this->config['pathinfo_depr']; $url = str_replace('/', $depr, $url); // URL后缀 - $suffix = in_array($url, ['/', '']) ? '' : self::parseSuffix($suffix); + if ('/' == substr($url, -1) || '' == $url) { + $suffix = ''; + } else { + $suffix = $this->parseSuffix($suffix); + } + // 锚点 $anchor = !empty($anchor) ? '#' . $anchor : ''; + // 参数组装 if (!empty($vars)) { // 添加参数 - if (Config::get('url_common_param')) { - $vars = urldecode(http_build_query($vars)); + if ($this->config['url_common_param']) { + $vars = http_build_query($vars); $url .= $suffix . '?' . $vars . $anchor; } else { - $paramType = Config::get('url_param_type'); + $paramType = $this->config['url_param_type']; + foreach ($vars as $var => $val) { if ('' !== trim($val)) { if ($paramType) { @@ -148,24 +218,29 @@ class Url } } } + $url .= $suffix . $anchor; } } else { $url .= $suffix . $anchor; } - // 检测域名 - $domain = self::parseDomain($url, $domain); - // URL组装 - $url = $domain . rtrim(self::$root ?: Request::instance()->root(), '/') . '/' . ltrim($url, '/'); - self::$bindCheck = false; + // 检测域名 + $domain = $this->parseDomain($url, $domain); + + // URL组装 + $url = $domain . rtrim($this->root ?: $this->app['request']->root(), '/') . '/' . ltrim($url, '/'); + + $this->bindCheck = false; + return $url; } // 直接解析URL地址 - protected static function parseUrl($url, &$domain) + protected function parseUrl($url) { - $request = Request::instance(); + $request = $this->app['request']; + if (0 === strpos($url, '/')) { // 直接作为路由地址解析 $url = substr($url, 1); @@ -177,67 +252,44 @@ class Url $url = substr($url, 1); } else { // 解析到 模块/控制器/操作 - $module = $request->module(); - $domains = Route::rules('domain'); - if (true === $domain && 2 == substr_count($url, '/')) { - $current = $request->host(); - $match = []; - $pos = []; - foreach ($domains as $key => $item) { - if (isset($item['[bind]']) && 0 === strpos($url, $item['[bind]'][0])) { - $pos[$key] = strlen($item['[bind]'][0]) + 1; - $match[] = $key; - $module = ''; - } - } - if ($match) { - $domain = current($match); - foreach ($match as $item) { - if (0 === strpos($current, $item)) { - $domain = $item; - } - } - self::$bindCheck = true; - $url = substr($url, $pos[$domain]); - } - } elseif ($domain) { - if (isset($domains[$domain]['[bind]'][0])) { - $bindModule = $domains[$domain]['[bind]'][0]; - if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) { - $module = ''; - } - } - } - $module = $module ? $module . '/' : ''; + $module = $request->module(); + $module = $module ? $module . '/' : ''; + $controller = $request->controller(); - $controller = Loader::parseName($request->controller()); if ('' == $url) { - // 空字符串输出当前的 模块/控制器/操作 - $url = $module . $controller . '/' . $request->action(); + $action = $request->action(); } else { $path = explode('/', $url); - $action = Config::get('url_convert') ? strtolower(array_pop($path)) : array_pop($path); - $controller = empty($path) ? $controller : (Config::get('url_convert') ? Loader::parseName(array_pop($path)) : array_pop($path)); + $action = array_pop($path); + $controller = empty($path) ? $controller : array_pop($path); $module = empty($path) ? $module : array_pop($path) . '/'; - $url = $module . $controller . '/' . $action; } + + if ($this->config['url_convert']) { + $action = strtolower($action); + $controller = Loader::parseName($controller); + } + + $url = $module . $controller . '/' . $action; } + return $url; } // 检测域名 - protected static function parseDomain(&$url, $domain) + protected function parseDomain(&$url, $domain) { if (!$domain) { return ''; } - $request = Request::instance(); - $rootDomain = Config::get('url_domain_root'); + + $rootDomain = $this->app['request']->rootDomain(); if (true === $domain) { // 自动判断域名 - $domain = $request->host(); + $domain = $this->config['app_host'] ?: $this->app['request']->host(); + + $domains = $this->app['route']->getDomains(); - $domains = Route::rules('domain'); if ($domains) { $route_domain = array_keys($domains); foreach ($route_domain as $domain_prefix) { @@ -247,6 +299,7 @@ class Url if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) { $url = ltrim($url, $rule); $domain = $key; + // 生成对应子域名 if (!empty($rootDomain)) { $domain .= $rootDomain; @@ -256,68 +309,96 @@ class Url if (!empty($rootDomain)) { $domain .= $rootDomain; } + break; } } } } } - - } else { - if (empty($rootDomain)) { - $host = $request->host(); - $rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host; - } - if (substr_count($domain, '.') < 2 && !strpos($domain, $rootDomain)) { - $domain .= '.' . $rootDomain; - } + } elseif (0 !== strpos($domain, $rootDomain) && false === strpos($domain, '.')) { + $domain .= '.' . $rootDomain; } - return ($request->isSsl() ? 'https://' : 'http://') . $domain; + + if (false !== strpos($domain, '://')) { + $scheme = ''; + } else { + $scheme = $this->app['request']->isSsl() || $this->config['is_https'] ? 'https://' : 'http://'; + + } + + return $scheme . $domain; } // 解析URL后缀 - protected static function parseSuffix($suffix) + protected function parseSuffix($suffix) { if ($suffix) { - $suffix = true === $suffix ? Config::get('url_html_suffix') : $suffix; + $suffix = true === $suffix ? $this->config['url_html_suffix'] : $suffix; + if ($pos = strpos($suffix, '|')) { $suffix = substr($suffix, 0, $pos); } } + return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix; } // 匹配路由地址 - public static function getRuleUrl($rule, &$vars = []) + public function getRuleUrl($rule, &$vars = [], $allowDomain = '') { foreach ($rule as $item) { - list($url, $pattern, $domain, $suffix) = $item; - if (empty($pattern)) { - return [$url, $domain, $suffix]; + list($url, $pattern, $domain, $suffix, $method) = $item; + + if (is_string($allowDomain) && $domain != $allowDomain) { + continue; } + + if (!in_array($this->app['request']->port(), [80, 443])) { + $domain .= ':' . $this->app['request']->port(); + } + + if (empty($pattern)) { + return [rtrim($url, '?/-'), $domain, $suffix]; + } + + $type = $this->config['url_common_param']; + foreach ($pattern as $key => $val) { if (isset($vars[$key])) { - $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], urlencode($vars[$key]), $url); + $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url); unset($vars[$key]); - $result = [$url, $domain, $suffix]; + $url = str_replace(['/?', '-?'], ['/', '-'], $url); + $result = [rtrim($url, '?/-'), $domain, $suffix]; } elseif (2 == $val) { $url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url); - $result = [$url, $domain, $suffix]; + $url = str_replace(['/?', '-?'], ['/', '-'], $url); + $result = [rtrim($url, '?/-'), $domain, $suffix]; } else { break; } } + if (isset($result)) { return $result; } } + return false; } // 指定当前生成URL地址的root - public static function root($root) + public function root($root) { - self::$root = $root; - Request::instance()->root($root); + $this->root = $root; + $this->app['request']->setRoot($root); + } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app']); + + return $data; } } diff --git a/thinkphp/library/think/Validate.php b/thinkphp/library/think/Validate.php index 693846678..12cefc0fc 100644 --- a/thinkphp/library/think/Validate.php +++ b/thinkphp/library/think/Validate.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,147 +12,231 @@ namespace think; use think\exception\ClassNotFoundException; +use think\validate\ValidateRule; class Validate { - // 实例 - protected static $instance; - // 自定义的验证类型 + /** + * 自定义验证类型 + * @var array + */ protected static $type = []; - // 验证类型别名 + /** + * 验证类型别名 + * @var array + */ protected $alias = [ '>' => 'gt', '>=' => 'egt', '<' => 'lt', '<=' => 'elt', '=' => 'eq', 'same' => 'eq', ]; - // 当前验证的规则 + /** + * 当前验证规则 + * @var array + */ protected $rule = []; - // 验证提示信息 + /** + * 验证提示信息 + * @var array + */ protected $message = []; - // 验证字段描述 + + /** + * 验证字段描述 + * @var array + */ protected $field = []; - // 验证规则默认提示信息 + /** + * 默认规则提示 + * @var array + */ protected static $typeMsg = [ - 'require' => ':attribute不能为空', - 'number' => ':attribute必须是数字', - 'float' => ':attribute必须是浮点数', - 'boolean' => ':attribute必须是布尔值', - 'email' => ':attribute格式不符', - 'array' => ':attribute必须是数组', - 'accepted' => ':attribute必须是yes、on或者1', - 'date' => ':attribute格式不符合', - 'file' => ':attribute不是有效的上传文件', - 'image' => ':attribute不是有效的图像文件', - 'alpha' => ':attribute只能是字母', - 'alphaNum' => ':attribute只能是字母和数字', - 'alphaDash' => ':attribute只能是字母、数字和下划线_及破折号-', - 'activeUrl' => ':attribute不是有效的域名或者IP', - 'chs' => ':attribute只能是汉字', - 'chsAlpha' => ':attribute只能是汉字、字母', - 'chsAlphaNum' => ':attribute只能是汉字、字母和数字', - 'chsDash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-', - 'url' => ':attribute不是有效的URL地址', - 'ip' => ':attribute不是有效的IP地址', - 'dateFormat' => ':attribute必须使用日期格式 :rule', - 'in' => ':attribute必须在 :rule 范围内', - 'notIn' => ':attribute不能在 :rule 范围内', - 'between' => ':attribute只能在 :1 - :2 之间', - 'notBetween' => ':attribute不能在 :1 - :2 之间', - 'length' => ':attribute长度不符合要求 :rule', - 'max' => ':attribute长度不能超过 :rule', - 'min' => ':attribute长度不能小于 :rule', - 'after' => ':attribute日期不能小于 :rule', - 'before' => ':attribute日期不能超过 :rule', - 'expire' => '不在有效期内 :rule', - 'allowIp' => '不允许的IP访问', - 'denyIp' => '禁止的IP访问', - 'confirm' => ':attribute和确认字段:2不一致', - 'different' => ':attribute和比较字段:2不能相同', - 'egt' => ':attribute必须大于等于 :rule', - 'gt' => ':attribute必须大于 :rule', - 'elt' => ':attribute必须小于等于 :rule', - 'lt' => ':attribute必须小于 :rule', - 'eq' => ':attribute必须等于 :rule', - 'unique' => ':attribute已存在', - 'regex' => ':attribute不符合指定规则', - 'method' => '无效的请求类型', - 'token' => '令牌数据无效', - 'fileSize' => '上传文件大小不符', - 'fileExt' => '上传文件后缀不符', - 'fileMime' => '上传文件类型不符', - + 'require' => ':attribute require', + 'must' => ':attribute must', + 'number' => ':attribute must be numeric', + 'integer' => ':attribute must be integer', + 'float' => ':attribute must be float', + 'boolean' => ':attribute must be bool', + 'email' => ':attribute not a valid email address', + 'mobile' => ':attribute not a valid mobile', + 'array' => ':attribute must be a array', + 'accepted' => ':attribute must be yes,on or 1', + 'date' => ':attribute not a valid datetime', + 'file' => ':attribute not a valid file', + 'image' => ':attribute not a valid image', + 'alpha' => ':attribute must be alpha', + 'alphaNum' => ':attribute must be alpha-numeric', + 'alphaDash' => ':attribute must be alpha-numeric, dash, underscore', + 'activeUrl' => ':attribute not a valid domain or ip', + 'chs' => ':attribute must be chinese', + 'chsAlpha' => ':attribute must be chinese or alpha', + 'chsAlphaNum' => ':attribute must be chinese,alpha-numeric', + 'chsDash' => ':attribute must be chinese,alpha-numeric,underscore, dash', + 'url' => ':attribute not a valid url', + 'ip' => ':attribute not a valid ip', + 'dateFormat' => ':attribute must be dateFormat of :rule', + 'in' => ':attribute must be in :rule', + 'notIn' => ':attribute be notin :rule', + 'between' => ':attribute must between :1 - :2', + 'notBetween' => ':attribute not between :1 - :2', + 'length' => 'size of :attribute must be :rule', + 'max' => 'max size of :attribute must be :rule', + 'min' => 'min size of :attribute must be :rule', + 'after' => ':attribute cannot be less than :rule', + 'before' => ':attribute cannot exceed :rule', + 'afterWith' => ':attribute cannot be less than :rule', + 'beforeWith' => ':attribute cannot exceed :rule', + 'expire' => ':attribute not within :rule', + 'allowIp' => 'access IP is not allowed', + 'denyIp' => 'access IP denied', + 'confirm' => ':attribute out of accord with :2', + 'different' => ':attribute cannot be same with :2', + 'egt' => ':attribute must greater than or equal :rule', + 'gt' => ':attribute must greater than :rule', + 'elt' => ':attribute must less than or equal :rule', + 'lt' => ':attribute must less than :rule', + 'eq' => ':attribute must equal :rule', + 'unique' => ':attribute has exists', + 'regex' => ':attribute not conform to the rules', + 'method' => 'invalid Request method', + 'token' => 'invalid token', + 'fileSize' => 'filesize not match', + 'fileExt' => 'extensions to upload is not allowed', + 'fileMime' => 'mimetype to upload is not allowed', ]; - // 当前验证场景 + /** + * 当前验证场景 + * @var array + */ protected $currentScene = null; - // 正则表达式 regex = ['zip'=>'\d{6}',...] - protected $regex = []; + /** + * Filter_var 规则 + * @var array + */ + protected $filter = [ + 'email' => FILTER_VALIDATE_EMAIL, + 'ip' => [FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6], + 'integer' => FILTER_VALIDATE_INT, + 'url' => FILTER_VALIDATE_URL, + 'macAddr' => FILTER_VALIDATE_MAC, + 'float' => FILTER_VALIDATE_FLOAT, + ]; - // 验证场景 scene = ['edit'=>'name1,name2,...'] + /** + * 内置正则验证规则 + * @var array + */ + protected $defaultRegex = [ + 'alphaDash' => '/^[A-Za-z0-9\-\_]+$/', + 'chs' => '/^[\x{4e00}-\x{9fa5}]+$/u', + 'chsAlpha' => '/^[\x{4e00}-\x{9fa5}a-zA-Z]+$/u', + 'chsAlphaNum' => '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]+$/u', + 'chsDash' => '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9\_\-]+$/u', + 'mobile' => '/^1[3-9][0-9]\d{8}$/', + 'idCard' => '/(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$)/', + 'zip' => '/\d{6}/', + ]; + + /** + * 验证场景定义 + * @var array + */ protected $scene = []; - // 验证失败错误信息 + /** + * 验证失败错误信息 + * @var array + */ protected $error = []; - // 批量验证 + /** + * 是否批量验证 + * @var bool + */ protected $batch = false; /** - * 构造函数 - * @access public - * @param array $rules 验证规则 - * @param array $message 验证提示信息 - * @param array $field 验证字段描述信息 + * 场景需要验证的规则 + * @var array */ - public function __construct(array $rules = [], $message = [], $field = []) + protected $only = []; + + /** + * 场景需要移除的验证规则 + * @var array + */ + protected $remove = []; + + /** + * 场景需要追加的验证规则 + * @var array + */ + protected $append = []; + + /** + * 验证正则定义 + * @var array + */ + protected $regex = []; + + /** + * 架构函数 + * @access public + * @param array $rules 验证规则 + * @param array $message 验证提示信息 + * @param array $field 验证字段描述信息 + */ + public function __construct(array $rules = [], array $message = [], array $field = []) { - $this->rule = array_merge($this->rule, $rules); + $this->rule = $rules + $this->rule; $this->message = array_merge($this->message, $message); $this->field = array_merge($this->field, $field); } /** - * 实例化验证 + * 创建一个验证器类 * @access public - * @param array $rules 验证规则 - * @param array $message 验证提示信息 - * @param array $field 验证字段描述信息 + * @param array $rules 验证规则 + * @param array $message 验证提示信息 + * @param array $field 验证字段描述信息 * @return Validate */ - public static function make($rules = [], $message = [], $field = []) + public static function make(array $rules = [], array $message = [], array $field = []) { - if (is_null(self::$instance)) { - self::$instance = new self($rules, $message, $field); - } - return self::$instance; + return new self($rules, $message, $field); } /** * 添加字段验证规则 * @access protected - * @param string|array $name 字段名称或者规则数组 - * @param mixed $rule 验证规则 - * @return Validate + * @param string|array $name 字段名称或者规则数组 + * @param mixed $rule 验证规则或者字段描述信息 + * @return $this */ public function rule($name, $rule = '') { if (is_array($name)) { - $this->rule = array_merge($this->rule, $name); + $this->rule = $name + $this->rule; + if (is_array($rule)) { + $this->field = array_merge($this->field, $rule); + } } else { $this->rule[$name] = $rule; } + return $this; } /** - * 注册验证(类型)规则 + * 注册扩展验证(类型)规则 * @access public - * @param string $type 验证规则类型 - * @param mixed $callback callback方法(或闭包) + * @param string $type 验证规则类型 + * @param mixed $callback callback方法(或闭包) * @return void */ public static function extend($type, $callback = null) @@ -165,10 +249,10 @@ class Validate } /** - * 获取验证规则的默认提示信息 - * @access protected - * @param string|array $type 验证规则类型名称或者数组 - * @param string $msg 验证提示信息 + * 设置验证规则的默认提示信息 + * @access public + * @param string|array $type 验证规则类型名称或者数组 + * @param string $msg 验证提示信息 * @return void */ public static function setTypeMsg($type, $msg = null) @@ -183,8 +267,8 @@ class Validate /** * 设置提示信息 * @access public - * @param string|array $name 字段名称 - * @param string $message 提示信息 + * @param string|array $name 字段名称 + * @param string $message 提示信息 * @return Validate */ public function message($name, $message = '') @@ -194,59 +278,119 @@ class Validate } else { $this->message[$name] = $message; } + return $this; } /** * 设置验证场景 * @access public - * @param string|array $name 场景名或者场景设置数组 - * @param mixed $fields 要验证的字段 - * @return Validate + * @param string $name 场景名 + * @return $this */ - public function scene($name, $fields = null) + public function scene($name) { - if (is_array($name)) { - $this->scene = array_merge($this->scene, $name); - }if (is_null($fields)) { - // 设置当前场景 - $this->currentScene = $name; - } else { - // 设置验证场景 - $this->scene[$name] = $fields; - } + // 设置当前场景 + $this->currentScene = $name; + return $this; } /** * 判断是否存在某个验证场景 * @access public - * @param string $name 场景名 + * @param string $name 场景名 * @return bool */ public function hasScene($name) { - return isset($this->scene[$name]); + return isset($this->scene[$name]) || method_exists($this, 'scene' . $name); } /** * 设置批量验证 * @access public - * @param bool $batch 是否批量验证 - * @return Validate + * @param bool $batch 是否批量验证 + * @return $this */ public function batch($batch = true) { $this->batch = $batch; + + return $this; + } + + /** + * 指定需要验证的字段列表 + * @access public + * @param array $fields 字段名 + * @return $this + */ + public function only($fields) + { + $this->only = $fields; + + return $this; + } + + /** + * 移除某个字段的验证规则 + * @access public + * @param string|array $field 字段名 + * @param mixed $rule 验证规则 null 移除所有规则 + * @return $this + */ + public function remove($field, $rule = null) + { + if (is_array($field)) { + foreach ($field as $key => $rule) { + if (is_int($key)) { + $this->remove($rule); + } else { + $this->remove($key, $rule); + } + } + } else { + if (is_string($rule)) { + $rule = explode('|', $rule); + } + + $this->remove[$field] = $rule; + } + + return $this; + } + + /** + * 追加某个字段的验证规则 + * @access public + * @param string|array $field 字段名 + * @param mixed $rule 验证规则 + * @return $this + */ + public function append($field, $rule = null) + { + if (is_array($field)) { + foreach ($field as $key => $rule) { + $this->append($key, $rule); + } + } else { + if (is_string($rule)) { + $rule = explode('|', $rule); + } + + $this->append[$field] = $rule; + } + return $this; } /** * 数据自动验证 * @access public - * @param array $data 数据 - * @param mixed $rules 验证规则 - * @param string $scene 验证场景 + * @param array $data 数据 + * @param mixed $rules 验证规则 + * @param string $scene 验证场景 * @return bool */ public function check($data, $rules = [], $scene = '') @@ -258,37 +402,17 @@ class Validate $rules = $this->rule; } - // 分析验证规则 - $scene = $this->getScene($scene); - if (is_array($scene)) { - // 处理场景验证字段 - $change = []; - $array = []; - foreach ($scene as $k => $val) { - if (is_numeric($k)) { - $array[] = $val; - } else { - $array[] = $k; - $change[$k] = $val; - } + // 获取场景定义 + $this->getScene($scene); + + foreach ($this->append as $key => $rule) { + if (!isset($rules[$key])) { + $rules[$key] = $rule; } } - foreach ($rules as $key => $item) { - // field => rule1|rule2... field=>['rule1','rule2',...] - if (is_numeric($key)) { - // [field,rule1|rule2,msg1|msg2] - $key = $item[0]; - $rule = $item[1]; - if (isset($item[2])) { - $msg = is_string($item[2]) ? explode('|', $item[2]) : $item[2]; - } else { - $msg = []; - } - } else { - $rule = $item; - $msg = []; - } + foreach ($rules as $key => $rule) { + // field => 'rule1|rule2...' field => ['rule1','rule2',...] if (strpos($key, '|')) { // 字段|描述 用于指定属性名称 list($key, $title) = explode('|', $key); @@ -297,28 +421,21 @@ class Validate } // 场景检测 - if (!empty($scene)) { - if ($scene instanceof \Closure && !call_user_func_array($scene, [$key, $data])) { - continue; - } elseif (is_array($scene)) { - if (!in_array($key, $array)) { - continue; - } elseif (isset($change[$key])) { - // 重载某个验证规则 - $rule = $change[$key]; - } - } + if (!empty($this->only) && !in_array($key, $this->only)) { + continue; } - // 获取数据 支持二维数组 + // 获取数据 支持多维数组 $value = $this->getDataValue($data, $key); // 字段验证 if ($rule instanceof \Closure) { - // 匿名函数验证 支持传入当前字段和所有字段两个数据 - $result = call_user_func_array($rule, [$value, $data]); + $result = call_user_func_array($rule, [$value, $data, $title, $this]); + } elseif ($rule instanceof ValidateRule) { + // 验证因子 + $result = $this->checkItem($key, $value, $rule->getRule(), $data, $rule->getTitle() ?: $title, $rule->getMsg()); } else { - $result = $this->checkItem($key, $value, $rule, $data, $title, $msg); + $result = $this->checkItem($key, $value, $rule, $data, $title); } if (true !== $result) { @@ -336,59 +453,100 @@ class Validate } } } + return !empty($this->error) ? false : true; } + /** + * 根据验证规则验证数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rules 验证规则 + * @return bool + */ + public function checkRule($value, $rules) + { + if ($rules instanceof \Closure) { + return call_user_func_array($rules, [$value]); + } elseif ($rules instanceof ValidateRule) { + $rules = $rules->getRule(); + } elseif (is_string($rules)) { + $rules = explode('|', $rules); + } + + foreach ($rules as $key => $rule) { + if ($rule instanceof \Closure) { + $result = call_user_func_array($rule, [$value]); + } else { + // 判断验证类型 + list($type, $rule) = $this->getValidateType($key, $rule); + + $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type]; + + $result = call_user_func_array($callback, [$value, $rule]); + } + + if (true !== $result) { + return $result; + } + } + + return true; + } + /** * 验证单个字段规则 * @access protected - * @param string $field 字段名 - * @param mixed $value 字段值 - * @param mixed $rules 验证规则 - * @param array $data 数据 - * @param string $title 字段描述 - * @param array $msg 提示信息 + * @param string $field 字段名 + * @param mixed $value 字段值 + * @param mixed $rules 验证规则 + * @param array $data 数据 + * @param string $title 字段描述 + * @param array $msg 提示信息 * @return mixed */ protected function checkItem($field, $value, $rules, $data, $title = '', $msg = []) { + if (isset($this->remove[$field]) && true === $this->remove[$field] && empty($this->append[$field])) { + // 字段已经移除 无需验证 + return true; + } + // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...] if (is_string($rules)) { $rules = explode('|', $rules); } - $i = 0; + + if (isset($this->append[$field])) { + // 追加额外的验证规则 + $rules = array_merge($rules, $this->append[$field]); + } + + $i = 0; + $result = true; + foreach ($rules as $key => $rule) { if ($rule instanceof \Closure) { $result = call_user_func_array($rule, [$value, $data]); $info = is_numeric($key) ? '' : $key; } else { // 判断验证类型 - if (is_numeric($key)) { - if (strpos($rule, ':')) { - list($type, $rule) = explode(':', $rule, 2); - if (isset($this->alias[$type])) { - // 判断别名 - $type = $this->alias[$type]; - } - $info = $type; - } elseif (method_exists($this, $rule)) { - $type = $rule; - $info = $rule; - $rule = ''; - } else { - $type = 'is'; - $info = $rule; - } - } else { - $info = $type = $key; + list($type, $rule, $info) = $this->getValidateType($key, $rule); + + if (isset($this->append[$field]) && in_array($info, $this->append[$field])) { + + } elseif (array_key_exists($field, $this->remove) && (null === $this->remove[$field] || in_array($info, $this->remove[$field]))) { + // 规则已经移除 + $i++; + continue; } - // 如果不是require 有数据才会行验证 - if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) { - // 验证类型 - $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type]; + // 验证类型 + if (isset(self::$type[$type])) { + $result = call_user_func_array(self::$type[$type], [$value, $rule, $data, $field, $title]); + } elseif ('must' == $info || 0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) { // 验证数据 - $result = call_user_func_array($callback, [$value, $rule, $data, $field, $title]); + $result = call_user_func_array([$this, $type], [$value, $rule, $data, $field, $title]); } else { $result = true; } @@ -396,37 +554,77 @@ class Validate if (false === $result) { // 验证失败 返回错误信息 - if (isset($msg[$i])) { + if (!empty($msg[$i])) { $message = $msg[$i]; if (is_string($message) && strpos($message, '{%') === 0) { - $message = Lang::get(substr($message, 2, -1)); + $message = facade\Lang::get(substr($message, 2, -1)); } } else { $message = $this->getRuleMsg($field, $title, $info, $rule); } + return $message; } elseif (true !== $result) { // 返回自定义错误信息 if (is_string($result) && false !== strpos($result, ':')) { - $result = str_replace([':attribute', ':rule'], [$title, (string) $rule], $result); + $result = str_replace(':attribute', $title, $result); + + if (strpos($result, ':rule') && is_scalar($rule)) { + $msg = str_replace(':rule', (string) $rule, $result); + } } + return $result; } $i++; } + return $result; } + /** + * 获取当前验证类型及规则 + * @access public + * @param mixed $key + * @param mixed $rule + * @return array + */ + protected function getValidateType($key, $rule) + { + // 判断验证类型 + if (!is_numeric($key)) { + return [$key, $rule, $key]; + } + + if (strpos($rule, ':')) { + list($type, $rule) = explode(':', $rule, 2); + if (isset($this->alias[$type])) { + // 判断别名 + $type = $this->alias[$type]; + } + $info = $type; + } elseif (method_exists($this, $rule)) { + $type = $rule; + $info = $rule; + $rule = ''; + } else { + $type = 'is'; + $info = $rule; + } + + return [$type, $rule, $info]; + } + /** * 验证是否和某个字段的值一致 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 - * @param string $field 字段名 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @param string $field 字段名 * @return bool */ - protected function confirm($value, $rule, $data, $field = '') + public function confirm($value, $rule, $data = [], $field = '') { if ('' == $rule) { if (strpos($field, '_confirm')) { @@ -435,101 +633,110 @@ class Validate $rule = $field . '_confirm'; } } + return $this->getDataValue($data, $rule) === $value; } /** * 验证是否和某个字段的值是否不同 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function different($value, $rule, $data) + public function different($value, $rule, $data = []) { return $this->getDataValue($data, $rule) != $value; } /** * 验证是否大于等于某个值 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function egt($value, $rule, $data) + public function egt($value, $rule, $data = []) { - $val = $this->getDataValue($data, $rule); - return !is_null($val) && $value >= $val; + return $value >= $this->getDataValue($data, $rule); } /** * 验证是否大于某个值 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function gt($value, $rule, $data) + public function gt($value, $rule, $data) { - $val = $this->getDataValue($data, $rule); - return !is_null($val) && $value > $val; + return $value > $this->getDataValue($data, $rule); } /** * 验证是否小于等于某个值 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function elt($value, $rule, $data) + public function elt($value, $rule, $data = []) { - $val = $this->getDataValue($data, $rule); - return !is_null($val) && $value <= $val; + return $value <= $this->getDataValue($data, $rule); } /** * 验证是否小于某个值 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function lt($value, $rule, $data) + public function lt($value, $rule, $data = []) { - $val = $this->getDataValue($data, $rule); - return !is_null($val) && $value < $val; + return $value < $this->getDataValue($data, $rule); } /** * 验证是否等于某个值 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function eq($value, $rule) + public function eq($value, $rule) { return $value == $rule; } /** - * 验证字段值是否为有效格式 - * @access protected - * @param mixed $value 字段值 - * @param string $rule 验证规则 - * @param array $data 验证数据 + * 必须验证 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function is($value, $rule, $data = []) + public function must($value, $rule = null) { - switch ($rule) { + return !empty($value) || '0' == $value; + } + + /** + * 验证字段值是否为有效格式 + * @access public + * @param mixed $value 字段值 + * @param string $rule 验证规则 + * @param array $data 验证数据 + * @return bool + */ + public function is($value, $rule, $data = []) + { + switch (Loader::parseName($rule, 1, false)) { case 'require': // 必须 $result = !empty($value) || '0' == $value; @@ -542,65 +749,21 @@ class Validate // 是否是一个有效日期 $result = false !== strtotime($value); break; - case 'alpha': - // 只允许字母 - $result = $this->regex($value, '/^[A-Za-z]+$/'); - break; - case 'alphaNum': - // 只允许字母和数字 - $result = $this->regex($value, '/^[A-Za-z0-9]+$/'); - break; - case 'alphaDash': - // 只允许字母、数字和下划线 破折号 - $result = $this->regex($value, '/^[A-Za-z0-9\-\_]+$/'); - break; - case 'chs': - // 只允许汉字 - $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}]+$/u'); - break; - case 'chsAlpha': - // 只允许汉字、字母 - $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z]+$/u'); - break; - case 'chsAlphaNum': - // 只允许汉字、字母和数字 - $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]+$/u'); - break; - case 'chsDash': - // 只允许汉字、字母、数字和下划线_及破折号- - $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9\_\-]+$/u'); - break; case 'activeUrl': // 是否为有效的网址 $result = checkdnsrr($value); break; - case 'ip': - // 是否为IP地址 - $result = $this->filter($value, [FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6]); - break; - case 'url': - // 是否为一个URL地址 - $result = $this->filter($value, FILTER_VALIDATE_URL); - break; - case 'float': - // 是否为float - $result = $this->filter($value, FILTER_VALIDATE_FLOAT); - break; - case 'number': - $result = is_numeric($value); - break; - case 'integer': - // 是否为整型 - $result = $this->filter($value, FILTER_VALIDATE_INT); - break; - case 'email': - // 是否为邮箱地址 - $result = $this->filter($value, FILTER_VALIDATE_EMAIL); - break; case 'boolean': + case 'bool': // 是否为布尔值 $result = in_array($value, [true, false, 0, 1, '0', '1'], true); break; + case 'number': + $result = ctype_digit((string) $value); + break; + case 'alphaNum': + $result = ctype_alnum($value); + break; case 'array': // 是否为数组 $result = is_array($value); @@ -618,11 +781,19 @@ class Validate if (isset(self::$type[$rule])) { // 注册的验证规则 $result = call_user_func_array(self::$type[$rule], [$value]); + } elseif (function_exists('ctype_' . $rule)) { + // ctype验证规则 + $ctypeFun = 'ctype_' . $rule; + $result = $ctypeFun($value); + } elseif (isset($this->filter[$rule])) { + // Filter_var验证规则 + $result = $this->filter($value, $this->filter[$rule]); } else { // 正则验证 $result = $this->regex($value, $rule); } } + return $result; } @@ -631,173 +802,176 @@ class Validate { if (function_exists('exif_imagetype')) { return exif_imagetype($image); - } else { + } + + try { $info = getimagesize($image); - return $info[2]; + return $info ? $info[2] : false; + } catch (\Exception $e) { + return false; } } /** * 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function activeUrl($value, $rule) + public function activeUrl($value, $rule = 'MX') { if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) { $rule = 'MX'; } + return checkdnsrr($value, $rule); } /** * 验证是否有效IP - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 ipv4 ipv6 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 ipv4 ipv6 * @return bool */ - protected function ip($value, $rule) + public function ip($value, $rule = 'ipv4') { if (!in_array($rule, ['ipv4', 'ipv6'])) { $rule = 'ipv4'; } + return $this->filter($value, [FILTER_VALIDATE_IP, 'ipv6' == $rule ? FILTER_FLAG_IPV6 : FILTER_FLAG_IPV4]); } /** * 验证上传文件后缀 - * @access protected - * @param mixed $file 上传文件 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $file 上传文件 + * @param mixed $rule 验证规则 * @return bool */ - protected function fileExt($file, $rule) + public function fileExt($file, $rule) { - if (!($file instanceof File)) { - return false; - } - if (is_string($rule)) { - $rule = explode(',', $rule); - } if (is_array($file)) { foreach ($file as $item) { - if (!$item->checkExt($rule)) { + if (!($item instanceof File) || !$item->checkExt($rule)) { return false; } } return true; - } else { + } elseif ($file instanceof File) { return $file->checkExt($rule); } + + return false; } /** * 验证上传文件类型 - * @access protected - * @param mixed $file 上传文件 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $file 上传文件 + * @param mixed $rule 验证规则 * @return bool */ - protected function fileMime($file, $rule) + public function fileMime($file, $rule) { - if (!($file instanceof File)) { - return false; - } - if (is_string($rule)) { - $rule = explode(',', $rule); - } if (is_array($file)) { foreach ($file as $item) { - if (!$item->checkMime($rule)) { + if (!($item instanceof File) || !$item->checkMime($rule)) { return false; } } return true; - } else { + } elseif ($file instanceof File) { return $file->checkMime($rule); } + + return false; } /** * 验证上传文件大小 - * @access protected - * @param mixed $file 上传文件 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $file 上传文件 + * @param mixed $rule 验证规则 * @return bool */ - protected function fileSize($file, $rule) + public function fileSize($file, $rule) { - if (!($file instanceof File)) { - return false; - } if (is_array($file)) { foreach ($file as $item) { - if (!$item->checkSize($rule)) { + if (!($item instanceof File) || !$item->checkSize($rule)) { return false; } } return true; - } else { + } elseif ($file instanceof File) { return $file->checkSize($rule); } + + return false; } /** * 验证图片的宽高及类型 - * @access protected - * @param mixed $file 上传文件 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $file 上传文件 + * @param mixed $rule 验证规则 * @return bool */ - protected function image($file, $rule) + public function image($file, $rule) { if (!($file instanceof File)) { return false; } + if ($rule) { - $rule = explode(',', $rule); + $rule = explode(',', $rule); + list($width, $height, $type) = getimagesize($file->getRealPath()); + if (isset($rule[2])) { $imageType = strtolower($rule[2]); + if ('jpeg' == $imageType) { $imageType = 'jpg'; } + if (image_type_to_extension($type, false) != $imageType) { return false; } } list($w, $h) = $rule; + return $w == $width && $h == $height; - } else { - return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]); } + + return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]); } /** * 验证请求类型 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function method($value, $rule) + public function method($value, $rule) { - $method = Request::instance()->method(); + $method = Container::get('request')->method(); return strtoupper($rule) == $method; } /** * 验证时间和日期是否符合指定格式 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function dateFormat($value, $rule) + public function dateFormat($value, $rule) { $info = date_parse_from_format($rule, $value); return 0 == $info['warning_count'] && 0 == $info['error_count']; @@ -805,76 +979,86 @@ class Validate /** * 验证是否唯一 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名 - * @param array $data 数据 - * @param string $field 验证字段名 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名 + * @param array $data 数据 + * @param string $field 验证字段名 * @return bool */ - protected function unique($value, $rule, $data, $field) + public function unique($value, $rule, $data, $field) { if (is_string($rule)) { $rule = explode(',', $rule); } + if (false !== strpos($rule[0], '\\')) { // 指定模型类 $db = new $rule[0]; } else { try { - $db = Loader::model($rule[0]); + $db = Container::get('app')->model($rule[0]); } catch (ClassNotFoundException $e) { $db = Db::name($rule[0]); } } + $key = isset($rule[1]) ? $rule[1] : $field; if (strpos($key, '^')) { // 支持多个字段验证 $fields = explode('^', $key); foreach ($fields as $key) { - $map[$key] = $data[$key]; + if (isset($data[$key])) { + $map[] = [$key, '=', $data[$key]]; + } } } elseif (strpos($key, '=')) { parse_str($key, $map); + } elseif (isset($data[$field])) { + $map[] = [$key, '=', $data[$field]]; } else { - $map[$key] = $data[$field]; + $map = []; } - $pk = strval(isset($rule[3]) ? $rule[3] : $db->getPk()); - if (isset($rule[2])) { - $map[$pk] = ['neq', $rule[2]]; - } elseif (isset($data[$pk])) { - $map[$pk] = ['neq', $data[$pk]]; + $pk = !empty($rule[3]) ? $rule[3] : $db->getPk(); + + if (is_string($pk)) { + if (isset($rule[2])) { + $map[] = [$pk, '<>', $rule[2]]; + } elseif (isset($data[$pk])) { + $map[] = [$pk, '<>', $data[$pk]]; + } } if ($db->where($map)->field($pk)->find()) { return false; } + return true; } /** * 使用行为类验证 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return mixed */ - protected function behavior($value, $rule, $data) + public function behavior($value, $rule, $data) { - return Hook::exec($rule, '', $data); + return Container::get('hook')->exec($rule, $data); } /** * 使用filter_var方式验证 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function filter($value, $rule) + public function filter($value, $rule) { if (is_string($rule) && strpos($rule, ',')) { list($rule, $param) = explode(',', $rule); @@ -884,127 +1068,133 @@ class Validate } else { $param = null; } + return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param); } /** * 验证某个字段等于某个值的时候必须 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function requireIf($value, $rule, $data) + public function requireIf($value, $rule, $data) { list($field, $val) = explode(',', $rule); + if ($this->getDataValue($data, $field) == $val) { - return !empty($value); - } else { - return true; + return !empty($value) || '0' == $value; } + + return true; } /** * 通过回调方法验证某个字段是否必须 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function requireCallback($value, $rule, $data) + public function requireCallback($value, $rule, $data) { - $result = call_user_func_array($rule, [$value, $data]); + $result = call_user_func_array([$this, $rule], [$value, $data]); + if ($result) { - return !empty($value); - } else { - return true; + return !empty($value) || '0' == $value; } + + return true; } /** * 验证某个字段有值的情况下必须 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function requireWith($value, $rule, $data) + public function requireWith($value, $rule, $data) { $val = $this->getDataValue($data, $rule); + if (!empty($val)) { - return !empty($value); - } else { - return true; + return !empty($value) || '0' == $value; } + + return true; } /** * 验证是否在范围内 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function in($value, $rule) + public function in($value, $rule) { return in_array($value, is_array($rule) ? $rule : explode(',', $rule)); } /** * 验证是否不在某个范围 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function notIn($value, $rule) + public function notIn($value, $rule) { return !in_array($value, is_array($rule) ? $rule : explode(',', $rule)); } /** * between验证数据 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function between($value, $rule) + public function between($value, $rule) { if (is_string($rule)) { $rule = explode(',', $rule); } list($min, $max) = $rule; + return $value >= $min && $value <= $max; } /** * 使用notbetween验证数据 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function notBetween($value, $rule) + public function notBetween($value, $rule) { if (is_string($rule)) { $rule = explode(',', $rule); } list($min, $max) = $rule; + return $value < $min || $value > $max; } /** * 验证数据长度 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function length($value, $rule) + public function length($value, $rule) { if (is_array($value)) { $length = count($value); @@ -1018,20 +1208,20 @@ class Validate // 长度区间 list($min, $max) = explode(',', $rule); return $length >= $min && $length <= $max; - } else { - // 指定长度 - return $length == $rule; } + + // 指定长度 + return $length == $rule; } /** * 验证数据最大长度 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function max($value, $rule) + public function max($value, $rule) { if (is_array($value)) { $length = count($value); @@ -1040,17 +1230,18 @@ class Validate } else { $length = mb_strlen((string) $value); } + return $length <= $rule; } /** * 验证数据最小长度 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 * @return bool */ - protected function min($value, $rule) + public function min($value, $rule) { if (is_array($value)) { $length = count($value); @@ -1059,46 +1250,79 @@ class Validate } else { $length = mb_strlen((string) $value); } + return $length >= $rule; } /** * 验证日期 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function after($value, $rule) + public function after($value, $rule, $data) { return strtotime($value) >= strtotime($rule); } /** * 验证日期 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function before($value, $rule) + public function before($value, $rule, $data) { return strtotime($value) <= strtotime($rule); } /** - * 验证有效期 + * 验证日期字段 * @access protected * @param mixed $value 字段值 * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function expire($value, $rule) + protected function afterWith($value, $rule, $data) + { + $rule = $this->getDataValue($data, $rule); + return !is_null($rule) && strtotime($value) >= strtotime($rule); + } + + /** + * 验证日期字段 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function beforeWith($value, $rule, $data) + { + $rule = $this->getDataValue($data, $rule); + return !is_null($rule) && strtotime($value) <= strtotime($rule); + } + + /** + * 验证有效期 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + public function expire($value, $rule) { if (is_string($rule)) { $rule = explode(',', $rule); } + list($start, $end) = $rule; + if (!is_numeric($start)) { $start = strtotime($start); } @@ -1106,76 +1330,85 @@ class Validate if (!is_numeric($end)) { $end = strtotime($end); } + return $_SERVER['REQUEST_TIME'] >= $start && $_SERVER['REQUEST_TIME'] <= $end; } /** * 验证IP许可 - * @access protected - * @param string $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param string $value 字段值 + * @param mixed $rule 验证规则 * @return mixed */ - protected function allowIp($value, $rule) + public function allowIp($value, $rule) { - return in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule)); + return in_array($value, is_array($rule) ? $rule : explode(',', $rule)); } /** * 验证IP禁用 - * @access protected - * @param string $value 字段值 - * @param mixed $rule 验证规则 + * @access public + * @param string $value 字段值 + * @param mixed $rule 验证规则 * @return mixed */ - protected function denyIp($value, $rule) + public function denyIp($value, $rule) { - return !in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule)); + return !in_array($value, is_array($rule) ? $rule : explode(',', $rule)); } /** * 使用正则验证数据 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 正则规则或者预定义正则名 - * @return mixed + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 正则规则或者预定义正则名 + * @return bool */ - protected function regex($value, $rule) + public function regex($value, $rule) { if (isset($this->regex[$rule])) { $rule = $this->regex[$rule]; + } elseif (isset($this->defaultRegex[$rule])) { + $rule = $this->defaultRegex[$rule]; } + if (0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) { // 不是正则表达式则两端补上/ $rule = '/^' . $rule . '$/'; } - return 1 === preg_match($rule, (string) $value); + + return is_scalar($value) && 1 === preg_match($rule, (string) $value); } /** * 验证表单令牌 - * @access protected - * @param mixed $value 字段值 - * @param mixed $rule 验证规则 - * @param array $data 数据 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 * @return bool */ - protected function token($value, $rule, $data) + public function token($value, $rule, $data) { - $rule = !empty($rule) ? $rule : '__token__'; - if (!isset($data[$rule]) || !Session::has($rule)) { + $rule = !empty($rule) ? $rule : '__token__'; + $session = Container::get('session'); + + if (!isset($data[$rule]) || !$session->has($rule)) { // 令牌数据无效 return false; } // 令牌验证 - if (isset($data[$rule]) && Session::get($rule) === $data[$rule]) { + if (isset($data[$rule]) && $session->get($rule) === $data[$rule]) { // 防止重复提交 - Session::delete($rule); // 验证完成销毁session + $session->delete($rule); // 验证完成销毁session return true; } + // 开启TOKEN重置 - Session::delete($rule); + $session->delete($rule); + return false; } @@ -1188,8 +1421,8 @@ class Validate /** * 获取数据值 * @access protected - * @param array $data 数据 - * @param string $key 数据标识 支持二维 + * @param array $data 数据 + * @param string $key 数据标识 支持多维 * @return mixed */ protected function getDataValue($data, $key) @@ -1197,26 +1430,34 @@ class Validate if (is_numeric($key)) { $value = $key; } elseif (strpos($key, '.')) { - // 支持二维数组验证 - list($name1, $name2) = explode('.', $key); - $value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null; + // 支持多维数组验证 + foreach (explode('.', $key) as $key) { + if (!isset($data[$key])) { + $value = null; + break; + } + $value = $data = $data[$key]; + } } else { $value = isset($data[$key]) ? $data[$key] : null; } + return $value; } /** * 获取验证规则的错误提示信息 * @access protected - * @param string $attribute 字段英文名 - * @param string $title 字段描述名 - * @param string $type 验证规则名称 - * @param mixed $rule 验证规则数据 + * @param string $attribute 字段英文名 + * @param string $title 字段描述名 + * @param string $type 验证规则名称 + * @param mixed $rule 验证规则数据 * @return string */ protected function getRuleMsg($attribute, $title, $type, $rule) { + $lang = Container::get('lang'); + if (isset($this->message[$attribute . '.' . $type])) { $msg = $this->message[$attribute . '.' . $type]; } elseif (isset($this->message[$attribute][$type])) { @@ -1225,15 +1466,23 @@ class Validate $msg = $this->message[$attribute]; } elseif (isset(self::$typeMsg[$type])) { $msg = self::$typeMsg[$type]; + } elseif (0 === strpos($type, 'require')) { + $msg = self::$typeMsg['require']; } else { - $msg = $title . '规则错误'; + $msg = $title . $lang->get('not conform to the rules'); } - if (is_string($msg) && 0 === strpos($msg, '{%')) { - $msg = Lang::get(substr($msg, 2, -1)); + if (!is_string($msg)) { + return $msg; } - if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) { + if (0 === strpos($msg, '{%')) { + $msg = $lang->get(substr($msg, 2, -1)); + } elseif ($lang->has($msg)) { + $msg = $lang->get($msg); + } + + if (is_scalar($rule) && false !== strpos($msg, ':')) { // 变量替换 if (is_string($rule) && strpos($rule, ',')) { $array = array_pad(explode(',', $rule), 3, ''); @@ -1241,18 +1490,22 @@ class Validate $array = array_pad([], 3, ''); } $msg = str_replace( - [':attribute', ':rule', ':1', ':2', ':3'], - [$title, (string) $rule, $array[0], $array[1], $array[2]], + [':attribute', ':1', ':2', ':3'], + [$title, $array[0], $array[1], $array[2]], $msg); + if (strpos($msg, ':rule')) { + $msg = str_replace(':rule', (string) $rule, $msg); + } } + return $msg; } /** * 获取数据验证的场景 * @access protected - * @param string $scene 验证场景 - * @return array + * @param string $scene 验证场景 + * @return void */ protected function getScene($scene = '') { @@ -1261,25 +1514,41 @@ class Validate $scene = $this->currentScene; } - if (!empty($scene) && isset($this->scene[$scene])) { + if (empty($scene)) { + return; + } + + $this->only = $this->append = $this->remove = []; + + if (method_exists($this, 'scene' . $scene)) { + call_user_func([$this, 'scene' . $scene]); + } elseif (isset($this->scene[$scene])) { // 如果设置了验证适用场景 $scene = $this->scene[$scene]; + if (is_string($scene)) { $scene = explode(',', $scene); } - } else { - $scene = []; + + $this->only = $scene; } - return $scene; } - public static function __callStatic($method, $params) + /** + * 动态方法 直接调用is方法进行验证 + * @access public + * @param string $method 方法名 + * @param array $args 调用参数 + * @return bool + */ + public function __call($method, $args) { - $class = self::make(); - if (method_exists($class, $method)) { - return call_user_func_array([$class, $method], $params); - } else { - throw new \BadMethodCallException('method not exists:' . __CLASS__ . '->' . $method); + if ('is' == strtolower(substr($method, 0, 2))) { + $method = substr($method, 2); } + + array_push($args, lcfirst($method)); + + return call_user_func_array([$this, 'is'], $args); } } diff --git a/thinkphp/library/think/View.php b/thinkphp/library/think/View.php index 96ae56c92..17860a6ba 100644 --- a/thinkphp/library/think/View.php +++ b/thinkphp/library/think/View.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,80 +13,83 @@ namespace think; class View { - // 视图实例 - protected static $instance; - // 模板引擎实例 + /** + * 模板引擎实例 + * @var object + */ public $engine; - // 模板变量 - protected $data = []; - // 用于静态赋值的模板变量 - protected static $var = []; - // 视图输出替换 - protected $replace = []; /** - * 构造函数 - * @access public - * @param array $engine 模板引擎参数 - * @param array $replace 字符串替换参数 + * 模板变量 + * @var array */ - public function __construct($engine = [], $replace = []) + protected $data = []; + + /** + * 内容过滤 + * @var mixed + */ + protected $filter; + + /** + * 全局模板变量 + * @var array + */ + protected static $var = []; + + /** + * 初始化 + * @access public + * @param mixed $engine 模板引擎参数 + * @return $this + */ + public function init($engine = []) { // 初始化模板引擎 - $this->engine((array) $engine); - // 基础替换字符串 - $request = Request::instance(); - $base = $request->root(); - $root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base; - if ('' != $root) { - $root = '/' . ltrim($root, '/'); - } - $baseReplace = [ - '__ROOT__' => $root, - '__URL__' => $base . '/' . $request->module() . '/' . Loader::parseName($request->controller()), - '__STATIC__' => $root . '/static', - '__CSS__' => $root . '/static/css', - '__JS__' => $root . '/static/js', - ]; - $this->replace = array_merge($baseReplace, (array) $replace); + $this->engine($engine); + + return $this; } - /** - * 初始化视图 - * @access public - * @param array $engine 模板引擎参数 - * @param array $replace 字符串替换参数 - * @return object - */ - public static function instance($engine = [], $replace = []) + public static function __make(Config $config) { - if (is_null(self::$instance)) { - self::$instance = new self($engine, $replace); - } - return self::$instance; + return (new static())->init($config->pull('template')); } /** * 模板变量静态赋值 * @access public - * @param mixed $name 变量名 - * @param mixed $value 变量值 - * @return void + * @param mixed $name 变量名 + * @param mixed $value 变量值 + * @return $this */ - public static function share($name, $value = '') + public function share($name, $value = '') { if (is_array($name)) { self::$var = array_merge(self::$var, $name); } else { self::$var[$name] = $value; } + + return $this; + } + + /** + * 清理模板变量 + * @access public + * @return void + */ + public function clear() + { + self::$var = []; + $this->data = []; } /** * 模板变量赋值 * @access public - * @param mixed $name 变量名 - * @param mixed $value 变量值 + * @param mixed $name 变量名 + * @param mixed $value 变量值 * @return $this */ public function assign($name, $value = '') @@ -96,13 +99,14 @@ class View } else { $this->data[$name] = $value; } + return $this; } /** * 设置当前模板解析的引擎 * @access public - * @param array|string $options 引擎参数 + * @param array|string $options 引擎参数 * @return $this */ public function engine($options = []) @@ -114,38 +118,63 @@ class View $type = !empty($options['type']) ? $options['type'] : 'Think'; } - $class = false !== strpos($type, '\\') ? $type : '\\think\\view\\driver\\' . ucfirst($type); if (isset($options['type'])) { unset($options['type']); } - $this->engine = new $class($options); + + $this->engine = Loader::factory($type, '\\think\\view\\driver\\', $options); + return $this; } /** * 配置模板引擎 - * @access private - * @param string|array $name 参数名 - * @param mixed $value 参数值 - * @return void + * @access public + * @param string|array $name 参数名 + * @param mixed $value 参数值 + * @return $this */ public function config($name, $value = null) { $this->engine->config($name, $value); + + return $this; + } + + /** + * 检查模板是否存在 + * @access public + * @param string|array $name 参数名 + * @return bool + */ + public function exists($name) + { + return $this->engine->exists($name); + } + + /** + * 视图过滤 + * @access public + * @param Callable $filter 过滤方法或闭包 + * @return $this + */ + public function filter($filter) + { + $this->filter = $filter; return $this; } /** * 解析和获取模板内容 用于输出 - * @param string $template 模板文件名或者内容 - * @param array $vars 模板输出变量 - * @param array $replace 替换内容 - * @param array $config 模板参数 - * @param bool $renderContent 是否渲染内容 + * @access public + * @param string $template 模板文件名或者内容 + * @param array $vars 模板输出变量 + * @param array $config 模板参数 + * @param bool $renderContent 是否渲染内容 * @return string - * @throws Exception + * @throws \Exception */ - public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false) + public function fetch($template = '', $vars = [], $config = [], $renderContent = false) { // 模板变量 $vars = array_merge(self::$var, $this->data, $vars); @@ -155,57 +184,42 @@ class View ob_implicit_flush(0); // 渲染输出 - $method = $renderContent ? 'display' : 'fetch'; - $this->engine->$method($template, $vars, $config); + try { + $method = $renderContent ? 'display' : 'fetch'; + $this->engine->$method($template, $vars, $config); + } catch (\Exception $e) { + ob_end_clean(); + throw $e; + } // 获取并清空缓存 $content = ob_get_clean(); - // 内容过滤标签 - Hook::listen('view_filter', $content); - // 允许用户自定义模板的字符串替换 - $replace = array_merge($this->replace, $replace); - if (!empty($replace)) { - $content = strtr($content, $replace); - } - return $content; - } - /** - * 视图内容替换 - * @access public - * @param string|array $content 被替换内容(支持批量替换) - * @param string $replace 替换内容 - * @return $this - */ - public function replace($content, $replace = '') - { - if (is_array($content)) { - $this->replace = array_merge($this->replace, $content); - } else { - $this->replace[$content] = $replace; + if ($this->filter) { + $content = call_user_func_array($this->filter, [$content]); } - return $this; + + return $content; } /** * 渲染内容输出 * @access public - * @param string $content 内容 - * @param array $vars 模板输出变量 - * @param array $replace 替换内容 - * @param array $config 模板参数 + * @param string $content 内容 + * @param array $vars 模板输出变量 + * @param array $config 模板参数 * @return mixed */ - public function display($content, $vars = [], $replace = [], $config = []) + public function display($content, $vars = [], $config = []) { - return $this->fetch($content, $vars, $replace, $config, true); + return $this->fetch($content, $vars, $config, true); } /** * 模板变量赋值 * @access public - * @param string $name 变量名 - * @param mixed $value 变量值 + * @param string $name 变量名 + * @param mixed $value 变量值 */ public function __set($name, $value) { @@ -215,7 +229,7 @@ class View /** * 取得模板显示变量的值 * @access protected - * @param string $name 模板变量 + * @param string $name 模板变量 * @return mixed */ public function __get($name) @@ -226,7 +240,7 @@ class View /** * 检测模板变量是否设置 * @access public - * @param string $name 模板变量名 + * @param string $name 模板变量名 * @return bool */ public function __isset($name) diff --git a/thinkphp/library/think/cache/Driver.php b/thinkphp/library/think/cache/Driver.php index 688507a81..f4c5dcbf7 100644 --- a/thinkphp/library/think/cache/Driver.php +++ b/thinkphp/library/think/cache/Driver.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,19 +11,53 @@ namespace think\cache; +use think\Container; + /** * 缓存基础类 */ abstract class Driver { + /** + * 驱动句柄 + * @var object + */ protected $handler = null; + + /** + * 缓存读取次数 + * @var integer + */ + protected $readTimes = 0; + + /** + * 缓存写入次数 + * @var integer + */ + protected $writeTimes = 0; + + /** + * 缓存参数 + * @var array + */ protected $options = []; + + /** + * 缓存标签 + * @var string + */ protected $tag; + /** + * 序列化方法 + * @var array + */ + protected static $serialize = ['serialize', 'unserialize', 'think_serialize:', 16]; + /** * 判断缓存是否存在 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return bool */ abstract public function has($name); @@ -31,8 +65,8 @@ abstract class Driver /** * 读取缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $default 默认值 + * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ abstract public function get($name, $default = false); @@ -40,9 +74,9 @@ abstract class Driver /** * 写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param int $expire 有效时间 0为永久 + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param int $expire 有效时间 0为永久 * @return boolean */ abstract public function set($name, $value, $expire = null); @@ -50,8 +84,8 @@ abstract class Driver /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ abstract public function inc($name, $step = 1); @@ -59,8 +93,8 @@ abstract class Driver /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ abstract public function dec($name, $step = 1); @@ -68,7 +102,7 @@ abstract class Driver /** * 删除缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return boolean */ abstract public function rm($name); @@ -76,15 +110,30 @@ abstract class Driver /** * 清除缓存 * @access public - * @param string $tag 标签名 + * @param string $tag 标签名 * @return boolean */ abstract public function clear($tag = null); + /** + * 获取有效期 + * @access protected + * @param integer|\DateTime $expire 有效期 + * @return integer + */ + protected function getExpireTime($expire) + { + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp() - time(); + } + + return $expire; + } + /** * 获取实际的缓存标识 - * @access public - * @param string $name 缓存名 + * @access protected + * @param string $name 缓存名 * @return string */ protected function getCacheKey($name) @@ -95,12 +144,13 @@ abstract class Driver /** * 读取缓存并删除 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return mixed */ public function pull($name) { $result = $this->get($name, false); + if ($result) { $this->rm($name); return $result; @@ -112,90 +162,184 @@ abstract class Driver /** * 如果不存在则写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param int $expire 有效时间 0为永久 + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param int $expire 有效时间 0为永久 * @return mixed */ public function remember($name, $value, $expire = null) { if (!$this->has($name)) { - if ($value instanceof \Closure) { - $value = call_user_func($value); + $time = time(); + while ($time + 5 > time() && $this->has($name . '_lock')) { + // 存在锁定则等待 + usleep(200000); + } + + try { + // 锁定 + $this->set($name . '_lock', true); + + if ($value instanceof \Closure) { + // 获取缓存数据 + $value = Container::getInstance()->invokeFunction($value); + } + + // 缓存数据 + $this->set($name, $value, $expire); + + // 解锁 + $this->rm($name . '_lock'); + } catch (\Exception $e) { + $this->rm($name . '_lock'); + throw $e; + } catch (\throwable $e) { + $this->rm($name . '_lock'); + throw $e; } - $this->set($name, $value, $expire); } else { $value = $this->get($name); } + return $value; } /** * 缓存标签 * @access public - * @param string $name 标签名 - * @param string|array $keys 缓存标识 - * @param bool $overlay 是否覆盖 + * @param string $name 标签名 + * @param string|array $keys 缓存标识 + * @param bool $overlay 是否覆盖 * @return $this */ public function tag($name, $keys = null, $overlay = false) { - if (is_null($keys)) { + if (is_null($name)) { + + } elseif (is_null($keys)) { $this->tag = $name; } else { - $key = 'tag_' . md5($name); + $key = $this->getTagkey($name); + if (is_string($keys)) { $keys = explode(',', $keys); } + $keys = array_map([$this, 'getCacheKey'], $keys); + if ($overlay) { $value = $keys; } else { $value = array_unique(array_merge($this->getTagItem($name), $keys)); } - $this->set($key, implode(',', $value)); + + $this->set($key, implode(',', $value), 0); } + return $this; } /** * 更新标签 - * @access public - * @param string $name 缓存标识 + * @access protected + * @param string $name 缓存标识 * @return void */ protected function setTagItem($name) { if ($this->tag) { - $key = 'tag_' . md5($this->tag); + $key = $this->getTagkey($this->tag); + $prev = $this->tag; $this->tag = null; + if ($this->has($key)) { - $value = $this->get($key); - $value .= ',' . $name; + $value = explode(',', $this->get($key)); + $value[] = $name; + + if (count($value) > 1000) { + array_shift($value); + } + + $value = implode(',', array_unique($value)); } else { $value = $name; } - $this->set($key, $value); + + $this->set($key, $value, 0); + $this->tag = $prev; } } /** * 获取标签包含的缓存标识 - * @access public - * @param string $tag 缓存标签 + * @access protected + * @param string $tag 缓存标签 * @return array */ protected function getTagItem($tag) { - $key = 'tag_' . md5($tag); + $key = $this->getTagkey($tag); $value = $this->get($key); + if ($value) { - return explode(',', $value); + return array_filter(explode(',', $value)); } else { return []; } } + protected function getTagKey($tag) + { + return 'tag_' . md5($tag); + } + + /** + * 序列化数据 + * @access protected + * @param mixed $data + * @return string + */ + protected function serialize($data) + { + if (is_scalar($data) || !$this->options['serialize']) { + return $data; + } + + $serialize = self::$serialize[0]; + + return self::$serialize[2] . $serialize($data); + } + + /** + * 反序列化数据 + * @access protected + * @param string $data + * @return mixed + */ + protected function unserialize($data) + { + if ($this->options['serialize'] && 0 === strpos($data, self::$serialize[2])) { + $unserialize = self::$serialize[1]; + + return $unserialize(substr($data, self::$serialize[3])); + } else { + return $data; + } + } + + /** + * 注册序列化机制 + * @access public + * @param callable $serialize 序列化方法 + * @param callable $unserialize 反序列化方法 + * @param string $prefix 序列化前缀标识 + * @return $this + */ + public static function registerSerialize($serialize, $unserialize, $prefix = 'think_serialize:') + { + self::$serialize = [$serialize, $unserialize, $prefix, strlen($prefix)]; + } + /** * 返回句柄对象,可执行其它高级方法 * @@ -206,4 +350,14 @@ abstract class Driver { return $this->handler; } + + public function getReadTimes() + { + return $this->readTimes; + } + + public function getWriteTimes() + { + return $this->writeTimes; + } } diff --git a/thinkphp/library/think/cache/driver/File.php b/thinkphp/library/think/cache/driver/File.php index 5aadb2b19..93d321f23 100644 --- a/thinkphp/library/think/cache/driver/File.php +++ b/thinkphp/library/think/cache/driver/File.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,6 +12,7 @@ namespace think\cache\driver; use think\cache\Driver; +use think\Container; /** * 文件类型缓存类 @@ -23,12 +24,16 @@ class File extends Driver 'expire' => 0, 'cache_subdir' => true, 'prefix' => '', - 'path' => CACHE_PATH, + 'path' => '', + 'hash_type' => 'md5', 'data_compress' => false, + 'serialize' => true, ]; + protected $expire; + /** - * 构造函数 + * 架构函数 * @param array $options */ public function __construct($options = []) @@ -36,9 +41,13 @@ class File extends Driver if (!empty($options)) { $this->options = array_merge($this->options, $options); } - if (substr($this->options['path'], -1) != DS) { - $this->options['path'] .= DS; + + if (empty($this->options['path'])) { + $this->options['path'] = Container::get('app')->getRuntimePath() . 'cache' . DIRECTORY_SEPARATOR; + } elseif (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) { + $this->options['path'] .= DIRECTORY_SEPARATOR; } + $this->init(); } @@ -50,77 +59,96 @@ class File extends Driver private function init() { // 创建项目缓存目录 - if (!is_dir($this->options['path'])) { - if (mkdir($this->options['path'], 0755, true)) { + try { + if (!is_dir($this->options['path']) && mkdir($this->options['path'], 0755, true)) { return true; } + } catch (\Exception $e) { } + return false; } /** * 取得变量的存储文件名 * @access protected - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 + * @param bool $auto 是否自动创建目录 * @return string */ - protected function getCacheKey($name) + protected function getCacheKey($name, $auto = false) { - $name = md5($name); + $name = hash($this->options['hash_type'], $name); + if ($this->options['cache_subdir']) { // 使用子目录 - $name = substr($name, 0, 2) . DS . substr($name, 2); + $name = substr($name, 0, 2) . DIRECTORY_SEPARATOR . substr($name, 2); } + if ($this->options['prefix']) { - $name = $this->options['prefix'] . DS . $name; + $name = $this->options['prefix'] . DIRECTORY_SEPARATOR . $name; } + $filename = $this->options['path'] . $name . '.php'; $dir = dirname($filename); - if (!is_dir($dir)) { - mkdir($dir, 0755, true); + + if ($auto && !is_dir($dir)) { + try { + mkdir($dir, 0755, true); + } catch (\Exception $e) { + } } + return $filename; } /** * 判断缓存是否存在 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return bool */ public function has($name) { - return $this->get($name) ? true : false; + return false !== $this->get($name) ? true : false; } /** * 读取缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $default 默认值 + * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ public function get($name, $default = false) { + $this->readTimes++; + $filename = $this->getCacheKey($name); + if (!is_file($filename)) { return $default; } - $content = file_get_contents($filename); + + $content = file_get_contents($filename); + $this->expire = null; + if (false !== $content) { $expire = (int) substr($content, 8, 12); - if (0 != $expire && $_SERVER['REQUEST_TIME'] > filemtime($filename) + $expire) { + if (0 != $expire && time() > filemtime($filename) + $expire) { //缓存过期删除缓存文件 $this->unlink($filename); return $default; } - $content = substr($content, 20, -3); + + $this->expire = $expire; + $content = substr($content, 32); + if ($this->options['data_compress'] && function_exists('gzcompress')) { //启用数据压缩 $content = gzuncompress($content); } - $content = unserialize($content); - return $content; + return $this->unserialize($content); } else { return $default; } @@ -129,27 +157,36 @@ class File extends Driver /** * 写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param int $expire 有效时间 0为永久 + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param int|\DateTime $expire 有效时间 0为永久 * @return boolean */ public function set($name, $value, $expire = null) { + $this->writeTimes++; + if (is_null($expire)) { $expire = $this->options['expire']; } - $filename = $this->getCacheKey($name); + + $expire = $this->getExpireTime($expire); + $filename = $this->getCacheKey($name, true); + if ($this->tag && !is_file($filename)) { $first = true; } - $data = serialize($value); + + $data = $this->serialize($value); + if ($this->options['data_compress'] && function_exists('gzcompress')) { //数据压缩 $data = gzcompress($data, 3); } - $data = ""; + + $data = "\n" . $data; $result = file_put_contents($filename, $data); + if ($result) { isset($first) && $this->setTagItem($filename); clearstatcache(); @@ -162,52 +199,63 @@ class File extends Driver /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function inc($name, $step = 1) { if ($this->has($name)) { - $value = $this->get($name) + $step; + $value = $this->get($name) + $step; + $expire = $this->expire; } else { - $value = $step; + $value = $step; + $expire = 0; } - return $this->set($name, $value, 0) ? $value : false; + + return $this->set($name, $value, $expire) ? $value : false; } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function dec($name, $step = 1) { if ($this->has($name)) { - $value = $this->get($name) - $step; + $value = $this->get($name) - $step; + $expire = $this->expire; } else { - $value = $step; + $value = -$step; + $expire = 0; } - return $this->set($name, $value, 0) ? $value : false; + + return $this->set($name, $value, $expire) ? $value : false; } /** * 删除缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return boolean */ public function rm($name) { - return $this->unlink($this->getCacheKey($name)); + $this->writeTimes++; + + try { + return $this->unlink($this->getCacheKey($name)); + } catch (\Exception $e) { + } } /** * 清除缓存 * @access public - * @param string $tag 标签名 + * @param string $tag 标签名 * @return boolean */ public function clear($tag = null) @@ -218,24 +266,33 @@ class File extends Driver foreach ($keys as $key) { $this->unlink($key); } - $this->rm('tag_' . md5($tag)); + $this->rm($this->getTagKey($tag)); return true; } - $files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*'); + + $this->writeTimes++; + + $files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DIRECTORY_SEPARATOR : '') . '*'); + foreach ($files as $path) { if (is_dir($path)) { - array_map('unlink', glob($path . '/*.php')); + $matches = glob($path . DIRECTORY_SEPARATOR . '*.php'); + if (is_array($matches)) { + array_map('unlink', $matches); + } rmdir($path); } else { unlink($path); } } + return true; } /** * 判断文件是否存在后,删除 - * @param $path + * @access private + * @param string $path * @return bool * @author byron sampson * @return boolean diff --git a/thinkphp/library/think/cache/driver/Lite.php b/thinkphp/library/think/cache/driver/Lite.php index eeb96a194..0cfe39079 100644 --- a/thinkphp/library/think/cache/driver/Lite.php +++ b/thinkphp/library/think/cache/driver/Lite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -26,18 +26,19 @@ class Lite extends Driver ]; /** - * 构造函数 + * 架构函数 * @access public * - * @param array $options + * @param array $options */ public function __construct($options = []) { if (!empty($options)) { $this->options = array_merge($this->options, $options); } - if (substr($this->options['path'], -1) != DS) { - $this->options['path'] .= DS; + + if (substr($this->options['path'], -1) != DIRECTORY_SEPARATOR) { + $this->options['path'] .= DIRECTORY_SEPARATOR; } } @@ -45,7 +46,7 @@ class Lite extends Driver /** * 取得变量的存储文件名 * @access protected - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return string */ protected function getCacheKey($name) @@ -56,7 +57,7 @@ class Lite extends Driver /** * 判断缓存是否存在 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return mixed */ public function has($name) @@ -67,21 +68,26 @@ class Lite extends Driver /** * 读取缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $default 默认值 + * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ public function get($name, $default = false) { + $this->readTimes++; + $filename = $this->getCacheKey($name); + if (is_file($filename)) { // 判断是否过期 $mtime = filemtime($filename); - if ($mtime < $_SERVER['REQUEST_TIME']) { + + if ($mtime < time()) { // 清除已经过期的文件 unlink($filename); return $default; } + return include $filename; } else { return $default; @@ -90,39 +96,49 @@ class Lite extends Driver /** * 写入缓存 - * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param int $expire 有效时间 0为永久 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param int|\DateTime $expire 有效时间 0为永久 * @return bool */ public function set($name, $value, $expire = null) { + $this->writeTimes++; + if (is_null($expire)) { $expire = $this->options['expire']; } - // 模拟永久 - if (0 === $expire) { - $expire = 10 * 365 * 24 * 3600; + + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp(); + } else { + $expire = 0 === $expire ? 10 * 365 * 24 * 3600 : $expire; + $expire = time() + $expire; } + $filename = $this->getCacheKey($name); + if ($this->tag && !is_file($filename)) { $first = true; } + $ret = file_put_contents($filename, ("setTagItem($filename); - touch($filename, $_SERVER['REQUEST_TIME'] + $expire); + touch($filename, $expire); } + return $ret; } /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function inc($name, $step = 1) @@ -132,14 +148,15 @@ class Lite extends Driver } else { $value = $step; } + return $this->set($name, $value, 0) ? $value : false; } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function dec($name, $step = 1) @@ -147,26 +164,29 @@ class Lite extends Driver if ($this->has($name)) { $value = $this->get($name) - $step; } else { - $value = $step; + $value = -$step; } + return $this->set($name, $value, 0) ? $value : false; } /** * 删除缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return boolean */ public function rm($name) { + $this->writeTimes++; + return unlink($this->getCacheKey($name)); } /** * 清除缓存 - * @access public - * @param string $tag 标签名 + * @access public + * @param string $tag 标签名 * @return bool */ public function clear($tag = null) @@ -177,9 +197,13 @@ class Lite extends Driver foreach ($keys as $key) { unlink($key); } - $this->rm('tag_' . md5($tag)); + + $this->rm($this->getTagKey($tag)); return true; } - array_map("unlink", glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*.php')); + + $this->writeTimes++; + + array_map("unlink", glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DIRECTORY_SEPARATOR : '') . '*.php')); } } diff --git a/thinkphp/library/think/cache/driver/Memcache.php b/thinkphp/library/think/cache/driver/Memcache.php index e464f38cf..1c535597e 100644 --- a/thinkphp/library/think/cache/driver/Memcache.php +++ b/thinkphp/library/think/cache/driver/Memcache.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -22,12 +22,13 @@ class Memcache extends Driver 'timeout' => 0, // 超时时间(单位:毫秒) 'persistent' => true, 'prefix' => '', + 'serialize' => true, ]; /** - * 构造函数 - * @param array $options 缓存参数 + * 架构函数 * @access public + * @param array $options 缓存参数 * @throws \BadFunctionCallException */ public function __construct($options = []) @@ -35,16 +36,21 @@ class Memcache extends Driver if (!extension_loaded('memcache')) { throw new \BadFunctionCallException('not support: memcache'); } + if (!empty($options)) { $this->options = array_merge($this->options, $options); } + $this->handler = new \Memcache; + // 支持集群 $hosts = explode(',', $this->options['host']); $ports = explode(',', $this->options['port']); + if (empty($ports[0])) { $ports[0] = 11211; } + // 建立连接 foreach ((array) $hosts as $i => $host) { $port = isset($ports[$i]) ? $ports[$i] : $ports[0]; @@ -57,96 +63,115 @@ class Memcache extends Driver /** * 判断缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return bool */ public function has($name) { $key = $this->getCacheKey($name); - return $this->handler->get($key) ? true : false; + + return false !== $this->handler->get($key); } /** * 读取缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $default 默认值 + * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ public function get($name, $default = false) { + $this->readTimes++; + $result = $this->handler->get($this->getCacheKey($name)); - return false !== $result ? $result : $default; + + return false !== $result ? $this->unserialize($result) : $default; } /** * 写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param integer $expire 有效时间(秒) + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param int|DateTime $expire 有效时间(秒) * @return bool */ public function set($name, $value, $expire = null) { + $this->writeTimes++; + if (is_null($expire)) { $expire = $this->options['expire']; } + if ($this->tag && !$this->has($name)) { $first = true; } - $key = $this->getCacheKey($name); + + $key = $this->getCacheKey($name); + $expire = $this->getExpireTime($expire); + $value = $this->serialize($value); + if ($this->handler->set($key, $value, 0, $expire)) { isset($first) && $this->setTagItem($key); return true; } + return false; } /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function inc($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + if ($this->handler->get($key)) { return $this->handler->increment($key, $step); } + return $this->handler->set($key, $step); } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function dec($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); $value = $this->handler->get($key) - $step; $res = $this->handler->set($key, $value); - if (!$res) { - return false; - } else { - return $value; - } + + return !$res ? false : $value; } /** * 删除缓存 - * @param string $name 缓存变量名 - * @param bool|false $ttl + * @access public + * @param string $name 缓存变量名 + * @param bool|false $ttl * @return bool */ public function rm($name, $ttl = false) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + return false === $ttl ? $this->handler->delete($key) : $this->handler->delete($key, $ttl); @@ -155,7 +180,7 @@ class Memcache extends Driver /** * 清除缓存 * @access public - * @param string $tag 标签名 + * @param string $tag 标签名 * @return bool */ public function clear($tag = null) @@ -163,12 +188,19 @@ class Memcache extends Driver if ($tag) { // 指定标签清除 $keys = $this->getTagItem($tag); + foreach ($keys as $key) { $this->handler->delete($key); } - $this->rm('tag_' . md5($tag)); + + $tagName = $this->getTagKey($tag); + $this->rm($tagName); return true; } + + $this->writeTimes++; + return $this->handler->flush(); } + } diff --git a/thinkphp/library/think/cache/driver/Memcached.php b/thinkphp/library/think/cache/driver/Memcached.php index 4a9d898c1..6af60d19b 100644 --- a/thinkphp/library/think/cache/driver/Memcached.php +++ b/thinkphp/library/think/cache/driver/Memcached.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -16,49 +16,58 @@ use think\cache\Driver; class Memcached extends Driver { protected $options = [ - 'host' => '127.0.0.1', - 'port' => 11211, - 'expire' => 0, - 'timeout' => 0, // 超时时间(单位:毫秒) - 'prefix' => '', - 'username' => '', //账号 - 'password' => '', //密码 - 'option' => [], + 'host' => '127.0.0.1', + 'port' => 11211, + 'expire' => 0, + 'timeout' => 0, // 超时时间(单位:毫秒) + 'prefix' => '', + 'username' => '', //账号 + 'password' => '', //密码 + 'option' => [], + 'serialize' => true, ]; /** - * 构造函数 - * @param array $options 缓存参数 + * 架构函数 * @access public + * @param array $options 缓存参数 */ public function __construct($options = []) { if (!extension_loaded('memcached')) { throw new \BadFunctionCallException('not support: memcached'); } + if (!empty($options)) { $this->options = array_merge($this->options, $options); } + $this->handler = new \Memcached; + if (!empty($this->options['option'])) { $this->handler->setOptions($this->options['option']); } + // 设置连接超时时间(单位:毫秒) if ($this->options['timeout'] > 0) { $this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->options['timeout']); } + // 支持集群 $hosts = explode(',', $this->options['host']); $ports = explode(',', $this->options['port']); if (empty($ports[0])) { $ports[0] = 11211; } + // 建立连接 $servers = []; foreach ((array) $hosts as $i => $host) { $servers[] = [$host, (isset($ports[$i]) ? $ports[$i] : $ports[0]), 1]; } + $this->handler->addServers($servers); + if ('' != $this->options['username']) { $this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); $this->handler->setSaslAuthData($this->options['username'], $this->options['password']); @@ -68,97 +77,115 @@ class Memcached extends Driver /** * 判断缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return bool */ public function has($name) { $key = $this->getCacheKey($name); + return $this->handler->get($key) ? true : false; } /** * 读取缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $default 默认值 + * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ public function get($name, $default = false) { + $this->readTimes++; + $result = $this->handler->get($this->getCacheKey($name)); - return false !== $result ? $result : $default; + + return false !== $result ? $this->unserialize($result) : $default; } /** * 写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param integer $expire 有效时间(秒) + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) * @return bool */ public function set($name, $value, $expire = null) { + $this->writeTimes++; + if (is_null($expire)) { $expire = $this->options['expire']; } + if ($this->tag && !$this->has($name)) { $first = true; } + $key = $this->getCacheKey($name); - $expire = 0 == $expire ? 0 : $_SERVER['REQUEST_TIME'] + $expire; + $expire = $this->getExpireTime($expire); + $value = $this->serialize($value); + if ($this->handler->set($key, $value, $expire)) { isset($first) && $this->setTagItem($key); return true; } + return false; } /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function inc($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + if ($this->handler->get($key)) { return $this->handler->increment($key, $step); } + return $this->handler->set($key, $step); } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function dec($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); $value = $this->handler->get($key) - $step; $res = $this->handler->set($key, $value); - if (!$res) { - return false; - } else { - return $value; - } + + return !$res ? false : $value; } /** * 删除缓存 - * @param string $name 缓存变量名 - * @param bool|false $ttl + * @access public + * @param string $name 缓存变量名 + * @param bool|false $ttl * @return bool */ public function rm($name, $ttl = false) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + return false === $ttl ? $this->handler->delete($key) : $this->handler->delete($key, $ttl); @@ -167,7 +194,7 @@ class Memcached extends Driver /** * 清除缓存 * @access public - * @param string $tag 标签名 + * @param string $tag 标签名 * @return bool */ public function clear($tag = null) @@ -175,10 +202,78 @@ class Memcached extends Driver if ($tag) { // 指定标签清除 $keys = $this->getTagItem($tag); + $this->handler->deleteMulti($keys); - $this->rm('tag_' . md5($tag)); + $this->rm($this->getTagKey($tag)); + return true; } + + $this->writeTimes++; + return $this->handler->flush(); } + + /** + * 缓存标签 + * @access public + * @param string $name 标签名 + * @param string|array $keys 缓存标识 + * @param bool $overlay 是否覆盖 + * @return $this + */ + public function tag($name, $keys = null, $overlay = false) + { + if (is_null($keys)) { + $this->tag = $name; + } else { + $tagName = $this->getTagKey($name); + if ($overlay) { + $this->handler->delete($tagName); + } + + if (!$this->handler->has($tagName)) { + $this->handler->set($tagName, ''); + } + + foreach ($keys as $key) { + $this->handler->append($tagName, ',' . $key); + } + } + + return $this; + } + + /** + * 更新标签 + * @access protected + * @param string $name 缓存标识 + * @return void + */ + protected function setTagItem($name) + { + if ($this->tag) { + $tagName = $this->getTagKey($this->tag); + + if ($this->handler->has($tagName)) { + $this->handler->append($tagName, ',' . $name); + } else { + $this->handler->set($tagName, $name); + } + + $this->tag = null; + } + } + + /** + * 获取标签包含的缓存标识 + * @access public + * @param string $tag 缓存标签 + * @return array + */ + public function getTagItem($tag) + { + $tagName = $this->getTagKey($tag); + return explode(',', trim($this->handler->get($tagName), ',')); + } } diff --git a/thinkphp/library/think/cache/driver/Redis.php b/thinkphp/library/think/cache/driver/Redis.php index 4f6187856..813746e77 100644 --- a/thinkphp/library/think/cache/driver/Redis.php +++ b/thinkphp/library/think/cache/driver/Redis.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -31,132 +31,173 @@ class Redis extends Driver 'expire' => 0, 'persistent' => false, 'prefix' => '', + 'serialize' => true, ]; /** - * 构造函数 - * @param array $options 缓存参数 + * 架构函数 * @access public + * @param array $options 缓存参数 */ public function __construct($options = []) { - if (!extension_loaded('redis')) { - throw new \BadFunctionCallException('not support: redis'); - } if (!empty($options)) { $this->options = array_merge($this->options, $options); } - $func = $this->options['persistent'] ? 'pconnect' : 'connect'; - $this->handler = new \Redis; - $this->handler->$func($this->options['host'], $this->options['port'], $this->options['timeout']); - if ('' != $this->options['password']) { - $this->handler->auth($this->options['password']); - } + if (extension_loaded('redis')) { + $this->handler = new \Redis; - if (0 != $this->options['select']) { - $this->handler->select($this->options['select']); + if ($this->options['persistent']) { + $this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']); + } else { + $this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']); + } + + if ('' != $this->options['password']) { + $this->handler->auth($this->options['password']); + } + + if (0 != $this->options['select']) { + $this->handler->select($this->options['select']); + } + } elseif (class_exists('\Predis\Client')) { + $params = []; + foreach ($this->options as $key => $val) { + if (in_array($key, ['aggregate', 'cluster', 'connections', 'exceptions', 'prefix', 'profile', 'replication', 'parameters'])) { + $params[$key] = $val; + unset($this->options[$key]); + } + } + + if ('' == $this->options['password']) { + unset($this->options['password']); + } + + $this->handler = new \Predis\Client($this->options, $params); + + $this->options['prefix'] = ''; + } else { + throw new \BadFunctionCallException('not support: redis'); } } /** * 判断缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return bool */ public function has($name) { - return $this->handler->get($this->getCacheKey($name)) ? true : false; + return $this->handler->exists($this->getCacheKey($name)); } /** * 读取缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $default 默认值 + * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ public function get($name, $default = false) { + $this->readTimes++; + $value = $this->handler->get($this->getCacheKey($name)); - if (is_null($value)) { + + if (is_null($value) || false === $value) { return $default; } - $jsonData = json_decode($value, true); - // 检测是否为JSON数据 true 返回JSON解析数组, false返回源数据 byron sampson - return (null === $jsonData) ? $value : $jsonData; + + return $this->unserialize($value); } /** * 写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param integer $expire 有效时间(秒) + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) * @return boolean */ public function set($name, $value, $expire = null) { + $this->writeTimes++; + if (is_null($expire)) { $expire = $this->options['expire']; } + if ($this->tag && !$this->has($name)) { $first = true; } - $key = $this->getCacheKey($name); - //对数组/对象数据进行缓存处理,保证数据完整性 byron sampson - $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value; - if (is_int($expire) && $expire) { + + $key = $this->getCacheKey($name); + $expire = $this->getExpireTime($expire); + + $value = $this->serialize($value); + + if ($expire) { $result = $this->handler->setex($key, $expire, $value); } else { $result = $this->handler->set($key, $value); } + isset($first) && $this->setTagItem($key); + return $result; } /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function inc($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + return $this->handler->incrby($key, $step); } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function dec($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + return $this->handler->decrby($key, $step); } /** * 删除缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return boolean */ public function rm($name) { - return $this->handler->delete($this->getCacheKey($name)); + $this->writeTimes++; + + return $this->handler->del($this->getCacheKey($name)); } /** * 清除缓存 * @access public - * @param string $tag 标签名 + * @param string $tag 标签名 * @return boolean */ public function clear($tag = null) @@ -164,13 +205,68 @@ class Redis extends Driver if ($tag) { // 指定标签清除 $keys = $this->getTagItem($tag); - foreach ($keys as $key) { - $this->handler->delete($key); - } - $this->rm('tag_' . md5($tag)); + + $this->handler->del($keys); + + $tagName = $this->getTagKey($tag); + $this->handler->del($tagName); return true; } + + $this->writeTimes++; + return $this->handler->flushDB(); } + /** + * 缓存标签 + * @access public + * @param string $name 标签名 + * @param string|array $keys 缓存标识 + * @param bool $overlay 是否覆盖 + * @return $this + */ + public function tag($name, $keys = null, $overlay = false) + { + if (is_null($keys)) { + $this->tag = $name; + } else { + $tagName = $this->getTagKey($name); + if ($overlay) { + $this->handler->del($tagName); + } + + foreach ($keys as $key) { + $this->handler->sAdd($tagName, $key); + } + } + + return $this; + } + + /** + * 更新标签 + * @access protected + * @param string $name 缓存标识 + * @return void + */ + protected function setTagItem($name) + { + if ($this->tag) { + $tagName = $this->getTagKey($this->tag); + $this->handler->sAdd($tagName, $name); + } + } + + /** + * 获取标签包含的缓存标识 + * @access protected + * @param string $tag 缓存标签 + * @return array + */ + protected function getTagItem($tag) + { + $tagName = $this->getTagKey($tag); + return $this->handler->sMembers($tagName); + } } diff --git a/thinkphp/library/think/cache/driver/Sqlite.php b/thinkphp/library/think/cache/driver/Sqlite.php index 305fd9e88..f57361e3c 100644 --- a/thinkphp/library/think/cache/driver/Sqlite.php +++ b/thinkphp/library/think/cache/driver/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -25,30 +25,34 @@ class Sqlite extends Driver 'prefix' => '', 'expire' => 0, 'persistent' => false, + 'serialize' => true, ]; /** - * 构造函数 - * @param array $options 缓存参数 - * @throws \BadFunctionCallException + * 架构函数 * @access public + * @param array $options 缓存参数 + * @throws \BadFunctionCallException */ public function __construct($options = []) { if (!extension_loaded('sqlite')) { throw new \BadFunctionCallException('not support: sqlite'); } + if (!empty($options)) { $this->options = array_merge($this->options, $options); } - $func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open'; + + $func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open'; + $this->handler = $func($this->options['db']); } /** * 获取实际的缓存标识 * @access public - * @param string $name 缓存名 + * @param string $name 缓存名 * @return string */ protected function getCacheKey($name) @@ -59,78 +63,101 @@ class Sqlite extends Driver /** * 判断缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return bool */ public function has($name) { - $name = $this->getCacheKey($name); - $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1'; + $name = $this->getCacheKey($name); + + $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . time() . ') LIMIT 1'; $result = sqlite_query($this->handler, $sql); + return sqlite_num_rows($result); } /** * 读取缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $default 默认值 + * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ public function get($name, $default = false) { - $name = $this->getCacheKey($name); - $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1'; + $this->readTimes++; + + $name = $this->getCacheKey($name); + + $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . time() . ') LIMIT 1'; + $result = sqlite_query($this->handler, $sql); + if (sqlite_num_rows($result)) { $content = sqlite_fetch_single($result); if (function_exists('gzcompress')) { //启用数据压缩 $content = gzuncompress($content); } - return unserialize($content); + + return $this->unserialize($content); } + return $default; } /** * 写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param integer $expire 有效时间(秒) + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) * @return boolean */ public function set($name, $value, $expire = null) { - $name = $this->getCacheKey($name); - $value = sqlite_escape_string(serialize($value)); + $this->writeTimes++; + + $name = $this->getCacheKey($name); + + $value = sqlite_escape_string($this->serialize($value)); + if (is_null($expire)) { $expire = $this->options['expire']; } - $expire = (0 == $expire) ? 0 : ($_SERVER['REQUEST_TIME'] + $expire); //缓存有效期为0表示永久缓存 + + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp(); + } else { + $expire = (0 == $expire) ? 0 : (time() + $expire); //缓存有效期为0表示永久缓存 + } + if (function_exists('gzcompress')) { //数据压缩 $value = gzcompress($value, 3); } + if ($this->tag) { $tag = $this->tag; $this->tag = null; } else { $tag = ''; } + $sql = 'REPLACE INTO ' . $this->options['table'] . ' (var, value, expire, tag) VALUES (\'' . $name . '\', \'' . $value . '\', \'' . $expire . '\', \'' . $tag . '\')'; + if (sqlite_query($this->handler, $sql)) { return true; } + return false; } /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function inc($name, $step = 1) @@ -140,14 +167,15 @@ class Sqlite extends Driver } else { $value = $step; } + return $this->set($name, $value, 0) ? $value : false; } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function dec($name, $step = 1) @@ -155,41 +183,51 @@ class Sqlite extends Driver if ($this->has($name)) { $value = $this->get($name) - $step; } else { - $value = $step; + $value = -$step; } + return $this->set($name, $value, 0) ? $value : false; } /** * 删除缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return boolean */ public function rm($name) { + $this->writeTimes++; + $name = $this->getCacheKey($name); - $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\''; + + $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\''; sqlite_query($this->handler, $sql); + return true; } /** * 清除缓存 * @access public - * @param string $tag 标签名 + * @param string $tag 标签名 * @return boolean */ public function clear($tag = null) { if ($tag) { - $name = sqlite_escape_string($tag); + $name = sqlite_escape_string($this->getTagKey($tag)); $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\''; sqlite_query($this->handler, $sql); return true; } + + $this->writeTimes++; + $sql = 'DELETE FROM ' . $this->options['table']; + sqlite_query($this->handler, $sql); + return true; } } diff --git a/thinkphp/library/think/cache/driver/Wincache.php b/thinkphp/library/think/cache/driver/Wincache.php index 3076fc140..ef1578417 100644 --- a/thinkphp/library/think/cache/driver/Wincache.php +++ b/thinkphp/library/think/cache/driver/Wincache.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -20,21 +20,23 @@ use think\cache\Driver; class Wincache extends Driver { protected $options = [ - 'prefix' => '', - 'expire' => 0, + 'prefix' => '', + 'expire' => 0, + 'serialize' => true, ]; /** - * 构造函数 - * @param array $options 缓存参数 - * @throws \BadFunctionCallException + * 架构函数 * @access public + * @param array $options 缓存参数 + * @throws \BadFunctionCallException */ public function __construct($options = []) { if (!function_exists('wincache_ucache_info')) { throw new \BadFunctionCallException('not support: WinCache'); } + if (!empty($options)) { $this->options = array_merge($this->options, $options); } @@ -43,107 +45,131 @@ class Wincache extends Driver /** * 判断缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return bool */ public function has($name) { + $this->readTimes++; + $key = $this->getCacheKey($name); + return wincache_ucache_exists($key); } /** * 读取缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $default 默认值 + * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ public function get($name, $default = false) { + $this->readTimes++; + $key = $this->getCacheKey($name); - return wincache_ucache_exists($key) ? wincache_ucache_get($key) : $default; + + return wincache_ucache_exists($key) ? $this->unserialize(wincache_ucache_get($key)) : $default; } /** * 写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param integer $expire 有效时间(秒) + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) * @return boolean */ public function set($name, $value, $expire = null) { + $this->writeTimes++; + if (is_null($expire)) { $expire = $this->options['expire']; } - $key = $this->getCacheKey($name); + + $key = $this->getCacheKey($name); + $expire = $this->getExpireTime($expire); + $value = $this->serialize($value); + if ($this->tag && !$this->has($name)) { $first = true; } + if (wincache_ucache_set($key, $value, $expire)) { isset($first) && $this->setTagItem($key); return true; } + return false; } /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function inc($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + return wincache_ucache_inc($key, $step); } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function dec($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + return wincache_ucache_dec($key, $step); } /** * 删除缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return boolean */ public function rm($name) { + $this->writeTimes++; + return wincache_ucache_delete($this->getCacheKey($name)); } /** * 清除缓存 * @access public - * @param string $tag 标签名 + * @param string $tag 标签名 * @return boolean */ public function clear($tag = null) { if ($tag) { $keys = $this->getTagItem($tag); - foreach ($keys as $key) { - wincache_ucache_delete($key); - } - $this->rm('tag_' . md5($tag)); + + wincache_ucache_delete($keys); + + $tagName = $this->getTagkey($tag); + $this->rm($tagName); return true; - } else { - return wincache_ucache_clear(); } + + $this->writeTimes++; + return wincache_ucache_clear(); } } diff --git a/thinkphp/library/think/cache/driver/Xcache.php b/thinkphp/library/think/cache/driver/Xcache.php index 2a0e07ad9..4e698597a 100644 --- a/thinkphp/library/think/cache/driver/Xcache.php +++ b/thinkphp/library/think/cache/driver/Xcache.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -20,14 +20,15 @@ use think\cache\Driver; class Xcache extends Driver { protected $options = [ - 'prefix' => '', - 'expire' => 0, + 'prefix' => '', + 'expire' => 0, + 'serialize' => true, ]; /** - * 构造函数 - * @param array $options 缓存参数 + * 架构函数 * @access public + * @param array $options 缓存参数 * @throws \BadFunctionCallException */ public function __construct($options = []) @@ -35,6 +36,7 @@ class Xcache extends Driver if (!function_exists('xcache_info')) { throw new \BadFunctionCallException('not support: Xcache'); } + if (!empty($options)) { $this->options = array_merge($this->options, $options); } @@ -43,93 +45,113 @@ class Xcache extends Driver /** * 判断缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return bool */ public function has($name) { $key = $this->getCacheKey($name); + return xcache_isset($key); } /** * 读取缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $default 默认值 + * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ public function get($name, $default = false) { + $this->readTimes++; + $key = $this->getCacheKey($name); - return xcache_isset($key) ? xcache_get($key) : $default; + + return xcache_isset($key) ? $this->unserialize(xcache_get($key)) : $default; } /** * 写入缓存 * @access public - * @param string $name 缓存变量名 - * @param mixed $value 存储数据 - * @param integer $expire 有效时间(秒) + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) * @return boolean */ public function set($name, $value, $expire = null) { + $this->writeTimes++; + if (is_null($expire)) { $expire = $this->options['expire']; } + if ($this->tag && !$this->has($name)) { $first = true; } - $key = $this->getCacheKey($name); + + $key = $this->getCacheKey($name); + $expire = $this->getExpireTime($expire); + $value = $this->serialize($value); + if (xcache_set($key, $value, $expire)) { isset($first) && $this->setTagItem($key); return true; } + return false; } /** * 自增缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function inc($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + return xcache_inc($key, $step); } /** * 自减缓存(针对数值缓存) * @access public - * @param string $name 缓存变量名 - * @param int $step 步长 + * @param string $name 缓存变量名 + * @param int $step 步长 * @return false|int */ public function dec($name, $step = 1) { + $this->writeTimes++; + $key = $this->getCacheKey($name); + return xcache_dec($key, $step); } /** * 删除缓存 * @access public - * @param string $name 缓存变量名 + * @param string $name 缓存变量名 * @return boolean */ public function rm($name) { + $this->writeTimes++; + return xcache_unset($this->getCacheKey($name)); } /** * 清除缓存 * @access public - * @param string $tag 标签名 + * @param string $tag 标签名 * @return boolean */ public function clear($tag = null) @@ -137,12 +159,17 @@ class Xcache extends Driver if ($tag) { // 指定标签清除 $keys = $this->getTagItem($tag); + foreach ($keys as $key) { xcache_unset($key); } - $this->rm('tag_' . md5($tag)); + + $this->rm($this->getTagKey($tag)); return true; } + + $this->writeTimes++; + if (function_exists('xcache_unset_by_prefix')) { return xcache_unset_by_prefix($this->options['prefix']); } else { diff --git a/thinkphp/library/think/config/driver/Ini.php b/thinkphp/library/think/config/driver/Ini.php index a223a5785..b2a647d11 100644 --- a/thinkphp/library/think/config/driver/Ini.php +++ b/thinkphp/library/think/config/driver/Ini.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,12 +13,19 @@ namespace think\config\driver; class Ini { - public function parse($config) + protected $config; + + public function __construct($config) { - if (is_file($config)) { - return parse_ini_file($config, true); + $this->config = $config; + } + + public function parse() + { + if (is_file($this->config)) { + return parse_ini_file($this->config, true); } else { - return parse_ini_string($config, true); + return parse_ini_string($this->config, true); } } } diff --git a/thinkphp/library/think/config/driver/Json.php b/thinkphp/library/think/config/driver/Json.php index 557f75fe0..0d77c8edc 100644 --- a/thinkphp/library/think/config/driver/Json.php +++ b/thinkphp/library/think/config/driver/Json.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,12 +13,19 @@ namespace think\config\driver; class Json { - public function parse($config) + protected $config; + + public function __construct($config) { if (is_file($config)) { $config = file_get_contents($config); } - $result = json_decode($config, true); - return $result; + + $this->config = $config; + } + + public function parse() + { + return json_decode($this->config, true); } } diff --git a/thinkphp/library/think/config/driver/Xml.php b/thinkphp/library/think/config/driver/Xml.php index b573a5627..9d6963384 100644 --- a/thinkphp/library/think/config/driver/Xml.php +++ b/thinkphp/library/think/config/driver/Xml.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,19 +13,28 @@ namespace think\config\driver; class Xml { - public function parse($config) + protected $config; + + public function __construct($config) { - if (is_file($config)) { - $content = simplexml_load_file($config); + $this->config = $config; + } + + public function parse() + { + if (is_file($this->config)) { + $content = simplexml_load_file($this->config); } else { - $content = simplexml_load_string($config); + $content = simplexml_load_string($this->config); } + $result = (array) $content; foreach ($result as $key => $val) { if (is_object($val)) { $result[$key] = (array) $val; } } + return $result; } } diff --git a/thinkphp/library/think/console/Command.php b/thinkphp/library/think/console/Command.php index d0caad2f6..a208e7b54 100644 --- a/thinkphp/library/think/console/Command.php +++ b/thinkphp/library/think/console/Command.php @@ -467,4 +467,16 @@ class Command throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); } } + + /** + * 输出表格 + * @param Table $table + * @return string + */ + protected function table(Table $table) + { + $content = $table->render(); + $this->output->writeln($content); + return $content; + } } diff --git a/thinkphp/library/think/console/Table.php b/thinkphp/library/think/console/Table.php new file mode 100644 index 000000000..9e28e266b --- /dev/null +++ b/thinkphp/library/think/console/Table.php @@ -0,0 +1,281 @@ + +// +---------------------------------------------------------------------- + +namespace think\console; + +class Table +{ + const ALIGN_LEFT = 1; + const ALIGN_RIGHT = 0; + const ALIGN_CENTER = 2; + + /** + * 头信息数据 + * @var array + */ + protected $header = []; + + /** + * 头部对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER + * @var int + */ + protected $headerAlign = 1; + + /** + * 表格数据(二维数组) + * @var array + */ + protected $rows = []; + + /** + * 单元格对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER + * @var int + */ + protected $cellAlign = 1; + + /** + * 单元格宽度信息 + * @var array + */ + protected $colWidth = []; + + /** + * 表格输出样式 + * @var string + */ + protected $style = 'default'; + + /** + * 表格样式定义 + * @var array + */ + protected $format = [ + 'compact' => [], + 'default' => [ + 'top' => ['+', '-', '+', '+'], + 'cell' => ['|', ' ', '|', '|'], + 'middle' => ['+', '-', '+', '+'], + 'bottom' => ['+', '-', '+', '+'], + 'cross-top' => ['+', '-', '-', '+'], + 'cross-bottom' => ['+', '-', '-', '+'], + ], + 'markdown' => [ + 'top' => [' ', ' ', ' ', ' '], + 'cell' => ['|', ' ', '|', '|'], + 'middle' => ['|', '-', '|', '|'], + 'bottom' => [' ', ' ', ' ', ' '], + 'cross-top' => ['|', ' ', ' ', '|'], + 'cross-bottom' => ['|', ' ', ' ', '|'], + ], + 'borderless' => [ + 'top' => ['=', '=', ' ', '='], + 'cell' => [' ', ' ', ' ', ' '], + 'middle' => ['=', '=', ' ', '='], + 'bottom' => ['=', '=', ' ', '='], + 'cross-top' => ['=', '=', ' ', '='], + 'cross-bottom' => ['=', '=', ' ', '='], + ], + 'box' => [ + 'top' => ['┌', '─', '┬', '┐'], + 'cell' => ['│', ' ', '│', '│'], + 'middle' => ['├', '─', '┼', '┤'], + 'bottom' => ['└', '─', '┴', '┘'], + 'cross-top' => ['├', '─', '┴', '┤'], + 'cross-bottom' => ['├', '─', '┬', '┤'], + ], + 'box-double' => [ + 'top' => ['╔', '═', '╤', '╗'], + 'cell' => ['║', ' ', '│', '║'], + 'middle' => ['╠', '─', '╪', '╣'], + 'bottom' => ['╚', '═', '╧', '╝'], + 'cross-top' => ['╠', '═', '╧', '╣'], + 'cross-bottom' => ['╠', '═', '╤', '╣'], + ], + ]; + + /** + * 设置表格头信息 以及对齐方式 + * @access public + * @param array $header 要输出的Header信息 + * @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER + * @return void + */ + public function setHeader(array $header, $align = self::ALIGN_LEFT) + { + $this->header = $header; + $this->headerAlign = $align; + + $this->checkColWidth($header); + } + + /** + * 设置输出表格数据 及对齐方式 + * @access public + * @param array $rows 要输出的表格数据(二维数组) + * @param int $align 对齐方式 默认1 ALGIN_LEFT 0 ALIGN_RIGHT 2 ALIGN_CENTER + * @return void + */ + public function setRows(array $rows, $align = self::ALIGN_LEFT) + { + $this->rows = $rows; + $this->cellAlign = $align; + + foreach ($rows as $row) { + $this->checkColWidth($row); + } + } + + /** + * 检查列数据的显示宽度 + * @access public + * @param mixed $row 行数据 + * @return void + */ + protected function checkColWidth($row) + { + if (is_array($row)) { + foreach ($row as $key => $cell) { + if (!isset($this->colWidth[$key]) || strlen($cell) > $this->colWidth[$key]) { + $this->colWidth[$key] = strlen($cell); + } + } + } + } + + /** + * 增加一行表格数据 + * @access public + * @param mixed $row 行数据 + * @param bool $first 是否在开头插入 + * @return void + */ + public function addRow($row, $first = false) + { + if ($first) { + array_unshift($this->rows, $row); + } else { + $this->rows[] = $row; + } + + $this->checkColWidth($row); + } + + /** + * 设置输出表格的样式 + * @access public + * @param string $style 样式名 + * @return void + */ + public function setStyle($style) + { + $this->style = isset($this->format[$style]) ? $style : 'default'; + } + + /** + * 输出分隔行 + * @access public + * @param string $pos 位置 + * @return string + */ + protected function renderSeparator($pos) + { + $style = $this->getStyle($pos); + $array = []; + + foreach ($this->colWidth as $width) { + $array[] = str_repeat($style[1], $width + 2); + } + + return $style[0] . implode($style[2], $array) . $style[3] . PHP_EOL; + } + + /** + * 输出表格头部 + * @access public + * @return string + */ + protected function renderHeader() + { + $style = $this->getStyle('cell'); + $content = $this->renderSeparator('top'); + + foreach ($this->header as $key => $header) { + $array[] = ' ' . str_pad($header, $this->colWidth[$key], $style[1], $this->headerAlign); + } + + if (!empty($array)) { + $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL; + + if ($this->rows) { + $content .= $this->renderSeparator('middle'); + } + } + + return $content; + } + + protected function getStyle($style) + { + if ($this->format[$this->style]) { + $style = $this->format[$this->style][$style]; + } else { + $style = [' ', ' ', ' ', ' ']; + } + + return $style; + } + + /** + * 输出表格 + * @access public + * @param array $dataList 表格数据 + * @return string + */ + public function render($dataList = []) + { + if ($dataList) { + $this->setRows($dataList); + } + + // 输出头部 + $content = $this->renderHeader(); + $style = $this->getStyle('cell'); + + if ($this->rows) { + foreach ($this->rows as $row) { + if (is_string($row) && '-' === $row) { + $content .= $this->renderSeparator('middle'); + } elseif (is_scalar($row)) { + $content .= $this->renderSeparator('cross-top'); + $array = str_pad($row, 3 * (count($this->colWidth) - 1) + array_reduce($this->colWidth, function ($a, $b) { + return $a + $b; + })); + + $content .= $style[0] . ' ' . $array . ' ' . $style[3] . PHP_EOL; + $content .= $this->renderSeparator('cross-bottom'); + } else { + $array = []; + + foreach ($row as $key => $val) { + $array[] = ' ' . str_pad($val, $this->colWidth[$key], ' ', $this->cellAlign); + } + + $content .= $style[0] . implode(' ' . $style[2], $array) . ' ' . $style[3] . PHP_EOL; + + } + } + } + + $content .= $this->renderSeparator('bottom'); + + return $content; + } +} diff --git a/thinkphp/library/think/console/command/Build.php b/thinkphp/library/think/console/command/Build.php index 39806c3f4..88a5bf820 100644 --- a/thinkphp/library/think/console/command/Build.php +++ b/thinkphp/library/think/console/command/Build.php @@ -15,10 +15,11 @@ use think\console\Command; use think\console\Input; use think\console\input\Option; use think\console\Output; +use think\facade\App; +use think\facade\Build as AppBuild; class Build extends Command { - /** * {@inheritdoc} */ @@ -35,7 +36,7 @@ class Build extends Command protected function execute(Input $input, Output $output) { if ($input->hasOption('module')) { - \think\Build::module($input->getOption('module')); + AppBuild::module($input->getOption('module')); $output->writeln("Successed"); return; } @@ -43,13 +44,15 @@ class Build extends Command if ($input->hasOption('config')) { $build = include $input->getOption('config'); } else { - $build = include APP_PATH . 'build.php'; + $build = include App::getAppPath() . 'build.php'; } + if (empty($build)) { $output->writeln("Build Config Is Empty"); return; } - \think\Build::run($build); + + AppBuild::run($build); $output->writeln("Successed"); } diff --git a/thinkphp/library/think/console/command/Clear.php b/thinkphp/library/think/console/command/Clear.php index 41019ceaf..144257593 100644 --- a/thinkphp/library/think/console/command/Clear.php +++ b/thinkphp/library/think/console/command/Clear.php @@ -14,6 +14,8 @@ use think\console\Command; use think\console\Input; use think\console\input\Option; use think\console\Output; +use think\facade\App; +use think\facade\Cache; class Clear extends Command { @@ -23,31 +25,45 @@ class Clear extends Command $this ->setName('clear') ->addOption('path', 'd', Option::VALUE_OPTIONAL, 'path to clear', null) + ->addOption('cache', 'c', Option::VALUE_NONE, 'clear cache file') + ->addOption('route', 'u', Option::VALUE_NONE, 'clear route cache') + ->addOption('log', 'l', Option::VALUE_NONE, 'clear log file') + ->addOption('dir', 'r', Option::VALUE_NONE, 'clear empty dir') ->setDescription('Clear runtime file'); } protected function execute(Input $input, Output $output) { - $path = $input->getOption('path') ?: RUNTIME_PATH; + if ($input->getOption('route')) { + Cache::clear('route_cache'); + } else { + if ($input->getOption('cache')) { + $path = App::getRuntimePath() . 'cache'; + } elseif ($input->getOption('log')) { + $path = App::getRuntimePath() . 'log'; + } else { + $path = $input->getOption('path') ?: App::getRuntimePath(); + } - if (is_dir($path)) { - $this->clearPath($path); + $rmdir = $input->getOption('dir') ? true : false; + $this->clear(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, $rmdir); } $output->writeln("Clear Successed"); } - protected function clearPath($path) + protected function clear($path, $rmdir) { - $path = realpath($path) . DS; - $files = scandir($path); - if ($files) { - foreach ($files as $file) { - if ('.' != $file && '..' != $file && is_dir($path . $file)) { - $this->clearPath($path . $file); - } elseif ('.gitignore' != $file && is_file($path . $file)) { - unlink($path . $file); + $files = is_dir($path) ? scandir($path) : []; + + foreach ($files as $file) { + if ('.' != $file && '..' != $file && is_dir($path . $file)) { + array_map('unlink', glob($path . $file . DIRECTORY_SEPARATOR . '*.*')); + if ($rmdir) { + rmdir($path . $file); } + } elseif ('.gitignore' != $file && is_file($path . $file)) { + unlink($path . $file); } } } diff --git a/thinkphp/library/think/console/command/Help.php b/thinkphp/library/think/console/command/Help.php index bae2c6533..f1b63b42e 100644 --- a/thinkphp/library/think/console/command/Help.php +++ b/thinkphp/library/think/console/command/Help.php @@ -19,7 +19,6 @@ use think\console\Output; class Help extends Command { - private $command; /** diff --git a/thinkphp/library/think/console/command/Lists.php b/thinkphp/library/think/console/command/Lists.php index 084ddaa23..6eb856c26 100644 --- a/thinkphp/library/think/console/command/Lists.php +++ b/thinkphp/library/think/console/command/Lists.php @@ -13,14 +13,13 @@ namespace think\console\command; use think\console\Command; use think\console\Input; -use think\console\Output; use think\console\input\Argument as InputArgument; -use think\console\input\Option as InputOption; use think\console\input\Definition as InputDefinition; +use think\console\input\Option as InputOption; +use think\console\Output; class Lists extends Command { - /** * {@inheritdoc} */ @@ -68,7 +67,7 @@ EOF { return new InputDefinition([ new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), - new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list') + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), ]); } } diff --git a/thinkphp/library/think/console/command/Make.php b/thinkphp/library/think/console/command/Make.php index b508f8d0b..2f20954ac 100644 --- a/thinkphp/library/think/console/command/Make.php +++ b/thinkphp/library/think/console/command/Make.php @@ -11,15 +11,16 @@ namespace think\console\command; -use think\Config; use think\console\Command; use think\console\Input; use think\console\input\Argument; use think\console\Output; +use think\facade\App; +use think\facade\Config; +use think\facade\Env; abstract class Make extends Command { - protected $type; abstract protected function getStub(); @@ -44,7 +45,7 @@ abstract class Make extends Command } if (!is_dir(dirname($pathname))) { - mkdir(strtolower(dirname($pathname)), 0755, true); + mkdir(dirname($pathname), 0755, true); } file_put_contents($pathname, $this->buildClass($classname)); @@ -61,26 +62,26 @@ abstract class Make extends Command $class = str_replace($namespace . '\\', '', $name); - return str_replace(['{%className%}', '{%namespace%}', '{%app_namespace%}'], [ + return str_replace(['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'], [ $class, + Config::get('action_suffix'), $namespace, - Config::get('app_namespace') + App::getNamespace(), ], $stub); - } protected function getPathName($name) { - $name = str_replace(Config::get('app_namespace') . '\\', '', $name); + $name = str_replace(App::getNamespace() . '\\', '', $name); - return APP_PATH . str_replace('\\', '/', $name) . '.php'; + return Env::get('app_path') . ltrim(str_replace('\\', '/', $name), '/') . '.php'; } protected function getClassName($name) { - $appNamespace = Config::get('app_namespace'); + $appNamespace = App::getNamespace(); - if (strpos($name, $appNamespace . '\\') === 0) { + if (strpos($name, $appNamespace . '\\') !== false) { return $name; } diff --git a/thinkphp/library/think/console/command/RouteList.php b/thinkphp/library/think/console/command/RouteList.php new file mode 100644 index 000000000..0405c31b6 --- /dev/null +++ b/thinkphp/library/think/console/command/RouteList.php @@ -0,0 +1,130 @@ + +// +---------------------------------------------------------------------- +namespace think\console\command; + +use think\console\Command; +use think\console\Input; +use think\console\input\Argument; +use think\console\input\Option; +use think\console\Output; +use think\console\Table; +use think\Container; + +class RouteList extends Command +{ + protected $sortBy = [ + 'rule' => 0, + 'route' => 1, + 'method' => 2, + 'name' => 3, + 'domain' => 4, + ]; + + protected function configure() + { + $this->setName('route:list') + ->addArgument('style', Argument::OPTIONAL, "the style of the table.", 'default') + ->addOption('sort', 's', Option::VALUE_OPTIONAL, 'order by rule name.', 0) + ->addOption('more', 'm', Option::VALUE_NONE, 'show route options.') + ->setDescription('show route list.'); + } + + protected function execute(Input $input, Output $output) + { + $filename = Container::get('app')->getRuntimePath() . 'route_list.php'; + + if (is_file($filename)) { + unlink($filename); + } + + $content = $this->getRouteList(); + file_put_contents($filename, 'Route List' . PHP_EOL . $content); + } + + protected function getRouteList() + { + Container::get('route')->setTestMode(true); + // 路由检测 + $path = Container::get('app')->getRoutePath(); + + $files = is_dir($path) ? scandir($path) : []; + + foreach ($files as $file) { + if (strpos($file, '.php')) { + $filename = $path . DIRECTORY_SEPARATOR . $file; + // 导入路由配置 + $rules = include $filename; + + if (is_array($rules)) { + Container::get('route')->import($rules); + } + } + } + + if (Container::get('config')->get('route_annotation')) { + $suffix = Container::get('config')->get('controller_suffix') || Container::get('config')->get('class_suffix'); + + include Container::get('build')->buildRoute($suffix); + } + + $table = new Table(); + + if ($this->input->hasOption('more')) { + $header = ['Rule', 'Route', 'Method', 'Name', 'Domain', 'Option', 'Pattern']; + } else { + $header = ['Rule', 'Route', 'Method', 'Name', 'Domain']; + } + + $table->setHeader($header); + + $routeList = Container::get('route')->getRuleList(); + $rows = []; + + foreach ($routeList as $domain => $items) { + foreach ($items as $item) { + $item['route'] = $item['route'] instanceof \Closure ? '' : $item['route']; + + if ($this->input->hasOption('more')) { + $item = [$item['rule'], $item['route'], $item['method'], $item['name'], $domain, json_encode($item['option']), json_encode($item['pattern'])]; + } else { + $item = [$item['rule'], $item['route'], $item['method'], $item['name'], $domain]; + } + + $rows[] = $item; + } + } + + if ($this->input->getOption('sort')) { + $sort = $this->input->getOption('sort'); + + if (isset($this->sortBy[$sort])) { + $sort = $this->sortBy[$sort]; + } + + uasort($rows, function ($a, $b) use ($sort) { + $itemA = isset($a[$sort]) ? $a[$sort] : null; + $itemB = isset($b[$sort]) ? $b[$sort] : null; + + return strcasecmp($itemA, $itemB); + }); + } + + $table->setRows($rows); + + if ($this->input->getArgument('style')) { + $style = $this->input->getArgument('style'); + $table->setStyle($style); + } + + return $this->table($table); + } + +} diff --git a/thinkphp/library/think/console/command/RunServer.php b/thinkphp/library/think/console/command/RunServer.php new file mode 100644 index 000000000..2e028dc6d --- /dev/null +++ b/thinkphp/library/think/console/command/RunServer.php @@ -0,0 +1,53 @@ + +// +---------------------------------------------------------------------- +namespace think\console\command; + +use think\console\Command; +use think\console\Input; +use think\console\input\Option; +use think\console\Output; +use think\facade\App; + +class RunServer extends Command +{ + public function configure() + { + $this->setName('run') + ->addOption('host', 'H', Option::VALUE_OPTIONAL, + 'The host to server the application on', '127.0.0.1') + ->addOption('port', 'p', Option::VALUE_OPTIONAL, + 'The port to server the application on', 8000) + ->addOption('root', 'r', Option::VALUE_OPTIONAL, + 'The document root of the application', App::getRootPath() . 'public') + ->setDescription('PHP Built-in Server for ThinkPHP'); + } + + public function execute(Input $input, Output $output) + { + $host = $input->getOption('host'); + $port = $input->getOption('port'); + $root = $input->getOption('root'); + + $command = sprintf( + 'php -S %s:%d -t %s %s', + $host, + $port, + escapeshellarg($root), + escapeshellarg($root . DIRECTORY_SEPARATOR . 'router.php') + ); + + $output->writeln(sprintf('ThinkPHP Development server is started On ', $host, $port)); + $output->writeln(sprintf('You can exit with `CTRL-C`')); + $output->writeln(sprintf('Document root is: %s', $root)); + passthru($command); + } + +} diff --git a/vendor/topthink/think-queue/src/queue/CallQueuedHandler.php b/thinkphp/library/think/console/command/Version.php similarity index 54% rename from vendor/topthink/think-queue/src/queue/CallQueuedHandler.php rename to thinkphp/library/think/console/command/Version.php index 0f1a627dd..ee7eca9c9 100644 --- a/vendor/topthink/think-queue/src/queue/CallQueuedHandler.php +++ b/thinkphp/library/think/console/command/Version.php @@ -6,31 +6,26 @@ // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- -// | Author: yunwuxin <448901948@qq.com> +// | Author: liu21st // +---------------------------------------------------------------------- +namespace think\console\command; -namespace think\queue; +use think\console\Command; +use think\console\Input; +use think\console\Output; +use think\facade\App; -class CallQueuedHandler +class Version extends Command { - - public function call(Job $job, array $data) + protected function configure() { - $command = unserialize($data['command']); - - call_user_func([$command, 'handle']); - - if (!$job->isDeletedOrReleased()) { - $job->delete(); - } + // 指令配置 + $this->setName('version') + ->setDescription('show thinkphp framework version'); } - public function failed(array $data) + protected function execute(Input $input, Output $output) { - $command = unserialize($data['command']); - - if (method_exists($command, 'failed')) { - $command->failed(); - } + $output->writeln('v' . App::version()); } } diff --git a/thinkphp/library/think/console/command/make/Command.php b/thinkphp/library/think/console/command/make/Command.php new file mode 100644 index 000000000..b539eb236 --- /dev/null +++ b/thinkphp/library/think/console/command/make/Command.php @@ -0,0 +1,56 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\command\make; + +use think\console\command\Make; +use think\console\input\Argument; +use think\facade\App; + +class Command extends Make +{ + protected $type = "Command"; + + protected function configure() + { + parent::configure(); + $this->setName('make:command') + ->addArgument('commandName', Argument::OPTIONAL, "The name of the command") + ->setDescription('Create a new command class'); + } + + protected function buildClass($name) + { + $commandName = $this->input->getArgument('commandName') ?: strtolower(basename($name)); + $namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\'); + + $class = str_replace($namespace . '\\', '', $name); + $stub = file_get_contents($this->getStub()); + + return str_replace(['{%commandName%}', '{%className%}', '{%namespace%}', '{%app_namespace%}'], [ + $commandName, + $class, + $namespace, + App::getNamespace(), + ], $stub); + } + + protected function getStub() + { + return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'command.stub'; + } + + protected function getNamespace($appNamespace, $module) + { + return $appNamespace . '\\command'; + } + +} diff --git a/thinkphp/library/think/console/command/make/Controller.php b/thinkphp/library/think/console/command/make/Controller.php index afa7be905..2a6ab770d 100644 --- a/thinkphp/library/think/console/command/make/Controller.php +++ b/thinkphp/library/think/console/command/make/Controller.php @@ -11,30 +11,36 @@ namespace think\console\command\make; -use think\Config; use think\console\command\Make; use think\console\input\Option; +use think\facade\Config; class Controller extends Make { - protected $type = "Controller"; protected function configure() { parent::configure(); $this->setName('make:controller') + ->addOption('api', null, Option::VALUE_NONE, 'Generate an api controller class.') ->addOption('plain', null, Option::VALUE_NONE, 'Generate an empty controller class.') ->setDescription('Create a new resource controller class'); } protected function getStub() { - if ($this->input->getOption('plain')) { - return __DIR__ . '/stubs/controller.plain.stub'; + $stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR; + + if ($this->input->getOption('api')) { + return $stubPath . 'controller.api.stub'; } - return __DIR__ . '/stubs/controller.stub'; + if ($this->input->getOption('plain')) { + return $stubPath . 'controller.plain.stub'; + } + + return $stubPath . 'controller.stub'; } protected function getClassName($name) diff --git a/thinkphp/library/think/console/command/make/Middleware.php b/thinkphp/library/think/console/command/make/Middleware.php new file mode 100644 index 000000000..bfe821b03 --- /dev/null +++ b/thinkphp/library/think/console/command/make/Middleware.php @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\command\make; + +use think\console\command\Make; + +class Middleware extends Make +{ + protected $type = "Middleware"; + + protected function configure() + { + parent::configure(); + $this->setName('make:middleware') + ->setDescription('Create a new middleware class'); + } + + protected function getStub() + { + return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'middleware.stub'; + } + + protected function getNamespace($appNamespace, $module) + { + return parent::getNamespace($appNamespace, 'http') . '\middleware'; + } +} diff --git a/thinkphp/library/think/console/command/make/Model.php b/thinkphp/library/think/console/command/make/Model.php index d4e9b5dd0..03e6b3fcd 100644 --- a/thinkphp/library/think/console/command/make/Model.php +++ b/thinkphp/library/think/console/command/make/Model.php @@ -26,7 +26,7 @@ class Model extends Make protected function getStub() { - return __DIR__ . '/stubs/model.stub'; + return __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'model.stub'; } protected function getNamespace($appNamespace, $module) diff --git a/thinkphp/library/think/console/command/make/Validate.php b/thinkphp/library/think/console/command/make/Validate.php new file mode 100644 index 000000000..89830ad1d --- /dev/null +++ b/thinkphp/library/think/console/command/make/Validate.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\command\make; + +use think\console\command\Make; + +class Validate extends Make +{ + protected $type = "Validate"; + + protected function configure() + { + parent::configure(); + $this->setName('make:validate') + ->setDescription('Create a validate class'); + } + + protected function getStub() + { + $stubPath = __DIR__ . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR; + + return $stubPath . 'validate.stub'; + } + + protected function getNamespace($appNamespace, $module) + { + return parent::getNamespace($appNamespace, $module) . '\validate'; + } + +} diff --git a/thinkphp/library/think/console/command/make/stubs/command.stub b/thinkphp/library/think/console/command/make/stubs/command.stub new file mode 100644 index 000000000..d2c7c1e7e --- /dev/null +++ b/thinkphp/library/think/console/command/make/stubs/command.stub @@ -0,0 +1,24 @@ +setName('{%commandName%}'); + // 设置参数 + + } + + protected function execute(Input $input, Output $output) + { + // 指令输出 + $output->writeln('{%commandName%}'); + } +} diff --git a/thinkphp/library/think/console/command/make/stubs/controller.api.stub b/thinkphp/library/think/console/command/make/stubs/controller.api.stub new file mode 100644 index 000000000..54ec0594e --- /dev/null +++ b/thinkphp/library/think/console/command/make/stubs/controller.api.stub @@ -0,0 +1,64 @@ + ['规则1','规则2'...] + * + * @var array + */ + protected $rule = []; + + /** + * 定义错误信息 + * 格式:'字段名.规则名' => '错误信息' + * + * @var array + */ + protected $message = []; +} diff --git a/thinkphp/library/think/console/command/optimize/Autoload.php b/thinkphp/library/think/console/command/optimize/Autoload.php index 6a77c2cba..b51fd2593 100644 --- a/thinkphp/library/think/console/command/optimize/Autoload.php +++ b/thinkphp/library/think/console/command/optimize/Autoload.php @@ -10,15 +10,13 @@ // +---------------------------------------------------------------------- namespace think\console\command\optimize; -use think\App; -use think\Config; use think\console\Command; use think\console\Input; use think\console\Output; +use think\Container; class Autoload extends Command { - protected function configure() { $this->setName('optimize:autoload') @@ -37,20 +35,14 @@ class Autoload extends Command return [ EOF; - + $app = Container::get('app'); $namespacesToScan = [ - App::$namespace . '\\' => realpath(rtrim(APP_PATH)), - 'think\\' => LIB_PATH . 'think', - 'behavior\\' => LIB_PATH . 'behavior', - 'traits\\' => LIB_PATH . 'traits', - '' => realpath(rtrim(EXTEND_PATH)), + $app->getNamespace() . '\\' => realpath(rtrim($app->getAppPath())), + 'think\\' => $app->getThinkPath() . 'library/think', + 'traits\\' => $app->getThinkPath() . 'library/traits', + '' => realpath(rtrim($app->getRootPath() . 'extend')), ]; - $root_namespace = Config::get('root_namespace'); - foreach ($root_namespace as $namespace => $dir) { - $namespacesToScan[$namespace . '\\'] = realpath($dir); - } - krsort($namespacesToScan); $classMap = []; foreach ($namespacesToScan as $namespace => $dir) { @@ -59,7 +51,7 @@ EOF; continue; } - $namespaceFilter = $namespace === '' ? null : $namespace; + $namespaceFilter = '' === $namespace ? null : $namespace; $classMap = $this->addClassMapCode($dir, $namespaceFilter, $classMap); } @@ -68,12 +60,12 @@ EOF; $classmapFile .= ' ' . var_export($class, true) . ' => ' . $code; } $classmapFile .= "];\n"; - - if (!is_dir(RUNTIME_PATH)) { - @mkdir(RUNTIME_PATH, 0755, true); + $runtimePath = $app->getRuntimePath(); + if (!is_dir($runtimePath)) { + @mkdir($runtimePath, 0755, true); } - file_put_contents(RUNTIME_PATH . 'classmap' . EXT, $classmapFile); + file_put_contents($runtimePath . 'classmap.php', $classmapFile); $output->writeln('Succeed!'); } @@ -100,40 +92,33 @@ EOF; protected function getPathCode($path) { - $baseDir = ''; - $libPath = $this->normalizePath(realpath(LIB_PATH)); - $appPath = $this->normalizePath(realpath(APP_PATH)); - $extendPath = $this->normalizePath(realpath(EXTEND_PATH)); - $rootPath = $this->normalizePath(realpath(ROOT_PATH)); + $app = Container::get('app'); + $appPath = $this->normalizePath(realpath($app->getAppPath())); + $libPath = $this->normalizePath(realpath($app->getThinkPath() . 'library')); + $extendPath = $this->normalizePath(realpath($app->getRootPath() . 'extend')); $path = $this->normalizePath($path); - if ($libPath !== null && strpos($path, $libPath . '/') === 0) { - $path = substr($path, strlen(LIB_PATH)); - $baseDir = 'LIB_PATH'; - } elseif ($appPath !== null && strpos($path, $appPath . '/') === 0) { + if (strpos($path, $libPath . '/') === 0) { + $path = substr($path, strlen($app->getThinkPath() . 'library')); + $baseDir = "'" . $libPath . "/'"; + } elseif (strpos($path, $appPath . '/') === 0) { $path = substr($path, strlen($appPath) + 1); - $baseDir = 'APP_PATH'; - } elseif ($extendPath !== null && strpos($path, $extendPath . '/') === 0) { + $baseDir = "'" . $appPath . "/'"; + } elseif (strpos($path, $extendPath . '/') === 0) { $path = substr($path, strlen($extendPath) + 1); - $baseDir = 'EXTEND_PATH'; - } elseif ($rootPath !== null && strpos($path, $rootPath . '/') === 0) { - $path = substr($path, strlen($rootPath) + 1); - $baseDir = 'ROOT_PATH'; + $baseDir = "'" . $extendPath . "/'"; } - if ($path !== false) { + if (false !== $path) { $baseDir .= " . "; } - return $baseDir . (($path !== false) ? var_export($path, true) : ""); + return $baseDir . ((false !== $path) ? var_export($path, true) : ""); } protected function normalizePath($path) { - if ($path === false) { - return; - } $parts = []; $path = strtr($path, '\\', '/'); $prefix = ''; @@ -252,7 +237,7 @@ EOF; // strip leading non-php code if needed if (substr($contents, 0, 2) !== 'setName('optimize:config') @@ -30,64 +28,80 @@ class Config extends Command protected function execute(Input $input, Output $output) { - if ($input->hasArgument('module')) { - $module = $input->getArgument('module') . DS; + if ($input->getArgument('module')) { + $module = $input->getArgument('module') . DIRECTORY_SEPARATOR; } else { $module = ''; } - $content = 'buildCacheContent($module); - - if (!is_dir(RUNTIME_PATH . $module)) { - @mkdir(RUNTIME_PATH . $module, 0755, true); + $content = 'buildCacheContent($module); + $runtimePath = App::getRuntimePath(); + if (!is_dir($runtimePath . $module)) { + @mkdir($runtimePath . $module, 0755, true); } - file_put_contents(RUNTIME_PATH . $module . 'init' . EXT, $content); + file_put_contents($runtimePath . $module . 'init.php', $content); $output->writeln('Succeed!'); } protected function buildCacheContent($module) { - $content = ''; - $path = realpath(APP_PATH . $module) . DS; - + $content = '// This cache file is automatically generated at:' . date('Y-m-d H:i:s') . PHP_EOL; + $path = realpath(App::getAppPath() . $module) . DIRECTORY_SEPARATOR; if ($module) { - // 加载模块配置 - $config = ThinkConfig::load(CONF_PATH . $module . 'config' . CONF_EXT); + $configPath = is_dir($path . 'config') ? $path . 'config' : App::getConfigPath() . $module; + } else { + $configPath = App::getConfigPath(); + } + $ext = App::getConfigExt(); + $config = Container::get('config'); - // 读取数据库配置文件 - $filename = CONF_PATH . $module . 'database' . CONF_EXT; - ThinkConfig::load($filename, 'database'); + $files = is_dir($configPath) ? scandir($configPath) : []; - // 加载应用状态配置 - if ($config['app_status']) { - $config = ThinkConfig::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT); - } - // 读取扩展配置文件 - if (is_dir(CONF_PATH . $module . 'extra')) { - $dir = CONF_PATH . $module . 'extra'; - $files = scandir($dir); - foreach ($files as $file) { - if (strpos($file, CONF_EXT)) { - $filename = $dir . DS . $file; - ThinkConfig::load($filename, pathinfo($file, PATHINFO_FILENAME)); - } - } + foreach ($files as $file) { + if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $ext) { + $filename = $configPath . DIRECTORY_SEPARATOR . $file; + $config->load($filename, pathinfo($file, PATHINFO_FILENAME)); } } // 加载行为扩展文件 - if (is_file(CONF_PATH . $module . 'tags' . EXT)) { - $content .= '\think\Hook::import(' . (var_export(include CONF_PATH . $module . 'tags' . EXT, true)) . ');' . PHP_EOL; + if (is_file($path . 'tags.php')) { + $tags = include $path . 'tags.php'; + if (is_array($tags)) { + $content .= PHP_EOL . '\think\facade\Hook::import(' . (var_export($tags, true)) . ');' . PHP_EOL; + } } // 加载公共文件 - if (is_file($path . 'common' . EXT)) { - $content .= substr(php_strip_whitespace($path . 'common' . EXT), 5) . PHP_EOL; + if (is_file($path . 'common.php')) { + $common = substr(php_strip_whitespace($path . 'common.php'), 6); + if ($common) { + $content .= PHP_EOL . $common . PHP_EOL; + } } - $content .= '\think\Config::set(' . var_export(ThinkConfig::get(), true) . ');'; + if ('' == $module) { + $content .= PHP_EOL . substr(php_strip_whitespace(App::getThinkPath() . 'helper.php'), 6) . PHP_EOL; + + if (is_file($path . 'middleware.php')) { + $middleware = include $path . 'middleware.php'; + if (is_array($middleware)) { + $content .= PHP_EOL . '\think\Container::get("middleware")->import(' . var_export($middleware, true) . ');' . PHP_EOL; + } + } + } + + if (is_file($path . 'provider.php')) { + $provider = include $path . 'provider.php'; + if (is_array($provider)) { + $content .= PHP_EOL . '\think\Container::getInstance()->bindTo(' . var_export($provider, true) . ');' . PHP_EOL; + } + } + + $content .= PHP_EOL . '\think\facade\Config::set(' . var_export($config->get(), true) . ');' . PHP_EOL; + return $content; } } diff --git a/thinkphp/library/think/console/command/optimize/Route.php b/thinkphp/library/think/console/command/optimize/Route.php index 911e4c147..f6dc63288 100644 --- a/thinkphp/library/think/console/command/optimize/Route.php +++ b/thinkphp/library/think/console/command/optimize/Route.php @@ -6,19 +6,17 @@ // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- -// | Author: liu21st +// | Author: yunwuxin <448901948@qq.com> // +---------------------------------------------------------------------- namespace think\console\command\optimize; use think\console\Command; use think\console\Input; use think\console\Output; +use think\Container; class Route extends Command { - /** @var Output */ - protected $output; - protected function configure() { $this->setName('optimize:route') @@ -27,44 +25,42 @@ class Route extends Command protected function execute(Input $input, Output $output) { - file_put_contents(RUNTIME_PATH . 'route.php', $this->buildRouteCache()); + $filename = Container::get('app')->getRuntimePath() . 'route.php'; + if (is_file($filename)) { + unlink($filename); + } + file_put_contents($filename, $this->buildRouteCache()); $output->writeln('Succeed!'); } protected function buildRouteCache() { - $files = \think\Config::get('route_config_file'); + Container::get('route')->setName([]); + Container::get('route')->setTestMode(true); + // 路由检测 + $path = Container::get('app')->getRoutePath(); + + $files = is_dir($path) ? scandir($path) : []; + foreach ($files as $file) { - if (is_file(CONF_PATH . $file . CONF_EXT)) { - $config = include CONF_PATH . $file . CONF_EXT; - if (is_array($config)) { - \think\Route::import($config); + if (strpos($file, '.php')) { + $filename = $path . DIRECTORY_SEPARATOR . $file; + // 导入路由配置 + $rules = include $filename; + if (is_array($rules)) { + Container::get('route')->import($rules); } } } - $rules = \think\Route::rules(true); - array_walk_recursive($rules, [$this, 'buildClosure']); + + if (Container::get('config')->get('route_annotation')) { + $suffix = Container::get('config')->get('controller_suffix') || Container::get('config')->get('class_suffix'); + include Container::get('build')->buildRoute($suffix); + } + $content = 'getName(), true) . ';'; return $content; } - protected function buildClosure(&$value) - { - if ($value instanceof \Closure) { - $reflection = new \ReflectionFunction($value); - $startLine = $reflection->getStartLine(); - $endLine = $reflection->getEndLine(); - $file = $reflection->getFileName(); - $item = file($file); - $content = ''; - for ($i = $startLine - 1; $i <= $endLine - 1; $i++) { - $content .= $item[$i]; - } - $start = strpos($content, 'function'); - $end = strrpos($content, '}'); - $value = '[__start__' . substr($content, $start, $end - $start + 1) . '__end__]'; - } - } } diff --git a/thinkphp/library/think/console/command/optimize/Schema.php b/thinkphp/library/think/console/command/optimize/Schema.php index 27eb9dbb7..16ac83d5d 100644 --- a/thinkphp/library/think/console/command/optimize/Schema.php +++ b/thinkphp/library/think/console/command/optimize/Schema.php @@ -10,22 +10,18 @@ // +---------------------------------------------------------------------- namespace think\console\command\optimize; -use think\App; use think\console\Command; use think\console\Input; use think\console\input\Option; use think\console\Output; use think\Db; +use think\facade\App; class Schema extends Command { - /** @var Output */ - protected $output; - protected function configure() { $this->setName('optimize:schema') - ->addOption('config', null, Option::VALUE_REQUIRED, 'db config .') ->addOption('db', null, Option::VALUE_REQUIRED, 'db name .') ->addOption('table', null, Option::VALUE_REQUIRED, 'table name .') ->addOption('module', null, Option::VALUE_REQUIRED, 'module name .') @@ -34,54 +30,58 @@ class Schema extends Command protected function execute(Input $input, Output $output) { - if (!is_dir(RUNTIME_PATH . 'schema')) { - @mkdir(RUNTIME_PATH . 'schema', 0755, true); - } - $config = []; - if ($input->hasOption('config')) { - $config = $input->getOption('config'); + if (!is_dir(App::getRuntimePath() . 'schema')) { + @mkdir(App::getRuntimePath() . 'schema', 0755, true); } + if ($input->hasOption('module')) { $module = $input->getOption('module'); // 读取模型 - $list = scandir(APP_PATH . $module . DS . 'model'); - $app = App::$namespace; + $path = App::getAppPath() . $module . DIRECTORY_SEPARATOR . 'model'; + $list = is_dir($path) ? scandir($path) : []; + $namespace = App::getNamespace(); + foreach ($list as $file) { if (0 === strpos($file, '.')) { continue; } - $class = '\\' . $app . '\\' . $module . '\\model\\' . pathinfo($file, PATHINFO_FILENAME); + $class = '\\' . $namespace . '\\' . $module . '\\model\\' . pathinfo($file, PATHINFO_FILENAME); $this->buildModelSchema($class); } + $output->writeln('Succeed!'); return; } elseif ($input->hasOption('table')) { $table = $input->getOption('table'); - if (!strpos($table, '.')) { - $dbName = Db::connect($config)->getConfig('database'); + if (false === strpos($table, '.')) { + $dbName = Db::getConfig('database'); } + $tables[] = $table; } elseif ($input->hasOption('db')) { $dbName = $input->getOption('db'); - $tables = Db::connect($config)->getTables($dbName); - } elseif (!\think\Config::get('app_multi_module')) { - $app = App::$namespace; - $list = scandir(APP_PATH . 'model'); + $tables = Db::getConnection()->getTables($dbName); + } elseif (!\think\facade\Config::get('app_multi_module')) { + $namespace = App::getNamespace(); + $path = App::getAppPath() . 'model'; + $list = is_dir($path) ? scandir($path) : []; + foreach ($list as $file) { if (0 === strpos($file, '.')) { continue; } - $class = '\\' . $app . '\\model\\' . pathinfo($file, PATHINFO_FILENAME); + $class = '\\' . $namespace . '\\model\\' . pathinfo($file, PATHINFO_FILENAME); $this->buildModelSchema($class); } + $output->writeln('Succeed!'); return; } else { - $tables = Db::connect($config)->getTables(); + $tables = Db::getConnection()->getTables(); } $db = isset($dbName) ? $dbName . '.' : ''; - $this->buildDataBaseSchema($tables, $db, $config); + $this->buildDataBaseSchema($tables, $db); $output->writeln('Succeed!'); } @@ -95,22 +95,24 @@ class Schema extends Command $content = 'getFields($table); $content .= var_export($info, true) . ';'; - file_put_contents(RUNTIME_PATH . 'schema' . DS . $dbName . '.' . $table . EXT, $content); + + file_put_contents(App::getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $dbName . '.' . $table . '.php', $content); } } - protected function buildDataBaseSchema($tables, $db, $config) + protected function buildDataBaseSchema($tables, $db) { if ('' == $db) { - $dbName = Db::connect($config)->getConfig('database') . '.'; + $dbName = Db::getConfig('database') . '.'; } else { $dbName = $db; } + foreach ($tables as $table) { $content = 'getFields($db . $table); + $info = Db::getConnection()->getFields($db . $table); $content .= var_export($info, true) . ';'; - file_put_contents(RUNTIME_PATH . 'schema' . DS . $dbName . $table . EXT, $content); + file_put_contents(App::getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $dbName . $table . '.php', $content); } } } diff --git a/thinkphp/library/think/console/output/descriptor/Console.php b/thinkphp/library/think/console/output/descriptor/Console.php index 4648b68e6..8739c536e 100644 --- a/thinkphp/library/think/console/output/descriptor/Console.php +++ b/thinkphp/library/think/console/output/descriptor/Console.php @@ -104,6 +104,10 @@ class Console /** @var Command $command */ foreach ($commands as $name => $command) { + if (is_string($command)) { + $command = new $command(); + } + if (!$command->getName()) { continue; } diff --git a/thinkphp/library/think/console/output/driver/Console.php b/thinkphp/library/think/console/output/driver/Console.php index 8f29fd020..e041b5250 100644 --- a/thinkphp/library/think/console/output/driver/Console.php +++ b/thinkphp/library/think/console/output/driver/Console.php @@ -37,11 +37,6 @@ class Console $this->formatter->setDecorated($decorated); } - public function getFormatter() - { - return $this->formatter; - } - public function setDecorated($decorated) { $this->formatter->setDecorated($decorated); @@ -173,7 +168,7 @@ class Console return $this->terminalDimensions; } - if ('\\' === DS) { + if ('\\' === DIRECTORY_SEPARATOR) { if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { return [(int) $matches[1], (int) $matches[2]]; } diff --git a/thinkphp/library/think/console/output/question/Choice.php b/thinkphp/library/think/console/output/question/Choice.php index f6760e5ef..cdc3b4e4a 100644 --- a/thinkphp/library/think/console/output/question/Choice.php +++ b/thinkphp/library/think/console/output/question/Choice.php @@ -147,7 +147,7 @@ class Choice extends Question $result = $value; } - if (empty($result)) { + if (false === $result) { throw new \InvalidArgumentException(sprintf($errorMessage, $value)); } array_push($multiselectChoices, $result); diff --git a/thinkphp/library/think/controller/Rest.php b/thinkphp/library/think/controller/Rest.php deleted file mode 100644 index 8c5911dff..000000000 --- a/thinkphp/library/think/controller/Rest.php +++ /dev/null @@ -1,99 +0,0 @@ - -// +---------------------------------------------------------------------- - -namespace think\controller; - -use think\App; -use think\Request; -use think\Response; - -abstract class Rest -{ - - protected $method; // 当前请求类型 - protected $type; // 当前资源类型 - // 输出类型 - protected $restMethodList = 'get|post|put|delete'; - protected $restDefaultMethod = 'get'; - protected $restTypeList = 'html|xml|json|rss'; - protected $restDefaultType = 'html'; - protected $restOutputType = [ // REST允许输出的资源类型列表 - 'xml' => 'application/xml', - 'json' => 'application/json', - 'html' => 'text/html', - ]; - - /** - * 构造函数 取得模板对象实例 - * @access public - */ - public function __construct() - { - // 资源类型检测 - $request = Request::instance(); - $ext = $request->ext(); - if ('' == $ext) { - // 自动检测资源类型 - $this->type = $request->type(); - } elseif (!preg_match('/\(' . $this->restTypeList . '\)$/i', $ext)) { - // 资源类型非法 则用默认资源类型访问 - $this->type = $this->restDefaultType; - } else { - $this->type = $ext; - } - // 请求方式检测 - $method = strtolower($request->method()); - if (false === stripos($this->restMethodList, $method)) { - // 请求方式非法 则用默认请求方法 - $method = $this->restDefaultMethod; - } - $this->method = $method; - } - - /** - * REST 调用 - * @access public - * @param string $method 方法名 - * @return mixed - * @throws \Exception - */ - public function _empty($method) - { - if (method_exists($this, $method . '_' . $this->method . '_' . $this->type)) { - // RESTFul方法支持 - $fun = $method . '_' . $this->method . '_' . $this->type; - } elseif ($this->method == $this->restDefaultMethod && method_exists($this, $method . '_' . $this->type)) { - $fun = $method . '_' . $this->type; - } elseif ($this->type == $this->restDefaultType && method_exists($this, $method . '_' . $this->method)) { - $fun = $method . '_' . $this->method; - } - if (isset($fun)) { - return App::invokeMethod([$this, $fun]); - } else { - // 抛出异常 - throw new \Exception('error action :' . $method); - } - } - - /** - * 输出返回数据 - * @access protected - * @param mixed $data 要返回的数据 - * @param String $type 返回类型 JSON XML - * @param integer $code HTTP状态码 - * @return Response - */ - protected function response($data, $type = 'json', $code = 200) - { - return Response::create($data, $type)->code($code); - } - -} diff --git a/thinkphp/library/think/controller/Yar.php b/thinkphp/library/think/controller/Yar.php deleted file mode 100644 index af4e9a1a8..000000000 --- a/thinkphp/library/think/controller/Yar.php +++ /dev/null @@ -1,51 +0,0 @@ - -// +---------------------------------------------------------------------- - -namespace think\controller; - -/** - * ThinkPHP Yar控制器类 - */ -abstract class Yar -{ - - /** - * 构造函数 - * @access public - */ - public function __construct() - { - //控制器初始化 - if (method_exists($this, '_initialize')) { - $this->_initialize(); - } - - //判断扩展是否存在 - if (!extension_loaded('yar')) { - throw new \Exception('not support yar'); - } - - //实例化Yar_Server - $server = new \Yar_Server($this); - // 启动server - $server->handle(); - } - - /** - * 魔术方法 有不存在的操作的时候执行 - * @access public - * @param string $method 方法名 - * @param array $args 参数 - * @return mixed - */ - public function __call($method, $args) - {} -} diff --git a/thinkphp/library/think/db/Builder.php b/thinkphp/library/think/db/Builder.php index 7a589d08c..7f8100599 100644 --- a/thinkphp/library/think/db/Builder.php +++ b/thinkphp/library/think/db/Builder.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -18,35 +18,49 @@ abstract class Builder { // connection对象实例 protected $connection; - // 查询对象实例 - protected $query; - // 数据库表达式 - protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME']; + // 查询表达式映射 + protected $exp = ['EQ' => '=', 'NEQ' => '<>', 'GT' => '>', 'EGT' => '>=', 'LT' => '<', 'ELT' => '<=', 'NOTLIKE' => 'NOT LIKE', 'NOTIN' => 'NOT IN', 'NOTBETWEEN' => 'NOT BETWEEN', 'NOTEXISTS' => 'NOT EXISTS', 'NOTNULL' => 'NOT NULL', 'NOTBETWEEN TIME' => 'NOT BETWEEN TIME']; + + // 查询表达式解析 + protected $parser = [ + 'parseCompare' => ['=', '<>', '>', '>=', '<', '<='], + 'parseLike' => ['LIKE', 'NOT LIKE'], + 'parseBetween' => ['NOT BETWEEN', 'BETWEEN'], + 'parseIn' => ['NOT IN', 'IN'], + 'parseExp' => ['EXP'], + 'parseNull' => ['NOT NULL', 'NULL'], + 'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'], + 'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'], + 'parseExists' => ['NOT EXISTS', 'EXISTS'], + 'parseColumn' => ['COLUMN'], + ]; // SQL表达式 - protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%'; - protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; - protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; - protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; - protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT% %LOCK%%COMMENT%'; + + protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + + protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; + + protected $updateSql = 'UPDATE %TABLE% SET %SET%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%'; + + protected $deleteSql = 'DELETE FROM %TABLE%%USING%%JOIN%%WHERE%%ORDER%%LIMIT% %LOCK%%COMMENT%'; /** - * 构造函数 + * 架构函数 * @access public - * @param Connection $connection 数据库连接对象实例 - * @param Query $query 数据库查询对象实例 + * @param Connection $connection 数据库连接对象实例 */ - public function __construct(Connection $connection, Query $query) + public function __construct(Connection $connection) { $this->connection = $connection; - $this->query = $query; } /** * 获取当前的连接对象实例 * @access public - * @return void + * @return Connection */ public function getConnection() { @@ -54,421 +68,651 @@ abstract class Builder } /** - * 获取当前的Query对象实例 + * 注册查询表达式解析 * @access public - * @return void + * @param string $name 解析方法 + * @param array $parser 匹配表达式数据 + * @return $this */ - public function getQuery() + public function bindParser($name, $parser) { - return $this->query; - } - - /** - * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写) - * @access protected - * @param string $sql sql语句 - * @return string - */ - protected function parseSqlTable($sql) - { - return $this->query->parseSqlTable($sql); + $this->parser[$name] = $parser; + return $this; } /** * 数据分析 * @access protected - * @param array $data 数据 - * @param array $options 查询参数 + * @param Query $query 查询对象 + * @param array $data 数据 + * @param array $fields 字段信息 + * @param array $bind 参数绑定 * @return array */ - protected function parseData($data, $options) + protected function parseData(Query $query, $data = [], $fields = [], $bind = []) { if (empty($data)) { return []; } + $options = $query->getOptions(); + // 获取绑定信息 - $bind = $this->query->getFieldsBind($options); - if ('*' == $options['field']) { - $fields = array_keys($bind); - } else { - $fields = $options['field']; + if (empty($bind)) { + $bind = $this->connection->getFieldsBind($options['table']); + } + + if (empty($fields)) { + if ('*' == $options['field']) { + $fields = array_keys($bind); + } else { + $fields = $options['field']; + } } $result = []; + foreach ($data as $key => $val) { - $item = $this->parseKey($key, $options); - if (is_object($val) && method_exists($val, '__toString')) { + if ('*' != $options['field'] && !in_array($key, $fields, true)) { + continue; + } + + $item = $this->parseKey($query, $key, true); + + if ($val instanceof Expression) { + $result[$item] = $val->getValue(); + continue; + } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $this->connection->getFieldsType($options['table'], $key))) { + $val = json_encode($val, JSON_UNESCAPED_UNICODE); + } elseif (is_object($val) && method_exists($val, '__toString')) { // 对象数据写入 $val = $val->__toString(); } - if (false === strpos($key, '.') && !in_array($key, $fields, true)) { + + if (false !== strpos($key, '->')) { + list($key, $name) = explode('->', $key); + $item = $this->parseKey($query, $key); + $result[$item] = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key, $val, $bind) . ')'; + } elseif ('*' == $options['field'] && false === strpos($key, '.') && !in_array($key, $fields, true)) { if ($options['strict']) { throw new Exception('fields not exists:[' . $key . ']'); } } elseif (is_null($val)) { $result[$item] = 'NULL'; - } elseif (isset($val[0]) && 'exp' == $val[0]) { - $result[$item] = $val[1]; + } elseif (is_array($val) && !empty($val)) { + switch (strtoupper($val[0])) { + case 'INC': + $result[$item] = $item . ' + ' . floatval($val[1]); + break; + case 'DEC': + $result[$item] = $item . ' - ' . floatval($val[1]); + break; + case 'EXP': + throw new Exception('not support data:[' . $val[0] . ']'); + } } elseif (is_scalar($val)) { // 过滤非标量数据 - if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) { - $result[$item] = $val; - } else { - $key = str_replace('.', '_', $key); - $this->query->bind('__data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR); - $result[$item] = ':__data__' . $key; - } + $result[$item] = $this->parseDataBind($query, $key, $val, $bind); } } + return $result; } /** - * 字段名分析 + * 数据绑定处理 * @access protected - * @param string $key - * @param array $options + * @param Query $query 查询对象 + * @param string $key 字段名 + * @param mixed $data 数据 + * @param array $bind 绑定数据 * @return string */ - protected function parseKey($key, $options = []) + protected function parseDataBind(Query $query, $key, $data, $bind = []) { - return $key; + if ($data instanceof Expression) { + return $data->getValue(); + } + + $name = $query->bind($data, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR); + + return ':' . $name; } /** - * value分析 - * @access protected - * @param mixed $value - * @param string $field - * @return string|array + * 字段名分析 + * @access public + * @param Query $query 查询对象 + * @param mixed $key 字段名 + * @param bool $strict 严格检测 + * @return string */ - protected function parseValue($value, $field = '') + public function parseKey(Query $query, $key, $strict = false) { - if (is_string($value)) { - $value = strpos($value, ':') === 0 && $this->query->isBind(substr($value, 1)) ? $value : $this->connection->quote($value); - } elseif (is_array($value)) { - $value = array_map([$this, 'parseValue'], $value); - } elseif (is_bool($value)) { - $value = $value ? '1' : '0'; - } elseif (is_null($value)) { - $value = 'null'; - } - return $value; + return $key instanceof Expression ? $key->getValue() : $key; } /** * field分析 * @access protected - * @param mixed $fields - * @param array $options + * @param Query $query 查询对象 + * @param mixed $fields 字段名 * @return string */ - protected function parseField($fields, $options = []) + protected function parseField(Query $query, $fields) { if ('*' == $fields || empty($fields)) { $fieldsStr = '*'; } elseif (is_array($fields)) { // 支持 'field1'=>'field2' 这样的字段别名定义 $array = []; + foreach ($fields as $key => $field) { if (!is_numeric($key)) { - $array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options); + $array[] = $this->parseKey($query, $key) . ' AS ' . $this->parseKey($query, $field, true); } else { - $array[] = $this->parseKey($field, $options); + $array[] = $this->parseKey($query, $field); } } + $fieldsStr = implode(',', $array); } + return $fieldsStr; } /** * table分析 * @access protected - * @param mixed $tables - * @param array $options + * @param Query $query 查询对象 + * @param mixed $tables 表名 * @return string */ - protected function parseTable($tables, $options = []) + protected function parseTable(Query $query, $tables) { - $item = []; + $item = []; + $options = $query->getOptions(); + foreach ((array) $tables as $key => $table) { if (!is_numeric($key)) { - if (strpos($key, '@think')) { - $key = strstr($key, '@think', true); - } - $key = $this->parseSqlTable($key); - $item[] = $this->parseKey($key) . ' ' . $this->parseKey($table); + $key = $this->connection->parseSqlTable($key); + $item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table); } else { - $table = $this->parseSqlTable($table); + $table = $this->connection->parseSqlTable($table); + if (isset($options['alias'][$table])) { - $item[] = $this->parseKey($table) . ' ' . $this->parseKey($options['alias'][$table]); + $item[] = $this->parseKey($query, $table) . ' ' . $this->parseKey($query, $options['alias'][$table]); } else { - $item[] = $this->parseKey($table); + $item[] = $this->parseKey($query, $table); } } } + return implode(',', $item); } /** * where分析 * @access protected - * @param mixed $where 查询条件 - * @param array $options 查询参数 + * @param Query $query 查询对象 + * @param mixed $where 查询条件 * @return string */ - protected function parseWhere($where, $options) + protected function parseWhere(Query $query, $where) { - $whereStr = $this->buildWhere($where, $options); + $options = $query->getOptions(); + $whereStr = $this->buildWhere($query, $where); + if (!empty($options['soft_delete'])) { // 附加软删除条件 list($field, $condition) = $options['soft_delete']; - $binds = $this->query->getFieldsBind($options); + $binds = $this->connection->getFieldsBind($options['table']); $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : ''; - $whereStr = $whereStr . $this->parseWhereItem($field, $condition, '', $options, $binds); + $whereStr = $whereStr . $this->parseWhereItem($query, $field, $condition, '', $binds); } + return empty($whereStr) ? '' : ' WHERE ' . $whereStr; } /** * 生成查询条件SQL * @access public - * @param mixed $where - * @param array $options + * @param Query $query 查询对象 + * @param mixed $where 查询条件 * @return string */ - public function buildWhere($where, $options) + public function buildWhere(Query $query, $where) { if (empty($where)) { $where = []; } - if ($where instanceof Query) { - return $this->buildWhere($where->getOptions('where'), $options); - } - $whereStr = ''; - $binds = $this->query->getFieldsBind($options); - foreach ($where as $key => $val) { + $binds = $this->connection->getFieldsBind($query->getOptions('table')); + + foreach ($where as $logic => $val) { $str = []; - foreach ($val as $field => $value) { + + foreach ($val as $value) { + if ($value instanceof Expression) { + $str[] = ' ' . $logic . ' ( ' . $value->getValue() . ' )'; + continue; + } + + if (is_array($value)) { + if (key($value) !== 0) { + throw new Exception('where express error:' . var_export($value, true)); + } + $field = array_shift($value); + } elseif (!($value instanceof \Closure)) { + throw new Exception('where express error:' . var_export($value, true)); + } + if ($value instanceof \Closure) { // 使用闭包查询 - $query = new Query($this->connection); - call_user_func_array($value, [ & $query]); - $whereClause = $this->buildWhere($query->getOptions('where'), $options); + $newQuery = $query->newQuery()->setConnection($this->connection); + $value($newQuery); + $whereClause = $this->buildWhere($query, $newQuery->getOptions('where')); + if (!empty($whereClause)) { - $str[] = ' ' . $key . ' ( ' . $whereClause . ' )'; + $str[] = ' ' . $logic . ' ( ' . $whereClause . ' )'; } + } elseif (is_array($field)) { + array_unshift($value, $field); + $str2 = []; + foreach ($value as $item) { + $str2[] = $this->parseWhereItem($query, array_shift($item), $item, $logic, $binds); + } + + $str[] = ' ' . $logic . ' ( ' . implode(' AND ', $str2) . ' )'; } elseif (strpos($field, '|')) { // 不同字段使用相同查询条件(OR) $array = explode('|', $field); $item = []; + foreach ($array as $k) { - $item[] = $this->parseWhereItem($k, $value, '', $options, $binds); + $item[] = $this->parseWhereItem($query, $k, $value, '', $binds); } - $str[] = ' ' . $key . ' ( ' . implode(' OR ', $item) . ' )'; + + $str[] = ' ' . $logic . ' ( ' . implode(' OR ', $item) . ' )'; } elseif (strpos($field, '&')) { // 不同字段使用相同查询条件(AND) $array = explode('&', $field); $item = []; + foreach ($array as $k) { - $item[] = $this->parseWhereItem($k, $value, '', $options, $binds); + $item[] = $this->parseWhereItem($query, $k, $value, '', $binds); } - $str[] = ' ' . $key . ' ( ' . implode(' AND ', $item) . ' )'; + + $str[] = ' ' . $logic . ' ( ' . implode(' AND ', $item) . ' )'; } else { // 对字段使用表达式查询 $field = is_string($field) ? $field : ''; - $str[] = ' ' . $key . ' ' . $this->parseWhereItem($field, $value, $key, $options, $binds); + $str[] = ' ' . $logic . ' ' . $this->parseWhereItem($query, $field, $value, $logic, $binds); } } - $whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($key) + 1) : implode(' ', $str); + $whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($logic) + 1) : implode(' ', $str); } return $whereStr; } // where子单元分析 - protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null) + protected function parseWhereItem(Query $query, $field, $val, $rule = '', $binds = []) { // 字段分析 - $key = $field ? $this->parseKey($field, $options) : ''; + $key = $field ? $this->parseKey($query, $field, true) : ''; // 查询规则和条件 if (!is_array($val)) { - $val = ['=', $val]; + $val = is_null($val) ? ['NULL', ''] : ['=', $val]; } + list($exp, $value) = $val; // 对一个字段使用多个查询条件 if (is_array($exp)) { $item = array_pop($val); + // 传入 or 或者 and if (is_string($item) && in_array($item, ['AND', 'and', 'OR', 'or'])) { $rule = $item; } else { array_push($val, $item); } + foreach ($val as $k => $item) { - $bindName = 'where_' . str_replace('.', '_', $field) . '_' . $k; - $str[] = $this->parseWhereItem($field, $item, $rule, $options, $binds, $bindName); + $str[] = $this->parseWhereItem($query, $field, $item, $rule, $binds); } + return '( ' . implode(' ' . $rule . ' ', $str) . ' )'; } // 检测操作符 - if (!in_array($exp, $this->exp)) { - $exp = strtolower($exp); - if (isset($this->exp[$exp])) { - $exp = $this->exp[$exp]; - } else { - throw new Exception('where express error:' . $exp); - } - } - $bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field); - if (preg_match('/\W/', $bindName)) { - // 处理带非单词字符的字段名 - $bindName = md5($bindName); + $exp = strtoupper($exp); + if (isset($this->exp[$exp])) { + $exp = $this->exp[$exp]; } - $bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR; - if (is_scalar($value) && array_key_exists($field, $binds) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) { - if (strpos($value, ':') !== 0 || !$this->query->isBind(substr($value, 1))) { - if ($this->query->isBind($bindName)) { - $bindName .= '_' . str_replace('.', '_', uniqid('', true)); - } - $this->query->bind($bindName, $value, $bindType); - $value = ':' . $bindName; + if ($value instanceof Expression) { + + } elseif (is_object($value) && method_exists($value, '__toString')) { + // 对象数据写入 + $value = $value->__toString(); + } + + if (strpos($field, '->')) { + $jsonType = $query->getJsonFieldType($field); + $bindType = $this->connection->getFieldBindType($jsonType); + } else { + $bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR; + } + + if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) { + if (0 === strpos($value, ':') && $query->isBind(substr($value, 1))) { + } else { + $name = $query->bind($value, $bindType); + $value = ':' . $name; } } - $whereStr = ''; - if (in_array($exp, ['=', '<>', '>', '>=', '<', '<='])) { - // 比较运算 - if ($value instanceof \Closure) { - $whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value); - } else { - $whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field); + // 解析查询表达式 + foreach ($this->parser as $fun => $parse) { + if (in_array($exp, $parse)) { + $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindType, isset($val[2]) ? $val[2] : 'AND'); + break; } - } elseif ('LIKE' == $exp || 'NOT LIKE' == $exp) { - // 模糊匹配 - if (is_array($value)) { - foreach ($value as $item) { - $array[] = $key . ' ' . $exp . ' ' . $this->parseValue($item, $field); - } - $logic = isset($val[2]) ? $val[2] : 'AND'; - $whereStr .= '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')'; - } else { - $whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field); - } - } elseif ('EXP' == $exp) { - // 表达式查询 - $whereStr .= '( ' . $key . ' ' . $value . ' )'; - } elseif (in_array($exp, ['NOT NULL', 'NULL'])) { - // NULL 查询 - $whereStr .= $key . ' IS ' . $exp; - } elseif (in_array($exp, ['NOT IN', 'IN'])) { - // IN 查询 - if ($value instanceof \Closure) { - $whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value); - } else { - $value = is_array($value) ? $value : explode(',', $value); - if (array_key_exists($field, $binds)) { - $bind = []; - $array = []; - foreach ($value as $k => $v) { - if ($this->query->isBind($bindName . '_in_' . $k)) { - $bindKey = $bindName . '_in_' . uniqid() . '_' . $k; - } else { - $bindKey = $bindName . '_in_' . $k; - } - $bind[$bindKey] = [$v, $bindType]; - $array[] = ':' . $bindKey; - } - $this->query->bind($bind); - $zone = implode(',', $array); - } else { - $zone = implode(',', $this->parseValue($value, $field)); - } - $whereStr .= $key . ' ' . $exp . ' (' . (empty($zone) ? "''" : $zone) . ')'; - } - } elseif (in_array($exp, ['NOT BETWEEN', 'BETWEEN'])) { - // BETWEEN 查询 - $data = is_array($value) ? $value : explode(',', $value); - if (array_key_exists($field, $binds)) { - if ($this->query->isBind($bindName . '_between_1')) { - $bindKey1 = $bindName . '_between_1' . uniqid(); - $bindKey2 = $bindName . '_between_2' . uniqid(); - } else { - $bindKey1 = $bindName . '_between_1'; - $bindKey2 = $bindName . '_between_2'; - } - $bind = [ - $bindKey1 => [$data[0], $bindType], - $bindKey2 => [$data[1], $bindType], - ]; - $this->query->bind($bind); - $between = ':' . $bindKey1 . ' AND :' . $bindKey2; - } else { - $between = $this->parseValue($data[0], $field) . ' AND ' . $this->parseValue($data[1], $field); - } - $whereStr .= $key . ' ' . $exp . ' ' . $between; - } elseif (in_array($exp, ['NOT EXISTS', 'EXISTS'])) { - // EXISTS 查询 - if ($value instanceof \Closure) { - $whereStr .= $exp . ' ' . $this->parseClosure($value); - } else { - $whereStr .= $exp . ' (' . $value . ')'; - } - } elseif (in_array($exp, ['< TIME', '> TIME', '<= TIME', '>= TIME'])) { - $whereStr .= $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($value, $field, $options, $bindName, $bindType); - } elseif (in_array($exp, ['BETWEEN TIME', 'NOT BETWEEN TIME'])) { - if (is_string($value)) { - $value = explode(',', $value); - } - - $whereStr .= $key . ' ' . substr($exp, 0, -4) . $this->parseDateTime($value[0], $field, $options, $bindName . '_between_1', $bindType) . ' AND ' . $this->parseDateTime($value[1], $field, $options, $bindName . '_between_2', $bindType); } + + if (!isset($whereStr)) { + throw new Exception('where express error:' . $exp); + } + return $whereStr; } - // 执行闭包子查询 - protected function parseClosure($call, $show = true) + /** + * 模糊查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType + * @param string $logic + * @return string + */ + protected function parseLike(Query $query, $key, $exp, $value, $field, $bindType, $logic) { - $query = new Query($this->connection); - call_user_func_array($call, [ & $query]); - return $query->buildSql($show); + // 模糊匹配 + if (is_array($value)) { + foreach ($value as $item) { + $name = $query->bind($item, $bindType); + $array[] = $key . ' ' . $exp . ' :' . $name; + } + + $whereStr = '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')'; + } else { + $whereStr = $key . ' ' . $exp . ' ' . $value; + } + + return $whereStr; + } + + /** + * 表达式查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param array $value + * @param string $field + * @param integer $bindType + * @return string + */ + protected function parseColumn(Query $query, $key, $exp, array $value, $field, $bindType) + { + // 字段比较查询 + list($op, $field2) = $value; + + if (!in_array($op, ['=', '<>', '>', '>=', '<', '<='])) { + throw new Exception('where express error:' . var_export($value, true)); + } + + return '( ' . $key . ' ' . $op . ' ' . $this->parseKey($query, $field2, true) . ' )'; + } + + /** + * 表达式查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param Expression $value + * @param string $field + * @param integer $bindType + * @return string + */ + protected function parseExp(Query $query, $key, $exp, Expression $value, $field, $bindType) + { + // 表达式查询 + return '( ' . $key . ' ' . $value->getValue() . ' )'; + } + + /** + * Null查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType + * @return string + */ + protected function parseNull(Query $query, $key, $exp, $value, $field, $bindType) + { + // NULL 查询 + return $key . ' IS ' . $exp; + } + + /** + * 范围查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType + * @return string + */ + protected function parseBetween(Query $query, $key, $exp, $value, $field, $bindType) + { + // BETWEEN 查询 + $data = is_array($value) ? $value : explode(',', $value); + + $min = $query->bind($data[0], $bindType); + $max = $query->bind($data[1], $bindType); + + return $key . ' ' . $exp . ' :' . $min . ' AND :' . $max . ' '; + } + + /** + * Exists查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType + * @return string + */ + protected function parseExists(Query $query, $key, $exp, $value, $field, $bindType) + { + // EXISTS 查询 + if ($value instanceof \Closure) { + $value = $this->parseClosure($query, $value, false); + } elseif ($value instanceof Expression) { + $value = $value->getValue(); + } else { + throw new Exception('where express error:' . $value); + } + + return $exp . ' (' . $value . ')'; + } + + /** + * 时间比较查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType + * @return string + */ + protected function parseTime(Query $query, $key, $exp, $value, $field, $bindType) + { + return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindType); + } + + /** + * 大小比较查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType + * @return string + */ + protected function parseCompare(Query $query, $key, $exp, $value, $field, $bindType) + { + if (is_array($value)) { + throw new Exception('where express error:' . $exp . var_export($value, true)); + } + + // 比较运算 + if ($value instanceof \Closure) { + $value = $this->parseClosure($query, $value); + } + + return $key . ' ' . $exp . ' ' . $value; + } + + /** + * 时间范围查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType + * @return string + */ + protected function parseBetweenTime(Query $query, $key, $exp, $value, $field, $bindType) + { + if (is_string($value)) { + $value = explode(',', $value); + } + + return $key . ' ' . substr($exp, 0, -4) + . $this->parseDateTime($query, $value[0], $field, $bindType) + . ' AND ' + . $this->parseDateTime($query, $value[1], $field, $bindType); + + } + + /** + * IN查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @param integer $bindType + * @return string + */ + protected function parseIn(Query $query, $key, $exp, $value, $field, $bindType) + { + // IN 查询 + if ($value instanceof \Closure) { + $value = $this->parseClosure($query, $value, false); + } elseif ($value instanceof Expression) { + $value = $value->getValue(); + } else { + $value = array_unique(is_array($value) ? $value : explode(',', $value)); + + $array = []; + + foreach ($value as $k => $v) { + $name = $query->bind($v, $bindType); + $array[] = ':' . $name; + } + + $zone = implode(',', $array); + + $value = empty($zone) ? "''" : $zone; + } + + return $key . ' ' . $exp . ' (' . $value . ')'; + } + + /** + * 闭包子查询 + * @access protected + * @param Query $query 查询对象 + * @param \Closure $call + * @param bool $show + * @return string + */ + protected function parseClosure(Query $query, $call, $show = true) + { + $newQuery = $query->newQuery()->setConnection($this->connection); + $call($newQuery); + + return $newQuery->buildSql($show); } /** * 日期时间条件解析 * @access protected - * @param string $value - * @param string $key - * @param array $options - * @param string $bindName - * @param integer $bindType + * @param Query $query 查询对象 + * @param string $value + * @param string $key + * @param integer $bindType * @return string */ - protected function parseDateTime($value, $key, $options = [], $bindName = null, $bindType = null) + protected function parseDateTime(Query $query, $value, $key, $bindType = null) { + $options = $query->getOptions(); + // 获取时间字段类型 if (strpos($key, '.')) { list($table, $key) = explode('.', $key); + if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) { $table = $pos; } } else { $table = $options['table']; } - $type = $this->query->getTableInfo($table, 'type'); + + $type = $this->connection->getTableInfo($table, 'type'); + if (isset($type[$key])) { $info = $type[$key]; } + if (isset($info)) { if (is_string($value)) { $value = strtotime($value) ?: $value; @@ -482,18 +726,20 @@ abstract class Builder $value = date('Y-m-d', $value); } } - $bindName = $bindName ?: $key; - $this->query->bind($bindName, $value, $bindType); - return ':' . $bindName; + + $name = $query->bind($value, $bindType); + + return ':' . $name; } /** * limit分析 * @access protected - * @param mixed $lmit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ - protected function parseLimit($limit) + protected function parseLimit(Query $query, $limit) { return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : ''; } @@ -501,81 +747,139 @@ abstract class Builder /** * join分析 * @access protected - * @param array $join - * @param array $options 查询条件 + * @param Query $query 查询对象 + * @param array $join * @return string */ - protected function parseJoin($join, $options = []) + protected function parseJoin(Query $query, $join) { $joinStr = ''; + if (!empty($join)) { foreach ($join as $item) { list($table, $type, $on) = $item; - $condition = []; + + $condition = []; + foreach ((array) $on as $val) { - if (strpos($val, '=')) { + if ($val instanceof Expression) { + $condition[] = $val->getValue(); + } elseif (strpos($val, '=')) { list($val1, $val2) = explode('=', $val, 2); - $condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options); + + $condition[] = $this->parseKey($query, $val1) . '=' . $this->parseKey($query, $val2); } else { $condition[] = $val; } } - $table = $this->parseTable($table, $options); + $table = $this->parseTable($query, $table); + $joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . implode(' AND ', $condition); } } + return $joinStr; } /** * order分析 * @access protected - * @param mixed $order - * @param array $options 查询条件 + * @param Query $query 查询对象 + * @param mixed $order * @return string */ - protected function parseOrder($order, $options = []) + protected function parseOrder(Query $query, $order) { - if (is_array($order)) { - $array = []; - foreach ($order as $key => $val) { + foreach ($order as $key => $val) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif (is_array($val) && preg_match('/^[\w\.]+$/', $key)) { + $array[] = $this->parseOrderField($query, $key, $val); + } elseif ('[rand]' == $val) { + $array[] = $this->parseRand($query); + } elseif (is_string($val)) { if (is_numeric($key)) { - if ('[rand]' == $val) { - $array[] = $this->parseRand(); - } elseif (false === strpos($val, '(')) { - $array[] = $this->parseKey($val, $options); - } else { - $array[] = $val; - } + list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' '); } else { - $sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : ''; - $array[] = $this->parseKey($key, $options) . ' ' . $sort; + $sort = $val; + } + + if (preg_match('/^[\w\.]+$/', $key)) { + $sort = strtoupper($sort); + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; + $array[] = $this->parseKey($query, $key, true) . $sort; + } else { + throw new Exception('order express error:' . $key); } } - $order = implode(',', $array); } - return !empty($order) ? ' ORDER BY ' . $order : ''; + + return empty($array) ? '' : ' ORDER BY ' . implode(',', $array); + } + + /** + * orderField分析 + * @access protected + * @param Query $query 查询对象 + * @param mixed $key + * @param array $val + * @return string + */ + protected function parseOrderField($query, $key, $val) + { + if (isset($val['sort'])) { + $sort = $val['sort']; + unset($val['sort']); + } else { + $sort = ''; + } + + $sort = strtoupper($sort); + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; + + $options = $query->getOptions(); + $bind = $this->connection->getFieldsBind($options['table']); + + foreach ($val as $k => $item) { + $val[$k] = $this->parseDataBind($query, $key, $item, $bind); + } + + return 'field(' . $this->parseKey($query, $key, true) . ',' . implode(',', $val) . ')' . $sort; } /** * group分析 * @access protected - * @param mixed $group + * @param Query $query 查询对象 + * @param mixed $group * @return string */ - protected function parseGroup($group) + protected function parseGroup(Query $query, $group) { - return !empty($group) ? ' GROUP BY ' . $group : ''; + if (empty($group)) { + return ''; + } + + if (is_string($group)) { + $group = explode(',', $group); + } + + foreach ($group as $key) { + $val[] = $this->parseKey($query, $key); + } + + return ' GROUP BY ' . implode(',', $val); } /** * having分析 * @access protected - * @param string $having + * @param Query $query 查询对象 + * @param string $having * @return string */ - protected function parseHaving($having) + protected function parseHaving(Query $query, $having) { return !empty($having) ? ' HAVING ' . $having : ''; } @@ -583,21 +887,27 @@ abstract class Builder /** * comment分析 * @access protected - * @param string $comment + * @param Query $query 查询对象 + * @param string $comment * @return string */ - protected function parseComment($comment) + protected function parseComment(Query $query, $comment) { + if (false !== strpos($comment, '*/')) { + $comment = strstr($comment, '*/', true); + } + return !empty($comment) ? ' /* ' . $comment . ' */' : ''; } /** * distinct分析 * @access protected - * @param mixed $distinct + * @param Query $query 查询对象 + * @param mixed $distinct * @return string */ - protected function parseDistinct($distinct) + protected function parseDistinct(Query $query, $distinct) { return !empty($distinct) ? ' DISTINCT ' : ''; } @@ -605,240 +915,252 @@ abstract class Builder /** * union分析 * @access protected - * @param mixed $union + * @param Query $query 查询对象 + * @param mixed $union * @return string */ - protected function parseUnion($union) + protected function parseUnion(Query $query, $union) { if (empty($union)) { return ''; } + $type = $union['type']; unset($union['type']); + foreach ($union as $u) { if ($u instanceof \Closure) { - $sql[] = $type . ' ' . $this->parseClosure($u, false); + $sql[] = $type . ' ' . $this->parseClosure($query, $u); } elseif (is_string($u)) { - $sql[] = $type . ' ' . $this->parseSqlTable($u); + $sql[] = $type . ' ( ' . $this->connection->parseSqlTable($u) . ' )'; } } - return implode(' ', $sql); + + return ' ' . implode(' ', $sql); } /** * index分析,可在操作链中指定需要强制使用的索引 * @access protected - * @param mixed $index + * @param Query $query 查询对象 + * @param mixed $index * @return string */ - protected function parseForce($index) + protected function parseForce(Query $query, $index) { if (empty($index)) { return ''; } - if (is_array($index)) { - $index = join(",", $index); - } - - return sprintf(" FORCE INDEX ( %s ) ", $index); + return sprintf(" FORCE INDEX ( %s ) ", is_array($index) ? implode(',', $index) : $index); } /** * 设置锁机制 * @access protected - * @param bool $locl + * @param Query $query 查询对象 + * @param bool|string $lock * @return string */ - protected function parseLock($lock = false) + protected function parseLock(Query $query, $lock = false) { - return $lock ? ' FOR UPDATE ' : ''; + if (is_bool($lock)) { + return $lock ? ' FOR UPDATE ' : ''; + } elseif (is_string($lock) && !empty($lock)) { + return ' ' . trim($lock) . ' '; + } } /** * 生成查询SQL * @access public - * @param array $options 表达式 + * @param Query $query 查询对象 * @return string */ - public function select($options = []) + public function select(Query $query) { - $sql = str_replace( + $options = $query->getOptions(); + + return str_replace( ['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'], [ - $this->parseTable($options['table'], $options), - $this->parseDistinct($options['distinct']), - $this->parseField($options['field'], $options), - $this->parseJoin($options['join'], $options), - $this->parseWhere($options['where'], $options), - $this->parseGroup($options['group']), - $this->parseHaving($options['having']), - $this->parseOrder($options['order'], $options), - $this->parseLimit($options['limit']), - $this->parseUnion($options['union']), - $this->parseLock($options['lock']), - $this->parseComment($options['comment']), - $this->parseForce($options['force']), - ], $this->selectSql); - return $sql; + $this->parseTable($query, $options['table']), + $this->parseDistinct($query, $options['distinct']), + $this->parseField($query, $options['field']), + $this->parseJoin($query, $options['join']), + $this->parseWhere($query, $options['where']), + $this->parseGroup($query, $options['group']), + $this->parseHaving($query, $options['having']), + $this->parseOrder($query, $options['order']), + $this->parseLimit($query, $options['limit']), + $this->parseUnion($query, $options['union']), + $this->parseLock($query, $options['lock']), + $this->parseComment($query, $options['comment']), + $this->parseForce($query, $options['force']), + ], + $this->selectSql); } /** - * 生成insert SQL + * 生成Insert SQL * @access public - * @param array $data 数据 - * @param array $options 表达式 - * @param bool $replace 是否replace + * @param Query $query 查询对象 + * @param bool $replace 是否replace * @return string */ - public function insert(array $data, $options = [], $replace = false) + public function insert(Query $query, $replace = false) { + $options = $query->getOptions(); + // 分析并处理数据 - $data = $this->parseData($data, $options); + $data = $this->parseData($query, $options['data']); if (empty($data)) { - return 0; + return ''; } + $fields = array_keys($data); $values = array_values($data); - $sql = str_replace( + return str_replace( ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], [ $replace ? 'REPLACE' : 'INSERT', - $this->parseTable($options['table'], $options), + $this->parseTable($query, $options['table']), implode(' , ', $fields), implode(' , ', $values), - $this->parseComment($options['comment']), - ], $this->insertSql); - - return $sql; + $this->parseComment($query, $options['comment']), + ], + $this->insertSql); } /** * 生成insertall SQL * @access public - * @param array $dataSet 数据集 - * @param array $options 表达式 + * @param Query $query 查询对象 + * @param array $dataSet 数据集 + * @param bool $replace 是否replace * @return string */ - public function insertAll($dataSet, $options) + public function insertAll(Query $query, $dataSet, $replace = false) { + $options = $query->getOptions(); + // 获取合法的字段 if ('*' == $options['field']) { - $fields = array_keys($this->query->getFieldsType($options)); + $allowFields = $this->connection->getTableFields($options['table']); } else { - $fields = $options['field']; + $allowFields = $options['field']; } - foreach ($dataSet as &$data) { - foreach ($data as $key => $val) { - if (!in_array($key, $fields, true)) { - if ($options['strict']) { - throw new Exception('fields not exists:[' . $key . ']'); - } - unset($data[$key]); - } elseif (is_null($val)) { - $data[$key] = 'NULL'; - } elseif (is_scalar($val)) { - $data[$key] = $this->parseValue($val, $key); - } elseif (is_object($val) && method_exists($val, '__toString')) { - // 对象数据写入 - $data[$key] = $val->__toString(); - } else { - // 过滤掉非标量数据 - unset($data[$key]); - } + // 获取绑定信息 + $bind = $this->connection->getFieldsBind($options['table']); + + foreach ($dataSet as $data) { + $data = $this->parseData($query, $data, $allowFields, $bind); + + $values[] = 'SELECT ' . implode(',', array_values($data)); + + if (!isset($insertFields)) { + $insertFields = array_keys($data); } - $value = array_values($data); - $values[] = 'SELECT ' . implode(',', $value); } - $fields = array_map([$this, 'parseKey'], array_keys(reset($dataSet))); - $sql = str_replace( - ['%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + + $fields = []; + + foreach ($insertFields as $field) { + $fields[] = $this->parseKey($query, $field); + } + + return str_replace( + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], [ - $this->parseTable($options['table'], $options), + $replace ? 'REPLACE' : 'INSERT', + $this->parseTable($query, $options['table']), implode(' , ', $fields), implode(' UNION ALL ', $values), - $this->parseComment($options['comment']), - ], $this->insertAllSql); - - return $sql; + $this->parseComment($query, $options['comment']), + ], + $this->insertAllSql); } /** - * 生成slectinsert SQL + * 生成slect insert SQL * @access public - * @param array $fields 数据 - * @param string $table 数据表 - * @param array $options 表达式 + * @param Query $query 查询对象 + * @param array $fields 数据 + * @param string $table 数据表 * @return string */ - public function selectInsert($fields, $table, $options) + public function selectInsert(Query $query, $fields, $table) { if (is_string($fields)) { $fields = explode(',', $fields); } - $fields = array_map([$this, 'parseKey'], $fields); - $sql = 'INSERT INTO ' . $this->parseTable($table, $options) . ' (' . implode(',', $fields) . ') ' . $this->select($options); - return $sql; + foreach ($fields as &$field) { + $field = $this->parseKey($query, $field, true); + } + + return 'INSERT INTO ' . $this->parseTable($query, $table) . ' (' . implode(',', $fields) . ') ' . $this->select($query); } /** * 生成update SQL * @access public - * @param array $fields 数据 - * @param array $options 表达式 + * @param Query $query 查询对象 * @return string */ - public function update($data, $options) + public function update(Query $query) { - $table = $this->parseTable($options['table'], $options); - $data = $this->parseData($data, $options); + $options = $query->getOptions(); + + $data = $this->parseData($query, $options['data']); + if (empty($data)) { return ''; } + foreach ($data as $key => $val) { - $set[] = $key . '=' . $val; + $set[] = $key . ' = ' . $val; } - $sql = str_replace( + return str_replace( ['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], [ - $this->parseTable($options['table'], $options), - implode(',', $set), - $this->parseJoin($options['join'], $options), - $this->parseWhere($options['where'], $options), - $this->parseOrder($options['order'], $options), - $this->parseLimit($options['limit']), - $this->parseLock($options['lock']), - $this->parseComment($options['comment']), - ], $this->updateSql); - - return $sql; + $this->parseTable($query, $options['table']), + implode(' , ', $set), + $this->parseJoin($query, $options['join']), + $this->parseWhere($query, $options['where']), + $this->parseOrder($query, $options['order']), + $this->parseLimit($query, $options['limit']), + $this->parseLock($query, $options['lock']), + $this->parseComment($query, $options['comment']), + ], + $this->updateSql); } /** * 生成delete SQL * @access public - * @param array $options 表达式 + * @param Query $query 查询对象 * @return string */ - public function delete($options) + public function delete(Query $query) { - $sql = str_replace( + $options = $query->getOptions(); + + return str_replace( ['%TABLE%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], [ - $this->parseTable($options['table'], $options), - !empty($options['using']) ? ' USING ' . $this->parseTable($options['using'], $options) . ' ' : '', - $this->parseJoin($options['join'], $options), - $this->parseWhere($options['where'], $options), - $this->parseOrder($options['order'], $options), - $this->parseLimit($options['limit']), - $this->parseLock($options['lock']), - $this->parseComment($options['comment']), - ], $this->deleteSql); - - return $sql; + $this->parseTable($query, $options['table']), + !empty($options['using']) ? ' USING ' . $this->parseTable($query, $options['using']) . ' ' : '', + $this->parseJoin($query, $options['join']), + $this->parseWhere($query, $options['where']), + $this->parseOrder($query, $options['order']), + $this->parseLimit($query, $options['limit']), + $this->parseLock($query, $options['lock']), + $this->parseComment($query, $options['comment']), + ], + $this->deleteSql); } } diff --git a/thinkphp/library/think/db/Connection.php b/thinkphp/library/think/db/Connection.php index fa773e51e..af27fd629 100644 --- a/thinkphp/library/think/db/Connection.php +++ b/thinkphp/library/think/db/Connection.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,25 +11,21 @@ namespace think\db; +use InvalidArgumentException; use PDO; use PDOStatement; +use think\Container; use think\Db; use think\db\exception\BindParamException; use think\Debug; use think\Exception; use think\exception\PDOException; -use think\Log; +use think\Loader; -/** - * Class Connection - * @package think - * @method Query table(string $table) 指定数据表(含前缀) - * @method Query name(string $name) 指定数据表(不含前缀) - * - */ abstract class Connection { - + const PARAM_FLOAT = 21; + protected static $instance = []; /** @var PDOStatement PDO操作实例 */ protected $PDOStatement; @@ -56,7 +52,13 @@ abstract class Connection protected $attrCase = PDO::CASE_LOWER; // 监听回调 protected static $event = []; + + // 数据表信息 + protected static $info = []; + // 使用Builder类 + protected $builderClassName; + // Builder对象 protected $builder; // 数据库连接参数配置 protected $config = [ @@ -90,12 +92,12 @@ abstract class Connection 'master_num' => 1, // 指定从服务器序号 'slave_no' => '', + // 模型写入后自动读取主服务器 + 'read_master' => false, // 是否严格检查字段是否存在 'fields_strict' => true, - // 数据返回类型 - 'result_type' => PDO::FETCH_ASSOC, // 数据集返回类型 - 'resultset_type' => 'array', + 'resultset_type' => '', // 自动写入时间戳字段 'auto_timestamp' => false, // 时间字段取出后的默认时间格式 @@ -108,6 +110,8 @@ abstract class Connection 'query' => '\\think\\db\\Query', // 是否需要断线重连 'break_reconnect' => false, + // 断线标识字符串 + 'break_match_str' => [], ]; // PDO连接参数 @@ -119,30 +123,82 @@ abstract class Connection PDO::ATTR_EMULATE_PREPARES => false, ]; + // 服务器断线标识字符 + protected $breakMatchStr = [ + 'server has gone away', + 'no connection to the server', + 'Lost connection', + 'is dead or not enabled', + 'Error while sending', + 'decryption failed or bad record mac', + 'server closed the connection unexpectedly', + 'SSL connection has been closed unexpectedly', + 'Error writing data to the connection', + 'Resource deadlock avoided', + 'failed with errno', + ]; + // 绑定参数 protected $bind = []; /** - * 构造函数 读取数据库配置信息 + * 架构函数 读取数据库配置信息 * @access public - * @param array $config 数据库配置数组 + * @param array $config 数据库配置数组 */ public function __construct(array $config = []) { if (!empty($config)) { $this->config = array_merge($this->config, $config); } + + // 创建Builder对象 + $class = $this->getBuilderClass(); + + $this->builder = new $class($this); + + // 执行初始化操作 + $this->initialize(); } /** - * 获取新的查询对象 + * 初始化 * @access protected - * @return Query + * @return void */ - protected function getQuery() + protected function initialize() + {} + + /** + * 取得数据库连接类实例 + * @access public + * @param mixed $config 连接配置 + * @param bool|string $name 连接标识 true 强制重新连接 + * @return Connection + * @throws Exception + */ + public static function instance($config = [], $name = false) { - $class = $this->config['query']; - return new $class($this); + if (false === $name) { + $name = md5(serialize($config)); + } + + if (true === $name || !isset(self::$instance[$name])) { + if (empty($config['type'])) { + throw new InvalidArgumentException('Undefined db type'); + } + + // 记录初始化信息 + Container::get('app')->log('[ DB ] INIT ' . $config['type']); + + if (true === $name) { + $name = md5(serialize($config)); + } + + self::$instance[$name] = Loader::factory($config['type'], '\\think\\db\\connector\\', $config); + } + + return self::$instance[$name]; } /** @@ -150,31 +206,42 @@ abstract class Connection * @access public * @return string */ - public function getBuilder() + public function getBuilderClass() { - if (!empty($this->builder)) { - return $this->builder; - } else { - return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type')); + if (!empty($this->builderClassName)) { + return $this->builderClassName; } + + return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type')); } /** - * 调用Query类的查询方法 - * @access public - * @param string $method 方法名称 - * @param array $args 调用参数 - * @return mixed + * 设置当前的数据库Builder对象 + * @access protected + * @param Builder $builder + * @return void */ - public function __call($method, $args) + protected function setBuilder(Builder $builder) { - return call_user_func_array([$this->getQuery(), $method], $args); + $this->builder = $builder; + + return $this; + } + + /** + * 获取当前的builder实例对象 + * @access public + * @return Builder + */ + public function getBuilder() + { + return $this->builder; } /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ abstract protected function parseDsn($config); @@ -182,7 +249,7 @@ abstract class Connection /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ abstract public function getFields($tableName); @@ -198,7 +265,7 @@ abstract class Connection /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ abstract protected function getExplain($sql); @@ -206,7 +273,7 @@ abstract class Connection /** * 对返数据表字段信息进行大小写转换出来 * @access public - * @param array $info 字段信息 + * @param array $info 字段信息 * @return array */ public function fieldCase($info) @@ -223,13 +290,174 @@ abstract class Connection default: // 不做转换 } + return $info; } + /** + * 获取字段绑定类型 + * @access public + * @param string $type 字段类型 + * @return integer + */ + public function getFieldBindType($type) + { + if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) { + $bind = PDO::PARAM_STR; + } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) { + $bind = self::PARAM_FLOAT; + } elseif (preg_match('/(int|serial|bit)/is', $type)) { + $bind = PDO::PARAM_INT; + } elseif (preg_match('/bool/is', $type)) { + $bind = PDO::PARAM_BOOL; + } else { + $bind = PDO::PARAM_STR; + } + + return $bind; + } + + /** + * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写) + * @access public + * @param string $sql sql语句 + * @return string + */ + public function parseSqlTable($sql) + { + if (false !== strpos($sql, '__')) { + $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) { + return $this->getConfig('prefix') . strtolower($match[1]); + }, $sql); + } + + return $sql; + } + + /** + * 获取数据表信息 + * @access public + * @param mixed $tableName 数据表名 留空自动获取 + * @param string $fetch 获取信息类型 包括 fields type bind pk + * @return mixed + */ + public function getTableInfo($tableName, $fetch = '') + { + if (is_array($tableName)) { + $tableName = key($tableName) ?: current($tableName); + } + + if (strpos($tableName, ',')) { + // 多表不获取字段信息 + return false; + } else { + $tableName = $this->parseSqlTable($tableName); + } + + // 修正子查询作为表名的问题 + if (strpos($tableName, ')')) { + return []; + } + + list($tableName) = explode(' ', $tableName); + + if (false === strpos($tableName, '.')) { + $schema = $this->getConfig('database') . '.' . $tableName; + } else { + $schema = $tableName; + } + + if (!isset(self::$info[$schema])) { + // 读取缓存 + $cacheFile = Container::get('app')->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php'; + + if (!$this->config['debug'] && is_file($cacheFile)) { + $info = include $cacheFile; + } else { + $info = $this->getFields($tableName); + } + + $fields = array_keys($info); + $bind = $type = []; + + foreach ($info as $key => $val) { + // 记录字段类型 + $type[$key] = $val['type']; + $bind[$key] = $this->getFieldBindType($val['type']); + + if (!empty($val['primary'])) { + $pk[] = $key; + } + } + + if (isset($pk)) { + // 设置主键 + $pk = count($pk) > 1 ? $pk : $pk[0]; + } else { + $pk = null; + } + + self::$info[$schema] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; + } + + return $fetch ? self::$info[$schema][$fetch] : self::$info[$schema]; + } + + /** + * 获取数据表的主键 + * @access public + * @param string $tableName 数据表名 + * @return string|array + */ + public function getPk($tableName) + { + return $this->getTableInfo($tableName, 'pk'); + } + + /** + * 获取数据表字段信息 + * @access public + * @param string $tableName 数据表名 + * @return array + */ + public function getTableFields($tableName) + { + return $this->getTableInfo($tableName, 'fields'); + } + + /** + * 获取数据表字段类型 + * @access public + * @param string $tableName 数据表名 + * @param string $field 字段名 + * @return array|string + */ + public function getFieldsType($tableName, $field = null) + { + $result = $this->getTableInfo($tableName, 'type'); + + if ($field && isset($result[$field])) { + return $result[$field]; + } + + return $result; + } + + /** + * 获取数据表绑定信息 + * @access public + * @param string $tableName 数据表名 + * @return array + */ + public function getFieldsBind($tableName) + { + return $this->getTableInfo($tableName, 'bind'); + } + /** * 获取数据库的配置参数 * @access public - * @param string $config 配置名称 + * @param string $config 配置名称 * @return mixed */ public function getConfig($config = '') @@ -240,8 +468,8 @@ abstract class Connection /** * 设置数据库的配置参数 * @access public - * @param string|array $config 配置名称 - * @param mixed $value 配置值 + * @param string|array $config 配置名称 + * @param mixed $value 配置值 * @return void */ public function setConfig($config, $value = '') @@ -256,55 +484,63 @@ abstract class Connection /** * 连接数据库方法 * @access public - * @param array $config 连接参数 - * @param integer $linkNum 连接序号 - * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) + * @param array $config 连接参数 + * @param integer $linkNum 连接序号 + * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) * @return PDO * @throws Exception */ public function connect(array $config = [], $linkNum = 0, $autoConnection = false) { - if (!isset($this->links[$linkNum])) { - if (!$config) { - $config = $this->config; - } else { - $config = array_merge($this->config, $config); - } - // 连接参数 - if (isset($config['params']) && is_array($config['params'])) { - $params = $config['params'] + $this->params; - } else { - $params = $this->params; - } - // 记录当前字段属性大小写设置 - $this->attrCase = $params[PDO::ATTR_CASE]; + if (isset($this->links[$linkNum])) { + return $this->links[$linkNum]; + } - // 数据返回类型 - if (isset($config['result_type'])) { - $this->fetchType = $config['result_type']; + if (!$config) { + $config = $this->config; + } else { + $config = array_merge($this->config, $config); + } + + // 连接参数 + if (isset($config['params']) && is_array($config['params'])) { + $params = $config['params'] + $this->params; + } else { + $params = $this->params; + } + + // 记录当前字段属性大小写设置 + $this->attrCase = $params[PDO::ATTR_CASE]; + + if (!empty($config['break_match_str'])) { + $this->breakMatchStr = array_merge($this->breakMatchStr, (array) $config['break_match_str']); + } + + try { + if (empty($config['dsn'])) { + $config['dsn'] = $this->parseDsn($config); } - try { - if (empty($config['dsn'])) { - $config['dsn'] = $this->parseDsn($config); - } - if ($config['debug']) { - $startTime = microtime(true); - } - $this->links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params); - if ($config['debug']) { - // 记录数据库连接信息 - Log::record('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn'], 'sql'); - } - } catch (\PDOException $e) { - if ($autoConnection) { - Log::record($e->getMessage(), 'error'); - return $this->connect($autoConnection, $linkNum); - } else { - throw $e; - } + + if ($config['debug']) { + $startTime = microtime(true); + } + + $this->links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params); + + if ($config['debug']) { + // 记录数据库连接信息 + $this->log('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn']); + } + + return $this->links[$linkNum]; + } catch (\PDOException $e) { + if ($autoConnection) { + $this->log($e->getMessage(), 'error'); + return $this->connect($autoConnection, $linkNum); + } else { + throw $e; } } - return $this->links[$linkNum]; } /** @@ -325,71 +561,141 @@ abstract class Connection { if (!$this->linkID) { return false; + } + + return $this->linkID; + } + + /** + * 执行查询 使用生成器返回数据 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $master 是否在主服务器读操作 + * @param Model $model 模型对象实例 + * @param array $condition 查询条件 + * @param mixed $relation 关联查询 + * @return \Generator + */ + public function getCursor($sql, $bind = [], $master = false, $model = null, $condition = null, $relation = null) + { + $this->initConnect($master); + + // 记录SQL语句 + $this->queryStr = $sql; + + $this->bind = $bind; + + Db::$queryTimes++; + + // 调试开始 + $this->debug(true); + + // 预处理 + $this->PDOStatement = $this->linkID->prepare($sql); + + // 是否为存储过程调用 + $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); + + // 参数绑定 + if ($procedure) { + $this->bindParam($bind); } else { - return $this->linkID; + $this->bindValue($bind); + } + + // 执行查询 + $this->PDOStatement->execute(); + + // 调试结束 + $this->debug(false, '', $master); + + // 返回结果集 + while ($result = $this->PDOStatement->fetch($this->fetchType)) { + if ($model) { + $instance = $model->newInstance($result, $condition); + + if ($relation) { + $instance->relationQuery($relation); + } + + yield $instance; + } else { + yield $result; + } } } /** * 执行查询 返回数据集 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param bool $master 是否在主服务器读操作 - * @param bool $pdo 是否返回PDO对象 - * @return mixed + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $master 是否在主服务器读操作 + * @param bool $pdo 是否返回PDO对象 + * @return array * @throws BindParamException - * @throws PDOException + * @throws \PDOException + * @throws \Exception + * @throws \Throwable */ public function query($sql, $bind = [], $master = false, $pdo = false) { $this->initConnect($master); + if (!$this->linkID) { return false; } // 记录SQL语句 $this->queryStr = $sql; - if ($bind) { - $this->bind = $bind; - } - // 释放前次的查询结果 - if (!empty($this->PDOStatement)) { - $this->free(); - } + $this->bind = $bind; Db::$queryTimes++; + try { // 调试开始 $this->debug(true); + // 预处理 - if (empty($this->PDOStatement)) { - $this->PDOStatement = $this->linkID->prepare($sql); - } + $this->PDOStatement = $this->linkID->prepare($sql); + // 是否为存储过程调用 $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); + // 参数绑定 if ($procedure) { $this->bindParam($bind); } else { $this->bindValue($bind); } + // 执行查询 $this->PDOStatement->execute(); + // 调试结束 - $this->debug(false); + $this->debug(false, '', $master); + // 返回结果集 return $this->getResult($pdo, $procedure); } catch (\PDOException $e) { if ($this->isBreak($e)) { return $this->close()->query($sql, $bind, $master, $pdo); } + throw new PDOException($e, $this->config, $this->getLastsql()); - } catch (\ErrorException $e) { + } catch (\Throwable $e) { if ($this->isBreak($e)) { return $this->close()->query($sql, $bind, $master, $pdo); } + + throw $e; + } catch (\Exception $e) { + if ($this->isBreak($e)) { + return $this->close()->query($sql, $bind, $master, $pdo); + } + throw $e; } } @@ -397,91 +703,780 @@ abstract class Connection /** * 执行语句 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param Query $query 查询对象 * @return int * @throws BindParamException - * @throws PDOException + * @throws \PDOException + * @throws \Exception + * @throws \Throwable */ - public function execute($sql, $bind = []) + public function execute($sql, $bind = [], Query $query = null) { $this->initConnect(true); + if (!$this->linkID) { return false; } // 记录SQL语句 $this->queryStr = $sql; - if ($bind) { - $this->bind = $bind; - } - //释放前次的查询结果 - if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) { - $this->free(); - } + $this->bind = $bind; Db::$executeTimes++; try { // 调试开始 $this->debug(true); + // 预处理 - if (empty($this->PDOStatement)) { - $this->PDOStatement = $this->linkID->prepare($sql); - } + $this->PDOStatement = $this->linkID->prepare($sql); + // 是否为存储过程调用 $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); + // 参数绑定 if ($procedure) { $this->bindParam($bind); } else { $this->bindValue($bind); } + // 执行语句 $this->PDOStatement->execute(); + // 调试结束 - $this->debug(false); + $this->debug(false, '', true); + + if ($query && !empty($this->config['deploy']) && !empty($this->config['read_master'])) { + $query->readMaster(); + } $this->numRows = $this->PDOStatement->rowCount(); + return $this->numRows; } catch (\PDOException $e) { if ($this->isBreak($e)) { - return $this->close()->execute($sql, $bind); + return $this->close()->execute($sql, $bind, $query); } + throw new PDOException($e, $this->config, $this->getLastsql()); - } catch (\ErrorException $e) { + } catch (\Throwable $e) { if ($this->isBreak($e)) { - return $this->close()->execute($sql, $bind); + return $this->close()->execute($sql, $bind, $query); } + + throw $e; + } catch (\Exception $e) { + if ($this->isBreak($e)) { + return $this->close()->execute($sql, $bind, $query); + } + throw $e; } } + /** + * 查找单条记录 + * @access public + * @param Query $query 查询对象 + * @return array|null|\PDOStatement|string + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function find(Query $query) + { + // 分析查询表达式 + $options = $query->getOptions(); + $pk = $query->getPk($options); + + $data = $options['data']; + $query->setOption('limit', 1); + + if (empty($options['fetch_sql']) && !empty($options['cache'])) { + // 判断查询缓存 + $cache = $options['cache']; + + if (is_string($cache['key'])) { + $key = $cache['key']; + } else { + $key = $this->getCacheKey($query, $data); + } + + $result = Container::get('cache')->get($key); + + if (false !== $result) { + return $result; + } + } + + if (is_string($pk) && !is_array($data)) { + if (isset($key) && strpos($key, '|')) { + list($a, $val) = explode('|', $key); + $item[$pk] = $val; + } else { + $item[$pk] = $data; + } + $data = $item; + } + + $query->setOption('data', $data); + + // 生成查询SQL + $sql = $this->builder->select($query); + + $query->removeOption('limit'); + + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + // 获取实际执行的SQL语句 + return $this->getRealSql($sql, $bind); + } + + // 事件回调 + $result = $query->trigger('before_find'); + + if (!$result) { + // 执行查询 + $resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']); + + if ($resultSet instanceof \PDOStatement) { + // 返回PDOStatement对象 + return $resultSet; + } + + $result = isset($resultSet[0]) ? $resultSet[0] : null; + } + + if (isset($cache) && $result) { + // 缓存数据 + $this->cacheData($key, $result, $cache); + } + + return $result; + } + + /** + * 使用游标查询记录 + * @access public + * @param Query $query 查询对象 + * @return \Generator + */ + public function cursor(Query $query) + { + // 分析查询表达式 + $options = $query->getOptions(); + + // 生成查询SQL + $sql = $this->builder->select($query); + + $bind = $query->getBind(); + + $condition = isset($options['where']['AND']) ? $options['where']['AND'] : null; + $relation = isset($options['relaltion']) ? $options['relation'] : null; + + // 执行查询操作 + return $this->getCursor($sql, $bind, $options['master'], $query->getModel(), $condition, $relation); + } + + /** + * 查找记录 + * @access public + * @param Query $query 查询对象 + * @return array|\PDOStatement|string + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function select(Query $query) + { + // 分析查询表达式 + $options = $query->getOptions(); + + if (empty($options['fetch_sql']) && !empty($options['cache'])) { + $resultSet = $this->getCacheData($query, $options['cache'], null, $key); + + if (false !== $resultSet) { + return $resultSet; + } + } + + // 生成查询SQL + $sql = $this->builder->select($query); + + $query->removeOption('limit'); + + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + // 获取实际执行的SQL语句 + return $this->getRealSql($sql, $bind); + } + + $resultSet = $query->trigger('before_select'); + + if (!$resultSet) { + // 执行查询操作 + $resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']); + + if ($resultSet instanceof \PDOStatement) { + // 返回PDOStatement对象 + return $resultSet; + } + } + + if (!empty($options['cache']) && false !== $resultSet) { + // 缓存数据集 + $this->cacheData($key, $resultSet, $options['cache']); + } + + return $resultSet; + } + + /** + * 插入记录 + * @access public + * @param Query $query 查询对象 + * @param boolean $replace 是否replace + * @param boolean $getLastInsID 返回自增主键 + * @param string $sequence 自增序列名 + * @return integer|string + */ + public function insert(Query $query, $replace = false, $getLastInsID = false, $sequence = null) + { + // 分析查询表达式 + $options = $query->getOptions(); + + // 生成SQL语句 + $sql = $this->builder->insert($query, $replace); + + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + // 获取实际执行的SQL语句 + return $this->getRealSql($sql, $bind); + } + + // 执行操作 + $result = '' == $sql ? 0 : $this->execute($sql, $bind, $query); + + if ($result) { + $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); + $lastInsId = $this->getLastInsID($sequence); + + $data = $options['data']; + + if ($lastInsId) { + $pk = $query->getPk($options); + if (is_string($pk)) { + $data[$pk] = $lastInsId; + } + } + + $query->setOption('data', $data); + + $query->trigger('after_insert'); + + if ($getLastInsID) { + return $lastInsId; + } + } + + return $result; + } + + /** + * 批量插入记录 + * @access public + * @param Query $query 查询对象 + * @param mixed $dataSet 数据集 + * @param bool $replace 是否replace + * @param integer $limit 每次写入数据限制 + * @return integer|string + * @throws \Exception + * @throws \Throwable + */ + public function insertAll(Query $query, $dataSet = [], $replace = false, $limit = null) + { + if (!is_array(reset($dataSet))) { + return false; + } + + $options = $query->getOptions(); + + if ($limit) { + // 分批写入 自动启动事务支持 + $this->startTrans(); + + try { + $array = array_chunk($dataSet, $limit, true); + $count = 0; + + foreach ($array as $item) { + $sql = $this->builder->insertAll($query, $item, $replace); + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + $fetchSql[] = $this->getRealSql($sql, $bind); + } else { + $count += $this->execute($sql, $bind, $query); + } + } + + // 提交事务 + $this->commit(); + } catch (\Exception $e) { + $this->rollback(); + throw $e; + } catch (\Throwable $e) { + $this->rollback(); + throw $e; + } + + return isset($fetchSql) ? implode(';', $fetchSql) : $count; + } + + $sql = $this->builder->insertAll($query, $dataSet, $replace); + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + return $this->getRealSql($sql, $bind); + } + + return $this->execute($sql, $bind, $query); + } + + /** + * 通过Select方式插入记录 + * @access public + * @param Query $query 查询对象 + * @param string $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 + * @return integer|string + * @throws PDOException + */ + public function selectInsert(Query $query, $fields, $table) + { + // 分析查询表达式 + $options = $query->getOptions(); + + $table = $this->parseSqlTable($table); + + $sql = $this->builder->selectInsert($query, $fields, $table); + + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + return $this->getRealSql($sql, $bind); + } + + return $this->execute($sql, $bind, $query); + } + + /** + * 更新记录 + * @access public + * @param Query $query 查询对象 + * @return integer|string + * @throws Exception + * @throws PDOException + */ + public function update(Query $query) + { + $options = $query->getOptions(); + + if (isset($options['cache']) && is_string($options['cache']['key'])) { + $key = $options['cache']['key']; + } + + $pk = $query->getPk($options); + $data = $options['data']; + + if (empty($options['where'])) { + // 如果存在主键数据 则自动作为更新条件 + if (is_string($pk) && isset($data[$pk])) { + $where[$pk] = [$pk, '=', $data[$pk]]; + if (!isset($key)) { + $key = $this->getCacheKey($query, $data[$pk]); + } + unset($data[$pk]); + } elseif (is_array($pk)) { + // 增加复合主键支持 + foreach ($pk as $field) { + if (isset($data[$field])) { + $where[$field] = [$field, '=', $data[$field]]; + } else { + // 如果缺少复合主键数据则不执行 + throw new Exception('miss complex primary data'); + } + unset($data[$field]); + } + } + + if (!isset($where)) { + // 如果没有任何更新条件则不执行 + throw new Exception('miss update condition'); + } else { + $options['where']['AND'] = $where; + $query->setOption('where', ['AND' => $where]); + } + } elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'])) { + foreach ($options['where']['AND'] as $val) { + if (is_array($val) && $val[0] == $pk) { + $key = $this->getCacheKey($query, $val); + } + } + } + + // 更新数据 + $query->setOption('data', $data); + + // 生成UPDATE SQL语句 + $sql = $this->builder->update($query); + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + // 获取实际执行的SQL语句 + return $this->getRealSql($sql, $bind); + } + + // 检测缓存 + $cache = Container::get('cache'); + + if (isset($key) && $cache->get($key)) { + // 删除缓存 + $cache->rm($key); + } elseif (!empty($options['cache']['tag'])) { + $cache->clear($options['cache']['tag']); + } + + // 执行操作 + $result = '' == $sql ? 0 : $this->execute($sql, $bind, $query); + + if ($result) { + if (is_string($pk) && isset($where[$pk])) { + $data[$pk] = $where[$pk]; + } elseif (is_string($pk) && isset($key) && strpos($key, '|')) { + list($a, $val) = explode('|', $key); + $data[$pk] = $val; + } + + $query->setOption('data', $data); + $query->trigger('after_update'); + } + + return $result; + } + + /** + * 删除记录 + * @access public + * @param Query $query 查询对象 + * @return int + * @throws Exception + * @throws PDOException + */ + public function delete(Query $query) + { + // 分析查询表达式 + $options = $query->getOptions(); + $pk = $query->getPk($options); + $data = $options['data']; + + if (isset($options['cache']) && is_string($options['cache']['key'])) { + $key = $options['cache']['key']; + } elseif (!is_null($data) && true !== $data && !is_array($data)) { + $key = $this->getCacheKey($query, $data); + } elseif (is_string($pk) && isset($options['where']['AND'])) { + foreach ($options['where']['AND'] as $val) { + if (is_array($val) && $val[0] == $pk) { + $key = $this->getCacheKey($query, $val); + } + } + } + + if (true !== $data && empty($options['where'])) { + // 如果条件为空 不进行删除操作 除非设置 1=1 + throw new Exception('delete without condition'); + } + + // 生成删除SQL语句 + $sql = $this->builder->delete($query); + + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + // 获取实际执行的SQL语句 + return $this->getRealSql($sql, $bind); + } + + // 检测缓存 + $cache = Container::get('cache'); + + if (isset($key) && $cache->get($key)) { + // 删除缓存 + $cache->rm($key); + } elseif (!empty($options['cache']['tag'])) { + $cache->clear($options['cache']['tag']); + } + + // 执行操作 + $result = $this->execute($sql, $bind, $query); + + if ($result) { + if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { + list($a, $val) = explode('|', $key); + $item[$pk] = $val; + $data = $item; + } + + $options['data'] = $data; + + $query->trigger('after_delete'); + } + + return $result; + } + + /** + * 得到某个字段的值 + * @access public + * @param Query $query 查询对象 + * @param string $field 字段名 + * @param bool $default 默认值 + * @return mixed + */ + public function value(Query $query, $field, $default = null) + { + $options = $query->getOptions(); + + if (empty($options['fetch_sql']) && !empty($options['cache'])) { + $cache = $options['cache']; + $result = $this->getCacheData($query, $cache, null, $key); + + if (false !== $result) { + return $result; + } + } + + if (isset($options['field'])) { + $query->removeOption('field'); + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + $query->setOption('field', $field); + $query->setOption('limit', 1); + + // 生成查询SQL + $sql = $this->builder->select($query); + + if (isset($options['field'])) { + $query->setOption('field', $options['field']); + } else { + $query->removeOption('field'); + } + + $query->removeOption('limit'); + + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + // 获取实际执行的SQL语句 + return $this->getRealSql($sql, $bind); + } + + // 执行查询操作 + $pdo = $this->query($sql, $bind, $options['master'], true); + + $result = $pdo->fetchColumn(); + + if (isset($cache) && false !== $result) { + // 缓存数据 + $this->cacheData($key, $result, $cache); + } + + return false !== $result ? $result : $default; + } + + /** + * 得到某个字段的值 + * @access public + * @param Query $query 查询对象 + * @param string $aggregate 聚合方法 + * @param mixed $field 字段名 + * @return mixed + */ + public function aggregate(Query $query, $aggregate, $field) + { + if (is_string($field) && 0 === stripos($field, 'DISTINCT ')) { + list($distinct, $field) = explode(' ', $field); + } + + $field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate); + + return $this->value($query, $field, 0); + } + + /** + * 得到某个列的数组 + * @access public + * @param Query $query 查询对象 + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 + * @return array + */ + public function column(Query $query, $field, $key = '') + { + $options = $query->getOptions(); + + if (empty($options['fetch_sql']) && !empty($options['cache'])) { + // 判断查询缓存 + $cache = $options['cache']; + $result = $this->getCacheData($query, $cache, null, $guid); + + if (false !== $result) { + return $result; + } + } + + if (isset($options['field'])) { + $query->removeOption('field'); + } + + if (is_null($field)) { + $field = ['*']; + } elseif (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + if ($key && ['*'] != $field) { + array_unshift($field, $key); + $field = array_unique($field); + } + + $query->setOption('field', $field); + + // 生成查询SQL + $sql = $this->builder->select($query); + + // 还原field参数 + if (isset($options['field'])) { + $query->setOption('field', $options['field']); + } else { + $query->removeOption('field'); + } + + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + // 获取实际执行的SQL语句 + return $this->getRealSql($sql, $bind); + } + + // 执行查询操作 + $pdo = $this->query($sql, $bind, $options['master'], true); + + if (1 == $pdo->columnCount()) { + $result = $pdo->fetchAll(PDO::FETCH_COLUMN); + } else { + $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); + + if (['*'] == $field && $key) { + $result = array_column($resultSet, null, $key); + } elseif ($resultSet) { + $fields = array_keys($resultSet[0]); + $count = count($fields); + $key1 = array_shift($fields); + $key2 = $fields ? array_shift($fields) : ''; + $key = $key ?: $key1; + + if (strpos($key, '.')) { + list($alias, $key) = explode('.', $key); + } + + if (2 == $count) { + $column = $key2; + } elseif (1 == $count) { + $column = $key1; + } else { + $column = null; + } + + $result = array_column($resultSet, $column, $key); + } else { + $result = []; + } + } + + if (isset($cache) && isset($guid)) { + // 缓存数据 + $this->cacheData($guid, $result, $cache); + } + + return $result; + } + + /** + * 执行查询但只返回PDOStatement对象 + * @access public + * @return \PDOStatement|string + */ + public function pdo(Query $query) + { + // 分析查询表达式 + $options = $query->getOptions(); + + // 生成查询SQL + $sql = $this->builder->select($query); + + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + // 获取实际执行的SQL语句 + return $this->getRealSql($sql, $bind); + } + + // 执行查询操作 + return $this->query($sql, $bind, $options['master'], true); + } + /** * 根据参数绑定组装最终的SQL语句 便于调试 * @access public - * @param string $sql 带参数绑定的sql语句 - * @param array $bind 参数绑定列表 + * @param string $sql 带参数绑定的sql语句 + * @param array $bind 参数绑定列表 * @return string */ public function getRealSql($sql, array $bind = []) { + if (is_array($sql)) { + $sql = implode(';', $sql); + } + foreach ($bind as $key => $val) { $value = is_array($val) ? $val[0] : $val; $type = is_array($val) ? $val[1] : PDO::PARAM_STR; - if (PDO::PARAM_STR == $type) { - $value = $this->quote($value); - } elseif (PDO::PARAM_INT == $type) { + + if (self::PARAM_FLOAT == $type) { $value = (float) $value; + } elseif (PDO::PARAM_STR == $type) { + $value = '\'' . addslashes($value) . '\''; + } elseif (PDO::PARAM_INT == $type && '' === $value) { + $value = 0; } + // 判断占位符 $sql = is_numeric($key) ? substr_replace($sql, $value, strpos($sql, '?'), 1) : - str_replace( - [':' . $key . ')', ':' . $key . ',', ':' . $key . ' '], - [$value . ')', $value . ',', $value . ' '], - $sql . ' '); + substr_replace($sql, $value, strpos($sql, ':' . $key), strlen(':' . $key)); } + return rtrim($sql); } @@ -490,7 +1485,7 @@ abstract class Connection * 支持 ['name'=>'value','id'=>123] 对应命名占位符 * 或者 ['value',123] 对应问号占位符 * @access public - * @param array $bind 要绑定的参数列表 + * @param array $bind 要绑定的参数列表 * @return void * @throws BindParamException */ @@ -498,15 +1493,21 @@ abstract class Connection { foreach ($bind as $key => $val) { // 占位符 - $param = is_numeric($key) ? $key + 1 : ':' . $key; + $param = is_int($key) ? $key + 1 : ':' . $key; + if (is_array($val)) { if (PDO::PARAM_INT == $val[1] && '' === $val[0]) { $val[0] = 0; + } elseif (self::PARAM_FLOAT == $val[1]) { + $val[0] = (float) $val[0]; + $val[1] = PDO::PARAM_STR; } + $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]); } else { $result = $this->PDOStatement->bindValue($param, $val); } + if (!$result) { throw new BindParamException( "Error occurred when binding parameters '{$param}'", @@ -521,22 +1522,25 @@ abstract class Connection /** * 存储过程的输入输出参数绑定 * @access public - * @param array $bind 要绑定的参数列表 + * @param array $bind 要绑定的参数列表 * @return void * @throws BindParamException */ protected function bindParam($bind) { foreach ($bind as $key => $val) { - $param = is_numeric($key) ? $key + 1 : ':' . $key; + $param = is_int($key) ? $key + 1 : ':' . $key; + if (is_array($val)) { array_unshift($val, $param); $result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val); } else { $result = $this->PDOStatement->bindValue($param, $val); } + if (!$result) { $param = array_shift($val); + throw new BindParamException( "Error occurred when binding parameters '{$param}'", $this->config, @@ -550,8 +1554,8 @@ abstract class Connection /** * 获得数据集数组 * @access protected - * @param bool $pdo 是否返回PDOStatement - * @param bool $procedure 是否存储过程 + * @param bool $pdo 是否返回PDOStatement + * @param bool $procedure 是否存储过程 * @return array */ protected function getResult($pdo = false, $procedure = false) @@ -560,12 +1564,16 @@ abstract class Connection // 返回PDOStatement对象处理 return $this->PDOStatement; } + if ($procedure) { // 存储过程返回结果 return $this->procedure(); } - $result = $this->PDOStatement->fetchAll($this->fetchType); + + $result = $this->PDOStatement->fetchAll($this->fetchType); + $this->numRows = count($result); + return $result; } @@ -577,20 +1585,23 @@ abstract class Connection protected function procedure() { $item = []; + do { $result = $this->getResult(); if ($result) { $item[] = $result; } } while ($this->PDOStatement->nextRowset()); + $this->numRows = count($item); + return $item; } /** * 执行数据库事务 * @access public - * @param callable $callback 数据操作方法回调 + * @param callable $callback 数据操作方法回调 * @return mixed * @throws PDOException * @throws \Exception @@ -599,11 +1610,13 @@ abstract class Connection public function transaction($callback) { $this->startTrans(); + try { $result = null; if (is_callable($callback)) { $result = call_user_func_array($callback, [$this]); } + $this->commit(); return $result; } catch (\Exception $e) { @@ -615,10 +1628,48 @@ abstract class Connection } } + /** + * 启动XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function startTransXa($xid) + {} + + /** + * 预编译XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function prepareXa($xid) + {} + + /** + * 提交XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function commitXa($xid) + {} + + /** + * 回滚XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function rollbackXa($xid) + {} + /** * 启动事务 * @access public * @return void + * @throws \PDOException + * @throws \Exception */ public function startTrans() { @@ -628,6 +1679,7 @@ abstract class Connection } ++$this->transTimes; + try { if (1 == $this->transTimes) { $this->linkID->beginTransaction(); @@ -636,14 +1688,9 @@ abstract class Connection $this->parseSavepoint('trans' . $this->transTimes) ); } - - } catch (\PDOException $e) { - if ($this->isBreak($e)) { - return $this->close()->startTrans(); - } - throw $e; - } catch (\ErrorException $e) { + } catch (\Exception $e) { if ($this->isBreak($e)) { + --$this->transTimes; return $this->close()->startTrans(); } throw $e; @@ -699,7 +1746,8 @@ abstract class Connection /** * 生成定义保存点的SQL - * @param $name + * @access protected + * @param $name * @return string */ protected function parseSavepoint($name) @@ -709,7 +1757,8 @@ abstract class Connection /** * 生成回滚到保存点的SQL - * @param $name + * @access protected + * @param $name * @return string */ protected function parseSavepointRollBack($name) @@ -721,19 +1770,22 @@ abstract class Connection * 批处理执行SQL语句 * 批处理的指令都认为是execute操作 * @access public - * @param array $sqlArray SQL批处理指令 + * @param array $sqlArray SQL批处理指令 + * @param array $bind 参数绑定 * @return boolean */ - public function batchQuery($sqlArray = []) + public function batchQuery($sqlArray = [], $bind = []) { if (!is_array($sqlArray)) { return false; } + // 自动启动事务支持 $this->startTrans(); + try { foreach ($sqlArray as $sql) { - $this->execute($sql); + $this->execute($sql, $bind); } // 提交事务 $this->commit(); @@ -741,13 +1793,14 @@ abstract class Connection $this->rollback(); throw $e; } + return true; } /** * 获得查询次数 * @access public - * @param boolean $execute 是否包含所有查询 + * @param boolean $execute 是否包含所有查询 * @return integer */ public function getQueryTimes($execute = false) @@ -776,13 +1829,17 @@ abstract class Connection $this->linkWrite = null; $this->linkRead = null; $this->links = []; + + // 释放查询 + $this->free(); + return $this; } /** * 是否断线 * @access protected - * @param \PDOException $e 异常对象 + * @param \PDOException|\Exception $e 异常对象 * @return bool */ protected function isBreak($e) @@ -791,22 +1848,9 @@ abstract class Connection return false; } - $info = [ - 'server has gone away', - 'no connection to the server', - 'Lost connection', - 'is dead or not enabled', - 'Error while sending', - 'decryption failed or bad record mac', - 'server closed the connection unexpectedly', - 'SSL connection has been closed unexpectedly', - 'Error writing data to the connection', - 'Resource deadlock avoided', - ]; - $error = $e->getMessage(); - foreach ($info as $msg) { + foreach ($this->breakMatchStr as $msg) { if (false !== stripos($error, $msg)) { return true; } @@ -827,7 +1871,7 @@ abstract class Connection /** * 获取最近插入的ID * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return string */ public function getLastInsID($sequence = null) @@ -858,50 +1902,44 @@ abstract class Connection } else { $error = ''; } + if ('' != $this->queryStr) { $error .= "\n [ SQL语句 ] : " . $this->getLastsql(); } - return $error; - } - /** - * SQL指令安全过滤 - * @access public - * @param string $str SQL字符串 - * @param bool $master 是否主库查询 - * @return string - */ - public function quote($str, $master = true) - { - $this->initConnect($master); - return $this->linkID ? $this->linkID->quote($str) : $str; + return $error; } /** * 数据库调试 记录当前SQL及分析性能 * @access protected - * @param boolean $start 调试开始标记 true 开始 false 结束 - * @param string $sql 执行的SQL语句 留空自动获取 + * @param boolean $start 调试开始标记 true 开始 false 结束 + * @param string $sql 执行的SQL语句 留空自动获取 + * @param bool $master 主从标记 * @return void */ - protected function debug($start, $sql = '') + protected function debug($start, $sql = '', $master = false) { if (!empty($this->config['debug'])) { // 开启数据库调试模式 + $debug = Container::get('debug'); + if ($start) { - Debug::remark('queryStartTime', 'time'); + $debug->remark('queryStartTime', 'time'); } else { // 记录操作结束时间 - Debug::remark('queryEndTime', 'time'); - $runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime'); + $debug->remark('queryEndTime', 'time'); + $runtime = $debug->getRangeTime('queryStartTime', 'queryEndTime'); $sql = $sql ?: $this->getLastsql(); $result = []; + // SQL性能分析 if ($this->config['sql_explain'] && 0 === stripos(trim($sql), 'select')) { $result = $this->getExplain($sql); } + // SQL监听 - $this->trigger($sql, $runtime, $result); + $this->triggerSql($sql, $runtime, $result, $master); } } } @@ -909,7 +1947,7 @@ abstract class Connection /** * 监听SQL执行 * @access public - * @param callable $callback 回调方法 + * @param callable $callback 回调方法 * @return void */ public function listen($callback) @@ -920,32 +1958,46 @@ abstract class Connection /** * 触发SQL事件 * @access protected - * @param string $sql SQL语句 - * @param float $runtime SQL运行时间 - * @param mixed $explain SQL分析 - * @return bool + * @param string $sql SQL语句 + * @param float $runtime SQL运行时间 + * @param mixed $explain SQL分析 + * @param bool $master 主从标记 + * @return void */ - protected function trigger($sql, $runtime, $explain = []) + protected function triggerSql($sql, $runtime, $explain = [], $master = false) { if (!empty(self::$event)) { foreach (self::$event as $callback) { if (is_callable($callback)) { - call_user_func_array($callback, [$sql, $runtime, $explain]); + call_user_func_array($callback, [$sql, $runtime, $explain, $master]); } } } else { + if ($this->config['deploy']) { + // 分布式记录当前操作的主从 + $master = $master ? 'master|' : 'slave|'; + } else { + $master = ''; + } + // 未注册监听则记录到日志中 - Log::record('[ SQL ] ' . $sql . ' [ RunTime:' . $runtime . 's ]', 'sql'); + $this->log('[ SQL ] ' . $sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]'); + if (!empty($explain)) { - Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql'); + $this->log('[ EXPLAIN : ' . var_export($explain, true) . ' ]'); } } } + public function log($log, $type = 'sql') + { + $this->config['debug'] && Container::get('log')->record($log, $type); + } + /** * 初始化数据库连接 * @access protected - * @param boolean $master 是否主服务器 + * @param boolean $master 是否主服务器 * @return void */ protected function initConnect($master = true) @@ -956,11 +2008,13 @@ abstract class Connection if (!$this->linkWrite) { $this->linkWrite = $this->multiConnect(true); } + $this->linkID = $this->linkWrite; } else { if (!$this->linkRead) { $this->linkRead = $this->multiConnect(false); } + $this->linkID = $this->linkRead; } } elseif (!$this->linkID) { @@ -972,15 +2026,16 @@ abstract class Connection /** * 连接分布式服务器 * @access protected - * @param boolean $master 主服务器 + * @param boolean $master 主服务器 * @return PDO */ protected function multiConnect($master = false) { $_config = []; + // 分布式数据库配置解析 foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { - $_config[$name] = explode(',', $this->config[$name]); + $_config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name]; } // 主服务器序号 @@ -1003,16 +2058,20 @@ abstract class Connection $r = floor(mt_rand(0, count($_config['hostname']) - 1)); } $dbMaster = false; + if ($m != $r) { $dbMaster = []; foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { $dbMaster[$name] = isset($_config[$name][$m]) ? $_config[$name][$m] : $_config[$name][0]; } } + $dbConfig = []; + foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { $dbConfig[$name] = isset($_config[$name][$r]) ? $_config[$name][$r] : $_config[$name][0]; } + return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster); } @@ -1022,11 +2081,70 @@ abstract class Connection */ public function __destruct() { - // 释放查询 - if ($this->PDOStatement) { - $this->free(); - } // 关闭连接 $this->close(); } + + /** + * 缓存数据 + * @access protected + * @param string $key 缓存标识 + * @param mixed $data 缓存数据 + * @param array $config 缓存参数 + */ + protected function cacheData($key, $data, $config = []) + { + $cache = Container::get('cache'); + + if (isset($config['tag'])) { + $cache->tag($config['tag'])->set($key, $data, $config['expire']); + } else { + $cache->set($key, $data, $config['expire']); + } + } + + /** + * 获取缓存数据 + * @access protected + * @param Query $query 查询对象 + * @param mixed $cache 缓存设置 + * @param array $options 缓存 + * @return mixed + */ + protected function getCacheData(Query $query, $cache, $data, &$key = null) + { + // 判断查询缓存 + $key = is_string($cache['key']) ? $cache['key'] : $this->getCacheKey($query, $data); + + return Container::get('cache')->get($key); + } + + /** + * 生成缓存标识 + * @access protected + * @param Query $query 查询对象 + * @param mixed $value 缓存数据 + * @return string + */ + protected function getCacheKey(Query $query, $value) + { + if (is_scalar($value)) { + $data = $value; + } elseif (is_array($value) && isset($value[1], $value[2]) && in_array($value[1], ['=', 'eq'], true) && is_scalar($value[2])) { + $data = $value[2]; + } + + $prefix = 'think:' . $this->getConfig('database') . '.'; + + if (isset($data)) { + return $prefix . $query->getTable() . '|' . $data; + } + + try { + return md5($prefix . serialize($query->getOptions()) . serialize($query->getBind(false))); + } catch (\Exception $e) { + throw new Exception('closure not support cache(true)'); + } + } + } diff --git a/thinkphp/library/think/db/Expression.php b/thinkphp/library/think/db/Expression.php new file mode 100644 index 000000000..f1b92abd7 --- /dev/null +++ b/thinkphp/library/think/db/Expression.php @@ -0,0 +1,48 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +class Expression +{ + /** + * 查询表达式 + * + * @var string + */ + protected $value; + + /** + * 创建一个查询表达式 + * + * @param string $value + * @return void + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * 获取表达式 + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + public function __toString() + { + return (string) $this->value; + } +} diff --git a/thinkphp/library/think/db/Query.php b/thinkphp/library/think/db/Query.php index d9cc56a53..df23b8f8f 100644 --- a/thinkphp/library/think/db/Query.php +++ b/thinkphp/library/think/db/Query.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,9 +12,8 @@ namespace think\db; use PDO; -use think\Cache; use think\Collection; -use think\Config; +use think\Container; use think\Db; use think\db\exception\BindParamException; use think\db\exception\DataNotFoundException; @@ -24,76 +23,196 @@ use think\exception\DbException; use think\exception\PDOException; use think\Loader; use think\Model; +use think\model\Collection as ModelCollection; use think\model\Relation; use think\model\relation\OneToOne; use think\Paginator; class Query { - // 数据库Connection对象实例 + /** + * 当前数据库连接对象 + * @var Connection + */ protected $connection; - // 数据库Builder对象实例 - protected $builder; - // 当前模型类名称 + + /** + * 当前模型对象 + * @var Model + */ protected $model; - // 当前数据表名称(含前缀) - protected $table = ''; - // 当前数据表名称(不含前缀) + + /** + * 当前数据表名称(不含前缀) + * @var string + */ protected $name = ''; - // 当前数据表主键 + + /** + * 当前数据表主键 + * @var string|array + */ protected $pk; - // 当前数据表前缀 + + /** + * 当前数据表前缀 + * @var string + */ protected $prefix = ''; - // 查询参数 + + /** + * 当前查询参数 + * @var array + */ protected $options = []; - // 参数绑定 + + /** + * 当前参数绑定 + * @var array + */ protected $bind = []; - // 数据表信息 - protected static $info = []; - // 回调事件 + + /** + * 事件回调 + * @var array + */ private static $event = []; /** - * 构造函数 - * @access public - * @param Connection $connection 数据库对象实例 - * @param string $model 模型名 + * 扩展查询方法 + * @var array */ - public function __construct(Connection $connection = null, $model = '') + private static $extend = []; + + /** + * 读取主库的表 + * @var array + */ + protected static $readMaster = []; + + /** + * 日期查询表达式 + * @var array + */ + protected $timeRule = [ + 'today' => ['today', 'tomorrow'], + 'yesterday' => ['yesterday', 'today'], + 'week' => ['this week 00:00:00', 'next week 00:00:00'], + 'last week' => ['last week 00:00:00', 'this week 00:00:00'], + 'month' => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'], + 'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'], + 'year' => ['this year 1/1', 'next year 1/1'], + 'last year' => ['last year 1/1', 'this year 1/1'], + ]; + + /** + * 日期查询快捷定义 + * @var array + */ + protected $timeExp = ['d' => 'today', 'w' => 'week', 'm' => 'month', 'y' => 'year']; + + /** + * 架构函数 + * @access public + */ + public function __construct(Connection $connection = null) { - $this->connection = $connection ?: Db::connect([], true); - $this->prefix = $this->connection->getConfig('prefix'); - $this->model = $model; - // 设置当前连接的Builder对象 - $this->setBuilder(); + if (is_null($connection)) { + $this->connection = Db::connect(); + } else { + $this->connection = $connection; + } + + $this->prefix = $this->connection->getConfig('prefix'); + } + + /** + * 创建一个新的查询对象 + * @access public + * @return Query + */ + public function newQuery() + { + return new static($this->connection); } /** * 利用__call方法实现一些特殊的Model方法 * @access public - * @param string $method 方法名称 - * @param array $args 调用参数 + * @param string $method 方法名称 + * @param array $args 调用参数 * @return mixed * @throws DbException * @throws Exception */ public function __call($method, $args) { - if (strtolower(substr($method, 0, 5)) == 'getby') { + if (isset(self::$extend[strtolower($method)])) { + // 调用扩展查询方法 + array_unshift($args, $this); + + return Container::getInstance() + ->invoke(self::$extend[strtolower($method)], $args); + } elseif (strtolower(substr($method, 0, 5)) == 'getby') { // 根据某个字段获取记录 - $field = Loader::parseName(substr($method, 5)); - $where[$field] = $args[0]; - return $this->where($where)->find(); + $field = Loader::parseName(substr($method, 5)); + return $this->where($field, '=', $args[0])->find(); } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { // 根据某个字段获取记录的某个值 - $name = Loader::parseName(substr($method, 10)); - $where[$name] = $args[0]; - return $this->where($where)->value($args[1]); + $name = Loader::parseName(substr($method, 10)); + return $this->where($name, '=', $args[0])->value($args[1]); + } elseif (strtolower(substr($method, 0, 7)) == 'whereor') { + $name = Loader::parseName(substr($method, 7)); + array_unshift($args, $name); + return call_user_func_array([$this, 'whereOr'], $args); + } elseif (strtolower(substr($method, 0, 5)) == 'where') { + $name = Loader::parseName(substr($method, 5)); + array_unshift($args, $name); + return call_user_func_array([$this, 'where'], $args); + } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { + // 动态调用命名范围 + $method = 'scope' . $method; + array_unshift($args, $this); + + call_user_func_array([$this->model, $method], $args); + return $this; } else { - throw new Exception('method not exist:' . __CLASS__ . '->' . $method); + throw new Exception('method not exist:' . ($this->model ? get_class($this->model) : static::class) . '->' . $method); } } + /** + * 扩展查询方法 + * @access public + * @param string|array $method 查询方法名 + * @param callable $callback + * @return void + */ + public static function extend($method, $callback = null) + { + if (is_array($method)) { + foreach ($method as $key => $val) { + self::$extend[strtolower($key)] = $val; + } + } else { + self::$extend[strtolower($method)] = $callback; + } + } + + /** + * 设置当前的数据库Connection对象 + * @access public + * @param Connection $connection + * @return $this + */ + public function setConnection(Connection $connection) + { + $this->connection = $connection; + $this->prefix = $this->connection->getConfig('prefix'); + + return $this; + } + /** * 获取当前的数据库Connection对象 * @access public @@ -105,53 +224,46 @@ class Query } /** - * 切换当前的数据库连接 + * 指定模型 * @access public - * @param mixed $config + * @param Model $model 模型对象实例 * @return $this */ - public function connect($config) + public function model(Model $model) { - $this->connection = Db::connect($config); - $this->setBuilder(); + $this->model = $model; return $this; } /** - * 设置当前的数据库Builder对象 - * @access protected - * @return void - */ - protected function setBuilder() - { - $class = $this->connection->getBuilder(); - $this->builder = new $class($this->connection, $this); - } - - /** - * 获取当前的模型对象名 + * 获取当前的模型对象 * @access public - * @return string + * @return Model|null */ public function getModel() { - return $this->model; + return $this->model ? $this->model->setQuery($this) : null; } /** - * 获取当前的builder实例对象 + * 设置从主库读取数据 * @access public - * @return Builder + * @param bool $all 是否所有表有效 + * @return $this */ - public function getBuilder() + public function readMaster($all = false) { - return $this->builder; + $table = $all ? '*' : $this->getTable(); + + static::$readMaster[$table] = true; + + return $this; } /** - * 指定默认的数据表名(不含前缀) + * 指定当前数据表名(不含前缀) * @access public - * @param string $name + * @param string $name * @return $this */ public function name($name) @@ -161,88 +273,77 @@ class Query } /** - * 指定默认数据表名(含前缀) + * 获取当前的数据表名称 * @access public - * @param string $table 表名 - * @return $this + * @return string */ - public function setTable($table) + public function getName() { - $this->table = $table; - return $this; + return $this->name ?: $this->model->getName(); } /** * 得到当前或者指定名称的数据表 * @access public - * @param string $name + * @param string $name * @return string */ public function getTable($name = '') { - if ($name || empty($this->table)) { - $name = $name ?: $this->name; - $tableName = $this->prefix; - if ($name) { - $tableName .= Loader::parseName($name); - } - } else { - $tableName = $this->table; + if (empty($name) && isset($this->options['table'])) { + return $this->options['table']; } - return $tableName; - } - /** - * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写) - * @access public - * @param string $sql sql语句 - * @return string - */ - public function parseSqlTable($sql) - { - if (false !== strpos($sql, '__')) { - $prefix = $this->prefix; - $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) { - return $prefix . strtolower($match[1]); - }, $sql); - } - return $sql; + $name = $name ?: $this->name; + + return $this->prefix . Loader::parseName($name); } /** * 执行查询 返回数据集 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 - * @param boolean $master 是否在主服务器读操作 - * @param bool|string $class 指定返回的数据集对象 + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param boolean $master 是否在主服务器读操作 + * @param bool $pdo 是否返回PDO对象 * @return mixed * @throws BindParamException * @throws PDOException */ - public function query($sql, $bind = [], $master = false, $class = false) + public function query($sql, $bind = [], $master = false, $pdo = false) { - return $this->connection->query($sql, $bind, $master, $class); + return $this->connection->query($sql, $bind, $master, $pdo); } /** * 执行语句 * @access public - * @param string $sql sql指令 - * @param array $bind 参数绑定 + * @param string $sql sql指令 + * @param array $bind 参数绑定 * @return int * @throws BindParamException * @throws PDOException */ public function execute($sql, $bind = []) { - return $this->connection->execute($sql, $bind); + return $this->connection->execute($sql, $bind, $this); + } + + /** + * 监听SQL执行 + * @access public + * @param callable $callback 回调方法 + * @return void + */ + public function listen($callback) + { + $this->connection->listen($callback); } /** * 获取最近插入的ID * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return string */ public function getLastInsID($sequence = null) @@ -250,6 +351,16 @@ class Query return $this->connection->getLastInsID($sequence); } + /** + * 获取返回或者影响的记录数 + * @access public + * @return integer + */ + public function getNumRows() + { + return $this->connection->getNumRows(); + } + /** * 获取最近一次查询的sql语句 * @access public @@ -260,10 +371,66 @@ class Query return $this->connection->getLastSql(); } + /** + * 执行数据库Xa事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @param array $dbs 多个查询对象或者连接对象 + * @return mixed + * @throws PDOException + * @throws \Exception + * @throws \Throwable + */ + public function transactionXa($callback, array $dbs = []) + { + $xid = uniqid('xa'); + + if (empty($dbs)) { + $dbs[] = $this->getConnection(); + } + + foreach ($dbs as $key => $db) { + if ($db instanceof Query) { + $db = $db->getConnection(); + + $dbs[$key] = $db; + } + + $db->startTransXa($xid); + } + + try { + $result = null; + if (is_callable($callback)) { + $result = call_user_func_array($callback, [$this]); + } + + foreach ($dbs as $db) { + $db->prepareXa($xid); + } + + foreach ($dbs as $db) { + $db->commitXa($xid); + } + + return $result; + } catch (\Exception $e) { + foreach ($dbs as $db) { + $db->rollbackXa($xid); + } + throw $e; + } catch (\Throwable $e) { + foreach ($dbs as $db) { + $db->rollbackXa($xid); + } + throw $e; + } + } + /** * 执行数据库事务 * @access public - * @param callable $callback 数据操作方法回调 + * @param callable $callback 数据操作方法回调 * @return mixed */ public function transaction($callback) @@ -307,7 +474,7 @@ class Query * 批处理执行SQL语句 * 批处理的指令都认为是execute操作 * @access public - * @param array $sql SQL批处理指令 + * @param array $sql SQL批处理指令 * @return boolean */ public function batchQuery($sql = []) @@ -318,21 +485,52 @@ class Query /** * 获取数据库的配置参数 * @access public - * @param string $name 参数名称 - * @return boolean + * @param string $name 参数名称 + * @return mixed */ public function getConfig($name = '') { return $this->connection->getConfig($name); } + /** + * 获取数据表字段信息 + * @access public + * @param string $tableName 数据表名 + * @return array + */ + public function getTableFields($tableName = '') + { + if ('' == $tableName) { + $tableName = isset($this->options['table']) ? $this->options['table'] : $this->getTable(); + } + + return $this->connection->getTableFields($tableName); + } + + /** + * 获取数据表字段类型 + * @access public + * @param string $tableName 数据表名 + * @param string $field 字段名 + * @return array|string + */ + public function getFieldsType($tableName = '', $field = null) + { + if ('' == $tableName) { + $tableName = isset($this->options['table']) ? $this->options['table'] : $this->getTable(); + } + + return $this->connection->getFieldsType($tableName, $field); + } + /** * 得到分表的的数据表名 * @access public - * @param array $data 操作的数据 - * @param string $field 分表依据的字段 - * @param array $rule 分表规则 - * @return string + * @param array $data 操作的数据 + * @param string $field 分表依据的字段 + * @param array $rule 分表规则 + * @return array */ public function getPartitionTableName($data, $field, $rule = []) { @@ -371,201 +569,150 @@ class Query } } return $this->getTable() . '_' . $seq; - } else { - // 当设置的分表字段不在查询条件或者数据中 - // 进行联合查询,必须设定 partition['num'] - $tableName = []; - for ($i = 0; $i < $rule['num']; $i++) { - $tableName[] = 'SELECT * FROM ' . $this->getTable() . '_' . ($i + 1); - } - - $tableName = '( ' . implode(" UNION ", $tableName) . ') AS ' . $this->name; - return $tableName; } + // 当设置的分表字段不在查询条件或者数据中 + // 进行联合查询,必须设定 partition['num'] + $tableName = []; + for ($i = 0; $i < $rule['num']; $i++) { + $tableName[] = 'SELECT * FROM ' . $this->getTable() . '_' . ($i + 1); + } + + return ['( ' . implode(" UNION ", $tableName) . ' )' => $this->name]; } /** * 得到某个字段的值 * @access public - * @param string $field 字段名 - * @param mixed $default 默认值 - * @param bool $force 强制转为数字类型 + * @param string $field 字段名 + * @param mixed $default 默认值 * @return mixed */ - public function value($field, $default = null, $force = false) + public function value($field, $default = null) { - $result = false; - if (empty($options['fetch_sql']) && !empty($this->options['cache'])) { - // 判断查询缓存 - $cache = $this->options['cache']; - if (empty($this->options['table'])) { - $this->options['table'] = $this->getTable(); - } - $key = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind)); - $result = Cache::get($key); - } - if (false === $result) { - if (isset($this->options['field'])) { - unset($this->options['field']); - } - $pdo = $this->field($field)->limit(1)->getPdo(); - if (is_string($pdo)) { - // 返回SQL语句 - return $pdo; - } - $result = $pdo->fetchColumn(); - if ($force) { - $result = is_numeric($result) ? $result + 0 : $result; - } - if (isset($cache)) { - // 缓存数据 - $this->cacheData($key, $result, $cache); - } - } else { - // 清空查询条件 - $this->options = []; - } - return false !== $result ? $result : $default; + $this->parseOptions(); + + return $this->connection->value($this, $field, $default); } /** * 得到某个列的数组 * @access public - * @param string $field 字段名 多个字段用逗号分隔 - * @param string $key 索引 + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 * @return array */ public function column($field, $key = '') { - $result = false; - if (empty($options['fetch_sql']) && !empty($this->options['cache'])) { - // 判断查询缓存 - $cache = $this->options['cache']; - if (empty($this->options['table'])) { - $this->options['table'] = $this->getTable(); - } - $guid = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options) . serialize($this->bind)); - $result = Cache::get($guid); - } - if (false === $result) { - if (isset($this->options['field'])) { - unset($this->options['field']); - } - if (is_null($field)) { - $field = '*'; - } elseif ($key && '*' != $field) { - $field = $key . ',' . $field; - } - $pdo = $this->field($field)->getPdo(); - if (is_string($pdo)) { - // 返回SQL语句 - return $pdo; - } - if (1 == $pdo->columnCount()) { - $result = $pdo->fetchAll(PDO::FETCH_COLUMN); - } else { - $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); - if ($resultSet) { - $fields = array_keys($resultSet[0]); - $count = count($fields); - $key1 = array_shift($fields); - $key2 = $fields ? array_shift($fields) : ''; - $key = $key ?: $key1; - if (strpos($key, '.')) { - list($alias, $key) = explode('.', $key); - } - foreach ($resultSet as $val) { - if ($count > 2) { - $result[$val[$key]] = $val; - } elseif (2 == $count) { - $result[$val[$key]] = $val[$key2]; - } elseif (1 == $count) { - $result[$val[$key]] = $val[$key1]; - } - } - } else { - $result = []; - } - } - if (isset($cache) && isset($guid)) { - // 缓存数据 - $this->cacheData($guid, $result, $cache); - } - } else { - // 清空查询条件 - $this->options = []; + $this->parseOptions(); + + return $this->connection->column($this, $field, $key); + } + + /** + * 聚合查询 + * @access public + * @param string $aggregate 聚合方法 + * @param string|Expression $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + public function aggregate($aggregate, $field, $force = false) + { + $this->parseOptions(); + + $result = $this->connection->aggregate($this, $aggregate, $field); + + if (!empty($this->options['fetch_sql'])) { + return $result; + } elseif ($force) { + $result = (float) $result; } + return $result; } /** * COUNT查询 * @access public - * @param string $field 字段名 - * @return integer|string + * @param string|Expression $field 字段名 + * @return float|string */ public function count($field = '*') { - if (isset($this->options['group'])) { + if (!empty($this->options['group'])) { // 支持GROUP $options = $this->getOptions(); - $subSql = $this->options($options)->field('count(' . $field . ')')->bind($this->bind)->buildSql(); - return $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true); + $subSql = $this->options($options) + ->field('count(' . $field . ') AS think_count') + ->bind($this->bind) + ->buildSql(); + + $query = $this->newQuery()->table([$subSql => '_group_count_']); + + if (!empty($options['fetch_sql'])) { + $query->fetchSql(true); + } + + $count = $query->aggregate('COUNT', '*', true); + } else { + $count = $this->aggregate('COUNT', $field, true); } - return $this->value('COUNT(' . $field . ') AS tp_count', 0, true); + return is_string($count) ? $count : (int) $count; } /** * SUM查询 * @access public - * @param string $field 字段名 - * @return float|int + * @param string|Expression $field 字段名 + * @return float */ public function sum($field) { - return $this->value('SUM(' . $field . ') AS tp_sum', 0, true); + return $this->aggregate('SUM', $field, true); } /** * MIN查询 * @access public - * @param string $field 字段名 + * @param string|Expression $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ - public function min($field) + public function min($field, $force = true) { - return $this->value('MIN(' . $field . ') AS tp_min', 0, true); + return $this->aggregate('MIN', $field, $force); } /** * MAX查询 * @access public - * @param string $field 字段名 + * @param string|Expression $field 字段名 + * @param bool $force 强制转为数字类型 * @return mixed */ - public function max($field) + public function max($field, $force = true) { - return $this->value('MAX(' . $field . ') AS tp_max', 0, true); + return $this->aggregate('MAX', $field, $force); } /** * AVG查询 * @access public - * @param string $field 字段名 - * @return float|int + * @param string|Expression $field 字段名 + * @return float */ public function avg($field) { - return $this->value('AVG(' . $field . ') AS tp_avg', 0, true); + return $this->aggregate('AVG', $field, true); } /** * 设置记录的某个字段值 * 支持使用数据库字段和方法 * @access public - * @param string|array $field 字段名 - * @param mixed $value 字段值 + * @param string|array $field 字段名 + * @param mixed $value 字段值 * @return integer */ public function setField($field, $value = '') @@ -575,105 +722,122 @@ class Query } else { $data[$field] = $value; } + return $this->update($data); } /** * 字段值(延迟)增长 * @access public - * @param string $field 字段名 - * @param integer $step 增长值 - * @param integer $lazyTime 延时时间(s) + * @param string $field 字段名 + * @param integer $step 增长值 + * @param integer $lazyTime 延时时间(s) * @return integer|true * @throws Exception */ public function setInc($field, $step = 1, $lazyTime = 0) { $condition = !empty($this->options['where']) ? $this->options['where'] : []; + if (empty($condition)) { // 没有条件不做任何更新 throw new Exception('no data to update'); } + if ($lazyTime > 0) { // 延迟写入 - $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition) . serialize($this->bind)); + $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); $step = $this->lazyWrite('inc', $guid, $step, $lazyTime); + if (false === $step) { // 清空查询条件 $this->options = []; return true; } } - return $this->setField($field, ['exp', $field . '+' . $step]); + + return $this->setField($field, ['INC', $step]); } /** * 字段值(延迟)减少 * @access public - * @param string $field 字段名 - * @param integer $step 减少值 - * @param integer $lazyTime 延时时间(s) + * @param string $field 字段名 + * @param integer $step 减少值 + * @param integer $lazyTime 延时时间(s) * @return integer|true * @throws Exception */ public function setDec($field, $step = 1, $lazyTime = 0) { $condition = !empty($this->options['where']) ? $this->options['where'] : []; + if (empty($condition)) { // 没有条件不做任何更新 throw new Exception('no data to update'); } + if ($lazyTime > 0) { // 延迟写入 - $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition) . serialize($this->bind)); + $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); $step = $this->lazyWrite('dec', $guid, $step, $lazyTime); + if (false === $step) { // 清空查询条件 $this->options = []; return true; } + + $value = ['INC', $step]; + } else { + $value = ['DEC', $step]; } - return $this->setField($field, ['exp', $field . '-' . $step]); + + return $this->setField($field, $value); } /** * 延时更新检查 返回false表示需要延时 * 否则返回实际写入的数值 * @access protected - * @param string $type 自增或者自减 - * @param string $guid 写入标识 - * @param integer $step 写入步进值 - * @param integer $lazyTime 延时时间(s) + * @param string $type 自增或者自减 + * @param string $guid 写入标识 + * @param integer $step 写入步进值 + * @param integer $lazyTime 延时时间(s) * @return false|integer */ protected function lazyWrite($type, $guid, $step, $lazyTime) { - if (!Cache::has($guid . '_time')) { + $cache = Container::get('cache'); + + if (!$cache->has($guid . '_time')) { // 计时开始 - Cache::set($guid . '_time', $_SERVER['REQUEST_TIME'], 0); - Cache::$type($guid, $step); - } elseif ($_SERVER['REQUEST_TIME'] > Cache::get($guid . '_time') + $lazyTime) { + $cache->set($guid . '_time', time(), 0); + $cache->$type($guid, $step); + } elseif (time() > $cache->get($guid . '_time') + $lazyTime) { // 删除缓存 - $value = Cache::$type($guid, $step); - Cache::rm($guid); - Cache::rm($guid . '_time'); + $value = $cache->$type($guid, $step); + $cache->rm($guid); + $cache->rm($guid . '_time'); return 0 === $value ? false : $value; } else { // 更新缓存 - Cache::$type($guid, $step); + $cache->$type($guid, $step); } + return false; } /** * 查询SQL组装 join * @access public - * @param mixed $join 关联的表名 - * @param mixed $condition 条件 - * @param string $type JOIN类型 + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param string $type JOIN类型 + * @param array $bind 参数绑定 * @return $this */ - public function join($join, $condition = null, $type = 'INNER') + public function join($join, $condition = null, $type = 'INNER', $bind = []) { if (empty($condition)) { // 如果为组数,则循环调用join @@ -684,26 +848,70 @@ class Query } } else { $table = $this->getJoinTable($join); - + if ($bind) { + $this->bindParams($condition, $bind); + } $this->options['join'][] = [$table, strtoupper($type), $condition]; } + return $this; } /** - * 获取Join表名及别名 支持 - * ['prefix_table或者子查询'=>'alias'] 'prefix_table alias' 'table alias' + * LEFT JOIN * @access public - * @param array|string $join - * @return array|string + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function leftJoin($join, $condition = null, $bind = []) + { + return $this->join($join, $condition, 'LEFT'); + } + + /** + * RIGHT JOIN + * @access public + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function rightJoin($join, $condition = null, $bind = []) + { + return $this->join($join, $condition, 'RIGHT'); + } + + /** + * FULL JOIN + * @access public + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function fullJoin($join, $condition = null, $bind = []) + { + return $this->join($join, $condition, 'FULL'); + } + + /** + * 获取Join表名及别名 支持 + * ['prefix_table或者子查询'=>'alias'] 'table alias' + * @access protected + * @param array|string $join + * @param string $alias + * @return string */ protected function getJoinTable($join, &$alias = null) { - // 传入的表名为数组 if (is_array($join)) { - list($table, $alias) = each($join); + $table = $join; + $alias = array_shift($join); } else { $join = trim($join); + if (false !== strpos($join, '(')) { // 使用子查询 $table = $join; @@ -718,30 +926,33 @@ class Query $alias = $join; } } + if ($prefix && false === strpos($table, '.') && 0 !== strpos($table, $prefix) && 0 !== strpos($table, '__')) { $table = $this->getTable($table); } } - } - if (isset($alias)) { - if (isset($this->options['alias'][$table])) { - $table = $table . '@think' . uniqid(); + + if (isset($alias) && $table != $alias) { + $table = [$table => $alias]; } - $table = [$table => $alias]; - $this->alias($table); } + return $table; } /** * 查询SQL组装 union * @access public - * @param mixed $union - * @param boolean $all + * @param mixed $union + * @param boolean $all * @return $this */ public function union($union, $all = false) { + if (empty($union)) { + return $this; + } + $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION'; if (is_array($union)) { @@ -749,59 +960,98 @@ class Query } else { $this->options['union'][] = $union; } + return $this; } + /** + * 查询SQL组装 union all + * @access public + * @param mixed $union + * @return $this + */ + public function unionAll($union) + { + return $this->union($union, true); + } + /** * 指定查询字段 支持字段排除和指定数据表 * @access public - * @param mixed $field - * @param boolean $except 是否排除 - * @param string $tableName 数据表名 - * @param string $prefix 字段前缀 - * @param string $alias 别名前缀 + * @param mixed $field + * @param boolean $except 是否排除 + * @param string $tableName 数据表名 + * @param string $prefix 字段前缀 + * @param string $alias 别名前缀 * @return $this */ public function field($field, $except = false, $tableName = '', $prefix = '', $alias = '') { if (empty($field)) { return $this; + } elseif ($field instanceof Expression) { + $this->options['field'][] = $field; + return $this; } + if (is_string($field)) { + if (preg_match('/[\<\'\"\(]/', $field)) { + return $this->fieldRaw($field); + } + $field = array_map('trim', explode(',', $field)); } + if (true === $field) { // 获取全部字段 - $fields = $this->getTableInfo($tableName ?: (isset($this->options['table']) ? $this->options['table'] : ''), 'fields'); + $fields = $this->getTableFields($tableName); $field = $fields ?: ['*']; } elseif ($except) { // 字段排除 - $fields = $this->getTableInfo($tableName ?: (isset($this->options['table']) ? $this->options['table'] : ''), 'fields'); + $fields = $this->getTableFields($tableName); $field = $fields ? array_diff($fields, $field) : $field; } + if ($tableName) { // 添加统一的前缀 $prefix = $prefix ?: $tableName; - foreach ($field as $key => $val) { - if (is_numeric($key)) { - $val = $prefix . '.' . $val . ($alias ? ' AS ' . $alias . $val : ''); + foreach ($field as $key => &$val) { + if (is_numeric($key) && $alias) { + $field[$prefix . '.' . $val] = $alias . $val; + unset($field[$key]); + } elseif (is_numeric($key)) { + $val = $prefix . '.' . $val; } - $field[$key] = $val; } } if (isset($this->options['field'])) { - $field = array_merge($this->options['field'], $field); + $field = array_merge((array) $this->options['field'], $field); } + $this->options['field'] = array_unique($field); + + return $this; + } + + /** + * 表达式方式指定查询字段 + * @access public + * @param string $field 字段名 + * @return $this + */ + public function fieldRaw($field) + { + $this->options['field'][] = $this->raw($field); + return $this; } /** * 设置数据 * @access public - * @param mixed $field 字段名或者数据 - * @param mixed $value 字段值 + * @param mixed $field 字段名或者数据 + * @param mixed $value 字段值 * @return $this */ public function data($field, $value = null) @@ -811,69 +1061,86 @@ class Query } else { $this->options['data'][$field] = $value; } + return $this; } /** * 字段值增长 * @access public - * @param string|array $field 字段名 - * @param integer $step 增长值 + * @param string|array $field 字段名 + * @param integer $step 增长值 * @return $this */ - public function inc($field, $step = 1) + public function inc($field, $step = 1, $op = 'INC') { $fields = is_string($field) ? explode(',', $field) : $field; - foreach ($fields as $field) { - $this->data($field, ['exp', $field . '+' . $step]); + + foreach ($fields as $field => $val) { + if (is_numeric($field)) { + $field = $val; + } else { + $step = $val; + } + + $this->data($field, [$op, $step]); } + return $this; } /** * 字段值减少 * @access public - * @param string|array $field 字段名 - * @param integer $step 增长值 + * @param string|array $field 字段名 + * @param integer $step 增长值 * @return $this */ public function dec($field, $step = 1) { - $fields = is_string($field) ? explode(',', $field) : $field; - foreach ($fields as $field) { - $this->data($field, ['exp', $field . '-' . $step]); - } + return $this->inc($field, $step, 'DEC'); + } + + /** + * 使用表达式设置数据 + * @access public + * @param string $field 字段名 + * @param string $value 字段值 + * @return $this + */ + public function exp($field, $value) + { + $this->data($field, $this->raw($value)); return $this; } /** * 使用表达式设置数据 * @access public - * @param string $field 字段名 - * @param string $value 字段值 - * @return $this + * @param mixed $value 表达式 + * @return Expression */ - public function exp($field, $value) + public function raw($value) { - $this->data($field, ['exp', $value]); - return $this; + return new Expression($value); } /** * 指定JOIN查询字段 * @access public - * @param string|array $table 数据表 - * @param string|array $field 查询字段 - * @param string|array $on JOIN条件 - * @param string $type JOIN类型 + * @param string|array $table 数据表 + * @param string|array $field 查询字段 + * @param mixed $on JOIN条件 + * @param string $type JOIN类型 * @return $this */ public function view($join, $field = true, $on = null, $type = 'INNER') { $this->options['view'] = true; - if (is_array($join) && key($join) !== 0) { + + if (is_array($join) && key($join) === 0) { foreach ($join as $key => $val) { - $this->view($key, $val[0], isset($val[1]) ? $val[1] : null, isset($val[2]) ? $val[2] : 'INNER'); + $this->view($val[0], $val[1], isset($val[2]) ? $val[2] : null, isset($val[3]) ? $val[3] : 'INNER'); } } else { $fields = []; @@ -885,9 +1152,11 @@ class Query if (is_string($field)) { $field = explode(',', $field); } + foreach ($field as $key => $val) { if (is_numeric($key)) { - $fields[] = $alias . '.' . $val; + $fields[] = $alias . '.' . $val; + $this->options['map'][$val] = $alias . '.' . $val; } else { if (preg_match('/[,=\.\'\"\(\s]/', $key)) { @@ -895,357 +1164,491 @@ class Query } else { $name = $alias . '.' . $key; } - $fields[$name] = $val; + + $fields[] = $name . ' AS ' . $val; + $this->options['map'][$val] = $name; } } } + $this->field($fields); + if ($on) { $this->join($table, $on, $type); } else { $this->table($table); } } + return $this; } /** * 设置分表规则 * @access public - * @param array $data 操作的数据 - * @param string $field 分表依据的字段 - * @param array $rule 分表规则 + * @param array $data 操作的数据 + * @param string $field 分表依据的字段 + * @param array $rule 分表规则 * @return $this */ public function partition($data, $field, $rule = []) { $this->options['table'] = $this->getPartitionTableName($data, $field, $rule); + return $this; } /** * 指定AND查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 * @return $this */ public function where($field, $op = null, $condition = null) { $param = func_get_args(); array_shift($param); - $this->parseWhereExp('AND', $field, $op, $condition, $param); - return $this; + return $this->parseWhereExp('AND', $field, $op, $condition, $param); } /** * 指定OR查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 * @return $this */ public function whereOr($field, $op = null, $condition = null) { $param = func_get_args(); array_shift($param); - $this->parseWhereExp('OR', $field, $op, $condition, $param); - return $this; + return $this->parseWhereExp('OR', $field, $op, $condition, $param); } /** * 指定XOR查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 * @return $this */ public function whereXor($field, $op = null, $condition = null) { $param = func_get_args(); array_shift($param); - $this->parseWhereExp('XOR', $field, $op, $condition, $param); - return $this; + return $this->parseWhereExp('XOR', $field, $op, $condition, $param); } /** * 指定Null查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNull($field, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'null', null); - return $this; + return $this->parseWhereExp($logic, $field, 'NULL', null, [], true); } /** * 指定NotNull查询条件 * @access public - * @param mixed $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotNull($field, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'notnull', null); - return $this; + return $this->parseWhereExp($logic, $field, 'NOTNULL', null, [], true); } /** * 指定Exists查询条件 * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereExists($condition, $logic = 'AND') { - $this->options['where'][strtoupper($logic)][] = ['exists', $condition]; + if (is_string($condition)) { + $condition = $this->raw($condition); + } + + $this->options['where'][strtoupper($logic)][] = ['', 'EXISTS', $condition]; return $this; } /** * 指定NotExists查询条件 * @access public - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotExists($condition, $logic = 'AND') { - $this->options['where'][strtoupper($logic)][] = ['not exists', $condition]; + if (is_string($condition)) { + $condition = $this->raw($condition); + } + + $this->options['where'][strtoupper($logic)][] = ['', 'NOT EXISTS', $condition]; return $this; } /** * 指定In查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereIn($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'in', $condition); - return $this; + return $this->parseWhereExp($logic, $field, 'IN', $condition, [], true); } /** * 指定NotIn查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotIn($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'not in', $condition); - return $this; + return $this->parseWhereExp($logic, $field, 'NOT IN', $condition, [], true); } /** * 指定Like查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereLike($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'like', $condition); - return $this; + return $this->parseWhereExp($logic, $field, 'LIKE', $condition, [], true); } /** * 指定NotLike查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotLike($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'not like', $condition); - return $this; + return $this->parseWhereExp($logic, $field, 'NOT LIKE', $condition, [], true); } /** * 指定Between查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereBetween($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'between', $condition); - return $this; + return $this->parseWhereExp($logic, $field, 'BETWEEN', $condition, [], true); } /** * 指定NotBetween查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function whereNotBetween($field, $condition, $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'not between', $condition); + return $this->parseWhereExp($logic, $field, 'NOT BETWEEN', $condition, [], true); + } + + /** + * 比较两个字段 + * @access public + * @param string|array $field1 查询字段 + * @param string $operator 比较操作符 + * @param string $field2 比较字段 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereColumn($field1, $operator = null, $field2 = null, $logic = 'AND') + { + if (is_array($field1)) { + foreach ($field1 as $item) { + $this->whereColumn($item[0], $item[1], isset($item[2]) ? $item[2] : null); + } + return $this; + } + + if (is_null($field2)) { + $field2 = $operator; + $operator = '='; + } + + return $this->parseWhereExp($logic, $field1, 'COLUMN', [$operator, $field2], [], true); + } + + /** + * 设置软删除字段及条件 + * @access public + * @param false|string $field 查询字段 + * @param mixed $condition 查询条件 + * @return $this + */ + public function useSoftDelete($field, $condition = null) + { + if ($field) { + $this->options['soft_delete'] = [$field, $condition]; + } + return $this; } /** * 指定Exp查询条件 * @access public - * @param mixed $field 查询字段 - * @param mixed $condition 查询条件 - * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function whereExp($field, $condition, $logic = 'AND') + public function whereExp($field, $where, $bind = [], $logic = 'AND') { - $this->parseWhereExp($logic, $field, 'exp', $condition); + if ($bind) { + $this->bindParams($where, $bind); + } + + $this->options['where'][$logic][] = [$field, 'EXP', $this->raw($where)]; + return $this; } /** - * 设置软删除字段及条件 + * 指定表达式查询条件 * @access public - * @param false|string $field 查询字段 - * @param mixed $condition 查询条件 + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor * @return $this */ - public function useSoftDelete($field, $condition = null) + public function whereRaw($where, $bind = [], $logic = 'AND') { - if ($field) { - $this->options['soft_delete'] = [$field, $condition ?: ['null', '']]; + if ($bind) { + $this->bindParams($where, $bind); } + + $this->options['where'][$logic][] = $this->raw($where); + + return $this; + } + + /** + * 参数绑定 + * @access public + * @param string $sql 绑定的sql表达式 + * @param array $bind 参数绑定 + * @return void + */ + protected function bindParams(&$sql, array $bind = []) + { + foreach ($bind as $key => $value) { + if (is_array($value)) { + $name = $this->bind($value[0], $value[1], isset($value[2]) ? $value[2] : null); + } else { + $name = $this->bind($value); + } + + if (is_numeric($key)) { + $sql = substr_replace($sql, ':' . $name, strpos($sql, '?'), 1); + } else { + $sql = str_replace(':' . $key, ':' . $name, $sql); + } + } + } + + /** + * 指定表达式查询条件 OR + * @access public + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function whereOrRaw($where, $bind = []) + { + return $this->whereRaw($where, $bind, 'OR'); + } + + /** + * 分析查询表达式 + * @access protected + * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @param array $param 查询参数 + * @param bool $strict 严格模式 + * @return $this + */ + protected function parseWhereExp($logic, $field, $op, $condition, array $param = [], $strict = false) + { + if ($field instanceof $this) { + $this->options['where'] = $field->getOptions('where'); + return $this; + } + + $logic = strtoupper($logic); + + if ($field instanceof Where) { + $this->options['where'][$logic] = $field->parse(); + return $this; + } + + if (is_string($field) && !empty($this->options['via']) && false === strpos($field, '.')) { + $field = $this->options['via'] . '.' . $field; + } + + if ($field instanceof Expression) { + return $this->whereRaw($field, is_array($op) ? $op : []); + } elseif ($strict) { + // 使用严格模式查询 + $where = [$field, $op, $condition, $logic]; + } elseif (is_array($field)) { + // 解析数组批量查询 + return $this->parseArrayWhereItems($field, $logic); + } elseif ($field instanceof \Closure) { + $where = $field; + } elseif (is_string($field)) { + if (preg_match('/[,=\<\'\"\(\s]/', $field)) { + return $this->whereRaw($field, $op); + } elseif (is_string($op) && strtolower($op) == 'exp') { + $bind = isset($param[2]) && is_array($param[2]) ? $param[2] : null; + return $this->whereExp($field, $condition, $bind, $logic); + } + + $where = $this->parseWhereItem($logic, $field, $op, $condition, $param); + } + + if (!empty($where)) { + $this->options['where'][$logic][] = $where; + } + return $this; } /** * 分析查询表达式 - * @access public - * @param string $logic 查询逻辑 and or xor - * @param string|array|\Closure $field 查询字段 - * @param mixed $op 查询表达式 - * @param mixed $condition 查询条件 - * @param array $param 查询参数 - * @return void + * @access protected + * @param string $logic 查询逻辑 and or xor + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @param array $param 查询参数 + * @return mixed */ - protected function parseWhereExp($logic, $field, $op, $condition, $param = []) + protected function parseWhereItem($logic, $field, $op, $condition, $param = []) { - $logic = strtoupper($logic); - if ($field instanceof \Closure) { - $this->options['where'][$logic][] = is_string($op) ? [$op, $field] : $field; - return; + if (is_array($op)) { + // 同一字段多条件查询 + array_unshift($param, $field); + $where = $param; + } elseif ($field && is_null($condition)) { + if (in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { + // null查询 + $where = [$field, $op, '']; + } elseif (in_array($op, ['=', 'eq', 'EQ', null], true)) { + $where = [$field, 'NULL', '']; + } elseif (in_array($op, ['<>', 'neq', 'NEQ'], true)) { + $where = [$field, 'NOTNULL', '']; + } else { + // 字段相等查询 + $where = [$field, '=', $op]; + } + } elseif (in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) { + $where = [$field, $op, is_string($condition) ? $this->raw($condition) : $condition]; + } else { + $where = $field ? [$field, $op, $condition, isset($param[2]) ? $param[2] : null] : null; } - if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) { - $field = $this->options['via'] . '.' . $field; - } - if (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) { - $where[] = ['exp', $field]; - if (is_array($op)) { - // 参数绑定 - $this->bind($op); - } - } elseif (is_null($op) && is_null($condition)) { - if (is_array($field)) { - // 数组批量查询 - $where = $field; - foreach ($where as $k => $val) { - $this->options['multi'][$logic][$k][] = $val; - } - } elseif ($field && is_string($field)) { - // 字符串查询 - $where[$field] = ['null', '']; - $this->options['multi'][$logic][$field][] = $where[$field]; - } - } elseif (is_array($op)) { - $where[$field] = $param; - } elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) { - // null查询 - $where[$field] = [$op, '']; - $this->options['multi'][$logic][$field][] = $where[$field]; - } elseif (is_null($condition)) { - // 字段相等查询 - $where[$field] = ['eq', $op]; - if ('AND' != $logic) { - $this->options['multi'][$logic][$field][] = $where[$field]; - } - } else { - $where[$field] = [$op, $condition, isset($param[2]) ? $param[2] : null]; - if ('exp' == strtolower($op) && isset($param[2]) && is_array($param[2])) { - // 参数绑定 - $this->bind($param[2]); - } - // 记录一个字段多次查询条件 - $this->options['multi'][$logic][$field][] = $where[$field]; - } - if (!empty($where)) { - if (!isset($this->options['where'][$logic])) { - $this->options['where'][$logic] = []; - } - if (is_string($field) && $this->checkMultiField($field, $logic)) { - $where[$field] = $this->options['multi'][$logic][$field]; - } elseif (is_array($field)) { - foreach ($field as $key => $val) { - if ($this->checkMultiField($key, $logic)) { - $where[$key] = $this->options['multi'][$logic][$key]; - } - } - } - $this->options['where'][$logic] = array_merge($this->options['where'][$logic], $where); - } + return $where; } /** - * 检查是否存在一个字段多次查询条件 - * @access public - * @param string $field 查询字段 - * @param string $logic 查询逻辑 and or xor - * @return bool + * 数组批量查询 + * @access protected + * @param array $field 批量查询 + * @param string $logic 查询逻辑 and or xor + * @return $this */ - private function checkMultiField($field, $logic) + protected function parseArrayWhereItems($field, $logic) { - return isset($this->options['multi'][$logic][$field]) && count($this->options['multi'][$logic][$field]) > 1; + if (key($field) !== 0) { + $where = []; + foreach ($field as $key => $val) { + if ($val instanceof Expression) { + $where[] = [$key, 'exp', $val]; + } elseif (is_null($val)) { + $where[] = [$key, 'NULL', '']; + } else { + $where[] = [$key, is_array($val) ? 'IN' : '=', $val]; + } + } + } else { + // 数组批量查询 + $where = $field; + } + + if (!empty($where)) { + $this->options['where'][$logic] = isset($this->options['where'][$logic]) ? array_merge($this->options['where'][$logic], $where) : $where; + } + + return $this; } /** * 去除某个查询条件 * @access public - * @param string $field 查询字段 - * @param string $logic 查询逻辑 and or xor + * @param string $field 查询字段 + * @param string $logic 查询逻辑 and or xor * @return $this */ public function removeWhereField($field, $logic = 'AND') { $logic = strtoupper($logic); - if (isset($this->options['where'][$logic][$field])) { - unset($this->options['where'][$logic][$field]); + + if (isset($this->options['where'][$logic])) { + foreach ($this->options['where'][$logic] as $key => $val) { + if (is_array($val) && $val[0] == $field) { + unset($this->options['where'][$logic][$key]); + } + } } + return $this; } /** * 去除查询参数 * @access public - * @param string|bool $option 参数名 true 表示去除所有参数 + * @param string|bool $option 参数名 true 表示去除所有参数 * @return $this */ public function removeOption($option = true) @@ -1255,14 +1658,46 @@ class Query } elseif (is_string($option) && isset($this->options[$option])) { unset($this->options[$option]); } + + return $this; + } + + /** + * 条件查询 + * @access public + * @param mixed $condition 满足条件(支持闭包) + * @param \Closure|array $query 满足条件后执行的查询表达式(闭包或数组) + * @param \Closure|array $otherwise 不满足条件后执行 + * @return $this + */ + public function when($condition, $query, $otherwise = null) + { + if ($condition instanceof \Closure) { + $condition = $condition($this); + } + + if ($condition) { + if ($query instanceof \Closure) { + $query($this, $condition); + } elseif (is_array($query)) { + $this->where($query); + } + } elseif ($otherwise) { + if ($otherwise instanceof \Closure) { + $otherwise($this, $condition); + } elseif (is_array($otherwise)) { + $this->where($otherwise); + } + } + return $this; } /** * 指定查询数量 * @access public - * @param mixed $offset 起始位置 - * @param mixed $length 查询数量 + * @param mixed $offset 起始位置 + * @param mixed $length 查询数量 * @return $this */ public function limit($offset, $length = null) @@ -1270,15 +1705,17 @@ class Query if (is_null($length) && strpos($offset, ',')) { list($offset, $length) = explode(',', $offset); } + $this->options['limit'] = intval($offset) . ($length ? ',' . intval($length) : ''); + return $this; } /** * 指定分页 * @access public - * @param mixed $page 页数 - * @param mixed $listRows 每页数量 + * @param mixed $page 页数 + * @param mixed $listRows 每页数量 * @return $this */ public function page($page, $listRows = null) @@ -1286,15 +1723,18 @@ class Query if (is_null($listRows) && strpos($page, ',')) { list($page, $listRows) = explode(',', $page); } + $this->options['page'] = [intval($page), intval($listRows)]; + return $this; } /** * 分页查询 - * @param int|array $listRows 每页数量 数组表示配置参数 - * @param int|bool $simple 是否简洁模式或者总记录数 - * @param array $config 配置参数 + * @access public + * @param int|array $listRows 每页数量 数组表示配置参数 + * @param int|bool $simple 是否简洁模式或者总记录数 + * @param array $config 配置参数 * page:当前页, * path:url路径, * query:url额外参数, @@ -1311,11 +1751,14 @@ class Query $total = $simple; $simple = false; } + + $paginate = Container::get('config')->pull('paginate'); + if (is_array($listRows)) { - $config = array_merge(Config::get('paginate'), $listRows); + $config = array_merge($paginate, $listRows); $listRows = $config['list_rows']; } else { - $config = array_merge(Config::get('paginate'), $config); + $config = array_merge($paginate, $config); $listRows = $listRows ?: $config['list_rows']; } @@ -1344,13 +1787,17 @@ class Query } else { $results = $this->page($page, $listRows)->select(); } + + $this->removeOption('limit'); + $this->removeOption('page'); + return $class::make($results, $listRows, $page, $total, $simple, $config); } /** * 指定当前操作的数据表 * @access public - * @param mixed $table 表名 + * @param mixed $table 表名 * @return $this */ public function table($table) @@ -1361,6 +1808,7 @@ class Query } elseif (strpos($table, ',')) { $tables = explode(',', $table); $table = []; + foreach ($tables as $item) { list($item, $alias) = explode(' ', trim($item)); if ($alias) { @@ -1379,6 +1827,7 @@ class Query } else { $tables = $table; $table = []; + foreach ($tables as $key => $val) { if (is_numeric($key)) { $table[] = $val; @@ -1388,14 +1837,16 @@ class Query } } } + $this->options['table'] = $table; + return $this; } /** * USING支持 用于多表删除 * @access public - * @param mixed $using + * @param mixed $using * @return $this */ public function using($using) @@ -1407,65 +1858,128 @@ class Query /** * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) * @access public - * @param string|array $field 排序字段 - * @param string $order 排序 + * @param string|array $field 排序字段 + * @param string $order 排序 * @return $this */ public function order($field, $order = null) { - if (!empty($field)) { - if (is_string($field)) { - if (!empty($this->options['via'])) { - $field = $this->options['via'] . '.' . $field; - } - $field = empty($order) ? $field : [$field => $order]; - } elseif (!empty($this->options['via'])) { - foreach ($field as $key => $val) { - if (is_numeric($key)) { - $field[$key] = $this->options['via'] . '.' . $val; - } else { - $field[$this->options['via'] . '.' . $key] = $val; - unset($field[$key]); - } - } + if (empty($field)) { + return $this; + } elseif ($field instanceof Expression) { + $this->options['order'][] = $field; + return $this; + } + + if (is_string($field)) { + if (!empty($this->options['via'])) { + $field = $this->options['via'] . '.' . $field; } - if (!isset($this->options['order'])) { - $this->options['order'] = []; - } - if (is_array($field)) { - $this->options['order'] = array_merge($this->options['order'], $field); + + if (strpos($field, ',')) { + $field = array_map('trim', explode(',', $field)); } else { - $this->options['order'][] = $field; + $field = empty($order) ? $field : [$field => $order]; + } + } elseif (!empty($this->options['via'])) { + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $field[$key] = $this->options['via'] . '.' . $val; + } else { + $field[$this->options['via'] . '.' . $key] = $val; + unset($field[$key]); + } } } + + if (!isset($this->options['order'])) { + $this->options['order'] = []; + } + + if (is_array($field)) { + $this->options['order'] = array_merge($this->options['order'], $field); + } else { + $this->options['order'][] = $field; + } + + return $this; + } + + /** + * 表达式方式指定Field排序 + * @access public + * @param string $field 排序字段 + * @param array $bind 参数绑定 + * @return $this + */ + public function orderRaw($field, $bind = []) + { + if ($bind) { + $this->bindParams($field, $bind); + } + + $this->options['order'][] = $this->raw($field); + + return $this; + } + + /** + * 指定Field排序 order('id',[1,2,3],'desc') + * @access public + * @param string|array $field 排序字段 + * @param array $values 排序值 + * @param string $order + * @return $this + */ + public function orderField($field, array $values, $order = '') + { + if (!empty($values)) { + $values['sort'] = $order; + + $this->options['order'][$field] = $values; + } + + return $this; + } + + /** + * 随机排序 + * @access public + * @return $this + */ + public function orderRand() + { + $this->options['order'][] = '[rand]'; return $this; } /** * 查询缓存 * @access public - * @param mixed $key 缓存key - * @param integer $expire 缓存有效期 - * @param string $tag 缓存标签 + * @param mixed $key 缓存key + * @param integer|\DateTime $expire 缓存有效期 + * @param string $tag 缓存标签 * @return $this */ public function cache($key = true, $expire = null, $tag = null) { // 增加快捷调用方式 cache(10) 等同于 cache(true, 10) - if (is_numeric($key) && is_null($expire)) { + if ($key instanceof \DateTime || (is_numeric($key) && is_null($expire))) { $expire = $key; $key = true; } + if (false !== $key) { $this->options['cache'] = ['key' => $key, 'expire' => $expire, 'tag' => $tag]; } + return $this; } /** * 指定group查询 * @access public - * @param string $group GROUP + * @param string|array $group GROUP * @return $this */ public function group($group) @@ -1477,7 +1991,7 @@ class Query /** * 指定having查询 * @access public - * @param string $having having + * @param string $having having * @return $this */ public function having($having) @@ -1489,20 +2003,21 @@ class Query /** * 指定查询lock * @access public - * @param boolean $lock 是否lock + * @param bool|string $lock 是否lock * @return $this */ public function lock($lock = false) { $this->options['lock'] = $lock; $this->options['master'] = true; + return $this; } /** * 指定distinct查询 * @access public - * @param string $distinct 是否唯一 + * @param string $distinct 是否唯一 * @return $this */ public function distinct($distinct) @@ -1514,20 +2029,25 @@ class Query /** * 指定数据表别名 * @access public - * @param mixed $alias 数据表别名 + * @param array|string $alias 数据表别名 * @return $this */ public function alias($alias) { if (is_array($alias)) { foreach ($alias as $key => $val) { - $this->options['alias'][$key] = $val; + if (false !== strpos($key, '__')) { + $table = $this->connection->parseSqlTable($key); + } else { + $table = $key; + } + $this->options['alias'][$table] = $val; } } else { if (isset($this->options['table'])) { $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; if (false !== strpos($table, '__')) { - $table = $this->parseSqlTable($table); + $table = $this->connection->parseSqlTable($table); } } else { $table = $this->getTable(); @@ -1535,13 +2055,14 @@ class Query $this->options['alias'][$table] = $alias; } + return $this; } /** * 指定强制索引 * @access public - * @param string $force 索引名称 + * @param string $force 索引名称 * @return $this */ public function force($force) @@ -1553,7 +2074,7 @@ class Query /** * 查询注释 * @access public - * @param string $comment 注释 + * @param string $comment 注释 * @return $this */ public function comment($comment) @@ -1565,7 +2086,7 @@ class Query /** * 获取执行的SQL语句 * @access public - * @param boolean $fetch 是否返回sql + * @param boolean $fetch 是否返回sql * @return $this */ public function fetchSql($fetch = true) @@ -1577,7 +2098,7 @@ class Query /** * 不主动获取数据集 * @access public - * @param bool $pdo 是否返回 PDOStatement 对象 + * @param bool $pdo 是否返回 PDOStatement 对象 * @return $this */ public function fetchPdo($pdo = true) @@ -1586,6 +2107,19 @@ class Query return $this; } + /** + * 设置是否返回数据集对象(支持设置数据集对象类名) + * @access public + * @param bool|string $collection 是否返回数据集对象 + * @return $this + */ + public function fetchCollection($collection = true) + { + $this->options['collection'] = $collection; + + return $this; + } + /** * 设置从主服务器读取数据 * @access public @@ -1600,7 +2134,7 @@ class Query /** * 设置是否严格检查字段名 * @access public - * @param bool $strict 是否严格检查字段 + * @param bool $strict 是否严格检查字段 * @return $this */ public function strict($strict = true) @@ -1612,7 +2146,7 @@ class Query /** * 设置查询数据不存在是否抛出异常 * @access public - * @param bool $fail 数据不存在是否抛出异常 + * @param bool $fail 数据不存在是否抛出异常 * @return $this */ public function failException($fail = true) @@ -1624,7 +2158,7 @@ class Query /** * 设置自增序列名 * @access public - * @param string $sequence 自增序列名 + * @param string $sequence 自增序列名 * @return $this */ public function sequence($sequence = null) @@ -1633,10 +2167,179 @@ class Query return $this; } + /** + * 设置需要隐藏的输出属性 + * @access public + * @param mixed $hidden 需要隐藏的字段名 + * @return $this + */ + public function hidden($hidden) + { + if ($this->model) { + $this->options['hidden'] = $hidden; + return $this; + } + + return $this->field($hidden, true); + } + + /** + * 设置需要输出的属性 + * @access public + * @param array $visible 需要输出的属性 + * @return $this + */ + public function visible(array $visible) + { + $this->options['visible'] = $visible; + return $this; + } + + /** + * 设置需要追加输出的属性 + * @access public + * @param array $append 需要追加的属性 + * @return $this + */ + public function append(array $append) + { + $this->options['append'] = $append; + return $this; + } + + /** + * 设置数据字段获取器 + * @access public + * @param string|array $name 字段名 + * @param callable $callback 闭包获取器 + * @return $this + */ + public function withAttr($name, $callback = null) + { + if (is_array($name)) { + $this->options['with_attr'] = $name; + } else { + $this->options['with_attr'][$name] = $callback; + } + + return $this; + } + + /** + * 设置JSON字段信息 + * @access public + * @param array $json JSON字段 + * @param bool $assoc 是否取出数组 + * @return $this + */ + public function json(array $json = [], $assoc = false) + { + $this->options['json'] = $json; + $this->options['json_assoc'] = $assoc; + return $this; + } + + /** + * 设置字段类型信息 + * @access public + * @param array $type 字段类型信息 + * @return $this + */ + public function setJsonFieldType(array $type) + { + $this->options['field_type'] = $type; + return $this; + } + + /** + * 获取字段类型信息 + * @access public + * @param string $field 字段名 + * @return string|null + */ + public function getJsonFieldType($field) + { + return isset($this->options['field_type'][$field]) ? $this->options['field_type'][$field] : null; + } + + /** + * 是否允许返回空数据(或空模型) + * @access public + * @param bool $allowEmpty 是否允许为空 + * @return $this + */ + public function allowEmpty($allowEmpty = true) + { + $this->options['allow_empty'] = $allowEmpty; + return $this; + } + + /** + * 添加查询范围 + * @access public + * @param array|string|\Closure $scope 查询范围定义 + * @param array $args 参数 + * @return $this + */ + public function scope($scope, ...$args) + { + // 查询范围的第一个参数始终是当前查询对象 + array_unshift($args, $this); + + if ($scope instanceof \Closure) { + call_user_func_array($scope, $args); + return $this; + } + + if (is_string($scope)) { + $scope = explode(',', $scope); + } + + if ($this->model) { + // 检查模型类的查询范围方法 + foreach ($scope as $name) { + $method = 'scope' . trim($name); + + if (method_exists($this->model, $method)) { + call_user_func_array([$this->model, $method], $args); + } + } + } + + return $this; + } + + /** + * 使用搜索器条件搜索字段 + * @access public + * @param array $fields 搜索字段 + * @param array $data 搜索数据 + * @param string $prefix 字段前缀标识 + * @return $this + */ + public function withSearch(array $fields, array $data = [], $prefix = '') + { + foreach ($fields as $key => $field) { + if ($field instanceof \Closure) { + $field($this, isset($data[$key]) ? $data[$key] : null, $data, $prefix); + } elseif ($this->model) { + // 检测搜索器 + $fieldName = is_numeric($key) ? $field : $key; + $method = 'search' . Loader::parseName($fieldName, 1) . 'Attr'; + + if (method_exists($this->model, $method)) { + $this->model->$method($this, isset($data[$field]) ? $data[$field] : null, $data, $prefix); + } + } + } + + return $this; + } + /** * 指定数据表主键 * @access public - * @param string $pk 主键 + * @param string $pk 主键 * @return $this */ public function pk($pk) @@ -1648,121 +2351,97 @@ class Query /** * 查询日期或者时间 * @access public - * @param string $field 日期字段名 - * @param string $op 比较运算符或者表达式 - * @param string|array $range 比较范围 + * @param string $name 时间表达式 + * @param string|array $rule 时间范围 * @return $this */ - public function whereTime($field, $op, $range = null) + public function timeRule($name, $rule) { - if (is_null($range)) { - // 使用日期表达式 - $date = getdate(); - switch (strtolower($op)) { - case 'today': - case 'd': - $range = ['today', 'tomorrow']; - break; - case 'week': - case 'w': - $range = 'this week 00:00:00'; - break; - case 'month': - case 'm': - $range = mktime(0, 0, 0, $date['mon'], 1, $date['year']); - break; - case 'year': - case 'y': - $range = mktime(0, 0, 0, 1, 1, $date['year']); - break; - case 'yesterday': - $range = ['yesterday', 'today']; - break; - case 'last week': - $range = ['last week 00:00:00', 'this week 00:00:00']; - break; - case 'last month': - $range = [date('y-m-01', strtotime('-1 month')), mktime(0, 0, 0, $date['mon'], 1, $date['year'])]; - break; - case 'last year': - $range = [mktime(0, 0, 0, 1, 1, $date['year'] - 1), mktime(0, 0, 0, 1, 1, $date['year'])]; - break; - default: - $range = $op; - } - $op = is_array($range) ? 'between' : '>'; - } - $this->where($field, strtolower($op) . ' time', $range); + $this->timeRule[$name] = $rule; return $this; } /** - * 获取数据表信息 + * 查询日期或者时间 * @access public - * @param mixed $tableName 数据表名 留空自动获取 - * @param string $fetch 获取信息类型 包括 fields type bind pk - * @return mixed + * @param string $field 日期字段名 + * @param string|array $op 比较运算符或者表达式 + * @param string|array $range 比较范围 + * @param string $logic AND OR + * @return $this */ - public function getTableInfo($tableName = '', $fetch = '') + public function whereTime($field, $op, $range = null, $logic = 'AND') { - if (!$tableName) { - $tableName = $this->getTable(); - } - if (is_array($tableName)) { - $tableName = key($tableName) ?: current($tableName); - } - - if (strpos($tableName, ',')) { - // 多表不获取字段信息 - return false; - } else { - $tableName = $this->parseSqlTable($tableName); - } - - // 修正子查询作为表名的问题 - if (strpos($tableName, ')')) { - return []; - } - - list($guid) = explode(' ', $tableName); - $db = $this->getConfig('database'); - if (!isset(self::$info[$db . '.' . $guid])) { - if (!strpos($guid, '.')) { - $schema = $db . '.' . $guid; + if (is_null($range)) { + if (is_array($op)) { + $range = $op; } else { - $schema = $guid; - } - // 读取缓存 - if (is_file(RUNTIME_PATH . 'schema/' . $schema . '.php')) { - $info = include RUNTIME_PATH . 'schema/' . $schema . '.php'; - } else { - $info = $this->connection->getFields($guid); - } - $fields = array_keys($info); - $bind = $type = []; - foreach ($info as $key => $val) { - // 记录字段类型 - $type[$key] = $val['type']; - $bind[$key] = $this->getFieldBindType($val['type']); - if (!empty($val['primary'])) { - $pk[] = $key; + if (isset($this->timeExp[strtolower($op)])) { + $op = $this->timeExp[strtolower($op)]; + } + + if (isset($this->timeRule[strtolower($op)])) { + $range = $this->timeRule[strtolower($op)]; + } else { + $range = $op; } } - if (isset($pk)) { - // 设置主键 - $pk = count($pk) > 1 ? $pk : $pk[0]; - } else { - $pk = null; - } - self::$info[$db . '.' . $guid] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; + + $op = is_array($range) ? 'between' : '>='; } - return $fetch ? self::$info[$db . '.' . $guid][$fetch] : self::$info[$db . '.' . $guid]; + + return $this->parseWhereExp($logic, $field, strtolower($op) . ' time', $range, [], true); + } + + /** + * 查询当前时间在两个时间字段范围 + * @access public + * @param string $startField 开始时间字段 + * @param string $endField 结束时间字段 + * @return $this + */ + public function whereBetweenTimeField($startField, $endField) + { + return $this->whereTime($startField, '<=', time()) + ->whereTime($endField, '>=', time()); + } + + /** + * 查询当前时间不在两个时间字段范围 + * @access public + * @param string $startField 开始时间字段 + * @param string $endField 结束时间字段 + * @return $this + */ + public function whereNotBetweenTimeField($startField, $endField) + { + return $this->whereTime($startField, '>', time()) + ->whereTime($endField, '<', time(), 'OR'); + } + + /** + * 查询日期或者时间范围 + * @access public + * @param string $field 日期字段名 + * @param string $startTime 开始时间 + * @param string $endTime 结束时间 + * @param string $logic AND OR + * @return $this + */ + public function whereBetweenTime($field, $startTime, $endTime = null, $logic = 'AND') + { + if (is_null($endTime)) { + $time = is_string($startTime) ? strtotime($startTime) : $startTime; + $endTime = strtotime('+1 day', $time); + } + + return $this->parseWhereExp($logic, $field, 'between time', [$startTime, $endTime], [], true); } /** * 获取当前数据表的主键 * @access public - * @param string|array $options 数据表名或者查询参数 + * @param string|array $options 数据表名或者查询参数 * @return string|array */ public function getPk($options = '') @@ -1770,76 +2449,38 @@ class Query if (!empty($this->pk)) { $pk = $this->pk; } else { - $pk = $this->getTableInfo(is_array($options) ? $options['table'] : $options, 'pk'); + $pk = $this->connection->getPk(is_array($options) && isset($options['table']) ? $options['table'] : $this->getTable()); } + return $pk; } - // 获取当前数据表字段信息 - public function getTableFields($options) - { - return $this->getTableInfo($options['table'], 'fields'); - } - - // 获取当前数据表字段类型 - public function getFieldsType($options) - { - return $this->getTableInfo($options['table'], 'type'); - } - - // 获取当前数据表绑定信息 - public function getFieldsBind($options) - { - $types = $this->getFieldsType($options); - $bind = []; - if ($types) { - foreach ($types as $key => $type) { - $bind[$key] = $this->getFieldBindType($type); - } - } - return $bind; - } - - /** - * 获取字段绑定类型 - * @access public - * @param string $type 字段类型 - * @return integer - */ - protected function getFieldBindType($type) - { - if (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) { - $bind = PDO::PARAM_INT; - } elseif (preg_match('/bool/is', $type)) { - $bind = PDO::PARAM_BOOL; - } else { - $bind = PDO::PARAM_STR; - } - return $bind; - } - /** * 参数绑定 * @access public - * @param mixed $key 参数名 - * @param mixed $value 绑定变量值 - * @param integer $type 绑定类型 - * @return $this + * @param mixed $value 绑定变量值 + * @param integer $type 绑定类型 + * @param string $name 绑定名称 + * @return $this|string */ - public function bind($key, $value = false, $type = PDO::PARAM_STR) + public function bind($value, $type = PDO::PARAM_STR, $name = null) { - if (is_array($key)) { - $this->bind = array_merge($this->bind, $key); + if (is_array($value)) { + $this->bind = array_merge($this->bind, $value); } else { - $this->bind[$key] = [$value, $type]; + $name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_'; + + $this->bind[$name] = [$value, $type]; + return $name; } + return $this; } /** * 检测参数是否已经绑定 * @access public - * @param string $key 参数名 + * @param string $key 参数名 * @return bool */ public function isBind($key) @@ -1847,10 +2488,23 @@ class Query return isset($this->bind[$key]); } + /** + * 查询参数赋值 + * @access public + * @param string $name 参数名 + * @param mixed $value 值 + * @return $this + */ + public function option($name, $value) + { + $this->options[$name] = $value; + return $this; + } + /** * 查询参数赋值 * @access protected - * @param array $options 表达式参数 + * @param array $options 表达式参数 * @return $this */ protected function options(array $options) @@ -1862,22 +2516,34 @@ class Query /** * 获取当前的查询参数 * @access public - * @param string $name 参数名 + * @param string $name 参数名 * @return mixed */ public function getOptions($name = '') { if ('' === $name) { return $this->options; - } else { - return isset($this->options[$name]) ? $this->options[$name] : null; } + return isset($this->options[$name]) ? $this->options[$name] : null; + } + + /** + * 设置当前的查询参数 + * @access public + * @param string $option 参数名 + * @param mixed $value 参数值 + * @return $this + */ + public function setOption($option, $value) + { + $this->options[$option] = $value; + return $this; } /** * 设置关联查询JOIN预查询 * @access public - * @param string|array $with 关联方法名称 + * @param string|array $with 关联方法名称 * @return $this */ public function with($with) @@ -1890,74 +2556,207 @@ class Query $with = explode(',', $with); } - $first = true; - $currentModel = $this->model; + $first = true; /** @var Model $class */ - $class = new $currentModel; + $class = $this->model; foreach ($with as $key => $relation) { - $subRelation = ''; - $closure = false; + $closure = null; + if ($relation instanceof \Closure) { // 支持闭包查询过滤关联条件 - $closure = $relation; - $relation = $key; - $with[$key] = $key; + $closure = $relation; + $relation = $key; } elseif (is_array($relation)) { - $subRelation = $relation; - $relation = $key; + $relation = $key; } elseif (is_string($relation) && strpos($relation, '.')) { - $with[$key] = $relation; list($relation, $subRelation) = explode('.', $relation, 2); } /** @var Relation $model */ $relation = Loader::parseName($relation, 1, false); $model = $class->$relation(); + if ($model instanceof OneToOne && 0 == $model->getEagerlyType()) { - $model->eagerly($this, $relation, $subRelation, $closure, $first); + $table = $model->getTable(); + $model->removeOption() + ->table($table) + ->eagerly($this, $relation, true, '', $closure, $first); $first = false; - } elseif ($closure) { - $with[$key] = $closure; } } + $this->via(); - if (isset($this->options['with'])) { - $this->options['with'] = array_merge($this->options['with'], $with); - } else { - $this->options['with'] = $with; + + $this->options['with'] = $with; + + return $this; + } + + /** + * 关联预载入 JOIN方式(不支持嵌套) + * @access protected + * @param string|array $with 关联方法名 + * @param string $joinType JOIN方式 + * @return $this + */ + public function withJoin($with, $joinType = '') + { + if (empty($with)) { + return $this; } + + if (is_string($with)) { + $with = explode(',', $with); + } + + $first = true; + + /** @var Model $class */ + $class = $this->model; + foreach ($with as $key => $relation) { + $closure = null; + $field = true; + + if ($relation instanceof \Closure) { + // 支持闭包查询过滤关联条件 + $closure = $relation; + $relation = $key; + } elseif (is_array($relation)) { + $field = $relation; + $relation = $key; + } elseif (is_string($relation) && strpos($relation, '.')) { + list($relation, $subRelation) = explode('.', $relation, 2); + } + + /** @var Relation $model */ + $relation = Loader::parseName($relation, 1, false); + $model = $class->$relation(); + + if ($model instanceof OneToOne) { + $model->eagerly($this, $relation, $field, $joinType, $closure, $first); + $first = false; + } else { + // 不支持其它关联 + unset($with[$key]); + } + } + + $this->via(); + + $this->options['with_join'] = $with; + + return $this; + } + + /** + * 关联统计 + * @access protected + * @param string|array $relation 关联方法名 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + protected function withAggregate($relation, $aggregate = 'count', $field = '*', $subQuery = true) + { + $relations = is_string($relation) ? explode(',', $relation) : $relation; + + if (!$subQuery) { + $this->options['with_count'][] = [$relations, $aggregate, $field]; + } else { + if (!isset($this->options['field'])) { + $this->field('*'); + } + + foreach ($relations as $key => $relation) { + $closure = $aggregateField = null; + + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } elseif (!is_int($key)) { + $aggregateField = $relation; + $relation = $key; + } + + $relation = Loader::parseName($relation, 1, false); + + $count = $this->model->$relation()->getRelationCountQuery($closure, $aggregate, $field, $aggregateField); + + if (empty($aggregateField)) { + $aggregateField = Loader::parseName($relation) . '_' . $aggregate; + } + + $this->field(['(' . $count . ')' => $aggregateField]); + } + } + return $this; } /** * 关联统计 * @access public - * @param string|array $relation 关联方法名 - * @param bool $subQuery 是否使用子查询 + * @param string|array $relation 关联方法名 + * @param bool $subQuery 是否使用子查询 * @return $this */ public function withCount($relation, $subQuery = true) { - if (!$subQuery) { - $this->options['with_count'] = $relation; - } else { - $relations = is_string($relation) ? explode(',', $relation) : $relation; - if (!isset($this->options['field'])) { - $this->field('*'); - } - foreach ($relations as $key => $relation) { - $closure = false; - if ($relation instanceof \Closure) { - $closure = $relation; - $relation = $key; - } - $relation = Loader::parseName($relation, 1, false); - $count = '(' . (new $this->model)->$relation()->getRelationCountQuery($closure) . ')'; - $this->field([$count => Loader::parseName($relation) . '_count']); - } - } - return $this; + return $this->withAggregate($relation, 'count', '*', $subQuery); + } + + /** + * 关联统计Sum + * @access public + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withSum($relation, $field, $subQuery = true) + { + return $this->withAggregate($relation, 'sum', $field, $subQuery); + } + + /** + * 关联统计Max + * @access public + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withMax($relation, $field, $subQuery = true) + { + return $this->withAggregate($relation, 'max', $field, $subQuery); + } + + /** + * 关联统计Min + * @access public + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withMin($relation, $field, $subQuery = true) + { + return $this->withAggregate($relation, 'min', $field, $subQuery); + } + + /** + * 关联统计Avg + * @access public + * @param string|array $relation 关联方法名 + * @param string $field 字段 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withAvg($relation, $field, $subQuery = true) + { + return $this->withAggregate($relation, 'avg', $field, $subQuery); } /** @@ -1967,31 +2766,34 @@ class Query * $query->withField("id,name"); * }]) * - * @param string | array $field 指定获取的字段 + * @access public + * @param string | array $field 指定获取的字段 * @return $this */ public function withField($field) { $this->options['with_field'] = $field; + return $this; } /** * 设置当前字段添加的表别名 * @access public - * @param string $via + * @param string $via * @return $this */ public function via($via = '') { $this->options['via'] = $via; + return $this; } /** * 设置关联查询 * @access public - * @param string|array $relation 关联名称 + * @param string|array $relation 关联名称 * @return $this */ public function relation($relation) @@ -1999,113 +2801,44 @@ class Query if (empty($relation)) { return $this; } + if (is_string($relation)) { $relation = explode(',', $relation); } + if (isset($this->options['relation'])) { $this->options['relation'] = array_merge($this->options['relation'], $relation); } else { $this->options['relation'] = $relation; } + return $this; } - /** - * 把主键值转换为查询条件 支持复合主键 - * @access public - * @param array|string $data 主键数据 - * @param mixed $options 表达式参数 - * @return void - * @throws Exception - */ - protected function parsePkWhere($data, &$options) - { - $pk = $this->getPk($options); - // 获取当前数据表 - $table = is_array($options['table']) ? key($options['table']) : $options['table']; - if (!empty($options['alias'][$table])) { - $alias = $options['alias'][$table]; - } - if (is_string($pk)) { - $key = isset($alias) ? $alias . '.' . $pk : $pk; - // 根据主键查询 - if (is_array($data)) { - $where[$key] = isset($data[$pk]) ? $data[$pk] : ['in', $data]; - } else { - $where[$key] = strpos($data, ',') ? ['IN', $data] : $data; - } - } elseif (is_array($pk) && is_array($data) && !empty($data)) { - // 根据复合主键查询 - foreach ($pk as $key) { - if (isset($data[$key])) { - $attr = isset($alias) ? $alias . '.' . $key : $key; - $where[$attr] = $data[$key]; - } else { - throw new Exception('miss complex primary data'); - } - } - } - - if (!empty($where)) { - if (isset($options['where']['AND'])) { - $options['where']['AND'] = array_merge($options['where']['AND'], $where); - } else { - $options['where']['AND'] = $where; - } - } - return; - } - /** * 插入记录 * @access public - * @param mixed $data 数据 - * @param boolean $replace 是否replace - * @param boolean $getLastInsID 返回自增主键 - * @param string $sequence 自增序列名 + * @param array $data 数据 + * @param boolean $replace 是否replace + * @param boolean $getLastInsID 返回自增主键 + * @param string $sequence 自增序列名 * @return integer|string */ public function insert(array $data = [], $replace = false, $getLastInsID = false, $sequence = null) { - // 分析查询表达式 - $options = $this->parseExpress(); - $data = array_merge($options['data'], $data); - // 生成SQL语句 - $sql = $this->builder->insert($data, $options, $replace); - // 获取参数绑定 - $bind = $this->getBind(); - if ($options['fetch_sql']) { - // 获取实际执行的SQL语句 - return $this->connection->getRealSql($sql, $bind); - } + $this->parseOptions(); - // 执行操作 - $result = $this->execute($sql, $bind); - if ($result) { - $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); - $lastInsId = $this->getLastInsID($sequence); - if ($lastInsId) { - $pk = $this->getPk($options); - if (is_string($pk)) { - $data[$pk] = $lastInsId; - } - } - $options['data'] = $data; - $this->trigger('after_insert', $options); + $this->options['data'] = array_merge($this->options['data'], $data); - if ($getLastInsID) { - return $lastInsId; - } - } - return $result; + return $this->connection->insert($this, $replace, $getLastInsID, $sequence); } /** * 插入记录并获取自增ID * @access public - * @param mixed $data 数据 - * @param boolean $replace 是否replace - * @param string $sequence 自增序列名 + * @param array $data 数据 + * @param boolean $replace 是否replace + * @param string $sequence 自增序列名 * @return integer|string */ public function insertGetId(array $data, $replace = false, $sequence = null) @@ -2116,131 +2849,89 @@ class Query /** * 批量插入记录 * @access public - * @param mixed $dataSet 数据集 + * @param array $dataSet 数据集 + * @param boolean $replace 是否replace + * @param integer $limit 每次写入数据限制 * @return integer|string */ - public function insertAll(array $dataSet) + public function insertAll(array $dataSet = [], $replace = false, $limit = null) { - // 分析查询表达式 - $options = $this->parseExpress(); - if (!is_array(reset($dataSet))) { - return false; + $this->parseOptions(); + + if (empty($dataSet)) { + $dataSet = $this->options['data']; } - // 生成SQL语句 - $sql = $this->builder->insertAll($dataSet, $options); - // 获取参数绑定 - $bind = $this->getBind(); - if ($options['fetch_sql']) { - // 获取实际执行的SQL语句 - return $this->connection->getRealSql($sql, $bind); - } else { - // 执行操作 - return $this->execute($sql, $bind); + + if (empty($limit) && !empty($this->options['limit'])) { + $limit = $this->options['limit']; } + + return $this->connection->insertAll($this, $dataSet, $replace, $limit); } /** * 通过Select方式插入记录 * @access public - * @param string $fields 要插入的数据表字段名 - * @param string $table 要插入的数据表名 + * @param string $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 * @return integer|string * @throws PDOException */ public function selectInsert($fields, $table) { - // 分析查询表达式 - $options = $this->parseExpress(); - // 生成SQL语句 - $table = $this->parseSqlTable($table); - $sql = $this->builder->selectInsert($fields, $table, $options); - // 获取参数绑定 - $bind = $this->getBind(); - if ($options['fetch_sql']) { - // 获取实际执行的SQL语句 - return $this->connection->getRealSql($sql, $bind); - } else { - // 执行操作 - return $this->execute($sql, $bind); - } + $this->parseOptions(); + + return $this->connection->selectInsert($this, $fields, $table); } /** * 更新记录 * @access public - * @param mixed $data 数据 + * @param mixed $data 数据 * @return integer|string * @throws Exception * @throws PDOException */ public function update(array $data = []) { - $options = $this->parseExpress(); - $data = array_merge($options['data'], $data); - $pk = $this->getPk($options); - if (isset($options['cache']) && is_string($options['cache']['key'])) { - $key = $options['cache']['key']; + $this->parseOptions(); + + $this->options['data'] = array_merge($this->options['data'], $data); + + return $this->connection->update($this); + } + + /** + * 删除记录 + * @access public + * @param mixed $data 表达式 true 表示强制删除 + * @return int + * @throws Exception + * @throws PDOException + */ + public function delete($data = null) + { + $this->parseOptions(); + + if (!is_null($data) && true !== $data) { + // AR模式分析主键条件 + $this->parsePkWhere($data); } - if (empty($options['where'])) { - // 如果存在主键数据 则自动作为更新条件 - if (is_string($pk) && isset($data[$pk])) { - $where[$pk] = $data[$pk]; - if (!isset($key)) { - $key = 'think:' . $options['table'] . '|' . $data[$pk]; - } - unset($data[$pk]); - } elseif (is_array($pk)) { - // 增加复合主键支持 - foreach ($pk as $field) { - if (isset($data[$field])) { - $where[$field] = $data[$field]; - } else { - // 如果缺少复合主键数据则不执行 - throw new Exception('miss complex primary data'); - } - unset($data[$field]); - } + if (!empty($this->options['soft_delete'])) { + // 软删除 + list($field, $condition) = $this->options['soft_delete']; + if ($condition) { + unset($this->options['soft_delete']); + $this->options['data'] = [$field => $condition]; + + return $this->connection->update($this); } - if (!isset($where)) { - // 如果没有任何更新条件则不执行 - throw new Exception('miss update condition'); - } else { - $options['where']['AND'] = $where; - } - } elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'][$pk])) { - $key = $this->getCacheKey($options['where']['AND'][$pk], $options, $this->bind); } - // 生成UPDATE SQL语句 - $sql = $this->builder->update($data, $options); - // 获取参数绑定 - $bind = $this->getBind(); - if ($options['fetch_sql']) { - // 获取实际执行的SQL语句 - return $this->connection->getRealSql($sql, $bind); - } else { - // 检测缓存 - if (isset($key) && Cache::get($key)) { - // 删除缓存 - Cache::rm($key); - } elseif (!empty($options['cache']['tag'])) { - Cache::clear($options['cache']['tag']); - } - // 执行操作 - $result = '' == $sql ? 0 : $this->execute($sql, $bind); - if ($result) { - if (is_string($pk) && isset($where[$pk])) { - $data[$pk] = $where[$pk]; - } elseif (is_string($pk) && isset($key) && strpos($key, '|')) { - list($a, $val) = explode('|', $key); - $data[$pk] = $val; - } - $options['data'] = $data; - $this->trigger('after_update', $options); - } - return $result; - } + $this->options['data'] = $data; + + return $this->connection->delete($this); } /** @@ -2250,25 +2941,43 @@ class Query */ public function getPdo() { - // 分析查询表达式 - $options = $this->parseExpress(); - // 生成查询SQL - $sql = $this->builder->select($options); - // 获取参数绑定 - $bind = $this->getBind(); - if ($options['fetch_sql']) { - // 获取实际执行的SQL语句 - return $this->connection->getRealSql($sql, $bind); + $this->parseOptions(); + + return $this->connection->pdo($this); + } + + /** + * 使用游标查找记录 + * @access public + * @param array|string|Query|\Closure $data + * @return \Generator + */ + public function cursor($data = null) + { + if ($data instanceof \Closure) { + $data($this); + $data = null; } - // 执行查询操作 - return $this->query($sql, $bind, $options['master'], true); + + $this->parseOptions(); + + if (!is_null($data)) { + // 主键条件分析 + $this->parsePkWhere($data); + } + + $this->options['data'] = $data; + + $connection = clone $this->connection; + + return $connection->cursor($this); } /** * 查找记录 * @access public - * @param array|string|Query|\Closure $data - * @return Collection|false|\PDOStatement|string + * @param array|string|Query|\Closure $data + * @return Collection|array|\PDOStatement|string * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException @@ -2278,138 +2987,124 @@ class Query if ($data instanceof Query) { return $data->select(); } elseif ($data instanceof \Closure) { - call_user_func_array($data, [ & $this]); + $data($this); $data = null; } - // 分析查询表达式 - $options = $this->parseExpress(); + + $this->parseOptions(); if (false === $data) { // 用于子查询 不查询只返回SQL - $options['fetch_sql'] = true; + $this->options['fetch_sql'] = true; } elseif (!is_null($data)) { // 主键条件分析 - $this->parsePkWhere($data, $options); + $this->parsePkWhere($data); } - $resultSet = false; - if (empty($options['fetch_sql']) && !empty($options['cache'])) { - // 判断查询缓存 - $cache = $options['cache']; - unset($options['cache']); - $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options) . serialize($this->bind)); - $resultSet = Cache::get($key); + $this->options['data'] = $data; + + $resultSet = $this->connection->select($this); + + if ($this->options['fetch_sql']) { + return $resultSet; } - if (!$resultSet) { - // 生成查询SQL - $sql = $this->builder->select($options); - // 获取参数绑定 - $bind = $this->getBind(); - if ($options['fetch_sql']) { - // 获取实际执行的SQL语句 - return $this->connection->getRealSql($sql, $bind); - } - $options['data'] = $data; - if ($resultSet = $this->trigger('before_select', $options)) { - } else { - // 执行查询操作 - $resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']); - - if ($resultSet instanceof \PDOStatement) { - // 返回PDOStatement对象 - return $resultSet; - } - } - - if (isset($cache) && $resultSet) { - // 缓存数据集 - $this->cacheData($key, $resultSet, $cache); - } + // 返回结果处理 + if (!empty($this->options['fail']) && count($resultSet) == 0) { + $this->throwNotFound($this->options); } // 数据列表读取后的处理 if (!empty($this->model)) { // 生成模型对象 - $modelName = $this->model; - if (count($resultSet) > 0) { - foreach ($resultSet as $key => $result) { - /** @var Model $result */ - $model = new $modelName($result); - $model->isUpdate(true); + $resultSet = $this->resultSetToModelCollection($resultSet); + } else { + $this->resultSet($resultSet); + } - // 关联查询 - if (!empty($options['relation'])) { - $model->relationQuery($options['relation']); - } - // 关联统计 - if (!empty($options['with_count'])) { - $model->relationCount($model, $options['with_count']); - } - $resultSet[$key] = $model; - } - if (!empty($options['with'])) { - // 预载入 - $model->eagerlyResultSet($resultSet, $options['with']); - } - // 模型数据集转换 - $resultSet = $model->toCollection($resultSet); - } else { - $resultSet = (new $modelName)->toCollection($resultSet); - } - } elseif ('collection' == $this->connection->getConfig('resultset_type')) { - // 返回Collection对象 - $resultSet = new Collection($resultSet); - } - // 返回结果处理 - if (!empty($options['fail']) && count($resultSet) == 0) { - $this->throwNotFound($options); - } return $resultSet; } /** - * 缓存数据 - * @access public - * @param string $key 缓存标识 - * @param mixed $data 缓存数据 - * @param array $config 缓存参数 + * 查询数据转换为模型数据集对象 + * @access protected + * @param array $resultSet 数据集 + * @return ModelCollection */ - protected function cacheData($key, $data, $config = []) + protected function resultSetToModelCollection(array $resultSet) { - if (isset($config['tag'])) { - Cache::tag($config['tag'])->set($key, $data, $config['expire']); - } else { - Cache::set($key, $data, $config['expire']); + if (!empty($this->options['collection']) && is_string($this->options['collection'])) { + $collection = $this->options['collection']; } + + if (empty($resultSet)) { + return $this->model->toCollection([], isset($collection) ? $collection : null); + } + + // 检查动态获取器 + if (!empty($this->options['with_attr'])) { + foreach ($this->options['with_attr'] as $name => $val) { + if (strpos($name, '.')) { + list($relation, $field) = explode('.', $name); + + $withRelationAttr[$relation][$field] = $val; + unset($this->options['with_attr'][$name]); + } + } + } + + $withRelationAttr = isset($withRelationAttr) ? $withRelationAttr : []; + + foreach ($resultSet as $key => &$result) { + // 数据转换为模型对象 + $this->resultToModel($result, $this->options, true, $withRelationAttr); + } + + if (!empty($this->options['with'])) { + // 预载入 + $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr); + } + + if (!empty($this->options['with_join'])) { + // JOIN预载入 + $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true); + } + + // 模型数据集转换 + return $result->toCollection($resultSet, isset($collection) ? $collection : null); } /** - * 生成缓存标识 + * 处理数据集 * @access public - * @param mixed $value 缓存数据 - * @param array $options 缓存参数 - * @param array $bind 绑定参数 + * @param array $resultSet + * @return void */ - protected function getCacheKey($value, $options, $bind = []) + protected function resultSet(&$resultSet) { - if (is_scalar($value)) { - $data = $value; - } elseif (is_array($value) && 'eq' == strtolower($value[0])) { - $data = $value[1]; + if (!empty($this->options['json'])) { + foreach ($resultSet as &$result) { + $this->jsonResult($result, $this->options['json'], true); + } } - if (isset($data)) { - return 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; - } else { - return md5(serialize($options) . serialize($bind)); + + if (!empty($this->options['with_attr'])) { + foreach ($resultSet as &$result) { + $this->getResultAttr($result, $this->options['with_attr']); + } + } + + if (!empty($this->options['collection']) || 'collection' == $this->connection->getConfig('resultset_type')) { + // 返回Collection对象 + $resultSet = new Collection($resultSet); } } /** * 查找单条记录 * @access public - * @param array|string|Query|\Closure $data - * @return array|false|\PDOStatement|string|Model + * @param array|string|Query|\Closure $data + * @return array|null|\PDOStatement|string|Model * @throws DbException * @throws ModelNotFoundException * @throws DataNotFoundException @@ -2419,120 +3114,309 @@ class Query if ($data instanceof Query) { return $data->find(); } elseif ($data instanceof \Closure) { - call_user_func_array($data, [ & $this]); + $data($this); $data = null; } - // 分析查询表达式 - $options = $this->parseExpress(); - $pk = $this->getPk($options); + + $this->parseOptions(); + if (!is_null($data)) { // AR模式分析主键条件 - $this->parsePkWhere($data, $options); - } elseif (!empty($options['cache']) && true === $options['cache']['key'] && is_string($pk) && isset($options['where']['AND'][$pk])) { - $key = $this->getCacheKey($options['where']['AND'][$pk], $options, $this->bind); + $this->parsePkWhere($data); } - $options['limit'] = 1; - $result = false; - if (empty($options['fetch_sql']) && !empty($options['cache'])) { - // 判断查询缓存 - $cache = $options['cache']; - if (true === $cache['key'] && !is_null($data) && !is_array($data)) { - $key = 'think:' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; - } elseif (is_string($cache['key'])) { - $key = $cache['key']; - } elseif (!isset($key)) { - $key = md5(serialize($options) . serialize($this->bind)); - } - $result = Cache::get($key); - } - if (false === $result) { - // 生成查询SQL - $sql = $this->builder->select($options); - // 获取参数绑定 - $bind = $this->getBind(); - if ($options['fetch_sql']) { - // 获取实际执行的SQL语句 - return $this->connection->getRealSql($sql, $bind); - } - if (is_string($pk)) { - if (!is_array($data)) { - if (isset($key) && strpos($key, '|')) { - list($a, $val) = explode('|', $key); - $item[$pk] = $val; - } else { - $item[$pk] = $data; - } - $data = $item; - } - } - $options['data'] = $data; - // 事件回调 - if ($result = $this->trigger('before_find', $options)) { - } else { - // 执行查询 - $resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']); + $this->options['data'] = $data; - if ($resultSet instanceof \PDOStatement) { - // 返回PDOStatement对象 - return $resultSet; - } - $result = isset($resultSet[0]) ? $resultSet[0] : null; - } + $result = $this->connection->find($this); - if (isset($cache) && $result) { - // 缓存数据 - $this->cacheData($key, $result, $cache); - } + if ($this->options['fetch_sql']) { + return $result; } // 数据处理 - if (!empty($result)) { - if (!empty($this->model)) { - // 返回模型对象 - $model = $this->model; - $result = new $model($result); - $result->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null); - // 关联查询 - if (!empty($options['relation'])) { - $result->relationQuery($options['relation']); - } - // 预载入查询 - if (!empty($options['with'])) { - $result->eagerlyResult($result, $options['with']); - } - // 关联统计 - if (!empty($options['with_count'])) { - $result->relationCount($result, $options['with_count']); - } - } - } elseif (!empty($options['fail'])) { - $this->throwNotFound($options); + if (empty($result)) { + return $this->resultToEmpty(); } + + if (!empty($this->model)) { + // 返回模型对象 + $this->resultToModel($result, $this->options); + } else { + $this->result($result); + } + return $result; } /** - * 查询失败 抛出异常 + * 处理空数据 + * @access protected + * @return array|Model|null + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + protected function resultToEmpty() + { + if (!empty($this->options['allow_empty'])) { + return !empty($this->model) ? $this->model->newInstance([], $this->getModelUpdateCondition($this->options)) : []; + } elseif (!empty($this->options['fail'])) { + $this->throwNotFound($this->options); + } + } + + /** + * 查找单条记录 * @access public - * @param array $options 查询参数 + * @param mixed $data 主键值或者查询条件(闭包) + * @param mixed $with 关联预查询 + * @param bool $cache 是否缓存 + * @param bool $failException 是否抛出异常 + * @return static|null + * @throws exception\DbException + */ + public function get($data, $with = [], $cache = false, $failException = false) + { + if (is_null($data)) { + return; + } + + if (true === $with || is_int($with)) { + $cache = $with; + $with = []; + } + + return $this->parseQuery($data, $with, $cache) + ->failException($failException) + ->find($data); + } + + /** + * 查找单条记录 如果不存在直接抛出异常 + * @access public + * @param mixed $data 主键值或者查询条件(闭包) + * @param mixed $with 关联预查询 + * @param bool $cache 是否缓存 + * @return static|null + * @throws exception\DbException + */ + public function getOrFail($data, $with = [], $cache = false) + { + return $this->get($data, $with, $cache, true); + } + + /** + * 查找所有记录 + * @access public + * @param mixed $data 主键列表或者查询条件(闭包) + * @param array|string $with 关联预查询 + * @param bool $cache 是否缓存 + * @return static[]|false + * @throws exception\DbException + */ + public function all($data = null, $with = [], $cache = false) + { + if (true === $with || is_int($with)) { + $cache = $with; + $with = []; + } + + return $this->parseQuery($data, $with, $cache)->select($data); + } + + /** + * 分析查询表达式 + * @access public + * @param mixed $data 主键列表或者查询条件(闭包) + * @param string $with 关联预查询 + * @param bool $cache 是否缓存 + * @return Query + */ + protected function parseQuery(&$data, $with, $cache) + { + $result = $this->with($with)->cache($cache); + + if ((is_array($data) && key($data) !== 0) || $data instanceof Where) { + $result = $result->where($data); + $data = null; + } elseif ($data instanceof \Closure) { + $data($result); + $data = null; + } elseif ($data instanceof Query) { + $result = $data->with($with)->cache($cache); + $data = null; + } + + return $result; + } + + /** + * 处理数据 + * @access protected + * @param array $result 查询数据 + * @return void + */ + protected function result(&$result) + { + if (!empty($this->options['json'])) { + $this->jsonResult($result, $this->options['json'], true); + } + + if (!empty($this->options['with_attr'])) { + $this->getResultAttr($result, $this->options['with_attr']); + } + } + + /** + * 使用获取器处理数据 + * @access protected + * @param array $result 查询数据 + * @param array $withAttr 字段获取器 + * @return void + */ + protected function getResultAttr(&$result, $withAttr = []) + { + foreach ($withAttr as $name => $closure) { + $name = Loader::parseName($name); + + if (strpos($name, '.')) { + // 支持JSON字段 获取器定义 + list($key, $field) = explode('.', $name); + + if (isset($result[$key])) { + $result[$key][$field] = $closure(isset($result[$key][$field]) ? $result[$key][$field] : null, $result[$key]); + } + } else { + $result[$name] = $closure(isset($result[$name]) ? $result[$name] : null, $result); + } + } + } + + /** + * JSON字段数据转换 + * @access protected + * @param array $result 查询数据 + * @param array $json JSON字段 + * @param bool $assoc 是否转换为数组 + * @param array $withRelationAttr 关联获取器 + * @return void + */ + protected function jsonResult(&$result, $json = [], $assoc = false, $withRelationAttr = []) + { + foreach ($json as $name) { + if (isset($result[$name])) { + $result[$name] = json_decode($result[$name], $assoc); + + if (isset($withRelationAttr[$name])) { + foreach ($withRelationAttr[$name] as $key => $closure) { + $data = get_object_vars($result[$name]); + $result[$name]->$key = $closure(isset($result[$name]->$key) ? $result[$name]->$key : null, $data); + } + } + } + } + } + + /** + * 查询数据转换为模型对象 + * @access protected + * @param array $result 查询数据 + * @param array $options 查询参数 + * @param bool $resultSet 是否为数据集查询 + * @param array $withRelationAttr 关联字段获取器 + * @return void + */ + protected function resultToModel(&$result, $options = [], $resultSet = false, $withRelationAttr = []) + { + // 动态获取器 + if (!empty($options['with_attr']) && empty($withRelationAttr)) { + foreach ($options['with_attr'] as $name => $val) { + if (strpos($name, '.')) { + list($relation, $field) = explode('.', $name); + + $withRelationAttr[$relation][$field] = $val; + unset($options['with_attr'][$name]); + } + } + } + + // JSON 数据处理 + if (!empty($options['json'])) { + $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr); + } + + $result = $this->model->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options)); + + // 动态获取器 + if (!empty($options['with_attr'])) { + $result->withAttribute($options['with_attr']); + } + + // 输出属性控制 + if (!empty($options['visible'])) { + $result->visible($options['visible']); + } elseif (!empty($options['hidden'])) { + $result->hidden($options['hidden']); + } + + if (!empty($options['append'])) { + $result->append($options['append']); + } + + // 关联查询 + if (!empty($options['relation'])) { + $result->relationQuery($options['relation'], $withRelationAttr); + } + + // 预载入查询 + if (!$resultSet && !empty($options['with'])) { + $result->eagerlyResult($result, $options['with'], $withRelationAttr); + } + + // JOIN预载入查询 + if (!$resultSet && !empty($options['with_join'])) { + $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true); + } + + // 关联统计 + if (!empty($options['with_count'])) { + foreach ($options['with_count'] as $val) { + $result->relationCount($result, $val[0], $val[1], $val[2]); + } + } + } + + /** + * 获取模型的更新条件 + * @access protected + * @param array $options 查询参数 + */ + protected function getModelUpdateCondition(array $options) + { + return isset($options['where']['AND']) ? $options['where']['AND'] : null; + } + + /** + * 查询失败 抛出异常 + * @access protected + * @param array $options 查询参数 * @throws ModelNotFoundException * @throws DataNotFoundException */ protected function throwNotFound($options = []) { if (!empty($this->model)) { - throw new ModelNotFoundException('model data Not Found:' . $this->model, $this->model, $options); - } else { - $table = is_array($options['table']) ? key($options['table']) : $options['table']; - throw new DataNotFoundException('table data not Found:' . $table, $table, $options); + $class = get_class($this->model); + throw new ModelNotFoundException('model data Not Found:' . $class, $class, $options); } + $table = is_array($options['table']) ? key($options['table']) : $options['table']; + throw new DataNotFoundException('table data not Found:' . $table, $table, $options); } /** * 查找多条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data + * @param array|string|Query|\Closure $data * @return array|\PDOStatement|string|Model * @throws DbException * @throws ModelNotFoundException @@ -2546,7 +3430,7 @@ class Query /** * 查找单条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data + * @param array|string|Query|\Closure $data * @return array|\PDOStatement|string|Model * @throws DbException * @throws ModelNotFoundException @@ -2557,69 +3441,106 @@ class Query return $this->failException(true)->find($data); } + /** + * 查找单条记录 如果不存在则抛出异常 + * @access public + * @param array|string|Query|\Closure $data + * @return array|\PDOStatement|string|Model + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function findOrEmpty($data = null) + { + return $this->allowEmpty(true)->find($data); + } + /** * 分批数据返回处理 * @access public - * @param integer $count 每次处理的数据数量 - * @param callable $callback 处理回调方法 - * @param string $column 分批处理的字段名 + * @param integer $count 每次处理的数据数量 + * @param callable $callback 处理回调方法 + * @param string|array $column 分批处理的字段名 + * @param string $order 字段排序 * @return boolean + * @throws DbException */ - public function chunk($count, $callback, $column = null) + public function chunk($count, $callback, $column = null, $order = 'asc') { $options = $this->getOptions(); - if (isset($options['table'])) { - $table = is_array($options['table']) ? key($options['table']) : $options['table']; - } else { - $table = ''; - } - $column = $column ?: $this->getPk($table); - $bind = $this->bind; - $resultSet = $this->limit($count)->order($column, 'asc')->select(); - if (strpos($column, '.')) { - list($alias, $key) = explode('.', $column); - } else { - $key = $column; - } - if ($resultSet instanceof Collection) { - $resultSet = $resultSet->all(); + $column = $column ?: $this->getPk($options); + + if (isset($options['order'])) { + if (Container::get('app')->isDebug()) { + throw new DbException('chunk not support call order'); + } + unset($options['order']); } - while (!empty($resultSet)) { - if (false === call_user_func($callback, $resultSet)) { - return false; + $bind = $this->bind; + + if (is_array($column)) { + $times = 1; + $query = $this->options($options)->page($times, $count); + } else { + $query = $this->options($options)->limit($count); + + if (strpos($column, '.')) { + list($alias, $key) = explode('.', $column); + } else { + $key = $column; } - $end = end($resultSet); - $lastId = is_array($end) ? $end[$key] : $end->$key; - $resultSet = $this->options($options) - ->limit($count) - ->bind($bind) - ->where($column, '>', $lastId) - ->order($column, 'asc') - ->select(); + } + + $resultSet = $query->order($column, $order)->select(); + + while (count($resultSet) > 0) { if ($resultSet instanceof Collection) { $resultSet = $resultSet->all(); } + + if (false === call_user_func($callback, $resultSet)) { + return false; + } + + if (isset($times)) { + $times++; + $query = $this->options($options)->page($times, $count); + } else { + $end = end($resultSet); + $lastId = is_array($end) ? $end[$key] : $end->getData($key); + + $query = $this->options($options) + ->limit($count) + ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); + } + + $resultSet = $query->bind($bind)->order($column, $order)->select(); } + return true; } /** * 获取绑定的参数 并清空 * @access public + * @param bool $clear * @return array */ - public function getBind() + public function getBind($clear = true) { - $bind = $this->bind; - $this->bind = []; + $bind = $this->bind; + if ($clear) { + $this->bind = []; + } + return $bind; } /** * 创建子查询SQL * @access public - * @param bool $sub + * @param bool $sub * @return string * @throws DbException */ @@ -2629,65 +3550,101 @@ class Query } /** - * 删除记录 - * @access public - * @param mixed $data 表达式 true 表示强制删除 - * @return int - * @throws Exception - * @throws PDOException + * 视图查询处理 + * @access protected + * @param array $options 查询参数 + * @return void */ - public function delete($data = null) + protected function parseView(&$options) { - // 分析查询表达式 - $options = $this->parseExpress(); - $pk = $this->getPk($options); - if (isset($options['cache']) && is_string($options['cache']['key'])) { - $key = $options['cache']['key']; + if (!isset($options['map'])) { + return; } - if (!is_null($data) && true !== $data) { - if (!isset($key) && !is_array($data)) { - // 缓存标识 - $key = 'think:' . $options['table'] . '|' . $data; + foreach (['AND', 'OR'] as $logic) { + if (isset($options['where'][$logic])) { + foreach ($options['where'][$logic] as $key => $val) { + if (array_key_exists($key, $options['map'])) { + array_shift($val); + array_unshift($val, $options['map'][$key]); + $options['where'][$logic][$options['map'][$key]] = $val; + unset($options['where'][$logic][$key]); + } + } } - // AR模式分析主键条件 - $this->parsePkWhere($data, $options); - } elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'][$pk])) { - $key = $this->getCacheKey($options['where']['AND'][$pk], $options, $this->bind); } - if (true !== $data && empty($options['where'])) { - // 如果条件为空 不进行删除操作 除非设置 1=1 - throw new Exception('delete without condition'); - } - // 生成删除SQL语句 - $sql = $this->builder->delete($options); - // 获取参数绑定 - $bind = $this->getBind(); - if ($options['fetch_sql']) { - // 获取实际执行的SQL语句 - return $this->connection->getRealSql($sql, $bind); - } - - // 检测缓存 - if (isset($key) && Cache::get($key)) { - // 删除缓存 - Cache::rm($key); - } elseif (!empty($options['cache']['tag'])) { - Cache::clear($options['cache']['tag']); - } - // 执行操作 - $result = $this->execute($sql, $bind); - if ($result) { - if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { - list($a, $val) = explode('|', $key); - $item[$pk] = $val; - $data = $item; + if (isset($options['order'])) { + // 视图查询排序处理 + if (is_string($options['order'])) { + $options['order'] = explode(',', $options['order']); + } + foreach ($options['order'] as $key => $val) { + if (is_numeric($key) && is_string($val)) { + if (strpos($val, ' ')) { + list($field, $sort) = explode(' ', $val); + if (array_key_exists($field, $options['map'])) { + $options['order'][$options['map'][$field]] = $sort; + unset($options['order'][$key]); + } + } elseif (array_key_exists($val, $options['map'])) { + $options['order'][$options['map'][$val]] = 'asc'; + unset($options['order'][$key]); + } + } elseif (array_key_exists($key, $options['map'])) { + $options['order'][$options['map'][$key]] = $val; + unset($options['order'][$key]); + } } - $options['data'] = $data; - $this->trigger('after_delete', $options); } - return $result; + } + + /** + * 把主键值转换为查询条件 支持复合主键 + * @access public + * @param array|string $data 主键数据 + * @return void + * @throws Exception + */ + public function parsePkWhere($data) + { + $pk = $this->getPk($this->options); + // 获取当前数据表 + $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; + + if (!empty($this->options['alias'][$table])) { + $alias = $this->options['alias'][$table]; + } + + if (is_string($pk)) { + $key = isset($alias) ? $alias . '.' . $pk : $pk; + // 根据主键查询 + if (is_array($data)) { + $where[$pk] = isset($data[$pk]) ? [$key, '=', $data[$pk]] : [$key, 'in', $data]; + } else { + $where[$pk] = strpos($data, ',') ? [$key, 'IN', $data] : [$key, '=', $data]; + } + } elseif (is_array($pk) && is_array($data) && !empty($data)) { + // 根据复合主键查询 + foreach ($pk as $key) { + if (isset($data[$key])) { + $attr = isset($alias) ? $alias . '.' . $key : $key; + $where[$key] = [$attr, '=', $data[$key]]; + } else { + throw new Exception('miss complex primary data'); + } + } + } + + if (!empty($where)) { + if (isset($this->options['where']['AND'])) { + $this->options['where']['AND'] = array_merge($this->options['where']['AND'], $where); + } else { + $this->options['where']['AND'] = $where; + } + } + + return; } /** @@ -2695,9 +3652,9 @@ class Query * @access protected * @return array */ - protected function parseExpress() + protected function parseOptions() { - $options = $this->options; + $options = $this->getOptions(); // 获取数据表 if (empty($options['table'])) { @@ -2708,48 +3665,17 @@ class Query $options['where'] = []; } elseif (isset($options['view'])) { // 视图查询条件处理 - foreach (['AND', 'OR'] as $logic) { - if (isset($options['where'][$logic])) { - foreach ($options['where'][$logic] as $key => $val) { - if (array_key_exists($key, $options['map'])) { - $options['where'][$logic][$options['map'][$key]] = $val; - unset($options['where'][$logic][$key]); - } - } - } - } - - if (isset($options['order'])) { - // 视图查询排序处理 - if (is_string($options['order'])) { - $options['order'] = explode(',', $options['order']); - } - foreach ($options['order'] as $key => $val) { - if (is_numeric($key)) { - if (strpos($val, ' ')) { - list($field, $sort) = explode(' ', $val); - if (array_key_exists($field, $options['map'])) { - $options['order'][$options['map'][$field]] = $sort; - unset($options['order'][$key]); - } - } elseif (array_key_exists($val, $options['map'])) { - $options['order'][$options['map'][$val]] = 'asc'; - unset($options['order'][$key]); - } - } elseif (array_key_exists($key, $options['map'])) { - $options['order'][$options['map'][$key]] = $val; - unset($options['order'][$key]); - } - } - } + $this->parseView($options); } if (!isset($options['field'])) { $options['field'] = '*'; } - if (!isset($options['data'])) { - $options['data'] = []; + foreach (['data', 'order', 'join', 'union'] as $name) { + if (!isset($options[$name])) { + $options[$name] = []; + } } if (!isset($options['strict'])) { @@ -2762,7 +3688,11 @@ class Query } } - foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment'] as $name) { + if (isset(static::$readMaster['*']) || (is_string($options['table']) && isset(static::$readMaster[$options['table']]))) { + $options['master'] = true; + } + + foreach (['group', 'having', 'limit', 'force', 'comment'] as $name) { if (!isset($options[$name])) { $options[$name] = ''; } @@ -2777,15 +3707,16 @@ class Query $options['limit'] = $offset . ',' . $listRows; } - $this->options = []; + $this->options = $options; + return $options; } /** * 注册回调方法 * @access public - * @param string $event 事件名 - * @param callable $callback 回调方法 + * @param string $event 事件名 + * @param callable $callback 回调方法 * @return void */ public static function event($event, $callback) @@ -2795,18 +3726,19 @@ class Query /** * 触发事件 - * @access protected - * @param string $event 事件名 - * @param mixed $params 额外参数 + * @access public + * @param string $event 事件名 * @return bool */ - protected function trigger($event, $params = []) + public function trigger($event) { $result = false; + if (isset(self::$event[$event])) { - $callback = self::$event[$event]; - $result = call_user_func_array($callback, [$params, $this]); + $result = Container::getInstance()->invoke(self::$event[$event], [$this]); } + return $result; } + } diff --git a/thinkphp/library/think/db/Where.php b/thinkphp/library/think/db/Where.php new file mode 100644 index 000000000..9132e5461 --- /dev/null +++ b/thinkphp/library/think/db/Where.php @@ -0,0 +1,178 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +use ArrayAccess; + +class Where implements ArrayAccess +{ + /** + * 查询表达式 + * @var array + */ + protected $where = []; + + /** + * 是否需要增加括号 + * @var bool + */ + protected $enclose = false; + + /** + * 创建一个查询表达式 + * + * @param array $where 查询条件数组 + * @param bool $enclose 是否增加括号 + */ + public function __construct(array $where = [], $enclose = false) + { + $this->where = $where; + $this->enclose = $enclose; + } + + /** + * 设置是否添加括号 + * @access public + * @param bool $enclose + * @return $this + */ + public function enclose($enclose = true) + { + $this->enclose = $enclose; + return $this; + } + + /** + * 解析为Query对象可识别的查询条件数组 + * @access public + * @return array + */ + public function parse() + { + $where = []; + + foreach ($this->where as $key => $val) { + if ($val instanceof Expression) { + $where[] = [$key, 'exp', $val]; + } elseif (is_null($val)) { + $where[] = [$key, 'NULL', '']; + } elseif (is_array($val)) { + $where[] = $this->parseItem($key, $val); + } else { + $where[] = [$key, '=', $val]; + } + } + + return $this->enclose ? [$where] : $where; + } + + /** + * 分析查询表达式 + * @access protected + * @param string $field 查询字段 + * @param array $where 查询条件 + * @return array + */ + protected function parseItem($field, $where = []) + { + $op = $where[0]; + $condition = isset($where[1]) ? $where[1] : null; + + if (is_array($op)) { + // 同一字段多条件查询 + array_unshift($where, $field); + } elseif (is_null($condition)) { + if (in_array(strtoupper($op), ['NULL', 'NOTNULL', 'NOT NULL'], true)) { + // null查询 + $where = [$field, $op, '']; + } elseif (in_array($op, ['=', 'eq', 'EQ', null], true)) { + $where = [$field, 'NULL', '']; + } elseif (in_array($op, ['<>', 'neq', 'NEQ'], true)) { + $where = [$field, 'NOTNULL', '']; + } else { + // 字段相等查询 + $where = [$field, '=', $op]; + } + } else { + $where = [$field, $op, $condition]; + } + + return $where; + } + + /** + * 修改器 设置数据对象的值 + * @access public + * @param string $name 名称 + * @param mixed $value 值 + * @return void + */ + public function __set($name, $value) + { + $this->where[$name] = $value; + } + + /** + * 获取器 获取数据对象的值 + * @access public + * @param string $name 名称 + * @return mixed + */ + public function __get($name) + { + return isset($this->where[$name]) ? $this->where[$name] : null; + } + + /** + * 检测数据对象的值 + * @access public + * @param string $name 名称 + * @return boolean + */ + public function __isset($name) + { + return isset($this->where[$name]); + } + + /** + * 销毁数据对象的值 + * @access public + * @param string $name 名称 + * @return void + */ + public function __unset($name) + { + unset($this->where[$name]); + } + + // ArrayAccess + public function offsetSet($name, $value) + { + $this->__set($name, $value); + } + + public function offsetExists($name) + { + return $this->__isset($name); + } + + public function offsetUnset($name) + { + $this->__unset($name); + } + + public function offsetGet($name) + { + return $this->__get($name); + } + +} diff --git a/thinkphp/library/think/db/builder/Mysql.php b/thinkphp/library/think/db/builder/Mysql.php index 5bc9d03b1..22f33900e 100644 --- a/thinkphp/library/think/db/builder/Mysql.php +++ b/thinkphp/library/think/db/builder/Mysql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,55 +12,165 @@ namespace think\db\builder; use think\db\Builder; +use think\db\Expression; +use think\db\Query; +use think\Exception; /** * mysql数据库驱动 */ class Mysql extends Builder { - protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + // 查询表达式解析 + protected $parser = [ + 'parseCompare' => ['=', '<>', '>', '>=', '<', '<='], + 'parseLike' => ['LIKE', 'NOT LIKE'], + 'parseBetween' => ['NOT BETWEEN', 'BETWEEN'], + 'parseIn' => ['NOT IN', 'IN'], + 'parseExp' => ['EXP'], + 'parseRegexp' => ['REGEXP', 'NOT REGEXP'], + 'parseNull' => ['NOT NULL', 'NULL'], + 'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'], + 'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'], + 'parseExists' => ['NOT EXISTS', 'EXISTS'], + 'parseColumn' => ['COLUMN'], + ]; + + protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%'; + protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + + /** + * 生成insertall SQL + * @access public + * @param Query $query 查询对象 + * @param array $dataSet 数据集 + * @param bool $replace 是否replace + * @return string + */ + public function insertAll(Query $query, $dataSet, $replace = false) + { + $options = $query->getOptions(); + + // 获取合法的字段 + if ('*' == $options['field']) { + $allowFields = $this->connection->getTableFields($options['table']); + } else { + $allowFields = $options['field']; + } + + // 获取绑定信息 + $bind = $this->connection->getFieldsBind($options['table']); + + foreach ($dataSet as $k => $data) { + $data = $this->parseData($query, $data, $allowFields, $bind, '_' . $k); + + $values[] = '( ' . implode(',', array_values($data)) . ' )'; + + if (!isset($insertFields)) { + $insertFields = array_keys($data); + } + } + + $fields = []; + foreach ($insertFields as $field) { + $fields[] = $this->parseKey($query, $field); + } + + return str_replace( + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + [ + $replace ? 'REPLACE' : 'INSERT', + $this->parseTable($query, $options['table']), + implode(' , ', $fields), + implode(' , ', $values), + $this->parseComment($query, $options['comment']), + ], + $this->insertAllSql); + } + + /** + * 正则查询 + * @access protected + * @param Query $query 查询对象 + * @param string $key + * @param string $exp + * @param mixed $value + * @param string $field + * @return string + */ + protected function parseRegexp(Query $query, $key, $exp, $value, $field) + { + if ($value instanceof Expression) { + $value = $value->getValue(); + } + + return $key . ' ' . $exp . ' ' . $value; + } /** * 字段和表名处理 - * @access protected - * @param string $key - * @param array $options + * @access public + * @param Query $query 查询对象 + * @param mixed $key 字段名 + * @param bool $strict 严格检测 * @return string */ - protected function parseKey($key, $options = []) + public function parseKey(Query $query, $key, $strict = false) { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + $key = trim($key); - if (strpos($key, '$.') && false === strpos($key, '(')) { + + if (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 - list($field, $name) = explode('$.', $key); - $key = 'json_extract(' . $field . ', \'$.' . $name . '\')'; + list($field, $name) = explode('->', $key, 2); + + return 'json_extract(' . $this->parseKey($query, $field, true) . ', \'$.' . str_replace('->', '.', $name) . '\')'; } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { list($table, $key) = explode('.', $key, 2); + + $alias = $query->getOptions('alias'); + if ('__TABLE__' == $table) { - $table = $this->query->getTable(); + $table = $query->getOptions('table'); + $table = is_array($table) ? array_shift($table) : $table; } - if (isset($options['alias'][$table])) { - $table = $options['alias'][$table]; + + if (isset($alias[$table])) { + $table = $alias[$table]; } } - if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { + + if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { + throw new Exception('not support data:' . $key); + } + + if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { $key = '`' . $key . '`'; } + if (isset($table)) { if (strpos($table, '.')) { $table = str_replace('.', '`.`', $table); } + $key = '`' . $table . '`.' . $key; } + return $key; } /** * 随机排序 * @access protected + * @param Query $query 查询对象 * @return string */ - protected function parseRand() + protected function parseRand(Query $query) { return 'rand()'; } diff --git a/thinkphp/library/think/db/builder/Pgsql.php b/thinkphp/library/think/db/builder/Pgsql.php index 5be0468a3..742c7db37 100644 --- a/thinkphp/library/think/db/builder/Pgsql.php +++ b/thinkphp/library/think/db/builder/Pgsql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,6 +12,7 @@ namespace think\db\builder; use think\db\Builder; +use think\db\Query; /** * Pgsql数据库驱动 @@ -19,15 +20,20 @@ use think\db\Builder; class Pgsql extends Builder { + protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; + /** * limit分析 * @access protected - * @param mixed $limit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ - public function parseLimit($limit) + public function parseLimit(Query $query, $limit) { $limitStr = ''; + if (!empty($limit)) { $limit = explode(',', $limit); if (count($limit) > 1) { @@ -36,44 +42,61 @@ class Pgsql extends Builder $limitStr .= ' LIMIT ' . $limit[0] . ' '; } } + return $limitStr; } /** * 字段和表名处理 - * @access protected - * @param string $key - * @param array $options + * @access public + * @param Query $query 查询对象 + * @param mixed $key 字段名 + * @param bool $strict 严格检测 * @return string */ - protected function parseKey($key, $options = []) + public function parseKey(Query $query, $key, $strict = false) { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + $key = trim($key); - if (strpos($key, '$.') && false === strpos($key, '(')) { + + if (strpos($key, '->') && false === strpos($key, '(')) { // JSON字段支持 - list($field, $name) = explode('$.', $key); + list($field, $name) = explode('->', $key); $key = $field . '->>\'' . $name . '\''; } elseif (strpos($key, '.')) { list($table, $key) = explode('.', $key, 2); + + $alias = $query->getOptions('alias'); + if ('__TABLE__' == $table) { - $table = $this->query->getTable(); + $table = $query->getOptions('table'); + $table = is_array($table) ? array_shift($table) : $table; } - if (isset($options['alias'][$table])) { - $table = $options['alias'][$table]; + + if (isset($alias[$table])) { + $table = $alias[$table]; } } + if (isset($table)) { $key = $table . '.' . $key; } + return $key; } /** * 随机排序 * @access protected + * @param Query $query 查询对象 * @return string */ - protected function parseRand() + protected function parseRand(Query $query) { return 'RANDOM()'; } diff --git a/thinkphp/library/think/db/builder/Sqlite.php b/thinkphp/library/think/db/builder/Sqlite.php index 55d3abc41..2b887ca8e 100644 --- a/thinkphp/library/think/db/builder/Sqlite.php +++ b/thinkphp/library/think/db/builder/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -12,6 +12,7 @@ namespace think\db\builder; use think\db\Builder; +use think\db\Query; /** * Sqlite数据库驱动 @@ -22,11 +23,14 @@ class Sqlite extends Builder /** * limit * @access public + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ - public function parseLimit($limit) + public function parseLimit(Query $query, $limit) { $limitStr = ''; + if (!empty($limit)) { $limit = explode(',', $limit); if (count($limit) > 1) { @@ -35,41 +39,58 @@ class Sqlite extends Builder $limitStr .= ' LIMIT ' . $limit[0] . ' '; } } + return $limitStr; } /** * 随机排序 * @access protected + * @param Query $query 查询对象 * @return string */ - protected function parseRand() + protected function parseRand(Query $query) { return 'RANDOM()'; } /** * 字段和表名处理 - * @access protected - * @param string $key - * @param array $options + * @access public + * @param Query $query 查询对象 + * @param mixed $key 字段名 + * @param bool $strict 严格检测 * @return string */ - protected function parseKey($key, $options = []) + public function parseKey(Query $query, $key, $strict = false) { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + $key = trim($key); + if (strpos($key, '.')) { list($table, $key) = explode('.', $key, 2); + + $alias = $query->getOptions('alias'); + if ('__TABLE__' == $table) { - $table = $this->query->getTable(); + $table = $query->getOptions('table'); + $table = is_array($table) ? array_shift($table) : $table; } - if (isset($options['alias'][$table])) { - $table = $options['alias'][$table]; + + if (isset($alias[$table])) { + $table = $alias[$table]; } } + if (isset($table)) { $key = $table . '.' . $key; } + return $key; } } diff --git a/thinkphp/library/think/db/builder/Sqlsrv.php b/thinkphp/library/think/db/builder/Sqlsrv.php index 2e13d3fb4..ef27aafa3 100644 --- a/thinkphp/library/think/db/builder/Sqlsrv.php +++ b/thinkphp/library/think/db/builder/Sqlsrv.php @@ -12,6 +12,9 @@ namespace think\db\builder; use think\db\Builder; +use think\db\Expression; +use think\db\Query; +use think\Exception; /** * Sqlsrv数据库驱动 @@ -21,99 +24,136 @@ class Sqlsrv extends Builder protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%'; protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%'; protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; - protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; /** * order分析 * @access protected - * @param mixed $order - * @param array $options + * @param Query $query 查询对象 + * @param mixed $order * @return string */ - protected function parseOrder($order, $options = []) + protected function parseOrder(Query $query, $order) { - if (is_array($order)) { - $array = []; - foreach ($order as $key => $val) { + if (empty($order)) { + return ' ORDER BY rand()'; + } + + foreach ($order as $key => $val) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif ('[rand]' == $val) { + $array[] = $this->parseRand($query); + } else { if (is_numeric($key)) { - if (false === strpos($val, '(')) { - $array[] = $this->parseKey($val, $options); - } elseif ('[rand]' == $val) { - $array[] = $this->parseRand(); - } + list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' '); } else { - $sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : ''; - $array[] = $this->parseKey($key, $options) . ' ' . $sort; + $sort = $val; + } + + if (preg_match('/^[\w\.]+$/', $key)) { + $sort = strtoupper($sort); + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; + $array[] = $this->parseKey($query, $key, true) . $sort; + } else { + throw new Exception('order express error:' . $key); } } - $order = implode(',', $array); } - return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()'; + + return empty($array) ? '' : ' ORDER BY ' . implode(',', $array); } /** * 随机排序 * @access protected + * @param Query $query 查询对象 * @return string */ - protected function parseRand() + protected function parseRand(Query $query) { return 'rand()'; } /** * 字段和表名处理 - * @access protected - * @param string $key - * @param array $options + * @access public + * @param Query $query 查询对象 + * @param mixed $key 字段名 + * @param bool $strict 严格检测 * @return string */ - protected function parseKey($key, $options = []) + public function parseKey(Query $query, $key, $strict = false) { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + $key = trim($key); + if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { list($table, $key) = explode('.', $key, 2); + + $alias = $query->getOptions('alias'); + if ('__TABLE__' == $table) { - $table = $this->query->getTable(); + $table = $query->getOptions('table'); + $table = is_array($table) ? array_shift($table) : $table; } - if (isset($options['alias'][$table])) { - $table = $options['alias'][$table]; + + if (isset($alias[$table])) { + $table = $alias[$table]; } } - if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { + + if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { + throw new Exception('not support data:' . $key); + } + + if ('*' != $key && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { $key = '[' . $key . ']'; } + if (isset($table)) { $key = '[' . $table . '].' . $key; } + return $key; } /** * limit * @access protected - * @param mixed $limit + * @param Query $query 查询对象 + * @param mixed $limit * @return string */ - protected function parseLimit($limit) + protected function parseLimit(Query $query, $limit) { if (empty($limit)) { return ''; } $limit = explode(',', $limit); + if (count($limit) > 1) { $limitStr = '(T1.ROW_NUMBER BETWEEN ' . $limit[0] . ' + 1 AND ' . $limit[0] . ' + ' . $limit[1] . ')'; } else { $limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND ' . $limit[0] . ")"; } + return 'WHERE ' . $limitStr; } - public function selectInsert($fields, $table, $options) + public function selectInsert(Query $query, $fields, $table) { $this->selectSql = $this->selectInsertSql; - return parent::selectInsert($fields, $table, $options); + + return parent::selectInsert($query, $fields, $table); } } diff --git a/thinkphp/library/think/db/connector/Mysql.php b/thinkphp/library/think/db/connector/Mysql.php index c38181dfc..93b8a1825 100644 --- a/thinkphp/library/think/db/connector/Mysql.php +++ b/thinkphp/library/think/db/connector/Mysql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -13,7 +13,7 @@ namespace think\db\connector; use PDO; use think\db\Connection; -use think\Log; +use think\db\Query; /** * mysql数据库驱动 @@ -23,50 +23,74 @@ class Mysql extends Connection protected $builder = '\\think\\db\\builder\\Mysql'; + /** + * 初始化 + * @access protected + * @return void + */ + protected function initialize() + { + // Point类型支持 + Query::extend('point', function ($query, $field, $value = null, $fun = 'GeomFromText', $type = 'POINT') { + if (!is_null($value)) { + $query->data($field, ['point', $value, $fun, $type]); + } else { + if (is_string($field)) { + $field = explode(',', $field); + } + $query->setOption('point', $field); + } + + return $query; + }); + } + /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ protected function parseDsn($config) { - $dsn = 'mysql:dbname=' . $config['database'] . ';host=' . $config['hostname']; - if (!empty($config['hostport'])) { - $dsn .= ';port=' . $config['hostport']; - } elseif (!empty($config['socket'])) { - $dsn .= ';unix_socket=' . $config['socket']; + if (!empty($config['socket'])) { + $dsn = 'mysql:unix_socket=' . $config['socket']; + } elseif (!empty($config['hostport'])) { + $dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport']; + } else { + $dsn = 'mysql:host=' . $config['hostname']; } + $dsn .= ';dbname=' . $config['database']; + if (!empty($config['charset'])) { $dsn .= ';charset=' . $config['charset']; } + return $dsn; } /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ public function getFields($tableName) { - $this->initConnect(false); list($tableName) = explode(' ', $tableName); + if (false === strpos($tableName, '`')) { if (strpos($tableName, '.')) { $tableName = str_replace('.', '`.`', $tableName); } $tableName = '`' . $tableName . '`'; } - $sql = 'SHOW COLUMNS FROM ' . $tableName; - // 调试开始 - $this->debug(true); - $pdo = $this->linkID->query($sql); - // 调试结束 - $this->debug(false, $sql); + + $sql = 'SHOW COLUMNS FROM ' . $tableName; + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; + if ($result) { foreach ($result as $key => $val) { $val = array_change_key_case($val); @@ -80,36 +104,34 @@ class Mysql extends Connection ]; } } + return $this->fieldCase($info); } /** * 取得数据库的表信息 * @access public - * @param string $dbName + * @param string $dbName * @return array */ public function getTables($dbName = '') { - $this->initConnect(false); - $sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES '; - // 调试开始 - $this->debug(true); - $pdo = $this->linkID->query($sql); - // 调试结束 - $this->debug(false, $sql); + $sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES '; + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; + foreach ($result as $key => $val) { $info[$key] = current($val); } + return $info; } /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ protected function getExplain($sql) @@ -117,11 +139,13 @@ class Mysql extends Connection $pdo = $this->linkID->query("EXPLAIN " . $sql); $result = $pdo->fetch(PDO::FETCH_ASSOC); $result = array_change_key_case($result); + if (isset($result['extra'])) { if (strpos($result['extra'], 'filesort') || strpos($result['extra'], 'temporary')) { - Log::record('SQL:' . $this->queryStr . '[' . $result['extra'] . ']', 'warn'); + $this->log('SQL:' . $this->queryStr . '[' . $result['extra'] . ']', 'warn'); } } + return $result; } @@ -130,4 +154,56 @@ class Mysql extends Connection return true; } + /** + * 启动XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function startTransXa($xid) + { + $this->initConnect(true); + if (!$this->linkID) { + return false; + } + + $this->execute("XA START '$xid'"); + } + + /** + * 预编译XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function prepareXa($xid) + { + $this->initConnect(true); + $this->execute("XA END '$xid'"); + $this->execute("XA PREPARE '$xid'"); + } + + /** + * 提交XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function commitXa($xid) + { + $this->initConnect(true); + $this->execute("XA COMMIT '$xid'"); + } + + /** + * 回滚XA事务 + * @access public + * @param string $xid XA事务id + * @return void + */ + public function rollbackXa($xid) + { + $this->initConnect(true); + $this->execute("XA ROLLBACK '$xid'"); + } } diff --git a/thinkphp/library/think/db/connector/Pgsql.php b/thinkphp/library/think/db/connector/Pgsql.php index 3ad780792..ee9fca017 100644 --- a/thinkphp/library/think/db/connector/Pgsql.php +++ b/thinkphp/library/think/db/connector/Pgsql.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -21,39 +21,46 @@ class Pgsql extends Connection { protected $builder = '\\think\\db\\builder\\Pgsql'; + // PDO连接参数 + protected $params = [ + PDO::ATTR_CASE => PDO::CASE_NATURAL, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, + PDO::ATTR_STRINGIFY_FETCHES => false, + ]; + /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ protected function parseDsn($config) { $dsn = 'pgsql:dbname=' . $config['database'] . ';host=' . $config['hostname']; + if (!empty($config['hostport'])) { $dsn .= ';port=' . $config['hostport']; } + return $dsn; } /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ public function getFields($tableName) { - $this->initConnect(false); list($tableName) = explode(' ', $tableName); $sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');'; - // 调试开始 - $this->debug(true); - $pdo = $this->linkID->query($sql); - // 调试结束 - $this->debug(false, $sql); + + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; + if ($result) { foreach ($result as $key => $val) { $val = array_change_key_case($val); @@ -67,36 +74,34 @@ class Pgsql extends Connection ]; } } + return $this->fieldCase($info); } /** * 取得数据库的表信息 * @access public - * @param string $dbName + * @param string $dbName * @return array */ public function getTables($dbName = '') { - $this->initConnect(false); - $sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'"; - // 调试开始 - $this->debug(true); - $pdo = $this->linkID->query($sql); - // 调试结束 - $this->debug(false, $sql); + $sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'"; + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; + foreach ($result as $key => $val) { $info[$key] = current($val); } + return $info; } /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ protected function getExplain($sql) diff --git a/thinkphp/library/think/db/connector/Sqlite.php b/thinkphp/library/think/db/connector/Sqlite.php index 1de177a15..5b9b3fa64 100644 --- a/thinkphp/library/think/db/connector/Sqlite.php +++ b/thinkphp/library/think/db/connector/Sqlite.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -25,33 +25,31 @@ class Sqlite extends Connection /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ protected function parseDsn($config) { $dsn = 'sqlite:' . $config['database']; + return $dsn; } /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ public function getFields($tableName) { - $this->initConnect(false); list($tableName) = explode(' ', $tableName); $sql = 'PRAGMA table_info( ' . $tableName . ' )'; - // 调试开始 - $this->debug(true); - $pdo = $this->linkID->query($sql); - // 调试结束 - $this->debug(false, $sql); + + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; + if ($result) { foreach ($result as $key => $val) { $val = array_change_key_case($val); @@ -65,38 +63,37 @@ class Sqlite extends Connection ]; } } + return $this->fieldCase($info); } /** * 取得数据库的表信息 * @access public - * @param string $dbName + * @param string $dbName * @return array */ public function getTables($dbName = '') { - $this->initConnect(false); $sql = "SELECT name FROM sqlite_master WHERE type='table' " . "UNION ALL SELECT name FROM sqlite_temp_master " . "WHERE type='table' ORDER BY name"; - // 调试开始 - $this->debug(true); - $pdo = $this->linkID->query($sql); - // 调试结束 - $this->debug(false, $sql); + + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; + foreach ($result as $key => $val) { $info[$key] = current($val); } + return $info; } /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ protected function getExplain($sql) diff --git a/thinkphp/library/think/db/connector/Sqlsrv.php b/thinkphp/library/think/db/connector/Sqlsrv.php index e9ee985b6..123affb87 100644 --- a/thinkphp/library/think/db/connector/Sqlsrv.php +++ b/thinkphp/library/think/db/connector/Sqlsrv.php @@ -13,6 +13,7 @@ namespace think\db\connector; use PDO; use think\db\Connection; +use think\db\Query; /** * Sqlsrv数据库驱动 @@ -23,48 +24,53 @@ class Sqlsrv extends Connection protected $params = [ PDO::ATTR_CASE => PDO::CASE_NATURAL, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, PDO::ATTR_STRINGIFY_FETCHES => false, ]; + protected $builder = '\\think\\db\\builder\\Sqlsrv'; + /** * 解析pdo连接的dsn信息 * @access protected - * @param array $config 连接信息 + * @param array $config 连接信息 * @return string */ protected function parseDsn($config) { $dsn = 'sqlsrv:Database=' . $config['database'] . ';Server=' . $config['hostname']; + if (!empty($config['hostport'])) { $dsn .= ',' . $config['hostport']; } + return $dsn; } /** * 取得数据表的字段信息 * @access public - * @param string $tableName + * @param string $tableName * @return array */ public function getFields($tableName) { - $this->initConnect(false); list($tableName) = explode(' ', $tableName); - $sql = "SELECT column_name, data_type, column_default, is_nullable + $tableNames = explode('.', $tableName); + $tableName = isset($tableNames[1]) ? $tableNames[1] : $tableNames[0]; + + $sql = "SELECT column_name, data_type, column_default, is_nullable FROM information_schema.tables AS t JOIN information_schema.columns AS c ON t.table_catalog = c.table_catalog AND t.table_schema = c.table_schema AND t.table_name = c.table_name WHERE t.table_name = '$tableName'"; - // 调试开始 - $this->debug(true); - $pdo = $this->linkID->query($sql); - // 调试结束 - $this->debug(false, $sql); + + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; + if ($result) { foreach ($result as $key => $val) { $val = array_change_key_case($val); @@ -78,49 +84,148 @@ class Sqlsrv extends Connection ]; } } + $sql = "SELECT column_name FROM information_schema.key_column_usage WHERE table_name='$tableName'"; + // 调试开始 $this->debug(true); + $pdo = $this->linkID->query($sql); + // 调试结束 $this->debug(false, $sql); + $result = $pdo->fetch(PDO::FETCH_ASSOC); + if ($result) { $info[$result['column_name']]['primary'] = true; } + return $this->fieldCase($info); } /** * 取得数据表的字段信息 * @access public - * @param string $dbName + * @param string $dbName * @return array */ public function getTables($dbName = '') { - $this->initConnect(false); $sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' "; - // 调试开始 - $this->debug(true); - $pdo = $this->linkID->query($sql); - // 调试结束 - $this->debug(false, $sql); + + $pdo = $this->query($sql, [], false, true); $result = $pdo->fetchAll(PDO::FETCH_ASSOC); $info = []; + foreach ($result as $key => $val) { $info[$key] = current($val); } + return $info; } + /** + * 得到某个列的数组 + * @access public + * @param Query $query 查询对象 + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 + * @return array + */ + public function column(Query $query, $field, $key = '') + { + $options = $query->getOptions(); + + if (empty($options['fetch_sql']) && !empty($options['cache'])) { + // 判断查询缓存 + $cache = $options['cache']; + + $guid = is_string($cache['key']) ? $cache['key'] : $this->getCacheKey($query, $field); + + $result = Container::get('cache')->get($guid); + + if (false !== $result) { + return $result; + } + } + + if (isset($options['field'])) { + $query->removeOption('field'); + } + + if (is_null($field)) { + $field = '*'; + } elseif ($key && '*' != $field) { + $field = $key . ',' . $field; + } + + if (is_string($field)) { + $field = array_map('trim', explode(',', $field)); + } + + $query->setOption('field', $field); + + // 生成查询SQL + $sql = $this->builder->select($query); + + $bind = $query->getBind(); + + if (!empty($options['fetch_sql'])) { + // 获取实际执行的SQL语句 + return $this->getRealSql($sql, $bind); + } + + // 执行查询操作 + $pdo = $this->query($sql, $bind, $options['master'], true); + + if (1 == $pdo->columnCount()) { + $result = $pdo->fetchAll(PDO::FETCH_COLUMN); + } else { + $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); + + if ('*' == $field && $key) { + $result = array_column($resultSet, null, $key); + } elseif ($resultSet) { + $fields = array_keys($resultSet[0]); + $count = count($fields); + $key1 = array_shift($fields); + $key2 = $fields ? array_shift($fields) : ''; + $key = $key ?: $key1; + + if (strpos($key, '.')) { + list($alias, $key) = explode('.', $key); + } + + if (3 == $count) { + $column = $key2; + } elseif ($count < 3) { + $column = $key1; + } else { + $column = null; + } + + $result = array_column($resultSet, $column, $key); + } else { + $result = []; + } + } + + if (isset($cache) && isset($guid)) { + // 缓存数据 + $this->cacheData($guid, $result, $cache); + } + + return $result; + } + /** * SQL性能分析 * @access protected - * @param string $sql + * @param string $sql * @return array */ protected function getExplain($sql) diff --git a/thinkphp/library/think/db/exception/BindParamException.php b/thinkphp/library/think/db/exception/BindParamException.php index d0e2387bc..dce0c7bfc 100644 --- a/thinkphp/library/think/db/exception/BindParamException.php +++ b/thinkphp/library/think/db/exception/BindParamException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -21,11 +21,12 @@ class BindParamException extends DbException /** * BindParamException constructor. - * @param string $message - * @param array $config - * @param string $sql - * @param array $bind - * @param int $code + * @access public + * @param string $message + * @param array $config + * @param string $sql + * @param array $bind + * @param int $code */ public function __construct($message, $config, $sql, $bind, $code = 10502) { diff --git a/thinkphp/library/think/db/exception/DataNotFoundException.php b/thinkphp/library/think/db/exception/DataNotFoundException.php index e399b0637..883e333e3 100644 --- a/thinkphp/library/think/db/exception/DataNotFoundException.php +++ b/thinkphp/library/think/db/exception/DataNotFoundException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -19,9 +19,10 @@ class DataNotFoundException extends DbException /** * DbException constructor. - * @param string $message - * @param string $table - * @param array $config + * @access public + * @param string $message + * @param string $table + * @param array $config */ public function __construct($message, $table = '', array $config = []) { diff --git a/thinkphp/library/think/db/exception/ModelNotFoundException.php b/thinkphp/library/think/db/exception/ModelNotFoundException.php index 2180ab072..ae52baf3b 100644 --- a/thinkphp/library/think/db/exception/ModelNotFoundException.php +++ b/thinkphp/library/think/db/exception/ModelNotFoundException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -19,8 +19,10 @@ class ModelNotFoundException extends DbException /** * 构造方法 - * @param string $message - * @param string $model + * @access public + * @param string $message + * @param string $model + * @param array $config */ public function __construct($message, $model = '', array $config = []) { diff --git a/thinkphp/library/think/debug/Console.php b/thinkphp/library/think/debug/Console.php index 8a232c8c1..5cbaa0f2f 100644 --- a/thinkphp/library/think/debug/Console.php +++ b/thinkphp/library/think/debug/Console.php @@ -11,11 +11,8 @@ namespace think\debug; -use think\Cache; -use think\Config; +use think\Container; use think\Db; -use think\Debug; -use think\Request; use think\Response; /** @@ -24,7 +21,7 @@ use think\Response; class Console { protected $config = [ - 'trace_tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'], + 'tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'], ]; // 实例化并传入参数 @@ -38,13 +35,13 @@ class Console /** * 调试输出接口 * @access public - * @param Response $response Response对象 - * @param array $log 日志信息 + * @param Response $response Response对象 + * @param array $log 日志信息 * @return bool */ public function output(Response $response, array $log = []) { - $request = Request::instance(); + $request = Container::get('request'); $contentType = $response->getHeader('Content-Type'); $accept = $request->header('accept'); if (strpos($accept, 'application/json') === 0 || $request->isAjax()) { @@ -53,12 +50,12 @@ class Console return false; } // 获取基本信息 - $runtime = number_format(microtime(true) - THINK_START_TIME, 10); + $runtime = number_format(microtime(true) - Container::get('app')->getBeginTime(), 10); $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; - $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + $mem = number_format((memory_get_usage() - Container::get('app')->getBeginMem()) / 1024, 2); - if (isset($_SERVER['HTTP_HOST'])) { - $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + if ($request->host()) { + $uri = $request->protocol() . ' ' . $request->method() . ' : ' . $request->url(true); } else { $uri = 'cmd:' . implode(' ', $_SERVER['argv']); } @@ -68,19 +65,18 @@ class Console '请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $uri, '运行时间' => number_format($runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()), '查询信息' => Db::$queryTimes . ' queries ' . Db::$executeTimes . ' writes ', - '缓存信息' => Cache::$readTimes . ' reads,' . Cache::$writeTimes . ' writes', - '配置加载' => count(Config::get()), + '缓存信息' => Container::get('cache')->getReadTimes() . ' reads,' . Container::get('cache')->getWriteTimes() . ' writes', ]; if (session_id()) { $base['会话信息'] = 'SESSION_ID=' . session_id(); } - $info = Debug::getFile(true); + $info = Container::get('debug')->getFile(true); // 页面Trace信息 $trace = []; - foreach ($this->config['trace_tabs'] as $name => $title) { + foreach ($this->config['tabs'] as $name => $title) { $name = strtolower($name); switch ($name) { case 'base': // 基本信息 @@ -121,7 +117,7 @@ JS; protected function console($type, $msg) { $type = strtolower($type); - $trace_tabs = array_values($this->config['trace_tabs']); + $trace_tabs = array_values($this->config['tabs']); $line[] = ($type == $trace_tabs[0] || '调试' == $type || '错误' == $type) ? "console.group('{$type}');" : "console.groupCollapsed('{$type}');"; @@ -137,12 +133,12 @@ JS; } break; case '错误': - $msg = str_replace("\n", '\n', $m); + $msg = str_replace("\n", '\n', addslashes(is_scalar($m) ? $m : json_encode($m))); $style = 'color:#F4006B;font-size:14px;'; $line[] = "console.error(\"%c{$msg}\", \"{$style}\");"; break; case 'sql': - $msg = str_replace("\n", '\n', $m); + $msg = str_replace("\n", '\n', addslashes($m)); $style = "color:#009bb4;"; $line[] = "console.log(\"%c{$msg}\", \"{$style}\");"; break; diff --git a/thinkphp/library/think/debug/Html.php b/thinkphp/library/think/debug/Html.php index f8651aa92..a123762ee 100644 --- a/thinkphp/library/think/debug/Html.php +++ b/thinkphp/library/think/debug/Html.php @@ -11,11 +11,8 @@ namespace think\debug; -use think\Cache; -use think\Config; +use think\Container; use think\Db; -use think\Debug; -use think\Request; use think\Response; /** @@ -24,27 +21,26 @@ use think\Response; class Html { protected $config = [ - 'trace_file' => '', - 'trace_tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'], + 'file' => '', + 'tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'], ]; // 实例化并传入参数 public function __construct(array $config = []) { - $this->config['trace_file'] = THINK_PATH . 'tpl/page_trace.tpl'; - $this->config = array_merge($this->config, $config); + $this->config = array_merge($this->config, $config); } /** * 调试输出接口 * @access public - * @param Response $response Response对象 - * @param array $log 日志信息 + * @param Response $response Response对象 + * @param array $log 日志信息 * @return bool */ public function output(Response $response, array $log = []) { - $request = Request::instance(); + $request = Container::get('request'); $contentType = $response->getHeader('Content-Type'); $accept = $request->header('accept'); if (strpos($accept, 'application/json') === 0 || $request->isAjax()) { @@ -53,13 +49,13 @@ class Html return false; } // 获取基本信息 - $runtime = number_format(microtime(true) - THINK_START_TIME, 10); + $runtime = number_format(microtime(true) - Container::get('app')->getBeginTime(), 10, '.', ''); $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; - $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + $mem = number_format((memory_get_usage() - Container::get('app')->getBeginMem()) / 1024, 2); // 页面Trace信息 - if (isset($_SERVER['HTTP_HOST'])) { - $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + if ($request->host()) { + $uri = $request->protocol() . ' ' . $request->method() . ' : ' . $request->url(true); } else { $uri = 'cmd:' . implode(' ', $_SERVER['argv']); } @@ -67,19 +63,18 @@ class Html '请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $uri, '运行时间' => number_format($runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()), '查询信息' => Db::$queryTimes . ' queries ' . Db::$executeTimes . ' writes ', - '缓存信息' => Cache::$readTimes . ' reads,' . Cache::$writeTimes . ' writes', - '配置加载' => count(Config::get()), + '缓存信息' => Container::get('cache')->getReadTimes() . ' reads,' . Container::get('cache')->getWriteTimes() . ' writes', ]; if (session_id()) { $base['会话信息'] = 'SESSION_ID=' . session_id(); } - $info = Debug::getFile(true); + $info = Container::get('debug')->getFile(true); // 页面Trace信息 $trace = []; - foreach ($this->config['trace_tabs'] as $name => $title) { + foreach ($this->config['tabs'] as $name => $title) { $name = strtolower($name); switch ($name) { case 'base': // 基本信息 @@ -104,7 +99,7 @@ class Html } // 调用Trace页面模板 ob_start(); - include $this->config['trace_file']; + include $this->config['file']; return ob_get_clean(); } diff --git a/thinkphp/library/think/exception/DbException.php b/thinkphp/library/think/exception/DbException.php index 656d69132..0f504257c 100644 --- a/thinkphp/library/think/exception/DbException.php +++ b/thinkphp/library/think/exception/DbException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -20,10 +20,11 @@ class DbException extends Exception { /** * DbException constructor. - * @param string $message - * @param array $config - * @param string $sql - * @param int $code + * @access public + * @param string $message + * @param array $config + * @param string $sql + * @param int $code */ public function __construct($message, array $config, $sql, $code = 10500) { @@ -36,6 +37,7 @@ class DbException extends Exception 'Error SQL' => $sql, ]); + unset($config['username'], $config['password']); $this->setData('Database Config', $config); } diff --git a/thinkphp/library/think/exception/ErrorException.php b/thinkphp/library/think/exception/ErrorException.php index e3f18375d..3143b8f70 100644 --- a/thinkphp/library/think/exception/ErrorException.php +++ b/thinkphp/library/think/exception/ErrorException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -29,25 +29,24 @@ class ErrorException extends Exception /** * 错误异常构造函数 - * @param integer $severity 错误级别 - * @param string $message 错误详细信息 - * @param string $file 出错文件路径 - * @param integer $line 出错行号 - * @param array $context 错误上下文,会包含错误触发处作用域内所有变量的数组 + * @access public + * @param integer $severity 错误级别 + * @param string $message 错误详细信息 + * @param string $file 出错文件路径 + * @param integer $line 出错行号 */ - public function __construct($severity, $message, $file, $line, array $context = []) + public function __construct($severity, $message, $file, $line) { $this->severity = $severity; $this->message = $message; $this->file = $file; $this->line = $line; $this->code = 0; - - empty($context) || $this->setData('Error Context', $context); } /** * 获取错误级别 + * @access public * @return integer 错误级别 */ final public function getSeverity() diff --git a/thinkphp/library/think/exception/Handle.php b/thinkphp/library/think/exception/Handle.php index 8b6e6c40d..02c85ec13 100644 --- a/thinkphp/library/think/exception/Handle.php +++ b/thinkphp/library/think/exception/Handle.php @@ -12,23 +12,26 @@ namespace think\exception; use Exception; -use think\App; -use think\Config; use think\console\Output; -use think\Lang; -use think\Log; +use think\Container; use think\Response; class Handle { - + protected $render; protected $ignoreReport = [ '\\think\\exception\\HttpException', ]; + public function setRender($render) + { + $this->render = $render; + } + /** * Report or log an exception. * + * @access public * @param \Exception $exception * @return void */ @@ -36,7 +39,7 @@ class Handle { if (!$this->isIgnoreReport($exception)) { // 收集异常数据 - if (App::$debug) { + if (Container::get('app')->isDebug()) { $data = [ 'file' => $exception->getFile(), 'line' => $exception->getLine(), @@ -52,7 +55,11 @@ class Handle $log = "[{$data['code']}]{$data['message']}"; } - Log::record($log, 'error'); + if (Container::get('app')->config('log.record_trace')) { + $log .= "\r\n" . $exception->getTraceAsString(); + } + + Container::get('log')->record($log, 'error'); } } @@ -63,17 +70,27 @@ class Handle return true; } } + return false; } /** * Render an exception into an HTTP response. * + * @access public * @param \Exception $e * @return Response */ public function render(Exception $e) { + if ($this->render && $this->render instanceof \Closure) { + $result = call_user_func_array($this->render, [$e]); + + if ($result) { + return $result; + } + } + if ($e instanceof HttpException) { return $this->renderHttpException($e); } else { @@ -82,26 +99,30 @@ class Handle } /** - * @param Output $output - * @param Exception $e + * @access public + * @param Output $output + * @param Exception $e */ public function renderForConsole(Output $output, Exception $e) { - if (App::$debug) { + if (Container::get('app')->isDebug()) { $output->setVerbosity(Output::VERBOSITY_DEBUG); } + $output->renderException($e); } /** - * @param HttpException $e + * @access protected + * @param HttpException $e * @return Response */ protected function renderHttpException(HttpException $e) { $status = $e->getStatusCode(); - $template = Config::get('http_exception_template'); - if (!App::$debug && !empty($template[$status])) { + $template = Container::get('app')->config('http_exception_template'); + + if (!Container::get('app')->isDebug() && !empty($template[$status])) { return Response::create($template[$status], 'view', $status)->assign(['e' => $e]); } else { return $this->convertExceptionToResponse($e); @@ -109,13 +130,14 @@ class Handle } /** - * @param Exception $exception + * @access protected + * @param Exception $exception * @return Response */ protected function convertExceptionToResponse(Exception $exception) { // 收集异常数据 - if (App::$debug) { + if (Container::get('app')->isDebug()) { // 调试模式,获取详细的错误信息 $data = [ 'name' => get_class($exception), @@ -144,9 +166,9 @@ class Handle 'message' => $this->getMessage($exception), ]; - if (!Config::get('show_error_msg')) { + if (!Container::get('app')->config('show_error_msg')) { // 不显示详细错误信息 - $data['message'] = Config::get('error_message'); + $data['message'] = Container::get('app')->config('error_message'); } } @@ -159,10 +181,11 @@ class Handle ob_start(); extract($data); - include Config::get('exception_tmpl'); + include Container::get('app')->config('exception_tmpl'); + // 获取并清空缓存 $content = ob_get_clean(); - $response = new Response($content, 'html'); + $response = Response::create($content, 'html'); if ($exception instanceof HttpException) { $statusCode = $exception->getStatusCode(); @@ -173,52 +196,62 @@ class Handle $statusCode = 500; } $response->code($statusCode); + return $response; } /** * 获取错误编码 * ErrorException则使用错误级别作为错误编码 + * @access protected * @param \Exception $exception * @return integer 错误编码 */ protected function getCode(Exception $exception) { $code = $exception->getCode(); + if (!$code && $exception instanceof ErrorException) { $code = $exception->getSeverity(); } + return $code; } /** * 获取错误信息 * ErrorException则使用错误级别作为错误编码 + * @access protected * @param \Exception $exception * @return string 错误信息 */ protected function getMessage(Exception $exception) { $message = $exception->getMessage(); - if (IS_CLI) { + + if (PHP_SAPI == 'cli') { return $message; } + $lang = Container::get('lang'); + if (strpos($message, ':')) { $name = strstr($message, ':', true); - $message = Lang::has($name) ? Lang::get($name) . strstr($message, ':') : $message; + $message = $lang->has($name) ? $lang->get($name) . strstr($message, ':') : $message; } elseif (strpos($message, ',')) { $name = strstr($message, ',', true); - $message = Lang::has($name) ? Lang::get($name) . ':' . substr(strstr($message, ','), 1) : $message; - } elseif (Lang::has($message)) { - $message = Lang::get($message); + $message = $lang->has($name) ? $lang->get($name) . ':' . substr(strstr($message, ','), 1) : $message; + } elseif ($lang->has($message)) { + $message = $lang->get($message); } + return $message; } /** * 获取出错文件内容 * 获取错误的前9行和后9行 + * @access protected * @param \Exception $exception * @return array 错误文件内容 */ @@ -237,30 +270,37 @@ class Handle } catch (Exception $e) { $source = []; } + return $source; } /** * 获取异常扩展信息 * 用于非调试模式html返回类型显示 + * @access protected * @param \Exception $exception * @return array 异常类定义的扩展数据 */ protected function getExtendData(Exception $exception) { $data = []; + if ($exception instanceof \think\Exception) { $data = $exception->getData(); } + return $data; } /** * 获取常量列表 + * @access private * @return array 常量列表 */ private static function getConst() { - return get_defined_constants(true)['user']; + $const = get_defined_constants(true); + + return isset($const['user']) ? $const['user'] : []; } } diff --git a/thinkphp/library/think/exception/PDOException.php b/thinkphp/library/think/exception/PDOException.php index ebd53dff9..25240b680 100644 --- a/thinkphp/library/think/exception/PDOException.php +++ b/thinkphp/library/think/exception/PDOException.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -19,10 +19,11 @@ class PDOException extends DbException { /** * PDOException constructor. - * @param \PDOException $exception - * @param array $config - * @param string $sql - * @param int $code + * @access public + * @param \PDOException $exception + * @param array $config + * @param string $sql + * @param int $code */ public function __construct(\PDOException $exception, array $config, $sql, $code = 10501) { diff --git a/thinkphp/library/think/exception/RouteNotFoundException.php b/thinkphp/library/think/exception/RouteNotFoundException.php index f85a3c4bb..d22e3a637 100644 --- a/thinkphp/library/think/exception/RouteNotFoundException.php +++ b/thinkphp/library/think/exception/RouteNotFoundException.php @@ -16,7 +16,7 @@ class RouteNotFoundException extends HttpException public function __construct() { - parent::__construct(404); + parent::__construct(404, 'Route Not Found'); } } diff --git a/thinkphp/library/think/exception/ValidateException.php b/thinkphp/library/think/exception/ValidateException.php index b3684169c..e3f843745 100644 --- a/thinkphp/library/think/exception/ValidateException.php +++ b/thinkphp/library/think/exception/ValidateException.php @@ -15,10 +15,11 @@ class ValidateException extends \RuntimeException { protected $error; - public function __construct($error) + public function __construct($error, $code = 0) { $this->error = $error; $this->message = is_array($error) ? implode("\n\r", $error) : $error; + $this->code = $code; } /** diff --git a/thinkphp/library/think/facade/App.php b/thinkphp/library/think/facade/App.php new file mode 100644 index 000000000..b375aa09f --- /dev/null +++ b/thinkphp/library/think/facade/App.php @@ -0,0 +1,63 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\App + * @mixin \think\App + * @method \think\App bind(string $bind) static 绑定模块或者控制器 + * @method void initialize() static 初始化应用 + * @method void init(string $module='') static 初始化模块 + * @method \think\Response run() static 执行应用 + * @method \think\App dispatch(\think\route\Dispatch $dispatch) static 设置当前请求的调度信息 + * @method void log(mixed $log, string $type = 'info') static 记录调试信息 + * @method mixed config(string $name='') static 获取配置参数 + * @method \think\route\Dispatch routeCheck() static URL路由检测(根据PATH_INFO) + * @method \think\App routeMust(bool $must = false) static 设置应用的路由检测机制 + * @method \think\Model model(string $name = '', string $layer = 'model', bool $appendSuffix = false, string $common = 'common') static 实例化模型 + * @method object controller(string $name, string $layer = 'controller', bool $appendSuffix = false, string $empty = '') static 实例化控制器 + * @method \think\Validate validate(string $name = '', string $layer = 'validate', bool $appendSuffix = false, string $common = 'common') static 实例化验证器类 + * @method \think\db\Query db(mixed $config = [], mixed $name = false) static 数据库初始化 + * @method mixed action(string $url, $vars = [], $layer = 'controller', $appendSuffix = false) static 调用模块的操作方法 + * @method string parseClass(string $module, string $layer, string $name, bool $appendSuffix = false) static 解析应用类的类名 + * @method string version() static 获取框架版本 + * @method bool isDebug() static 是否为调试模式 + * @method string getModulePath() static 获取当前模块路径 + * @method void setModulePath(string $path) static 设置当前模块路径 + * @method string getRootPath() static 获取应用根目录 + * @method string getAppPath() static 获取应用类库目录 + * @method string getRuntimePath() static 获取应用运行时目录 + * @method string getThinkPath() static 获取核心框架目录 + * @method string getRoutePath() static 获取路由目录 + * @method string getConfigPath() static 获取应用配置目录 + * @method string getConfigExt() static 获取配置后缀 + * @method string setNamespace(string $namespace) static 设置应用类库命名空间 + * @method string getNamespace() static 获取应用类库命名空间 + * @method string getSuffix() static 是否启用类库后缀 + * @method float getBeginTime() static 获取应用开启时间 + * @method integer getBeginMem() static 获取应用初始内存占用 + * @method \think\Container container() static 获取容器实例 + */ +class App extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'app'; + } +} diff --git a/thinkphp/library/think/facade/Build.php b/thinkphp/library/think/facade/Build.php new file mode 100644 index 000000000..c051bea11 --- /dev/null +++ b/thinkphp/library/think/facade/Build.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Build + * @mixin \think\Build + * @method void run(array $build = [], string $namespace = 'app', bool $suffix = false) static 根据传入的build资料创建目录和文件 + * @method void module(string $module = '', array $list = [], string $namespace = 'app', bool $suffix = false) static 创建模块 + */ +class Build extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'build'; + } +} diff --git a/thinkphp/library/think/facade/Cache.php b/thinkphp/library/think/facade/Cache.php new file mode 100644 index 000000000..9743486eb --- /dev/null +++ b/thinkphp/library/think/facade/Cache.php @@ -0,0 +1,45 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Cache + * @mixin \think\Cache + * @method \think\cache\Driver connect(array $options = [], mixed $name = false) static 连接缓存 + * @method \think\cache\Driver init(array $options = []) static 初始化缓存 + * @method \think\cache\Driver store(string $name = '') static 切换缓存类型 + * @method bool has(string $name) static 判断缓存是否存在 + * @method mixed get(string $name, mixed $default = false) static 读取缓存 + * @method mixed pull(string $name) static 读取缓存并删除 + * @method mixed set(string $name, mixed $value, int $expire = null) static 设置缓存 + * @method mixed remember(string $name, mixed $value, int $expire = null) static 如果不存在则写入缓存 + * @method mixed inc(string $name, int $step = 1) static 自增缓存(针对数值缓存) + * @method mixed dec(string $name, int $step = 1) static 自减缓存(针对数值缓存) + * @method bool rm(string $name) static 删除缓存 + * @method bool clear(string $tag = null) static 清除缓存 + * @method mixed tag(string $name, mixed $keys = null, bool $overlay = false) static 缓存标签 + * @method object handler() static 返回句柄对象,可执行其它高级方法 + */ +class Cache extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'cache'; + } +} diff --git a/thinkphp/library/think/facade/Config.php b/thinkphp/library/think/facade/Config.php new file mode 100644 index 000000000..8646d1271 --- /dev/null +++ b/thinkphp/library/think/facade/Config.php @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Config + * @mixin \think\Config + * @method bool has(string $name) static 检测配置是否存在 + * @method array pull(string $name) static 获取一级配置 + * @method mixed get(string $name,mixed $default = null) static 获取配置参数 + * @method mixed set(string $name, mixed $value = null) static 设置配置参数 + * @method array reset(string $prefix ='') static 重置配置参数 + */ +class Config extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'config'; + } +} diff --git a/thinkphp/library/think/facade/Cookie.php b/thinkphp/library/think/facade/Cookie.php new file mode 100644 index 000000000..4d7cea250 --- /dev/null +++ b/thinkphp/library/think/facade/Cookie.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Cookie + * @mixin \think\Cookie + * @method void init(array $config = []) static 初始化 + * @method bool has(string $name,string $prefix = null) static 判断Cookie数据 + * @method mixed prefix(string $prefix = '') static 设置或者获取cookie作用域(前缀) + * @method mixed get(string $name,string $prefix = null) static Cookie获取 + * @method mixed set(string $name, mixed $value = null, mixed $option = null) static 设置Cookie + * @method void forever(string $name, mixed $value = null, mixed $option = null) static 永久保存Cookie数据 + * @method void delete(string $name, string $prefix = null) static Cookie删除 + * @method void clear($prefix = null) static Cookie清空 + */ +class Cookie extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'cookie'; + } +} diff --git a/thinkphp/library/think/facade/Debug.php b/thinkphp/library/think/facade/Debug.php new file mode 100644 index 000000000..df20086d1 --- /dev/null +++ b/thinkphp/library/think/facade/Debug.php @@ -0,0 +1,40 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Debug + * @mixin \think\Debug + * @method void remark(string $name, mixed $value = '') static 记录时间(微秒)和内存使用情况 + * @method int getRangeTime(string $start, string $end, mixed $dec = 6) static 统计某个区间的时间(微秒)使用情况 + * @method int getUseTime(int $dec = 6) static 统计从开始到统计时的时间(微秒)使用情况 + * @method string getThroughputRate(string $start, string $end, mixed $dec = 6) static 获取当前访问的吞吐率情况 + * @method string getRangeMem(string $start, string $end, mixed $dec = 2) static 记录区间的内存使用情况 + * @method int getUseMem(int $dec = 2) static 统计从开始到统计时的内存使用情况 + * @method string getMemPeak(string $start, string $end, mixed $dec = 2) static 统计区间的内存峰值情况 + * @method mixed getFile(bool $detail = false) static 获取文件加载信息 + * @method mixed dump(mixed $var, bool $echo = true, string $label = null, int $flags = ENT_SUBSTITUTE) static 浏览器友好的变量输出 + */ +class Debug extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'debug'; + } +} diff --git a/thinkphp/library/think/facade/Env.php b/thinkphp/library/think/facade/Env.php new file mode 100644 index 000000000..5d0472443 --- /dev/null +++ b/thinkphp/library/think/facade/Env.php @@ -0,0 +1,34 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Env + * @mixin \think\Env + * @method void load(string $file) static 读取环境变量定义文件 + * @method mixed get(string $name = null, mixed $default = null) static 获取环境变量值 + * @method void set(mixed $env, string $value = null) static 设置环境变量值 + */ +class Env extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'env'; + } +} diff --git a/thinkphp/library/think/facade/Hook.php b/thinkphp/library/think/facade/Hook.php new file mode 100644 index 000000000..e9e120838 --- /dev/null +++ b/thinkphp/library/think/facade/Hook.php @@ -0,0 +1,37 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Hook + * @mixin \think\Hook + * @method \think\Hook alias(mixed $name, mixed $behavior = null) static 指定行为标识 + * @method void add(string $tag, mixed $behavior, bool $first = false) static 动态添加行为扩展到某个标签 + * @method void import(array $tags, bool $recursive = true) static 批量导入插件 + * @method array get(string $tag = '') static 获取插件信息 + * @method mixed listen(string $tag, mixed $params = null, bool $once = false) static 监听标签的行为 + * @method mixed exec(mixed $class, mixed $params = null) static 执行行为 + */ +class Hook extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'hook'; + } +} diff --git a/thinkphp/library/think/facade/Lang.php b/thinkphp/library/think/facade/Lang.php new file mode 100644 index 000000000..56c4777db --- /dev/null +++ b/thinkphp/library/think/facade/Lang.php @@ -0,0 +1,41 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Lang + * @mixin \think\Lang + * @method mixed range($range = '') static 设定当前的语言 + * @method mixed set(mixed $name, string $value = null, string $range = '') static 设置语言定义 + * @method array load(mixed $file, string $range = '') static 加载语言定义 + * @method mixed get(string $name = null, array $vars = [], string $range = '') static 获取语言定义 + * @method mixed has(string $name, string $range = '') static 获取语言定义 + * @method string detect() static 自动侦测设置获取语言选择 + * @method void saveToCookie(string $lang = null) static 设置当前语言到Cookie + * @method void setLangDetectVar(string $var) static 设置语言自动侦测的变量 + * @method void setLangCookieVar(string $var) static 设置语言的cookie保存变量 + * @method void setAllowLangList(array $list) static 设置允许的语言列表 + */ +class Lang extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'lang'; + } +} diff --git a/thinkphp/library/think/facade/Log.php b/thinkphp/library/think/facade/Log.php new file mode 100644 index 000000000..ddf851ea4 --- /dev/null +++ b/thinkphp/library/think/facade/Log.php @@ -0,0 +1,49 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Log + * @mixin \think\Log + * @method \think\Log init(array $config = []) static 日志初始化 + * @method mixed getLog(string $type = '') static 获取日志信息 + * @method \think\Log record(mixed $msg, string $type = 'info', array $context = []) static 记录日志信息 + * @method \think\Log clear() static 清空日志信息 + * @method \think\Log key(string $key) static 当前日志记录的授权key + * @method bool check(array $config) static 检查日志写入权限 + * @method bool save() static 保存调试信息 + * @method void write(mixed $msg, string $type = 'info', bool $force = false) static 实时写入日志信息 + * @method void log(string $level,mixed $message, array $context = []) static 记录日志信息 + * @method void emergency(mixed $message, array $context = []) static 记录emergency信息 + * @method void alert(mixed $message, array $context = []) static 记录alert信息 + * @method void critical(mixed $message, array $context = []) static 记录critical信息 + * @method void error(mixed $message, array $context = []) static 记录error信息 + * @method void warning(mixed $message, array $context = []) static 记录warning信息 + * @method void notice(mixed $message, array $context = []) static 记录notice信息 + * @method void info(mixed $message, array $context = []) static 记录info信息 + * @method void debug(mixed $message, array $context = []) static 记录debug信息 + * @method void sql(mixed $message, array $context = []) static 记录sql信息 + */ +class Log extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'log'; + } +} diff --git a/thinkphp/library/think/facade/Middleware.php b/thinkphp/library/think/facade/Middleware.php new file mode 100644 index 000000000..5e4cac747 --- /dev/null +++ b/thinkphp/library/think/facade/Middleware.php @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Middleware + * @mixin \think\Middleware + * @method void import(array $middlewares = []) static 批量设置中间件 + * @method void add(mixed $middleware) static 添加中间件到队列 + * @method void unshift(mixed $middleware) static 添加中间件到队列开头 + * @method array all() static 获取中间件队列 + * @method \think\Response dispatch(\think\Request $request) static 执行中间件调度 + */ +class Middleware extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'middleware'; + } +} diff --git a/thinkphp/library/think/facade/Request.php b/thinkphp/library/think/facade/Request.php new file mode 100644 index 000000000..0989253f9 --- /dev/null +++ b/thinkphp/library/think/facade/Request.php @@ -0,0 +1,97 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Request + * @mixin \think\Request + * @method void hook(mixed $method, mixed $callback = null) static Hook 方法注入 + * @method \think\Request create(string $uri, string $method = 'GET', array $params = [], array $cookie = [], array $files = [], array $server = [], string $content = null) static 创建一个URL请求 + * @method mixed domain(bool $port = false) static 获取当前包含协议、端口的域名 + * @method mixed url(bool $domain = false) static 获取当前完整URL + * @method mixed baseUrl(bool $domain = false) static 获取当前URL + * @method mixed baseFile(bool $domain = false) static 获取当前执行的文件 + * @method mixed root(bool $domain = false) static 获取URL访问根地址 + * @method string rootUrl() static 获取URL访问根目录 + * @method string pathinfo() static 获取当前请求URL的pathinfo信息(含URL后缀) + * @method string path() static 获取当前请求URL的pathinfo信息(不含URL后缀) + * @method string ext() static 当前URL的访问后缀 + * @method float time(bool $float = false) static 获取当前请求的时间 + * @method mixed type() static 当前请求的资源类型 + * @method void mimeType(mixed $type, string $val = '') static 设置资源类型 + * @method string method(bool $method = false) static 当前的请求类型 + * @method bool isGet() static 是否为GET请求 + * @method bool isPost() static 是否为POST请求 + * @method bool isPut() static 是否为PUT请求 + * @method bool isDelete() static 是否为DELTE请求 + * @method bool isHead() static 是否为HEAD请求 + * @method bool isPatch() static 是否为PATCH请求 + * @method bool isOptions() static 是否为OPTIONS请求 + * @method bool isCli() static 是否为cli + * @method bool isCgi() static 是否为cgi + * @method mixed param(string $name = '', mixed $default = null, mixed $filter = '') static 获取当前请求的参数 + * @method mixed route(string $name = '', mixed $default = null, mixed $filter = '') static 设置获取路由参数 + * @method mixed get(string $name = '', mixed $default = null, mixed $filter = '') static 设置获取GET参数 + * @method mixed post(string $name = '', mixed $default = null, mixed $filter = '') static 设置获取POST参数 + * @method mixed put(string $name = '', mixed $default = null, mixed $filter = '') static 设置获取PUT参数 + * @method mixed delete(string $name = '', mixed $default = null, mixed $filter = '') static 设置获取DELETE参数 + * @method mixed patch(string $name = '', mixed $default = null, mixed $filter = '') static 设置获取PATCH参数 + * @method mixed request(string $name = '', mixed $default = null, mixed $filter = '') static 获取request变量 + * @method mixed session(string $name = '', mixed $default = null, mixed $filter = '') static 获取session数据 + * @method mixed cookie(string $name = '', mixed $default = null, mixed $filter = '') static 获取cookie参数 + * @method mixed server(string $name = '', mixed $default = null, mixed $filter = '') static 获取server参数 + * @method mixed env(string $name = '', mixed $default = null, mixed $filter = '') static 获取环境变量 + * @method mixed file(string $name = '') static 获取上传的文件信息 + * @method mixed header(string $name = '', mixed $default = null) static 设置或者获取当前的Header + * @method mixed input(array $data,mixed $name = '', mixed $default = null, mixed $filter = '') static 获取变量 支持过滤和默认值 + * @method mixed filter(mixed $filter = null) static 设置或获取当前的过滤规则 + * @method mixed has(string $name, string $type = 'param', bool $checkEmpty = false) static 是否存在某个请求参数 + * @method mixed only(mixed $name, string $type = 'param') static 获取指定的参数 + * @method mixed except(mixed $name, string $type = 'param') static 排除指定参数获取 + * @method bool isSsl() static 当前是否ssl + * @method bool isAjax(bool $ajax = false) static 当前是否Ajax请求 + * @method bool isPjax(bool $pjax = false) static 当前是否Pjax请求 + * @method mixed ip(int $type = 0, bool $adv = true) static 获取客户端IP地址 + * @method bool isMobile() static 检测是否使用手机访问 + * @method string scheme() static 当前URL地址中的scheme参数 + * @method string query() static 当前请求URL地址中的query参数 + * @method string host(bool $stric = false) static 当前请求的host + * @method string port() static 当前请求URL地址中的port参数 + * @method string protocol() static 当前请求 SERVER_PROTOCOL + * @method string remotePort() static 当前请求 REMOTE_PORT + * @method string contentType() static 当前请求 HTTP_CONTENT_TYPE + * @method array routeInfo() static 获取当前请求的路由信息 + * @method array dispatch() static 获取当前请求的调度信息 + * @method string module() static 获取当前的模块名 + * @method string controller(bool $convert = false) static 获取当前的控制器名 + * @method string action(bool $convert = false) static 获取当前的操作名 + * @method string langset() static 获取当前的语言 + * @method string getContent() static 设置或者获取当前请求的content + * @method string getInput() static 获取当前请求的php://input + * @method string token(string $name = '__token__', mixed $type = 'md5') static 生成请求令牌 + * @method string cache(string $key, mixed $expire = null, array $except = [], string $tag = null) static 设置当前地址的请求缓存 + * @method string getCache() static 读取请求缓存设置 + */ +class Request extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'request'; + } +} diff --git a/thinkphp/library/think/facade/Response.php b/thinkphp/library/think/facade/Response.php new file mode 100644 index 000000000..d7de142f0 --- /dev/null +++ b/thinkphp/library/think/facade/Response.php @@ -0,0 +1,47 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Response + * @mixin \think\Response + * @method \think\response create(mixed $data = '', string $type = '', int $code = 200, array $header = [], array $options = []) static 创建Response对象 + * @method void send() static 发送数据到客户端 + * @method \think\Response options(mixed $options = []) static 输出的参数 + * @method \think\Response data(mixed $data) static 输出数据设置 + * @method \think\Response header(mixed $name, string $value = null) static 设置响应头 + * @method \think\Response content(mixed $content) static 设置页面输出内容 + * @method \think\Response code(int $code) static 发送HTTP状态 + * @method \think\Response lastModified(string $time) static LastModified + * @method \think\Response expires(string $time) static expires + * @method \think\Response eTag(string $eTag) static eTag + * @method \think\Response cacheControl(string $cache) static 页面缓存控制 + * @method \think\Response contentType(string $contentType, string $charset = 'utf-8') static 页面输出类型 + * @method mixed getHeader(string $name) static 获取头部信息 + * @method mixed getData() static 获取原始数据 + * @method mixed getContent() static 获取输出数据 + * @method int getCode() static 获取状态码 + */ +class Response extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'response'; + } +} diff --git a/thinkphp/library/think/facade/Route.php b/thinkphp/library/think/facade/Route.php new file mode 100644 index 000000000..77196dfe4 --- /dev/null +++ b/thinkphp/library/think/facade/Route.php @@ -0,0 +1,57 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Route + * @mixin \think\Route + * @method \think\route\Domain domain(mixed $name, mixed $rule = '', array $option = [], array $pattern = []) static 注册域名路由 + * @method \think\Route pattern(mixed $name, string $rule = '') static 注册变量规则 + * @method \think\Route option(mixed $name, mixed $value = '') static 注册路由参数 + * @method \think\Route bind(string $bind) static 设置路由绑定 + * @method mixed getBind(string $bind) static 读取路由绑定 + * @method \think\Route name(string $name) static 设置当前路由标识 + * @method mixed getName(string $name) static 读取路由标识 + * @method void setName(string $name) static 批量导入路由标识 + * @method void import(array $rules, string $type = '*') static 导入配置文件的路由规则 + * @method \think\route\RuleItem rule(string $rule, mixed $route, string $method = '*', array $option = [], array $pattern = []) static 注册路由规则 + * @method void rules(array $rules, string $method = '*', array $option = [], array $pattern = []) static 批量注册路由规则 + * @method \think\route\RuleGroup group(string|array $name, mixed $route, string $method = '*', array $option = [], array $pattern = []) static 注册路由分组 + * @method \think\route\RuleItem any(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册路由 + * @method \think\route\RuleItem get(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册路由 + * @method \think\route\RuleItem post(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册路由 + * @method \think\route\RuleItem put(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册路由 + * @method \think\route\RuleItem delete(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册路由 + * @method \think\route\RuleItem patch(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册路由 + * @method \think\route\Resource resource(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册资源路由 + * @method \think\Route controller(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册控制器路由 + * @method \think\Route alias(string $rule, mixed $route, array $option = [], array $pattern = []) static 注册别名路由 + * @method \think\Route setMethodPrefix(mixed $method, string $prefix = '') static 设置不同请求类型下面的方法前缀 + * @method \think\Route rest(string $name, array $resource = []) static rest方法定义和修改 + * @method \think\Route\RuleItem miss(string $route, string $method = '*', array $option = []) static 注册未匹配路由规则后的处理 + * @method \think\Route\RuleItem auto(string $route) static 注册一个自动解析的URL路由 + * @method \think\Route\Dispatch check(string $url, string $depr = '/', bool $must = false, bool $completeMatch = false) static 检测URL路由 + */ +class Route extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'route'; + } +} diff --git a/thinkphp/library/think/facade/Session.php b/thinkphp/library/think/facade/Session.php new file mode 100644 index 000000000..fb9206afa --- /dev/null +++ b/thinkphp/library/think/facade/Session.php @@ -0,0 +1,46 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Session + * @mixin \think\Session + * @method void init(array $config = []) static session初始化 + * @method bool has(string $name,string $prefix = null) static 判断session数据 + * @method mixed prefix(string $prefix = '') static 设置或者获取session作用域(前缀) + * @method mixed get(string $name = '',string $prefix = null) static session获取 + * @method mixed pull(string $name,string $prefix = null) static session获取并删除 + * @method void push(string $key, mixed $value) static 添加数据到一个session数组 + * @method void set(string $name, mixed $value , string $prefix = null) static 设置session数据 + * @method void flash(string $name, mixed $value = null) static session设置 下一次请求有效 + * @method void flush() static 清空当前请求的session数据 + * @method void delete(string $name, string $prefix = null) static 删除session数据 + * @method void clear($prefix = null) static 清空session数据 + * @method void start() static 启动session + * @method void destroy() static 销毁session + * @method void pause() static 暂停session + * @method void regenerate(bool $delete = false) static 重新生成session_id + */ +class Session extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'session'; + } +} diff --git a/thinkphp/library/think/facade/Template.php b/thinkphp/library/think/facade/Template.php new file mode 100644 index 000000000..f91b11821 --- /dev/null +++ b/thinkphp/library/think/facade/Template.php @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Template + * @mixin \think\Template + * @method void assign(mixed $name, mixed $value = '') static 模板变量赋值 + * @method mixed get(string $name = '') static 获取模板变量 + * @method void fetch(string $template, array $vars = [], array $config = []) static 渲染模板文件 + * @method void display(string $content, array $vars = [], array $config = []) static 渲染模板内容 + * @method mixed layout(string $name, string $replace = '') static 设置模板布局 + */ +class Template extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'template'; + } +} diff --git a/thinkphp/library/think/facade/Url.php b/thinkphp/library/think/facade/Url.php new file mode 100644 index 000000000..639591ac8 --- /dev/null +++ b/thinkphp/library/think/facade/Url.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Url + * @mixin \think\Url + * @method string build(string $url = '', mixed $vars = '', mixed $suffix = true, mixed $domain = false) static URL生成 支持路由反射 + * @method void root(string $root) static 指定当前生成URL地址的root + */ +class Url extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'url'; + } +} diff --git a/thinkphp/library/think/facade/Validate.php b/thinkphp/library/think/facade/Validate.php new file mode 100644 index 000000000..a6eec23e4 --- /dev/null +++ b/thinkphp/library/think/facade/Validate.php @@ -0,0 +1,75 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\Validate + * @mixin \think\Validate + * @method \think\Validate make(array $rules = [], array $message = [], array $field = []) static 创建一个验证器类 + * @method \think\Validate rule(mixed $name, mixed $rule = '') static 添加字段验证规则 + * @method void extend(string $type, mixed $callback = null) static 注册扩展验证(类型)规则 + * @method void setTypeMsg(mixed $type, string $msg = null) static 设置验证规则的默认提示信息 + * @method \think\Validate message(mixed $name, string $message = '') static 设置提示信息 + * @method \think\Validate scene(string $name) static 设置验证场景 + * @method bool hasScene(string $name) static 判断是否存在某个验证场景 + * @method \think\Validate batch(bool $batch = true) static 设置批量验证 + * @method \think\Validate only(array $fields) static 指定需要验证的字段列表 + * @method \think\Validate remove(mixed $field, mixed $rule = true) static 移除某个字段的验证规则 + * @method \think\Validate append(mixed $field, mixed $rule = null) static 追加某个字段的验证规则 + * @method bool confirm(mixed $value, mixed $rule, array $data = [], string $field = '') static 验证是否和某个字段的值一致 + * @method bool different(mixed $value, mixed $rule, array $data = []) static 验证是否和某个字段的值是否不同 + * @method bool egt(mixed $value, mixed $rule, array $data = []) static 验证是否大于等于某个值 + * @method bool gt(mixed $value, mixed $rule, array $data = []) static 验证是否大于某个值 + * @method bool elt(mixed $value, mixed $rule, array $data = []) static 验证是否小于等于某个值 + * @method bool lt(mixed $value, mixed $rule, array $data = []) static 验证是否小于某个值 + * @method bool eq(mixed $value, mixed $rule) static 验证是否等于某个值 + * @method bool must(mixed $value, mixed $rule) static 必须验证 + * @method bool is(mixed $value, mixed $rule, array $data = []) static 验证字段值是否为有效格式 + * @method bool ip(mixed $value, mixed $rule) static 验证是否有效IP + * @method bool requireIf(mixed $value, mixed $rule) static 验证某个字段等于某个值的时候必须 + * @method bool requireCallback(mixed $value, mixed $rule,array $data) static 通过回调方法验证某个字段是否必须 + * @method bool requireWith(mixed $value, mixed $rule, array $data) static 验证某个字段有值的情况下必须 + * @method bool filter(mixed $value, mixed $rule) static 使用filter_var方式验证 + * @method bool in(mixed $value, mixed $rule) static 验证是否在范围内 + * @method bool notIn(mixed $value, mixed $rule) static 验证是否不在范围内 + * @method bool between(mixed $value, mixed $rule) static between验证数据 + * @method bool notBetween(mixed $value, mixed $rule) static 使用notbetween验证数据 + * @method bool length(mixed $value, mixed $rule) static 验证数据长度 + * @method bool max(mixed $value, mixed $rule) static 验证数据最大长度 + * @method bool min(mixed $value, mixed $rule) static 验证数据最小长度 + * @method bool after(mixed $value, mixed $rule) static 验证日期 + * @method bool before(mixed $value, mixed $rule) static 验证日期 + * @method bool expire(mixed $value, mixed $rule) static 验证有效期 + * @method bool allowIp(mixed $value, mixed $rule) static 验证IP许可 + * @method bool denyIp(mixed $value, mixed $rule) static 验证IP禁用 + * @method bool regex(mixed $value, mixed $rule) static 使用正则验证数据 + * @method bool token(mixed $value, mixed $rule) static 验证表单令牌 + * @method bool dateFormat(mixed $value, mixed $rule) static 验证时间和日期是否符合指定格式 + * @method bool unique(mixed $value, mixed $rule, array $data = [], string $field = '') static 验证是否唯一 + * @method bool check(array $data, mixed $rules = [], string $scene = '') static 数据自动验证 + * @method mixed getError(mixed $value, mixed $rule) static 获取错误信息 + */ +class Validate extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'validate'; + } + +} diff --git a/thinkphp/library/think/facade/View.php b/thinkphp/library/think/facade/View.php new file mode 100644 index 000000000..084339178 --- /dev/null +++ b/thinkphp/library/think/facade/View.php @@ -0,0 +1,40 @@ + +// +---------------------------------------------------------------------- + +namespace think\facade; + +use think\Facade; + +/** + * @see \think\View + * @mixin \think\View + * @method \think\View init(mixed $engine = [], array $replace = []) static 初始化 + * @method \think\View share(mixed $name, mixed $value = '') static 模板变量静态赋值 + * @method \think\View assign(mixed $name, mixed $value = '') static 模板变量赋值 + * @method \think\View config(mixed $name, mixed $value = '') static 配置模板引擎 + * @method \think\View exists(mixed $name) static 检查模板是否存在 + * @method \think\View filter(Callable $filter) static 视图内容过滤 + * @method \think\View engine(mixed $engine = []) static 设置当前模板解析的引擎 + * @method string fetch(string $template = '', array $vars = [], array $config = [], bool $renderContent = false) static 解析和获取模板内容 + * @method string display(string $content = '', array $vars = [], array $config = []) static 渲染内容输出 + */ +class View extends Facade +{ + /** + * 获取当前Facade对应类名(或者已经绑定的容器对象标识) + * @access protected + * @return string + */ + protected static function getFacadeClass() + { + return 'view'; + } +} diff --git a/thinkphp/library/think/log/driver/File.php b/thinkphp/library/think/log/driver/File.php index d82a5243e..c506105fd 100644 --- a/thinkphp/library/think/log/driver/File.php +++ b/thinkphp/library/think/log/driver/File.php @@ -19,101 +19,269 @@ use think\App; class File { protected $config = [ - 'time_format' => ' c ', + 'time_format' => 'c', + 'single' => false, 'file_size' => 2097152, - 'path' => LOG_PATH, + 'path' => '', 'apart_level' => [], + 'max_files' => 0, + 'json' => false, ]; - protected $writed = []; + protected $app; // 实例化并传入参数 - public function __construct($config = []) + public function __construct(App $app, $config = []) { + $this->app = $app; + if (is_array($config)) { $this->config = array_merge($this->config, $config); } + + if (empty($this->config['path'])) { + $this->config['path'] = $this->app->getRuntimePath() . 'log' . DIRECTORY_SEPARATOR; + } elseif (substr($this->config['path'], -1) != DIRECTORY_SEPARATOR) { + $this->config['path'] .= DIRECTORY_SEPARATOR; + } } /** * 日志写入接口 * @access public - * @param array $log 日志信息 + * @param array $log 日志信息 + * @param bool $append 是否追加请求信息 * @return bool */ - public function save(array $log = []) + public function save(array $log = [], $append = false) { - $cli = IS_CLI ? '_cli' : ''; - $destination = $this->config['path'] . date('Ym') . DS . date('d') . $cli . '.log'; + $destination = $this->getMasterLogFile(); $path = dirname($destination); !is_dir($path) && mkdir($path, 0755, true); - $info = ''; + $info = []; + foreach ($log as $type => $val) { - $level = ''; + foreach ($val as $msg) { if (!is_string($msg)) { $msg = var_export($msg, true); } - $level .= '[ ' . $type . ' ] ' . $msg . "\r\n"; + + $info[$type][] = $this->config['json'] ? $msg : '[ ' . $type . ' ] ' . $msg; } - if (in_array($type, $this->config['apart_level'])) { + + if (!$this->config['json'] && (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level']))) { // 独立记录的日志级别 - $filename = $path . DS . date('d') . '_' . $type . $cli . '.log'; - $this->write($level, $filename, true); - } else { - $info .= $level; + $filename = $this->getApartLevelFile($path, $type); + + $this->write($info[$type], $filename, true, $append); + + unset($info[$type]); } } + if ($info) { - return $this->write($info, $destination); + return $this->write($info, $destination, false, $append); } + return true; } - protected function write($message, $destination, $apart = false) + /** + * 日志写入 + * @access protected + * @param array $message 日志信息 + * @param string $destination 日志文件 + * @param bool $apart 是否独立文件写入 + * @param bool $append 是否追加请求信息 + * @return bool + */ + protected function write($message, $destination, $apart = false, $append = false) { - //检测日志文件大小,超过配置大小则备份日志文件重新生成 - if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { - rename($destination, dirname($destination) . DS . time() . '-' . basename($destination)); - $this->writed[$destination] = false; - } + // 检测日志文件大小,超过配置大小则备份日志文件重新生成 + $this->checkLogSize($destination); - if (empty($this->writed[$destination]) && !IS_CLI) { - if (App::$debug && !$apart) { - // 获取基本信息 - if (isset($_SERVER['HTTP_HOST'])) { - $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; - } else { - $current_uri = "cmd:" . implode(' ', $_SERVER['argv']); - } + // 日志信息封装 + $info['timestamp'] = date($this->config['time_format']); - $runtime = round(microtime(true) - THINK_START_TIME, 10); - $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; - $time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]'; - $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); - $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; - $file_load = ' [文件加载:' . count(get_included_files()) . ']'; - - $message = '[ info ] ' . $current_uri . $time_str . $memory_str . $file_load . "\r\n" . $message; + foreach ($message as $type => $msg) { + $msg = is_array($msg) ? implode("\r\n", $msg) : $msg; + if (PHP_SAPI == 'cli') { + $info['msg'] = $msg; + $info['type'] = $type; + } else { + $info[$type] = $msg; } - $now = date($this->config['time_format']); - $server = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '0.0.0.0'; - $remote = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'; - $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI'; - $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; - $message = "---------------------------------------------------------------\r\n[{$now}] {$server} {$remote} {$method} {$uri}\r\n" . $message; - - $this->writed[$destination] = true; } - if (IS_CLI) { - $now = date($this->config['time_format']); - $message = "[{$now}]" . $message; + if (PHP_SAPI == 'cli') { + $message = $this->parseCliLog($info); + } else { + // 添加调试日志 + $this->getDebugLog($info, $append, $apart); + + $message = $this->parseLog($info); } return error_log($message, 3, $destination); } + /** + * 获取主日志文件名 + * @access public + * @return string + */ + protected function getMasterLogFile() + { + if ($this->config['max_files']) { + $files = glob($this->config['path'] . '*.log'); + + try { + if (count($files) > $this->config['max_files']) { + unlink($files[0]); + } + } catch (\Exception $e) { + } + } + + $cli = PHP_SAPI == 'cli' ? '_cli' : ''; + + if ($this->config['single']) { + $name = is_string($this->config['single']) ? $this->config['single'] : 'single'; + + $destination = $this->config['path'] . $name . $cli . '.log'; + } else { + if ($this->config['max_files']) { + $filename = date('Ymd') . $cli . '.log'; + } else { + $filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log'; + } + + $destination = $this->config['path'] . $filename; + } + + return $destination; + } + + /** + * 获取独立日志文件名 + * @access public + * @param string $path 日志目录 + * @param string $type 日志类型 + * @return string + */ + protected function getApartLevelFile($path, $type) + { + $cli = PHP_SAPI == 'cli' ? '_cli' : ''; + + if ($this->config['single']) { + $name = is_string($this->config['single']) ? $this->config['single'] : 'single'; + } elseif ($this->config['max_files']) { + $name = date('Ymd'); + } else { + $name = date('d'); + } + + return $path . DIRECTORY_SEPARATOR . $name . '_' . $type . $cli . '.log'; + } + + /** + * 检查日志文件大小并自动生成备份文件 + * @access protected + * @param string $destination 日志文件 + * @return void + */ + protected function checkLogSize($destination) + { + if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { + try { + rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination)); + } catch (\Exception $e) { + } + } + } + + /** + * CLI日志解析 + * @access protected + * @param array $info 日志信息 + * @return string + */ + protected function parseCliLog($info) + { + if ($this->config['json']) { + $message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n"; + } else { + $now = $info['timestamp']; + unset($info['timestamp']); + + $message = implode("\r\n", $info); + + $message = "[{$now}]" . $message . "\r\n"; + } + + return $message; + } + + /** + * 解析日志 + * @access protected + * @param array $info 日志信息 + * @return string + */ + protected function parseLog($info) + { + $requestInfo = [ + 'ip' => $this->app['request']->ip(), + 'method' => $this->app['request']->method(), + 'host' => $this->app['request']->host(), + 'uri' => $this->app['request']->url(), + ]; + + if ($this->config['json']) { + $info = $requestInfo + $info; + return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n"; + } + + array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}"); + unset($info['timestamp']); + + return implode("\r\n", $info) . "\r\n"; + } + + protected function getDebugLog(&$info, $append, $apart) + { + if ($this->app->isDebug() && $append) { + + if ($this->config['json']) { + // 获取基本信息 + $runtime = round(microtime(true) - $this->app->getBeginTime(), 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + + $memory_use = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2); + + $info = [ + 'runtime' => number_format($runtime, 6) . 's', + 'reqs' => $reqs . 'req/s', + 'memory' => $memory_use . 'kb', + 'file' => count(get_included_files()), + ] + $info; + + } elseif (!$apart) { + // 增加额外的调试信息 + $runtime = round(microtime(true) - $this->app->getBeginTime(), 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + + $memory_use = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2); + + $time_str = '[运行时间:' . number_format($runtime, 6) . 's] [吞吐率:' . $reqs . 'req/s]'; + $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; + $file_load = ' [文件加载:' . count(get_included_files()) . ']'; + + array_unshift($info, $time_str . $memory_str . $file_load); + } + } + } } diff --git a/thinkphp/library/think/log/driver/Socket.php b/thinkphp/library/think/log/driver/Socket.php index d30bba304..5e4f8bfd8 100644 --- a/thinkphp/library/think/log/driver/Socket.php +++ b/thinkphp/library/think/log/driver/Socket.php @@ -30,6 +30,8 @@ class Socket 'force_client_ids' => [], // 限制允许读取日志的client_id 'allow_client_ids' => [], + //输出到浏览器默认展开的日志级别 + 'expand_level' => ['debug'], ]; protected $css = [ @@ -41,14 +43,17 @@ class Socket ]; protected $allowForceClientIds = []; //配置强制推送且被授权的client_id + protected $app; /** - * 构造函数 - * @param array $config 缓存参数 + * 架构函数 * @access public + * @param array $config 缓存参数 */ - public function __construct(array $config = []) + public function __construct(App $app, array $config = []) { + $this->app = $app; + if (!empty($config)) { $this->config = array_merge($this->config, $config); } @@ -57,20 +62,22 @@ class Socket /** * 调试输出接口 * @access public - * @param array $log 日志信息 + * @param array $log 日志信息 * @return bool */ - public function save(array $log = []) + public function save(array $log = [], $append = false) { if (!$this->check()) { return false; } + $trace = []; - if (App::$debug) { - $runtime = round(microtime(true) - THINK_START_TIME, 10); + + if ($this->app->isDebug()) { + $runtime = round(microtime(true) - $this->app->getBeginTime(), 10); $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; $time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]'; - $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + $memory_use = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2); $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; $file_load = ' [文件加载:' . count(get_included_files()) . ']'; @@ -79,6 +86,7 @@ class Socket } else { $current_uri = 'cmd:' . implode(' ', $_SERVER['argv']); } + // 基本信息 $trace[] = [ 'type' => 'group', @@ -89,10 +97,11 @@ class Socket foreach ($log as $type => $val) { $trace[] = [ - 'type' => 'groupCollapsed', + 'type' => in_array($type, $this->config['expand_level']) ? 'group' : 'groupCollapsed', 'msg' => '[ ' . $type . ' ]', 'css' => isset($this->css[$type]) ? $this->css[$type] : '', ]; + foreach ($val as $msg) { if (!is_string($msg)) { $msg = var_export($msg, true); @@ -103,6 +112,7 @@ class Socket 'css' => '', ]; } + $trace[] = [ 'type' => 'groupEnd', 'msg' => '', @@ -116,11 +126,13 @@ class Socket 'msg' => '[ file ]', 'css' => '', ]; + $trace[] = [ 'type' => 'log', 'msg' => implode("\n", get_included_files()), 'css' => '', ]; + $trace[] = [ 'type' => 'groupEnd', 'msg' => '', @@ -135,6 +147,7 @@ class Socket ]; $tabid = $this->getClientArg('tabid'); + if (!$client_id = $this->getClientArg('client_id')) { $client_id = ''; } @@ -148,16 +161,18 @@ class Socket } else { $this->sendToClient($tabid, $client_id, $trace, ''); } + return true; } /** * 发送给指定客户端 + * @access protected * @author Zjmainstay - * @param $tabid - * @param $client_id - * @param $logs - * @param $force_client_id + * @param $tabid + * @param $client_id + * @param $logs + * @param $force_client_id */ protected function sendToClient($tabid, $client_id, $logs, $force_client_id) { @@ -167,20 +182,25 @@ class Socket 'logs' => $logs, 'force_client_id' => $force_client_id, ]; + $msg = @json_encode($logs); $address = '/' . $client_id; //将client_id作为地址, server端通过地址判断将日志发布给谁 + $this->send($this->config['host'], $msg, $address); } protected function check() { $tabid = $this->getClientArg('tabid'); + //是否记录日志的检查 if (!$tabid && !$this->config['force_client_ids']) { return false; } + //用户认证 $allow_client_ids = $this->config['allow_client_ids']; + if (!empty($allow_client_ids)) { //通过数组交集得出授权强制推送的client_id $this->allowForceClientIds = array_intersect($allow_client_ids, $this->config['force_client_ids']); @@ -195,6 +215,7 @@ class Socket } else { $this->allowForceClientIds = $this->config['force_client_ids']; } + return true; } @@ -211,6 +232,7 @@ class Socket if (!isset($_SERVER[$key])) { return; } + if (empty($args)) { if (!preg_match('/SocketLog\((.*?)\)/', $_SERVER[$key], $match)) { $args = ['tabid' => null]; @@ -218,32 +240,39 @@ class Socket } parse_str($match[1], $args); } + if (isset($args[$name])) { return $args[$name]; } + return; } /** - * @param string $host - $host of socket server - * @param string $message - 发送的消息 - * @param string $address - 地址 + * @access protected + * @param string $host - $host of socket server + * @param string $message - 发送的消息 + * @param string $address - 地址 * @return bool */ protected function send($host, $message = '', $address = '/') { $url = 'http://' . $host . ':' . $this->port . $address; $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $message); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 10); + $headers = [ "Content-Type: application/json;charset=UTF-8", ]; + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header + return curl_exec($ch); } diff --git a/thinkphp/library/think/model/Collection.php b/thinkphp/library/think/model/Collection.php index 4e4bb4dc6..09b61a803 100644 --- a/thinkphp/library/think/model/Collection.php +++ b/thinkphp/library/think/model/Collection.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -16,38 +16,25 @@ use think\Model; class Collection extends BaseCollection { - /** - * 返回数组中指定的一列 - * @param string $column_key - * @param string|null $index_key - * @return array - */ - public function column($column_key, $index_key = null) - { - if (function_exists('array_column')) { - return array_column($this->toArray(), $column_key, $index_key); - } - return parent::column($column_key, $index_key); - } - /** * 延迟预载入关联查询 * @access public - * @param mixed $relation 关联 + * @param mixed $relation 关联 * @return $this */ public function load($relation) { $item = current($this->items); $item->eagerlyResultSet($this->items, $relation); + return $this; } /** * 设置需要隐藏的输出属性 * @access public - * @param array $hidden 属性列表 - * @param bool $override 是否覆盖 + * @param array $hidden 属性列表 + * @param bool $override 是否覆盖 * @return $this */ public function hidden($hidden = [], $override = false) @@ -56,13 +43,15 @@ class Collection extends BaseCollection /** @var Model $model */ $model->hidden($hidden, $override); }); + return $this; } /** * 设置需要输出的属性 - * @param array $visible - * @param bool $override 是否覆盖 + * @access public + * @param array $visible + * @param bool $override 是否覆盖 * @return $this */ public function visible($visible = [], $override = false) @@ -71,23 +60,41 @@ class Collection extends BaseCollection /** @var Model $model */ $model->visible($visible, $override); }); + return $this; } /** * 设置需要追加的输出属性 * @access public - * @param array $append 属性列表 - * @param bool $override 是否覆盖 + * @param array $append 属性列表 + * @param bool $override 是否覆盖 * @return $this */ public function append($append = [], $override = false) { $this->each(function ($model) use ($append, $override) { /** @var Model $model */ - $model->append($append, $override); + $model && $model->append($append, $override); }); + return $this; } + /** + * 设置数据字段获取器 + * @access public + * @param string|array $name 字段名 + * @param callable $callback 闭包获取器 + * @return $this + */ + public function withAttr($name, $callback = null) + { + $this->each(function ($model) use ($name, $callback) { + /** @var Model $model */ + $model && $model->withAttribute($name, $callback); + }); + + return $this; + } } diff --git a/thinkphp/library/think/model/Merge.php b/thinkphp/library/think/model/Merge.php deleted file mode 100644 index d944979e1..000000000 --- a/thinkphp/library/think/model/Merge.php +++ /dev/null @@ -1,322 +0,0 @@ - -// +---------------------------------------------------------------------- - -namespace think\model; - -use think\Db; -use think\db\Query; -use think\Model; - -class Merge extends Model -{ - - protected $relationModel = []; // HAS ONE 关联的模型列表 - protected $fk = ''; // 外键名 默认为主表名_id - protected $mapFields = []; // 需要处理的模型映射字段,避免混淆 array( id => 'user.id' ) - - /** - * 构造函数 - * @access public - * @param array|object $data 数据 - */ - public function __construct($data = []) - { - parent::__construct($data); - - // 设置默认外键名 仅支持单一外键 - if (empty($this->fk)) { - $this->fk = strtolower($this->name) . '_id'; - } - } - - /** - * 查找单条记录 - * @access public - * @param mixed $data 主键值或者查询条件(闭包) - * @param string|array $with 关联预查询 - * @param bool $cache 是否缓存 - * @return \think\Model - */ - public static function get($data = null, $with = [], $cache = false) - { - $query = self::parseQuery($data, $with, $cache); - $query = self::attachQuery($query); - return $query->find($data); - } - - /** - * 附加查询表达式 - * @access protected - * @param \think\db\Query $query 查询对象 - * @return \think\db\Query - */ - protected static function attachQuery($query) - { - $class = new static(); - $master = $class->name; - $fields = self::getModelField($query, $master, '', $class->mapFields, $class->field); - $query->alias($master)->field($fields); - - foreach ($class->relationModel as $key => $model) { - $name = is_int($key) ? $model : $key; - $table = is_int($key) ? $query->getTable($name) : $model; - $query->join($table . ' ' . $name, $name . '.' . $class->fk . '=' . $master . '.' . $class->getPk()); - $fields = self::getModelField($query, $name, $table, $class->mapFields, $class->field); - $query->field($fields); - } - return $query; - } - - /** - * 获取关联模型的字段 并解决混淆 - * @access protected - * @param \think\db\Query $query 查询对象 - * @param string $name 模型名称 - * @param string $table 关联表名称 - * @param array $map 字段映射 - * @param array $fields 查询字段 - * @return array - */ - protected static function getModelField($query, $name, $table = '', $map = [], $fields = []) - { - // 获取模型的字段信息 - $fields = $fields ?: $query->getTableInfo($table, 'fields'); - $array = []; - foreach ($fields as $field) { - if ($key = array_search($name . '.' . $field, $map)) { - // 需要处理映射字段 - $array[] = $name . '.' . $field . ' AS ' . $key; - } else { - $array[] = $field; - } - } - return $array; - } - - /** - * 查找所有记录 - * @access public - * @param mixed $data 主键列表或者查询条件(闭包) - * @param array|string $with 关联预查询 - * @param bool $cache - * @return array|false|string - */ - public static function all($data = null, $with = [], $cache = false) - { - $query = self::parseQuery($data, $with, $cache); - $query = self::attachQuery($query); - return $query->select($data); - } - - /** - * 处理写入的模型数据 - * @access public - * @param string $model 模型名称 - * @param array $data 数据 - * @return array - */ - protected function parseData($model, $data) - { - $item = []; - foreach ($data as $key => $val) { - if ($this->fk != $key && array_key_exists($key, $this->mapFields)) { - list($name, $key) = explode('.', $this->mapFields[$key]); - if ($model == $name) { - $item[$key] = $val; - } - } else { - $item[$key] = $val; - } - } - return $item; - } - - /** - * 保存模型数据 以及关联数据 - * @access public - * @param mixed $data 数据 - * @param array $where 更新条件 - * @param string $sequence 自增序列名 - * @return false|int - * @throws \Exception - */ - public function save($data = [], $where = [], $sequence = null) - { - if (!empty($data)) { - // 数据自动验证 - if (!$this->validateData($data)) { - return false; - } - // 数据对象赋值 - foreach ($data as $key => $value) { - $this->setAttr($key, $value, $data); - } - if (!empty($where)) { - $this->isUpdate = true; - } - } - - // 数据自动完成 - $this->autoCompleteData($this->auto); - - // 自动写入更新时间 - if ($this->autoWriteTimestamp && $this->updateTime && !isset($this->data[$this->updateTime])) { - $this->setAttr($this->updateTime, null); - } - - // 事件回调 - if (false === $this->trigger('before_write', $this)) { - return false; - } - - $db = $this->db(); - $db->startTrans(); - $pk = $this->getPk(); - try { - if ($this->isUpdate) { - // 自动写入 - $this->autoCompleteData($this->update); - - if (false === $this->trigger('before_update', $this)) { - return false; - } - - if (empty($where) && !empty($this->updateWhere)) { - $where = $this->updateWhere; - } - - // 获取有更新的数据 - $data = $this->getChangedData(); - // 保留主键数据 - foreach ($this->data as $key => $val) { - if ($this->isPk($key)) { - $data[$key] = $val; - } - } - // 处理模型数据 - $data = $this->parseData($this->name, $data); - if (is_string($pk) && isset($data[$pk])) { - if (!isset($where[$pk])) { - unset($where); - $where[$pk] = $data[$pk]; - } - unset($data[$pk]); - } - // 写入主表数据 - $result = $db->strict(false)->where($where)->update($data); - - // 写入附表数据 - foreach ($this->relationModel as $key => $model) { - $name = is_int($key) ? $model : $key; - $table = is_int($key) ? $db->getTable($model) : $model; - // 处理关联模型数据 - $data = $this->parseData($name, $data); - if (Db::table($table)->strict(false)->where($this->fk, $this->data[$this->getPk()])->update($data)) { - $result = 1; - } - } - - // 新增回调 - $this->trigger('after_update', $this); - } else { - // 自动写入 - $this->autoCompleteData($this->insert); - - // 自动写入创建时间 - if ($this->autoWriteTimestamp && $this->createTime && !isset($this->data[$this->createTime])) { - $this->setAttr($this->createTime, null); - } - - if (false === $this->trigger('before_insert', $this)) { - return false; - } - - // 处理模型数据 - $data = $this->parseData($this->name, $this->data); - // 写入主表数据 - $result = $db->name($this->name)->strict(false)->insert($data); - if ($result) { - $insertId = $db->getLastInsID($sequence); - // 写入外键数据 - if ($insertId) { - if (is_string($pk)) { - $this->data[$pk] = $insertId; - } - $this->data[$this->fk] = $insertId; - } - - // 写入附表数据 - $source = $this->data; - if ($insertId && is_string($pk) && isset($source[$pk]) && $this->fk != $pk) { - unset($source[$pk]); - } - foreach ($this->relationModel as $key => $model) { - $name = is_int($key) ? $model : $key; - $table = is_int($key) ? $db->getTable($model) : $model; - // 处理关联模型数据 - $data = $this->parseData($name, $source); - Db::table($table)->strict(false)->insert($data); - } - } - // 标记为更新 - $this->isUpdate = true; - // 新增回调 - $this->trigger('after_insert', $this); - } - $db->commit(); - // 写入回调 - $this->trigger('after_write', $this); - - $this->origin = $this->data; - return $result; - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } - } - - /** - * 删除当前的记录 并删除关联数据 - * @access public - * @return int - * @throws \Exception - */ - public function delete() - { - if (false === $this->trigger('before_delete', $this)) { - return false; - } - - $db = $this->db(); - $db->startTrans(); - try { - $result = $db->delete($this->data); - if ($result) { - // 获取主键数据 - $pk = $this->data[$this->getPk()]; - - // 删除关联数据 - foreach ($this->relationModel as $key => $model) { - $table = is_int($key) ? $db->getTable($model) : $model; - $query = new Query; - $query->table($table)->where($this->fk, $pk)->delete(); - } - } - $this->trigger('after_delete', $this); - $db->commit(); - return $result; - } catch (\Exception $e) { - $db->rollback(); - throw $e; - } - } - -} diff --git a/thinkphp/library/think/model/Pivot.php b/thinkphp/library/think/model/Pivot.php index 06c6b1817..a3a395e3f 100644 --- a/thinkphp/library/think/model/Pivot.php +++ b/thinkphp/library/think/model/Pivot.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -24,11 +24,11 @@ class Pivot extends Model /** * 架构函数 * @access public - * @param Model $parent 上级模型 - * @param array|object $data 数据 - * @param string $table 中间数据表名 + * @param array|object $data 数据 + * @param Model $parent 上级模型 + * @param string $table 中间数据表名 */ - public function __construct(Model $parent, $data = [], $table = '') + public function __construct($data = [], Model $parent = null, $table = '') { $this->parent = $parent; @@ -37,8 +37,6 @@ class Pivot extends Model } parent::__construct($data); - - $this->class = $this->name; } } diff --git a/thinkphp/library/think/model/Relation.php b/thinkphp/library/think/model/Relation.php index 4869e5261..c2b9adc7b 100644 --- a/thinkphp/library/think/model/Relation.php +++ b/thinkphp/library/think/model/Relation.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -35,6 +35,8 @@ abstract class Relation protected $localKey; // 基础查询 protected $baseQuery; + // 是否为自关联 + protected $selfRelation; /** * 获取关联的所属模型 @@ -47,29 +49,41 @@ abstract class Relation } /** - * 获取当前的关联模型类 + * 获取当前的关联模型类的实例 * @access public - * @return string + * @return Model */ public function getModel() { - return $this->model; + return $this->query->getModel(); } /** - * 获取关联的查询对象 + * 设置当前关联为自关联 * @access public - * @return Query + * @param bool $self 是否自关联 + * @return $this */ - public function getQuery() + public function selfRelation($self = true) { - return $this->query; + $this->selfRelation = $self; + return $this; + } + + /** + * 当前关联是否为自关联 + * @access public + * @return bool + */ + public function isSelfRelation() + { + return $this->selfRelation; } /** * 封装关联数据集 * @access public - * @param array $resultSet 数据集 + * @param array $resultSet 数据集 * @return mixed */ protected function resultSetBuild($resultSet) @@ -103,12 +117,38 @@ abstract class Relation return $fields; } + protected function getQueryWhere(&$where, $relation) + { + foreach ($where as $key => &$val) { + if (is_string($key)) { + $where[] = [false === strpos($key, '.') ? $relation . '.' . $key : $key, '=', $val]; + unset($where[$key]); + } elseif (isset($val[0]) && false === strpos($val[0], '.')) { + $val[0] = $relation . '.' . $val[0]; + } + } + } + + /** + * 删除记录 + * @access public + * @param mixed $data 表达式 true 表示强制删除 + * @return int + * @throws Exception + * @throws PDOException + */ + public function delete($data = null) + { + return $this->query->delete($data); + } + /** * 执行基础查询(仅执行一次) * @access protected * @return void */ - abstract protected function baseQuery(); + protected function baseQuery() + {} public function __call($method, $args) { @@ -116,13 +156,9 @@ abstract class Relation // 执行基础查询 $this->baseQuery(); - $result = call_user_func_array([$this->query, $method], $args); - if ($result instanceof Query) { - return $this; - } else { - $this->baseQuery = false; - return $result; - } + $result = call_user_func_array([$this->query->getModel(), $method], $args); + + return $result === $this->query && !in_array(strtolower($method), ['fetchsql', 'fetchpdo']) ? $this : $result; } else { throw new Exception('method not exists:' . __CLASS__ . '->' . $method); } diff --git a/thinkphp/library/think/model/concern/Attribute.php b/thinkphp/library/think/model/concern/Attribute.php new file mode 100644 index 000000000..0605478a0 --- /dev/null +++ b/thinkphp/library/think/model/concern/Attribute.php @@ -0,0 +1,656 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\concern; + +use InvalidArgumentException; +use think\db\Expression; +use think\Exception; +use think\Loader; +use think\model\Relation; + +trait Attribute +{ + /** + * 数据表主键 复合主键使用数组定义 + * @var string|array + */ + protected $pk = 'id'; + + /** + * 数据表字段信息 留空则自动获取 + * @var array + */ + protected $field = []; + + /** + * JSON数据表字段 + * @var array + */ + protected $json = []; + + /** + * JSON数据取出是否需要转换为数组 + * @var bool + */ + protected $jsonAssoc = false; + + /** + * JSON数据表字段类型 + * @var array + */ + protected $jsonType = []; + + /** + * 数据表废弃字段 + * @var array + */ + protected $disuse = []; + + /** + * 数据表只读字段 + * @var array + */ + protected $readonly = []; + + /** + * 数据表字段类型 + * @var array + */ + protected $type = []; + + /** + * 当前模型数据 + * @var array + */ + private $data = []; + + /** + * 修改器执行记录 + * @var array + */ + private $set = []; + + /** + * 原始数据 + * @var array + */ + private $origin = []; + + /** + * 动态获取器 + * @var array + */ + private $withAttr = []; + + /** + * 获取模型对象的主键 + * @access public + * @return string|array + */ + public function getPk() + { + return $this->pk; + } + + /** + * 判断一个字段名是否为主键字段 + * @access public + * @param string $key 名称 + * @return bool + */ + protected function isPk($key) + { + $pk = $this->getPk(); + if (is_string($pk) && $pk == $key) { + return true; + } elseif (is_array($pk) && in_array($key, $pk)) { + return true; + } + + return false; + } + + /** + * 获取模型对象的主键值 + * @access public + * @return integer + */ + public function getKey() + { + $pk = $this->getPk(); + if (is_string($pk) && array_key_exists($pk, $this->data)) { + return $this->data[$pk]; + } + + return; + } + + /** + * 设置允许写入的字段 + * @access public + * @param array|string|true $field 允许写入的字段 如果为true只允许写入数据表字段 + * @return $this + */ + public function allowField($field) + { + if (is_string($field)) { + $field = explode(',', $field); + } + + $this->field = $field; + + return $this; + } + + /** + * 设置只读字段 + * @access public + * @param array|string $field 只读字段 + * @return $this + */ + public function readonly($field) + { + if (is_string($field)) { + $field = explode(',', $field); + } + + $this->readonly = $field; + + return $this; + } + + /** + * 设置数据对象值 + * @access public + * @param mixed $data 数据或者属性名 + * @param mixed $value 值 + * @return $this + */ + public function data($data, $value = null) + { + if (is_string($data)) { + $this->data[$data] = $value; + return $this; + } + + // 清空数据 + $this->data = []; + + if (is_object($data)) { + $data = get_object_vars($data); + } + + if ($this->disuse) { + // 废弃字段 + foreach ((array) $this->disuse as $key) { + if (array_key_exists($key, $data)) { + unset($data[$key]); + } + } + } + + if (true === $value) { + // 数据对象赋值 + foreach ($data as $key => $value) { + $this->setAttr($key, $value, $data); + } + } elseif (is_array($value)) { + foreach ($value as $name) { + if (isset($data[$name])) { + $this->data[$name] = $data[$name]; + } + } + } else { + $this->data = $data; + } + + return $this; + } + + /** + * 批量设置数据对象值 + * @access public + * @param mixed $data 数据 + * @param bool $set 是否需要进行数据处理 + * @return $this + */ + public function appendData($data, $set = false) + { + if ($set) { + // 进行数据处理 + foreach ($data as $key => $value) { + $this->setAttr($key, $value, $data); + } + } else { + if (is_object($data)) { + $data = get_object_vars($data); + } + + $this->data = array_merge($this->data, $data); + } + + return $this; + } + + /** + * 获取对象原始数据 如果不存在指定字段返回null + * @access public + * @param string $name 字段名 留空获取全部 + * @return mixed + */ + public function getOrigin($name = null) + { + if (is_null($name)) { + return $this->origin; + } + return array_key_exists($name, $this->origin) ? $this->origin[$name] : null; + } + + /** + * 获取对象原始数据 如果不存在指定字段返回false + * @access public + * @param string $name 字段名 留空获取全部 + * @return mixed + * @throws InvalidArgumentException + */ + public function getData($name = null) + { + if (is_null($name)) { + return $this->data; + } elseif (array_key_exists($name, $this->data)) { + return $this->data[$name]; + } elseif (array_key_exists($name, $this->relation)) { + return $this->relation[$name]; + } + throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name); + } + + /** + * 获取变化的数据 并排除只读数据 + * @access public + * @return array + */ + public function getChangedData() + { + if ($this->force) { + $data = $this->data; + } else { + $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) { + if ((empty($a) || empty($b)) && $a !== $b) { + return 1; + } + + return is_object($a) || $a != $b ? 1 : 0; + }); + } + + if (!empty($this->readonly)) { + // 只读字段不允许更新 + foreach ($this->readonly as $key => $field) { + if (isset($data[$field])) { + unset($data[$field]); + } + } + } + + return $data; + } + + /** + * 修改器 设置数据对象值 + * @access public + * @param string $name 属性名 + * @param mixed $value 属性值 + * @param array $data 数据 + * @return void + */ + public function setAttr($name, $value, $data = []) + { + if (isset($this->set[$name])) { + return; + } + + if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { + // 自动写入的时间戳字段 + $value = $this->autoWriteTimestamp($name); + } else { + // 检测修改器 + $method = 'set' . Loader::parseName($name, 1) . 'Attr'; + + if (method_exists($this, $method)) { + $value = $this->$method($value, array_merge($this->data, $data)); + + $this->set[$name] = true; + } elseif (isset($this->type[$name])) { + // 类型转换 + $value = $this->writeTransform($value, $this->type[$name]); + } + } + + // 设置数据对象属性 + $this->data[$name] = $value; + } + + /** + * 是否需要自动写入时间字段 + * @access public + * @param bool $auto + * @return $this + */ + public function isAutoWriteTimestamp($auto) + { + $this->autoWriteTimestamp = $auto; + + return $this; + } + + /** + * 自动写入时间戳 + * @access protected + * @param string $name 时间戳字段 + * @return mixed + */ + protected function autoWriteTimestamp($name) + { + if (isset($this->type[$name])) { + $type = $this->type[$name]; + + if (strpos($type, ':')) { + list($type, $param) = explode(':', $type, 2); + } + + switch ($type) { + case 'datetime': + case 'date': + $format = !empty($param) ? $param : $this->dateFormat; + $format .= strpos($format, 'u') || false !== strpos($format, '\\') ? '' : '.u'; + $value = $this->formatDateTime($format); + break; + case 'timestamp': + case 'integer': + default: + $value = time(); + break; + } + } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ + 'datetime', + 'date', + 'timestamp', + ])) { + $format = strpos($this->dateFormat, 'u') || false !== strpos($this->dateFormat, '\\') ? '' : '.u'; + $value = $this->formatDateTime($this->dateFormat . $format); + } else { + $value = time(); + } + + return $value; + } + + /** + * 数据写入 类型转换 + * @access protected + * @param mixed $value 值 + * @param string|array $type 要转换的类型 + * @return mixed + */ + protected function writeTransform($value, $type) + { + if (is_null($value)) { + return; + } + + if ($value instanceof Expression) { + return $value; + } + + if (is_array($type)) { + list($type, $param) = $type; + } elseif (strpos($type, ':')) { + list($type, $param) = explode(':', $type, 2); + } + + switch ($type) { + case 'integer': + $value = (int) $value; + break; + case 'float': + if (empty($param)) { + $value = (float) $value; + } else { + $value = (float) number_format($value, $param, '.', ''); + } + break; + case 'boolean': + $value = (bool) $value; + break; + case 'timestamp': + if (!is_numeric($value)) { + $value = strtotime($value); + } + break; + case 'datetime': + $format = !empty($param) ? $param : $this->dateFormat; + $value = is_numeric($value) ? $value : strtotime($value); + $value = $this->formatDateTime($format, $value); + break; + case 'object': + if (is_object($value)) { + $value = json_encode($value, JSON_FORCE_OBJECT); + } + break; + case 'array': + $value = (array) $value; + case 'json': + $option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE; + $value = json_encode($value, $option); + break; + case 'serialize': + $value = serialize($value); + break; + } + + return $value; + } + + /** + * 获取器 获取数据对象的值 + * @access public + * @param string $name 名称 + * @param array $item 数据 + * @return mixed + * @throws InvalidArgumentException + */ + public function getAttr($name, &$item = null) + { + try { + $notFound = false; + $value = $this->getData($name); + } catch (InvalidArgumentException $e) { + $notFound = true; + $value = null; + } + + // 检测属性获取器 + $fieldName = Loader::parseName($name); + $method = 'get' . Loader::parseName($name, 1) . 'Attr'; + + if (isset($this->withAttr[$fieldName])) { + if ($notFound && $relation = $this->isRelationAttr($name)) { + $modelRelation = $this->$relation(); + $value = $this->getRelationData($modelRelation); + } + + $closure = $this->withAttr[$fieldName]; + $value = $closure($value, $this->data); + } elseif (method_exists($this, $method)) { + if ($notFound && $relation = $this->isRelationAttr($name)) { + $modelRelation = $this->$relation(); + $value = $this->getRelationData($modelRelation); + } + + $value = $this->$method($value, $this->data); + } elseif (isset($this->type[$name])) { + // 类型转换 + $value = $this->readTransform($value, $this->type[$name]); + } elseif ($this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { + if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ + 'datetime', + 'date', + 'timestamp', + ])) { + $value = $this->formatDateTime($this->dateFormat, $value); + } else { + $value = $this->formatDateTime($this->dateFormat, $value, true); + } + } elseif ($notFound) { + $value = $this->getRelationAttribute($name, $item); + } + + return $value; + } + + /** + * 获取关联属性值 + * @access protected + * @param string $name 属性名 + * @param array $item 数据 + * @return mixed + */ + protected function getRelationAttribute($name, &$item) + { + $relation = $this->isRelationAttr($name); + + if ($relation) { + $modelRelation = $this->$relation(); + if ($modelRelation instanceof Relation) { + $value = $this->getRelationData($modelRelation); + + if ($item && method_exists($modelRelation, 'getBindAttr') && $bindAttr = $modelRelation->getBindAttr()) { + + foreach ($bindAttr as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + + if (isset($item[$key])) { + throw new Exception('bind attr has exists:' . $key); + } else { + $item[$key] = $value ? $value->getAttr($attr) : null; + } + } + + return false; + } + + // 保存关联对象值 + $this->relation[$name] = $value; + + return $value; + } + } + + throw new InvalidArgumentException('property not exists:' . static::class . '->' . $name); + } + + /** + * 数据读取 类型转换 + * @access protected + * @param mixed $value 值 + * @param string|array $type 要转换的类型 + * @return mixed + */ + protected function readTransform($value, $type) + { + if (is_null($value)) { + return; + } + + if (is_array($type)) { + list($type, $param) = $type; + } elseif (strpos($type, ':')) { + list($type, $param) = explode(':', $type, 2); + } + + switch ($type) { + case 'integer': + $value = (int) $value; + break; + case 'float': + if (empty($param)) { + $value = (float) $value; + } else { + $value = (float) number_format($value, $param, '.', ''); + } + break; + case 'boolean': + $value = (bool) $value; + break; + case 'timestamp': + if (!is_null($value)) { + $format = !empty($param) ? $param : $this->dateFormat; + $value = $this->formatDateTime($format, $value, true); + } + break; + case 'datetime': + if (!is_null($value)) { + $format = !empty($param) ? $param : $this->dateFormat; + $value = $this->formatDateTime($format, $value); + } + break; + case 'json': + $value = json_decode($value, true); + break; + case 'array': + $value = empty($value) ? [] : json_decode($value, true); + break; + case 'object': + $value = empty($value) ? new \stdClass() : json_decode($value); + break; + case 'serialize': + try { + $value = unserialize($value); + } catch (\Exception $e) { + $value = null; + } + break; + default: + if (false !== strpos($type, '\\')) { + // 对象类型 + $value = new $type($value); + } + } + + return $value; + } + + /** + * 设置数据字段获取器 + * @access public + * @param string|array $name 字段名 + * @param callable $callback 闭包获取器 + * @return $this + */ + public function withAttribute($name, $callback = null) + { + if (is_array($name)) { + foreach ($name as $key => $val) { + $key = Loader::parseName($key); + + $this->withAttr[$key] = $val; + } + } else { + $name = Loader::parseName($name); + + $this->withAttr[$name] = $callback; + } + + return $this; + } +} diff --git a/thinkphp/library/think/model/concern/Conversion.php b/thinkphp/library/think/model/concern/Conversion.php new file mode 100644 index 000000000..b88528adb --- /dev/null +++ b/thinkphp/library/think/model/concern/Conversion.php @@ -0,0 +1,288 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\concern; + +use think\Collection; +use think\Exception; +use think\Loader; +use think\Model; +use think\model\Collection as ModelCollection; + +/** + * 模型数据转换处理 + */ +trait Conversion +{ + /** + * 数据输出显示的属性 + * @var array + */ + protected $visible = []; + + /** + * 数据输出隐藏的属性 + * @var array + */ + protected $hidden = []; + + /** + * 数据输出需要追加的属性 + * @var array + */ + protected $append = []; + + /** + * 数据集对象名 + * @var string + */ + protected $resultSetType; + + /** + * 设置需要附加的输出属性 + * @access public + * @param array $append 属性列表 + * @param bool $override 是否覆盖 + * @return $this + */ + public function append(array $append = [], $override = false) + { + $this->append = $override ? $append : array_merge($this->append, $append); + + return $this; + } + + /** + * 设置附加关联对象的属性 + * @access public + * @param string $attr 关联属性 + * @param string|array $append 追加属性名 + * @return $this + * @throws Exception + */ + public function appendRelationAttr($attr, $append) + { + if (is_string($append)) { + $append = explode(',', $append); + } + + $relation = Loader::parseName($attr, 1, false); + if (isset($this->relation[$relation])) { + $model = $this->relation[$relation]; + } else { + $model = $this->getRelationData($this->$relation()); + } + + if ($model instanceof Model) { + foreach ($append as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + if (isset($this->data[$key])) { + throw new Exception('bind attr has exists:' . $key); + } else { + $this->data[$key] = $model->$attr; + } + } + } + + return $this; + } + + /** + * 设置需要隐藏的输出属性 + * @access public + * @param array $hidden 属性列表 + * @param bool $override 是否覆盖 + * @return $this + */ + public function hidden(array $hidden = [], $override = false) + { + $this->hidden = $override ? $hidden : array_merge($this->hidden, $hidden); + + return $this; + } + + /** + * 设置需要输出的属性 + * @access public + * @param array $visible + * @param bool $override 是否覆盖 + * @return $this + */ + public function visible(array $visible = [], $override = false) + { + $this->visible = $override ? $visible : array_merge($this->visible, $visible); + + return $this; + } + + /** + * 转换当前模型对象为数组 + * @access public + * @return array + */ + public function toArray() + { + $item = []; + $visible = []; + $hidden = []; + + // 合并关联数据 + $data = array_merge($this->data, $this->relation); + + // 过滤属性 + if (!empty($this->visible)) { + $array = $this->parseAttr($this->visible, $visible); + $data = array_intersect_key($data, array_flip($array)); + } elseif (!empty($this->hidden)) { + $array = $this->parseAttr($this->hidden, $hidden, false); + $data = array_diff_key($data, array_flip($array)); + } + + foreach ($data as $key => $val) { + if ($val instanceof Model || $val instanceof ModelCollection) { + // 关联模型对象 + if (isset($visible[$key])) { + $val->visible($visible[$key]); + } elseif (isset($hidden[$key])) { + $val->hidden($hidden[$key]); + } + // 关联模型对象 + $item[$key] = $val->toArray(); + } else { + // 模型属性 + $item[$key] = $this->getAttr($key); + } + } + + // 追加属性(必须定义获取器) + if (!empty($this->append)) { + foreach ($this->append as $key => $name) { + if (is_array($name)) { + // 追加关联对象属性 + $relation = $this->getRelation($key); + + if (!$relation) { + $relation = $this->getAttr($key); + $relation->visible($name); + } + + $item[$key] = $relation->append($name)->toArray(); + } elseif (strpos($name, '.')) { + list($key, $attr) = explode('.', $name); + // 追加关联对象属性 + $relation = $this->getRelation($key); + + if (!$relation) { + $relation = $this->getAttr($key); + $relation->visible([$attr]); + } + + $item[$key] = $relation->append([$attr])->toArray(); + } else { + $value = $this->getAttr($name, $item); + if (false !== $value) { + $item[$name] = $value; + } + } + } + } + + return $item; + } + + /** + * 转换当前模型对象为JSON字符串 + * @access public + * @param integer $options json参数 + * @return string + */ + public function toJson($options = JSON_UNESCAPED_UNICODE) + { + return json_encode($this->toArray(), $options); + } + + /** + * 移除当前模型的关联属性 + * @access public + * @return $this + */ + public function removeRelation() + { + $this->relation = []; + return $this; + } + + public function __toString() + { + return $this->toJson(); + } + + // JsonSerializable + public function jsonSerialize() + { + return $this->toArray(); + } + + /** + * 转换数据集为数据集对象 + * @access public + * @param array|Collection $collection 数据集 + * @param string $resultSetType 数据集类 + * @return Collection + */ + public function toCollection($collection, $resultSetType = null) + { + $resultSetType = $resultSetType ?: $this->resultSetType; + + if ($resultSetType && false !== strpos($resultSetType, '\\')) { + $collection = new $resultSetType($collection); + } else { + $collection = new ModelCollection($collection); + } + + return $collection; + } + + /** + * 解析隐藏及显示属性 + * @access protected + * @param array $attrs 属性 + * @param array $result 结果集 + * @param bool $visible + * @return array + */ + protected function parseAttr($attrs, &$result, $visible = true) + { + $array = []; + + foreach ($attrs as $key => $val) { + if (is_array($val)) { + if ($visible) { + $array[] = $key; + } + + $result[$key] = $val; + } elseif (strpos($val, '.')) { + list($key, $name) = explode('.', $val); + + if ($visible) { + $array[] = $key; + } + + $result[$key][] = $name; + } else { + $array[] = $val; + } + } + + return $array; + } +} diff --git a/thinkphp/library/think/model/concern/ModelEvent.php b/thinkphp/library/think/model/concern/ModelEvent.php new file mode 100644 index 000000000..3a874846f --- /dev/null +++ b/thinkphp/library/think/model/concern/ModelEvent.php @@ -0,0 +1,238 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\concern; + +use think\Container; +use think\Loader; + +/** + * 模型事件处理 + */ +trait ModelEvent +{ + /** + * 模型回调 + * @var array + */ + private static $event = []; + + /** + * 模型事件观察 + * @var array + */ + protected static $observe = ['before_write', 'after_write', 'before_insert', 'after_insert', 'before_update', 'after_update', 'before_delete', 'after_delete', 'before_restore', 'after_restore']; + + /** + * 绑定模型事件观察者类 + * @var array + */ + protected $observerClass; + + /** + * 是否需要事件响应 + * @var bool + */ + private $withEvent = true; + + /** + * 注册回调方法 + * @access public + * @param string $event 事件名 + * @param callable $callback 回调方法 + * @param bool $override 是否覆盖 + * @return void + */ + public static function event($event, $callback, $override = false) + { + $class = static::class; + + if ($override) { + self::$event[$class][$event] = []; + } + + self::$event[$class][$event][] = $callback; + } + + /** + * 清除回调方法 + * @access public + * @return void + */ + public static function flushEvent() + { + self::$event[static::class] = []; + } + + /** + * 注册一个模型观察者 + * + * @param object|string $class + * @return void + */ + public static function observe($class) + { + self::flushEvent(); + + foreach (static::$observe as $event) { + $eventFuncName = Loader::parseName($event, 1, false); + + if (method_exists($class, $eventFuncName)) { + static::event($event, [$class, $eventFuncName]); + } + } + } + + /** + * 当前操作的事件响应 + * @access protected + * @param bool $event 是否需要事件响应 + * @return $this + */ + public function withEvent($event) + { + $this->withEvent = $event; + return $this; + } + + /** + * 触发事件 + * @access protected + * @param string $event 事件名 + * @return bool + */ + protected function trigger($event) + { + $class = static::class; + + if ($this->withEvent && isset(self::$event[$class][$event])) { + foreach (self::$event[$class][$event] as $callback) { + $result = Container::getInstance()->invoke($callback, [$this]); + + if (false === $result) { + return false; + } + } + } + + return true; + } + + /** + * 模型before_insert事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function beforeInsert($callback, $override = false) + { + self::event('before_insert', $callback, $override); + } + + /** + * 模型after_insert事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function afterInsert($callback, $override = false) + { + self::event('after_insert', $callback, $override); + } + + /** + * 模型before_update事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function beforeUpdate($callback, $override = false) + { + self::event('before_update', $callback, $override); + } + + /** + * 模型after_update事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function afterUpdate($callback, $override = false) + { + self::event('after_update', $callback, $override); + } + + /** + * 模型before_write事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function beforeWrite($callback, $override = false) + { + self::event('before_write', $callback, $override); + } + + /** + * 模型after_write事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function afterWrite($callback, $override = false) + { + self::event('after_write', $callback, $override); + } + + /** + * 模型before_delete事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function beforeDelete($callback, $override = false) + { + self::event('before_delete', $callback, $override); + } + + /** + * 模型after_delete事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function afterDelete($callback, $override = false) + { + self::event('after_delete', $callback, $override); + } + + /** + * 模型before_restore事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function beforeRestore($callback, $override = false) + { + self::event('before_restore', $callback, $override); + } + + /** + * 模型after_restore事件快捷方法 + * @access protected + * @param callable $callback + * @param bool $override + */ + protected static function afterRestore($callback, $override = false) + { + self::event('after_restore', $callback, $override); + } +} diff --git a/thinkphp/library/think/model/concern/RelationShip.php b/thinkphp/library/think/model/concern/RelationShip.php new file mode 100644 index 000000000..38ad5d20f --- /dev/null +++ b/thinkphp/library/think/model/concern/RelationShip.php @@ -0,0 +1,670 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\concern; + +use think\Collection; +use think\db\Query; +use think\Loader; +use think\Model; +use think\model\Relation; +use think\model\relation\BelongsTo; +use think\model\relation\BelongsToMany; +use think\model\relation\HasMany; +use think\model\relation\HasManyThrough; +use think\model\relation\HasOne; +use think\model\relation\MorphMany; +use think\model\relation\MorphOne; +use think\model\relation\MorphTo; + +/** + * 模型关联处理 + */ +trait RelationShip +{ + /** + * 父关联模型对象 + * @var object + */ + private $parent; + + /** + * 模型关联数据 + * @var array + */ + private $relation = []; + + /** + * 关联写入定义信息 + * @var array + */ + private $together; + + /** + * 关联自动写入信息 + * @var array + */ + protected $relationWrite; + + /** + * 设置父关联对象 + * @access public + * @param Model $model 模型对象 + * @return $this + */ + public function setParent($model) + { + $this->parent = $model; + + return $this; + } + + /** + * 获取父关联对象 + * @access public + * @return Model + */ + public function getParent() + { + return $this->parent; + } + + /** + * 获取当前模型的关联模型数据 + * @access public + * @param string $name 关联方法名 + * @return mixed + */ + public function getRelation($name = null) + { + if (is_null($name)) { + return $this->relation; + } elseif (array_key_exists($name, $this->relation)) { + return $this->relation[$name]; + } + return; + } + + /** + * 设置关联数据对象值 + * @access public + * @param string $name 属性名 + * @param mixed $value 属性值 + * @param array $data 数据 + * @return $this + */ + public function setRelation($name, $value, $data = []) + { + // 检测修改器 + $method = 'set' . Loader::parseName($name, 1) . 'Attr'; + + if (method_exists($this, $method)) { + $value = $this->$method($value, array_merge($this->data, $data)); + } + + $this->relation[$name] = $value; + + return $this; + } + + /** + * 关联数据写入 + * @access public + * @param array|string $relation 关联 + * @return $this + */ + public function together($relation) + { + if (is_string($relation)) { + $relation = explode(',', $relation); + } + + $this->together = $relation; + + $this->checkAutoRelationWrite(); + + return $this; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $relation 关联方法名 + * @param mixed $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @return Query + */ + public static function has($relation, $operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + { + $relation = (new static())->$relation(); + + if (is_array($operator) || $operator instanceof \Closure) { + return $relation->hasWhere($operator); + } + + return $relation->has($operator, $count, $id, $joinType); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $relation 关联方法名 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + */ + public static function hasWhere($relation, $where = [], $fields = '*') + { + return (new static())->$relation()->hasWhere($where, $fields); + } + + /** + * 查询当前模型的关联数据 + * @access public + * @param string|array $relations 关联名 + * @param array $withRelationAttr 关联获取器 + * @return $this + */ + public function relationQuery($relations, $withRelationAttr = []) + { + if (is_string($relations)) { + $relations = explode(',', $relations); + } + + foreach ($relations as $key => $relation) { + $subRelation = ''; + $closure = null; + + if ($relation instanceof \Closure) { + // 支持闭包查询过滤关联条件 + $closure = $relation; + $relation = $key; + } + + if (is_array($relation)) { + $subRelation = $relation; + $relation = $key; + } elseif (strpos($relation, '.')) { + list($relation, $subRelation) = explode('.', $relation, 2); + } + + $method = Loader::parseName($relation, 1, false); + $relationName = Loader::parseName($relation); + + $relationResult = $this->$method(); + + if (isset($withRelationAttr[$relationName])) { + $relationResult->withAttr($withRelationAttr[$relationName]); + } + + $this->relation[$relation] = $relationResult->getRelation($subRelation, $closure); + } + + return $this; + } + + /** + * 预载入关联查询 返回数据集 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 关联名 + * @param array $withRelationAttr 关联获取器 + * @param bool $join 是否为JOIN方式 + * @return array + */ + public function eagerlyResultSet(&$resultSet, $relation, $withRelationAttr = [], $join = false) + { + $relations = is_string($relation) ? explode(',', $relation) : $relation; + + foreach ($relations as $key => $relation) { + $subRelation = ''; + $closure = null; + + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } + + if (is_array($relation)) { + $subRelation = $relation; + $relation = $key; + } elseif (strpos($relation, '.')) { + list($relation, $subRelation) = explode('.', $relation, 2); + } + + $relation = Loader::parseName($relation, 1, false); + $relationName = Loader::parseName($relation); + + $relationResult = $this->$relation(); + + if (isset($withRelationAttr[$relationName])) { + $relationResult->withAttr($withRelationAttr[$relationName]); + } + + $relationResult->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $join); + } + } + + /** + * 预载入关联查询 返回模型对象 + * @access public + * @param Model $result 数据对象 + * @param string $relation 关联名 + * @param array $withRelationAttr 关联获取器 + * @param bool $join 是否为JOIN方式 + * @return Model + */ + public function eagerlyResult(&$result, $relation, $withRelationAttr = [], $join = false) + { + $relations = is_string($relation) ? explode(',', $relation) : $relation; + + foreach ($relations as $key => $relation) { + $subRelation = ''; + $closure = null; + + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } + + if (is_array($relation)) { + $subRelation = $relation; + $relation = $key; + } elseif (strpos($relation, '.')) { + list($relation, $subRelation) = explode('.', $relation, 2); + } + + $relation = Loader::parseName($relation, 1, false); + $relationName = Loader::parseName($relation); + + $relationResult = $this->$relation(); + + if (isset($withRelationAttr[$relationName])) { + $relationResult->withAttr($withRelationAttr[$relationName]); + } + + $relationResult->eagerlyResult($result, $relation, $subRelation, $closure, $join); + } + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param array $relations 关联名 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @return void + */ + public function relationCount(&$result, $relations, $aggregate = 'sum', $field = '*') + { + foreach ($relations as $key => $relation) { + $closure = $name = null; + + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } elseif (is_string($key)) { + $name = $relation; + $relation = $key; + } + + $relation = Loader::parseName($relation, 1, false); + + $count = $this->$relation()->relationCount($result, $closure, $aggregate, $field, $name); + + if (empty($name)) { + $name = Loader::parseName($relation) . '_' . $aggregate; + } + + $result->setAttr($name, $count); + } + } + + /** + * HAS ONE 关联定义 + * @access public + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前主键 + * @return HasOne + */ + public function hasOne($model, $foreignKey = '', $localKey = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $localKey = $localKey ?: $this->getPk(); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + + return new HasOne($this, $model, $foreignKey, $localKey); + } + + /** + * BELONGS TO 关联定义 + * @access public + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 关联主键 + * @return BelongsTo + */ + public function belongsTo($model, $foreignKey = '', $localKey = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $foreignKey = $foreignKey ?: $this->getForeignKey((new $model)->getName()); + $localKey = $localKey ?: (new $model)->getPk(); + $trace = debug_backtrace(false, 2); + $relation = Loader::parseName($trace[1]['function']); + + return new BelongsTo($this, $model, $foreignKey, $localKey, $relation); + } + + /** + * HAS MANY 关联定义 + * @access public + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前主键 + * @return HasMany + */ + public function hasMany($model, $foreignKey = '', $localKey = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $localKey = $localKey ?: $this->getPk(); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + + return new HasMany($this, $model, $foreignKey, $localKey); + } + + /** + * HAS MANY 远程关联定义 + * @access public + * @param string $model 模型名 + * @param string $through 中间模型名 + * @param string $foreignKey 关联外键 + * @param string $throughKey 关联外键 + * @param string $localKey 当前主键 + * @return HasManyThrough + */ + public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $through = $this->parseModel($through); + $localKey = $localKey ?: $this->getPk(); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + $throughKey = $throughKey ?: $this->getForeignKey((new $through)->getName()); + + return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey); + } + + /** + * BELONGS TO MANY 关联定义 + * @access public + * @param string $model 模型名 + * @param string $table 中间表名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型关联键 + * @return BelongsToMany + */ + public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $name = Loader::parseName(basename(str_replace('\\', '/', $model))); + $table = $table ?: Loader::parseName($this->name) . '_' . $name; + $foreignKey = $foreignKey ?: $name . '_id'; + $localKey = $localKey ?: $this->getForeignKey($this->name); + + return new BelongsToMany($this, $model, $table, $foreignKey, $localKey); + } + + /** + * MORPH One 关联定义 + * @access public + * @param string $model 模型名 + * @param string|array $morph 多态字段信息 + * @param string $type 多态类型 + * @return MorphOne + */ + public function morphOne($model, $morph = null, $type = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + + if (is_null($morph)) { + $trace = debug_backtrace(false, 2); + $morph = Loader::parseName($trace[1]['function']); + } + + if (is_array($morph)) { + list($morphType, $foreignKey) = $morph; + } else { + $morphType = $morph . '_type'; + $foreignKey = $morph . '_id'; + } + + $type = $type ?: get_class($this); + + return new MorphOne($this, $model, $foreignKey, $morphType, $type); + } + + /** + * MORPH MANY 关联定义 + * @access public + * @param string $model 模型名 + * @param string|array $morph 多态字段信息 + * @param string $type 多态类型 + * @return MorphMany + */ + public function morphMany($model, $morph = null, $type = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + + if (is_null($morph)) { + $trace = debug_backtrace(false, 2); + $morph = Loader::parseName($trace[1]['function']); + } + + $type = $type ?: get_class($this); + + if (is_array($morph)) { + list($morphType, $foreignKey) = $morph; + } else { + $morphType = $morph . '_type'; + $foreignKey = $morph . '_id'; + } + + return new MorphMany($this, $model, $foreignKey, $morphType, $type); + } + + /** + * MORPH TO 关联定义 + * @access public + * @param string|array $morph 多态字段信息 + * @param array $alias 多态别名定义 + * @return MorphTo + */ + public function morphTo($morph = null, $alias = []) + { + $trace = debug_backtrace(false, 2); + $relation = Loader::parseName($trace[1]['function']); + + if (is_null($morph)) { + $morph = $relation; + } + + // 记录当前关联信息 + if (is_array($morph)) { + list($morphType, $foreignKey) = $morph; + } else { + $morphType = $morph . '_type'; + $foreignKey = $morph . '_id'; + } + + return new MorphTo($this, $morphType, $foreignKey, $alias, $relation); + } + + /** + * 解析模型的完整命名空间 + * @access protected + * @param string $model 模型名(或者完整类名) + * @return string + */ + protected function parseModel($model) + { + if (false === strpos($model, '\\')) { + $path = explode('\\', static::class); + array_pop($path); + array_push($path, Loader::parseName($model, 1)); + $model = implode('\\', $path); + } + + return $model; + } + + /** + * 获取模型的默认外键名 + * @access protected + * @param string $name 模型名 + * @return string + */ + protected function getForeignKey($name) + { + if (strpos($name, '\\')) { + $name = basename(str_replace('\\', '/', $name)); + } + + return Loader::parseName($name) . '_id'; + } + + /** + * 检查属性是否为关联属性 如果是则返回关联方法名 + * @access protected + * @param string $attr 关联属性名 + * @return string|false + */ + protected function isRelationAttr($attr) + { + $relation = Loader::parseName($attr, 1, false); + + if (method_exists($this, $relation) && !method_exists('think\Model', $relation)) { + return $relation; + } + + return false; + } + + /** + * 智能获取关联模型数据 + * @access protected + * @param Relation $modelRelation 模型关联对象 + * @return mixed + */ + protected function getRelationData(Relation $modelRelation) + { + if ($this->parent && !$modelRelation->isSelfRelation() && get_class($this->parent) == get_class($modelRelation->getModel())) { + $value = $this->parent; + } else { + // 获取关联数据 + $value = $modelRelation->getRelation(); + } + + return $value; + } + + /** + * 关联数据自动写入检查 + * @access protected + * @return void + */ + protected function checkAutoRelationWrite() + { + foreach ($this->together as $key => $name) { + if (is_array($name)) { + if (key($name) === 0) { + $this->relationWrite[$key] = []; + // 绑定关联属性 + foreach ((array) $name as $val) { + if (isset($this->data[$val])) { + $this->relationWrite[$key][$val] = $this->data[$val]; + } + } + } else { + // 直接传入关联数据 + $this->relationWrite[$key] = $name; + } + } elseif (isset($this->relation[$name])) { + $this->relationWrite[$name] = $this->relation[$name]; + } elseif (isset($this->data[$name])) { + $this->relationWrite[$name] = $this->data[$name]; + unset($this->data[$name]); + } + } + } + + /** + * 自动关联数据更新(针对一对一关联) + * @access protected + * @return void + */ + protected function autoRelationUpdate() + { + foreach ($this->relationWrite as $name => $val) { + if ($val instanceof Model) { + $val->isUpdate()->save(); + } else { + $model = $this->getRelation($name); + if ($model instanceof Model) { + $model->isUpdate()->save($val); + } + } + } + } + + /** + * 自动关联数据写入(针对一对一关联) + * @access protected + * @return void + */ + protected function autoRelationInsert() + { + foreach ($this->relationWrite as $name => $val) { + $method = Loader::parseName($name, 1, false); + $this->$method()->save($val); + } + } + + /** + * 自动关联数据删除(支持一对一及一对多关联) + * @access protected + * @return void + */ + protected function autoRelationDelete() + { + foreach ($this->relationWrite as $key => $name) { + $name = is_numeric($key) ? $name : $key; + $result = $this->getRelation($name); + + if ($result instanceof Model) { + $result->delete(); + } elseif ($result instanceof Collection) { + foreach ($result as $model) { + $model->delete(); + } + } + } + } +} diff --git a/thinkphp/library/think/model/concern/SoftDelete.php b/thinkphp/library/think/model/concern/SoftDelete.php new file mode 100644 index 000000000..7dc96e126 --- /dev/null +++ b/thinkphp/library/think/model/concern/SoftDelete.php @@ -0,0 +1,241 @@ +getDeleteTimeField(); + + if ($field && !empty($this->getOrigin($field))) { + return true; + } + + return false; + } + + /** + * 查询软删除数据 + * @access public + * @return Query + */ + public static function withTrashed() + { + $model = new static(); + + return $model->withTrashedData(true)->db(false); + } + + /** + * 是否包含软删除数据 + * @access protected + * @param bool $withTrashed 是否包含软删除数据 + * @return $this + */ + protected function withTrashedData($withTrashed) + { + $this->withTrashed = $withTrashed; + return $this; + } + + /** + * 只查询软删除数据 + * @access public + * @return Query + */ + public static function onlyTrashed() + { + $model = new static(); + $field = $model->getDeleteTimeField(true); + + if ($field) { + return $model + ->db(false) + ->useSoftDelete($field, $model->getWithTrashedExp()); + } + + return $model->db(false); + } + + /** + * 获取软删除数据的查询条件 + * @access protected + * @return array + */ + protected function getWithTrashedExp() + { + return is_null($this->defaultSoftDelete) ? + ['notnull', ''] : ['<>', $this->defaultSoftDelete]; + } + + /** + * 删除当前的记录 + * @access public + * @return bool + */ + public function delete($force = false) + { + if (!$this->isExists() || false === $this->trigger('before_delete', $this)) { + return false; + } + + $force = $force ?: $this->isForce(); + $name = $this->getDeleteTimeField(); + + if ($name && !$force) { + // 软删除 + $this->data($name, $this->autoWriteTimestamp($name)); + + $result = $this->isUpdate()->withEvent(false)->save(); + + $this->withEvent(true); + } else { + // 读取更新条件 + $where = $this->getWhere(); + + // 删除当前模型数据 + $result = $this->db(false) + ->where($where) + ->removeOption('soft_delete') + ->delete(); + } + + // 关联删除 + if (!empty($this->relationWrite)) { + $this->autoRelationDelete(); + } + + $this->trigger('after_delete', $this); + + $this->exists(false); + + return true; + } + + /** + * 删除记录 + * @access public + * @param mixed $data 主键列表 支持闭包查询条件 + * @param bool $force 是否强制删除 + * @return bool + */ + public static function destroy($data, $force = false) + { + // 包含软删除数据 + $query = (new static())->db(false); + + if (is_array($data) && key($data) !== 0) { + $query->where($data); + $data = null; + } elseif ($data instanceof \Closure) { + call_user_func_array($data, [ & $query]); + $data = null; + } elseif (is_null($data)) { + return false; + } + + $resultSet = $query->select($data); + + if ($resultSet) { + foreach ($resultSet as $data) { + $data->force($force)->delete(); + } + } + + return true; + } + + /** + * 恢复被软删除的记录 + * @access public + * @param array $where 更新条件 + * @return bool + */ + public function restore($where = []) + { + $name = $this->getDeleteTimeField(); + + if ($name) { + if (false === $this->trigger('before_restore')) { + return false; + } + + if (empty($where)) { + $pk = $this->getPk(); + + $where[] = [$pk, '=', $this->getData($pk)]; + } + + // 恢复删除 + $this->db(false) + ->where($where) + ->useSoftDelete($name, $this->getWithTrashedExp()) + ->update([$name => $this->defaultSoftDelete]); + + $this->trigger('after_restore'); + + return true; + } + + return false; + } + + /** + * 获取软删除字段 + * @access protected + * @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 + * @return string|false + */ + protected function getDeleteTimeField($read = false) + { + $field = property_exists($this, 'deleteTime') && isset($this->deleteTime) ? $this->deleteTime : 'delete_time'; + + if (false === $field) { + return false; + } + + if (false === strpos($field, '.')) { + $field = '__TABLE__.' . $field; + } + + if (!$read && strpos($field, '.')) { + $array = explode('.', $field); + $field = array_pop($array); + } + + return $field; + } + + /** + * 查询的时候默认排除软删除数据 + * @access protected + * @param Query $query + * @return void + */ + protected function withNoTrashed($query) + { + $field = $this->getDeleteTimeField(true); + + if ($field) { + $query->useSoftDelete($field, $this->defaultSoftDelete); + } + } +} diff --git a/thinkphp/library/think/model/concern/TimeStamp.php b/thinkphp/library/think/model/concern/TimeStamp.php new file mode 100644 index 000000000..99a31fa7d --- /dev/null +++ b/thinkphp/library/think/model/concern/TimeStamp.php @@ -0,0 +1,92 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\concern; + +use DateTime; + +/** + * 自动时间戳 + */ +trait TimeStamp +{ + /** + * 是否需要自动写入时间戳 如果设置为字符串 则表示时间字段的类型 + * @var bool|string + */ + protected $autoWriteTimestamp; + + /** + * 创建时间字段 false表示关闭 + * @var false|string + */ + protected $createTime = 'create_time'; + + /** + * 更新时间字段 false表示关闭 + * @var false|string + */ + protected $updateTime = 'update_time'; + + /** + * 时间字段显示格式 + * @var string + */ + protected $dateFormat; + + /** + * 时间日期字段格式化处理 + * @access protected + * @param mixed $format 日期格式 + * @param mixed $time 时间日期表达式 + * @param bool $timestamp 是否进行时间戳转换 + * @return mixed + */ + protected function formatDateTime($format, $time = 'now', $timestamp = false) + { + if (empty($time)) { + return; + } + + if (false === $format) { + return $time; + } elseif (false !== strpos($format, '\\')) { + return new $format($time); + } + + if ($timestamp) { + $dateTime = new DateTime(); + $dateTime->setTimestamp($time); + } else { + $dateTime = new DateTime($time); + } + + return $dateTime->format($format); + } + + /** + * 检查时间字段写入 + * @access protected + * @return void + */ + protected function checkTimeStampWrite() + { + // 自动写入创建时间和更新时间 + if ($this->autoWriteTimestamp) { + if ($this->createTime && !isset($this->data[$this->createTime])) { + $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime); + } + if ($this->updateTime && !isset($this->data[$this->updateTime])) { + $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + } + } + } +} diff --git a/thinkphp/library/think/model/relation/BelongsTo.php b/thinkphp/library/think/model/relation/BelongsTo.php index 905ea9443..98d176e8f 100644 --- a/thinkphp/library/think/model/relation/BelongsTo.php +++ b/thinkphp/library/think/model/relation/BelongsTo.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -17,40 +17,46 @@ use think\Model; class BelongsTo extends OneToOne { /** - * 构造函数 + * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @param string $joinType JOIN类型 - * @param string $relation 关联名 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 关联主键 + * @param string $relation 关联名 */ - public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER', $relation = null) + public function __construct(Model $parent, $model, $foreignKey, $localKey, $relation = null) { $this->parent = $parent; $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; - $this->joinType = $joinType; + $this->joinType = 'INNER'; $this->query = (new $model)->db(); $this->relation = $relation; + + if (get_class($parent) == $model) { + $this->selfRelation = true; + } } /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 * @access public - * @return array|false|\PDOStatement|string|Model + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return Model */ public function getRelation($subRelation = '', $closure = null) { - $foreignKey = $this->foreignKey; if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $closure($this->query); } + + $foreignKey = $this->foreignKey; + $relationModel = $this->query + ->removeWhereField($this->localKey) ->where($this->localKey, $this->parent->$foreignKey) ->relation($subRelation) ->find(); @@ -63,51 +69,120 @@ class BelongsTo extends OneToOne } /** - * 根据关联条件查询当前模型 + * 创建关联统计子查询 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 - * @return Query + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $aggregateAlias 聚合字段别名 + * @return string */ - public function has($operator = '>=', $count = 1, $id = '*') + public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '') { - return $this->parent; + if ($closure) { + $return = $closure($this->query); + + if ($return && is_string($return)) { + $aggregateAlias = $return; + } + } + + return $this->query + ->whereExp($this->localKey, '=' . $this->parent->getTable() . '.' . $this->foreignKey) + ->fetchSql() + ->$aggregate($field); + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 + * @return integer + */ + public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '') + { + $foreignKey = $this->foreignKey; + + if (!isset($result->$foreignKey)) { + return 0; + } + + if ($closure) { + $return = $closure($this->query); + + if ($return && is_string($return)) { + $name = $return; + } + } + + return $this->query + ->where($this->localKey, '=', $result->$foreignKey) + ->$aggregate($field); } /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function hasWhere($where = []) + public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + { + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + return $this->parent->db() + ->alias($model) + ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) { + $query->table([$table => $relation]) + ->field($relation . '.' . $localKey) + ->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey); + }); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + */ + public function hasWhere($where = [], $fields = null) { $table = $this->query->getTable(); $model = basename(str_replace('\\', '/', get_class($this->parent))); $relation = basename(str_replace('\\', '/', $this->model)); + if (is_array($where)) { - foreach ($where as $key => $val) { - if (false === strpos($key, '.')) { - $where[$relation . '.' . $key] = $val; - unset($where[$key]); - } - } + $this->getQueryWhere($where, $relation); } - return $this->parent->db()->alias($model) - ->field($model . '.*') - ->join($table . ' ' . $relation, $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType) + + $fields = $this->getRelationQueryFields($fields, $model); + + return $this->parent->db() + ->alias($model) + ->field($fields) + ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType) ->where($where); } /** * 预载入关联查询(数据集) - * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @access protected + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) @@ -124,14 +199,15 @@ class BelongsTo extends OneToOne } if (!empty($range)) { - $data = $this->eagerlyWhere($this, [ - $localKey => [ - 'in', - $range, - ], + $this->query->removeWhereField($localKey); + + $data = $this->eagerlyWhere([ + [$localKey, 'in', $range], ], $localKey, $relation, $subRelation, $closure); + // 关联属性名 $attr = Loader::parseName($relation); + // 关联数据封装 foreach ($resultSet as $result) { // 关联模型 @@ -143,30 +219,37 @@ class BelongsTo extends OneToOne $relationModel->isUpdate(true); } - if ($relationModel && !empty($this->bindAttr)) { + if (!empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $result, $this->bindAttr); + $this->bindAttr($relationModel, $result); + } else { + // 设置关联属性 + $result->setRelation($attr, $relationModel); } - // 设置关联属性 - $result->setRelation($attr, $relationModel); } } } /** * 预载入关联查询(数据) - * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @access protected + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ protected function eagerlyOne(&$result, $relation, $subRelation, $closure) { $localKey = $this->localKey; $foreignKey = $this->foreignKey; - $data = $this->eagerlyWhere($this, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure); + + $this->query->removeWhereField($localKey); + + $data = $this->eagerlyWhere([ + [$localKey, '=', $result->$foreignKey], + ], $localKey, $relation, $subRelation, $closure); + // 关联模型 if (!isset($data[$result->$foreignKey])) { $relationModel = null; @@ -175,26 +258,25 @@ class BelongsTo extends OneToOne $relationModel->setParent(clone $result); $relationModel->isUpdate(true); } - if ($relationModel && !empty($this->bindAttr)) { + + if (!empty($this->bindAttr)) { // 绑定关联属性 - $this->bindAttr($relationModel, $result, $this->bindAttr); + $this->bindAttr($relationModel, $result); + } else { + // 设置关联属性 + $result->setRelation(Loader::parseName($relation), $relationModel); } - // 设置关联属性 - $result->setRelation(Loader::parseName($relation), $relationModel); } /** * 添加关联数据 * @access public - * @param Model $model 关联模型对象 + * @param Model $model 关联模型对象 * @return Model */ public function associate($model) { - $foreignKey = $this->foreignKey; - $pk = $model->getPk(); - - $this->parent->setAttr($foreignKey, $model->$pk); + $this->parent->setAttr($this->foreignKey, $model->getKey()); $this->parent->save(); return $this->parent->setRelation($this->relation, $model); @@ -207,11 +289,26 @@ class BelongsTo extends OneToOne */ public function dissociate() { - $foreignKey = $this->foreignKey; - - $this->parent->setAttr($foreignKey, null); + $this->parent->setAttr($this->foreignKey, null); $this->parent->save(); return $this->parent->setRelation($this->relation, null); } + + /** + * 执行基础查询(仅执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + { + if (empty($this->baseQuery)) { + if (isset($this->parent->{$this->foreignKey})) { + // 关联查询带入关联条件 + $this->query->where($this->localKey, '=', $this->parent->{$this->foreignKey}); + } + + $this->baseQuery = true; + } + } } diff --git a/thinkphp/library/think/model/relation/BelongsToMany.php b/thinkphp/library/think/model/relation/BelongsToMany.php index 9eeb88ddf..54df68b0c 100644 --- a/thinkphp/library/think/model/relation/BelongsToMany.php +++ b/thinkphp/library/think/model/relation/BelongsToMany.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -18,7 +18,6 @@ use think\Loader; use think\Model; use think\model\Pivot; use think\model\Relation; -use think\Paginator; class BelongsToMany extends Relation { @@ -26,17 +25,19 @@ class BelongsToMany extends Relation protected $middle; // 中间表模型名称 protected $pivotName; + // 中间表数据名称 + protected $pivotDataName = 'pivot'; // 中间表模型对象 protected $pivot; /** - * 构造函数 + * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $table 中间表名 - * @param string $foreignKey 关联模型外键 - * @param string $localKey 当前模型关联键 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $table 中间表名 + * @param string $foreignKey 关联模型外键 + * @param string $localKey 当前模型关联键 */ public function __construct(Model $parent, $model, $table, $foreignKey, $localKey) { @@ -44,19 +45,22 @@ class BelongsToMany extends Relation $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; + if (false !== strpos($table, '\\')) { $this->pivotName = $table; $this->middle = basename(str_replace('\\', '/', $table)); } else { $this->middle = $table; } + $this->query = (new $model)->db(); $this->pivot = $this->newPivot(); } /** * 设置中间表模型 - * @param $pivot + * @access public + * @param $pivot * @return $this */ public function pivot($pivot) @@ -66,111 +70,163 @@ class BelongsToMany extends Relation } /** - * 实例化中间表模型 - * @param $data - * @return mixed + * 设置中间表数据名称 + * @access public + * @param string $name + * @return $this */ - protected function newPivot($data = []) + public function pivotDataName($name) { - $pivot = $this->pivotName ?: '\\think\\model\\Pivot'; - return new $pivot($this->parent, $data, $this->middle); + $this->pivotDataName = $name; + return $this; + } + + /** + * 获取中间表更新条件 + * @param $data + * @return array + */ + protected function getUpdateWhere($data) + { + return [ + $this->localKey => $data[$this->localKey], + $this->foreignKey => $data[$this->foreignKey], + ]; + } + + /** + * 实例化中间表模型 + * @access public + * @param array $data + * @param bool $isUpdate + * @return Pivot + * @throws Exception + */ + protected function newPivot($data = [], $isUpdate = false) + { + $class = $this->pivotName ?: '\\think\\model\\Pivot'; + $pivot = new $class($data, $this->parent, $this->middle); + + if ($pivot instanceof Pivot) { + return $isUpdate ? $pivot->isUpdate(true, $this->getUpdateWhere($data)) : $pivot; + } + + throw new Exception('pivot model must extends: \think\model\Pivot'); } /** * 合成中间表模型 - * @param array|Collection|Paginator $models + * @access protected + * @param array|Collection|Paginator $models */ protected function hydratePivot($models) { foreach ($models as $model) { $pivot = []; + foreach ($model->getData() as $key => $val) { if (strpos($key, '__')) { list($name, $attr) = explode('__', $key, 2); + if ('pivot' == $name) { $pivot[$attr] = $val; unset($model->$key); } } } - $model->pivot = $this->newPivot($pivot); + + $model->setRelation($this->pivotDataName, $this->newPivot($pivot, true)); } } /** * 创建关联查询Query对象 + * @access protected * @return Query */ protected function buildQuery() { $foreignKey = $this->foreignKey; $localKey = $this->localKey; - $middle = $this->middle; + // 关联查询 - $pk = $this->parent->getPk(); - $condition['pivot.' . $localKey] = $this->parent->$pk; + $pk = $this->parent->getPk(); + + $condition[] = ['pivot.' . $localKey, '=', $this->parent->$pk]; + return $this->belongsToManyQuery($foreignKey, $localKey, $condition); } /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return false|\PDOStatement|string|\think\Collection + * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return Collection */ public function getRelation($subRelation = '', $closure = null) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $closure($this->query); } + $result = $this->buildQuery()->relation($subRelation)->select(); $this->hydratePivot($result); + return $result; } /** * 重载select方法 - * @param null $data - * @return false|\PDOStatement|string|Collection + * @access public + * @param mixed $data + * @return Collection */ public function select($data = null) { $result = $this->buildQuery()->select($data); $this->hydratePivot($result); + return $result; } /** * 重载paginate方法 - * @param null $listRows - * @param bool $simple - * @param array $config + * @access public + * @param null $listRows + * @param bool $simple + * @param array $config * @return Paginator */ public function paginate($listRows = null, $simple = false, $config = []) { $result = $this->buildQuery()->paginate($listRows, $simple, $config); $this->hydratePivot($result); + return $result; } /** * 重载find方法 - * @param null $data - * @return array|false|\PDOStatement|string|Model + * @access public + * @param mixed $data + * @return Model */ public function find($data = null) { $result = $this->buildQuery()->find($data); - $this->hydratePivot([$result]); + if ($result) { + $this->hydratePivot([$result]); + } + return $result; } /** * 查找多条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data - * @return array|\PDOStatement|string|Model + * @param array|string|Query|\Closure $data + * @return Collection */ public function selectOrFail($data = null) { @@ -180,8 +236,8 @@ class BelongsToMany extends Relation /** * 查找单条记录 如果不存在则抛出异常 * @access public - * @param array|string|Query|\Closure $data - * @return array|\PDOStatement|string|Model + * @param array|string|Query|\Closure $data + * @return Model */ public function findOrFail($data = null) { @@ -191,10 +247,10 @@ class BelongsToMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') @@ -205,36 +261,37 @@ class BelongsToMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query * @throws Exception */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } /** * 设置中间表的查询条件 - * @param $field - * @param null $op - * @param null $condition + * @access public + * @param string $field + * @param string $op + * @param mixed $condition * @return $this */ public function wherePivot($field, $op = null, $condition = null) { - $field = 'pivot.' . $field; - $this->query->where($field, $op, $condition); + $this->query->where('pivot.' . $field, $op, $condition); return $this; } /** * 预载入关联查询(数据集) * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) @@ -254,13 +311,12 @@ class BelongsToMany extends Relation if (!empty($range)) { // 查询关联数据 $data = $this->eagerlyManyToMany([ - 'pivot.' . $localKey => [ - 'in', - $range, - ], - ], $relation, $subRelation); + ['pivot.' . $localKey, 'in', $range], + ], $relation, $subRelation, $closure); + // 关联属性名 $attr = Loader::parseName($relation); + // 关联数据封装 foreach ($resultSet as $result) { if (!isset($data[$result->$pk])) { @@ -275,24 +331,28 @@ class BelongsToMany extends Relation /** * 预载入关联查询(单个数据) * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ public function eagerlyResult(&$result, $relation, $subRelation, $closure) { $pk = $result->getPk(); + if (isset($result->$pk)) { $pk = $result->$pk; // 查询管理数据 - $data = $this->eagerlyManyToMany(['pivot.' . $this->localKey => $pk], $relation, $subRelation); + $data = $this->eagerlyManyToMany([ + ['pivot.' . $this->localKey, '=', $pk], + ], $relation, $subRelation, $closure); // 关联数据封装 if (!isset($data[$pk])) { $data[$pk] = []; } + $result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$pk])); } } @@ -300,49 +360,81 @@ class BelongsToMany extends Relation /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount($result, $closure) + public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '') { - $pk = $result->getPk(); - $count = 0; - if (isset($result->$pk)) { - $pk = $result->$pk; - $count = $this->belongsToManyQuery($this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => $pk])->count(); + $pk = $result->getPk(); + + if (!isset($result->$pk)) { + return 0; } - return $count; + + $pk = $result->$pk; + + if ($closure) { + $return = $closure($this->query); + + if ($return && is_string($return)) { + $name = $return; + } + } + + return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ + ['pivot.' . $this->localKey, '=', $pk], + ])->$aggregate($field); } /** * 获取关联统计子查询 * @access public - * @param \Closure $closure 闭包 - * @return string + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $aggregateAlias 聚合字段别名 + * @return array */ - public function getRelationCountQuery($closure) + public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '') { + if ($closure) { + $return = $closure($this->query); + + if ($return && is_string($return)) { + $aggregateAlias = $return; + } + } + return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ - 'pivot.' . $this->localKey => [ - 'exp', - '=' . $this->parent->getTable() . '.' . $this->parent->getPk(), + [ + 'pivot.' . $this->localKey, 'exp', $this->query->raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()), ], - ])->fetchSql()->count(); + ])->fetchSql()->$aggregate($field); } /** * 多对多 关联模型预查询 - * @access public - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 + * @access protected + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param \Closure $closure 闭包 * @return array */ - protected function eagerlyManyToMany($where, $relation, $subRelation = '') + protected function eagerlyManyToMany($where, $relation, $subRelation = '', $closure = null) { // 预载入关联查询 支持嵌套预载入 - $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)->with($subRelation)->select(); + if ($closure) { + $closure($this->query); + } + + $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where) + ->with($subRelation) + ->select(); // 组装模型数据 $data = []; @@ -357,18 +449,21 @@ class BelongsToMany extends Relation } } } - $set->pivot = $this->newPivot($pivot); + + $set->setRelation($this->pivotDataName, $this->newPivot($pivot, true)); + $data[$pivot[$this->localKey]][] = $set; } + return $data; } /** * BELONGS TO MANY 关联查询 - * @access public - * @param string $foreignKey 关联模型关联键 - * @param string $localKey 当前模型关联键 - * @param array $condition 关联查询条件 + * @access protected + * @param string $foreignKey 关联模型关联键 + * @param string $localKey 当前模型关联键 + * @param array $condition 关联查询条件 * @return Query */ protected function belongsToManyQuery($foreignKey, $localKey, $condition = []) @@ -378,23 +473,25 @@ class BelongsToMany extends Relation $table = $this->pivot->getTable(); $fields = $this->getQueryFields($tableName); - $query = $this->query->field($fields) + $query = $this->query + ->field($fields) ->field(true, false, $table, 'pivot', 'pivot__'); if (empty($this->baseQuery)) { $relationFk = $this->query->getPk(); - $query->join($table . ' pivot', 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) + $query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) ->where($condition); } + return $query; } /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 - * @param array $pivot 中间表额外数据 - * @return integer + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param array $pivot 中间表额外数据 + * @return array|Pivot */ public function save($data, array $pivot = []) { @@ -405,30 +502,33 @@ class BelongsToMany extends Relation /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 - * @param array $pivot 中间表额外数据 - * @param bool $samePivot 额外数据是否相同 - * @return integer + * @param array $dataSet 数据集 + * @param array $pivot 中间表额外数据 + * @param bool $samePivot 额外数据是否相同 + * @return array|false */ public function saveAll(array $dataSet, array $pivot = [], $samePivot = false) { - $result = false; + $result = []; + foreach ($dataSet as $key => $data) { if (!$samePivot) { $pivotData = isset($pivot[$key]) ? $pivot[$key] : []; } else { $pivotData = $pivot; } - $result = $this->attach($data, $pivotData); + + $result[] = $this->attach($data, $pivotData); } - return $result; + + return empty($result) ? false : $result; } /** * 附加关联的一个中间表数据 * @access public - * @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键 - * @param array $pivot 中间表额外数据 + * @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键 + * @param array $pivot 中间表额外数据 * @return array|Pivot * @throws Exception */ @@ -440,8 +540,7 @@ class BelongsToMany extends Relation } else { // 保存关联表数据 $model = new $this->model; - $model->save($data); - $id = $model->getLastInsID(); + $id = $model->insertGetId($data); } } elseif (is_numeric($data) || is_string($data)) { // 根据关联表主键直接写入中间表 @@ -457,26 +556,55 @@ class BelongsToMany extends Relation $pk = $this->parent->getPk(); $pivot[$this->localKey] = $this->parent->$pk; $ids = (array) $id; + foreach ($ids as $id) { $pivot[$this->foreignKey] = $id; - $this->pivot->insert($pivot, true); - $result[] = $this->newPivot($pivot); + $this->pivot->replace() + ->exists(false) + ->data([]) + ->save($pivot); + $result[] = $this->newPivot($pivot, true); } + if (count($result) == 1) { // 返回中间表模型对象 $result = $result[0]; } + return $result; } else { throw new Exception('miss relation data'); } } + /** + * 判断是否存在关联数据 + * @access public + * @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键 + * @return Pivot + * @throws Exception + */ + public function attached($data) + { + if ($data instanceof Model) { + $id = $data->getKey(); + } else { + $id = $data; + } + + $pivot = $this->pivot + ->where($this->localKey, $this->parent->getKey()) + ->where($this->foreignKey, $id) + ->find(); + + return $pivot ?: false; + } + /** * 解除关联的一个中间表数据 * @access public - * @param integer|array $data 数据 可以使用关联对象的主键 - * @param bool $relationDel 是否同时删除关联表数据 + * @param integer|array $data 数据 可以使用关联对象的主键 + * @param bool $relationDel 是否同时删除关联表数据 * @return integer */ public function detach($data = null, $relationDel = false) @@ -491,24 +619,31 @@ class BelongsToMany extends Relation $relationFk = $data->getPk(); $id = $data->$relationFk; } + // 删除中间表数据 - $pk = $this->parent->getPk(); - $pivot[$this->localKey] = $this->parent->$pk; + $pk = $this->parent->getPk(); + $pivot[] = [$this->localKey, '=', $this->parent->$pk]; + if (isset($id)) { - $pivot[$this->foreignKey] = is_array($id) ? ['in', $id] : $id; + $pivot[] = [$this->foreignKey, is_array($id) ? 'in' : '=', $id]; } - $this->pivot->where($pivot)->delete(); + + $result = $this->pivot->where($pivot)->delete(); + // 删除关联表数据 if (isset($id) && $relationDel) { $model = $this->model; $model::destroy($id); } + + return $result; } /** * 数据同步 - * @param array $ids - * @param bool $detaching + * @access public + * @param array $ids + * @param bool $detaching * @return array */ public function sync($ids, $detaching = true) @@ -518,9 +653,13 @@ class BelongsToMany extends Relation 'detached' => [], 'updated' => [], ]; - $pk = $this->parent->getPk(); - $current = $this->pivot->where($this->localKey, $this->parent->$pk) + + $pk = $this->parent->getPk(); + + $current = $this->pivot + ->where($this->localKey, $this->parent->$pk) ->column($this->foreignKey); + $records = []; foreach ($ids as $key => $value) { @@ -535,7 +674,6 @@ class BelongsToMany extends Relation if ($detaching && count($detach) > 0) { $this->detach($detach); - $changes['detached'] = $detach; } @@ -543,19 +681,16 @@ class BelongsToMany extends Relation if (!in_array($id, $current)) { $this->attach($id, $attributes); $changes['attached'][] = $id; - } elseif (count($attributes) > 0 && - $this->attach($id, $attributes) - ) { + } elseif (count($attributes) > 0 && $this->attach($id, $attributes)) { $changes['updated'][] = $id; } } return $changes; - } /** - * 执行基础查询(进执行一次) + * 执行基础查询(仅执行一次) * @access protected * @return void */ @@ -564,7 +699,10 @@ class BelongsToMany extends Relation if (empty($this->baseQuery) && $this->parent->getData()) { $pk = $this->parent->getPk(); $table = $this->pivot->getTable(); - $this->query->join($table . ' pivot', 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk())->where('pivot.' . $this->localKey, $this->parent->$pk); + + $this->query + ->join([$table => 'pivot'], 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk()) + ->where('pivot.' . $this->localKey, $this->parent->$pk); $this->baseQuery = true; } } diff --git a/thinkphp/library/think/model/relation/HasMany.php b/thinkphp/library/think/model/relation/HasMany.php index 9624fc676..dbb8fa083 100644 --- a/thinkphp/library/think/model/relation/HasMany.php +++ b/thinkphp/library/think/model/relation/HasMany.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -19,12 +19,12 @@ use think\model\Relation; class HasMany extends Relation { /** - * 构造函数 + * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型主键 */ public function __construct(Model $parent, $model, $foreignKey, $localKey) { @@ -33,20 +33,30 @@ class HasMany extends Relation $this->foreignKey = $foreignKey; $this->localKey = $localKey; $this->query = (new $model)->db(); + + if (get_class($parent) == $model) { + $this->selfRelation = true; + } } /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return false|\PDOStatement|string|\think\Collection + * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return \think\Collection */ public function getRelation($subRelation = '', $closure = null) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $closure($this->query); } - $list = $this->relation($subRelation)->select(); + + $list = $this->query + ->where($this->foreignKey, $this->parent->{$this->localKey}) + ->relation($subRelation) + ->select(); + $parent = clone $this->parent; foreach ($list as &$model) { @@ -58,17 +68,18 @@ class HasMany extends Relation /** * 预载入关联查询 - * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) { $localKey = $this->localKey; $range = []; + foreach ($resultSet as $result) { // 获取关联外键列表 if (isset($result->$localKey)) { @@ -77,36 +88,37 @@ class HasMany extends Relation } if (!empty($range)) { - $data = $this->eagerlyOneToMany($this, [ - $this->foreignKey => [ - 'in', - $range, - ], - ], $relation, $subRelation, $closure); + $where = [ + [$this->foreignKey, 'in', $range], + ]; + $data = $this->eagerlyOneToMany($where, $relation, $subRelation, $closure); + // 关联属性名 $attr = Loader::parseName($relation); + // 关联数据封装 foreach ($resultSet as $result) { - if (!isset($data[$result->$localKey])) { - $data[$result->$localKey] = []; + $pk = $result->$localKey; + if (!isset($data[$pk])) { + $data[$pk] = []; } - foreach ($data[$result->$localKey] as &$relationModel) { + foreach ($data[$pk] as &$relationModel) { $relationModel->setParent(clone $result); } - $result->setRelation($attr, $this->resultSetBuild($data[$result->$localKey])); + $result->setRelation($attr, $this->resultSetBuild($data[$pk])); } } } /** * 预载入关联查询 - * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ public function eagerlyResult(&$result, $relation, $subRelation, $closure) @@ -114,164 +126,213 @@ class HasMany extends Relation $localKey = $this->localKey; if (isset($result->$localKey)) { - $data = $this->eagerlyOneToMany($this, [$this->foreignKey => $result->$localKey], $relation, $subRelation, $closure); + $pk = $result->$localKey; + $where = [ + [$this->foreignKey, '=', $pk], + ]; + $data = $this->eagerlyOneToMany($where, $relation, $subRelation, $closure); + // 关联数据封装 - if (!isset($data[$result->$localKey])) { - $data[$result->$localKey] = []; + if (!isset($data[$pk])) { + $data[$pk] = []; } - foreach ($data[$result->$localKey] as &$relationModel) { + foreach ($data[$pk] as &$relationModel) { $relationModel->setParent(clone $result); } - $result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$localKey])); + $result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$pk])); } } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount($result, $closure) + public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '') { $localKey = $this->localKey; - $count = 0; - if (isset($result->$localKey)) { - if ($closure) { - call_user_func_array($closure, [ & $this->query]); - } - $count = $this->query->where([$this->foreignKey => $result->$localKey])->count(); + + if (!isset($result->$localKey)) { + return 0; } - return $count; + + if ($closure) { + $return = $closure($this->query); + if ($return && is_string($return)) { + $name = $return; + } + } + + return $this->query + ->where($this->foreignKey, '=', $result->$localKey) + ->$aggregate($field); } /** * 创建关联统计子查询 * @access public - * @param \Closure $closure 闭包 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $aggregateAlias 聚合字段别名 * @return string */ - public function getRelationCountQuery($closure) + public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '') { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $return = $closure($this->query); + + if ($return && is_string($return)) { + $aggregateAlias = $return; + } } - return $this->query->where([ - $this->foreignKey => [ - 'exp', - '=' . $this->parent->getTable() . '.' . $this->parent->getPk(), - ], - ])->fetchSql()->count(); + return $this->query + ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey) + ->fetchSql() + ->$aggregate($field); } /** * 一对多 关联模型预查询 * @access public - * @param object $model 关联模型对象 - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param bool $closure + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param \Closure $closure * @return array */ - protected function eagerlyOneToMany($model, $where, $relation, $subRelation = '', $closure = false) + protected function eagerlyOneToMany($where, $relation, $subRelation = '', $closure = null) { $foreignKey = $this->foreignKey; + + $this->query->removeWhereField($this->foreignKey); + // 预载入关联查询 支持嵌套预载入 if ($closure) { - call_user_func_array($closure, [ & $model]); + $closure($this->query); } - $list = $model->where($where)->with($subRelation)->select(); + + $list = $this->query->where($where)->with($subRelation)->select(); // 组装模型数据 $data = []; + foreach ($list as $set) { $data[$set->$foreignKey][] = $set; } + return $data; } /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param boolean $replace 是否自动识别更新和写入 * @return Model|false */ - public function save($data) + public function save($data, $replace = true) + { + $model = $this->make(); + + return $model->replace($replace)->save($data) ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []) { if ($data instanceof Model) { $data = $data->getData(); } + // 保存关联表数据 - $model = new $this->model; $data[$this->foreignKey] = $this->parent->{$this->localKey}; - return $model->save($data) ? $model : false; + + return new $this->model($data); } /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 - * @return integer + * @param array $dataSet 数据集 + * @param boolean $replace 是否自动识别更新和写入 + * @return array|false */ - public function saveAll(array $dataSet) + public function saveAll(array $dataSet, $replace = true) { - $result = false; + $result = []; + foreach ($dataSet as $key => $data) { - $result = $this->save($data); + $result[] = $this->save($data, $replace); } - return $result; + + return empty($result) ? false : $result; } /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') { - $table = $this->query->getTable(); - return $this->parent->db()->alias('a') - ->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $joinType) - ->group('b.' . $this->foreignKey) + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); + + return $this->parent->db() + ->alias($model) + ->field($model . '.*') + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType) + ->group($relation . '.' . $this->foreignKey) ->having('count(' . $id . ')' . $operator . $count); } /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { $table = $this->query->getTable(); $model = basename(str_replace('\\', '/', get_class($this->parent))); $relation = basename(str_replace('\\', '/', $this->model)); + if (is_array($where)) { - foreach ($where as $key => $val) { - if (false === strpos($key, '.')) { - $where[$relation . '.' . $key] = $val; - unset($where[$key]); - } - } + $this->getQueryWhere($where, $relation); } - return $this->parent->db()->alias($model) - ->field($model . '.*') - ->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) + + $fields = $this->getRelationQueryFields($fields, $model); + + return $this->parent->db() + ->alias($model) + ->group($model . '.' . $this->localKey) + ->field($fields) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) ->where($where); } /** - * 执行基础查询(进执行一次) + * 执行基础查询(仅执行一次) * @access protected * @return void */ @@ -280,8 +341,9 @@ class HasMany extends Relation if (empty($this->baseQuery)) { if (isset($this->parent->{$this->localKey})) { // 关联查询带入关联条件 - $this->query->where($this->foreignKey, $this->parent->{$this->localKey}); + $this->query->where($this->foreignKey, '=', $this->parent->{$this->localKey}); } + $this->baseQuery = true; } } diff --git a/thinkphp/library/think/model/relation/HasManyThrough.php b/thinkphp/library/think/model/relation/HasManyThrough.php index 7d0d5124d..7c7acaa07 100644 --- a/thinkphp/library/think/model/relation/HasManyThrough.php +++ b/thinkphp/library/think/model/relation/HasManyThrough.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -25,14 +25,14 @@ class HasManyThrough extends Relation protected $through; /** - * 构造函数 - * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $through 中间模型名 - * @param string $foreignKey 关联外键 - * @param string $throughKey 关联外键 - * @param string $localKey 关联主键 + * 架构函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $through 中间模型名 + * @param string $foreignKey 关联外键 + * @param string $throughKey 关联外键 + * @param string $localKey 当前主键 */ public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey) { @@ -47,26 +47,29 @@ class HasManyThrough extends Relation /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return false|\PDOStatement|string|\think\Collection + * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return \think\Collection */ public function getRelation($subRelation = '', $closure = null) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $closure($this->query); } - return $this->relation($subRelation)->select(); + $this->baseQuery(); + + return $this->query->relation($subRelation)->select(); } /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') @@ -77,10 +80,11 @@ class HasManyThrough extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } @@ -88,41 +92,42 @@ class HasManyThrough extends Relation /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) {} /** * 预载入关联查询 返回模型对象 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 - * @param string $class 数据集对象名 为空表示数组 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) + public function eagerlyResult(&$result, $relation, $subRelation, $closure) {} /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount($result, $closure) + public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '') {} /** - * 执行基础查询(进执行一次) + * 执行基础查询(仅执行一次) * @access protected * @return void */ @@ -130,16 +135,20 @@ class HasManyThrough extends Relation { if (empty($this->baseQuery) && $this->parent->getData()) { $through = $this->through; - $model = $this->model; - $alias = Loader::parseName(basename(str_replace('\\', '/', $model))); + $alias = Loader::parseName(basename(str_replace('\\', '/', $this->model))); $throughTable = $through::getTable(); - $pk = (new $this->model)->getPk(); + $pk = (new $through)->getPk(); $throughKey = $this->throughKey; $modelTable = $this->parent->getTable(); - $this->query->field($alias . '.*')->alias($alias) + $fields = $this->getQueryFields($alias); + + $this->query + ->field($fields) + ->alias($alias) ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey) ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey) ->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey}); + $this->baseQuery = true; } } diff --git a/thinkphp/library/think/model/relation/HasOne.php b/thinkphp/library/think/model/relation/HasOne.php index c500c02ab..d8e3ec798 100644 --- a/thinkphp/library/think/model/relation/HasOne.php +++ b/thinkphp/library/think/model/relation/HasOne.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -18,39 +18,48 @@ use think\Model; class HasOne extends OneToOne { /** - * 构造函数 + * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $foreignKey 关联外键 - * @param string $localKey 关联主键 - * @param string $joinType JOIN类型 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型主键 */ - public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER') + public function __construct(Model $parent, $model, $foreignKey, $localKey) { $this->parent = $parent; $this->model = $model; $this->foreignKey = $foreignKey; $this->localKey = $localKey; - $this->joinType = $joinType; + $this->joinType = 'INNER'; $this->query = (new $model)->db(); + + if (get_class($parent) == $model) { + $this->selfRelation = true; + } } /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return array|false|\PDOStatement|string|Model + * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return Model */ public function getRelation($subRelation = '', $closure = null) { - // 执行关联定义方法 $localKey = $this->localKey; + if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $closure($this->query); } + // 判断关联类型执行查询 - $relationModel = $this->query->where($this->foreignKey, $this->parent->$localKey)->relation($subRelation)->find(); + $relationModel = $this->query + ->removeWhereField($this->foreignKey) + ->where($this->foreignKey, $this->parent->$localKey) + ->relation($subRelation) + ->find(); if ($relationModel) { $relationModel->setParent(clone $this->parent); @@ -59,54 +68,120 @@ class HasOne extends OneToOne return $relationModel; } + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $aggregateAlias 聚合字段别名 + * @return string + */ + public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '') + { + if ($closure) { + $return = $closure($this->query); + + if ($return && is_string($return)) { + $aggregateAlias = $return; + } + } + + return $this->query + ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey) + ->fetchSql() + ->$aggregate($field); + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 + * @return integer + */ + public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '') + { + $localKey = $this->localKey; + + if (!isset($result->$localKey)) { + return 0; + } + + if ($closure) { + $return = $closure($this->query); + if ($return && is_string($return)) { + $name = $return; + } + } + + return $this->query + ->where($this->foreignKey, '=', $result->$localKey) + ->$aggregate($field); + } + /** * 根据关联条件查询当前模型 * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ - public function has() + public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') { $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); $localKey = $this->localKey; $foreignKey = $this->foreignKey; - return $this->parent->db()->alias('a') - ->whereExists(function ($query) use ($table, $localKey, $foreignKey) { - $query->table([$table => 'b'])->field('b.' . $foreignKey)->whereExp('a.' . $localKey, '=b.' . $foreignKey); + + return $this->parent->db() + ->alias($model) + ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) { + $query->table([$table => $relation]) + ->field($relation . '.' . $foreignKey) + ->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey); }); } /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { $table = $this->query->getTable(); $model = basename(str_replace('\\', '/', get_class($this->parent))); $relation = basename(str_replace('\\', '/', $this->model)); + if (is_array($where)) { - foreach ($where as $key => $val) { - if (false === strpos($key, '.')) { - $where[$relation . '.' . $key] = $val; - unset($where[$key]); - } - } + $this->getQueryWhere($where, $relation); } - return $this->parent->db()->alias($model) - ->field($model . '.*') - ->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType) + + $fields = $this->getRelationQueryFields($fields, $model); + + return $this->parent->db() + ->alias($model) + ->field($fields) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType) ->where($where); } /** * 预载入关联查询(数据集) - * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @access protected + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) @@ -123,14 +198,15 @@ class HasOne extends OneToOne } if (!empty($range)) { - $data = $this->eagerlyWhere($this, [ - $foreignKey => [ - 'in', - $range, - ], + $this->query->removeWhereField($foreignKey); + + $data = $this->eagerlyWhere([ + [$foreignKey, 'in', $range], ], $foreignKey, $relation, $subRelation, $closure); + // 关联属性名 $attr = Loader::parseName($relation); + // 关联数据封装 foreach ($resultSet as $result) { // 关联模型 @@ -140,31 +216,38 @@ class HasOne extends OneToOne $relationModel = $data[$result->$localKey]; $relationModel->setParent(clone $result); $relationModel->isUpdate(true); - if (!empty($this->bindAttr)) { - // 绑定关联属性 - $this->bindAttr($relationModel, $result, $this->bindAttr); - } } - // 设置关联属性 - $result->setRelation($attr, $relationModel); + + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } else { + // 设置关联属性 + $result->setRelation($attr, $relationModel); + } } } } /** * 预载入关联查询(数据) - * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @access protected + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ protected function eagerlyOne(&$result, $relation, $subRelation, $closure) { $localKey = $this->localKey; $foreignKey = $this->foreignKey; - $data = $this->eagerlyWhere($this, [$foreignKey => $result->$localKey], $foreignKey, $relation, $subRelation, $closure); + + $this->query->removeWhereField($foreignKey); + + $data = $this->eagerlyWhere([ + [$foreignKey, '=', $result->$localKey], + ], $foreignKey, $relation, $subRelation, $closure); // 关联模型 if (!isset($data[$result->$localKey])) { @@ -173,13 +256,30 @@ class HasOne extends OneToOne $relationModel = $data[$result->$localKey]; $relationModel->setParent(clone $result); $relationModel->isUpdate(true); - if (!empty($this->bindAttr)) { - // 绑定关联属性 - $this->bindAttr($relationModel, $result, $this->bindAttr); - } } - $result->setRelation(Loader::parseName($relation), $relationModel); + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } else { + $result->setRelation(Loader::parseName($relation), $relationModel); + } } + /** + * 执行基础查询(仅执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + { + if (empty($this->baseQuery)) { + if (isset($this->parent->{$this->localKey})) { + // 关联查询带入关联条件 + $this->query->where($this->foreignKey, '=', $this->parent->{$this->localKey}); + } + + $this->baseQuery = true; + } + } } diff --git a/thinkphp/library/think/model/relation/MorphMany.php b/thinkphp/library/think/model/relation/MorphMany.php index 87c2d66c3..a1f54889b 100644 --- a/thinkphp/library/think/model/relation/MorphMany.php +++ b/thinkphp/library/think/model/relation/MorphMany.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -26,13 +26,13 @@ class MorphMany extends Relation protected $type; /** - * 构造函数 + * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $morphKey 关联外键 - * @param string $morphType 多态字段名 - * @param string $type 多态类型 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $morphKey 关联外键 + * @param string $morphType 多态字段名 + * @param string $type 多态类型 */ public function __construct(Model $parent, $model, $morphKey, $morphType, $type) { @@ -46,16 +46,20 @@ class MorphMany extends Relation /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return false|\PDOStatement|string|\think\Collection + * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return \think\Collection */ public function getRelation($subRelation = '', $closure = null) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $closure($this->query); } - $list = $this->relation($subRelation)->select(); + + $this->baseQuery(); + + $list = $this->query->relation($subRelation)->select(); $parent = clone $this->parent; foreach ($list as &$model) { @@ -68,10 +72,10 @@ class MorphMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') @@ -82,10 +86,11 @@ class MorphMany extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } @@ -93,10 +98,10 @@ class MorphMany extends Relation /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) @@ -105,6 +110,7 @@ class MorphMany extends Relation $morphKey = $this->morphKey; $type = $this->type; $range = []; + foreach ($resultSet as $result) { $pk = $result->getPk(); // 获取关联外键列表 @@ -114,21 +120,26 @@ class MorphMany extends Relation } if (!empty($range)) { - $data = $this->eagerlyMorphToMany([ - $morphKey => ['in', $range], - $morphType => $type, - ], $relation, $subRelation, $closure); + $where = [ + [$morphKey, 'in', $range], + [$morphType, '=', $type], + ]; + $data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure); + // 关联属性名 $attr = Loader::parseName($relation); + // 关联数据封装 foreach ($resultSet as $result) { if (!isset($data[$result->$pk])) { $data[$result->$pk] = []; } + foreach ($data[$result->$pk] as &$relationModel) { $relationModel->setParent(clone $result); $relationModel->isUpdate(true); } + $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk])); } } @@ -137,147 +148,192 @@ class MorphMany extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ public function eagerlyResult(&$result, $relation, $subRelation, $closure) { $pk = $result->getPk(); - if (isset($result->$pk)) { - $data = $this->eagerlyMorphToMany([ - $this->morphKey => $result->$pk, - $this->morphType => $this->type, - ], $relation, $subRelation, $closure); - if (!isset($data[$result->$pk])) { - $data[$result->$pk] = []; + if (isset($result->$pk)) { + $key = $result->$pk; + $where = [ + [$this->morphKey, '=', $key], + [$this->morphType, '=', $this->type], + ]; + $data = $this->eagerlyMorphToMany($where, $relation, $subRelation, $closure); + + if (!isset($data[$key])) { + $data[$key] = []; } - foreach ($data[$result->$pk] as &$relationModel) { + foreach ($data[$key] as &$relationModel) { $relationModel->setParent(clone $result); $relationModel->isUpdate(true); } - $result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$pk])); + $result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$key])); } } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount($result, $closure) + public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '') { - $pk = $result->getPk(); - $count = 0; - if (isset($result->$pk)) { - if ($closure) { - call_user_func_array($closure, [ & $this->query]); - } - $count = $this->query->where([$this->morphKey => $result->$pk, $this->morphType => $this->type])->count(); + $pk = $result->getPk(); + + if (!isset($result->$pk)) { + return 0; } - return $count; + + if ($closure) { + $return = $closure($this->query); + + if ($return && is_string($return)) { + $name = $return; + } + } + + return $this->query + ->where([ + [$this->morphKey, '=', $result->$pk], + [$this->morphType, '=', $this->type], + ]) + ->$aggregate($field); } /** * 获取关联统计子查询 * @access public - * @param \Closure $closure 闭包 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $aggregateAlias 聚合字段别名 * @return string */ - public function getRelationCountQuery($closure) + public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '') { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $return = $closure($this->query); + + if ($return && is_string($return)) { + $aggregateAlias = $return; + } } - return $this->query->where([ - $this->morphKey => [ - 'exp', - '=' . $this->parent->getTable() . '.' . $this->parent->getPk(), - ], - $this->morphType => $this->type, - ])->fetchSql()->count(); + return $this->query + ->whereExp($this->morphKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk()) + ->where($this->morphType, '=', $this->type) + ->fetchSql() + ->$aggregate($field); } /** * 多态一对多 关联模型预查询 - * @access public - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param bool|\Closure $closure 闭包 + * @access protected + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param \Closure $closure 闭包 * @return array */ - protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $closure = false) + protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $closure = null) { // 预载入关联查询 支持嵌套预载入 + $this->query->removeOption('where'); + if ($closure) { - call_user_func_array($closure, [ & $this]); + $closure($this->query); } + $list = $this->query->where($where)->with($subRelation)->select(); $morphKey = $this->morphKey; + // 组装模型数据 $data = []; foreach ($list as $set) { $data[$set->$morphKey][] = $set; } + return $data; } /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 * @return Model|false */ public function save($data) + { + $model = $this->make(); + + return $model->save($data) ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []) { if ($data instanceof Model) { $data = $data->getData(); } + // 保存关联表数据 $pk = $this->parent->getPk(); - $model = new $this->model; $data[$this->morphKey] = $this->parent->$pk; $data[$this->morphType] = $this->type; - return $model->save($data) ? $model : false; + + return new $this->model($data); } /** * 批量保存当前关联数据对象 * @access public - * @param array $dataSet 数据集 - * @return integer + * @param array $dataSet 数据集 + * @return array|false */ public function saveAll(array $dataSet) { - $result = false; + $result = []; + foreach ($dataSet as $key => $data) { - $result = $this->save($data); + $result[] = $this->save($data); } - return $result; + + return empty($result) ? false : $result; } /** - * 执行基础查询(进执行一次) + * 执行基础查询(仅执行一次) * @access protected * @return void */ protected function baseQuery() { if (empty($this->baseQuery) && $this->parent->getData()) { - $pk = $this->parent->getPk(); - $map[$this->morphKey] = $this->parent->$pk; - $map[$this->morphType] = $this->type; - $this->query->where($map); + $pk = $this->parent->getPk(); + + $this->query->where([ + [$this->morphKey, '=', $this->parent->$pk], + [$this->morphType, '=', $this->type], + ]); + $this->baseQuery = true; } } diff --git a/thinkphp/library/think/model/relation/MorphOne.php b/thinkphp/library/think/model/relation/MorphOne.php index 4dfd1c0ff..775b2dfd7 100644 --- a/thinkphp/library/think/model/relation/MorphOne.php +++ b/thinkphp/library/think/model/relation/MorphOne.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -28,11 +28,11 @@ class MorphOne extends Relation /** * 构造函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $model 模型名 - * @param string $morphKey 关联外键 - * @param string $morphType 多态字段名 - * @param string $type 多态类型 + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $morphKey 关联外键 + * @param string $morphType 多态字段名 + * @param string $type 多态类型 */ public function __construct(Model $parent, $model, $morphKey, $morphType, $type) { @@ -46,16 +46,20 @@ class MorphOne extends Relation /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return false|\PDOStatement|string|\think\Collection + * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return Model */ public function getRelation($subRelation = '', $closure = null) { if ($closure) { - call_user_func_array($closure, [ & $this->query]); + $closure($this->query); } - $relationModel = $this->relation($subRelation)->find(); + + $this->baseQuery(); + + $relationModel = $this->query->relation($subRelation)->find(); if ($relationModel) { $relationModel->setParent(clone $this->parent); @@ -67,10 +71,10 @@ class MorphOne extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') @@ -81,10 +85,11 @@ class MorphOne extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } @@ -92,10 +97,10 @@ class MorphOne extends Relation /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) @@ -104,6 +109,7 @@ class MorphOne extends Relation $morphKey = $this->morphKey; $type = $this->type; $range = []; + foreach ($resultSet as $result) { $pk = $result->getPk(); // 获取关联外键列表 @@ -114,11 +120,13 @@ class MorphOne extends Relation if (!empty($range)) { $data = $this->eagerlyMorphToOne([ - $morphKey => ['in', $range], - $morphType => $type, + [$morphKey, 'in', $range], + [$morphType, '=', $type], ], $relation, $subRelation, $closure); + // 关联属性名 $attr = Loader::parseName($relation); + // 关联数据封装 foreach ($resultSet as $result) { if (!isset($data[$result->$pk])) { @@ -137,20 +145,21 @@ class MorphOne extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ public function eagerlyResult(&$result, $relation, $subRelation, $closure) { $pk = $result->getPk(); + if (isset($result->$pk)) { $pk = $result->$pk; $data = $this->eagerlyMorphToOne([ - $this->morphKey => $pk, - $this->morphType => $this->type, + [$this->morphKey, '=', $pk], + [$this->morphType, '=', $this->type], ], $relation, $subRelation, $closure); if (isset($data[$pk])) { @@ -167,47 +176,63 @@ class MorphOne extends Relation /** * 多态一对一 关联模型预查询 - * @access public - * @param array $where 关联预查询条件 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param bool|\Closure $closure 闭包 + * @access protected + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param \Closure $closure 闭包 * @return array */ - protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $closure = false) + protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $closure = null) { // 预载入关联查询 支持嵌套预载入 if ($closure) { - call_user_func_array($closure, [ & $this]); + $closure($this->query); } - $list = $this->query->where($where)->with($subRelation)->find(); + + $list = $this->query->where($where)->with($subRelation)->select(); $morphKey = $this->morphKey; + // 组装模型数据 $data = []; + foreach ($list as $set) { - $data[$set->$morphKey][] = $set; + $data[$set->$morphKey] = $set; } + return $data; } /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 * @return Model|false */ public function save($data) + { + $model = $this->make(); + return $model->save($data) ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []) { if ($data instanceof Model) { $data = $data->getData(); } + // 保存关联表数据 $pk = $this->parent->getPk(); - $model = new $this->model; $data[$this->morphKey] = $this->parent->$pk; $data[$this->morphType] = $this->type; - return $model->save($data) ? $model : false; + + return new $this->model($data); } /** @@ -218,10 +243,12 @@ class MorphOne extends Relation protected function baseQuery() { if (empty($this->baseQuery) && $this->parent->getData()) { - $pk = $this->parent->getPk(); - $map[$this->morphKey] = $this->parent->$pk; - $map[$this->morphType] = $this->type; - $this->query->where($map); + $pk = $this->parent->getPk(); + + $this->query->where([ + [$this->morphKey, '=', $this->parent->$pk], + [$this->morphType, '=', $this->type], + ]); $this->baseQuery = true; } } diff --git a/thinkphp/library/think/model/relation/MorphTo.php b/thinkphp/library/think/model/relation/MorphTo.php index df104a907..6da6a0252 100644 --- a/thinkphp/library/think/model/relation/MorphTo.php +++ b/thinkphp/library/think/model/relation/MorphTo.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -23,16 +23,17 @@ class MorphTo extends Relation protected $morphType; // 多态别名 protected $alias; + // 关联名 protected $relation; /** - * 构造函数 + * 架构函数 * @access public - * @param Model $parent 上级模型对象 - * @param string $morphType 多态字段名 - * @param string $morphKey 外键名 - * @param array $alias 多态别名定义 - * @param string $relation 关联名 + * @param Model $parent 上级模型对象 + * @param string $morphType 多态字段名 + * @param string $morphKey 外键名 + * @param array $alias 多态别名定义 + * @param string $relation 关联名 */ public function __construct(Model $parent, $morphType, $morphKey, $alias = [], $relation = null) { @@ -43,35 +44,53 @@ class MorphTo extends Relation $this->relation = $relation; } + /** + * 获取当前的关联模型类的实例 + * @access public + * @return Model + */ + public function getModel() + { + $morphType = $this->morphType; + $model = $this->parseModel($this->parent->$morphType); + + return (new $model); + } + /** * 延迟获取关联数据 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包查询条件 - * @return mixed + * @access public + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return Model */ public function getRelation($subRelation = '', $closure = null) { $morphKey = $this->morphKey; $morphType = $this->morphType; + // 多态模型 $model = $this->parseModel($this->parent->$morphType); + // 主键数据 - $pk = $this->parent->$morphKey; + $pk = $this->parent->$morphKey; + $relationModel = (new $model)->relation($subRelation)->find($pk); if ($relationModel) { $relationModel->setParent(clone $this->parent); } + return $relationModel; } /** * 根据关联条件查询当前模型 * @access public - * @param string $operator 比较操作符 - * @param integer $count 个数 - * @param string $id 关联表的统计字段 - * @param string $joinType JOIN类型 + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 * @return Query */ public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') @@ -82,18 +101,19 @@ class MorphTo extends Relation /** * 根据关联条件查询当前模型 * @access public - * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 * @return Query */ - public function hasWhere($where = []) + public function hasWhere($where = [], $fields = null) { throw new Exception('relation not support: hasWhere'); } /** * 解析模型的完整命名空间 - * @access public - * @param string $model 模型名(或者完整类名) + * @access protected + * @param string $model 模型名(或者完整类名) * @return string */ protected function parseModel($model) @@ -101,24 +121,27 @@ class MorphTo extends Relation if (isset($this->alias[$model])) { $model = $this->alias[$model]; } + if (false === strpos($model, '\\')) { $path = explode('\\', get_class($this->parent)); array_pop($path); array_push($path, Loader::parseName($model, 1)); $model = implode('\\', $path); } + return $model; } /** * 设置多态别名 * @access public - * @param array $alias 别名定义 + * @param array $alias 别名定义 * @return $this */ public function setAlias($alias) { $this->alias = $alias; + return $this; } @@ -135,10 +158,10 @@ class MorphTo extends Relation /** * 预载入关联查询 * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void * @throws Exception */ @@ -147,6 +170,7 @@ class MorphTo extends Relation $morphKey = $this->morphKey; $morphType = $this->morphType; $range = []; + foreach ($resultSet as $result) { // 获取关联外键列表 if (!empty($result->$morphKey)) { @@ -157,6 +181,7 @@ class MorphTo extends Relation if (!empty($range)) { // 关联属性名 $attr = Loader::parseName($relation); + foreach ($range as $key => $val) { // 多态类型映射 $model = $this->parseModel($key); @@ -164,21 +189,23 @@ class MorphTo extends Relation $pk = $obj->getPk(); $list = $obj->all($val, $subRelation); $data = []; + foreach ($list as $k => $vo) { $data[$vo->$pk] = $vo; } + foreach ($resultSet as $result) { if ($key == $result->$morphType) { // 关联模型 if (!isset($data[$result->$morphKey])) { - throw new Exception('relation data not exists :' . $this->model); + $relationModel = null; } else { $relationModel = $data[$result->$morphKey]; $relationModel->setParent(clone $result); $relationModel->isUpdate(true); - - $result->setRelation($attr, $relationModel); } + + $result->setRelation($attr, $relationModel); } } } @@ -188,10 +215,10 @@ class MorphTo extends Relation /** * 预载入关联查询 * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 * @return void */ public function eagerlyResult(&$result, $relation, $subRelation, $closure) @@ -200,27 +227,30 @@ class MorphTo extends Relation $morphType = $this->morphType; // 多态类型映射 $model = $this->parseModel($result->{$this->morphType}); + $this->eagerlyMorphToOne($model, $relation, $result, $subRelation); } /** * 关联统计 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @param string $aggregate 聚合查询方法 + * @param string $field 字段 + * @param string $name 统计字段别名 * @return integer */ - public function relationCount($result, $closure) - { - } + public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '') + {} /** * 多态MorphTo 关联模型预查询 - * @access public - * @param object $model 关联模型对象 - * @param string $relation 关联名 - * @param $result - * @param string $subRelation 子关联 + * @access protected + * @param string $model 关联模型对象 + * @param string $relation 关联名 + * @param Model $result + * @param string $subRelation 子关联 * @return void */ protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = '') @@ -228,27 +258,30 @@ class MorphTo extends Relation // 预载入关联查询 支持嵌套预载入 $pk = $this->parent->{$this->morphKey}; $data = (new $model)->with($subRelation)->find($pk); + if ($data) { $data->setParent(clone $result); $data->isUpdate(true); } + $result->setRelation(Loader::parseName($relation), $data ?: null); } /** * 添加关联数据 * @access public - * @param Model $model 关联模型对象 + * @param Model $model 关联模型对象 + * @param string $type 多态类型 * @return Model */ - public function associate($model) + public function associate($model, $type = '') { $morphKey = $this->morphKey; $morphType = $this->morphType; $pk = $model->getPk(); $this->parent->setAttr($morphKey, $model->$pk); - $this->parent->setAttr($morphType, get_class($model)); + $this->parent->setAttr($morphType, $type ?: get_class($model)); $this->parent->save(); return $this->parent->setRelation($this->relation, $model); @@ -271,11 +304,4 @@ class MorphTo extends Relation return $this->parent->setRelation($this->relation, null); } - /** - * 执行基础查询(进执行一次) - * @access protected - * @return void - */ - protected function baseQuery() - {} } diff --git a/thinkphp/library/think/model/relation/OneToOne.php b/thinkphp/library/think/model/relation/OneToOne.php index ff066ed3d..59fce77fb 100644 --- a/thinkphp/library/think/model/relation/OneToOne.php +++ b/thinkphp/library/think/model/relation/OneToOne.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -30,13 +30,13 @@ abstract class OneToOne extends Relation protected $joinType; // 要绑定的属性 protected $bindAttr = []; - // 关联方法名 + // 关联名 protected $relation; /** * 设置join类型 * @access public - * @param string $type JOIN类型 + * @param string $type JOIN类型 * @return $this */ public function joinType($type) @@ -48,72 +48,78 @@ abstract class OneToOne extends Relation /** * 预载入关联查询(JOIN方式) * @access public - * @param Query $query 查询对象 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param \Closure $closure 闭包条件 - * @param bool $first + * @param Query $query 查询对象 + * @param string $relation 关联名 + * @param mixed $field 关联字段 + * @param string $joinType JOIN方式 + * @param \Closure $closure 闭包条件 + * @param bool $first * @return void */ - public function eagerly(Query $query, $relation, $subRelation, $closure, $first) + public function eagerly(Query $query, $relation, $field, $joinType, $closure, $first) { - $name = Loader::parseName(basename(str_replace('\\', '/', $query->getModel()))); - $alias = $name; + $name = Loader::parseName(basename(str_replace('\\', '/', get_class($this->parent)))); + if ($first) { $table = $query->getTable(); - $query->table([$table => $alias]); + $query->table([$table => $name]); + if ($query->getOptions('field')) { - $field = $query->getOptions('field'); + $masterField = $query->getOptions('field'); $query->removeOption('field'); } else { - $field = true; + $masterField = true; } - $query->field($field, false, $table, $alias); - $field = null; + + $query->field($masterField, false, $table, $name); } // 预载入封装 $joinTable = $this->query->getTable(); $joinAlias = $relation; + $joinType = $joinType ?: $this->joinType; + $query->via($joinAlias); if ($this instanceof BelongsTo) { - $query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey, $this->joinType); + $joinOn = $name . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey; } else { - $query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey, $this->joinType); + $joinOn = $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey; } if ($closure) { // 执行闭包查询 - call_user_func_array($closure, [ & $query]); + $closure($query); // 使用withField指定获取关联的字段,如 // $query->where(['id'=>1])->withField('id,name'); if ($query->getOptions('with_field')) { $field = $query->getOptions('with_field'); $query->removeOption('with_field'); } - } elseif (isset($this->option['field'])) { - $field = $this->option['field']; } - $query->field(isset($field) ? $field : true, false, $joinTable, $joinAlias, $relation . '__'); + + $query->join([$joinTable => $joinAlias], $joinOn, $joinType) + ->field($field, false, $joinTable, $joinAlias, $relation . '__'); } /** * 预载入关联查询(数据集) - * @param array $resultSet - * @param string $relation - * @param string $subRelation - * @param \Closure $closure + * @access protected + * @param array $resultSet + * @param string $relation + * @param string $subRelation + * @param \Closure $closure * @return mixed */ abstract protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure); /** * 预载入关联查询(数据) - * @param Model $result - * @param string $relation - * @param string $subRelation - * @param \Closure $closure + * @access protected + * @param Model $result + * @param string $relation + * @param string $subRelation + * @param \Closure $closure * @return mixed */ abstract protected function eagerlyOne(&$result, $relation, $subRelation, $closure); @@ -121,49 +127,51 @@ abstract class OneToOne extends Relation /** * 预载入关联查询(数据集) * @access public - * @param array $resultSet 数据集 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param bool $join 是否为JOIN方式 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $join = false) { - if (1 == $this->eagerlyType) { - // IN查询 - $this->eagerlySet($resultSet, $relation, $subRelation, $closure); - } else { - // 模型关联组装 + if ($join || 0 == $this->eagerlyType) { + // 模型JOIN关联组装 foreach ($resultSet as $result) { $this->match($this->model, $relation, $result); } + } else { + // IN查询 + $this->eagerlySet($resultSet, $relation, $subRelation, $closure); } } /** * 预载入关联查询(数据) * @access public - * @param Model $result 数据对象 - * @param string $relation 当前关联名 - * @param string $subRelation 子关联名 - * @param \Closure $closure 闭包 + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param bool $join 是否为JOIN方式 * @return void */ - public function eagerlyResult(&$result, $relation, $subRelation, $closure) + public function eagerlyResult(&$result, $relation, $subRelation, $closure, $join = false) { - if (1 == $this->eagerlyType) { + if (0 == $this->eagerlyType || $join) { + // 模型JOIN关联组装 + $this->match($this->model, $relation, $result); + } else { // IN查询 $this->eagerlyOne($result, $relation, $subRelation, $closure); - } else { - // 模型关联组装 - $this->match($this->model, $relation, $result); } } /** * 保存(新增)当前关联数据对象 * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 * @return Model|false */ public function save($data) @@ -171,21 +179,24 @@ abstract class OneToOne extends Relation if ($data instanceof Model) { $data = $data->getData(); } + $model = new $this->model; // 保存关联表数据 $data[$this->foreignKey] = $this->parent->{$this->localKey}; + return $model->save($data) ? $model : false; } /** * 设置预载入方式 * @access public - * @param integer $type 预载入方式 0 JOIN查询 1 IN查询 + * @param integer $type 预载入方式 0 JOIN查询 1 IN查询 * @return $this */ public function setEagerlyType($type) { $this->eagerlyType = $type; + return $this; } @@ -202,7 +213,7 @@ abstract class OneToOne extends Relation /** * 绑定关联表的属性到父模型属性 * @access public - * @param mixed $attr 要绑定的属性列表 + * @param mixed $attr 要绑定的属性列表 * @return $this */ public function bind($attr) @@ -211,26 +222,26 @@ abstract class OneToOne extends Relation $attr = explode(',', $attr); } $this->bindAttr = $attr; + return $this; } /** - * 关联统计 + * 获取绑定属性 * @access public - * @param Model $result 数据对象 - * @param \Closure $closure 闭包 - * @return integer + * @return array */ - public function relationCount($result, $closure) + public function getBindAttr() { + return $this->bindAttr; } /** * 一对一 关联模型预查询拼装 * @access public - * @param string $model 模型名称 - * @param string $relation 关联名 - * @param Model $result 模型对象实例 + * @param string $model 模型名称 + * @param string $relation 关联名 + * @param Model $result 模型对象实例 * @return void */ protected function match($model, $relation, &$result) @@ -247,9 +258,15 @@ abstract class OneToOne extends Relation } if (isset($list[$relation])) { - $relationModel = new $model($list[$relation]); - $relationModel->setParent(clone $result); - $relationModel->isUpdate(true); + $array = array_unique($list[$relation]); + + if (count($array) == 1 && null === current($array)) { + $relationModel = null; + } else { + $relationModel = new $model($list[$relation]); + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + } if (!empty($this->bindAttr)) { $this->bindAttr($relationModel, $result, $this->bindAttr); @@ -257,26 +274,26 @@ abstract class OneToOne extends Relation } else { $relationModel = null; } + $result->setRelation(Loader::parseName($relation), $relationModel); } /** * 绑定关联属性到父模型 * @access protected - * @param Model $model 关联模型对象 - * @param Model $result 父模型对象 - * @param array $bindAttr 绑定属性 + * @param Model $model 关联模型对象 + * @param Model $result 父模型对象 * @return void * @throws Exception */ - protected function bindAttr($model, &$result, $bindAttr) + protected function bindAttr($model, &$result) { - foreach ($bindAttr as $key => $attr) { + foreach ($this->bindAttr as $key => $attr) { $key = is_numeric($key) ? $attr : $key; if (isset($result->$key)) { throw new Exception('bind attr has exists:' . $key); } else { - $result->setAttr($key, $model->$attr); + $result->setAttr($key, $model ? $model->$attr : null); } } } @@ -284,38 +301,34 @@ abstract class OneToOne extends Relation /** * 一对一 关联模型预查询(IN方式) * @access public - * @param object $model 关联模型对象 - * @param array $where 关联预查询条件 - * @param string $key 关联键名 - * @param string $relation 关联名 - * @param string $subRelation 子关联 - * @param bool|\Closure $closure + * @param array $where 关联预查询条件 + * @param string $key 关联键名 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param \Closure $closure * @return array */ - protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = '', $closure = false) + protected function eagerlyWhere($where, $key, $relation, $subRelation = '', $closure = null) { // 预载入关联查询 支持嵌套预载入 if ($closure) { - call_user_func_array($closure, [ & $model]); - if ($field = $model->getOptions('with_field')) { - $model->field($field)->removeOption('with_field'); + $closure($this->query); + + if ($field = $this->query->getOptions('with_field')) { + $this->query->field($field)->removeOption('with_field'); } } - $list = $model->where($where)->with($subRelation)->select(); + + $list = $this->query->where($where)->with($subRelation)->select(); // 组装模型数据 $data = []; + foreach ($list as $set) { $data[$set->$key] = $set; } + return $data; } - /** - * 执行基础查询(进执行一次) - * @access protected - * @return void - */ - protected function baseQuery() - {} } diff --git a/thinkphp/library/think/paginator/driver/Bootstrap.php b/thinkphp/library/think/paginator/driver/Bootstrap.php index 58fa94364..ab5315c06 100644 --- a/thinkphp/library/think/paginator/driver/Bootstrap.php +++ b/thinkphp/library/think/paginator/driver/Bootstrap.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -57,13 +57,14 @@ class Bootstrap extends Paginator */ protected function getLinks() { - if ($this->simple) + if ($this->simple) { return ''; + } $block = [ 'first' => null, 'slider' => null, - 'last' => null + 'last' => null, ]; $side = 3; @@ -196,7 +197,7 @@ class Bootstrap extends Paginator */ protected function getPageLinkWrapper($url, $page) { - if ($page == $this->currentPage()) { + if ($this->currentPage() == $page) { return $this->getActivePageWrapper($page); } diff --git a/thinkphp/library/think/process/exception/Faild.php b/thinkphp/library/think/process/exception/Faild.php new file mode 100644 index 000000000..38647bc11 --- /dev/null +++ b/thinkphp/library/think/process/exception/Faild.php @@ -0,0 +1,42 @@ + +// +---------------------------------------------------------------------- + +namespace think\process\exception; + +use think\Process; + +class Faild extends \RuntimeException +{ + + private $process; + + public function __construct(Process $process) + { + if ($process->isSuccessful()) { + throw new \InvalidArgumentException('Expected a failed process, but the given process was successful.'); + } + + $error = sprintf('The command "%s" failed.' . "\nExit Code: %s(%s)", $process->getCommandLine(), $process->getExitCode(), $process->getExitCodeText()); + + if (!$process->isOutputDisabled()) { + $error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", $process->getOutput(), $process->getErrorOutput()); + } + + parent::__construct($error); + + $this->process = $process; + } + + public function getProcess() + { + return $this->process; + } +} diff --git a/thinkphp/library/think/process/pipes/Windows.php b/thinkphp/library/think/process/pipes/Windows.php index bba7e9b86..1b8b0d4f2 100644 --- a/thinkphp/library/think/process/pipes/Windows.php +++ b/thinkphp/library/think/process/pipes/Windows.php @@ -196,7 +196,7 @@ class Windows extends Pipes return; } - if (null !== $w && 0 < count($r)) { + if (null !== $r && 0 < count($r)) { $data = ''; while ($dataread = fread($r['input'], self::CHUNK_SIZE)) { $data .= $dataread; diff --git a/thinkphp/library/think/response/Download.php b/thinkphp/library/think/response/Download.php new file mode 100644 index 000000000..5595f9ab4 --- /dev/null +++ b/thinkphp/library/think/response/Download.php @@ -0,0 +1,148 @@ + +// +---------------------------------------------------------------------- + +namespace think\response; + +use think\Exception; +use think\Response; + +class Download extends Response +{ + protected $expire = 360; + protected $name; + protected $mimeType; + protected $isContent = false; + protected $openinBrowser = false; + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + * @throws \Exception + */ + protected function output($data) + { + if (!$this->isContent && !is_file($data)) { + throw new Exception('file not exists:' . $data); + } + + ob_end_clean(); + + if (!empty($this->name)) { + $name = $this->name; + } else { + $name = !$this->isContent ? pathinfo($data, PATHINFO_BASENAME) : ''; + } + + if ($this->isContent) { + $mimeType = $this->mimeType; + $size = strlen($data); + } else { + $mimeType = $this->getMimeType($data); + $size = filesize($data); + } + + $this->header['Pragma'] = 'public'; + $this->header['Content-Type'] = $mimeType ?: 'application/octet-stream'; + $this->header['Cache-control'] = 'max-age=' . $this->expire; + $this->header['Content-Disposition'] = $this->openinBrowser ? 'inline' : 'attachment; filename="' . $name . '"'; + $this->header['Content-Length'] = $size; + $this->header['Content-Transfer-Encoding'] = 'binary'; + $this->header['Expires'] = gmdate("D, d M Y H:i:s", time() + $this->expire) . ' GMT'; + + $this->lastModified(gmdate('D, d M Y H:i:s', time()) . ' GMT'); + + $data = $this->isContent ? $data : file_get_contents($data); + return $data; + } + + /** + * 设置是否为内容 必须配合mimeType方法使用 + * @access public + * @param bool $content + * @return $this + */ + public function isContent($content = true) + { + $this->isContent = $content; + return $this; + } + + /** + * 设置有效期 + * @access public + * @param integer $expire 有效期 + * @return $this + */ + public function expire($expire) + { + $this->expire = $expire; + return $this; + } + + /** + * 设置文件类型 + * @access public + * @param string $filename 文件名 + * @return $this + */ + public function mimeType($mimeType) + { + $this->mimeType = $mimeType; + return $this; + } + + /** + * 获取文件类型信息 + * @access public + * @param string $filename 文件名 + * @return string + */ + protected function getMimeType($filename) + { + if (!empty($this->mimeType)) { + return $this->mimeType; + } + + $finfo = finfo_open(FILEINFO_MIME_TYPE); + + return finfo_file($finfo, $filename); + } + + /** + * 设置下载文件的显示名称 + * @access public + * @param string $filename 文件名 + * @param bool $extension 后缀自动识别 + * @return $this + */ + public function name($filename, $extension = true) + { + $this->name = $filename; + + if ($extension && false === strpos($filename, '.')) { + $this->name .= '.' . pathinfo($this->data, PATHINFO_EXTENSION); + } + + return $this; + } + + /** + * 设置是否在浏览器中显示文件 + * @access public + * @param bool $openinBrowser 是否在浏览器中显示文件 + * @return $this + */ + public function openinBrowser($openinBrowser) { + $this->openinBrowser = $openinBrowser; + return $this; + } +} diff --git a/thinkphp/library/think/response/Json.php b/thinkphp/library/think/response/Json.php index 538fc5a0d..aa5bbd6fa 100644 --- a/thinkphp/library/think/response/Json.php +++ b/thinkphp/library/think/response/Json.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -25,7 +25,7 @@ class Json extends Response /** * 处理数据 * @access protected - * @param mixed $data 要处理的数据 + * @param mixed $data 要处理的数据 * @return mixed * @throws \Exception */ @@ -35,7 +35,7 @@ class Json extends Response // 返回JSON数据格式到客户端 包含状态信息 $data = json_encode($data, $this->options['json_encode_param']); - if ($data === false) { + if (false === $data) { throw new \InvalidArgumentException(json_last_error_msg()); } diff --git a/thinkphp/library/think/response/Jsonp.php b/thinkphp/library/think/response/Jsonp.php index de8fb3041..f69e88e1d 100644 --- a/thinkphp/library/think/response/Jsonp.php +++ b/thinkphp/library/think/response/Jsonp.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,7 +11,6 @@ namespace think\response; -use think\Request; use think\Response; class Jsonp extends Response @@ -28,7 +27,7 @@ class Jsonp extends Response /** * 处理数据 * @access protected - * @param mixed $data 要处理的数据 + * @param mixed $data 要处理的数据 * @return mixed * @throws \Exception */ @@ -36,16 +35,17 @@ class Jsonp extends Response { try { // 返回JSON数据格式到客户端 包含状态信息 [当url_common_param为false时是无法获取到$_GET的数据的,故使用Request来获取] - $var_jsonp_handler = Request::instance()->param($this->options['var_jsonp_handler'], ""); + $var_jsonp_handler = $this->app['request']->param($this->options['var_jsonp_handler'], ""); $handler = !empty($var_jsonp_handler) ? $var_jsonp_handler : $this->options['default_jsonp_handler']; $data = json_encode($data, $this->options['json_encode_param']); - if ($data === false) { + if (false === $data) { throw new \InvalidArgumentException(json_last_error_msg()); } $data = $handler . '(' . $data . ');'; + return $data; } catch (\Exception $e) { if ($e->getPrevious()) { diff --git a/thinkphp/library/think/response/Jump.php b/thinkphp/library/think/response/Jump.php new file mode 100644 index 000000000..258448ca4 --- /dev/null +++ b/thinkphp/library/think/response/Jump.php @@ -0,0 +1,32 @@ + +// +---------------------------------------------------------------------- + +namespace think\response; + +use think\Response; + +class Jump extends Response +{ + protected $contentType = 'text/html'; + + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + * @throws \Exception + */ + protected function output($data) + { + $data = $this->app['view']->fetch($this->options['jump_template'], $data); + return $data; + } +} diff --git a/thinkphp/library/think/response/Redirect.php b/thinkphp/library/think/response/Redirect.php index f20027790..73729ce88 100644 --- a/thinkphp/library/think/response/Redirect.php +++ b/thinkphp/library/think/response/Redirect.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,10 +11,7 @@ namespace think\response; -use think\Request; use think\Response; -use think\Session; -use think\Url; class Redirect extends Response { @@ -27,75 +24,95 @@ class Redirect extends Response public function __construct($data = '', $code = 302, array $header = [], array $options = []) { parent::__construct($data, $code, $header, $options); + $this->cacheControl('no-cache,must-revalidate'); } /** * 处理数据 * @access protected - * @param mixed $data 要处理的数据 + * @param mixed $data 要处理的数据 * @return mixed */ protected function output($data) { $this->header['Location'] = $this->getTargetUrl(); + return; } /** * 重定向传值(通过Session) * @access protected - * @param string|array $name 变量名或者数组 - * @param mixed $value 值 + * @param string|array $name 变量名或者数组 + * @param mixed $value 值 * @return $this */ public function with($name, $value = null) { + $session = $this->app['session']; + if (is_array($name)) { foreach ($name as $key => $val) { - Session::flash($key, $val); + $session->flash($key, $val); } } else { - Session::flash($name, $value); + $session->flash($name, $value); } + return $this; } /** * 获取跳转地址 + * @access public * @return string */ public function getTargetUrl() { - return (strpos($this->data, '://') || 0 === strpos($this->data, '/')) ? $this->data : Url::build($this->data, $this->params); + if (strpos($this->data, '://') || (0 === strpos($this->data, '/') && empty($this->params))) { + return $this->data; + } else { + return $this->app['url']->build($this->data, $this->params); + } } public function params($params = []) { $this->params = $params; + return $this; } /** * 记住当前url后跳转 + * @access public * @return $this */ public function remember() { - Session::set('redirect_url', Request::instance()->url()); + $this->app['session']->set('redirect_url', $this->app['request']->url()); + return $this; } /** * 跳转到上次记住的url + * @access public + * @param string $url 闪存数据不存在时的跳转地址 * @return $this */ - public function restore() + public function restore($url = null) { - if (Session::has('redirect_url')) { - $this->data = Session::get('redirect_url'); - Session::delete('redirect_url'); + $session = $this->app['session']; + + if ($session->has('redirect_url')) { + $this->data = $session->get('redirect_url'); + $session->delete('redirect_url'); + } elseif ($url) { + $this->data = $url; } + return $this; } } diff --git a/thinkphp/library/think/response/View.php b/thinkphp/library/think/response/View.php index de75515a9..c836ccb5d 100644 --- a/thinkphp/library/think/response/View.php +++ b/thinkphp/library/think/response/View.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,35 +11,34 @@ namespace think\response; -use think\Config; use think\Response; -use think\View as ViewTemplate; class View extends Response { // 输出参数 - protected $options = []; - protected $vars = []; - protected $replace = []; + protected $options = []; + protected $vars = []; + protected $filter; protected $contentType = 'text/html'; /** * 处理数据 * @access protected - * @param mixed $data 要处理的数据 + * @param mixed $data 要处理的数据 * @return mixed */ protected function output($data) { // 渲染模板输出 - return ViewTemplate::instance(Config::get('template'), Config::get('view_replace_str')) - ->fetch($data, $this->vars, $this->replace); + return $this->app['view'] + ->filter($this->filter) + ->fetch($data, $this->vars); } /** * 获取视图变量 * @access public - * @param string $name 模板变量 + * @param string $name 模板变量 * @return mixed */ public function getVars($name = null) @@ -54,36 +53,42 @@ class View extends Response /** * 模板变量赋值 * @access public - * @param mixed $name 变量名 - * @param mixed $value 变量值 + * @param mixed $name 变量名 + * @param mixed $value 变量值 * @return $this */ public function assign($name, $value = '') { if (is_array($name)) { $this->vars = array_merge($this->vars, $name); - return $this; } else { $this->vars[$name] = $value; } + return $this; } /** - * 视图内容替换 + * 视图内容过滤 * @access public - * @param string|array $content 被替换内容(支持批量替换) - * @param string $replace 替换内容 + * @param callable $filter * @return $this */ - public function replace($content, $replace = '') + public function filter($filter) { - if (is_array($content)) { - $this->replace = array_merge($this->replace, $content); - } else { - $this->replace[$content] = $replace; - } + $this->filter = $filter; return $this; } + /** + * 检查模板是否存在 + * @access private + * @param string|array $name 参数名 + * @return bool + */ + public function exists($name) + { + return $this->app['view']->exists($name); + } + } diff --git a/thinkphp/library/think/response/Xml.php b/thinkphp/library/think/response/Xml.php index ca12e295e..9c1681a4a 100644 --- a/thinkphp/library/think/response/Xml.php +++ b/thinkphp/library/think/response/Xml.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,6 +11,8 @@ namespace think\response; +use think\Collection; +use think\Model; use think\Response; class Xml extends Response @@ -34,23 +36,33 @@ class Xml extends Response /** * 处理数据 * @access protected - * @param mixed $data 要处理的数据 + * @param mixed $data 要处理的数据 * @return mixed */ protected function output($data) { + if (is_string($data)) { + if (0 !== strpos($data, 'options['encoding']; + $xml = ""; + $data = $xml . $data; + } + return $data; + } + // XML数据转换 return $this->xmlEncode($data, $this->options['root_node'], $this->options['item_node'], $this->options['root_attr'], $this->options['item_key'], $this->options['encoding']); } /** * XML编码 - * @param mixed $data 数据 - * @param string $root 根节点名 - * @param string $item 数字索引的子节点名 - * @param string $attr 根节点属性 - * @param string $id 数字索引子节点key转换的属性名 - * @param string $encoding 数据编码 + * @access protected + * @param mixed $data 数据 + * @param string $root 根节点名 + * @param string $item 数字索引的子节点名 + * @param string $attr 根节点属性 + * @param string $id 数字索引子节点key转换的属性名 + * @param string $encoding 数据编码 * @return string */ protected function xmlEncode($data, $root, $item, $attr, $id, $encoding) @@ -62,25 +74,33 @@ class Xml extends Response } $attr = implode(' ', $array); } + $attr = trim($attr); $attr = empty($attr) ? '' : " {$attr}"; $xml = ""; $xml .= "<{$root}{$attr}>"; $xml .= $this->dataToXml($data, $item, $id); $xml .= ""; + return $xml; } /** * 数据XML编码 - * @param mixed $data 数据 - * @param string $item 数字索引时的节点名称 - * @param string $id 数字索引key转换为的属性名 + * @access protected + * @param mixed $data 数据 + * @param string $item 数字索引时的节点名称 + * @param string $id 数字索引key转换为的属性名 * @return string */ protected function dataToXml($data, $item, $id) { $xml = $attr = ''; + + if ($data instanceof Collection || $data instanceof Model) { + $data = $data->toArray(); + } + foreach ($data as $key => $val) { if (is_numeric($key)) { $id && $attr = " {$id}=\"{$key}\""; @@ -90,6 +110,7 @@ class Xml extends Response $xml .= (is_array($val) || is_object($val)) ? $this->dataToXml($val, $item, $id) : $val; $xml .= ""; } + return $xml; } } diff --git a/thinkphp/library/think/route/AliasRule.php b/thinkphp/library/think/route/AliasRule.php new file mode 100644 index 000000000..393cb3107 --- /dev/null +++ b/thinkphp/library/think/route/AliasRule.php @@ -0,0 +1,119 @@ + +// +---------------------------------------------------------------------- + +namespace think\route; + +use think\Route; + +class AliasRule extends Domain +{ + /** + * 架构函数 + * @access public + * @param Route $router 路由实例 + * @param RuleGroup $parent 上级对象 + * @param string $name 路由别名 + * @param string $route 路由绑定 + * @param array $option 路由参数 + */ + public function __construct(Route $router, RuleGroup $parent, $name, $route, $option = []) + { + $this->router = $router; + $this->parent = $parent; + $this->name = $name; + $this->route = $route; + $this->option = $option; + } + + /** + * 检测路由别名 + * @access public + * @param Request $request 请求对象 + * @param string $url 访问地址 + * @param bool $completeMatch 路由是否完全匹配 + * @return Dispatch|false + */ + public function check($request, $url, $completeMatch = false) + { + if ($dispatch = $this->checkCrossDomain($request)) { + // 允许跨域 + return $dispatch; + } + + // 检查参数有效性 + if (!$this->checkOption($this->option, $request)) { + return false; + } + + list($action, $bind) = array_pad(explode('|', $url, 2), 2, ''); + + if (isset($this->option['allow']) && !in_array($action, $this->option['allow'])) { + // 允许操作 + return false; + } elseif (isset($this->option['except']) && in_array($action, $this->option['except'])) { + // 排除操作 + return false; + } + + if (isset($this->option['method'][$action])) { + $this->option['method'] = $this->option['method'][$action]; + } + + // 匹配后执行的行为 + $this->afterMatchGroup($request); + + if ($this->parent) { + // 合并分组参数 + $this->mergeGroupOptions(); + } + + if (isset($this->option['ext'])) { + // 路由ext参数 优先于系统配置的URL伪静态后缀参数 + $bind = preg_replace('/\.(' . $request->ext() . ')$/i', '', $bind); + } + + $this->parseBindAppendParam($this->route); + + if (0 === strpos($this->route, '\\')) { + // 路由到类 + return $this->bindToClass($request, $bind, substr($this->route, 1)); + } elseif (0 === strpos($this->route, '@')) { + // 路由到控制器类 + return $this->bindToController($request, $bind, substr($this->route, 1)); + } else { + // 路由到模块/控制器 + return $this->bindToModule($request, $bind, $this->route); + } + } + + /** + * 设置允许的操作方法 + * @access public + * @param array $action 操作方法 + * @return $this + */ + public function allow($action = []) + { + return $this->option('allow', $action); + } + + /** + * 设置排除的操作方法 + * @access public + * @param array $action 操作方法 + * @return $this + */ + public function except($action = []) + { + return $this->option('except', $action); + } + +} diff --git a/thinkphp/library/think/route/Dispatch.php b/thinkphp/library/think/route/Dispatch.php new file mode 100644 index 000000000..93afe73b6 --- /dev/null +++ b/thinkphp/library/think/route/Dispatch.php @@ -0,0 +1,365 @@ + +// +---------------------------------------------------------------------- + +namespace think\route; + +use think\Container; +use think\exception\ValidateException; +use think\App; +use think\Request; +use think\Response; + +abstract class Dispatch +{ + /** + * 应用对象 + * @var App + */ + protected $app; + + /** + * 请求对象 + * @var Request + */ + protected $request; + + /** + * 路由规则 + * @var Rule + */ + protected $rule; + + /** + * 调度信息 + * @var mixed + */ + protected $dispatch; + + /** + * 调度参数 + * @var array + */ + protected $param; + + /** + * 状态码 + * @var string + */ + protected $code; + + /** + * 是否进行大小写转换 + * @var bool + */ + protected $convert; + + public function __construct(Request $request, Rule $rule, $dispatch, $param = [], $code = null) + { + $this->request = $request; + $this->rule = $rule; + $this->app = Container::get('app'); + $this->dispatch = $dispatch; + $this->param = $param; + $this->code = $code; + + if (isset($param['convert'])) { + $this->convert = $param['convert']; + } + } + + public function init() + { + // 执行路由后置操作 + if ($this->rule->doAfter()) { + // 设置请求的路由信息 + + // 设置当前请求的参数 + $this->request->setRouteVars($this->rule->getVars()); + $this->request->routeInfo([ + 'rule' => $this->rule->getRule(), + 'route' => $this->rule->getRoute(), + 'option' => $this->rule->getOption(), + 'var' => $this->rule->getVars(), + ]); + + $this->doRouteAfter(); + } + + return $this; + } + + /** + * 检查路由后置操作 + * @access protected + * @return void + */ + protected function doRouteAfter() + { + // 记录匹配的路由信息 + $option = $this->rule->getOption(); + $matches = $this->rule->getVars(); + + // 添加中间件 + if (!empty($option['middleware'])) { + $this->app['middleware']->import($option['middleware']); + } + + // 绑定模型数据 + if (!empty($option['model'])) { + $this->createBindModel($option['model'], $matches); + } + + // 指定Header数据 + if (!empty($option['header'])) { + $header = $option['header']; + $this->app['hook']->add('response_send', function ($response) use ($header) { + $response->header($header); + }); + } + + // 指定Response响应数据 + if (!empty($option['response'])) { + foreach ($option['response'] as $response) { + $this->app['hook']->add('response_send', $response); + } + } + + // 开启请求缓存 + if (isset($option['cache']) && $this->request->isGet()) { + $this->parseRequestCache($option['cache']); + } + + if (!empty($option['append'])) { + $this->request->setRouteVars($option['append']); + } + } + + /** + * 执行路由调度 + * @access public + * @return mixed + */ + public function run() + { + $option = $this->rule->getOption(); + + // 检测路由after行为 + if (!empty($option['after'])) { + $dispatch = $this->checkAfter($option['after']); + + if ($dispatch instanceof Response) { + return $dispatch; + } + } + + // 数据自动验证 + if (isset($option['validate'])) { + $this->autoValidate($option['validate']); + } + + $data = $this->exec(); + + return $this->autoResponse($data); + } + + protected function autoResponse($data) + { + if ($data instanceof Response) { + $response = $data; + } elseif (!is_null($data)) { + // 默认自动识别响应输出类型 + $isAjax = $this->request->isAjax(); + $type = $isAjax ? $this->rule->getConfig('default_ajax_return') : $this->rule->getConfig('default_return_type'); + + $response = Response::create($data, $type); + } else { + $data = ob_get_clean(); + $content = false === $data ? '' : $data; + $status = '' === $content && $this->request->isAjax() ? 204 : 200; + $response = Response::create($content, '', $status); + } + + return $response; + } + + /** + * 检查路由后置行为 + * @access protected + * @param mixed $after 后置行为 + * @return mixed + */ + protected function checkAfter($after) + { + $this->app['log']->notice('路由后置行为建议使用中间件替代!'); + + $hook = $this->app['hook']; + + $result = null; + + foreach ((array) $after as $behavior) { + $result = $hook->exec($behavior); + + if (!is_null($result)) { + break; + } + } + + // 路由规则重定向 + if ($result instanceof Response) { + return $result; + } + + return false; + } + + /** + * 验证数据 + * @access protected + * @param array $option + * @return void + * @throws ValidateException + */ + protected function autoValidate($option) + { + list($validate, $scene, $message, $batch) = $option; + + if (is_array($validate)) { + // 指定验证规则 + $v = $this->app->validate(); + $v->rule($validate); + } else { + // 调用验证器 + $v = $this->app->validate($validate); + if (!empty($scene)) { + $v->scene($scene); + } + } + + if (!empty($message)) { + $v->message($message); + } + + // 批量验证 + if ($batch) { + $v->batch(true); + } + + if (!$v->check($this->request->param())) { + throw new ValidateException($v->getError()); + } + } + + /** + * 处理路由请求缓存 + * @access protected + * @param Request $request 请求对象 + * @param string|array $cache 路由缓存 + * @return void + */ + protected function parseRequestCache($cache) + { + if (is_array($cache)) { + list($key, $expire, $tag) = array_pad($cache, 3, null); + } else { + $key = str_replace('|', '/', $this->request->url()); + $expire = $cache; + $tag = null; + } + + $cache = $this->request->cache($key, $expire, $tag); + $this->app->setResponseCache($cache); + } + + /** + * 路由绑定模型实例 + * @access protected + * @param array|\Clousre $bindModel 绑定模型 + * @param array $matches 路由变量 + * @return void + */ + protected function createBindModel($bindModel, $matches) + { + foreach ($bindModel as $key => $val) { + if ($val instanceof \Closure) { + $result = $this->app->invokeFunction($val, $matches); + } else { + $fields = explode('&', $key); + + if (is_array($val)) { + list($model, $exception) = $val; + } else { + $model = $val; + $exception = true; + } + + $where = []; + $match = true; + + foreach ($fields as $field) { + if (!isset($matches[$field])) { + $match = false; + break; + } else { + $where[] = [$field, '=', $matches[$field]]; + } + } + + if ($match) { + $query = strpos($model, '\\') ? $model::where($where) : $this->app->model($model)->where($where); + $result = $query->failException($exception)->find(); + } + } + + if (!empty($result)) { + // 注入容器 + $this->app->instance(get_class($result), $result); + } + } + } + + public function convert($convert) + { + $this->convert = $convert; + + return $this; + } + + public function getDispatch() + { + return $this->dispatch; + } + + public function getParam() + { + return $this->param; + } + + abstract public function exec(); + + public function __sleep() + { + return ['rule', 'dispatch', 'convert', 'param', 'code', 'controller', 'actionName']; + } + + public function __wakeup() + { + $this->app = Container::get('app'); + $this->request = $this->app['request']; + } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['app'], $data['request'], $data['rule']); + + return $data; + } +} diff --git a/thinkphp/library/think/route/Domain.php b/thinkphp/library/think/route/Domain.php new file mode 100644 index 000000000..80950dc28 --- /dev/null +++ b/thinkphp/library/think/route/Domain.php @@ -0,0 +1,236 @@ + +// +---------------------------------------------------------------------- + +namespace think\route; + +use think\Container; +use think\Loader; +use think\Route; +use think\route\dispatch\Callback as CallbackDispatch; +use think\route\dispatch\Controller as ControllerDispatch; +use think\route\dispatch\Module as ModuleDispatch; + +class Domain extends RuleGroup +{ + /** + * 架构函数 + * @access public + * @param Route $router 路由对象 + * @param string $name 路由域名 + * @param mixed $rule 域名路由 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + */ + public function __construct(Route $router, $name = '', $rule = null, $option = [], $pattern = []) + { + $this->router = $router; + $this->domain = $name; + $this->option = $option; + $this->rule = $rule; + $this->pattern = $pattern; + } + + /** + * 检测域名路由 + * @access public + * @param Request $request 请求对象 + * @param string $url 访问地址 + * @param bool $completeMatch 路由是否完全匹配 + * @return Dispatch|false + */ + public function check($request, $url, $completeMatch = false) + { + // 检测别名路由 + $result = $this->checkRouteAlias($request, $url); + + if (false !== $result) { + return $result; + } + + // 检测URL绑定 + $result = $this->checkUrlBind($request, $url); + + if (!empty($this->option['append'])) { + $request->setRouteVars($this->option['append']); + unset($this->option['append']); + } + + if (false !== $result) { + return $result; + } + + // 添加域名中间件 + if (!empty($this->option['middleware'])) { + Container::get('middleware')->import($this->option['middleware']); + unset($this->option['middleware']); + } + + return parent::check($request, $url, $completeMatch); + } + + /** + * 设置路由绑定 + * @access public + * @param string $bind 绑定信息 + * @return $this + */ + public function bind($bind) + { + $this->router->bind($bind, $this->domain); + return $this; + } + + /** + * 检测路由别名 + * @access private + * @param Request $request + * @param string $url URL地址 + * @return Dispatch|false + */ + private function checkRouteAlias($request, $url) + { + $alias = strpos($url, '|') ? strstr($url, '|', true) : $url; + + $item = $this->router->getAlias($alias); + + return $item ? $item->check($request, $url) : false; + } + + /** + * 检测URL绑定 + * @access private + * @param Request $request + * @param string $url URL地址 + * @return Dispatch|false + */ + private function checkUrlBind($request, $url) + { + $bind = $this->router->getBind($this->domain); + + if (!empty($bind)) { + $this->parseBindAppendParam($bind); + + // 记录绑定信息 + Container::get('app')->log('[ BIND ] ' . var_export($bind, true)); + + // 如果有URL绑定 则进行绑定检测 + $type = substr($bind, 0, 1); + $bind = substr($bind, 1); + + $bindTo = [ + '\\' => 'bindToClass', + '@' => 'bindToController', + ':' => 'bindToNamespace', + ]; + + if (isset($bindTo[$type])) { + return $this->{$bindTo[$type]}($request, $url, $bind); + } + } + + return false; + } + + protected function parseBindAppendParam(&$bind) + { + if (false !== strpos($bind, '?')) { + list($bind, $query) = explode('?', $bind); + parse_str($query, $vars); + $this->append($vars); + } + } + + /** + * 绑定到类 + * @access protected + * @param Request $request + * @param string $url URL地址 + * @param string $class 类名(带命名空间) + * @return CallbackDispatch + */ + protected function bindToClass($request, $url, $class) + { + $array = explode('|', $url, 2); + $action = !empty($array[0]) ? $array[0] : $this->router->config('default_action'); + $param = []; + + if (!empty($array[1])) { + $this->parseUrlParams($request, $array[1], $param); + } + + return new CallbackDispatch($request, $this, [$class, $action], $param); + } + + /** + * 绑定到命名空间 + * @access protected + * @param Request $request + * @param string $url URL地址 + * @param string $namespace 命名空间 + * @return CallbackDispatch + */ + protected function bindToNamespace($request, $url, $namespace) + { + $array = explode('|', $url, 3); + $class = !empty($array[0]) ? $array[0] : $this->router->config('default_controller'); + $method = !empty($array[1]) ? $array[1] : $this->router->config('default_action'); + $param = []; + + if (!empty($array[2])) { + $this->parseUrlParams($request, $array[2], $param); + } + + return new CallbackDispatch($request, $this, [$namespace . '\\' . Loader::parseName($class, 1), $method], $param); + } + + /** + * 绑定到控制器类 + * @access protected + * @param Request $request + * @param string $url URL地址 + * @param string $controller 控制器名 (支持带模块名 index/user ) + * @return ControllerDispatch + */ + protected function bindToController($request, $url, $controller) + { + $array = explode('|', $url, 2); + $action = !empty($array[0]) ? $array[0] : $this->router->config('default_action'); + $param = []; + + if (!empty($array[1])) { + $this->parseUrlParams($request, $array[1], $param); + } + + return new ControllerDispatch($request, $this, $controller . '/' . $action, $param); + } + + /** + * 绑定到模块/控制器 + * @access protected + * @param Request $request + * @param string $url URL地址 + * @param string $controller 控制器类名(带命名空间) + * @return ModuleDispatch + */ + protected function bindToModule($request, $url, $controller) + { + $array = explode('|', $url, 2); + $action = !empty($array[0]) ? $array[0] : $this->router->config('default_action'); + $param = []; + + if (!empty($array[1])) { + $this->parseUrlParams($request, $array[1], $param); + } + + return new ModuleDispatch($request, $this, $controller . '/' . $action, $param); + } + +} diff --git a/thinkphp/library/think/route/Resource.php b/thinkphp/library/think/route/Resource.php new file mode 100644 index 000000000..ff1392824 --- /dev/null +++ b/thinkphp/library/think/route/Resource.php @@ -0,0 +1,126 @@ + +// +---------------------------------------------------------------------- + +namespace think\route; + +use think\Route; + +class Resource extends RuleGroup +{ + // 资源路由名称 + protected $resource; + + // REST路由方法定义 + protected $rest = []; + + /** + * 架构函数 + * @access public + * @param Route $router 路由对象 + * @param RuleGroup $parent 上级对象 + * @param string $name 资源名称 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @param array $rest 资源定义 + */ + public function __construct(Route $router, RuleGroup $parent = null, $name = '', $route = '', $option = [], $pattern = [], $rest = []) + { + $this->router = $router; + $this->parent = $parent; + $this->resource = $name; + $this->route = $route; + $this->name = strpos($name, '.') ? strstr($name, '.', true) : $name; + + $this->setFullName(); + + // 资源路由默认为完整匹配 + $option['complete_match'] = true; + + $this->pattern = $pattern; + $this->option = $option; + $this->rest = $rest; + + if ($this->parent) { + $this->domain = $this->parent->getDomain(); + $this->parent->addRuleItem($this); + } + + if ($router->isTest()) { + $this->buildResourceRule(); + } + } + + /** + * 生成资源路由规则 + * @access protected + * @return void + */ + protected function buildResourceRule() + { + $origin = $this->router->getGroup(); + $this->router->setGroup($this); + + $rule = $this->resource; + $option = $this->option; + + if (strpos($rule, '.')) { + // 注册嵌套资源路由 + $array = explode('.', $rule); + $last = array_pop($array); + $item = []; + + foreach ($array as $val) { + $item[] = $val . '/<' . (isset($option['var'][$val]) ? $option['var'][$val] : $val . '_id') . '>'; + } + + $rule = implode('/', $item) . '/' . $last; + } + + $prefix = substr($rule, strlen($this->name) + 1); + + // 注册资源路由 + foreach ($this->rest as $key => $val) { + if ((isset($option['only']) && !in_array($key, $option['only'])) + || (isset($option['except']) && in_array($key, $option['except']))) { + continue; + } + + if (isset($last) && strpos($val[1], '') && isset($option['var'][$last])) { + $val[1] = str_replace('', '<' . $option['var'][$last] . '>', $val[1]); + } elseif (strpos($val[1], '') && isset($option['var'][$rule])) { + $val[1] = str_replace('', '<' . $option['var'][$rule] . '>', $val[1]); + } + + $this->addRule(trim($prefix . $val[1], '/'), $this->route . '/' . $val[2], $val[0]); + } + + $this->router->setGroup($origin); + } + + /** + * rest方法定义和修改 + * @access public + * @param string $name 方法名称 + * @param array|bool $resource 资源 + * @return $this + */ + public function rest($name, $resource = []) + { + if (is_array($name)) { + $this->rest = $resource ? $name : array_merge($this->rest, $name); + } else { + $this->rest[$name] = $resource; + } + + return $this; + } +} diff --git a/thinkphp/library/think/route/Rule.php b/thinkphp/library/think/route/Rule.php new file mode 100644 index 000000000..f841ada6e --- /dev/null +++ b/thinkphp/library/think/route/Rule.php @@ -0,0 +1,1121 @@ + +// +---------------------------------------------------------------------- + +namespace think\route; + +use think\Container; +use think\Request; +use think\Response; +use think\route\dispatch\Callback as CallbackDispatch; +use think\route\dispatch\Controller as ControllerDispatch; +use think\route\dispatch\Module as ModuleDispatch; +use think\route\dispatch\Redirect as RedirectDispatch; +use think\route\dispatch\Response as ResponseDispatch; +use think\route\dispatch\View as ViewDispatch; + +abstract class Rule +{ + /** + * 路由标识 + * @var string + */ + protected $name; + + /** + * 路由对象 + * @var Route + */ + protected $router; + + /** + * 路由所属分组 + * @var RuleGroup + */ + protected $parent; + + /** + * 路由规则 + * @var mixed + */ + protected $rule; + + /** + * 路由地址 + * @var string|\Closure + */ + protected $route; + + /** + * 请求类型 + * @var string + */ + protected $method; + + /** + * 路由变量 + * @var array + */ + protected $vars = []; + + /** + * 路由参数 + * @var array + */ + protected $option = []; + + /** + * 路由变量规则 + * @var array + */ + protected $pattern = []; + + /** + * 需要和分组合并的路由参数 + * @var array + */ + protected $mergeOptions = ['after', 'model', 'header', 'response', 'append', 'middleware']; + + /** + * 是否需要后置操作 + * @var bool + */ + protected $doAfter; + + /** + * 是否锁定参数 + * @var bool + */ + protected $lockOption = false; + + abstract public function check($request, $url, $completeMatch = false); + + /** + * 获取Name + * @access public + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * 获取当前路由规则 + * @access public + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * 获取当前路由地址 + * @access public + * @return mixed + */ + public function getRoute() + { + return $this->route; + } + + /** + * 获取当前路由的请求类型 + * @access public + * @return string + */ + public function getMethod() + { + return strtolower($this->method); + } + + /** + * 获取当前路由的变量 + * @access public + * @return array + */ + public function getVars() + { + return $this->vars; + } + + /** + * 获取路由对象 + * @access public + * @return Route + */ + public function getRouter() + { + return $this->router; + } + + /** + * 路由是否有后置操作 + * @access public + * @return bool + */ + public function doAfter() + { + return $this->doAfter; + } + + /** + * 获取路由分组 + * @access public + * @return RuleGroup|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * 获取路由所在域名 + * @access public + * @return string + */ + public function getDomain() + { + return $this->parent->getDomain(); + } + + /** + * 获取变量规则定义 + * @access public + * @param string $name 变量名 + * @return mixed + */ + public function getPattern($name = '') + { + if ('' === $name) { + return $this->pattern; + } + + return isset($this->pattern[$name]) ? $this->pattern[$name] : null; + } + + /** + * 获取路由参数 + * @access public + * @param string $name 变量名 + * @return mixed + */ + public function getConfig($name = '') + { + return $this->router->config($name); + } + + /** + * 获取路由参数定义 + * @access public + * @param string $name 参数名 + * @return mixed + */ + public function getOption($name = '') + { + if ('' === $name) { + return $this->option; + } + + return isset($this->option[$name]) ? $this->option[$name] : null; + } + + /** + * 注册路由参数 + * @access public + * @param string|array $name 参数名 + * @param mixed $value 值 + * @return $this + */ + public function option($name, $value = '') + { + if (is_array($name)) { + $this->option = array_merge($this->option, $name); + } else { + $this->option[$name] = $value; + } + + return $this; + } + + /** + * 注册变量规则 + * @access public + * @param string|array $name 变量名 + * @param string $rule 变量规则 + * @return $this + */ + public function pattern($name, $rule = '') + { + if (is_array($name)) { + $this->pattern = array_merge($this->pattern, $name); + } else { + $this->pattern[$name] = $rule; + } + + return $this; + } + + /** + * 设置标识 + * @access public + * @param string $name 标识名 + * @return $this + */ + public function name($name) + { + $this->name = $name; + + return $this; + } + + /** + * 设置变量 + * @access public + * @param array $vars 变量 + * @return $this + */ + public function vars($vars) + { + $this->vars = $vars; + + return $this; + } + + /** + * 设置路由请求类型 + * @access public + * @param string $method + * @return $this + */ + public function method($method) + { + return $this->option('method', strtolower($method)); + } + + /** + * 设置路由前置行为 + * @access public + * @param array|\Closure $before + * @return $this + */ + public function before($before) + { + return $this->option('before', $before); + } + + /** + * 设置路由后置行为 + * @access public + * @param array|\Closure $after + * @return $this + */ + public function after($after) + { + return $this->option('after', $after); + } + + /** + * 检查后缀 + * @access public + * @param string $ext + * @return $this + */ + public function ext($ext = '') + { + return $this->option('ext', $ext); + } + + /** + * 检查禁止后缀 + * @access public + * @param string $ext + * @return $this + */ + public function denyExt($ext = '') + { + return $this->option('deny_ext', $ext); + } + + /** + * 检查域名 + * @access public + * @param string $domain + * @return $this + */ + public function domain($domain) + { + return $this->option('domain', $domain); + } + + /** + * 设置参数过滤检查 + * @access public + * @param string|array $name + * @param mixed $value + * @return $this + */ + public function filter($name, $value = null) + { + if (is_array($name)) { + $this->option['filter'] = $name; + } else { + $this->option['filter'][$name] = $value; + } + + return $this; + } + + /** + * 绑定模型 + * @access public + * @param array|string $var 路由变量名 多个使用 & 分割 + * @param string|\Closure $model 绑定模型类 + * @param bool $exception 是否抛出异常 + * @return $this + */ + public function model($var, $model = null, $exception = true) + { + if ($var instanceof \Closure) { + $this->option['model'][] = $var; + } elseif (is_array($var)) { + $this->option['model'] = $var; + } elseif (is_null($model)) { + $this->option['model']['id'] = [$var, true]; + } else { + $this->option['model'][$var] = [$model, $exception]; + } + + return $this; + } + + /** + * 附加路由隐式参数 + * @access public + * @param array $append + * @return $this + */ + public function append(array $append = []) + { + if (isset($this->option['append'])) { + $this->option['append'] = array_merge($this->option['append'], $append); + } else { + $this->option['append'] = $append; + } + + return $this; + } + + /** + * 绑定验证 + * @access public + * @param mixed $validate 验证器类 + * @param string $scene 验证场景 + * @param array $message 验证提示 + * @param bool $batch 批量验证 + * @return $this + */ + public function validate($validate, $scene = null, $message = [], $batch = false) + { + $this->option['validate'] = [$validate, $scene, $message, $batch]; + + return $this; + } + + /** + * 绑定Response对象 + * @access public + * @param mixed $response + * @return $this + */ + public function response($response) + { + $this->option['response'][] = $response; + return $this; + } + + /** + * 设置Response Header信息 + * @access public + * @param string|array $name 参数名 + * @param string $value 参数值 + * @return $this + */ + public function header($header, $value = null) + { + if (is_array($header)) { + $this->option['header'] = $header; + } else { + $this->option['header'][$header] = $value; + } + + return $this; + } + + /** + * 指定路由中间件 + * @access public + * @param string|array|\Closure $middleware + * @param mixed $param + * @return $this + */ + public function middleware($middleware, $param = null) + { + if (is_null($param) && is_array($middleware)) { + $this->option['middleware'] = $middleware; + } else { + foreach ((array) $middleware as $item) { + $this->option['middleware'][] = [$item, $param]; + } + } + + return $this; + } + + /** + * 设置路由缓存 + * @access public + * @param array|string $cache + * @return $this + */ + public function cache($cache) + { + return $this->option('cache', $cache); + } + + /** + * 检查URL分隔符 + * @access public + * @param bool $depr + * @return $this + */ + public function depr($depr) + { + return $this->option('param_depr', $depr); + } + + /** + * 是否合并额外参数 + * @access public + * @param bool $merge + * @return $this + */ + public function mergeExtraVars($merge = true) + { + return $this->option('merge_extra_vars', $merge); + } + + /** + * 设置需要合并的路由参数 + * @access public + * @param array $option + * @return $this + */ + public function mergeOptions($option = []) + { + $this->mergeOptions = array_merge($this->mergeOptions, $option); + return $this; + } + + /** + * 检查是否为HTTPS请求 + * @access public + * @param bool $https + * @return $this + */ + public function https($https = true) + { + return $this->option('https', $https); + } + + /** + * 检查是否为AJAX请求 + * @access public + * @param bool $ajax + * @return $this + */ + public function ajax($ajax = true) + { + return $this->option('ajax', $ajax); + } + + /** + * 检查是否为PJAX请求 + * @access public + * @param bool $pjax + * @return $this + */ + public function pjax($pjax = true) + { + return $this->option('pjax', $pjax); + } + + /** + * 检查是否为手机访问 + * @access public + * @param bool $mobile + * @return $this + */ + public function mobile($mobile = true) + { + return $this->option('mobile', $mobile); + } + + /** + * 当前路由到一个模板地址 当使用数组的时候可以传入模板变量 + * @access public + * @param bool|array $view + * @return $this + */ + public function view($view = true) + { + return $this->option('view', $view); + } + + /** + * 当前路由为重定向 + * @access public + * @param bool $redirect 是否为重定向 + * @return $this + */ + public function redirect($redirect = true) + { + return $this->option('redirect', $redirect); + } + + /** + * 设置路由完整匹配 + * @access public + * @param bool $match + * @return $this + */ + public function completeMatch($match = true) + { + return $this->option('complete_match', $match); + } + + /** + * 是否去除URL最后的斜线 + * @access public + * @param bool $remove + * @return $this + */ + public function removeSlash($remove = true) + { + return $this->option('remove_slash', $remove); + } + + /** + * 设置是否允许跨域 + * @access public + * @param bool $allow + * @param array $header + * @return $this + */ + public function allowCrossDomain($allow = true, $header = []) + { + if (!empty($header)) { + $this->header($header); + } + + if ($allow && $this->parent) { + $this->parent->addRuleItem($this, 'options'); + } + + return $this->option('cross_domain', $allow); + } + + /** + * 检查OPTIONS请求 + * @access public + * @param Request $request + * @return Dispatch|void + */ + protected function checkCrossDomain($request) + { + if (!empty($this->option['cross_domain'])) { + + $header = [ + 'Access-Control-Allow-Origin' => '*', + 'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE', + 'Access-Control-Allow-Headers' => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With', + ]; + + if (!empty($this->option['header'])) { + $header = array_merge($header, $this->option['header']); + } + + $this->option['header'] = $header; + + if ($request->method(true) == 'OPTIONS') { + return new ResponseDispatch($request, $this, Response::create()->code(204)->header($header)); + } + } + } + + /** + * 设置路由规则全局有效 + * @access public + * @return $this + */ + public function crossDomainRule() + { + if ($this instanceof RuleGroup) { + $method = '*'; + } else { + $method = $this->method; + } + + $this->router->setCrossDomainRule($this, $method); + + return $this; + } + + /** + * 合并分组参数 + * @access public + * @return array + */ + public function mergeGroupOptions() + { + if (!$this->lockOption) { + $parentOption = $this->parent->getOption(); + // 合并分组参数 + foreach ($this->mergeOptions as $item) { + if (isset($parentOption[$item]) && isset($this->option[$item])) { + $this->option[$item] = array_merge($parentOption[$item], $this->option[$item]); + } + } + + $this->option = array_merge($parentOption, $this->option); + $this->lockOption = true; + } + + return $this->option; + } + + /** + * 解析匹配到的规则路由 + * @access public + * @param Request $request 请求对象 + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param string $url URL地址 + * @param array $option 路由参数 + * @param array $matches 匹配的变量 + * @return Dispatch + */ + public function parseRule($request, $rule, $route, $url, $option = [], $matches = []) + { + if (is_string($route) && isset($option['prefix'])) { + // 路由地址前缀 + $route = $option['prefix'] . $route; + } + + // 替换路由地址中的变量 + if (is_string($route) && !empty($matches)) { + $search = $replace = []; + + foreach ($matches as $key => $value) { + $search[] = '<' . $key . '>'; + $replace[] = $value; + + $search[] = ':' . $key; + $replace[] = $value; + } + + $route = str_replace($search, $replace, $route); + } + + // 解析额外参数 + $count = substr_count($rule, '/'); + $url = array_slice(explode('|', $url), $count + 1); + $this->parseUrlParams($request, implode('|', $url), $matches); + + $this->vars = $matches; + $this->option = $option; + $this->doAfter = true; + + // 发起路由调度 + return $this->dispatch($request, $route, $option); + } + + /** + * 检查路由前置行为 + * @access protected + * @param mixed $before 前置行为 + * @return mixed + */ + protected function checkBefore($before) + { + $hook = Container::get('hook'); + + foreach ((array) $before as $behavior) { + $result = $hook->exec($behavior); + + if (false === $result) { + return false; + } + } + } + + /** + * 发起路由调度 + * @access protected + * @param Request $request Request对象 + * @param mixed $route 路由地址 + * @param array $option 路由参数 + * @return Dispatch + */ + protected function dispatch($request, $route, $option) + { + if ($route instanceof \Closure) { + // 执行闭包 + $result = new CallbackDispatch($request, $this, $route); + } elseif ($route instanceof Response) { + $result = new ResponseDispatch($request, $this, $route); + } elseif (isset($option['view']) && false !== $option['view']) { + $result = new ViewDispatch($request, $this, $route, is_array($option['view']) ? $option['view'] : []); + } elseif (!empty($option['redirect']) || 0 === strpos($route, '/') || strpos($route, '://')) { + // 路由到重定向地址 + $result = new RedirectDispatch($request, $this, $route, [], isset($option['status']) ? $option['status'] : 301); + } elseif (false !== strpos($route, '\\')) { + // 路由到方法 + $result = $this->dispatchMethod($request, $route); + } elseif (0 === strpos($route, '@')) { + // 路由到控制器 + $result = $this->dispatchController($request, substr($route, 1)); + } else { + // 路由到模块/控制器/操作 + $result = $this->dispatchModule($request, $route); + } + + return $result; + } + + /** + * 解析URL地址为 模块/控制器/操作 + * @access protected + * @param Request $request Request对象 + * @param string $route 路由地址 + * @return CallbackDispatch + */ + protected function dispatchMethod($request, $route) + { + list($path, $var) = $this->parseUrlPath($route); + + $route = str_replace('/', '@', implode('/', $path)); + $method = strpos($route, '@') ? explode('@', $route) : $route; + + return new CallbackDispatch($request, $this, $method, $var); + } + + /** + * 解析URL地址为 模块/控制器/操作 + * @access protected + * @param Request $request Request对象 + * @param string $route 路由地址 + * @return ControllerDispatch + */ + protected function dispatchController($request, $route) + { + list($route, $var) = $this->parseUrlPath($route); + + $result = new ControllerDispatch($request, $this, implode('/', $route), $var); + + $request->setAction(array_pop($route)); + $request->setController($route ? array_pop($route) : $this->getConfig('default_controller')); + $request->setModule($route ? array_pop($route) : $this->getConfig('default_module')); + + return $result; + } + + /** + * 解析URL地址为 模块/控制器/操作 + * @access protected + * @param Request $request Request对象 + * @param string $route 路由地址 + * @return ModuleDispatch + */ + protected function dispatchModule($request, $route) + { + list($path, $var) = $this->parseUrlPath($route); + + $action = array_pop($path); + $controller = !empty($path) ? array_pop($path) : null; + $module = $this->getConfig('app_multi_module') && !empty($path) ? array_pop($path) : null; + $method = $request->method(); + + if ($this->getConfig('use_action_prefix') && $this->router->getMethodPrefix($method)) { + $prefix = $this->router->getMethodPrefix($method); + // 操作方法前缀支持 + $action = 0 !== strpos($action, $prefix) ? $prefix . $action : $action; + } + + // 设置当前请求的路由变量 + $request->setRouteVars($var); + + // 路由到模块/控制器/操作 + return new ModuleDispatch($request, $this, [$module, $controller, $action], ['convert' => false]); + } + + /** + * 路由检查 + * @access protected + * @param array $option 路由参数 + * @param Request $request Request对象 + * @return bool + */ + protected function checkOption($option, Request $request) + { + // 请求类型检测 + if (!empty($option['method'])) { + if (is_string($option['method']) && false === stripos($option['method'], $request->method())) { + return false; + } + } + + // AJAX PJAX 请求检查 + foreach (['ajax', 'pjax', 'mobile'] as $item) { + if (isset($option[$item])) { + $call = 'is' . $item; + if ($option[$item] && !$request->$call() || !$option[$item] && $request->$call()) { + return false; + } + } + } + + // 伪静态后缀检测 + if ($request->url() != '/' && ((isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|')) + || (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', '|' . $request->ext() . '|')))) { + return false; + } + + // 域名检查 + if ((isset($option['domain']) && !in_array($option['domain'], [$request->host(true), $request->subDomain()]))) { + return false; + } + + // HTTPS检查 + if ((isset($option['https']) && $option['https'] && !$request->isSsl()) + || (isset($option['https']) && !$option['https'] && $request->isSsl())) { + return false; + } + + // 请求参数检查 + if (isset($option['filter'])) { + foreach ($option['filter'] as $name => $value) { + if ($request->param($name, '', null) != $value) { + return false; + } + } + } + return true; + } + + /** + * 解析URL地址中的参数Request对象 + * @access protected + * @param Request $request + * @param string $rule 路由规则 + * @param array $var 变量 + * @return void + */ + protected function parseUrlParams($request, $url, &$var = []) + { + if ($url) { + if ($this->getConfig('url_param_type')) { + $var += explode('|', $url); + } else { + preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) { + $var[$match[1]] = strip_tags($match[2]); + }, $url); + } + } + } + + /** + * 解析URL的pathinfo参数和变量 + * @access public + * @param string $url URL地址 + * @return array + */ + public function parseUrlPath($url) + { + // 分隔符替换 确保路由定义使用统一的分隔符 + $url = str_replace('|', '/', $url); + $url = trim($url, '/'); + $var = []; + + if (false !== strpos($url, '?')) { + // [模块/控制器/操作?]参数1=值1&参数2=值2... + $info = parse_url($url); + $path = explode('/', $info['path']); + parse_str($info['query'], $var); + } elseif (strpos($url, '/')) { + // [模块/控制器/操作] + $path = explode('/', $url); + } elseif (false !== strpos($url, '=')) { + // 参数1=值1&参数2=值2... + $path = []; + parse_str($url, $var); + } else { + $path = [$url]; + } + + return [$path, $var]; + } + + /** + * 生成路由的正则规则 + * @access protected + * @param string $rule 路由规则 + * @param array $match 匹配的变量 + * @param array $pattern 路由变量规则 + * @param array $option 路由参数 + * @param bool $completeMatch 路由是否完全匹配 + * @param string $suffix 路由正则变量后缀 + * @return string + */ + protected function buildRuleRegex($rule, $match, $pattern = [], $option = [], $completeMatch = false, $suffix = '') + { + foreach ($match as $name) { + $replace[] = $this->buildNameRegex($name, $pattern, $suffix); + } + + // 是否区分 / 地址访问 + if ('/' != $rule) { + if (!empty($option['remove_slash'])) { + $rule = rtrim($rule, '/'); + } elseif (substr($rule, -1) == '/') { + $rule = rtrim($rule, '/'); + $hasSlash = true; + } + } + + $regex = str_replace(array_unique($match), array_unique($replace), $rule); + $regex = str_replace([')?/', ')/', ')?-', ')-', '\\\\/'], [')\/', ')\/', ')\-', ')\-', '\/'], $regex); + + if (isset($hasSlash)) { + $regex .= '\/'; + } + + return $regex . ($completeMatch ? '$' : ''); + } + + /** + * 生成路由变量的正则规则 + * @access protected + * @param string $name 路由变量 + * @param string $pattern 变量规则 + * @param string $suffix 路由正则变量后缀 + * @return string + */ + protected function buildNameRegex($name, $pattern, $suffix) + { + $optional = ''; + $slash = substr($name, 0, 1); + + if (in_array($slash, ['/', '-'])) { + $prefix = '\\' . $slash; + $name = substr($name, 1); + $slash = substr($name, 0, 1); + } else { + $prefix = ''; + } + + if ('<' != $slash) { + return $prefix . preg_quote($name, '/'); + } + + if (strpos($name, '?')) { + $name = substr($name, 1, -2); + $optional = '?'; + } elseif (strpos($name, '>')) { + $name = substr($name, 1, -1); + } + + if (isset($pattern[$name])) { + $nameRule = $pattern[$name]; + if (0 === strpos($nameRule, '/') && '/' == substr($nameRule, -1)) { + $nameRule = substr($nameRule, 1, -1); + } + } else { + $nameRule = $this->getConfig('default_route_pattern'); + } + + return '(' . $prefix . '(?<' . $name . $suffix . '>' . $nameRule . '))' . $optional; + } + + /** + * 分析路由规则中的变量 + * @access protected + * @param string $rule 路由规则 + * @return array + */ + protected function parseVar($rule) + { + // 提取路由规则中的变量 + $var = []; + + if (preg_match_all('/<\w+\??>/', $rule, $matches)) { + foreach ($matches[0] as $name) { + $optional = false; + + if (strpos($name, '?')) { + $name = substr($name, 1, -2); + $optional = true; + } else { + $name = substr($name, 1, -1); + } + + $var[$name] = $optional ? 2 : 1; + } + } + + return $var; + } + + /** + * 设置路由参数 + * @access public + * @param string $method 方法名 + * @param array $args 调用参数 + * @return $this + */ + public function __call($method, $args) + { + if (count($args) > 1) { + $args[0] = $args; + } + array_unshift($args, $method); + + return call_user_func_array([$this, 'option'], $args); + } + + public function __sleep() + { + return ['name', 'rule', 'route', 'method', 'vars', 'option', 'pattern', 'doAfter']; + } + + public function __wakeup() + { + $this->router = Container::get('route'); + } + + public function __debugInfo() + { + $data = get_object_vars($this); + unset($data['parent'], $data['router'], $data['route']); + + return $data; + } +} diff --git a/thinkphp/library/think/route/RuleGroup.php b/thinkphp/library/think/route/RuleGroup.php new file mode 100644 index 000000000..36be2f444 --- /dev/null +++ b/thinkphp/library/think/route/RuleGroup.php @@ -0,0 +1,605 @@ + +// +---------------------------------------------------------------------- + +namespace think\route; + +use think\Container; +use think\Exception; +use think\Request; +use think\Response; +use think\Route; +use think\route\dispatch\Response as ResponseDispatch; +use think\route\dispatch\Url as UrlDispatch; + +class RuleGroup extends Rule +{ + // 分组路由(包括子分组) + protected $rules = [ + '*' => [], + 'get' => [], + 'post' => [], + 'put' => [], + 'patch' => [], + 'delete' => [], + 'head' => [], + 'options' => [], + ]; + + // MISS路由 + protected $miss; + + // 自动路由 + protected $auto; + + // 完整名称 + protected $fullName; + + // 所在域名 + protected $domain; + + /** + * 架构函数 + * @access public + * @param Route $router 路由对象 + * @param RuleGroup $parent 上级对象 + * @param string $name 分组名称 + * @param mixed $rule 分组路由 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + */ + public function __construct(Route $router, RuleGroup $parent = null, $name = '', $rule = [], $option = [], $pattern = []) + { + $this->router = $router; + $this->parent = $parent; + $this->rule = $rule; + $this->name = trim($name, '/'); + $this->option = $option; + $this->pattern = $pattern; + + $this->setFullName(); + + if ($this->parent) { + $this->domain = $this->parent->getDomain(); + $this->parent->addRuleItem($this); + } + + if (!empty($option['cross_domain'])) { + $this->router->setCrossDomainRule($this); + } + + if ($router->isTest()) { + $this->lazy(false); + } + } + + /** + * 设置分组的路由规则 + * @access public + * @return void + */ + protected function setFullName() + { + if (false !== strpos($this->name, ':')) { + $this->name = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/'], ['<\1?>', '<\1>'], $this->name); + } + + if ($this->parent && $this->parent->getFullName()) { + $this->fullName = $this->parent->getFullName() . ($this->name ? '/' . $this->name : ''); + } else { + $this->fullName = $this->name; + } + } + + /** + * 获取所属域名 + * @access public + * @return string + */ + public function getDomain() + { + return $this->domain; + } + + /** + * 检测分组路由 + * @access public + * @param Request $request 请求对象 + * @param string $url 访问地址 + * @param bool $completeMatch 路由是否完全匹配 + * @return Dispatch|false + */ + public function check($request, $url, $completeMatch = false) + { + if ($dispatch = $this->checkCrossDomain($request)) { + // 跨域OPTIONS请求 + return $dispatch; + } + + // 检查分组有效性 + if (!$this->checkOption($this->option, $request) || !$this->checkUrl($url)) { + return false; + } + + // 检查前置行为 + if (isset($this->option['before'])) { + if (false === $this->checkBefore($this->option['before'])) { + return false; + } + unset($this->option['before']); + } + + // 解析分组路由 + if ($this instanceof Resource) { + $this->buildResourceRule(); + } elseif ($this->rule) { + if ($this->rule instanceof Response) { + return new ResponseDispatch($request, $this, $this->rule); + } + + $this->parseGroupRule($this->rule); + } + + // 获取当前路由规则 + $method = strtolower($request->method()); + $rules = $this->getMethodRules($method); + + if (count($rules) == 0) { + return false; + } + + if ($this->parent) { + // 合并分组参数 + $this->mergeGroupOptions(); + // 合并分组变量规则 + $this->pattern = array_merge($this->parent->getPattern(), $this->pattern); + } + + if (isset($this->option['complete_match'])) { + $completeMatch = $this->option['complete_match']; + } + + if (!empty($this->option['merge_rule_regex'])) { + // 合并路由正则规则进行路由匹配检查 + $result = $this->checkMergeRuleRegex($request, $rules, $url, $completeMatch); + + if (false !== $result) { + return $result; + } + } + + // 检查分组路由 + foreach ($rules as $key => $item) { + $result = $item->check($request, $url, $completeMatch); + + if (false !== $result) { + return $result; + } + } + + if ($this->auto) { + // 自动解析URL地址 + $result = new UrlDispatch($request, $this, $this->auto . '/' . $url, ['auto_search' => false]); + } elseif ($this->miss && in_array($this->miss->getMethod(), ['*', $method])) { + // 未匹配所有路由的路由规则处理 + $result = $this->miss->parseRule($request, '', $this->miss->getRoute(), $url, $this->miss->mergeGroupOptions()); + } else { + $result = false; + } + + return $result; + } + + /** + * 获取当前请求的路由规则(包括子分组、资源路由) + * @access protected + * @param string $method + * @return array + */ + protected function getMethodRules($method) + { + return array_merge($this->rules[$method], $this->rules['*']); + } + + /** + * 分组URL匹配检查 + * @access protected + * @param string $url + * @return bool + */ + protected function checkUrl($url) + { + if ($this->fullName) { + $pos = strpos($this->fullName, '<'); + + if (false !== $pos) { + $str = substr($this->fullName, 0, $pos); + } else { + $str = $this->fullName; + } + + if ($str && 0 !== stripos(str_replace('|', '/', $url), $str)) { + return false; + } + } + + return true; + } + + /** + * 延迟解析分组的路由规则 + * @access public + * @param bool $lazy 路由是否延迟解析 + * @return $this + */ + public function lazy($lazy = true) + { + if (!$lazy) { + $this->parseGroupRule($this->rule); + $this->rule = null; + } + + return $this; + } + + /** + * 解析分组和域名的路由规则及绑定 + * @access public + * @param mixed $rule 路由规则 + * @return void + */ + public function parseGroupRule($rule) + { + $origin = $this->router->getGroup(); + $this->router->setGroup($this); + + if ($rule instanceof \Closure) { + Container::getInstance()->invokeFunction($rule); + } elseif (is_array($rule)) { + $this->addRules($rule); + } elseif (is_string($rule) && $rule) { + $this->router->bind($rule, $this->domain); + } + + $this->router->setGroup($origin); + } + + /** + * 检测分组路由 + * @access public + * @param Request $request 请求对象 + * @param array $rules 路由规则 + * @param string $url 访问地址 + * @param bool $completeMatch 路由是否完全匹配 + * @return Dispatch|false + */ + protected function checkMergeRuleRegex($request, &$rules, $url, $completeMatch) + { + $depr = $this->router->config('pathinfo_depr'); + $url = $depr . str_replace('|', $depr, $url); + + foreach ($rules as $key => $item) { + if ($item instanceof RuleItem) { + $rule = $depr . str_replace('/', $depr, $item->getRule()); + if ($depr == $rule && $depr != $url) { + unset($rules[$key]); + continue; + } + + $complete = null !== $item->getOption('complete_match') ? $item->getOption('complete_match') : $completeMatch; + + if (false === strpos($rule, '<')) { + if (0 === strcasecmp($rule, $url) || (!$complete && 0 === strncasecmp($rule, $url, strlen($rule)))) { + return $item->checkRule($request, $url, []); + } + + unset($rules[$key]); + continue; + } + + $slash = preg_quote('/-' . $depr, '/'); + + if ($matchRule = preg_split('/[' . $slash . ']<\w+\??>/', $rule, 2)) { + if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) { + unset($rules[$key]); + continue; + } + } + + if (preg_match_all('/[' . $slash . ']??/', $rule, $matches)) { + unset($rules[$key]); + $pattern = array_merge($this->getPattern(), $item->getPattern()); + $option = array_merge($this->getOption(), $item->getOption()); + + $regex[$key] = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $complete, '_THINK_' . $key); + $items[$key] = $item; + } + } + } + + if (empty($regex)) { + return false; + } + + try { + $result = preg_match('/^(?:' . implode('|', $regex) . ')/u', $url, $match); + } catch (\Exception $e) { + throw new Exception('route pattern error'); + } + + if ($result) { + $var = []; + foreach ($match as $key => $val) { + if (is_string($key) && '' !== $val) { + list($name, $pos) = explode('_THINK_', $key); + + $var[$name] = $val; + } + } + + if (!isset($pos)) { + foreach ($regex as $key => $item) { + if (0 === strpos(str_replace(['\/', '\-', '\\' . $depr], ['/', '-', $depr], $item), $match[0])) { + $pos = $key; + break; + } + } + } + + $rule = $items[$pos]->getRule(); + $array = $this->router->getRule($rule); + + foreach ($array as $item) { + if (in_array($item->getMethod(), ['*', strtolower($request->method())])) { + $result = $item->checkRule($request, $url, $var); + + if (false !== $result) { + return $result; + } + } + } + } + + return false; + } + + /** + * 获取分组的MISS路由 + * @access public + * @return RuleItem|null + */ + public function getMissRule() + { + return $this->miss; + } + + /** + * 获取分组的自动路由 + * @access public + * @return string + */ + public function getAutoRule() + { + return $this->auto; + } + + /** + * 注册自动路由 + * @access public + * @param string $route 路由规则 + * @return void + */ + public function addAutoRule($route) + { + $this->auto = $route; + } + + /** + * 注册MISS路由 + * @access public + * @param string $route 路由地址 + * @param string $method 请求类型 + * @param array $option 路由参数 + * @return RuleItem + */ + public function addMissRule($route, $method = '*', $option = []) + { + // 创建路由规则实例 + $ruleItem = new RuleItem($this->router, $this, null, '', $route, strtolower($method), $option); + + $this->miss = $ruleItem; + + return $ruleItem; + } + + /** + * 添加分组下的路由规则或者子分组 + * @access public + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param string $method 请求类型 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return $this + */ + public function addRule($rule, $route, $method = '*', $option = [], $pattern = []) + { + // 读取路由标识 + if (is_array($rule)) { + $name = $rule[0]; + $rule = $rule[1]; + } elseif (is_string($route)) { + $name = $route; + } else { + $name = null; + } + + $method = strtolower($method); + + if ('/' === $rule || '' === $rule) { + // 首页自动完整匹配 + $rule .= '$'; + } + + // 创建路由规则实例 + $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method, $option, $pattern); + + if (!empty($option['cross_domain'])) { + $this->router->setCrossDomainRule($ruleItem, $method); + } + + $this->addRuleItem($ruleItem, $method); + + return $ruleItem; + } + + /** + * 批量注册路由规则 + * @access public + * @param array $rules 路由规则 + * @param string $method 请求类型 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public function addRules($rules, $method = '*', $option = [], $pattern = []) + { + foreach ($rules as $key => $val) { + if (is_numeric($key)) { + $key = array_shift($val); + } + + if (is_array($val)) { + $route = array_shift($val); + $option = $val ? array_shift($val) : []; + $pattern = $val ? array_shift($val) : []; + } else { + $route = $val; + } + + $this->addRule($key, $route, $method, $option, $pattern); + } + } + + public function addRuleItem($rule, $method = '*') + { + if (strpos($method, '|')) { + $rule->method($method); + $method = '*'; + } + + $this->rules[$method][] = $rule; + + return $this; + } + + /** + * 设置分组的路由前缀 + * @access public + * @param string $prefix + * @return $this + */ + public function prefix($prefix) + { + if ($this->parent && $this->parent->getOption('prefix')) { + $prefix = $this->parent->getOption('prefix') . $prefix; + } + + return $this->option('prefix', $prefix); + } + + /** + * 设置资源允许 + * @access public + * @param array $only + * @return $this + */ + public function only($only) + { + return $this->option('only', $only); + } + + /** + * 设置资源排除 + * @access public + * @param array $except + * @return $this + */ + public function except($except) + { + return $this->option('except', $except); + } + + /** + * 设置资源路由的变量 + * @access public + * @param array $vars + * @return $this + */ + public function vars($vars) + { + return $this->option('var', $vars); + } + + /** + * 合并分组的路由规则正则 + * @access public + * @param bool $merge + * @return $this + */ + public function mergeRuleRegex($merge = true) + { + return $this->option('merge_rule_regex', $merge); + } + + /** + * 获取完整分组Name + * @access public + * @return string + */ + public function getFullName() + { + return $this->fullName; + } + + /** + * 获取分组的路由规则 + * @access public + * @param string $method + * @return array + */ + public function getRules($method = '') + { + if ('' === $method) { + return $this->rules; + } + + return isset($this->rules[strtolower($method)]) ? $this->rules[strtolower($method)] : []; + } + + /** + * 清空分组下的路由规则 + * @access public + * @return void + */ + public function clear() + { + $this->rules = [ + '*' => [], + 'get' => [], + 'post' => [], + 'put' => [], + 'patch' => [], + 'delete' => [], + 'head' => [], + 'options' => [], + ]; + } +} diff --git a/thinkphp/library/think/route/RuleItem.php b/thinkphp/library/think/route/RuleItem.php new file mode 100644 index 000000000..e4bddd90c --- /dev/null +++ b/thinkphp/library/think/route/RuleItem.php @@ -0,0 +1,288 @@ + +// +---------------------------------------------------------------------- + +namespace think\route; + +use think\Container; +use think\Exception; +use think\Route; + +class RuleItem extends Rule +{ + protected $hasSetRule; + + /** + * 架构函数 + * @access public + * @param Route $router 路由实例 + * @param RuleGroup $parent 上级对象 + * @param string $name 路由标识 + * @param string|array $rule 路由规则 + * @param string|\Closure $route 路由地址 + * @param string $method 请求类型 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + */ + public function __construct(Route $router, RuleGroup $parent, $name, $rule, $route, $method = '*', $option = [], $pattern = []) + { + $this->router = $router; + $this->parent = $parent; + $this->name = $name; + $this->route = $route; + $this->method = $method; + $this->option = $option; + $this->pattern = $pattern; + + $this->setRule($rule); + + if (!empty($option['cross_domain'])) { + $this->router->setCrossDomainRule($this, $method); + } + } + + /** + * 路由规则预处理 + * @access public + * @param string $rule 路由规则 + * @return void + */ + public function setRule($rule) + { + if ('$' == substr($rule, -1, 1)) { + // 是否完整匹配 + $rule = substr($rule, 0, -1); + + $this->option['complete_match'] = true; + } + + $rule = '/' != $rule ? ltrim($rule, '/') : ''; + + if ($this->parent && $prefix = $this->parent->getFullName()) { + $rule = $prefix . ($rule ? '/' . ltrim($rule, '/') : ''); + } + + if (false !== strpos($rule, ':')) { + $this->rule = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/'], ['<\1?>', '<\1>'], $rule); + } else { + $this->rule = $rule; + } + + // 生成路由标识的快捷访问 + $this->setRuleName(); + } + + /** + * 检查后缀 + * @access public + * @param string $ext + * @return $this + */ + public function ext($ext = '') + { + $this->option('ext', $ext); + $this->setRuleName(true); + + return $this; + } + + /** + * 设置别名 + * @access public + * @param string $name + * @return $this + */ + public function name($name) + { + $this->name = $name; + $this->setRuleName(true); + + return $this; + } + + /** + * 设置路由标识 用于URL反解生成 + * @access protected + * @param bool $first 是否插入开头 + * @return void + */ + protected function setRuleName($first = false) + { + if ($this->name) { + $vars = $this->parseVar($this->rule); + $name = strtolower($this->name); + + if (isset($this->option['ext'])) { + $suffix = $this->option['ext']; + } elseif ($this->parent->getOption('ext')) { + $suffix = $this->parent->getOption('ext'); + } else { + $suffix = null; + } + + $value = [$this->rule, $vars, $this->parent->getDomain(), $suffix, $this->method]; + + Container::get('rule_name')->set($name, $value, $first); + } + + if (!$this->hasSetRule) { + Container::get('rule_name')->setRule($this->rule, $this); + $this->hasSetRule = true; + } + } + + /** + * 检测路由 + * @access public + * @param Request $request 请求对象 + * @param string $url 访问地址 + * @param array $match 匹配路由变量 + * @param bool $completeMatch 路由是否完全匹配 + * @return Dispatch|false + */ + public function checkRule($request, $url, $match = null, $completeMatch = false) + { + if ($dispatch = $this->checkCrossDomain($request)) { + // 允许跨域 + return $dispatch; + } + + // 检查参数有效性 + if (!$this->checkOption($this->option, $request)) { + return false; + } + + // 合并分组参数 + $option = $this->mergeGroupOptions(); + + $url = $this->urlSuffixCheck($request, $url, $option); + + if (is_null($match)) { + $match = $this->match($url, $option, $completeMatch); + } + + if (false !== $match) { + // 检查前置行为 + if (isset($option['before']) && false === $this->checkBefore($option['before'])) { + return false; + } + + return $this->parseRule($request, $this->rule, $this->route, $url, $option, $match); + } + + return false; + } + + /** + * 检测路由(含路由匹配) + * @access public + * @param Request $request 请求对象 + * @param string $url 访问地址 + * @param string $depr 路径分隔符 + * @param bool $completeMatch 路由是否完全匹配 + * @return Dispatch|false + */ + public function check($request, $url, $completeMatch = false) + { + return $this->checkRule($request, $url, null, $completeMatch); + } + + /** + * URL后缀及Slash检查 + * @access protected + * @param Request $request 请求对象 + * @param string $url 访问地址 + * @param array $option 路由参数 + * @return string + */ + protected function urlSuffixCheck($request, $url, $option = []) + { + // 是否区分 / 地址访问 + if (!empty($option['remove_slash']) && '/' != $this->rule) { + $this->rule = rtrim($this->rule, '/'); + $url = rtrim($url, '|'); + } + + if (isset($option['ext'])) { + // 路由ext参数 优先于系统配置的URL伪静态后缀参数 + $url = preg_replace('/\.(' . $request->ext() . ')$/i', '', $url); + } + + return $url; + } + + /** + * 检测URL和规则路由是否匹配 + * @access private + * @param string $url URL地址 + * @param array $option 路由参数 + * @param bool $completeMatch 路由是否完全匹配 + * @return array|false + */ + private function match($url, $option, $completeMatch) + { + if (isset($option['complete_match'])) { + $completeMatch = $option['complete_match']; + } + + $pattern = array_merge($this->parent->getPattern(), $this->pattern); + $depr = $this->router->config('pathinfo_depr'); + + // 检查完整规则定义 + if (isset($pattern['__url__']) && !preg_match(0 === strpos($pattern['__url__'], '/') ? $pattern['__url__'] : '/^' . $pattern['__url__'] . '/', str_replace('|', $depr, $url))) { + return false; + } + + $var = []; + $url = $depr . str_replace('|', $depr, $url); + $rule = $depr . str_replace('/', $depr, $this->rule); + + if ($depr == $rule && $depr != $url) { + return false; + } + + if (false === strpos($rule, '<')) { + if (0 === strcasecmp($rule, $url) || (!$completeMatch && 0 === strncasecmp($rule . $depr, $url . $depr, strlen($rule . $depr)))) { + return $var; + } + return false; + } + + $slash = preg_quote('/-' . $depr, '/'); + + if ($matchRule = preg_split('/[' . $slash . ']?<\w+\??>/', $rule, 2)) { + if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) { + return false; + } + } + + if (preg_match_all('/[' . $slash . ']??/', $rule, $matches)) { + $regex = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $completeMatch); + + try { + if (!preg_match('/^' . $regex . ($completeMatch ? '$' : '') . '/u', $url, $match)) { + return false; + } + } catch (\Exception $e) { + throw new Exception('route pattern error'); + } + + foreach ($match as $key => $val) { + if (is_string($key)) { + $var[$key] = $val; + } + } + } + + // 成功匹配后返回URL中的动态变量数组 + return $var; + } + +} diff --git a/thinkphp/library/think/route/RuleName.php b/thinkphp/library/think/route/RuleName.php new file mode 100644 index 000000000..202fb0e23 --- /dev/null +++ b/thinkphp/library/think/route/RuleName.php @@ -0,0 +1,147 @@ + +// +---------------------------------------------------------------------- + +namespace think\route; + +class RuleName +{ + protected $item = []; + protected $rule = []; + + /** + * 注册路由标识 + * @access public + * @param string $name 路由标识 + * @param array $value 路由规则 + * @param bool $first 是否置顶 + * @return void + */ + public function set($name, $value, $first = false) + { + if ($first && isset($this->item[$name])) { + array_unshift($this->item[$name], $value); + } else { + $this->item[$name][] = $value; + } + } + + /** + * 注册路由规则 + * @access public + * @param string $rule 路由规则 + * @param RuleItem $route 路由 + * @return void + */ + public function setRule($rule, $route) + { + $this->rule[$route->getDomain()][$rule][$route->getMethod()] = $route; + } + + /** + * 根据路由规则获取路由对象(列表) + * @access public + * @param string $name 路由标识 + * @param string $domain 域名 + * @return array + */ + public function getRule($rule, $domain = null) + { + return isset($this->rule[$domain][$rule]) ? $this->rule[$domain][$rule] : []; + } + + /** + * 获取全部路由列表 + * @access public + * @param string $domain 域名 + * @return array + */ + public function getRuleList($domain = null) + { + $list = []; + + foreach ($this->rule as $ruleDomain => $rules) { + foreach ($rules as $rule => $items) { + foreach ($items as $item) { + $val['domain'] = $ruleDomain; + + foreach (['method', 'rule', 'name', 'route', 'pattern', 'option'] as $param) { + $call = 'get' . $param; + $val[$param] = $item->$call(); + } + + $list[$ruleDomain][] = $val; + } + } + } + + if ($domain) { + return isset($list[$domain]) ? $list[$domain] : []; + } + + return $list; + } + + /** + * 导入路由标识 + * @access public + * @param array $name 路由标识 + * @return void + */ + public function import($item) + { + $this->item = $item; + } + + /** + * 根据路由标识获取路由信息(用于URL生成) + * @access public + * @param string $name 路由标识 + * @param string $domain 域名 + * @return array|null + */ + public function get($name = null, $domain = null, $method = '*') + { + if (is_null($name)) { + return $this->item; + } + + $name = strtolower($name); + $method = strtolower($method); + + if (isset($this->item[$name])) { + if (is_null($domain)) { + $result = $this->item[$name]; + } else { + $result = []; + foreach ($this->item[$name] as $item) { + if ($item[2] == $domain && ('*' == $item[4] || $method == $item[4])) { + $result[] = $item; + } + } + } + } else { + $result = null; + } + + return $result; + } + + /** + * 清空路由规则 + * @access public + * @return void + */ + public function clear() + { + $this->item = []; + $this->rule = []; + } +} diff --git a/vendor/topthink/think-queue/src/queue/ShouldQueue.php b/thinkphp/library/think/route/dispatch/Callback.php similarity index 51% rename from vendor/topthink/think-queue/src/queue/ShouldQueue.php rename to thinkphp/library/think/route/dispatch/Callback.php index cb49c12d0..ca76fc993 100644 --- a/vendor/topthink/think-queue/src/queue/ShouldQueue.php +++ b/thinkphp/library/think/route/dispatch/Callback.php @@ -1,17 +1,26 @@ +// | Author: liu21st // +---------------------------------------------------------------------- -namespace think\queue; +namespace think\route\dispatch; -interface ShouldQueue +use think\route\Dispatch; + +class Callback extends Dispatch { + public function exec() + { + // 执行回调方法 + $vars = array_merge($this->request->param(), $this->param); + + return $this->app->invoke($this->dispatch, $vars); + } } diff --git a/thinkphp/library/think/route/dispatch/Controller.php b/thinkphp/library/think/route/dispatch/Controller.php new file mode 100644 index 000000000..1de829926 --- /dev/null +++ b/thinkphp/library/think/route/dispatch/Controller.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- + +namespace think\route\dispatch; + +use think\route\Dispatch; + +class Controller extends Dispatch +{ + public function exec() + { + // 执行控制器的操作方法 + $vars = array_merge($this->request->param(), $this->param); + + return $this->app->action( + $this->dispatch, $vars, + $this->rule->getConfig('url_controller_layer'), + $this->rule->getConfig('controller_suffix') + ); + } + +} diff --git a/thinkphp/library/think/route/dispatch/Module.php b/thinkphp/library/think/route/dispatch/Module.php new file mode 100644 index 000000000..e8842cd3a --- /dev/null +++ b/thinkphp/library/think/route/dispatch/Module.php @@ -0,0 +1,139 @@ + +// +---------------------------------------------------------------------- + +namespace think\route\dispatch; + +use ReflectionMethod; +use think\Controller; +use think\exception\ClassNotFoundException; +use think\exception\HttpException; +use think\Loader; +use think\Request; +use think\route\Dispatch; + +class Module extends Dispatch +{ + protected $controller; + protected $actionName; + + public function init() + { + parent::init(); + + $result = $this->dispatch; + + if (is_string($result)) { + $result = explode('/', $result); + } + + if ($this->rule->getConfig('app_multi_module')) { + // 多模块部署 + $module = strip_tags(strtolower($result[0] ?: $this->rule->getConfig('default_module'))); + $bind = $this->rule->getRouter()->getBind(); + $available = false; + + if ($bind && preg_match('/^[a-z]/is', $bind)) { + // 绑定模块 + list($bindModule) = explode('/', $bind); + if (empty($result[0])) { + $module = $bindModule; + } + $available = true; + } elseif (!in_array($module, $this->rule->getConfig('deny_module_list')) && is_dir($this->app->getAppPath() . $module)) { + $available = true; + } elseif ($this->rule->getConfig('empty_module')) { + $module = $this->rule->getConfig('empty_module'); + $available = true; + } + + // 模块初始化 + if ($module && $available) { + // 初始化模块 + $this->request->setModule($module); + $this->app->init($module); + } else { + throw new HttpException(404, 'module not exists:' . $module); + } + } + + // 是否自动转换控制器和操作名 + $convert = is_bool($this->convert) ? $this->convert : $this->rule->getConfig('url_convert'); + // 获取控制器名 + $controller = strip_tags($result[1] ?: $this->rule->getConfig('default_controller')); + + $this->controller = $convert ? strtolower($controller) : $controller; + + // 获取操作名 + $this->actionName = strip_tags($result[2] ?: $this->rule->getConfig('default_action')); + + // 设置当前请求的控制器、操作 + $this->request + ->setController(Loader::parseName($this->controller, 1)) + ->setAction($this->actionName); + + return $this; + } + + public function exec() + { + // 监听module_init + $this->app['hook']->listen('module_init'); + + try { + // 实例化控制器 + $instance = $this->app->controller($this->controller, + $this->rule->getConfig('url_controller_layer'), + $this->rule->getConfig('controller_suffix'), + $this->rule->getConfig('empty_controller')); + } catch (ClassNotFoundException $e) { + throw new HttpException(404, 'controller not exists:' . $e->getClass()); + } + + $this->app['middleware']->controller(function (Request $request, $next) use ($instance) { + // 获取当前操作名 + $action = $this->actionName . $this->rule->getConfig('action_suffix'); + + if (is_callable([$instance, $action])) { + // 执行操作方法 + $call = [$instance, $action]; + + // 严格获取当前操作方法名 + $reflect = new ReflectionMethod($instance, $action); + $methodName = $reflect->getName(); + $suffix = $this->rule->getConfig('action_suffix'); + $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName; + $this->request->setAction($actionName); + + // 自动获取请求变量 + $vars = $this->rule->getConfig('url_param_type') + ? $this->request->route() + : $this->request->param(); + $vars = array_merge($vars, $this->param); + } elseif (is_callable([$instance, '_empty'])) { + // 空操作 + $call = [$instance, '_empty']; + $vars = [$this->actionName]; + $reflect = new ReflectionMethod($instance, '_empty'); + } else { + // 操作不存在 + throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()'); + } + + $this->app['hook']->listen('action_begin', $call); + + $data = $this->app->invokeReflectMethod($instance, $reflect, $vars); + + return $this->autoResponse($data); + }); + + return $this->app['middleware']->dispatch($this->request, 'controller'); + } +} diff --git a/thinkphp/library/think/log/driver/Test.php b/thinkphp/library/think/route/dispatch/Redirect.php similarity index 58% rename from thinkphp/library/think/log/driver/Test.php rename to thinkphp/library/think/route/dispatch/Redirect.php index 7f663384c..fae2c9a65 100644 --- a/thinkphp/library/think/log/driver/Test.php +++ b/thinkphp/library/think/route/dispatch/Redirect.php @@ -1,30 +1,23 @@ // +---------------------------------------------------------------------- -namespace think\log\driver; +namespace think\route\dispatch; -/** - * 模拟测试输出 - */ -class Test +use think\Response; +use think\route\Dispatch; + +class Redirect extends Dispatch { - /** - * 日志写入接口 - * @access public - * @param array $log 日志信息 - * @return bool - */ - public function save(array $log = []) + public function exec() { - return true; + return Response::create($this->dispatch, 'redirect')->code($this->code); } - } diff --git a/thinkphp/start.php b/thinkphp/library/think/route/dispatch/Response.php similarity index 69% rename from thinkphp/start.php rename to thinkphp/library/think/route/dispatch/Response.php index 8af62ef3a..66f4e5abb 100644 --- a/thinkphp/start.php +++ b/thinkphp/library/think/route/dispatch/Response.php @@ -2,17 +2,22 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st // +---------------------------------------------------------------------- -namespace think; +namespace think\route\dispatch; -// ThinkPHP 引导文件 -// 加载基础文件 -require __DIR__ . '/base.php'; -// 执行应用 -App::run()->send(); +use think\route\Dispatch; + +class Response extends Dispatch +{ + public function exec() + { + return $this->dispatch; + } + +} diff --git a/thinkphp/library/think/route/dispatch/Url.php b/thinkphp/library/think/route/dispatch/Url.php new file mode 100644 index 000000000..00dc8cca3 --- /dev/null +++ b/thinkphp/library/think/route/dispatch/Url.php @@ -0,0 +1,169 @@ + +// +---------------------------------------------------------------------- + +namespace think\route\dispatch; + +use think\exception\HttpException; +use think\Loader; +use think\route\Dispatch; + +class Url extends Dispatch +{ + public function init() + { + // 解析默认的URL规则 + $result = $this->parseUrl($this->dispatch); + + return (new Module($this->request, $this->rule, $result))->init(); + } + + public function exec() + {} + + /** + * 解析URL地址 + * @access protected + * @param string $url URL + * @return array + */ + protected function parseUrl($url) + { + $depr = $this->rule->getConfig('pathinfo_depr'); + $bind = $this->rule->getRouter()->getBind(); + + if (!empty($bind) && preg_match('/^[a-z]/is', $bind)) { + $bind = str_replace('/', $depr, $bind); + // 如果有模块/控制器绑定 + $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr); + } + + list($path, $var) = $this->rule->parseUrlPath($url); + if (empty($path)) { + return [null, null, null]; + } + + // 解析模块 + $module = $this->rule->getConfig('app_multi_module') ? array_shift($path) : null; + + if ($this->param['auto_search']) { + $controller = $this->autoFindController($module, $path); + } else { + // 解析控制器 + $controller = !empty($path) ? array_shift($path) : null; + } + + if ($controller && !preg_match('/^[A-Za-z][\w|\.]*$/', $controller)) { + throw new HttpException(404, 'controller not exists:' . $controller); + } + + // 解析操作 + $action = !empty($path) ? array_shift($path) : null; + + // 解析额外参数 + if ($path) { + if ($this->rule->getConfig('url_param_type')) { + $var += $path; + } else { + preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) { + $var[$match[1]] = strip_tags($match[2]); + }, implode('|', $path)); + } + } + + $panDomain = $this->request->panDomain(); + + if ($panDomain && $key = array_search('*', $var)) { + // 泛域名赋值 + $var[$key] = $panDomain; + } + + // 设置当前请求的参数 + $this->request->setRouteVars($var); + + // 封装路由 + $route = [$module, $controller, $action]; + + if ($this->hasDefinedRoute($route, $bind)) { + throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url)); + } + + return $route; + } + + /** + * 检查URL是否已经定义过路由 + * @access protected + * @param string $route 路由信息 + * @param string $bind 绑定信息 + * @return bool + */ + protected function hasDefinedRoute($route, $bind) + { + list($module, $controller, $action) = $route; + + // 检查地址是否被定义过路由 + $name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action); + + $name2 = ''; + + if (empty($module) || $module == $bind) { + $name2 = strtolower(Loader::parseName($controller, 1) . '/' . $action); + } + + $host = $this->request->host(true); + + $method = $this->request->method(); + + if ($this->rule->getRouter()->getName($name, $host, $method) || $this->rule->getRouter()->getName($name2, $host, $method)) { + return true; + } + + return false; + } + + /** + * 自动定位控制器类 + * @access protected + * @param string $module 模块名 + * @param array $path URL + * @return string + */ + protected function autoFindController($module, &$path) + { + $dir = $this->app->getAppPath() . ($module ? $module . '/' : '') . $this->rule->getConfig('url_controller_layer'); + $suffix = $this->app->getSuffix() || $this->rule->getConfig('controller_suffix') ? ucfirst($this->rule->getConfig('url_controller_layer')) : ''; + + $item = []; + $find = false; + + foreach ($path as $val) { + $item[] = $val; + $file = $dir . '/' . str_replace('.', '/', $val) . $suffix . '.php'; + $file = pathinfo($file, PATHINFO_DIRNAME) . '/' . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . '.php'; + if (is_file($file)) { + $find = true; + break; + } else { + $dir .= '/' . Loader::parseName($val); + } + } + + if ($find) { + $controller = implode('.', $item); + $path = array_slice($path, count($item)); + } else { + $controller = array_shift($path); + } + + return $controller; + } + +} diff --git a/thinkphp/library/think/route/dispatch/View.php b/thinkphp/library/think/route/dispatch/View.php new file mode 100644 index 000000000..ea3ef11b0 --- /dev/null +++ b/thinkphp/library/think/route/dispatch/View.php @@ -0,0 +1,26 @@ + +// +---------------------------------------------------------------------- + +namespace think\route\dispatch; + +use think\Response; +use think\route\Dispatch; + +class View extends Dispatch +{ + public function exec() + { + // 渲染模板输出 + $vars = array_merge($this->request->param(), $this->param); + + return Response::create($this->dispatch, 'view')->assign($vars); + } +} diff --git a/thinkphp/library/think/session/driver/Memcache.php b/thinkphp/library/think/session/driver/Memcache.php index 0c02e23ec..40d7bb825 100644 --- a/thinkphp/library/think/session/driver/Memcache.php +++ b/thinkphp/library/think/session/driver/Memcache.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,10 +11,10 @@ namespace think\session\driver; -use SessionHandler; +use SessionHandlerInterface; use think\Exception; -class Memcache extends SessionHandler +class Memcache implements SessionHandlerInterface { protected $handler = null; protected $config = [ @@ -34,8 +34,8 @@ class Memcache extends SessionHandler /** * 打开Session * @access public - * @param string $savePath - * @param mixed $sessName + * @param string $savePath + * @param mixed $sessName */ public function open($savePath, $sessName) { @@ -43,13 +43,17 @@ class Memcache extends SessionHandler if (!extension_loaded('memcache')) { throw new Exception('not support:memcache'); } + $this->handler = new \Memcache; + // 支持集群 $hosts = explode(',', $this->config['host']); $ports = explode(',', $this->config['port']); + if (empty($ports[0])) { $ports[0] = 11211; } + // 建立连接 foreach ((array) $hosts as $i => $host) { $port = isset($ports[$i]) ? $ports[$i] : $ports[0]; @@ -57,6 +61,7 @@ class Memcache extends SessionHandler $this->handler->addServer($host, $port, $this->config['persistent'], 1, $this->config['timeout']) : $this->handler->addServer($host, $port, $this->config['persistent'], 1); } + return true; } @@ -69,13 +74,14 @@ class Memcache extends SessionHandler $this->gc(ini_get('session.gc_maxlifetime')); $this->handler->close(); $this->handler = null; + return true; } /** * 读取Session * @access public - * @param string $sessID + * @param string $sessID */ public function read($sessID) { @@ -85,8 +91,8 @@ class Memcache extends SessionHandler /** * 写入Session * @access public - * @param string $sessID - * @param String $sessData + * @param string $sessID + * @param string $sessData * @return bool */ public function write($sessID, $sessData) @@ -97,7 +103,7 @@ class Memcache extends SessionHandler /** * 删除Session * @access public - * @param string $sessID + * @param string $sessID * @return bool */ public function destroy($sessID) @@ -108,7 +114,7 @@ class Memcache extends SessionHandler /** * Session 垃圾回收 * @access public - * @param string $sessMaxLifeTime + * @param string $sessMaxLifeTime * @return true */ public function gc($sessMaxLifeTime) diff --git a/thinkphp/library/think/session/driver/Memcached.php b/thinkphp/library/think/session/driver/Memcached.php index bcaf8a1b5..074b2ff73 100644 --- a/thinkphp/library/think/session/driver/Memcached.php +++ b/thinkphp/library/think/session/driver/Memcached.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,10 +11,10 @@ namespace think\session\driver; -use SessionHandler; +use SessionHandlerInterface; use think\Exception; -class Memcached extends SessionHandler +class Memcached implements SessionHandlerInterface { protected $handler = null; protected $config = [ @@ -35,8 +35,8 @@ class Memcached extends SessionHandler /** * 打开Session * @access public - * @param string $savePath - * @param mixed $sessName + * @param string $savePath + * @param mixed $sessName */ public function open($savePath, $sessName) { @@ -44,27 +44,35 @@ class Memcached extends SessionHandler if (!extension_loaded('memcached')) { throw new Exception('not support:memcached'); } + $this->handler = new \Memcached; + // 设置连接超时时间(单位:毫秒) if ($this->config['timeout'] > 0) { $this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->config['timeout']); } + // 支持集群 $hosts = explode(',', $this->config['host']); $ports = explode(',', $this->config['port']); + if (empty($ports[0])) { $ports[0] = 11211; } + // 建立连接 $servers = []; foreach ((array) $hosts as $i => $host) { $servers[] = [$host, (isset($ports[$i]) ? $ports[$i] : $ports[0]), 1]; } + $this->handler->addServers($servers); + if ('' != $this->config['username']) { $this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); $this->handler->setSaslAuthData($this->config['username'], $this->config['password']); } + return true; } @@ -77,13 +85,14 @@ class Memcached extends SessionHandler $this->gc(ini_get('session.gc_maxlifetime')); $this->handler->quit(); $this->handler = null; + return true; } /** * 读取Session * @access public - * @param string $sessID + * @param string $sessID */ public function read($sessID) { @@ -93,8 +102,8 @@ class Memcached extends SessionHandler /** * 写入Session * @access public - * @param string $sessID - * @param String $sessData + * @param string $sessID + * @param string $sessData * @return bool */ public function write($sessID, $sessData) @@ -105,7 +114,7 @@ class Memcached extends SessionHandler /** * 删除Session * @access public - * @param string $sessID + * @param string $sessID * @return bool */ public function destroy($sessID) @@ -116,7 +125,7 @@ class Memcached extends SessionHandler /** * Session 垃圾回收 * @access public - * @param string $sessMaxLifeTime + * @param string $sessMaxLifeTime * @return true */ public function gc($sessMaxLifeTime) diff --git a/thinkphp/library/think/session/driver/Redis.php b/thinkphp/library/think/session/driver/Redis.php index a4c2b54a6..646700bd6 100644 --- a/thinkphp/library/think/session/driver/Redis.php +++ b/thinkphp/library/think/session/driver/Redis.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -11,10 +11,10 @@ namespace think\session\driver; -use SessionHandler; +use SessionHandlerInterface; use think\Exception; -class Redis extends SessionHandler +class Redis implements SessionHandlerInterface { /** @var \Redis */ protected $handler = null; @@ -37,29 +37,38 @@ class Redis extends SessionHandler /** * 打开Session * @access public - * @param string $savePath - * @param mixed $sessName + * @param string $savePath + * @param mixed $sessName * @return bool * @throws Exception */ public function open($savePath, $sessName) { - // 检测php环境 - if (!extension_loaded('redis')) { - throw new Exception('not support:redis'); - } - $this->handler = new \Redis; + if (extension_loaded('redis')) { + $this->handler = new \Redis; - // 建立连接 - $func = $this->config['persistent'] ? 'pconnect' : 'connect'; - $this->handler->$func($this->config['host'], $this->config['port'], $this->config['timeout']); + // 建立连接 + $func = $this->config['persistent'] ? 'pconnect' : 'connect'; + $this->handler->$func($this->config['host'], $this->config['port'], $this->config['timeout']); - if ('' != $this->config['password']) { - $this->handler->auth($this->config['password']); - } + if ('' != $this->config['password']) { + $this->handler->auth($this->config['password']); + } - if (0 != $this->config['select']) { - $this->handler->select($this->config['select']); + if (0 != $this->config['select']) { + $this->handler->select($this->config['select']); + } + } elseif (class_exists('\Predis\Client')) { + $params = []; + foreach ($this->config as $key => $val) { + if (in_array($key, ['aggregate', 'cluster', 'connections', 'exceptions', 'prefix', 'profile', 'replication'])) { + $params[$key] = $val; + unset($this->config[$key]); + } + } + $this->handler = new \Predis\Client($this->config, $params); + } else { + throw new \BadFunctionCallException('not support: redis'); } return true; @@ -74,13 +83,14 @@ class Redis extends SessionHandler $this->gc(ini_get('session.gc_maxlifetime')); $this->handler->close(); $this->handler = null; + return true; } /** * 读取Session * @access public - * @param string $sessID + * @param string $sessID * @return string */ public function read($sessID) @@ -91,23 +101,25 @@ class Redis extends SessionHandler /** * 写入Session * @access public - * @param string $sessID - * @param String $sessData + * @param string $sessID + * @param string $sessData * @return bool */ public function write($sessID, $sessData) { if ($this->config['expire'] > 0) { - return $this->handler->setex($this->config['session_name'] . $sessID, $this->config['expire'], $sessData); + $result = $this->handler->setex($this->config['session_name'] . $sessID, $this->config['expire'], $sessData); } else { - return $this->handler->set($this->config['session_name'] . $sessID, $sessData); + $result = $this->handler->set($this->config['session_name'] . $sessID, $sessData); } + + return $result ? true : false; } /** * 删除Session * @access public - * @param string $sessID + * @param string $sessID * @return bool */ public function destroy($sessID) @@ -118,11 +130,50 @@ class Redis extends SessionHandler /** * Session 垃圾回收 * @access public - * @param string $sessMaxLifeTime + * @param string $sessMaxLifeTime * @return bool */ public function gc($sessMaxLifeTime) { return true; } + + /** + * Redis Session 驱动的加锁机制 + * @access public + * @param string $sessID 用于加锁的sessID + * @param integer $timeout 默认过期时间 + * @return bool + */ + public function lock($sessID, $timeout = 10) + { + if (null == $this->handler) { + $this->open('', ''); + } + + $lockKey = 'LOCK_PREFIX_' . $sessID; + // 使用setnx操作加锁 + $isLock = $this->handler->setnx($lockKey, 1); + if ($isLock) { + // 设置过期时间,防止死任务的出现 + $this->handler->expire($lockKey, $timeout); + return true; + } + + return false; + } + + /** + * Redis Session 驱动的解锁机制 + * @access public + * @param string $sessID 用于解锁的sessID + */ + public function unlock($sessID) + { + if (null == $this->handler) { + $this->open('', ''); + } + + $this->handler->del('LOCK_PREFIX_' . $sessID); + } } diff --git a/thinkphp/library/think/template/TagLib.php b/thinkphp/library/think/template/TagLib.php index d343bed05..bbbb2c035 100644 --- a/thinkphp/library/think/template/TagLib.php +++ b/thinkphp/library/think/template/TagLib.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -68,9 +68,9 @@ class TagLib protected $comparison = [' nheq ' => ' !== ', ' heq ' => ' === ', ' neq ' => ' != ', ' eq ' => ' == ', ' egt ' => ' >= ', ' gt ' => ' > ', ' elt ' => ' <= ', ' lt ' => ' < ']; /** - * 构造函数 + * 架构函数 * @access public - * @param \stdClass $template 模板引擎对象 + * @param \stdClass $template 模板引擎对象 */ public function __construct($template) { @@ -88,6 +88,7 @@ class TagLib { $tags = []; $lib = $lib ? strtolower($lib) . ':' : ''; + foreach ($this->tags as $name => $val) { $close = !isset($val['close']) || $val['close'] ? 1 : 0; $tags[$close][$lib . $name] = $name; @@ -136,11 +137,14 @@ class TagLib // 对应的标签名 $name = $tags[1][$node['name']]; $alias = $lib . $name != $node['name'] ? ($lib ? strstr($node['name'], $lib) : $node['name']) : ''; + // 解析标签属性 $attrs = $this->parseAttr($node['begin'][0], $name, $alias); $method = 'tag' . $name; + // 读取标签库中对应的标签内容 replace[0]用来替换标签头,replace[1]用来替换标签尾 $replace = explode($break, $this->$method($attrs, $break)); + if (count($replace) > 1) { while ($beginArray) { $begin = end($beginArray); @@ -160,6 +164,7 @@ class TagLib $beginArray[] = ['pos' => $node['begin'][1], 'len' => strlen($node['begin'][0]), 'str' => $replace[0]]; } } + while ($beginArray) { $begin = array_pop($beginArray); // 替换标签头部 @@ -180,12 +185,13 @@ class TagLib return $this->$method($attrs, ''); }, $content); } + return; } /** * 按标签生成正则 - * @access private + * @access public * @param array|string $tags 标签名 * @param boolean $close 是否为闭合标签 * @return string @@ -196,6 +202,7 @@ class TagLib $end = $this->tpl->config('taglib_end'); $single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false; $tagName = is_array($tags) ? implode('|', $tags) : $tags; + if ($single) { if ($close) { // 如果是闭合标签 @@ -211,25 +218,28 @@ class TagLib $regex = $begin . '(' . $tagName . ')\b(?>(?:(?!' . $end . ').)*)' . $end; } } + return '/' . $regex . '/is'; } /** * 分析标签属性 正则方式 * @access public - * @param string $str 标签属性字符串 - * @param string $name 标签名 - * @param string $alias 别名 + * @param string $str 标签属性字符串 + * @param string $name 标签名 + * @param string $alias 别名 * @return array */ public function parseAttr($str, $name, $alias = '') { $regex = '/\s+(?>(?P[\w-]+)\s*)=(?>\s*)([\"\'])(?P(?:(?!\\2).)*)\\2/is'; $result = []; + if (preg_match_all($regex, $str, $matches)) { foreach ($matches['name'] as $key => $val) { $result[$val] = $matches['value'][$key]; } + if (!isset($this->tags[$name])) { // 检测是否存在别名定义 foreach ($this->tags as $key => $val) { @@ -251,6 +261,7 @@ class TagLib $result[$type] = $alias; } } + if (!empty($tag['must'])) { $must = explode(',', $tag['must']); foreach ($must as $name) { @@ -264,8 +275,8 @@ class TagLib if (!empty($this->tags[$name]['expression'])) { static $_taglibs; if (!isset($_taglibs[$name])) { - $_taglibs[$name][0] = strlen(ltrim($this->tpl->config('taglib_begin'), '\\') . $name); - $_taglibs[$name][1] = strlen(ltrim($this->tpl->config('taglib_end'), '\\')); + $_taglibs[$name][0] = strlen($this->tpl->config('taglib_begin_origin') . $name); + $_taglibs[$name][1] = strlen($this->tpl->config('taglib_end_origin')); } $result['expression'] = substr($str, $_taglibs[$name][0], -$_taglibs[$name][1]); // 清除自闭合标签尾部/ @@ -275,6 +286,7 @@ class TagLib throw new Exception('tag error:' . $name); } } + return $result; } @@ -286,11 +298,13 @@ class TagLib */ public function parseCondition($condition) { - if (strpos($condition, ':')) { + if (!strpos($condition, '::') && strpos($condition, ':')) { $condition = ' ' . substr(strstr($condition, ':'), 1); } + $condition = str_ireplace(array_keys($this->comparison), array_values($this->comparison), $condition); $this->tpl->parseVar($condition); + // $this->tpl->parseVarFunction($condition); // XXX: 此句能解析表达式中用|分隔的函数,但表达式中如果有|、||这样的逻辑运算就产生了歧异 return $condition; } @@ -298,12 +312,13 @@ class TagLib /** * 自动识别构建变量 * @access public - * @param string $name 变量描述 + * @param string $name 变量描述 * @return string */ public function autoBuildVar(&$name) { $flag = substr($name, 0, 1); + if (':' == $flag) { // 以:开头为函数调用,解析前去掉: $name = substr($name, 1); @@ -313,11 +328,14 @@ class TagLib if (defined($name)) { return $name; } + // 不以$开头并且也不是常量,自动补上$前缀 $name = '$' . $name; } + $this->tpl->parseVar($name); - $this->tpl->parseVarFunction($name); + $this->tpl->parseVarFunction($name, false); + return $name; } @@ -326,7 +344,6 @@ class TagLib * @access public * @return array */ - // 获取标签定义 public function getTags() { return $this->tags; diff --git a/thinkphp/library/think/template/driver/File.php b/thinkphp/library/think/template/driver/File.php index b27e72659..3b96a0f3b 100644 --- a/thinkphp/library/think/template/driver/File.php +++ b/thinkphp/library/think/template/driver/File.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -15,19 +15,24 @@ use think\Exception; class File { + protected $cacheFile; + /** * 写入编译缓存 - * @param string $cacheFile 缓存的文件名 - * @param string $content 缓存的内容 + * @access public + * @param string $cacheFile 缓存的文件名 + * @param string $content 缓存的内容 * @return void|array */ public function write($cacheFile, $content) { // 检测模板目录 $dir = dirname($cacheFile); + if (!is_dir($dir)) { mkdir($dir, 0755, true); } + // 生成模板缓存文件 if (false === file_put_contents($cacheFile, $content)) { throw new Exception('cache write error:' . $cacheFile, 11602); @@ -36,24 +41,29 @@ class File /** * 读取编译编译 - * @param string $cacheFile 缓存的文件名 - * @param array $vars 变量数组 + * @access public + * @param string $cacheFile 缓存的文件名 + * @param array $vars 变量数组 * @return void */ public function read($cacheFile, $vars = []) { + $this->cacheFile = $cacheFile; + if (!empty($vars) && is_array($vars)) { // 模板阵列变量分解成为独立变量 extract($vars, EXTR_OVERWRITE); } + //载入模版缓存文件 - include $cacheFile; + include $this->cacheFile; } /** * 检查编译缓存是否有效 - * @param string $cacheFile 缓存的文件名 - * @param int $cacheTime 缓存时间 + * @access public + * @param string $cacheFile 缓存的文件名 + * @param int $cacheTime 缓存时间 * @return boolean */ public function check($cacheFile, $cacheTime) @@ -62,10 +72,12 @@ class File if (!file_exists($cacheFile)) { return false; } - if (0 != $cacheTime && $_SERVER['REQUEST_TIME'] > filemtime($cacheFile) + $cacheTime) { + + if (0 != $cacheTime && time() > filemtime($cacheFile) + $cacheTime) { // 缓存是否在有效期 return false; } + return true; } } diff --git a/thinkphp/library/think/template/taglib/Cx.php b/thinkphp/library/think/template/taglib/Cx.php index af7a54c89..ad741f289 100644 --- a/thinkphp/library/think/template/taglib/Cx.php +++ b/thinkphp/library/think/template/taglib/Cx.php @@ -56,8 +56,8 @@ class Cx extends Taglib * 格式: * {php}echo $name{/php} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagPhp($tag, $content) @@ -74,8 +74,8 @@ class Cx extends Taglib * {user.email} * {/volist} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string|void */ public function tagVolist($tag, $content) @@ -90,6 +90,7 @@ class Cx extends Taglib // 允许使用函数设定数据集 {$vo.name} $parseStr = 'autoBuildVar($name); $parseStr .= '$_result=' . $name . ';'; @@ -99,12 +100,14 @@ class Cx extends Taglib } $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): $' . $key . ' = 0;'; + // 设置了输出数组长度 if (0 != $offset || 'null' != $length) { $parseStr .= '$__LIST__ = is_array(' . $name . ') ? array_slice(' . $name . ',' . $offset . ',' . $length . ', true) : ' . $name . '->slice(' . $offset . ',' . $length . ', true); '; } else { $parseStr .= ' $__LIST__ = ' . $name . ';'; } + $parseStr .= 'if( count($__LIST__)==0 ) : echo "' . $empty . '" ;'; $parseStr .= 'else: '; $parseStr .= 'foreach($__LIST__ as $key=>$' . $id . '): '; @@ -116,6 +119,7 @@ class Cx extends Taglib if (!empty($parseStr)) { return $parseStr; } + return; } @@ -126,8 +130,8 @@ class Cx extends Taglib * {user.username} * {/foreach} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string|void */ public function tagForeach($tag, $content) @@ -141,6 +145,7 @@ class Cx extends Taglib $parseStr .= ''; return $parseStr; } + $name = $tag['name']; $key = !empty($tag['key']) ? $tag['key'] : 'key'; $item = !empty($tag['id']) ? $tag['id'] : $tag['item']; @@ -149,6 +154,7 @@ class Cx extends Taglib $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null'; $parseStr = 'autoBuildVar($name); } + $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): '; + // 设置了输出数组长度 if (0 != $offset || 'null' != $length) { if (!isset($var)) { @@ -177,7 +185,9 @@ class Cx extends Taglib $index = $tag['index']; $parseStr .= '$' . $index . '=0; '; } + $parseStr .= 'foreach(' . $var . ' as $' . $key . '=>$' . $item . '): '; + // 设置了索引项 if (isset($tag['index'])) { $index = $tag['index']; @@ -187,6 +197,7 @@ class Cx extends Taglib } $parseStr .= '++$' . $index . '; '; } + $parseStr .= '?>'; // 循环体中的内容 $parseStr .= $content; @@ -195,6 +206,7 @@ class Cx extends Taglib if (!empty($parseStr)) { return $parseStr; } + return; } @@ -207,8 +219,8 @@ class Cx extends Taglib * {/if} * 表达式支持 eq neq gt egt lt elt == > >= < <= or and || && * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagIf($tag, $content) @@ -216,6 +228,7 @@ class Cx extends Taglib $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; $condition = $this->parseCondition($condition); $parseStr = '' . $content . ''; + return $parseStr; } @@ -223,8 +236,8 @@ class Cx extends Taglib * elseif标签解析 * 格式:见if标签 * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagElseif($tag, $content) @@ -232,6 +245,7 @@ class Cx extends Taglib $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; $condition = $this->parseCondition($condition); $parseStr = ''; + return $parseStr; } @@ -239,12 +253,13 @@ class Cx extends Taglib * else标签解析 * 格式:见if标签 * @access public - * @param array $tag 标签属性 + * @param array $tag 标签属性 * @return string */ public function tagElse($tag) { $parseStr = ''; + return $parseStr; } @@ -257,8 +272,8 @@ class Cx extends Taglib * {default /}other * {/switch} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagSwitch($tag, $content) @@ -266,20 +281,22 @@ class Cx extends Taglib $name = !empty($tag['expression']) ? $tag['expression'] : $tag['name']; $name = $this->autoBuildVar($name); $parseStr = '' . $content . ''; + return $parseStr; } /** * case标签解析 需要配合switch才有效 * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagCase($tag, $content) { - $value = !empty($tag['expression']) ? $tag['expression'] : $tag['value']; + $value = isset($tag['expression']) ? $tag['expression'] : $tag['value']; $flag = substr($value, 0, 1); + if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); $value = 'case ' . $value . ':'; @@ -292,11 +309,14 @@ class Cx extends Taglib } else { $value = 'case "' . $value . '":'; } + $parseStr = '' . $content; $isBreak = isset($tag['break']) ? $tag['break'] : ''; + if ('' == $isBreak || $isBreak) { $parseStr .= ''; } + return $parseStr; } @@ -304,13 +324,14 @@ class Cx extends Taglib * default标签解析 需要配合switch才有效 * 使用: {default /}ddfdf * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagDefault($tag) { $parseStr = ''; + return $parseStr; } @@ -319,8 +340,8 @@ class Cx extends Taglib * 用于值的比较 支持 eq neq gt lt egt elt heq nheq 默认是eq * 格式: {compare name="" type="eq" value="" }content{/compare} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagCompare($tag, $content) @@ -330,11 +351,13 @@ class Cx extends Taglib $type = isset($tag['type']) ? $tag['type'] : 'eq'; // 比较类型 $name = $this->autoBuildVar($name); $flag = substr($value, 0, 1); + if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); } else { $value = '\'' . $value . '\''; } + switch ($type) { case 'equal': $type = 'eq'; @@ -345,6 +368,7 @@ class Cx extends Taglib } $type = $this->parseCondition(' ' . $type . ' '); $parseStr = '' . $content . ''; + return $parseStr; } @@ -354,8 +378,8 @@ class Cx extends Taglib * 格式: {range name="var|function" value="val" type='in|notin' }content{/range} * example: {range name="a" value="1,2,3" type='in' }content{/range} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagRange($tag, $content) @@ -366,6 +390,7 @@ class Cx extends Taglib $name = $this->autoBuildVar($name); $flag = substr($value, 0, 1); + if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($value); $str = 'is_array(' . $value . ')?' . $value . ':explode(\',\',' . $value . ')'; @@ -373,6 +398,7 @@ class Cx extends Taglib $value = '"' . $value . '"'; $str = 'explode(\',\',' . $value . ')'; } + if ('between' == $type) { $parseStr = '= $_RANGE_VAR_[0] && ' . $name . '<= $_RANGE_VAR_[1]):?>' . $content . ''; } elseif ('notbetween' == $type) { @@ -381,6 +407,7 @@ class Cx extends Taglib $fun = ('in' == $type) ? 'in_array' : '!in_array'; $parseStr = '' . $content . ''; } + return $parseStr; } @@ -389,8 +416,8 @@ class Cx extends Taglib * 如果某个变量已经设置 则输出内容 * 格式: {present name="" }content{/present} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagPresent($tag, $content) @@ -398,6 +425,7 @@ class Cx extends Taglib $name = $tag['name']; $name = $this->autoBuildVar($name); $parseStr = '' . $content . ''; + return $parseStr; } @@ -406,8 +434,8 @@ class Cx extends Taglib * 如果某个变量没有设置,则输出内容 * 格式: {notpresent name="" }content{/notpresent} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagNotpresent($tag, $content) @@ -415,6 +443,7 @@ class Cx extends Taglib $name = $tag['name']; $name = $this->autoBuildVar($name); $parseStr = '' . $content . ''; + return $parseStr; } @@ -423,8 +452,8 @@ class Cx extends Taglib * 如果某个变量为empty 则输出内容 * 格式: {empty name="" }content{/empty} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagEmpty($tag, $content) @@ -432,6 +461,7 @@ class Cx extends Taglib $name = $tag['name']; $name = $this->autoBuildVar($name); $parseStr = 'isEmpty())): ?>' . $content . ''; + return $parseStr; } @@ -440,8 +470,8 @@ class Cx extends Taglib * 如果某个变量不为empty 则输出内容 * 格式: {notempty name="" }content{/notempty} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagNotempty($tag, $content) @@ -449,34 +479,39 @@ class Cx extends Taglib $name = $tag['name']; $name = $this->autoBuildVar($name); $parseStr = 'isEmpty()))): ?>' . $content . ''; + return $parseStr; } /** * 判断是否已经定义了该常量 * {defined name='TXT'}已定义{/defined} - * @param array $tag - * @param string $content + * @access public + * @param array $tag + * @param string $content * @return string */ public function tagDefined($tag, $content) { $name = $tag['name']; $parseStr = '' . $content . ''; + return $parseStr; } /** * 判断是否没有定义了该常量 * {notdefined name='TXT'}已定义{/notdefined} - * @param array $tag - * @param string $content + * @access public + * @param array $tag + * @param string $content * @return string */ public function tagNotdefined($tag, $content) { $name = $tag['name']; $parseStr = '' . $content . ''; + return $parseStr; } @@ -484,16 +519,18 @@ class Cx extends Taglib * load 标签解析 {load file="/static/js/base.js" /} * 格式:{load file="/static/css/base.css" /} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagLoad($tag, $content) { - $file = isset($tag['file']) ? $tag['file'] : $tag['href']; - $type = isset($tag['type']) ? strtolower($tag['type']) : ''; + $file = isset($tag['file']) ? $tag['file'] : $tag['href']; + $type = isset($tag['type']) ? strtolower($tag['type']) : ''; + $parseStr = ''; $endStr = ''; + // 判断是否存在加载条件 允许使用函数判断(默认为isset) if (isset($tag['value'])) { $name = $tag['value']; @@ -505,6 +542,7 @@ class Cx extends Taglib // 文件方式导入 $array = explode(',', $file); + foreach ($array as $val) { $type = strtolower(substr(strrchr($val, '.'), 1)); switch ($type) { @@ -519,6 +557,7 @@ class Cx extends Taglib break; } } + return $parseStr . $endStr; } @@ -527,20 +566,23 @@ class Cx extends Taglib * 在模板中给某个变量赋值 支持变量赋值 * 格式: {assign name="" value="" /} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagAssign($tag, $content) { $name = $this->autoBuildVar($tag['name']); $flag = substr($tag['value'], 0, 1); + if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($tag['value']); } else { $value = '\'' . $tag['value'] . '\''; } + $parseStr = ''; + return $parseStr; } @@ -549,20 +591,23 @@ class Cx extends Taglib * 在模板中定义常量 支持变量赋值 * 格式: {define name="" value="" /} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagDefine($tag, $content) { $name = '\'' . $tag['name'] . '\''; $flag = substr($tag['value'], 0, 1); + if ('$' == $flag || ':' == $flag) { $value = $this->autoBuildVar($tag['value']); } else { $value = '\'' . $tag['value'] . '\''; } + $parseStr = ''; + return $parseStr; } @@ -573,8 +618,8 @@ class Cx extends Taglib * content * {/for} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagFor($tag, $content) @@ -586,6 +631,7 @@ class Cx extends Taglib $comparison = 'lt'; $name = 'i'; $rand = rand(); //添加随机数,防止嵌套变量冲突 + //获取属性 foreach ($tag as $key => $value) { $value = trim($value); @@ -617,6 +663,7 @@ class Cx extends Taglib $parseStr .= 'for($' . $name . '=$__FOR_START_' . $rand . '__;' . $this->parseCondition('$' . $name . ' ' . $comparison . ' $__FOR_END_' . $rand . '__') . ';$' . $name . '+=' . $step . '){ ?>'; $parseStr .= $content; $parseStr .= ''; + return $parseStr; } @@ -624,8 +671,8 @@ class Cx extends Taglib * url函数的tag标签 * 格式:{url link="模块/控制器/方法" vars="参数" suffix="true或者false 是否带有后缀" domain="true或者false 是否携带域名" /} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagUrl($tag, $content) @@ -634,6 +681,7 @@ class Cx extends Taglib $vars = isset($tag['vars']) ? $tag['vars'] : ''; $suffix = isset($tag['suffix']) ? $tag['suffix'] : 'true'; $domain = isset($tag['domain']) ? $tag['domain'] : 'false'; + return ''; } @@ -650,8 +698,8 @@ class Cx extends Taglib * {/if} * {/function} * @access public - * @param array $tag 标签属性 - * @param string $content 标签内容 + * @param array $tag 标签属性 + * @param string $content 标签内容 * @return string */ public function tagFunction($tag, $content) @@ -660,14 +708,17 @@ class Cx extends Taglib $vars = !empty($tag['vars']) ? $tag['vars'] : ''; $call = !empty($tag['call']) ? $tag['call'] : ''; $use = ['&$' . $name]; + if (!empty($tag['use'])) { foreach (explode(',', $tag['use']) as $val) { $use[] = '&' . ltrim(trim($val), '&'); } } + $parseStr = '' . $content . '' : '?>'; + return $parseStr; } } diff --git a/thinkphp/library/think/validate/ValidateRule.php b/thinkphp/library/think/validate/ValidateRule.php new file mode 100644 index 000000000..7cd701747 --- /dev/null +++ b/thinkphp/library/think/validate/ValidateRule.php @@ -0,0 +1,171 @@ + +// +---------------------------------------------------------------------- + +namespace think\validate; + +/** + * Class ValidateRule + * @package think\validate + * @method ValidateRule confirm(mixed $rule, string $msg = '') static 验证是否和某个字段的值一致 + * @method ValidateRule different(mixed $rule, string $msg = '') static 验证是否和某个字段的值是否不同 + * @method ValidateRule egt(mixed $rule, string $msg = '') static 验证是否大于等于某个值 + * @method ValidateRule gt(mixed $rule, string $msg = '') static 验证是否大于某个值 + * @method ValidateRule elt(mixed $rule, string $msg = '') static 验证是否小于等于某个值 + * @method ValidateRule lt(mixed $rule, string $msg = '') static 验证是否小于某个值 + * @method ValidateRule eg(mixed $rule, string $msg = '') static 验证是否等于某个值 + * @method ValidateRule in(mixed $rule, string $msg = '') static 验证是否在范围内 + * @method ValidateRule notIn(mixed $rule, string $msg = '') static 验证是否不在某个范围 + * @method ValidateRule between(mixed $rule, string $msg = '') static 验证是否在某个区间 + * @method ValidateRule notBetween(mixed $rule, string $msg = '') static 验证是否不在某个区间 + * @method ValidateRule length(mixed $rule, string $msg = '') static 验证数据长度 + * @method ValidateRule max(mixed $rule, string $msg = '') static 验证数据最大长度 + * @method ValidateRule min(mixed $rule, string $msg = '') static 验证数据最小长度 + * @method ValidateRule after(mixed $rule, string $msg = '') static 验证日期 + * @method ValidateRule before(mixed $rule, string $msg = '') static 验证日期 + * @method ValidateRule expire(mixed $rule, string $msg = '') static 验证有效期 + * @method ValidateRule allowIp(mixed $rule, string $msg = '') static 验证IP许可 + * @method ValidateRule denyIp(mixed $rule, string $msg = '') static 验证IP禁用 + * @method ValidateRule regex(mixed $rule, string $msg = '') static 使用正则验证数据 + * @method ValidateRule token(mixed $rule='__token__', string $msg = '') static 验证表单令牌 + * @method ValidateRule is(mixed $rule, string $msg = '') static 验证字段值是否为有效格式 + * @method ValidateRule isRequire(mixed $rule = null, string $msg = '') static 验证字段必须 + * @method ValidateRule isNumber(mixed $rule = null, string $msg = '') static 验证字段值是否为数字 + * @method ValidateRule isArray(mixed $rule = null, string $msg = '') static 验证字段值是否为数组 + * @method ValidateRule isInteger(mixed $rule = null, string $msg = '') static 验证字段值是否为整形 + * @method ValidateRule isFloat(mixed $rule = null, string $msg = '') static 验证字段值是否为浮点数 + * @method ValidateRule isMobile(mixed $rule = null, string $msg = '') static 验证字段值是否为手机 + * @method ValidateRule isIdCard(mixed $rule = null, string $msg = '') static 验证字段值是否为身份证号码 + * @method ValidateRule isChs(mixed $rule = null, string $msg = '') static 验证字段值是否为中文 + * @method ValidateRule isChsDash(mixed $rule = null, string $msg = '') static 验证字段值是否为中文字母及下划线 + * @method ValidateRule isChsAlpha(mixed $rule = null, string $msg = '') static 验证字段值是否为中文和字母 + * @method ValidateRule isChsAlphaNum(mixed $rule = null, string $msg = '') static 验证字段值是否为中文字母和数字 + * @method ValidateRule isDate(mixed $rule = null, string $msg = '') static 验证字段值是否为有效格式 + * @method ValidateRule isBool(mixed $rule = null, string $msg = '') static 验证字段值是否为布尔值 + * @method ValidateRule isAlpha(mixed $rule = null, string $msg = '') static 验证字段值是否为字母 + * @method ValidateRule isAlphaDash(mixed $rule = null, string $msg = '') static 验证字段值是否为字母和下划线 + * @method ValidateRule isAlphaNum(mixed $rule = null, string $msg = '') static 验证字段值是否为字母和数字 + * @method ValidateRule isAccepted(mixed $rule = null, string $msg = '') static 验证字段值是否为yes, on, 或是 1 + * @method ValidateRule isEmail(mixed $rule = null, string $msg = '') static 验证字段值是否为有效邮箱格式 + * @method ValidateRule isUrl(mixed $rule = null, string $msg = '') static 验证字段值是否为有效URL地址 + * @method ValidateRule activeUrl(mixed $rule, string $msg = '') static 验证是否为合格的域名或者IP + * @method ValidateRule ip(mixed $rule, string $msg = '') static 验证是否有效IP + * @method ValidateRule fileExt(mixed $rule, string $msg = '') static 验证文件后缀 + * @method ValidateRule fileMime(mixed $rule, string $msg = '') static 验证文件类型 + * @method ValidateRule fileSize(mixed $rule, string $msg = '') static 验证文件大小 + * @method ValidateRule image(mixed $rule, string $msg = '') static 验证图像文件 + * @method ValidateRule method(mixed $rule, string $msg = '') static 验证请求类型 + * @method ValidateRule dateFormat(mixed $rule, string $msg = '') static 验证时间和日期是否符合指定格式 + * @method ValidateRule unique(mixed $rule, string $msg = '') static 验证是否唯一 + * @method ValidateRule behavior(mixed $rule, string $msg = '') static 使用行为类验证 + * @method ValidateRule filter(mixed $rule, string $msg = '') static 使用filter_var方式验证 + * @method ValidateRule requireIf(mixed $rule, string $msg = '') static 验证某个字段等于某个值的时候必须 + * @method ValidateRule requireCallback(mixed $rule, string $msg = '') static 通过回调方法验证某个字段是否必须 + * @method ValidateRule requireWith(mixed $rule, string $msg = '') static 验证某个字段有值的情况下必须 + * @method ValidateRule must(mixed $rule = null, string $msg = '') static 必须验证 + */ +class ValidateRule +{ + // 验证字段的名称 + protected $title; + + // 当前验证规则 + protected $rule = []; + + // 验证提示信息 + protected $message = []; + + /** + * 添加验证因子 + * @access protected + * @param string $name 验证名称 + * @param mixed $rule 验证规则 + * @param string $msg 提示信息 + * @return $this + */ + protected function addItem($name, $rule = null, $msg = '') + { + if ($rule || 0 === $rule) { + $this->rule[$name] = $rule; + } else { + $this->rule[] = $name; + } + + $this->message[] = $msg; + + return $this; + } + + /** + * 获取验证规则 + * @access public + * @return array + */ + public function getRule() + { + return $this->rule; + } + + /** + * 获取验证字段名称 + * @access public + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * 获取验证提示 + * @access public + * @return array + */ + public function getMsg() + { + return $this->message; + } + + /** + * 设置验证字段名称 + * @access public + * @return $this + */ + public function title($title) + { + $this->title = $title; + + return $this; + } + + public function __call($method, $args) + { + if ('is' == strtolower(substr($method, 0, 2))) { + $method = substr($method, 2); + } + + array_unshift($args, lcfirst($method)); + + return call_user_func_array([$this, 'addItem'], $args); + } + + public static function __callStatic($method, $args) + { + $rule = new static(); + + if ('is' == strtolower(substr($method, 0, 2))) { + $method = substr($method, 2); + } + + array_unshift($args, lcfirst($method)); + + return call_user_func_array([$rule, 'addItem'], $args); + } +} diff --git a/thinkphp/library/think/view/driver/Php.php b/thinkphp/library/think/view/driver/Php.php index 468d3611d..7948dc053 100644 --- a/thinkphp/library/think/view/driver/Php.php +++ b/thinkphp/library/think/view/driver/Php.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -14,13 +14,13 @@ namespace think\view\driver; use think\App; use think\exception\TemplateNotFoundException; use think\Loader; -use think\Log; -use think\Request; class Php { // 模板引擎参数 protected $config = [ + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 + 'auto_rule' => 1, // 视图基础目录(集中式) 'view_base' => '', // 模板起始路径 @@ -28,18 +28,23 @@ class Php // 模板文件后缀 'view_suffix' => 'php', // 模板文件名分隔符 - 'view_depr' => DS, + 'view_depr' => DIRECTORY_SEPARATOR, ]; - public function __construct($config = []) + protected $template; + protected $app; + protected $content; + + public function __construct(App $app, $config = []) { - $this->config = array_merge($this->config, $config); + $this->app = $app; + $this->config = array_merge($this->config, (array) $config); } /** * 检测是否存在模板文件 * @access public - * @param string $template 模板文件或者模板规则 + * @param string $template 模板文件或者模板规则 * @return bool */ public function exists($template) @@ -48,14 +53,15 @@ class Php // 获取模板文件名 $template = $this->parseTemplate($template); } + return is_file($template); } /** * 渲染模板文件 * @access public - * @param string $template 模板文件 - * @param array $data 模板变量 + * @param string $template 模板文件 + * @param array $data 模板变量 * @return void */ public function fetch($template, $data = []) @@ -64,90 +70,99 @@ class Php // 获取模板文件名 $template = $this->parseTemplate($template); } + // 模板不存在 抛出异常 if (!is_file($template)) { throw new TemplateNotFoundException('template not exists:' . $template, $template); } + + $this->template = $template; + // 记录视图信息 - App::$debug && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]', 'info'); - if (isset($data['template'])) { - $__template__ = $template; - extract($data, EXTR_OVERWRITE); - include $__template__; - } else { - extract($data, EXTR_OVERWRITE); - include $template; - } + $this->app + ->log('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]'); + + extract($data, EXTR_OVERWRITE); + include $this->template; } /** * 渲染模板内容 * @access public - * @param string $content 模板内容 - * @param array $data 模板变量 + * @param string $content 模板内容 + * @param array $data 模板变量 * @return void */ public function display($content, $data = []) { - if (isset($data['content'])) { - $__content__ = $content; - extract($data, EXTR_OVERWRITE); - eval('?>' . $__content__); - } else { - extract($data, EXTR_OVERWRITE); - eval('?>' . $content); - } + $this->content = $content; + + extract($data, EXTR_OVERWRITE); + eval('?>' . $this->content); } /** * 自动定位模板文件 * @access private - * @param string $template 模板文件规则 + * @param string $template 模板文件规则 * @return string */ private function parseTemplate($template) { if (empty($this->config['view_path'])) { - $this->config['view_path'] = App::$modulePath . 'view' . DS; + $this->config['view_path'] = $this->app->getModulePath() . 'view' . DIRECTORY_SEPARATOR; } - $request = Request::instance(); + $request = $this->app['request']; + // 获取视图根目录 if (strpos($template, '@')) { // 跨模块调用 list($module, $template) = explode('@', $template); } + if ($this->config['view_base']) { // 基础视图目录 $module = isset($module) ? $module : $request->module(); - $path = $this->config['view_base'] . ($module ? $module . DS : ''); + $path = $this->config['view_base'] . ($module ? $module . DIRECTORY_SEPARATOR : ''); } else { - $path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path']; + $path = isset($module) ? $this->app->getAppPath() . $module . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR : $this->config['view_path']; } $depr = $this->config['view_depr']; + if (0 !== strpos($template, '/')) { $template = str_replace(['/', ':'], $depr, $template); $controller = Loader::parseName($request->controller()); + if ($controller) { if ('' == $template) { // 如果模板文件名为空 按照默认规则定位 - $template = str_replace('.', DS, $controller) . $depr . $request->action(); + $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $this->getActionTemplate($request); } elseif (false === strpos($template, $depr)) { - $template = str_replace('.', DS, $controller) . $depr . $template; + $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template; } } } else { $template = str_replace(['/', ':'], $depr, substr($template, 1)); } + return $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.'); } + protected function getActionTemplate($request) + { + $rule = [$request->action(true), Loader::parseName($request->action(true)), $request->action()]; + $type = $this->config['auto_rule']; + + return isset($rule[$type]) ? $rule[$type] : $rule[0]; + } + /** * 配置模板引擎 * @access private - * @param string|array $name 参数名 - * @param mixed $value 参数值 + * @param string|array $name 参数名 + * @param mixed $value 参数值 * @return void */ public function config($name, $value = null) @@ -161,4 +176,8 @@ class Php } } + public function __debugInfo() + { + return ['config' => $this->config]; + } } diff --git a/thinkphp/library/think/view/driver/Think.php b/thinkphp/library/think/view/driver/Think.php index 39a14ca33..877aee85b 100644 --- a/thinkphp/library/think/view/driver/Think.php +++ b/thinkphp/library/think/view/driver/Think.php @@ -2,7 +2,7 @@ // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- -// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved. +// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- @@ -14,16 +14,18 @@ namespace think\view\driver; use think\App; use think\exception\TemplateNotFoundException; use think\Loader; -use think\Log; -use think\Request; use think\Template; class Think { // 模板引擎实例 private $template; + private $app; + // 模板引擎参数 protected $config = [ + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 + 'auto_rule' => 1, // 视图基础目录(集中式) 'view_base' => '', // 模板起始路径 @@ -31,25 +33,27 @@ class Think // 模板文件后缀 'view_suffix' => 'html', // 模板文件名分隔符 - 'view_depr' => DS, + 'view_depr' => DIRECTORY_SEPARATOR, // 是否开启模板编译缓存,设为false则每次都会重新编译 'tpl_cache' => true, ]; - public function __construct($config = []) + public function __construct(App $app, $config = []) { - $this->config = array_merge($this->config, $config); + $this->app = $app; + $this->config = array_merge($this->config, (array) $config); + if (empty($this->config['view_path'])) { - $this->config['view_path'] = App::$modulePath . 'view' . DS; + $this->config['view_path'] = $app->getModulePath() . 'view' . DIRECTORY_SEPARATOR; } - $this->template = new Template($this->config); + $this->template = new Template($app, $this->config); } /** * 检测是否存在模板文件 * @access public - * @param string $template 模板文件或者模板规则 + * @param string $template 模板文件或者模板规则 * @return bool */ public function exists($template) @@ -58,15 +62,16 @@ class Think // 获取模板文件名 $template = $this->parseTemplate($template); } + return is_file($template); } /** * 渲染模板文件 * @access public - * @param string $template 模板文件 - * @param array $data 模板变量 - * @param array $config 模板参数 + * @param string $template 模板文件 + * @param array $data 模板变量 + * @param array $config 模板参数 * @return void */ public function fetch($template, $data = [], $config = []) @@ -75,21 +80,25 @@ class Think // 获取模板文件名 $template = $this->parseTemplate($template); } + // 模板不存在 抛出异常 if (!is_file($template)) { throw new TemplateNotFoundException('template not exists:' . $template, $template); } + // 记录视图信息 - App::$debug && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]', 'info'); + $this->app + ->log('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]'); + $this->template->fetch($template, $data, $config); } /** * 渲染模板内容 * @access public - * @param string $template 模板内容 - * @param array $data 模板变量 - * @param array $config 模板参数 + * @param string $template 模板内容 + * @param array $data 模板变量 + * @param array $config 模板参数 * @return void */ public function display($template, $data = [], $config = []) @@ -100,49 +109,62 @@ class Think /** * 自动定位模板文件 * @access private - * @param string $template 模板文件规则 + * @param string $template 模板文件规则 * @return string */ private function parseTemplate($template) { // 分析模板文件规则 - $request = Request::instance(); + $request = $this->app['request']; + // 获取视图根目录 if (strpos($template, '@')) { // 跨模块调用 list($module, $template) = explode('@', $template); } + if ($this->config['view_base']) { // 基础视图目录 $module = isset($module) ? $module : $request->module(); - $path = $this->config['view_base'] . ($module ? $module . DS : ''); + $path = $this->config['view_base'] . ($module ? $module . DIRECTORY_SEPARATOR : ''); } else { - $path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path']; + $path = isset($module) ? $this->app->getAppPath() . $module . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR : $this->config['view_path']; } $depr = $this->config['view_depr']; + if (0 !== strpos($template, '/')) { $template = str_replace(['/', ':'], $depr, $template); $controller = Loader::parseName($request->controller()); + if ($controller) { if ('' == $template) { // 如果模板文件名为空 按照默认规则定位 - $template = str_replace('.', DS, $controller) . $depr . $request->action(); + $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $this->getActionTemplate($request); } elseif (false === strpos($template, $depr)) { - $template = str_replace('.', DS, $controller) . $depr . $template; + $template = str_replace('.', DIRECTORY_SEPARATOR, $controller) . $depr . $template; } } } else { $template = str_replace(['/', ':'], $depr, substr($template, 1)); } + return $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.'); } + protected function getActionTemplate($request) + { + $rule = [$request->action(true), Loader::parseName($request->action(true)), $request->action()]; + $type = $this->config['auto_rule']; + + return isset($rule[$type]) ? $rule[$type] : $rule[0]; + } + /** * 配置或者获取模板引擎参数 * @access private - * @param string|array $name 参数名 - * @param mixed $value 参数值 + * @param string|array $name 参数名 + * @param mixed $value 参数值 * @return mixed */ public function config($name, $value = null) @@ -162,4 +184,9 @@ class Think { return call_user_func_array([$this->template, $method], $params); } + + public function __debugInfo() + { + return ['config' => $this->config]; + } } diff --git a/thinkphp/library/traits/controller/Jump.php b/thinkphp/library/traits/controller/Jump.php index 6e6f2dec1..41f7e930a 100644 --- a/thinkphp/library/traits/controller/Jump.php +++ b/thinkphp/library/traits/controller/Jump.php @@ -2,7 +2,6 @@ /** * 用法: - * load_trait('controller/Jump'); * class index * { * use \traits\controller\Jump; @@ -14,40 +13,39 @@ */ namespace traits\controller; -use think\Config; +use think\Container; use think\exception\HttpResponseException; -use think\Request; use think\Response; use think\response\Redirect; -use think\Url; -use think\View as ViewTemplate; trait Jump { + /** + * 应用实例 + * @var \think\App + */ + protected $app; + /** * 操作成功跳转的快捷方法 * @access protected - * @param mixed $msg 提示信息 - * @param string $url 跳转的URL地址 - * @param mixed $data 返回的数据 - * @param integer $wait 跳转等待时间 - * @param array $header 发送的Header信息 + * @param mixed $msg 提示信息 + * @param string $url 跳转的URL地址 + * @param mixed $data 返回的数据 + * @param integer $wait 跳转等待时间 + * @param array $header 发送的Header信息 * @return void */ protected function success($msg = '', $url = null, $data = '', $wait = 3, array $header = []) { - $code = 1; - if (is_numeric($msg)) { - $code = $msg; - $msg = ''; - } if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) { $url = $_SERVER["HTTP_REFERER"]; } elseif ('' !== $url) { - $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); + $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Container::get('url')->build($url); } + $result = [ - 'code' => $code, + 'code' => 1, 'msg' => $msg, 'data' => $data, 'url' => $url, @@ -55,61 +53,60 @@ trait Jump ]; $type = $this->getResponseType(); + // 把跳转模板的渲染下沉,这样在 response_send 行为里通过getData()获得的数据是一致性的格式 if ('html' == strtolower($type)) { - $result = ViewTemplate::instance(Config::get('template'), Config::get('view_replace_str')) - ->fetch(Config::get('dispatch_success_tmpl'), $result); + $type = 'jump'; } - $response = Response::create($result, $type)->header($header); + + $response = Response::create($result, $type)->header($header)->options(['jump_template' => $this->app['config']->get('dispatch_success_tmpl')]); + throw new HttpResponseException($response); } /** * 操作错误跳转的快捷方法 * @access protected - * @param mixed $msg 提示信息 - * @param string $url 跳转的URL地址 - * @param mixed $data 返回的数据 - * @param integer $wait 跳转等待时间 - * @param array $header 发送的Header信息 + * @param mixed $msg 提示信息 + * @param string $url 跳转的URL地址 + * @param mixed $data 返回的数据 + * @param integer $wait 跳转等待时间 + * @param array $header 发送的Header信息 * @return void */ protected function error($msg = '', $url = null, $data = '', $wait = 3, array $header = []) { - $code = 0; - if (is_numeric($msg)) { - $code = $msg; - $msg = ''; - } + $type = $this->getResponseType(); if (is_null($url)) { - $url = Request::instance()->isAjax() ? '' : 'javascript:history.back(-1);'; + $url = $this->app['request']->isAjax() ? '' : 'javascript:history.back(-1);'; } elseif ('' !== $url) { - $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); + $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : $this->app['url']->build($url); } + $result = [ - 'code' => $code, + 'code' => 0, 'msg' => $msg, 'data' => $data, 'url' => $url, 'wait' => $wait, ]; - $type = $this->getResponseType(); if ('html' == strtolower($type)) { - $result = ViewTemplate::instance(Config::get('template'), Config::get('view_replace_str')) - ->fetch(Config::get('dispatch_error_tmpl'), $result); + $type = 'jump'; } - $response = Response::create($result, $type)->header($header); + + $response = Response::create($result, $type)->header($header)->options(['jump_template' => $this->app['config']->get('dispatch_error_tmpl')]); + throw new HttpResponseException($response); } /** * 返回封装后的API数据到客户端 * @access protected - * @param mixed $data 要返回的数据 - * @param integer $code 返回的code - * @param mixed $msg 提示信息 - * @param string $type 返回数据格式 - * @param array $header 发送的Header信息 + * @param mixed $data 要返回的数据 + * @param integer $code 返回的code + * @param mixed $msg 提示信息 + * @param string $type 返回数据格式 + * @param array $header 发送的Header信息 * @return void */ protected function result($data, $code = 0, $msg = '', $type = '', array $header = []) @@ -117,31 +114,36 @@ trait Jump $result = [ 'code' => $code, 'msg' => $msg, - 'time' => $_SERVER['REQUEST_TIME'], + 'time' => time(), 'data' => $data, ]; + $type = $type ?: $this->getResponseType(); $response = Response::create($result, $type)->header($header); + throw new HttpResponseException($response); } /** * URL重定向 * @access protected - * @param string $url 跳转的URL表达式 - * @param array|integer $params 其它URL参数 - * @param integer $code http code - * @param array $with 隐式传参 + * @param string $url 跳转的URL表达式 + * @param array|integer $params 其它URL参数 + * @param integer $code http code + * @param array $with 隐式传参 * @return void */ protected function redirect($url, $params = [], $code = 302, $with = []) { $response = new Redirect($url); + if (is_integer($params)) { $code = $params; $params = []; } + $response->code($code)->params($params)->with($with); + throw new HttpResponseException($response); } @@ -152,7 +154,15 @@ trait Jump */ protected function getResponseType() { - $isAjax = Request::instance()->isAjax(); - return $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type'); + if (!$this->app) { + $this->app = Container::get('app'); + } + + $isAjax = $this->app['request']->isAjax(); + $config = $this->app['config']; + + return $isAjax + ? $config->get('default_ajax_return') + : $config->get('default_return_type'); } } diff --git a/thinkphp/library/traits/model/SoftDelete.php b/thinkphp/library/traits/model/SoftDelete.php deleted file mode 100644 index be5863429..000000000 --- a/thinkphp/library/traits/model/SoftDelete.php +++ /dev/null @@ -1,155 +0,0 @@ -getDeleteTimeField(); - if (!empty($this->data[$field])) { - return true; - } - return false; - } - - /** - * 查询软删除数据 - * @access public - * @return Query - */ - public static function withTrashed() - { - $model = new static(); - $field = $model->getDeleteTimeField(true); - return $model->getQuery(); - } - - /** - * 只查询软删除数据 - * @access public - * @return Query - */ - public static function onlyTrashed() - { - $model = new static(); - $field = $model->getDeleteTimeField(true); - return $model->getQuery() - ->useSoftDelete($field, ['not null', '']); - } - - /** - * 删除当前的记录 - * @access public - * @param bool $force 是否强制删除 - * @return integer - */ - public function delete($force = false) - { - if (false === $this->trigger('before_delete', $this)) { - return false; - } - $name = $this->getDeleteTimeField(); - if (!$force) { - // 软删除 - $this->data[$name] = $this->autoWriteTimestamp($name); - $result = $this->isUpdate()->save(); - } else { - $result = $this->getQuery()->delete($this->data); - } - - $this->trigger('after_delete', $this); - return $result; - } - - /** - * 删除记录 - * @access public - * @param mixed $data 主键列表 支持闭包查询条件 - * @param bool $force 是否强制删除 - * @return integer 成功删除的记录数 - */ - public static function destroy($data, $force = false) - { - // 包含软删除数据 - $query = self::withTrashed(); - if (is_array($data) && key($data) !== 0) { - $query->where($data); - $data = null; - } elseif ($data instanceof \Closure) { - call_user_func_array($data, [ & $query]); - $data = null; - } elseif (is_null($data)) { - return 0; - } - - $resultSet = $query->select($data); - $count = 0; - if ($resultSet) { - foreach ($resultSet as $data) { - $result = $data->delete($force); - $count += $result; - } - } - return $count; - } - - /** - * 恢复被软删除的记录 - * @access public - * @param array $where 更新条件 - * @return integer - */ - public function restore($where = []) - { - $name = $this->getDeleteTimeField(); - if (empty($where)) { - $pk = $this->getPk(); - $where[$pk] = $this->getData($pk); - } - // 恢复删除 - return $this->getQuery() - ->useSoftDelete($name, ['not null', '']) - ->where($where) - ->update([$name => null]); - } - - /** - * 查询默认不包含软删除数据 - * @access protected - * @param Query $query 查询对象 - * @return void - */ - protected function base($query) - { - $field = $this->getDeleteTimeField(true); - $query->useSoftDelete($field); - } - - /** - * 获取软删除字段 - * @access public - * @param bool $read 是否查询操作 写操作的时候会自动去掉表别名 - * @return string - */ - protected function getDeleteTimeField($read = false) - { - $field = property_exists($this, 'deleteTime') && isset($this->deleteTime) ? $this->deleteTime : 'delete_time'; - if (!strpos($field, '.')) { - $field = '__TABLE__.' . $field; - } - if (!$read && strpos($field, '.')) { - $array = explode('.', $field); - $field = array_pop($array); - } - return $field; - } -} diff --git a/thinkphp/library/traits/think/Instance.php b/thinkphp/library/traits/think/Instance.php deleted file mode 100644 index ba45ddd83..000000000 --- a/thinkphp/library/traits/think/Instance.php +++ /dev/null @@ -1,45 +0,0 @@ - -// +---------------------------------------------------------------------- - -namespace traits\think; - -use think\Exception; - -trait Instance -{ - protected static $instance = null; - - /** - * @param array $options - * @return static - */ - public static function instance($options = []) - { - if (is_null(self::$instance)) { - self::$instance = new self($options); - } - return self::$instance; - } - - // 静态调用 - public static function __callStatic($method, $params) - { - if (is_null(self::$instance)) { - self::$instance = new self(); - } - $call = substr($method, 1); - if (0 === strpos($method, '_') && is_callable([self::$instance, $call])) { - return call_user_func_array([self::$instance, $call], $params); - } else { - throw new Exception("method not exists:" . $method); - } - } -} diff --git a/thinkphp/phpunit.xml b/thinkphp/phpunit.xml deleted file mode 100644 index 7c6ef03ca..000000000 --- a/thinkphp/phpunit.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - ./tests/thinkphp/ - - - - - - - - ./ - - tests - vendor - - - - - - - - - - diff --git a/thinkphp/phpunit.xml.dist b/thinkphp/phpunit.xml.dist new file mode 100644 index 000000000..37c3d2b53 --- /dev/null +++ b/thinkphp/phpunit.xml.dist @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + ./library/think/*/tests/ + + + + + + ./library/ + + ./library/think/*/tests + ./library/think/*/assets + ./library/think/*/resources + ./library/think/*/vendor + + + + \ No newline at end of file diff --git a/thinkphp/tpl/default_index.tpl b/thinkphp/tpl/default_index.tpl index 8538b4dfa..e5c1363ac 100644 --- a/thinkphp/tpl/default_index.tpl +++ b/thinkphp/tpl/default_index.tpl @@ -5,6 +5,6 @@ class Index{$suffix} { public function index() { - return '

                  :)

                  ThinkPHP V5
                  十年磨一剑 - 为API开发设计的高性能框架

                  [ V5.0 版本由 七牛云 独家赞助发布 ]
                  '; + return '

                  :)

                  ThinkPHP V5.1
                  12载初心不改(2006-2018) - 你值得信赖的PHP框架

                  '; } } diff --git a/thinkphp/tpl/dispatch_jump.tpl b/thinkphp/tpl/dispatch_jump.tpl index 18ee01bd5..583376bbb 100644 --- a/thinkphp/tpl/dispatch_jump.tpl +++ b/thinkphp/tpl/dispatch_jump.tpl @@ -2,6 +2,7 @@ + 跳转提示