├── src ├── main │ ├── resources │ │ ├── system.properties │ │ ├── mongodb.properties │ │ ├── log4j.properties │ │ ├── servlet-context.xml │ │ └── service-context.xml │ ├── webapp │ │ ├── skin │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ └── glyphicons-halflings-regular.woff │ │ │ ├── css │ │ │ │ ├── signin.css │ │ │ │ ├── home.css │ │ │ │ ├── bootstrap-theme.min.css │ │ │ │ └── bootstrap-theme.css │ │ │ └── js │ │ │ │ ├── jquery.cookie.js │ │ │ │ ├── home.js │ │ │ │ ├── bootstrap-maxlength.min.js │ │ │ │ ├── bootbox.min.js │ │ │ │ ├── bootstrap-maxlength.js │ │ │ │ ├── holder.js │ │ │ │ └── bootstrap.min.js │ │ ├── pages │ │ │ ├── common │ │ │ │ └── meta.html │ │ │ ├── twibo.html │ │ │ ├── register.html │ │ │ ├── signin.html │ │ │ ├── test.html │ │ │ ├── index.html │ │ │ ├── fans.html │ │ │ ├── follow.html │ │ │ └── home.html │ │ └── WEB-INF │ │ │ └── web.xml │ └── java │ │ └── com │ │ └── github │ │ └── watermelon │ │ ├── core │ │ ├── vo │ │ │ ├── SysDefinition.java │ │ │ └── ResultVO.java │ │ ├── entity │ │ │ └── BaseEntity.java │ │ ├── ext │ │ │ ├── MorphiaFactoryBean.java │ │ │ ├── DataStoreFactoryBean.java │ │ │ └── MongoFactoryBean.java │ │ ├── service │ │ │ └── MongoBaseService.java │ │ └── util │ │ │ └── PasswordEncoderUtil.java │ │ └── module │ │ ├── common │ │ ├── entity │ │ │ ├── Signature.java │ │ │ └── MUser.java │ │ ├── dao │ │ │ └── MUserDAO.java │ │ ├── interceptor │ │ │ └── UserInterceptor.java │ │ ├── web │ │ │ ├── IndexController.java │ │ │ └── MUserController.java │ │ └── service │ │ │ └── MUserService.java │ │ └── weibo │ │ ├── dao │ │ ├── TwiboDAO.java │ │ └── TwicommentDAO.java │ │ ├── entity │ │ ├── Twicomment.java │ │ └── Twibo.java │ │ ├── service │ │ └── TwiboService.java │ │ └── web │ │ └── TwiboController.java └── test │ └── resources │ └── MongoBaseServiceImpl.java.txt ├── .gitignore ├── README.md ├── pom.xml └── LICENSE /src/main/resources/system.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings/ 3 | .classpath 4 | .project 5 | -------------------------------------------------------------------------------- /src/main/resources/mongodb.properties: -------------------------------------------------------------------------------- 1 | #mongodb 2 | mongo.db.host=localhost:27017 3 | mongo.db.databaseName=watermelon -------------------------------------------------------------------------------- /src/main/webapp/skin/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdsiyan/watermelon/HEAD/src/main/webapp/skin/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/main/webapp/skin/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdsiyan/watermelon/HEAD/src/main/webapp/skin/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/main/webapp/skin/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zdsiyan/watermelon/HEAD/src/main/webapp/skin/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Watermelon开源项目 2 | === 3 | 基于 maven + spring + spring mvc + mongodb + json + morphia 框架 4 | 5 | 6 | # 填坑 7 | 8 | 近期准备开一个互联网项目,正好拿这个项目来练手。不打tag了,直接在现有版本上升级。 9 | 更新计划将在wiki中填写 10 | 11 | 12 | # 参考 13 | 14 | ## morphia API http://mongodb.github.io/morphia/1.1/javadoc/ -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/core/vo/SysDefinition.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.core.vo; 2 | 3 | public class SysDefinition { 4 | 5 | public static final String USER_SESSION_KEY = "USER_SESSION"; 6 | public static Integer CODE_SUCCESS =10000; 7 | public static Integer CODE_ERROR =11000; 8 | public static Integer CODE_NODATA =11001; 9 | 10 | public static final String COOKIE_SESSIONID_KEY="SESSIONID"; 11 | public static final String COOKIE_LOGINID_KEY="LOGINID"; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/core/entity/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.core.entity; 2 | 3 | import org.bson.types.ObjectId; 4 | import org.mongodb.morphia.annotations.Id; 5 | 6 | import com.alibaba.fastjson.annotation.JSONField; 7 | 8 | public abstract class BaseEntity { 9 | @Id 10 | @JSONField(serialize=false) 11 | private ObjectId oid; 12 | 13 | public ObjectId getOid() { 14 | return oid; 15 | } 16 | 17 | public void setOid(ObjectId oid) { 18 | this.oid = oid; 19 | } 20 | 21 | public String get_id(){ 22 | return oid.toStringMongod(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/common/entity/Signature.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.common.entity; 2 | 3 | import org.mongodb.morphia.annotations.Embedded; 4 | 5 | @Embedded 6 | public class Signature { 7 | 8 | private String content; 9 | private Long timestamp; 10 | 11 | public String getContent() { 12 | return content; 13 | } 14 | public void setContent(String content) { 15 | this.content = content; 16 | } 17 | public Long getTimestamp() { 18 | return timestamp; 19 | } 20 | public void setTimestamp(Long timestamp) { 21 | this.timestamp = timestamp; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/weibo/dao/TwiboDAO.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.weibo.dao; 2 | 3 | import org.bson.types.ObjectId; 4 | import org.mongodb.morphia.Datastore; 5 | import org.mongodb.morphia.dao.BasicDAO; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import com.github.watermelon.module.weibo.entity.Twibo; 10 | 11 | @Repository 12 | public class TwiboDAO extends BasicDAO{ 13 | 14 | @Autowired 15 | protected TwiboDAO(Datastore datastore) { 16 | super(datastore); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/common/dao/MUserDAO.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.common.dao; 2 | 3 | import org.bson.types.ObjectId; 4 | import org.mongodb.morphia.Datastore; 5 | import org.mongodb.morphia.dao.BasicDAO; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import com.github.watermelon.module.common.entity.MUser; 10 | 11 | @Repository 12 | public class MUserDAO extends BasicDAO { 13 | 14 | @Autowired 15 | protected MUserDAO(Datastore datastore) { 16 | super(datastore); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/weibo/dao/TwicommentDAO.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.weibo.dao; 2 | 3 | import org.bson.types.ObjectId; 4 | import org.mongodb.morphia.Datastore; 5 | import org.mongodb.morphia.dao.BasicDAO; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import com.github.watermelon.module.weibo.entity.Twicomment; 10 | 11 | @Repository 12 | public class TwicommentDAO extends BasicDAO{ 13 | 14 | @Autowired 15 | protected TwicommentDAO(Datastore datastore) { 16 | super(datastore); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/webapp/pages/common/meta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <#assign ctx=(rc.getContextPath())!''> 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Output pattern : date [thread] priority category - message 2 | log4j.rootLogger=INFO, console, logFile 3 | 4 | #Console 5 | log4j.appender.console=org.apache.log4j.ConsoleAppender 6 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.console.layout.ConversionPattern=%d %p [%c] - <%m>%n 8 | 9 | #RollingFile 10 | log4j.appender.logFile=org.apache.log4j.DailyRollingFileAppender 11 | log4j.appender.logFile.File=../logs/watermelon.log 12 | log4j.appender.logFile.layout=org.apache.log4j.PatternLayout 13 | log4j.appender.logFile.layout.ConversionPattern=%d %p [%c] - <%m>%n 14 | 15 | #log4j.logger.com.bolo.examples=debug 16 | log4j.logger.java.sql=debug -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/core/vo/ResultVO.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.core.vo; 2 | 3 | public class ResultVO { 4 | private Integer status; 5 | private String message; 6 | private T result; 7 | 8 | public ResultVO(Integer status, String message, T result){ 9 | this.setStatus(status); 10 | this.setMessage(message); 11 | this.setResult(result); 12 | } 13 | 14 | public Integer getStatus() { 15 | return status; 16 | } 17 | 18 | public void setStatus(Integer status) { 19 | this.status = status; 20 | } 21 | 22 | public String getMessage() { 23 | return message; 24 | } 25 | 26 | public void setMessage(String error) { 27 | this.message = error; 28 | } 29 | 30 | public T getResult() { 31 | return result; 32 | } 33 | 34 | public void setResult(T result) { 35 | this.result = result; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/common/interceptor/UserInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.common.interceptor; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | 6 | import org.springframework.web.servlet.ModelAndView; 7 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 8 | 9 | import com.github.watermelon.core.vo.SysDefinition; 10 | import com.github.watermelon.module.common.entity.MUser; 11 | 12 | /** 13 | * 登录状态检查,临时处理,以后调整 14 | * @author admin 15 | */ 16 | public class UserInterceptor extends HandlerInterceptorAdapter{ 17 | 18 | @Override 19 | public void postHandle(HttpServletRequest request, HttpServletResponse response, 20 | Object obj, ModelAndView mav) throws Exception { 21 | MUser result= (MUser)(request.getSession()).getAttribute(SysDefinition.USER_SESSION_KEY); 22 | if(result==null){ 23 | mav.setViewName("redirect:/signin"); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/webapp/skin/css/signin.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 40px; 3 | padding-bottom: 40px; 4 | background-color: #eee; 5 | } 6 | 7 | .form-signin { 8 | max-width: 330px; 9 | padding: 15px; 10 | margin: 0 auto; 11 | } 12 | .form-signin .form-signin-heading, 13 | .form-signin .checkbox { 14 | margin-bottom: 10px; 15 | } 16 | .form-signin .checkbox { 17 | font-weight: normal; 18 | } 19 | .form-signin .form-control { 20 | position: relative; 21 | font-size: 16px; 22 | height: auto; 23 | padding: 10px; 24 | -webkit-box-sizing: border-box; 25 | -moz-box-sizing: border-box; 26 | box-sizing: border-box; 27 | } 28 | .form-signin .form-control:focus { 29 | z-index: 2; 30 | } 31 | .form-signin input[type="text"] { 32 | margin-bottom: -1px; 33 | border-bottom-left-radius: 0; 34 | border-bottom-right-radius: 0; 35 | } 36 | .form-signin input[type="password"] { 37 | margin-bottom: 10px; 38 | border-top-left-radius: 0; 39 | border-top-right-radius: 0; 40 | } -------------------------------------------------------------------------------- /src/main/webapp/pages/twibo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test Page 5 | <#include "/common/meta.html"/> 6 | 7 | 8 |

post twibo

9 |
10 | Content: 11 | 13 |

14 |
15 |
16 |

objectid get

17 |
18 |

ObjectId:

19 |

20 |
21 |

twibo list

22 |
23 |

offset:

24 |

length:

25 |

26 |
27 | 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/weibo/entity/Twicomment.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.weibo.entity; 2 | 3 | import org.mongodb.morphia.annotations.Entity; 4 | import org.mongodb.morphia.annotations.Reference; 5 | 6 | import com.github.watermelon.core.entity.BaseEntity; 7 | import com.github.watermelon.module.common.entity.MUser; 8 | 9 | /** 10 | * 相当于评论 11 | * @author bruce 12 | */ 13 | @Entity("twicomment") 14 | public class Twicomment extends BaseEntity{ 15 | @Reference 16 | private Twibo twibo; 17 | 18 | private String content; 19 | private Long timestamp; 20 | // 发表评论的用户 21 | @Reference 22 | private MUser user; 23 | 24 | public Twibo getTwibo() { 25 | return twibo; 26 | } 27 | public void setTwibo(Twibo twibo) { 28 | this.twibo = twibo; 29 | } 30 | public String getContent() { 31 | return content; 32 | } 33 | public void setContent(String content) { 34 | this.content = content; 35 | } 36 | public Long getTimestamp() { 37 | return timestamp; 38 | } 39 | public void setTimestamp(Long timestamp) { 40 | this.timestamp = timestamp; 41 | } 42 | 43 | public MUser getUser() { 44 | return user; 45 | } 46 | public void setUser(MUser user) { 47 | this.user = user; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/common/web/IndexController.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.common.web; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpSession; 5 | 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.servlet.ModelAndView; 9 | 10 | import com.github.watermelon.core.vo.SysDefinition; 11 | import com.github.watermelon.module.common.entity.MUser; 12 | 13 | /** 14 | * @author Bruce 15 | */ 16 | @Controller 17 | public class IndexController { 18 | @RequestMapping("/") 19 | public String index(HttpSession session, HttpServletRequest request){ 20 | System.out.println("index"); 21 | request.getCookies(); 22 | return "/index"; 23 | } 24 | 25 | @RequestMapping("/test") 26 | public String test(){ 27 | System.out.println("test"); 28 | return "/test"; 29 | } 30 | 31 | @RequestMapping("/signin") 32 | public String signin(){ 33 | return "/signin"; 34 | } 35 | 36 | @RequestMapping("/register") 37 | public String register(){ 38 | return "/register"; 39 | } 40 | 41 | @RequestMapping("/home") 42 | public ModelAndView home(HttpSession session){ 43 | ModelAndView view = new ModelAndView("/home"); 44 | MUser muser= (MUser)session.getAttribute(SysDefinition.USER_SESSION_KEY); 45 | if(muser!=null){ 46 | view.addObject("user",muser); 47 | } 48 | return view; 49 | } 50 | 51 | @RequestMapping("/twibo") 52 | public String twibo(){ 53 | System.out.println("twibo"); 54 | return "/twibo"; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/webapp/pages/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 注册账号 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 31 | 32 |
33 | 34 | 35 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/main/webapp/pages/signin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 登陆 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 32 | 33 |
34 | 35 | 36 | 37 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/core/ext/MorphiaFactoryBean.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.core.ext; 2 | 3 | import org.mongodb.morphia.Morphia; 4 | import org.springframework.beans.factory.config.AbstractFactoryBean; 5 | 6 | public class MorphiaFactoryBean extends AbstractFactoryBean { 7 | /** 8 | * 要扫描并映射的包 9 | */ 10 | private String[] mapPackages; 11 | 12 | /** 13 | * 要映射的类 14 | */ 15 | private String[] mapClasses; 16 | 17 | /** 18 | * 扫描包时,是否忽略不映射的类 这里按照Morphia的原始定义,默认设为false 19 | */ 20 | private boolean ignoreInvalidClasses; 21 | 22 | @Override 23 | protected Morphia createInstance() throws Exception { 24 | Morphia m = new Morphia(); 25 | if (mapPackages != null) { 26 | for (String packageName : mapPackages) { 27 | m.mapPackage(packageName, ignoreInvalidClasses); 28 | } 29 | } 30 | if (mapClasses != null) { 31 | for (String entityClass : mapClasses) { 32 | m.map(Class.forName(entityClass)); 33 | } 34 | } 35 | return m; 36 | } 37 | 38 | @Override 39 | public Class getObjectType() { 40 | return Morphia.class; 41 | } 42 | 43 | public String[] getMapPackages() { 44 | return mapPackages; 45 | } 46 | 47 | public void setMapPackages(String[] mapPackages) { 48 | this.mapPackages = mapPackages; 49 | } 50 | 51 | public String[] getMapClasses() { 52 | return mapClasses; 53 | } 54 | 55 | public void setMapClasses(String[] mapClasses) { 56 | this.mapClasses = mapClasses; 57 | } 58 | 59 | public boolean isIgnoreInvalidClasses() { 60 | return ignoreInvalidClasses; 61 | } 62 | 63 | public void setIgnoreInvalidClasses(boolean ignoreInvalidClasses) { 64 | this.ignoreInvalidClasses = ignoreInvalidClasses; 65 | } 66 | 67 | /*----------------------setters-----------------------*/ 68 | } -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/weibo/entity/Twibo.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.weibo.entity; 2 | 3 | import org.mongodb.morphia.annotations.Entity; 4 | import org.mongodb.morphia.annotations.Reference; 5 | 6 | import com.github.watermelon.core.entity.BaseEntity; 7 | import com.github.watermelon.module.common.entity.MUser; 8 | 9 | /** 10 | * 随便起的名字, 相当于微博 11 | * @author bruce 12 | */ 13 | @Entity("twibo") 14 | public class Twibo extends BaseEntity { 15 | //发布的内容 16 | private String content; 17 | //时间戳 18 | private Long timestamp; 19 | //标识 0:删除 1:正常 2:隐藏只有自己可见 3:群组可见 20 | private Integer status=0; 21 | //发布的用户 22 | @Reference 23 | private MUser user; 24 | //转发的原始微博,可为空 25 | @Reference 26 | private Twibo original; 27 | //评论计数器 28 | private Integer commentSize; 29 | 30 | public String getContent() { 31 | return content; 32 | } 33 | public void setContent(String content) { 34 | this.content = content; 35 | } 36 | public Integer getStatus() { 37 | return status; 38 | } 39 | public void setStatus(Integer status) { 40 | this.status = status; 41 | } 42 | public MUser getUser() { 43 | return user; 44 | } 45 | public void setUser(MUser user) { 46 | this.user = user; 47 | } 48 | public Twibo getOriginal() { 49 | return original; 50 | } 51 | public void setOriginal(Twibo original) { 52 | this.original = original; 53 | } 54 | public Long getTimestamp() { 55 | return timestamp; 56 | } 57 | public void setTimestamp(Long timestamp) { 58 | this.timestamp = timestamp; 59 | } 60 | public Integer getCommentSize() { 61 | if(commentSize!=null) 62 | return commentSize; 63 | return 0; 64 | } 65 | public void setCommentSize(Integer commentSize) { 66 | this.commentSize = commentSize; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | watermelon 8 | 9 | 10 | contextConfigLocation 11 | classpath*:service-context.xml 12 | 13 | 14 | 15 | encodingFilter 16 | org.springframework.web.filter.CharacterEncodingFilter 17 | 18 | encoding 19 | UTF-8 20 | 21 | 22 | forceEncoding 23 | true 24 | 25 | 26 | 27 | 28 | encodingFilter 29 | /* 30 | 31 | 32 | 33 | 30 34 | 35 | 36 | 37 | 38 | org.springframework.web.context.ContextLoaderListener 39 | 40 | 41 | 42 | 43 | watermelon 44 | org.springframework.web.servlet.DispatcherServlet 45 | 46 | contextConfigLocation 47 | classpath*:servlet-context.xml 48 | 49 | 1 50 | 51 | 52 | 53 | watermelon 54 | / 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/main/webapp/pages/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Page Title 5 | <#include "/common/meta.html"/> 6 | 7 | 8 |

register

9 |
10 |

username:

11 |

password:

12 |

nickname:

13 |

email:

14 |

15 |
16 |
17 |

login

18 |
19 |

username:

20 |

password:

21 |

22 |
23 |
24 |

logout

25 |
26 |

get

27 |
28 |

username:

29 |

30 |
31 |
32 |

delete

33 |
34 |

password:

35 |

36 |
37 |

signature

38 |
39 | Content: 40 | 42 |

43 |
44 | 49 | 50 | -------------------------------------------------------------------------------- /src/test/resources/MongoBaseServiceImpl.java.txt: -------------------------------------------------------------------------------- 1 | package com.mongodb.services; 2 | 3 | import com.google.code.morphia.Datastore; 4 | import com.google.code.morphia.Key; 5 | import com.google.code.morphia.dao.DAO; 6 | import com.google.code.morphia.query.Query; 7 | import com.google.code.morphia.query.QueryResults; 8 | 9 | /** 10 | * 11 | */ 12 | public class MongoBaseServiceImpl implements IMongoBaseService { 13 | 14 | private DAO baseDao; 15 | 16 | public long count() { 17 | return baseDao.count(); 18 | } 19 | 20 | public long count(Query q) { 21 | return baseDao.count(q); 22 | } 23 | 24 | public long count(String key, Object value) { 25 | return baseDao.count(key, value); 26 | } 27 | 28 | public void delete(T entity) { 29 | baseDao.delete(entity); 30 | } 31 | 32 | public void deleteById(K id) { 33 | baseDao.deleteById(id); 34 | } 35 | 36 | public void deleteByQuery(Query q) { 37 | baseDao.deleteByQuery(q); 38 | } 39 | 40 | public boolean exists(Query q) { 41 | return baseDao.exists(q); 42 | } 43 | 44 | public boolean exists(String key, Object value) { 45 | return baseDao.exists(key, value); 46 | } 47 | 48 | public QueryResults find() { 49 | return baseDao.find(); 50 | } 51 | 52 | public QueryResults find(Query q) { 53 | return baseDao.find(q); 54 | } 55 | 56 | public T findOne(Query q) { 57 | return (T) baseDao.findOne(q); 58 | } 59 | 60 | public T findOne(String key, Object value) { 61 | return (T) baseDao.findOne(key, value); 62 | } 63 | 64 | public Key save(T entity) { 65 | return baseDao.save(entity); 66 | } 67 | 68 | public DAO getBaseDao() { 69 | return baseDao; 70 | } 71 | 72 | public void setBaseDao(DAO baseDao) { 73 | this.baseDao = baseDao; 74 | } 75 | 76 | public Datastore getDatastore() { 77 | return baseDao.getDatastore(); 78 | } 79 | 80 | public T get(K id) { 81 | return (T) baseDao.get(id); 82 | } 83 | 84 | public Query createQuery() { 85 | return baseDao.createQuery(); 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/core/service/MongoBaseService.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.core.service; 2 | 3 | 4 | import org.mongodb.morphia.Datastore; 5 | import org.mongodb.morphia.Key; 6 | import org.mongodb.morphia.dao.DAO; 7 | import org.mongodb.morphia.query.Query; 8 | import org.mongodb.morphia.query.QueryResults; 9 | 10 | /** 11 | * 12 | */ 13 | public class MongoBaseService { 14 | 15 | protected DAO baseDao; 16 | 17 | public long count() { 18 | return baseDao.count(); 19 | } 20 | 21 | public long count(Query q) { 22 | return baseDao.count(q); 23 | } 24 | 25 | public long count(String key, Object value) { 26 | return baseDao.count(key, value); 27 | } 28 | 29 | public void delete(T entity) { 30 | baseDao.delete(entity); 31 | } 32 | 33 | public void deleteById(K id) { 34 | baseDao.deleteById(id); 35 | } 36 | 37 | public void deleteByQuery(Query q) { 38 | baseDao.deleteByQuery(q); 39 | } 40 | 41 | public boolean exists(Query q) { 42 | return baseDao.exists(q); 43 | } 44 | 45 | public boolean exists(String key, Object value) { 46 | return baseDao.exists(key, value); 47 | } 48 | 49 | public QueryResults find() { 50 | return baseDao.find(); 51 | } 52 | 53 | public QueryResults find(Query q) { 54 | return baseDao.find(q); 55 | } 56 | 57 | public T findOne(Query q) { 58 | return (T) baseDao.findOne(q); 59 | } 60 | 61 | public T findOne(String key, Object value) { 62 | return (T) baseDao.findOne(key, value); 63 | } 64 | 65 | public Key save(T entity) { 66 | return baseDao.save(entity); 67 | } 68 | 69 | public DAO getBaseDao() { 70 | return baseDao; 71 | } 72 | 73 | public void setBaseDao(DAO baseDao) { 74 | this.baseDao = baseDao; 75 | } 76 | 77 | public Datastore getDatastore() { 78 | return baseDao.getDatastore(); 79 | } 80 | 81 | public T get(K id) { 82 | return (T) baseDao.get(id); 83 | } 84 | 85 | public Query createQuery() { 86 | return baseDao.createQuery(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/webapp/skin/css/home.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles 2 | -------------------------------------------------- */ 3 | 4 | html, 5 | body { 6 | height: 100%; 7 | /* The html and body elements cannot have any padding or margin. */ 8 | } 9 | 10 | /* Wrapper for page content to push down footer */ 11 | #wrap { 12 | min-height: 100%; 13 | height: auto !important; 14 | height: 100%; 15 | /* Negative indent footer by its height */ 16 | margin: 0 auto -120px; 17 | /* Pad bottom by footer height */ 18 | padding: 0 0 120px; 19 | } 20 | 21 | /* Set the fixed height of the footer here */ 22 | #footer { 23 | height: 240px; 24 | background-color: #6CA6CD; 25 | font-size: 16px; 26 | color:#2E2E2E; 27 | text-shadow: 0 0px 1px #7A7A7A; 28 | } 29 | 30 | /* for min footer */ 31 | @media (min-width: 768px){ 32 | #footer{height: 120px;} 33 | } 34 | 35 | 36 | 37 | /* Custom page CSS 38 | -------------------------------------------------- */ 39 | /* Not required for template or sticky footer method. */ 40 | 41 | #wrap > .container { 42 | padding: 60px 15px 0; 43 | } 44 | 45 | .container .credit { 46 | margin: 20px 0; 47 | } 48 | 49 | #footer > .container { 50 | padding-left: 15px; 51 | padding-right: 15px; 52 | } 53 | 54 | code { 55 | font-size: 80%; 56 | } 57 | 58 | .name-header{ 59 | text-shadow: 0 1px 1px #123; 60 | } 61 | 62 | #twibo{ 63 | border:1px solid; 64 | } 65 | 66 | .twibo-split-hr{ 67 | color:#F48C12; 68 | background:#fffde0; 69 | border:2px solid #f9f2a7; 70 | border-radius:25px; 71 | -webkit-border-radius:25px; 72 | -moz-border-radius:25px; 73 | } 74 | 75 | .twibo-bk,.comment-bk{ 76 | position:relative; 77 | font-size: 12px; 78 | margin: 5px 5px 5px 5px; 79 | padding: 2px 2px 2px 2px; 80 | border-bottom: 1px solid #ddd; 81 | } 82 | .twibo-line{ 83 | font-size: 8px; 84 | } 85 | .twibo-time{ 86 | color: #9da0a4; 87 | } 88 | .twibo-link{ 89 | } 90 | .twibo-info,.comment-info{ 91 | font-size: 14px; 92 | font-weight: bold; 93 | line-height: 16px; 94 | margin:2px; 95 | color:#333333; 96 | } 97 | 98 | .footer-font{ 99 | color:white; 100 | } -------------------------------------------------------------------------------- /src/main/webapp/pages/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 大西瓜 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 49 | 50 | 51 |
52 |
53 |

大西瓜社交

54 |

大西瓜开源社交, 欢迎热爱开源的开发人员加入

55 |
56 |
57 |
58 | 59 | 64 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/main/webapp/skin/js/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.3.1 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2013 Klaus Hartl 6 | * Released under the MIT license 7 | */ 8 | (function (factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | // AMD. Register as anonymous module. 11 | define(['jquery'], factory); 12 | } else { 13 | // Browser globals. 14 | factory(jQuery); 15 | } 16 | }(function ($) { 17 | 18 | var pluses = /\+/g; 19 | 20 | function decode(s) { 21 | if (config.raw) { 22 | return s; 23 | } 24 | return decodeURIComponent(s.replace(pluses, ' ')); 25 | } 26 | 27 | function decodeAndParse(s) { 28 | if (s.indexOf('"') === 0) { 29 | // This is a quoted cookie as according to RFC2068, unescape... 30 | s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); 31 | } 32 | 33 | s = decode(s); 34 | 35 | try { 36 | return config.json ? JSON.parse(s) : s; 37 | } catch(e) {} 38 | } 39 | 40 | var config = $.cookie = function (key, value, options) { 41 | 42 | // Write 43 | if (value !== undefined) { 44 | options = $.extend({}, config.defaults, options); 45 | 46 | if (typeof options.expires === 'number') { 47 | var days = options.expires, t = options.expires = new Date(); 48 | t.setDate(t.getDate() + days); 49 | } 50 | 51 | value = config.json ? JSON.stringify(value) : String(value); 52 | 53 | return (document.cookie = [ 54 | config.raw ? key : encodeURIComponent(key), 55 | '=', 56 | config.raw ? value : encodeURIComponent(value), 57 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 58 | options.path ? '; path=' + options.path : '', 59 | options.domain ? '; domain=' + options.domain : '', 60 | options.secure ? '; secure' : '' 61 | ].join('')); 62 | } 63 | 64 | // Read 65 | var cookies = document.cookie.split('; '); 66 | var result = key ? undefined : {}; 67 | for (var i = 0, l = cookies.length; i < l; i++) { 68 | var parts = cookies[i].split('='); 69 | var name = decode(parts.shift()); 70 | var cookie = parts.join('='); 71 | 72 | if (key && key === name) { 73 | result = decodeAndParse(cookie); 74 | break; 75 | } 76 | 77 | if (!key) { 78 | result[name] = decodeAndParse(cookie); 79 | } 80 | } 81 | 82 | return result; 83 | }; 84 | 85 | config.defaults = {}; 86 | 87 | $.removeCookie = function (key, options) { 88 | if ($.cookie(key) !== undefined) { 89 | // Must not alter options, thus extending a fresh object... 90 | $.cookie(key, '', $.extend({}, options, { expires: -1 })); 91 | return true; 92 | } 93 | return false; 94 | }; 95 | 96 | })); 97 | -------------------------------------------------------------------------------- /src/main/webapp/skin/js/home.js: -------------------------------------------------------------------------------- 1 | //cookie 记住userid, 但不保存密码 2 | $(document).ready(function(){ 3 | $("#twibo").maxlength(); 4 | $("#sendTwibo").click(sendTwibo); 5 | getTwibo(); 6 | }); 7 | 8 | function showDialog(_id){ 9 | showComment(_id); 10 | bootbox.prompt($("p[name='content"+_id+"']").text(), function(result) { 11 | if (result) { 12 | //发送评论 13 | sendComment(_id,result); 14 | } 15 | }); 16 | } 17 | 18 | function showComment(_id){ 19 | $.post(ctx+"/twibo/comments",{tid:_id,offset:0,length:20}, function(data){ 20 | if(data && data.status==10000){ 21 | var list=data.result; 22 | for(var i=0,len=list.length;i"; 25 | $("div .modal-body").append("
"+list[i].user.nickName+"
"+list[i].content+"

"+timevar+"

"); 26 | } 27 | 28 | } 29 | }); 30 | } 31 | 32 | function sendComment(_id,_content){ 33 | $.post(ctx+"/twibo/post/comment",{tid:_id,content:_content}, function(data){ 34 | if(data && data.status==10000){ 35 | alert("评论成功"); 36 | } 37 | }); 38 | } 39 | 40 | 41 | function sendTwibo(){ 42 | $.post(ctx+"/twibo/post/twibo",{content:$("#twibo").val()},function(data){ 43 | //发送成功 44 | if(data && data.status==10000){ 45 | $("#twibo").val(""); 46 | var twiSize=parseInt($("#twiSize").text()); 47 | $("#twiSize").text(++twiSize); 48 | } 49 | }); 50 | } 51 | 52 | function getTwibo(){ 53 | //默认显示20条 54 | $.post(ctx+"/twibo/list",{offset:0,length:20},function(data){ 55 | if(data && data.status==10000){ 56 | var list=data.result; 57 | for(var i=0,len=list.length;i"; 61 | }else{ 62 | headvar="
"+list[i].user.nickName+"
"; 63 | } 64 | var timevar="
"+d.toLocaleString()+"
"; 65 | 66 | var link=""; 67 | $("#twibo-pool").append("
"+headvar+"

"+list[i].content+"

"+link+timevar+"

"); 68 | } 69 | } 70 | }); 71 | } -------------------------------------------------------------------------------- /src/main/webapp/skin/js/bootstrap-maxlength.min.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * bootstrap-maxlength.js v1.4.1 3 | * 4 | * Copyright (c) 2013 Maurizio Napoleoni; 5 | * 6 | * Licensed under the terms of the MIT license. 7 | * See: https://github.com/mimo84/bootstrap-maxlength/blob/master/LICENSE 8 | * ========================================================== */ 9 | (function(f){f.fn.extend({maxlength:function(d,p){function l(b){var c=b.val().match(/\n/g),c=c?c.length:0;return b.val().length+c}function q(b,c){var a="";d.preText&&(a+=d.preText);a=d.showCharsTyped?a+c:a+(b-l(c));d.showMaxLength&&(a+=d.separator+b);d.postText&&(a+=d.postText);return a}function r(b,c,a,e){e.html(q(a,a-b));if(0b&&(g=!1);g?e.removeClass(d.limitReachedClass).addClass(d.warningClass).css({display:"block"}):e.css({display:"none"})}else e.removeClass(d.warningClass).addClass(d.limitReachedClass).css({display:"block"})} 10 | function m(b,c){var a;a=b[0];a=f.extend({},"function"===typeof a.getBoundingClientRect?a.getBoundingClientRect():{width:a.offsetWidth,height:a.offsetHeight},b.offset());var e=b.outerWidth(),g=c.outerWidth(),n=c.width(),h=c.height();switch(d.placement){case "bottom":c.css({top:a.top+a.height,left:a.left+a.width/2-n/2});break;case "top":c.css({top:a.top-h,left:a.left+a.width/2-n/2});break;case "left":c.css({top:a.top+a.height/2-h/2,left:a.left-n});break;case "right":c.css({top:a.top+a.height/2-h/2, 11 | left:a.left+a.width});break;case "bottom-right":c.css({top:a.top+a.height,left:a.left+a.width});break;case "top-right":c.css({top:a.top-h,left:a.left+e});break;case "top-left":c.css({top:a.top-h,left:a.left-g});break;case "bottom-left":c.css({top:a.top+b.outerHeight(),left:a.left-g});break;case "centered-right":c.css({top:a.top+h/2,left:a.left+e-g-3})}}function k(b){return b.attr("maxlength")||b.attr("size")}var s=f("body");f.isFunction(d)&&!p&&(p=d,d={});d=f.extend({alwaysShow:!1,threshold:10,warningClass:"badge badge-success", 12 | limitReachedClass:"badge badge-important",separator:" / ",preText:"",postText:"",showMaxLength:!0,placement:"bottom",showCharsTyped:!0,validate:!1},d);return this.each(function(){var b=f(this),c=k(b),a=f("").css({display:"none",position:"absolute",whiteSpace:"nowrap",zIndex:999}).html(q(c,"0"));b.is("textarea")&&(b.data("maxlenghtsizex",b.outerWidth()),b.data("maxlenghtsizey",b.outerHeight()),b.mouseup(function(){b.outerWidth()===b.data("maxlenghtsizex")&&b.outerHeight()===b.data("maxlenghtsizey")|| 13 | m(b,a);b.data("maxlenghtsizex",b.outerWidth());b.data("maxlenghtsizey",b.outerHeight())}));s.append(a);b.focus(function(){var d;d=k(b)-l(b);r(d,b,c,a);m(b,a)});f(window).resize(function(){m(b,a)});b.blur(function(){a.css("display","none")});b.keyup(function(e){var g;g=k(b)-l(b);var f=!0;e=e.keyCode||e.which;0===g&&9===e&&b.attr("maxlength",k(b)+1).trigger({type:"keypress",which:9}).attr("maxlength",k(b)-1);d.validate&&0>g?f=!1:r(g,b,c,a);return f})})}})})(jQuery); -------------------------------------------------------------------------------- /src/main/webapp/pages/fans.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 粉丝 - 大西瓜 11 | 12 | <#assign ctx=(rc.getContextPath())!''> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | 49 | 50 | 51 |
52 |
53 | <#if fans?has_content> 54 | <#list fans as u> 55 |
56 |

${u.nickName}

57 |

${u.signature.content}

58 |
59 | 60 | 61 | 62 |
63 |
64 |
65 | 66 | 71 | 72 | 74 | 75 | 76 | 77 | 78 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/core/ext/DataStoreFactoryBean.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.core.ext; 2 | 3 | import org.mongodb.morphia.Datastore; 4 | import org.mongodb.morphia.Morphia; 5 | import org.springframework.beans.factory.config.AbstractFactoryBean; 6 | 7 | import com.mongodb.MongoClient; 8 | 9 | /** 10 | * @author Gabriel 11 | */ 12 | public class DataStoreFactoryBean extends AbstractFactoryBean { 13 | 14 | private Morphia morphia; //morphia实例,最好是单例 15 | //private Mongo mongo; //mongo实例,最好是单例 16 | private String dbName; //数据库名 17 | private String username; //用户名,可为空 18 | private String password; //密码,可为空 19 | private boolean toEnsureIndexes=false; //是否确认索引存在,默认false 20 | private boolean toEnsureCaps=false; //是否确认caps存在,默认false 21 | 22 | 23 | @Override 24 | protected Datastore createInstance() throws Exception { 25 | //这里的username和password可以为null,morphia对象会去处理 26 | Datastore ds = morphia.createDatastore(new MongoClient(), dbName); 27 | if(toEnsureIndexes){ 28 | ds.ensureIndexes(); 29 | } 30 | if(toEnsureCaps){ 31 | ds.ensureCaps(); 32 | } 33 | return ds; 34 | } 35 | 36 | @Override 37 | public Class getObjectType() { 38 | return Datastore.class; 39 | } 40 | 41 | @Override 42 | public void afterPropertiesSet() throws Exception { 43 | super.afterPropertiesSet(); 44 | /* if (mongo == null) { 45 | throw new IllegalStateException("mongo is not set"); 46 | }*/ 47 | if (morphia == null) { 48 | throw new IllegalStateException("morphia is not set"); 49 | } 50 | } 51 | 52 | /*----------------------setters-----------------------*/ 53 | 54 | public Morphia getMorphia() { 55 | return morphia; 56 | } 57 | 58 | public void setMorphia(Morphia morphia) { 59 | this.morphia = morphia; 60 | } 61 | 62 | /*public Mongo getMongo() { 63 | return mongo; 64 | } 65 | 66 | public void setMongo(Mongo mongo) { 67 | this.mongo = mongo; 68 | }*/ 69 | 70 | public String getDbName() { 71 | return dbName; 72 | } 73 | 74 | public void setDbName(String dbName) { 75 | this.dbName = dbName; 76 | } 77 | 78 | public String getUsername() { 79 | return username; 80 | } 81 | 82 | public void setUsername(String username) { 83 | this.username = username; 84 | } 85 | 86 | public String getPassword() { 87 | return password; 88 | } 89 | 90 | public void setPassword(String password) { 91 | this.password = password; 92 | } 93 | 94 | public boolean isToEnsureIndexes() { 95 | return toEnsureIndexes; 96 | } 97 | 98 | public void setToEnsureIndexes(boolean toEnsureIndexes) { 99 | this.toEnsureIndexes = toEnsureIndexes; 100 | } 101 | 102 | public boolean isToEnsureCaps() { 103 | return toEnsureCaps; 104 | } 105 | 106 | public void setToEnsureCaps(boolean toEnsureCaps) { 107 | this.toEnsureCaps = toEnsureCaps; 108 | } 109 | } -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/weibo/service/TwiboService.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.weibo.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.bson.types.ObjectId; 7 | import org.mongodb.morphia.query.Query; 8 | import org.mongodb.morphia.query.QueryResults; 9 | import org.mongodb.morphia.query.UpdateOperations; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Service; 12 | 13 | import com.github.watermelon.core.service.MongoBaseService; 14 | import com.github.watermelon.module.common.dao.MUserDAO; 15 | import com.github.watermelon.module.common.entity.MUser; 16 | import com.github.watermelon.module.weibo.dao.TwiboDAO; 17 | import com.github.watermelon.module.weibo.dao.TwicommentDAO; 18 | import com.github.watermelon.module.weibo.entity.Twibo; 19 | import com.github.watermelon.module.weibo.entity.Twicomment; 20 | 21 | @Service 22 | public class TwiboService extends MongoBaseService { 23 | 24 | 25 | @Autowired 26 | private MUserDAO mUserDAO; 27 | 28 | @Autowired 29 | private TwiboDAO twiboDAO; 30 | 31 | @Autowired 32 | private TwicommentDAO twicommentDAO; 33 | 34 | public TwiboDAO getTwiboDAO() { 35 | return twiboDAO; 36 | } 37 | 38 | public void setTwiboDAO(TwiboDAO twiboDAO) { 39 | this.twiboDAO = twiboDAO; 40 | super.setBaseDao(twiboDAO); 41 | } 42 | 43 | /** 44 | * 发表twibo 45 | * @param content 46 | * @param user 47 | * @return 48 | */ 49 | public Twibo postTwibo(String content, MUser user) { 50 | Twibo result = new Twibo(); 51 | result.setContent(content); 52 | result.setTimestamp(System.currentTimeMillis()); 53 | result.setUser(user); 54 | baseDao.save(result); 55 | 56 | Query query = mUserDAO.createQuery().field("_id").equal(user.get_id()); 57 | UpdateOperations uo = mUserDAO.createUpdateOperations().inc("twiSize"); 58 | mUserDAO.update(query, uo); 59 | 60 | return result; 61 | 62 | } 63 | 64 | /** 65 | * 简单分页 66 | * @param user 67 | * @param offset 68 | * @param length 69 | * @return 70 | */ 71 | public List getList(MUser user, Integer offset, Integer length) { 72 | List lists = new ArrayList(); 73 | lists.add(user); 74 | for(MUser u : user.getFollows()){ 75 | lists.add(u); 76 | } 77 | Query query = baseDao.createQuery(); 78 | query.filter("user in ", lists).offset(offset).limit(length).order("-timestamp"); 79 | QueryResults result = baseDao.find(query); 80 | return result.asList(); 81 | } 82 | 83 | /** 84 | * 发表评论 85 | * @param twibo 86 | * @param user 87 | * @param content 88 | * @return 89 | */ 90 | public Twicomment postComment(String tid, MUser user, String content) { 91 | Twibo twibo = get(new ObjectId(tid)); 92 | //保存评论 93 | Twicomment comment = new Twicomment(); 94 | comment.setTwibo(twibo); 95 | comment.setUser(user); 96 | comment.setContent(content); 97 | comment.setTimestamp(System.currentTimeMillis()); 98 | twicommentDAO.save(comment); 99 | 100 | Query query = twiboDAO.createQuery().field("_id").equal(tid); 101 | UpdateOperations uo = twiboDAO.createUpdateOperations().inc("commentSize"); 102 | twiboDAO.update(query, uo); 103 | 104 | return comment; 105 | } 106 | 107 | public List getComments(String tid, Integer offset, Integer length) { 108 | Twibo twibo=twiboDAO.get(new ObjectId(tid)); 109 | 110 | Query query = twicommentDAO.createQuery(); 111 | query.filter("twibo", twibo).offset(offset).limit(length).order("-timestamp"); 112 | QueryResults result = twicommentDAO.find(query); 113 | return result.asList(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/webapp/pages/follow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 关注 - 大西瓜 11 | 12 | <#assign ctx=(rc.getContextPath())!''> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | 49 | 50 | 51 |
52 |
53 |
54 |
55 | 56 | 57 | 58 | 59 |
60 | 61 |

62 | 我关注的人 63 |

64 |
65 |
66 |
67 | <#if follows?has_content> 68 | <#list follows as u> 69 |
70 |

${u.nickName}

71 |

${u.signatureContent}

72 |
73 | 74 | 75 | 76 |
77 |
78 |
79 | 80 | 85 | 86 | 88 | 89 | 90 | 91 | 92 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/main/resources/servlet-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | Spring-web MVC配置 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | classpath*:system.properties 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 81 | 18000 82 | 83 | zh_CN 84 | 85 | 86 | 87 | 88 | 91 | 92 | 104857600 93 | 94 | 95 | 4096 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/main/resources/service-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | Spring-database配置 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | classpath*:mongodb.properties 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | com.github.watermelon.module.common.entity 69 | com.github.watermelon.module.weibo.entity 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/core/ext/MongoFactoryBean.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.core.ext; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.beans.factory.config.AbstractFactoryBean; 7 | 8 | import com.mongodb.Mongo; 9 | import com.mongodb.MongoOptions; 10 | import com.mongodb.ReadPreference; 11 | import com.mongodb.ServerAddress; 12 | import com.mongodb.WriteConcern; 13 | 14 | /** 15 | * @author Gabriel 16 | */ 17 | public class MongoFactoryBean extends AbstractFactoryBean { 18 | 19 | // 表示服务器列表(主从复制或者分片)的字符串数组 20 | private String[] serverStrings; 21 | // mongoDB配置对象 22 | @SuppressWarnings("deprecation") 23 | private MongoOptions mongoOptions; 24 | // 是否主从分离(读取从库),默认读写都在主库 25 | private boolean readSecondary = false; 26 | // 设定写策略(出错时是否抛异常),默认采用SAFE模式(需要抛异常) 27 | @SuppressWarnings("deprecation") 28 | private WriteConcern writeConcern = WriteConcern.SAFE; 29 | 30 | @Override 31 | public Class getObjectType() { 32 | return Mongo.class; 33 | } 34 | 35 | @Override 36 | protected Mongo createInstance() throws Exception { 37 | Mongo mongo = initMongo(); 38 | 39 | // 设定主从分离 40 | if (readSecondary) { 41 | mongo.setReadPreference(ReadPreference.secondaryPreferred()); 42 | } 43 | 44 | // 设定写策略 45 | mongo.setWriteConcern(writeConcern); 46 | return mongo; 47 | } 48 | 49 | /** 50 | * 初始化mongo实例 51 | * @return 52 | * @throws Exception 53 | */ 54 | private Mongo initMongo() throws Exception { 55 | // 根据条件创建Mongo实例 56 | Mongo mongo = null; 57 | List serverList = getServerList(); 58 | 59 | if (serverList.size() == 0) { 60 | mongo = new Mongo(); 61 | }else if(serverList.size() == 1){ 62 | if (mongoOptions != null) { 63 | mongo = new Mongo(serverList.get(0), mongoOptions); 64 | }else{ 65 | mongo = new Mongo(serverList.get(0)); 66 | } 67 | }else{ 68 | if (mongoOptions != null) { 69 | mongo = new Mongo(serverList, mongoOptions); 70 | }else{ 71 | mongo = new Mongo(serverList); 72 | } 73 | } 74 | return mongo; 75 | } 76 | 77 | 78 | /** 79 | * 根据服务器字符串列表,解析出服务器对象列表 80 | *

81 | * 82 | * @Title: getServerList 83 | *

84 | * 85 | * @return 86 | * @throws Exception 87 | */ 88 | private List getServerList() throws Exception { 89 | List serverList = new ArrayList(); 90 | try { 91 | for (String serverString : serverStrings) { 92 | String[] temp = serverString.split(":"); 93 | String host = temp[0]; 94 | if (temp.length > 2) { 95 | throw new IllegalArgumentException( 96 | "Invalid server address string: " + serverString); 97 | } 98 | if (temp.length == 2) { 99 | serverList.add(new ServerAddress(host, Integer 100 | .parseInt(temp[1]))); 101 | } else { 102 | serverList.add(new ServerAddress(host)); 103 | } 104 | } 105 | return serverList; 106 | } catch (Exception e) { 107 | throw new Exception( 108 | "Error while converting serverString to ServerAddressList", 109 | e); 110 | } 111 | } 112 | 113 | public String[] getServerStrings() { 114 | return serverStrings; 115 | } 116 | 117 | public void setServerStrings(String[] serverStrings) { 118 | this.serverStrings = serverStrings; 119 | } 120 | 121 | public MongoOptions getMongoOptions() { 122 | return mongoOptions; 123 | } 124 | 125 | public void setMongoOptions(MongoOptions mongoOptions) { 126 | this.mongoOptions = mongoOptions; 127 | } 128 | 129 | public boolean isReadSecondary() { 130 | return readSecondary; 131 | } 132 | 133 | public void setReadSecondary(boolean readSecondary) { 134 | this.readSecondary = readSecondary; 135 | } 136 | 137 | public WriteConcern getWriteConcern() { 138 | return writeConcern; 139 | } 140 | 141 | public void setWriteConcern(WriteConcern writeConcern) { 142 | this.writeConcern = writeConcern; 143 | } 144 | 145 | /* ------------------- setters --------------------- */ 146 | } -------------------------------------------------------------------------------- /src/main/webapp/skin/js/bootbox.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootbox.js v4.0.0 3 | * 4 | * http://bootboxjs.com/license.txt 5 | */ 6 | window.bootbox=window.bootbox||function a(b,c){"use strict";function d(a){var b=s[q.locale];return b?b[a]:s.en[a]}function e(a,c,d){a.preventDefault();var e=b.isFunction(d)&&d(a)===!1;e||c.modal("hide")}function f(a){var b,c=0;for(b in a)c++;return c}function g(a,c){var d=0;b.each(a,function(a,b){c(a,b,d++)})}function h(a){var c,d;if("object"!=typeof a)throw new Error("Please supply an object of options");if(!a.message)throw new Error("Please specify a message");return a=b.extend({},q,a),a.buttons||(a.buttons={}),a.backdrop=a.backdrop?"static":!1,c=a.buttons,d=f(c),g(c,function(a,e,f){if(b.isFunction(e)&&(e=c[a]={callback:e}),"object"!==b.type(e))throw new Error("button with key "+a+" must be an object");e.label||(e.label=a),e.className||(e.className=2>=d&&f===d-1?"btn-primary":"btn-default")}),a}function i(a,b){var c=a.length,d={};if(1>c||c>2)throw new Error("Invalid argument length");return 2===c||"string"==typeof a[0]?(d[b[0]]=a[0],d[b[1]]=a[1]):d=a[0],d}function j(a,c,d){return b.extend(!0,{},a,i(c,d))}function k(a,b,c){return n(j(m.apply(null,a),b,c),a)}function l(){for(var a={},b=0,c=arguments.length;c>b;b++){var e=arguments[b],f=e.toLowerCase(),g=e.toUpperCase();a[f]={label:d(g)}}return a}function m(){return{buttons:l.apply(null,arguments)}}function n(a,b){var d={};return g(b,function(a,b){d[b]=!0}),g(a.buttons,function(a){if(d[a]===c)throw new Error("button key "+a+" is not allowed (options are "+b.join("\n")+")")}),a}var o={dialog:"",header:"",footer:"",closeButton:"",form:"
",inputs:{text:""}},p=b("body"),q={locale:"en",backdrop:!0,animate:!0,className:null,closeButton:!0,show:!0},r={};r.alert=function(){var a;if(a=k(["ok"],arguments,["message","callback"]),a.callback&&!b.isFunction(a.callback))throw new Error("alert requires callback property to be a function when provided");return a.buttons.ok.callback=a.onEscape=function(){return b.isFunction(a.callback)?a.callback():!0},r.dialog(a)},r.confirm=function(){var a;if(a=k(["cancel","confirm"],arguments,["message","callback"]),a.buttons.cancel.callback=a.onEscape=function(){return a.callback(!1)},a.buttons.confirm.callback=function(){return a.callback(!0)},!b.isFunction(a.callback))throw new Error("confirm requires a callback");return r.dialog(a)},r.prompt=function(){var a,d,e,f,g,h;if(f=b(o.form),d={buttons:l("cancel","confirm"),value:""},a=n(j(d,arguments,["title","callback"]),["cancel","confirm"]),h=a.show===c?!0:a.show,a.message=f,a.buttons.cancel.callback=a.onEscape=function(){return a.callback(null)},a.buttons.confirm.callback=function(){return a.callback(g.val())},a.show=!1,!a.title)throw new Error("prompt requires a title");if(!b.isFunction(a.callback))throw new Error("prompt requires a callback");return g=b(o.inputs.text),g.val(a.value),f.append(g),f.on("submit",function(a){a.preventDefault(),e.find(".btn-primary").click()}),e=r.dialog(a),e.off("shown.bs.modal"),e.on("shown.bs.modal",function(){g.focus()}),h===!0&&e.modal("show"),e},r.dialog=function(a){a=h(a);var c=b(o.dialog),d=c.find(".modal-body"),f=a.buttons,i="",j={onEscape:a.onEscape};if(g(f,function(a,b){i+="",j[a]=b.callback}),d.find(".bootbox-body").html(a.message),a.animate===!0&&c.addClass("fade"),a.className&&c.addClass(a.className),a.title&&d.before(o.header),a.closeButton){var k=b(o.closeButton);a.title?c.find(".modal-header").prepend(k):k.css("margin-top","-10px").prependTo(d)}return a.title&&c.find(".modal-title").html(a.title),i.length&&(d.after(o.footer),c.find(".modal-footer").html(i)),c.on("hidden.bs.modal",function(a){a.target===this&&c.remove()}),c.on("shown.bs.modal",function(){c.find(".btn-primary:first").focus()}),c.on("escape.close.bb",function(a){j.onEscape&&e(a,c,j.onEscape)}),c.on("click",".modal-footer button",function(a){var d=b(this).data("bb-handler");e(a,c,j[d])}),c.on("click",".bootbox-close-button",function(a){e(a,c,j.onEscape)}),c.on("keyup",function(a){27===a.which&&c.trigger("escape.close.bb")}),p.append(c),c.modal({backdrop:a.backdrop,keyboard:!1,show:!1}),a.show&&c.modal("show"),c},r.setDefaults=function(a){b.extend(q,a)},r.hideAll=function(){b(".bootbox").modal("hide")};var s={br:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Sim"},da:{OK:"OK",CANCEL:"Annuller",CONFIRM:"Accepter"},de:{OK:"OK",CANCEL:"Abbrechen",CONFIRM:"Akzeptieren"},en:{OK:"OK",CANCEL:"Cancel",CONFIRM:"OK"},es:{OK:"OK",CANCEL:"Cancelar",CONFIRM:"Aceptar"},fi:{OK:"OK",CANCEL:"Peruuta",CONFIRM:"OK"},fr:{OK:"OK",CANCEL:"Annuler",CONFIRM:"D'accord"},it:{OK:"OK",CANCEL:"Annulla",CONFIRM:"Conferma"},nl:{OK:"OK",CANCEL:"Annuleren",CONFIRM:"Accepteren"},pl:{OK:"OK",CANCEL:"Anuluj",CONFIRM:"Potwierdź"},ru:{OK:"OK",CANCEL:"Отмена",CONFIRM:"Применить"},zh_CN:{OK:"OK",CANCEL:"取消",CONFIRM:"确认"},zh_TW:{OK:"OK",CANCEL:"取消",CONFIRM:"確認"}};return r.init=function(c){window.bootbox=a(c||b)},r}(window.jQuery); -------------------------------------------------------------------------------- /src/main/webapp/pages/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 大西瓜 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | <#assign ctx=(rc.getContextPath())!''> 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | 56 | 57 | 58 |
59 |
60 | 61 | 81 |
82 |

83 | 84 |

85 |

86 |

    87 |
  • #
  • #
  • #
  • 88 |
  • 89 |
90 |

91 |

92 | 大西瓜小企业开源社交, 欢迎热爱开源的开发人员加入 93 |

94 |
95 |
96 |

小尾巴...

97 |
98 |
99 |
100 | 101 |
102 |
103 |
104 | 105 | 110 | 112 | 113 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/common/entity/MUser.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.common.entity; 2 | 3 | import java.io.Serializable; 4 | import java.util.Set; 5 | 6 | import org.mongodb.morphia.annotations.Embedded; 7 | import org.mongodb.morphia.annotations.Entity; 8 | import org.mongodb.morphia.annotations.Indexed; 9 | import org.mongodb.morphia.annotations.Reference; 10 | import org.mongodb.morphia.utils.IndexDirection; 11 | 12 | import com.alibaba.fastjson.annotation.JSONField; 13 | import com.github.watermelon.core.entity.BaseEntity; 14 | 15 | /** 16 | * @author bruce 17 | */ 18 | @Entity("muser") 19 | public class MUser extends BaseEntity implements Serializable{ 20 | /** 21 | * 序列化, 为Set做处理 22 | */ 23 | private static final long serialVersionUID = -4000181846290366322L; 24 | 25 | //用户名 26 | @Indexed(value=IndexDirection.ASC, unique=true, dropDups=true) 27 | private String userName; 28 | //昵称 29 | @Indexed(value=IndexDirection.ASC, unique=true, dropDups=true) 30 | private String nickName; 31 | //验证邮箱 32 | @Indexed(value=IndexDirection.ASC, unique=true, dropDups=true) 33 | private String validationEmail; 34 | //密码 35 | @JSONField(serialize=false) 36 | private String password; 37 | 38 | //引用 关注人 39 | @Reference 40 | @JSONField(serialize=false) 41 | private Set follows; 42 | //引用 被关注人 43 | @Reference 44 | @JSONField(serialize=false) 45 | private Set fans; 46 | //子文档 签名 47 | @Embedded 48 | @JSONField(serialize=false) 49 | private Signature signature; 50 | 51 | private Integer twiSize; 52 | 53 | public String getUserName() { 54 | return userName; 55 | } 56 | 57 | public void setUserName(String userName) { 58 | this.userName = userName; 59 | } 60 | 61 | public String getNickName() { 62 | return nickName; 63 | } 64 | 65 | public void setNickName(String nickName) { 66 | this.nickName = nickName; 67 | } 68 | 69 | public String getValidationEmail() { 70 | return validationEmail; 71 | } 72 | 73 | public void setValidationEmail(String validationEmail) { 74 | this.validationEmail = validationEmail; 75 | } 76 | 77 | public String getPassword() { 78 | return password; 79 | } 80 | 81 | public void setPassword(String password) { 82 | this.password = password; 83 | } 84 | 85 | public Set getFollows() { 86 | return follows; 87 | } 88 | 89 | public void setFollows(Set follows) { 90 | this.follows = follows; 91 | } 92 | 93 | public Set getFans() { 94 | return fans; 95 | } 96 | 97 | public void setFans(Set fans) { 98 | this.fans = fans; 99 | } 100 | 101 | public Integer getFollowsSize(){ 102 | if(follows!=null) 103 | return follows.size(); 104 | return 0; 105 | } 106 | 107 | public Integer getFansSize(){ 108 | if(fans!=null) 109 | return fans.size(); 110 | return 0; 111 | } 112 | 113 | public Signature getSignature() { 114 | return signature; 115 | } 116 | 117 | public void setSignature(Signature signature) { 118 | this.signature = signature; 119 | } 120 | 121 | public String getSignatureContent(){ 122 | if(signature!=null){ 123 | return signature.getContent(); 124 | } 125 | return null; 126 | } 127 | 128 | public Integer getTwiSize() { 129 | if(twiSize!=null) 130 | return twiSize; 131 | return 0; 132 | } 133 | 134 | public void setTwiSize(Integer twiSize) { 135 | this.twiSize = twiSize; 136 | } 137 | 138 | @Override 139 | public int hashCode() { 140 | final int prime = 31; 141 | int result = 1; 142 | result = prime * result 143 | + ((nickName == null) ? 0 : nickName.hashCode()); 144 | result = prime * result 145 | + ((password == null) ? 0 : password.hashCode()); 146 | result = prime * result 147 | + ((signature == null) ? 0 : signature.hashCode()); 148 | result = prime * result 149 | + ((userName == null) ? 0 : userName.hashCode()); 150 | result = prime * result 151 | + ((validationEmail == null) ? 0 : validationEmail.hashCode()); 152 | return result; 153 | } 154 | 155 | @Override 156 | public boolean equals(Object obj) { 157 | if (this == obj) 158 | return true; 159 | if (obj == null) 160 | return false; 161 | if (getClass() != obj.getClass()) 162 | return false; 163 | MUser other = (MUser) obj; 164 | if (nickName == null) { 165 | if (other.nickName != null) 166 | return false; 167 | } else if (!nickName.equals(other.nickName)) 168 | return false; 169 | if (password == null) { 170 | if (other.password != null) 171 | return false; 172 | } else if (!password.equals(other.password)) 173 | return false; 174 | if (signature == null) { 175 | if (other.signature != null) 176 | return false; 177 | } else if (!signature.equals(other.signature)) 178 | return false; 179 | if (userName == null) { 180 | if (other.userName != null) 181 | return false; 182 | } else if (!userName.equals(other.userName)) 183 | return false; 184 | if (validationEmail == null) { 185 | if (other.validationEmail != null) 186 | return false; 187 | } else if (!validationEmail.equals(other.validationEmail)) 188 | return false; 189 | return true; 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/common/service/MUserService.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.common.service; 2 | 3 | import java.util.Set; 4 | 5 | import org.apache.commons.lang.StringUtils; 6 | import org.bson.types.ObjectId; 7 | import org.mongodb.morphia.query.Query; 8 | import org.mongodb.morphia.query.UpdateOperations; 9 | import org.mongodb.morphia.query.UpdateResults; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Service; 12 | 13 | import com.github.watermelon.core.service.MongoBaseService; 14 | import com.github.watermelon.core.util.PasswordEncoderUtil; 15 | import com.github.watermelon.module.common.dao.MUserDAO; 16 | import com.github.watermelon.module.common.entity.MUser; 17 | import com.github.watermelon.module.common.entity.Signature; 18 | 19 | @Service 20 | public class MUserService extends MongoBaseService{ 21 | @Autowired 22 | private MUserDAO mUserDAO; 23 | 24 | @Autowired 25 | private PasswordEncoderUtil passwordEncoderUtil; 26 | 27 | public MUserDAO getmUserDAO() { 28 | return mUserDAO; 29 | } 30 | 31 | public void setmUserDAO(MUserDAO mUserDAO) { 32 | this.mUserDAO = mUserDAO; 33 | super.setBaseDao(mUserDAO); 34 | } 35 | 36 | public PasswordEncoderUtil getPasswordEncoderUtil() { 37 | return passwordEncoderUtil; 38 | } 39 | 40 | public void setPasswordEncoderUtil(PasswordEncoderUtil passwordEncoderUtil) { 41 | this.passwordEncoderUtil = passwordEncoderUtil; 42 | } 43 | 44 | /** 45 | * 登录, 需要userName或validationEmail 46 | * @param param 47 | * @return 48 | */ 49 | public MUser login(MUser param) { 50 | MUser result=null; 51 | if(StringUtils.isNotBlank(param.getUserName())){ 52 | result=findOne("userName",param.getUserName()); 53 | }else if(StringUtils.isNotBlank(param.getValidationEmail())){ 54 | result=findOne("validationEmail", param.getValidationEmail()); 55 | } 56 | //查询并验证密码 57 | if(result!=null && passwordEncoderUtil.isPasswordValid(result.getPassword(), param.getPassword())){ 58 | return result; 59 | } 60 | return null; 61 | } 62 | 63 | /** 64 | * 删除 65 | * @param result 66 | * @param password 67 | */ 68 | public void delete(MUser result, String password) { 69 | //删除前验证密码 70 | if(result!=null && passwordEncoderUtil.isPasswordValid(result.getPassword(), password)){ 71 | delete(result); 72 | } 73 | } 74 | 75 | /** 76 | * 注册前检查 77 | * @param param 78 | * @return 79 | */ 80 | public MUser register(MUser param) { 81 | Query query = createQuery(); 82 | query.or(query.criteria("userName").equal(param.getUserName()), 83 | query.criteria("nickName").equal(param.getNickName()), 84 | query.criteria("validationEmail").equal(param.getValidationEmail())); 85 | MUser result=findOne(query); 86 | //是否有重名 87 | if(result!=null){ 88 | return null; 89 | } 90 | 91 | result = new MUser(); 92 | result.setUserName(param.getUserName()); 93 | result.setPassword(passwordEncoderUtil.encodePassword(param.getPassword())); 94 | result.setNickName(param.getNickName()); 95 | result.setValidationEmail(param.getValidationEmail()); 96 | save(result); 97 | return result; 98 | } 99 | 100 | public void signature(MUser user, String content) { 101 | Query query = createQuery().filter("userName", user.getUserName()); 102 | 103 | Signature signature = new Signature(); 104 | signature.setContent(content); 105 | signature.setTimestamp(System.currentTimeMillis()); 106 | 107 | UpdateOperations uo = baseDao.createUpdateOperations().set("signature", signature); 108 | 109 | UpdateResults results= baseDao.update(query, uo); 110 | if(results!=null && results.getUpdatedCount()==1){ 111 | user.setSignature(signature); 112 | } 113 | } 114 | 115 | public boolean follow(MUser me, String name) { 116 | MUser followUser = findOne("userName", name); 117 | 118 | Set meFos = me.getFollows(); 119 | if(meFos.contains(followUser)==false){ 120 | meFos.add(followUser); 121 | } 122 | Set foFans= followUser.getFans(); 123 | if(foFans.contains(me)==false){ 124 | foFans.add(me); 125 | } 126 | Query query = createQuery().filter("userName", me.getUserName()); 127 | UpdateOperations uo = baseDao.createUpdateOperations().set("follows", meFos); 128 | baseDao.update(query, uo); 129 | 130 | query=createQuery().filter("userName", followUser.getUserName()); 131 | uo = baseDao.createUpdateOperations().set("fans", foFans); 132 | baseDao.update(query, uo); 133 | return true; 134 | } 135 | 136 | public boolean unfollow(MUser me, String name) { 137 | MUser followUser = findOne("userName", name); 138 | 139 | Set meFos = me.getFollows(); 140 | if(meFos.contains(followUser)==true){ 141 | meFos.remove(followUser); 142 | } 143 | Set foFans= followUser.getFans(); 144 | if(foFans.contains(me)==true){ 145 | foFans.remove(me); 146 | } 147 | 148 | Query query = createQuery().filter("userName", me.getUserName()); 149 | UpdateOperations uo = baseDao.createUpdateOperations().set("follows", meFos); 150 | baseDao.update(query, uo); 151 | 152 | query=createQuery().filter("userName", followUser.getUserName()); 153 | uo = baseDao.createUpdateOperations().set("fans", foFans); 154 | baseDao.update(query, uo); 155 | return true; 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/weibo/web/TwiboController.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.weibo.web; 2 | 3 | import java.util.List; 4 | 5 | import javax.servlet.http.HttpSession; 6 | 7 | import org.apache.log4j.Logger; 8 | import org.bson.types.ObjectId; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.ResponseBody; 14 | import org.springframework.web.servlet.ModelAndView; 15 | 16 | import com.github.watermelon.core.vo.ResultVO; 17 | import com.github.watermelon.core.vo.SysDefinition; 18 | import com.github.watermelon.module.common.entity.MUser; 19 | import com.github.watermelon.module.common.service.MUserService; 20 | import com.github.watermelon.module.weibo.entity.Twibo; 21 | import com.github.watermelon.module.weibo.entity.Twicomment; 22 | import com.github.watermelon.module.weibo.service.TwiboService; 23 | 24 | @Controller 25 | @RequestMapping("/twibo") 26 | public class TwiboController { 27 | /** 28 | * logger. 29 | */ 30 | private Logger logger=Logger.getLogger(getClass()); 31 | 32 | @Autowired 33 | private TwiboService twiboService; 34 | 35 | @Autowired 36 | private MUserService mUserService; 37 | 38 | /** 39 | * 发微博 40 | * @param content 41 | * @return 42 | */ 43 | @ResponseBody 44 | @RequestMapping(value="/post/twibo") 45 | public ResultVO postTwibo(HttpSession session, String content){ 46 | MUser user= (MUser)session.getAttribute(SysDefinition.USER_SESSION_KEY); 47 | if(user==null){ 48 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 49 | } 50 | Twibo result = null; 51 | try{ 52 | result = twiboService.postTwibo(content, user); 53 | }catch(Exception e){ 54 | logger.error(e.getMessage()); 55 | } 56 | if(result!=null){ 57 | return new ResultVO(SysDefinition.CODE_SUCCESS,null,result); 58 | }else{ 59 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 60 | } 61 | } 62 | 63 | /** 64 | * 发表评论 65 | * @param session 66 | * @param tid 67 | * @param content 68 | * @return 69 | */ 70 | @ResponseBody 71 | @RequestMapping(value="/post/comment") 72 | public ResultVO postComment(HttpSession session, String tid, String content){ 73 | MUser user= (MUser)session.getAttribute(SysDefinition.USER_SESSION_KEY); 74 | if(user==null){ 75 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 76 | } 77 | Twicomment result = null; 78 | try{ 79 | result = twiboService.postComment(tid, user, content); 80 | }catch(Exception e){ 81 | logger.error(e.getMessage()); 82 | } 83 | if(result!=null){ 84 | return new ResultVO(SysDefinition.CODE_SUCCESS,null,result); 85 | }else{ 86 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 87 | } 88 | } 89 | 90 | /** 91 | * 获取评论 92 | * @param tid 93 | * @param offset 94 | * @param length 95 | * @return 96 | */ 97 | @ResponseBody 98 | @RequestMapping(value="/comments") 99 | public ResultVO> comments(String tid, Integer offset, Integer length){ 100 | List result = null; 101 | try{ 102 | //TODO:未分页 103 | result = twiboService.getComments(tid, offset, length); 104 | }catch(Exception e){ 105 | logger.error(e.getMessage()); 106 | } 107 | if(result!=null){ 108 | return new ResultVO>(SysDefinition.CODE_SUCCESS,null,result); 109 | }else{ 110 | return new ResultVO>(SysDefinition.CODE_NODATA,null,null); 111 | } 112 | } 113 | 114 | /** 115 | * 列表获取twibo 116 | * @param session 117 | * @param offset 118 | * @param length 119 | * @return 120 | */ 121 | @ResponseBody 122 | @RequestMapping(value="/list") 123 | public ResultVO> list(HttpSession session, Integer offset, Integer length){ 124 | MUser user= (MUser)session.getAttribute(SysDefinition.USER_SESSION_KEY); 125 | if(user==null){ 126 | return new ResultVO>(SysDefinition.CODE_NODATA,null,null); 127 | } 128 | List result = null; 129 | try{ 130 | result = twiboService.getList(user, offset, length); 131 | }catch(Exception e){ 132 | logger.error(e.getMessage()); 133 | } 134 | if(result!=null){ 135 | return new ResultVO>(SysDefinition.CODE_SUCCESS,null,result); 136 | }else{ 137 | return new ResultVO>(SysDefinition.CODE_NODATA,null,null); 138 | } 139 | } 140 | 141 | /** 142 | * 查询所有关注者 143 | * @param session 144 | * @param oid 145 | * @return 146 | */ 147 | @RequestMapping(value="/{oid}/follow") 148 | public ModelAndView follow(HttpSession session, @PathVariable String oid){ 149 | ModelAndView view = new ModelAndView("/follow"); 150 | try{ 151 | MUser muser= mUserService.get(new ObjectId(oid)); 152 | if(muser!=null){ 153 | view.addObject("follows",muser.getFollows()); 154 | } 155 | }catch(Exception e){ 156 | logger.error(e.getMessage()); 157 | } 158 | return view; 159 | } 160 | 161 | /** 162 | * 查询所有粉丝 163 | * @param session 164 | * @param oid 165 | * @return 166 | */ 167 | @RequestMapping(value="/{oid}/fans") 168 | public ModelAndView fans(HttpSession session, @PathVariable String oid){ 169 | ModelAndView view = new ModelAndView("/fans"); 170 | try{ 171 | MUser muser= mUserService.get(new ObjectId(oid)); 172 | if(muser!=null){ 173 | view.addObject("fans",muser.getFans()); 174 | } 175 | }catch(Exception e){ 176 | logger.error(e.getMessage()); 177 | } 178 | return view; 179 | } 180 | 181 | @ResponseBody 182 | @RequestMapping(value="/get") 183 | public ResultVO get(String oid){ 184 | Twibo result = null; 185 | try{ 186 | result = twiboService.get(new ObjectId(oid)); 187 | }catch(Exception e){ 188 | logger.error(e.getMessage()); 189 | } 190 | if(result!=null){ 191 | return new ResultVO(SysDefinition.CODE_SUCCESS,null,result); 192 | }else{ 193 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 194 | } 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/module/common/web/MUserController.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.module.common.web; 2 | 3 | import javax.servlet.http.Cookie; 4 | import javax.servlet.http.HttpServletResponse; 5 | import javax.servlet.http.HttpSession; 6 | 7 | import org.apache.log4j.Logger; 8 | import org.mongodb.morphia.query.Query; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.ResponseBody; 13 | import org.springframework.web.servlet.ModelAndView; 14 | 15 | import com.github.watermelon.core.vo.ResultVO; 16 | import com.github.watermelon.core.vo.SysDefinition; 17 | import com.github.watermelon.module.common.entity.MUser; 18 | import com.github.watermelon.module.common.service.MUserService; 19 | 20 | @Controller 21 | @RequestMapping("/user") 22 | public class MUserController { 23 | /** 24 | * logger. 25 | */ 26 | private Logger logger=Logger.getLogger(getClass()); 27 | 28 | @Autowired 29 | private MUserService mUserService; 30 | 31 | /** 32 | * 查询信息 33 | * @param username 34 | * @return 35 | */ 36 | @ResponseBody 37 | @RequestMapping(value="/get") 38 | public ResultVO get(String userName){ 39 | MUser result=null; 40 | try{ 41 | Query query = mUserService.createQuery().filter("userName",userName); 42 | result=mUserService.findOne(query); 43 | }catch(Exception e){ 44 | logger.error(e.getMessage()); 45 | return new ResultVO(SysDefinition.CODE_ERROR,null,null); 46 | } 47 | if(result!=null){ 48 | return new ResultVO(SysDefinition.CODE_SUCCESS,null,result); 49 | }else{ 50 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 51 | } 52 | } 53 | 54 | /** 55 | * 新增用户 56 | * @param username 57 | * @param password 58 | * @param nickname 59 | * @param email 60 | * @return 61 | */ 62 | @RequestMapping(value="/register") 63 | public ModelAndView register(HttpSession session, HttpServletResponse response, MUser param){ 64 | MUser result=null; 65 | try{ 66 | result=mUserService.register(param); 67 | }catch(Exception e){ 68 | logger.error(e.getMessage()); 69 | return new ModelAndView("redirect:/signin"); 70 | } 71 | if(result!=null){ 72 | session.setAttribute(SysDefinition.USER_SESSION_KEY, result); 73 | saveCookie(session, result, response); 74 | return new ModelAndView("redirect:/home"); 75 | }else{ 76 | return new ModelAndView("redirect:/signin"); 77 | } 78 | } 79 | 80 | /** 81 | * 登录 82 | * @param session 83 | * @param param 84 | * @return 85 | */ 86 | @RequestMapping(value="/login") 87 | public ModelAndView login(HttpSession session, HttpServletResponse response, MUser param){ 88 | MUser result=null; 89 | try{ 90 | result=mUserService.login(param); 91 | }catch(Exception e){ 92 | logger.error(e.getMessage()); 93 | return new ModelAndView("redirect:/signin"); 94 | } 95 | if(result!=null){ 96 | session.setAttribute(SysDefinition.USER_SESSION_KEY, result); 97 | saveCookie(session, result, response); 98 | return new ModelAndView("redirect:/home"); 99 | }else{ 100 | return new ModelAndView("redirect:/signin"); 101 | } 102 | } 103 | 104 | private void saveCookie(HttpSession session, MUser muser, HttpServletResponse response){ 105 | Cookie sessionCookie = new Cookie(SysDefinition.COOKIE_SESSIONID_KEY, session.getId()); 106 | sessionCookie.setMaxAge(3600); 107 | sessionCookie.setPath("/"); 108 | response.addCookie(sessionCookie); 109 | 110 | Cookie loginidCookie = new Cookie(SysDefinition.COOKIE_LOGINID_KEY, muser.getUserName()); 111 | //30*24*60*60 112 | loginidCookie.setMaxAge(2592000); 113 | loginidCookie.setPath("/"); 114 | response.addCookie(loginidCookie); 115 | } 116 | 117 | /** 118 | * 登出 119 | * @param session 120 | * @return 121 | */ 122 | @ResponseBody 123 | @RequestMapping(value="/logout") 124 | public ResultVO logout(HttpSession session){ 125 | session.removeAttribute(SysDefinition.USER_SESSION_KEY); 126 | return new ResultVO(SysDefinition.CODE_SUCCESS,null,null); 127 | } 128 | 129 | /** 130 | * 删除当前账号, 删除必须是登录状态, 且需要提交密码验证身份 131 | * @param session 132 | * @param password 133 | * @return 134 | */ 135 | @ResponseBody 136 | @RequestMapping(value="/delete") 137 | public ResultVO delete(HttpSession session, String password){ 138 | MUser result= (MUser)session.getAttribute(SysDefinition.USER_SESSION_KEY); 139 | if(result==null){ 140 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 141 | } 142 | try{ 143 | mUserService.delete(result, password); 144 | session.removeAttribute(SysDefinition.USER_SESSION_KEY); 145 | }catch(Exception e){ 146 | logger.error(e.getMessage()); 147 | return new ResultVO(SysDefinition.CODE_ERROR,null,null); 148 | } 149 | return new ResultVO(SysDefinition.CODE_SUCCESS,null,null); 150 | } 151 | 152 | @ResponseBody 153 | @RequestMapping(value="/update/signature") 154 | public ResultVO signature(HttpSession session, String content){ 155 | MUser result= (MUser)session.getAttribute(SysDefinition.USER_SESSION_KEY); 156 | if(result==null){ 157 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 158 | } 159 | try{ 160 | mUserService.signature(result, content); 161 | session.setAttribute(SysDefinition.USER_SESSION_KEY, result); 162 | }catch(Exception e){ 163 | logger.error(e.getMessage()); 164 | return new ResultVO(SysDefinition.CODE_ERROR,null,null); 165 | } 166 | return new ResultVO(SysDefinition.CODE_SUCCESS,null,content); 167 | } 168 | 169 | @ResponseBody 170 | @RequestMapping(value="/follow") 171 | public ResultVO follow(HttpSession session, String name){ 172 | MUser result= (MUser)session.getAttribute(SysDefinition.USER_SESSION_KEY); 173 | if(result==null){ 174 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 175 | } 176 | try{ 177 | if(mUserService.follow(result, name)){ 178 | return new ResultVO(SysDefinition.CODE_SUCCESS,null,null); 179 | } 180 | }catch(Exception e){ 181 | logger.error(e.getMessage()); 182 | return new ResultVO(SysDefinition.CODE_ERROR,null,null); 183 | } 184 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 185 | } 186 | 187 | @ResponseBody 188 | @RequestMapping(value="/unfollow") 189 | public ResultVO unfollow(HttpSession session, String name){ 190 | MUser result= (MUser)session.getAttribute(SysDefinition.USER_SESSION_KEY); 191 | if(result==null){ 192 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 193 | } 194 | try{ 195 | if(mUserService.unfollow(result, name)){ 196 | return new ResultVO(SysDefinition.CODE_SUCCESS,null,null); 197 | } 198 | }catch(Exception e){ 199 | logger.error(e.getMessage()); 200 | return new ResultVO(SysDefinition.CODE_ERROR,null,null); 201 | } 202 | return new ResultVO(SysDefinition.CODE_NODATA,null,null); 203 | } 204 | 205 | 206 | } 207 | -------------------------------------------------------------------------------- /src/main/java/com/github/watermelon/core/util/PasswordEncoderUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.watermelon.core.util; 2 | 3 | import java.security.MessageDigest; 4 | import java.util.Random; 5 | 6 | import org.springframework.security.authentication.encoding.PasswordEncoder; 7 | import org.springframework.security.crypto.codec.Base64; 8 | 9 | @SuppressWarnings("deprecation") 10 | public class PasswordEncoderUtil implements PasswordEncoder { 11 | // ~ Static fields/initializers 12 | // ===================================================================================== 13 | 14 | private static final int SHA_LENGTH = 20; 15 | private static final int MD5_LENGTH = 16; 16 | private static final String SSHA = "SSHA"; 17 | private static final String SHA = "SHA"; 18 | private static final String MD5 = "MD5"; 19 | private static final String SMD5 = "SMD5"; 20 | private static final String SSHA_PREFIX = "{SSHA}"; 21 | private static final String SHA_PREFIX = "{SHA}"; 22 | private static final String MD5_PREFIX = "{MD5}"; 23 | private static final String SMD5_PREFIX = "{SMD5}"; 24 | 25 | private static final String SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 26 | 27 | // ~ Instance fields 28 | // ================================================================================================ 29 | private boolean forceLowerCasePrefix; 30 | private String encodeAlgorithm = "plaintext"; 31 | 32 | // ~ Constructors 33 | // =================================================================================================== 34 | public PasswordEncoderUtil() { 35 | } 36 | 37 | // ~ Methods 38 | // ======================================================================================================== 39 | public String encodePassword(String rawPass, Object salt) { 40 | if (encodeAlgorithm.equalsIgnoreCase(MD5)) { 41 | return encodeMD5Password(rawPass); 42 | } 43 | if (encodeAlgorithm.equalsIgnoreCase(SMD5)) { 44 | return encodeSMD5Password(rawPass); 45 | } 46 | if (encodeAlgorithm.equalsIgnoreCase(SHA)) { 47 | return encodeSHAPassword(rawPass); 48 | } 49 | if (encodeAlgorithm.equalsIgnoreCase(SSHA)) { 50 | return encodeSSHAPassword(rawPass); 51 | } 52 | return rawPass; 53 | } 54 | 55 | public String encodePassword(String rawPass) { 56 | return encodePassword(rawPass, null); 57 | } 58 | 59 | private String encodeMD5Password(String rawPass) { 60 | String prefix = forceLowerCasePrefix ? MD5_PREFIX.toLowerCase() 61 | : MD5_PREFIX; 62 | return prefix + encodePassword(rawPass, null, MD5); 63 | } 64 | 65 | private String encodeSMD5Password(String rawPass) { 66 | String prefix = forceLowerCasePrefix ? SMD5_PREFIX.toLowerCase() 67 | : SMD5_PREFIX; 68 | return prefix + encodePassword(rawPass, generateSalt(), MD5); 69 | } 70 | 71 | private String encodeSHAPassword(String rawPass) { 72 | String prefix = forceLowerCasePrefix ? SHA_PREFIX.toLowerCase() 73 | : SHA_PREFIX; 74 | return prefix + encodePassword(rawPass, null, SHA); 75 | } 76 | 77 | private String encodeSSHAPassword(String rawPass) { 78 | String prefix = forceLowerCasePrefix ? SSHA_PREFIX.toLowerCase() 79 | : SSHA_PREFIX; 80 | return prefix + encodePassword(rawPass, generateSalt(), SHA); 81 | } 82 | 83 | private String encodePassword(String rawPass, byte[] salt, 84 | String encodeAlgorithm) { 85 | MessageDigest messageDigest = getMessageDigest(encodeAlgorithm); 86 | messageDigest.update(rawPass.getBytes()); 87 | String encPass; 88 | if (salt != null) { 89 | messageDigest.update(salt); 90 | byte[] hash = combineHashAndSalt(messageDigest.digest(), salt); 91 | encPass = new String(Base64.encode(hash)); 92 | } else { 93 | encPass = new String(Base64.encode(messageDigest.digest())); 94 | } 95 | return encPass; 96 | } 97 | 98 | public boolean isPasswordValid(String encPass, String rawPass, Object salt) { 99 | if (encPass.toUpperCase().startsWith(MD5_PREFIX)) 100 | return isMD5PasswordValid(encPass, rawPass); 101 | if (encPass.toUpperCase().startsWith(SMD5_PREFIX)) 102 | return isSMD5PasswordValid(encPass, rawPass); 103 | if (encPass.toUpperCase().startsWith(SHA_PREFIX)) 104 | return isSHAPasswordValid(encPass, rawPass); 105 | if (encPass.toUpperCase().startsWith(SSHA_PREFIX)) 106 | return isSSHAPasswordValid(encPass, rawPass); 107 | return isPlaintextPasswordValid(encPass, rawPass); 108 | } 109 | 110 | public boolean isPasswordValid(String encPass, String rawPass) { 111 | return isPasswordValid(encPass, rawPass, null); 112 | } 113 | 114 | private boolean isPlaintextPasswordValid(String encPass, String rawPass) { 115 | String pass1 = encPass + ""; 116 | return pass1.equals(rawPass); 117 | } 118 | 119 | private boolean isMD5PasswordValid(String encPass, String rawPass) { 120 | String encPassWithoutPrefix = encPass.substring(MD5_PREFIX.length()); 121 | return encodePassword(rawPass, null, MD5).equals(encPassWithoutPrefix); 122 | } 123 | 124 | private boolean isSMD5PasswordValid(String encPass, String rawPass) { 125 | String encPassWithoutPrefix = encPass.substring(SMD5_PREFIX.length()); 126 | byte[] salt = extractSalt(encPassWithoutPrefix, MD5_LENGTH); 127 | return encodePassword(rawPass, salt, MD5).equals(encPassWithoutPrefix); 128 | } 129 | 130 | private boolean isSHAPasswordValid(String encPass, String rawPass) { 131 | String encPassWithoutPrefix = encPass.substring(SHA_PREFIX.length()); 132 | return encodePassword(rawPass, null, SHA).equals(encPassWithoutPrefix); 133 | } 134 | 135 | private boolean isSSHAPasswordValid(String encPass, String rawPass) { 136 | String encPassWithoutPrefix = encPass.substring(SSHA_PREFIX.length()); 137 | byte[] salt = extractSalt(encPassWithoutPrefix, SHA_LENGTH); 138 | return encodePassword(rawPass, salt, SHA).equals(encPassWithoutPrefix); 139 | } 140 | 141 | public void setForceLowerCasePrefix(boolean forceLowerCasePrefix) { 142 | this.forceLowerCasePrefix = forceLowerCasePrefix; 143 | } 144 | 145 | private byte[] combineHashAndSalt(byte[] hash, byte[] salt) { 146 | if (salt == null) { 147 | return hash; 148 | } 149 | byte[] hashAndSalt = new byte[hash.length + salt.length]; 150 | System.arraycopy(hash, 0, hashAndSalt, 0, hash.length); 151 | System.arraycopy(salt, 0, hashAndSalt, hash.length, salt.length); 152 | return hashAndSalt; 153 | } 154 | 155 | private MessageDigest getMessageDigest(String algorithm) { 156 | MessageDigest messageDigest; 157 | try { 158 | messageDigest = MessageDigest.getInstance(algorithm); 159 | } catch (java.security.NoSuchAlgorithmException e) { 160 | throw new IllegalArgumentException("No such algorithm [" 161 | + algorithm + "]"); 162 | } 163 | return messageDigest; 164 | } 165 | 166 | private byte[] extractSalt(String encPass, int hashLength) { 167 | byte[] hashAndSalt = Base64.decode(encPass.getBytes()); 168 | int saltLength = hashAndSalt.length - hashLength; 169 | byte[] salt = new byte[saltLength]; 170 | System.arraycopy(hashAndSalt, hashLength, salt, 0, saltLength); 171 | return salt; 172 | } 173 | 174 | private byte[] generateSalt() { 175 | StringBuffer salt = new StringBuffer(); 176 | Random randgen = new Random(); 177 | while (salt.length() < 4) { 178 | int index = (int) (randgen.nextFloat() * SALT_CHARS.length()); 179 | salt.append(SALT_CHARS.substring(index, index + 1)); 180 | } 181 | return salt.toString().getBytes(); 182 | } 183 | 184 | public void setEncodeAlgorithm(String encodeAlgorithm) { 185 | this.encodeAlgorithm = encodeAlgorithm; 186 | } 187 | 188 | /** 189 | * 生成随即密码 190 | * 191 | * @param pwd_len 192 | * 生成的密码的总长度 193 | * @return 密码的字符串 194 | */ 195 | public static String genRandomNum(int pwd_len) { 196 | // 35是因为数组是从0开始的,26个字母+10个数字 197 | final int maxNum = 36; 198 | int i; // 生成的随机数 199 | int count = 0; // 生成的密码的长度 200 | char[] str = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 201 | 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 202 | 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; 203 | 204 | StringBuffer pwd = new StringBuffer(""); 205 | Random r = new Random(); 206 | while (count < pwd_len) { 207 | // 生成随机数,取绝对值,防止生成负数, 208 | 209 | i = Math.abs(r.nextInt(maxNum)); // 生成的数最大为36-1 210 | 211 | if (i >= 0 && i < str.length) { 212 | pwd.append(str[i]); 213 | count++; 214 | } 215 | } 216 | 217 | return pwd.toString(); 218 | } 219 | 220 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.mgs 5 | watermelon 6 | 0.1.3-SNAPSHOT 7 | war 8 | watermelon webapp 9 | http://maven.apache.org 10 | 11 | 4.2.1.RELEASE 12 | 4.1.1.RELEASE 13 | 1.6.10 14 | 1.1.40 15 | 16 | 7.6.14.v20131031 17 | 8181 18 | UTF-8 19 | 20 | 21 | 22 | log4j 23 | log4j 24 | 1.2.16 25 | 26 | 27 | junit 28 | junit 29 | 4.8.2 30 | test 31 | 32 | 33 | commons-dbcp 34 | commons-dbcp 35 | 1.4 36 | 37 | 38 | 39 | org.mongodb 40 | mongo-java-driver 41 | 3.2.2 42 | 43 | 44 | 45 | 46 | 47 | org.mongodb.morphia 48 | morphia 49 | 1.2.1 50 | 51 | 52 | 59 | 60 | com.thoughtworks.proxytoys 61 | proxytoys 62 | 1.0 63 | jar 64 | true 65 | 66 | 67 | 68 | 69 | commons-fileupload 70 | commons-fileupload 71 | 1.3 72 | 73 | 74 | commons-io 75 | commons-io 76 | 2.2 77 | 78 | 79 | 80 | cglib 81 | cglib 82 | 2.2 83 | 84 | 85 | org.freemarker 86 | freemarker 87 | 2.3.20 88 | 89 | 90 | javax.servlet 91 | servlet-api 92 | 2.5 93 | 94 | 95 | org.springframework 96 | spring-core 97 | ${spring.version} 98 | 99 | 100 | org.springframework 101 | spring-webmvc 102 | ${spring.version} 103 | 104 | 105 | org.springframework 106 | spring-beans 107 | ${spring.version} 108 | 109 | 110 | org.springframework 111 | spring-context 112 | ${spring.version} 113 | 114 | 115 | org.springframework 116 | spring-context-support 117 | ${spring.version} 118 | 119 | 120 | org.springframework 121 | spring-aop 122 | ${spring.version} 123 | 124 | 125 | commons-logging 126 | commons-logging 127 | 128 | 129 | 130 | 131 | org.springframework 132 | spring-tx 133 | ${spring.version} 134 | 135 | 136 | org.springframework 137 | spring-test 138 | ${spring.version} 139 | 140 | 141 | org.springframework.security 142 | spring-security-core 143 | ${spring.security.version} 144 | 145 | 146 | org.aspectj 147 | aspectjrt 148 | ${aspectj.version} 149 | 150 | 151 | org.aspectj 152 | aspectjweaver 153 | ${aspectj.version} 154 | 155 | 156 | 157 | 158 | org.eclipse.jetty 159 | jetty-jsp 160 | ${jetty.version} 161 | 162 | 163 | org.eclipse.jetty.orbit 164 | javax.servlet 165 | 166 | 167 | org.eclipse.jetty.orbit 168 | org.apache.taglibs.standard.glassfish 169 | 170 | 171 | 172 | 173 | 174 | 175 | org.eclipse.jetty 176 | jetty-webapp 177 | compile 178 | ${jetty.version} 179 | true 180 | 181 | 182 | 183 | javax.servlet 184 | servlet-api 185 | compile 186 | 2.5 187 | 188 | 189 | 190 | javax.servlet 191 | jstl 192 | 1.2 193 | 194 | 195 | 196 | 197 | com.alibaba 198 | fastjson 199 | ${fastjson.version} 200 | 201 | 202 | 203 | org.apache.geronimo.specs 204 | geronimo-javamail_1.4_spec 205 | 1.4 206 | 207 | 208 | 209 | 210 | com.thoughtworks.xstream 211 | xstream 212 | 1.3.1 213 | 214 | 215 | commons-lang 216 | commons-lang 217 | 2.6 218 | 219 | 220 | 221 | log4j 222 | log4j 223 | 1.2.17 224 | 225 | 226 | 227 | commons-collections 228 | commons-collections 229 | 3.2.2 230 | 231 | 232 | 233 | net.sf.json-lib 234 | json-lib 235 | 2.4 236 | jdk15 237 | 238 | 239 | 240 | 241 | javax.mail 242 | mail 243 | 1.4 244 | 245 | 246 | 247 | 248 | 249 | 250 | src/main/resources 251 | true 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | org.apache.maven.plugins 260 | maven-compiler-plugin 261 | 3.5 262 | 263 | 1.8 264 | 1.8 265 | utf-8 266 | 267 | 268 | 269 | 270 | org.apache.maven.plugins 271 | maven-resources-plugin 272 | 273 | UTF-8 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | dev 283 | 284 | 285 | 286 | 287 | 288 | prod 289 | 290 | 291 | 292 | 293 | 294 | -------------------------------------------------------------------------------- /src/main/webapp/skin/js/bootstrap-maxlength.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * bootstrap-maxlength.js v1.4.1 3 | * 4 | * Copyright (c) 2013 Maurizio Napoleoni; 5 | * 6 | * Licensed under the terms of the MIT license. 7 | * See: https://github.com/mimo84/bootstrap-maxlength/blob/master/LICENSE 8 | * ========================================================== */ 9 | /*jslint browser:true*/ 10 | /*global jQuery*/ 11 | (function ($) { 12 | "use strict"; 13 | 14 | $.fn.extend({ 15 | maxlength: function (options, callback) { 16 | 17 | var documentBody = $('body'), 18 | defaults = { 19 | alwaysShow: false, // if true the indicator it's always shown. 20 | threshold: 10, // Represents how many chars left are needed to show up the counter 21 | warningClass: "badge badge-success", 22 | limitReachedClass: "badge badge-important", 23 | separator: ' / ', 24 | preText: '', 25 | postText: '', 26 | showMaxLength : true, 27 | placement: 'bottom', 28 | showCharsTyped: true, // show the number of characters typed and not the number of characters remaining 29 | validate: false // if the browser doesn't support the maxlength attribute, attempt to type more than 30 | // the indicated chars, will be prevented. 31 | }; 32 | 33 | if ($.isFunction(options) && !callback) { 34 | callback = options; 35 | options = {}; 36 | } 37 | options = $.extend(defaults, options); 38 | 39 | /** 40 | * Return the length of the specified input. 41 | * 42 | * @param input 43 | * @return {number} 44 | */ 45 | function inputLength(input) { 46 | var text = input.val(), 47 | matches = text.match(/\n/g), 48 | breaks = matches ? matches.length : 0; 49 | return input.val().length + breaks; 50 | } 51 | 52 | /** 53 | * Return true if the indicator should be showing up. 54 | * 55 | * @param input 56 | * @param thereshold 57 | * @param maxlength 58 | * @return {number} 59 | */ 60 | function charsLeftThreshold(input, thereshold, maxlength) { 61 | var output = true; 62 | if (!options.alwaysShow && (maxlength - inputLength(input) > thereshold)) { 63 | output = false; 64 | } 65 | return output; 66 | } 67 | 68 | /** 69 | * Returns how many chars are left to complete the fill up of the form. 70 | * 71 | * @param input 72 | * @param maxlength 73 | * @return {number} 74 | */ 75 | function remainingChars(input, maxlength) { 76 | var length = maxlength - inputLength(input); 77 | return length; 78 | } 79 | 80 | /** 81 | * When called displays the indicator. 82 | * 83 | * @param indicator 84 | */ 85 | function showRemaining(indicator) { 86 | indicator.css({ 87 | display: 'block' 88 | }); 89 | } 90 | 91 | /** 92 | * When called shows the indicator. 93 | * 94 | * @param indicator 95 | */ 96 | function hideRemaining(indicator) { 97 | indicator.css({ 98 | display: 'none' 99 | }); 100 | } 101 | 102 | /** 103 | * This function updates the value in the indicator 104 | * 105 | * @param maxlengthIndicator 106 | * @param typedChars 107 | * @return String 108 | */ 109 | function updateMaxLengthHTML(maxLengthThisInput, typedChars) { 110 | var output = ''; 111 | if (options.preText) { 112 | output += options.preText; 113 | } 114 | if (!options.showCharsTyped) { 115 | output += remainingChars(typedChars, maxLengthThisInput); 116 | } 117 | else { 118 | output += typedChars; 119 | } 120 | if (options.showMaxLength) { 121 | output += options.separator + maxLengthThisInput; 122 | } 123 | if (options.postText) { 124 | output += options.postText; 125 | } 126 | return output; 127 | } 128 | 129 | /** 130 | * This function updates the value of the counter in the indicator. 131 | * Wants as parameters: the number of remaining chars, the element currently managed, 132 | * the maxLength for the current input and the indicator generated for it. 133 | * 134 | * @param remaining 135 | * @param currentInput 136 | * @param maxLengthCurrentInput 137 | * @param maxLengthIndicator 138 | */ 139 | function manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator) { 140 | maxLengthIndicator.html(updateMaxLengthHTML(maxLengthCurrentInput, (maxLengthCurrentInput - remaining))); 141 | 142 | if (remaining > 0) { 143 | if (charsLeftThreshold(currentInput, options.threshold, maxLengthCurrentInput)) { 144 | showRemaining(maxLengthIndicator.removeClass(options.limitReachedClass).addClass(options.warningClass)); 145 | } else { 146 | hideRemaining(maxLengthIndicator); 147 | } 148 | } else { 149 | showRemaining(maxLengthIndicator.removeClass(options.warningClass).addClass(options.limitReachedClass)); 150 | } 151 | } 152 | 153 | /** 154 | * This function returns an object containing all the 155 | * informations about the position of the current input 156 | * 157 | * @param currentInput 158 | * @return object {bottom height left right top width} 159 | * 160 | */ 161 | function getPosition(currentInput) { 162 | var el = currentInput[0]; 163 | return $.extend({}, (typeof el.getBoundingClientRect === 'function') ? el.getBoundingClientRect() : { 164 | width: el.offsetWidth, 165 | height: el.offsetHeight 166 | }, currentInput.offset()); 167 | } 168 | 169 | /** 170 | * This function places the maxLengthIndicator at the 171 | * top / bottom / left / right of the currentInput 172 | * 173 | * @param currentInput 174 | * @param maxLengthIndicator 175 | * @return null 176 | * 177 | */ 178 | function place(currentInput, maxLengthIndicator) { 179 | var pos = getPosition(currentInput), 180 | inputOuter = currentInput.outerWidth(), 181 | outerWidth = maxLengthIndicator.outerWidth(), 182 | actualWidth = maxLengthIndicator.width(), 183 | actualHeight = maxLengthIndicator.height(); 184 | 185 | switch (options.placement) { 186 | case 'bottom': 187 | maxLengthIndicator.css({top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}); 188 | break; 189 | case 'top': 190 | maxLengthIndicator.css({top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}); 191 | break; 192 | case 'left': 193 | maxLengthIndicator.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}); 194 | break; 195 | case 'right': 196 | maxLengthIndicator.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}); 197 | break; 198 | case 'bottom-right': 199 | maxLengthIndicator.css({top: pos.top + pos.height, left: pos.left + pos.width}); 200 | break; 201 | case 'top-right': 202 | maxLengthIndicator.css({top: pos.top - actualHeight, left: pos.left + inputOuter}); 203 | break; 204 | case 'top-left': 205 | maxLengthIndicator.css({top: pos.top - actualHeight, left: pos.left - outerWidth}); 206 | break; 207 | case 'bottom-left': 208 | maxLengthIndicator.css({top: pos.top + currentInput.outerHeight(), left: pos.left - outerWidth}); 209 | break; 210 | case 'centered-right': 211 | maxLengthIndicator.css({top: pos.top + (actualHeight / 2), left: pos.left + inputOuter - outerWidth - 3}); 212 | break; 213 | } 214 | } 215 | 216 | /** 217 | * This function retrieves the maximum length of currentInput 218 | * 219 | * @param currentInput 220 | * @return {number} 221 | * 222 | */ 223 | function getMaxLength(currentInput) { 224 | return currentInput.attr('maxlength') || currentInput.attr('size'); 225 | } 226 | 227 | return this.each(function() { 228 | 229 | var currentInput = $(this), 230 | maxLengthCurrentInput = getMaxLength(currentInput), 231 | maxLengthIndicator = $('').css({ 232 | display: 'none', 233 | position: 'absolute', 234 | whiteSpace: 'nowrap', 235 | zIndex: 999 236 | }).html(updateMaxLengthHTML(maxLengthCurrentInput, '0')); 237 | 238 | // We need to detect resizes if we are dealing with a textarea: 239 | if (currentInput.is('textarea')) { 240 | currentInput.data('maxlenghtsizex', currentInput.outerWidth()); 241 | currentInput.data('maxlenghtsizey', currentInput.outerHeight()); 242 | 243 | currentInput.mouseup(function() { 244 | if (currentInput.outerWidth() !== currentInput.data('maxlenghtsizex') || currentInput.outerHeight() !== currentInput.data('maxlenghtsizey')) { 245 | place(currentInput, maxLengthIndicator); 246 | } 247 | 248 | currentInput.data('maxlenghtsizex', currentInput.outerWidth()); 249 | currentInput.data('maxlenghtsizey', currentInput.outerHeight()); 250 | }); 251 | } 252 | 253 | documentBody.append(maxLengthIndicator); 254 | 255 | currentInput.focus(function() { 256 | var remaining = remainingChars(currentInput, getMaxLength(currentInput)); 257 | manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator); 258 | place(currentInput, maxLengthIndicator); 259 | }); 260 | 261 | $(window).resize(function() { 262 | place(currentInput, maxLengthIndicator); 263 | }); 264 | 265 | currentInput.blur(function() { 266 | maxLengthIndicator.css('display', 'none'); 267 | }); 268 | 269 | currentInput.keyup(function(e) { 270 | var remaining = remainingChars(currentInput, getMaxLength(currentInput)), 271 | output = true, 272 | keyCode = e.keyCode || e.which; 273 | // Handle the tab press when the maxlength have been reached. 274 | if (remaining===0 && keyCode===9) { 275 | currentInput.attr('maxlength',getMaxLength(currentInput)+1) 276 | .trigger({ 277 | type: 'keypress', 278 | which: 9 279 | }).attr('maxlength',getMaxLength(currentInput)-1); 280 | 281 | } 282 | if (options.validate && remaining < 0) { 283 | output = false; 284 | } else { 285 | manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator); 286 | } 287 | return output; 288 | }); 289 | }); 290 | } 291 | }); 292 | }(jQuery)); 293 | -------------------------------------------------------------------------------- /src/main/webapp/skin/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,0%,#e6e6e6,100%);background-image:-moz-linear-gradient(top,#fff 0,#e6e6e6 100%);background-image:linear-gradient(to bottom,#fff 0,#e6e6e6 100%);background-repeat:repeat-x;border-color:#e0e0e0;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0)}.btn-default:active,.btn-default.active{background-color:#e6e6e6;border-color:#e0e0e0}.btn-primary{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;border-color:#2d6ca2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.btn-primary:active,.btn-primary.active{background-color:#3071a9;border-color:#2d6ca2}.btn-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;border-color:#419641;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.btn-success:active,.btn-success.active{background-color:#449d44;border-color:#419641}.btn-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;border-color:#eb9316;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.btn-warning:active,.btn-warning.active{background-color:#ec971f;border-color:#eb9316}.btn-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;border-color:#c12e2a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.btn-danger:active,.btn-danger.active{background-color:#c9302c;border-color:#c12e2a}.btn-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;border-color:#2aabd2;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.btn-info:active,.btn-info.active{background-color:#31b0d5;border-color:#2aabd2}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#f8f8f8));background-image:-webkit-linear-gradient(top,#fff,0%,#f8f8f8,100%);background-image:-moz-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar .navbar-nav>.active>a{background-color:#f8f8f8}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-gradient(linear,left 0,left 100%,from(#3c3c3c),to(#222));background-image:-webkit-linear-gradient(top,#3c3c3c,0%,#222,100%);background-image:-moz-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0)}.navbar-inverse .navbar-nav>.active>a{background-color:#222}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#c8e5bc));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#c8e5bc,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#b9def0));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#b9def0,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#f8efc0));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#f8efc0,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#e7c3c3));background-image:-webkit-linear-gradient(top,#f2dede,0%,#e7c3c3,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-gradient(linear,left 0,left 100%,from(#ebebeb),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#ebebeb,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca,0%,#3071a9,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c,0%,#449d44,100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de,0%,#31b0d5,100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e,0%,#ec971f,100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f,0%,#c9302c,100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3278b3));background-image:-webkit-linear-gradient(top,#428bca,0%,#3278b3,100%);background-image:-moz-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f5f5f5),to(#e8e8e8));background-image:-webkit-linear-gradient(top,#f5f5f5,0%,#e8e8e8,100%);background-image:-moz-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#d0e9c6));background-image:-webkit-linear-gradient(top,#dff0d8,0%,#d0e9c6,100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#c4e3f3));background-image:-webkit-linear-gradient(top,#d9edf7,0%,#c4e3f3,100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#faf2cc));background-image:-webkit-linear-gradient(top,#fcf8e3,0%,#faf2cc,100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#ebcccc));background-image:-webkit-linear-gradient(top,#f2dede,0%,#ebcccc,100%);background-image:-moz-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-gradient(linear,left 0,left 100%,from(#e8e8e8),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#e8e8e8,0%,#f5f5f5,100%);background-image:-moz-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} -------------------------------------------------------------------------------- /src/main/webapp/skin/js/holder.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Holder - 2.0 - client side image placeholders 4 | (c) 2012-2013 Ivan Malopinsky / http://imsky.co 5 | 6 | Provided under the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 7 | Commercial use requires attribution. 8 | 9 | */ 10 | 11 | var Holder = Holder || {}; 12 | (function (app, win) { 13 | 14 | var preempted = false, 15 | fallback = false, 16 | canvas = document.createElement('canvas'); 17 | 18 | //getElementsByClassName polyfill 19 | document.getElementsByClassName||(document.getElementsByClassName=function(e){var t=document,n,r,i,s=[];if(t.querySelectorAll)return t.querySelectorAll("."+e);if(t.evaluate){r=".//*[contains(concat(' ', @class, ' '), ' "+e+" ')]",n=t.evaluate(r,t,null,0,null);while(i=n.iterateNext())s.push(i)}else{n=t.getElementsByTagName("*"),r=new RegExp("(^|\\s)"+e+"(\\s|$)");for(i=0;i= 0.75) { 72 | text_height = Math.floor(text_height * 0.75 * (width/text_width)); 73 | } 74 | //Resetting font size if necessary 75 | ctx.font = "bold " + (text_height * ratio) + "px " + font; 76 | ctx.fillText(text, (width / 2), (height / 2), width); 77 | return canvas.toDataURL("image/png"); 78 | } 79 | 80 | function render(mode, el, holder, src) { 81 | var dimensions = holder.dimensions, 82 | theme = holder.theme, 83 | text = holder.text ? decodeURIComponent(holder.text) : holder.text; 84 | var dimensions_caption = dimensions.width + "x" + dimensions.height; 85 | theme = (text ? extend(theme, { 86 | text: text 87 | }) : theme); 88 | theme = (holder.font ? extend(theme, { 89 | font: holder.font 90 | }) : theme); 91 | if (mode == "image") { 92 | el.setAttribute("data-src", src); 93 | el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption); 94 | if (fallback || !holder.auto) { 95 | el.style.width = dimensions.width + "px"; 96 | el.style.height = dimensions.height + "px"; 97 | } 98 | if (fallback) { 99 | el.style.backgroundColor = theme.background; 100 | } else { 101 | el.setAttribute("src", draw(ctx, dimensions, theme, ratio)); 102 | } 103 | } else if (mode == "background") { 104 | if (!fallback) { 105 | el.style.backgroundImage = "url(" + draw(ctx, dimensions, theme, ratio) + ")"; 106 | el.style.backgroundSize = dimensions.width + "px " + dimensions.height + "px"; 107 | } 108 | } else if (mode == "fluid") { 109 | el.setAttribute("data-src", src); 110 | el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption); 111 | if (dimensions.height.substr(-1) == "%") { 112 | el.style.height = dimensions.height 113 | } else { 114 | el.style.height = dimensions.height + "px" 115 | } 116 | if (dimensions.width.substr(-1) == "%") { 117 | el.style.width = dimensions.width 118 | } else { 119 | el.style.width = dimensions.width + "px" 120 | } 121 | if (el.style.display == "inline" || el.style.display == "") { 122 | el.style.display = "block"; 123 | } 124 | if (fallback) { 125 | el.style.backgroundColor = theme.background; 126 | } else { 127 | el.holderData = holder; 128 | fluid_images.push(el); 129 | fluid_update(el); 130 | } 131 | } 132 | }; 133 | 134 | function fluid_update(element) { 135 | var images; 136 | if (element.nodeType == null) { 137 | images = fluid_images; 138 | } else { 139 | images = [element] 140 | } 141 | for (i in images) { 142 | var el = images[i] 143 | if (el.holderData) { 144 | var holder = el.holderData; 145 | el.setAttribute("src", draw(ctx, { 146 | height: el.clientHeight, 147 | width: el.clientWidth 148 | }, holder.theme, ratio)); 149 | } 150 | } 151 | } 152 | 153 | function parse_flags(flags, options) { 154 | 155 | var ret = { 156 | theme: settings.themes.gray 157 | }, render = false; 158 | 159 | for (sl = flags.length, j = 0; j < sl; j++) { 160 | var flag = flags[j]; 161 | if (app.flags.dimensions.match(flag)) { 162 | render = true; 163 | ret.dimensions = app.flags.dimensions.output(flag); 164 | } else if (app.flags.fluid.match(flag)) { 165 | render = true; 166 | ret.dimensions = app.flags.fluid.output(flag); 167 | ret.fluid = true; 168 | } else if (app.flags.colors.match(flag)) { 169 | ret.theme = app.flags.colors.output(flag); 170 | } else if (options.themes[flag]) { 171 | //If a theme is specified, it will override custom colors 172 | ret.theme = options.themes[flag]; 173 | } else if (app.flags.text.match(flag)) { 174 | ret.text = app.flags.text.output(flag); 175 | } else if (app.flags.font.match(flag)) { 176 | ret.font = app.flags.font.output(flag); 177 | } else if (app.flags.auto.match(flag)) { 178 | ret.auto = true; 179 | } 180 | } 181 | 182 | return render ? ret : false; 183 | 184 | }; 185 | 186 | 187 | 188 | if (!canvas.getContext) { 189 | fallback = true; 190 | } else { 191 | if (canvas.toDataURL("image/png") 192 | .indexOf("data:image/png") < 0) { 193 | //Android doesn't support data URI 194 | fallback = true; 195 | } else { 196 | var ctx = canvas.getContext("2d"); 197 | } 198 | } 199 | 200 | var dpr = 1, bsr = 1; 201 | 202 | if(!fallback){ 203 | dpr = window.devicePixelRatio || 1, 204 | bsr = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; 205 | } 206 | 207 | var ratio = dpr / bsr; 208 | 209 | var fluid_images = []; 210 | 211 | var settings = { 212 | domain: "holder.js", 213 | images: "img", 214 | bgnodes: ".holderjs", 215 | themes: { 216 | "gray": { 217 | background: "#eee", 218 | foreground: "#aaa", 219 | size: 12 220 | }, 221 | "social": { 222 | background: "#3a5a97", 223 | foreground: "#fff", 224 | size: 12 225 | }, 226 | "industrial": { 227 | background: "#434A52", 228 | foreground: "#C2F200", 229 | size: 12 230 | } 231 | }, 232 | stylesheet: ".holderjs-fluid {font-size:16px;font-weight:bold;text-align:center;font-family:sans-serif;margin:0}" 233 | }; 234 | 235 | 236 | app.flags = { 237 | dimensions: { 238 | regex: /^(\d+)x(\d+)$/, 239 | output: function (val) { 240 | var exec = this.regex.exec(val); 241 | return { 242 | width: +exec[1], 243 | height: +exec[2] 244 | } 245 | } 246 | }, 247 | fluid: { 248 | regex: /^([0-9%]+)x([0-9%]+)$/, 249 | output: function (val) { 250 | var exec = this.regex.exec(val); 251 | return { 252 | width: exec[1], 253 | height: exec[2] 254 | } 255 | } 256 | }, 257 | colors: { 258 | regex: /#([0-9a-f]{3,})\:#([0-9a-f]{3,})/i, 259 | output: function (val) { 260 | var exec = this.regex.exec(val); 261 | return { 262 | size: settings.themes.gray.size, 263 | foreground: "#" + exec[2], 264 | background: "#" + exec[1] 265 | } 266 | } 267 | }, 268 | text: { 269 | regex: /text\:(.*)/, 270 | output: function (val) { 271 | return this.regex.exec(val)[1]; 272 | } 273 | }, 274 | font: { 275 | regex: /font\:(.*)/, 276 | output: function (val) { 277 | return this.regex.exec(val)[1]; 278 | } 279 | }, 280 | auto: { 281 | regex: /^auto$/ 282 | } 283 | } 284 | 285 | for (var flag in app.flags) { 286 | if (!app.flags.hasOwnProperty(flag)) continue; 287 | app.flags[flag].match = function (val) { 288 | return val.match(this.regex) 289 | } 290 | } 291 | 292 | app.add_theme = function (name, theme) { 293 | name != null && theme != null && (settings.themes[name] = theme); 294 | return app; 295 | }; 296 | 297 | app.add_image = function (src, el) { 298 | var node = selector(el); 299 | if (node.length) { 300 | for (var i = 0, l = node.length; i < l; i++) { 301 | var img = document.createElement("img") 302 | img.setAttribute("data-src", src); 303 | node[i].appendChild(img); 304 | } 305 | } 306 | return app; 307 | }; 308 | 309 | app.run = function (o) { 310 | var options = extend(settings, o), 311 | images = [], imageNodes = [], bgnodes = []; 312 | 313 | if(typeof(options.images) == "string"){ 314 | imageNodes = selector(options.images); 315 | } 316 | else if (window.NodeList && options.images instanceof window.NodeList) { 317 | imageNodes = options.images; 318 | } else if (window.Node && options.images instanceof window.Node) { 319 | imageNodes = [options.images]; 320 | } 321 | 322 | if(typeof(options.bgnodes) == "string"){ 323 | bgnodes = selector(options.bgnodes); 324 | } else if (window.NodeList && options.elements instanceof window.NodeList) { 325 | bgnodes = options.bgnodes; 326 | } else if (window.Node && options.bgnodes instanceof window.Node) { 327 | bgnodes = [options.bgnodes]; 328 | } 329 | 330 | preempted = true; 331 | 332 | for (i = 0, l = imageNodes.length; i < l; i++) images.push(imageNodes[i]); 333 | 334 | var holdercss = document.getElementById("holderjs-style"); 335 | if (!holdercss) { 336 | holdercss = document.createElement("style"); 337 | holdercss.setAttribute("id", "holderjs-style"); 338 | holdercss.type = "text/css"; 339 | document.getElementsByTagName("head")[0].appendChild(holdercss); 340 | } 341 | 342 | if (!options.nocss) { 343 | if (holdercss.styleSheet) { 344 | holdercss.styleSheet.cssText += options.stylesheet; 345 | } else { 346 | holdercss.appendChild(document.createTextNode(options.stylesheet)); 347 | } 348 | } 349 | 350 | var cssregex = new RegExp(options.domain + "\/(.*?)\"?\\)"); 351 | 352 | for (var l = bgnodes.length, i = 0; i < l; i++) { 353 | var src = window.getComputedStyle(bgnodes[i], null) 354 | .getPropertyValue("background-image"); 355 | var flags = src.match(cssregex); 356 | var bgsrc = bgnodes[i].getAttribute("data-background-src"); 357 | 358 | if (flags) { 359 | var holder = parse_flags(flags[1].split("/"), options); 360 | if (holder) { 361 | render("background", bgnodes[i], holder, src); 362 | } 363 | } 364 | else if(bgsrc != null){ 365 | var holder = parse_flags(bgsrc.substr(bgsrc.lastIndexOf(options.domain) + options.domain.length + 1) 366 | .split("/"), options); 367 | if(holder){ 368 | render("background", bgnodes[i], holder, src); 369 | } 370 | } 371 | } 372 | 373 | for (l = images.length, i = 0; i < l; i++) { 374 | 375 | var attr_src = attr_data_src = src = null; 376 | 377 | try{ 378 | attr_src = images[i].getAttribute("src"); 379 | attr_datasrc = images[i].getAttribute("data-src"); 380 | }catch(e){} 381 | 382 | if (attr_datasrc == null && !! attr_src && attr_src.indexOf(options.domain) >= 0) { 383 | src = attr_src; 384 | } else if ( !! attr_datasrc && attr_datasrc.indexOf(options.domain) >= 0) { 385 | src = attr_datasrc; 386 | } 387 | 388 | if (src) { 389 | var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1) 390 | .split("/"), options); 391 | if (holder) { 392 | if (holder.fluid) { 393 | render("fluid", images[i], holder, src) 394 | } else { 395 | render("image", images[i], holder, src); 396 | } 397 | } 398 | } 399 | } 400 | return app; 401 | }; 402 | 403 | contentLoaded(win, function () { 404 | if (window.addEventListener) { 405 | window.addEventListener("resize", fluid_update, false); 406 | window.addEventListener("orientationchange", fluid_update, false); 407 | } else { 408 | window.attachEvent("onresize", fluid_update) 409 | } 410 | preempted || app.run(); 411 | }); 412 | 413 | if (typeof define === "function" && define.amd) { 414 | define("Holder", [], function () { 415 | return app; 416 | }); 417 | } 418 | 419 | })(Holder, window); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright © 2013-2014 Watermelon All rights reserved. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | ====================================================================== 206 | 207 | Apache许可证 208 | 版本 2.0,2004年1月 209 | http://www.apache.org/licenses/ 210 | 211 | 使用、重生成及分发的术语和条件: 212 | 213 | 1.定义 214 | 215 | "许可证"是指根据本文档第1到第9部分关于使用、重生成和分发的术语和条件。 216 | 217 | "许可证颁发者"是指版权所有者或者由版权所有者批准的授权许可证的实体。 218 | 219 | "法律实体"是指实施实体和进行控制的所有其它实体受该实体控制,或者受该实体集中控制。 220 | 根据此定义,"控制"是指(i)让无论是否签订协议的上述实体,进行指导或管理的直接权利或间接权利, 221 | 或者(ii)拥有百分之五十(50%)或以上已发行股票的所有者,或者(iii)上述实体的实权所有者。 222 | 223 | "用户"(或"用户的")是指行使本许可证所授予权限的个人或法律实体。 224 | 225 | "源程序"形式是指对包含但不限制软件源代码、文档源程序和配置文件进行修改的首选形式。 226 | 227 | "目标"形式是指对源程序形式进行机械转换或翻译的任何形式,包括但不限于对编译的目标代码, 228 | 生成的文件以及转换为其它媒体类型。 229 | 230 | "作品"是指根据本许可证所制作的源程序形式或目标形式的著作,在著作中包含的或附加的版权通知 231 | (在下面附录中提供了一个示例)。 232 | 233 | "衍生作品"是指基于作品(或从作品衍生而来)的源程序形式或目标形式的任何作品,以及编辑修订、 234 | 注释、详细描述或其它修订等构成原创著作作品的整体。根据本许可证,衍生作品不得包括与作品及其 235 | 衍生作品分离之作品,或仅与作品及其衍生作品的接口相链接(或按名称结合)之作品。 236 | 237 | "贡献"是指任何著作作品,包括作品的原始版本和对该作品或衍生作品所做的任何修订或补充, 238 | 意在提交给许可证颁发者以让版权所有者或代表版权所有者的授权个人或法律实体包含在其作品中。 239 | 根据此定义,"提交"一词表示发送给许可证颁发者或其代表人,任何电子的、口头的或书面的交流信息形式, 240 | 包括但不限于在由许可证颁发者或者代表其管理的电子邮件清单、源代码控制系统、以及发布跟踪系统上为 241 | 讨论和提高作品的交流,但不包括由版权所有者以书面形式明显标注或指定为"非贡献"的交流活动。 242 | 243 | "贡献者"是指许可证颁发者和代表从许可证颁发者接受之贡献的并随后包含在作品之贡献中的任何个人或法律实体。 244 | 245 | 2.版权许可证的授予 246 | 247 | 根据本许可证的条款,每个贡献者授予用户永久性的、全球性的、非专有性的、免费的、无版权费的、 248 | 不可撤销的版权许可证以源程序形式或目标形式复制、准备衍生作品、公开显示、公开执行、 249 | 授予分许可证、以及分发作品和这样的衍生作品。 250 | 251 | 3.专利许可证的授予 252 | 253 | 根据本许可证的条款,每个贡献者授予用户永久性的、全球性的、非专有性的、免费的、无版权费的、 254 | 不可撤销的(除在本部分进行说明)专利许可证对作品进行制作、让人制作、使用、提供销售、销售、 255 | 进口和其它转让,且这样的许可证仅适用于在所递交作品的贡献中因可由单一的或多个这样的贡献者 256 | 授予而必须侵犯的申请专利。如果用户对任何实体针对作品或作品中所涉及贡献提出因直接性或贡献性 257 | 专利侵权而提起专利法律诉讼(包括交互诉讼请求或反索赔),那么根据本许可证,授予用户针对作品 258 | 的任何专利许可证将在提起上述诉讼之日起终止。 259 | 260 | 4.重新分发 261 | 262 | 用户可在任何媒介中复制和分发作品或衍生作品之副本,无论是否修订,还是以源程序形式或目标形式, 263 | 条件是用户需满足下列条款: 264 | 265 | a) 用户必须为作品或衍生作品的任何其他接收者提供本许可证的副本;并且 266 | 267 | b) 用户必须让任何修改过的文件附带明显的通知,声明用户已更改文件;并且 268 | 269 | c) 用户必须从作品的源程序形式中保留衍生作品源程序形式的用户所分发的所有版权、专利、 270 | 商标和属性通知,但不包括不属于衍生作品任何部分的类似通知;并且 271 | 272 | d) 如果作品将"通知"文本文件包括为其分发作品的一部分,那么用户分发的任何衍生作品中须至少 273 | 在下列地方之一包括,在这样的通知文件中所包含的属性通知的可读副本,但不包括那些不属于衍生 274 | 作品任何部分的通知:在作为衍生作品一部分而分发的通知文本文件中;如果与衍生作品一起提供则 275 | 在源程序形式或文件中;或者通常作为第三方通知出现的时候和地方,在衍生作品中产生的画面中。 276 | 通知文件的内容仅供信息提供,并未对许可证进行修改。用户可在其分发的衍生作品中在作品的通知 277 | 文本后或作为附录添加自己的属性通知,条件是附加的属性通知不得构成修改本许可证。 278 | 279 | 用户可以为自身所做出的修订添加自己的版权声明并可对自身所做出修订内容或为这样的衍生作品作为 280 | 整体的使用、复制或分发提供附加或不同的条款,条件是用户对作品的使用、复制和分发必须符合本许 281 | 可证中声明的条款。 282 | 283 | 5.贡献的提交。 284 | 285 | 除非用户明确声明,在作品中由用户向许可证颁发者的提交若要包含在贡献中,必须在无任何附加条款下 286 | 符合本许可证的条款。尽管上面如此规定,执行许可证颁发者有关贡献的条款时,任何情况下均不得替代 287 | 或修改任何单独许可证协议的条款。 288 | 289 | 6.商标。本许可证并未授予用户使用许可证颁发者的商号、商标、服务标记或产品名称,除非将这些名称 290 | 用于合理性和惯例性描述作品起源和复制通知文件的内容时。 291 | 292 | 7.保证否认条款。除非因适用法律需要或书面同意,许可证颁发者以"按原样"基础提供作品(并且每个 293 | 贡献者提供其贡献),无任何明示的或暗示的保证或条件,包括但不限于关于所有权、不侵权、 294 | 商品适销性、或适用性的保证或条件。用户仅对使用或重新分发作品的正确性负责,并需承担根据本 295 | 许可证行使权限时的任何风险。 296 | 297 | 8.责任限制条款。在任何情况下并根据任何法律,无论是因侵权(包括过失)或根据合同,还是其它原因, 298 | 除非根据适用法律需要(例如故意行为和重大过失行为)或经书面同意,即使贡献者事先已被告知发生 299 | 损害的可能性,任何贡献者不就用户因使用本许可证或不能使用或无法使用作品(包括但不限于商誉损失、 300 | 停工、计算机失效或故障,或任何商业损坏或损失)而造成的损失,包括直接的、非直接的、特殊的、意外 301 | 的或间接的字符损坏而负责。 302 | 303 | 9.接受保证或附加责任。重新分发作品或及其衍生作品时,用户可选择提供或为符合本许可证承担之支持、 304 | 担保、赔偿或其它职责义务和/或权利而收取费用。但是,在承担上述义务时,用户只可代表用户本身和 305 | 用户本身责任来执行,无需代表任何其它贡献者,并且用户仅可保证、防护并保持每个贡献者不受任何 306 | 因此而产生的责任或对因用户自身承担这样的保证或附加责任而对这样的贡献者所提出的索赔。 307 | 308 | 条款结束 309 | 310 | 附录:如何向用户作品中应用Apache许可证。 311 | 312 | 若要向用户作品应用Apache许可证,请附加下列样本通知,将括号"[]"中的字段以用户自身的 313 | 区分信息来替换(但不包括括号)。文本必须以文件格式适当的注释句法包含在其中。 314 | 另外建议将文件名或类别名以及目的说明包含在相同的"打印页"上作为版权通知,以更加容易的区分出第三方档案。 315 | 316 | 版权所有 2013-2014 Watermelon 根据2.0版本Apache许可证("许可证")授权; 317 | 根据本许可证,用户可以不使用此文件。 318 | 319 | 用户可从下列网址获得许可证副本:http://www.apache.org/licenses/LICENSE-2.0 320 | 除非因适用法律需要或书面同意,根据许可证分发的软件是基于"按原样"基础提供, 321 | 无任何明示的或暗示的保证或条件。详见根据许可证许可下,特定语言的管辖权限和限制。 322 | 323 | ======================================================= 324 | 325 | 简要解释: 326 | 327 | 1.需要给代码的用户一份Apache Licence 328 | 2.如果你修改了代码,需要在被修改的文件中说明。 329 | 3.在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标, 330 | 专利声明和其他原来作者规定需要包含的说明。 331 | 4.如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有 Apache Licence。 332 | 你可以在Notice中增加自己的许可,但不可以表现为对Apache Licence构成更改。 -------------------------------------------------------------------------------- /src/main/webapp/skin/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | .btn-default, 2 | .btn-primary, 3 | .btn-success, 4 | .btn-info, 5 | .btn-warning, 6 | .btn-danger { 7 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); 8 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); 9 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); 10 | } 11 | 12 | .btn-default:active, 13 | .btn-primary:active, 14 | .btn-success:active, 15 | .btn-info:active, 16 | .btn-warning:active, 17 | .btn-danger:active, 18 | .btn-default.active, 19 | .btn-primary.active, 20 | .btn-success.active, 21 | .btn-info.active, 22 | .btn-warning.active, 23 | .btn-danger.active { 24 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 25 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); 26 | } 27 | 28 | .btn:active, 29 | .btn.active { 30 | background-image: none; 31 | } 32 | 33 | .btn-default { 34 | text-shadow: 0 1px 0 #fff; 35 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e6e6e6)); 36 | background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e6e6e6, 100%); 37 | background-image: -moz-linear-gradient(top, #ffffff 0%, #e6e6e6 100%); 38 | background-image: linear-gradient(to bottom, #ffffff 0%, #e6e6e6 100%); 39 | background-repeat: repeat-x; 40 | border-color: #e0e0e0; 41 | border-color: #ccc; 42 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); 43 | } 44 | 45 | .btn-default:active, 46 | .btn-default.active { 47 | background-color: #e6e6e6; 48 | border-color: #e0e0e0; 49 | } 50 | 51 | .btn-primary { 52 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9)); 53 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%); 54 | background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%); 55 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 56 | background-repeat: repeat-x; 57 | border-color: #2d6ca2; 58 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 59 | } 60 | 61 | .btn-primary:active, 62 | .btn-primary.active { 63 | background-color: #3071a9; 64 | border-color: #2d6ca2; 65 | } 66 | 67 | .btn-success { 68 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44)); 69 | background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%); 70 | background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%); 71 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 72 | background-repeat: repeat-x; 73 | border-color: #419641; 74 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 75 | } 76 | 77 | .btn-success:active, 78 | .btn-success.active { 79 | background-color: #449d44; 80 | border-color: #419641; 81 | } 82 | 83 | .btn-warning { 84 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f)); 85 | background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%); 86 | background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 87 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 88 | background-repeat: repeat-x; 89 | border-color: #eb9316; 90 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 91 | } 92 | 93 | .btn-warning:active, 94 | .btn-warning.active { 95 | background-color: #ec971f; 96 | border-color: #eb9316; 97 | } 98 | 99 | .btn-danger { 100 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c)); 101 | background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%); 102 | background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%); 103 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 104 | background-repeat: repeat-x; 105 | border-color: #c12e2a; 106 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 107 | } 108 | 109 | .btn-danger:active, 110 | .btn-danger.active { 111 | background-color: #c9302c; 112 | border-color: #c12e2a; 113 | } 114 | 115 | .btn-info { 116 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5)); 117 | background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%); 118 | background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 119 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 120 | background-repeat: repeat-x; 121 | border-color: #2aabd2; 122 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 123 | } 124 | 125 | .btn-info:active, 126 | .btn-info.active { 127 | background-color: #31b0d5; 128 | border-color: #2aabd2; 129 | } 130 | 131 | .thumbnail, 132 | .img-thumbnail { 133 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 134 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 135 | } 136 | 137 | .dropdown-menu > li > a:hover, 138 | .dropdown-menu > li > a:focus, 139 | .dropdown-menu > .active > a, 140 | .dropdown-menu > .active > a:hover, 141 | .dropdown-menu > .active > a:focus { 142 | background-color: #357ebd; 143 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd)); 144 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%); 145 | background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%); 146 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 147 | background-repeat: repeat-x; 148 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 149 | } 150 | 151 | .navbar { 152 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#f8f8f8)); 153 | background-image: -webkit-linear-gradient(top, #ffffff, 0%, #f8f8f8, 100%); 154 | background-image: -moz-linear-gradient(top, #ffffff 0%, #f8f8f8 100%); 155 | background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%); 156 | background-repeat: repeat-x; 157 | border-radius: 4px; 158 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 159 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); 160 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075); 161 | } 162 | 163 | .navbar .navbar-nav > .active > a { 164 | background-color: #f8f8f8; 165 | } 166 | 167 | .navbar-brand, 168 | .navbar-nav > li > a { 169 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); 170 | } 171 | 172 | .navbar-inverse { 173 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#3c3c3c), to(#222222)); 174 | background-image: -webkit-linear-gradient(top, #3c3c3c, 0%, #222222, 100%); 175 | background-image: -moz-linear-gradient(top, #3c3c3c 0%, #222222 100%); 176 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%); 177 | background-repeat: repeat-x; 178 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 179 | } 180 | 181 | .navbar-inverse .navbar-nav > .active > a { 182 | background-color: #222222; 183 | } 184 | 185 | .navbar-inverse .navbar-brand, 186 | .navbar-inverse .navbar-nav > li > a { 187 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 188 | } 189 | 190 | .navbar-static-top, 191 | .navbar-fixed-top, 192 | .navbar-fixed-bottom { 193 | border-radius: 0; 194 | } 195 | 196 | .alert { 197 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2); 198 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 199 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); 200 | } 201 | 202 | .alert-success { 203 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#c8e5bc)); 204 | background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #c8e5bc, 100%); 205 | background-image: -moz-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 206 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 207 | background-repeat: repeat-x; 208 | border-color: #b2dba1; 209 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 210 | } 211 | 212 | .alert-info { 213 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#b9def0)); 214 | background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #b9def0, 100%); 215 | background-image: -moz-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 216 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 217 | background-repeat: repeat-x; 218 | border-color: #9acfea; 219 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 220 | } 221 | 222 | .alert-warning { 223 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#f8efc0)); 224 | background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #f8efc0, 100%); 225 | background-image: -moz-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 226 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 227 | background-repeat: repeat-x; 228 | border-color: #f5e79e; 229 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 230 | } 231 | 232 | .alert-danger { 233 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#e7c3c3)); 234 | background-image: -webkit-linear-gradient(top, #f2dede, 0%, #e7c3c3, 100%); 235 | background-image: -moz-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 236 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 237 | background-repeat: repeat-x; 238 | border-color: #dca7a7; 239 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 240 | } 241 | 242 | .progress { 243 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ebebeb), to(#f5f5f5)); 244 | background-image: -webkit-linear-gradient(top, #ebebeb, 0%, #f5f5f5, 100%); 245 | background-image: -moz-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 246 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 247 | background-repeat: repeat-x; 248 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 249 | } 250 | 251 | .progress-bar { 252 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9)); 253 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%); 254 | background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%); 255 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 256 | background-repeat: repeat-x; 257 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 258 | } 259 | 260 | .progress-bar-success { 261 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44)); 262 | background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%); 263 | background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%); 264 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 265 | background-repeat: repeat-x; 266 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 267 | } 268 | 269 | .progress-bar-info { 270 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5)); 271 | background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%); 272 | background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 273 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 274 | background-repeat: repeat-x; 275 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 276 | } 277 | 278 | .progress-bar-warning { 279 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f)); 280 | background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%); 281 | background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 282 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 283 | background-repeat: repeat-x; 284 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 285 | } 286 | 287 | .progress-bar-danger { 288 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c)); 289 | background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%); 290 | background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%); 291 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 292 | background-repeat: repeat-x; 293 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 294 | } 295 | 296 | .list-group { 297 | border-radius: 4px; 298 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 299 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 300 | } 301 | 302 | .list-group-item.active, 303 | .list-group-item.active:hover, 304 | .list-group-item.active:focus { 305 | text-shadow: 0 -1px 0 #3071a9; 306 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3278b3)); 307 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #3278b3, 100%); 308 | background-image: -moz-linear-gradient(top, #428bca 0%, #3278b3 100%); 309 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); 310 | background-repeat: repeat-x; 311 | border-color: #3278b3; 312 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); 313 | } 314 | 315 | .panel { 316 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 317 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 318 | } 319 | 320 | .panel-default > .panel-heading { 321 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f5f5f5), to(#e8e8e8)); 322 | background-image: -webkit-linear-gradient(top, #f5f5f5, 0%, #e8e8e8, 100%); 323 | background-image: -moz-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 324 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 325 | background-repeat: repeat-x; 326 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 327 | } 328 | 329 | .panel-primary > .panel-heading { 330 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd)); 331 | background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%); 332 | background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%); 333 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 334 | background-repeat: repeat-x; 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 336 | } 337 | 338 | .panel-success > .panel-heading { 339 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#d0e9c6)); 340 | background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #d0e9c6, 100%); 341 | background-image: -moz-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 342 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 343 | background-repeat: repeat-x; 344 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 345 | } 346 | 347 | .panel-info > .panel-heading { 348 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#c4e3f3)); 349 | background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #c4e3f3, 100%); 350 | background-image: -moz-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 351 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 352 | background-repeat: repeat-x; 353 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 354 | } 355 | 356 | .panel-warning > .panel-heading { 357 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#faf2cc)); 358 | background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #faf2cc, 100%); 359 | background-image: -moz-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 360 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 361 | background-repeat: repeat-x; 362 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 363 | } 364 | 365 | .panel-danger > .panel-heading { 366 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#ebcccc)); 367 | background-image: -webkit-linear-gradient(top, #f2dede, 0%, #ebcccc, 100%); 368 | background-image: -moz-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 369 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 370 | background-repeat: repeat-x; 371 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 372 | } 373 | 374 | .well { 375 | background-image: -webkit-gradient(linear, left 0%, left 100%, from(#e8e8e8), to(#f5f5f5)); 376 | background-image: -webkit-linear-gradient(top, #e8e8e8, 0%, #f5f5f5, 100%); 377 | background-image: -moz-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 378 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 379 | background-repeat: repeat-x; 380 | border-color: #dcdcdc; 381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 382 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); 383 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1); 384 | } -------------------------------------------------------------------------------- /src/main/webapp/skin/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap.js v3.0.0 by @fat and @mdo 3 | * Copyright 2013 Twitter Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery); --------------------------------------------------------------------------------