diff --git a/Readme.md b/Readme.md
index 75c0974..29a06c8 100644
--- a/Readme.md
+++ b/Readme.md
@@ -8,4 +8,17 @@
2020/5/29 : 增加Properties文件字段读取两种方式(propertiesScan),PostConstruct的使用、BeanPostProcessor在Bean初始化前后进行处理(init)、统一异常处理的两种方式(exceptionHandler)、统一出参报文处理(response)
新增上下文刷新判断处理类(refreshEvent)、添加HttpSession监听器(listener)
-2020/6/3 :增加Servlet监听器启动Schedule定时任务(SchemeTaskListener)
\ No newline at end of file
+2020/6/3 :增加Servlet监听器启动Schedule定时任务(SchemeTaskListener)
+
+2020/6/30 : 新增WangEditor使用示例
+
+2020/6/30 :优化类名、@auther、添加控制层测试
+ CacheController 缓存
+ EncodedController 编码
+ FreemarkerController 模板
+ FullFillTimeValueController 补时间
+ ITextController pdf生成
+ LoginController 登录控制
+ RedisController redis操作
+ RequestLimitController 接口访问频率控制
+ WangEditorController WangEditor编辑器使用
\ No newline at end of file
diff --git a/src/main/java/Aspect/AopTestClass.java b/src/main/java/Aspect/AopTestClass.java
index 3a441ea..aa65c94 100644
--- a/src/main/java/Aspect/AopTestClass.java
+++ b/src/main/java/Aspect/AopTestClass.java
@@ -10,11 +10,11 @@ import org.aspectj.lang.annotation.*;
*/
@Aspect
public class AopTestClass {
- @Before("execution(* service.MainServiceImpl.*(..))")
+ @Before("execution(* service.CommonServiceImpl.*(..))")
public void begin(){
System.out.println("表演开始");
}
- @After("execution(* service.MainServiceImpl.*(..))")
+ @After("execution(* service.CommonServiceImpl.*(..))")
public void end(){
System.out.println("表演结束");
}
@@ -23,11 +23,11 @@ public class AopTestClass {
* 通过这个声明来实现环绕通知,所谓的环绕通知就是说在在一个方法中规定了类运行几个状态直接的通知事件
* 可以这样声明来实现不用写一大串的代码
*/
- @Pointcut("execution(* service.MainServiceImpl.*(..))")
+ @Pointcut("execution(* service.CommonServiceImpl.*(..))")
public void perform(){}
@Around("perform()")
- public void AroundAop(ProceedingJoinPoint proceedingJoinPoint){
+ public Object aroundAop(ProceedingJoinPoint proceedingJoinPoint){
try{
System.out.println("我在程序运行之前进行了通知");
//这个方法代表被切接口进行执行,上面的方法表示之前执行,下面的方法表示之后执行
@@ -37,17 +37,18 @@ public class AopTestClass {
throwable.printStackTrace();
System.out.println("运行错误产生的aop通知");
}
+ return null;
}
/**
* 这个是测试有传参的Aop接口环绕
* @param testNum 入参,根据方法决定入参的数量与类型
*/
- @Pointcut("execution(* service.MainServiceImpl.testAopParam(int)) && args(testNum)")
+ @Pointcut("execution(* service.CommonServiceImpl.testAopParam(int)) && args(testNum)")
public void testAopParams(int testNum){
}
- @After("testAopParams(testNum)")
+ @After(value = "testAopParams(testNum)", argNames = "testNum")
public void testHasParam(int testNum){
System.out.println(testNum);
}
diff --git a/src/main/java/Aspect/SensitiveAop.java b/src/main/java/Aspect/SensitiveAop.java
index c7f2de4..4215ea4 100644
--- a/src/main/java/Aspect/SensitiveAop.java
+++ b/src/main/java/Aspect/SensitiveAop.java
@@ -29,6 +29,7 @@ public class SensitiveAop {
*/
@Around("execution(* controller.*.*(..)) && @annotation(sensitive)")
public Object getClassMessage(ProceedingJoinPoint pjp, Sensitive sensitive) {
-
- }
+ //todo 这边需要怎么去处理,我还没想清楚
+ return null;
+ }
}
diff --git a/src/main/java/adapter/FreeMarkerConfigurationAdapter.java b/src/main/java/adapter/FreeMarkerConfigurationAdapter.java
index c1a276f..10dc5b4 100644
--- a/src/main/java/adapter/FreeMarkerConfigurationAdapter.java
+++ b/src/main/java/adapter/FreeMarkerConfigurationAdapter.java
@@ -36,19 +36,23 @@ import java.lang.reflect.InvocationTargetException;
*/
public class FreeMarkerConfigurationAdapter {
- //配置器的类路径
+ /**
+ * 配置器的类路径
+ */
private String filePath;
- //默认路径配置
- private final String defaultPath = "tpl";
- //编码配置
+ /**
+ * 编码配置
+ */
private String encoding;
- //默认编码配置
- private final String defaultEncoding = "UTF-8";
- //错误解决类配置
+ /**
+ * 错误解决类配置
+ */
private TemplateExceptionHandler exceptionHandler;
- //默认错误解决类配置
+ /**
+ * 默认错误解决类配置
+ */
private final TemplateExceptionHandler defaultExceptionHandler = TemplateExceptionHandler.RETHROW_HANDLER;
public FreeMarkerConfigurationAdapter() {
@@ -64,25 +68,21 @@ public class FreeMarkerConfigurationAdapter {
*
* 我们使用反射的机制,构造有参数的构造器,作为一个适配器,接受生成继承Configuration的子类对象
- * 相当于web.xml的加载配置
- */
-
-/***
+ * 相当于web.xml的加载配置
+ *
* 这里使用了反射的机制,可以了解一下
- * @param clazz
- * @param
* 1.类的有一些参数是可以使用默认设置的,我们这边给他一个使用默认设置的机会
* 2.类的有一些参数也可以是用户来配置的,所以说这里我们提供了一个设置参数的机会
*/
- return freeMarkerConfigurationAdapter.createConfiguration(AlarmTplConfiguration.class);
+ return freeMarkerConfigurationAdapter.createConfiguration(TplService.class);
}
diff --git a/src/main/java/config/WebInitializer.java b/src/main/java/config/WebInitializer.java
index 9a6480e..c6bf8fb 100644
--- a/src/main/java/config/WebInitializer.java
+++ b/src/main/java/config/WebInitializer.java
@@ -19,18 +19,17 @@ import filter.FilterDelegator;
import filter.WelcomeHandlingFilter;
/**
+ *
* Created by com on 2017/7/25.
*
Copyright: Copyright (c) 2018
+ *
Company:网宿科技
+ *
@author Administrator
+ *
@description 功能描述
+ *
2018-12-26 22:11:24
+ */
+
+package controller;
+
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Controller
+@RequestMapping("/encoded/test/")
+public class EncodedController {
+
+ /**
+ * POST的方式,如果后端不设置过滤器去拦截请求并设置请求编码的话
+ * 会根据Content-type的character来标识
+ */
+
+ @RequestMapping(value = "/post-default")
+ public void postDefault(HttpServletRequest request){
+ System.out.println(request.getCharacterEncoding());
+ }
+
+ @RequestMapping(value = "/post-utf8")
+ public void postUtf8(HttpServletRequest request){
+ System.out.println(request.getCharacterEncoding());
+ }
+
+
+ @RequestMapping(value = "/post-gbk")
+ public void postGbk(HttpServletRequest request){
+ System.out.println(request.getCharacterEncoding());
+ }
+
+ @RequestMapping(value = "/get")
+ public void getGbk(HttpServletRequest request){
+ System.out.println(request.getParameter("testName"));
+ }
+}
diff --git a/src/main/java/controller/FreemarkerController.java b/src/main/java/controller/FreemarkerController.java
new file mode 100644
index 0000000..145253a
--- /dev/null
+++ b/src/main/java/controller/FreemarkerController.java
@@ -0,0 +1,72 @@
+/*
+ * @(#) freemarkerController
+ * 版权声明 黄志军, 版权所有 违者必究
+ *
+ *
Copyright: Copyright (c) 2018
+ *
Company:黄志军
+ *
@author selfImpr
+ *
2018-05-21 10:04:51
+ *
@description
+ *
+ *
+ */
+
+package controller;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import model.tpl.TplMessage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.freemarker.FreeMarkerConfigurationFactory;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
+import service.TplService;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+
+
+@Controller
+public class FreemarkerController {
+
+ @Autowired
+ private TplService tplService;
+
+ @RequestMapping("/freemaker")
+ private String freeMaker() throws IOException, TemplateException {
+ TplMessage tplMessage = new TplMessage();
+ tplMessage.setIdentification("[mx1234567890]");
+ DateFormat dateFormat = new SimpleDateFormat("yyyy年mm月dd HH:MM:ss");
+ tplMessage.setTime(dateFormat.format(new Date()));
+ tplMessage.setPlatform("XXXX平台");
+ tplMessage.setStatistic("123333");
+
+ Template temp = tplService.getTemplate("alarm.tpl");
+ Writer out = new StringWriter();
+ temp.process(tplMessage, out);
+ System.out.print(temp.toString());
+ return "";
+ }
+
+
+ @RequestMapping("springMaker")
+ public void springMarker() throws IOException, TemplateException {
+ FreeMarkerConfigurationFactory freeMarkerConfigurationFactory = new FreeMarkerConfigurationFactory();
+ freeMarkerConfigurationFactory.setDefaultEncoding("UTF-8");
+ Configuration configuration = freeMarkerConfigurationFactory.createConfiguration();
+
+ FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
+ freeMarkerConfigurer.setConfiguration(configuration);
+
+ }
+
+}
+
+
diff --git a/src/main/java/controller/FullFillTimeValueController.java b/src/main/java/controller/FullFillTimeValueController.java
new file mode 100644
index 0000000..2b29133
--- /dev/null
+++ b/src/main/java/controller/FullFillTimeValueController.java
@@ -0,0 +1,73 @@
+/*
+ * @(#) FullFillTimeValueController
+ * 版权声明 网宿科技, 版权所有 违者必究
+ *
+ *
Copyright: Copyright (c) 2018
+ *
Company:网宿科技
+ *
@author Administrator
+ *
@description 功能描述
+ *
2018-11-24 01:14:40
+ */
+
+package controller;
+
+import com.google.common.collect.Lists;
+import model.fullFillTime.OriginTimeValue;
+import model.fullFillTime.TargetTimeValue;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import util.time.FillFullTimeValueUtil;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.TimeZone;
+
+/**
+ * @author 志军
+ * 测试补时间点
+ */
+@Controller
+public class FullFillTimeValueController {
+
+
+ @RequestMapping(value = "/time-test")
+ public void fullFillTimeValue() {
+ List
Copyright: Copyright (c) 2018
+ *
Company:黄志军
+ *
@author selfImpr
+ *
2018-06-22 09:16:02
+ *
@description
+ *
+ *
+ */
+
+package controller;
+
+import model.redis.RedisUser;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import javax.annotation.Resource;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 为了方便使用,直接把到层的操作放在这里面
+ */
+ @Controller
+public class RedisController {
+
+ @Resource(name = "redisTemplate")
+ private RedisTemplate redisTemplate;
+
+
+ /**
+ * 非复杂对象的操作
+ * 参考地址: http://www.cnblogs.com/qlqwjy/p/8562703.html
+ */
+
+ @RequestMapping(value = "/redis/simple", method = RequestMethod.GET)
+ public String simpleOperation() {
+ //String操作
+ redisTemplate.opsForValue().set("myStr", "skyLine");
+ redisTemplate.delete("myStr");
+ redisTemplate.opsForValue().set("myStr", "skyLine");
+ System.out.println(redisTemplate.opsForValue().get("myStr"));
+ System.out.println("---------------");
+ // List读写
+ redisTemplate.delete("myList");
+ redisTemplate.opsForList().rightPush("myList", "T");
+ redisTemplate.opsForList().rightPush("myList", "L");
+ redisTemplate.opsForList().leftPush("myList", "A");
+ List
Copyright: Copyright (c) 2018
+ *
Company:网宿科技
+ *
@author huangzj1
+ *
@description 功能描述
+ *
2018-12-16 17:27:24
+ */
+
+package controller;
+
+import logic.requestLimit.RequestLimit;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * ${DESCRIPTION}
+ *
+ * @author huangzj1
+ * @date 2018-12-16 17:27
+ */
+@Controller
+public class RequestLimitController {
+
+ @RequestMapping(value = "/request/limit")
+ @RequestLimit(time = 55, count = 2, waits = 100)
+ @ResponseBody
+ public String requestLimit(HttpServletRequest request) {
+ System.out.println("我被打扰了");
+ return "我被打扰了";
+ }
+}
diff --git a/src/main/java/controller/WangEditorController.java b/src/main/java/controller/WangEditorController.java
new file mode 100644
index 0000000..17caa86
--- /dev/null
+++ b/src/main/java/controller/WangEditorController.java
@@ -0,0 +1,112 @@
+package controller;
+
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.fileupload.FileUploadException;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import tool.WangEditorTool;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+
+
+/**
+ * @author 志军
+ * 用来测试WangEditor插件
+ */
+
+@Controller
+@RequestMapping("")
+public class WangEditorController {
+
+ @RequestMapping(value = "/Editor/index",method = RequestMethod.GET)
+ public String index(){
+ return "wangEditor";
+ }
+
+
+ @RequestMapping("/Editor/updateImage")
+ public void updateImage(HttpServletRequest request, HttpServletResponse response) throws FileUploadException, InterruptedException, IOException {
+ WangEditorTool tool = new WangEditorTool(request);
+ tool.execRequest();
+ String fileName = "http://localhost:8033/Spring/" + tool.getMap().get("FilePath").toString().substring(78);
+
+ String href = "
欢迎使用 wangEditor 富文本编辑器
欢迎使用 wangEditor 富文本编辑器
+设置标题
'), + type: 'list', // droplist 以列表形式展示 + list: [{ $elem: $('正文
'), value: '' }], + onClick: function onClick(value) { + // 注意 this 是指向当前的 Head 对象 + _this._command(value); + } + }); +} + +// 原型 +Head.prototype = { + constructor: Head, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + + var $selectionElem = editor.selection.getSelectionContainerElem(); + if (editor.$textElem.equal($selectionElem)) { + // 不能选中多行来设置标题,否则会出现问题 + // 例如选中的是
xxx
yyy
来设置标题,设置之后会成为设置列表
'), + type: 'list', // droplist 以列表形式展示 + list: [{ $elem: $(' 有序列表'), value: 'insertOrderedList' }, { $elem: $(' 无序列表'), value: 'insertUnorderedList' }], + onClick: function onClick(value) { + // 注意 this 是指向当前的 List 对象 + _this._command(value); + } + }); +} + +// 原型 +List.prototype = { + constructor: List, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + var $textElem = editor.$textElem; + editor.selection.restoreSelection(); + if (editor.cmd.queryCommandState(value)) { + return; + } + editor.cmd.do(value); + + // 验证列表是否被包裹在之内 + var $selectionElem = editor.selection.getSelectionContainerElem(); + if ($selectionElem.getNodeName() === 'LI') { + $selectionElem = $selectionElem.parent(); + } + if (/^ol|ul$/i.test($selectionElem.getNodeName()) === false) { + return; + } + if ($selectionElem.equal($textElem)) { + // 证明是顶级标签,没有被
包裹 + return; + } + var $parent = $selectionElem.parent(); + if ($parent.equal($textElem)) { + // $parent 是顶级标签,不能删除 + return; + } + + $selectionElem.insertAfter($parent); + $parent.remove(); + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + if (editor.cmd.queryCommandState('insertUnOrderedList') || editor.cmd.queryCommandState('insertOrderedList')) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + menu - justify +*/ +// 构造函数 +function Justify(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $('
'); + this.type = 'droplist'; + + // 当前是否 active 状态 + this._active = false; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 100, + $title: $('对齐方式
'), + type: 'list', // droplist 以列表形式展示 + list: [{ $elem: $(' 靠左'), value: 'justifyLeft' }, { $elem: $(' 居中'), value: 'justifyCenter' }, { $elem: $(' 靠右'), value: 'justifyRight' }], + onClick: function onClick(value) { + // 注意 this 是指向当前的 List 对象 + _this._command(value); + } + }); +} + +// 原型 +Justify.prototype = { + constructor: Justify, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + editor.cmd.do(value); + } +}; + +/* + menu - Forecolor +*/ +// 构造函数 +function ForeColor(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $(''); + this.type = 'droplist'; + + // 当前是否 active 状态 + this._active = false; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 120, + $title: $('文字颜色
'), + type: 'inline-block', // droplist 内容以 block 形式展示 + list: [{ $elem: $(''), value: '#000000' }, { $elem: $(''), value: '#eeece0' }, { $elem: $(''), value: '#1c487f' }, { $elem: $(''), value: '#4d80bf' }, { $elem: $(''), value: '#c24f4a' }, { $elem: $(''), value: '#8baa4a' }, { $elem: $(''), value: '#7b5ba1' }, { $elem: $(''), value: '#46acc8' }, { $elem: $(''), value: '#f9963b' }, { $elem: $(''), value: '#ffffff' }], + onClick: function onClick(value) { + // 注意 this 是指向当前的 ForeColor 对象 + _this._command(value); + } + }); +} + +// 原型 +ForeColor.prototype = { + constructor: ForeColor, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + editor.cmd.do('foreColor', value); + } +}; + +/* + menu - BackColor +*/ +// 构造函数 +function BackColor(editor) { + var _this = this; + + this.editor = editor; + this.$elem = $(''); + this.type = 'droplist'; + + // 当前是否 active 状态 + this._active = false; + + // 初始化 droplist + this.droplist = new DropList(this, { + width: 120, + $title: $('背景色
'), + type: 'inline-block', // droplist 内容以 block 形式展示 + list: [{ $elem: $(''), value: '#000000' }, { $elem: $(''), value: '#eeece0' }, { $elem: $(''), value: '#1c487f' }, { $elem: $(''), value: '#4d80bf' }, { $elem: $(''), value: '#c24f4a' }, { $elem: $(''), value: '#8baa4a' }, { $elem: $(''), value: '#7b5ba1' }, { $elem: $(''), value: '#46acc8' }, { $elem: $(''), value: '#f9963b' }, { $elem: $(''), value: '#ffffff' }], + onClick: function onClick(value) { + // 注意 this 是指向当前的 BackColor 对象 + _this._command(value); + } + }); +} + +// 原型 +BackColor.prototype = { + constructor: BackColor, + + // 执行命令 + _command: function _command(value) { + var editor = this.editor; + editor.cmd.do('backColor', value); + } +}; + +/* + menu - quote +*/ +// 构造函数 +function Quote(editor) { + this.editor = editor; + this.$elem = $(''); + this.type = 'click'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Quote.prototype = { + constructor: Quote, + + onClick: function onClick(e) { + var editor = this.editor; + var $selectionElem = editor.selection.getSelectionContainerElem(); + var nodeName = $selectionElem.getNodeName(); + + if (!UA.isIE()) { + if (nodeName === 'BLOCKQUOTE') { + // 撤销 quote + editor.cmd.do('formatBlock', ''); + } else { + // 转换为 quote + editor.cmd.do('formatBlock', '
'); + } + return; + } + + // IE 中不支持 formatBlock,要用其他方式兼容 + var content = void 0, + $targetELem = void 0; + if (nodeName === 'P') { + // 将 P 转换为 quote + content = $selectionElem.text(); + $targetELem = $('' + content + ''); + $targetELem.insertAfter($selectionElem); + $selectionElem.remove(); + return; + } + if (nodeName === 'BLOCKQUOTE') { + // 撤销 quote + content = $selectionElem.text(); + $targetELem = $('' + content + '
'); + $targetELem.insertAfter($selectionElem); + $selectionElem.remove(); + } + }, + + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + var reg = /^BLOCKQUOTE$/i; + var cmdValue = editor.cmd.queryCommandValue('formatBlock'); + if (reg.test(cmdValue)) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + menu - code +*/ +// 构造函数 +function Code(editor) { + this.editor = editor; + this.$elem = $(''); + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Code.prototype = { + constructor: Code, + + onClick: function onClick(e) { + var editor = this.editor; + var $startElem = editor.selection.getSelectionStartElem(); + var $endElem = editor.selection.getSelectionEndElem(); + var isSeleEmpty = editor.selection.isSelectionEmpty(); + var selectionText = editor.selection.getSelectionText(); + var $code = void 0; + + if (!$startElem.equal($endElem)) { + // 跨元素选择,不做处理 + editor.selection.restoreSelection(); + return; + } + if (!isSeleEmpty) { + // 选取不是空,用包裹即可 + $code = $('' + selectionText + ''); + editor.cmd.do('insertElem', $code); + editor.selection.createRangeByElem($code, false); + editor.selection.restoreSelection(); + return; + } + + // 选取是空,且没有夸元素选择,则插入+ if (this._active) { + // 选中状态,将编辑内容 + this._createPanel($startElem.html()); + } else { + // 未选中状态,将创建内容 + this._createPanel(); + } + }, + + _createPanel: function _createPanel(value) { + var _this = this; + + // value - 要编辑的内容 + value = value || ''; + var type = !value ? 'new' : 'edit'; + var textId = getRandom('texxt'); + var btnId = getRandom('btn'); + + var panel = new Panel(this, { + width: 500, + // 一个 Panel 包含多个 tab + tabs: [{ + // 标题 + title: '插入代码', + // 模板 + tpl: '\n \n \n', + // 事件绑定 + events: [ + // 插入代码 + { + selector: '#' + btnId, + type: 'click', + fn: function fn() { + var $text = $('#' + textId); + var text = $text.val() || $text.html(); + text = replaceHtmlSymbol(text); + if (type === 'new') { + // 新插入 + _this._insertCode(text); + } else { + // 编辑更新 + _this._updateCode(text); + } + + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + } // first tab end + ] // tabs end + }); // new Panel end + + // 显示 panel + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 插入代码 + _insertCode: function _insertCode(value) { + var editor = this.editor; + editor.cmd.do('insertHTML', '' + value + ''); + }, + + // 更新代码 + _updateCode: function _updateCode(value) { + var editor = this.editor; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + $selectionELem.html(value); + editor.selection.restoreSelection(); + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + var $parentElem = $selectionELem.parent(); + if ($selectionELem.getNodeName() === 'CODE' && $parentElem.getNodeName() === 'PRE') { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + menu - emoticon +*/ +// 构造函数 +function Emoticon(editor) { + this.editor = editor; + this.$elem = $(''); + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Emoticon.prototype = { + constructor: Emoticon, + + onClick: function onClick() { + this._createPanel(); + }, + + _createPanel: function _createPanel() { + var _this = this; + + // 拼接表情字符串 + var faceHtml = ''; + var faceStr = '😀 😃 😄 😁 😆 😅 😂 😊 😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 😜 😝 😛 🤑 🤗 🤓 😎 😏 😒 😞 😔 😟 😕 🙁 😣 😖 😫 😩 😤 😠 😡 😶 😐 😑 😯 😦 😧 😮 😲 😵 😳 😱 😨 😰 😢 😥 😭 😓 😪 😴 🙄 🤔 😬 🤐'; + faceStr.split(/\s/).forEach(function (item) { + if (item) { + faceHtml += '' + item + ''; + } + }); + + var handHtml = ''; + var handStr = '🙌 👏 👋 👍 👎 👊 ✊ ️👌 ✋ 👐 💪 🙏 ️👆 👇 👈 👉 🖕 🖐 🤘 🖖'; + handStr.split(/\s/).forEach(function (item) { + if (item) { + handHtml += '' + item + ''; + } + }); + + var panel = new Panel(this, { + width: 300, + height: 200, + // 一个 Panel 包含多个 tab + tabs: [{ + // 标题 + title: '表情', + // 模板 + tpl: '
' + faceHtml + '', + // 事件绑定 + events: [{ + selector: 'span.w-e-item', + type: 'click', + fn: function fn(e) { + var target = e.target; + _this._insert(target.innerHTML); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + }, // first tab end + { + // 标题 + title: '手势', + // 模板 + tpl: '' + handHtml + '', + // 事件绑定 + events: [{ + selector: 'span.w-e-item', + type: 'click', + fn: function fn(e) { + var target = e.target; + _this._insert(target.innerHTML); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + } // second tab end + ] // tabs end + }); + + // 显示 panel + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 插入表情 + _insert: function _insert(emoji) { + var editor = this.editor; + editor.cmd.do('insertHTML', '' + emoji + ''); + } +}; + +/* + menu - table +*/ +// 构造函数 +function Table(editor) { + this.editor = editor; + this.$elem = $(''); + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Table.prototype = { + constructor: Table, + + onClick: function onClick() { + if (this._active) { + // 编辑现有表格 + this._createEditPanel(); + } else { + // 插入新表格 + this._createInsertPanel(); + } + }, + + // 创建插入新表格的 panel + _createInsertPanel: function _createInsertPanel() { + var _this = this; + + // 用到的 id + var btnInsertId = getRandom('btn'); + var textRowNum = getRandom('row'); + var textColNum = getRandom('col'); + + var panel = new Panel(this, { + width: 250, + // panel 包含多个 tab + tabs: [{ + // 标题 + title: '插入表格', + // 模板 + tpl: '\n', + // 事件绑定 + events: [{ + // 点击按钮,插入表格 + selector: '#' + btnInsertId, + type: 'click', + fn: function fn() { + var rowNum = parseInt($('#' + textRowNum).val()); + var colNum = parseInt($('#' + textColNum).val()); + + if (rowNum && colNum && rowNum > 0 && colNum > 0) { + // form 数据有效 + _this._insert(rowNum, colNum); + } + + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + } // first tab end + ] // tabs end + }); // panel end + + // 展示 panel + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 插入表格 + _insert: function _insert(rowNum, colNum) { + // 拼接 table 模板 + var r = void 0, + c = void 0; + var html = '\n \u521B\u5EFA\n \n \u884C\n \n \u5217\u7684\u8868\u683C\n
\n \n'; + for (r = 0; r < rowNum; r++) { + html += '
'; + if (r === 0) { + for (c = 0; c < colNum; c++) { + html += ' '; + } + html += ''; + } + } else { + for (c = 0; c < colNum; c++) { + html += ' '; + } + } + html += ' '; + + // 执行命令 + var editor = this.editor; + editor.cmd.do('insertHTML', html); + + // 防止 firefox 下出现 resize 的控制点 + editor.cmd.do('enableObjectResizing', false); + editor.cmd.do('enableInlineTableEditing', false); + }, + + // 创建编辑表格的 panel + _createEditPanel: function _createEditPanel() { + var _this2 = this; + + // 可用的 id + var addRowBtnId = getRandom('add-row'); + var addColBtnId = getRandom('add-col'); + var delRowBtnId = getRandom('del-row'); + var delColBtnId = getRandom('del-col'); + var delTableBtnId = getRandom('del-table'); + + // 创建 panel 对象 + var panel = new Panel(this, { + width: 320, + // panel 包含多个 tab + tabs: [{ + // 标题 + title: '编辑表格', + // 模板 + tpl: '
\n \n ', + // 事件绑定 + events: [{ + // 增加行 + selector: '#' + addRowBtnId, + type: 'click', + fn: function fn() { + _this2._addRow(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + // 增加列 + selector: '#' + addColBtnId, + type: 'click', + fn: function fn() { + _this2._addCol(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + // 删除行 + selector: '#' + delRowBtnId, + type: 'click', + fn: function fn() { + _this2._delRow(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + // 删除列 + selector: '#' + delColBtnId, + type: 'click', + fn: function fn() { + _this2._delCol(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + // 删除表格 + selector: '#' + delTableBtnId, + type: 'click', + fn: function fn() { + _this2._delTable(); + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + }] + }); + // 显示 panel + panel.show(); + }, + + // 获取选中的单元格的位置信息 + _getLocationData: function _getLocationData() { + var result = {}; + var editor = this.editor; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + var nodeName = $selectionELem.getNodeName(); + if (nodeName !== 'TD' && nodeName !== 'TH') { + return; + } + + // 获取 td index + var $tr = $selectionELem.parent(); + var $tds = $tr.children(); + var tdLength = $tds.length; + $tds.forEach(function (td, index) { + if (td === $selectionELem[0]) { + // 记录并跳出循环 + result.td = { + index: index, + elem: td, + length: tdLength + }; + return false; + } + }); + + // 获取 tr index + var $tbody = $tr.parent(); + var $trs = $tbody.children(); + var trLength = $trs.length; + $trs.forEach(function (tr, index) { + if (tr === $tr[0]) { + // 记录并跳出循环 + result.tr = { + index: index, + elem: tr, + length: trLength + }; + return false; + } + }); + + // 返回结果 + return result; + }, + + // 增加行 + _addRow: function _addRow() { + // 获取当前单元格的位置信息 + var locationData = this._getLocationData(); + if (!locationData) { + return; + } + var trData = locationData.tr; + var $currentTr = $(trData.elem); + var tdData = locationData.td; + var tdLength = tdData.length; + + // 拼接即将插入的字符串 + var newTr = document.createElement('tr'); + var tpl = '', + i = void 0; + for (i = 0; i < tdLength; i++) { + tpl += ''; + } + newTr.innerHTML = tpl; + // 插入 + $(newTr).insertAfter($currentTr); + }, + + // 增加列 + _addCol: function _addCol() { + // 获取当前单元格的位置信息 + var locationData = this._getLocationData(); + if (!locationData) { + return; + } + var trData = locationData.tr; + var tdData = locationData.td; + var tdIndex = tdData.index; + var $currentTr = $(trData.elem); + var $trParent = $currentTr.parent(); + var $trs = $trParent.children(); + + // 遍历所有行 + $trs.forEach(function (tr) { + var $tr = $(tr); + var $tds = $tr.children(); + var $currentTd = $tds.get(tdIndex); + var name = $currentTd.getNodeName().toLowerCase(); + + // new 一个 td,并插入 + var newTd = document.createElement(name); + $(newTd).insertAfter($currentTd); + }); + }, + + // 删除行 + _delRow: function _delRow() { + // 获取当前单元格的位置信息 + var locationData = this._getLocationData(); + if (!locationData) { + return; + } + var trData = locationData.tr; + var $currentTr = $(trData.elem); + $currentTr.remove(); + }, + + // 删除列 + _delCol: function _delCol() { + // 获取当前单元格的位置信息 + var locationData = this._getLocationData(); + if (!locationData) { + return; + } + var trData = locationData.tr; + var tdData = locationData.td; + var tdIndex = tdData.index; + var $currentTr = $(trData.elem); + var $trParent = $currentTr.parent(); + var $trs = $trParent.children(); + + // 遍历所有行 + $trs.forEach(function (tr) { + var $tr = $(tr); + var $tds = $tr.children(); + var $currentTd = $tds.get(tdIndex); + // 删除 + $currentTd.remove(); + }); + }, + + // 删除表格 + _delTable: function _delTable() { + var editor = this.editor; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + var $table = $selectionELem.parentUntil('table'); + if (!$table) { + return; + } + $table.remove(); + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + var $selectionELem = editor.selection.getSelectionContainerElem(); + if (!$selectionELem) { + return; + } + var nodeName = $selectionELem.getNodeName(); + if (nodeName === 'TD' || nodeName === 'TH') { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + menu - video +*/ +// 构造函数 +function Video(editor) { + this.editor = editor; + this.$elem = $(''); + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Video.prototype = { + constructor: Video, + + onClick: function onClick() { + this._createPanel(); + }, + + _createPanel: function _createPanel() { + var _this = this; + + // 创建 id + var textValId = getRandom('text-val'); + var btnId = getRandom('btn'); + + // 创建 panel + var panel = new Panel(this, { + width: 350, + // 一个 panel 多个 tab + tabs: [{ + // 标题 + title: '插入视频', + // 模板 + tpl: ' \n \n \n', + // 事件绑定 + events: [{ + selector: '#' + btnId, + type: 'click', + fn: function fn() { + var $text = $('#' + textValId); + var val = $text.val().trim(); + + // 测试用视频地址 + // + + if (val) { + // 插入视频 + _this._insert(val); + } + + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + } // first tab end + ] // tabs end + }); // panel end + + // 显示 panel + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 插入视频 + _insert: function _insert(val) { + var editor = this.editor; + editor.cmd.do('insertHTML', val + ''); + } +}; + +/* + menu - img +*/ +// 构造函数 +function Image(editor) { + this.editor = editor; + var imgMenuId = getRandom('w-e-img'); + this.$elem = $(''); + editor.imgMenuId = imgMenuId; + this.type = 'panel'; + + // 当前是否 active 状态 + this._active = false; +} + +// 原型 +Image.prototype = { + constructor: Image, + + onClick: function onClick() { + var editor = this.editor; + var config = editor.config; + if (config.qiniu) { + return; + } + if (this._active) { + this._createEditPanel(); + } else { + this._createInsertPanel(); + } + }, + + _createEditPanel: function _createEditPanel() { + var editor = this.editor; + + // id + var width30 = getRandom('width-30'); + var width50 = getRandom('width-50'); + var width100 = getRandom('width-100'); + var delBtn = getRandom('del-btn'); + + // tab 配置 + var tabsConfig = [{ + title: '编辑图片', + tpl: '
\n \n ', + events: [{ + selector: '#' + width30, + type: 'click', + fn: function fn() { + var $img = editor._selectedImg; + if ($img) { + $img.css('max-width', '30%'); + } + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + selector: '#' + width50, + type: 'click', + fn: function fn() { + var $img = editor._selectedImg; + if ($img) { + $img.css('max-width', '50%'); + } + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + selector: '#' + width100, + type: 'click', + fn: function fn() { + var $img = editor._selectedImg; + if ($img) { + $img.css('max-width', '100%'); + } + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }, { + selector: '#' + delBtn, + type: 'click', + fn: function fn() { + var $img = editor._selectedImg; + if ($img) { + $img.remove(); + } + // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 + return true; + } + }] + }]; + + // 创建 panel 并显示 + var panel = new Panel(this, { + width: 300, + tabs: tabsConfig + }); + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + _createInsertPanel: function _createInsertPanel() { + var editor = this.editor; + var uploadImg = editor.uploadImg; + var config = editor.config; + + // id + var upTriggerId = getRandom('up-trigger'); + var upFileId = getRandom('up-file'); + var linkUrlId = getRandom('link-url'); + var linkBtnId = getRandom('link-btn'); + + // tabs 的配置 + var tabsConfig = [{ + title: '上传图片', + tpl: '\n', + events: [{ + // 触发选择图片 + selector: '#' + upTriggerId, + type: 'click', + fn: function fn() { + var $file = $('#' + upFileId); + var fileElem = $file[0]; + if (fileElem) { + fileElem.click(); + } else { + // 返回 true 可关闭 panel + return true; + } + } + }, { + // 选择图片完毕 + selector: '#' + upFileId, + type: 'change', + fn: function fn() { + var $file = $('#' + upFileId); + var fileElem = $file[0]; + if (!fileElem) { + // 返回 true 可关闭 panel + return true; + } + + // 获取选中的 file 对象列表 + var fileList = fileElem.files; + if (fileList.length) { + uploadImg.uploadImg(fileList); + } + + // 返回 true 可关闭 panel + return true; + } + }] + }, // first tab end + { + title: '网络图片', + tpl: '\n \n\n \n\n \n \n', + events: [{ + selector: '#' + linkBtnId, + type: 'click', + fn: function fn() { + var $linkUrl = $('#' + linkUrlId); + var url = $linkUrl.val().trim(); + + if (url) { + uploadImg.insertLinkImg(url); + } + + // 返回 true 表示函数执行结束之后关闭 panel + return true; + } + }] + } // second tab end + ]; // tabs end + + // 判断 tabs 的显示 + var tabsConfigResult = []; + if ((config.uploadImgShowBase64 || config.uploadImgServer || config.customUploadImg) && window.FileReader) { + // 显示“上传图片” + tabsConfigResult.push(tabsConfig[0]); + } + if (config.showLinkImg) { + // 显示“网络图片” + tabsConfigResult.push(tabsConfig[1]); + } + + // 创建 panel 并显示 + var panel = new Panel(this, { + width: 300, + tabs: tabsConfigResult + }); + panel.show(); + + // 记录属性 + this.panel = panel; + }, + + // 试图改变 active 状态 + tryChangeActive: function tryChangeActive(e) { + var editor = this.editor; + var $elem = this.$elem; + if (editor._selectedImg) { + this._active = true; + $elem.addClass('w-e-active'); + } else { + this._active = false; + $elem.removeClass('w-e-active'); + } + } +}; + +/* + 所有菜单的汇总 +*/ + +// 存储菜单的构造函数 +var MenuConstructors = {}; + +MenuConstructors.bold = Bold; + +MenuConstructors.head = Head; + +MenuConstructors.link = Link; + +MenuConstructors.italic = Italic; + +MenuConstructors.redo = Redo; + +MenuConstructors.strikeThrough = StrikeThrough; + +MenuConstructors.underline = Underline; + +MenuConstructors.undo = Undo; + +MenuConstructors.list = List; + +MenuConstructors.justify = Justify; + +MenuConstructors.foreColor = ForeColor; + +MenuConstructors.backColor = BackColor; + +MenuConstructors.quote = Quote; + +MenuConstructors.code = Code; + +MenuConstructors.emoticon = Emoticon; + +MenuConstructors.table = Table; + +MenuConstructors.video = Video; + +MenuConstructors.image = Image; + +/* + 菜单集合 +*/ +// 构造函数 +function Menus(editor) { + this.editor = editor; + this.menus = {}; +} + +// 修改原型 +Menus.prototype = { + constructor: Menus, + + // 初始化菜单 + init: function init() { + var _this = this; + + var editor = this.editor; + var config = editor.config || {}; + var configMenus = config.menus || []; // 获取配置中的菜单 + + // 根据配置信息,创建菜单 + configMenus.forEach(function (menuKey) { + var MenuConstructor = MenuConstructors[menuKey]; + if (MenuConstructor && typeof MenuConstructor === 'function') { + // 创建单个菜单 + _this.menus[menuKey] = new MenuConstructor(editor); + } + }); + + // 添加到菜单栏 + this._addToToolbar(); + + // 绑定事件 + this._bindEvent(); + }, + + // 添加到菜单栏 + _addToToolbar: function _addToToolbar() { + var editor = this.editor; + var $toolbarElem = editor.$toolbarElem; + var menus = this.menus; + var config = editor.config; + // config.zIndex 是配置的编辑区域的 z-index,菜单的 z-index 得在其基础上 +1 + var zIndex = config.zIndex + 1; + objForEach(menus, function (key, menu) { + var $elem = menu.$elem; + if ($elem) { + // 设置 z-index + $elem.css('z-index', zIndex); + $toolbarElem.append($elem); + } + }); + }, + + // 绑定菜单 click mouseenter 事件 + _bindEvent: function _bindEvent() { + var menus = this.menus; + var editor = this.editor; + objForEach(menus, function (key, menu) { + var type = menu.type; + if (!type) { + return; + } + var $elem = menu.$elem; + var droplist = menu.droplist; + var panel = menu.panel; + + // 点击类型,例如 bold + if (type === 'click' && menu.onClick) { + $elem.on('click', function (e) { + if (editor.selection.getRange() == null) { + return; + } + menu.onClick(e); + }); + } + + // 下拉框,例如 head + if (type === 'droplist' && droplist) { + $elem.on('mouseenter', function (e) { + if (editor.selection.getRange() == null) { + return; + } + // 显示 + droplist.showTimeoutId = setTimeout(function () { + droplist.show(); + }, 200); + }).on('mouseleave', function (e) { + // 隐藏 + droplist.hideTimeoutId = setTimeout(function () { + droplist.hide(); + }, 0); + }); + } + + // 弹框类型,例如 link + if (type === 'panel' && menu.onClick) { + $elem.on('click', function (e) { + e.stopPropagation(); + if (editor.selection.getRange() == null) { + return; + } + // 在自定义事件中显示 panel + menu.onClick(e); + }); + } + }); + }, + + // 尝试修改菜单状态 + changeActive: function changeActive() { + var menus = this.menus; + objForEach(menus, function (key, menu) { + if (menu.tryChangeActive) { + setTimeout(function () { + menu.tryChangeActive(); + }, 100); + } + }); + } +}; + +/* + 粘贴信息的处理 +*/ + +// 获取粘贴的纯文本 +function getPasteText(e) { + var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData; + var pasteText = void 0; + if (clipboardData == null) { + pasteText = window.clipboardData && window.clipboardData.getData('text'); + } else { + pasteText = clipboardData.getData('text/plain'); + } + + return replaceHtmlSymbol(pasteText); +} + +// 获取粘贴的html +function getPasteHtml(e, filterStyle) { + var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData; + var pasteText = void 0, + pasteHtml = void 0; + if (clipboardData == null) { + pasteText = window.clipboardData && window.clipboardData.getData('text'); + } else { + pasteText = clipboardData.getData('text/plain'); + pasteHtml = clipboardData.getData('text/html'); + } + if (!pasteHtml && pasteText) { + pasteHtml = '' + replaceHtmlSymbol(pasteText) + '
'; + } + if (!pasteHtml) { + return; + } + + // 过滤word中状态过来的无用字符 + var docSplitHtml = pasteHtml.split('