permissions = adminService.myPermissionUrls(adminId);
+ info.setStringPermissions(permissions);
+ return info;
+ }
+
+ /**
+ * 清空当前用户权限信息
+ */
+ public void clearCachedAuthorizationInfo() {
+ PrincipalCollection principalCollection = SecurityUtils.getSubject().getPrincipals();
+ SimplePrincipalCollection principals = new SimplePrincipalCollection(principalCollection, getName());
+ super.clearCachedAuthorizationInfo(principals);
+ }
+
+ /**
+ * 指定principalCollection 清除
+ */
+ @Override
+ public void clearCachedAuthorizationInfo(PrincipalCollection principalCollection) {
+ SimplePrincipalCollection principals = new SimplePrincipalCollection(principalCollection, getName());
+ super.clearCachedAuthorizationInfo(principals);
+ }
+}
diff --git a/src/main/java/ga/clive/demo/shiro/shiro/TokenManager.java b/src/main/java/ga/clive/demo/shiro/shiro/TokenManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f9ed9a31dd804567259274baafef04c4b08c102
--- /dev/null
+++ b/src/main/java/ga/clive/demo/shiro/shiro/TokenManager.java
@@ -0,0 +1,87 @@
+package ga.clive.demo.shiro.shiro;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.session.Session;
+
+import ga.clive.demo.shiro.pojo.AdminVo;
+
+/**
+ *
+ * Title: token管理
+ * Description: RT
+ *
+ *
+ * @author clive
+ * @version 1.0
+ * @date 2017-10-26 11:53:27
+ *
+ */
+public class TokenManager {
+ /**
+ * 获取当前登录的用户User对象
+ *
+ * @return
+ */
+ public static AdminVo getToken() {
+ AdminVo token = (AdminVo) SecurityUtils.getSubject().getPrincipal();
+ return token;
+ }
+
+ /**
+ * 获取当前用户的Session
+ *
+ * @return
+ */
+ public static Session getSession() {
+ return SecurityUtils.getSubject().getSession();
+ }
+
+ /**
+ * 获取当前管理员ID
+ *
+ * @return
+ */
+ public static String getAdminId() {
+ return getToken() == null ? null : getToken().getId();
+ }
+
+ /**
+ * 把值放入到当前登录用户的Session里
+ *
+ * @param key
+ * @param value
+ */
+ public static void setVal2Session(Object key, Object value) {
+ getSession().setAttribute(key, value);
+ }
+
+ /**
+ * 登录
+ *
+ * @param user
+ * @param rememberMe
+ * @return
+ */
+ public static AdminVo login(AdminVo user) {
+ UsernamePasswordToken token = new UsernamePasswordToken(user.getAccount(), user.getPassword().toCharArray());
+ SecurityUtils.getSubject().login(token);
+ return getToken();
+ }
+
+ /**
+ * 判断是否登录
+ *
+ * @return
+ */
+ public static boolean isLogin() {
+ return null != SecurityUtils.getSubject().getPrincipal();
+ }
+
+ /**
+ * 退出登录
+ */
+ public static void logout() {
+ SecurityUtils.getSubject().logout();
+ }
+}
diff --git a/src/main/java/ga/clive/demo/shiro/shiro/filter/LoginFilter.java b/src/main/java/ga/clive/demo/shiro/shiro/filter/LoginFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f654d26aa5daa60c6f25e6d2b7ab43a6520e49d
--- /dev/null
+++ b/src/main/java/ga/clive/demo/shiro/shiro/filter/LoginFilter.java
@@ -0,0 +1,58 @@
+package ga.clive.demo.shiro.shiro.filter;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.shiro.web.filter.AccessControlFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import ga.clive.demo.shiro.dto.TransDto;
+import ga.clive.demo.shiro.pojo.AdminVo;
+import ga.clive.demo.shiro.shiro.TokenManager;
+
+/**
+ *
+ * Title: 登录过滤器
+ * Description: 拦截资源要求为authc的请求
+ *
+ *
+ * @author clive
+ * @version 1.0
+ * @date 2017-10-26 11:54:44
+ *
+ */
+@Component
+public class LoginFilter extends AccessControlFilter {
+
+ private final static Logger logger = LoggerFactory.getLogger(LoginFilter.class);
+
+ @Override
+ protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
+ throws Exception {
+
+ AdminVo token = TokenManager.getToken();
+
+ if (null != token || isLoginRequest(request, response)) {
+ return Boolean.TRUE;
+ }
+ if (ShiroFilterUtils.isAjax(request)) {// ajax请求
+ logger.info("当前用户没有登录,并且是Ajax请求!");
+ TransDto dto = TransDto.getInstance();
+ dto.setMessage("当前用户没有登录");
+ ShiroFilterUtils.out(response, dto);
+ } else {
+ // 保存Request和Response 到登录后的链接
+ saveRequestAndRedirectToLogin(request, response);
+ }
+ return Boolean.FALSE;
+
+ }
+
+ @Override
+ protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+ return Boolean.FALSE;
+ }
+
+}
diff --git a/src/main/java/ga/clive/demo/shiro/shiro/filter/PermissionFilter.java b/src/main/java/ga/clive/demo/shiro/shiro/filter/PermissionFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..e24747246175a2594e9ac4c0fb95d8e4ee14736e
--- /dev/null
+++ b/src/main/java/ga/clive/demo/shiro/shiro/filter/PermissionFilter.java
@@ -0,0 +1,68 @@
+package ga.clive.demo.shiro.shiro.filter;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.web.filter.AccessControlFilter;
+import org.apache.shiro.web.util.WebUtils;
+import org.springframework.stereotype.Component;
+
+import ga.clive.demo.shiro.dto.TransDto;
+
+/**
+ *
+ * Title: 权限过滤器
+ * Description: 拦截权限permission的请求
+ *
+ *
+ * @author clive
+ * @version 1.0
+ * @date 2017-10-26 11:55:24
+ *
+ */
+@Component
+public class PermissionFilter extends AccessControlFilter {
+
+ @Override
+ protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
+ throws Exception {
+
+ // 先判断带参数的权限判断
+ Subject subject = getSubject(request, response);
+ if (null != mappedValue) {
+ String[] arra = (String[]) mappedValue;
+ for (String permission : arra) {
+ if (subject.isPermitted(permission)) {
+ return Boolean.TRUE;
+ }
+ }
+ }
+ //移除上下文后校验
+ String uri = ShiroFilterUtils.removeContextPath(request);
+ if (subject.isPermitted(uri)) {
+ return Boolean.TRUE;
+ }
+
+ //对restful接口的兼容
+ uri = ShiroFilterUtils.compatibleRestful(request);
+ if (subject.isPermitted(uri)) {
+ return Boolean.TRUE;
+ }
+
+ if (ShiroFilterUtils.isAjax(request)) {
+ TransDto dto = TransDto.getInstance();
+ dto.setMessage("抱歉,您没有权限操作此项内容");
+ ShiroFilterUtils.out(response, dto);
+ } else {
+ WebUtils.issueRedirect(request, response, ShiroFilterUtils.UNAUTHORIZED);
+ }
+ return Boolean.FALSE;
+ }
+
+ @Override
+ protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+ return Boolean.FALSE;
+ }
+
+}
diff --git a/src/main/java/ga/clive/demo/shiro/shiro/filter/ShiroFilterUtils.java b/src/main/java/ga/clive/demo/shiro/shiro/filter/ShiroFilterUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..b338089c9d1152e581159a3aa8777533ae515e53
--- /dev/null
+++ b/src/main/java/ga/clive/demo/shiro/shiro/filter/ShiroFilterUtils.java
@@ -0,0 +1,93 @@
+package ga.clive.demo.shiro.shiro.filter;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.fastjson.JSON;
+
+import ga.clive.demo.shiro.dto.TransDto;
+
+public class ShiroFilterUtils {
+
+ private final static Logger logger = LoggerFactory.getLogger(LoginFilter.class);
+
+ /**
+ * 没有权限页面地址
+ */
+ final static String UNAUTHORIZED = "/admin/guest/unauthorized.shtml";
+
+ /**
+ * 是否是Ajax请求
+ *
+ * @param request
+ * @return
+ */
+ public static boolean isAjax(ServletRequest request) {
+ return "XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"));
+ }
+
+ /**
+ * response 输出JSON
+ *
+ * @param hresponse
+ * @param resultMap
+ * @throws IOException
+ */
+ public static void out(ServletResponse response, TransDto transDto) {
+
+ PrintWriter out = null;
+ try {
+ response.setCharacterEncoding("UTF-8");
+ response.setContentType("application/json;charset=UTF-8");
+ out = response.getWriter();
+ out.println(JSON.toJSONString(transDto));
+ } catch (Exception e) {
+ logger.error("输出JSON报错", e);
+ } finally {
+ if (null != out) {
+ out.flush();
+ out.close();
+ }
+ }
+ }
+
+ /**
+ * 移除地址中的上下文
+ *
+ * @param request
+ * @return
+ */
+ public static String removeContextPath(ServletRequest request) {
+ HttpServletRequest httpRequest = ((HttpServletRequest) request);
+
+ String uri = httpRequest.getRequestURI();// 获取URI
+ String basePath = httpRequest.getContextPath();// 获取contenxtPath
+ if (null != uri && uri.startsWith(basePath)) {
+ uri = uri.replaceFirst(basePath, "");
+ }
+ return uri;
+ }
+
+ /**
+ * 兼容restful
+ *
+ * @param request
+ * @return
+ */
+ public static String compatibleRestful(ServletRequest request) {
+ String uri = ShiroFilterUtils.removeContextPath(request);
+ int lastIndexOf = uri.lastIndexOf("/");
+ if (lastIndexOf > 0) {
+ uri = uri.substring(0, lastIndexOf + 1);
+ }
+ return uri;
+ }
+
+}
diff --git a/src/main/java/ga/clive/demo/shiro/util/CaptchaUtils.java b/src/main/java/ga/clive/demo/shiro/util/CaptchaUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..986370855efcf46a8717509522cc70f4b36c46f7
--- /dev/null
+++ b/src/main/java/ga/clive/demo/shiro/util/CaptchaUtils.java
@@ -0,0 +1,49 @@
+package ga.clive.demo.shiro.util;
+
+import javax.servlet.http.HttpSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Assert;
+
+import com.alibaba.fastjson.JSONObject;
+
+import ga.clive.demo.shiro.constants.Constants;
+import ga.clive.demo.shiro.dto.Captcha;
+import ga.clive.demo.shiro.exception.ServiceException;
+
+
+/**
+ * 验证码工具类
+ *
+ * @author clive
+ * @date 2017-07-13 9:20:33 PM
+ */
+public class CaptchaUtils {
+
+ private final static Logger logger = LoggerFactory.getLogger(CaptchaUtils.class);
+
+
+ /**
+ * 验证验证码
+ */
+ public static void validateCaptcha(Captcha captcha, HttpSession session) throws ServiceException {
+ logger.info("####### 验证验证码: {}", captcha);
+ try {
+ Assert.notNull(captcha, "验证码错误[0]");
+ Assert.isTrue(StrUtils.isNotBlank(captcha.getCaptcha()), "请输入验证码");
+ String vcodeJsonStr = (String)session.getAttribute(Constants.CAPTCHA_SESSION_NAME);
+ Assert.isTrue(StrUtils.isNotBlank(vcodeJsonStr), "验证码错误[1]");
+ Captcha sessionCaptcha = JSONObject.parseObject(vcodeJsonStr, Captcha.class);
+ Assert.notNull(sessionCaptcha, "验证码错误[2]");
+ logger.debug("### 输入的验证码: {}, 生成的验证码: {}", captcha, sessionCaptcha);
+ Assert.isTrue(captcha.equals(sessionCaptcha), "验证码错误");
+ } catch (IllegalArgumentException e) {
+ throw new ServiceException(e.getMessage());
+ } catch (Exception e) {
+ logger.error("validateCaptcha", e);
+ throw new IllegalArgumentException("验证码错误[!]");
+ }
+ }
+
+}
diff --git a/src/main/java/ga/clive/demo/shiro/util/MD5Utils.java b/src/main/java/ga/clive/demo/shiro/util/MD5Utils.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2381ddbc59b5b0118b15517a29214b36ce249da
--- /dev/null
+++ b/src/main/java/ga/clive/demo/shiro/util/MD5Utils.java
@@ -0,0 +1,42 @@
+package ga.clive.demo.shiro.util;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+
+import ga.clive.demo.shiro.constants.Constants;
+
+/**
+ *
+ * Title: md5工具
+ * Description:
+ *
+ *
+ * @author clive
+ * @version 1.0
+ * @date 2017-10-26 11:57:16
+ *
+ */
+public class MD5Utils {
+
+
+ /**
+ * 对字符串md5加密
+ *
+ * @param str
+ * @return
+ */
+ public static String getMD5(String str) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ md.update(str.getBytes());
+ return new BigInteger(1, md.digest()).toString(16);
+ } catch (Exception e) {
+ return StrUtils.EMPTY;
+ }
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(MD5Utils.getMD5("admin"+Constants.PWD_SALT));
+ }
+}
diff --git a/src/main/java/ga/clive/demo/shiro/util/StrUtils.java b/src/main/java/ga/clive/demo/shiro/util/StrUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..25d18f96a6dc521a5036c287236216a5cf9f667d
--- /dev/null
+++ b/src/main/java/ga/clive/demo/shiro/util/StrUtils.java
@@ -0,0 +1,102 @@
+package ga.clive.demo.shiro.util;
+
+import java.util.Random;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * 字符串工具 继承StringUtils工具类 并做了扩展
+ *
+ * @author clive
+ * @date 2017-10-24 9:23:46 PM
+ */
+public class StrUtils extends StringUtils {
+
+ private StrUtils() {
+ }
+
+ /**
+ * unicode 转字符串
+ */
+ public static String unicode2String(String unicode) {
+
+ StringBuffer string = new StringBuffer();
+
+ String[] hex = unicode.split("\\\\u");
+
+ for (String str : hex) {
+ string.append(str);
+ }
+
+ return string.toString();
+ }
+
+ /**
+ * 正则表达式匹配
+ *
+ * @param text
+ * 待匹配的文本
+ * @param reg
+ * 正则表达式
+ * @return
+ */
+ private final static boolean match(String text, String reg) {
+ if (StringUtils.isBlank(text) || StringUtils.isBlank(reg))
+ return false;
+ return Pattern.compile(reg).matcher(text).matches();
+ }
+
+ /**
+ * 匹配手机号
+ *
+ * @param text
+ * @return
+ */
+ public final static boolean isPhone(String text) {
+ return match(text, "^(\\d{3,4}-?)?\\d{7,9}$");
+ }
+
+ /**
+ * 匹配Email地址
+ *
+ * @param str
+ * @return
+ */
+ public final static boolean isEmail(String str) {
+ return match(str, "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$");
+ }
+
+ /**
+ * 获取文件扩展名
+ *
+ * @param fileName
+ * @return
+ */
+ public static String getExtension(String fileName) {
+ try {
+ if (StrUtils.isBlank(fileName))
+ return "";
+ return fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length());
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ /**
+ * 随机生成{length}位字母
+ *
+ * @param length
+ * @return
+ */
+ public static String createLetterCode(int length) {
+ StringBuffer charValue = new StringBuffer();
+ for (int i = 0; i < length; i++) {
+ Random random = new Random();
+ int r = random.nextInt(26);
+ char c = (char) (r + 'a');
+ charValue.append(String.valueOf(c));
+ }
+ return charValue.toString();
+ }
+}
diff --git a/src/main/java/ga/clive/demo/shiro/util/UUIDGenerator.java b/src/main/java/ga/clive/demo/shiro/util/UUIDGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6d5f70afe3671ac40e91751ec8a87da0cb3d817
--- /dev/null
+++ b/src/main/java/ga/clive/demo/shiro/util/UUIDGenerator.java
@@ -0,0 +1,51 @@
+package ga.clive.demo.shiro.util;
+
+import java.util.UUID;
+
+/**
+ * 唯一id生成工具
+ *
+ * @author clive
+ * @date 2017-10-24 9:24:17 PM
+ */
+public class UUIDGenerator {
+
+ private UUIDGenerator() {
+ }
+
+ /**
+ * 获得一个UUID
+ *
+ * @return String UUID (32位)
+ */
+ public static String getUUID() {
+ String s = UUID.randomUUID().toString();
+ // 去掉“-”符号
+ return s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24);
+ }
+
+ /**
+ * 获得指定数目的UUID
+ *
+ * @param number
+ * int 需要获得的UUID数量
+ * @return String[] UUID数组
+ */
+ public static String[] getUUID(int number) {
+ if (number < 1) {
+ return null;
+ }
+ String[] ss = new String[number];
+ for (int i = 0; i < number; i++) {
+ ss[i] = getUUID();
+ }
+ return ss;
+ }
+
+ public static void main(String[] args) {
+ String[] ss = getUUID(10);
+ for (int i = 0; i < ss.length; i++) {
+ System.out.println(ss[i]);
+ }
+ }
+}
diff --git a/src/main/java/ga/clive/demo/shiro/util/VerifyCodeUtils.java b/src/main/java/ga/clive/demo/shiro/util/VerifyCodeUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..78202139fc78932b9464907ff94a305d3511ad5b
--- /dev/null
+++ b/src/main/java/ga/clive/demo/shiro/util/VerifyCodeUtils.java
@@ -0,0 +1,268 @@
+package ga.clive.demo.shiro.util;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+
+import javax.imageio.ImageIO;
+
+/**
+ * 验证码工具
+ *
+ * @author clive
+ * @date 2017年10月17日 下午8:47:20
+ */
+public class VerifyCodeUtils {
+
+ // 使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
+ public static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
+ private static Random random = new Random();
+
+ /**
+ * 使用系统默认字符源生成验证码
+ *
+ * @param verifySize
+ * 验证码长度
+ * @return
+ */
+ public static String generateVerifyCode(int verifySize) {
+ return generateVerifyCode(verifySize, VERIFY_CODES);
+ }
+
+ /**
+ * 使用指定源生成验证码
+ *
+ * @param verifySize
+ * 验证码长度
+ * @param sources
+ * 验证码字符源
+ * @return
+ */
+ public static String generateVerifyCode(int verifySize, String sources) {
+ if (sources == null || sources.length() == 0) {
+ sources = VERIFY_CODES;
+ }
+ int codesLen = sources.length();
+ Random rand = new Random(System.currentTimeMillis());
+ StringBuilder verifyCode = new StringBuilder(verifySize);
+ for (int i = 0; i < verifySize; i++) {
+ verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
+ }
+ return verifyCode.toString();
+ }
+
+ /**
+ * 生成随机验证码文件,并返回验证码值
+ *
+ * @param w
+ * @param h
+ * @param outputFile
+ * @param verifySize
+ * @return
+ * @throws IOException
+ */
+ public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException {
+ String verifyCode = generateVerifyCode(verifySize);
+ outputImage(w, h, outputFile, verifyCode);
+ return verifyCode;
+ }
+
+ /**
+ * 输出随机验证码图片流,并返回验证码值
+ *
+ * @param w
+ * @param h
+ * @param os
+ * @param verifySize
+ * @return
+ * @throws IOException
+ */
+ public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException {
+ String verifyCode = generateVerifyCode(verifySize);
+ outputImage(w, h, os, verifyCode);
+ return verifyCode;
+ }
+
+ /**
+ * 生成指定验证码图像文件
+ *
+ * @param w
+ * @param h
+ * @param outputFile
+ * @param code
+ * @throws IOException
+ */
+ public static void outputImage(int w, int h, File outputFile, String code) throws IOException {
+ if (outputFile == null) {
+ return;
+ }
+ File dir = outputFile.getParentFile();
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+ try {
+ outputFile.createNewFile();
+ FileOutputStream fos = new FileOutputStream(outputFile);
+ outputImage(w, h, fos, code);
+ fos.close();
+ } catch (IOException e) {
+ throw e;
+ }
+ }
+
+ /**
+ * 输出指定验证码图片流
+ *
+ * @param w
+ * @param h
+ * @param os
+ * @param code
+ * @throws IOException
+ */
+ public static void outputImage(int w, int h, OutputStream os, String code) throws IOException {
+ int verifySize = code.length();
+ BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
+ Random rand = new Random();
+ Graphics2D g2 = image.createGraphics();
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ Color[] colors = new Color[5];
+ Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA,
+ Color.ORANGE, Color.PINK, Color.YELLOW };
+ float[] fractions = new float[colors.length];
+ for (int i = 0; i < colors.length; i++) {
+ colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
+ fractions[i] = rand.nextFloat();
+ }
+ Arrays.sort(fractions);
+
+ g2.setColor(Color.GRAY);// 设置边框色
+ g2.fillRect(0, 0, w, h);
+
+ Color c = getRandColor(200, 250);
+ g2.setColor(c);// 设置背景色
+ g2.fillRect(0, 2, w, h - 4);
+
+ // 绘制干扰线
+ Random random = new Random();
+ g2.setColor(getRandColor(160, 200));// 设置线条的颜色
+ for (int i = 0; i < 20; i++) {
+ int x = random.nextInt(w - 1);
+ int y = random.nextInt(h - 1);
+ int xl = random.nextInt(6) + 1;
+ int yl = random.nextInt(12) + 1;
+ g2.drawLine(x, y, x + xl + 40, y + yl + 20);
+ }
+
+ // 添加噪点
+ float yawpRate = 0.05f;// 噪声率
+ int area = (int) (yawpRate * w * h);
+ for (int i = 0; i < area; i++) {
+ int x = random.nextInt(w);
+ int y = random.nextInt(h);
+ int rgb = getRandomIntColor();
+ image.setRGB(x, y, rgb);
+ }
+
+ shear(g2, w, h, c);// 使图片扭曲
+
+ g2.setColor(getRandColor(100, 160));
+ int fontSize = h - 4;
+ Font font = new Font("Algerian", Font.ITALIC, fontSize);
+ g2.setFont(font);
+ char[] chars = code.toCharArray();
+ for (int i = 0; i < verifySize; i++) {
+ AffineTransform affine = new AffineTransform();
+ affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1),
+ (w / verifySize) * i + fontSize / 2, h / 2);
+ g2.setTransform(affine);
+ g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);
+ }
+
+ g2.dispose();
+ ImageIO.write(image, "jpg", os);
+ }
+
+ private static Color getRandColor(int fc, int bc) {
+ if (fc > 255)
+ fc = 255;
+ if (bc > 255)
+ bc = 255;
+ int r = fc + random.nextInt(bc - fc);
+ int g = fc + random.nextInt(bc - fc);
+ int b = fc + random.nextInt(bc - fc);
+ return new Color(r, g, b);
+ }
+
+ private static int getRandomIntColor() {
+ int[] rgb = getRandomRgb();
+ int color = 0;
+ for (int c : rgb) {
+ color = color << 8;
+ color = color | c;
+ }
+ return color;
+ }
+
+ private static int[] getRandomRgb() {
+ int[] rgb = new int[3];
+ for (int i = 0; i < 3; i++) {
+ rgb[i] = random.nextInt(255);
+ }
+ return rgb;
+ }
+
+ private static void shear(Graphics g, int w1, int h1, Color color) {
+ shearX(g, w1, h1, color);
+ shearY(g, w1, h1, color);
+ }
+
+ private static void shearX(Graphics g, int w1, int h1, Color color) {
+
+ int period = random.nextInt(2);
+
+ boolean borderGap = true;
+ int frames = 1;
+ int phase = random.nextInt(2);
+
+ for (int i = 0; i < h1; i++) {
+ double d = (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * phase) / frames);
+ g.copyArea(0, i, w1, 1, (int) d, 0);
+ if (borderGap) {
+ g.setColor(color);
+ g.drawLine((int) d, i, 0, i);
+ g.drawLine((int) d + w1, i, w1, i);
+ }
+ }
+
+ }
+
+ private static void shearY(Graphics g, int w1, int h1, Color color) {
+
+ int period = random.nextInt(40) + 10; // 50;
+
+ boolean borderGap = true;
+ int frames = 20;
+ int phase = 7;
+ for (int i = 0; i < w1; i++) {
+ double d = (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * phase) / frames);
+ g.copyArea(i, 0, 1, h1, 0, (int) d);
+ if (borderGap) {
+ g.setColor(color);
+ g.drawLine(i, (int) d, i, 0);
+ g.drawLine(i, (int) d + h1, i, h1);
+ }
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/config/applicationContext.xml b/src/main/resources/config/applicationContext.xml
new file mode 100644
index 0000000000000000000000000000000000000000..34c80a12ebace73e21f8984c09070aaf46140350
--- /dev/null
+++ b/src/main/resources/config/applicationContext.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+ classpath:config/shiro-demo.properties
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/config/mybatis/mybatis-cfg.xml b/src/main/resources/config/mybatis/mybatis-cfg.xml
new file mode 100644
index 0000000000000000000000000000000000000000..52126548cf3d30ed5efbf8bd7fc7371ac1c34090
--- /dev/null
+++ b/src/main/resources/config/mybatis/mybatis-cfg.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/config/mybatis/mybatis-spring.xml b/src/main/resources/config/mybatis/mybatis-spring.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b3a37e07584899e21f92193042939a4287a776e3
--- /dev/null
+++ b/src/main/resources/config/mybatis/mybatis-spring.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/config/shiro-demo.properties b/src/main/resources/config/shiro-demo.properties
new file mode 100644
index 0000000000000000000000000000000000000000..ea3d7760e7f2d50a5dad95bc6f423c4b911aa1b7
--- /dev/null
+++ b/src/main/resources/config/shiro-demo.properties
@@ -0,0 +1,10 @@
+driver=com.mysql.jdbc.Driver
+url=jdbc:mysql://127.0.0.1:3306/shiro_demo?useUnicode=true&characterEncoding=utf8
+username=root
+password=123456
+
+#c3p0
+idleConnectionTestPeriod = 60
+preferredTestQuery = select 1
+testConnectionOnCheckin = true
+testConnectionOnCheckout = true
diff --git a/src/main/resources/config/shiro/spring-shiro.xml b/src/main/resources/config/shiro/spring-shiro.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dfafadbc1f80cd3e9033e6d807a0bbb6a89ff6a9
--- /dev/null
+++ b/src/main/resources/config/shiro/spring-shiro.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /static/** = anon
+ / = anon
+ /admin/guest/** = anon
+ /admin/index.html = login
+ /admin/authc/** = login
+ /admin/** = login,permission
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/config/spring-mvc.xml b/src/main/resources/config/spring-mvc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..240ec33d6df957b458b4868f934a1a69cfc14abe
--- /dev/null
+++ b/src/main/resources/config/spring-mvc.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+ text/json;charset=UTF-8
+
+
+
+
+
+
+
+
+
+
+
+
+ /WEB-INF/view/
+
+
+
+ zh_CN
+ auto_detect
+ yyyy-MM-dd HH:mm:ss
+ yyyy-MM-dd
+ HH:mm:ss
+ 0
+ UTF-8
+ 0.##########
+ true
+ ignore
+
+
+
+
+
+
+
+
+
+
+ org.springframework.web.servlet.view.freemarker.FreeMarkerView
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
new file mode 100644
index 0000000000000000000000000000000000000000..f1622b5aca3f201550822b7b0c27b1eee1e59dea
--- /dev/null
+++ b/src/main/resources/log4j.properties
@@ -0,0 +1,19 @@
+# set log levels
+log4j.rootLogger = debug, stdout, log
+
+# output to console
+log4j.appender.stdout = org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target = System.out
+log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern =[%-d{yyyy-MM-dd HH:mm:ss}]-%C{1}(%L)(%r) | %m%n
+
+# set output style
+log4j.appender.log.layout=org.apache.log4j.PatternLayout
+log4j.appender.log.layout.ConversionPattern = [%-d{yyyy-MM-dd HH:mm:ss}]-%C{1}(%L)(%r) | %m%n
+
+# output to file
+log4j.appender.log=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.log.File=/data/logs/shiro-demo/today.log
+log4j.appender.log.DatePattern = '_'yyyy-MM-dd
+log4j.appender.log.Append = true
+
diff --git a/src/main/resources/sqlMapper/AdminMapper.xml b/src/main/resources/sqlMapper/AdminMapper.xml
new file mode 100644
index 0000000000000000000000000000000000000000..508937e1d0fc1b5e1b4903a4c68dd7f502fc6a12
--- /dev/null
+++ b/src/main/resources/sqlMapper/AdminMapper.xml
@@ -0,0 +1,129 @@
+
+
+
+
+ `id`,
+ `account`,
+ `password`,
+ `name`,
+ `created_by`,
+ `created_at`,
+ `updated_by`,
+ `updated_at`,
+ `status`
+
+
+
+ INSERT INTO
+ `t_admin`(`id`, `account`, `password`, `name`, `created_by`,
+ `created_at`, `updated_by`, `updated_at`, `status` )
+ VALUES(#{id}, #{account}, #{password}, #{name},
+ #{createdBy}, NOW(), #{updatedBy}, NOW(), #{status} )
+
+
+
+ DELETE FROM `t_admin` WHERE `id` IN
+
+ #{idItem}
+
+
+
+
+ UPDATE `t_admin`
+
+
+ `account` = #{account},
+
+
+ `password` = #{password},
+
+
+ `name` = #{name},
+
+
+ `updated_by` = #{updatedBy},
+
+
+ `status` = #{status},
+
+ `updated_at` = NOW()
+
+ WHERE `id` = #{id}
+
+
+
+
+
+ AND `account` LIKE "%"#{account}"%"
+
+
+ AND `name` LIKE "%"#{name}"%"
+
+
+ AND `created_by` LIKE "%"#{createdBy}"%"
+
+
+ AND `created_at` = #{createdAt}
+
+
+ AND `updated_by` LIKE "%"#{updatedBy}"%"
+
+
+ AND `updated_at` = #{updatedAt}
+
+
+ AND `status` = #{status}
+
+
+ = #{beginDate}
+ ]]>
+
+
+
+
+
+ ORDER BY `created_at` DESC
+
+
+
+ SELECT
+
+ FROM `t_admin`
+
+
+
+
+ SELECT
+
+ FROM `t_admin`
+ WHERE `id` = #{id}
+
+
+
+ SELECT
+ COUNT(`id`)
+ FROM `t_admin`
+
+
+
+
+ SELECT
+
+ FROM `t_admin`
+ WHERE `account` = #{account}
+ LIMIT 1
+
+
+
+ SELECT
+
+ FROM `t_admin`
+ WHERE `account` = #{account,jdbcType=VARCHAR}
+ AND `password` = #{password,jdbcType=VARCHAR}
+ LIMIT 1
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/sqlMapper/AdminRoleMapper.xml b/src/main/resources/sqlMapper/AdminRoleMapper.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7139a47916b0f734274d77bbf3d012355f91944e
--- /dev/null
+++ b/src/main/resources/sqlMapper/AdminRoleMapper.xml
@@ -0,0 +1,81 @@
+
+
+
+
+ `id`,
+ `admin_id`,
+ `role_id`
+
+
+
+ INSERT INTO
+ `t_admin_role`(`id`, `admin_id`, `role_id` )
+ VALUES(#{id}, #{adminId}, #{roleId} )
+
+
+
+ DELETE FROM `t_admin_role` WHERE `id` IN
+
+ #{idItem}
+
+
+
+
+ DELETE FROM `t_admin_role` WHERE `admin_id` = #{adminId}
+
+
+
+ DELETE FROM `t_admin_role` WHERE `role_id` = #{roleId}
+
+
+
+ UPDATE `t_admin_role`
+
+
+ `admin_id` = #{adminId},
+
+
+ `role_id` = #{roleId},
+
+
+ WHERE `id` = #{id}
+
+
+
+
+
+ AND `admin_id` = #{adminId}
+
+
+ AND `role_id` = #{roleId}
+
+
+
+
+
+ SELECT
+ FROM `t_admin_role`
+
+
+
+
+ SELECT
+ FROM `t_admin_role`
+ WHERE `id` = #{id}
+
+
+
+ SELECT
+ COUNT(`id`)
+ FROM `t_admin_role`
+
+
+
+
+ SELECT DISTINCT `name`FROM `t_role` r
+ LEFT JOIN `t_admin_role` ar
+ ON ar.`role_id` = r.`id`
+ WHERE ar.`admin_id` = #{adminId} AND r.status = 1
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/sqlMapper/PermissionMapper.xml b/src/main/resources/sqlMapper/PermissionMapper.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5111f9cc116abe1c69f8a721e459d43fb862e3d0
--- /dev/null
+++ b/src/main/resources/sqlMapper/PermissionMapper.xml
@@ -0,0 +1,136 @@
+
+
+
+
+ `id`,
+ `url`,
+ `name`,
+ `created_by`,
+ `created_at`,
+ `updated_by`,
+ `updated_at`,
+ `status`
+
+
+
+ INSERT INTO
+ `t_permission`(`id`, `url`, `name`, `created_by`, `created_at`, `updated_by`, `updated_at`, `status` )
+ VALUES(#{id}, #{url}, #{name}, #{createdBy}, NOW(), #{updatedBy}, NOW(), #{status} )
+
+
+
+ DELETE FROM `t_permission` WHERE `id` IN
+
+ #{idItem}
+
+
+
+
+ UPDATE `t_permission`
+
+
+ `url` = #{url},
+
+
+ `name` = #{name},
+
+
+ `updated_by` = #{updatedBy},
+
+
+ `updated_at` = #{updatedAt},
+
+
+ `status` = #{status},
+
+ `updated_at` = NOW()
+
+ WHERE `id` = #{id}
+
+
+
+
+
+ AND `url` LIKE "%"#{url}"%"
+
+
+ AND `name` LIKE "%"#{name}"%"
+
+
+ AND `created_by` LIKE "%"#{createdBy}"%"
+
+
+ AND `updated_by` LIKE "%"#{updatedBy}"%"
+
+
+ AND `status` = #{status}
+
+
+ = #{beginDate}
+ ]]>
+
+
+
+
+
+ ORDER BY `url` ASC, `created_at` DESC
+
+
+
+ SELECT
+ FROM `t_permission`
+
+
+
+
+ SELECT
+ FROM `t_permission`
+ WHERE `id` = #{id}
+
+
+
+ SELECT
+ FROM `t_permission`
+ WHERE `url` = #{url} LIMIT 1
+
+
+
+ SELECT
+ COUNT(`id`)
+ FROM `t_permission`
+
+
+
+
+
+ AND p.url LIKE "%"#{url}"%"
+
+
+ AND p.name LIKE "%"#{name}"%"
+
+
+
+
+ SELECT DISTINCT p.* FROM t_permission p, t_role_permission rp
+ WHERE rp.permission_id = p.id AND rp.role_id in
+ (
+ SELECT r.id from t_role r, t_admin_role ar
+ WHERE ar.admin_id = #{adminId} AND ar.role_id = r.id AND r.status = 1
+ ) AND p.status = 1
+
+
+
+
+ SELECT COUNT(DISTINCT p.id) FROM t_permission p, t_role_permission rp
+ WHERE rp.permission_id = p.id AND rp.role_id in
+ (
+ SELECT r.id from t_role r, t_admin_role ar
+ WHERE ar.admin_id = #{adminId} AND ar.role_id = r.id AND r.status = 1
+ ) AND p.status = 1
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/sqlMapper/RoleMapper.xml b/src/main/resources/sqlMapper/RoleMapper.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1cc476e494d314ffb29b414af9f536716d1df5a4
--- /dev/null
+++ b/src/main/resources/sqlMapper/RoleMapper.xml
@@ -0,0 +1,102 @@
+
+
+
+
+ `id`,
+ `name`,
+ `created_by`,
+ `created_at`,
+ `updated_by`,
+ `update_at`,
+ `status`
+
+
+
+ INSERT INTO
+ `t_role`(`id`,
+ `name`, `created_by`, `created_at`, `updated_by`, `update_at`,
+ `status` )
+ VALUES(#{id}, #{name}, #{createdBy}, NOW(),
+ #{updatedBy}, NOW(), #{status} )
+
+
+
+ DELETE FROM `t_role` WHERE `id` IN
+
+ #{idItem}
+
+
+
+
+ UPDATE `t_role`
+
+
+ `name` = #{name},
+
+
+ `updated_by` = #{updatedBy},
+
+
+ `status` = #{status},
+
+ `update_at` = NOW()
+
+ WHERE `id` = #{id}
+
+
+
+
+
+ AND `name` = #{name}
+
+
+ AND `created_by` LIKE "%"#{createdBy}"%"
+
+
+ AND `updated_by` LIKE "%"#{updatedBy}"%"
+
+
+ AND `update_at` = #{updateAt}
+
+
+ AND `status` = #{status}
+
+
+ = #{beginDate}
+ ]]>
+
+
+
+
+
+ ORDER BY `created_at` DESC
+
+
+
+ SELECT
+
+ FROM `t_role`
+
+
+
+
+ SELECT
+
+ FROM `t_role`
+ WHERE `id` = #{id}
+
+
+
+ SELECT
+ COUNT(`id`)
+ FROM `t_role`
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/sqlMapper/RolePermissionMapper.xml b/src/main/resources/sqlMapper/RolePermissionMapper.xml
new file mode 100644
index 0000000000000000000000000000000000000000..338bcb7bfccfd292cbc6c501c93a920123c451cf
--- /dev/null
+++ b/src/main/resources/sqlMapper/RolePermissionMapper.xml
@@ -0,0 +1,83 @@
+
+
+
+
+ `id`,
+ `role_id`,
+ `permission_id`
+
+
+
+ INSERT INTO
+ `t_role_permission`(`id`,`role_id`, `permission_id` )
+ VALUES(#{id}, #{roleId}, #{permissionId} )
+
+
+
+ DELETE FROM `t_role_permission` WHERE `id` IN
+
+ #{idItem}
+
+
+
+
+ DELETE FROM `t_role_permission` WHERE `role_id` = #{roleId}
+
+
+
+ DELETE FROM `t_role_permission` WHERE `permission_id` = #{permissionId}
+
+
+
+ UPDATE `t_role_permission`
+
+
+ `role_id` = #{roleId},
+
+
+ `permission_id` = #{permissionId},
+
+
+ WHERE `id` = #{id}
+
+
+
+
+
+ AND `role_id` = #{roleId}
+
+
+ AND `permission_id` = #{permissionId}
+
+
+
+
+
+ SELECT
+ FROM `t_role_permission`
+
+
+
+
+ SELECT
+ FROM `t_role_permission`
+ WHERE `id` = #{id}
+
+
+
+ SELECT
+ COUNT(`id`)
+ FROM `t_role_permission`
+
+
+
+
+ SELECT DISTINCT p.url FROM t_permission p, t_role_permission rp
+ WHERE rp.permission_id = p.id AND rp.role_id in
+ (
+ SELECT r.id from t_role r, t_admin_role ar
+ WHERE ar.admin_id = #{adminId} AND ar.role_id = r.id AND r.status = 1
+ ) AND p.status = 1
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/.gitignore b/src/main/webapp/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2a7b55a92af7f07aa1b6117ff6a00e3b36c57d6d
--- /dev/null
+++ b/src/main/webapp/.gitignore
@@ -0,0 +1 @@
+/upload/
diff --git a/src/main/webapp/WEB-INF/view/admin/authc/my_permissions.ftl b/src/main/webapp/WEB-INF/view/admin/authc/my_permissions.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..5716ad9f4d39c1b1ffad1d3fdd9e43b3e8783f9c
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/authc/my_permissions.ftl
@@ -0,0 +1,67 @@
+
+
+
+
+
+ 我的权限
+ [#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'admin']
+ [#assign adminAdminTab = 'myPermissions']
+ [#include "/common/admin_nav.ftl"]
+ [#assign model = 'permission']
+
+ [#include "/common/admin_admin_tab.ftl"]
+
+
+
+
+
+
+
+ #
+ URL地址
URL描述
+
+
+ [#if list?exists && list?size gt 0] [#list list as permission]
+
+ ${permission_index + 1}
+ ${permission.url}
${permission.name}
[/#list] [#else]
+
+ 暂无数据
+ [/#if]
+
+
+ [#--分页组件s--]
+ [#if list?exists && list?size gt 0]
+
+ [#include "/common/pagination.ftl"]
+ [/#if]
+ [#--分页组件e--]
+
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/admin/authc/my_roles.ftl b/src/main/webapp/WEB-INF/view/admin/authc/my_roles.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..7fab20926dbf746bb5d445a0f36000ecb181c58c
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/authc/my_roles.ftl
@@ -0,0 +1,47 @@
+
+
+
+
+
+ 我的角色列表
+ [#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'admin']
+ [#assign adminAdminTab = 'myRoles']
+ [#include "/common/admin_nav.ftl"]
+ [#assign model = 'role']
+
+
+ [#include "/common/admin_admin_tab.ftl"]
+
+
+
+
+
+
+ [#if list?exists && list?size gt 0]
+ [#list list as role]
+
+ ${role.name}
+
+ [/#list]
+ [#else]
暂无角色
+ [/#if]
+
+
+
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/admin/authc/update_pwd.ftl b/src/main/webapp/WEB-INF/view/admin/authc/update_pwd.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..edfd6aeabd6e1cd2a132d76536a3027d2c946137
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/authc/update_pwd.ftl
@@ -0,0 +1,60 @@
+
+
+
+
+
+修改密码
+[#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'admin']
+ [#assign adminAdminTab = 'updatePwd']
+ [#include "/common/admin_nav.ftl"]
+
+
+ [#include "/common/admin_admin_tab.ftl"]
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+ [#include "/common/footer.ftl"]
+
+
diff --git a/src/main/webapp/WEB-INF/view/admin/dashboard.ftl b/src/main/webapp/WEB-INF/view/admin/dashboard.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..d85b795164d3dd3e8e583ca7693802ccd360d950
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/dashboard.ftl
@@ -0,0 +1,7 @@
+
+
+
+
+
+
WELCOME TO SHIRO DEMO !
+
diff --git a/src/main/webapp/WEB-INF/view/admin/guest/login.ftl b/src/main/webapp/WEB-INF/view/admin/guest/login.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..6208e50522266829499ac874a3a680d030bfa761
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/guest/login.ftl
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+管理员登录 [#include "/common/head.ftl"]
+
+
+
+
+
+
+
+
+ ${webName}后台管理登录
+
+
+
+
+
+ [#assign callBack = '../index.html']
+ [#if returnURI != '']
+ [#assign callBack = returnURI]
+ [/#if]
+
+
+
+
Copyright © 2017 ${webName} 版权所有
+
+
+
+
+
+ [#include "/common/footer.ftl" /]
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/admin/guest/unauthorized.ftl b/src/main/webapp/WEB-INF/view/admin/guest/unauthorized.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..c9d15477768e6c81cc1fd97cffdb827d1dd8841f
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/guest/unauthorized.ftl
@@ -0,0 +1,25 @@
+[#-- 错误页面 --]
+
+
+
+
+
+ 未授权访问
+ [#include "/common/head.ftl"]
+
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
diff --git a/src/main/webapp/WEB-INF/view/admin/index.ftl b/src/main/webapp/WEB-INF/view/admin/index.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..e05efeda231018460dee87b948110f2cdec4bcf5
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/index.ftl
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+后台管理
+[#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'index']
+ [#include "/common/admin_nav.ftl"]
+
+
+ [#include "/admin/dashboard.ftl"]
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
diff --git a/src/main/webapp/WEB-INF/view/admin/limit/admin/add_or_edit.ftl b/src/main/webapp/WEB-INF/view/admin/limit/admin/add_or_edit.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..735aabb03139ec97fd8181c16ec3abebe00aeead
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/limit/admin/add_or_edit.ftl
@@ -0,0 +1,114 @@
+
+
+
+
+
+ ${isEdit?string('编辑', '添加')}管理员
+ [#include "/common/head.ftl"]
+
+
+
+
+ [#assign adminMenu = 'limit']
+ [#assign adminLimitTab = 'admin']
+ [#include "/common/admin_nav.ftl"]
+
+
+ [#include "/common/admin_limit_tab.ftl"]
+
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/admin/limit/admin/list.ftl b/src/main/webapp/WEB-INF/view/admin/limit/admin/list.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..19ccd464fef42b9af29f60992be9452b06fa7daf
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/limit/admin/list.ftl
@@ -0,0 +1,153 @@
+
+
+
+
+
+ 管理员列表
+ [#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'limit']
+ [#assign adminLimitTab = 'admin']
+ [#include "/common/admin_nav.ftl"]
+ [#assign model = 'limit/admin']
+
+
+ [#include "/common/admin_limit_tab.ftl"]
+
+
+
+
+ [#include "/common/page_size.ftl"]
+
+
+
+ #
+ 账号
姓名
创建人
创建时间
更新人
+ 更新时间
+ 状态
+ 操作
+
+
+
+ [#if list?exists && list?size gt 0] [#list list as admin]
+
+
+ [#if admin.id != loginedAdmin.id]
+
+ [#else]
+
+ [/#if]
+
+ ${admin.account}
${admin.name}
${admin.createdBy}
${admin.createdAt?string('yyyy-MM-dd HH:mm')}
${admin.updatedBy}
${admin.updatedAt?string('yyyy-MM-dd HH:mm')}
+ ${admin.status?string('启用 ', '禁用 ')}
+
+
+ 操作
+
+
+
+ [/#list] [#else]
+
+ 暂无数据
+ [/#if]
+
+
+ [#include "/common/page_size.ftl"]
+ [#--分页组件s--]
+ [#if list?exists && list?size gt 0]
+
+ [#include "/common/pagination.ftl"]
+ [/#if]
+ [#--分页组件e--]
+
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/admin/limit/admin/view.ftl b/src/main/webapp/WEB-INF/view/admin/limit/admin/view.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..4ab5e1a9674068d30e56b0f5daae5094b0eb3cc2
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/limit/admin/view.ftl
@@ -0,0 +1,84 @@
+
+
+
+
+
+查看管理员 [#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'limit'] [#assign adminLimitTab = 'admin'] [#include "/common/admin_nav.ftl"]
+
+
+ [#include "/common/admin_limit_tab.ftl"]
+
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
diff --git a/src/main/webapp/WEB-INF/view/admin/limit/permission/add_or_edit.ftl b/src/main/webapp/WEB-INF/view/admin/limit/permission/add_or_edit.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..b20fc694f42a0b954644a1b81e1f7ee5ce2cf5da
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/limit/permission/add_or_edit.ftl
@@ -0,0 +1,55 @@
+
+
+
+
+
+ ${isEdit?string('编辑', '添加')}权限
+ [#include "/common/head.ftl"]
+
+
+
+
+ [#assign adminMenu = 'limit']
+ [#assign adminLimitTab = 'permission']
+ [#include "/common/admin_nav.ftl"]
+
+ [#include "/common/admin_limit_tab.ftl"]
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/admin/limit/permission/list.ftl b/src/main/webapp/WEB-INF/view/admin/limit/permission/list.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..b1e1eb8b4158b59d142f5c6563933679d7a3f89a
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/limit/permission/list.ftl
@@ -0,0 +1,128 @@
+
+
+
+
+
+ 权限列表
+ [#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'limit']
+ [#assign adminLimitTab = 'permission']
+ [#include "/common/admin_nav.ftl"]
+ [#assign model = 'limit/permission']
+
+ [#include "/common/admin_limit_tab.ftl"]
+
+
+
+
+ [#include "/common/page_size.ftl"]
+
+
+
+ #
+ URL地址
URL描述
创建人
创建时间
更新人
更新时间
+ 状态
+ 操作
+
+
+
+ [#if list?exists && list?size gt 0] [#list list as permission]
+
+
+ ${permission.url}
${permission.name}
${permission.createdBy}
${permission.createdAt?string('yyyy-MM-dd HH:mm')}
${permission.updatedBy}
+ ${permission.updatedAt?string('yyyy-MM-dd HH:mm')}
${permission.status?string('启用 ', '禁用 ')}
+
+
+ 操作
+
+
+ [/#list] [#else]
+
+ 暂无数据
+ [/#if]
+
+
+ [#include "/common/page_size.ftl"]
+ [#--分页组件s--]
+ [#if list?exists && list?size gt 0]
+
+ [#include "/common/pagination.ftl"]
+ [/#if]
+ [#--分页组件e--]
+
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/admin/limit/permission/view.ftl b/src/main/webapp/WEB-INF/view/admin/limit/permission/view.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..08ccf797bc979a915560aa702a14f72485cf8a9d
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/limit/permission/view.ftl
@@ -0,0 +1,72 @@
+
+
+
+
+
+查看权限
+[#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'permission']
+ [#include "/common/admin_nav.ftl"]
+
+
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+ [#include "/common/footer.ftl"]
+
+
diff --git a/src/main/webapp/WEB-INF/view/admin/limit/role/add_or_edit.ftl b/src/main/webapp/WEB-INF/view/admin/limit/role/add_or_edit.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..fd3cf15d1e0f0224131f518e839938df477b3466
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/limit/role/add_or_edit.ftl
@@ -0,0 +1,102 @@
+
+
+
+
+
+ ${isEdit?string('编辑', '添加')}角色
+ [#include "/common/head.ftl"]
+
+
+
+
+ [#assign adminMenu = 'limit']
+ [#assign adminLimitTab = 'role']
+ [#include "/common/admin_nav.ftl"]
+
+
+ [#include "/common/admin_limit_tab.ftl"]
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/admin/limit/role/list.ftl b/src/main/webapp/WEB-INF/view/admin/limit/role/list.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..5dd4e10b7beb3c75d43a2a917126fe528fa6f81d
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/limit/role/list.ftl
@@ -0,0 +1,122 @@
+
+
+
+
+
+ 角色列表
+ [#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'limit']
+ [#assign adminLimitTab = 'role']
+ [#include "/common/admin_nav.ftl"]
+ [#assign model = 'limit/role']
+
+
+ [#include "/common/admin_limit_tab.ftl"]
+
+
+
+ [#include "/common/page_size.ftl"]
+
+
+
+ #
+ 角色名称
创建人
创建时间
修改人
修改时间
状态
+ 操作
+
+
+
+ [#if list?exists && list?size gt 0] [#list list as role]
+
+
+ ${role.name}
${role.createdBy}
${role.createdAt?string('yyyy-MM-dd HH:mm')}
${role.updatedBy}
${role.updateAt?string('yyyy-MM-dd HH:mm')}
${role.status?string('启用 ', '禁用 ')}
+
+
+ 操作
+
+
+ [/#list] [#else]
+
+ 暂无数据
+ [/#if]
+
+
+ [#include "/common/page_size.ftl"]
+ [#--分页组件s--]
+ [#if list?exists && list?size gt 0]
+
+ [#include "/common/pagination.ftl"]
+ [/#if]
+ [#--分页组件e--]
+
+
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/admin/limit/role/view.ftl b/src/main/webapp/WEB-INF/view/admin/limit/role/view.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..ca5c4e8cbd544fc1eac2f5433ca436cb0e55dde9
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/limit/role/view.ftl
@@ -0,0 +1,79 @@
+
+
+
+
+
+查看角色 [#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'limit'] [#assign adminLimitTab = 'role'] [#include "/common/admin_nav.ftl"]
+
+ [#include "/common/admin_limit_tab.ftl"]
+
+
+
+ [#include "/common/admin_copyright.ftl"]
+
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
diff --git a/src/main/webapp/WEB-INF/view/admin/test/index.ftl b/src/main/webapp/WEB-INF/view/admin/test/index.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..c94e9db08bb894cdabf647ed4391a9726feeb5ec
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/admin/test/index.ftl
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+TEST
+[#include "/common/head.ftl"]
+
+
+
+ [#assign adminMenu = 'test']
+ [#include "/common/admin_nav.ftl"]
+
+
+
测试页面
+
+
testAjax
+
+
+
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/common/admin_admin_tab.ftl b/src/main/webapp/WEB-INF/view/common/admin_admin_tab.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..8bf7b4bc14ed4c63e05c14722920956818610a81
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/admin_admin_tab.ftl
@@ -0,0 +1,6 @@
+
+
diff --git a/src/main/webapp/WEB-INF/view/common/admin_copyright.ftl b/src/main/webapp/WEB-INF/view/common/admin_copyright.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..615eb08b23dd17b4330eedb5018489a470d75efb
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/admin_copyright.ftl
@@ -0,0 +1 @@
+Copyright © 2017 ${webName} 版权所有
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/common/admin_limit_tab.ftl b/src/main/webapp/WEB-INF/view/common/admin_limit_tab.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..3bbc8320bd54f9e92baa1e3a99616370b068f0e9
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/admin_limit_tab.ftl
@@ -0,0 +1,12 @@
+
+
+ [@shiro.hasPermission name="/admin/limit/admin/list.html"]
+ 管理员列表
+ [/@shiro.hasPermission]
+ [@shiro.hasPermission name="/admin/limit/permission/list.html"]
+ 权限列表
+ [/@shiro.hasPermission]
+ [@shiro.hasPermission name="/admin/limit/role/list.html"]
+ 角色列表
+ [/@shiro.hasPermission]
+
diff --git a/src/main/webapp/WEB-INF/view/common/admin_nav.ftl b/src/main/webapp/WEB-INF/view/common/admin_nav.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..0a06bb92277035085c97b4eb6d350fce34271503
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/admin_nav.ftl
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+ 主面板
+
+ [@shiro.hasPermission name="/admin/test/index.html"]
+
+ 测试页面
+
+ [/@shiro.hasPermission]
+
+
+
+ [@shiro.hasAnyRoles name="超级管理员,管理员操作者,角色操作者,权限操作者"]
+
+ [/@shiro.hasAnyRoles]
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/common/daterangepicker.ftl b/src/main/webapp/WEB-INF/view/common/daterangepicker.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..e542c467319856b87a1255bb6984a528b789dfed
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/daterangepicker.ftl
@@ -0,0 +1,4 @@
+[#-- 日期选择插件 --]
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/common/error.ftl b/src/main/webapp/WEB-INF/view/common/error.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..43d6205a61a57df4d3140bdcb875a60c4b652b7a
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/error.ftl
@@ -0,0 +1,26 @@
+[#-- 错误页面 --]
+
+
+
+
+
+ ${error.code}
+ [#include "/common/head.ftl"]
+
+
+
+
+
+
+
${error.code}
+
${error.msg}
+
+ 返回首页
+ 返回上一页
+
+
+
+
+ [#include "/common/footer.ftl"]
+
+
diff --git a/src/main/webapp/WEB-INF/view/common/footer.ftl b/src/main/webapp/WEB-INF/view/common/footer.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..5972c638e4088f362d55c18b5ebef43aea2d085c
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/footer.ftl
@@ -0,0 +1,33 @@
+
+[#-- bootstrap--]
+
+
+[#-- toast消息插件--]
+
+
+[#-- jquery-validate 验证插件
+ form's class is 'validate-form'
+--]
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/common/head.ftl b/src/main/webapp/WEB-INF/view/common/head.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..22ed000a5f7d3c7363b41e02b87283f2fb9aaa13
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/head.ftl
@@ -0,0 +1,35 @@
+[#-- Bootstrap --]
+
+
+[#-- font-awesome --]
+
+
+
+
+[#-- jqPaginator --]
+
+
+[#-- common css --]
+
+
+[#-- toast消息插件--]
+
+
+[#-- jquery-validate 验证插件 --]
+
+
+
+
+
+
+
+
+
+
+
+
+[#assign webName = "shiro-demo"]
+[#assign adminPath = "admin"]
diff --git a/src/main/webapp/WEB-INF/view/common/page_size.ftl b/src/main/webapp/WEB-INF/view/common/page_size.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..ee985a746c36d433ad3b33279acded9c35cc2290
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/page_size.ftl
@@ -0,0 +1,18 @@
+[#if list?exists && list?size gt 0]
+
+
+
删除
+
+ 每页显示${pageSize }
+
+
+
+
+[/#if]
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/common/pagination.ftl b/src/main/webapp/WEB-INF/view/common/pagination.ftl
new file mode 100644
index 0000000000000000000000000000000000000000..18d8c0f60389c8b34b46b996a1c280c8937fbdf8
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/common/pagination.ftl
@@ -0,0 +1,42 @@
+[#-- 分页模块
+ ->需要表单id=searchForm,隐藏域name=pageNo和name=pageSize
+ --]
+
+
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000000000000000000000000000000000..57cdfeaa71cc081e5b309bc0155a6d6f0690c6ae
--- /dev/null
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,110 @@
+
+
+ shiro-demo
+
+
+ contextConfigLocation
+ classpath:config/applicationContext.xml
+
+
+
+
+ shiroFilter
+ org.springframework.web.filter.DelegatingFilterProxy
+
+ targetFilterLifecycle
+ true
+
+
+
+ shiroFilter
+ /*
+
+
+
+
+
+ encodingFilter
+ org.springframework.web.filter.CharacterEncodingFilter
+ true
+
+ encoding
+ UTF-8
+
+
+
+ encodingFilter
+ /*
+
+
+
+
+ org.springframework.web.context.ContextLoaderListener
+
+
+
+
+ org.springframework.web.util.IntrospectorCleanupListener
+
+
+
+ org.springframework.web.context.request.RequestContextListener
+
+
+
+ SpringMVC
+ org.springframework.web.servlet.DispatcherServlet
+
+ contextConfigLocation
+ classpath:config/spring-mvc.xml
+
+ 1
+ true
+
+
+ SpringMVC
+ *.html
+
+
+ SpringMVC
+ *.json
+
+
+ SpringMVC
+ /
+
+
+
+
+ /index.html
+
+
+
+ 30
+
+
+
+
+
+ 400
+ /error/badRequest.html
+
+
+
+ 404
+ /error/notFound.html
+
+
+
+ 405
+ /error/methodNotSupported.html
+
+
+
+ 500
+ /error/sysError.html
+
+
+
+
diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..5bd08ad9d8c0acb4078116f91f6efe8d5871380f
--- /dev/null
+++ b/src/main/webapp/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+Loading...
+
+
+ Loading...
+
+
\ No newline at end of file
diff --git a/src/main/webapp/static/css/common.css b/src/main/webapp/static/css/common.css
new file mode 100644
index 0000000000000000000000000000000000000000..e3f5bf3bd75a17f2a5a08b41b623cf271eb36cee
--- /dev/null
+++ b/src/main/webapp/static/css/common.css
@@ -0,0 +1,223 @@
+@CHARSET "UTF-8";
+
+.captcha-img {
+ cursor: pointer;
+}
+
+.front-bg {
+ color: #505050;
+ line-height: 1.75em;
+ background: #ebebeb;
+ position: relative;
+ font-family: "Helvetica Neue", Helvetica, Arial, "Hiragino Sans GB",
+ "Hiragino Sans GB W3", "WenQuanYi Micro Hei",
+ "Microsoft YaHei UI", "Microsoft YaHei", sans-serif;
+}
+
+/* Edit Below to Customize Widths > 768px */
+@media ( min-width :768px) {
+ /* Wrappers */
+ #wrapper {
+ padding-left: 135px;
+ }
+ #page-wrapper {
+ padding: 60px 7px;
+ }
+ #main-iframe {
+ width: 98%;
+ }
+
+ /* Side Nav */
+ .side-nav {
+ margin-left: -225px;
+ left: 225px;
+ width: 135px;
+ position: fixed;
+ top: 50px;
+ height: 100%;
+ border-radius: 0;
+ border: none;
+ background-color: #222222;
+ overflow-y: auto;
+ }
+
+ /* Bootstrap Default Overrides - Customized Dropdowns for the Side Nav */
+ .side-nav>li.dropdown>ul.dropdown-menu {
+ position: relative;
+ min-width: 225px;
+ margin: 0;
+ padding: 0;
+ border: none;
+ border-radius: 0;
+ background-color: transparent;
+ box-shadow: none;
+ -webkit-box-shadow: none;
+ }
+ .side-nav>li.dropdown>ul.dropdown-menu>li>a {
+ color: #999999;
+ padding: 15px 15px 15px 25px;
+ }
+ .side-nav>li.dropdown>ul.dropdown-menu>li>a:hover, .side-nav>li.dropdown>ul.dropdown-menu>li>a.active,
+ .side-nav>li.dropdown>ul.dropdown-menu>li>a:focus {
+ color: #fff;
+ background-color: #080808;
+ }
+ .side-nav>li>a {
+ width: 135px;
+ }
+ .navbar-inverse .navbar-nav>li>a:hover, .navbar-inverse .navbar-nav>li>a:focus
+ {
+ background-color: #080808;
+ }
+}
+
+@media ( min-width : 1100px) {
+ .col-lg-3 {
+ width: 25%;
+ }
+}
+
+.announcement-heading {
+ font-size: 40px;
+ margin: 0;
+}
+
+#mainIfram {
+ width: 100%;
+ /* height: 585px; */
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.filter {
+ margin: 5px auto;
+ width: 100%;
+ padding: 5px;
+}
+
+.dropdown-menu>li>a.color-red {
+ color: red;
+}
+
+#searchForm .form-control {
+ width: 150px;
+}
+
+#searchForm .form-group {
+ margin-bottom: 5px;
+}
+
+/*分页*/
+.pagination .page-record a {
+ color: #9999B2
+}
+
+.pagination .page-record a:hover {
+ background: #fff;
+ color: #9999B2;
+}
+
+.copyright {
+ width: 100%;
+ color: #9A9A9A;
+}
+
+.color-blue {
+ color: blue;
+}
+
+.color-gray {
+ color: #A7A2A2;
+}
+
+.color-green {
+ color: green;
+}
+
+.color-red {
+ color: red;
+}
+
+.checkAll, .idCheckbox {
+ cursor: pointer;
+}
+
+.fa-square-o {
+ width: 13px;
+}
+
+.clear-input {
+ position: relative;
+ left: -22px;
+ color: #757678;
+}
+
+#page-wrapper {
+ padding: 60px 7px;
+ overflow-x: hidden;
+}
+
+.form-horizontal .control-label.text-left {
+ text-align: left;
+}
+
+textarea {
+ resize: none;
+}
+
+.nav-bottom {
+ width: 100%;
+ height: 3.6em;
+}
+
+.post {
+ padding: 35px;
+ background: #ffffff;
+ margin-bottom: 35px;
+ position: relative;
+ overflow: hidden;
+}
+
+.post .post-head .post-title a {
+ color: #303030;
+}
+
+.post .post-head .post-title a:hover {
+ text-decoration: none;
+}
+
+.article-attr-ul {
+ list-style: none;
+ height: 13px;
+ padding: 0;
+}
+
+.article-attr-ul li {
+ float: left;
+ margin: 0 10px;
+}
+
+.footer-top {
+ width: 100%;
+ height: 2em;
+}
+
+.format-error-1 label.error {
+ position: absolute;
+ width: 104px;
+ top: 10px;
+}
+
+.format-error-1 #captcha-error {
+ right: -70px;
+}
+
+.format-error-1 .captcha-box label.error {
+ right: -70px;
+}
+
+.admin-body-bg {
+ background-color: #e9e9e9;
+ background-repeat: no-repeat;
+ background-position: center;
+}
\ No newline at end of file
diff --git a/src/main/webapp/static/css/index.css b/src/main/webapp/static/css/index.css
new file mode 100644
index 0000000000000000000000000000000000000000..8833f82dddbfd34fded52d803bf6e641948d8671
--- /dev/null
+++ b/src/main/webapp/static/css/index.css
@@ -0,0 +1,134 @@
+/* GLOBAL STYLES
+-------------------------------------------------- */
+/* Padding below the footer and lighter body text */
+
+body {
+ padding-bottom: 40px;
+ color: #5a5a5a;
+}
+
+
+/* CUSTOMIZE THE NAVBAR
+-------------------------------------------------- */
+
+/* Special class on .container surrounding .navbar, used for positioning it into place. */
+.navbar-wrapper {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 20;
+}
+
+/* Flip around the padding for proper display in narrow viewports */
+.navbar-wrapper > .container {
+ padding-right: 0;
+ padding-left: 0;
+}
+.navbar-wrapper .navbar {
+ padding-right: 15px;
+ padding-left: 15px;
+}
+.navbar-wrapper .navbar .container {
+ width: auto;
+}
+
+
+/* CUSTOMIZE THE CAROUSEL
+-------------------------------------------------- */
+
+/* Carousel base class */
+.carousel {
+ height: 500px;
+ margin-bottom: 60px;
+}
+/* Since positioning the image, we need to help out the caption */
+.carousel-caption {
+ z-index: 10;
+}
+
+/* Declare heights because of positioning of img element */
+.carousel .item {
+ height: 500px;
+ background-color: #777;
+}
+.carousel-inner > .item > img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ min-width: 100%;
+ height: 500px;
+}
+
+
+/* MARKETING CONTENT
+-------------------------------------------------- */
+
+/* Center align the text within the three columns below the carousel */
+.marketing .col-lg-4 {
+ margin-bottom: 20px;
+ text-align: center;
+}
+.marketing h2 {
+ font-weight: normal;
+}
+.marketing .col-lg-4 p {
+ margin-right: 10px;
+ margin-left: 10px;
+}
+
+
+/* Featurettes
+------------------------- */
+
+.featurette-divider {
+ margin: 80px 0; /* Space out the Bootstrap more */
+}
+
+/* Thin out the marketing headings */
+.featurette-heading {
+ font-weight: 300;
+ line-height: 1;
+ letter-spacing: -1px;
+}
+
+
+/* RESPONSIVE CSS
+-------------------------------------------------- */
+
+@media (min-width: 768px) {
+ /* Navbar positioning foo */
+ .navbar-wrapper {
+ margin-top: 20px;
+ }
+ .navbar-wrapper .container {
+ padding-right: 15px;
+ padding-left: 15px;
+ }
+ .navbar-wrapper .navbar {
+ padding-right: 0;
+ padding-left: 0;
+ }
+
+ /* The navbar becomes detached from the top, so we round the corners */
+ .navbar-wrapper .navbar {
+ border-radius: 4px;
+ }
+
+ /* Bump up size of carousel content */
+ .carousel-caption p {
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: 1.4;
+ }
+
+ .featurette-heading {
+ font-size: 50px;
+ }
+}
+
+@media (min-width: 992px) {
+ .featurette-heading {
+ margin-top: 120px;
+ }
+}
diff --git a/src/main/webapp/static/images/default.png b/src/main/webapp/static/images/default.png
new file mode 100644
index 0000000000000000000000000000000000000000..a0a4566bff823439d91dac0c8fd9d3954887880b
Binary files /dev/null and b/src/main/webapp/static/images/default.png differ
diff --git a/src/main/webapp/static/images/favicon.ico b/src/main/webapp/static/images/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..d8c40c4802f5d81832b9be54fc43b58f9bb1224d
Binary files /dev/null and b/src/main/webapp/static/images/favicon.ico differ
diff --git a/src/main/webapp/static/images/web/banner/banner_1.jpg b/src/main/webapp/static/images/web/banner/banner_1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..025daa0ab939e67dc10def18eb8d29b37ba693e1
Binary files /dev/null and b/src/main/webapp/static/images/web/banner/banner_1.jpg differ
diff --git a/src/main/webapp/static/images/web/banner/banner_2.jpg b/src/main/webapp/static/images/web/banner/banner_2.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..ecd373236f538d8a4c7d7c4c70f8878c2c823c70
Binary files /dev/null and b/src/main/webapp/static/images/web/banner/banner_2.jpg differ
diff --git a/src/main/webapp/static/images/web/banner/banner_3.jpg b/src/main/webapp/static/images/web/banner/banner_3.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..c29eaa8a52719d7f7cf446fe21f4ec230d73f7f0
Binary files /dev/null and b/src/main/webapp/static/images/web/banner/banner_3.jpg differ
diff --git a/src/main/webapp/static/images/web/featurettes/featurettes_1.jpg b/src/main/webapp/static/images/web/featurettes/featurettes_1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..de6cf23bdd5ceeb190943f33c5a65d12b3d88886
Binary files /dev/null and b/src/main/webapp/static/images/web/featurettes/featurettes_1.jpg differ
diff --git a/src/main/webapp/static/images/web/featurettes/featurettes_2.jpg b/src/main/webapp/static/images/web/featurettes/featurettes_2.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..741a28b3f0e494a5755bbb96a2a4f8433f71cd95
Binary files /dev/null and b/src/main/webapp/static/images/web/featurettes/featurettes_2.jpg differ
diff --git a/src/main/webapp/static/images/web/featurettes/featurettes_3.jpg b/src/main/webapp/static/images/web/featurettes/featurettes_3.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..8a8df5fc8bcd78389f598948fe7f544f2fe9a6ec
Binary files /dev/null and b/src/main/webapp/static/images/web/featurettes/featurettes_3.jpg differ
diff --git a/src/main/webapp/static/js/common.js b/src/main/webapp/static/js/common.js
new file mode 100644
index 0000000000000000000000000000000000000000..68482b2ea280ee18e9f25d936b955bb95edaf40c
--- /dev/null
+++ b/src/main/webapp/static/js/common.js
@@ -0,0 +1,332 @@
+
+//auto run
+$(function() {
+
+ updateCaptcha();//更新验证码
+
+ ajaxSubmit();//异步提交表单
+
+ datePicker();//日期选择器
+
+ selectMore();//多选
+
+ clearInput();//清除输入内容
+
+ imgPreview();//图片预览
+});
+
+//更新验证码
+function updateCaptcha() {
+ $('.captcha-img').bind('click', function(){
+ var captchaId = parseInt(Math.random() * 1000000);
+ $(this).attr('src', contextPath + '/captcha.html?captchaId=' + captchaId);
+ $('input[name="captchaId"]').val(captchaId);
+ $('input.captchaId').val(captchaId);
+ });
+
+}
+
+
+
+//toast message
+/*
+ * $.toast('password is wrong');
+ * $.toast('success', '添加成功');
+ * $.toast('warning', 'what fuck are you doing', 'left-center');
+ *
+ * */
+$.toast = function() {
+ var len= arguments.length;
+ var type = "error";
+ var msg = "No Message";
+ var position = "top-center";
+
+ if(len == 1) {
+ msg = arguments[0];
+ }
+
+ if(len == 2) {
+ type = arguments[0];
+ msg = arguments[1];
+ }
+
+ if(len >= 3) {
+ type = arguments[0];
+ msg = arguments[1];
+ position = arguments[2];
+ }
+
+ //user configuration of all toastmessages to come:
+ $().toastmessage('showToast', {
+ text : msg,
+ sticky : false,
+ position : position,// top-left, top-center, top-right, middle-left, middle-center, middle-right
+ type : type// notice, warning, error, success
+ });
+ return this;
+};
+
+function datePicker() {
+ $('input[name="daterange"]').daterangepicker({
+ "showDropdowns": true
+ });
+ datePicker4S();
+}
+function datePicker4S() {
+ $('input.daterange').daterangepicker({
+ "showDropdowns": true
+ });
+}
+
+//[公用] 异步提交表单 ->一个页面只有第一个form有效
+function ajaxSubmit() {
+ $('.ajax-submit').validate({
+ submitHandler : function(form) {
+ form = $(form);
+ var action = form.attr('action');
+ var formDate = form.serialize();
+ var submitBtn = form.find('button[type="submit"]');
+ var redirect = form.attr('redirect');//成功之后重定向地址
+ var successMsg = form.attr('successMsg');//成功提示信息
+ //console.log(action);
+ console.log(formDate);
+ if (!action) {
+ $.toast('action不能为空');
+ return false;
+ }
+ if (!redirect) {
+ $.toast('redirect不能为空');
+ return false;
+ }
+ if (!successMsg) {
+ successMsg = '操作成功';
+ }
+ $.ajax( {
+ url : action,
+ dataType : 'json',
+ data : formDate,
+ type : "POST",
+ beforeSend : function() {
+ submitBtn.attr('title', submitBtn.text()); //将按钮名字暂存起来
+ submitBtn.text('提交中...');
+ submitBtn.attr('disabled', true);
+ },
+ success : function(resp) {
+ //兼容另一种访问模式
+ if (resp.transDto) {
+ resp = resp.transDto;
+ }
+ // 请求成功时处理
+ console.log(resp);
+ if (resp) {
+ if (resp.result) {
+ submitBtn.attr('type', 'button');
+ $.toast('success', successMsg);
+ setTimeout('location.href = "' + redirect +'"', 1000);
+ } else {
+ $.toast(resp.message);
+ if( typeof ajaxSubmitErrorCallback === 'function' ){
+ ajaxSubmitErrorCallback();//报错时回调方法
+ }
+ }
+ } else {
+ $.toast('请求失败, 请稍后重试');
+ setTimeout('location.reload()', 2000);
+ }
+ },
+ complete : function() {
+ // 请求完成的处理
+ submitBtn.text(submitBtn.attr('title'));
+ submitBtn.attr('disabled', false);
+ },
+ error : function(err) {
+ // 请求出错处理
+ $.toast('系统繁忙, 稍后重试');
+ console.log(err);
+ }
+ });
+ return false;
+ }
+ });
+
+}
+
+
+
+function selectMore() {
+ $('.checkAll').bind('click', function() {
+ var checkAll = $(this).is(".fa-check-square-o");
+ //checked($(this), checkAll);
+ checked($('.checkAll'), checkAll);
+ checked($('.idCheckbox'), checkAll);
+ });
+ var $subBox = $(".idCheckbox");
+ $subBox.click(function() {
+ checked($(this), $(this).is(".fa-check-square-o"));
+ var checkedAll = $subBox.length == $("i[name='idCheckbox'].fa-check-square-o").length;
+ console.log( $("i[name='idCheckbox'].fa-check-square-o").length);
+ checked($('.checkAll'), !checkedAll);
+ });
+}
+
+//多选框的图标
+function checked(selector, isChecked) {
+ if (isChecked) {
+ selector.removeClass('fa-check-square-o');
+ selector.addClass('fa-square-o');
+ } else {
+ selector.removeClass('fa-square-o');
+ selector.addClass('fa-check-square-o');
+ }
+}
+
+function muliDel(model) {
+ // 多项删除
+ $('.multi-delete-btn').bind('click', function() {
+ var checkedItem = $("i[name='idCheckbox'].fa-check-square-o");
+ if (checkedItem.length == 0) {
+ $.toast('warning', '请先勾选要删除的对象');
+ return false;
+ }
+ if (!confirm('确认删除所选项?')) {
+ return false;
+ }
+ var ids = new Array();
+ checkedItem.each(function() {
+ ids.push($(this).attr('value'));
+ });
+ postDelete(ids, model);
+ });
+}
+
+//提交删除
+function postDelete(ids, model) {
+ console.debug(ids);
+ if (ids == '' || ids == null || ids.length == 0) {
+ $.toast('warning', '请选择要删除的数据');
+ return false;
+ }
+
+ $.ajax({
+ url : contextPath + '/admin/' + model + '/delete.json',
+ data : {ids: ids},
+ type : "POST",
+ success : function(resp) {
+ if (resp) {
+ if (resp.result) {
+ $.toast('success', '删除成功');
+ setTimeout('location.reload()', 1000);
+ } else {
+ $.toast(resp.message);
+ }
+ } else {
+ $.toast('请求失败, 稍后重试');
+ }
+ },
+ error : function(err) {
+ console.log(err);
+ $.toast('系统繁忙, 稍后重试');
+ }
+ });
+}
+// 单个删除
+function singleDel(id, model) {
+ var ids= new Array();
+ ids.push(id);
+ postDelete(ids, model);
+}
+
+//每页多少条
+$('.per-page li a').bind('click', function() {
+ $('.per-page-count').text($(this).attr('data-id'));
+ $('input[name="pageSize"]').val($(this).attr('data-id'));
+ $('input.pageSize').val($(this).attr('data-id'));//兼容Struts2版本
+ $('#searchForm').submit();
+});
+
+
+function clearInput() {
+ $('.clear-input').click(function(){
+ $(this).prev('input').val('');
+ });
+}
+
+/**
+ * 图片预览
+ *
+ *
+ * */
+function imgPreview() {
+ $('.preview-input').change(function(e) {
+ var file = e.target.files[0];
+ var img = new Image(),
+ url = img.src = URL.createObjectURL(file);
+ var $img = $(img);
+ $img.css('max-width', '100%');
+ $('.preview-img').empty().append($img);
+ });
+}
+
+
+
+//--------------s
+function batchDel(model) {
+ // 多项删除
+ $('.batch-delete-btn').bind('click', function() {
+ var checkedItem = $("i[name='idCheckbox'].fa-check-square-o");
+ if (checkedItem.length == 0) {
+ $.toast('warning', '请先勾选要删除的对象');
+ return false;
+ }
+ if (!confirm('确认删除所选项?')) {
+ return false;
+ }
+ var ids = new Array();
+ checkedItem.each(function() {
+ ids.push($(this).attr('value'));
+ });
+ pDelete(ids, model);
+ });
+}
+
+//提交删除
+function pDelete(ids, model) {
+ console.debug(ids);
+ var delIds = ids.join(',');
+ console.debug(delIds);
+ if (ids == '' || ids == null || ids.length == 0) {
+ $.toast('warning', '请选择要删除的数据');
+ return false;
+ }
+
+ $.ajax({
+ url : contextPath + '/admin/' + model + '/delete.json',
+ data : {ids: delIds},
+ type : "POST",
+ success : function(resp) {
+ //兼容另一种模式
+ resp = resp.transDto;
+ if (resp) {
+ if (resp.result) {
+ $.toast('success', '删除成功');
+ setTimeout('location.reload()', 1000);
+ } else {
+ $.toast(resp.message);
+ }
+ } else {
+ $.toast('请求失败, 稍后重试');
+ }
+ },
+ error : function(err) {
+ console.log(err);
+ $.toast('系统繁忙, 稍后重试');
+ }
+ });
+}
+
+// 单个删除
+function oneDel(id, model) {
+ var ids= new Array();
+ ids.push(id);
+ pDelete(ids, model);
+}
\ No newline at end of file
diff --git a/src/main/webapp/static/js/jqPaginator.js b/src/main/webapp/static/js/jqPaginator.js
new file mode 100644
index 0000000000000000000000000000000000000000..a7dfe2ce1d2b88b4242c18b5587c1a36a5e8d7e9
--- /dev/null
+++ b/src/main/webapp/static/js/jqPaginator.js
@@ -0,0 +1,274 @@
+(function ($) {
+ 'use strict';
+
+ $.jqPaginator = function (el, options) {
+ if(!(this instanceof $.jqPaginator)){
+ return new $.jqPaginator(el, options);
+ }
+
+ var self = this;
+
+ self.$container = $(el);
+
+ self.$container.data('jqPaginator', self);
+
+ self.init = function () {
+
+ if (options.first || options.prev || options.next || options.last || options.page) {
+ options = $.extend({}, {
+ first: '',
+ prev: '',
+ next: '',
+ last: '',
+ page: ''
+ }, options);
+ }
+
+ self.options = $.extend({}, $.jqPaginator.defaultOptions, options);
+
+ self.verify();
+
+ self.extendJquery();
+
+ self.render();
+
+ self.fireEvent(this.options.currentPage, 'init');
+ };
+
+ self.verify = function () {
+ var opts = self.options;
+
+ if (!self.isNumber(opts.totalPages)) {
+ throw new Error('[jqPaginator] type error: totalPages');
+ }
+
+ if (!self.isNumber(opts.totalCounts)) {
+ throw new Error('[jqPaginator] type error: totalCounts');
+ }
+
+ if (!self.isNumber(opts.pageSize)) {
+ throw new Error('[jqPaginator] type error: pageSize');
+ }
+
+ if (!self.isNumber(opts.currentPage)) {
+ throw new Error('[jqPaginator] type error: currentPage');
+ }
+
+ if (!self.isNumber(opts.visiblePages)) {
+ throw new Error('[jqPaginator] type error: visiblePages');
+ }
+
+ if (!opts.totalPages && !opts.totalCounts) {
+ throw new Error('[jqPaginator] totalCounts or totalPages is required');
+ }
+
+ if (!opts.totalPages && !opts.totalCounts) {
+ throw new Error('[jqPaginator] totalCounts or totalPages is required');
+ }
+
+ if (!opts.totalPages && opts.totalCounts && !opts.pageSize) {
+ throw new Error('[jqPaginator] pageSize is required');
+ }
+
+ if (opts.totalCounts && opts.pageSize) {
+ opts.totalPages = Math.ceil(opts.totalCounts / opts.pageSize);
+ }
+
+ if (opts.currentPage < 1 || opts.currentPage > opts.totalPages) {
+ throw new Error('[jqPaginator] currentPage is incorrect');
+ }
+
+ if (opts.totalPages < 1) {
+ throw new Error('[jqPaginator] totalPages cannot be less currentPage');
+ }
+ };
+
+ self.extendJquery = function () {
+ $.fn.jqPaginatorHTML = function (s) {
+ return s ? this.before(s).remove() : $('').append(this.eq(0).clone()).html();
+ };
+ };
+
+ self.render = function () {
+ self.renderHtml();
+ self.setStatus();
+ self.bindEvents();
+ };
+
+ self.renderHtml = function () {
+ var html = [];
+
+ var pages = self.getPages();
+ for (var i = 0, j = pages.length; i < j; i++) {
+ html.push(self.buildItem('page', pages[i]));
+ }
+
+ self.isEnable('prev') && html.unshift(self.buildItem('prev', self.options.currentPage - 1));
+ self.isEnable('first') && html.unshift(self.buildItem('first', 1));
+ self.isEnable('statistics') && html.unshift(self.buildItem('statistics'));
+ self.isEnable('next') && html.push(self.buildItem('next', self.options.currentPage + 1));
+ self.isEnable('last') && html.push(self.buildItem('last', self.options.totalPages));
+
+ if (self.options.wrapper) {
+ self.$container.html($(self.options.wrapper).html(html.join('')).jqPaginatorHTML());
+ } else {
+ self.$container.html(html.join(''));
+ }
+ };
+
+ self.buildItem = function (type, pageData) {
+ var html = self.options[type]
+ .replace(/{{page}}/g, pageData)
+ .replace(/{{totalPages}}/g, self.options.totalPages)
+ .replace(/{{totalCounts}}/g, self.options.totalCounts);
+
+ return $(html).attr({
+ 'jp-role': type,
+ 'jp-data': pageData
+ }).jqPaginatorHTML();
+ };
+
+ self.setStatus = function () {
+ var options = self.options;
+
+ if (!self.isEnable('first') || options.currentPage === 1) {
+ $('[jp-role=first]', self.$container).addClass(options.disableClass);
+ }
+ if (!self.isEnable('prev') || options.currentPage === 1) {
+ $('[jp-role=prev]', self.$container).addClass(options.disableClass);
+ }
+ if (!self.isEnable('next') || options.currentPage >= options.totalPages) {
+ $('[jp-role=next]', self.$container).addClass(options.disableClass);
+ }
+ if (!self.isEnable('last') || options.currentPage >= options.totalPages) {
+ $('[jp-role=last]', self.$container).addClass(options.disableClass);
+ }
+
+ $('[jp-role=page]', self.$container).removeClass(options.activeClass);
+ $('[jp-role=page][jp-data=' + options.currentPage + ']', self.$container).addClass(options.activeClass);
+ };
+
+ self.getPages = function () {
+ var pages = [],
+ visiblePages = self.options.visiblePages,
+ currentPage = self.options.currentPage,
+ totalPages = self.options.totalPages;
+
+ if (visiblePages > totalPages) {
+ visiblePages = totalPages;
+ }
+
+ var half = Math.floor(visiblePages / 2);
+ var start = currentPage - half + 1 - visiblePages % 2;
+ var end = currentPage + half;
+
+ if (start < 1) {
+ start = 1;
+ end = visiblePages;
+ }
+ if (end > totalPages) {
+ end = totalPages;
+ start = 1 + totalPages - visiblePages;
+ }
+
+ var itPage = start;
+ while (itPage <= end) {
+ pages.push(itPage);
+ itPage++;
+ }
+
+ return pages;
+ };
+
+ self.isNumber = function (value) {
+ var type = typeof value;
+ return type === 'number' || type === 'undefined';
+ };
+
+ self.isEnable = function (type) {
+ return self.options[type] && typeof self.options[type] === 'string';
+ };
+
+ self.switchPage = function (pageIndex) {
+ self.options.currentPage = pageIndex;
+ self.render();
+ };
+
+ self.fireEvent = function (pageIndex, type) {
+ return (typeof self.options.onPageChange !== 'function') || (self.options.onPageChange(pageIndex, type) !== false);
+ };
+
+ self.callMethod = function (method, options) {
+ switch (method) {
+ case 'option':
+ self.options = $.extend({}, self.options, options);
+ self.verify();
+ self.render();
+ break;
+ case 'destroy':
+ self.$container.empty();
+ self.$container.removeData('jqPaginator');
+ break;
+ default :
+ throw new Error('[jqPaginator] method "' + method + '" does not exist');
+ }
+
+ return self.$container;
+ };
+
+ self.bindEvents = function () {
+ var opts = self.options;
+
+ self.$container.off();
+ self.$container.on('click', '[jp-role]', function () {
+ var $el = $(this);
+ if ($el.hasClass(opts.disableClass) || $el.hasClass(opts.activeClass)) {
+ return;
+ }
+
+ var pageIndex = +$el.attr('jp-data');
+ if (self.fireEvent(pageIndex, 'change')) {
+ self.switchPage(pageIndex);
+ }
+ });
+ };
+
+ self.init();
+
+ return self.$container;
+ };
+
+ $.jqPaginator.defaultOptions = {
+ wrapper: '',
+ first: '
首页 ',
+ prev: '上一页 ',
+ next: '下一页 ',
+ last: '尾页 ',
+ page: '{{page}} ',
+ totalPages: 0,
+ totalCounts: 0,
+ pageSize: 0,
+ currentPage: 1,
+ visiblePages: 7,
+ disableClass: 'disabled',
+ activeClass: 'active',
+ onPageChange: null
+ };
+
+ $.fn.jqPaginator = function () {
+ var self = this,
+ args = Array.prototype.slice.call(arguments);
+
+ if (typeof args[0] === 'string') {
+ var $instance = $(self).data('jqPaginator');
+ if (!$instance) {
+ throw new Error('[jqPaginator] the element is not instantiated');
+ } else {
+ return $instance.callMethod(args[0], args[1]);
+ }
+ } else {
+ return new $.jqPaginator(this, args[0]);
+ }
+ };
+
+})(jQuery);
\ No newline at end of file
diff --git a/src/main/webapp/static/js/jquery-3.1.0.min.js b/src/main/webapp/static/js/jquery-3.1.0.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..f6a6a99e60ee9a5ce4d0922a3a82c6d81c1db7f2
--- /dev/null
+++ b/src/main/webapp/static/js/jquery-3.1.0.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v3.1.0 | (c) jQuery Foundation | jquery.org/license */
+!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.0",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null!=a?a<0?this[a+this.length]:this[a]:f.call(this)},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"label"in b&&b.disabled===a||"form"in b&&b.disabled===a||"form"in b&&b.disabled===!1&&(b.isDisabled===a||b.isDisabled!==!a&&("label"in b||!ea(b))!==a)}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML=" ",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML=" ";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(_,aa),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=V.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(_,aa),$.test(j[0].type)&&qa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&sa(j),!a)return G.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||$.test(a)&&qa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML=" ","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML=" ",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){if(r.isFunction(b))return r.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return r.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(C.test(b))return r.filter(b,a,c);b=r.filter(b,a)}return r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType})}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/\S+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,
+r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ja=/^$|\/(?:java|ecma)script/i,ka={option:[1,""," "],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};ka.optgroup=ka.option,ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead,ka.th=ka.td;function la(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function ma(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=la(l.appendChild(f),"script"),j&&ma(g),c){k=0;while(f=g[k++])ja.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var pa=d.documentElement,qa=/^key/,ra=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,sa=/^([^.]*)(?:\.(.+)|)/;function ta(){return!0}function ua(){return!1}function va(){try{return d.activeElement}catch(a){}}function wa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)wa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ua;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(pa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c-1:r.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h\x20\t\r\n\f]*)[^>]*)\/>/gi,ya=/This is a p