From fa41bbc491bc3fa201f408e026e0886a8651db70 Mon Sep 17 00:00:00 2001 From: RYAN0UP_ Date: Tue, 10 Apr 2018 20:52:46 +0800 Subject: [PATCH] =?UTF-8?q?:alien:=20=E5=AE=8C=E5=96=84=E6=96=87=E7=AB=A0?= =?UTF-8?q?=E7=B3=BB=E7=BB=9Fing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ryanc/halo/repository/TagRepository.java | 9 + .../cc/ryanc/halo/service/TagService.java | 16 + .../halo/service/impl/TagServiceImpl.java | 37 ++ .../java/cc/ryanc/halo/util/HaloUtil.java | 14 +- .../halo/web/controller/IndexController.java | 4 +- .../web/controller/admin/AdminController.java | 2 +- .../admin/AttachmentController.java | 4 +- .../controller/admin/BackupController.java | 2 +- .../controller/admin/CategoryController.java | 4 +- .../controller/admin/CommentController.java | 8 +- .../controller/admin/OptionController.java | 2 +- .../web/controller/admin/PageController.java | 4 +- .../web/controller/admin/PostController.java | 29 +- .../web/controller/admin/TagController.java | 4 +- .../web/controller/admin/ThemeController.java | 6 +- .../web/controller/admin/UserController.java | 7 +- src/main/resources/application.yaml | 2 +- .../jquery-tageditor/jquery.caret.min.js | 2 + .../jquery-tageditor/jquery.tag-editor.css | 45 +++ .../jquery-tageditor/jquery.tag-editor.js | 370 ++++++++++++++++++ .../jquery-tageditor/jquery.tag-editor.min.js | 3 + .../templates/admin/admin_md-editor.ftl | 45 ++- .../resources/templates/admin/admin_post.ftl | 10 +- 23 files changed, 577 insertions(+), 52 deletions(-) create mode 100644 src/main/resources/static/plugins/jquery-tageditor/jquery.caret.min.js create mode 100644 src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.css create mode 100644 src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.js create mode 100644 src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.min.js diff --git a/src/main/java/cc/ryanc/halo/repository/TagRepository.java b/src/main/java/cc/ryanc/halo/repository/TagRepository.java index 3b2acabaf..e56190b4b 100644 --- a/src/main/java/cc/ryanc/halo/repository/TagRepository.java +++ b/src/main/java/cc/ryanc/halo/repository/TagRepository.java @@ -13,8 +13,17 @@ public interface TagRepository extends JpaRepository{ /** * 根据标签路径查询,用于验证是否已经存在该路径 + * * @param tagUrl tagUrl * @return tag */ Tag findTagByTagUrl(String tagUrl); + + /** + * 根据标签名称查询 + * + * @param tagName 标签名 + * @return tag + */ + Tag findTagByTagName(String tagName); } diff --git a/src/main/java/cc/ryanc/halo/service/TagService.java b/src/main/java/cc/ryanc/halo/service/TagService.java index 4da7a2b7d..668a2eeb2 100644 --- a/src/main/java/cc/ryanc/halo/service/TagService.java +++ b/src/main/java/cc/ryanc/halo/service/TagService.java @@ -51,4 +51,20 @@ public interface TagService { * @return tag */ Tag findByTagUrl(String tagUrl); + + /** + * 根据标签名称查询 + * + * @param tagName tagName + * @return tag + */ + Tag findTagByTagName(String tagName); + + /** + * 转换标签字符串为实体集合 + * + * @param tagList tagList + * @return list + */ + List strListToTagList(String tagList); } diff --git a/src/main/java/cc/ryanc/halo/service/impl/TagServiceImpl.java b/src/main/java/cc/ryanc/halo/service/impl/TagServiceImpl.java index 4ab7e89cb..e9ff13aa1 100644 --- a/src/main/java/cc/ryanc/halo/service/impl/TagServiceImpl.java +++ b/src/main/java/cc/ryanc/halo/service/impl/TagServiceImpl.java @@ -6,6 +6,7 @@ import cc.ryanc.halo.service.TagService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -80,4 +81,40 @@ public class TagServiceImpl implements TagService { public Tag findByTagUrl(String tagUrl) { return tagRepository.findTagByTagUrl(tagUrl); } + + /** + * 根据标签名称查询 + * + * @param tagName tagName + * @return tag + */ + @Override + public Tag findTagByTagName(String tagName) { + return tagRepository.findTagByTagName(tagName); + } + + /** + * 转换标签字符串为实体集合 + * + * @param tagList tagList + * @return list + */ + @Override + public List strListToTagList(String tagList) { + String [] tags = tagList.split(","); + List tagsList = new ArrayList<>(); + for(String tag:tags){ + Tag t = findTagByTagName(tag); + Tag nt = null; + if(null!=t){ + tagsList.add(t); + }else{ + nt = new Tag(); + nt.setTagName(tag); + nt.setTagUrl(tag); + tagsList.add(saveByTag(nt)); + } + } + return tagsList; + } } diff --git a/src/main/java/cc/ryanc/halo/util/HaloUtil.java b/src/main/java/cc/ryanc/halo/util/HaloUtil.java index 6dd52a5e5..018046750 100755 --- a/src/main/java/cc/ryanc/halo/util/HaloUtil.java +++ b/src/main/java/cc/ryanc/halo/util/HaloUtil.java @@ -94,7 +94,7 @@ public class HaloUtil { in.close(); } } catch (Exception e) { - log.error("解压失败:"+e.getMessage()); + log.error("解压失败:{0}",e.getMessage()); }finally{ try { if(zip!=null) @@ -104,7 +104,7 @@ public class HaloUtil { if(out!=null) out.close(); } catch (IOException e) { - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } } } @@ -174,7 +174,7 @@ public class HaloUtil { BufferedImage bi = reader.read(0,param); ImageIO.write(bi, suffix, new File(dest)); }catch (Exception e){ - log.error("剪裁失败,图片本身尺寸小于需要修剪的尺寸:"+e.getMessage()); + log.error("剪裁失败,图片本身尺寸小于需要修剪的尺寸:{0}",e.getMessage()); } } @@ -200,7 +200,7 @@ public class HaloUtil { } } }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return FILE_LIST; } @@ -234,7 +234,7 @@ public class HaloUtil { } } }catch (Exception e){ - log.error("主题获取失败:"+e.getMessage()); + log.error("主题获取失败:{0}",e.getMessage()); } return themes; } @@ -273,7 +273,7 @@ public class HaloUtil { } } }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return tpls; } @@ -293,7 +293,7 @@ public class HaloUtil { inputStream.close(); return new String(fileContent,"UTF-8"); }catch (Exception e){ - log.error("读取模板文件错误:"+e.getMessage()); + log.error("读取模板文件错误:{0}",e.getMessage()); } return null; } diff --git a/src/main/java/cc/ryanc/halo/web/controller/IndexController.java b/src/main/java/cc/ryanc/halo/web/controller/IndexController.java index ea9b9f38e..a24738e4e 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/IndexController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/IndexController.java @@ -170,7 +170,7 @@ public class IndexController extends BaseController{ model.addAttribute("afterPost",afterPosts.get(afterPosts.size()-1)); } }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } model.addAttribute("post",post); @@ -514,7 +514,7 @@ public class IndexController extends BaseController{ map.put("commentContent",comment.getCommentContent()); mailService.sendTemplateMail(userService.findUser().getUserEmail(),"有新的评论",map,"common/mail/mail_admin.ftl"); }catch (Exception e){ - log.error("邮件服务器未配置:"+e.getMessage()); + log.error("邮件服务器未配置:{0}",e.getMessage()); } } return true; diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java index 7aa10f6f4..5fdb41ac4 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java @@ -149,7 +149,7 @@ public class AdminController extends BaseController{ } userService.updateUserLoginLast(new Date()); logsService.saveByLogs(new Logs(LogsRecord.LOGIN,LogsRecord.LOGIN_ERROR,HaloUtil.getIpAddr(request),new Date())); - log.error("登录失败!:"+e.getMessage()); + log.error("登录失败!:{0}",e.getMessage()); } return status; } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/AttachmentController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/AttachmentController.java index a423086a1..0fab1098b 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/AttachmentController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/AttachmentController.java @@ -151,7 +151,7 @@ public class AttachmentController { result.put("message","上传成功!"); result.put("url",attachment.getAttachPath()); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); result.put("success","0"); result.put("message","上传失败!"); } @@ -222,7 +222,7 @@ public class AttachmentController { } } }catch (Exception e){ - log.error("删除附件["+delFileName+"]失败!"+e.getMessage()); + log.error("删除附件["+delFileName+"]失败!:",e.getMessage()); return false; } return true; diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/BackupController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/BackupController.java index 3418df711..93e5dabb1 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/BackupController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/BackupController.java @@ -56,7 +56,7 @@ public class BackupController { String savePath = path.getAbsolutePath()+"/backup/database"; HaloUtil.exportDatabase("localhost","root","123456",savePath,fileName,"testdb"); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/backup"; } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/CategoryController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/CategoryController.java index 4d45355a8..757906522 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/CategoryController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/CategoryController.java @@ -54,7 +54,7 @@ public class CategoryController { try{ categoryService.saveByCategory(category); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/category"; } @@ -88,7 +88,7 @@ public class CategoryController { Category category = categoryService.removeByCateId(cateId); log.info("删除的分类目录:"+category); } catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/category"; } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/CommentController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/CommentController.java index 5181ed355..9c8bfe260 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/CommentController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/CommentController.java @@ -93,7 +93,7 @@ public class CommentController extends BaseController{ commentService.updateCommentStatus(commentId,2); this.getNewComments(session); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/comments"; } @@ -131,7 +131,7 @@ public class CommentController extends BaseController{ "您在" + HaloConst.OPTIONS.get("site_title") + "的评论已审核通过!", map, "common/mail/mail_passed.ftl"); } } catch (Exception e) { - log.error("邮件服务器未配置:" + e.getMessage()); + log.error("邮件服务器未配置:{0}",e.getMessage()); } } this.getNewComments(session); @@ -154,7 +154,7 @@ public class CommentController extends BaseController{ commentService.removeByCommentId(commentId); this.getNewComments(session); }catch (Exception e){ - log.error("删除评论失败:"+e.getMessage()); + log.error("删除评论失败:{0}",e.getMessage()); } return "redirect:/admin/comments?status="+status; } @@ -225,7 +225,7 @@ public class CommentController extends BaseController{ } } }catch (Exception e){ - log.error("回复评论失败!"+e.getMessage()); + log.error("回复评论失败!{0}",e.getMessage()); } return "redirect:/admin/comments"; } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/OptionController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/OptionController.java index 3731d94eb..510910c06 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/OptionController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/OptionController.java @@ -50,7 +50,7 @@ public class OptionController { log.info("所保存的设置选项列表:"+options); return true; }catch (Exception e){ - log.error("未知错误:",e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); return false; } } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/PageController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/PageController.java index 25c6ea24c..b01f27e28 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/PageController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/PageController.java @@ -96,7 +96,7 @@ public class PageController { Link backLink = linkService.saveByLink(link); log.info("保存成功,数据为:"+backLink); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/page/links"; } @@ -113,7 +113,7 @@ public class PageController { Link link = linkService.removeByLinkId(linkId); log.info("删除的友情链接:"+link); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/page/links"; } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/PostController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/PostController.java index 66e8afc27..b3d592df0 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/PostController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/PostController.java @@ -1,14 +1,12 @@ package cc.ryanc.halo.web.controller.admin; -import cc.ryanc.halo.model.domain.Category; -import cc.ryanc.halo.model.domain.Logs; -import cc.ryanc.halo.model.domain.Post; -import cc.ryanc.halo.model.domain.User; +import cc.ryanc.halo.model.domain.*; import cc.ryanc.halo.model.dto.HaloConst; import cc.ryanc.halo.model.dto.LogsRecord; import cc.ryanc.halo.service.CategoryService; import cc.ryanc.halo.service.LogsService; import cc.ryanc.halo.service.PostService; +import cc.ryanc.halo.service.TagService; import cc.ryanc.halo.util.HaloUtil; import cc.ryanc.halo.web.controller.BaseController; import lombok.extern.slf4j.Slf4j; @@ -44,6 +42,9 @@ public class PostController extends BaseController{ @Autowired private CategoryService categoryService; + @Autowired + private TagService tagService; + @Autowired private LogsService logsService; @@ -98,7 +99,7 @@ public class PostController extends BaseController{ Pageable pageable = new PageRequest(page,size,sort); model.addAttribute("posts",postService.searchPosts(keyword,pageable)); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "admin/admin_post"; } @@ -133,7 +134,7 @@ public class PostController extends BaseController{ //设置选项 model.addAttribute("options",HaloConst.OPTIONS); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "admin/admin_editor"; } @@ -145,7 +146,7 @@ public class PostController extends BaseController{ */ @PostMapping(value = "/new/push") @ResponseBody - public void pushPost(@ModelAttribute Post post,@RequestParam("cateList") List cateList, HttpSession session){ + public void pushPost(@ModelAttribute Post post, @RequestParam("cateList") List cateList, @RequestParam("tagList") String tagList, HttpSession session){ try{ //提取摘要 int postSummary = 50; @@ -162,11 +163,13 @@ public class PostController extends BaseController{ post.setUser(user); List categories = categoryService.strListToCateList(cateList); post.setCategories(categories); + List tags = tagService.strListToTagList(tagList); + post.setTags(tags); postService.saveByPost(post); log.info("已发表新文章:"+post.getPostTitle()); logsService.saveByLogs(new Logs(LogsRecord.PUSH_POST,post.getPostTitle(),HaloUtil.getIpAddr(request),HaloUtil.getDate())); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } } @@ -182,7 +185,7 @@ public class PostController extends BaseController{ postService.updatePostStatus(postId,2); log.info("编号为"+postId+"的文章已被移到回收站"); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/posts"; } @@ -200,7 +203,7 @@ public class PostController extends BaseController{ postService.updatePostStatus(postId,0); log.info("编号为"+postId+"的文章已改变为发布状态"); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/posts?status="+status; } @@ -218,7 +221,7 @@ public class PostController extends BaseController{ postService.removeByPostId(postId); logsService.saveByLogs(new Logs(LogsRecord.REMOVE_POST,post.get().getPostTitle(),HaloUtil.getIpAddr(request),HaloUtil.getDate())); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/posts?status=2"; } @@ -241,7 +244,7 @@ public class PostController extends BaseController{ //设置选项 model.addAttribute("options",HaloConst.OPTIONS); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "admin/admin_editor"; } @@ -259,7 +262,7 @@ public class PostController extends BaseController{ postService.updateAllSummary(postSummary); return true; }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); return false; } } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/TagController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/TagController.java index c72e97df5..bfd2edf1e 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/TagController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/TagController.java @@ -53,7 +53,7 @@ public class TagController { try{ tagService.saveByTag(tag); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/tag"; } @@ -87,7 +87,7 @@ public class TagController { Tag tag = tagService.removeByTagId(tagId); log.info("删除的标签:"+tag); }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); } return "redirect:/admin/tag"; } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/ThemeController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/ThemeController.java index adbad2154..867f10e83 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/ThemeController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/ThemeController.java @@ -111,7 +111,7 @@ public class ThemeController extends BaseController{ log.error("上传失败,没有选择文件"); } }catch (Exception e){ - log.error("上传失败:"+e.getMessage()); + log.error("上传失败:{0}",e.getMessage()); } return false; } @@ -159,7 +159,7 @@ public class ThemeController extends BaseController{ File themesPath = new File(basePath.getAbsolutePath(),new StringBuffer("templates/themes/").append(BaseController.THEME).append("/").append(tplName).toString()); tplContent = HaloUtil.getFileContent(themesPath.getAbsolutePath()); }catch (Exception e){ - log.error("获取模板文件错误:"+e.getMessage()); + log.error("获取模板文件错误:{0}",e.getMessage()); } return tplContent; } @@ -186,7 +186,7 @@ public class ThemeController extends BaseController{ byte[] tplContentByte = tplContent.getBytes("UTF-8"); Files.write(Paths.get(tplPath.getAbsolutePath()),tplContentByte); }catch (Exception e){ - log.error("文件保存失败:"+e.getMessage()); + log.error("文件保存失败:{0}",e.getMessage()); return false; } return true; diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/UserController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/UserController.java index fab8c82d3..d2767c8eb 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/UserController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/UserController.java @@ -53,11 +53,10 @@ public class UserController { userService.saveByUser(user); session.invalidate(); }else{ - log.error("用户信息不能为空值"); return false; } }catch (Exception e){ - log.error("未知错误:"+e.getMessage()); + log.error("未知错误:{0}",e.getMessage()); return false; } return true; @@ -81,14 +80,12 @@ public class UserController { if(null!=user){ user.setUserPass(HaloUtil.getMD5(newPass)); userService.saveByUser(user); - log.info("修改密码:成功"); session.invalidate(); }else{ - log.error("修改密码:原密码错误!"); return false; } }catch (Exception e){ - log.error("修改密码:未知错误,"+e.getMessage()); + log.error("修改密码:未知错误,{0}",e.getMessage()); return false; } return true; diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index efacec492..1abc4755a 100755 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,5 +1,5 @@ server: - port: 8090 + port: 8080 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource diff --git a/src/main/resources/static/plugins/jquery-tageditor/jquery.caret.min.js b/src/main/resources/static/plugins/jquery-tageditor/jquery.caret.min.js new file mode 100644 index 000000000..0f9ef48d6 --- /dev/null +++ b/src/main/resources/static/plugins/jquery-tageditor/jquery.caret.min.js @@ -0,0 +1,2 @@ +// http://code.accursoft.com/caret - 1.3.3 +!function(e){e.fn.caret=function(e){var t=this[0],n="true"===t.contentEditable;if(0==arguments.length){if(window.getSelection){if(n){t.focus();var o=window.getSelection().getRangeAt(0),r=o.cloneRange();return r.selectNodeContents(t),r.setEnd(o.endContainer,o.endOffset),r.toString().length}return t.selectionStart}if(document.selection){if(t.focus(),n){var o=document.selection.createRange(),r=document.body.createTextRange();return r.moveToElementText(t),r.setEndPoint("EndToEnd",o),r.text.length}var e=0,c=t.createTextRange(),r=document.selection.createRange().duplicate(),a=r.getBookmark();for(c.moveToBookmark(a);0!==c.moveStart("character",-1);)e++;return e}return t.selectionStart?t.selectionStart:0}if(-1==e&&(e=this[n?"text":"val"]().length),window.getSelection)n?(t.focus(),window.getSelection().collapse(t.firstChild,e)):t.setSelectionRange(e,e);else if(document.body.createTextRange)if(n){var c=document.body.createTextRange();c.moveToElementText(t),c.moveStart("character",e),c.collapse(!0),c.select()}else{var c=t.createTextRange();c.move("character",e),c.select()}return n||t.focus(),e}}(jQuery); diff --git a/src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.css b/src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.css new file mode 100644 index 000000000..8529741c5 --- /dev/null +++ b/src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.css @@ -0,0 +1,45 @@ +/* surrounding tag container */ +.tag-editor { + list-style-type: none; padding:5px; margin: 0; overflow: hidden; border: 1px solid #eee; cursor: text; + font: normal 14px sans-serif; color: #555; background: #fff; line-height: 20px; +} + +/* core styles usually need no change */ +.tag-editor li { display: block; float: left; overflow: hidden; margin: 3px 0; } +.tag-editor div { float: left; padding: 0 4px; } +.tag-editor .placeholder { padding: 0 8px; color: #bbb; } +.tag-editor .tag-editor-spacer { padding: 0; width: 8px; overflow: hidden; color: transparent; background: none; } +.tag-editor input { + vertical-align: inherit; border: 0; outline: none; padding: 0; margin: 0; cursor: text; + font-family: inherit; font-weight: inherit; font-size: inherit; font-style: inherit; + box-shadow: none; background: none; color: #444; +} +/* hide original input field or textarea visually to allow tab navigation */ +.tag-editor-hidden-src { position: absolute !important; left: -99999px; } +/* hide IE10 "clear field" X */ +.tag-editor ::-ms-clear { display: none; } + +/* tag style */ +.tag-editor .tag-editor-tag { + padding-left: 5px; color: #46799b; background: #e0eaf1; white-space: nowrap; + overflow: hidden; cursor: pointer; border-radius: 2px 0 0 2px; +} + +/* delete icon */ +.tag-editor .tag-editor-delete { background: #e0eaf1; cursor: pointer; border-radius: 0 2px 2px 0; padding-left: 3px; padding-right: 4px; } +.tag-editor .tag-editor-delete i { line-height: 18px; display: inline-block; } +.tag-editor .tag-editor-delete i:before { font-size: 16px; color: #8ba7ba; content: "×"; font-style: normal; } +.tag-editor .tag-editor-delete:hover i:before { color: #d65454; } +.tag-editor .tag-editor-tag.active+.tag-editor-delete, .tag-editor .tag-editor-tag.active+.tag-editor-delete i { visibility: hidden; cursor: text; } + +.tag-editor .tag-editor-tag.active { background: none !important; } + +/* jQuery UI autocomplete - code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css */ +.ui-autocomplete { position: absolute; top: 0; left: 0; cursor: default; font-size: 14px; } +.ui-front { z-index: 9999; } +.ui-menu { list-style: none; padding: 1px; margin: 0; display: block; outline: none; } +.ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.4; min-height: 0; /* support: IE7 */ } +.ui-widget-content { border: 1px solid #bbb; background: #fff; color: #555; } +.ui-widget-content a { color: #46799b; } +.ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { background: #e0eaf1; } +.ui-helper-hidden-accessible { display: none; } diff --git a/src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.js b/src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.js new file mode 100644 index 000000000..719ae0422 --- /dev/null +++ b/src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.js @@ -0,0 +1,370 @@ +/* + jQuery tagEditor v1.0.21 + Copyright (c) 2014 Simon Steinberger / Pixabay + GitHub: https://github.com/Pixabay/jQuery-tagEditor + License: http://www.opensource.org/licenses/mit-license.php +*/ + +(function($){ + // auto grow input (stackoverflow.com/questions/931207) + $.fn.tagEditorInput=function(){var t=" ",e=$(this),n=parseInt(e.css("fontSize")),i=$("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:e.css("fontSize"),fontFamily:e.css("fontFamily"),fontWeight:e.css("fontWeight"),letterSpacing:e.css("letterSpacing"),whiteSpace:"nowrap"}),s=function(){if(t!==(t=e.val())){i.text(t);var s=i.width()+n;20>s&&(s=20),s!=e.width()&&e.width(s)}};return i.insertAfter(e),e.bind("keyup keydown focus",s)}; + + // plugin with val as parameter for public methods + $.fn.tagEditor = function(options, val, blur){ + + // helper + function escape(tag) { + return tag.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); + } + + // build options dictionary with default values + var blur_result, o = $.extend({}, $.fn.tagEditor.defaults, options), selector = this; + + // store regex and default delimiter in options for later use + o.dregex = new RegExp('['+o.delimiter.replace('-', '\-')+']', 'g'); + + // public methods + if (typeof options == 'string') { + // depending on selector, response may contain tag lists of multiple editor instances + var response = []; + selector.each(function(){ + // the editor is the next sibling to the hidden, original field + var el = $(this), o = el.data('options'), ed = el.next('.tag-editor'); + if (options == 'getTags') + response.push({field: el[0], editor: ed, tags: ed.data('tags')}); + else if (options == 'addTag') { + if (o.maxTags && ed.data('tags').length >= o.maxTags) return false; + // insert new tag + $('
  •  '+o.delimiter[0]+'
  • ').appendTo(ed).find('.tag-editor-tag') + .html('').addClass('active').find('input').val(val).blur(); + if (!blur) ed.click(); + else $('.placeholder', ed).remove(); + } else if (options == 'removeTag') { + // trigger delete on matching tag, then click editor to create a new tag + $('.tag-editor-tag', ed).filter(function(){return $(this).text()==val;}).closest('li').find('.tag-editor-delete').click(); + if (!blur) ed.click(); + } else if (options == 'destroy') { + el.removeClass('tag-editor-hidden-src').removeData('options').off('focus.tag-editor').next('.tag-editor').remove(); + } + }); + return options == 'getTags' ? response : this; + } + + // delete selected tags on backspace, delete, ctrl+x + if (window.getSelection) $(document).off('keydown.tag-editor').on('keydown.tag-editor', function(e){ + if (e.which == 8 || e.which == 46 || e.ctrlKey && e.which == 88) { + try { + var sel = getSelection(), el = document.activeElement.tagName != 'INPUT' ? $(sel.getRangeAt(0).startContainer.parentNode).closest('.tag-editor') : 0; + } catch(e){ el = 0; } + if (sel.rangeCount > 0 && el && el.length) { + var tags = [], splits = sel.toString().split(el.prev().data('options').dregex); + for (i=0; i').insertAfter(el); + el.addClass('tag-editor-hidden-src') // hide original field + .data('options', o) // set data on hidden field + .on('focus.tag-editor', function(){ ed.click(); }); // simulate tabindex + + // add dummy item for min-height on empty editor + ed.append('
  •  
  • '); + + // markup for new tag + var new_tag = '
  •  '+o.delimiter[0]+'
  • '; + + // helper: update global data + function set_placeholder(){ + if (o.placeholder && !tag_list.length && !$('.deleted, .placeholder, input', ed).length) + ed.append('
  • '+o.placeholder+'
  • '); + } + + // helper: update global data + function update_globals(init){ + var old_tags = tag_list.toString(); + tag_list = $('.tag-editor-tag:not(.deleted)', ed).map(function(i, e) { + var val = $.trim($(this).hasClass('active') ? $(this).find('input').val() : $(e).text()); + if (val) return val; + }).get(); + ed.data('tags', tag_list); + el.val(tag_list.join(o.delimiter[0])); + // change callback except for plugin init + if (!init) if (old_tags != tag_list.toString()) o.onChange(el, ed, tag_list); + set_placeholder(); + } + + ed.click(function(e, closest_tag){ + var d, dist = 99999, loc; + + // do not create tag when user selects tags by text selection + if (window.getSelection && getSelection() != '') return; + + if (o.maxTags && ed.data('tags').length >= o.maxTags) { ed.find('input').blur(); return false; } + + blur_result = true + $('input:focus', ed).blur(); + if (!blur_result) return false; + blur_result = true + + // always remove placeholder on click + $('.placeholder', ed).remove(); + if (closest_tag && closest_tag.length) + loc = 'before'; + else { + // calculate tag closest to click position + $('.tag-editor-tag', ed).each(function(){ + var tag = $(this), to = tag.offset(), tag_x = to.left, tag_y = to.top; + if (e.pageY >= tag_y && e.pageY <= tag_y+tag.height()) { + if (e.pageX < tag_x) loc = 'before', d = tag_x - e.pageX; + else loc = 'after', d = e.pageX - tag_x - tag.width(); + if (d < dist) dist = d, closest_tag = tag; + } + }); + } + + if (loc == 'before') { + $(new_tag).insertBefore(closest_tag.closest('li')).find('.tag-editor-tag').click(); + } else if (loc == 'after') + $(new_tag).insertAfter(closest_tag.closest('li')).find('.tag-editor-tag').click(); + else // empty editor + $(new_tag).appendTo(ed).find('.tag-editor-tag').click(); + return false; + }); + + ed.on('click', '.tag-editor-delete', function(e){ + // delete icon is hidden when input is visible; place cursor near invisible delete icon on click + if ($(this).prev().hasClass('active')) { $(this).closest('li').find('input').caret(-1); return false; } + + var li = $(this).closest('li'), tag = li.find('.tag-editor-tag'); + if (o.beforeTagDelete(el, ed, tag_list, tag.text()) === false) return false; + tag.addClass('deleted').animate({width: 0}, o.animateDelete, function(){ li.remove(); set_placeholder(); }); + update_globals(); + return false; + }); + + // delete on right mouse click or ctrl+click + if (o.clickDelete) + ed.on('mousedown', '.tag-editor-tag', function(e){ + if (e.ctrlKey || e.which > 1) { + var li = $(this).closest('li'), tag = li.find('.tag-editor-tag'); + if (o.beforeTagDelete(el, ed, tag_list, tag.text()) === false) return false; + tag.addClass('deleted').animate({width: 0}, o.animateDelete, function(){ li.remove(); set_placeholder(); }); + update_globals(); + return false; + } + }); + + ed.on('click', '.tag-editor-tag', function(e){ + // delete on right click or ctrl+click -> exit + if (o.clickDelete && (e.ctrlKey || e.which > 1)) return false; + + if (!$(this).hasClass('active')) { + var tag = $(this).text(); + // guess cursor position in text input + var left_percent = Math.abs(($(this).offset().left - e.pageX)/$(this).width()), caret_pos = parseInt(tag.length*left_percent), + input = $(this).html('').addClass('active').find('input'); + input.data('old_tag', tag).tagEditorInput().focus().caret(caret_pos); + if (o.autocomplete) { + var aco = $.extend({}, o.autocomplete); + // extend user provided autocomplete select method + var ac_select = 'select' in aco ? o.autocomplete.select : ''; + aco.select = function(e, ui){ if (ac_select) ac_select(e, ui); setTimeout(function(){ + ed.trigger('click', [$('.active', ed).find('input').closest('li').next('li').find('.tag-editor-tag')]); + }, 20); }; + input.autocomplete(aco); + } + } + return false; + }); + + // helper: split into multiple tags, e.g. after paste + function split_cleanup(input){ + var li = input.closest('li'), sub_tags = input.val().replace(/ +/, ' ').split(o.dregex), + old_tag = input.data('old_tag'), old_tags = tag_list.slice(0), exceeded = false, cb_val; // copy tag_list + for (var i=0; i
     '+o.delimiter[0]+'
    '+escape(tag)+'
    '); + if (o.maxTags && old_tags.length >= o.maxTags) { exceeded = true; break; } + } + input.attr('maxlength', o.maxLength).removeData('old_tag').val('') + if (exceeded) input.blur(); else input.focus(); + update_globals(); + } + + ed.on('blur', 'input', function(e){ + e.stopPropagation(); + var input = $(this), old_tag = input.data('old_tag'), tag = $.trim(input.val().replace(/ +/, ' ').replace(o.dregex, o.delimiter[0])); + if (!tag) { + if (old_tag && o.beforeTagDelete(el, ed, tag_list, old_tag) === false) { + input.val(old_tag).focus(); + blur_result = false; + update_globals(); + return; + } + try { input.closest('li').remove(); } catch(e){} + if (old_tag) update_globals(); + } + else if (tag.indexOf(o.delimiter[0])>=0) { split_cleanup(input); return; } + else if (tag != old_tag) { + if (o.forceLowercase) tag = tag.toLowerCase(); + cb_val = o.beforeTagSave(el, ed, tag_list, old_tag, tag); + tag = cb_val || tag; + if (cb_val === false) { + if (old_tag) { + input.val(old_tag).focus(); + blur_result = false; + update_globals(); + return; + } + try { input.closest('li').remove(); } catch(e){} + if (old_tag) update_globals(); + } + // remove duplicates + else if (o.removeDuplicates) + $('.tag-editor-tag:not(.active)', ed).each(function(){ if ($(this).text() == tag) $(this).closest('li').remove(); }); + } + input.parent().html(escape(tag)).removeClass('active'); + if (tag != old_tag) update_globals(); + set_placeholder(); + }); + + var pasted_content; + ed.on('paste', 'input', function(e){ + $(this).removeAttr('maxlength'); + pasted_content = $(this); + setTimeout(function(){ split_cleanup(pasted_content); }, 30); + }); + + // keypress delimiter + var inp; + ed.on('keypress', 'input', function(e){ + if (o.delimiter.indexOf(String.fromCharCode(e.which))>=0) { + inp = $(this); + setTimeout(function(){ split_cleanup(inp); }, 20); + } + }); + + ed.on('keydown', 'input', function(e){ + var $t = $(this); + + // left/up key + backspace key on empty field + if ((e.which == 37 || !o.autocomplete && e.which == 38) && !$t.caret() || e.which == 8 && !$t.val()) { + var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag'); + if (prev_tag.length) prev_tag.click().find('input').caret(-1); + else if ($t.val() && !(o.maxTags && ed.data('tags').length >= o.maxTags)) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click(); + return false; + } + // right/down key + else if ((e.which == 39 || !o.autocomplete && e.which == 40) && ($t.caret() == $t.val().length)) { + var next_tag = $t.closest('li').next('li').find('.tag-editor-tag'); + if (next_tag.length) next_tag.click().find('input').caret(0); + else if ($t.val()) ed.click(); + return false; + } + // tab key + else if (e.which == 9) { + // shift+tab + if (e.shiftKey) { + var prev_tag = $t.closest('li').prev('li').find('.tag-editor-tag'); + if (prev_tag.length) prev_tag.click().find('input').caret(0); + else if ($t.val() && !(o.maxTags && ed.data('tags').length >= o.maxTags)) $(new_tag).insertBefore($t.closest('li')).find('.tag-editor-tag').click(); + // allow tabbing to previous element + else { + el.attr('disabled', 'disabled'); + setTimeout(function(){ el.removeAttr('disabled'); }, 30); + return; + } + return false; + // tab + } else { + var next_tag = $t.closest('li').next('li').find('.tag-editor-tag'); + if (next_tag.length) next_tag.click().find('input').caret(0); + else if ($t.val()) ed.click(); + else return; // allow tabbing to next element + return false; + } + } + // del key + else if (e.which == 46 && (!$.trim($t.val()) || ($t.caret() == $t.val().length))) { + var next_tag = $t.closest('li').next('li').find('.tag-editor-tag'); + if (next_tag.length) next_tag.click().find('input').caret(0); + else if ($t.val()) ed.click(); + return false; + } + // enter key + else if (e.which == 13) { + ed.trigger('click', [$t.closest('li').next('li').find('.tag-editor-tag')]); + + // trigger blur if maxTags limit is reached + if (o.maxTags && ed.data('tags').length >= o.maxTags) ed.find('input').blur(); + + return false; + } + // pos1 + else if (e.which == 36 && !$t.caret()) ed.find('.tag-editor-tag').first().click(); + // end + else if (e.which == 35 && $t.caret() == $t.val().length) ed.find('.tag-editor-tag').last().click(); + // esc + else if (e.which == 27) { + $t.val($t.data('old_tag') ? $t.data('old_tag') : '').blur(); + return false; + } + }); + + // create initial tags + var tags = o.initialTags.length ? o.initialTags : el.val().split(o.dregex); + for (var i=0; i= o.maxTags) break; + var tag = $.trim(tags[i].replace(/ +/, ' ')); + if (tag) { + if (o.forceLowercase) tag = tag.toLowerCase(); + tag_list.push(tag); + ed.append('
  •  '+o.delimiter[0]+'
    '+escape(tag)+'
  • '); + } + } + update_globals(true); // true -> no onChange callback + + // init sortable + if (o.sortable && $.fn.sortable) ed.sortable({ + distance: 5, cancel: '.tag-editor-spacer, input', helper: 'clone', + update: function(){ update_globals(); } + }); + }); + }; + + $.fn.tagEditor.defaults = { + initialTags: [], + maxTags: 0, + maxLength: 50, + delimiter: ',;', + placeholder: '', + forceLowercase: true, + removeDuplicates: true, + clickDelete: false, + animateDelete: 175, + sortable: true, // jQuery UI sortable + autocomplete: null, // options dict for jQuery UI autocomplete + + // callbacks + onChange: function(){}, + beforeTagSave: function(){}, + beforeTagDelete: function(){} + }; +}(jQuery)); diff --git a/src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.min.js b/src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.min.js new file mode 100644 index 000000000..94293448f --- /dev/null +++ b/src/main/resources/static/plugins/jquery-tageditor/jquery.tag-editor.min.js @@ -0,0 +1,3 @@ +// jQuery tagEditor v1.0.21 +// https://github.com/Pixabay/jQuery-tagEditor +!function(t){t.fn.tagEditorInput=function(){var e=" ",i=t(this),a=parseInt(i.css("fontSize")),r=t("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:i.css("fontSize"),fontFamily:i.css("fontFamily"),fontWeight:i.css("fontWeight"),letterSpacing:i.css("letterSpacing"),whiteSpace:"nowrap"}),l=function(){if(e!==(e=i.val())){r.text(e);var t=r.width()+a;20>t&&(t=20),t!=i.width()&&i.width(t)}};return r.insertAfter(i),i.bind("keyup keydown focus",l)},t.fn.tagEditor=function(e,a,r){function l(t){return t.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}var n,o=t.extend({},t.fn.tagEditor.defaults,e),c=this;if(o.dregex=new RegExp("["+o.delimiter.replace("-","-")+"]","g"),"string"==typeof e){var s=[];return c.each(function(){var i=t(this),l=i.data("options"),n=i.next(".tag-editor");if("getTags"==e)s.push({field:i[0],editor:n,tags:n.data("tags")});else if("addTag"==e){if(l.maxTags&&n.data("tags").length>=l.maxTags)return!1;t('
  •  '+l.delimiter[0]+'
  • ').appendTo(n).find(".tag-editor-tag").html('').addClass("active").find("input").val(a).blur(),r?t(".placeholder",n).remove():n.click()}else"removeTag"==e?(t(".tag-editor-tag",n).filter(function(){return t(this).text()==a}).closest("li").find(".tag-editor-delete").click(),r||n.click()):"destroy"==e&&i.removeClass("tag-editor-hidden-src").removeData("options").off("focus.tag-editor").next(".tag-editor").remove()}),"getTags"==e?s:this}return window.getSelection&&t(document).off("keydown.tag-editor").on("keydown.tag-editor",function(e){if(8==e.which||46==e.which||e.ctrlKey&&88==e.which){try{var a=getSelection(),r="INPUT"!=document.activeElement.tagName?t(a.getRangeAt(0).startContainer.parentNode).closest(".tag-editor"):0}catch(e){r=0}if(a.rangeCount>0&&r&&r.length){var l=[],n=a.toString().split(r.prev().data("options").dregex);for(i=0;i
    '+o.placeholder+"
    ")}function i(i){var a=c.toString();c=t(".tag-editor-tag:not(.deleted)",s).map(function(e,i){var a=t.trim(t(this).hasClass("active")?t(this).find("input").val():t(i).text());return a?a:void 0}).get(),s.data("tags",c),r.val(c.join(o.delimiter[0])),i||a!=c.toString()&&o.onChange(r,s,c),e()}function a(e){for(var a,n=e.closest("li"),d=e.val().replace(/ +/," ").split(o.dregex),g=e.data("old_tag"),f=c.slice(0),h=!1,u=0;u
     '+o.delimiter[0]+'
    '+l(v)+'
    '),o.maxTags&&f.length>=o.maxTags)){h=!0;break}e.attr("maxlength",o.maxLength).removeData("old_tag").val(""),h?e.blur():e.focus(),i()}var r=t(this),c=[],s=t("
      ').insertAfter(r);r.addClass("tag-editor-hidden-src").data("options",o).on("focus.tag-editor",function(){s.click()}),s.append('
    •  
    • ');var d='
    •  '+o.delimiter[0]+'
    • ';s.click(function(e,i){var a,r,l=99999;if(!window.getSelection||""==getSelection())return o.maxTags&&s.data("tags").length>=o.maxTags?(s.find("input").blur(),!1):(n=!0,t("input:focus",s).blur(),n?(n=!0,t(".placeholder",s).remove(),i&&i.length?r="before":t(".tag-editor-tag",s).each(function(){var n=t(this),o=n.offset(),c=o.left,s=o.top;e.pageY>=s&&e.pageY<=s+n.height()&&(e.pageXa&&(l=a,i=n))}),"before"==r?t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click():"after"==r?t(d).insertAfter(i.closest("li")).find(".tag-editor-tag").click():t(d).appendTo(s).find(".tag-editor-tag").click(),!1):!1)}),s.on("click",".tag-editor-delete",function(){if(t(this).prev().hasClass("active"))return t(this).closest("li").find("input").caret(-1),!1;var a=t(this).closest("li"),l=a.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,l.text())===!1?!1:(l.addClass("deleted").animate({width:0},o.animateDelete,function(){a.remove(),e()}),i(),!1)}),o.clickDelete&&s.on("mousedown",".tag-editor-tag",function(a){if(a.ctrlKey||a.which>1){var l=t(this).closest("li"),n=l.find(".tag-editor-tag");return o.beforeTagDelete(r,s,c,n.text())===!1?!1:(n.addClass("deleted").animate({width:0},o.animateDelete,function(){l.remove(),e()}),i(),!1)}}),s.on("click",".tag-editor-tag",function(e){if(o.clickDelete&&(e.ctrlKey||e.which>1))return!1;if(!t(this).hasClass("active")){var i=t(this).text(),a=Math.abs((t(this).offset().left-e.pageX)/t(this).width()),r=parseInt(i.length*a),n=t(this).html('').addClass("active").find("input");if(n.data("old_tag",i).tagEditorInput().focus().caret(r),o.autocomplete){var c=t.extend({},o.autocomplete),d="select"in c?o.autocomplete.select:"";c.select=function(e,i){d&&d(e,i),setTimeout(function(){s.trigger("click",[t(".active",s).find("input").closest("li").next("li").find(".tag-editor-tag")])},20)},n.autocomplete(c)}}return!1}),s.on("blur","input",function(d){d.stopPropagation();var g=t(this),f=g.data("old_tag"),h=t.trim(g.val().replace(/ +/," ").replace(o.dregex,o.delimiter[0]));if(h){if(h.indexOf(o.delimiter[0])>=0)return void a(g);if(h!=f)if(o.forceLowercase&&(h=h.toLowerCase()),cb_val=o.beforeTagSave(r,s,c,f,h),h=cb_val||h,cb_val===!1){if(f)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}else o.removeDuplicates&&t(".tag-editor-tag:not(.active)",s).each(function(){t(this).text()==h&&t(this).closest("li").remove()})}else{if(f&&o.beforeTagDelete(r,s,c,f)===!1)return g.val(f).focus(),n=!1,void i();try{g.closest("li").remove()}catch(d){}f&&i()}g.parent().html(l(h)).removeClass("active"),h!=f&&i(),e()});var g;s.on("paste","input",function(){t(this).removeAttr("maxlength"),g=t(this),setTimeout(function(){a(g)},30)});var f;s.on("keypress","input",function(e){o.delimiter.indexOf(String.fromCharCode(e.which))>=0&&(f=t(this),setTimeout(function(){a(f)},20))}),s.on("keydown","input",function(e){var i=t(this);if((37==e.which||!o.autocomplete&&38==e.which)&&!i.caret()||8==e.which&&!i.val()){var a=i.closest("li").prev("li").find(".tag-editor-tag");return a.length?a.click().find("input").caret(-1):!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags||t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click(),!1}if((39==e.which||!o.autocomplete&&40==e.which)&&i.caret()==i.val().length){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(9==e.which){if(e.shiftKey){var a=i.closest("li").prev("li").find(".tag-editor-tag");if(a.length)a.click().find("input").caret(0);else{if(!i.val()||o.maxTags&&s.data("tags").length>=o.maxTags)return r.attr("disabled","disabled"),void setTimeout(function(){r.removeAttr("disabled")},30);t(d).insertBefore(i.closest("li")).find(".tag-editor-tag").click()}return!1}var l=i.closest("li").next("li").find(".tag-editor-tag");if(l.length)l.click().find("input").caret(0);else{if(!i.val())return;s.click()}return!1}if(!(46!=e.which||t.trim(i.val())&&i.caret()!=i.val().length)){var l=i.closest("li").next("li").find(".tag-editor-tag");return l.length?l.click().find("input").caret(0):i.val()&&s.click(),!1}if(13==e.which)return s.trigger("click",[i.closest("li").next("li").find(".tag-editor-tag")]),o.maxTags&&s.data("tags").length>=o.maxTags&&s.find("input").blur(),!1;if(36!=e.which||i.caret()){if(35==e.which&&i.caret()==i.val().length)s.find(".tag-editor-tag").last().click();else if(27==e.which)return i.val(i.data("old_tag")?i.data("old_tag"):"").blur(),!1}else s.find(".tag-editor-tag").first().click()});for(var h=o.initialTags.length?o.initialTags:r.val().split(o.dregex),u=0;u=o.maxTags);u++){var v=t.trim(h[u].replace(/ +/," "));v&&(o.forceLowercase&&(v=v.toLowerCase()),c.push(v),s.append('
    •  '+o.delimiter[0]+'
      '+l(v)+'
    • '))}i(!0),o.sortable&&t.fn.sortable&&s.sortable({distance:5,cancel:".tag-editor-spacer, input",helper:"clone",update:function(){i()}})})},t.fn.tagEditor.defaults={initialTags:[],maxTags:0,maxLength:50,delimiter:",;",placeholder:"",forceLowercase:!0,removeDuplicates:!0,clickDelete:!1,animateDelete:175,sortable:!0,autocomplete:null,onChange:function(){},beforeTagSave:function(){},beforeTagDelete:function(){}}}(jQuery); \ No newline at end of file diff --git a/src/main/resources/templates/admin/admin_md-editor.ftl b/src/main/resources/templates/admin/admin_md-editor.ftl index f881213d2..f5741548c 100644 --- a/src/main/resources/templates/admin/admin_md-editor.ftl +++ b/src/main/resources/templates/admin/admin_md-editor.ftl @@ -9,6 +9,7 @@
      +