From 826a3a0cf193f145510931f409088e898bed6a57 Mon Sep 17 00:00:00 2001 From: WinterChenS <1085143002@qq.com> Date: Thu, 3 May 2018 17:03:54 +0800 Subject: [PATCH] 0.1 verison MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 首次提交 --- .gitignore | 25 + .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 47610 bytes .mvn/wrapper/maven-wrapper.properties | 1 + mvnw | 225 + mvnw.cmd | 143 + pom.xml | 190 + src/main/docker/Dockerfile | 4 + .../java/cn/luischen/MySiteApplication.java | 12 + src/main/java/cn/luischen/Swagger2.java | 45 + .../java/cn/luischen/api/QCloudService.java | 73 + .../cn/luischen/api/QiniuCloudService.java | 61 + .../java/cn/luischen/aspect/WebLogAspect.java | 64 + .../cn/luischen/config/DataSourceConfig.java | 42 + .../cn/luischen/config/DruidDBConfig.java | 108 + .../cn/luischen/config/PageHelperConfig.java | 30 + .../cn/luischen/constant/ErrorConstant.java | 60 + .../java/cn/luischen/constant/LogActions.java | 26 + src/main/java/cn/luischen/constant/Types.java | 45 + .../java/cn/luischen/constant/WebConst.java | 63 + .../luischen/controller/BaseController.java | 97 + .../luischen/controller/BlogController.java | 263 + .../luischen/controller/HomeController.java | 54 + .../luischen/controller/PhotoController.java | 73 + .../controller/admin/ArticleController.java | 213 + .../controller/admin/AttAchController.java | 166 + .../controller/admin/AuthController.java | 118 + .../controller/admin/CategoryController.java | 93 + .../controller/admin/CommentController.java | 108 + .../controller/admin/IndexController.java | 149 + .../controller/admin/LinksController.java | 106 + .../controller/admin/SettingController.java | 80 + .../controller/admin/UserController.java | 29 + src/main/java/cn/luischen/dao/AttAchDao.java | 63 + src/main/java/cn/luischen/dao/CommentDao.java | 63 + src/main/java/cn/luischen/dao/ContentDao.java | 87 + src/main/java/cn/luischen/dao/LogDao.java | 34 + src/main/java/cn/luischen/dao/MetaDao.java | 69 + src/main/java/cn/luischen/dao/OptionDao.java | 42 + .../java/cn/luischen/dao/RelationShipDao.java | 73 + src/main/java/cn/luischen/dao/UserDao.java | 40 + src/main/java/cn/luischen/dto/ArchiveDto.java | 40 + src/main/java/cn/luischen/dto/AttAchDto.java | 69 + src/main/java/cn/luischen/dto/BaseDto.java | 20 + src/main/java/cn/luischen/dto/MetaDto.java | 20 + .../java/cn/luischen/dto/StatisticsDto.java | 57 + .../cn/luischen/dto/cond/CommentCond.java | 57 + .../cn/luischen/dto/cond/ContentCond.java | 107 + .../java/cn/luischen/dto/cond/MetaCond.java | 34 + .../java/cn/luischen/dto/cond/UserCond.java | 28 + .../luischen/exception/BusinessException.java | 92 + .../handler/GlobalExceptionHandler.java | 40 + .../luischen/interceptor/BaseInterceptor.java | 105 + .../cn/luischen/interceptor/WebMvcConfig.java | 32 + .../java/cn/luischen/model/AttAchDomain.java | 70 + .../java/cn/luischen/model/CommentDomain.java | 191 + .../java/cn/luischen/model/ContentDomain.java | 216 + .../java/cn/luischen/model/LogDomain.java | 98 + .../java/cn/luischen/model/MetaDomain.java | 113 + .../java/cn/luischen/model/OptionsDomain.java | 39 + .../cn/luischen/model/RelationShipDomain.java | 33 + .../java/cn/luischen/model/UserDomain.java | 108 + .../service/attach/AttAchService.java | 55 + .../attach/impl/AttAchServiceImpl.java | 74 + .../service/comment/CommentService.java | 61 + .../comment/impl/CommentServiceImpl.java | 108 + .../service/content/ContentService.java | 77 + .../content/impl/ContentServiceImpl.java | 167 + .../cn/luischen/service/log/LogService.java | 33 + .../service/log/impl/LogServiceImpl.java | 49 + .../cn/luischen/service/meta/MetaService.java | 85 + .../service/meta/impl/MetaServiceImpl.java | 220 + .../service/option/OptionService.java | 45 + .../option/impl/OptionServiceImpl.java | 65 + .../service/relation/RelationShipService.java | 10 + .../impl/RelationShipServiceImpl.java | 18 + .../cn/luischen/service/site/SiteService.java | 70 + .../service/site/impl/SiteServiceImpl.java | 165 + .../cn/luischen/service/user/UserService.java | 37 + .../service/user/impl/UserServiceImpl.java | 52 + .../java/cn/luischen/utils/APIResponse.java | 74 + .../java/cn/luischen/utils/AdminCommons.java | 41 + src/main/java/cn/luischen/utils/Commons.java | 480 + src/main/java/cn/luischen/utils/DateKit.java | 697 ++ .../java/cn/luischen/utils/GsonUtils.java | 16 + src/main/java/cn/luischen/utils/IPKit.java | 64 + src/main/java/cn/luischen/utils/MapCache.java | 167 + .../java/cn/luischen/utils/TaleUtils.java | 432 + src/main/java/cn/luischen/utils/Tools.java | 89 + src/main/java/cn/luischen/utils/UUID.java | 250 + src/main/resources/application-dev.yml | 64 + src/main/resources/application-prod.yml | 67 + src/main/resources/application.yml | 3 + src/main/resources/banner-2323.gif | Bin 0 -> 899314 bytes src/main/resources/luischen.cn.pfx | Bin 0 -> 4281 bytes src/main/resources/mapper/AttAchMapper.xml | 116 + src/main/resources/mapper/CommentMapper.xml | 96 + src/main/resources/mapper/ContentMapper.xml | 204 + src/main/resources/mapper/LogMapper.xml | 45 + src/main/resources/mapper/MetaMapper.xml | 125 + src/main/resources/mapper/OptionMapper.xml | 52 + .../resources/mapper/RelationShipMapper.xml | 81 + src/main/resources/mapper/UserMapper.xml | 51 + .../resources/static/admin/css/contact.css | 21 + .../resources/static/admin/css/flexslider.css | 71 + src/main/resources/static/admin/css/reset.css | 97 + .../resources/static/admin/css/responsive.css | 65 + .../resources/static/admin/css/style.min.css | 1 + .../resources/static/admin/css/styles.css | 459 + .../resources/static/admin/editormd/.jshintrc | 20 + .../resources/static/admin/editormd/BUGS.md | 22 + .../resources/static/admin/editormd/CHANGE.md | 534 + .../static/admin/editormd/Gulpfile.js | 342 + .../resources/static/admin/editormd/LICENSE | 22 + .../resources/static/admin/editormd/README.md | 119 + .../static/admin/editormd/bower.json | 24 + .../static/admin/editormd/css/editormd.css | 4450 +++++++++ .../admin/editormd/css/editormd.logo.css | 98 + .../admin/editormd/css/editormd.logo.min.css | 2 + .../admin/editormd/css/editormd.min.css | 5 + .../admin/editormd/css/editormd.preview.css | 3554 +++++++ .../editormd/css/editormd.preview.min.css | 5 + .../static/admin/editormd/editormd.amd.js | 4667 +++++++++ .../static/admin/editormd/editormd.amd.min.js | 4 + .../static/admin/editormd/editormd.js | 4598 +++++++++ .../static/admin/editormd/editormd.min.js | 3 + .../admin/editormd/fonts/FontAwesome.otf | Bin 0 -> 93888 bytes .../admin/editormd/fonts/editormd-logo.eot | Bin 0 -> 1320 bytes .../admin/editormd/fonts/editormd-logo.svg | 11 + .../admin/editormd/fonts/editormd-logo.ttf | Bin 0 -> 1156 bytes .../admin/editormd/fonts/editormd-logo.woff | Bin 0 -> 1232 bytes .../editormd/fonts/fontawesome-webfont.eot | Bin 0 -> 60767 bytes .../editormd/fonts/fontawesome-webfont.svg | 565 ++ .../editormd/fonts/fontawesome-webfont.ttf | Bin 0 -> 122092 bytes .../editormd/fonts/fontawesome-webfont.woff | Bin 0 -> 71508 bytes .../editormd/fonts/fontawesome-webfont.woff2 | Bin 0 -> 56780 bytes .../static/admin/editormd/images/loading.gif | Bin 0 -> 7726 bytes .../admin/editormd/images/loading@2x.gif | Bin 0 -> 16166 bytes .../admin/editormd/images/loading@3x.gif | Bin 0 -> 21727 bytes .../images/logos/editormd-favicon-16x16.ico | Bin 0 -> 1150 bytes .../images/logos/editormd-favicon-24x24.ico | Bin 0 -> 1150 bytes .../images/logos/editormd-favicon-32x32.ico | Bin 0 -> 5430 bytes .../images/logos/editormd-favicon-48x48.ico | Bin 0 -> 15086 bytes .../images/logos/editormd-favicon-64x64.ico | Bin 0 -> 32038 bytes .../images/logos/editormd-logo-114x114.png | Bin 0 -> 5869 bytes .../images/logos/editormd-logo-120x120.png | Bin 0 -> 6141 bytes .../images/logos/editormd-logo-144x144.png | Bin 0 -> 7545 bytes .../images/logos/editormd-logo-16x16.png | Bin 0 -> 1448 bytes .../images/logos/editormd-logo-180x180.png | Bin 0 -> 7963 bytes .../images/logos/editormd-logo-240x240.png | Bin 0 -> 10470 bytes .../images/logos/editormd-logo-24x24.png | Bin 0 -> 1790 bytes .../images/logos/editormd-logo-320x320.png | Bin 0 -> 14714 bytes .../images/logos/editormd-logo-32x32.png | Bin 0 -> 2064 bytes .../images/logos/editormd-logo-48x48.png | Bin 0 -> 2701 bytes .../images/logos/editormd-logo-57x57.png | Bin 0 -> 3054 bytes .../images/logos/editormd-logo-64x64.png | Bin 0 -> 3408 bytes .../images/logos/editormd-logo-72x72.png | Bin 0 -> 3691 bytes .../images/logos/editormd-logo-96x96.png | Bin 0 -> 4857 bytes .../static/admin/editormd/images/logos/vi.png | Bin 0 -> 46203 bytes .../static/admin/editormd/languages/en.js | 127 + .../static/admin/editormd/languages/zh-tw.js | 127 + .../admin/editormd/lib/codemirror/AUTHORS | 436 + .../admin/editormd/lib/codemirror/LICENSE | 19 + .../admin/editormd/lib/codemirror/README.md | 12 + .../lib/codemirror/addon/comment/comment.js | 183 + .../addon/comment/continuecomment.js | 85 + .../lib/codemirror/addon/dialog/dialog.css | 32 + .../lib/codemirror/addon/dialog/dialog.js | 155 + .../codemirror/addon/display/fullscreen.css | 6 + .../codemirror/addon/display/fullscreen.js | 41 + .../lib/codemirror/addon/display/panel.js | 94 + .../codemirror/addon/display/placeholder.js | 58 + .../lib/codemirror/addon/display/rulers.js | 64 + .../codemirror/addon/edit/closebrackets.js | 161 + .../lib/codemirror/addon/edit/closetag.js | 166 + .../lib/codemirror/addon/edit/continuelist.js | 51 + .../codemirror/addon/edit/matchbrackets.js | 120 + .../lib/codemirror/addon/edit/matchtags.js | 66 + .../codemirror/addon/edit/trailingspace.js | 27 + .../lib/codemirror/addon/fold/brace-fold.js | 105 + .../lib/codemirror/addon/fold/comment-fold.js | 57 + .../lib/codemirror/addon/fold/foldcode.js | 149 + .../lib/codemirror/addon/fold/foldgutter.css | 20 + .../lib/codemirror/addon/fold/foldgutter.js | 144 + .../lib/codemirror/addon/fold/indent-fold.js | 44 + .../codemirror/addon/fold/markdown-fold.js | 49 + .../lib/codemirror/addon/fold/xml-fold.js | 182 + .../lib/codemirror/addon/hint/anyword-hint.js | 41 + .../lib/codemirror/addon/hint/css-hint.js | 56 + .../lib/codemirror/addon/hint/html-hint.js | 348 + .../codemirror/addon/hint/javascript-hint.js | 146 + .../lib/codemirror/addon/hint/show-hint.css | 38 + .../lib/codemirror/addon/hint/show-hint.js | 394 + .../lib/codemirror/addon/hint/sql-hint.js | 240 + .../lib/codemirror/addon/hint/xml-hint.js | 110 + .../addon/lint/coffeescript-lint.js | 41 + .../lib/codemirror/addon/lint/css-lint.js | 35 + .../codemirror/addon/lint/javascript-lint.js | 136 + .../lib/codemirror/addon/lint/json-lint.js | 31 + .../lib/codemirror/addon/lint/lint.css | 73 + .../lib/codemirror/addon/lint/lint.js | 205 + .../lib/codemirror/addon/lint/yaml-lint.js | 28 + .../lib/codemirror/addon/merge/merge.css | 112 + .../lib/codemirror/addon/merge/merge.js | 735 ++ .../lib/codemirror/addon/mode/loadmode.js | 64 + .../lib/codemirror/addon/mode/multiplex.js | 118 + .../codemirror/addon/mode/multiplex_test.js | 33 + .../lib/codemirror/addon/mode/overlay.js | 85 + .../lib/codemirror/addon/mode/simple.js | 213 + .../lib/codemirror/addon/runmode/colorize.js | 40 + .../addon/runmode/runmode-standalone.js | 157 + .../lib/codemirror/addon/runmode/runmode.js | 72 + .../codemirror/addon/runmode/runmode.node.js | 120 + .../addon/scroll/annotatescrollbar.js | 100 + .../codemirror/addon/scroll/scrollpastend.js | 46 + .../addon/scroll/simplescrollbars.css | 66 + .../addon/scroll/simplescrollbars.js | 141 + .../addon/search/match-highlighter.js | 128 + .../addon/search/matchesonscrollbar.css | 8 + .../addon/search/matchesonscrollbar.js | 95 + .../lib/codemirror/addon/search/search.js | 164 + .../codemirror/addon/search/searchcursor.js | 189 + .../codemirror/addon/selection/active-line.js | 71 + .../addon/selection/mark-selection.js | 118 + .../addon/selection/selection-pointer.js | 98 + .../lib/codemirror/addon/tern/tern.css | 86 + .../lib/codemirror/addon/tern/tern.js | 697 ++ .../lib/codemirror/addon/tern/worker.js | 44 + .../lib/codemirror/addon/wrap/hardwrap.js | 139 + .../editormd/lib/codemirror/addons.min.js | 4 + .../admin/editormd/lib/codemirror/bower.json | 16 + .../lib/codemirror/codemirror.min.css | 3 + .../editormd/lib/codemirror/codemirror.min.js | 54 + .../lib/codemirror/lib/codemirror.css | 331 + .../editormd/lib/codemirror/lib/codemirror.js | 8645 +++++++++++++++++ .../editormd/lib/codemirror/mode/apl/apl.js | 175 + .../lib/codemirror/mode/apl/index.html | 72 + .../lib/codemirror/mode/asterisk/asterisk.js | 198 + .../lib/codemirror/mode/asterisk/index.html | 154 + .../lib/codemirror/mode/clike/clike.js | 493 + .../lib/codemirror/mode/clike/index.html | 251 + .../lib/codemirror/mode/clike/scala.html | 767 ++ .../lib/codemirror/mode/clojure/clojure.js | 243 + .../lib/codemirror/mode/clojure/index.html | 88 + .../lib/codemirror/mode/cobol/cobol.js | 255 + .../lib/codemirror/mode/cobol/index.html | 210 + .../mode/coffeescript/coffeescript.js | 369 + .../codemirror/mode/coffeescript/index.html | 740 ++ .../codemirror/mode/commonlisp/commonlisp.js | 122 + .../lib/codemirror/mode/commonlisp/index.html | 177 + .../editormd/lib/codemirror/mode/css/css.js | 766 ++ .../lib/codemirror/mode/css/index.html | 75 + .../lib/codemirror/mode/css/less.html | 152 + .../lib/codemirror/mode/css/less_test.js | 51 + .../lib/codemirror/mode/css/scss.html | 157 + .../lib/codemirror/mode/css/scss_test.js | 110 + .../editormd/lib/codemirror/mode/css/test.js | 195 + .../lib/codemirror/mode/cypher/cypher.js | 146 + .../lib/codemirror/mode/cypher/index.html | 63 + .../admin/editormd/lib/codemirror/mode/d/d.js | 218 + .../editormd/lib/codemirror/mode/d/index.html | 273 + .../editormd/lib/codemirror/mode/dart/dart.js | 50 + .../lib/codemirror/mode/dart/index.html | 71 + .../editormd/lib/codemirror/mode/diff/diff.js | 47 + .../lib/codemirror/mode/diff/index.html | 117 + .../lib/codemirror/mode/django/django.js | 67 + .../lib/codemirror/mode/django/index.html | 63 + .../codemirror/mode/dockerfile/dockerfile.js | 76 + .../lib/codemirror/mode/dockerfile/index.html | 73 + .../editormd/lib/codemirror/mode/dtd/dtd.js | 142 + .../lib/codemirror/mode/dtd/index.html | 89 + .../lib/codemirror/mode/dylan/dylan.js | 299 + .../lib/codemirror/mode/dylan/index.html | 407 + .../editormd/lib/codemirror/mode/ebnf/ebnf.js | 195 + .../lib/codemirror/mode/ebnf/index.html | 102 + .../editormd/lib/codemirror/mode/ecl/ecl.js | 207 + .../lib/codemirror/mode/ecl/index.html | 52 + .../lib/codemirror/mode/eiffel/eiffel.js | 162 + .../lib/codemirror/mode/eiffel/index.html | 429 + .../lib/codemirror/mode/erlang/erlang.js | 622 ++ .../lib/codemirror/mode/erlang/index.html | 76 + .../lib/codemirror/mode/forth/forth.js | 180 + .../lib/codemirror/mode/forth/index.html | 75 + .../lib/codemirror/mode/fortran/fortran.js | 188 + .../lib/codemirror/mode/fortran/index.html | 81 + .../editormd/lib/codemirror/mode/gas/gas.js | 345 + .../lib/codemirror/mode/gas/index.html | 68 + .../editormd/lib/codemirror/mode/gfm/gfm.js | 123 + .../lib/codemirror/mode/gfm/index.html | 93 + .../editormd/lib/codemirror/mode/gfm/test.js | 213 + .../lib/codemirror/mode/gherkin/gherkin.js | 178 + .../lib/codemirror/mode/gherkin/index.html | 48 + .../editormd/lib/codemirror/mode/go/go.js | 185 + .../lib/codemirror/mode/go/index.html | 85 + .../lib/codemirror/mode/groovy/groovy.js | 226 + .../lib/codemirror/mode/groovy/index.html | 84 + .../editormd/lib/codemirror/mode/haml/haml.js | 159 + .../lib/codemirror/mode/haml/index.html | 79 + .../editormd/lib/codemirror/mode/haml/test.js | 97 + .../lib/codemirror/mode/haskell/haskell.js | 267 + .../lib/codemirror/mode/haskell/index.html | 73 + .../editormd/lib/codemirror/mode/haxe/haxe.js | 518 + .../lib/codemirror/mode/haxe/index.html | 124 + .../mode/htmlembedded/htmlembedded.js | 86 + .../codemirror/mode/htmlembedded/index.html | 58 + .../codemirror/mode/htmlmixed/htmlmixed.js | 121 + .../lib/codemirror/mode/htmlmixed/index.html | 89 + .../editormd/lib/codemirror/mode/http/http.js | 113 + .../lib/codemirror/mode/http/index.html | 45 + .../editormd/lib/codemirror/mode/idl/idl.js | 290 + .../lib/codemirror/mode/idl/index.html | 64 + .../editormd/lib/codemirror/mode/index.html | 134 + .../lib/codemirror/mode/jade/index.html | 70 + .../editormd/lib/codemirror/mode/jade/jade.js | 590 ++ .../lib/codemirror/mode/javascript/index.html | 114 + .../codemirror/mode/javascript/javascript.js | 692 ++ .../codemirror/mode/javascript/json-ld.html | 72 + .../lib/codemirror/mode/javascript/test.js | 200 + .../mode/javascript/typescript.html | 61 + .../lib/codemirror/mode/jinja2/index.html | 54 + .../lib/codemirror/mode/jinja2/jinja2.js | 142 + .../lib/codemirror/mode/julia/index.html | 195 + .../lib/codemirror/mode/julia/julia.js | 301 + .../lib/codemirror/mode/kotlin/index.html | 89 + .../lib/codemirror/mode/kotlin/kotlin.js | 280 + .../lib/codemirror/mode/livescript/index.html | 459 + .../codemirror/mode/livescript/livescript.js | 280 + .../lib/codemirror/mode/lua/index.html | 85 + .../editormd/lib/codemirror/mode/lua/lua.js | 159 + .../lib/codemirror/mode/markdown/index.html | 359 + .../lib/codemirror/mode/markdown/markdown.js | 765 ++ .../lib/codemirror/mode/markdown/test.js | 754 ++ .../editormd/lib/codemirror/mode/meta.js | 177 + .../lib/codemirror/mode/mirc/index.html | 160 + .../editormd/lib/codemirror/mode/mirc/mirc.js | 193 + .../lib/codemirror/mode/mllike/index.html | 179 + .../lib/codemirror/mode/mllike/mllike.js | 205 + .../lib/codemirror/mode/modelica/index.html | 67 + .../lib/codemirror/mode/modelica/modelica.js | 245 + .../lib/codemirror/mode/nginx/index.html | 181 + .../lib/codemirror/mode/nginx/nginx.js | 178 + .../lib/codemirror/mode/ntriples/index.html | 45 + .../lib/codemirror/mode/ntriples/ntriples.js | 186 + .../lib/codemirror/mode/octave/index.html | 83 + .../lib/codemirror/mode/octave/octave.js | 135 + .../lib/codemirror/mode/pascal/index.html | 61 + .../lib/codemirror/mode/pascal/pascal.js | 109 + .../lib/codemirror/mode/pegjs/index.html | 66 + .../lib/codemirror/mode/pegjs/pegjs.js | 114 + .../lib/codemirror/mode/perl/index.html | 75 + .../editormd/lib/codemirror/mode/perl/perl.js | 837 ++ .../lib/codemirror/mode/php/index.html | 64 + .../editormd/lib/codemirror/mode/php/php.js | 226 + .../editormd/lib/codemirror/mode/php/test.js | 154 + .../lib/codemirror/mode/pig/index.html | 55 + .../editormd/lib/codemirror/mode/pig/pig.js | 188 + .../lib/codemirror/mode/properties/index.html | 53 + .../codemirror/mode/properties/properties.js | 78 + .../lib/codemirror/mode/puppet/index.html | 121 + .../lib/codemirror/mode/puppet/puppet.js | 220 + .../lib/codemirror/mode/python/index.html | 198 + .../lib/codemirror/mode/python/python.js | 359 + .../editormd/lib/codemirror/mode/q/index.html | 144 + .../admin/editormd/lib/codemirror/mode/q/q.js | 139 + .../editormd/lib/codemirror/mode/r/index.html | 85 + .../admin/editormd/lib/codemirror/mode/r/r.js | 162 + .../codemirror/mode/rpm/changes/index.html | 66 + .../lib/codemirror/mode/rpm/index.html | 149 + .../editormd/lib/codemirror/mode/rpm/rpm.js | 101 + .../lib/codemirror/mode/rst/index.html | 535 + .../editormd/lib/codemirror/mode/rst/rst.js | 557 ++ .../lib/codemirror/mode/ruby/index.html | 183 + .../editormd/lib/codemirror/mode/ruby/ruby.js | 285 + .../editormd/lib/codemirror/mode/ruby/test.js | 14 + .../lib/codemirror/mode/rust/index.html | 60 + .../editormd/lib/codemirror/mode/rust/rust.js | 451 + .../lib/codemirror/mode/sass/index.html | 66 + .../editormd/lib/codemirror/mode/sass/sass.js | 414 + .../lib/codemirror/mode/scheme/index.html | 77 + .../lib/codemirror/mode/scheme/scheme.js | 248 + .../lib/codemirror/mode/shell/index.html | 66 + .../lib/codemirror/mode/shell/shell.js | 139 + .../lib/codemirror/mode/shell/test.js | 58 + .../lib/codemirror/mode/sieve/index.html | 93 + .../lib/codemirror/mode/sieve/sieve.js | 193 + .../lib/codemirror/mode/slim/index.html | 96 + .../editormd/lib/codemirror/mode/slim/slim.js | 575 ++ .../editormd/lib/codemirror/mode/slim/test.js | 96 + .../lib/codemirror/mode/smalltalk/index.html | 68 + .../codemirror/mode/smalltalk/smalltalk.js | 168 + .../lib/codemirror/mode/smarty/index.html | 136 + .../lib/codemirror/mode/smarty/smarty.js | 221 + .../codemirror/mode/smartymixed/index.html | 114 + .../mode/smartymixed/smartymixed.js | 197 + .../lib/codemirror/mode/solr/index.html | 57 + .../editormd/lib/codemirror/mode/solr/solr.js | 104 + .../lib/codemirror/mode/soy/index.html | 68 + .../editormd/lib/codemirror/mode/soy/soy.js | 198 + .../lib/codemirror/mode/sparql/index.html | 61 + .../lib/codemirror/mode/sparql/sparql.js | 174 + .../codemirror/mode/spreadsheet/index.html | 42 + .../mode/spreadsheet/spreadsheet.js | 109 + .../lib/codemirror/mode/sql/index.html | 84 + .../editormd/lib/codemirror/mode/sql/sql.js | 391 + .../lib/codemirror/mode/stex/index.html | 110 + .../editormd/lib/codemirror/mode/stex/stex.js | 251 + .../editormd/lib/codemirror/mode/stex/test.js | 123 + .../lib/codemirror/mode/stylus/index.html | 104 + .../lib/codemirror/mode/stylus/stylus.js | 444 + .../lib/codemirror/mode/tcl/index.html | 142 + .../editormd/lib/codemirror/mode/tcl/tcl.js | 147 + .../lib/codemirror/mode/textile/index.html | 191 + .../lib/codemirror/mode/textile/test.js | 417 + .../lib/codemirror/mode/textile/textile.js | 469 + .../lib/codemirror/mode/tiddlywiki/index.html | 154 + .../codemirror/mode/tiddlywiki/tiddlywiki.css | 14 + .../codemirror/mode/tiddlywiki/tiddlywiki.js | 369 + .../lib/codemirror/mode/tiki/index.html | 95 + .../lib/codemirror/mode/tiki/tiki.css | 26 + .../editormd/lib/codemirror/mode/tiki/tiki.js | 323 + .../lib/codemirror/mode/toml/index.html | 73 + .../editormd/lib/codemirror/mode/toml/toml.js | 88 + .../lib/codemirror/mode/tornado/index.html | 63 + .../lib/codemirror/mode/tornado/tornado.js | 68 + .../lib/codemirror/mode/turtle/index.html | 50 + .../lib/codemirror/mode/turtle/turtle.js | 162 + .../lib/codemirror/mode/vb/index.html | 102 + .../editormd/lib/codemirror/mode/vb/vb.js | 274 + .../lib/codemirror/mode/vbscript/index.html | 55 + .../lib/codemirror/mode/vbscript/vbscript.js | 350 + .../lib/codemirror/mode/velocity/index.html | 118 + .../lib/codemirror/mode/velocity/velocity.js | 201 + .../lib/codemirror/mode/verilog/index.html | 120 + .../lib/codemirror/mode/verilog/test.js | 273 + .../lib/codemirror/mode/verilog/verilog.js | 537 + .../lib/codemirror/mode/xml/index.html | 57 + .../editormd/lib/codemirror/mode/xml/test.js | 51 + .../editormd/lib/codemirror/mode/xml/xml.js | 384 + .../lib/codemirror/mode/xquery/index.html | 210 + .../lib/codemirror/mode/xquery/test.js | 67 + .../lib/codemirror/mode/xquery/xquery.js | 447 + .../lib/codemirror/mode/yaml/index.html | 80 + .../editormd/lib/codemirror/mode/yaml/yaml.js | 117 + .../lib/codemirror/mode/z80/index.html | 52 + .../editormd/lib/codemirror/mode/z80/z80.js | 100 + .../editormd/lib/codemirror/modes.min.js | 10 + .../editormd/lib/codemirror/package.json | 21 + .../lib/codemirror/theme/3024-day.css | 40 + .../lib/codemirror/theme/3024-night.css | 39 + .../lib/codemirror/theme/ambiance-mobile.css | 5 + .../lib/codemirror/theme/ambiance.css | 75 + .../lib/codemirror/theme/base16-dark.css | 38 + .../lib/codemirror/theme/base16-light.css | 38 + .../lib/codemirror/theme/blackboard.css | 32 + .../editormd/lib/codemirror/theme/cobalt.css | 25 + .../lib/codemirror/theme/colorforth.css | 33 + .../editormd/lib/codemirror/theme/eclipse.css | 23 + .../editormd/lib/codemirror/theme/elegant.css | 13 + .../lib/codemirror/theme/erlang-dark.css | 34 + .../lib/codemirror/theme/lesser-dark.css | 47 + .../editormd/lib/codemirror/theme/mbo.css | 37 + .../lib/codemirror/theme/mdn-like.css | 46 + .../lib/codemirror/theme/midnight.css | 47 + .../editormd/lib/codemirror/theme/monokai.css | 33 + .../editormd/lib/codemirror/theme/neat.css | 12 + .../editormd/lib/codemirror/theme/neo.css | 43 + .../editormd/lib/codemirror/theme/night.css | 28 + .../lib/codemirror/theme/paraiso-dark.css | 38 + .../lib/codemirror/theme/paraiso-light.css | 38 + .../lib/codemirror/theme/pastel-on-dark.css | 53 + .../lib/codemirror/theme/rubyblue.css | 25 + .../lib/codemirror/theme/solarized.css | 165 + .../lib/codemirror/theme/the-matrix.css | 30 + .../theme/tomorrow-night-bright.css | 35 + .../theme/tomorrow-night-eighties.css | 38 + .../lib/codemirror/theme/twilight.css | 32 + .../lib/codemirror/theme/vibrant-ink.css | 34 + .../editormd/lib/codemirror/theme/xq-dark.css | 53 + .../lib/codemirror/theme/xq-light.css | 43 + .../editormd/lib/codemirror/theme/zenburn.css | 37 + .../admin/editormd/lib/flowchart.min.js | 5 + .../editormd/lib/jquery.flowchart.min.js | 2 + .../static/admin/editormd/lib/marked.min.js | 9 + .../static/admin/editormd/lib/prettify.min.js | 15 + .../static/admin/editormd/lib/raphael.min.js | 11 + .../editormd/lib/sequence-diagram.min.js | 7 + .../admin/editormd/lib/underscore.min.js | 5 + .../static/admin/editormd/package.json | 42 + .../code-block-dialog/code-block-dialog.js | 237 + .../plugins/emoji-dialog/emoji-dialog.js | 327 + .../editormd/plugins/emoji-dialog/emoji.json | 28 + .../goto-line-dialog/goto-line-dialog.js | 157 + .../plugins/help-dialog/help-dialog.js | 102 + .../editormd/plugins/help-dialog/help.md | 77 + .../html-entities-dialog.js | 173 + .../html-entities-dialog/html-entities.json | 936 ++ .../plugins/image-dialog/image-dialog.js | 221 + .../plugins/link-dialog/link-dialog.js | 133 + .../admin/editormd/plugins/plugin-template.js | 111 + .../preformatted-text-dialog.js | 172 + .../reference-link-dialog.js | 153 + .../plugins/table-dialog/table-dialog.js | 218 + .../plugins/test-plugin/test-plugin.js | 66 + .../editormd/scss/editormd.codemirror.scss | 90 + .../admin/editormd/scss/editormd.dialog.scss | 184 + .../admin/editormd/scss/editormd.form.scss | 130 + .../admin/editormd/scss/editormd.grid.scss | 36 + .../admin/editormd/scss/editormd.logo.scss | 89 + .../admin/editormd/scss/editormd.menu.scss | 113 + .../admin/editormd/scss/editormd.preview.scss | 322 + .../scss/editormd.preview.themes.scss | 131 + .../static/admin/editormd/scss/editormd.scss | 137 + .../admin/editormd/scss/editormd.tab.scss | 49 + .../admin/editormd/scss/editormd.themes.scss | 28 + .../admin/editormd/scss/font-awesome.scss | 1801 ++++ .../admin/editormd/scss/github-markdown.scss | 665 ++ .../admin/editormd/scss/lib/prefixes.scss | 784 ++ .../admin/editormd/scss/lib/variables.scss | 11 + .../static/admin/editormd/scss/prettify.scss | 53 + .../static/admin/editormd/src/editormd.js | 4588 +++++++++ .../resources/static/admin/images/attach.png | Bin 0 -> 4160 bytes .../resources/static/admin/images/bg/1.png | Bin 0 -> 343815 bytes .../resources/static/admin/images/bg/2.png | Bin 0 -> 251042 bytes .../resources/static/admin/images/bg/3.png | Bin 0 -> 196742 bytes .../resources/static/admin/images/bg/4.png | Bin 0 -> 195297 bytes .../resources/static/admin/images/bg/5.png | Bin 0 -> 317360 bytes .../resources/static/admin/images/favicon.png | Bin 0 -> 4286 bytes .../admin/images/icons/bg-adjust@2x.png | Bin 0 -> 384 bytes .../static/admin/images/icons/bg-apple@2x.png | Bin 0 -> 977 bytes .../admin/images/icons/bg-balance@2x.png | Bin 0 -> 732 bytes .../admin/images/icons/bg-beaker@2x.png | Bin 0 -> 668 bytes .../static/admin/images/icons/bg-beer@2x.png | Bin 0 -> 1118 bytes .../static/admin/images/icons/bg-books@2x.png | Bin 0 -> 848 bytes .../static/admin/images/icons/bg-box@2x.png | Bin 0 -> 835 bytes .../static/admin/images/icons/bg-cake@2x.png | Bin 0 -> 718 bytes .../admin/images/icons/bg-calcuLator@2x.png | 39 + .../admin/images/icons/bg-camera@2x.png | Bin 0 -> 876 bytes .../static/admin/images/icons/bg-cd@2x.png | Bin 0 -> 724 bytes .../admin/images/icons/bg-certified@2x.png | 39 + .../admin/images/icons/bg-champagne@2x.png | Bin 0 -> 667 bytes .../static/admin/images/icons/bg-chart@2x.png | Bin 0 -> 578 bytes .../static/admin/images/icons/bg-chat@2x.png | Bin 0 -> 294 bytes .../admin/images/icons/bg-cheese@2x.png | Bin 0 -> 987 bytes .../admin/images/icons/bg-commerce@2x.png | Bin 0 -> 529 bytes .../static/admin/images/icons/bg-court@2x.png | Bin 0 -> 638 bytes .../admin/images/icons/bg-delivery@2x.png | Bin 0 -> 827 bytes .../admin/images/icons/bg-document@2x.png | Bin 0 -> 311 bytes .../static/admin/images/icons/bg-dvd@2x.png | Bin 0 -> 985 bytes .../static/admin/images/icons/bg-eco@2x.png | Bin 0 -> 1271 bytes .../static/admin/images/icons/bg-film@2x.png | Bin 0 -> 337 bytes .../static/admin/images/icons/bg-grape@2x.png | Bin 0 -> 872 bytes .../static/admin/images/icons/bg-hot@2x.png | Bin 0 -> 612 bytes .../static/admin/images/icons/bg-house@2x.png | Bin 0 -> 673 bytes .../admin/images/icons/bg-icecream@2x.png | Bin 0 -> 844 bytes .../admin/images/icons/bg-illustration@2x.png | Bin 0 -> 907 bytes .../admin/images/icons/bg-joystick@2x.png | Bin 0 -> 716 bytes .../admin/images/icons/bg-keynote@2x.png | Bin 0 -> 693 bytes .../static/admin/images/icons/bg-like@2x.png | Bin 0 -> 753 bytes .../static/admin/images/icons/bg-link@2x.png | Bin 0 -> 788 bytes .../static/admin/images/icons/bg-magic@2x.png | Bin 0 -> 663 bytes .../static/admin/images/icons/bg-mail@2x.png | Bin 0 -> 667 bytes .../static/admin/images/icons/bg-map@2x.png | Bin 0 -> 934 bytes .../admin/images/icons/bg-mediation@2x.png | 39 + .../admin/images/icons/bg-microphone@2x.png | Bin 0 -> 784 bytes .../admin/images/icons/bg-network@2x.png | Bin 0 -> 871 bytes .../static/admin/images/icons/bg-notes@2x.png | Bin 0 -> 386 bytes .../admin/images/icons/bg-palette@2x.png | Bin 0 -> 1242 bytes .../static/admin/images/icons/bg-pig@2x.png | Bin 0 -> 897 bytes .../admin/images/icons/bg-plaster@2x.png | Bin 0 -> 987 bytes .../admin/images/icons/bg-player@2x.png | Bin 0 -> 691 bytes .../admin/images/icons/bg-polaroid@2x.png | Bin 0 -> 858 bytes .../admin/images/icons/bg-printer@2x.png | Bin 0 -> 591 bytes .../static/admin/images/icons/bg-pulse@2x.png | Bin 0 -> 1168 bytes .../static/admin/images/icons/bg-quote@2x.png | Bin 0 -> 775 bytes .../static/admin/images/icons/bg-radio@2x.png | Bin 0 -> 566 bytes .../admin/images/icons/bg-recorder@2x.png | Bin 0 -> 865 bytes .../admin/images/icons/bg-scissors@2x.png | Bin 0 -> 849 bytes .../admin/images/icons/bg-search@2x.png | Bin 0 -> 1135 bytes .../admin/images/icons/bg-select@2x.png | Bin 0 -> 620 bytes .../admin/images/icons/bg-serving@2x.png | Bin 0 -> 823 bytes .../static/admin/images/icons/bg-share@2x.png | Bin 0 -> 639 bytes .../static/admin/images/icons/bg-shop@2x.png | Bin 0 -> 725 bytes .../admin/images/icons/bg-shopping@2x.png | Bin 0 -> 575 bytes .../admin/images/icons/bg-smartphone@2x.png | Bin 0 -> 520 bytes .../admin/images/icons/bg-social@2x.png | Bin 0 -> 448 bytes .../static/admin/images/icons/bg-sofa@2x.png | Bin 0 -> 1039 bytes .../admin/images/icons/bg-syringe@2x.png | Bin 0 -> 447 bytes .../static/admin/images/icons/bg-tags@2x.png | Bin 0 -> 621 bytes .../static/admin/images/icons/bg-tape@2x.png | Bin 0 -> 554 bytes .../admin/images/icons/bg-target@2x.png | Bin 0 -> 1221 bytes .../static/admin/images/icons/bg-team@2x.png | Bin 0 -> 922 bytes .../admin/images/icons/bg-television@2x.png | Bin 0 -> 777 bytes .../static/admin/images/icons/bg-time@2x.png | Bin 0 -> 842 bytes .../admin/images/icons/bg-upload@2x.png | Bin 0 -> 341 bytes .../admin/images/icons/bg-vector@2x.png | Bin 0 -> 677 bytes .../static/admin/images/icons/bg-video@2x.png | Bin 0 -> 747 bytes .../admin/images/icons/bg-walkman@2x.png | Bin 0 -> 881 bytes .../admin/images/icons/bg-winner@2x.png | Bin 0 -> 1111 bytes .../static/admin/images/icons/bg-works@2x.png | Bin 0 -> 1074 bytes .../static/admin/images/icons/bg-zoom@2x.png | Bin 0 -> 660 bytes .../resources/static/admin/images/loading.gif | Bin 0 -> 65292 bytes .../resources/static/admin/images/logo.png | Bin 0 -> 21142 bytes .../static/admin/images/small/bg.jpg | Bin 0 -> 19790 bytes src/main/resources/static/admin/js/article.js | 110 + src/main/resources/static/admin/js/base.js | 161 + src/main/resources/static/admin/js/install.js | 88 + .../resources/static/admin/js/jquery.app.js | 270 + .../jquery-multi-select/img/switch.png | Bin 0 -> 3080 bytes .../jquery-multi-select/jquery.quicksearch.js | 181 + .../jquery.steps/demo/css/jquery.steps.css | 328 + .../static/admin/plugins/md/css/style.css | 56 + .../static/admin/plugins/md/img/editor.png | Bin 0 -> 7370 bytes .../static/admin/plugins/md/js/diff.js | 1 + .../static/admin/plugins/md/js/jquery.js | 25 + .../admin/plugins/md/js/jquery.scrollto.js | 1 + .../static/admin/plugins/md/js/md.js | 204 + .../admin/plugins/md/js/pagedown-extra.js | 1 + .../static/admin/plugins/md/js/pagedown.js | 1 + .../select2.dist.css/select2-bootstrap.css | 87 + .../select2.dist.css/select2-spinner.gif | Bin 0 -> 1849 bytes .../plugins/select2.dist.css/select2.css | 646 ++ .../plugins/select2.dist.css/select2.png | Bin 0 -> 613 bytes .../plugins/select2.dist.css/select2x2.png | Bin 0 -> 845 bytes .../plugins/tagsinput/jquery.tagsinput.css | 67 + .../plugins/tagsinput/jquery.tagsinput.min.js | 1 + .../static/admin/plugins/toggles/toggles.css | 260 + .../blog/css/fonts/fontawesome-webfont-.eot | Bin 0 -> 38205 bytes .../blog/css/fonts/fontawesome-webfont-v=.eot | Bin 0 -> 38205 bytes .../blog/css/fonts/fontawesome-webfont-v=.ttf | Bin 0 -> 80652 bytes .../css/fonts/fontawesome-webfont-v=.woff | Bin 0 -> 44432 bytes .../blog/css/fonts/fontawesome-webfont.svg | 414 + .../resources/static/blog/css/images/logo.png | Bin 0 -> 19763 bytes .../static/blog/css/images/preloader.gif | Bin 0 -> 6567 bytes .../static/blog/css/images/preloader@2x.gif | Bin 0 -> 13984 bytes src/main/resources/static/blog/css/style.css | 1519 +++ .../resources/static/blog/fancybox/blank.gif | Bin 0 -> 43 bytes .../static/blog/fancybox/fancybox_loading.gif | Bin 0 -> 6567 bytes .../blog/fancybox/fancybox_loading@2x.gif | Bin 0 -> 13984 bytes .../static/blog/fancybox/fancybox_overlay.png | Bin 0 -> 1003 bytes .../static/blog/fancybox/fancybox_sprite.png | Bin 0 -> 1362 bytes .../blog/fancybox/fancybox_sprite@2x.png | Bin 0 -> 6553 bytes .../static/blog/fancybox/jquery.fancybox.css | 273 + .../blog/fancybox/jquery.fancybox.pack.js | 46 + .../resources/static/blog/js/jquery.min.js | 6 + .../static/blog/js/jquery.scrollLoading.js | 75 + src/main/resources/static/blog/js/script.js | 134 + src/main/resources/static/error/css/style.css | 1 + .../resources/static/error/fonts/icons-.eot | Bin 0 -> 8870 bytes ...icons-64c061773912345c985cb3365a710a98.eot | Bin 0 -> 8870 bytes ...icons-64c061773912345c985cb3365a710a98.ttf | Bin 0 -> 8704 bytes ...cons-64c061773912345c985cb3365a710a98.woff | Bin 0 -> 5672 bytes src/main/resources/static/error/img/icons.png | Bin 0 -> 12206 bytes .../static/home/assets/images/Wechat.jpg | Bin 0 -> 91617 bytes .../home/assets/images/icon-behance.png | Bin 0 -> 9551 bytes .../static/home/assets/images/icon-csdn.png | Bin 0 -> 6473 bytes .../home/assets/images/icon-dribbble.png | Bin 0 -> 9440 bytes .../static/home/assets/images/icon-email.png | Bin 0 -> 8098 bytes .../static/home/assets/images/icon-github.png | Bin 0 -> 1771 bytes .../home/assets/images/icon-instagram.png | Bin 0 -> 17885 bytes .../home/assets/images/icon-jianshu.png | Bin 0 -> 1013 bytes .../static/home/assets/images/icon-px.png | Bin 0 -> 3303 bytes .../static/home/assets/images/icon-resume.png | Bin 0 -> 4832 bytes .../home/assets/images/icon-twitter.png | Bin 0 -> 4820 bytes .../static/home/assets/images/icon-uicn.png | Bin 0 -> 2047 bytes .../static/home/assets/images/icon-weibo.png | Bin 0 -> 2123 bytes .../static/home/assets/images/icon-zhihu.png | Bin 0 -> 771 bytes .../static/home/assets/images/profile-1.png | Bin 0 -> 19241 bytes .../static/home/assets/images/profile.jpg | Bin 0 -> 26512 bytes .../home/assets/styles/sass/parts/_about.scss | 60 + .../assets/styles/sass/parts/_profile.scss | 25 + .../assets/styles/sass/parts/_working.scss | 47 + .../static/home/assets/styles/sass/site.scss | 15 + .../styles/sass/universal/_animations.scss | 83 + .../assets/styles/sass/universal/_body.scss | 7 + .../assets/styles/sass/universal/_icons.scss | 93 + .../styles/sass/universal/_normalize.scss | 49 + .../styles/sass/universal/_typography.scss | 43 + .../styles/sass/universal/_variables.scss | 19 + .../static/home/assets/styles/site.css | 744 ++ .../resources/static/photo/css/styles.css | 222 + .../static/photo/favicon/favicon.ico | Bin 0 -> 1110 bytes .../templates/admin/article_edit.html | 199 + .../templates/admin/article_list.html | 101 + .../resources/templates/admin/attach.html | 169 + .../resources/templates/admin/category.html | 151 + .../templates/admin/comment_list.html | 115 + .../resources/templates/admin/footer.html | 15 + .../resources/templates/admin/header.html | 118 + src/main/resources/templates/admin/index.html | 121 + src/main/resources/templates/admin/links.html | 154 + src/main/resources/templates/admin/login.html | 116 + .../resources/templates/admin/page_edit.html | 108 + .../resources/templates/admin/page_list.html | 84 + .../resources/templates/admin/profile.html | 139 + .../resources/templates/admin/setting.html | 277 + .../resources/templates/blog/archives.html | 68 + .../resources/templates/blog/categories.html | 69 + src/main/resources/templates/blog/footer.html | 12 + src/main/resources/templates/blog/head.html | 20 + src/main/resources/templates/blog/header.html | 169 + src/main/resources/templates/blog/index.html | 87 + .../resources/templates/blog/page_nav.html | 22 + src/main/resources/templates/blog/post.html | 77 + src/main/resources/templates/comm/macros.html | 50 + .../templates/comm/tale_comment.html | 62 + src/main/resources/templates/error/404.html | 73 + src/main/resources/templates/error/500.html | 73 + src/main/resources/templates/home/about.html | 54 + src/main/resources/templates/home/footer.html | 8 + src/main/resources/templates/home/head.html | 27 + src/main/resources/templates/home/header.html | 13 + src/main/resources/templates/home/index.html | 52 + .../resources/templates/photo/article.html | 24 + .../resources/templates/photo/footer.html | 13 + src/main/resources/templates/photo/head.html | 32 + .../resources/templates/photo/header.html | 46 + src/main/resources/templates/photo/index.html | 30 + .../cn/luischen/MySiteApplicationTests.java | 16 + tree.log | 545 ++ 718 files changed, 110421 insertions(+) create mode 100644 .gitignore create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100755 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 src/main/docker/Dockerfile create mode 100644 src/main/java/cn/luischen/MySiteApplication.java create mode 100644 src/main/java/cn/luischen/Swagger2.java create mode 100644 src/main/java/cn/luischen/api/QCloudService.java create mode 100644 src/main/java/cn/luischen/api/QiniuCloudService.java create mode 100644 src/main/java/cn/luischen/aspect/WebLogAspect.java create mode 100644 src/main/java/cn/luischen/config/DataSourceConfig.java create mode 100644 src/main/java/cn/luischen/config/DruidDBConfig.java create mode 100644 src/main/java/cn/luischen/config/PageHelperConfig.java create mode 100644 src/main/java/cn/luischen/constant/ErrorConstant.java create mode 100644 src/main/java/cn/luischen/constant/LogActions.java create mode 100644 src/main/java/cn/luischen/constant/Types.java create mode 100644 src/main/java/cn/luischen/constant/WebConst.java create mode 100644 src/main/java/cn/luischen/controller/BaseController.java create mode 100644 src/main/java/cn/luischen/controller/BlogController.java create mode 100644 src/main/java/cn/luischen/controller/HomeController.java create mode 100644 src/main/java/cn/luischen/controller/PhotoController.java create mode 100644 src/main/java/cn/luischen/controller/admin/ArticleController.java create mode 100644 src/main/java/cn/luischen/controller/admin/AttAchController.java create mode 100644 src/main/java/cn/luischen/controller/admin/AuthController.java create mode 100644 src/main/java/cn/luischen/controller/admin/CategoryController.java create mode 100644 src/main/java/cn/luischen/controller/admin/CommentController.java create mode 100644 src/main/java/cn/luischen/controller/admin/IndexController.java create mode 100644 src/main/java/cn/luischen/controller/admin/LinksController.java create mode 100644 src/main/java/cn/luischen/controller/admin/SettingController.java create mode 100644 src/main/java/cn/luischen/controller/admin/UserController.java create mode 100644 src/main/java/cn/luischen/dao/AttAchDao.java create mode 100644 src/main/java/cn/luischen/dao/CommentDao.java create mode 100644 src/main/java/cn/luischen/dao/ContentDao.java create mode 100644 src/main/java/cn/luischen/dao/LogDao.java create mode 100644 src/main/java/cn/luischen/dao/MetaDao.java create mode 100644 src/main/java/cn/luischen/dao/OptionDao.java create mode 100644 src/main/java/cn/luischen/dao/RelationShipDao.java create mode 100644 src/main/java/cn/luischen/dao/UserDao.java create mode 100644 src/main/java/cn/luischen/dto/ArchiveDto.java create mode 100644 src/main/java/cn/luischen/dto/AttAchDto.java create mode 100644 src/main/java/cn/luischen/dto/BaseDto.java create mode 100644 src/main/java/cn/luischen/dto/MetaDto.java create mode 100644 src/main/java/cn/luischen/dto/StatisticsDto.java create mode 100644 src/main/java/cn/luischen/dto/cond/CommentCond.java create mode 100644 src/main/java/cn/luischen/dto/cond/ContentCond.java create mode 100644 src/main/java/cn/luischen/dto/cond/MetaCond.java create mode 100644 src/main/java/cn/luischen/dto/cond/UserCond.java create mode 100644 src/main/java/cn/luischen/exception/BusinessException.java create mode 100644 src/main/java/cn/luischen/handler/GlobalExceptionHandler.java create mode 100644 src/main/java/cn/luischen/interceptor/BaseInterceptor.java create mode 100644 src/main/java/cn/luischen/interceptor/WebMvcConfig.java create mode 100644 src/main/java/cn/luischen/model/AttAchDomain.java create mode 100644 src/main/java/cn/luischen/model/CommentDomain.java create mode 100644 src/main/java/cn/luischen/model/ContentDomain.java create mode 100644 src/main/java/cn/luischen/model/LogDomain.java create mode 100644 src/main/java/cn/luischen/model/MetaDomain.java create mode 100644 src/main/java/cn/luischen/model/OptionsDomain.java create mode 100644 src/main/java/cn/luischen/model/RelationShipDomain.java create mode 100644 src/main/java/cn/luischen/model/UserDomain.java create mode 100644 src/main/java/cn/luischen/service/attach/AttAchService.java create mode 100644 src/main/java/cn/luischen/service/attach/impl/AttAchServiceImpl.java create mode 100644 src/main/java/cn/luischen/service/comment/CommentService.java create mode 100644 src/main/java/cn/luischen/service/comment/impl/CommentServiceImpl.java create mode 100644 src/main/java/cn/luischen/service/content/ContentService.java create mode 100644 src/main/java/cn/luischen/service/content/impl/ContentServiceImpl.java create mode 100644 src/main/java/cn/luischen/service/log/LogService.java create mode 100644 src/main/java/cn/luischen/service/log/impl/LogServiceImpl.java create mode 100644 src/main/java/cn/luischen/service/meta/MetaService.java create mode 100644 src/main/java/cn/luischen/service/meta/impl/MetaServiceImpl.java create mode 100644 src/main/java/cn/luischen/service/option/OptionService.java create mode 100644 src/main/java/cn/luischen/service/option/impl/OptionServiceImpl.java create mode 100644 src/main/java/cn/luischen/service/relation/RelationShipService.java create mode 100644 src/main/java/cn/luischen/service/relation/impl/RelationShipServiceImpl.java create mode 100644 src/main/java/cn/luischen/service/site/SiteService.java create mode 100644 src/main/java/cn/luischen/service/site/impl/SiteServiceImpl.java create mode 100644 src/main/java/cn/luischen/service/user/UserService.java create mode 100644 src/main/java/cn/luischen/service/user/impl/UserServiceImpl.java create mode 100644 src/main/java/cn/luischen/utils/APIResponse.java create mode 100644 src/main/java/cn/luischen/utils/AdminCommons.java create mode 100644 src/main/java/cn/luischen/utils/Commons.java create mode 100644 src/main/java/cn/luischen/utils/DateKit.java create mode 100644 src/main/java/cn/luischen/utils/GsonUtils.java create mode 100644 src/main/java/cn/luischen/utils/IPKit.java create mode 100644 src/main/java/cn/luischen/utils/MapCache.java create mode 100644 src/main/java/cn/luischen/utils/TaleUtils.java create mode 100644 src/main/java/cn/luischen/utils/Tools.java create mode 100644 src/main/java/cn/luischen/utils/UUID.java create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/application-prod.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/banner-2323.gif create mode 100755 src/main/resources/luischen.cn.pfx create mode 100644 src/main/resources/mapper/AttAchMapper.xml create mode 100644 src/main/resources/mapper/CommentMapper.xml create mode 100644 src/main/resources/mapper/ContentMapper.xml create mode 100644 src/main/resources/mapper/LogMapper.xml create mode 100644 src/main/resources/mapper/MetaMapper.xml create mode 100644 src/main/resources/mapper/OptionMapper.xml create mode 100644 src/main/resources/mapper/RelationShipMapper.xml create mode 100644 src/main/resources/mapper/UserMapper.xml create mode 100644 src/main/resources/static/admin/css/contact.css create mode 100644 src/main/resources/static/admin/css/flexslider.css create mode 100644 src/main/resources/static/admin/css/reset.css create mode 100644 src/main/resources/static/admin/css/responsive.css create mode 100644 src/main/resources/static/admin/css/style.min.css create mode 100644 src/main/resources/static/admin/css/styles.css create mode 100755 src/main/resources/static/admin/editormd/.jshintrc create mode 100755 src/main/resources/static/admin/editormd/BUGS.md create mode 100755 src/main/resources/static/admin/editormd/CHANGE.md create mode 100755 src/main/resources/static/admin/editormd/Gulpfile.js create mode 100755 src/main/resources/static/admin/editormd/LICENSE create mode 100755 src/main/resources/static/admin/editormd/README.md create mode 100755 src/main/resources/static/admin/editormd/bower.json create mode 100755 src/main/resources/static/admin/editormd/css/editormd.css create mode 100755 src/main/resources/static/admin/editormd/css/editormd.logo.css create mode 100755 src/main/resources/static/admin/editormd/css/editormd.logo.min.css create mode 100755 src/main/resources/static/admin/editormd/css/editormd.min.css create mode 100755 src/main/resources/static/admin/editormd/css/editormd.preview.css create mode 100755 src/main/resources/static/admin/editormd/css/editormd.preview.min.css create mode 100755 src/main/resources/static/admin/editormd/editormd.amd.js create mode 100755 src/main/resources/static/admin/editormd/editormd.amd.min.js create mode 100755 src/main/resources/static/admin/editormd/editormd.js create mode 100755 src/main/resources/static/admin/editormd/editormd.min.js create mode 100755 src/main/resources/static/admin/editormd/fonts/FontAwesome.otf create mode 100755 src/main/resources/static/admin/editormd/fonts/editormd-logo.eot create mode 100755 src/main/resources/static/admin/editormd/fonts/editormd-logo.svg create mode 100755 src/main/resources/static/admin/editormd/fonts/editormd-logo.ttf create mode 100755 src/main/resources/static/admin/editormd/fonts/editormd-logo.woff create mode 100755 src/main/resources/static/admin/editormd/fonts/fontawesome-webfont.eot create mode 100755 src/main/resources/static/admin/editormd/fonts/fontawesome-webfont.svg create mode 100755 src/main/resources/static/admin/editormd/fonts/fontawesome-webfont.ttf create mode 100755 src/main/resources/static/admin/editormd/fonts/fontawesome-webfont.woff create mode 100755 src/main/resources/static/admin/editormd/fonts/fontawesome-webfont.woff2 create mode 100755 src/main/resources/static/admin/editormd/images/loading.gif create mode 100755 src/main/resources/static/admin/editormd/images/loading@2x.gif create mode 100755 src/main/resources/static/admin/editormd/images/loading@3x.gif create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-favicon-16x16.ico create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-favicon-24x24.ico create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-favicon-32x32.ico create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-favicon-48x48.ico create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-favicon-64x64.ico create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-114x114.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-120x120.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-144x144.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-16x16.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-180x180.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-240x240.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-24x24.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-320x320.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-32x32.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-48x48.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-57x57.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-64x64.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-72x72.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/editormd-logo-96x96.png create mode 100755 src/main/resources/static/admin/editormd/images/logos/vi.png create mode 100755 src/main/resources/static/admin/editormd/languages/en.js create mode 100755 src/main/resources/static/admin/editormd/languages/zh-tw.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/AUTHORS create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/LICENSE create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/README.md create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/comment/comment.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/comment/continuecomment.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/dialog/dialog.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/dialog/dialog.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/display/fullscreen.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/display/fullscreen.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/display/panel.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/display/placeholder.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/display/rulers.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/edit/closebrackets.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/edit/closetag.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/edit/continuelist.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/edit/matchbrackets.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/edit/matchtags.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/edit/trailingspace.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/fold/brace-fold.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/fold/comment-fold.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/fold/foldcode.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/fold/foldgutter.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/fold/foldgutter.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/fold/indent-fold.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/fold/markdown-fold.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/fold/xml-fold.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/hint/anyword-hint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/hint/css-hint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/hint/html-hint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/hint/javascript-hint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/hint/show-hint.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/hint/show-hint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/hint/sql-hint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/hint/xml-hint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/lint/coffeescript-lint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/lint/css-lint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/lint/javascript-lint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/lint/json-lint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/lint/lint.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/lint/lint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/lint/yaml-lint.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/merge/merge.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/merge/merge.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/mode/loadmode.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/mode/multiplex.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/mode/multiplex_test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/mode/overlay.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/mode/simple.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/runmode/colorize.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/runmode/runmode-standalone.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/runmode/runmode.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/runmode/runmode.node.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/scroll/annotatescrollbar.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/scroll/scrollpastend.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/scroll/simplescrollbars.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/scroll/simplescrollbars.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/search/match-highlighter.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/search/matchesonscrollbar.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/search/matchesonscrollbar.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/search/search.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/search/searchcursor.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/selection/active-line.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/selection/mark-selection.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/selection/selection-pointer.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/tern/tern.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/tern/tern.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/tern/worker.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addon/wrap/hardwrap.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/addons.min.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/bower.json create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/codemirror.min.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/codemirror.min.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/lib/codemirror.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/lib/codemirror.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/apl/apl.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/apl/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/asterisk/asterisk.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/asterisk/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/clike/clike.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/clike/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/clike/scala.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/clojure/clojure.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/clojure/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/cobol/cobol.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/cobol/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/coffeescript/coffeescript.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/coffeescript/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/commonlisp/commonlisp.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/commonlisp/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/css/css.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/css/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/css/less.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/css/less_test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/css/scss.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/css/scss_test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/css/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/cypher/cypher.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/cypher/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/d/d.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/d/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/dart/dart.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/dart/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/diff/diff.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/diff/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/django/django.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/django/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/dockerfile/dockerfile.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/dockerfile/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/dtd/dtd.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/dtd/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/dylan/dylan.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/dylan/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/ebnf/ebnf.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/ebnf/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/ecl/ecl.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/ecl/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/eiffel/eiffel.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/eiffel/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/erlang/erlang.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/erlang/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/forth/forth.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/forth/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/fortran/fortran.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/fortran/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/gas/gas.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/gas/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/gfm/gfm.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/gfm/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/gfm/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/gherkin/gherkin.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/gherkin/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/go/go.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/go/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/groovy/groovy.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/groovy/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/haml/haml.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/haml/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/haml/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/haskell/haskell.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/haskell/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/haxe/haxe.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/haxe/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/htmlembedded/htmlembedded.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/htmlembedded/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/htmlmixed/htmlmixed.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/htmlmixed/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/http/http.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/http/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/idl/idl.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/idl/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/jade/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/jade/jade.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/javascript/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/javascript/javascript.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/javascript/json-ld.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/javascript/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/javascript/typescript.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/jinja2/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/jinja2/jinja2.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/julia/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/julia/julia.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/kotlin/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/kotlin/kotlin.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/livescript/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/livescript/livescript.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/lua/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/lua/lua.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/markdown/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/markdown/markdown.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/markdown/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/meta.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/mirc/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/mirc/mirc.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/mllike/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/mllike/mllike.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/modelica/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/modelica/modelica.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/nginx/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/nginx/nginx.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/ntriples/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/ntriples/ntriples.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/octave/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/octave/octave.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/pascal/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/pascal/pascal.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/pegjs/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/pegjs/pegjs.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/perl/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/perl/perl.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/php/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/php/php.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/php/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/pig/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/pig/pig.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/properties/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/properties/properties.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/puppet/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/puppet/puppet.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/python/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/python/python.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/q/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/q/q.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/r/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/r/r.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/rpm/changes/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/rpm/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/rpm/rpm.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/rst/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/rst/rst.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/ruby/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/ruby/ruby.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/ruby/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/rust/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/rust/rust.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/sass/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/sass/sass.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/scheme/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/scheme/scheme.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/shell/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/shell/shell.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/shell/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/sieve/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/sieve/sieve.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/slim/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/slim/slim.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/slim/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/smalltalk/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/smalltalk/smalltalk.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/smarty/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/smarty/smarty.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/smartymixed/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/smartymixed/smartymixed.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/solr/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/solr/solr.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/soy/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/soy/soy.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/sparql/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/sparql/sparql.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/spreadsheet/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/spreadsheet/spreadsheet.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/sql/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/sql/sql.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/stex/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/stex/stex.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/stex/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/stylus/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/stylus/stylus.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tcl/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tcl/tcl.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/textile/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/textile/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/textile/textile.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tiddlywiki/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tiddlywiki/tiddlywiki.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tiddlywiki/tiddlywiki.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tiki/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tiki/tiki.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tiki/tiki.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/toml/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/toml/toml.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tornado/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/tornado/tornado.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/turtle/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/turtle/turtle.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/vb/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/vb/vb.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/vbscript/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/vbscript/vbscript.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/velocity/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/velocity/velocity.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/verilog/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/verilog/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/verilog/verilog.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/xml/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/xml/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/xml/xml.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/xquery/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/xquery/test.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/xquery/xquery.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/yaml/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/yaml/yaml.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/z80/index.html create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/mode/z80/z80.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/modes.min.js create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/package.json create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/3024-day.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/3024-night.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/ambiance-mobile.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/ambiance.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/base16-dark.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/base16-light.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/blackboard.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/cobalt.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/colorforth.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/eclipse.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/elegant.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/erlang-dark.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/lesser-dark.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/mbo.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/mdn-like.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/midnight.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/monokai.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/neat.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/neo.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/night.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/paraiso-dark.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/paraiso-light.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/pastel-on-dark.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/rubyblue.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/solarized.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/the-matrix.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/tomorrow-night-bright.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/tomorrow-night-eighties.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/twilight.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/vibrant-ink.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/xq-dark.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/xq-light.css create mode 100755 src/main/resources/static/admin/editormd/lib/codemirror/theme/zenburn.css create mode 100755 src/main/resources/static/admin/editormd/lib/flowchart.min.js create mode 100755 src/main/resources/static/admin/editormd/lib/jquery.flowchart.min.js create mode 100755 src/main/resources/static/admin/editormd/lib/marked.min.js create mode 100755 src/main/resources/static/admin/editormd/lib/prettify.min.js create mode 100755 src/main/resources/static/admin/editormd/lib/raphael.min.js create mode 100755 src/main/resources/static/admin/editormd/lib/sequence-diagram.min.js create mode 100755 src/main/resources/static/admin/editormd/lib/underscore.min.js create mode 100755 src/main/resources/static/admin/editormd/package.json create mode 100755 src/main/resources/static/admin/editormd/plugins/code-block-dialog/code-block-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/emoji-dialog/emoji-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/emoji-dialog/emoji.json create mode 100755 src/main/resources/static/admin/editormd/plugins/goto-line-dialog/goto-line-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/help-dialog/help-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/help-dialog/help.md create mode 100755 src/main/resources/static/admin/editormd/plugins/html-entities-dialog/html-entities-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/html-entities-dialog/html-entities.json create mode 100755 src/main/resources/static/admin/editormd/plugins/image-dialog/image-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/link-dialog/link-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/plugin-template.js create mode 100755 src/main/resources/static/admin/editormd/plugins/preformatted-text-dialog/preformatted-text-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/reference-link-dialog/reference-link-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/table-dialog/table-dialog.js create mode 100755 src/main/resources/static/admin/editormd/plugins/test-plugin/test-plugin.js create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.codemirror.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.dialog.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.form.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.grid.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.logo.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.menu.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.preview.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.preview.themes.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.tab.scss create mode 100755 src/main/resources/static/admin/editormd/scss/editormd.themes.scss create mode 100755 src/main/resources/static/admin/editormd/scss/font-awesome.scss create mode 100755 src/main/resources/static/admin/editormd/scss/github-markdown.scss create mode 100755 src/main/resources/static/admin/editormd/scss/lib/prefixes.scss create mode 100755 src/main/resources/static/admin/editormd/scss/lib/variables.scss create mode 100755 src/main/resources/static/admin/editormd/scss/prettify.scss create mode 100755 src/main/resources/static/admin/editormd/src/editormd.js create mode 100644 src/main/resources/static/admin/images/attach.png create mode 100644 src/main/resources/static/admin/images/bg/1.png create mode 100644 src/main/resources/static/admin/images/bg/2.png create mode 100644 src/main/resources/static/admin/images/bg/3.png create mode 100644 src/main/resources/static/admin/images/bg/4.png create mode 100644 src/main/resources/static/admin/images/bg/5.png create mode 100644 src/main/resources/static/admin/images/favicon.png create mode 100644 src/main/resources/static/admin/images/icons/bg-adjust@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-apple@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-balance@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-beaker@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-beer@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-books@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-box@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-cake@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-calcuLator@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-camera@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-cd@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-certified@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-champagne@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-chart@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-chat@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-cheese@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-commerce@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-court@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-delivery@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-document@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-dvd@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-eco@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-film@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-grape@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-hot@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-house@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-icecream@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-illustration@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-joystick@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-keynote@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-like@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-link@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-magic@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-mail@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-map@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-mediation@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-microphone@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-network@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-notes@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-palette@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-pig@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-plaster@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-player@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-polaroid@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-printer@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-pulse@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-quote@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-radio@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-recorder@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-scissors@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-search@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-select@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-serving@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-share@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-shop@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-shopping@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-smartphone@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-social@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-sofa@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-syringe@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-tags@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-tape@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-target@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-team@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-television@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-time@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-upload@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-vector@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-video@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-walkman@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-winner@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-works@2x.png create mode 100644 src/main/resources/static/admin/images/icons/bg-zoom@2x.png create mode 100644 src/main/resources/static/admin/images/loading.gif create mode 100644 src/main/resources/static/admin/images/logo.png create mode 100644 src/main/resources/static/admin/images/small/bg.jpg create mode 100644 src/main/resources/static/admin/js/article.js create mode 100644 src/main/resources/static/admin/js/base.js create mode 100644 src/main/resources/static/admin/js/install.js create mode 100644 src/main/resources/static/admin/js/jquery.app.js create mode 100644 src/main/resources/static/admin/plugins/jquery-multi-select/img/switch.png create mode 100644 src/main/resources/static/admin/plugins/jquery-multi-select/jquery.quicksearch.js create mode 100644 src/main/resources/static/admin/plugins/jquery.steps/demo/css/jquery.steps.css create mode 100644 src/main/resources/static/admin/plugins/md/css/style.css create mode 100644 src/main/resources/static/admin/plugins/md/img/editor.png create mode 100644 src/main/resources/static/admin/plugins/md/js/diff.js create mode 100644 src/main/resources/static/admin/plugins/md/js/jquery.js create mode 100644 src/main/resources/static/admin/plugins/md/js/jquery.scrollto.js create mode 100644 src/main/resources/static/admin/plugins/md/js/md.js create mode 100644 src/main/resources/static/admin/plugins/md/js/pagedown-extra.js create mode 100644 src/main/resources/static/admin/plugins/md/js/pagedown.js create mode 100644 src/main/resources/static/admin/plugins/select2.dist.css/select2-bootstrap.css create mode 100644 src/main/resources/static/admin/plugins/select2.dist.css/select2-spinner.gif create mode 100644 src/main/resources/static/admin/plugins/select2.dist.css/select2.css create mode 100644 src/main/resources/static/admin/plugins/select2.dist.css/select2.png create mode 100644 src/main/resources/static/admin/plugins/select2.dist.css/select2x2.png create mode 100644 src/main/resources/static/admin/plugins/tagsinput/jquery.tagsinput.css create mode 100644 src/main/resources/static/admin/plugins/tagsinput/jquery.tagsinput.min.js create mode 100644 src/main/resources/static/admin/plugins/toggles/toggles.css create mode 100644 src/main/resources/static/blog/css/fonts/fontawesome-webfont-.eot create mode 100644 src/main/resources/static/blog/css/fonts/fontawesome-webfont-v=.eot create mode 100644 src/main/resources/static/blog/css/fonts/fontawesome-webfont-v=.ttf create mode 100644 src/main/resources/static/blog/css/fonts/fontawesome-webfont-v=.woff create mode 100644 src/main/resources/static/blog/css/fonts/fontawesome-webfont.svg create mode 100644 src/main/resources/static/blog/css/images/logo.png create mode 100644 src/main/resources/static/blog/css/images/preloader.gif create mode 100644 src/main/resources/static/blog/css/images/preloader@2x.gif create mode 100644 src/main/resources/static/blog/css/style.css create mode 100644 src/main/resources/static/blog/fancybox/blank.gif create mode 100644 src/main/resources/static/blog/fancybox/fancybox_loading.gif create mode 100644 src/main/resources/static/blog/fancybox/fancybox_loading@2x.gif create mode 100644 src/main/resources/static/blog/fancybox/fancybox_overlay.png create mode 100644 src/main/resources/static/blog/fancybox/fancybox_sprite.png create mode 100644 src/main/resources/static/blog/fancybox/fancybox_sprite@2x.png create mode 100644 src/main/resources/static/blog/fancybox/jquery.fancybox.css create mode 100644 src/main/resources/static/blog/fancybox/jquery.fancybox.pack.js create mode 100644 src/main/resources/static/blog/js/jquery.min.js create mode 100644 src/main/resources/static/blog/js/jquery.scrollLoading.js create mode 100644 src/main/resources/static/blog/js/script.js create mode 100644 src/main/resources/static/error/css/style.css create mode 100644 src/main/resources/static/error/fonts/icons-.eot create mode 100644 src/main/resources/static/error/fonts/icons-64c061773912345c985cb3365a710a98.eot create mode 100644 src/main/resources/static/error/fonts/icons-64c061773912345c985cb3365a710a98.ttf create mode 100644 src/main/resources/static/error/fonts/icons-64c061773912345c985cb3365a710a98.woff create mode 100644 src/main/resources/static/error/img/icons.png create mode 100644 src/main/resources/static/home/assets/images/Wechat.jpg create mode 100755 src/main/resources/static/home/assets/images/icon-behance.png create mode 100644 src/main/resources/static/home/assets/images/icon-csdn.png create mode 100755 src/main/resources/static/home/assets/images/icon-dribbble.png create mode 100644 src/main/resources/static/home/assets/images/icon-email.png create mode 100755 src/main/resources/static/home/assets/images/icon-github.png create mode 100755 src/main/resources/static/home/assets/images/icon-instagram.png create mode 100644 src/main/resources/static/home/assets/images/icon-jianshu.png create mode 100755 src/main/resources/static/home/assets/images/icon-px.png create mode 100644 src/main/resources/static/home/assets/images/icon-resume.png create mode 100755 src/main/resources/static/home/assets/images/icon-twitter.png create mode 100755 src/main/resources/static/home/assets/images/icon-uicn.png create mode 100755 src/main/resources/static/home/assets/images/icon-weibo.png create mode 100644 src/main/resources/static/home/assets/images/icon-zhihu.png create mode 100755 src/main/resources/static/home/assets/images/profile-1.png create mode 100755 src/main/resources/static/home/assets/images/profile.jpg create mode 100755 src/main/resources/static/home/assets/styles/sass/parts/_about.scss create mode 100755 src/main/resources/static/home/assets/styles/sass/parts/_profile.scss create mode 100755 src/main/resources/static/home/assets/styles/sass/parts/_working.scss create mode 100755 src/main/resources/static/home/assets/styles/sass/site.scss create mode 100755 src/main/resources/static/home/assets/styles/sass/universal/_animations.scss create mode 100755 src/main/resources/static/home/assets/styles/sass/universal/_body.scss create mode 100755 src/main/resources/static/home/assets/styles/sass/universal/_icons.scss create mode 100755 src/main/resources/static/home/assets/styles/sass/universal/_normalize.scss create mode 100755 src/main/resources/static/home/assets/styles/sass/universal/_typography.scss create mode 100755 src/main/resources/static/home/assets/styles/sass/universal/_variables.scss create mode 100755 src/main/resources/static/home/assets/styles/site.css create mode 100644 src/main/resources/static/photo/css/styles.css create mode 100644 src/main/resources/static/photo/favicon/favicon.ico create mode 100644 src/main/resources/templates/admin/article_edit.html create mode 100644 src/main/resources/templates/admin/article_list.html create mode 100644 src/main/resources/templates/admin/attach.html create mode 100644 src/main/resources/templates/admin/category.html create mode 100644 src/main/resources/templates/admin/comment_list.html create mode 100644 src/main/resources/templates/admin/footer.html create mode 100644 src/main/resources/templates/admin/header.html create mode 100644 src/main/resources/templates/admin/index.html create mode 100644 src/main/resources/templates/admin/links.html create mode 100644 src/main/resources/templates/admin/login.html create mode 100644 src/main/resources/templates/admin/page_edit.html create mode 100644 src/main/resources/templates/admin/page_list.html create mode 100644 src/main/resources/templates/admin/profile.html create mode 100644 src/main/resources/templates/admin/setting.html create mode 100644 src/main/resources/templates/blog/archives.html create mode 100644 src/main/resources/templates/blog/categories.html create mode 100644 src/main/resources/templates/blog/footer.html create mode 100644 src/main/resources/templates/blog/head.html create mode 100644 src/main/resources/templates/blog/header.html create mode 100644 src/main/resources/templates/blog/index.html create mode 100644 src/main/resources/templates/blog/page_nav.html create mode 100644 src/main/resources/templates/blog/post.html create mode 100644 src/main/resources/templates/comm/macros.html create mode 100644 src/main/resources/templates/comm/tale_comment.html create mode 100644 src/main/resources/templates/error/404.html create mode 100644 src/main/resources/templates/error/500.html create mode 100755 src/main/resources/templates/home/about.html create mode 100644 src/main/resources/templates/home/footer.html create mode 100644 src/main/resources/templates/home/head.html create mode 100644 src/main/resources/templates/home/header.html create mode 100755 src/main/resources/templates/home/index.html create mode 100644 src/main/resources/templates/photo/article.html create mode 100644 src/main/resources/templates/photo/footer.html create mode 100644 src/main/resources/templates/photo/head.html create mode 100644 src/main/resources/templates/photo/header.html create mode 100644 src/main/resources/templates/photo/index.html create mode 100644 src/test/java/cn/luischen/MySiteApplicationTests.java create mode 100644 tree.log diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..82eca336 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cc84ea9b4d95453115d0c26488d6a78694e0bc6 GIT binary patch literal 47610 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 literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..b573bb50 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip diff --git a/mvnw b/mvnw new file mode 100755 index 00000000..5bf251c0 --- /dev/null +++ b/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 00000000..019bd74d --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..9e0e2c89 --- /dev/null +++ b/pom.xml @@ -0,0 +1,190 @@ + + + 4.0.0 + + cn.luischen + my-site + 0.0.1-SNAPSHOT + jar + + my-site + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.1.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + springboot + + + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + org.springframework.boot + spring-boot-starter-aop + + + + mysql + mysql-connector-java + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.apache.commons + commons-lang3 + 3.4 + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + + + com.fasterxml.jackson.module + jackson-module-parameter-names + + + + com.github.pagehelper + pagehelper + 5.1.3 + + + + com.alibaba + druid-spring-boot-starter + 1.1.0 + + + + + + + com.vdurmont + emoji-java + 3.2.0 + + + + + io.springfox + springfox-swagger2 + 2.2.2 + + + io.springfox + springfox-swagger-ui + 2.2.2 + + + + + net.sourceforge.nekohtml + nekohtml + 1.9.22 + + + + + com.qcloud + cos_api + 5.2.4 + + + + com.google.code.gson + gson + 2.8.0 + + + + + + com.atlassian.commonmark + commonmark + 0.8.0 + + + + com.atlassian.commonmark + commonmark-ext-gfm-tables + 0.8.0 + + + + + com.qiniu + qiniu-java-sdk + 7.2.11 + + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + com.spotify + docker-maven-plugin + 1.0.0 + + ${docker.image.prefix}/${project.artifactId} + src/main/docker + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + + + + + + + + diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile new file mode 100644 index 00000000..35495f8c --- /dev/null +++ b/src/main/docker/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ADD my-site-0.0.1-SNAPSHOT.jar app.jar +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] \ No newline at end of file diff --git a/src/main/java/cn/luischen/MySiteApplication.java b/src/main/java/cn/luischen/MySiteApplication.java new file mode 100644 index 00000000..55dd8e1e --- /dev/null +++ b/src/main/java/cn/luischen/MySiteApplication.java @@ -0,0 +1,12 @@ +package cn.luischen; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MySiteApplication { + + public static void main(String[] args) { + SpringApplication.run(MySiteApplication.class, args); + } +} diff --git a/src/main/java/cn/luischen/Swagger2.java b/src/main/java/cn/luischen/Swagger2.java new file mode 100644 index 00000000..be7e9f85 --- /dev/null +++ b/src/main/java/cn/luischen/Swagger2.java @@ -0,0 +1,45 @@ +package cn.luischen; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Swagger2 配置 + * Created by Donghua.Chen on 2018/4/20. + */ +@Configuration +@EnableSwagger2 +public class Swagger2 { + + @Value("${swagger.show}") + private boolean swaggerShow; + + + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .enable(swaggerShow) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.basePackage("cn.luischen.controller")) + .paths(PathSelectors.any()) + .build(); + } + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("Luis Site Swagger Restful API") + .description("更多Spring Boot相关文章请关注:https://luischen.com/") + .termsOfServiceUrl("https://luischen.com/") + .contact("Luis chen") + .version("1.0") + .build(); + } +} diff --git a/src/main/java/cn/luischen/api/QCloudService.java b/src/main/java/cn/luischen/api/QCloudService.java new file mode 100644 index 00000000..f68ea2d6 --- /dev/null +++ b/src/main/java/cn/luischen/api/QCloudService.java @@ -0,0 +1,73 @@ +package cn.luischen.api; + +import cn.luischen.utils.Commons; +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.auth.COSCredentials; +import com.qcloud.cos.http.HttpMethodName; +import com.qcloud.cos.model.GeneratePresignedUrlRequest; +import com.qcloud.cos.model.ObjectMetadata; +import com.qcloud.cos.model.PutObjectRequest; +import com.qcloud.cos.model.PutObjectResult; +import com.qcloud.cos.region.Region; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.URL; + +/** + * 七牛云对象存储API + * Created by Donghua.Chen on 2018/3/25. + */ +@Service +public class QCloudService { + + public static final String SECRETID = "AKIDZ2KRludhKn4FHAGQo4B8xeBXwGPNalBQ"; + public static final String SECRETKEY = "CPWUJg8UhniS3TmuwmRvEOQK5jFcopfJ"; + //bucket的命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 + public static final String BUCKET = "pic-1253516459"; + public static final String UPLOAD_URL = "http://" + BUCKET + ".cos.ap-shanghai.myqcloud.com/"; + + + public COSClient getCOSClient() { + // 1 初始化用户身份信息(secretId, secretKey) + COSCredentials cred = new BasicCOSCredentials(SECRETID, SECRETKEY); + // 2 设置bucket的区域, COS地域的简称请参照 https://cloud.tencent.com/document/product/436/6224 + ClientConfig clientConfig = new ClientConfig(new Region("ap-shanghai")); + // 3 生成cos客户端 + COSClient cosclient = new COSClient(cred, clientConfig); + + return cosclient; + } + + public String fileUpLoad(MultipartFile file) { + + COSClient cosClient = this.getCOSClient(); + // 简单文件上传, 最大支持 5 GB, 适用于小文件上传, 建议 20 M 以下的文件使用该接口 + // 大文件上传请参照 API 文档高级 API 上传 + // 指定要上传到 COS 上的路径 + String key = Commons.getFileRename(file.getOriginalFilename()); + PutObjectRequest putObjectRequest = null; + try { + ObjectMetadata objectMetadata = new ObjectMetadata(); + // 设置输入流长度为 500 + objectMetadata.setContentLength(500); + // 设置 Content type, 默认是 application/octet-stream + objectMetadata.setContentType(file.getContentType()); + putObjectRequest = new PutObjectRequest(BUCKET, key, file.getInputStream(),objectMetadata); + } catch (IOException e) { + e.printStackTrace(); + } + PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest); + GeneratePresignedUrlRequest req = + new GeneratePresignedUrlRequest(BUCKET, key, HttpMethodName.GET); + URL url = cosClient.generatePresignedUrl(req); + + cosClient.shutdown(); + + return url.toString(); + + } +} diff --git a/src/main/java/cn/luischen/api/QiniuCloudService.java b/src/main/java/cn/luischen/api/QiniuCloudService.java new file mode 100644 index 00000000..75d9172e --- /dev/null +++ b/src/main/java/cn/luischen/api/QiniuCloudService.java @@ -0,0 +1,61 @@ +package cn.luischen.api; + +import cn.luischen.utils.TaleUtils; +import com.google.gson.Gson; +import com.qiniu.common.QiniuException; +import com.qiniu.common.Zone; +import com.qiniu.http.Response; +import com.qiniu.storage.Configuration; +import com.qiniu.storage.UploadManager; +import com.qiniu.storage.model.DefaultPutRet; +import com.qiniu.util.Auth; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +/** + * Created by Donghua.Chen on 2018/5/1. + */ +public class QiniuCloudService { + + private static final String ACCESS_KEY = "FCS-foi-VhE2qje5vux2gE0nVS4-LJQNRkl6YFP4"; + private static final String SECRET_KEY = "J6cBu-9ZLZHNIznb88_hYDFfANaRnKr3qGDPNnWS"; + private static final String BUCKET = "blog"; + + public static final String QINIU_UPLOAD_SITE = "http://ozlpw4ja9.bkt.clouddn.com/"; + + public static String upload(MultipartFile file, String fileName) { + + //构造一个带指定Zone对象的配置类 + Configuration cfg = new Configuration(Zone.zone0()); + //...其他参数参考类注释 + UploadManager uploadManager = new UploadManager(cfg); + //默认不指定key的情况下,以文件内容的hash值作为文件名 + String key = null; + Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY); + String upToken = auth.uploadToken(BUCKET); + try { + Response response = null; + + response = uploadManager.put(file.getInputStream(), fileName, upToken, null, null); + + //解析上传成功的结果 + DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class); + System.out.println(putRet.key); + System.out.println(putRet.hash); + return putRet.key; + } catch (QiniuException ex) { + Response r = ex.response; + System.err.println(r.toString()); + try { + System.err.println(r.bodyString()); + } catch (QiniuException ex2) { + //ignore + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/src/main/java/cn/luischen/aspect/WebLogAspect.java b/src/main/java/cn/luischen/aspect/WebLogAspect.java new file mode 100644 index 00000000..11ca64f8 --- /dev/null +++ b/src/main/java/cn/luischen/aspect/WebLogAspect.java @@ -0,0 +1,64 @@ +package cn.luischen.aspect; + +import cn.luischen.service.log.LogService; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.util.Arrays; + +/** + * 请求的日志处理 + * Created by Donghua.Chen on 2018/4/28. + */ +@Aspect +@Component +public class WebLogAspect { + + @Autowired + private LogService logService; + + private static Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class); + + ThreadLocal startTime = new ThreadLocal<>(); + + @Pointcut("execution(public * cn.luischen.controller..*.*(..))") + public void webLog(){} + + + @Before("webLog()") + public void doBefore(JoinPoint joinPoint){ + + startTime.set(System.currentTimeMillis()); + + //接收到请求,记录请求内容 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = attributes.getRequest(); + HttpSession session = request.getSession(); + // 记录下请求内容 + LOGGER.info("URL : " + request.getRequestURL().toString()); + LOGGER.info("HTTP_METHOD : " + request.getMethod()); + LOGGER.info("IP : " + request.getRemoteAddr()); + LOGGER.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); + LOGGER.info("ARGS : " + Arrays.toString(joinPoint.getArgs())); + } + + @AfterReturning(returning = "ret", pointcut = "webLog()") + public void doAfterReturning(Object ret) throws Throwable { + // 处理完请求,返回内容 + LOGGER.info("RESPONSE : " + ret); + LOGGER.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get())); + startTime.remove();//用完之后记得清除,不然可能导致内存泄露; + } + +} diff --git a/src/main/java/cn/luischen/config/DataSourceConfig.java b/src/main/java/cn/luischen/config/DataSourceConfig.java new file mode 100644 index 00000000..f074630a --- /dev/null +++ b/src/main/java/cn/luischen/config/DataSourceConfig.java @@ -0,0 +1,42 @@ +package cn.luischen.config; + +import com.github.pagehelper.PageInterceptor; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; + +/** + * Created by Donghua.Chen on 2018/4/20. + */ +@Configuration +@MapperScan("cn.luischen.dao") +@EnableTransactionManagement +public class DataSourceConfig { + + + @Autowired + private Environment env; + + @Autowired + private PageInterceptor pageInterceptor; + + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { + SqlSessionFactoryBean fb = new SqlSessionFactoryBean(); + fb.setDataSource(dataSource); + //该配置非常的重要,如果不将PageInterceptor设置到SqlSessionFactoryBean中,导致分页失效 + fb.setPlugins(new Interceptor[]{pageInterceptor}); + fb.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package")); + fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapper-locations"))); + return fb.getObject(); + } +} diff --git a/src/main/java/cn/luischen/config/DruidDBConfig.java b/src/main/java/cn/luischen/config/DruidDBConfig.java new file mode 100644 index 00000000..6ede882e --- /dev/null +++ b/src/main/java/cn/luischen/config/DruidDBConfig.java @@ -0,0 +1,108 @@ +package cn.luischen.config; + +import com.alibaba.druid.pool.DruidDataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import javax.sql.DataSource; +import java.sql.SQLException; + +/** + * Created by Donghua.Chen on 2018/4/20. + */ +@Configuration +public class DruidDBConfig { + + private Logger logger = LoggerFactory.getLogger(DruidDBConfig.class); + + @Value("${spring.datasource.url}") + private String dbUrl; + + @Value("${spring.datasource.username}") + private String username; + + @Value("${spring.datasource.password}") + private String password; + + @Value("${spring.datasource.driver-class-name}") + private String driverClassName; + + @Value("${spring.datasource.initialSize}") + private int initialSize; + + @Value("${spring.datasource.minIdle}") + private int minIdle; + + @Value("${spring.datasource.maxActive}") + private int maxActive; + + @Value("${spring.datasource.maxWait}") + private int maxWait; + + @Value("${spring.datasource.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.testOnReturn}") + private boolean testOnReturn; + + @Value("${spring.datasource.poolPreparedStatements}") + private boolean poolPreparedStatements; + + @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}") + private int maxPoolPreparedStatementPerConnectionSize; + + @Value("${spring.datasource.filters}") + private String filters; + + @Value("{spring.datasource.connectionProperties}") + private String connectionProperties; + + @Bean //声明其为Bean实例 + @Primary //在同样的DataSource中,首先使用被标注的DataSource + public DataSource dataSource(){ + DruidDataSource datasource = new DruidDataSource(); + + datasource.setUrl(this.dbUrl); + datasource.setUsername(username); + datasource.setPassword(password); + datasource.setDriverClassName(driverClassName); + + //configuration + datasource.setInitialSize(initialSize); + datasource.setMinIdle(minIdle); + datasource.setMaxActive(maxActive); + datasource.setMaxWait(maxWait); + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setValidationQuery(validationQuery); + datasource.setTestWhileIdle(testWhileIdle); + datasource.setTestOnBorrow(testOnBorrow); + datasource.setTestOnReturn(testOnReturn); + datasource.setPoolPreparedStatements(poolPreparedStatements); + datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); + try { + datasource.setFilters(filters); + } catch (SQLException e) { + logger.error("druid configuration initialization filter", e); + } + datasource.setConnectionProperties(connectionProperties); + + return datasource; + } +} diff --git a/src/main/java/cn/luischen/config/PageHelperConfig.java b/src/main/java/cn/luischen/config/PageHelperConfig.java new file mode 100644 index 00000000..2e67cc5a --- /dev/null +++ b/src/main/java/cn/luischen/config/PageHelperConfig.java @@ -0,0 +1,30 @@ +package cn.luischen.config; + +import com.github.pagehelper.PageInterceptor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Properties; + +/** + * Created by Donghua.Chen on 2018/4/20. + */ +@Configuration +public class PageHelperConfig { + + + @Value("${pagehelper.helperDialect}") + private String helperDialect; + + + @Bean + public PageInterceptor pageInterceptor(){ + PageInterceptor pageInterceptor = new PageInterceptor(); + Properties properties = new Properties(); + properties.setProperty("helperDialect", helperDialect); + pageInterceptor.setProperties(properties); + return pageInterceptor; + } +} + diff --git a/src/main/java/cn/luischen/constant/ErrorConstant.java b/src/main/java/cn/luischen/constant/ErrorConstant.java new file mode 100644 index 00000000..6c5c5183 --- /dev/null +++ b/src/main/java/cn/luischen/constant/ErrorConstant.java @@ -0,0 +1,60 @@ +package cn.luischen.constant; + +/** + * Created by Donghua.Chen on 2018/4/29. + */ +public interface ErrorConstant { + + interface Common { + static final String PARAM_IS_EMPTY = "参数为空"; + static final String INVALID_PARAM = "无效的参数"; + static final String CAN_NOT_FIND_PARAM_TO_CONTIUNE = "找不到参数继续运行"; + } + + interface Article { + static final String UPDATE_ARTICLE_FAIL = "更新文章失败"; + static final String ADD_NEW_ARTICLE_FAIL = "添加文章失败"; + static final String DELETE_ARTICLE_ERROR = "删除文章失败"; + static final String TITLE_IS_TOO_LONG = "文章标题过长"; + static final String TITLE_CAN_NOT_EMPTY = "文章标题不能为空"; + static final String CONTENT_CAN_NOT_EMPTY = "文章内容不能为空"; + static final String CONTENT_IS_TOO_LONG = "文章字数超过限制"; + + } + + interface Att { + static final String ADD_NEW_ATT_FAIL = "添加附件信息失败"; + static final String UPDATE_ATT_FAIL = "更新附件信息失败"; + static final String DELETE_ATT_FAIL = "删除附件信息失败"; + static final String UPLOAD_FILE_FAIL = "上传附件失败"; + } + + interface Comment { + static final String ADD_NEW_COMMENT_FAIL = "添加评论失败"; + static final String UPDATE_COMMENT_FAIL = "更新评论失败"; + static final String DELETE_COMMENT_FAIL = "删除评论失败"; + static final String COMMENT_NOT_EXIST = "评论不存在"; + } + + interface Option { + static final String DELETE_OPTION_FAIL = "删除配置失败"; + static final String UPDATE_OPTION_FAIL = "更新配置失败"; + } + + interface Meta { + static final String ADD_META_FAIL = "添加项目信息失败"; + static final String UPDATE_META_FAIL = "更新项目信息失败"; + static final String DELETE_META_FAIL = "删除项目信息失败"; + static final String NOT_ONE_RESULT = "获取的项目的数量不止一个"; + static final String META_IS_EXIST = "该项目已经存在"; + } + + interface Auth { + static final String USERNAME_PASSWORD_IS_EMPTY = "用户名和密码不可为空"; + static final String USERNAME_PASSWORD_ERROR = "用户名不存在或密码错误"; + static final String NOT_LOGIN = "用户未登录"; + } + + + +} diff --git a/src/main/java/cn/luischen/constant/LogActions.java b/src/main/java/cn/luischen/constant/LogActions.java new file mode 100644 index 00000000..1800b781 --- /dev/null +++ b/src/main/java/cn/luischen/constant/LogActions.java @@ -0,0 +1,26 @@ +package cn.luischen.constant; + +/** + * 日志表的action字段 + * Created by Donghua.Chen on 2018/4/30. + */ +public enum LogActions { + + LOGIN("登录后台"), UP_PWD("修改密码"), UP_INFO("修改个人信息"), + DEL_ARTICLE("删除文章"), DEL_PAGE("删除页面"), SYS_BACKUP("系统备份"), + SYS_SETTING("保存系统设置"), INIT_SITE("初始化站点"); + + private String action; + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + LogActions(String action) { + this.action = action; + } +} diff --git a/src/main/java/cn/luischen/constant/Types.java b/src/main/java/cn/luischen/constant/Types.java new file mode 100644 index 00000000..6a610e12 --- /dev/null +++ b/src/main/java/cn/luischen/constant/Types.java @@ -0,0 +1,45 @@ +package cn.luischen.constant; + +/** + * Created by Donghua.Chen on 2018/4/30. + */ +public enum Types { + + TAG("tag"), + CATEGORY("category"), + ARTICLE("post"), + PUBLISH("publish"), + PAGE("page"), + DRAFT("draft"), + LINK("link"), + IMAGE("image"), + FILE("file"), + CSRF_TOKEN("csrf_token"), + COMMENTS_FREQUENCY("comments:frequency"), + PHOTO("photo"), + + /** + * 附件存放的URL,默认为网站地址,如集成第三方则为第三方CDN域名 + */ + ATTACH_URL("attach_url"), + + /** + * 网站要过滤,禁止访问的ip列表 + */ + BLOCK_IPS("site_block_ips"); + + + private String type; + + public java.lang.String getType() { + return type; + } + + public void setType(java.lang.String type) { + this.type = type; + } + + Types(java.lang.String type) { + this.type = type; + } +} diff --git a/src/main/java/cn/luischen/constant/WebConst.java b/src/main/java/cn/luischen/constant/WebConst.java new file mode 100644 index 00000000..c60a14b8 --- /dev/null +++ b/src/main/java/cn/luischen/constant/WebConst.java @@ -0,0 +1,63 @@ +package cn.luischen.constant; + +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Donghua.Chen on 2018/4/29. + */ +@Component +public class WebConst { + + /** + * 一些网站配置 + */ + public static Map initConfig = new HashMap<>(); + + /** + * session的key + */ + public static String LOGIN_SESSION_KEY = "login_user"; + + public static final String USER_IN_COOKIE = "S_L_ID"; + + + + + /** + * aes加密加盐 + */ + public static String AES_SALT = "0123456789abcdef"; + + /** + * 最大获取文章条数 + */ + public static final int MAX_POSTS = 9999; + + /** + * 最大页码 + */ + public static final int MAX_PAGE = 100; + + /** + * 文章最多可以输入的文字数 + */ + public static final int MAX_TEXT_COUNT = 200000; + + /** + * 文章标题最多可以输入的文字个数 + */ + public static final int MAX_TITLE_COUNT = 200; + + /** + * 点击次数超过多少更新到数据库 + */ + public static final int HIT_EXCEED = 10; + + /** + * 上传文件最大1M + */ + public static Integer MAX_FILE_SIZE = 1048576; +} diff --git a/src/main/java/cn/luischen/controller/BaseController.java b/src/main/java/cn/luischen/controller/BaseController.java new file mode 100644 index 00000000..85110204 --- /dev/null +++ b/src/main/java/cn/luischen/controller/BaseController.java @@ -0,0 +1,97 @@ +package cn.luischen.controller; + +import cn.luischen.constant.Types; +import cn.luischen.constant.WebConst; +import cn.luischen.dto.ArchiveDto; +import cn.luischen.dto.MetaDto; +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.dto.cond.MetaCond; +import cn.luischen.model.ContentDomain; +import cn.luischen.model.UserDomain; +import cn.luischen.service.content.ContentService; +import cn.luischen.service.meta.MetaService; +import cn.luischen.service.site.SiteService; +import cn.luischen.utils.MapCache; +import cn.luischen.utils.TaleUtils; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * + * Created by Donghua.Chen on 2018/4/30. + */ +public abstract class BaseController { + + @Autowired + private ContentService contentService; + + @Autowired + private MetaService metaService; + + @Autowired + private SiteService siteService; + + + protected MapCache cache = MapCache.single(); + + public BaseController title(HttpServletRequest request, String title) { + request.setAttribute("title", title); + return this; + } + + /** + * 获取blog页面需要的公共数据 + * @param request + * @return + */ + public BaseController blogBaseData(HttpServletRequest request, ContentCond contentCond){ + List categories = metaService.getMetaList(Types.CATEGORY.getType(), null, WebConst.MAX_POSTS); + List tags = metaService.getMetaList(Types.TAG.getType(), null, WebConst.MAX_POSTS); + List links = metaService.getMetaList(Types.LINK.getType(), null,WebConst.MAX_POSTS); + request.setAttribute("categories", categories);//分类 + request.setAttribute("tags", tags);//标签 + request.setAttribute("links", links); + PageInfo recentlyArticles = contentService.getRecentlyArticle(1, 10); + ContentCond cond = new ContentCond(); + cond.setType(contentCond.getType()); + request.setAttribute("recentlyArticles", recentlyArticles); + List archives = siteService.getArchivesSimple(cond); + request.setAttribute("archives", archives);//归档数据 + + return this; + } + /** + * 获取请求绑定的登录对象 + * @param request + * @return + */ + public UserDomain user(HttpServletRequest request) { + return TaleUtils.getLoginUser(request); + } + + public Integer getUid(HttpServletRequest request){ + return this.user(request).getUid(); + } + + /** + * 数组转字符串 + * + * @param arr + * @return + */ + public String join(String[] arr) { + StringBuilder ret = new StringBuilder(); + String[] var3 = arr; + int var4 = arr.length; + + for (int var5 = 0; var5 < var4; ++var5) { + String item = var3[var5]; + ret.append(',').append(item); + } + + return ret.length() > 0 ? ret.substring(1) : ret.toString(); + } +} diff --git a/src/main/java/cn/luischen/controller/BlogController.java b/src/main/java/cn/luischen/controller/BlogController.java new file mode 100644 index 00000000..08df0797 --- /dev/null +++ b/src/main/java/cn/luischen/controller/BlogController.java @@ -0,0 +1,263 @@ +package cn.luischen.controller; + +import cn.luischen.constant.Types; +import cn.luischen.constant.WebConst; +import cn.luischen.dto.ArchiveDto; +import cn.luischen.dto.MetaDto; +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.dto.cond.MetaCond; +import cn.luischen.model.ContentDomain; +import cn.luischen.service.content.ContentService; +import cn.luischen.service.meta.MetaService; +import cn.luischen.service.site.SiteService; +import cn.luischen.utils.DateKit; +import com.github.pagehelper.Page; +import com.github.pagehelper.PageInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.security.PermitAll; +import javax.servlet.http.HttpServletRequest; +import java.util.Date; +import java.util.List; + +/** + * Created by Donghua.Chen on 2018/5/1. + */ +@Api("blog接口") +@Controller +@RequestMapping("/blog") +public class BlogController extends BaseController { + + @Autowired + private ContentService contentService; + + @Autowired + private MetaService metaService; + + @Autowired + private SiteService siteService; + + + @ApiOperation("blog首页") + @GetMapping(value = {"","index"}) + public String index( + HttpServletRequest request, + @ApiParam(name = "limit", value = "页数", required = false) + @RequestParam(name = "limit", required = false, defaultValue = "10") + int limit + ){ + return this.index(request, 1, limit); + } + + @ApiOperation("blog首页-分页") + @GetMapping(value = "page/{p}") + public String index( + HttpServletRequest request, + @PathVariable("p") + int p, + @RequestParam(value = "limit", required = false, defaultValue = "10") + int limit + ){ + p = p < 0 || p > WebConst.MAX_PAGE ? 1 : p; + ContentCond contentCond = new ContentCond(); + contentCond.setType(Types.ARTICLE.getType()); + PageInfo articles = contentService.getArticlesByCond(contentCond, p, limit); + request.setAttribute("articles", articles);//文章列表 + request.setAttribute("type", "articles"); + this.blogBaseData(request, contentCond);//获取公共分类标签等数据 + return "blog/index"; + } + + @ApiOperation("文章内容页") + @GetMapping(value = "/article/{cid}") + public String post( + @ApiParam(name = "cid", value = "文章主键", required = true) + @PathVariable("cid") + Integer cid, + HttpServletRequest request + ){ + ContentDomain atricle = contentService.getAtricleById(cid); + request.setAttribute("article", atricle); + ContentCond contentCond = new ContentCond(); + contentCond.setType(Types.ARTICLE.getType()); + this.blogBaseData(request, contentCond);//获取公共分类标签等数据 + return "blog/post"; + + } + + @ApiOperation("归档页-按日期") + @GetMapping(value = "/archives/{date}") + public String archives( + @ApiParam(name = "date", value = "归档日期", required = true) + @PathVariable("date") + String date, + HttpServletRequest request + ){ + ContentCond contentCond = new ContentCond(); + Date sd = DateKit.dateFormat(date, "yyyy年MM月"); + int start = DateKit.getUnixTimeByDate(sd); + int end = DateKit.getUnixTimeByDate(DateKit.dateAdd(DateKit.INTERVAL_MONTH, sd, 1)) - 1; + contentCond.setStartTime(start); + contentCond.setEndTime(end); + contentCond.setType(Types.ARTICLE.getType()); + List archives = siteService.getArchives(contentCond); + request.setAttribute("archives_list",archives); + this.blogBaseData(request, contentCond);//获取公共分类标签等数据 + return "blog/archives"; + } + + + + + @ApiOperation("归档页-按年份") + @GetMapping(value = "/archives/year/{year}") + public String archivesAtYear( + @ApiParam(name = "year", value = "归档日期", required = true) + @PathVariable("year") + String year, + HttpServletRequest request + ){ + ContentCond contentCond = new ContentCond(); + int start = DateKit.getUnixTimeByDate(DateKit.getYearStartDay(year, "yyyy")); + int end = DateKit.getUnixTimeByDate(DateKit.getYearEndDay(year, "yyyy")); + contentCond.setStartTime(start); + contentCond.setEndTime(end); + contentCond.setType(Types.ARTICLE.getType()); + List archives = siteService.getArchives(contentCond); + request.setAttribute("archives_list",archives); + this.blogBaseData(request, contentCond);//获取公共分类标签等数据 + return "blog/archives"; + } + + + + + + @ApiOperation("归档页") + @GetMapping(value = {"/archives", "/archives/index"}) + public String archives(HttpServletRequest request){ + ContentCond contentCond = new ContentCond(); + contentCond.setType(Types.ARTICLE.getType()); + List archives = siteService.getArchives(contentCond); + request.setAttribute("archives_list",archives); + this.blogBaseData(request,contentCond);//获取公共分类标签等数据 + return "blog/archives"; + } + + + @ApiOperation("分类") + @GetMapping(value = "/categories/{category}") + public String categories( + @ApiParam(name = "category", value = "分类名", required = true) + @PathVariable("category") + String category, + HttpServletRequest request + ){ + return this.categories(category, 1, 10, request); + } + + @ApiOperation("分类-分页") + @GetMapping(value = "/categories/{category}/page/{page}") + public String categories( + @ApiParam(name = "category", value = "分类名", required = true) + @PathVariable("category") + String category, + @ApiParam(name = "page", value = "页数", required = true) + @PathVariable("page") + int page, + @ApiParam(name = "limit", value = "条数", required = true) + @RequestParam(name = "limit", required = false, defaultValue = "10") + int limit, + HttpServletRequest request + ){ + ContentCond contentCond = new ContentCond(); + contentCond.setType(Types.ARTICLE.getType()); + contentCond.setCategory(category); + PageInfo articles = contentService.getArticlesByCond(contentCond, page, limit); + this.blogBaseData(request,contentCond);//获取公共分类标签等数据 + request.setAttribute("articles_list", articles); + request.setAttribute("type", "categories"); + request.setAttribute("param_name", category); + return "blog/categories"; + + } + + @ApiOperation("标签页") + @GetMapping(value = "/tag/{tag}") + public String tags( + @ApiParam(name = "tag", value = "标签名", required = true) + @PathVariable("tag") + String tag, + HttpServletRequest request + ){ + return this.tags(tag, 1, 10, request); + } + + @ApiOperation("标签页-分页") + @GetMapping(value = "/tag/{tag}/page/{page}") + public String tags( + @ApiParam(name = "tag", value = "标签名", required = true) + @PathVariable("tag") + String tag, + @ApiParam(name = "page", value = "页数", required = true) + @PathVariable("page") + int page, + @ApiParam(name = "limit", value = "条数", required = false) + @RequestParam(name = "limit", required = false, defaultValue = "10") + int limit, + HttpServletRequest request + ){ + ContentCond contentCond = new ContentCond(); + contentCond.setTag(tag); + contentCond.setType(Types.ARTICLE.getType()); + PageInfo articles = contentService.getArticlesByCond(contentCond, page, limit); + this.blogBaseData(request,contentCond);//获取公共分类标签等数据 + request.setAttribute("articles_list", articles); + request.setAttribute("type", "tag"); + request.setAttribute("param_name", tag); + return "blog/categories"; + } + + @ApiOperation("搜索文章") + @GetMapping("/search") + public String search( + @ApiParam(name = "param", value = "搜索的文字", required = true) + @RequestParam(name = "param", required = true) + String param, + HttpServletRequest request + ){ + return this.search(param, 1, 10, request); + } + + @ApiOperation("搜索文章-分页") + @GetMapping(value = "/search/{param}/page/{page}") + public String search( + @ApiParam(name = "param", value = "搜索的文字", required = true) + @PathVariable("param") + String param, + @ApiParam(name = "page", value = "页数", required = true) + @PathVariable("page") + int page, + @ApiParam(name = "limit", value = "条数", required = false) + @RequestParam(name = "limit", required = false, defaultValue = "10") + int limit, + HttpServletRequest request + ){ + PageInfo pageInfo = contentService.searchArticle(param, page, limit); + ContentCond contentCond = new ContentCond(); + contentCond.setType(Types.ARTICLE.getType()); + this.blogBaseData(request,contentCond);//获取公共分类标签等数据 + request.setAttribute("articles", pageInfo); + request.setAttribute("type", "search"); + request.setAttribute("param_name", param); + return "blog/index"; + } + + + +} diff --git a/src/main/java/cn/luischen/controller/HomeController.java b/src/main/java/cn/luischen/controller/HomeController.java new file mode 100644 index 00000000..ba139f6a --- /dev/null +++ b/src/main/java/cn/luischen/controller/HomeController.java @@ -0,0 +1,54 @@ +package cn.luischen.controller; + +import cn.luischen.constant.WebConst; +import cn.luischen.dao.OptionDao; +import cn.luischen.model.OptionsDomain; +import cn.luischen.service.option.OptionService; +import cn.luischen.utils.Commons; +import io.swagger.annotations.Api; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import springfox.documentation.annotations.ApiIgnore; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 首页和关于我的页面控制器 + * Created by Donghua.Chen on 2018/4/28. + */ +@Api("网站首页和关于页面") +@Controller +public class HomeController extends BaseController{ + + @Autowired + private OptionService optionService; + + @ApiIgnore + @GetMapping("/") + public String getIndex(HttpServletRequest request){ + return "home/index"; + } + + + + @ApiIgnore + @GetMapping("/about") + public String getAbout(HttpServletRequest request){ + return "home/about"; + } + + + + + + + + + + +} diff --git a/src/main/java/cn/luischen/controller/PhotoController.java b/src/main/java/cn/luischen/controller/PhotoController.java new file mode 100644 index 00000000..06d5f090 --- /dev/null +++ b/src/main/java/cn/luischen/controller/PhotoController.java @@ -0,0 +1,73 @@ +package cn.luischen.controller; + +import cn.luischen.constant.Types; +import cn.luischen.constant.WebConst; +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.model.ContentDomain; +import cn.luischen.service.content.ContentService; +import com.github.pagehelper.PageInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.servlet.http.HttpServletRequest; + +/** + * Created by Donghua.Chen on 2018/5/1. + */ +@Api("作品") +@Controller +@RequestMapping(value = "/photo") +public class PhotoController extends BaseController { + + @Autowired + private ContentService contentService; + + + @ApiOperation("作品主页") + @GetMapping(value = {"", "/index"}) + public String index(HttpServletRequest request, @RequestParam(value = "limit", defaultValue = "12") int limit) { + return this.index(1, limit, request); + } + + @ApiOperation("作品主页-分页") + @GetMapping(value = "page/{p}") + public String index( + @ApiParam(name = "page", value = "页数", required = false) + @PathVariable(name = "p") + int page, + @ApiParam(name = "limit", value = "条数", required = false) + @RequestParam(name = "limit", required = false, defaultValue = "9999") + int limit, + HttpServletRequest request + ){ + page = page < 0 || page > WebConst.MAX_PAGE ? 1 : page; + ContentCond contentCond = new ContentCond(); + contentCond.setType(Types.PHOTO.getType()); + PageInfo articles = contentService.getArticlesByCond(contentCond, page, limit); + request.setAttribute("archives", articles); + if (page > 1) { + this.title(request, "第" + page + "页"); + } + return "photo/index"; + } + + + @ApiOperation("作品内容") + @GetMapping(value = "/article/{cid}") + public String article( + @PathVariable("cid") + Integer cid, + HttpServletRequest request + ){ + ContentDomain article = contentService.getAtricleById(cid); + request.setAttribute("archive", article); + return "photo/article"; + } +} diff --git a/src/main/java/cn/luischen/controller/admin/ArticleController.java b/src/main/java/cn/luischen/controller/admin/ArticleController.java new file mode 100644 index 00000000..30d7fb6d --- /dev/null +++ b/src/main/java/cn/luischen/controller/admin/ArticleController.java @@ -0,0 +1,213 @@ +package cn.luischen.controller.admin; + +import cn.luischen.constant.LogActions; +import cn.luischen.constant.Types; +import cn.luischen.controller.BaseController; +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.dto.cond.MetaCond; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.ContentDomain; +import cn.luischen.model.MetaDomain; +import cn.luischen.service.content.ContentService; +import cn.luischen.service.log.LogService; +import cn.luischen.service.meta.MetaService; +import cn.luischen.utils.APIResponse; +import com.github.pagehelper.PageInfo; +import com.sun.org.apache.xpath.internal.operations.Bool; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * 文章管理 + * Created by Donghua.Chen on 2018/4/30. + */ +@Api("文章管理") +@Controller +@RequestMapping("/admin/article") +@Transactional(rollbackFor = BusinessException.class) +public class ArticleController extends BaseController { + + private static final Logger LOGGER = LoggerFactory.getLogger(ArticleController.class); + + @Autowired + private ContentService contentService; + + @Autowired + private MetaService metaService; + + @Autowired + private LogService logService; + + + @ApiOperation("文章页") + @GetMapping(value = "") + public String index( + HttpServletRequest request, + @ApiParam(name = "page", value = "页数", required = false) + @RequestParam(name = "page", required = false, defaultValue = "1") + int page, + @ApiParam(name = "limit", value = "每页数量", required = false) + @RequestParam(name = "limit", required = false, defaultValue = "15") + int limit + ){ + PageInfo articles = contentService.getArticlesByCond(new ContentCond(), page, limit); + request.setAttribute("articles", articles); + return "admin/article_list"; + } + + + @ApiOperation("发布文章页") + @GetMapping(value = "/publish") + public String newArticle(HttpServletRequest request){ + MetaCond metaCond = new MetaCond(); + metaCond.setType(Types.CATEGORY.getType()); + List metas = metaService.getMetas(metaCond); + request.setAttribute("categories", metas); + return "admin/article_edit"; + } + + @ApiOperation("发布新文章") + @PostMapping(value = "/publish") + @ResponseBody + public APIResponse publishArticle( + HttpServletRequest request, + @ApiParam(name = "title", value = "标题", required = true) + @RequestParam(name = "title", required = true) + String title, + @ApiParam(name = "titlePic", value = "标题图片", required = false) + @RequestParam(name = "titlePic", required = false) + String titlePic, + @ApiParam(name = "slug", value = "内容缩略名", required = false) + @RequestParam(name = "slug", required = false) + String slug, + @ApiParam(name = "content", value = "内容", required = true) + @RequestParam(name = "content", required = true) + String content, + @ApiParam(name = "type", value = "文章类型", required = true) + @RequestParam(name = "type", required = true) + String type, + @ApiParam(name = "status", value = "文章状态", required = true) + @RequestParam(name = "status", required = true) + String status, + @ApiParam(name = "tags", value = "标签", required = false) + @RequestParam(name = "tags", required = false) + String tags, + @ApiParam(name = "categories", value = "分类", required = false) + @RequestParam(name = "categories", required = false, defaultValue = "默认分类") + String categories, + @ApiParam(name = "allowComment", value = "是否允许评论", required = true) + @RequestParam(name = "allowComment", required = true) + Boolean allowComment + ){ + ContentDomain contentDomain = new ContentDomain(); + contentDomain.setTitle(title); + contentDomain.setTitlePic(titlePic); + contentDomain.setSlug(slug); + contentDomain.setContent(content); + contentDomain.setType(type); + contentDomain.setStatus(status); + contentDomain.setTags(tags); + contentDomain.setCategories(categories); + contentDomain.setAllowComment(allowComment ? 1 : 0); + + contentService.addArticle(contentDomain); + + return APIResponse.success(); + + + } + + @ApiOperation("文章编辑页") + @GetMapping(value = "/{cid}") + public String editArticle( + @ApiParam(name = "cid", value = "文章编号", required = true) + @PathVariable + Integer cid, + HttpServletRequest request + ){ + ContentDomain content = contentService.getAtricleById(cid); + request.setAttribute("contents", content); + MetaCond metaCond = new MetaCond(); + metaCond.setType(Types.CATEGORY.getType()); + List categories = metaService.getMetas(metaCond); + request.setAttribute("categories", categories); + request.setAttribute("active", "article"); + return "admin/article_edit"; + } + + @ApiOperation("编辑保存文章") + @PostMapping("/modify") + @ResponseBody + public APIResponse modifyArticle( + HttpServletRequest request, + @ApiParam(name = "cid", value = "文章主键", required = true) + @RequestParam(name = "cid", required = true) + Integer cid, + @ApiParam(name = "title", value = "标题", required = true) + @RequestParam(name = "title", required = true) + String title, + @ApiParam(name = "titlePic", value = "标题图片", required = false) + @RequestParam(name = "titlePic", required = false) + String titlePic, + @ApiParam(name = "slug", value = "内容缩略名", required = false) + @RequestParam(name = "slug", required = false) + String slug, + @ApiParam(name = "content", value = "内容", required = true) + @RequestParam(name = "content", required = true) + String content, + @ApiParam(name = "type", value = "文章类型", required = true) + @RequestParam(name = "type", required = true) + String type, + @ApiParam(name = "status", value = "文章状态", required = true) + @RequestParam(name = "status", required = true) + String status, + @ApiParam(name = "tags", value = "标签", required = false) + @RequestParam(name = "tags", required = false) + String tags, + @ApiParam(name = "categories", value = "分类", required = false) + @RequestParam(name = "categories", required = false, defaultValue = "默认分类") + String categories, + @ApiParam(name = "allowComment", value = "是否允许评论", required = true) + @RequestParam(name = "allowComment", required = true) + Boolean allowComment + ){ + ContentDomain contentDomain = new ContentDomain(); + contentDomain.setCid(cid); + contentDomain.setTitle(title); + contentDomain.setTitlePic(titlePic); + contentDomain.setSlug(slug); + contentDomain.setContent(content); + contentDomain.setType(type); + contentDomain.setStatus(status); + contentDomain.setTags(tags); + contentDomain.setCategories(categories); + contentDomain.setAllowComment(allowComment ? 1 : 0); + + contentService.updateArticleById(contentDomain); + return APIResponse.success(); + } + + @ApiOperation("删除文章") + @PostMapping(value = "/delete") + @ResponseBody + public APIResponse deleteArticle( + @ApiParam(name = "cid", value = "文章主键", required = true) + @RequestParam(name = "cid", required = true) + Integer cid, + HttpServletRequest request + ){ + contentService.deleteArticleById(cid); + logService.addLog(LogActions.DEL_ARTICLE.getAction(), cid + "", request.getRemoteAddr(), this.getUid(request)); + return APIResponse.success(); + } +} diff --git a/src/main/java/cn/luischen/controller/admin/AttAchController.java b/src/main/java/cn/luischen/controller/admin/AttAchController.java new file mode 100644 index 00000000..44b78430 --- /dev/null +++ b/src/main/java/cn/luischen/controller/admin/AttAchController.java @@ -0,0 +1,166 @@ +package cn.luischen.controller.admin; + +import cn.luischen.api.QCloudService; +import cn.luischen.api.QiniuCloudService; +import cn.luischen.constant.ErrorConstant; +import cn.luischen.constant.Types; +import cn.luischen.constant.WebConst; +import cn.luischen.dto.AttAchDto; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.AttAchDomain; +import cn.luischen.model.UserDomain; +import cn.luischen.service.attach.AttAchService; +import cn.luischen.utils.APIResponse; +import cn.luischen.utils.Commons; +import cn.luischen.utils.TaleUtils; +import com.github.pagehelper.PageInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +/** + * 附件控制器 + * Created by Donghua.Chen on 2018/4/30. + */ +@Api("附件相关接口") +@Controller +@RequestMapping("admin/attach") +public class AttAchController { + + private static final Logger LOGGER = LoggerFactory.getLogger(AttAchController.class); + + public static final String CLASSPATH = TaleUtils.getUplodFilePath(); + + @Autowired + private QCloudService qCloudService; + + @Autowired + private AttAchService attAchService; + + + + @ApiOperation("文件管理首页") + @GetMapping(value = "") + public String index( + @ApiParam(name = "page", value = "页数", required = false) + @RequestParam(name = "page", required = false, defaultValue = "1") + int page, + @ApiParam(name = "limit", value = "条数", required = false) + @RequestParam(name = "limit", required = false, defaultValue = "12") + int limit, + HttpServletRequest request + ){ + PageInfo atts = attAchService.getAtts(page, limit); + request.setAttribute("attachs", atts); + request.setAttribute(Types.ATTACH_URL.getType(), Commons.site_option(Types.ATTACH_URL.getType(), Commons.site_url())); + request.setAttribute("max_file_size", WebConst.MAX_FILE_SIZE / 1024); + return "admin/attach"; + } + + + + + @ApiOperation("markdown文件上传") + @PostMapping("/uploadfile") + public void fileUpLoadToTencentCloud(HttpServletRequest request, + HttpServletResponse response, + @ApiParam(name = "editormd-image-file", value = "文件数组", required = true) + @RequestParam(name = "editormd-image-file", required = true) + MultipartFile file){ + //文件上传 + try { + request.setCharacterEncoding( "utf-8" ); + response.setHeader( "Content-Type" , "text/html" ); + + String fileName = TaleUtils.getFileKey(file.getOriginalFilename()).replaceFirst("/",""); + + QiniuCloudService.upload(file, fileName); + AttAchDomain attAch = new AttAchDomain(); + HttpSession session = request.getSession(); + UserDomain sessionUser = (UserDomain) session.getAttribute(WebConst.LOGIN_SESSION_KEY); + attAch.setAuthorId(sessionUser.getUid()); + attAch.setFtype(TaleUtils.isImage(file.getInputStream()) ? Types.IMAGE.getType() : Types.FILE.getType()); + attAch.setFname(fileName); + attAch.setFkey(QiniuCloudService.QINIU_UPLOAD_SITE + fileName); + attAchService.addAttAch(attAch); + response.getWriter().write( "{\"success\": 1, \"message\":\"上传成功\",\"url\":\"" + attAch.getFkey() + "\"}" ); + } catch (IOException e) { + e.printStackTrace(); + try { + response.getWriter().write( "{\"success\":0}" ); + } catch (IOException e1) { + throw BusinessException.withErrorCode(ErrorConstant.Att.UPLOAD_FILE_FAIL) + .withErrorMessageArguments(e.getMessage()); + } + throw BusinessException.withErrorCode(ErrorConstant.Att.UPLOAD_FILE_FAIL) + .withErrorMessageArguments(e.getMessage()); + } + } + + @ApiOperation("多文件上传") + @PostMapping(value = "upload") + @ResponseBody + public APIResponse filesUploadToCloud(HttpServletRequest request, + HttpServletResponse response, + @ApiParam(name = "file", value = "文件数组", required = true) + @RequestParam(name = "file", required = true) + MultipartFile[] files){ + //文件上传 + try { + request.setCharacterEncoding( "utf-8" ); + response.setHeader( "Content-Type" , "text/html" ); + + for (MultipartFile file : files) { + + String fileName = TaleUtils.getFileKey(file.getOriginalFilename()).replaceFirst("/",""); + + QiniuCloudService.upload(file, fileName); + AttAchDomain attAch = new AttAchDomain(); + HttpSession session = request.getSession(); + UserDomain sessionUser = (UserDomain) session.getAttribute(WebConst.LOGIN_SESSION_KEY); + attAch.setAuthorId(sessionUser.getUid()); + attAch.setFtype(TaleUtils.isImage(file.getInputStream()) ? Types.IMAGE.getType() : Types.FILE.getType()); + attAch.setFname(fileName); + attAch.setFkey(QiniuCloudService.QINIU_UPLOAD_SITE + fileName); + attAchService.addAttAch(attAch); + } + return APIResponse.success(); + } catch (IOException e) { + e.printStackTrace(); + throw BusinessException.withErrorCode(ErrorConstant.Att.UPLOAD_FILE_FAIL) + .withErrorMessageArguments(e.getMessage()); + } + } + + @ApiOperation("删除文件记录") + @PostMapping(value = "/delete") + @ResponseBody + public APIResponse deleteFileInfo( + @ApiParam(name = "id", value = "文件主键", required = true) + @RequestParam(name = "id", required = true) + Integer id, + HttpServletRequest request + ){ + try { + AttAchDto attAch = attAchService.getAttAchById(id); + if (null == attAch) + throw BusinessException.withErrorCode(ErrorConstant.Att.DELETE_ATT_FAIL + ": 文件不存在"); + attAchService.deleteAttAch(id); + return APIResponse.success(); + } catch (Exception e) { + e.printStackTrace(); + throw BusinessException.withErrorCode(e.getMessage()); + } + } +} diff --git a/src/main/java/cn/luischen/controller/admin/AuthController.java b/src/main/java/cn/luischen/controller/admin/AuthController.java new file mode 100644 index 00000000..fcc8e725 --- /dev/null +++ b/src/main/java/cn/luischen/controller/admin/AuthController.java @@ -0,0 +1,118 @@ +package cn.luischen.controller.admin; + +import cn.luischen.constant.LogActions; +import cn.luischen.constant.WebConst; +import cn.luischen.controller.BaseController; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.UserDomain; +import cn.luischen.service.log.LogService; +import cn.luischen.service.user.UserService; +import cn.luischen.utils.APIResponse; +import cn.luischen.utils.TaleUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.apache.commons.lang3.StringUtils; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +/** + * Created by Donghua.Chen on 2018/4/30. + */ +@Api("登录相关接口") +@Controller +@RequestMapping(value = "/admin") +public class AuthController extends BaseController{ + + private static final Logger LOGGER = LogManager.getLogger(AuthController.class); + + @Autowired + private UserService userService; + + @Autowired + private LogService logService; + + @ApiOperation("跳转登录页") + @GetMapping(value = "/login") + public String login(){ + return "admin/login"; + } + + @ApiOperation("登录") + @PostMapping(value = "/login") + @ResponseBody + public APIResponse toLogin( + HttpServletRequest request, + HttpServletResponse response, + @ApiParam(name = "username", value = "用户名", required = true) + @RequestParam(name = "username", required = true) + String username, + @ApiParam(name = "password", value = "密码", required = true) + @RequestParam(name = "password", required = true) + String password, + @ApiParam(name = "remeber_me", value = "记住我", required = false) + @RequestParam(name = "remeber_me", required = false) + String remeber_me + ){ + Integer error_count = cache.get("login_error_count"); + try { + UserDomain userInfo = userService.login(username, password); + request.getSession().setAttribute(WebConst.LOGIN_SESSION_KEY, userInfo); + if (StringUtils.isNotBlank(remeber_me)) { + TaleUtils.setCookie(response, userInfo.getUid()); + } + logService.addLog(LogActions.LOGIN.getAction(), null, request.getRemoteAddr(), userInfo.getUid()); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + error_count = null == error_count ? 1 : error_count + 1; + if (error_count > 3) { + return APIResponse.fail("您输入密码已经错误超过3次,请10分钟后尝试"); + } + cache.set("login_error_count", error_count, 10 * 60); + String msg = "登录失败"; + if (e instanceof BusinessException) { + msg = e.getMessage(); + } else { + LOGGER.error(msg, e); + } + return APIResponse.fail(msg); + } + + return APIResponse.success(); + + } + + /** + * 注销 + * + * @param session + * @param response + */ + @RequestMapping("/logout") + public void logout(HttpSession session, HttpServletResponse response, org.apache.catalina.servlet4preview.http.HttpServletRequest request) { + session.removeAttribute(WebConst.LOGIN_SESSION_KEY); + Cookie cookie = new Cookie(WebConst.USER_IN_COOKIE, ""); + cookie.setValue(null); + cookie.setMaxAge(0);// 立即销毁cookie + cookie.setPath("/"); + response.addCookie(cookie); + try { + response.sendRedirect("/admin/login"); + } catch (IOException e) { + e.printStackTrace(); + LOGGER.error("注销失败", e); + } + } + + +} diff --git a/src/main/java/cn/luischen/controller/admin/CategoryController.java b/src/main/java/cn/luischen/controller/admin/CategoryController.java new file mode 100644 index 00000000..623905af --- /dev/null +++ b/src/main/java/cn/luischen/controller/admin/CategoryController.java @@ -0,0 +1,93 @@ +package cn.luischen.controller.admin; + +import cn.luischen.constant.Types; +import cn.luischen.constant.WebConst; +import cn.luischen.controller.BaseController; +import cn.luischen.dto.MetaDto; +import cn.luischen.dto.cond.MetaCond; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.MetaDomain; +import cn.luischen.service.meta.MetaService; +import cn.luischen.utils.APIResponse; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * Created by Donghua.Chen on 2018/5/1. + */ +@Api("分类和标签") +@Controller +@RequestMapping("admin/category") +public class CategoryController extends BaseController { + + private static final Logger LOGGER = LoggerFactory.getLogger(CategoryController.class); + + @Autowired + private MetaService metaService; + + @ApiOperation("进入分类和标签页") + @GetMapping(value = "") + public String index(HttpServletRequest request){ + List categories = metaService.getMetaList(Types.CATEGORY.getType(), null, WebConst.MAX_POSTS); + List tags = metaService.getMetaList(Types.TAG.getType(), null, WebConst.MAX_POSTS); + request.setAttribute("categories", categories); + request.setAttribute("tags", tags); + return "admin/category"; + } + + @ApiOperation("保存分类") + @PostMapping(value = "save") + @ResponseBody + public APIResponse addCategory( + @ApiParam(name = "cname", value = "分类名", required = true) + @RequestParam(name = "cname", required = true) + String cname, + @ApiParam(name = "mid", value = "meta编号", required = false) + @RequestParam(name = "mid", required = false) + Integer mid + ){ + try { + metaService.saveMeta(Types.CATEGORY.getType(),cname,mid); + + } catch (Exception e) { + e.printStackTrace(); + String msg = "分类保存失败"; + if (e instanceof BusinessException){ + BusinessException ex = (BusinessException) e; + msg = ex.getErrorCode(); + } + LOGGER.error(msg, e); + + return APIResponse.fail(msg); + } + return APIResponse.success(); + } + + @ApiOperation("删除分类") + @PostMapping(value = "delete") + @ResponseBody + public APIResponse delete( + @ApiParam(name = "mid", value = "主键", required = true) + @RequestParam(name = "mid", required = true) + Integer mid + ){ + try { + metaService.deleteMetaById(mid); + + } catch (Exception e) { + e.printStackTrace(); + LOGGER.error(e.getMessage()); + return APIResponse.fail(e.getMessage()); + } + return APIResponse.success(); + } +} diff --git a/src/main/java/cn/luischen/controller/admin/CommentController.java b/src/main/java/cn/luischen/controller/admin/CommentController.java new file mode 100644 index 00000000..774d888a --- /dev/null +++ b/src/main/java/cn/luischen/controller/admin/CommentController.java @@ -0,0 +1,108 @@ +package cn.luischen.controller.admin; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.controller.BaseController; +import cn.luischen.dto.cond.CommentCond; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.CommentDomain; +import cn.luischen.model.UserDomain; +import cn.luischen.service.comment.CommentService; +import cn.luischen.utils.APIResponse; +import com.github.pagehelper.PageInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +/** + * 评论 + * Created by Donghua.Chen on 2018/4/30. + */ +@Api("评论相关接口") +@Controller +@RequestMapping("/admin/comments") +public class CommentController extends BaseController{ + + private static final Logger LOGGER = LoggerFactory.getLogger(CommentController.class); + + + @Autowired + private CommentService commentService; + + @ApiOperation("进入评论列表页") + @GetMapping(value = "") + public String index( + @ApiParam(name = "page", value = "页数", required = false) + @RequestParam(name = "page", required = false, defaultValue = "1") + int page, + @ApiParam(name = "limit", value = "每页条数", required = false) + @RequestParam(name = "limit", required = false, defaultValue = "15") + int limit, + HttpServletRequest request + ){ + UserDomain user = this.user(request); + + PageInfo comments = commentService.getCommentsByCond(new CommentCond(), page, limit); + request.setAttribute("comments", comments); + return "admin/comment_list"; + } + + @ApiOperation("删除一条评论") + @PostMapping(value = "/delete") + @ResponseBody + public APIResponse deleteComment( + @ApiParam(name = "coid", value = "评论编号", required = true) + @RequestParam(name = "coid", required = true) + Integer coid + ){ + + try { + CommentDomain comment = commentService.getCommentById(coid); + if (null == comment) + throw BusinessException.withErrorCode(ErrorConstant.Comment.COMMENT_NOT_EXIST); + + commentService.deleteComment(coid); + } catch (Exception e) { + e.printStackTrace(); + LOGGER.error(e.getMessage()); + return APIResponse.fail(e.getMessage()); + } + return APIResponse.success(); + } + + @ApiOperation("更改评论状态") + @PostMapping(value = "/status") + @ResponseBody + public APIResponse changeStatus( + @ApiParam(name = "coid", value = "评论主键", required = true) + @RequestParam(name = "coid", required = true) + Integer coid, + @ApiParam(name = "status", value = "状态", required = true) + @RequestParam(name = "status", required = true) + String status + ){ + try { + CommentDomain comment = commentService.getCommentById(coid); + if (null == comment){ + commentService.updateCommentStatus(coid, status); + }else{ + return APIResponse.fail("删除失败"); + } + } catch (Exception e) { + e.printStackTrace(); + LOGGER.error(e.getMessage()); + return APIResponse.fail(e.getMessage()); + } + return APIResponse.success(); + } + + + + +} diff --git a/src/main/java/cn/luischen/controller/admin/IndexController.java b/src/main/java/cn/luischen/controller/admin/IndexController.java new file mode 100644 index 00000000..a4800442 --- /dev/null +++ b/src/main/java/cn/luischen/controller/admin/IndexController.java @@ -0,0 +1,149 @@ +package cn.luischen.controller.admin; + +import cn.luischen.constant.LogActions; +import cn.luischen.constant.WebConst; +import cn.luischen.controller.BaseController; +import cn.luischen.dto.StatisticsDto; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.CommentDomain; +import cn.luischen.model.ContentDomain; +import cn.luischen.model.LogDomain; +import cn.luischen.model.UserDomain; +import cn.luischen.service.log.LogService; +import cn.luischen.service.site.SiteService; +import cn.luischen.service.user.UserService; +import cn.luischen.utils.APIResponse; +import cn.luischen.utils.GsonUtils; +import cn.luischen.utils.TaleUtils; +import com.github.pagehelper.PageInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.util.List; + +/** + * Created by Donghua.Chen on 2018/4/30. + */ +@Api("后台首页") +@Controller("adminIndexController") +@RequestMapping(value = "/admin") +public class IndexController extends BaseController{ + + private static final Logger LOGGER = LoggerFactory.getLogger(IndexController.class); + + @Autowired + private SiteService siteService; + + @Autowired + private LogService logService; + + @Autowired + private UserService userService; + + + + @ApiOperation("进入首页") + @GetMapping(value = {"","/index"}) + public String index(HttpServletRequest request){ + LOGGER.info("Enter admin index method"); + List comments = siteService.getComments(5); + List contents = siteService.getNewArticles(5); + StatisticsDto statistics = siteService.getStatistics(); + // 取最新的20条日志 + PageInfo logs = logService.getLogs(1, 5); + List list = logs.getList(); + request.setAttribute("comments", comments); + request.setAttribute("articles", contents); + request.setAttribute("statistics", statistics); + request.setAttribute("logs", list); + LOGGER.info("Exit admin index method"); + return "admin/index"; + } + + /** + * 个人设置页面 + */ + @GetMapping(value = "/profile") + public String profile() { + return "admin/profile"; + } + + + /** + * 保存个人信息 + */ + @PostMapping(value = "/profile") + @ResponseBody + public APIResponse saveProfile(@RequestParam String screenName, @RequestParam String email, HttpServletRequest request, HttpSession session) { + UserDomain users = this.user(request); + if (StringUtils.isNotBlank(screenName) && StringUtils.isNotBlank(email)) { + UserDomain temp = new UserDomain(); + temp.setUid(users.getUid()); + temp.setScreenName(screenName); + temp.setEmail(email); + userService.updateUserInfo(temp); + logService.addLog(LogActions.UP_INFO.getAction(), GsonUtils.toJsonString(temp), request.getRemoteAddr(), this.getUid(request)); + + //更新session中的数据 + UserDomain original= (UserDomain) session.getAttribute(WebConst.LOGIN_SESSION_KEY); + original.setScreenName(screenName); + original.setEmail(email); + session.setAttribute(WebConst.LOGIN_SESSION_KEY,original); + } + return APIResponse.success(); + } + + /** + * 修改密码 + */ + @PostMapping(value = "/password") + @ResponseBody + public APIResponse upPwd(@RequestParam String oldPassword, @RequestParam String password, HttpServletRequest request,HttpSession session) { + UserDomain users = this.user(request); + if (StringUtils.isBlank(oldPassword) || StringUtils.isBlank(password)) { + return APIResponse.fail("请确认信息输入完整"); + } + + if (!users.getPassword().equals(TaleUtils.MD5encode(users.getUsername() + oldPassword))) { + return APIResponse.fail("旧密码错误"); + } + if (password.length() < 6 || password.length() > 14) { + return APIResponse.fail("请输入6-14位密码"); + } + + try { + UserDomain temp = new UserDomain(); + temp.setUid(users.getUid()); + String pwd = TaleUtils.MD5encode(users.getUsername() + password); + temp.setPassword(pwd); + userService.updateUserInfo(temp); + logService.addLog(LogActions.UP_PWD.getAction(), null, request.getRemoteAddr(), this.getUid(request)); + + //更新session中的数据 + UserDomain original= (UserDomain)session.getAttribute(WebConst.LOGIN_SESSION_KEY); + original.setPassword(pwd); + session.setAttribute(WebConst.LOGIN_SESSION_KEY,original); + return APIResponse.success(); + } catch (Exception e){ + String msg = "密码修改失败"; + if (e instanceof BusinessException) { + msg = e.getMessage(); + } else { + LOGGER.error(msg, e); + } + return APIResponse.fail(msg); + } + } + + + + +} diff --git a/src/main/java/cn/luischen/controller/admin/LinksController.java b/src/main/java/cn/luischen/controller/admin/LinksController.java new file mode 100644 index 00000000..8f776593 --- /dev/null +++ b/src/main/java/cn/luischen/controller/admin/LinksController.java @@ -0,0 +1,106 @@ +package cn.luischen.controller.admin; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.constant.Types; +import cn.luischen.controller.BaseController; +import cn.luischen.dto.cond.MetaCond; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.MetaDomain; +import cn.luischen.service.meta.MetaService; +import cn.luischen.utils.APIResponse; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * Created by Donghua.Chen on 2018/5/1. + */ +@Api("友链") +@Controller +@RequestMapping(value = "admin/links") +public class LinksController extends BaseController { + + + private static final Logger LOGGER = LoggerFactory.getLogger(LinksController.class); + + @Autowired + private MetaService metaService; + + @ApiOperation("友链页面") + @GetMapping(value = "") + public String index(HttpServletRequest request) { + + MetaCond metaCond = new MetaCond(); + metaCond.setType(Types.LINK.getType()); + List metas = metaService.getMetas(metaCond); + request.setAttribute("links", metas); + return "admin/links"; + } + + @ApiOperation("新增友链") + @PostMapping(value = "save") + @ResponseBody + public APIResponse addLink( + @ApiParam(name = "title", value = "标签", required = true) + @RequestParam(name = "title", required = true) + String title, + @ApiParam(name = "url", value = "链接", required = true) + @RequestParam(name = "url", required = true) + String url, + @ApiParam(name = "logo", value = "logo", required = false) + @RequestParam(name = "logo", required = false) + String logo, + @ApiParam(name = "mid", value = "meta编号", required = false) + @RequestParam(name = "mid", required = false) + Integer mid, + @ApiParam(name = "sort", value = "sort", required = false) + @RequestParam(name = "sort", required = false, defaultValue = "0") + int sort + ){ + try { + MetaDomain meta = new MetaDomain(); + meta.setName(title); + meta.setSlug(url); + meta.setDescription(logo); + meta.setSort(sort); + meta.setType(Types.LINK.getType()); + if (null != mid){ + meta.setMid(mid); + metaService.updateMeta(meta); + }else { + metaService.addMeta(meta); + } + } catch (Exception e) { + e.printStackTrace(); + throw BusinessException.withErrorCode(ErrorConstant.Meta.ADD_META_FAIL); + } + return APIResponse.success(); + } + + + @ApiOperation("删除友链") + @PostMapping(value = "delete") + @ResponseBody + public APIResponse delete( + @ApiParam(name = "mid", value = "meta主键", required = true) + @RequestParam(name = "mid", required = true) + int mid + ) { + try { + metaService.deleteMetaById(mid); + } catch (Exception e) { + e.printStackTrace(); + throw BusinessException.withErrorCode(ErrorConstant.Meta.ADD_META_FAIL); + } + return APIResponse.success(); + + } +} diff --git a/src/main/java/cn/luischen/controller/admin/SettingController.java b/src/main/java/cn/luischen/controller/admin/SettingController.java new file mode 100644 index 00000000..332078b4 --- /dev/null +++ b/src/main/java/cn/luischen/controller/admin/SettingController.java @@ -0,0 +1,80 @@ +package cn.luischen.controller.admin; + +import cn.luischen.constant.LogActions; +import cn.luischen.constant.WebConst; +import cn.luischen.controller.BaseController; +import cn.luischen.model.OptionsDomain; +import cn.luischen.service.log.LogService; +import cn.luischen.service.option.OptionService; +import cn.luischen.utils.APIResponse; +import cn.luischen.utils.GsonUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by Donghua.Chen on 2018/4/30. + */ +@Api("系统设置") +@Controller +@RequestMapping("/admin/setting") +public class SettingController extends BaseController { + + @Autowired + private OptionService optionService; + + @Autowired + private LogService logService; + + + @ApiOperation("进入设置页") + @GetMapping(value = "") + public String setting(HttpServletRequest request){ + List optionsList = optionService.getOptions(); + Map options = new HashMap<>(); + optionsList.forEach((option) -> { + options.put(option.getName(), option.getValue()); + }); + request.setAttribute("options", options); + return "admin/setting"; + } + + + @ApiOperation("保存系统设置") + @PostMapping(value = "") + @ResponseBody + public APIResponse saveSetting(HttpServletRequest request) { + try { + Map parameterMap = request.getParameterMap(); + Map querys = new HashMap<>(); + parameterMap.forEach((key, value) -> { + querys.put(key, join(value)); + }); + optionService.saveOptions(querys); + WebConst.initConfig = querys; + + logService.addLog(LogActions.SYS_SETTING.getAction(), GsonUtils.toJsonString(querys), request.getRemoteAddr(), this.getUid(request)); + return APIResponse.success(); + } catch (Exception e) { + String msg = "保存设置失败"; + return APIResponse.fail(e.getMessage()); + } + } + + + + + + + +} diff --git a/src/main/java/cn/luischen/controller/admin/UserController.java b/src/main/java/cn/luischen/controller/admin/UserController.java new file mode 100644 index 00000000..5222e022 --- /dev/null +++ b/src/main/java/cn/luischen/controller/admin/UserController.java @@ -0,0 +1,29 @@ +package cn.luischen.controller.admin; + +import cn.luischen.model.UserDomain; +import cn.luischen.service.user.UserService; +import cn.luischen.utils.APIResponse; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +/** + * Created by Donghua.Chen on 2018/4/20. + */ +@Api("用户管理类") +@Controller +@RequestMapping(value = "/user") +public class UserController { + + @Autowired + private UserService userService; + + @GetMapping("/docker") + @ResponseBody + public APIResponse dockerTest(){ + return APIResponse.success((Object) "hello docker"); + } + +} diff --git a/src/main/java/cn/luischen/dao/AttAchDao.java b/src/main/java/cn/luischen/dao/AttAchDao.java new file mode 100644 index 00000000..66636ac3 --- /dev/null +++ b/src/main/java/cn/luischen/dao/AttAchDao.java @@ -0,0 +1,63 @@ +package cn.luischen.dao; + +import cn.luischen.dto.AttAchDto; +import cn.luischen.model.AttAchDomain; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * Created by Donghua.Chen on 2018/4/29. + */ +@Mapper +public interface AttAchDao { + + + /** + * 添加单个附件信息 + * @param attAchDomain + * @return + */ + int addAttAch(AttAchDomain attAchDomain); + + /** + * 批量添加附件信息 + * @param list + * @return + */ + int batchAddAttAch(List list); + + /** + * 根据主键编号删除附件信息 + * @param id + * @return + */ + int deleteAttAch(int id); + + /** + * 更新附件信息 + * @param attAchDomain + * @return + */ + int updateAttAch(AttAchDomain attAchDomain); + + /** + * 根据主键获取附件信息 + * @param id + * @return + */ + AttAchDto getAttAchById(@Param("id") int id); + + /** + * 获取所有的附件信息 + * @return + */ + List getAtts(); + + /** + * 查找附件的数量 + * @return + */ + Long getAttsCount(); +} diff --git a/src/main/java/cn/luischen/dao/CommentDao.java b/src/main/java/cn/luischen/dao/CommentDao.java new file mode 100644 index 00000000..3d4e067f --- /dev/null +++ b/src/main/java/cn/luischen/dao/CommentDao.java @@ -0,0 +1,63 @@ +package cn.luischen.dao; + +import cn.luischen.dto.cond.CommentCond; +import cn.luischen.model.CommentDomain; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 评论实体类 + * Created by Donghua.Chen on 2018/4/29. + */ +@Mapper +public interface CommentDao { + + /** + * 新增评论 + * @param commentDomain + * @return + */ + int addComment(CommentDomain commentDomain); + + /** + * 删除评论 + * @param coid + * @return + */ + int deleteComment(@Param("coid") Integer coid); + + /** + * 更新评论的状态 + * @param coid + * @return + */ + int updateCommentStatus(@Param("coid") Integer coid, @Param("status") String status); + + /** + * 获取单条评论 + * @param coid + * @return + */ + CommentDomain getCommentById(@Param("coid") Integer coid); + /** + * 根据文章编号获取评论列表 + * @param cid + * @return + */ + List getCommentsByCId(@Param("cid") Integer cid); + + /** + * 根据条件获取评论列表 + * @param commentCond + * @return + */ + List getCommentsByCond(CommentCond commentCond); + + /** + * 获取文章数量 + * @return + */ + Long getCommentsCount(); +} diff --git a/src/main/java/cn/luischen/dao/ContentDao.java b/src/main/java/cn/luischen/dao/ContentDao.java new file mode 100644 index 00000000..f3cd301c --- /dev/null +++ b/src/main/java/cn/luischen/dao/ContentDao.java @@ -0,0 +1,87 @@ +package cn.luischen.dao; + +import cn.luischen.dto.ArchiveDto; +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.model.ContentDomain; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 文章持久层 + * Created by Donghua.Chen on 2018/4/29. + */ +@Mapper +public interface ContentDao { + + /** + * 添加文章 + * @param contentDomain + * @return + */ + int addArticle(ContentDomain contentDomain); + + /** + * 根据编号删除文章 + * @param cid + * @return + */ + int deleteArticleById(@Param("cid") Integer cid); + + /** + * 更新文章 + * @param contentDomain + * @return + */ + int updateArticleById(ContentDomain contentDomain); + + /** + * 更新文章的评论数 + * @param cid + * @param commentsNum + * @return + */ + int updateArticleCommentCountById(@Param("cid") Integer cid, @Param("commentsNum") Integer commentsNum); + + /** + * 根据编号获取文章 + * @param cid + * @return + */ + ContentDomain getArticleById(@Param("cid") Integer cid); + + /** + * 根据条件获取文章列表 + * @param contentCond + * @return + */ + List getArticlesByCond(ContentCond contentCond); + + /** + * 获取文章总数量 + * @return + */ + Long getArticleCount(); + + /** + * 获取归档数据 + * @param contentCond 查询条件(只包含开始时间和结束时间) + * @return + */ + List getArchive(ContentCond contentCond); + + /** + * 获取最近的文章(只包含id和title) + * @return + */ + List getRecentlyArticle(); + + /** + * 搜索文章-根据标题 或 内容匹配 + * @param param + * @return + */ + List searchArticle(@Param("param") String param); + +} diff --git a/src/main/java/cn/luischen/dao/LogDao.java b/src/main/java/cn/luischen/dao/LogDao.java new file mode 100644 index 00000000..924b2357 --- /dev/null +++ b/src/main/java/cn/luischen/dao/LogDao.java @@ -0,0 +1,34 @@ +package cn.luischen.dao; + +import cn.luischen.model.LogDomain; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * Created by Donghua.Chen on 2018/4/29. + */ +@Mapper +public interface LogDao { + + /** + * 添加日志 + * @param logDomain + * @return + */ + int addLog(LogDomain logDomain); + + /** + * 删除日志 + * @param id + * @return + */ + int deleteLogById(@Param("id") Integer id); + + /** + * 获取日志 + * @return + */ + List getLogs(); +} diff --git a/src/main/java/cn/luischen/dao/MetaDao.java b/src/main/java/cn/luischen/dao/MetaDao.java new file mode 100644 index 00000000..20e0e179 --- /dev/null +++ b/src/main/java/cn/luischen/dao/MetaDao.java @@ -0,0 +1,69 @@ +package cn.luischen.dao; + +import cn.luischen.dto.MetaDto; +import cn.luischen.dto.cond.MetaCond; +import cn.luischen.model.MetaDomain; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 项目dao + * Created by Donghua.Chen on 2018/4/29. + */ +@Mapper +public interface MetaDao { + + /** + * 添加项目 + * @param meta + * @return + */ + int addMeta(MetaDomain meta); + + /** + * 删除项目 + * @param mid + * @return + */ + int deleteMetaById(@Param("mid") Integer mid); + + /** + * 更新项目 + * @param meta + * @return + */ + int updateMeta(MetaDomain meta); + + /** + * 根据编号获取项目 + * @param mid + * @return + */ + MetaDomain getMetaById(@Param("mid") Integer mid); + + + /** + * 根据条件查询 + * @param metaCond + * @return + */ + List getMetasByCond(MetaCond metaCond); + + /** + * 根据类型获取meta数量 + * @param type + * @return + */ + Long getMetasCountByType(@Param("type") String type); + + /** + * 根据sql查询 + * @param paraMap + * @return + */ + List selectFromSql(Map paraMap); + +} diff --git a/src/main/java/cn/luischen/dao/OptionDao.java b/src/main/java/cn/luischen/dao/OptionDao.java new file mode 100644 index 00000000..7dea60cc --- /dev/null +++ b/src/main/java/cn/luischen/dao/OptionDao.java @@ -0,0 +1,42 @@ +package cn.luischen.dao; + +import cn.luischen.model.OptionsDomain; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 网站配置dao + * Created by Donghua.Chen on 2018/4/29. + */ +@Mapper +public interface OptionDao { + + /** + * 删除网站配置 + * @param name + * @return + */ + int deleteOptionByName(@Param("name") String name); + + /** + * 更新网站配置 + * @param options + * @return + */ + int updateOptionByName(OptionsDomain options); + + /*** + * 根据名称获取网站配置 + * @param name + * @return + */ + OptionsDomain getOptionByName(@Param("name") String name); + + /** + * 获取全部网站配置 + * @return + */ + List getOptions(); +} diff --git a/src/main/java/cn/luischen/dao/RelationShipDao.java b/src/main/java/cn/luischen/dao/RelationShipDao.java new file mode 100644 index 00000000..a703b09d --- /dev/null +++ b/src/main/java/cn/luischen/dao/RelationShipDao.java @@ -0,0 +1,73 @@ +package cn.luischen.dao; + +import cn.luischen.model.RelationShipDomain; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 中间表 + * Created by Donghua.Chen on 2018/4/30. + */ +@Mapper +public interface RelationShipDao { + + /** + * 添加 + * @param relationShip + * @return + */ + int addRelationShip(RelationShipDomain relationShip); + + /** + * 根据文章编号和meta编号删除关联 + * @param cid + * @param mid + * @return + */ + int deleteRelationShipById(@Param("cid") Integer cid, @Param("mid") Integer mid); + + /** + * 根据文章编号删除关联 + * @param cid + * @return + */ + int deleteRelationShipByCid(@Param("cid") Integer cid); + + /** + * 根据meta编号删除关联 + * @param mid + * @return + */ + int deleteRelationShipByMid(@Param("mid") Integer mid); + + /** + * 更新 + * @param relationShip + * @return + */ + int updateRelationShip(RelationShipDomain relationShip); + + /** + * 根据文章主键获取关联 + * @param cid + * @return + */ + List getRelationShipByCid(@Param("cid") Integer cid); + + /** + * 根据meta编号获取关联 + * @param mid + * @return + */ + List getRelationShipByMid(@Param("mid") Integer mid); + + /** + * 获取数量 + * @param cid + * @param mid + * @return + */ + Long getCountById(@Param("cid") Integer cid, @Param("mid") Integer mid); +} diff --git a/src/main/java/cn/luischen/dao/UserDao.java b/src/main/java/cn/luischen/dao/UserDao.java new file mode 100644 index 00000000..55654b47 --- /dev/null +++ b/src/main/java/cn/luischen/dao/UserDao.java @@ -0,0 +1,40 @@ +package cn.luischen.dao; + +import cn.luischen.dto.cond.UserCond; +import cn.luischen.model.UserDomain; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * Created by Donghua.Chen on 2018/4/20. + */ +@Mapper +public interface UserDao { + + /** + * @Author: Donghua.Chen + * @Description: 更改用户信息 + * @Date: 2018/4/20 + * @param user + */ + int updateUserInfo(UserDomain user); + + /** + * @Author: Donghua.Chen + * @Description: 根据主键编号获取用户信息 + * @Date: 2018/4/20 + * @param uId 主键 + */ + UserDomain getUserInfoById(@Param("uid") Integer uId); + + /** + * 根据用户名和密码获取用户信息 + * @param username + * @param password + * @return + */ + UserDomain getUserInfoByCond(@Param("username") String username, @Param("password") String password); + +} diff --git a/src/main/java/cn/luischen/dto/ArchiveDto.java b/src/main/java/cn/luischen/dto/ArchiveDto.java new file mode 100644 index 00000000..d59551ee --- /dev/null +++ b/src/main/java/cn/luischen/dto/ArchiveDto.java @@ -0,0 +1,40 @@ +package cn.luischen.dto; + +import cn.luischen.model.ContentDomain; + +import java.util.List; + +/** + * 文章归档类 + * Created by Donghua.Chen on 2018/4/30. + */ +public class ArchiveDto { + + private String date; + private String count; + private List articles; + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } + + public String getCount() { + return count; + } + + public void setCount(String count) { + this.count = count; + } + + public List getArticles() { + return articles; + } + + public void setArticles(List articles) { + this.articles = articles; + } +} diff --git a/src/main/java/cn/luischen/dto/AttAchDto.java b/src/main/java/cn/luischen/dto/AttAchDto.java new file mode 100644 index 00000000..f5365e5a --- /dev/null +++ b/src/main/java/cn/luischen/dto/AttAchDto.java @@ -0,0 +1,69 @@ +package cn.luischen.dto; + +/** + * Created by Donghua.Chen on 2018/4/29. + */ +public class AttAchDto extends BaseDto{ + + /** 主键编号 */ + private Integer id; + /** 文件名称 */ + private String fname; + /** 文件类型 */ + private String ftype; + /** 文件的地址 */ + private String fkey; + /** 创建人的id */ + private Integer authorId; + /** 创建的时间戳 */ + private Integer created; + + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFname() { + return fname; + } + + public void setFname(String fname) { + this.fname = fname; + } + + public String getFtype() { + return ftype; + } + + public void setFtype(String ftype) { + this.ftype = ftype; + } + + public String getFkey() { + return fkey; + } + + public void setFkey(String fkey) { + this.fkey = fkey; + } + + public Integer getAuthorId() { + return authorId; + } + + public void setAuthorId(Integer authorId) { + this.authorId = authorId; + } + + public Integer getCreated() { + return created; + } + + public void setCreated(Integer created) { + this.created = created; + } +} diff --git a/src/main/java/cn/luischen/dto/BaseDto.java b/src/main/java/cn/luischen/dto/BaseDto.java new file mode 100644 index 00000000..51cc28d0 --- /dev/null +++ b/src/main/java/cn/luischen/dto/BaseDto.java @@ -0,0 +1,20 @@ +package cn.luischen.dto; + +/** + * 公共属性的类 + * Created by Donghua.Chen on 2018/4/29. + */ +public class BaseDto { + + + /** 用户名 */ + private String userName; + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } +} diff --git a/src/main/java/cn/luischen/dto/MetaDto.java b/src/main/java/cn/luischen/dto/MetaDto.java new file mode 100644 index 00000000..f7dc8b69 --- /dev/null +++ b/src/main/java/cn/luischen/dto/MetaDto.java @@ -0,0 +1,20 @@ +package cn.luischen.dto; + +import cn.luischen.model.MetaDomain; + +/** + * 标签、分类列表 + * Created by Donghua.Chen on 2018/4/30. + */ +public class MetaDto extends MetaDomain { + + private int count; + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/src/main/java/cn/luischen/dto/StatisticsDto.java b/src/main/java/cn/luischen/dto/StatisticsDto.java new file mode 100644 index 00000000..6933de56 --- /dev/null +++ b/src/main/java/cn/luischen/dto/StatisticsDto.java @@ -0,0 +1,57 @@ +package cn.luischen.dto; + +/** + * 后台统计对象 + * Created by Donghua.Chen on 2018/4/30. + */ +public class StatisticsDto { + + /** + * 文章数 + */ + private Long articles; + /** + * 评论数 + */ + private Long comments; + /** + * 连接数 + */ + private Long links; + /** + * 附件数 + */ + private Long attachs; + + public Long getArticles() { + return articles; + } + + public void setArticles(Long articles) { + this.articles = articles; + } + + public Long getComments() { + return comments; + } + + public void setComments(Long comments) { + this.comments = comments; + } + + public Long getLinks() { + return links; + } + + public void setLinks(Long links) { + this.links = links; + } + + public Long getAttachs() { + return attachs; + } + + public void setAttachs(Long attachs) { + this.attachs = attachs; + } +} diff --git a/src/main/java/cn/luischen/dto/cond/CommentCond.java b/src/main/java/cn/luischen/dto/cond/CommentCond.java new file mode 100644 index 00000000..0af6e60b --- /dev/null +++ b/src/main/java/cn/luischen/dto/cond/CommentCond.java @@ -0,0 +1,57 @@ +package cn.luischen.dto.cond; + +/** + * 评论的查找参数 + * Created by Donghua.Chen on 2018/4/29. + */ +public class CommentCond { + /** + * 状态 + */ + private String status; + /** + * 开始时间戳 + */ + private Integer startTime; + /** + * 结束时间戳 + */ + private Integer endTime; + + /** + * 父评论编号 + */ + private Integer parent; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Integer getStartTime() { + return startTime; + } + + public void setStartTime(Integer startTime) { + this.startTime = startTime; + } + + public Integer getEndTime() { + return endTime; + } + + public void setEndTime(Integer endTime) { + this.endTime = endTime; + } + + public Integer getParent() { + return parent; + } + + public void setParent(Integer parent) { + this.parent = parent; + } +} diff --git a/src/main/java/cn/luischen/dto/cond/ContentCond.java b/src/main/java/cn/luischen/dto/cond/ContentCond.java new file mode 100644 index 00000000..92c31739 --- /dev/null +++ b/src/main/java/cn/luischen/dto/cond/ContentCond.java @@ -0,0 +1,107 @@ +package cn.luischen.dto.cond; + +/** + * 文章查询条件 + * Created by Donghua.Chen on 2018/4/29. + */ +public class ContentCond { + + /** + * 标签 + */ + private String tag; + /** + * 类别 + */ + private String category; + /** + * 状态 + */ + private String status; + /** + * 标题 + */ + private String title; + /** + * 内容匹配 + */ + private String content; + /** + * 文章类型 + */ + private String type; + + /** + * 开始时间戳 + */ + private Integer startTime; + + /** + * 结束时间戳 + */ + private Integer endTime; + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Integer getStartTime() { + return startTime; + } + + public void setStartTime(Integer startTime) { + this.startTime = startTime; + } + + public Integer getEndTime() { + return endTime; + } + + public void setEndTime(Integer endTime) { + this.endTime = endTime; + } +} diff --git a/src/main/java/cn/luischen/dto/cond/MetaCond.java b/src/main/java/cn/luischen/dto/cond/MetaCond.java new file mode 100644 index 00000000..170f1a5c --- /dev/null +++ b/src/main/java/cn/luischen/dto/cond/MetaCond.java @@ -0,0 +1,34 @@ +package cn.luischen.dto.cond; + +/** + * meta查询条件 + * Created by Donghua.Chen on 2018/4/30. + */ +public class MetaCond { + + /** + * meta Name + */ + private String name; + /** + * 类型 + */ + private String type; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/src/main/java/cn/luischen/dto/cond/UserCond.java b/src/main/java/cn/luischen/dto/cond/UserCond.java new file mode 100644 index 00000000..a11e5c4f --- /dev/null +++ b/src/main/java/cn/luischen/dto/cond/UserCond.java @@ -0,0 +1,28 @@ +package cn.luischen.dto.cond; + +import cn.luischen.utils.TaleUtils; + +/** + * 用户查找条件 + * Created by Donghua.Chen on 2018/4/30. + */ +public class UserCond { + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/src/main/java/cn/luischen/exception/BusinessException.java b/src/main/java/cn/luischen/exception/BusinessException.java new file mode 100644 index 00000000..f4a4be24 --- /dev/null +++ b/src/main/java/cn/luischen/exception/BusinessException.java @@ -0,0 +1,92 @@ +package cn.luischen.exception; + +import cn.luischen.utils.APIResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.MessageFormat; + +/** + * 统一异常类 + * Created by Donghua.Chen on 2018/4/20. + */ +public class BusinessException extends RuntimeException { + + private static final Logger logger = LoggerFactory.getLogger(BusinessException.class); + protected String errorCode; + protected String[] errorMessageArguments; + protected APIResponse apiResponse; + + protected BusinessException() { + this(""); + } + + public BusinessException(String message) { + super(message); + this.errorCode = "fail"; + this.errorMessageArguments = new String[0]; + } + + public BusinessException(String message, Throwable cause) { + super(message, cause); + this.errorCode = "fail"; + this.errorMessageArguments = new String[0]; + } + + public String getErrorCode() { + return this.errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String[] getErrorMessageArguments() { + return this.errorMessageArguments; + } + + public void setErrorMessageArguments(String[] errorMessageArguments) { + this.errorMessageArguments = errorMessageArguments; + } + + public static BusinessException withErrorCode(String errorCode) { + BusinessException businessException = new BusinessException(); + businessException.errorCode = errorCode; + return businessException; + } + + public static BusinessException fromAPIResponse(APIResponse apiResponse) { + BusinessException businessException = new BusinessException(); + if(apiResponse == null) { + apiResponse = APIResponse.fail("NULL"); + } + + businessException.apiResponse = apiResponse; + return businessException; + } + + public BusinessException withErrorMessageArguments(String... errorMessageArguments) { + if(errorMessageArguments != null) { + this.errorMessageArguments = errorMessageArguments; + } + + return this; + } + public APIResponse response() { + if(this.apiResponse != null) { + return this.apiResponse; + } else { + this.apiResponse = APIResponse.widthCode(this.getErrorCode()); + if(this.getErrorMessageArguments() != null && this.getErrorMessageArguments().length > 0) { + try { + this.apiResponse.setMsg(MessageFormat.format(this.apiResponse.getMsg(), this.getErrorMessageArguments())); + } catch (Exception var2) { + logger.error(var2.getMessage()); + } + } + + return this.apiResponse; + } + } + +} diff --git a/src/main/java/cn/luischen/handler/GlobalExceptionHandler.java b/src/main/java/cn/luischen/handler/GlobalExceptionHandler.java new file mode 100644 index 00000000..f056bc44 --- /dev/null +++ b/src/main/java/cn/luischen/handler/GlobalExceptionHandler.java @@ -0,0 +1,40 @@ +package cn.luischen.handler; + +import cn.luischen.exception.BusinessException; +import cn.luischen.utils.APIResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * 统一异常处理 + * Created by Donghua.Chen on 2018/4/20. + */ +@ControllerAdvice +public class GlobalExceptionHandler { + + private static final Logger logger = LoggerFactory.getLogger(BusinessException.class); + + @ExceptionHandler(value = BusinessException.class) + @ResponseBody + public APIResponse businessException(Exception e){ + + String msg = "请求错误"; + if (e instanceof BusinessException){ + msg = ((BusinessException) e).getErrorCode(); + } + logger.error("find exception:e={}",e.getMessage()); + e.printStackTrace(); + return APIResponse.fail(msg); + } +// +// @ExceptionHandler(value = Exception.class) +// public String exception(Exception e){ +// logger.error("find exception:e={}",e.getMessage()); +// e.printStackTrace(); +// return "error/400"; +// } + +} diff --git a/src/main/java/cn/luischen/interceptor/BaseInterceptor.java b/src/main/java/cn/luischen/interceptor/BaseInterceptor.java new file mode 100644 index 00000000..efafaae9 --- /dev/null +++ b/src/main/java/cn/luischen/interceptor/BaseInterceptor.java @@ -0,0 +1,105 @@ +package cn.luischen.interceptor; + +import cn.luischen.constant.Types; +import cn.luischen.constant.WebConst; +import cn.luischen.model.OptionsDomain; +import cn.luischen.model.UserDomain; +import cn.luischen.service.option.OptionService; +import cn.luischen.service.user.UserService; +import cn.luischen.utils.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 自定义拦截器 + */ +@Component +public class BaseInterceptor implements HandlerInterceptor { + private static final Logger LOGGE = LoggerFactory.getLogger(BaseInterceptor.class); + private static final String USER_AGENT = "user-agent"; + + @Autowired + private UserService userService; + + @Autowired + private OptionService optionService; + + + @Autowired + private Commons commons; + + @Autowired + private AdminCommons adminCommons; + + private MapCache cache = MapCache.single(); + + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { + String uri = request.getRequestURI(); + + LOGGE.info("UserAgent: {}", request.getHeader(USER_AGENT)); + LOGGE.info("用户访问地址: {}, 来路地址: {}", uri, IPKit.getIpAddrByRequest(request)); + + + //请求拦截处理 + UserDomain user = TaleUtils.getLoginUser(request); + if (null == user) { + Integer uid = TaleUtils.getCookieUid(request); + if (null != uid) { + //这里还是有安全隐患,cookie是可以伪造的 + user = userService.getUserInfoById(uid); + request.getSession().setAttribute(WebConst.LOGIN_SESSION_KEY, user); + } + } + if (uri.startsWith("/admin") && !uri.startsWith("/admin/login") && null == user + && !uri.startsWith("/admin/css") && !uri.startsWith("/admin/images") + && !uri.startsWith("/admin/js") && !uri.startsWith("/admin/plugins") + && !uri.startsWith("/admin/editormd")) { + response.sendRedirect(request.getContextPath() + "/admin/login"); + return false; + } + //设置get请求的token + if (request.getMethod().equals("GET")) { + String csrf_token = UUID.UU64(); + // 默认存储30分钟 + cache.hset(Types.CSRF_TOKEN.getType(), csrf_token, uri, 30 * 60); + request.setAttribute("_csrf_token", csrf_token); + } + return true; + } + + @Override + public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { + OptionsDomain ov = optionService.getOptionByName("site_record"); + httpServletRequest.setAttribute("commons", commons);//一些工具类和公共方法 + httpServletRequest.setAttribute("option", ov); + httpServletRequest.setAttribute("adminCommons", adminCommons); + initSiteConfig(httpServletRequest); + + } + + private void initSiteConfig(HttpServletRequest request) { + List options = optionService.getOptions(); + Map querys = new HashMap<>(); + options.forEach(option -> { + querys.put(option.getName(), option.getValue()); + }); + WebConst.initConfig = querys; + } + + @Override + public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { + + } +} diff --git a/src/main/java/cn/luischen/interceptor/WebMvcConfig.java b/src/main/java/cn/luischen/interceptor/WebMvcConfig.java new file mode 100644 index 00000000..54537214 --- /dev/null +++ b/src/main/java/cn/luischen/interceptor/WebMvcConfig.java @@ -0,0 +1,32 @@ +package cn.luischen.interceptor; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 向MVC中添加自定义组件 + * Created by Donghua.Chen on 2018/4/30. + */ +@Component +public class WebMvcConfig implements WebMvcConfigurer { + + @Autowired + private BaseInterceptor baseInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(baseInterceptor); + } + +// @Override +// public void addResourceHandlers(ResourceHandlerRegistry registry) { +// registry.addResourceHandler("/admin/**") +// .addResourceLocations("/public", "classpath:/admin/") +// .setCachePeriod(31556926); +// } + +} diff --git a/src/main/java/cn/luischen/model/AttAchDomain.java b/src/main/java/cn/luischen/model/AttAchDomain.java new file mode 100644 index 00000000..13361bb4 --- /dev/null +++ b/src/main/java/cn/luischen/model/AttAchDomain.java @@ -0,0 +1,70 @@ +package cn.luischen.model; + +/** + * 网站图片文件相关 + * Created by Donghua.Chen on 2018/4/29. + */ +public class AttAchDomain { + + /** 主键编号 */ + private Integer id; + /** 文件名称 */ + private String fname; + /** 文件类型 */ + private String ftype; + /** 文件的地址 */ + private String fkey; + /** 创建人的id */ + private Integer authorId; + /** 创建的时间戳 */ + private Integer created; + + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFname() { + return fname; + } + + public void setFname(String fname) { + this.fname = fname; + } + + public String getFtype() { + return ftype; + } + + public void setFtype(String ftype) { + this.ftype = ftype; + } + + public String getFkey() { + return fkey; + } + + public void setFkey(String fkey) { + this.fkey = fkey; + } + + public Integer getAuthorId() { + return authorId; + } + + public void setAuthorId(Integer authorId) { + this.authorId = authorId; + } + + public Integer getCreated() { + return created; + } + + public void setCreated(Integer created) { + this.created = created; + } +} diff --git a/src/main/java/cn/luischen/model/CommentDomain.java b/src/main/java/cn/luischen/model/CommentDomain.java new file mode 100644 index 00000000..04569fba --- /dev/null +++ b/src/main/java/cn/luischen/model/CommentDomain.java @@ -0,0 +1,191 @@ +package cn.luischen.model; + +/** + * Created by Donghua.Chen on 2018/4/29. + */ +public class CommentDomain { + + /** + * comment表主键 + */ + private Integer coid; + + /** + * contents表主键,关联字段 + */ + private Integer cid; + + /** + * 评论生成时的GMT unix时间戳 + */ + private Integer created; + + /** + * 评论作者 + */ + private String author; + + /** + * 评论所属用户id + */ + private Integer authorId; + + /** + * 评论所属内容作者id + */ + private Integer ownerId; + + /** + * 评论者邮件 + */ + private String mail; + + /** + * 评论者网址 + */ + private String url; + + /** + * 评论者ip地址 + */ + private String ip; + + /** + * 评论者客户端 + */ + private String agent; + + /** + * 评论类型 + */ + private String type; + + /** + * 评论状态 + */ + private String status; + + /** + * 父级评论 + */ + private Integer parent; + + /** + * 评论内容 + */ + private String content; + + private static final long serialVersionUID = 1L; + + public Integer getCoid() { + return coid; + } + + public void setCoid(Integer coid) { + this.coid = coid; + } + + public Integer getCid() { + return cid; + } + + public void setCid(Integer cid) { + this.cid = cid; + } + + public Integer getCreated() { + return created; + } + + public void setCreated(Integer created) { + this.created = created; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public Integer getAuthorId() { + return authorId; + } + + public void setAuthorId(Integer authorId) { + this.authorId = authorId; + } + + public Integer getOwnerId() { + return ownerId; + } + + public void setOwnerId(Integer ownerId) { + this.ownerId = ownerId; + } + + public String getMail() { + return mail; + } + + public void setMail(String mail) { + this.mail = mail; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getAgent() { + return agent; + } + + public void setAgent(String agent) { + this.agent = agent; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Integer getParent() { + return parent; + } + + public void setParent(Integer parent) { + this.parent = parent; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } +} diff --git a/src/main/java/cn/luischen/model/ContentDomain.java b/src/main/java/cn/luischen/model/ContentDomain.java new file mode 100644 index 00000000..5bdc44b6 --- /dev/null +++ b/src/main/java/cn/luischen/model/ContentDomain.java @@ -0,0 +1,216 @@ +package cn.luischen.model; + +/** + * 文章实体 + * Created by Donghua.Chen on 2018/4/29. + */ +public class ContentDomain { + + /** + * 文章的主键编号 + */ + private Integer cid; + /** + * 内容标题 + */ + private String title; + + /** + * 标题图片 + */ + private String titlePic; + /** + * 内容缩略名 + */ + private String slug; + /** + * 内容生成时的GMT unix时间戳 + */ + private Integer created; + /** + * 内容更改时的GMT unix时间戳 + */ + private Integer modified; + /** + * 内容文字 + */ + private String content; + /** + * 内容所属用户id + */ + private Integer authorId; + /** + * 内容类别 + */ + private String type; + /** + * 内容状态 + */ + private String status; + /** + * 标签列表 + */ + private String tags; + /** + * 分类列表 + */ + private String categories; + /** + * 点击次数 + */ + private Integer hits; + /** + * 内容所属评论数 + */ + private Integer commentsNum; + /** + * 是否允许评论 + */ + private Integer allowComment; + /** + * 是否允许ping + */ + private Integer allowPing; + /** + * 允许出现在聚合中 + */ + private Integer allowFeed; + + + + public Integer getCid() { + return cid; + } + + public void setCid(Integer cid) { + this.cid = cid; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getTitlePic() { + return titlePic; + } + + public void setTitlePic(String titlePic) { + this.titlePic = titlePic; + } + + public String getSlug() { + return slug; + } + + public void setSlug(String slug) { + this.slug = slug; + } + + public Integer getCreated() { + return created; + } + + public void setCreated(Integer created) { + this.created = created; + } + + public Integer getModified() { + return modified; + } + + public void setModified(Integer modified) { + this.modified = modified; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getAuthorId() { + return authorId; + } + + public void setAuthorId(Integer authorId) { + this.authorId = authorId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getTags() { + return tags; + } + + public void setTags(String tags) { + this.tags = tags; + } + + public String getCategories() { + return categories; + } + + public void setCategories(String categories) { + this.categories = categories; + } + + public Integer getHits() { + return hits; + } + + public void setHits(Integer hits) { + this.hits = hits; + } + + public Integer getCommentsNum() { + return commentsNum; + } + + public void setCommentsNum(Integer commentsNum) { + this.commentsNum = commentsNum; + } + + public Integer getAllowComment() { + return allowComment; + } + + public void setAllowComment(Integer allowComment) { + this.allowComment = allowComment; + } + + public Integer getAllowPing() { + return allowPing; + } + + public void setAllowPing(Integer allowPing) { + this.allowPing = allowPing; + } + + public Integer getAllowFeed() { + return allowFeed; + } + + public void setAllowFeed(Integer allowFeed) { + this.allowFeed = allowFeed; + } +} diff --git a/src/main/java/cn/luischen/model/LogDomain.java b/src/main/java/cn/luischen/model/LogDomain.java new file mode 100644 index 00000000..29dc89d9 --- /dev/null +++ b/src/main/java/cn/luischen/model/LogDomain.java @@ -0,0 +1,98 @@ +package cn.luischen.model; + +import java.io.Serializable; + +/** + * 日志类 + * Created by Donghua.Chen on 2018/4/29. + */ +public class LogDomain implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + private Integer id; + + /** + * 产生的动作 + */ + private String action; + + /** + * 产生的数据 + */ + private String data; + + /** + * 发生人id + */ + private Integer authorId; + + /** + * 日志产生的ip + */ + private String ip; + + /** + * 日志创建时间 + */ + private Integer created; + + + + + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public Integer getAuthorId() { + return authorId; + } + + public void setAuthorId(Integer authorId) { + this.authorId = authorId; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public Integer getCreated() { + return created; + } + + public void setCreated(Integer created) { + this.created = created; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } +} diff --git a/src/main/java/cn/luischen/model/MetaDomain.java b/src/main/java/cn/luischen/model/MetaDomain.java new file mode 100644 index 00000000..931fe084 --- /dev/null +++ b/src/main/java/cn/luischen/model/MetaDomain.java @@ -0,0 +1,113 @@ +package cn.luischen.model; + +import java.io.Serializable; + +/** + * 项目类 + * Created by Donghua.Chen on 2018/4/29. + */ +public class MetaDomain implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 项目主键 + */ + private Integer mid; + + /** + * 名称 + */ + private String name; + + /** + * 项目缩略名 + */ + private String slug; + + /** + * 项目类型 + */ + private String type; + + /** + * 对应的文章类型 + */ + private String contentType; + + /** + * 选项描述 + */ + private String description; + + /** + * 项目排序 + */ + private Integer sort; + + private Integer parent; + + public Integer getMid() { + return mid; + } + + public void setMid(Integer mid) { + this.mid = mid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSlug() { + return slug; + } + + public void setSlug(String slug) { + this.slug = slug; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getSort() { + return sort; + } + + public void setSort(Integer sort) { + this.sort = sort; + } + + public Integer getParent() { + return parent; + } + + public void setParent(Integer parent) { + this.parent = parent; + } +} diff --git a/src/main/java/cn/luischen/model/OptionsDomain.java b/src/main/java/cn/luischen/model/OptionsDomain.java new file mode 100644 index 00000000..2e1c5908 --- /dev/null +++ b/src/main/java/cn/luischen/model/OptionsDomain.java @@ -0,0 +1,39 @@ +package cn.luischen.model; + +/** + * 网站配置项 + * Created by Donghua.Chen on 2018/4/28. + */ +public class OptionsDomain { + + /** 名称 */ + private String name; + /** 内容 */ + private String value; + /** 备注 */ + private String description; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/src/main/java/cn/luischen/model/RelationShipDomain.java b/src/main/java/cn/luischen/model/RelationShipDomain.java new file mode 100644 index 00000000..d3ff2a42 --- /dev/null +++ b/src/main/java/cn/luischen/model/RelationShipDomain.java @@ -0,0 +1,33 @@ +package cn.luischen.model; + +/** + * 文章关联信息表 + * Created by Donghua.Chen on 2018/4/30. + */ +public class RelationShipDomain { + + /** + * 文章主键编号 + */ + private Integer cid; + /** + * 项目编号 + */ + private Integer mid; + + public Integer getCid() { + return cid; + } + + public void setCid(Integer cid) { + this.cid = cid; + } + + public Integer getMid() { + return mid; + } + + public void setMid(Integer mid) { + this.mid = mid; + } +} diff --git a/src/main/java/cn/luischen/model/UserDomain.java b/src/main/java/cn/luischen/model/UserDomain.java new file mode 100644 index 00000000..422933c7 --- /dev/null +++ b/src/main/java/cn/luischen/model/UserDomain.java @@ -0,0 +1,108 @@ +package cn.luischen.model; + +/** + * Created by Donghua.Chen on 2018/4/20. + */ +public class UserDomain { + + /** 主键编号 */ + private Integer uid; + /** 用户名 */ + private String username; + /** 密码 */ + private String password; + /** email */ + private String email; + /** 主页地址 */ + private String homeUrl; + /** 用户显示的名称 */ + private String screenName; + /** 用户注册时的GMT unix时间戳 */ + private Integer created; + /** 最后活动时间 */ + private Integer activated; + /** 上次登录最后活跃时间 */ + private Integer logged; + /** 用户组 */ + private String groupName; + + public Integer getUid() { + return uid; + } + + public void setUid(Integer uid) { + this.uid = uid; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getHomeUrl() { + return homeUrl; + } + + public void setHomeUrl(String homeUrl) { + this.homeUrl = homeUrl; + } + + public String getScreenName() { + return screenName; + } + + public void setScreenName(String screenName) { + this.screenName = screenName; + } + + public Integer getCreated() { + return created; + } + + public void setCreated(Integer created) { + this.created = created; + } + + public Integer getActivated() { + return activated; + } + + public void setActivated(Integer activated) { + this.activated = activated; + } + + public Integer getLogged() { + return logged; + } + + public void setLogged(Integer logged) { + this.logged = logged; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } +} diff --git a/src/main/java/cn/luischen/service/attach/AttAchService.java b/src/main/java/cn/luischen/service/attach/AttAchService.java new file mode 100644 index 00000000..21376228 --- /dev/null +++ b/src/main/java/cn/luischen/service/attach/AttAchService.java @@ -0,0 +1,55 @@ +package cn.luischen.service.attach; + +import cn.luischen.dto.AttAchDto; +import cn.luischen.model.AttAchDomain; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +/** + * 附件服务层 + * Created by Donghua.Chen on 2018/4/29. + */ +public interface AttAchService { + + /** + * 添加单个附件信息 + * @param attAchDomain + * @return + */ + void addAttAch(AttAchDomain attAchDomain); + + /** + * 批量添加附件信息 + * @param list + * @return + */ + void batchAddAttAch(List list); + + /** + * 根据主键编号删除附件信息 + * @param id + * @return + */ + void deleteAttAch(Integer id); + + /** + * 更新附件信息 + * @param attAchDomain + * @return + */ + void updateAttAch(AttAchDomain attAchDomain); + + /** + * 根据主键获取附件信息 + * @param id + * @return + */ + AttAchDto getAttAchById(Integer id); + + /** + * 获取所有的附件信息 + * @return + */ + PageInfo getAtts(int pageNum, int pageSize); +} diff --git a/src/main/java/cn/luischen/service/attach/impl/AttAchServiceImpl.java b/src/main/java/cn/luischen/service/attach/impl/AttAchServiceImpl.java new file mode 100644 index 00000000..0459ae8e --- /dev/null +++ b/src/main/java/cn/luischen/service/attach/impl/AttAchServiceImpl.java @@ -0,0 +1,74 @@ +package cn.luischen.service.attach.impl; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.dao.AttAchDao; +import cn.luischen.dto.AttAchDto; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.AttAchDomain; +import cn.luischen.service.attach.AttAchService; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 附件服务实现层 + * Created by Donghua.Chen on 2018/4/29. + */ +@Service +public class AttAchServiceImpl implements AttAchService { + + @Autowired + private AttAchDao attAchDao; + + @Override + public void addAttAch(AttAchDomain attAchDomain) { + if (null == attAchDomain) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + attAchDao.addAttAch(attAchDomain); + + } + + @Override + public void batchAddAttAch(List list) { + if (null == list || list.size() == 0) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + attAchDao.batchAddAttAch(list); + + } + + @Override + public void deleteAttAch(Integer id) { + if (null == id) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + attAchDao.deleteAttAch(id); + + } + + @Override + public void updateAttAch(AttAchDomain attAchDomain) { + if (null == attAchDomain || null == attAchDomain.getId()) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + attAchDao.updateAttAch(attAchDomain); + + } + + @Override + public AttAchDto getAttAchById(Integer id) { + if (null == id) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + return attAchDao.getAttAchById(id); + } + + @Override + public PageInfo getAtts(int pageNum, int pageSize) { + PageHelper.startPage(pageNum, pageSize); + List atts = attAchDao.getAtts(); + PageInfo pageInfo = new PageInfo<>(atts); + return pageInfo; + } + + +} diff --git a/src/main/java/cn/luischen/service/comment/CommentService.java b/src/main/java/cn/luischen/service/comment/CommentService.java new file mode 100644 index 00000000..c81c7f85 --- /dev/null +++ b/src/main/java/cn/luischen/service/comment/CommentService.java @@ -0,0 +1,61 @@ +package cn.luischen.service.comment; + +import cn.luischen.dto.cond.CommentCond; +import cn.luischen.model.CommentDomain; +import com.github.pagehelper.PageInfo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 评论服务层 + * Created by Donghua.Chen on 2018/4/29. + */ +public interface CommentService { + + /** + * 新增评论 + * @param commentDomain 评论的实体 + * @return + */ + void addComment(CommentDomain commentDomain); + + /** + * 删除评论 + * @param coid 评论的主键编号 + * @return + */ + void deleteComment(Integer coid); + + /** + * 更新评论的状态 + * @param coid 评论的主键编号 + * @param status 状态 + * @return + */ + void updateCommentStatus(Integer coid, String status); + + + /** + * 查找单条评论 + * @param coid + * @return + */ + CommentDomain getCommentById(Integer coid); + + /** + * 根据文章编号获取评论列表--只显示通过审核的评论-正常状态的 + * @param cid 文章主键编号 + * @return + */ + List getCommentsByCId(Integer cid); + + /** + * 根据条件获取评论列表 + * @param commentCond 查询条件 + * @param pageNum 分页参数 第几页 + * @param pageSize 分页参数 每页条数 + * @return + */ + PageInfo getCommentsByCond(CommentCond commentCond, int pageNum, int pageSize); +} diff --git a/src/main/java/cn/luischen/service/comment/impl/CommentServiceImpl.java b/src/main/java/cn/luischen/service/comment/impl/CommentServiceImpl.java new file mode 100644 index 00000000..ee0ca6e0 --- /dev/null +++ b/src/main/java/cn/luischen/service/comment/impl/CommentServiceImpl.java @@ -0,0 +1,108 @@ +package cn.luischen.service.comment.impl; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.dao.CommentDao; +import cn.luischen.dto.cond.CommentCond; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.CommentDomain; +import cn.luischen.service.comment.CommentService; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 评论实现类 + * Created by Donghua.Chen on 2018/4/29. + */ +@Service +public class CommentServiceImpl implements CommentService { + + @Autowired + private CommentDao commentDao; + + + + private static final Map STATUS_MAP = new ConcurrentHashMap<>(); + + /** + * 评论状态:正常 + */ + private static final String STATUS_NORMAL = "normal"; + /** + * 评论状态:不显示 + */ + private static final String STATUS_BLANK = "blank"; + + static { + STATUS_MAP.put("normal",STATUS_NORMAL); + STATUS_MAP.put("blank",STATUS_BLANK); + } + + @Override + public void addComment(CommentDomain commentDomain) { + if (StringUtils.isBlank(commentDomain.getAuthor())) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + + commentDao.addComment(commentDomain); + } + + @Transactional + @Override + public void deleteComment(Integer coid) { + if (null == coid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + // 如果删除的评论存在子评论,一并删除 + //查找当前评论是否有子评论 + CommentCond commentCond = new CommentCond(); + commentCond.setParent(coid); + List childComments = commentDao.getCommentsByCond(commentCond); + //删除子评论 + if (null != childComments && childComments.size() > 0){ + for (int i = 0; i < childComments.size(); i++) { + commentDao.deleteComment(childComments.get(i).getCoid()); + } + } + //删除当前评论 + commentDao.deleteComment(coid); + + } + + @Override + public void updateCommentStatus(Integer coid, String status) { + if (null == coid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + commentDao.updateCommentStatus(coid, status); + } + + @Override + public CommentDomain getCommentById(Integer coid) { + if (null == coid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + + return commentDao.getCommentById(coid); + } + + @Override + public List getCommentsByCId(Integer cid) { + if (null == cid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + return commentDao.getCommentsByCId(cid); + } + + @Override + public PageInfo getCommentsByCond(CommentCond commentCond, int pageNum, int pageSize) { + if (null == commentCond) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + PageHelper.startPage(pageNum, pageSize); + List comments = commentDao.getCommentsByCond(commentCond); + PageInfo pageInfo = new PageInfo<>(comments); + return pageInfo; + } +} diff --git a/src/main/java/cn/luischen/service/content/ContentService.java b/src/main/java/cn/luischen/service/content/ContentService.java new file mode 100644 index 00000000..069f1e0b --- /dev/null +++ b/src/main/java/cn/luischen/service/content/ContentService.java @@ -0,0 +1,77 @@ +package cn.luischen.service.content; + +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.model.ContentDomain; +import com.github.pagehelper.PageInfo; + +/** + * 文章服务层 + * Created by Donghua.Chen on 2018/4/29. + */ +public interface ContentService { + + /** + * 添加文章 + * @param contentDomain + * @return + */ + void addArticle(ContentDomain contentDomain); + + /** + * 根据编号删除文章 + * @param cid + * @return + */ + void deleteArticleById(Integer cid); + + /** + * 更新文章 + * @param contentDomain + * @return + */ + void updateArticleById(ContentDomain contentDomain); + + /** + * 更新分类 + * @param ordinal + * @param newCatefory + */ + void updateCategory(String ordinal, String newCatefory); + + /** + * 重新统计文章的评论数 + * @param cid + */ + void updateContentCommentCount(Integer cid); + + /** + * 根据编号获取文章 + * @param cid + * @return + */ + ContentDomain getAtricleById(Integer cid); + + /** + * 根据条件获取文章列表 + * @param contentCond + * @return + */ + PageInfo getArticlesByCond(ContentCond contentCond, int pageNum, int pageSize); + + /** + * 获取最近的文章(只包含id和title) + * @param pageNum + * @param pageSize + * @return + */ + PageInfo getRecentlyArticle(int pageNum, int pageSize); + + /** + * 搜索文章 + * @param param + * @param pageNun + * @param pageSize + * @return + */ + PageInfo searchArticle(String param, int pageNun, int pageSize); +} diff --git a/src/main/java/cn/luischen/service/content/impl/ContentServiceImpl.java b/src/main/java/cn/luischen/service/content/impl/ContentServiceImpl.java new file mode 100644 index 00000000..fc14dac4 --- /dev/null +++ b/src/main/java/cn/luischen/service/content/impl/ContentServiceImpl.java @@ -0,0 +1,167 @@ +package cn.luischen.service.content.impl; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.constant.Types; +import cn.luischen.constant.WebConst; +import cn.luischen.dao.CommentDao; +import cn.luischen.dao.ContentDao; +import cn.luischen.dao.RelationShipDao; +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.dto.cond.MetaCond; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.CommentDomain; +import cn.luischen.model.ContentDomain; +import cn.luischen.model.RelationShipDomain; +import cn.luischen.service.content.ContentService; +import cn.luischen.service.meta.MetaService; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * Created by Donghua.Chen on 2018/4/29. + */ +@Service +public class ContentServiceImpl implements ContentService { + + @Autowired + private ContentDao contentDao; + + @Autowired + private CommentDao commentDao; + + @Autowired + private MetaService metaService; + + @Autowired + private RelationShipDao relationShipDao; + + + @Transactional + @Override + public void addArticle(ContentDomain contentDomain) { + if (null == contentDomain) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + if (StringUtils.isBlank(contentDomain.getTitle())) + throw BusinessException.withErrorCode(ErrorConstant.Article.TITLE_CAN_NOT_EMPTY); + if (contentDomain.getTitle().length() > WebConst.MAX_TITLE_COUNT) + throw BusinessException.withErrorCode(ErrorConstant.Article.TITLE_IS_TOO_LONG); + if (StringUtils.isBlank(contentDomain.getContent())) + throw BusinessException.withErrorCode(ErrorConstant.Article.CONTENT_CAN_NOT_EMPTY); + if (contentDomain.getContent().length() > WebConst.MAX_TEXT_COUNT) + throw BusinessException.withErrorCode(ErrorConstant.Article.CONTENT_IS_TOO_LONG); + + //标签和分类 + String tags = contentDomain.getTags(); + String categories = contentDomain.getCategories(); + + contentDao.addArticle(contentDomain); + + int cid = contentDomain.getCid(); + metaService.addMetas(cid, tags, Types.TAG.getType()); + metaService.addMetas(cid, categories, Types.CATEGORY.getType()); + } + + @Override + @Transactional + public void deleteArticleById(Integer cid) { + if (null == cid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + contentDao.deleteArticleById(cid); + //同时也要删除该文章下的所有评论 + List comments = commentDao.getCommentsByCId(cid); + if (null != comments && comments.size() > 0){ + comments.forEach(comment ->{ + commentDao.deleteComment(comment.getCoid()); + }); + } + //删除标签和分类关联 + List relationShips = relationShipDao.getRelationShipByCid(cid); + if (null != relationShips && relationShips.size() > 0){ + relationShipDao.deleteRelationShipByCid(cid); + } + + } + + @Override + @Transactional + public void updateArticleById(ContentDomain contentDomain) { + //标签和分类 + String tags = contentDomain.getTags(); + String categories = contentDomain.getCategories(); + + contentDao.updateArticleById(contentDomain); + int cid = contentDomain.getCid(); + relationShipDao.deleteRelationShipByCid(cid); + metaService.addMetas(cid, tags, Types.TAG.getType()); + metaService.addMetas(cid, categories, Types.CATEGORY.getType()); + + } + + @Override + @Transactional + public void updateCategory(String ordinal, String newCatefory) { + ContentCond cond = new ContentCond(); + cond.setCategory(ordinal); + List atricles = contentDao.getArticlesByCond(cond); + atricles.forEach(atricle -> { + atricle.setCategories(atricle.getCategories().replace(ordinal, newCatefory)); + contentDao.updateArticleById(atricle); + }); + } + + @Override + @Transactional + public void updateContentCommentCount(Integer cid) { + /** + * 重新统计评论数:只重新统计状态为显示的评论 + */ + if (null == cid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + List comments = commentDao.getCommentsByCId(cid); + if (null != comments && comments.size() > 0){ + contentDao.updateArticleCommentCountById(cid, comments.size()); + } + + } + + @Override + public ContentDomain getAtricleById(Integer cid) { + if (null == cid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + //更新评论数 + updateContentCommentCount(cid); + return contentDao.getArticleById(cid); + } + + @Override + public PageInfo getArticlesByCond(ContentCond contentCond, int pageNum, int pageSize) { + if (null == contentCond) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + PageHelper.startPage(pageNum, pageSize); + List contents = contentDao.getArticlesByCond(contentCond); + PageInfo pageInfo = new PageInfo<>(contents); + return pageInfo; + } + + @Override + public PageInfo getRecentlyArticle(int pageNum, int pageSize) { + PageHelper.startPage(pageNum, pageSize); + List recentlyArticle = contentDao.getRecentlyArticle(); + PageInfo pageInfo = new PageInfo<>(recentlyArticle); + return pageInfo; + } + + @Override + public PageInfo searchArticle(String param, int pageNun, int pageSize) { + PageHelper.startPage(pageNun,pageSize); + List contentDomains = contentDao.searchArticle(param); + PageInfo pageInfo = new PageInfo<>(contentDomains); + return pageInfo; + } +} diff --git a/src/main/java/cn/luischen/service/log/LogService.java b/src/main/java/cn/luischen/service/log/LogService.java new file mode 100644 index 00000000..d7a29ab6 --- /dev/null +++ b/src/main/java/cn/luischen/service/log/LogService.java @@ -0,0 +1,33 @@ +package cn.luischen.service.log; + +import cn.luischen.model.LogDomain; +import com.github.pagehelper.PageInfo; + +/** + * 用户请求日志 + * Created by Donghua.Chen on 2018/4/29. + */ +public interface LogService { + + /** + * 添加 + * @param action + * @param data + * @param ip + * @param authorId + */ + void addLog(String action, String data, String ip, Integer authorId); + + /** + * 删除日志 + * @param id + * @return + */ + void deleteLogById(Integer id); + + /** + * 获取日志 + * @return + */ + PageInfo getLogs(int pageNum, int pageSize); +} diff --git a/src/main/java/cn/luischen/service/log/impl/LogServiceImpl.java b/src/main/java/cn/luischen/service/log/impl/LogServiceImpl.java new file mode 100644 index 00000000..863bcbf9 --- /dev/null +++ b/src/main/java/cn/luischen/service/log/impl/LogServiceImpl.java @@ -0,0 +1,49 @@ +package cn.luischen.service.log.impl; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.dao.LogDao; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.LogDomain; +import cn.luischen.service.log.LogService; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 请求日志 + * Created by Donghua.Chen on 2018/4/29. + */ +@Service +public class LogServiceImpl implements LogService { + + @Autowired + private LogDao logDao; + + @Override + public void addLog(String action, String data, String ip, Integer authorId) { + LogDomain logDomain = new LogDomain(); + logDomain.setAuthorId(authorId); + logDomain.setIp(ip); + logDomain.setData(data); + logDomain.setAction(action); + logDao.addLog(logDomain); + } + + @Override + public void deleteLogById(Integer id) { + if (null == id) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + deleteLogById(id); + } + + @Override + public PageInfo getLogs(int pageNum, int pageSize) { + PageHelper.startPage(pageNum, pageSize); + List logs = logDao.getLogs(); + PageInfo pageInfo = new PageInfo<>(logs); + return pageInfo; + } +} diff --git a/src/main/java/cn/luischen/service/meta/MetaService.java b/src/main/java/cn/luischen/service/meta/MetaService.java new file mode 100644 index 00000000..4e1b855e --- /dev/null +++ b/src/main/java/cn/luischen/service/meta/MetaService.java @@ -0,0 +1,85 @@ +package cn.luischen.service.meta; + +import cn.luischen.dto.MetaDto; +import cn.luischen.dto.cond.MetaCond; +import cn.luischen.model.MetaDomain; + +import java.util.List; + +/** + * 项目服务层 + * Created by Donghua.Chen on 2018/4/29. + */ +public interface MetaService { + /** + * 添加项目 + * @param meta + * @return + */ + void addMeta(MetaDomain meta); + + /** + * 添加 + * @param type + * @param name + * @param mid + */ + void saveMeta(String type, String name, Integer mid); + + + + /** + * 批量添加 + * @param cid + * @param names + * @param type + */ + void addMetas(Integer cid, String names, String type); + + + + /** + * 添加或者更新 + * @param cid + * @param name + * @param type + */ + void saveOrUpdate(Integer cid, String name, String type); + + /** + * 删除项目 + * @param mid + * @return + */ + void deleteMetaById(Integer mid); + + /** + * 更新项目 + * @param meta + * @return + */ + void updateMeta(MetaDomain meta); + + /** + * 根据编号获取项目 + * @param mid + * @return + */ + MetaDomain getMetaById(Integer mid); + + /** + * 获取所有的项目 + * @param metaCond 查询条件 + * @return + */ + List getMetas(MetaCond metaCond); + + /** + * 根据类型查询项目列表,带项目下面的文章数 + * @param type + * @param orderby + * @param limit + * @return + */ + List getMetaList(String type, String orderby, int limit); +} diff --git a/src/main/java/cn/luischen/service/meta/impl/MetaServiceImpl.java b/src/main/java/cn/luischen/service/meta/impl/MetaServiceImpl.java new file mode 100644 index 00000000..88418d53 --- /dev/null +++ b/src/main/java/cn/luischen/service/meta/impl/MetaServiceImpl.java @@ -0,0 +1,220 @@ +package cn.luischen.service.meta.impl; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.constant.Types; +import cn.luischen.constant.WebConst; +import cn.luischen.dao.ContentDao; +import cn.luischen.dao.MetaDao; +import cn.luischen.dao.RelationShipDao; +import cn.luischen.dto.MetaDto; +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.dto.cond.MetaCond; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.ContentDomain; +import cn.luischen.model.MetaDomain; +import cn.luischen.model.RelationShipDomain; +import cn.luischen.service.content.ContentService; +import cn.luischen.service.meta.MetaService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by Donghua.Chen on 2018/4/29. + */ +@Service +public class MetaServiceImpl implements MetaService { + + @Autowired + private MetaDao metaDao; + + @Autowired + private RelationShipDao relationShipDao; + + + @Autowired + private ContentService contentService; + + @Override + @Transactional + public void addMeta(MetaDomain meta) { + if (null == meta) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + metaDao.addMeta(meta); + + } + + @Override + public void saveMeta(String type, String name, Integer mid) { + if (StringUtils.isNotBlank(type) && StringUtils.isNotBlank(name)){ + MetaCond metaCond = new MetaCond(); + metaCond.setName(name); + metaCond.setType(type); + List metas = metaDao.getMetasByCond(metaCond); + if (null == metas || metas.size() == 0){ + MetaDomain metaDomain = new MetaDomain(); + metaDomain.setName(name); + if (null != mid){ + MetaDomain meta = metaDao.getMetaById(mid); + if (null != meta) + metaDomain.setMid(mid); + + metaDao.updateMeta(metaDomain); + //更新原有的文章分类 + contentService.updateCategory(meta.getName(), name); + } else { + metaDomain.setType(type); + metaDao.addMeta(metaDomain); + } + } else { + throw BusinessException.withErrorCode(ErrorConstant.Meta.META_IS_EXIST); + + } + + } + } + + @Override + @Transactional + public void addMetas(Integer cid, String names, String type) { + if (null == cid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + + if (StringUtils.isNotBlank(names) && StringUtils.isNotBlank(type)) { + String[] nameArr = StringUtils.split(names, ","); + for (String name : nameArr) { + this.saveOrUpdate(cid, name, type); + } + } + } + + @Override + public void saveOrUpdate(Integer cid, String name, String type) { + MetaCond metaCond = new MetaCond(); + metaCond.setName(name); + metaCond.setType(type); + List metas = this.getMetas(metaCond); + + int mid; + MetaDomain metaDomain; + if (metas.size() == 1){ + MetaDomain meta = metas.get(0); + mid = meta.getMid(); + }else if (metas.size() > 1){ + throw BusinessException.withErrorCode(ErrorConstant.Meta.NOT_ONE_RESULT); + } else { + metaDomain = new MetaDomain(); + metaDomain.setSlug(name); + metaDomain.setName(name); + metaDomain.setType(type); + this.addMeta(metaDomain); + mid = metaDomain.getMid(); + } + if (mid != 0){ + Long count = relationShipDao.getCountById(cid, mid); + if (count == 0){ + RelationShipDomain relationShip = new RelationShipDomain(); + relationShip.setCid(cid); + relationShip.setMid(mid); + relationShipDao.addRelationShip(relationShip); + } + + } + } + + @Override + @Transactional + public void deleteMetaById(Integer mid) { + if (null == mid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + + MetaDomain meta = metaDao.getMetaById(mid); + if (null != meta){ + String type = meta.getType(); + String name = meta.getName(); + metaDao.deleteMetaById(mid); + //需要把相关的数据删除 + List relationShips = relationShipDao.getRelationShipByMid(mid); + if (null != relationShips && relationShips.size() > 0){ + for (RelationShipDomain relationShip : relationShips) { + ContentDomain article = contentService.getAtricleById(relationShip.getCid()); + if (null != article){ + ContentDomain temp = new ContentDomain(); + temp.setCid(relationShip.getCid()); + if (type.equals(Types.CATEGORY.getType())) { + temp.setCategories(reMeta(name, article.getCategories())); + } + if (type.equals(Types.TAG.getType())) { + temp.setTags(reMeta(name, article.getTags())); + } + //将删除的资源去除 + contentService.updateArticleById(temp); + } + } + relationShipDao.deleteRelationShipByMid(mid); + } + } + + + + } + + @Override + @Transactional + public void updateMeta(MetaDomain meta) { + if (null == meta || null == meta.getMid()) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + metaDao.updateMeta(meta); + + } + + @Override + public MetaDomain getMetaById(Integer mid) { + if (null == mid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + return metaDao.getMetaById(mid); + } + + @Override + public List getMetas(MetaCond metaCond) { + return metaDao.getMetasByCond(metaCond); + } + + + @Override + public List getMetaList(String type, String orderby, int limit) { + if (StringUtils.isNotBlank(type)){ + if (StringUtils.isBlank(orderby)) { + orderby = "count desc, a.mid desc"; + } + if (limit < 1 || limit > WebConst.MAX_POSTS) { + limit = 10; + } + Map paraMap = new HashMap<>(); + paraMap.put("type", type); + paraMap.put("order", orderby); + paraMap.put("limit", limit); + return metaDao.selectFromSql(paraMap); + } + return null; + } + + private String reMeta(String name, String metas) { + String[] ms = StringUtils.split(metas, ","); + StringBuilder sbuf = new StringBuilder(); + for (String m : ms) { + if (!name.equals(m)) { + sbuf.append(",").append(m); + } + } + if (sbuf.length() > 0) { + return sbuf.substring(1); + } + return ""; + } +} diff --git a/src/main/java/cn/luischen/service/option/OptionService.java b/src/main/java/cn/luischen/service/option/OptionService.java new file mode 100644 index 00000000..a8ecd8ad --- /dev/null +++ b/src/main/java/cn/luischen/service/option/OptionService.java @@ -0,0 +1,45 @@ +package cn.luischen.service.option; + +import cn.luischen.model.OptionsDomain; + +import java.util.List; +import java.util.Map; + +/** + * Created by Donghua.Chen on 2018/4/28. + */ +public interface OptionService { + + /** + * 删除网站配置 + * @param name + * @return + */ + void deleteOptionByName(String name); + + /** + * 更新网站配置 + * @param name + * @param value + * @return + */ + void updateOptionByName(String name, String value); + + /** + * 保存网站配置 + * @param options + */ + void saveOptions(Map options); + /*** + * 根据名称获取网站配置 + * @param name + * @return + */ + OptionsDomain getOptionByName(String name); + + /** + * 获取全部网站配置 + * @return + */ + List getOptions(); +} diff --git a/src/main/java/cn/luischen/service/option/impl/OptionServiceImpl.java b/src/main/java/cn/luischen/service/option/impl/OptionServiceImpl.java new file mode 100644 index 00000000..a5bab7de --- /dev/null +++ b/src/main/java/cn/luischen/service/option/impl/OptionServiceImpl.java @@ -0,0 +1,65 @@ +package cn.luischen.service.option.impl; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.dao.OptionDao; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.OptionsDomain; +import cn.luischen.service.option.OptionService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 网站配置服务层 + * Created by Donghua.Chen on 2018/4/28. + */ +@Service +public class OptionServiceImpl implements OptionService { + + @Autowired + private OptionDao optionDao; + + @Override + public void deleteOptionByName(String name) { + if(StringUtils.isBlank(name)) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + optionDao.deleteOptionByName(name); + + } + + @Override + @Transactional + public void updateOptionByName(String name, String value) { + if(StringUtils.isBlank(name)) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + OptionsDomain option = new OptionsDomain(); + option.setName(name); + option.setValue(value); + optionDao.updateOptionByName(option); + + } + + @Override + @Transactional + public void saveOptions(Map options) { + if (null != options && !options.isEmpty()) { + options.forEach(this::updateOptionByName); + } + } + + @Override + public OptionsDomain getOptionByName(String name) { + if(StringUtils.isBlank(name)) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + return optionDao.getOptionByName(name); + } + + @Override + public List getOptions() { + return optionDao.getOptions(); + } +} diff --git a/src/main/java/cn/luischen/service/relation/RelationShipService.java b/src/main/java/cn/luischen/service/relation/RelationShipService.java new file mode 100644 index 00000000..1a3fb0c2 --- /dev/null +++ b/src/main/java/cn/luischen/service/relation/RelationShipService.java @@ -0,0 +1,10 @@ +package cn.luischen.service.relation; + +/** + * 关联关系 + * Created by Donghua.Chen on 2018/4/30. + */ +public interface RelationShipService { + + +} diff --git a/src/main/java/cn/luischen/service/relation/impl/RelationShipServiceImpl.java b/src/main/java/cn/luischen/service/relation/impl/RelationShipServiceImpl.java new file mode 100644 index 00000000..bbb2b60b --- /dev/null +++ b/src/main/java/cn/luischen/service/relation/impl/RelationShipServiceImpl.java @@ -0,0 +1,18 @@ +package cn.luischen.service.relation.impl; + +import cn.luischen.dao.RelationShipDao; +import cn.luischen.service.relation.RelationShipService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Created by Donghua.Chen on 2018/4/30. + */ +@Service +public class RelationShipServiceImpl implements RelationShipService { + + @Autowired + private RelationShipDao relationShipDao; + + +} diff --git a/src/main/java/cn/luischen/service/site/SiteService.java b/src/main/java/cn/luischen/service/site/SiteService.java new file mode 100644 index 00000000..90252198 --- /dev/null +++ b/src/main/java/cn/luischen/service/site/SiteService.java @@ -0,0 +1,70 @@ +package cn.luischen.service.site; + +import cn.luischen.dto.ArchiveDto; +import cn.luischen.dto.MetaDto; +import cn.luischen.dto.StatisticsDto; +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.model.CommentDomain; +import cn.luischen.model.ContentDomain; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +/** + * 站点服务 + * Created by Donghua.Chen on 2018/4/30. + */ +public interface SiteService { + + /** + * 获取评论列表 + * @param limit + * @return + */ + List getComments(int limit); + + /** + * 获取最新的文章 + * @param limit + * @return + */ + List getNewArticles(int limit); + + /** + * 获取单条评论 + * @param coid + * @return + */ + CommentDomain getComment(Integer coid); + + /** + * 获取 后台统计数据 + * @return + */ + StatisticsDto getStatistics(); + + /** + * 获取归档列表 - 只是获取日期和数量 + * @param contentCond + * @return + */ + List getArchivesSimple(ContentCond contentCond); + + /** + * 获取归档列表 + * @param contentCond 查询条件(只包含开始时间和结束时间) + * @return + */ + List getArchives(ContentCond contentCond); + + + + /** + * 获取分类/标签列表 + * @param type + * @param orderBy + * @param limit + * @return + */ + List getMetas(String type, String orderBy, int limit); +} diff --git a/src/main/java/cn/luischen/service/site/impl/SiteServiceImpl.java b/src/main/java/cn/luischen/service/site/impl/SiteServiceImpl.java new file mode 100644 index 00000000..a5a958d1 --- /dev/null +++ b/src/main/java/cn/luischen/service/site/impl/SiteServiceImpl.java @@ -0,0 +1,165 @@ +package cn.luischen.service.site.impl; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.constant.Types; +import cn.luischen.constant.WebConst; +import cn.luischen.dao.AttAchDao; +import cn.luischen.dao.CommentDao; +import cn.luischen.dao.ContentDao; +import cn.luischen.dao.MetaDao; +import cn.luischen.dto.ArchiveDto; +import cn.luischen.dto.MetaDto; +import cn.luischen.dto.StatisticsDto; +import cn.luischen.dto.cond.CommentCond; +import cn.luischen.dto.cond.ContentCond; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.CommentDomain; +import cn.luischen.model.ContentDomain; +import cn.luischen.service.site.SiteService; +import cn.luischen.utils.DateKit; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 站点服务 + * Created by Donghua.Chen on 2018/4/30. + */ +@Service +public class SiteServiceImpl implements SiteService{ + + private static final Logger LOGGER = LoggerFactory.getLogger(SiteServiceImpl.class); + + @Autowired + private CommentDao commentDao; + + @Autowired + private ContentDao contentDao; + + @Autowired + private MetaDao metaDao; + + @Autowired + private AttAchDao attAchDao; + + @Override + public List getComments(int limit) { + LOGGER.debug("Enter recentComments method:limit={}", limit); + if (limit < 0 || limit > 10){ + limit = 10; + } + PageHelper.startPage(1, limit); + List rs = commentDao.getCommentsByCond(new CommentCond()); + LOGGER.debug("Exit recentComments method"); + return rs; + } + + @Override + public List getNewArticles(int limit) { + LOGGER.debug("Enter recentArticles method:limit={}", limit); + if (limit < 0 || limit > 10) + limit = 10; + PageHelper.startPage(1, limit); + List rs = contentDao.getArticlesByCond(new ContentCond()); + LOGGER.debug("Exit recentArticles method"); + return rs; + } + + @Override + public CommentDomain getComment(Integer coid) { + LOGGER.debug("Enter recentComment method"); + if (null == coid) + throw BusinessException.withErrorCode(ErrorConstant.Common.PARAM_IS_EMPTY); + CommentDomain comment = commentDao.getCommentById(coid); + LOGGER.debug("Exit recentComment method"); + return comment; + } + + @Override + public StatisticsDto getStatistics() { + LOGGER.debug("Enter recentStatistics method"); + //文章总数 + Long artices = contentDao.getArticleCount(); + + Long comments = commentDao.getCommentsCount(); + + Long links = metaDao.getMetasCountByType(Types.LINK.getType()); + + Long atts = attAchDao.getAttsCount(); + + StatisticsDto rs = new StatisticsDto(); + rs.setArticles(artices); + rs.setAttachs(atts); + rs.setComments(comments); + rs.setLinks(links); + + LOGGER.debug("Exit recentStatistics method"); + return rs; + } + + @Override + public List getArchivesSimple(ContentCond contentCond) { + LOGGER.debug("Enter getArchives method"); + List archives = contentDao.getArchive(contentCond); + LOGGER.debug("Exit getArchives method"); + return archives; + } + + @Override + public List getArchives(ContentCond contentCond) { + LOGGER.debug("Enter getArchives method"); + List archives = contentDao.getArchive(contentCond); + parseArchives(archives, contentCond); + LOGGER.debug("Exit getArchives method"); + return archives; + } + + + + private void parseArchives(List archives, ContentCond contentCond) { + if (null != archives){ + archives.forEach(archive -> { + String date = archive.getDate(); + Date sd = DateKit.dateFormat(date, "yyyy年MM月"); + int start = DateKit.getUnixTimeByDate(sd); + int end = DateKit.getUnixTimeByDate(DateKit.dateAdd(DateKit.INTERVAL_MONTH, sd, 1)) - 1; + ContentCond cond = new ContentCond(); + cond.setStartTime(start); + cond.setEndTime(end); + cond.setType(contentCond.getType()); + List contentss = contentDao.getArticlesByCond(cond); + archive.setArticles(contentss); + }); + } + } + + @Override + public List getMetas(String type, String orderBy, int limit) { + LOGGER.debug("Enter metas method:type={},order={},limit={}", type, orderBy, limit); + List retList=null; + if (StringUtils.isNotBlank(type)) { + if(StringUtils.isBlank(orderBy)){ + orderBy = "count desc, a.mid desc"; + } + if(limit < 1 || limit > WebConst.MAX_POSTS){ + limit = 10; + } + Map paraMap = new HashMap<>(); + paraMap.put("type", type); + paraMap.put("order", orderBy); + paraMap.put("limit", limit); + retList= metaDao.selectFromSql(paraMap); + } + LOGGER.debug("Exit metas method"); + return retList; + } +} diff --git a/src/main/java/cn/luischen/service/user/UserService.java b/src/main/java/cn/luischen/service/user/UserService.java new file mode 100644 index 00000000..2aa33778 --- /dev/null +++ b/src/main/java/cn/luischen/service/user/UserService.java @@ -0,0 +1,37 @@ +package cn.luischen.service.user; + +import cn.luischen.model.UserDomain; + +import java.util.List; + +/** + * Created by Donghua.Chen on 2018/4/20. + */ +public interface UserService { + + /** + * @Author: Donghua.Chen + * @Description: 更改用户信息 + * @Date: 2018/4/20 + * @param user + */ + int updateUserInfo(UserDomain user); + + /** + * @Author: Donghua.Chen + * @Description: 根据主键编号获取用户信息 + * @Date: 2018/4/20 + * @param uId 主键 + */ + UserDomain getUserInfoById(Integer uId); + + + /** + * 用户登录 + * @param username 用户名 + * @param password 密码 + * @return + */ + UserDomain login(String username, String password); + +} diff --git a/src/main/java/cn/luischen/service/user/impl/UserServiceImpl.java b/src/main/java/cn/luischen/service/user/impl/UserServiceImpl.java new file mode 100644 index 00000000..50fbbf1b --- /dev/null +++ b/src/main/java/cn/luischen/service/user/impl/UserServiceImpl.java @@ -0,0 +1,52 @@ +package cn.luischen.service.user.impl; + +import cn.luischen.constant.ErrorConstant; +import cn.luischen.dao.UserDao; +import cn.luischen.dto.cond.UserCond; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.UserDomain; +import cn.luischen.service.user.UserService; +import cn.luischen.utils.TaleUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * Created by Donghua.Chen on 2018/4/20. + */ +@Service +public class UserServiceImpl implements UserService { + + @Autowired + private UserDao userDao;//这里会报错,但是并不会影响 + + + @Transactional + @Override + public int updateUserInfo(UserDomain user) { + if (null == user.getUid()) + throw BusinessException.withErrorCode("用户编号不可能为空"); + return userDao.updateUserInfo(user); + } + + @Override + public UserDomain getUserInfoById(Integer uId) { + return userDao.getUserInfoById(uId); + } + + @Override + public UserDomain login(String username, String password) { + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) + throw BusinessException.withErrorCode(ErrorConstant.Auth.USERNAME_PASSWORD_IS_EMPTY); + + String pwd = TaleUtils.MD5encode(username + password); + UserDomain user = userDao.getUserInfoByCond(username, pwd); + if (null == user) + throw BusinessException.withErrorCode(ErrorConstant.Auth.USERNAME_PASSWORD_ERROR); + + return user; + } + + +} diff --git a/src/main/java/cn/luischen/utils/APIResponse.java b/src/main/java/cn/luischen/utils/APIResponse.java new file mode 100644 index 00000000..1df38c33 --- /dev/null +++ b/src/main/java/cn/luischen/utils/APIResponse.java @@ -0,0 +1,74 @@ +package cn.luischen.utils; + +/** + * 返回的参数封装类 + * Created by Donghua.Chen on 2018/4/20. + */ +public class APIResponse { + + private static final String CODE_SUCCESS = "success"; + + private static final String CODE_FAIL = "fail"; + + private String code; + private T data; + private String msg; + + public APIResponse(){ + + } + + public APIResponse(String code){ + this.code = code; + } + + public APIResponse(String code, T data){ + this.code = code; + this.data = data; + } + + public APIResponse(String code, String msg){ + this.code = code; + this.msg = msg; + } + + public static APIResponse success(){ + return new APIResponse(CODE_SUCCESS); + } + + public static APIResponse success(Object data){ + return new APIResponse(CODE_SUCCESS, data); + } + + public static APIResponse fail(String msg){ + return new APIResponse(CODE_FAIL, msg); + } + + public static APIResponse widthCode(String errorCode) { + return new APIResponse(errorCode); + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/src/main/java/cn/luischen/utils/AdminCommons.java b/src/main/java/cn/luischen/utils/AdminCommons.java new file mode 100644 index 00000000..3bcb399f --- /dev/null +++ b/src/main/java/cn/luischen/utils/AdminCommons.java @@ -0,0 +1,41 @@ +package cn.luischen.utils; + + +import cn.luischen.model.MetaDomain; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +/** + * 后台公共函数 + *

+ * Created by Luis on 2018/4/30. + */ +@Component +public final class AdminCommons { + + /** + * 判断category和cat的交集 + * + * @param cats + * @return + */ + public static boolean exist_cat(MetaDomain category, String cats) { + String[] arr = StringUtils.split(cats, ","); + if (null != arr && arr.length > 0) { + for (String c : arr) { + if (c.trim().equals(category.getName())) { + return true; + } + } + } + return false; + } + + private static final String[] COLORS = {"default", "primary", "success", "info", "warning", "danger", "inverse", "purple", "pink"}; + + public static String rand_color() { + int r = Tools.rand(0, COLORS.length - 1); + return COLORS[r]; + } + +} diff --git a/src/main/java/cn/luischen/utils/Commons.java b/src/main/java/cn/luischen/utils/Commons.java new file mode 100644 index 00000000..1d3e02b1 --- /dev/null +++ b/src/main/java/cn/luischen/utils/Commons.java @@ -0,0 +1,480 @@ +package cn.luischen.utils; + +import cn.luischen.constant.WebConst; +import cn.luischen.model.ContentDomain; +import cn.luischen.model.OptionsDomain; +import cn.luischen.service.option.OptionService; +import com.github.pagehelper.PageInfo; +import com.vdurmont.emoji.EmojiParser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 公共函数 + * Created by Donghua.Chen on 2018/4/30. + */ +@Component +public class Commons { + + + /** + * 网站链接 + * + * @return + */ + public static String site_url() { + return site_url(""); + } + + /** + * 返回网站链接下的全址 + * + * @param sub 后面追加的地址 + * @return + */ + public static String site_url(String sub) { + return site_option("site_url") + sub; + } + + + /** + * 文件上传,为文件重新命名 + **/ + public static String getFileRename(String name) { + Date date = new Date(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + String sdfDate = sdf.format(date); + int pos = name.lastIndexOf("."); + String suffix = name.substring(pos); + String rename = sdfDate+suffix; + return rename; + } + + /** + * 获取网站的描述 + * @return + */ + public static String site_desc(){ + return site_option("site_description"); + } + + public static String site_keywords(){ + return site_option("site_keywords"); + } + + /** + * 获取网站的备案信息 + * @return + */ + public static String site_record() { + return site_option("site_record"); + } + + /** + * 获取网站标题 + * @return + */ + public static String site_title() { + return site_option("site_title"); + } + + /** + * 网站配置项 + * + * @param key + * @return + */ + public static String site_option(String key) { + return site_option(key, ""); + } + + /** + * 获取GitHub地址 + * @return + */ + public static String social_github(){ + return site_option("social_github"); + } + + /** + * 获取google网站验证码 + * @return + */ + public static String google_site_verification(){ + return site_option("google_site_verification"); + } + + /** + * 获取百度网站验证码 + * @return + */ + public static String baidu_site_verification(){ + return site_option("baidu_site_verification"); + } + /** + * 网站配置项 + * + * @param key + * @param defalutValue 默认值 + * @return + */ + public static String site_option(String key, String defalutValue) { + if (StringUtils.isBlank(key)) { + return ""; + } + String str = WebConst.initConfig.get(key); + if (StringUtils.isNotBlank(str)) { + return str; + } else { + return defalutValue; + } + } + + /** + * 格式化unix时间戳为日期 + * + * @param unixTime + * @return + */ + public static String fmtdate(Integer unixTime) { + return fmtdate(unixTime, "yyyy-MM-dd"); + } + + /** + * 格式化unix时间戳为日期 + * + * @param unixTime + * @param patten + * @return + */ + public static String fmtdate(Integer unixTime, String patten) { + if (null != unixTime && StringUtils.isNotBlank(patten)) { + return DateKit.formatDateByUnixTime(unixTime, patten); + } + return ""; + } + + /** + * 英文格式的日期 + * @param unixTime + * @return + */ + public static String fmtdate_en(Integer unixTime){ + return fmtdate(unixTime,"MMM d, yyyy"); + } + + + /** + * 英文格式的日期-月,日 + * @param unixTime + * @return + */ + public static String fmtdate_en_m(Integer unixTime){ + return fmtdate(unixTime,"MMM d"); + } + + /** + * 日期-年 + * @param unixTime + * @return + */ + public static String fmtdate_en_y(Integer unixTime){ + return fmtdate(unixTime,"yyyy"); + } + + /** + * 将中文的yyyy年MM月 - > yyyy + * @param date + * @return + */ + public static String parsedate_zh_y_m(String date){ + if (StringUtils.isNotBlank(date)){ + Date d = DateKit.dateFormat(date, "yyyy年MM月"); + return DateKit.dateFormat(d, "yyyy"); + } + return null; + } + + /** + * 字符串转Date + * @param date + * @return + */ + public static Date fmtdate_date(String date){ + if (StringUtils.isNotBlank(date)){ + return DateKit.dateFormat(date, "yyyy年MM月"); + } + return null; + } + + /** + * 根据nuix时间戳获取Date + * @param nuixTime + * @return + */ + public static Date fmtdate_unxtime(Integer nuixTime){ + if (null != nuixTime){ + return DateKit.getDateByUnixTime(nuixTime); + } + return null; + } + /** + * 获取社交的链接地址 + * + * @return + */ + public static Map social() { + final String prefix = "social_"; + Map map = new HashMap<>(); + map.put("csdn", WebConst.initConfig.get(prefix + "csdn")); + map.put("jianshu", WebConst.initConfig.get(prefix + "jianshu")); + map.put("resume", WebConst.initConfig.get(prefix + "resume")); + map.put("weibo", WebConst.initConfig.get(prefix + "weibo")); + map.put("zhihu", WebConst.initConfig.get(prefix + "zhihu")); + map.put("github", WebConst.initConfig.get(prefix + "github")); + map.put("twitter", WebConst.initConfig.get(prefix + "twitter")); + return map; + } + + + /** + * An :grinning:awesome :smiley:string 😄with a few :wink:emojis! + *

+ * 这种格式的字符转换为emoji表情 + * + * @param value + * @return + */ + public static String emoji(String value) { + return EmojiParser.parseToUnicode(value); + } + + /** + * 获取随机数 + * + * @param max + * @param str + * @return + */ + public static String random(int max, String str) { + return UUID.random(1, max) + str; + } + + /** + * 返回github头像地址 + * + * @param email + * @return + */ + public static String gravatar(String email) { + String avatarUrl = "https://github.com/identicons/"; + if (StringUtils.isBlank(email)) { + email = "user@hanshuai.xin"; + } + String hash = TaleUtils.MD5encode(email.trim().toLowerCase()); + return avatarUrl + hash + ".png"; + } + + /** + * 显示文章内容,转换markdown为html + * + * @param value + * @return + */ + public static String article(String value) { + if (StringUtils.isNotBlank(value)) { + value = value.replace("", "\r\n"); + value = value.replace("", "\r\n"); + return TaleUtils.mdToHtml(value); + } + return ""; + } + + /** + * 返回文章链接地址 + * + * @param contents + * @return + */ + public static String permalink(ContentDomain contents) { + return permalink(contents.getCid(), contents.getSlug()); + } + + + + /** + * 返回文章链接地址 + * + * @param cid + * @param slug + * @return + */ + public static String permalink(Integer cid, String slug) { + return site_url("/article/" + (StringUtils.isNotBlank(slug) ? slug : cid.toString())); + } + + /** + * 判断分页中是否有数据 + * + * @param paginator + * @return + */ + public static boolean is_empty(PageInfo paginator) { + return paginator == null || (paginator.getList() == null) || (paginator.getList().size() == 0); + } + + /** + * 截取字符串 + * + * @param str + * @param len + * @return + */ + public static String substr(String str, int len) { + if (str.length() > len) { + return str.substring(0, len); + } + return str; + } + + /** + * 返回作品文章地址 + * @param cid + * @return + */ + public static String photoPermalink(Integer cid) { + return site_url("/photo/article/" + cid.toString()); + } + + /** + * 返回blog文章地址 + * @param cid + * @return + */ + public static String blogPermalink(Integer cid) { + return site_url("/blog/article/" + cid.toString()); + } + + /** + * 获取blog归档地址 + * @param date + * @return + */ + public static String archivePermalink(String date){ + return site_url("/blog/archives/" + date); + } + + + public static String archiveYearPermalink(String date){ + return site_url("/blog/archives/year/" + date); + } + /** + * 返回blog分类的地址 + * @param categorie + * @return + */ + public static String categoriePermalink(String categorie){ + return site_url("/blog/categories/" + categorie); + } + + /** + * 返回blog标签页的地址 + * @param tag + * @return + */ + public static String tagPermalink(String tag){ + return site_url("/blog/tag/" + tag); + } + /** + * 获取文章第一张图片 + * + * @return + */ + public static String show_thumb(String content) { + content = TaleUtils.mdToHtml(content); + if (content.contains("|\\s+)").matcher(img); + if (m.find()) { + return m.group(1); + } + } + } + return ""; + } + + /** + * 显示分类 + * + * @param categories + * @return + */ + public static String show_categories(String categories) throws UnsupportedEncodingException { + if (StringUtils.isNotBlank(categories)) { + String[] arr = categories.split(","); + StringBuffer sbuf = new StringBuffer(); + for (String c : arr) { + sbuf.append("" + c + ""); + } + return sbuf.toString(); + } + return show_categories("默认分类"); + } + + /** + * 显示标签 + * + * @param tags + * @return + */ + public static String show_tags(String tags) throws UnsupportedEncodingException { + if (StringUtils.isNotBlank(tags)) { + String[] arr = tags.split(","); + StringBuffer sbuf = new StringBuffer(); + for (String c : arr) { + sbuf.append("

  • #" + c + "
  • "); + } + return sbuf.toString(); + } + return ""; + } + + /** + * 截取文章摘要 + * + * @param value 文章内容 + * @param len 要截取文字的个数 + * @return + */ + public static String intro(String value, int len) { + Integer pos = value.indexOf(""); + if (null == pos || pos == 0 || pos == -1) + pos = value.indexOf(""); + if (pos != -1) { + String html = value.substring(0, pos); + return TaleUtils.mdToHtml(TaleUtils.mdToHtml(html)); + } else { + String text = TaleUtils.mdToHtml(TaleUtils.mdToHtml(value)); + if (text.length() > len) { + return text.substring(0, len); + } + return text; + } + } + + +} diff --git a/src/main/java/cn/luischen/utils/DateKit.java b/src/main/java/cn/luischen/utils/DateKit.java new file mode 100644 index 00000000..4c029777 --- /dev/null +++ b/src/main/java/cn/luischen/utils/DateKit.java @@ -0,0 +1,697 @@ +package cn.luischen.utils; + +import org.apache.commons.lang3.StringUtils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * Created by Administrator on 2017/3/10 010. + */ +public class DateKit { + public static final int INTERVAL_DAY = 1; + public static final int INTERVAL_WEEK = 2; + public static final int INTERVAL_MONTH = 3; + public static final int INTERVAL_YEAR = 4; + public static final int INTERVAL_HOUR = 5; + public static final int INTERVAL_MINUTE = 6; + public static final int INTERVAL_SECOND = 7; + public static final Date tempDate = new Date((new Long("-2177481952000")).longValue()); + private static List dateFormats = new ArrayList(12) { + private static final long serialVersionUID = 2249396579858199535L; + + { + this.add(new SimpleDateFormat("yyyy-MM-dd")); + this.add(new SimpleDateFormat("yyyy/MM/dd")); + this.add(new SimpleDateFormat("yyyy.MM.dd")); + this.add(new SimpleDateFormat("yyyy-MM-dd HH:24:mm:ss")); + this.add(new SimpleDateFormat("yyyy/MM/dd HH:24:mm:ss")); + this.add(new SimpleDateFormat("yyyy.MM.dd HH:24:mm:ss")); + this.add(new SimpleDateFormat("M/dd/yyyy")); + this.add(new SimpleDateFormat("dd.M.yyyy")); + this.add(new SimpleDateFormat("M/dd/yyyy hh:mm:ss a")); + this.add(new SimpleDateFormat("dd.M.yyyy hh:mm:ss a")); + this.add(new SimpleDateFormat("dd.MMM.yyyy")); + this.add(new SimpleDateFormat("dd-MMM-yyyy")); + this.add(new SimpleDateFormat("MMM d, yyyy",Locale.ENGLISH)); + } + }; + + public DateKit() { + } + + + public static boolean isToday(Date date) { + Date now = new Date(); + boolean result = true; + result &= date.getYear() == now.getYear(); + result &= date.getMonth() == now.getMonth(); + result &= date.getDate() == now.getDate(); + return result; + } + + public static long DaysBetween(Date date1, Date date2) { + if(date2 == null) { + date2 = new Date(); + } + + long day = (date2.getTime() - date1.getTime()) / 86400000L; + return day; + } + + public static boolean compareDate(String date1, String date2) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + Date e = format.parse(date1); + Date d2 = format.parse(date2); + return !e.after(d2); + } catch (ParseException var5) { + var5.printStackTrace(); + return false; + } + } + + public static Date dateFormat(String date, String dateFormat) { + if(date == null) { + return null; + } else { + SimpleDateFormat format = new SimpleDateFormat(dateFormat); + try { + return format.parse(date); + } catch (Exception ignored) { + } + + return null; + } + } + + public static Date dateFormat(String date) { + return dateFormat(date, "yyyy-MM-dd HH:mm:ss"); + } + + public static String dateFormat(Date date, String dateFormat) { + if(date != null) { + SimpleDateFormat format = new SimpleDateFormat(dateFormat); + if(date != null) { + return format.format(date); + } + } + + return ""; + } + + + public static String birthdayFormat(Date date) { + if(date != null) { + SimpleDateFormat format = null; + if(date.before(tempDate)) { + format = new SimpleDateFormat("MM-dd"); + } else { + format = new SimpleDateFormat("yyyy-MM-dd"); + } + + if(date != null) { + return format.format(date); + } + } + + return ""; + } + + public static String dateFormat(Date date) { + return dateFormat(date, "yyyy-MM-dd HH:mm:ss"); + } + + public static boolean isExpiredDay(Date date1) { + long day = ((new Date()).getTime() - date1.getTime()) / 86400000L; + return day >= 1L; + } + + public static Date getYesterday() { + Date date = new Date(); + long time = date.getTime() / 1000L - 86400L; + date.setTime(time * 1000L); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + date = format.parse(format.format(date)); + } catch (Exception var5) { + System.out.println(var5.getMessage()); + } + + return date; + } + + public static Date getWeekAgo() { + Date date = new Date(); + long time = date.getTime() / 1000L - 604800L; + date.setTime(time * 1000L); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + date = format.parse(format.format(date)); + } catch (Exception var5) { + System.out.println(var5.getMessage()); + } + + return date; + } + + public static String getDaysAgo(int interval) { + Date date = new Date(); + long time = date.getTime() / 1000L - (long)(interval * 60 * 60 * 24); + date.setTime(time * 1000L); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + return format.format(date); + } catch (Exception var6) { + System.out.println(var6.getMessage()); + return ""; + } + } + + public static Date getTomorrow() { + Date date = new Date(); + long time = date.getTime() / 1000L + 86400L; + date.setTime(time * 1000L); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + date = format.parse(format.format(date)); + } catch (Exception var5) { + System.out.println(var5.getMessage()); + } + + return date; + } + + public static Date getBeforeDate(String range) { + Calendar today = Calendar.getInstance(); + if("week".equalsIgnoreCase(range)) { + today.add(4, -1); + } else if("month".equalsIgnoreCase(range)) { + today.add(2, -1); + } else { + today.clear(); + } + + return today.getTime(); + } + + public static Date getThisWeekStartTime() { + Calendar today = Calendar.getInstance(); + today.set(7, today.getFirstDayOfWeek()); + Calendar weekFirstDay = Calendar.getInstance(); + weekFirstDay.clear(); + weekFirstDay.set(1, today.get(1)); + weekFirstDay.set(2, today.get(2)); + weekFirstDay.set(5, today.get(5)); + return weekFirstDay.getTime(); + } + + public static String getToday(String format) { + String result = ""; + + try { + Date today = new Date(); + SimpleDateFormat simpleFormat = new SimpleDateFormat(format); + result = simpleFormat.format(today); + } catch (Exception var4) { + ; + } + + return result; + } + + public static Date getStartDay(int year, int month) { + Calendar today = Calendar.getInstance(); + today.clear(); + today.set(1, year); + today.set(2, month - 1); + today.set(5, 1); + return today.getTime(); + } + + public static List getBeforeYearList(int before) { + Calendar today = Calendar.getInstance(); + int theYear = today.get(1); + ArrayList list = new ArrayList(); + + for(int i = before; i >= 0; --i) { + list.add(Integer.valueOf(theYear - i)); + } + + return list; + } + + public static Date dateAdd(int interval, Date date, int n) { + long time = date.getTime() / 1000L; + switch(interval) { + case 1: + time += (long)(n * 86400); + break; + case 2: + time += (long)(n * 604800); + break; + case 3: + time += (long)(n * 2678400); + break; + case 4: + time += (long)(n * 31536000); + break; + case 5: + time += (long)(n * 3600); + break; + case 6: + time += (long)(n * 60); + break; + case 7: + time += (long)n; + } + + Date result = new Date(); + result.setTime(time * 1000L); + return result; + } + + public static int dateDiff(int interval, Date begin, Date end) { + long beginTime = begin.getTime() / 1000L; + long endTime = end.getTime() / 1000L; + long tmp = 0L; + if(endTime == beginTime) { + return 0; + } else { + if(endTime < beginTime) { + tmp = beginTime; + beginTime = endTime; + endTime = tmp; + } + + long intervalTime = endTime - beginTime; + long result = 0L; + switch(interval) { + case 1: + result = intervalTime / 86400L; + break; + case 2: + result = intervalTime / 604800L; + break; + case 3: + result = intervalTime / 2678400L; + break; + case 4: + result = intervalTime / 31536000L; + break; + case 5: + result = intervalTime / 3600L; + break; + case 6: + result = intervalTime / 60L; + break; + case 7: + result = intervalTime / 1L; + } + + if(tmp > 0L) { + result = 0L - result; + } + + return (int)result; + } + } + + public static int getTodayYear() { + int yyyy = Integer.parseInt(dateFormat(new Date(), "yyyy")); + return yyyy; + } + + public static Date getNow() { + return new Date(); + } + + public static String dateFormatRss(Date date) { + return date != null?dateFormat(date, "E, d MMM yyyy H:mm:ss") + " GMT":""; + } + + public static boolean betweenStartDateAndEndDate(Date startDate, Date endDate) { + boolean bool = false; + Date curDate = new Date(); + if(curDate.after(startDate) && curDate.before(dateAdd(1, endDate, 1))) { + bool = true; + } + + return bool; + } + + public static boolean nowDateBetweenStartDateAndEndDate(Date startDate, Date endDate) { + boolean bool = false; + Date curDate = new Date(); + if(curDate.after(startDate) && curDate.before(endDate)) { + bool = true; + } + + return bool; + } + + public static boolean nowDateAfterDate(Date date) { + boolean bool = false; + Date curDate = new Date(); + if(curDate.after(date)) { + bool = true; + } + + return bool; + } + + public static int getBetweenTodaysStartDateAndEndDate(Date startDate, Date endDate) { + byte betweentoday = 0; + if(startDate == null) { + return betweentoday; + } else { + if(endDate == null) { + Calendar calendar = Calendar.getInstance(); + String year = Integer.toString(calendar.get(Calendar.YEAR)); + String month = Integer.toString(calendar.get(Calendar.MONTH) + 1); + String day = Integer.toString(calendar.get(Calendar.DATE)); + String strtodaytime = year + "-" + month + "-" + day; + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + + try { + endDate = formatter.parse(strtodaytime); + } catch (ParseException var10) { + var10.printStackTrace(); + } + } + + int betweentoday1; + if(endDate.after(startDate)) { + betweentoday1 = (int)((endDate.getTime() - startDate.getTime()) / 86400000L); + } else { + betweentoday1 = (int)((startDate.getTime() - endDate.getTime()) / 86400000L); + } + + return betweentoday1; + } + } + + public static String getTime(int format) { + StringBuffer cTime = new StringBuffer(10); + Calendar time = Calendar.getInstance(); + int miltime = time.get(14); + int second = time.get(13); + int minute = time.get(12); + int hour = time.get(11); + int day = time.get(5); + int month = time.get(2) + 1; + int year = time.get(1); + if(format != 14) { + if(year >= 2000) { + year -= 2000; + } else { + year -= 1900; + } + } + + if(format >= 2) { + if(format == 14) { + cTime.append(year); + } else { + cTime.append(getFormatTime(year, 2)); + } + } + + if(format >= 4) { + cTime.append(getFormatTime(month, 2)); + } + + if(format >= 6) { + cTime.append(getFormatTime(day, 2)); + } + + if(format >= 8) { + cTime.append(getFormatTime(hour, 2)); + } + + if(format >= 10) { + cTime.append(getFormatTime(minute, 2)); + } + + if(format >= 12) { + cTime.append(getFormatTime(second, 2)); + } + + if(format >= 15) { + cTime.append(getFormatTime(miltime, 3)); + } + + return cTime.toString(); + } + + private static String getFormatTime(int time, int format) { + StringBuffer numm = new StringBuffer(); + int length = String.valueOf(time).length(); + if(format < length) { + return null; + } else { + for(int i = 0; i < format - length; ++i) { + numm.append("0"); + } + + numm.append(time); + return numm.toString().trim(); + } + } + + public static int getUserAge(Date birthday) { + if(birthday == null) { + return 0; + } else { + Calendar cal = Calendar.getInstance(); + if(cal.before(birthday)) { + return 0; + } else { + int yearNow = cal.get(1); + cal.setTime(birthday); + int yearBirth = cal.get(1); + return yearNow - yearBirth; + } + } + } + + public static Date getDateByUnixTime(int unixTime) { + return new Date((long)unixTime * 1000L); + } + + public static long getUnixTimeLong() { + return (long)getUnixTimeByDate(new Date()); + } + + public static int getCurrentUnixTime() { + return getUnixTimeByDate(new Date()); + } + + public static int getUnixTimeByDate(Date date) { + return (int)(date.getTime() / 1000L); + } + + public static long getUnixTimeLong(Date date) { + return date.getTime() / 1000L; + } + + public static Date getNextDay(Date date) { + long time = date.getTime() / 1000L + 86400L; + date.setTime(time * 1000L); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + date = format.parse(format.format(date)); + } catch (Exception var5) { + System.out.println(var5.getMessage()); + } + + return date; + } + + public static Date nextDay(Date date) { + Date newDate = (Date)date.clone(); + long time = newDate.getTime() / 1000L + 86400L; + newDate.setTime(time * 1000L); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + newDate = format.parse(format.format(newDate)); + } catch (Exception var6) { + System.out.println(var6.getMessage()); + } + + return newDate; + } + + public static Date getNowTime() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = new Date(); + String dateStr = dateFormat(date); + + try { + date = format.parse(dateStr); + } catch (ParseException var4) { + var4.printStackTrace(); + } + + return date; + } + + public static Date getTomorrow(Date date1) { + Calendar now = Calendar.getInstance(); + now.setTime(date1); + now.add(5, 1); + return now.getTime(); + } + + public static Date getWeekAgo(Date date) { + Date newDate = (Date)date.clone(); + long time = newDate.getTime() / 1000L - 604800L; + newDate.setTime(time * 1000L); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + newDate = format.parse(format.format(newDate)); + } catch (Exception var6) { + System.out.println(var6.getMessage()); + } + + return newDate; + } + + public static Date getDatebyTime(Date date, int n) { + String str = dateFormat(date, "yyyy-MM-dd"); + String[] strs = StringUtils.split(str, "-"); + int month = Integer.parseInt(strs[1]); + int monthnow = (month + n) % 12; + int year = Integer.parseInt(strs[0]) + (month + n) / 12; + str = year + "-" + monthnow + "-" + strs[2]; + return dateFormat(str, "yyyy-MM-dd"); + } + + public static Date yesterday(Date date) { + Date newDate = (Date)date.clone(); + long time = newDate.getTime() / 1000L - 86400L; + newDate.setTime(time * 1000L); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + newDate = format.parse(format.format(newDate)); + } catch (Exception var6) { + System.out.println(var6.getMessage()); + } + + return newDate; + } + + public static Date getYesterday(Date date) { + long time = date.getTime() / 1000L - 86400L; + date.setTime(time * 1000L); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + try { + date = format.parse(format.format(date)); + } catch (Exception var5) { + System.out.println(var5.getMessage()); + } + + return date; + } + + public static String getStringNowTime() { + Date date = new Date(); + String dateStr = dateFormat(date); + return dateStr; + } + + public static long getSpecifyTimeSec(long time, int range) { + Date date = new Date((time * 1000L + 28800000L) / 86400000L * 86400000L - 28800000L); + long zeroTime = date.getTime() / 1000L; + long specifyTime = (long)(range * 24 * 3600); + return zeroTime + specifyTime; + } + + public static String formatDateByUnixTime(long unixTime, String dateFormat) { + return dateFormat(new Date(unixTime * 1000L), dateFormat); + } + + public static Date convertToDate(String input) { + Date date = null; + if(null == input) { + return null; + } else { + Iterator var2 = dateFormats.iterator(); + + while(var2.hasNext()) { + SimpleDateFormat format = (SimpleDateFormat)var2.next(); + + try { + format.setLenient(false); + date = format.parse(input); + } catch (ParseException var5) { + ; + } + + if(date != null) { + break; + } + } + + return date; + } + } + + public static Long getTodayTime() { + Calendar today = Calendar.getInstance(); + today.set(11, 0); + today.set(12, 0); + today.set(13, 0); + return Long.valueOf(String.valueOf(today.getTimeInMillis()).substring(0, 10)); + } + + public static Long getYesterdayTime() { + Calendar today = Calendar.getInstance(); + today.set(11, -24); + today.set(12, 0); + today.set(13, 0); + return Long.valueOf(String.valueOf(today.getTimeInMillis()).substring(0, 10)); + } + + public static Long getTomorrowTime() { + Calendar tomorrow = Calendar.getInstance(); + tomorrow.set(11, 24); + tomorrow.set(12, 0); + tomorrow.set(13, 0); + return Long.valueOf(String.valueOf(tomorrow.getTimeInMillis()).substring(0, 10)); + } + + public static Date getYearStartDay(String year, String dateFormat){ + Calendar cale = Calendar.getInstance(); + SimpleDateFormat format = new SimpleDateFormat(dateFormat); + // 获取前月的第一天 + cale = Calendar.getInstance(); + cale.add(Calendar.YEAR, 0); + cale.set(Calendar.DAY_OF_YEAR, 1); + return cale.getTime(); + } + + public static Date getYearEndDay(String year, String dateFormat){ + Calendar cale = Calendar.getInstance(); + SimpleDateFormat format = new SimpleDateFormat(dateFormat); + // 获取前月的最后一天 + cale = Calendar.getInstance(); + cale.add(Calendar.YEAR, 1); + cale.set(Calendar.DAY_OF_YEAR, 0); + return cale.getTime(); + } + + + +} diff --git a/src/main/java/cn/luischen/utils/GsonUtils.java b/src/main/java/cn/luischen/utils/GsonUtils.java new file mode 100644 index 00000000..d65183d1 --- /dev/null +++ b/src/main/java/cn/luischen/utils/GsonUtils.java @@ -0,0 +1,16 @@ +package cn.luischen.utils; + +import com.google.gson.Gson; + +/** + * json转换工具 + * Created by Administrator on 2017/3/13 013. + */ +public class GsonUtils { + + private static final Gson gson = new Gson(); + + public static String toJsonString(Object object){ + return object==null?null:gson.toJson(object); + } +} diff --git a/src/main/java/cn/luischen/utils/IPKit.java b/src/main/java/cn/luischen/utils/IPKit.java new file mode 100644 index 00000000..b21ac30d --- /dev/null +++ b/src/main/java/cn/luischen/utils/IPKit.java @@ -0,0 +1,64 @@ +package cn.luischen.utils; + +import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +/** + * ip工具类 + * Created by BlueT on 2017/3/9. + */ +public class IPKit { + /** + * @param request 请求 + * @return IP Address + */ + public static String getIpAddrByRequest(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return ip; + } + + /** + * @return 本机IPSocketException + * @throws SocketException + */ + public static String getRealIp() throws SocketException { + String localip = null;// 本地IP,如果没有配置外网IP则返回它 + String netip = null;// 外网IP + + Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces(); + InetAddress ip = null; + boolean finded = false;// 是否找到外网IP + while (netInterfaces.hasMoreElements() && !finded) { + NetworkInterface ni = netInterfaces.nextElement(); + Enumeration address = ni.getInetAddresses(); + while (address.hasMoreElements()) { + ip = address.nextElement(); + if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && !ip.getHostAddress().contains(":")) {// 外网IP + netip = ip.getHostAddress(); + finded = true; + break; + } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && !ip.getHostAddress().contains(":")) {// 内网IP + localip = ip.getHostAddress(); + } + } + } + + if (netip != null && !"".equals(netip)) { + return netip; + } else { + return localip; + } + } +} diff --git a/src/main/java/cn/luischen/utils/MapCache.java b/src/main/java/cn/luischen/utils/MapCache.java new file mode 100644 index 00000000..0ad1d9d8 --- /dev/null +++ b/src/main/java/cn/luischen/utils/MapCache.java @@ -0,0 +1,167 @@ +package cn.luischen.utils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Map 缓存实现 + * Created by Donghua.Chen on 2018/4/30. + */ +public class MapCache { + + /** + * 默认存储1024个缓存 + */ + private static final int DEFAULT_CACHES = 1024; + + private static final MapCache INS = new MapCache(); + + public static MapCache single() { + return INS; + } + + /** + * 缓存容器 + */ + private Map cachePool; + + public MapCache() { + this(DEFAULT_CACHES); + } + + public MapCache(int cacheCount) { + cachePool = new ConcurrentHashMap<>(cacheCount); + } + + /** + * 读取一个缓存 + * + * @param key 缓存key + * @param + * @return + */ + public T get(String key) { + CacheObject cacheObject = cachePool.get(key); + if (null != cacheObject) { + long cur = System.currentTimeMillis() / 1000; + if (cacheObject.getExpired() <= 0 || cacheObject.getExpired() > cur) { + Object result = cacheObject.getValue(); + return (T) result; + } + } + return null; + } + + /** + * 读取一个hash类型缓存 + * + * @param key 缓存key + * @param field 缓存field + * @param + * @return + */ + public T hget(String key, String field) { + key = key + ":" + field; + return this.get(key); + } + + /** + * 设置一个缓存 + * + * @param key 缓存key + * @param value 缓存value + */ + public void set(String key, Object value) { + this.set(key, value, -1); + } + + /** + * 设置一个缓存并带过期时间 + * + * @param key 缓存key + * @param value 缓存value + * @param expired 过期时间,单位为秒 + */ + public void set(String key, Object value, long expired) { + expired = expired > 0 ? System.currentTimeMillis() / 1000 + expired : expired; + CacheObject cacheObject = new CacheObject(key, value, expired); + cachePool.put(key, cacheObject); + } + + /** + * 设置一个hash缓存 + * + * @param key 缓存key + * @param field 缓存field + * @param value 缓存value + */ + public void hset(String key, String field, Object value) { + this.hset(key, field, value, -1); + } + + /** + * 设置一个hash缓存并带过期时间 + * + * @param key 缓存key + * @param field 缓存field + * @param value 缓存value + * @param expired 过期时间,单位为秒 + */ + public void hset(String key, String field, Object value, long expired) { + key = key + ":" + field; + expired = expired > 0 ? System.currentTimeMillis() / 1000 + expired : expired; + CacheObject cacheObject = new CacheObject(key, value, expired); + cachePool.put(key, cacheObject); + } + + /** + * 根据key删除缓存 + * + * @param key 缓存key + */ + public void del(String key) { + cachePool.remove(key); + } + + /** + * 根据key和field删除缓存 + * + * @param key 缓存key + * @param field 缓存field + */ + public void hdel(String key, String field) { + key = key + ":" + field; + this.del(key); + } + + /** + * 清空缓存 + */ + public void clean() { + cachePool.clear(); + } + + static class CacheObject { + private String key; + private Object value; + private long expired; + + public CacheObject(String key, Object value, long expired) { + this.key = key; + this.value = value; + this.expired = expired; + } + + public String getKey() { + return key; + } + + public Object getValue() { + return value; + } + + public long getExpired() { + return expired; + } + } +} diff --git a/src/main/java/cn/luischen/utils/TaleUtils.java b/src/main/java/cn/luischen/utils/TaleUtils.java new file mode 100644 index 00000000..76a5de24 --- /dev/null +++ b/src/main/java/cn/luischen/utils/TaleUtils.java @@ -0,0 +1,432 @@ +package cn.luischen.utils; + +import cn.luischen.constant.WebConst; +import cn.luischen.controller.admin.AttAchController; +import cn.luischen.exception.BusinessException; +import cn.luischen.model.UserDomain; +import org.apache.commons.lang3.StringUtils; +import org.commonmark.Extension; +import org.commonmark.ext.gfm.tables.TablesExtension; +import org.commonmark.node.Node; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.imageio.ImageIO; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.sql.DataSource; +import java.awt.*; +import java.io.*; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.Normalizer; +import java.util.Arrays; +import java.util.Date; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by Donghua.Chen on 2018/4/30. + */ +public class TaleUtils { + + + private static final Logger LOGGER = LoggerFactory.getLogger(TaleUtils.class); + + /** + * 一个月 + */ + private static final int one_month = 30 * 24 * 60 * 60; + /** + * 匹配邮箱正则 + */ + private static final Pattern VALID_EMAIL_ADDRESS_REGEX = + Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); + private static final Pattern SLUG_REGEX = Pattern.compile("^[A-Za-z0-9_-]{5,100}$", Pattern.CASE_INSENSITIVE); + // 使用双重检查锁的单例方式需要添加 volatile 关键字 + private static volatile DataSource newDataSource; + /** + * markdown解析器 + */ + private static Parser parser = Parser.builder().build(); + /** + * 获取文件所在目录 + */ + private static String location = TaleUtils.class.getClassLoader().getResource("").getPath(); + + /** + * 判断是否是邮箱 + * + * @param emailStr + * @return + */ + public static boolean isEmail(String emailStr) { + Matcher matcher = VALID_EMAIL_ADDRESS_REGEX.matcher(emailStr); + return matcher.find(); + } + + /** + * @param fileName 获取jar外部的文件 + * @return 返回属性 + */ + private static Properties getPropFromFile(String fileName) { + Properties properties = new Properties(); + try { +// 默认是classPath路径 + InputStream resourceAsStream = new FileInputStream(fileName); + properties.load(resourceAsStream); + } catch (BusinessException | IOException e) { + LOGGER.error("get properties file fail={}", e.getMessage()); + } + return properties; + } + + /** + * md5加密 + * + * @param source 数据源 + * @return 加密字符串 + */ + public static String MD5encode(String source) { + if (StringUtils.isBlank(source)) { + return null; + } + MessageDigest messageDigest = null; + try { + messageDigest = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ignored) { + } + byte[] encode = messageDigest.digest(source.getBytes()); + StringBuilder hexString = new StringBuilder(); + for (byte anEncode : encode) { + String hex = Integer.toHexString(0xff & anEncode); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + /** + * 获取新的数据源 + * + * @return + */ + public static DataSource getNewDataSource() { + if (newDataSource == null) synchronized (TaleUtils.class) { + if (newDataSource == null) { + Properties properties = TaleUtils.getPropFromFile("application-default.properties"); + if (properties.size() == 0) { + return newDataSource; + } + DriverManagerDataSource managerDataSource = new DriverManagerDataSource(); + managerDataSource.setDriverClassName("com.mysql.jdbc.Driver"); + managerDataSource.setPassword(properties.getProperty("spring.datasource.password")); + String str = "jdbc:mysql://" + properties.getProperty("spring.datasource.url") + "/" + properties.getProperty("spring.datasource.dbname") + "?useUnicode=true&characterEncoding=utf-8&useSSL=false"; + managerDataSource.setUrl(str); + managerDataSource.setUsername(properties.getProperty("spring.datasource.username")); + newDataSource = managerDataSource; + } + } + return newDataSource; + } + + /** + * 返回当前登录用户 + * + * @return + */ + public static UserDomain getLoginUser(HttpServletRequest request) { + HttpSession session = request.getSession(); + if (null == session) { + return null; + } + return (UserDomain) session.getAttribute(WebConst.LOGIN_SESSION_KEY); + } + + + /** + * 获取cookie中的用户id + * + * @param request + * @return + */ + public static Integer getCookieUid(HttpServletRequest request) { + if (null != request) { + Cookie cookie = cookieRaw(WebConst.USER_IN_COOKIE, request); + if (cookie != null && cookie.getValue() != null) { + try { + String uid = Tools.deAes(cookie.getValue(), WebConst.AES_SALT); + return StringUtils.isNotBlank(uid) && Tools.isNumber(uid) ? Integer.valueOf(uid) : null; + } catch (Exception e) { + } + } + } + return null; + } + + /** + * 从cookies中获取指定cookie + * + * @param name 名称 + * @param request 请求 + * @return cookie + */ + private static Cookie cookieRaw(String name, HttpServletRequest request) { + javax.servlet.http.Cookie[] servletCookies = request.getCookies(); + if (servletCookies == null) { + return null; + } + for (javax.servlet.http.Cookie c : servletCookies) { + if (c.getName().equals(name)) { + return c; + } + } + return null; + } + + /** + * 设置记住密码cookie + * + * @param response + * @param uid + */ + public static void setCookie(HttpServletResponse response, Integer uid) { + try { + String val = Tools.enAes(uid.toString(), WebConst.AES_SALT); + boolean isSSL = false; + Cookie cookie = new Cookie(WebConst.USER_IN_COOKIE, val); + cookie.setPath("/"); + cookie.setMaxAge(60 * 30); + cookie.setSecure(isSSL); + response.addCookie(cookie); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 提取html中的文字 + * + * @param html + * @return + */ + public static String htmlToText(String html) { + if (StringUtils.isNotBlank(html)) { + return html.replaceAll("(?s)<[^>]*>(\\s*<[^>]*>)*", " "); + } + return ""; + } + + /** + * markdown转换为html + * + * @param markdown + * @return + */ + public static String mdToHtml(String markdown) { + if (StringUtils.isBlank(markdown)) { + return ""; + } + java.util.List extensions = Arrays.asList(TablesExtension.create()); + Parser parser = Parser.builder().extensions(extensions).build(); + Node document = parser.parse(markdown); + HtmlRenderer renderer = HtmlRenderer.builder().extensions(extensions).build(); + String content = renderer.render(document); + content = Commons.emoji(content); + return content; + } + + /** + * 退出登录状态 + * + * @param session + * @param response + */ + public static void logout(HttpSession session, HttpServletResponse response) { + session.removeAttribute(WebConst.LOGIN_SESSION_KEY); + Cookie cookie = new Cookie(WebConst.USER_IN_COOKIE, ""); + cookie.setMaxAge(0); + response.addCookie(cookie); + try { + response.sendRedirect("/"); + } catch (IOException e) { + LOGGER.error(e.getMessage(), e); + } + } + + /** + * 替换HTML脚本 + * + * @param value + * @return + */ + public static String cleanXSS(String value) { + //You'll need to remove the spaces from the html entities below + value = value.replaceAll("<", "<").replaceAll(">", ">"); + value = value.replaceAll("\\(", "(").replaceAll("\\)", ")"); + value = value.replaceAll("'", "'"); + value = value.replaceAll("eval\\((.*)\\)", ""); + value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); + value = value.replaceAll("script", ""); + return value; + } + + /** + * 过滤XSS注入 + * + * @param value + * @return + */ + public static String filterXSS(String value) { + String cleanValue = null; + if (value != null) { + cleanValue = Normalizer.normalize(value, Normalizer.Form.NFD); + // Avoid null characters + cleanValue = cleanValue.replaceAll("\0", ""); + + // Avoid anything between script tags + Pattern scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE); + cleanValue = scriptPattern.matcher(cleanValue).replaceAll(""); + + // Avoid anything in a src='...' type of expression + scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); + cleanValue = scriptPattern.matcher(cleanValue).replaceAll(""); + + scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); + cleanValue = scriptPattern.matcher(cleanValue).replaceAll(""); + + // Remove any lonesome tag + scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE); + cleanValue = scriptPattern.matcher(cleanValue).replaceAll(""); + + // Remove any lonesome ` 来存放 Markdown 源文档; + - 创建 Editor.md 只需要写一个 `
    ` ,如果没有添加 `class="editormd"` 属性会自动添加,另外如果不存在 ` + +``` + +> Tip: Editor.md can auto append `"); + markdownTextarea = this.markdownTextarea = editor.children("textarea"); + } + + markdownTextarea.addClass(classNames.textarea.markdown).attr("placeholder", settings.placeholder); + + if (typeof markdownTextarea.attr("name") === "undefined" || markdownTextarea.attr("name") === "") + { + markdownTextarea.attr("name", (settings.name !== "") ? settings.name : id + "-markdown-doc"); + } + + var appendElements = [ + (!settings.readOnly) ? "" : "", + ( (settings.saveHTMLToTextarea) ? "" : "" ), + "
    ", + "
    ", + "
    " + ].join("\n"); + + editor.append(appendElements).addClass(classPrefix + "vertical"); + + if (settings.theme !== "") + { + editor.addClass(classPrefix + "theme-" + settings.theme); + } + + this.mask = editor.children("." + classPrefix + "mask"); + this.containerMask = editor.children("." + classPrefix + "container-mask"); + + if (settings.markdown !== "") + { + markdownTextarea.val(settings.markdown); + } + + if (settings.appendMarkdown !== "") + { + markdownTextarea.val(markdownTextarea.val() + settings.appendMarkdown); + } + + this.htmlTextarea = editor.children("." + classNames.textarea.html); + this.preview = editor.children("." + classPrefix + "preview"); + this.previewContainer = this.preview.children("." + classPrefix + "preview-container"); + + if (settings.previewTheme !== "") + { + this.preview.addClass(classPrefix + "preview-theme-" + settings.previewTheme); + } + + if (typeof define === "function" && define.amd) + { + if (typeof katex !== "undefined") + { + editormd.$katex = katex; + } + + if (settings.searchReplace && !settings.readOnly) + { + editormd.loadCSS(settings.path + "codemirror/addon/dialog/dialog"); + editormd.loadCSS(settings.path + "codemirror/addon/search/matchesonscrollbar"); + } + } + + if ((typeof define === "function" && define.amd) || !settings.autoLoadModules) + { + if (typeof CodeMirror !== "undefined") { + editormd.$CodeMirror = CodeMirror; + } + + if (typeof marked !== "undefined") { + editormd.$marked = marked; + } + + this.setCodeMirror().setToolbar().loadedDisplay(); + } + else + { + this.loadQueues(); + } + + return this; + }, + + /** + * 所需组件加载队列 + * Required components loading queue + * + * @returns {editormd} 返回editormd的实例对象 + */ + + loadQueues : function() { + var _this = this; + var settings = this.settings; + var loadPath = settings.path; + + var loadFlowChartOrSequenceDiagram = function() { + + if (editormd.isIE8) + { + _this.loadedDisplay(); + + return ; + } + + if (settings.flowChart || settings.sequenceDiagram) + { + editormd.loadScript(loadPath + "raphael.min", function() { + + editormd.loadScript(loadPath + "underscore.min", function() { + + if (!settings.flowChart && settings.sequenceDiagram) + { + editormd.loadScript(loadPath + "sequence-diagram.min", function() { + _this.loadedDisplay(); + }); + } + else if (settings.flowChart && !settings.sequenceDiagram) + { + editormd.loadScript(loadPath + "flowchart.min", function() { + editormd.loadScript(loadPath + "jquery.flowchart.min", function() { + _this.loadedDisplay(); + }); + }); + } + else if (settings.flowChart && settings.sequenceDiagram) + { + editormd.loadScript(loadPath + "flowchart.min", function() { + editormd.loadScript(loadPath + "jquery.flowchart.min", function() { + editormd.loadScript(loadPath + "sequence-diagram.min", function() { + _this.loadedDisplay(); + }); + }); + }); + } + }); + + }); + } + else + { + _this.loadedDisplay(); + } + }; + + editormd.loadCSS(loadPath + "codemirror/codemirror.min"); + + if (settings.searchReplace && !settings.readOnly) + { + editormd.loadCSS(loadPath + "codemirror/addon/dialog/dialog"); + editormd.loadCSS(loadPath + "codemirror/addon/search/matchesonscrollbar"); + } + + if (settings.codeFold) + { + editormd.loadCSS(loadPath + "codemirror/addon/fold/foldgutter"); + } + + editormd.loadScript(loadPath + "codemirror/codemirror.min", function() { + editormd.$CodeMirror = CodeMirror; + + editormd.loadScript(loadPath + "codemirror/modes.min", function() { + + editormd.loadScript(loadPath + "codemirror/addons.min", function() { + + _this.setCodeMirror(); + + if (settings.mode !== "gfm" && settings.mode !== "markdown") + { + _this.loadedDisplay(); + + return false; + } + + _this.setToolbar(); + + editormd.loadScript(loadPath + "marked.min", function() { + + editormd.$marked = marked; + + if (settings.previewCodeHighlight) + { + editormd.loadScript(loadPath + "prettify.min", function() { + loadFlowChartOrSequenceDiagram(); + }); + } + else + { + loadFlowChartOrSequenceDiagram(); + } + }); + + }); + + }); + + }); + + return this; + }, + + /** + * 设置 Editor.md 的整体主题,主要是工具栏 + * Setting Editor.md theme + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setTheme : function(theme) { + var editor = this.editor; + var oldTheme = this.settings.theme; + var themePrefix = this.classPrefix + "theme-"; + + editor.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme); + + this.settings.theme = theme; + + return this; + }, + + /** + * 设置 CodeMirror(编辑区)的主题 + * Setting CodeMirror (Editor area) theme + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setEditorTheme : function(theme) { + var settings = this.settings; + settings.editorTheme = theme; + + if (theme !== "default") + { + editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme); + } + + this.cm.setOption("theme", theme); + + return this; + }, + + /** + * setEditorTheme() 的别名 + * setEditorTheme() alias + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setCodeMirrorTheme : function (theme) { + this.setEditorTheme(theme); + + return this; + }, + + /** + * 设置 Editor.md 的主题 + * Setting Editor.md theme + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setPreviewTheme : function(theme) { + var preview = this.preview; + var oldTheme = this.settings.previewTheme; + var themePrefix = this.classPrefix + "preview-theme-"; + + preview.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme); + + this.settings.previewTheme = theme; + + return this; + }, + + /** + * 配置和初始化CodeMirror组件 + * CodeMirror initialization + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setCodeMirror : function() { + var settings = this.settings; + var editor = this.editor; + + if (settings.editorTheme !== "default") + { + editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme); + } + + var codeMirrorConfig = { + mode : settings.mode, + theme : settings.editorTheme, + tabSize : settings.tabSize, + dragDrop : false, + autofocus : settings.autoFocus, + autoCloseTags : settings.autoCloseTags, + readOnly : (settings.readOnly) ? "nocursor" : false, + indentUnit : settings.indentUnit, + lineNumbers : settings.lineNumbers, + lineWrapping : settings.lineWrapping, + extraKeys : { + "Ctrl-Q": function(cm) { + cm.foldCode(cm.getCursor()); + } + }, + foldGutter : settings.codeFold, + gutters : ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], + matchBrackets : settings.matchBrackets, + indentWithTabs : settings.indentWithTabs, + styleActiveLine : settings.styleActiveLine, + styleSelectedText : settings.styleSelectedText, + autoCloseBrackets : settings.autoCloseBrackets, + showTrailingSpace : settings.showTrailingSpace, + highlightSelectionMatches : ( (!settings.matchWordHighlight) ? false : { showToken: (settings.matchWordHighlight === "onselected") ? false : /\w/ } ) + }; + + this.codeEditor = this.cm = editormd.$CodeMirror.fromTextArea(this.markdownTextarea[0], codeMirrorConfig); + this.codeMirror = this.cmElement = editor.children(".CodeMirror"); + + if (settings.value !== "") + { + this.cm.setValue(settings.value); + } + + this.codeMirror.css({ + fontSize : settings.fontSize, + width : (!settings.watch) ? "100%" : "50%" + }); + + if (settings.autoHeight) + { + this.codeMirror.css("height", "auto"); + this.cm.setOption("viewportMargin", Infinity); + } + + if (!settings.lineNumbers) + { + this.codeMirror.find(".CodeMirror-gutters").css("border-right", "none"); + } + + return this; + }, + + /** + * 获取CodeMirror的配置选项 + * Get CodeMirror setting options + * + * @returns {Mixed} return CodeMirror setting option value + */ + + getCodeMirrorOption : function(key) { + return this.cm.getOption(key); + }, + + /** + * 配置和重配置CodeMirror的选项 + * CodeMirror setting options / resettings + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setCodeMirrorOption : function(key, value) { + + this.cm.setOption(key, value); + + return this; + }, + + /** + * 添加 CodeMirror 键盘快捷键 + * Add CodeMirror keyboard shortcuts key map + * + * @returns {editormd} 返回editormd的实例对象 + */ + + addKeyMap : function(map, bottom) { + this.cm.addKeyMap(map, bottom); + + return this; + }, + + /** + * 移除 CodeMirror 键盘快捷键 + * Remove CodeMirror keyboard shortcuts key map + * + * @returns {editormd} 返回editormd的实例对象 + */ + + removeKeyMap : function(map) { + this.cm.removeKeyMap(map); + + return this; + }, + + /** + * 跳转到指定的行 + * Goto CodeMirror line + * + * @param {String|Intiger} line line number or "first"|"last" + * @returns {editormd} 返回editormd的实例对象 + */ + + gotoLine : function (line) { + + var settings = this.settings; + + if (!settings.gotoLine) + { + return this; + } + + var cm = this.cm; + var editor = this.editor; + var count = cm.lineCount(); + var preview = this.preview; + + if (typeof line === "string") + { + if(line === "last") + { + line = count; + } + + if (line === "first") + { + line = 1; + } + } + + if (typeof line !== "number") + { + alert("Error: The line number must be an integer."); + return this; + } + + line = parseInt(line) - 1; + + if (line > count) + { + alert("Error: The line number range 1-" + count); + + return this; + } + + cm.setCursor( {line : line, ch : 0} ); + + var scrollInfo = cm.getScrollInfo(); + var clientHeight = scrollInfo.clientHeight; + var coords = cm.charCoords({line : line, ch : 0}, "local"); + + cm.scrollTo(null, (coords.top + coords.bottom - clientHeight) / 2); + + if (settings.watch) + { + var cmScroll = this.codeMirror.find(".CodeMirror-scroll")[0]; + var height = $(cmScroll).height(); + var scrollTop = cmScroll.scrollTop; + var percent = (scrollTop / cmScroll.scrollHeight); + + if (scrollTop === 0) + { + preview.scrollTop(0); + } + else if (scrollTop + height >= cmScroll.scrollHeight - 16) + { + preview.scrollTop(preview[0].scrollHeight); + } + else + { + preview.scrollTop(preview[0].scrollHeight * percent); + } + } + + cm.focus(); + + return this; + }, + + /** + * 扩展当前实例对象,可同时设置多个或者只设置一个 + * Extend editormd instance object, can mutil setting. + * + * @returns {editormd} this(editormd instance object.) + */ + + extend : function() { + if (typeof arguments[1] !== "undefined") + { + if (typeof arguments[1] === "function") + { + arguments[1] = $.proxy(arguments[1], this); + } + + this[arguments[0]] = arguments[1]; + } + + if (typeof arguments[0] === "object" && typeof arguments[0].length === "undefined") + { + $.extend(true, this, arguments[0]); + } + + return this; + }, + + /** + * 设置或扩展当前实例对象,单个设置 + * Extend editormd instance object, one by one + * + * @param {String|Object} key option key + * @param {String|Object} value option value + * @returns {editormd} this(editormd instance object.) + */ + + set : function (key, value) { + + if (typeof value !== "undefined" && typeof value === "function") + { + value = $.proxy(value, this); + } + + this[key] = value; + + return this; + }, + + /** + * 重新配置 + * Resetting editor options + * + * @param {String|Object} key option key + * @param {String|Object} value option value + * @returns {editormd} this(editormd instance object.) + */ + + config : function(key, value) { + var settings = this.settings; + + if (typeof key === "object") + { + settings = $.extend(true, settings, key); + } + + if (typeof key === "string") + { + settings[key] = value; + } + + this.settings = settings; + this.recreate(); + + return this; + }, + + /** + * 注册事件处理方法 + * Bind editor event handle + * + * @param {String} eventType event type + * @param {Function} callback 回调函数 + * @returns {editormd} this(editormd instance object.) + */ + + on : function(eventType, callback) { + var settings = this.settings; + + if (typeof settings["on" + eventType] !== "undefined") + { + settings["on" + eventType] = $.proxy(callback, this); + } + + return this; + }, + + /** + * 解除事件处理方法 + * Unbind editor event handle + * + * @param {String} eventType event type + * @returns {editormd} this(editormd instance object.) + */ + + off : function(eventType) { + var settings = this.settings; + + if (typeof settings["on" + eventType] !== "undefined") + { + settings["on" + eventType] = function(){}; + } + + return this; + }, + + /** + * 显示工具栏 + * Display toolbar + * + * @param {Function} [callback=function(){}] 回调函数 + * @returns {editormd} 返回editormd的实例对象 + */ + + showToolbar : function(callback) { + var settings = this.settings; + + if(settings.readOnly) { + return this; + } + + if (settings.toolbar && (this.toolbar.length < 1 || this.toolbar.find("." + this.classPrefix + "menu").html() === "") ) + { + this.setToolbar(); + } + + settings.toolbar = true; + + this.toolbar.show(); + this.resize(); + + $.proxy(callback || function(){}, this)(); + + return this; + }, + + /** + * 隐藏工具栏 + * Hide toolbar + * + * @param {Function} [callback=function(){}] 回调函数 + * @returns {editormd} this(editormd instance object.) + */ + + hideToolbar : function(callback) { + var settings = this.settings; + + settings.toolbar = false; + this.toolbar.hide(); + this.resize(); + + $.proxy(callback || function(){}, this)(); + + return this; + }, + + /** + * 页面滚动时工具栏的固定定位 + * Set toolbar in window scroll auto fixed position + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setToolbarAutoFixed : function(fixed) { + + var state = this.state; + var editor = this.editor; + var toolbar = this.toolbar; + var settings = this.settings; + + if (typeof fixed !== "undefined") + { + settings.toolbarAutoFixed = fixed; + } + + var autoFixedHandle = function(){ + var $window = $(window); + var top = $window.scrollTop(); + + if (!settings.toolbarAutoFixed) + { + return false; + } + + if (top - editor.offset().top > 10 && top < editor.height()) + { + toolbar.css({ + position : "fixed", + width : editor.width() + "px", + left : ($window.width() - editor.width()) / 2 + "px" + }); + } + else + { + toolbar.css({ + position : "absolute", + width : "100%", + left : 0 + }); + } + }; + + if (!state.fullscreen && !state.preview && settings.toolbar && settings.toolbarAutoFixed) + { + $(window).bind("scroll", autoFixedHandle); + } + + return this; + }, + + /** + * 配置和初始化工具栏 + * Set toolbar and Initialization + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setToolbar : function() { + var settings = this.settings; + + if(settings.readOnly) { + return this; + } + + var editor = this.editor; + var preview = this.preview; + var classPrefix = this.classPrefix; + + var toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar"); + + if (settings.toolbar && toolbar.length < 1) + { + var toolbarHTML = "
      "; + + editor.append(toolbarHTML); + toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar"); + } + + if (!settings.toolbar) + { + toolbar.hide(); + + return this; + } + + toolbar.show(); + + var icons = (typeof settings.toolbarIcons === "function") ? settings.toolbarIcons() + : ((typeof settings.toolbarIcons === "string") ? editormd.toolbarModes[settings.toolbarIcons] : settings.toolbarIcons); + + var toolbarMenu = toolbar.find("." + this.classPrefix + "menu"), menu = ""; + var pullRight = false; + + for (var i = 0, len = icons.length; i < len; i++) + { + var name = icons[i]; + + if (name === "||") + { + pullRight = true; + } + else if (name === "|") + { + menu += "
    • |
    • "; + } + else + { + var isHeader = (/h(\d)/.test(name)); + var index = name; + + if (name === "watch" && !settings.watch) { + index = "unwatch"; + } + + var title = settings.lang.toolbar[index]; + var iconTexts = settings.toolbarIconTexts[index]; + var iconClass = settings.toolbarIconsClass[index]; + + title = (typeof title === "undefined") ? "" : title; + iconTexts = (typeof iconTexts === "undefined") ? "" : iconTexts; + iconClass = (typeof iconClass === "undefined") ? "" : iconClass; + + var menuItem = pullRight ? "
    • " : "
    • "; + + if (typeof settings.toolbarCustomIcons[name] !== "undefined" && typeof settings.toolbarCustomIcons[name] !== "function") + { + menuItem += settings.toolbarCustomIcons[name]; + } + else + { + menuItem += ""; + menuItem += ""+((isHeader) ? name.toUpperCase() : ( (iconClass === "") ? iconTexts : "") ) + ""; + menuItem += ""; + } + + menuItem += "
    • "; + + menu = pullRight ? menuItem + menu : menu + menuItem; + } + } + + toolbarMenu.html(menu); + + toolbarMenu.find("[title=\"Lowercase\"]").attr("title", settings.lang.toolbar.lowercase); + toolbarMenu.find("[title=\"ucwords\"]").attr("title", settings.lang.toolbar.ucwords); + + this.setToolbarHandler(); + this.setToolbarAutoFixed(); + + return this; + }, + + /** + * 工具栏图标事件处理对象序列 + * Get toolbar icons event handlers + * + * @param {Object} cm CodeMirror的实例对象 + * @param {String} name 要获取的事件处理器名称 + * @returns {Object} 返回处理对象序列 + */ + + dialogLockScreen : function() { + $.proxy(editormd.dialogLockScreen, this)(); + + return this; + }, + + dialogShowMask : function(dialog) { + $.proxy(editormd.dialogShowMask, this)(dialog); + + return this; + }, + + getToolbarHandles : function(name) { + var toolbarHandlers = this.toolbarHandlers = editormd.toolbarHandlers; + + return (name && typeof toolbarIconHandlers[name] !== "undefined") ? toolbarHandlers[name] : toolbarHandlers; + }, + + /** + * 工具栏图标事件处理器 + * Bind toolbar icons event handle + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setToolbarHandler : function() { + var _this = this; + var settings = this.settings; + + if (!settings.toolbar || settings.readOnly) { + return this; + } + + var toolbar = this.toolbar; + var cm = this.cm; + var classPrefix = this.classPrefix; + var toolbarIcons = this.toolbarIcons = toolbar.find("." + classPrefix + "menu > li > a"); + var toolbarIconHandlers = this.getToolbarHandles(); + + toolbarIcons.bind(editormd.mouseOrTouch("click", "touchend"), function(event) { + + var icon = $(this).children(".fa"); + var name = icon.attr("name"); + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (name === "") { + return ; + } + + _this.activeIcon = icon; + + if (typeof toolbarIconHandlers[name] !== "undefined") + { + $.proxy(toolbarIconHandlers[name], _this)(cm); + } + else + { + if (typeof settings.toolbarHandlers[name] !== "undefined") + { + $.proxy(settings.toolbarHandlers[name], _this)(cm, icon, cursor, selection); + } + } + + if (name !== "link" && name !== "reference-link" && name !== "image" && name !== "code-block" && + name !== "preformatted-text" && name !== "watch" && name !== "preview" && name !== "search" && name !== "fullscreen" && name !== "info") + { + cm.focus(); + } + + return false; + + }); + + return this; + }, + + /** + * 动态创建对话框 + * Creating custom dialogs + * + * @param {Object} options 配置项键值对 Key/Value + * @returns {dialog} 返回创建的dialog的jQuery实例对象 + */ + + createDialog : function(options) { + return $.proxy(editormd.createDialog, this)(options); + }, + + /** + * 创建关于Editor.md的对话框 + * Create about Editor.md dialog + * + * @returns {editormd} 返回editormd的实例对象 + */ + + createInfoDialog : function() { + var _this = this; + var editor = this.editor; + var classPrefix = this.classPrefix; + + var infoDialogHTML = [ + "
      ", + "
      ", + "

      " + editormd.title + "v" + editormd.version + "

      ", + "

      " + this.lang.description + "

      ", + "

      " + editormd.homePage + "

      ", + "

      Copyright © 2015 Pandao, The MIT License.

      ", + "
      ", + "", + "
      " + ].join("\n"); + + editor.append(infoDialogHTML); + + var infoDialog = this.infoDialog = editor.children("." + classPrefix + "dialog-info"); + + infoDialog.find("." + classPrefix + "dialog-close").bind(editormd.mouseOrTouch("click", "touchend"), function() { + _this.hideInfoDialog(); + }); + + infoDialog.css("border", (editormd.isIE8) ? "1px solid #ddd" : "").css("z-index", editormd.dialogZindex).show(); + + this.infoDialogPosition(); + + return this; + }, + + /** + * 关于Editor.md对话居中定位 + * Editor.md dialog position handle + * + * @returns {editormd} 返回editormd的实例对象 + */ + + infoDialogPosition : function() { + var infoDialog = this.infoDialog; + + var _infoDialogPosition = function() { + infoDialog.css({ + top : ($(window).height() - infoDialog.height()) / 2 + "px", + left : ($(window).width() - infoDialog.width()) / 2 + "px" + }); + }; + + _infoDialogPosition(); + + $(window).resize(_infoDialogPosition); + + return this; + }, + + /** + * 显示关于Editor.md + * Display about Editor.md dialog + * + * @returns {editormd} 返回editormd的实例对象 + */ + + showInfoDialog : function() { + + $("html,body").css("overflow-x", "hidden"); + + var _this = this; + var editor = this.editor; + var settings = this.settings; + var infoDialog = this.infoDialog = editor.children("." + this.classPrefix + "dialog-info"); + + if (infoDialog.length < 1) + { + this.createInfoDialog(); + } + + this.lockScreen(true); + + this.mask.css({ + opacity : settings.dialogMaskOpacity, + backgroundColor : settings.dialogMaskBgColor + }).show(); + + infoDialog.css("z-index", editormd.dialogZindex).show(); + + this.infoDialogPosition(); + + return this; + }, + + /** + * 隐藏关于Editor.md + * Hide about Editor.md dialog + * + * @returns {editormd} 返回editormd的实例对象 + */ + + hideInfoDialog : function() { + $("html,body").css("overflow-x", ""); + this.infoDialog.hide(); + this.mask.hide(); + this.lockScreen(false); + + return this; + }, + + /** + * 锁屏 + * lock screen + * + * @param {Boolean} lock Boolean 布尔值,是否锁屏 + * @returns {editormd} 返回editormd的实例对象 + */ + + lockScreen : function(lock) { + editormd.lockScreen(lock); + this.resize(); + + return this; + }, + + /** + * 编辑器界面重建,用于动态语言包或模块加载等 + * Recreate editor + * + * @returns {editormd} 返回editormd的实例对象 + */ + + recreate : function() { + var _this = this; + var editor = this.editor; + var settings = this.settings; + + this.codeMirror.remove(); + + this.setCodeMirror(); + + if (!settings.readOnly) + { + if (editor.find(".editormd-dialog").length > 0) { + editor.find(".editormd-dialog").remove(); + } + + if (settings.toolbar) + { + this.getToolbarHandles(); + this.setToolbar(); + } + } + + this.loadedDisplay(true); + + return this; + }, + + /** + * 高亮预览HTML的pre代码部分 + * highlight of preview codes + * + * @returns {editormd} 返回editormd的实例对象 + */ + + previewCodeHighlight : function() { + var settings = this.settings; + var previewContainer = this.previewContainer; + + if (settings.previewCodeHighlight) + { + previewContainer.find("pre").addClass("prettyprint linenums"); + + if (typeof prettyPrint !== "undefined") + { + prettyPrint(); + } + } + + return this; + }, + + /** + * 解析TeX(KaTeX)科学公式 + * TeX(KaTeX) Renderer + * + * @returns {editormd} 返回editormd的实例对象 + */ + + katexRender : function() { + + if (timer === null) + { + return this; + } + + this.previewContainer.find("." + editormd.classNames.tex).each(function(){ + var tex = $(this); + editormd.$katex.render(tex.text(), tex[0]); + + tex.find(".katex").css("font-size", "1.6em"); + }); + + return this; + }, + + /** + * 解析和渲染流程图及时序图 + * FlowChart and SequenceDiagram Renderer + * + * @returns {editormd} 返回editormd的实例对象 + */ + + flowChartAndSequenceDiagramRender : function() { + var $this = this; + var settings = this.settings; + var previewContainer = this.previewContainer; + + if (editormd.isIE8) { + return this; + } + + if (settings.flowChart) { + if (flowchartTimer === null) { + return this; + } + + previewContainer.find(".flowchart").flowChart(); + } + + if (settings.sequenceDiagram) { + previewContainer.find(".sequence-diagram").sequenceDiagram({theme: "simple"}); + } + + var preview = $this.preview; + var codeMirror = $this.codeMirror; + var codeView = codeMirror.find(".CodeMirror-scroll"); + + var height = codeView.height(); + var scrollTop = codeView.scrollTop(); + var percent = (scrollTop / codeView[0].scrollHeight); + var tocHeight = 0; + + preview.find(".markdown-toc-list").each(function(){ + tocHeight += $(this).height(); + }); + + var tocMenuHeight = preview.find(".editormd-toc-menu").height(); + tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight; + + if (scrollTop === 0) + { + preview.scrollTop(0); + } + else if (scrollTop + height >= codeView[0].scrollHeight - 16) + { + preview.scrollTop(preview[0].scrollHeight); + } + else + { + preview.scrollTop((preview[0].scrollHeight + tocHeight + tocMenuHeight) * percent); + } + + return this; + }, + + /** + * 注册键盘快捷键处理 + * Register CodeMirror keyMaps (keyboard shortcuts). + * + * @param {Object} keyMap KeyMap key/value {"(Ctrl/Shift/Alt)-Key" : function(){}} + * @returns {editormd} return this + */ + + registerKeyMaps : function(keyMap) { + + var _this = this; + var cm = this.cm; + var settings = this.settings; + var toolbarHandlers = editormd.toolbarHandlers; + var disabledKeyMaps = settings.disabledKeyMaps; + + keyMap = keyMap || null; + + if (keyMap) + { + for (var i in keyMap) + { + if ($.inArray(i, disabledKeyMaps) < 0) + { + var map = {}; + map[i] = keyMap[i]; + + cm.addKeyMap(keyMap); + } + } + } + else + { + for (var k in editormd.keyMaps) + { + var _keyMap = editormd.keyMaps[k]; + var handle = (typeof _keyMap === "string") ? $.proxy(toolbarHandlers[_keyMap], _this) : $.proxy(_keyMap, _this); + + if ($.inArray(k, ["F9", "F10", "F11"]) < 0 && $.inArray(k, disabledKeyMaps) < 0) + { + var _map = {}; + _map[k] = handle; + + cm.addKeyMap(_map); + } + } + + $(window).keydown(function(event) { + + var keymaps = { + "120" : "F9", + "121" : "F10", + "122" : "F11" + }; + + if ( $.inArray(keymaps[event.keyCode], disabledKeyMaps) < 0 ) + { + switch (event.keyCode) + { + case 120: + $.proxy(toolbarHandlers["watch"], _this)(); + return false; + break; + + case 121: + $.proxy(toolbarHandlers["preview"], _this)(); + return false; + break; + + case 122: + $.proxy(toolbarHandlers["fullscreen"], _this)(); + return false; + break; + + default: + break; + } + } + }); + } + + return this; + }, + + /** + * 绑定同步滚动 + * + * @returns {editormd} return this + */ + + bindScrollEvent : function() { + + var _this = this; + var preview = this.preview; + var settings = this.settings; + var codeMirror = this.codeMirror; + var mouseOrTouch = editormd.mouseOrTouch; + + if (!settings.syncScrolling) { + return this; + } + + var cmBindScroll = function() { + codeMirror.find(".CodeMirror-scroll").bind(mouseOrTouch("scroll", "touchmove"), function(event) { + var height = $(this).height(); + var scrollTop = $(this).scrollTop(); + var percent = (scrollTop / $(this)[0].scrollHeight); + + var tocHeight = 0; + + preview.find(".markdown-toc-list").each(function(){ + tocHeight += $(this).height(); + }); + + var tocMenuHeight = preview.find(".editormd-toc-menu").height(); + tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight; + + if (scrollTop === 0) + { + preview.scrollTop(0); + } + else if (scrollTop + height >= $(this)[0].scrollHeight - 16) + { + preview.scrollTop(preview[0].scrollHeight); + } + else + { + preview.scrollTop((preview[0].scrollHeight + tocHeight + tocMenuHeight) * percent); + } + + $.proxy(settings.onscroll, _this)(event); + }); + }; + + var cmUnbindScroll = function() { + codeMirror.find(".CodeMirror-scroll").unbind(mouseOrTouch("scroll", "touchmove")); + }; + + var previewBindScroll = function() { + + preview.bind(mouseOrTouch("scroll", "touchmove"), function(event) { + var height = $(this).height(); + var scrollTop = $(this).scrollTop(); + var percent = (scrollTop / $(this)[0].scrollHeight); + var codeView = codeMirror.find(".CodeMirror-scroll"); + + if(scrollTop === 0) + { + codeView.scrollTop(0); + } + else if (scrollTop + height >= $(this)[0].scrollHeight) + { + codeView.scrollTop(codeView[0].scrollHeight); + } + else + { + codeView.scrollTop(codeView[0].scrollHeight * percent); + } + + $.proxy(settings.onpreviewscroll, _this)(event); + }); + + }; + + var previewUnbindScroll = function() { + preview.unbind(mouseOrTouch("scroll", "touchmove")); + }; + + codeMirror.bind({ + mouseover : cmBindScroll, + mouseout : cmUnbindScroll, + touchstart : cmBindScroll, + touchend : cmUnbindScroll + }); + + if (settings.syncScrolling === "single") { + return this; + } + + preview.bind({ + mouseover : previewBindScroll, + mouseout : previewUnbindScroll, + touchstart : previewBindScroll, + touchend : previewUnbindScroll + }); + + return this; + }, + + bindChangeEvent : function() { + + var _this = this; + var cm = this.cm; + var settings = this.settings; + + if (!settings.syncScrolling) { + return this; + } + + cm.on("change", function(_cm, changeObj) { + + if (settings.watch) + { + _this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px"); + } + + timer = setTimeout(function() { + clearTimeout(timer); + _this.save(); + timer = null; + }, settings.delay); + }); + + return this; + }, + + /** + * 加载队列完成之后的显示处理 + * Display handle of the module queues loaded after. + * + * @param {Boolean} recreate 是否为重建编辑器 + * @returns {editormd} 返回editormd的实例对象 + */ + + loadedDisplay : function(recreate) { + + recreate = recreate || false; + + var _this = this; + var editor = this.editor; + var preview = this.preview; + var settings = this.settings; + + this.containerMask.hide(); + + this.save(); + + if (settings.watch) { + preview.show(); + } + + editor.data("oldWidth", editor.width()).data("oldHeight", editor.height()); // 为了兼容Zepto + + this.resize(); + this.registerKeyMaps(); + + $(window).resize(function(){ + _this.resize(); + }); + + this.bindScrollEvent().bindChangeEvent(); + + if (!recreate) + { + $.proxy(settings.onload, this)(); + } + + this.state.loaded = true; + + return this; + }, + + /** + * 设置编辑器的宽度 + * Set editor width + * + * @param {Number|String} width 编辑器宽度值 + * @returns {editormd} 返回editormd的实例对象 + */ + + width : function(width) { + + this.editor.css("width", (typeof width === "number") ? width + "px" : width); + this.resize(); + + return this; + }, + + /** + * 设置编辑器的高度 + * Set editor height + * + * @param {Number|String} height 编辑器高度值 + * @returns {editormd} 返回editormd的实例对象 + */ + + height : function(height) { + + this.editor.css("height", (typeof height === "number") ? height + "px" : height); + this.resize(); + + return this; + }, + + /** + * 调整编辑器的尺寸和布局 + * Resize editor layout + * + * @param {Number|String} [width=null] 编辑器宽度值 + * @param {Number|String} [height=null] 编辑器高度值 + * @returns {editormd} 返回editormd的实例对象 + */ + + resize : function(width, height) { + + width = width || null; + height = height || null; + + var state = this.state; + var editor = this.editor; + var preview = this.preview; + var toolbar = this.toolbar; + var settings = this.settings; + var codeMirror = this.codeMirror; + + if (width) + { + editor.css("width", (typeof width === "number") ? width + "px" : width); + } + + if (settings.autoHeight && !state.fullscreen && !state.preview) + { + editor.css("height", "auto"); + codeMirror.css("height", "auto"); + } + else + { + if (height) + { + editor.css("height", (typeof height === "number") ? height + "px" : height); + } + + if (state.fullscreen) + { + editor.height($(window).height()); + } + + if (settings.toolbar && !settings.readOnly) + { + codeMirror.css("margin-top", toolbar.height() + 1).height(editor.height() - toolbar.height()); + } + else + { + codeMirror.css("margin-top", 0).height(editor.height()); + } + } + + if(settings.watch) + { + codeMirror.width(editor.width() / 2); + preview.width((!state.preview) ? editor.width() / 2 : editor.width()); + + this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px"); + + if (settings.toolbar && !settings.readOnly) + { + preview.css("top", toolbar.height() + 1); + } + else + { + preview.css("top", 0); + } + + if (settings.autoHeight && !state.fullscreen && !state.preview) + { + preview.height(""); + } + else + { + var previewHeight = (settings.toolbar && !settings.readOnly) ? editor.height() - toolbar.height() : editor.height(); + + preview.height(previewHeight); + } + } + else + { + codeMirror.width(editor.width()); + preview.hide(); + } + + if (state.loaded) + { + $.proxy(settings.onresize, this)(); + } + + return this; + }, + + /** + * 解析和保存Markdown代码 + * Parse & Saving Markdown source code + * + * @returns {editormd} 返回editormd的实例对象 + */ + + save : function() { + + if (timer === null) + { + return this; + } + + var _this = this; + var state = this.state; + var settings = this.settings; + var cm = this.cm; + var cmValue = cm.getValue(); + var previewContainer = this.previewContainer; + + if (settings.mode !== "gfm" && settings.mode !== "markdown") + { + this.markdownTextarea.val(cmValue); + + return this; + } + + var marked = editormd.$marked; + var markdownToC = this.markdownToC = []; + var rendererOptions = this.markedRendererOptions = { + toc : settings.toc, + tocm : settings.tocm, + tocStartLevel : settings.tocStartLevel, + pageBreak : settings.pageBreak, + taskList : settings.taskList, + emoji : settings.emoji, + tex : settings.tex, + atLink : settings.atLink, // for @link + emailLink : settings.emailLink, // for mail address auto link + flowChart : settings.flowChart, + sequenceDiagram : settings.sequenceDiagram, + previewCodeHighlight : settings.previewCodeHighlight, + }; + + var markedOptions = this.markedOptions = { + renderer : editormd.markedRenderer(markdownToC, rendererOptions), + gfm : true, + tables : true, + breaks : true, + pedantic : false, + sanitize : (settings.htmlDecode) ? false : true, // 关闭忽略HTML标签,即开启识别HTML标签,默认为false + smartLists : true, + smartypants : true + }; + + marked.setOptions(markedOptions); + + var newMarkdownDoc = editormd.$marked(cmValue, markedOptions); + + //console.info("cmValue", cmValue, newMarkdownDoc); + + newMarkdownDoc = editormd.filterHTMLTags(newMarkdownDoc, settings.htmlDecode); + + //console.error("cmValue", cmValue, newMarkdownDoc); + + this.markdownTextarea.text(cmValue); + + cm.save(); + + if (settings.saveHTMLToTextarea) + { + this.htmlTextarea.text(newMarkdownDoc); + } + + if(settings.watch || (!settings.watch && state.preview)) + { + previewContainer.html(newMarkdownDoc); + + this.previewCodeHighlight(); + + if (settings.toc) + { + var tocContainer = (settings.tocContainer === "") ? previewContainer : $(settings.tocContainer); + var tocMenu = tocContainer.find("." + this.classPrefix + "toc-menu"); + + tocContainer.attr("previewContainer", (settings.tocContainer === "") ? "true" : "false"); + + if (settings.tocContainer !== "" && tocMenu.length > 0) + { + tocMenu.remove(); + } + + editormd.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel); + + if (settings.tocDropdown || tocContainer.find("." + this.classPrefix + "toc-menu").length > 0) + { + editormd.tocDropdownMenu(tocContainer, (settings.tocTitle !== "") ? settings.tocTitle : this.lang.tocTitle); + } + + if (settings.tocContainer !== "") + { + previewContainer.find(".markdown-toc").css("border", "none"); + } + } + + if (settings.tex) + { + if (!editormd.kaTeXLoaded && settings.autoLoadModules) + { + editormd.loadKaTeX(function() { + editormd.$katex = katex; + editormd.kaTeXLoaded = true; + _this.katexRender(); + }); + } + else + { + editormd.$katex = katex; + this.katexRender(); + } + } + + if (settings.flowChart || settings.sequenceDiagram) + { + flowchartTimer = setTimeout(function(){ + clearTimeout(flowchartTimer); + _this.flowChartAndSequenceDiagramRender(); + flowchartTimer = null; + }, 10); + } + + if (state.loaded) + { + $.proxy(settings.onchange, this)(); + } + } + + return this; + }, + + /** + * 聚焦光标位置 + * Focusing the cursor position + * + * @returns {editormd} 返回editormd的实例对象 + */ + + focus : function() { + this.cm.focus(); + + return this; + }, + + /** + * 设置光标的位置 + * Set cursor position + * + * @param {Object} cursor 要设置的光标位置键值对象,例:{line:1, ch:0} + * @returns {editormd} 返回editormd的实例对象 + */ + + setCursor : function(cursor) { + this.cm.setCursor(cursor); + + return this; + }, + + /** + * 获取当前光标的位置 + * Get the current position of the cursor + * + * @returns {Cursor} 返回一个光标Cursor对象 + */ + + getCursor : function() { + return this.cm.getCursor(); + }, + + /** + * 设置光标选中的范围 + * Set cursor selected ranges + * + * @param {Object} from 开始位置的光标键值对象,例:{line:1, ch:0} + * @param {Object} to 结束位置的光标键值对象,例:{line:1, ch:0} + * @returns {editormd} 返回editormd的实例对象 + */ + + setSelection : function(from, to) { + + this.cm.setSelection(from, to); + + return this; + }, + + /** + * 获取光标选中的文本 + * Get the texts from cursor selected + * + * @returns {String} 返回选中文本的字符串形式 + */ + + getSelection : function() { + return this.cm.getSelection(); + }, + + /** + * 设置光标选中的文本范围 + * Set the cursor selection ranges + * + * @param {Array} ranges cursor selection ranges array + * @returns {Array} return this + */ + + setSelections : function(ranges) { + this.cm.setSelections(ranges); + + return this; + }, + + /** + * 获取光标选中的文本范围 + * Get the cursor selection ranges + * + * @returns {Array} return selection ranges array + */ + + getSelections : function() { + return this.cm.getSelections(); + }, + + /** + * 替换当前光标选中的文本或在当前光标处插入新字符 + * Replace the text at the current cursor selected or insert a new character at the current cursor position + * + * @param {String} value 要插入的字符值 + * @returns {editormd} 返回editormd的实例对象 + */ + + replaceSelection : function(value) { + this.cm.replaceSelection(value); + + return this; + }, + + /** + * 在当前光标处插入新字符 + * Insert a new character at the current cursor position + * + * 同replaceSelection()方法 + * With the replaceSelection() method + * + * @param {String} value 要插入的字符值 + * @returns {editormd} 返回editormd的实例对象 + */ + + insertValue : function(value) { + this.replaceSelection(value); + + return this; + }, + + /** + * 追加markdown + * append Markdown to editor + * + * @param {String} md 要追加的markdown源文档 + * @returns {editormd} 返回editormd的实例对象 + */ + + appendMarkdown : function(md) { + var settings = this.settings; + var cm = this.cm; + + cm.setValue(cm.getValue() + md); + + return this; + }, + + /** + * 设置和传入编辑器的markdown源文档 + * Set Markdown source document + * + * @param {String} md 要传入的markdown源文档 + * @returns {editormd} 返回editormd的实例对象 + */ + + setMarkdown : function(md) { + this.cm.setValue(md || this.settings.markdown); + + return this; + }, + + /** + * 获取编辑器的markdown源文档 + * Set Editor.md markdown/CodeMirror value + * + * @returns {editormd} 返回editormd的实例对象 + */ + + getMarkdown : function() { + return this.cm.getValue(); + }, + + /** + * 获取编辑器的源文档 + * Get CodeMirror value + * + * @returns {editormd} 返回editormd的实例对象 + */ + + getValue : function() { + return this.cm.getValue(); + }, + + /** + * 设置编辑器的源文档 + * Set CodeMirror value + * + * @param {String} value set code/value/string/text + * @returns {editormd} 返回editormd的实例对象 + */ + + setValue : function(value) { + this.cm.setValue(value); + + return this; + }, + + /** + * 清空编辑器 + * Empty CodeMirror editor container + * + * @returns {editormd} 返回editormd的实例对象 + */ + + clear : function() { + this.cm.setValue(""); + + return this; + }, + + /** + * 获取解析后存放在Textarea的HTML源码 + * Get parsed html code from Textarea + * + * @returns {String} 返回HTML源码 + */ + + getHTML : function() { + if (!this.settings.saveHTMLToTextarea) + { + alert("Error: settings.saveHTMLToTextarea == false"); + + return false; + } + + return this.htmlTextarea.val(); + }, + + /** + * getHTML()的别名 + * getHTML (alias) + * + * @returns {String} Return html code 返回HTML源码 + */ + + getTextareaSavedHTML : function() { + return this.getHTML(); + }, + + /** + * 获取预览窗口的HTML源码 + * Get html from preview container + * + * @returns {editormd} 返回editormd的实例对象 + */ + + getPreviewedHTML : function() { + if (!this.settings.watch) + { + alert("Error: settings.watch == false"); + + return false; + } + + return this.previewContainer.html(); + }, + + /** + * 开启实时预览 + * Enable real-time watching + * + * @returns {editormd} 返回editormd的实例对象 + */ + + watch : function(callback) { + var settings = this.settings; + + if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0) + { + return this; + } + + this.state.watching = settings.watch = true; + this.preview.show(); + + if (this.toolbar) + { + var watchIcon = settings.toolbarIconsClass.watch; + var unWatchIcon = settings.toolbarIconsClass.unwatch; + + var icon = this.toolbar.find(".fa[name=watch]"); + icon.parent().attr("title", settings.lang.toolbar.watch); + icon.removeClass(unWatchIcon).addClass(watchIcon); + } + + this.codeMirror.css("border-right", "1px solid #ddd").width(this.editor.width() / 2); + + timer = 0; + + this.save().resize(); + + if (!settings.onwatch) + { + settings.onwatch = callback || function() {}; + } + + $.proxy(settings.onwatch, this)(); + + return this; + }, + + /** + * 关闭实时预览 + * Disable real-time watching + * + * @returns {editormd} 返回editormd的实例对象 + */ + + unwatch : function(callback) { + var settings = this.settings; + this.state.watching = settings.watch = false; + this.preview.hide(); + + if (this.toolbar) + { + var watchIcon = settings.toolbarIconsClass.watch; + var unWatchIcon = settings.toolbarIconsClass.unwatch; + + var icon = this.toolbar.find(".fa[name=watch]"); + icon.parent().attr("title", settings.lang.toolbar.unwatch); + icon.removeClass(watchIcon).addClass(unWatchIcon); + } + + this.codeMirror.css("border-right", "none").width(this.editor.width()); + + this.resize(); + + if (!settings.onunwatch) + { + settings.onunwatch = callback || function() {}; + } + + $.proxy(settings.onunwatch, this)(); + + return this; + }, + + /** + * 显示编辑器 + * Show editor + * + * @param {Function} [callback=function()] 回调函数 + * @returns {editormd} 返回editormd的实例对象 + */ + + show : function(callback) { + callback = callback || function() {}; + + var _this = this; + this.editor.show(0, function() { + $.proxy(callback, _this)(); + }); + + return this; + }, + + /** + * 隐藏编辑器 + * Hide editor + * + * @param {Function} [callback=function()] 回调函数 + * @returns {editormd} 返回editormd的实例对象 + */ + + hide : function(callback) { + callback = callback || function() {}; + + var _this = this; + this.editor.hide(0, function() { + $.proxy(callback, _this)(); + }); + + return this; + }, + + /** + * 隐藏编辑器部分,只预览HTML + * Enter preview html state + * + * @returns {editormd} 返回editormd的实例对象 + */ + + previewing : function() { + + var _this = this; + var editor = this.editor; + var preview = this.preview; + var toolbar = this.toolbar; + var settings = this.settings; + var codeMirror = this.codeMirror; + var previewContainer = this.previewContainer; + + if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0) { + return this; + } + + if (settings.toolbar && toolbar) { + toolbar.toggle(); + toolbar.find(".fa[name=preview]").toggleClass("active"); + } + + codeMirror.toggle(); + + var escHandle = function(event) { + if (event.shiftKey && event.keyCode === 27) { + _this.previewed(); + } + }; + + if (codeMirror.css("display") === "none") // 为了兼容Zepto,而不使用codeMirror.is(":hidden") + { + this.state.preview = true; + + if (this.state.fullscreen) { + preview.css("background", "#fff"); + } + + editor.find("." + this.classPrefix + "preview-close-btn").show().bind(editormd.mouseOrTouch("click", "touchend"), function(){ + _this.previewed(); + }); + + if (!settings.watch) + { + this.save(); + } + else + { + previewContainer.css("padding", ""); + } + + previewContainer.addClass(this.classPrefix + "preview-active"); + + preview.show().css({ + position : "", + top : 0, + width : editor.width(), + height : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height() + }); + + if (this.state.loaded) + { + $.proxy(settings.onpreviewing, this)(); + } + + $(window).bind("keyup", escHandle); + } + else + { + $(window).unbind("keyup", escHandle); + this.previewed(); + } + }, + + /** + * 显示编辑器部分,退出只预览HTML + * Exit preview html state + * + * @returns {editormd} 返回editormd的实例对象 + */ + + previewed : function() { + + var editor = this.editor; + var preview = this.preview; + var toolbar = this.toolbar; + var settings = this.settings; + var previewContainer = this.previewContainer; + var previewCloseBtn = editor.find("." + this.classPrefix + "preview-close-btn"); + + this.state.preview = false; + + this.codeMirror.show(); + + if (settings.toolbar) { + toolbar.show(); + } + + preview[(settings.watch) ? "show" : "hide"](); + + previewCloseBtn.hide().unbind(editormd.mouseOrTouch("click", "touchend")); + + previewContainer.removeClass(this.classPrefix + "preview-active"); + + if (settings.watch) + { + previewContainer.css("padding", "20px"); + } + + preview.css({ + background : null, + position : "absolute", + width : editor.width() / 2, + height : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height() - toolbar.height(), + top : (settings.toolbar) ? toolbar.height() : 0 + }); + + if (this.state.loaded) + { + $.proxy(settings.onpreviewed, this)(); + } + + return this; + }, + + /** + * 编辑器全屏显示 + * Fullscreen show + * + * @returns {editormd} 返回editormd的实例对象 + */ + + fullscreen : function() { + + var _this = this; + var state = this.state; + var editor = this.editor; + var preview = this.preview; + var toolbar = this.toolbar; + var settings = this.settings; + var fullscreenClass = this.classPrefix + "fullscreen"; + + if (toolbar) { + toolbar.find(".fa[name=fullscreen]").parent().toggleClass("active"); + } + + var escHandle = function(event) { + if (!event.shiftKey && event.keyCode === 27) + { + if (state.fullscreen) + { + _this.fullscreenExit(); + } + } + }; + + if (!editor.hasClass(fullscreenClass)) + { + state.fullscreen = true; + + $("html,body").css("overflow", "hidden"); + + editor.css({ + width : $(window).width(), + height : $(window).height() + }).addClass(fullscreenClass); + + this.resize(); + + $.proxy(settings.onfullscreen, this)(); + + $(window).bind("keyup", escHandle); + } + else + { + $(window).unbind("keyup", escHandle); + this.fullscreenExit(); + } + + return this; + }, + + /** + * 编辑器退出全屏显示 + * Exit fullscreen state + * + * @returns {editormd} 返回editormd的实例对象 + */ + + fullscreenExit : function() { + + var editor = this.editor; + var settings = this.settings; + var toolbar = this.toolbar; + var fullscreenClass = this.classPrefix + "fullscreen"; + + this.state.fullscreen = false; + + if (toolbar) { + toolbar.find(".fa[name=fullscreen]").parent().removeClass("active"); + } + + $("html,body").css("overflow", ""); + + editor.css({ + width : editor.data("oldWidth"), + height : editor.data("oldHeight") + }).removeClass(fullscreenClass); + + this.resize(); + + $.proxy(settings.onfullscreenExit, this)(); + + return this; + }, + + /** + * 加载并执行插件 + * Load and execute the plugin + * + * @param {String} name plugin name / function name + * @param {String} path plugin load path + * @returns {editormd} 返回editormd的实例对象 + */ + + executePlugin : function(name, path) { + + var _this = this; + var cm = this.cm; + var settings = this.settings; + + path = settings.pluginPath + path; + + if (typeof define === "function") + { + if (typeof this[name] === "undefined") + { + alert("Error: " + name + " plugin is not found, you are not load this plugin."); + + return this; + } + + this[name](cm); + + return this; + } + + if ($.inArray(path, editormd.loadFiles.plugin) < 0) + { + editormd.loadPlugin(path, function() { + editormd.loadPlugins[name] = _this[name]; + _this[name](cm); + }); + } + else + { + $.proxy(editormd.loadPlugins[name], this)(cm); + } + + return this; + }, + + /** + * 搜索替换 + * Search & replace + * + * @param {String} command CodeMirror serach commands, "find, fintNext, fintPrev, clearSearch, replace, replaceAll" + * @returns {editormd} return this + */ + + search : function(command) { + var settings = this.settings; + + if (!settings.searchReplace) + { + alert("Error: settings.searchReplace == false"); + return this; + } + + if (!settings.readOnly) + { + this.cm.execCommand(command || "find"); + } + + return this; + }, + + searchReplace : function() { + this.search("replace"); + + return this; + }, + + searchReplaceAll : function() { + this.search("replaceAll"); + + return this; + } + }; + + editormd.fn.init.prototype = editormd.fn; + + /** + * 锁屏 + * lock screen when dialog opening + * + * @returns {void} + */ + + editormd.dialogLockScreen = function() { + var settings = this.settings || {dialogLockScreen : true}; + + if (settings.dialogLockScreen) + { + $("html,body").css("overflow", "hidden"); + this.resize(); + } + }; + + /** + * 显示透明背景层 + * Display mask layer when dialog opening + * + * @param {Object} dialog dialog jQuery object + * @returns {void} + */ + + editormd.dialogShowMask = function(dialog) { + var editor = this.editor; + var settings = this.settings || {dialogShowMask : true}; + + dialog.css({ + top : ($(window).height() - dialog.height()) / 2 + "px", + left : ($(window).width() - dialog.width()) / 2 + "px" + }); + + if (settings.dialogShowMask) { + editor.children("." + this.classPrefix + "mask").css("z-index", parseInt(dialog.css("z-index")) - 1).show(); + } + }; + + editormd.toolbarHandlers = { + undo : function() { + this.cm.undo(); + }, + + redo : function() { + this.cm.redo(); + }, + + bold : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("**" + selection + "**"); + + if(selection === "") { + cm.setCursor(cursor.line, cursor.ch + 2); + } + }, + + del : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("~~" + selection + "~~"); + + if(selection === "") { + cm.setCursor(cursor.line, cursor.ch + 2); + } + }, + + italic : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("*" + selection + "*"); + + if(selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + + quote : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("> " + selection); + cm.setCursor(cursor.line, cursor.ch + 2); + } + else + { + cm.replaceSelection("> " + selection); + } + + //cm.replaceSelection("> " + selection); + //cm.setCursor(cursor.line, (selection === "") ? cursor.ch + 2 : cursor.ch + selection.length + 2); + }, + + ucfirst : function() { + var cm = this.cm; + var selection = cm.getSelection(); + var selections = cm.listSelections(); + + cm.replaceSelection(editormd.firstUpperCase(selection)); + cm.setSelections(selections); + }, + + ucwords : function() { + var cm = this.cm; + var selection = cm.getSelection(); + var selections = cm.listSelections(); + + cm.replaceSelection(editormd.wordsFirstUpperCase(selection)); + cm.setSelections(selections); + }, + + uppercase : function() { + var cm = this.cm; + var selection = cm.getSelection(); + var selections = cm.listSelections(); + + cm.replaceSelection(selection.toUpperCase()); + cm.setSelections(selections); + }, + + lowercase : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + var selections = cm.listSelections(); + + cm.replaceSelection(selection.toLowerCase()); + cm.setSelections(selections); + }, + + h1 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("# " + selection); + cm.setCursor(cursor.line, cursor.ch + 2); + } + else + { + cm.replaceSelection("# " + selection); + } + }, + + h2 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("## " + selection); + cm.setCursor(cursor.line, cursor.ch + 3); + } + else + { + cm.replaceSelection("## " + selection); + } + }, + + h3 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("### " + selection); + cm.setCursor(cursor.line, cursor.ch + 4); + } + else + { + cm.replaceSelection("### " + selection); + } + }, + + h4 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("#### " + selection); + cm.setCursor(cursor.line, cursor.ch + 5); + } + else + { + cm.replaceSelection("#### " + selection); + } + }, + + h5 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("##### " + selection); + cm.setCursor(cursor.line, cursor.ch + 6); + } + else + { + cm.replaceSelection("##### " + selection); + } + }, + + h6 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("###### " + selection); + cm.setCursor(cursor.line, cursor.ch + 7); + } + else + { + cm.replaceSelection("###### " + selection); + } + }, + + "list-ul" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (selection === "") + { + cm.replaceSelection("- " + selection); + } + else + { + var selectionText = selection.split("\n"); + + for (var i = 0, len = selectionText.length; i < len; i++) + { + selectionText[i] = (selectionText[i] === "") ? "" : "- " + selectionText[i]; + } + + cm.replaceSelection(selectionText.join("\n")); + } + }, + + "list-ol" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if(selection === "") + { + cm.replaceSelection("1. " + selection); + } + else + { + var selectionText = selection.split("\n"); + + for (var i = 0, len = selectionText.length; i < len; i++) + { + selectionText[i] = (selectionText[i] === "") ? "" : (i+1) + ". " + selectionText[i]; + } + + cm.replaceSelection(selectionText.join("\n")); + } + }, + + hr : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection(((cursor.ch !== 0) ? "\n\n" : "\n") + "------------\n\n"); + }, + + tex : function() { + if (!this.settings.tex) + { + alert("settings.tex === false"); + return this; + } + + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("$$" + selection + "$$"); + + if(selection === "") { + cm.setCursor(cursor.line, cursor.ch + 2); + } + }, + + link : function() { + this.executePlugin("linkDialog", "link-dialog/link-dialog"); + }, + + "reference-link" : function() { + this.executePlugin("referenceLinkDialog", "reference-link-dialog/reference-link-dialog"); + }, + + pagebreak : function() { + if (!this.settings.pageBreak) + { + alert("settings.pageBreak === false"); + return this; + } + + var cm = this.cm; + var selection = cm.getSelection(); + + cm.replaceSelection("\r\n[========]\r\n"); + }, + + image : function() { + this.executePlugin("imageDialog", "image-dialog/image-dialog"); + }, + + code : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("`" + selection + "`"); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + + "code-block" : function() { + this.executePlugin("codeBlockDialog", "code-block-dialog/code-block-dialog"); + }, + + "preformatted-text" : function() { + this.executePlugin("preformattedTextDialog", "preformatted-text-dialog/preformatted-text-dialog"); + }, + + table : function() { + this.executePlugin("tableDialog", "table-dialog/table-dialog"); + }, + + datetime : function() { + var cm = this.cm; + var selection = cm.getSelection(); + var date = new Date(); + var langName = this.settings.lang.name; + var datefmt = editormd.dateFormat() + " " + editormd.dateFormat((langName === "zh-cn" || langName === "zh-tw") ? "cn-week-day" : "week-day"); + + cm.replaceSelection(datefmt); + }, + + emoji : function() { + this.executePlugin("emojiDialog", "emoji-dialog/emoji-dialog"); + }, + + "html-entities" : function() { + this.executePlugin("htmlEntitiesDialog", "html-entities-dialog/html-entities-dialog"); + }, + + "goto-line" : function() { + this.executePlugin("gotoLineDialog", "goto-line-dialog/goto-line-dialog"); + }, + + watch : function() { + this[this.settings.watch ? "unwatch" : "watch"](); + }, + + preview : function() { + this.previewing(); + }, + + fullscreen : function() { + this.fullscreen(); + }, + + clear : function() { + this.clear(); + }, + + search : function() { + this.search(); + }, + + help : function() { + this.executePlugin("helpDialog", "help-dialog/help-dialog"); + }, + + info : function() { + this.showInfoDialog(); + } + }; + + editormd.keyMaps = { + "Ctrl-1" : "h1", + "Ctrl-2" : "h2", + "Ctrl-3" : "h3", + "Ctrl-4" : "h4", + "Ctrl-5" : "h5", + "Ctrl-6" : "h6", + "Ctrl-B" : "bold", // if this is string == editormd.toolbarHandlers.xxxx + "Ctrl-D" : "datetime", + + "Ctrl-E" : function() { // emoji + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (!this.settings.emoji) + { + alert("Error: settings.emoji == false"); + return ; + } + + cm.replaceSelection(":" + selection + ":"); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + "Ctrl-Alt-G" : "goto-line", + "Ctrl-H" : "hr", + "Ctrl-I" : "italic", + "Ctrl-K" : "code", + + "Ctrl-L" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + var title = (selection === "") ? "" : " \""+selection+"\""; + + cm.replaceSelection("[" + selection + "]("+title+")"); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + "Ctrl-U" : "list-ul", + + "Shift-Ctrl-A" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (!this.settings.atLink) + { + alert("Error: settings.atLink == false"); + return ; + } + + cm.replaceSelection("@" + selection); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + + "Shift-Ctrl-C" : "code", + "Shift-Ctrl-Q" : "quote", + "Shift-Ctrl-S" : "del", + "Shift-Ctrl-K" : "tex", // KaTeX + + "Shift-Alt-C" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection(["```", selection, "```"].join("\n")); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 3); + } + }, + + "Shift-Ctrl-Alt-C" : "code-block", + "Shift-Ctrl-H" : "html-entities", + "Shift-Alt-H" : "help", + "Shift-Ctrl-E" : "emoji", + "Shift-Ctrl-U" : "uppercase", + "Shift-Alt-U" : "ucwords", + "Shift-Ctrl-Alt-U" : "ucfirst", + "Shift-Alt-L" : "lowercase", + + "Shift-Ctrl-I" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + var title = (selection === "") ? "" : " \""+selection+"\""; + + cm.replaceSelection("![" + selection + "]("+title+")"); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 4); + } + }, + + "Shift-Ctrl-Alt-I" : "image", + "Shift-Ctrl-L" : "link", + "Shift-Ctrl-O" : "list-ol", + "Shift-Ctrl-P" : "preformatted-text", + "Shift-Ctrl-T" : "table", + "Shift-Alt-P" : "pagebreak", + "F9" : "watch", + "F10" : "preview", + "F11" : "fullscreen", + }; + + /** + * 清除字符串两边的空格 + * Clear the space of strings both sides. + * + * @param {String} str string + * @returns {String} trimed string + */ + + var trim = function(str) { + return (!String.prototype.trim) ? str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") : str.trim(); + }; + + editormd.trim = trim; + + /** + * 所有单词首字母大写 + * Words first to uppercase + * + * @param {String} str string + * @returns {String} string + */ + + var ucwords = function (str) { + return str.toLowerCase().replace(/\b(\w)|\s(\w)/g, function($1) { + return $1.toUpperCase(); + }); + }; + + editormd.ucwords = editormd.wordsFirstUpperCase = ucwords; + + /** + * 字符串首字母大写 + * Only string first char to uppercase + * + * @param {String} str string + * @returns {String} string + */ + + var firstUpperCase = function(str) { + return str.toLowerCase().replace(/\b(\w)/, function($1){ + return $1.toUpperCase(); + }); + }; + + var ucfirst = firstUpperCase; + + editormd.firstUpperCase = editormd.ucfirst = firstUpperCase; + + editormd.urls = { + atLinkBase : "https://github.com/" + }; + + editormd.regexs = { + atLink : /@(\w+)/g, + email : /(\w+)@(\w+)\.(\w+)\.?(\w+)?/g, + emailLink : /(mailto:)?([\w\.\_]+)@(\w+)\.(\w+)\.?(\w+)?/g, + emoji : /:([\w\+-]+):/g, + emojiDatetime : /(\d{2}:\d{2}:\d{2})/g, + twemoji : /:(tw-([\w]+)-?(\w+)?):/g, + fontAwesome : /:(fa-([\w]+)(-(\w+)){0,}):/g, + editormdLogo : /:(editormd-logo-?(\w+)?):/g, + pageBreak : /^\[[=]{8,}\]$/ + }; + + // Emoji graphics files url path + editormd.emoji = { + path : "http://www.emoji-cheat-sheet.com/graphics/emojis/", + ext : ".png" + }; + + // Twitter Emoji (Twemoji) graphics files url path + editormd.twemoji = { + path : "http://twemoji.maxcdn.com/36x36/", + ext : ".png" + }; + + /** + * 自定义marked的解析器 + * Custom Marked renderer rules + * + * @param {Array} markdownToC 传入用于接收TOC的数组 + * @returns {Renderer} markedRenderer 返回marked的Renderer自定义对象 + */ + + editormd.markedRenderer = function(markdownToC, options) { + var defaults = { + toc : true, // Table of contents + tocm : false, + tocStartLevel : 1, // Said from H1 to create ToC + pageBreak : true, + atLink : true, // for @link + emailLink : true, // for mail address auto link + taskList : false, // Enable Github Flavored Markdown task lists + emoji : false, // :emoji: , Support Twemoji, fontAwesome, Editor.md logo emojis. + tex : false, // TeX(LaTeX), based on KaTeX + flowChart : false, // flowChart.js only support IE9+ + sequenceDiagram : false, // sequenceDiagram.js only support IE9+ + }; + + var settings = $.extend(defaults, options || {}); + var marked = editormd.$marked; + var markedRenderer = new marked.Renderer(); + markdownToC = markdownToC || []; + + var regexs = editormd.regexs; + var atLinkReg = regexs.atLink; + var emojiReg = regexs.emoji; + var emailReg = regexs.email; + var emailLinkReg = regexs.emailLink; + var twemojiReg = regexs.twemoji; + var faIconReg = regexs.fontAwesome; + var editormdLogoReg = regexs.editormdLogo; + var pageBreakReg = regexs.pageBreak; + + markedRenderer.emoji = function(text) { + + text = text.replace(editormd.regexs.emojiDatetime, function($1) { + return $1.replace(/:/g, ":"); + }); + + var matchs = text.match(emojiReg); + + if (!matchs || !settings.emoji) { + return text; + } + + for (var i = 0, len = matchs.length; i < len; i++) + { + if (matchs[i] === ":+1:") { + matchs[i] = ":\\+1:"; + } + + text = text.replace(new RegExp(matchs[i]), function($1, $2){ + var faMatchs = $1.match(faIconReg); + var name = $1.replace(/:/g, ""); + + if (faMatchs) + { + for (var fa = 0, len1 = faMatchs.length; fa < len1; fa++) + { + var faName = faMatchs[fa].replace(/:/g, ""); + + return ""; + } + } + else + { + var emdlogoMathcs = $1.match(editormdLogoReg); + var twemojiMatchs = $1.match(twemojiReg); + + if (emdlogoMathcs) + { + for (var x = 0, len2 = emdlogoMathcs.length; x < len2; x++) + { + var logoName = emdlogoMathcs[x].replace(/:/g, ""); + return ""; + } + } + else if (twemojiMatchs) + { + for (var t = 0, len3 = twemojiMatchs.length; t < len3; t++) + { + var twe = twemojiMatchs[t].replace(/:/g, "").replace("tw-", ""); + return "\"twemoji-""; + } + } + else + { + var src = (name === "+1") ? "plus1" : name; + src = (src === "black_large_square") ? "black_square" : src; + src = (src === "moon") ? "waxing_gibbous_moon" : src; + + return "\":""; + } + } + }); + } + + return text; + }; + + markedRenderer.atLink = function(text) { + + if (atLinkReg.test(text)) + { + if (settings.atLink) + { + text = text.replace(emailReg, function($1, $2, $3, $4) { + return $1.replace(/@/g, "_#_@_#_"); + }); + + text = text.replace(atLinkReg, function($1, $2) { + return "" + $1 + ""; + }).replace(/_#_@_#_/g, "@"); + } + + if (settings.emailLink) + { + text = text.replace(emailLinkReg, function($1, $2, $3, $4, $5) { + return (!$2 && $.inArray($5, "jpg|jpeg|png|gif|webp|ico|icon|pdf".split("|")) < 0) ? ""+$1+"" : $1; + }); + } + + return text; + } + + return text; + }; + + markedRenderer.link = function (href, title, text) { + + if (this.options.sanitize) { + try { + var prot = decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase(); + } catch(e) { + return ""; + } + + if (prot.indexOf("javascript:") === 0) { + return ""; + } + } + + var out = "" + text.replace(/@/g, "@") + ""; + } + + if (title) { + out += " title=\"" + title + "\""; + } + + out += ">" + text + ""; + + return out; + }; + + markedRenderer.heading = function(text, level, raw) { + + var linkText = text; + var hasLinkReg = /\s*\]*)\>(.*)\<\/a\>\s*/; + var getLinkTextReg = /\s*\]+)\>([^\>]*)\<\/a\>\s*/g; + + if (hasLinkReg.test(text)) + { + var tempText = []; + text = text.split(/\]+)\>([^\>]*)\<\/a\>/); + + for (var i = 0, len = text.length; i < len; i++) + { + tempText.push(text[i].replace(/\s*href\=\"(.*)\"\s*/g, "")); + } + + text = tempText.join(" "); + } + + text = trim(text); + + var escapedText = text.toLowerCase().replace(/[^\w]+/g, "-"); + var toc = { + text : text, + level : level, + slug : escapedText + }; + + var isChinese = /^[\u4e00-\u9fa5]+$/.test(text); + var id = (isChinese) ? escape(text).replace(/\%/g, "") : text.toLowerCase().replace(/[^\w]+/g, "-"); + + markdownToC.push(toc); + + var headingHTML = ""; + + headingHTML += ""; + headingHTML += ""; + headingHTML += (hasLinkReg) ? this.atLink(this.emoji(linkText)) : this.atLink(this.emoji(text)); + headingHTML += ""; + + return headingHTML; + }; + + markedRenderer.pageBreak = function(text) { + if (pageBreakReg.test(text) && settings.pageBreak) + { + text = "
      "; + } + + return text; + }; + + markedRenderer.paragraph = function(text) { + var isTeXInline = /\$\$(.*)\$\$/g.test(text); + var isTeXLine = /^\$\$(.*)\$\$$/.test(text); + var isTeXAddClass = (isTeXLine) ? " class=\"" + editormd.classNames.tex + "\"" : ""; + var isToC = (settings.tocm) ? /^(\[TOC\]|\[TOCM\])$/.test(text) : /^\[TOC\]$/.test(text); + var isToCMenu = /^\[TOCM\]$/.test(text); + + if (!isTeXLine && isTeXInline) + { + text = text.replace(/(\$\$([^\$]*)\$\$)+/g, function($1, $2) { + return "" + $2.replace(/\$/g, "") + ""; + }); + } + else + { + text = (isTeXLine) ? text.replace(/\$/g, "") : text; + } + + var tocHTML = "
      " + text + "
      "; + + return (isToC) ? ( (isToCMenu) ? "
      " + tocHTML + "

      " : tocHTML ) + : ( (pageBreakReg.test(text)) ? this.pageBreak(text) : "" + this.atLink(this.emoji(text)) + "

      \n" ); + }; + + markedRenderer.code = function (code, lang, escaped) { + + if (lang === "seq" || lang === "sequence") + { + return "
      " + code + "
      "; + } + else if ( lang === "flow") + { + return "
      " + code + "
      "; + } + else if ( lang === "math" || lang === "latex" || lang === "katex") + { + return "

      " + code + "

      "; + } + else + { + + return marked.Renderer.prototype.code.apply(this, arguments); + } + }; + + markedRenderer.tablecell = function(content, flags) { + var type = (flags.header) ? "th" : "td"; + var tag = (flags.align) ? "<" + type +" style=\"text-align:" + flags.align + "\">" : "<" + type + ">"; + + return tag + this.atLink(this.emoji(content)) + "\n"; + }; + + markedRenderer.listitem = function(text) { + if (settings.taskList && /^\s*\[[x\s]\]\s*/.test(text)) + { + text = text.replace(/^\s*\[\s\]\s*/, " ") + .replace(/^\s*\[x\]\s*/, " "); + + return "
    • " + this.atLink(this.emoji(text)) + "
    • "; + } + else + { + return "
    • " + this.atLink(this.emoji(text)) + "
    • "; + } + }; + + return markedRenderer; + }; + + /** + * + * 生成TOC(Table of Contents) + * Creating ToC (Table of Contents) + * + * @param {Array} toc 从marked获取的TOC数组列表 + * @param {Element} container 插入TOC的容器元素 + * @param {Integer} startLevel Hx 起始层级 + * @returns {Object} tocContainer 返回ToC列表容器层的jQuery对象元素 + */ + + editormd.markdownToCRenderer = function(toc, container, tocDropdown, startLevel) { + + var html = ""; + var lastLevel = 0; + var classPrefix = this.classPrefix; + + startLevel = startLevel || 1; + + for (var i = 0, len = toc.length; i < len; i++) + { + var text = toc[i].text; + var level = toc[i].level; + + if (level < startLevel) { + continue; + } + + if (level > lastLevel) + { + html += ""; + } + else if (level < lastLevel) + { + html += (new Array(lastLevel - level + 2)).join(""); + } + else + { + html += ""; + } + + html += "
    • " + text + "
        "; + lastLevel = level; + } + + var tocContainer = container.find(".markdown-toc"); + + if ((tocContainer.length < 1 && container.attr("previewContainer") === "false")) + { + var tocHTML = "
        "; + + tocHTML = (tocDropdown) ? "
        " + tocHTML + "
        " : tocHTML; + + container.html(tocHTML); + + tocContainer = container.find(".markdown-toc"); + } + + if (tocDropdown) + { + tocContainer.wrap("

        "); + } + + tocContainer.html("
          ").children(".markdown-toc-list").html(html.replace(/\r?\n?\\<\/ul\>/g, "")); + + return tocContainer; + }; + + /** + * + * 生成TOC下拉菜单 + * Creating ToC dropdown menu + * + * @param {Object} container 插入TOC的容器jQuery对象元素 + * @param {String} tocTitle ToC title + * @returns {Object} return toc-menu object + */ + + editormd.tocDropdownMenu = function(container, tocTitle) { + + tocTitle = tocTitle || "Table of Contents"; + + var zindex = 400; + var tocMenus = container.find("." + this.classPrefix + "toc-menu"); + + tocMenus.each(function() { + var $this = $(this); + var toc = $this.children(".markdown-toc"); + var icon = ""; + var btn = "" + icon + tocTitle + ""; + var menu = toc.children("ul"); + var list = menu.find("li"); + + toc.append(btn); + + list.first().before("
        • " + tocTitle + " " + icon + "

        • "); + + $this.mouseover(function(){ + menu.show(); + + list.each(function(){ + var li = $(this); + var ul = li.children("ul"); + + if (ul.html() === "") + { + ul.remove(); + } + + if (ul.length > 0 && ul.html() !== "") + { + var firstA = li.children("a").first(); + + if (firstA.children(".fa").length < 1) + { + firstA.append( $(icon).css({ float:"right", paddingTop:"4px" }) ); + } + } + + li.mouseover(function(){ + ul.css("z-index", zindex).show(); + zindex += 1; + }).mouseleave(function(){ + ul.hide(); + }); + }); + }).mouseleave(function(){ + menu.hide(); + }); + }); + + return tocMenus; + }; + + /** + * 简单地过滤指定的HTML标签 + * Filter custom html tags + * + * @param {String} html 要过滤HTML + * @param {String} filters 要过滤的标签 + * @returns {String} html 返回过滤的HTML + */ + + editormd.filterHTMLTags = function(html, filters) { + + if (typeof html !== "string") { + html = new String(html); + } + + if (typeof filters !== "string") { + return html; + } + + var expression = filters.split("|"); + var filterTags = expression[0].split(","); + var attrs = expression[1]; + + for (var i = 0, len = filterTags.length; i < len; i++) + { + var tag = filterTags[i]; + + html = html.replace(new RegExp("\<\s*" + tag + "\s*([^\>]*)\>([^\>]*)\<\s*\/" + tag + "\s*\>", "igm"), ""); + } + + //return html; + + if (typeof attrs !== "undefined") + { + var htmlTagRegex = /\<(\w+)\s*([^\>]*)\>([^\>]*)\<\/(\w+)\>/ig; + + if (attrs === "*") + { + html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) { + return "<" + $2 + ">" + $4 + ""; + }); + } + else if (attrs === "on*") + { + html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) { + var el = $("<" + $2 + ">" + $4 + ""); + var _attrs = $($1)[0].attributes; + var $attrs = {}; + + $.each(_attrs, function(i, e) { + if (e.nodeName !== '"') $attrs[e.nodeName] = e.nodeValue; + }); + + $.each($attrs, function(i) { + if (i.indexOf("on") === 0) { + delete $attrs[i]; + } + }); + + el.attr($attrs); + + var text = (typeof el[1] !== "undefined") ? $(el[1]).text() : ""; + + return el[0].outerHTML + text; + }); + } + else + { + html = html.replace(htmlTagRegex, function($1, $2, $3, $4) { + var filterAttrs = attrs.split(","); + var el = $($1); + el.html($4); + + $.each(filterAttrs, function(i) { + el.attr(filterAttrs[i], null); + }); + + return el[0].outerHTML; + }); + } + } + + return html; + }; + + /** + * 将Markdown文档解析为HTML用于前台显示 + * Parse Markdown to HTML for Font-end preview. + * + * @param {String} id 用于显示HTML的对象ID + * @param {Object} [options={}] 配置选项,可选 + * @returns {Object} div 返回jQuery对象元素 + */ + + editormd.markdownToHTML = function(id, options) { + var defaults = { + gfm : true, + toc : true, + tocm : false, + tocStartLevel : 1, + tocTitle : "目录", + tocDropdown : false, + tocContainer : "", + markdown : "", + markdownSourceCode : false, + htmlDecode : false, + autoLoadKaTeX : true, + pageBreak : true, + atLink : true, // for @link + emailLink : true, // for mail address auto link + tex : false, + taskList : false, // Github Flavored Markdown task lists + emoji : false, + flowChart : false, + sequenceDiagram : false, + previewCodeHighlight : true + }; + + editormd.$marked = marked; + + var div = $("#" + id); + var settings = div.settings = $.extend(true, defaults, options || {}); + var saveTo = div.find("textarea"); + + if (saveTo.length < 1) + { + div.append(""); + saveTo = div.find("textarea"); + } + + var markdownDoc = (settings.markdown === "") ? saveTo.val() : settings.markdown; + var markdownToC = []; + + var rendererOptions = { + toc : settings.toc, + tocm : settings.tocm, + tocStartLevel : settings.tocStartLevel, + taskList : settings.taskList, + emoji : settings.emoji, + tex : settings.tex, + pageBreak : settings.pageBreak, + atLink : settings.atLink, // for @link + emailLink : settings.emailLink, // for mail address auto link + flowChart : settings.flowChart, + sequenceDiagram : settings.sequenceDiagram, + previewCodeHighlight : settings.previewCodeHighlight, + }; + + var markedOptions = { + renderer : editormd.markedRenderer(markdownToC, rendererOptions), + gfm : settings.gfm, + tables : true, + breaks : true, + pedantic : false, + sanitize : (settings.htmlDecode) ? false : true, // 是否忽略HTML标签,即是否开启HTML标签解析,为了安全性,默认不开启 + smartLists : true, + smartypants : true + }; + + markdownDoc = new String(markdownDoc); + + var markdownParsed = marked(markdownDoc, markedOptions); + + markdownParsed = editormd.filterHTMLTags(markdownParsed, settings.htmlDecode); + + if (settings.markdownSourceCode) { + saveTo.text(markdownDoc); + } else { + saveTo.remove(); + } + + div.addClass("markdown-body " + this.classPrefix + "html-preview").append(markdownParsed); + + var tocContainer = (settings.tocContainer !== "") ? $(settings.tocContainer) : div; + + if (settings.tocContainer !== "") + { + tocContainer.attr("previewContainer", false); + } + + if (settings.toc) + { + div.tocContainer = this.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel); + + if (settings.tocDropdown || div.find("." + this.classPrefix + "toc-menu").length > 0) + { + this.tocDropdownMenu(div, settings.tocTitle); + } + + if (settings.tocContainer !== "") + { + div.find(".editormd-toc-menu, .editormd-markdown-toc").remove(); + } + } + + if (settings.previewCodeHighlight) + { + div.find("pre").addClass("prettyprint linenums"); + prettyPrint(); + } + + if (!editormd.isIE8) + { + if (settings.flowChart) { + div.find(".flowchart").flowChart(); + } + + if (settings.sequenceDiagram) { + div.find(".sequence-diagram").sequenceDiagram({theme: "simple"}); + } + } + + if (settings.tex) + { + var katexHandle = function() { + div.find("." + editormd.classNames.tex).each(function(){ + var tex = $(this); + katex.render(tex.html().replace(/</g, "<").replace(/>/g, ">"), tex[0]); + tex.find(".katex").css("font-size", "1.6em"); + }); + }; + + if (settings.autoLoadKaTeX && !editormd.$katex && !editormd.kaTeXLoaded) + { + this.loadKaTeX(function() { + editormd.$katex = katex; + editormd.kaTeXLoaded = true; + katexHandle(); + }); + } + else + { + katexHandle(); + } + } + + div.getMarkdown = function() { + return saveTo.val(); + }; + + return div; + }; + + // Editor.md themes, change toolbar themes etc. + // added @1.5.0 + editormd.themes = ["default", "dark"]; + + // Preview area themes + // added @1.5.0 + editormd.previewThemes = ["default", "dark"]; + + // CodeMirror / editor area themes + // @1.5.0 rename -> editorThemes, old version -> themes + editormd.editorThemes = [ + "default", "3024-day", "3024-night", + "ambiance", "ambiance-mobile", + "base16-dark", "base16-light", "blackboard", + "cobalt", + "eclipse", "elegant", "erlang-dark", + "lesser-dark", + "mbo", "mdn-like", "midnight", "monokai", + "neat", "neo", "night", + "paraiso-dark", "paraiso-light", "pastel-on-dark", + "rubyblue", + "solarized", + "the-matrix", "tomorrow-night-eighties", "twilight", + "vibrant-ink", + "xq-dark", "xq-light" + ]; + + editormd.loadPlugins = {}; + + editormd.loadFiles = { + js : [], + css : [], + plugin : [] + }; + + /** + * 动态加载Editor.md插件,但不立即执行 + * Load editor.md plugins + * + * @param {String} fileName 插件文件路径 + * @param {Function} [callback=function()] 加载成功后执行的回调函数 + * @param {String} [into="head"] 嵌入页面的位置 + */ + + editormd.loadPlugin = function(fileName, callback, into) { + callback = callback || function() {}; + + this.loadScript(fileName, function() { + editormd.loadFiles.plugin.push(fileName); + callback(); + }, into); + }; + + /** + * 动态加载CSS文件的方法 + * Load css file method + * + * @param {String} fileName CSS文件名 + * @param {Function} [callback=function()] 加载成功后执行的回调函数 + * @param {String} [into="head"] 嵌入页面的位置 + */ + + editormd.loadCSS = function(fileName, callback, into) { + into = into || "head"; + callback = callback || function() {}; + + var css = document.createElement("link"); + css.type = "text/css"; + css.rel = "stylesheet"; + css.onload = css.onreadystatechange = function() { + editormd.loadFiles.css.push(fileName); + callback(); + }; + + css.href = fileName + ".css"; + + if(into === "head") { + document.getElementsByTagName("head")[0].appendChild(css); + } else { + document.body.appendChild(css); + } + }; + + editormd.isIE = (navigator.appName == "Microsoft Internet Explorer"); + editormd.isIE8 = (editormd.isIE && navigator.appVersion.match(/8./i) == "8."); + + /** + * 动态加载JS文件的方法 + * Load javascript file method + * + * @param {String} fileName JS文件名 + * @param {Function} [callback=function()] 加载成功后执行的回调函数 + * @param {String} [into="head"] 嵌入页面的位置 + */ + + editormd.loadScript = function(fileName, callback, into) { + + into = into || "head"; + callback = callback || function() {}; + + var script = null; + script = document.createElement("script"); + script.id = fileName.replace(/[\./]+/g, "-"); + script.type = "text/javascript"; + script.src = fileName + ".js"; + + if (editormd.isIE8) + { + script.onreadystatechange = function() { + if(script.readyState) + { + if (script.readyState === "loaded" || script.readyState === "complete") + { + script.onreadystatechange = null; + editormd.loadFiles.js.push(fileName); + callback(); + } + } + }; + } + else + { + script.onload = function() { + editormd.loadFiles.js.push(fileName); + callback(); + }; + } + + if (into === "head") { + document.getElementsByTagName("head")[0].appendChild(script); + } else { + document.body.appendChild(script); + } + }; + + // 使用国外的CDN,加载速度有时会很慢,或者自定义URL + // You can custom KaTeX load url. + editormd.katexURL = { + css : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min", + js : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min" + }; + + editormd.kaTeXLoaded = false; + + /** + * 加载KaTeX文件 + * load KaTeX files + * + * @param {Function} [callback=function()] 加载成功后执行的回调函数 + */ + + editormd.loadKaTeX = function (callback) { + editormd.loadCSS(editormd.katexURL.css, function(){ + editormd.loadScript(editormd.katexURL.js, callback || function(){}); + }); + }; + + /** + * 锁屏 + * lock screen + * + * @param {Boolean} lock Boolean 布尔值,是否锁屏 + * @returns {void} + */ + + editormd.lockScreen = function(lock) { + $("html,body").css("overflow", (lock) ? "hidden" : ""); + }; + + /** + * 动态创建对话框 + * Creating custom dialogs + * + * @param {Object} options 配置项键值对 Key/Value + * @returns {dialog} 返回创建的dialog的jQuery实例对象 + */ + + editormd.createDialog = function(options) { + var defaults = { + name : "", + width : 420, + height: 240, + title : "", + drag : true, + closed : true, + content : "", + mask : true, + maskStyle : { + backgroundColor : "#fff", + opacity : 0.1 + }, + lockScreen : true, + footer : true, + buttons : false + }; + + options = $.extend(true, defaults, options); + + var $this = this; + var editor = this.editor; + var classPrefix = editormd.classPrefix; + var guid = (new Date()).getTime(); + var dialogName = ( (options.name === "") ? classPrefix + "dialog-" + guid : options.name); + var mouseOrTouch = editormd.mouseOrTouch; + + var html = "
          "; + + if (options.title !== "") + { + html += "
          "; + html += "" + options.title + ""; + html += "
          "; + } + + if (options.closed) + { + html += ""; + } + + html += "
          " + options.content; + + if (options.footer || typeof options.footer === "string") + { + html += "
          " + ( (typeof options.footer === "boolean") ? "" : options.footer) + "
          "; + } + + html += "
          "; + + html += "
          "; + html += "
          "; + html += "
          "; + + editor.append(html); + + var dialog = editor.find("." + dialogName); + + dialog.lockScreen = function(lock) { + if (options.lockScreen) + { + $("html,body").css("overflow", (lock) ? "hidden" : ""); + $this.resize(); + } + + return dialog; + }; + + dialog.showMask = function() { + if (options.mask) + { + editor.find("." + classPrefix + "mask").css(options.maskStyle).css("z-index", editormd.dialogZindex - 1).show(); + } + return dialog; + }; + + dialog.hideMask = function() { + if (options.mask) + { + editor.find("." + classPrefix + "mask").hide(); + } + + return dialog; + }; + + dialog.loading = function(show) { + var loading = dialog.find("." + classPrefix + "dialog-mask"); + loading[(show) ? "show" : "hide"](); + + return dialog; + }; + + dialog.lockScreen(true).showMask(); + + dialog.show().css({ + zIndex : editormd.dialogZindex, + border : (editormd.isIE8) ? "1px solid #ddd" : "", + width : (typeof options.width === "number") ? options.width + "px" : options.width, + height : (typeof options.height === "number") ? options.height + "px" : options.height + }); + + var dialogPosition = function(){ + dialog.css({ + top : ($(window).height() - dialog.height()) / 2 + "px", + left : ($(window).width() - dialog.width()) / 2 + "px" + }); + }; + + dialogPosition(); + + $(window).resize(dialogPosition); + + dialog.children("." + classPrefix + "dialog-close").bind(mouseOrTouch("click", "touchend"), function() { + dialog.hide().lockScreen(false).hideMask(); + }); + + if (typeof options.buttons === "object") + { + var footer = dialog.footer = dialog.find("." + classPrefix + "dialog-footer"); + + for (var key in options.buttons) + { + var btn = options.buttons[key]; + var btnClassName = classPrefix + key + "-btn"; + + footer.append(""); + btn[1] = $.proxy(btn[1], dialog); + footer.children("." + btnClassName).bind(mouseOrTouch("click", "touchend"), btn[1]); + } + } + + if (options.title !== "" && options.drag) + { + var posX, posY; + var dialogHeader = dialog.children("." + classPrefix + "dialog-header"); + + if (!options.mask) { + dialogHeader.bind(mouseOrTouch("click", "touchend"), function(){ + editormd.dialogZindex += 2; + dialog.css("z-index", editormd.dialogZindex); + }); + } + + dialogHeader.mousedown(function(e) { + e = e || window.event; //IE + posX = e.clientX - parseInt(dialog[0].style.left); + posY = e.clientY - parseInt(dialog[0].style.top); + + document.onmousemove = moveAction; + }); + + var userCanSelect = function (obj) { + obj.removeClass(classPrefix + "user-unselect").off("selectstart"); + }; + + var userUnselect = function (obj) { + obj.addClass(classPrefix + "user-unselect").on("selectstart", function(event) { // selectstart for IE + return false; + }); + }; + + var moveAction = function (e) { + e = e || window.event; //IE + + var left, top, nowLeft = parseInt(dialog[0].style.left), nowTop = parseInt(dialog[0].style.top); + + if( nowLeft >= 0 ) { + if( nowLeft + dialog.width() <= $(window).width()) { + left = e.clientX - posX; + } else { + left = $(window).width() - dialog.width(); + document.onmousemove = null; + } + } else { + left = 0; + document.onmousemove = null; + } + + if( nowTop >= 0 ) { + top = e.clientY - posY; + } else { + top = 0; + document.onmousemove = null; + } + + + document.onselectstart = function() { + return false; + }; + + userUnselect($("body")); + userUnselect(dialog); + dialog[0].style.left = left + "px"; + dialog[0].style.top = top + "px"; + }; + + document.onmouseup = function() { + userCanSelect($("body")); + userCanSelect(dialog); + + document.onselectstart = null; + document.onmousemove = null; + }; + + dialogHeader.touchDraggable = function() { + var offset = null; + var start = function(e) { + var orig = e.originalEvent; + var pos = $(this).parent().position(); + + offset = { + x : orig.changedTouches[0].pageX - pos.left, + y : orig.changedTouches[0].pageY - pos.top + }; + }; + + var move = function(e) { + e.preventDefault(); + var orig = e.originalEvent; + + $(this).parent().css({ + top : orig.changedTouches[0].pageY - offset.y, + left : orig.changedTouches[0].pageX - offset.x + }); + }; + + this.bind("touchstart", start).bind("touchmove", move); + }; + + dialogHeader.touchDraggable(); + } + + editormd.dialogZindex += 2; + + return dialog; + }; + + /** + * 鼠标和触摸事件的判断/选择方法 + * MouseEvent or TouchEvent type switch + * + * @param {String} [mouseEventType="click"] 供选择的鼠标事件 + * @param {String} [touchEventType="touchend"] 供选择的触摸事件 + * @returns {String} EventType 返回事件类型名称 + */ + + editormd.mouseOrTouch = function(mouseEventType, touchEventType) { + mouseEventType = mouseEventType || "click"; + touchEventType = touchEventType || "touchend"; + + var eventType = mouseEventType; + + try { + document.createEvent("TouchEvent"); + eventType = touchEventType; + } catch(e) {} + + return eventType; + }; + + /** + * 日期时间的格式化方法 + * Datetime format method + * + * @param {String} [format=""] 日期时间的格式,类似PHP的格式 + * @returns {String} datefmt 返回格式化后的日期时间字符串 + */ + + editormd.dateFormat = function(format) { + format = format || ""; + + var addZero = function(d) { + return (d < 10) ? "0" + d : d; + }; + + var date = new Date(); + var year = date.getFullYear(); + var year2 = year.toString().slice(2, 4); + var month = addZero(date.getMonth() + 1); + var day = addZero(date.getDate()); + var weekDay = date.getDay(); + var hour = addZero(date.getHours()); + var min = addZero(date.getMinutes()); + var second = addZero(date.getSeconds()); + var ms = addZero(date.getMilliseconds()); + var datefmt = ""; + + var ymd = year2 + "-" + month + "-" + day; + var fymd = year + "-" + month + "-" + day; + var hms = hour + ":" + min + ":" + second; + + switch (format) + { + case "UNIX Time" : + datefmt = date.getTime(); + break; + + case "UTC" : + datefmt = date.toUTCString(); + break; + + case "yy" : + datefmt = year2; + break; + + case "year" : + case "yyyy" : + datefmt = year; + break; + + case "month" : + case "mm" : + datefmt = month; + break; + + case "cn-week-day" : + case "cn-wd" : + var cnWeekDays = ["日", "一", "二", "三", "四", "五", "六"]; + datefmt = "星期" + cnWeekDays[weekDay]; + break; + + case "week-day" : + case "wd" : + var weekDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; + datefmt = weekDays[weekDay]; + break; + + case "day" : + case "dd" : + datefmt = day; + break; + + case "hour" : + case "hh" : + datefmt = hour; + break; + + case "min" : + case "ii" : + datefmt = min; + break; + + case "second" : + case "ss" : + datefmt = second; + break; + + case "ms" : + datefmt = ms; + break; + + case "yy-mm-dd" : + datefmt = ymd; + break; + + case "yyyy-mm-dd" : + datefmt = fymd; + break; + + case "yyyy-mm-dd h:i:s ms" : + case "full + ms" : + datefmt = fymd + " " + hms + " " + ms; + break; + + case "full" : + case "yyyy-mm-dd h:i:s" : + default: + datefmt = fymd + " " + hms; + break; + } + + return datefmt; + }; + + return editormd; + +})); diff --git a/src/main/resources/static/admin/editormd/editormd.amd.min.js b/src/main/resources/static/admin/editormd/editormd.amd.min.js new file mode 100755 index 00000000..23013692 --- /dev/null +++ b/src/main/resources/static/admin/editormd/editormd.amd.min.js @@ -0,0 +1,4 @@ +/*! Editor.md v1.5.0 | editormd.amd.min.js | Open source online markdown editor. | MIT License | By: Pandao | https://github.com/pandao/editor.md | 2015-06-09 */ +!function(e){"use strict";if("function"==typeof require&&"object"==typeof exports&&"object"==typeof module)module.exports=e;else if("function"==typeof define)if(define.amd){var t="codemirror/mode/",i="codemirror/addon/",o=["jquery","marked","prettify","katex","raphael","underscore","flowchart","jqueryflowchart","sequenceDiagram","codemirror/lib/codemirror",t+"css/css",t+"sass/sass",t+"shell/shell",t+"sql/sql",t+"clike/clike",t+"php/php",t+"xml/xml",t+"markdown/markdown",t+"javascript/javascript",t+"htmlmixed/htmlmixed",t+"gfm/gfm",t+"http/http",t+"go/go",t+"dart/dart",t+"coffeescript/coffeescript",t+"nginx/nginx",t+"python/python",t+"perl/perl",t+"lua/lua",t+"r/r",t+"ruby/ruby",t+"rst/rst",t+"smartymixed/smartymixed",t+"vb/vb",t+"vbscript/vbscript",t+"velocity/velocity",t+"xquery/xquery",t+"yaml/yaml",t+"erlang/erlang",t+"jade/jade",i+"edit/trailingspace",i+"dialog/dialog",i+"search/searchcursor",i+"search/search",i+"scroll/annotatescrollbar",i+"search/matchesonscrollbar",i+"display/placeholder",i+"edit/closetag",i+"fold/foldcode",i+"fold/foldgutter",i+"fold/indent-fold",i+"fold/brace-fold",i+"fold/xml-fold",i+"fold/markdown-fold",i+"fold/comment-fold",i+"mode/overlay",i+"selection/active-line",i+"edit/closebrackets",i+"display/fullscreen",i+"search/match-highlighter"];define(o,e)}else define(["jquery"],e);else window.editormd=e()}(function(){"function"==typeof define&&define.amd&&(e=arguments[0],marked=arguments[1],prettify=arguments[2],katex=arguments[3],Raphael=arguments[4],_=arguments[5],flowchart=arguments[6],CodeMirror=arguments[9]);var e="undefined"!=typeof jQuery?jQuery:Zepto;if("undefined"!=typeof e){var t=function(e,i){return new t.fn.init(e,i)};t.title=t.$name="Editor.md",t.version="1.5.0",t.homePage="https://pandao.github.io/editor.md/",t.classPrefix="editormd-",t.toolbarModes={full:["undo","redo","|","bold","del","italic","quote","ucwords","uppercase","lowercase","|","h1","h2","h3","h4","h5","h6","|","list-ul","list-ol","hr","|","link","reference-link","image","code","preformatted-text","code-block","table","datetime","emoji","html-entities","pagebreak","|","goto-line","watch","preview","fullscreen","clear","search","|","help","info"],simple:["undo","redo","|","bold","del","italic","quote","uppercase","lowercase","|","h1","h2","h3","h4","h5","h6","|","list-ul","list-ol","hr","|","watch","preview","fullscreen","|","help","info"],mini:["undo","redo","|","watch","preview","|","help","info"]},t.defaults={mode:"gfm",name:"",value:"",theme:"",editorTheme:"default",previewTheme:"",markdown:"",appendMarkdown:"",width:"100%",height:"100%",path:"./lib/",pluginPath:"",delay:300,autoLoadModules:!0,watch:!0,placeholder:"Enjoy Markdown! coding now...",gotoLine:!0,codeFold:!1,autoHeight:!1,autoFocus:!0,autoCloseTags:!0,searchReplace:!0,syncScrolling:!0,readOnly:!1,tabSize:4,indentUnit:4,lineNumbers:!0,lineWrapping:!0,autoCloseBrackets:!0,showTrailingSpace:!0,matchBrackets:!0,indentWithTabs:!0,styleSelectedText:!0,matchWordHighlight:!0,styleActiveLine:!0,dialogLockScreen:!0,dialogShowMask:!0,dialogDraggable:!0,dialogMaskBgColor:"#fff",dialogMaskOpacity:.1,fontSize:"13px",saveHTMLToTextarea:!1,disabledKeyMaps:[],onload:function(){},onresize:function(){},onchange:function(){},onwatch:null,onunwatch:null,onpreviewing:function(){},onpreviewed:function(){},onfullscreen:function(){},onfullscreenExit:function(){},onscroll:function(){},onpreviewscroll:function(){},imageUpload:!1,imageFormats:["jpg","jpeg","gif","png","bmp","webp"],imageUploadURL:"",crossDomainUpload:!1,uploadCallbackURL:"",toc:!0,tocm:!1,tocTitle:"",tocDropdown:!1,tocContainer:"",tocStartLevel:1,htmlDecode:!1,pageBreak:!0,atLink:!0,emailLink:!0,taskList:!1,emoji:!1,tex:!1,flowChart:!1,sequenceDiagram:!1,previewCodeHighlight:!0,toolbar:!0,toolbarAutoFixed:!0,toolbarIcons:"full",toolbarTitles:{},toolbarHandlers:{ucwords:function(){return t.toolbarHandlers.ucwords},lowercase:function(){return t.toolbarHandlers.lowercase}},toolbarCustomIcons:{lowercase:'a',ucwords:'Aa'},toolbarIconsClass:{undo:"fa-undo",redo:"fa-repeat",bold:"fa-bold",del:"fa-strikethrough",italic:"fa-italic",quote:"fa-quote-left",uppercase:"fa-font",h1:t.classPrefix+"bold",h2:t.classPrefix+"bold",h3:t.classPrefix+"bold",h4:t.classPrefix+"bold",h5:t.classPrefix+"bold",h6:t.classPrefix+"bold","list-ul":"fa-list-ul","list-ol":"fa-list-ol",hr:"fa-minus",link:"fa-link","reference-link":"fa-anchor",image:"fa-picture-o",code:"fa-code","preformatted-text":"fa-file-code-o","code-block":"fa-file-code-o",table:"fa-table",datetime:"fa-clock-o",emoji:"fa-smile-o","html-entities":"fa-copyright",pagebreak:"fa-newspaper-o","goto-line":"fa-terminal",watch:"fa-eye-slash",unwatch:"fa-eye",preview:"fa-desktop",search:"fa-search",fullscreen:"fa-arrows-alt",clear:"fa-eraser",help:"fa-question-circle",info:"fa-info-circle"},toolbarIconTexts:{},lang:{name:"zh-cn",description:"开源在线Markdown编辑器
          Open source online Markdown editor.",tocTitle:"目录",toolbar:{undo:"撤销(Ctrl+Z)",redo:"重做(Ctrl+Y)",bold:"粗体",del:"删除线",italic:"斜体",quote:"引用",ucwords:"将每个单词首字母转成大写",uppercase:"将所选转换成大写",lowercase:"将所选转换成小写",h1:"标题1",h2:"标题2",h3:"标题3",h4:"标题4",h5:"标题5",h6:"标题6","list-ul":"无序列表","list-ol":"有序列表",hr:"横线",link:"链接","reference-link":"引用链接",image:"添加图片",code:"行内代码","preformatted-text":"预格式文本 / 代码块(缩进风格)","code-block":"代码块(多语言风格)",table:"添加表格",datetime:"日期时间",emoji:"Emoji表情","html-entities":"HTML实体字符",pagebreak:"插入分页符","goto-line":"跳转到行",watch:"关闭实时预览",unwatch:"开启实时预览",preview:"全窗口预览HTML(按 Shift + ESC还原)",fullscreen:"全屏(按ESC还原)",clear:"清空",search:"搜索",help:"使用帮助",info:"关于"+t.title},buttons:{enter:"确定",cancel:"取消",close:"关闭"},dialog:{link:{title:"添加链接",url:"链接地址",urlTitle:"链接标题",urlEmpty:"错误:请填写链接地址。"},referenceLink:{title:"添加引用链接",name:"引用名称",url:"链接地址",urlId:"链接ID",urlTitle:"链接标题",nameEmpty:"错误:引用链接的名称不能为空。",idEmpty:"错误:请填写引用链接的ID。",urlEmpty:"错误:请填写引用链接的URL地址。"},image:{title:"添加图片",url:"图片地址",link:"图片链接",alt:"图片描述",uploadButton:"本地上传",imageURLEmpty:"错误:图片地址不能为空。",uploadFileEmpty:"错误:上传的图片不能为空。",formatNotAllowed:"错误:只允许上传图片文件,允许上传的图片文件格式有:"},preformattedText:{title:"添加预格式文本或代码块",emptyAlert:"错误:请填写预格式文本或代码的内容。"},codeBlock:{title:"添加代码块",selectLabel:"代码语言:",selectDefaultText:"请选择代码语言",otherLanguage:"其他语言",unselectedLanguageAlert:"错误:请选择代码所属的语言类型。",codeEmptyAlert:"错误:请填写代码内容。"},htmlEntities:{title:"HTML 实体字符"},help:{title:"使用帮助"}}}},t.classNames={tex:t.classPrefix+"tex"},t.dialogZindex=99999,t.$katex=null,t.$marked=null,t.$CodeMirror=null,t.$prettyPrint=null;var i,o;t.prototype=t.fn={state:{watching:!1,loaded:!1,preview:!1,fullscreen:!1},init:function(i,o){o=o||{},"object"==typeof i&&(o=i);var r=this.classPrefix=t.classPrefix,n=this.settings=e.extend(!0,t.defaults,o);i="object"==typeof i?n.id:i;var a=this.editor=e("#"+i);this.id=i,this.lang=n.lang;var s=this.classNames={textarea:{html:r+"html-textarea",markdown:r+"markdown-textarea"}};n.pluginPath=""===n.pluginPath?n.path+"../plugins/":n.pluginPath,this.state.watching=n.watch?!0:!1,a.hasClass("editormd")||a.addClass("editormd"),a.css({width:"number"==typeof n.width?n.width+"px":n.width,height:"number"==typeof n.height?n.height+"px":n.height}),n.autoHeight&&a.css("height","auto");var l=this.markdownTextarea=a.children("textarea");l.length<1&&(a.append(""),l=this.markdownTextarea=a.children("textarea")),l.addClass(s.textarea.markdown).attr("placeholder",n.placeholder),("undefined"==typeof l.attr("name")||""===l.attr("name"))&&l.attr("name",""!==n.name?n.name:i+"-markdown-doc");var c=[n.readOnly?"":'',n.saveHTMLToTextarea?'':"",'
          ','
          ','
          '].join("\n");return a.append(c).addClass(r+"vertical"),""!==n.theme&&a.addClass(r+"theme-"+n.theme),this.mask=a.children("."+r+"mask"),this.containerMask=a.children("."+r+"container-mask"),""!==n.markdown&&l.val(n.markdown),""!==n.appendMarkdown&&l.val(l.val()+n.appendMarkdown),this.htmlTextarea=a.children("."+s.textarea.html),this.preview=a.children("."+r+"preview"),this.previewContainer=this.preview.children("."+r+"preview-container"),""!==n.previewTheme&&this.preview.addClass(r+"preview-theme-"+n.previewTheme),"function"==typeof define&&define.amd&&("undefined"!=typeof katex&&(t.$katex=katex),n.searchReplace&&!n.readOnly&&(t.loadCSS(n.path+"codemirror/addon/dialog/dialog"),t.loadCSS(n.path+"codemirror/addon/search/matchesonscrollbar"))),"function"==typeof define&&define.amd||!n.autoLoadModules?("undefined"!=typeof CodeMirror&&(t.$CodeMirror=CodeMirror),"undefined"!=typeof marked&&(t.$marked=marked),this.setCodeMirror().setToolbar().loadedDisplay()):this.loadQueues(),this},loadQueues:function(){var e=this,i=this.settings,o=i.path,r=function(){return t.isIE8?void e.loadedDisplay():void(i.flowChart||i.sequenceDiagram?t.loadScript(o+"raphael.min",function(){t.loadScript(o+"underscore.min",function(){!i.flowChart&&i.sequenceDiagram?t.loadScript(o+"sequence-diagram.min",function(){e.loadedDisplay()}):i.flowChart&&!i.sequenceDiagram?t.loadScript(o+"flowchart.min",function(){t.loadScript(o+"jquery.flowchart.min",function(){e.loadedDisplay()})}):i.flowChart&&i.sequenceDiagram&&t.loadScript(o+"flowchart.min",function(){t.loadScript(o+"jquery.flowchart.min",function(){t.loadScript(o+"sequence-diagram.min",function(){e.loadedDisplay()})})})})}):e.loadedDisplay())};return t.loadCSS(o+"codemirror/codemirror.min"),i.searchReplace&&!i.readOnly&&(t.loadCSS(o+"codemirror/addon/dialog/dialog"),t.loadCSS(o+"codemirror/addon/search/matchesonscrollbar")),i.codeFold&&t.loadCSS(o+"codemirror/addon/fold/foldgutter"),t.loadScript(o+"codemirror/codemirror.min",function(){t.$CodeMirror=CodeMirror,t.loadScript(o+"codemirror/modes.min",function(){t.loadScript(o+"codemirror/addons.min",function(){return e.setCodeMirror(),"gfm"!==i.mode&&"markdown"!==i.mode?(e.loadedDisplay(),!1):(e.setToolbar(),void t.loadScript(o+"marked.min",function(){t.$marked=marked,i.previewCodeHighlight?t.loadScript(o+"prettify.min",function(){r()}):r()}))})})}),this},setTheme:function(e){var t=this.editor,i=this.settings.theme,o=this.classPrefix+"theme-";return t.removeClass(o+i).addClass(o+e),this.settings.theme=e,this},setEditorTheme:function(e){var i=this.settings;return i.editorTheme=e,"default"!==e&&t.loadCSS(i.path+"codemirror/theme/"+i.editorTheme),this.cm.setOption("theme",e),this},setCodeMirrorTheme:function(e){return this.setEditorTheme(e),this},setPreviewTheme:function(e){var t=this.preview,i=this.settings.previewTheme,o=this.classPrefix+"preview-theme-";return t.removeClass(o+i).addClass(o+e),this.settings.previewTheme=e,this},setCodeMirror:function(){var e=this.settings,i=this.editor;"default"!==e.editorTheme&&t.loadCSS(e.path+"codemirror/theme/"+e.editorTheme);var o={mode:e.mode,theme:e.editorTheme,tabSize:e.tabSize,dragDrop:!1,autofocus:e.autoFocus,autoCloseTags:e.autoCloseTags,readOnly:e.readOnly?"nocursor":!1,indentUnit:e.indentUnit,lineNumbers:e.lineNumbers,lineWrapping:e.lineWrapping,extraKeys:{"Ctrl-Q":function(e){e.foldCode(e.getCursor())}},foldGutter:e.codeFold,gutters:["CodeMirror-linenumbers","CodeMirror-foldgutter"],matchBrackets:e.matchBrackets,indentWithTabs:e.indentWithTabs,styleActiveLine:e.styleActiveLine,styleSelectedText:e.styleSelectedText,autoCloseBrackets:e.autoCloseBrackets,showTrailingSpace:e.showTrailingSpace,highlightSelectionMatches:e.matchWordHighlight?{showToken:"onselected"===e.matchWordHighlight?!1:/\w/}:!1};return this.codeEditor=this.cm=t.$CodeMirror.fromTextArea(this.markdownTextarea[0],o),this.codeMirror=this.cmElement=i.children(".CodeMirror"),""!==e.value&&this.cm.setValue(e.value),this.codeMirror.css({fontSize:e.fontSize,width:e.watch?"50%":"100%"}),e.autoHeight&&(this.codeMirror.css("height","auto"),this.cm.setOption("viewportMargin",1/0)),e.lineNumbers||this.codeMirror.find(".CodeMirror-gutters").css("border-right","none"),this},getCodeMirrorOption:function(e){return this.cm.getOption(e)},setCodeMirrorOption:function(e,t){return this.cm.setOption(e,t),this},addKeyMap:function(e,t){return this.cm.addKeyMap(e,t),this},removeKeyMap:function(e){return this.cm.removeKeyMap(e),this},gotoLine:function(t){var i=this.settings;if(!i.gotoLine)return this;var o=this.cm,r=(this.editor,o.lineCount()),n=this.preview;if("string"==typeof t&&("last"===t&&(t=r),"first"===t&&(t=1)),"number"!=typeof t)return alert("Error: The line number must be an integer."),this;if(t=parseInt(t)-1,t>r)return alert("Error: The line number range 1-"+r),this;o.setCursor({line:t,ch:0});var a=o.getScrollInfo(),s=a.clientHeight,l=o.charCoords({line:t,ch:0},"local");if(o.scrollTo(null,(l.top+l.bottom-s)/2),i.watch){var c=this.codeMirror.find(".CodeMirror-scroll")[0],h=e(c).height(),d=c.scrollTop,u=d/c.scrollHeight;n.scrollTop(0===d?0:d+h>=c.scrollHeight-16?n[0].scrollHeight:n[0].scrollHeight*u)}return o.focus(),this},extend:function(){return"undefined"!=typeof arguments[1]&&("function"==typeof arguments[1]&&(arguments[1]=e.proxy(arguments[1],this)),this[arguments[0]]=arguments[1]),"object"==typeof arguments[0]&&"undefined"==typeof arguments[0].length&&e.extend(!0,this,arguments[0]),this},set:function(t,i){return"undefined"!=typeof i&&"function"==typeof i&&(i=e.proxy(i,this)),this[t]=i,this},config:function(t,i){var o=this.settings;return"object"==typeof t&&(o=e.extend(!0,o,t)),"string"==typeof t&&(o[t]=i),this.settings=o,this.recreate(),this},on:function(t,i){var o=this.settings;return"undefined"!=typeof o["on"+t]&&(o["on"+t]=e.proxy(i,this)),this},off:function(e){var t=this.settings;return"undefined"!=typeof t["on"+e]&&(t["on"+e]=function(){}),this},showToolbar:function(t){var i=this.settings;return i.readOnly?this:(i.toolbar&&(this.toolbar.length<1||""===this.toolbar.find("."+this.classPrefix+"menu").html())&&this.setToolbar(),i.toolbar=!0,this.toolbar.show(),this.resize(),e.proxy(t||function(){},this)(),this)},hideToolbar:function(t){var i=this.settings;return i.toolbar=!1,this.toolbar.hide(),this.resize(),e.proxy(t||function(){},this)(),this},setToolbarAutoFixed:function(t){var i=this.state,o=this.editor,r=this.toolbar,n=this.settings;"undefined"!=typeof t&&(n.toolbarAutoFixed=t);var a=function(){var t=e(window),i=t.scrollTop();return n.toolbarAutoFixed?void r.css(i-o.offset().top>10&&i
            ';i.append(n),r=this.toolbar=i.children("."+o+"toolbar")}if(!e.toolbar)return r.hide(),this;r.show();for(var a="function"==typeof e.toolbarIcons?e.toolbarIcons():"string"==typeof e.toolbarIcons?t.toolbarModes[e.toolbarIcons]:e.toolbarIcons,s=r.find("."+this.classPrefix+"menu"),l="",c=!1,h=0,d=a.length;d>h;h++){var u=a[h];if("||"===u)c=!0;else if("|"===u)l+='
          • |
          • ';else{var f=/h(\d)/.test(u),g=u;"watch"!==u||e.watch||(g="unwatch");var p=e.lang.toolbar[g],m=e.toolbarIconTexts[g],w=e.toolbarIconsClass[g];p="undefined"==typeof p?"":p,m="undefined"==typeof m?"":m,w="undefined"==typeof w?"":w;var v=c?'
          • ':"
          • ";"undefined"!=typeof e.toolbarCustomIcons[u]&&"function"!=typeof e.toolbarCustomIcons[u]?v+=e.toolbarCustomIcons[u]:(v+='',v+=''+(f?u.toUpperCase():""===w?m:"")+"",v+=""),v+="
          • ",l=c?v+l:l+v}}return s.html(l),s.find('[title="Lowercase"]').attr("title",e.lang.toolbar.lowercase),s.find('[title="ucwords"]').attr("title",e.lang.toolbar.ucwords),this.setToolbarHandler(),this.setToolbarAutoFixed(),this},dialogLockScreen:function(){return e.proxy(t.dialogLockScreen,this)(),this},dialogShowMask:function(i){return e.proxy(t.dialogShowMask,this)(i),this},getToolbarHandles:function(e){var i=this.toolbarHandlers=t.toolbarHandlers;return e&&"undefined"!=typeof toolbarIconHandlers[e]?i[e]:i},setToolbarHandler:function(){var i=this,o=this.settings;if(!o.toolbar||o.readOnly)return this;var r=this.toolbar,n=this.cm,a=this.classPrefix,s=this.toolbarIcons=r.find("."+a+"menu > li > a"),l=this.getToolbarHandles();return s.bind(t.mouseOrTouch("click","touchend"),function(t){var r=e(this).children(".fa"),a=r.attr("name"),s=n.getCursor(),c=n.getSelection();return""!==a?(i.activeIcon=r,"undefined"!=typeof l[a]?e.proxy(l[a],i)(n):"undefined"!=typeof o.toolbarHandlers[a]&&e.proxy(o.toolbarHandlers[a],i)(n,r,s,c),"link"!==a&&"reference-link"!==a&&"image"!==a&&"code-block"!==a&&"preformatted-text"!==a&&"watch"!==a&&"preview"!==a&&"search"!==a&&"fullscreen"!==a&&"info"!==a&&n.focus(),!1):void 0}),this},createDialog:function(i){return e.proxy(t.createDialog,this)(i)},createInfoDialog:function(){var e=this,i=this.editor,o=this.classPrefix,r=['
            ','
            ','

            '+t.title+"v"+t.version+"

            ","

            "+this.lang.description+"

            ",'

            '+t.homePage+'

            ','

            Copyright © 2015 Pandao, The MIT License.

            ',"
            ",'',"
            "].join("\n");i.append(r);var n=this.infoDialog=i.children("."+o+"dialog-info");return n.find("."+o+"dialog-close").bind(t.mouseOrTouch("click","touchend"),function(){e.hideInfoDialog()}),n.css("border",t.isIE8?"1px solid #ddd":"").css("z-index",t.dialogZindex).show(),this.infoDialogPosition(),this},infoDialogPosition:function(){var t=this.infoDialog,i=function(){t.css({top:(e(window).height()-t.height())/2+"px",left:(e(window).width()-t.width())/2+"px"})};return i(),e(window).resize(i),this},showInfoDialog:function(){e("html,body").css("overflow-x","hidden");var i=this.editor,o=this.settings,r=this.infoDialog=i.children("."+this.classPrefix+"dialog-info");return r.length<1&&this.createInfoDialog(),this.lockScreen(!0),this.mask.css({opacity:o.dialogMaskOpacity,backgroundColor:o.dialogMaskBgColor}).show(),r.css("z-index",t.dialogZindex).show(),this.infoDialogPosition(),this},hideInfoDialog:function(){return e("html,body").css("overflow-x",""),this.infoDialog.hide(),this.mask.hide(),this.lockScreen(!1),this},lockScreen:function(e){return t.lockScreen(e),this.resize(),this},recreate:function(){var e=this.editor,t=this.settings;return this.codeMirror.remove(),this.setCodeMirror(),t.readOnly||(e.find(".editormd-dialog").length>0&&e.find(".editormd-dialog").remove(),t.toolbar&&(this.getToolbarHandles(),this.setToolbar())),this.loadedDisplay(!0),this},previewCodeHighlight:function(){var e=this.settings,t=this.previewContainer;return e.previewCodeHighlight&&(t.find("pre").addClass("prettyprint linenums"),"undefined"!=typeof prettyPrint&&prettyPrint()),this},katexRender:function(){return null===i?this:(this.previewContainer.find("."+t.classNames.tex).each(function(){var i=e(this);t.$katex.render(i.text(),i[0]),i.find(".katex").css("font-size","1.6em")}),this)},flowChartAndSequenceDiagramRender:function(){var i=this,r=this.settings,n=this.previewContainer;if(t.isIE8)return this;if(r.flowChart){if(null===o)return this;n.find(".flowchart").flowChart()}r.sequenceDiagram&&n.find(".sequence-diagram").sequenceDiagram({theme:"simple"});var a=i.preview,s=i.codeMirror,l=s.find(".CodeMirror-scroll"),c=l.height(),h=l.scrollTop(),d=h/l[0].scrollHeight,u=0;a.find(".markdown-toc-list").each(function(){u+=e(this).height()});var f=a.find(".editormd-toc-menu").height();return f=f?f:0,a.scrollTop(0===h?0:h+c>=l[0].scrollHeight-16?a[0].scrollHeight:(a[0].scrollHeight+u+f)*d),this},registerKeyMaps:function(i){var o=this,r=this.cm,n=this.settings,a=t.toolbarHandlers,s=n.disabledKeyMaps;if(i=i||null){for(var l in i)if(e.inArray(l,s)<0){var c={};c[l]=i[l],r.addKeyMap(i)}}else{for(var h in t.keyMaps){var d=t.keyMaps[h],u="string"==typeof d?e.proxy(a[d],o):e.proxy(d,o);if(e.inArray(h,["F9","F10","F11"])<0&&e.inArray(h,s)<0){var f={};f[h]=u,r.addKeyMap(f)}}e(window).keydown(function(t){var i={120:"F9",121:"F10",122:"F11"};if(e.inArray(i[t.keyCode],s)<0)switch(t.keyCode){case 120:return e.proxy(a.watch,o)(),!1;case 121:return e.proxy(a.preview,o)(),!1;case 122:return e.proxy(a.fullscreen,o)(),!1}})}return this},bindScrollEvent:function(){var i=this,o=this.preview,r=this.settings,n=this.codeMirror,a=t.mouseOrTouch;if(!r.syncScrolling)return this;var s=function(){n.find(".CodeMirror-scroll").bind(a("scroll","touchmove"),function(t){var n=e(this).height(),a=e(this).scrollTop(),s=a/e(this)[0].scrollHeight,l=0;o.find(".markdown-toc-list").each(function(){l+=e(this).height()});var c=o.find(".editormd-toc-menu").height();c=c?c:0,o.scrollTop(0===a?0:a+n>=e(this)[0].scrollHeight-16?o[0].scrollHeight:(o[0].scrollHeight+l+c)*s),e.proxy(r.onscroll,i)(t)})},l=function(){n.find(".CodeMirror-scroll").unbind(a("scroll","touchmove"))},c=function(){o.bind(a("scroll","touchmove"),function(t){var o=e(this).height(),a=e(this).scrollTop(),s=a/e(this)[0].scrollHeight,l=n.find(".CodeMirror-scroll");l.scrollTop(0===a?0:a+o>=e(this)[0].scrollHeight?l[0].scrollHeight:l[0].scrollHeight*s),e.proxy(r.onpreviewscroll,i)(t)})},h=function(){o.unbind(a("scroll","touchmove"))};return n.bind({mouseover:s,mouseout:l,touchstart:s,touchend:l}),"single"===r.syncScrolling?this:(o.bind({mouseover:c,mouseout:h,touchstart:c,touchend:h}),this)},bindChangeEvent:function(){var e=this,t=this.cm,o=this.settings;return o.syncScrolling?(t.on("change",function(t,r){o.watch&&e.previewContainer.css("padding",o.autoHeight?"20px 20px 50px 40px":"20px"),i=setTimeout(function(){clearTimeout(i),e.save(),i=null},o.delay)}),this):this},loadedDisplay:function(t){t=t||!1;var i=this,o=this.editor,r=this.preview,n=this.settings;return this.containerMask.hide(),this.save(),n.watch&&r.show(),o.data("oldWidth",o.width()).data("oldHeight",o.height()),this.resize(),this.registerKeyMaps(),e(window).resize(function(){i.resize()}),this.bindScrollEvent().bindChangeEvent(),t||e.proxy(n.onload,this)(),this.state.loaded=!0,this},width:function(e){return this.editor.css("width","number"==typeof e?e+"px":e),this.resize(),this},height:function(e){return this.editor.css("height","number"==typeof e?e+"px":e),this.resize(),this},resize:function(t,i){t=t||null,i=i||null;var o=this.state,r=this.editor,n=this.preview,a=this.toolbar,s=this.settings,l=this.codeMirror;if(t&&r.css("width","number"==typeof t?t+"px":t),!s.autoHeight||o.fullscreen||o.preview?(i&&r.css("height","number"==typeof i?i+"px":i),o.fullscreen&&r.height(e(window).height()),s.toolbar&&!s.readOnly?l.css("margin-top",a.height()+1).height(r.height()-a.height()):l.css("margin-top",0).height(r.height())):(r.css("height","auto"),l.css("height","auto")),s.watch)if(l.width(r.width()/2),n.width(o.preview?r.width():r.width()/2),this.previewContainer.css("padding",s.autoHeight?"20px 20px 50px 40px":"20px"),s.toolbar&&!s.readOnly?n.css("top",a.height()+1):n.css("top",0),!s.autoHeight||o.fullscreen||o.preview){var c=s.toolbar&&!s.readOnly?r.height()-a.height():r.height();n.height(c)}else n.height("");else l.width(r.width()),n.hide();return o.loaded&&e.proxy(s.onresize,this)(),this},save:function(){if(null===i)return this;var r=this,n=this.state,a=this.settings,s=this.cm,l=s.getValue(),c=this.previewContainer;if("gfm"!==a.mode&&"markdown"!==a.mode)return this.markdownTextarea.val(l),this;var h=t.$marked,d=this.markdownToC=[],u=this.markedRendererOptions={toc:a.toc,tocm:a.tocm,tocStartLevel:a.tocStartLevel,pageBreak:a.pageBreak,taskList:a.taskList,emoji:a.emoji,tex:a.tex,atLink:a.atLink,emailLink:a.emailLink,flowChart:a.flowChart,sequenceDiagram:a.sequenceDiagram,previewCodeHighlight:a.previewCodeHighlight},f=this.markedOptions={renderer:t.markedRenderer(d,u),gfm:!0,tables:!0,breaks:!0,pedantic:!1,sanitize:a.htmlDecode?!1:!0,smartLists:!0,smartypants:!0};h.setOptions(f);var g=t.$marked(l,f);if(g=t.filterHTMLTags(g,a.htmlDecode),this.markdownTextarea.text(l),s.save(),a.saveHTMLToTextarea&&this.htmlTextarea.text(g),a.watch||!a.watch&&n.preview){if(c.html(g),this.previewCodeHighlight(),a.toc){var p=""===a.tocContainer?c:e(a.tocContainer),m=p.find("."+this.classPrefix+"toc-menu");p.attr("previewContainer",""===a.tocContainer?"true":"false"),""!==a.tocContainer&&m.length>0&&m.remove(),t.markdownToCRenderer(d,p,a.tocDropdown,a.tocStartLevel),(a.tocDropdown||p.find("."+this.classPrefix+"toc-menu").length>0)&&t.tocDropdownMenu(p,""!==a.tocTitle?a.tocTitle:this.lang.tocTitle),""!==a.tocContainer&&c.find(".markdown-toc").css("border","none")}a.tex&&(!t.kaTeXLoaded&&a.autoLoadModules?t.loadKaTeX(function(){t.$katex=katex,t.kaTeXLoaded=!0,r.katexRender()}):(t.$katex=katex,this.katexRender())),(a.flowChart||a.sequenceDiagram)&&(o=setTimeout(function(){clearTimeout(o),r.flowChartAndSequenceDiagramRender(),o=null},10)),n.loaded&&e.proxy(a.onchange,this)()}return this},focus:function(){return this.cm.focus(),this},setCursor:function(e){return this.cm.setCursor(e),this},getCursor:function(){return this.cm.getCursor()},setSelection:function(e,t){return this.cm.setSelection(e,t),this},getSelection:function(){return this.cm.getSelection()},setSelections:function(e){return this.cm.setSelections(e),this},getSelections:function(){return this.cm.getSelections()},replaceSelection:function(e){return this.cm.replaceSelection(e),this},insertValue:function(e){return this.replaceSelection(e),this},appendMarkdown:function(e){var t=(this.settings,this.cm);return t.setValue(t.getValue()+e),this},setMarkdown:function(e){return this.cm.setValue(e||this.settings.markdown),this},getMarkdown:function(){return this.cm.getValue()},getValue:function(){return this.cm.getValue()},setValue:function(e){return this.cm.setValue(e),this},clear:function(){return this.cm.setValue(""),this},getHTML:function(){return this.settings.saveHTMLToTextarea?this.htmlTextarea.val():(alert("Error: settings.saveHTMLToTextarea == false"),!1)},getTextareaSavedHTML:function(){return this.getHTML()},getPreviewedHTML:function(){return this.settings.watch?this.previewContainer.html():(alert("Error: settings.watch == false"),!1)},watch:function(t){var o=this.settings;if(e.inArray(o.mode,["gfm","markdown"])<0)return this;if(this.state.watching=o.watch=!0,this.preview.show(),this.toolbar){var r=o.toolbarIconsClass.watch,n=o.toolbarIconsClass.unwatch,a=this.toolbar.find(".fa[name=watch]");a.parent().attr("title",o.lang.toolbar.watch),a.removeClass(n).addClass(r)}return this.codeMirror.css("border-right","1px solid #ddd").width(this.editor.width()/2),i=0,this.save().resize(),o.onwatch||(o.onwatch=t||function(){}),e.proxy(o.onwatch,this)(),this},unwatch:function(t){var i=this.settings;if(this.state.watching=i.watch=!1,this.preview.hide(),this.toolbar){var o=i.toolbarIconsClass.watch,r=i.toolbarIconsClass.unwatch,n=this.toolbar.find(".fa[name=watch]");n.parent().attr("title",i.lang.toolbar.unwatch),n.removeClass(o).addClass(r)}return this.codeMirror.css("border-right","none").width(this.editor.width()),this.resize(),i.onunwatch||(i.onunwatch=t||function(){}),e.proxy(i.onunwatch,this)(),this},show:function(t){t=t||function(){};var i=this;return this.editor.show(0,function(){e.proxy(t,i)()}),this},hide:function(t){t=t||function(){};var i=this;return this.editor.hide(0,function(){e.proxy(t,i)()}),this},previewing:function(){var i=this,o=this.editor,r=this.preview,n=this.toolbar,a=this.settings,s=this.codeMirror,l=this.previewContainer;if(e.inArray(a.mode,["gfm","markdown"])<0)return this;a.toolbar&&n&&(n.toggle(),n.find(".fa[name=preview]").toggleClass("active")),s.toggle();var c=function(e){e.shiftKey&&27===e.keyCode&&i.previewed()};"none"===s.css("display")?(this.state.preview=!0,this.state.fullscreen&&r.css("background","#fff"),o.find("."+this.classPrefix+"preview-close-btn").show().bind(t.mouseOrTouch("click","touchend"),function(){i.previewed()}),a.watch?l.css("padding",""):this.save(),l.addClass(this.classPrefix+"preview-active"),r.show().css({position:"",top:0,width:o.width(),height:a.autoHeight&&!this.state.fullscreen?"auto":o.height()}),this.state.loaded&&e.proxy(a.onpreviewing,this)(),e(window).bind("keyup",c)):(e(window).unbind("keyup",c),this.previewed())},previewed:function(){var i=this.editor,o=this.preview,r=this.toolbar,n=this.settings,a=this.previewContainer,s=i.find("."+this.classPrefix+"preview-close-btn");return this.state.preview=!1,this.codeMirror.show(),n.toolbar&&r.show(),o[n.watch?"show":"hide"](),s.hide().unbind(t.mouseOrTouch("click","touchend")),a.removeClass(this.classPrefix+"preview-active"),n.watch&&a.css("padding","20px"),o.css({background:null,position:"absolute",width:i.width()/2,height:n.autoHeight&&!this.state.fullscreen?"auto":i.height()-r.height(),top:n.toolbar?r.height():0}),this.state.loaded&&e.proxy(n.onpreviewed,this)(),this},fullscreen:function(){var t=this,i=this.state,o=this.editor,r=(this.preview,this.toolbar),n=this.settings,a=this.classPrefix+"fullscreen";r&&r.find(".fa[name=fullscreen]").parent().toggleClass("active");var s=function(e){e.shiftKey||27!==e.keyCode||i.fullscreen&&t.fullscreenExit()};return o.hasClass(a)?(e(window).unbind("keyup",s),this.fullscreenExit()):(i.fullscreen=!0,e("html,body").css("overflow","hidden"),o.css({width:e(window).width(),height:e(window).height()}).addClass(a),this.resize(),e.proxy(n.onfullscreen,this)(),e(window).bind("keyup",s)),this},fullscreenExit:function(){var t=this.editor,i=this.settings,o=this.toolbar,r=this.classPrefix+"fullscreen";return this.state.fullscreen=!1,o&&o.find(".fa[name=fullscreen]").parent().removeClass("active"),e("html,body").css("overflow",""),t.css({width:t.data("oldWidth"),height:t.data("oldHeight")}).removeClass(r),this.resize(),e.proxy(i.onfullscreenExit,this)(),this},executePlugin:function(i,o){var r=this,n=this.cm,a=this.settings;return o=a.pluginPath+o,"function"==typeof define?"undefined"==typeof this[i]?(alert("Error: "+i+" plugin is not found, you are not load this plugin."),this):(this[i](n),this):(e.inArray(o,t.loadFiles.plugin)<0?t.loadPlugin(o,function(){t.loadPlugins[i]=r[i],r[i](n)}):e.proxy(t.loadPlugins[i],this)(n),this)},search:function(e){var t=this.settings;return t.searchReplace?(t.readOnly||this.cm.execCommand(e||"find"),this):(alert("Error: settings.searchReplace == false"),this)},searchReplace:function(){return this.search("replace"),this},searchReplaceAll:function(){return this.search("replaceAll"),this}},t.fn.init.prototype=t.fn,t.dialogLockScreen=function(){var t=this.settings||{dialogLockScreen:!0};t.dialogLockScreen&&(e("html,body").css("overflow","hidden"),this.resize())},t.dialogShowMask=function(t){var i=this.editor,o=this.settings||{dialogShowMask:!0};t.css({top:(e(window).height()-t.height())/2+"px",left:(e(window).width()-t.width())/2+"px"}),o.dialogShowMask&&i.children("."+this.classPrefix+"mask").css("z-index",parseInt(t.css("z-index"))-1).show()},t.toolbarHandlers={undo:function(){this.cm.undo()},redo:function(){this.cm.redo()},bold:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection(); + +e.replaceSelection("**"+i+"**"),""===i&&e.setCursor(t.line,t.ch+2)},del:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection("~~"+i+"~~"),""===i&&e.setCursor(t.line,t.ch+2)},italic:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection("*"+i+"*"),""===i&&e.setCursor(t.line,t.ch+1)},quote:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("> "+i),e.setCursor(t.line,t.ch+2)):e.replaceSelection("> "+i)},ucfirst:function(){var e=this.cm,i=e.getSelection(),o=e.listSelections();e.replaceSelection(t.firstUpperCase(i)),e.setSelections(o)},ucwords:function(){var e=this.cm,i=e.getSelection(),o=e.listSelections();e.replaceSelection(t.wordsFirstUpperCase(i)),e.setSelections(o)},uppercase:function(){var e=this.cm,t=e.getSelection(),i=e.listSelections();e.replaceSelection(t.toUpperCase()),e.setSelections(i)},lowercase:function(){var e=this.cm,t=(e.getCursor(),e.getSelection()),i=e.listSelections();e.replaceSelection(t.toLowerCase()),e.setSelections(i)},h1:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("# "+i),e.setCursor(t.line,t.ch+2)):e.replaceSelection("# "+i)},h2:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("## "+i),e.setCursor(t.line,t.ch+3)):e.replaceSelection("## "+i)},h3:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("### "+i),e.setCursor(t.line,t.ch+4)):e.replaceSelection("### "+i)},h4:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("#### "+i),e.setCursor(t.line,t.ch+5)):e.replaceSelection("#### "+i)},h5:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("##### "+i),e.setCursor(t.line,t.ch+6)):e.replaceSelection("##### "+i)},h6:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("###### "+i),e.setCursor(t.line,t.ch+7)):e.replaceSelection("###### "+i)},"list-ul":function(){var e=this.cm,t=(e.getCursor(),e.getSelection());if(""===t)e.replaceSelection("- "+t);else{for(var i=t.split("\n"),o=0,r=i.length;r>o;o++)i[o]=""===i[o]?"":"- "+i[o];e.replaceSelection(i.join("\n"))}},"list-ol":function(){var e=this.cm,t=(e.getCursor(),e.getSelection());if(""===t)e.replaceSelection("1. "+t);else{for(var i=t.split("\n"),o=0,r=i.length;r>o;o++)i[o]=""===i[o]?"":o+1+". "+i[o];e.replaceSelection(i.join("\n"))}},hr:function(){{var e=this.cm,t=e.getCursor();e.getSelection()}e.replaceSelection((0!==t.ch?"\n\n":"\n")+"------------\n\n")},tex:function(){if(!this.settings.tex)return alert("settings.tex === false"),this;var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection("$$"+i+"$$"),""===i&&e.setCursor(t.line,t.ch+2)},link:function(){this.executePlugin("linkDialog","link-dialog/link-dialog")},"reference-link":function(){this.executePlugin("referenceLinkDialog","reference-link-dialog/reference-link-dialog")},pagebreak:function(){if(!this.settings.pageBreak)return alert("settings.pageBreak === false"),this;{var e=this.cm;e.getSelection()}e.replaceSelection("\r\n[========]\r\n")},image:function(){this.executePlugin("imageDialog","image-dialog/image-dialog")},code:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection("`"+i+"`"),""===i&&e.setCursor(t.line,t.ch+1)},"code-block":function(){this.executePlugin("codeBlockDialog","code-block-dialog/code-block-dialog")},"preformatted-text":function(){this.executePlugin("preformattedTextDialog","preformatted-text-dialog/preformatted-text-dialog")},table:function(){this.executePlugin("tableDialog","table-dialog/table-dialog")},datetime:function(){var e=this.cm,i=(e.getSelection(),new Date,this.settings.lang.name),o=t.dateFormat()+" "+t.dateFormat("zh-cn"===i||"zh-tw"===i?"cn-week-day":"week-day");e.replaceSelection(o)},emoji:function(){this.executePlugin("emojiDialog","emoji-dialog/emoji-dialog")},"html-entities":function(){this.executePlugin("htmlEntitiesDialog","html-entities-dialog/html-entities-dialog")},"goto-line":function(){this.executePlugin("gotoLineDialog","goto-line-dialog/goto-line-dialog")},watch:function(){this[this.settings.watch?"unwatch":"watch"]()},preview:function(){this.previewing()},fullscreen:function(){this.fullscreen()},clear:function(){this.clear()},search:function(){this.search()},help:function(){this.executePlugin("helpDialog","help-dialog/help-dialog")},info:function(){this.showInfoDialog()}},t.keyMaps={"Ctrl-1":"h1","Ctrl-2":"h2","Ctrl-3":"h3","Ctrl-4":"h4","Ctrl-5":"h5","Ctrl-6":"h6","Ctrl-B":"bold","Ctrl-D":"datetime","Ctrl-E":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();return this.settings.emoji?(e.replaceSelection(":"+i+":"),void(""===i&&e.setCursor(t.line,t.ch+1))):void alert("Error: settings.emoji == false")},"Ctrl-Alt-G":"goto-line","Ctrl-H":"hr","Ctrl-I":"italic","Ctrl-K":"code","Ctrl-L":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection(),o=""===i?"":' "'+i+'"';e.replaceSelection("["+i+"]("+o+")"),""===i&&e.setCursor(t.line,t.ch+1)},"Ctrl-U":"list-ul","Shift-Ctrl-A":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();return this.settings.atLink?(e.replaceSelection("@"+i),void(""===i&&e.setCursor(t.line,t.ch+1))):void alert("Error: settings.atLink == false")},"Shift-Ctrl-C":"code","Shift-Ctrl-Q":"quote","Shift-Ctrl-S":"del","Shift-Ctrl-K":"tex","Shift-Alt-C":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection(["```",i,"```"].join("\n")),""===i&&e.setCursor(t.line,t.ch+3)},"Shift-Ctrl-Alt-C":"code-block","Shift-Ctrl-H":"html-entities","Shift-Alt-H":"help","Shift-Ctrl-E":"emoji","Shift-Ctrl-U":"uppercase","Shift-Alt-U":"ucwords","Shift-Ctrl-Alt-U":"ucfirst","Shift-Alt-L":"lowercase","Shift-Ctrl-I":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection(),o=""===i?"":' "'+i+'"';e.replaceSelection("!["+i+"]("+o+")"),""===i&&e.setCursor(t.line,t.ch+4)},"Shift-Ctrl-Alt-I":"image","Shift-Ctrl-L":"link","Shift-Ctrl-O":"list-ol","Shift-Ctrl-P":"preformatted-text","Shift-Ctrl-T":"table","Shift-Alt-P":"pagebreak",F9:"watch",F10:"preview",F11:"fullscreen"};var r=function(e){return String.prototype.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")};t.trim=r;var n=function(e){return e.toLowerCase().replace(/\b(\w)|\s(\w)/g,function(e){return e.toUpperCase()})};t.ucwords=t.wordsFirstUpperCase=n;var a=function(e){return e.toLowerCase().replace(/\b(\w)/,function(e){return e.toUpperCase()})};return t.firstUpperCase=t.ucfirst=a,t.urls={atLinkBase:"https://github.com/"},t.regexs={atLink:/@(\w+)/g,email:/(\w+)@(\w+)\.(\w+)\.?(\w+)?/g,emailLink:/(mailto:)?([\w\.\_]+)@(\w+)\.(\w+)\.?(\w+)?/g,emoji:/:([\w\+-]+):/g,emojiDatetime:/(\d{2}:\d{2}:\d{2})/g,twemoji:/:(tw-([\w]+)-?(\w+)?):/g,fontAwesome:/:(fa-([\w]+)(-(\w+)){0,}):/g,editormdLogo:/:(editormd-logo-?(\w+)?):/g,pageBreak:/^\[[=]{8,}\]$/},t.emoji={path:"http://www.emoji-cheat-sheet.com/graphics/emojis/",ext:".png"},t.twemoji={path:"http://twemoji.maxcdn.com/36x36/",ext:".png"},t.markedRenderer=function(i,o){var n={toc:!0,tocm:!1,tocStartLevel:1,pageBreak:!0,atLink:!0,emailLink:!0,taskList:!1,emoji:!1,tex:!1,flowChart:!1,sequenceDiagram:!1},a=e.extend(n,o||{}),s=t.$marked,l=new s.Renderer;i=i||[];var c=t.regexs,h=c.atLink,d=c.emoji,u=c.email,f=c.emailLink,g=c.twemoji,p=c.fontAwesome,m=c.editormdLogo,w=c.pageBreak;return l.emoji=function(e){e=e.replace(t.regexs.emojiDatetime,function(e){return e.replace(/:/g,":")});var i=e.match(d);if(!i||!a.emoji)return e;for(var o=0,r=i.length;r>o;o++)":+1:"===i[o]&&(i[o]=":\\+1:"),e=e.replace(new RegExp(i[o]),function(e,i){var o=e.match(p),r=e.replace(/:/g,"");if(o)for(var n=0,a=o.length;a>n;n++){var s=o[n].replace(/:/g,"");return''}else{var l=e.match(m),c=e.match(g);if(l)for(var h=0,d=l.length;d>h;h++){var u=l[h].replace(/:/g,"");return''}else{if(!c){var f="+1"===r?"plus1":r;return f="black_large_square"===f?"black_square":f,f="moon"===f?"waxing_gibbous_moon":f,':'+r+':'}for(var w=0,v=c.length;v>w;w++){var k=c[w].replace(/:/g,"").replace("tw-","");return'twemoji-'+k+''}}}});return e},l.atLink=function(i){return h.test(i)?(a.atLink&&(i=i.replace(u,function(e,t,i,o){return e.replace(/@/g,"_#_@_#_")}),i=i.replace(h,function(e,i){return''+e+""}).replace(/_#_@_#_/g,"@")),a.emailLink&&(i=i.replace(f,function(t,i,o,r,n){return!i&&e.inArray(n,"jpg|jpeg|png|gif|webp|ico|icon|pdf".split("|"))<0?''+t+"":t})),i):i},l.link=function(e,t,i){if(this.options.sanitize){try{var o=decodeURIComponent(unescape(e)).replace(/[^\w:]/g,"").toLowerCase()}catch(r){return""}if(0===o.indexOf("javascript:"))return""}var n=''+i.replace(/@/g,"@")+""):(t&&(n+=' title="'+t+'"'),n+=">"+i+"")},l.heading=function(e,t,o){var n=e,a=/\s*\]*)\>(.*)\<\/a\>\s*/;if(a.test(e)){var s=[];e=e.split(/\]+)\>([^\>]*)\<\/a\>/);for(var l=0,c=e.length;c>l;l++)s.push(e[l].replace(/\s*href\=\"(.*)\"\s*/g,""));e=s.join(" ")}e=r(e);var h=e.toLowerCase().replace(/[^\w]+/g,"-"),d={text:e,level:t,slug:h},u=/^[\u4e00-\u9fa5]+$/.test(e),f=u?escape(e).replace(/\%/g,""):e.toLowerCase().replace(/[^\w]+/g,"-");i.push(d);var g="';return g+='',g+='',g+=this.atLink(a?this.emoji(n):this.emoji(e)),g+=""},l.pageBreak=function(e){return w.test(e)&&a.pageBreak&&(e='
            '),e},l.paragraph=function(e){var i=/\$\$(.*)\$\$/g.test(e),o=/^\$\$(.*)\$\$$/.test(e),r=o?' class="'+t.classNames.tex+'"':"",n=a.tocm?/^(\[TOC\]|\[TOCM\])$/.test(e):/^\[TOC\]$/.test(e),s=/^\[TOCM\]$/.test(e);e=!o&&i?e.replace(/(\$\$([^\$]*)\$\$)+/g,function(e,i){return''+i.replace(/\$/g,"")+""}):o?e.replace(/\$/g,""):e;var l='
            '+e+"
            ";return n?s?'
            '+l+"

            ":l:w.test(e)?this.pageBreak(e):""+this.atLink(this.emoji(e))+"

            \n"},l.code=function(e,i,o){return"seq"===i||"sequence"===i?'
            '+e+"
            ":"flow"===i?'
            '+e+"
            ":"math"===i||"latex"===i||"katex"===i?'

            '+e+"

            ":s.Renderer.prototype.code.apply(this,arguments)},l.tablecell=function(e,t){var i=t.header?"th":"td",o=t.align?"<"+i+' style="text-align:'+t.align+'">':"<"+i+">";return o+this.atLink(this.emoji(e))+"\n"},l.listitem=function(e){return a.taskList&&/^\s*\[[x\s]\]\s*/.test(e)?(e=e.replace(/^\s*\[\s\]\s*/,' ').replace(/^\s*\[x\]\s*/,' '),'
          • '+this.atLink(this.emoji(e))+"
          • "):"
          • "+this.atLink(this.emoji(e))+"
          • "},l},t.markdownToCRenderer=function(e,t,i,o){var r="",n=0,a=this.classPrefix;o=o||1;for(var s=0,l=e.length;l>s;s++){var c=e[s].text,h=e[s].level;o>h||(r+=h>n?"":n>h?new Array(n-h+2).join("
        • "):"",r+='
        • '+c+"
            ",n=h)}var d=t.find(".markdown-toc");if(d.length<1&&"false"===t.attr("previewContainer")){var u='
            ';u=i?'
            '+u+"
            ":u,t.html(u),d=t.find(".markdown-toc")}return i&&d.wrap('

            '),d.html('
              ').children(".markdown-toc-list").html(r.replace(/\r?\n?\\<\/ul\>/g,"")),d},t.tocDropdownMenu=function(t,i){i=i||"Table of Contents";var o=400,r=t.find("."+this.classPrefix+"toc-menu");return r.each(function(){var t=e(this),r=t.children(".markdown-toc"),n='',a=''+n+i+"",s=r.children("ul"),l=s.find("li");r.append(a),l.first().before("
            • "+i+" "+n+"

            • "),t.mouseover(function(){s.show(),l.each(function(){var t=e(this),i=t.children("ul");if(""===i.html()&&i.remove(),i.length>0&&""!==i.html()){var r=t.children("a").first();r.children(".fa").length<1&&r.append(e(n).css({"float":"right",paddingTop:"4px"}))}t.mouseover(function(){i.css("z-index",o).show(),o+=1}).mouseleave(function(){i.hide()})})}).mouseleave(function(){s.hide()})}),r},t.filterHTMLTags=function(t,i){if("string"!=typeof t&&(t=new String(t)),"string"!=typeof i)return t;for(var o=i.split("|"),r=o[0].split(","),n=o[1],a=0,s=r.length;s>a;a++){var l=r[a];t=t.replace(new RegExp("]*)>([^>]*)","igm"),"")}if("undefined"!=typeof n){var c=/\<(\w+)\s*([^\>]*)\>([^\>]*)\<\/(\w+)\>/gi;t="*"===n?t.replace(c,function(e,t,i,o,r){return"<"+t+">"+o+""}):"on*"===n?t.replace(c,function(t,i,o,r,n){var a=e("<"+i+">"+r+""),s=e(t)[0].attributes,l={};e.each(s,function(e,t){'"'!==t.nodeName&&(l[t.nodeName]=t.nodeValue)}),e.each(l,function(e){0===e.indexOf("on")&&delete l[e]}),a.attr(l);var c="undefined"!=typeof a[1]?e(a[1]).text():"";return a[0].outerHTML+c}):t.replace(c,function(t,i,o,r){var a=n.split(","),s=e(t);return s.html(r),e.each(a,function(e){s.attr(a[e],null)}),s[0].outerHTML})}return t},t.markdownToHTML=function(i,o){var r={gfm:!0,toc:!0,tocm:!1,tocStartLevel:1,tocTitle:"目录",tocDropdown:!1,tocContainer:"",markdown:"",markdownSourceCode:!1,htmlDecode:!1,autoLoadKaTeX:!0,pageBreak:!0,atLink:!0,emailLink:!0,tex:!1,taskList:!1,emoji:!1,flowChart:!1,sequenceDiagram:!1,previewCodeHighlight:!0};t.$marked=marked;var n=e("#"+i),a=n.settings=e.extend(!0,r,o||{}),s=n.find("textarea");s.length<1&&(n.append(""),s=n.find("textarea"));var l=""===a.markdown?s.val():a.markdown,c=[],h={toc:a.toc,tocm:a.tocm,tocStartLevel:a.tocStartLevel,taskList:a.taskList,emoji:a.emoji,tex:a.tex,pageBreak:a.pageBreak,atLink:a.atLink,emailLink:a.emailLink,flowChart:a.flowChart,sequenceDiagram:a.sequenceDiagram,previewCodeHighlight:a.previewCodeHighlight},d={renderer:t.markedRenderer(c,h),gfm:a.gfm,tables:!0,breaks:!0,pedantic:!1,sanitize:a.htmlDecode?!1:!0,smartLists:!0,smartypants:!0};l=new String(l);var u=marked(l,d);u=t.filterHTMLTags(u,a.htmlDecode),a.markdownSourceCode?s.text(l):s.remove(),n.addClass("markdown-body "+this.classPrefix+"html-preview").append(u);var f=""!==a.tocContainer?e(a.tocContainer):n;if(""!==a.tocContainer&&f.attr("previewContainer",!1),a.toc&&(n.tocContainer=this.markdownToCRenderer(c,f,a.tocDropdown,a.tocStartLevel),(a.tocDropdown||n.find("."+this.classPrefix+"toc-menu").length>0)&&this.tocDropdownMenu(n,a.tocTitle),""!==a.tocContainer&&n.find(".editormd-toc-menu, .editormd-markdown-toc").remove()),a.previewCodeHighlight&&(n.find("pre").addClass("prettyprint linenums"),prettyPrint()),t.isIE8||(a.flowChart&&n.find(".flowchart").flowChart(),a.sequenceDiagram&&n.find(".sequence-diagram").sequenceDiagram({theme:"simple"})),a.tex){var g=function(){n.find("."+t.classNames.tex).each(function(){var t=e(this);katex.render(t.html().replace(/</g,"<").replace(/>/g,">"),t[0]),t.find(".katex").css("font-size","1.6em")})};!a.autoLoadKaTeX||t.$katex||t.kaTeXLoaded?g():this.loadKaTeX(function(){t.$katex=katex,t.kaTeXLoaded=!0,g()})}return n.getMarkdown=function(){return s.val()},n},t.themes=["default","dark"],t.previewThemes=["default","dark"],t.editorThemes=["default","3024-day","3024-night","ambiance","ambiance-mobile","base16-dark","base16-light","blackboard","cobalt","eclipse","elegant","erlang-dark","lesser-dark","mbo","mdn-like","midnight","monokai","neat","neo","night","paraiso-dark","paraiso-light","pastel-on-dark","rubyblue","solarized","the-matrix","tomorrow-night-eighties","twilight","vibrant-ink","xq-dark","xq-light"],t.loadPlugins={},t.loadFiles={js:[],css:[],plugin:[]},t.loadPlugin=function(e,i,o){i=i||function(){},this.loadScript(e,function(){t.loadFiles.plugin.push(e),i()},o)},t.loadCSS=function(e,i,o){o=o||"head",i=i||function(){};var r=document.createElement("link");r.type="text/css",r.rel="stylesheet",r.onload=r.onreadystatechange=function(){t.loadFiles.css.push(e),i()},r.href=e+".css","head"===o?document.getElementsByTagName("head")[0].appendChild(r):document.body.appendChild(r)},t.isIE="Microsoft Internet Explorer"==navigator.appName,t.isIE8=t.isIE&&"8."==navigator.appVersion.match(/8./i),t.loadScript=function(e,i,o){o=o||"head",i=i||function(){};var r=null;r=document.createElement("script"),r.id=e.replace(/[\./]+/g,"-"),r.type="text/javascript",r.src=e+".js",t.isIE8?r.onreadystatechange=function(){r.readyState&&("loaded"===r.readyState||"complete"===r.readyState)&&(r.onreadystatechange=null,t.loadFiles.js.push(e),i())}:r.onload=function(){t.loadFiles.js.push(e),i()},"head"===o?document.getElementsByTagName("head")[0].appendChild(r):document.body.appendChild(r)},t.katexURL={css:"//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min",js:"//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min"},t.kaTeXLoaded=!1,t.loadKaTeX=function(e){t.loadCSS(t.katexURL.css,function(){t.loadScript(t.katexURL.js,e||function(){})})},t.lockScreen=function(t){e("html,body").css("overflow",t?"hidden":"")},t.createDialog=function(i){var o={name:"",width:420,height:240,title:"",drag:!0,closed:!0,content:"",mask:!0,maskStyle:{backgroundColor:"#fff",opacity:.1},lockScreen:!0,footer:!0,buttons:!1};i=e.extend(!0,o,i);var r=this,n=this.editor,a=t.classPrefix,s=(new Date).getTime(),l=""===i.name?a+"dialog-"+s:i.name,c=t.mouseOrTouch,h='
              ';""!==i.title&&(h+='
              ",h+=''+i.title+"",h+="
              "),i.closed&&(h+=''),h+='
              '+i.content,(i.footer||"string"==typeof i.footer)&&(h+='"),h+="
              ",h+='
              ',h+='
              ',h+="
              ",n.append(h);var d=n.find("."+l);d.lockScreen=function(t){return i.lockScreen&&(e("html,body").css("overflow",t?"hidden":""),r.resize()),d},d.showMask=function(){return i.mask&&n.find("."+a+"mask").css(i.maskStyle).css("z-index",t.dialogZindex-1).show(),d},d.hideMask=function(){return i.mask&&n.find("."+a+"mask").hide(),d},d.loading=function(e){var t=d.find("."+a+"dialog-mask");return t[e?"show":"hide"](),d},d.lockScreen(!0).showMask(),d.show().css({zIndex:t.dialogZindex,border:t.isIE8?"1px solid #ddd":"",width:"number"==typeof i.width?i.width+"px":i.width,height:"number"==typeof i.height?i.height+"px":i.height});var u=function(){d.css({top:(e(window).height()-d.height())/2+"px",left:(e(window).width()-d.width())/2+"px"})};if(u(),e(window).resize(u),d.children("."+a+"dialog-close").bind(c("click","touchend"),function(){d.hide().lockScreen(!1).hideMask()}),"object"==typeof i.buttons){var f=d.footer=d.find("."+a+"dialog-footer");for(var g in i.buttons){var p=i.buttons[g],m=a+g+"-btn";f.append('"),p[1]=e.proxy(p[1],d),f.children("."+m).bind(c("click","touchend"),p[1])}}if(""!==i.title&&i.drag){var w,v,k=d.children("."+a+"dialog-header");i.mask||k.bind(c("click","touchend"),function(){t.dialogZindex+=2,d.css("z-index",t.dialogZindex)}),k.mousedown(function(e){e=e||window.event,w=e.clientX-parseInt(d[0].style.left),v=e.clientY-parseInt(d[0].style.top),document.onmousemove=y});var b=function(e){e.removeClass(a+"user-unselect").off("selectstart")},x=function(e){e.addClass(a+"user-unselect").on("selectstart",function(e){return!1})},y=function(t){t=t||window.event;var i,o,r=parseInt(d[0].style.left),n=parseInt(d[0].style.top);r>=0?r+d.width()<=e(window).width()?i=t.clientX-w:(i=e(window).width()-d.width(),document.onmousemove=null):(i=0,document.onmousemove=null),n>=0?o=t.clientY-v:(o=0,document.onmousemove=null),document.onselectstart=function(){return!1},x(e("body")),x(d),d[0].style.left=i+"px",d[0].style.top=o+"px"};document.onmouseup=function(){b(e("body")),b(d),document.onselectstart=null,document.onmousemove=null},k.touchDraggable=function(){var t=null,i=function(i){var o=i.originalEvent,r=e(this).parent().position();t={x:o.changedTouches[0].pageX-r.left,y:o.changedTouches[0].pageY-r.top}},o=function(i){i.preventDefault();var o=i.originalEvent;e(this).parent().css({top:o.changedTouches[0].pageY-t.y,left:o.changedTouches[0].pageX-t.x})};this.bind("touchstart",i).bind("touchmove",o)},k.touchDraggable()}return t.dialogZindex+=2,d},t.mouseOrTouch=function(e,t){e=e||"click",t=t||"touchend";var i=e;try{document.createEvent("TouchEvent"),i=t}catch(o){}return i},t.dateFormat=function(e){e=e||"";var t=function(e){return 10>e?"0"+e:e},i=new Date,o=i.getFullYear(),r=o.toString().slice(2,4),n=t(i.getMonth()+1),a=t(i.getDate()),s=i.getDay(),l=t(i.getHours()),c=t(i.getMinutes()),h=t(i.getSeconds()),d=t(i.getMilliseconds()),u="",f=r+"-"+n+"-"+a,g=o+"-"+n+"-"+a,p=l+":"+c+":"+h;switch(e){case"UNIX Time":u=i.getTime();break;case"UTC":u=i.toUTCString();break;case"yy":u=r;break;case"year":case"yyyy":u=o;break;case"month":case"mm":u=n;break;case"cn-week-day":case"cn-wd":var m=["日","一","二","三","四","五","六"];u="星期"+m[s];break;case"week-day":case"wd":var w=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];u=w[s];break;case"day":case"dd":u=a;break;case"hour":case"hh":u=l;break;case"min":case"ii":u=c;break;case"second":case"ss":u=h;break;case"ms":u=d;break;case"yy-mm-dd":u=f;break;case"yyyy-mm-dd":u=g;break;case"yyyy-mm-dd h:i:s ms":case"full + ms":u=g+" "+p+" "+d;break;case"full":case"yyyy-mm-dd h:i:s":default:u=g+" "+p}return u},t}}); \ No newline at end of file diff --git a/src/main/resources/static/admin/editormd/editormd.js b/src/main/resources/static/admin/editormd/editormd.js new file mode 100755 index 00000000..157d0c23 --- /dev/null +++ b/src/main/resources/static/admin/editormd/editormd.js @@ -0,0 +1,4598 @@ +/* + * Editor.md + * + * @file editormd.js + * @version v1.5.0 + * @description Open source online markdown editor. + * @license MIT License + * @author Pandao + * {@link https://github.com/pandao/editor.md} + * @updateTime 2015-06-09 + */ + +;(function(factory) { + "use strict"; + + // CommonJS/Node.js + if (typeof require === "function" && typeof exports === "object" && typeof module === "object") + { + module.exports = factory; + } + else if (typeof define === "function") // AMD/CMD/Sea.js + { + if (define.amd) // for Require.js + { + /* Require.js define replace */ + } + else + { + define(["jquery"], factory); // for Sea.js + } + } + else + { + window.editormd = factory(); + } + +}(function() { + + /* Require.js assignment replace */ + + "use strict"; + + var $ = (typeof (jQuery) !== "undefined") ? jQuery : Zepto; + + if (typeof ($) === "undefined") { + return ; + } + + /** + * editormd + * + * @param {String} id 编辑器的ID + * @param {Object} options 配置选项 Key/Value + * @returns {Object} editormd 返回editormd对象 + */ + + var editormd = function (id, options) { + return new editormd.fn.init(id, options); + }; + + editormd.title = editormd.$name = "Editor.md"; + editormd.version = "1.5.0"; + editormd.homePage = "https://pandao.github.io/editor.md/"; + editormd.classPrefix = "editormd-"; + + editormd.toolbarModes = { + full : [ + "undo", "redo", "|", + "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|", + "h1", "h2", "h3", "h4", "h5", "h6", "|", + "list-ul", "list-ol", "hr", "|", + "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", + "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|", + "help", "info" + ], + simple : [ + "undo", "redo", "|", + "bold", "del", "italic", "quote", "uppercase", "lowercase", "|", + "h1", "h2", "h3", "h4", "h5", "h6", "|", + "list-ul", "list-ol", "hr", "|", + "watch", "preview", "fullscreen", "|", + "help", "info" + ], + mini : [ + "undo", "redo", "|", + "watch", "preview", "|", + "help", "info" + ] + }; + + editormd.defaults = { + mode : "gfm", //gfm or markdown + name : "", // Form element name + value : "", // value for CodeMirror, if mode not gfm/markdown + theme : "", // Editor.md self themes, before v1.5.0 is CodeMirror theme, default empty + editorTheme : "default", // Editor area, this is CodeMirror theme at v1.5.0 + previewTheme : "", // Preview area theme, default empty + markdown : "", // Markdown source code + appendMarkdown : "", // if in init textarea value not empty, append markdown to textarea + width : "100%", + height : "100%", + path : "./lib/", // Dependents module file directory + pluginPath : "", // If this empty, default use settings.path + "../plugins/" + delay : 300, // Delay parse markdown to html, Uint : ms + autoLoadModules : true, // Automatic load dependent module files + watch : true, + placeholder : "Enjoy Markdown! coding now...", + gotoLine : true, + codeFold : false, + autoHeight : false, + autoFocus : true, + autoCloseTags : true, + searchReplace : true, + syncScrolling : true, // true | false | "single", default true + readOnly : false, + tabSize : 4, + indentUnit : 4, + lineNumbers : true, + lineWrapping : true, + autoCloseBrackets : true, + showTrailingSpace : true, + matchBrackets : true, + indentWithTabs : true, + styleSelectedText : true, + matchWordHighlight : true, // options: true, false, "onselected" + styleActiveLine : true, // Highlight the current line + dialogLockScreen : true, + dialogShowMask : true, + dialogDraggable : true, + dialogMaskBgColor : "#fff", + dialogMaskOpacity : 0.1, + fontSize : "13px", + saveHTMLToTextarea : false, + disabledKeyMaps : [], + + onload : function() {}, + onresize : function() {}, + onchange : function() {}, + onwatch : null, + onunwatch : null, + onpreviewing : function() {}, + onpreviewed : function() {}, + onfullscreen : function() {}, + onfullscreenExit : function() {}, + onscroll : function() {}, + onpreviewscroll : function() {}, + + imageUpload : false, + imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], + imageUploadURL : "", + crossDomainUpload : false, + uploadCallbackURL : "", + + toc : true, // Table of contents + tocm : false, // Using [TOCM], auto create ToC dropdown menu + tocTitle : "", // for ToC dropdown menu btn + tocDropdown : false, + tocContainer : "", + tocStartLevel : 1, // Said from H1 to create ToC + htmlDecode : false, // Open the HTML tag identification + pageBreak : true, // Enable parse page break [========] + atLink : true, // for @link + emailLink : true, // for email address auto link + taskList : false, // Enable Github Flavored Markdown task lists + emoji : false, // :emoji: , Support Github emoji, Twitter Emoji (Twemoji); + // Support FontAwesome icon emoji :fa-xxx: > Using fontAwesome icon web fonts; + // Support Editor.md logo icon emoji :editormd-logo: :editormd-logo-1x: > 1~8x; + tex : false, // TeX(LaTeX), based on KaTeX + flowChart : false, // flowChart.js only support IE9+ + sequenceDiagram : false, // sequenceDiagram.js only support IE9+ + previewCodeHighlight : true, + + toolbar : true, // show/hide toolbar + toolbarAutoFixed : true, // on window scroll auto fixed position + toolbarIcons : "full", + toolbarTitles : {}, + toolbarHandlers : { + ucwords : function() { + return editormd.toolbarHandlers.ucwords; + }, + lowercase : function() { + return editormd.toolbarHandlers.lowercase; + } + }, + toolbarCustomIcons : { // using html tag create toolbar icon, unused default tag. + lowercase : "a", + "ucwords" : "Aa" + }, + toolbarIconsClass : { + undo : "fa-undo", + redo : "fa-repeat", + bold : "fa-bold", + del : "fa-strikethrough", + italic : "fa-italic", + quote : "fa-quote-left", + uppercase : "fa-font", + h1 : editormd.classPrefix + "bold", + h2 : editormd.classPrefix + "bold", + h3 : editormd.classPrefix + "bold", + h4 : editormd.classPrefix + "bold", + h5 : editormd.classPrefix + "bold", + h6 : editormd.classPrefix + "bold", + "list-ul" : "fa-list-ul", + "list-ol" : "fa-list-ol", + hr : "fa-minus", + link : "fa-link", + "reference-link" : "fa-anchor", + image : "fa-picture-o", + code : "fa-code", + "preformatted-text" : "fa-file-code-o", + "code-block" : "fa-file-code-o", + table : "fa-table", + datetime : "fa-clock-o", + emoji : "fa-smile-o", + "html-entities" : "fa-copyright", + pagebreak : "fa-newspaper-o", + "goto-line" : "fa-terminal", // fa-crosshairs + watch : "fa-eye-slash", + unwatch : "fa-eye", + preview : "fa-desktop", + search : "fa-search", + fullscreen : "fa-arrows-alt", + clear : "fa-eraser", + help : "fa-question-circle", + info : "fa-info-circle" + }, + toolbarIconTexts : {}, + + lang : { + name : "zh-cn", + description : "开源在线Markdown编辑器
              Open source online Markdown editor.", + tocTitle : "目录", + toolbar : { + undo : "撤销(Ctrl+Z)", + redo : "重做(Ctrl+Y)", + bold : "粗体", + del : "删除线", + italic : "斜体", + quote : "引用", + ucwords : "将每个单词首字母转成大写", + uppercase : "将所选转换成大写", + lowercase : "将所选转换成小写", + h1 : "标题1", + h2 : "标题2", + h3 : "标题3", + h4 : "标题4", + h5 : "标题5", + h6 : "标题6", + "list-ul" : "无序列表", + "list-ol" : "有序列表", + hr : "横线", + link : "链接", + "reference-link" : "引用链接", + image : "添加图片", + code : "行内代码", + "preformatted-text" : "预格式文本 / 代码块(缩进风格)", + "code-block" : "代码块(多语言风格)", + table : "添加表格", + datetime : "日期时间", + emoji : "Emoji表情", + "html-entities" : "HTML实体字符", + pagebreak : "插入分页符", + "goto-line" : "跳转到行", + watch : "关闭实时预览", + unwatch : "开启实时预览", + preview : "全窗口预览HTML(按 Shift + ESC还原)", + fullscreen : "全屏(按ESC还原)", + clear : "清空", + search : "搜索", + help : "使用帮助", + info : "关于" + editormd.title + }, + buttons : { + enter : "确定", + cancel : "取消", + close : "关闭" + }, + dialog : { + link : { + title : "添加链接", + url : "链接地址", + urlTitle : "链接标题", + urlEmpty : "错误:请填写链接地址。" + }, + referenceLink : { + title : "添加引用链接", + name : "引用名称", + url : "链接地址", + urlId : "链接ID", + urlTitle : "链接标题", + nameEmpty: "错误:引用链接的名称不能为空。", + idEmpty : "错误:请填写引用链接的ID。", + urlEmpty : "错误:请填写引用链接的URL地址。" + }, + image : { + title : "添加图片", + url : "图片地址", + link : "图片链接", + alt : "图片描述", + uploadButton : "本地上传", + imageURLEmpty : "错误:图片地址不能为空。", + uploadFileEmpty : "错误:上传的图片不能为空。", + formatNotAllowed : "错误:只允许上传图片文件,允许上传的图片文件格式有:" + }, + preformattedText : { + title : "添加预格式文本或代码块", + emptyAlert : "错误:请填写预格式文本或代码的内容。" + }, + codeBlock : { + title : "添加代码块", + selectLabel : "代码语言:", + selectDefaultText : "请选择代码语言", + otherLanguage : "其他语言", + unselectedLanguageAlert : "错误:请选择代码所属的语言类型。", + codeEmptyAlert : "错误:请填写代码内容。" + }, + htmlEntities : { + title : "HTML 实体字符" + }, + help : { + title : "使用帮助" + } + } + } + }; + + editormd.classNames = { + tex : editormd.classPrefix + "tex" + }; + + editormd.dialogZindex = 99999; + + editormd.$katex = null; + editormd.$marked = null; + editormd.$CodeMirror = null; + editormd.$prettyPrint = null; + + var timer, flowchartTimer; + + editormd.prototype = editormd.fn = { + state : { + watching : false, + loaded : false, + preview : false, + fullscreen : false + }, + + /** + * 构造函数/实例初始化 + * Constructor / instance initialization + * + * @param {String} id 编辑器的ID + * @param {Object} [options={}] 配置选项 Key/Value + * @returns {editormd} 返回editormd的实例对象 + */ + + init : function (id, options) { + + options = options || {}; + + if (typeof id === "object") + { + options = id; + } + + var _this = this; + var classPrefix = this.classPrefix = editormd.classPrefix; + var settings = this.settings = $.extend(true, editormd.defaults, options); + + id = (typeof id === "object") ? settings.id : id; + + var editor = this.editor = $("#" + id); + + this.id = id; + this.lang = settings.lang; + + var classNames = this.classNames = { + textarea : { + html : classPrefix + "html-textarea", + markdown : classPrefix + "markdown-textarea" + } + }; + + settings.pluginPath = (settings.pluginPath === "") ? settings.path + "../plugins/" : settings.pluginPath; + + this.state.watching = (settings.watch) ? true : false; + + if ( !editor.hasClass("editormd") ) { + editor.addClass("editormd"); + } + + editor.css({ + width : (typeof settings.width === "number") ? settings.width + "px" : settings.width, + height : (typeof settings.height === "number") ? settings.height + "px" : settings.height + }); + + if (settings.autoHeight) + { + editor.css("height", "auto"); + } + + var markdownTextarea = this.markdownTextarea = editor.children("textarea"); + + if (markdownTextarea.length < 1) + { + editor.append(""); + markdownTextarea = this.markdownTextarea = editor.children("textarea"); + } + + markdownTextarea.addClass(classNames.textarea.markdown).attr("placeholder", settings.placeholder); + + if (typeof markdownTextarea.attr("name") === "undefined" || markdownTextarea.attr("name") === "") + { + markdownTextarea.attr("name", (settings.name !== "") ? settings.name : id + "-markdown-doc"); + } + + var appendElements = [ + (!settings.readOnly) ? "" : "", + ( (settings.saveHTMLToTextarea) ? "" : "" ), + "
              ", + "
              ", + "
              " + ].join("\n"); + + editor.append(appendElements).addClass(classPrefix + "vertical"); + + if (settings.theme !== "") + { + editor.addClass(classPrefix + "theme-" + settings.theme); + } + + this.mask = editor.children("." + classPrefix + "mask"); + this.containerMask = editor.children("." + classPrefix + "container-mask"); + + if (settings.markdown !== "") + { + markdownTextarea.val(settings.markdown); + } + + if (settings.appendMarkdown !== "") + { + markdownTextarea.val(markdownTextarea.val() + settings.appendMarkdown); + } + + this.htmlTextarea = editor.children("." + classNames.textarea.html); + this.preview = editor.children("." + classPrefix + "preview"); + this.previewContainer = this.preview.children("." + classPrefix + "preview-container"); + + if (settings.previewTheme !== "") + { + this.preview.addClass(classPrefix + "preview-theme-" + settings.previewTheme); + } + + if (typeof define === "function" && define.amd) + { + if (typeof katex !== "undefined") + { + editormd.$katex = katex; + } + + if (settings.searchReplace && !settings.readOnly) + { + editormd.loadCSS(settings.path + "codemirror/addon/dialog/dialog"); + editormd.loadCSS(settings.path + "codemirror/addon/search/matchesonscrollbar"); + } + } + + if ((typeof define === "function" && define.amd) || !settings.autoLoadModules) + { + if (typeof CodeMirror !== "undefined") { + editormd.$CodeMirror = CodeMirror; + } + + if (typeof marked !== "undefined") { + editormd.$marked = marked; + } + + this.setCodeMirror().setToolbar().loadedDisplay(); + } + else + { + this.loadQueues(); + } + + return this; + }, + + /** + * 所需组件加载队列 + * Required components loading queue + * + * @returns {editormd} 返回editormd的实例对象 + */ + + loadQueues : function() { + var _this = this; + var settings = this.settings; + var loadPath = settings.path; + + var loadFlowChartOrSequenceDiagram = function() { + + if (editormd.isIE8) + { + _this.loadedDisplay(); + + return ; + } + + if (settings.flowChart || settings.sequenceDiagram) + { + editormd.loadScript(loadPath + "raphael.min", function() { + + editormd.loadScript(loadPath + "underscore.min", function() { + + if (!settings.flowChart && settings.sequenceDiagram) + { + editormd.loadScript(loadPath + "sequence-diagram.min", function() { + _this.loadedDisplay(); + }); + } + else if (settings.flowChart && !settings.sequenceDiagram) + { + editormd.loadScript(loadPath + "flowchart.min", function() { + editormd.loadScript(loadPath + "jquery.flowchart.min", function() { + _this.loadedDisplay(); + }); + }); + } + else if (settings.flowChart && settings.sequenceDiagram) + { + editormd.loadScript(loadPath + "flowchart.min", function() { + editormd.loadScript(loadPath + "jquery.flowchart.min", function() { + editormd.loadScript(loadPath + "sequence-diagram.min", function() { + _this.loadedDisplay(); + }); + }); + }); + } + }); + + }); + } + else + { + _this.loadedDisplay(); + } + }; + + editormd.loadCSS(loadPath + "codemirror/codemirror.min"); + + if (settings.searchReplace && !settings.readOnly) + { + editormd.loadCSS(loadPath + "codemirror/addon/dialog/dialog"); + editormd.loadCSS(loadPath + "codemirror/addon/search/matchesonscrollbar"); + } + + if (settings.codeFold) + { + editormd.loadCSS(loadPath + "codemirror/addon/fold/foldgutter"); + } + + editormd.loadScript(loadPath + "codemirror/codemirror.min", function() { + editormd.$CodeMirror = CodeMirror; + + editormd.loadScript(loadPath + "codemirror/modes.min", function() { + + editormd.loadScript(loadPath + "codemirror/addons.min", function() { + + _this.setCodeMirror(); + + if (settings.mode !== "gfm" && settings.mode !== "markdown") + { + _this.loadedDisplay(); + + return false; + } + + _this.setToolbar(); + + editormd.loadScript(loadPath + "marked.min", function() { + + editormd.$marked = marked; + + if (settings.previewCodeHighlight) + { + editormd.loadScript(loadPath + "prettify.min", function() { + loadFlowChartOrSequenceDiagram(); + }); + } + else + { + loadFlowChartOrSequenceDiagram(); + } + }); + + }); + + }); + + }); + + return this; + }, + + /** + * 设置 Editor.md 的整体主题,主要是工具栏 + * Setting Editor.md theme + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setTheme : function(theme) { + var editor = this.editor; + var oldTheme = this.settings.theme; + var themePrefix = this.classPrefix + "theme-"; + + editor.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme); + + this.settings.theme = theme; + + return this; + }, + + /** + * 设置 CodeMirror(编辑区)的主题 + * Setting CodeMirror (Editor area) theme + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setEditorTheme : function(theme) { + var settings = this.settings; + settings.editorTheme = theme; + + if (theme !== "default") + { + editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme); + } + + this.cm.setOption("theme", theme); + + return this; + }, + + /** + * setEditorTheme() 的别名 + * setEditorTheme() alias + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setCodeMirrorTheme : function (theme) { + this.setEditorTheme(theme); + + return this; + }, + + /** + * 设置 Editor.md 的主题 + * Setting Editor.md theme + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setPreviewTheme : function(theme) { + var preview = this.preview; + var oldTheme = this.settings.previewTheme; + var themePrefix = this.classPrefix + "preview-theme-"; + + preview.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme); + + this.settings.previewTheme = theme; + + return this; + }, + + /** + * 配置和初始化CodeMirror组件 + * CodeMirror initialization + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setCodeMirror : function() { + var settings = this.settings; + var editor = this.editor; + + if (settings.editorTheme !== "default") + { + editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme); + } + + var codeMirrorConfig = { + mode : settings.mode, + theme : settings.editorTheme, + tabSize : settings.tabSize, + dragDrop : false, + autofocus : settings.autoFocus, + autoCloseTags : settings.autoCloseTags, + readOnly : (settings.readOnly) ? "nocursor" : false, + indentUnit : settings.indentUnit, + lineNumbers : settings.lineNumbers, + lineWrapping : settings.lineWrapping, + extraKeys : { + "Ctrl-Q": function(cm) { + cm.foldCode(cm.getCursor()); + } + }, + foldGutter : settings.codeFold, + gutters : ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], + matchBrackets : settings.matchBrackets, + indentWithTabs : settings.indentWithTabs, + styleActiveLine : settings.styleActiveLine, + styleSelectedText : settings.styleSelectedText, + autoCloseBrackets : settings.autoCloseBrackets, + showTrailingSpace : settings.showTrailingSpace, + highlightSelectionMatches : ( (!settings.matchWordHighlight) ? false : { showToken: (settings.matchWordHighlight === "onselected") ? false : /\w/ } ) + }; + + this.codeEditor = this.cm = editormd.$CodeMirror.fromTextArea(this.markdownTextarea[0], codeMirrorConfig); + this.codeMirror = this.cmElement = editor.children(".CodeMirror"); + + if (settings.value !== "") + { + this.cm.setValue(settings.value); + } + + this.codeMirror.css({ + fontSize : settings.fontSize, + width : (!settings.watch) ? "100%" : "50%" + }); + + if (settings.autoHeight) + { + this.codeMirror.css("height", "auto"); + this.cm.setOption("viewportMargin", Infinity); + } + + if (!settings.lineNumbers) + { + this.codeMirror.find(".CodeMirror-gutters").css("border-right", "none"); + } + + return this; + }, + + /** + * 获取CodeMirror的配置选项 + * Get CodeMirror setting options + * + * @returns {Mixed} return CodeMirror setting option value + */ + + getCodeMirrorOption : function(key) { + return this.cm.getOption(key); + }, + + /** + * 配置和重配置CodeMirror的选项 + * CodeMirror setting options / resettings + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setCodeMirrorOption : function(key, value) { + + this.cm.setOption(key, value); + + return this; + }, + + /** + * 添加 CodeMirror 键盘快捷键 + * Add CodeMirror keyboard shortcuts key map + * + * @returns {editormd} 返回editormd的实例对象 + */ + + addKeyMap : function(map, bottom) { + this.cm.addKeyMap(map, bottom); + + return this; + }, + + /** + * 移除 CodeMirror 键盘快捷键 + * Remove CodeMirror keyboard shortcuts key map + * + * @returns {editormd} 返回editormd的实例对象 + */ + + removeKeyMap : function(map) { + this.cm.removeKeyMap(map); + + return this; + }, + + /** + * 跳转到指定的行 + * Goto CodeMirror line + * + * @param {String|Intiger} line line number or "first"|"last" + * @returns {editormd} 返回editormd的实例对象 + */ + + gotoLine : function (line) { + + var settings = this.settings; + + if (!settings.gotoLine) + { + return this; + } + + var cm = this.cm; + var editor = this.editor; + var count = cm.lineCount(); + var preview = this.preview; + + if (typeof line === "string") + { + if(line === "last") + { + line = count; + } + + if (line === "first") + { + line = 1; + } + } + + if (typeof line !== "number") + { + alert("Error: The line number must be an integer."); + return this; + } + + line = parseInt(line) - 1; + + if (line > count) + { + alert("Error: The line number range 1-" + count); + + return this; + } + + cm.setCursor( {line : line, ch : 0} ); + + var scrollInfo = cm.getScrollInfo(); + var clientHeight = scrollInfo.clientHeight; + var coords = cm.charCoords({line : line, ch : 0}, "local"); + + cm.scrollTo(null, (coords.top + coords.bottom - clientHeight) / 2); + + if (settings.watch) + { + var cmScroll = this.codeMirror.find(".CodeMirror-scroll")[0]; + var height = $(cmScroll).height(); + var scrollTop = cmScroll.scrollTop; + var percent = (scrollTop / cmScroll.scrollHeight); + + if (scrollTop === 0) + { + preview.scrollTop(0); + } + else if (scrollTop + height >= cmScroll.scrollHeight - 16) + { + preview.scrollTop(preview[0].scrollHeight); + } + else + { + preview.scrollTop(preview[0].scrollHeight * percent); + } + } + + cm.focus(); + + return this; + }, + + /** + * 扩展当前实例对象,可同时设置多个或者只设置一个 + * Extend editormd instance object, can mutil setting. + * + * @returns {editormd} this(editormd instance object.) + */ + + extend : function() { + if (typeof arguments[1] !== "undefined") + { + if (typeof arguments[1] === "function") + { + arguments[1] = $.proxy(arguments[1], this); + } + + this[arguments[0]] = arguments[1]; + } + + if (typeof arguments[0] === "object" && typeof arguments[0].length === "undefined") + { + $.extend(true, this, arguments[0]); + } + + return this; + }, + + /** + * 设置或扩展当前实例对象,单个设置 + * Extend editormd instance object, one by one + * + * @param {String|Object} key option key + * @param {String|Object} value option value + * @returns {editormd} this(editormd instance object.) + */ + + set : function (key, value) { + + if (typeof value !== "undefined" && typeof value === "function") + { + value = $.proxy(value, this); + } + + this[key] = value; + + return this; + }, + + /** + * 重新配置 + * Resetting editor options + * + * @param {String|Object} key option key + * @param {String|Object} value option value + * @returns {editormd} this(editormd instance object.) + */ + + config : function(key, value) { + var settings = this.settings; + + if (typeof key === "object") + { + settings = $.extend(true, settings, key); + } + + if (typeof key === "string") + { + settings[key] = value; + } + + this.settings = settings; + this.recreate(); + + return this; + }, + + /** + * 注册事件处理方法 + * Bind editor event handle + * + * @param {String} eventType event type + * @param {Function} callback 回调函数 + * @returns {editormd} this(editormd instance object.) + */ + + on : function(eventType, callback) { + var settings = this.settings; + + if (typeof settings["on" + eventType] !== "undefined") + { + settings["on" + eventType] = $.proxy(callback, this); + } + + return this; + }, + + /** + * 解除事件处理方法 + * Unbind editor event handle + * + * @param {String} eventType event type + * @returns {editormd} this(editormd instance object.) + */ + + off : function(eventType) { + var settings = this.settings; + + if (typeof settings["on" + eventType] !== "undefined") + { + settings["on" + eventType] = function(){}; + } + + return this; + }, + + /** + * 显示工具栏 + * Display toolbar + * + * @param {Function} [callback=function(){}] 回调函数 + * @returns {editormd} 返回editormd的实例对象 + */ + + showToolbar : function(callback) { + var settings = this.settings; + + if(settings.readOnly) { + return this; + } + + if (settings.toolbar && (this.toolbar.length < 1 || this.toolbar.find("." + this.classPrefix + "menu").html() === "") ) + { + this.setToolbar(); + } + + settings.toolbar = true; + + this.toolbar.show(); + this.resize(); + + $.proxy(callback || function(){}, this)(); + + return this; + }, + + /** + * 隐藏工具栏 + * Hide toolbar + * + * @param {Function} [callback=function(){}] 回调函数 + * @returns {editormd} this(editormd instance object.) + */ + + hideToolbar : function(callback) { + var settings = this.settings; + + settings.toolbar = false; + this.toolbar.hide(); + this.resize(); + + $.proxy(callback || function(){}, this)(); + + return this; + }, + + /** + * 页面滚动时工具栏的固定定位 + * Set toolbar in window scroll auto fixed position + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setToolbarAutoFixed : function(fixed) { + + var state = this.state; + var editor = this.editor; + var toolbar = this.toolbar; + var settings = this.settings; + + if (typeof fixed !== "undefined") + { + settings.toolbarAutoFixed = fixed; + } + + var autoFixedHandle = function(){ + var $window = $(window); + var top = $window.scrollTop(); + + if (!settings.toolbarAutoFixed) + { + return false; + } + + if (top - editor.offset().top > 10 && top < editor.height()) + { + toolbar.css({ + position : "fixed", + width : editor.width() + "px", + left : ($window.width() - editor.width()) / 2 + "px" + }); + } + else + { + toolbar.css({ + position : "absolute", + width : "100%", + left : 0 + }); + } + }; + + if (!state.fullscreen && !state.preview && settings.toolbar && settings.toolbarAutoFixed) + { + $(window).bind("scroll", autoFixedHandle); + } + + return this; + }, + + /** + * 配置和初始化工具栏 + * Set toolbar and Initialization + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setToolbar : function() { + var settings = this.settings; + + if(settings.readOnly) { + return this; + } + + var editor = this.editor; + var preview = this.preview; + var classPrefix = this.classPrefix; + + var toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar"); + + if (settings.toolbar && toolbar.length < 1) + { + var toolbarHTML = "
                "; + + editor.append(toolbarHTML); + toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar"); + } + + if (!settings.toolbar) + { + toolbar.hide(); + + return this; + } + + toolbar.show(); + + var icons = (typeof settings.toolbarIcons === "function") ? settings.toolbarIcons() + : ((typeof settings.toolbarIcons === "string") ? editormd.toolbarModes[settings.toolbarIcons] : settings.toolbarIcons); + + var toolbarMenu = toolbar.find("." + this.classPrefix + "menu"), menu = ""; + var pullRight = false; + + for (var i = 0, len = icons.length; i < len; i++) + { + var name = icons[i]; + + if (name === "||") + { + pullRight = true; + } + else if (name === "|") + { + menu += "
              • |
              • "; + } + else + { + var isHeader = (/h(\d)/.test(name)); + var index = name; + + if (name === "watch" && !settings.watch) { + index = "unwatch"; + } + + var title = settings.lang.toolbar[index]; + var iconTexts = settings.toolbarIconTexts[index]; + var iconClass = settings.toolbarIconsClass[index]; + + title = (typeof title === "undefined") ? "" : title; + iconTexts = (typeof iconTexts === "undefined") ? "" : iconTexts; + iconClass = (typeof iconClass === "undefined") ? "" : iconClass; + + var menuItem = pullRight ? "
              • " : "
              • "; + + if (typeof settings.toolbarCustomIcons[name] !== "undefined" && typeof settings.toolbarCustomIcons[name] !== "function") + { + menuItem += settings.toolbarCustomIcons[name]; + } + else + { + menuItem += ""; + menuItem += ""+((isHeader) ? name.toUpperCase() : ( (iconClass === "") ? iconTexts : "") ) + ""; + menuItem += ""; + } + + menuItem += "
              • "; + + menu = pullRight ? menuItem + menu : menu + menuItem; + } + } + + toolbarMenu.html(menu); + + toolbarMenu.find("[title=\"Lowercase\"]").attr("title", settings.lang.toolbar.lowercase); + toolbarMenu.find("[title=\"ucwords\"]").attr("title", settings.lang.toolbar.ucwords); + + this.setToolbarHandler(); + this.setToolbarAutoFixed(); + + return this; + }, + + /** + * 工具栏图标事件处理对象序列 + * Get toolbar icons event handlers + * + * @param {Object} cm CodeMirror的实例对象 + * @param {String} name 要获取的事件处理器名称 + * @returns {Object} 返回处理对象序列 + */ + + dialogLockScreen : function() { + $.proxy(editormd.dialogLockScreen, this)(); + + return this; + }, + + dialogShowMask : function(dialog) { + $.proxy(editormd.dialogShowMask, this)(dialog); + + return this; + }, + + getToolbarHandles : function(name) { + var toolbarHandlers = this.toolbarHandlers = editormd.toolbarHandlers; + + return (name && typeof toolbarIconHandlers[name] !== "undefined") ? toolbarHandlers[name] : toolbarHandlers; + }, + + /** + * 工具栏图标事件处理器 + * Bind toolbar icons event handle + * + * @returns {editormd} 返回editormd的实例对象 + */ + + setToolbarHandler : function() { + var _this = this; + var settings = this.settings; + + if (!settings.toolbar || settings.readOnly) { + return this; + } + + var toolbar = this.toolbar; + var cm = this.cm; + var classPrefix = this.classPrefix; + var toolbarIcons = this.toolbarIcons = toolbar.find("." + classPrefix + "menu > li > a"); + var toolbarIconHandlers = this.getToolbarHandles(); + + toolbarIcons.bind(editormd.mouseOrTouch("click", "touchend"), function(event) { + + var icon = $(this).children(".fa"); + var name = icon.attr("name"); + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (name === "") { + return ; + } + + _this.activeIcon = icon; + + if (typeof toolbarIconHandlers[name] !== "undefined") + { + $.proxy(toolbarIconHandlers[name], _this)(cm); + } + else + { + if (typeof settings.toolbarHandlers[name] !== "undefined") + { + $.proxy(settings.toolbarHandlers[name], _this)(cm, icon, cursor, selection); + } + } + + if (name !== "link" && name !== "reference-link" && name !== "image" && name !== "code-block" && + name !== "preformatted-text" && name !== "watch" && name !== "preview" && name !== "search" && name !== "fullscreen" && name !== "info") + { + cm.focus(); + } + + return false; + + }); + + return this; + }, + + /** + * 动态创建对话框 + * Creating custom dialogs + * + * @param {Object} options 配置项键值对 Key/Value + * @returns {dialog} 返回创建的dialog的jQuery实例对象 + */ + + createDialog : function(options) { + return $.proxy(editormd.createDialog, this)(options); + }, + + /** + * 创建关于Editor.md的对话框 + * Create about Editor.md dialog + * + * @returns {editormd} 返回editormd的实例对象 + */ + + createInfoDialog : function() { + var _this = this; + var editor = this.editor; + var classPrefix = this.classPrefix; + + var infoDialogHTML = [ + "
                ", + "
                ", + "

                " + editormd.title + "v" + editormd.version + "

                ", + "

                " + this.lang.description + "

                ", + "

                " + editormd.homePage + "

                ", + "

                Copyright © 2015 Pandao, The MIT License.

                ", + "
                ", + "", + "
                " + ].join("\n"); + + editor.append(infoDialogHTML); + + var infoDialog = this.infoDialog = editor.children("." + classPrefix + "dialog-info"); + + infoDialog.find("." + classPrefix + "dialog-close").bind(editormd.mouseOrTouch("click", "touchend"), function() { + _this.hideInfoDialog(); + }); + + infoDialog.css("border", (editormd.isIE8) ? "1px solid #ddd" : "").css("z-index", editormd.dialogZindex).show(); + + this.infoDialogPosition(); + + return this; + }, + + /** + * 关于Editor.md对话居中定位 + * Editor.md dialog position handle + * + * @returns {editormd} 返回editormd的实例对象 + */ + + infoDialogPosition : function() { + var infoDialog = this.infoDialog; + + var _infoDialogPosition = function() { + infoDialog.css({ + top : ($(window).height() - infoDialog.height()) / 2 + "px", + left : ($(window).width() - infoDialog.width()) / 2 + "px" + }); + }; + + _infoDialogPosition(); + + $(window).resize(_infoDialogPosition); + + return this; + }, + + /** + * 显示关于Editor.md + * Display about Editor.md dialog + * + * @returns {editormd} 返回editormd的实例对象 + */ + + showInfoDialog : function() { + + $("html,body").css("overflow-x", "hidden"); + + var _this = this; + var editor = this.editor; + var settings = this.settings; + var infoDialog = this.infoDialog = editor.children("." + this.classPrefix + "dialog-info"); + + if (infoDialog.length < 1) + { + this.createInfoDialog(); + } + + this.lockScreen(true); + + this.mask.css({ + opacity : settings.dialogMaskOpacity, + backgroundColor : settings.dialogMaskBgColor + }).show(); + + infoDialog.css("z-index", editormd.dialogZindex).show(); + + this.infoDialogPosition(); + + return this; + }, + + /** + * 隐藏关于Editor.md + * Hide about Editor.md dialog + * + * @returns {editormd} 返回editormd的实例对象 + */ + + hideInfoDialog : function() { + $("html,body").css("overflow-x", ""); + this.infoDialog.hide(); + this.mask.hide(); + this.lockScreen(false); + + return this; + }, + + /** + * 锁屏 + * lock screen + * + * @param {Boolean} lock Boolean 布尔值,是否锁屏 + * @returns {editormd} 返回editormd的实例对象 + */ + + lockScreen : function(lock) { + editormd.lockScreen(lock); + this.resize(); + + return this; + }, + + /** + * 编辑器界面重建,用于动态语言包或模块加载等 + * Recreate editor + * + * @returns {editormd} 返回editormd的实例对象 + */ + + recreate : function() { + var _this = this; + var editor = this.editor; + var settings = this.settings; + + this.codeMirror.remove(); + + this.setCodeMirror(); + + if (!settings.readOnly) + { + if (editor.find(".editormd-dialog").length > 0) { + editor.find(".editormd-dialog").remove(); + } + + if (settings.toolbar) + { + this.getToolbarHandles(); + this.setToolbar(); + } + } + + this.loadedDisplay(true); + + return this; + }, + + /** + * 高亮预览HTML的pre代码部分 + * highlight of preview codes + * + * @returns {editormd} 返回editormd的实例对象 + */ + + previewCodeHighlight : function() { + var settings = this.settings; + var previewContainer = this.previewContainer; + + if (settings.previewCodeHighlight) + { + previewContainer.find("pre").addClass("prettyprint linenums"); + + if (typeof prettyPrint !== "undefined") + { + prettyPrint(); + } + } + + return this; + }, + + /** + * 解析TeX(KaTeX)科学公式 + * TeX(KaTeX) Renderer + * + * @returns {editormd} 返回editormd的实例对象 + */ + + katexRender : function() { + + if (timer === null) + { + return this; + } + + this.previewContainer.find("." + editormd.classNames.tex).each(function(){ + var tex = $(this); + editormd.$katex.render(tex.text(), tex[0]); + + tex.find(".katex").css("font-size", "1.6em"); + }); + + return this; + }, + + /** + * 解析和渲染流程图及时序图 + * FlowChart and SequenceDiagram Renderer + * + * @returns {editormd} 返回editormd的实例对象 + */ + + flowChartAndSequenceDiagramRender : function() { + var $this = this; + var settings = this.settings; + var previewContainer = this.previewContainer; + + if (editormd.isIE8) { + return this; + } + + if (settings.flowChart) { + if (flowchartTimer === null) { + return this; + } + + previewContainer.find(".flowchart").flowChart(); + } + + if (settings.sequenceDiagram) { + previewContainer.find(".sequence-diagram").sequenceDiagram({theme: "simple"}); + } + + var preview = $this.preview; + var codeMirror = $this.codeMirror; + var codeView = codeMirror.find(".CodeMirror-scroll"); + + var height = codeView.height(); + var scrollTop = codeView.scrollTop(); + var percent = (scrollTop / codeView[0].scrollHeight); + var tocHeight = 0; + + preview.find(".markdown-toc-list").each(function(){ + tocHeight += $(this).height(); + }); + + var tocMenuHeight = preview.find(".editormd-toc-menu").height(); + tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight; + + if (scrollTop === 0) + { + preview.scrollTop(0); + } + else if (scrollTop + height >= codeView[0].scrollHeight - 16) + { + preview.scrollTop(preview[0].scrollHeight); + } + else + { + preview.scrollTop((preview[0].scrollHeight + tocHeight + tocMenuHeight) * percent); + } + + return this; + }, + + /** + * 注册键盘快捷键处理 + * Register CodeMirror keyMaps (keyboard shortcuts). + * + * @param {Object} keyMap KeyMap key/value {"(Ctrl/Shift/Alt)-Key" : function(){}} + * @returns {editormd} return this + */ + + registerKeyMaps : function(keyMap) { + + var _this = this; + var cm = this.cm; + var settings = this.settings; + var toolbarHandlers = editormd.toolbarHandlers; + var disabledKeyMaps = settings.disabledKeyMaps; + + keyMap = keyMap || null; + + if (keyMap) + { + for (var i in keyMap) + { + if ($.inArray(i, disabledKeyMaps) < 0) + { + var map = {}; + map[i] = keyMap[i]; + + cm.addKeyMap(keyMap); + } + } + } + else + { + for (var k in editormd.keyMaps) + { + var _keyMap = editormd.keyMaps[k]; + var handle = (typeof _keyMap === "string") ? $.proxy(toolbarHandlers[_keyMap], _this) : $.proxy(_keyMap, _this); + + if ($.inArray(k, ["F9", "F10", "F11"]) < 0 && $.inArray(k, disabledKeyMaps) < 0) + { + var _map = {}; + _map[k] = handle; + + cm.addKeyMap(_map); + } + } + + $(window).keydown(function(event) { + + var keymaps = { + "120" : "F9", + "121" : "F10", + "122" : "F11" + }; + + if ( $.inArray(keymaps[event.keyCode], disabledKeyMaps) < 0 ) + { + switch (event.keyCode) + { + case 120: + $.proxy(toolbarHandlers["watch"], _this)(); + return false; + break; + + case 121: + $.proxy(toolbarHandlers["preview"], _this)(); + return false; + break; + + case 122: + $.proxy(toolbarHandlers["fullscreen"], _this)(); + return false; + break; + + default: + break; + } + } + }); + } + + return this; + }, + + /** + * 绑定同步滚动 + * + * @returns {editormd} return this + */ + + bindScrollEvent : function() { + + var _this = this; + var preview = this.preview; + var settings = this.settings; + var codeMirror = this.codeMirror; + var mouseOrTouch = editormd.mouseOrTouch; + + if (!settings.syncScrolling) { + return this; + } + + var cmBindScroll = function() { + codeMirror.find(".CodeMirror-scroll").bind(mouseOrTouch("scroll", "touchmove"), function(event) { + var height = $(this).height(); + var scrollTop = $(this).scrollTop(); + var percent = (scrollTop / $(this)[0].scrollHeight); + + var tocHeight = 0; + + preview.find(".markdown-toc-list").each(function(){ + tocHeight += $(this).height(); + }); + + var tocMenuHeight = preview.find(".editormd-toc-menu").height(); + tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight; + + if (scrollTop === 0) + { + preview.scrollTop(0); + } + else if (scrollTop + height >= $(this)[0].scrollHeight - 16) + { + preview.scrollTop(preview[0].scrollHeight); + } + else + { + preview.scrollTop((preview[0].scrollHeight + tocHeight + tocMenuHeight) * percent); + } + + $.proxy(settings.onscroll, _this)(event); + }); + }; + + var cmUnbindScroll = function() { + codeMirror.find(".CodeMirror-scroll").unbind(mouseOrTouch("scroll", "touchmove")); + }; + + var previewBindScroll = function() { + + preview.bind(mouseOrTouch("scroll", "touchmove"), function(event) { + var height = $(this).height(); + var scrollTop = $(this).scrollTop(); + var percent = (scrollTop / $(this)[0].scrollHeight); + var codeView = codeMirror.find(".CodeMirror-scroll"); + + if(scrollTop === 0) + { + codeView.scrollTop(0); + } + else if (scrollTop + height >= $(this)[0].scrollHeight) + { + codeView.scrollTop(codeView[0].scrollHeight); + } + else + { + codeView.scrollTop(codeView[0].scrollHeight * percent); + } + + $.proxy(settings.onpreviewscroll, _this)(event); + }); + + }; + + var previewUnbindScroll = function() { + preview.unbind(mouseOrTouch("scroll", "touchmove")); + }; + + codeMirror.bind({ + mouseover : cmBindScroll, + mouseout : cmUnbindScroll, + touchstart : cmBindScroll, + touchend : cmUnbindScroll + }); + + if (settings.syncScrolling === "single") { + return this; + } + + preview.bind({ + mouseover : previewBindScroll, + mouseout : previewUnbindScroll, + touchstart : previewBindScroll, + touchend : previewUnbindScroll + }); + + return this; + }, + + bindChangeEvent : function() { + + var _this = this; + var cm = this.cm; + var settings = this.settings; + + if (!settings.syncScrolling) { + return this; + } + + cm.on("change", function(_cm, changeObj) { + + if (settings.watch) + { + _this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px"); + } + + timer = setTimeout(function() { + clearTimeout(timer); + _this.save(); + timer = null; + }, settings.delay); + }); + + return this; + }, + + /** + * 加载队列完成之后的显示处理 + * Display handle of the module queues loaded after. + * + * @param {Boolean} recreate 是否为重建编辑器 + * @returns {editormd} 返回editormd的实例对象 + */ + + loadedDisplay : function(recreate) { + + recreate = recreate || false; + + var _this = this; + var editor = this.editor; + var preview = this.preview; + var settings = this.settings; + + this.containerMask.hide(); + + this.save(); + + if (settings.watch) { + preview.show(); + } + + editor.data("oldWidth", editor.width()).data("oldHeight", editor.height()); // 为了兼容Zepto + + this.resize(); + this.registerKeyMaps(); + + $(window).resize(function(){ + _this.resize(); + }); + + this.bindScrollEvent().bindChangeEvent(); + + if (!recreate) + { + $.proxy(settings.onload, this)(); + } + + this.state.loaded = true; + + return this; + }, + + /** + * 设置编辑器的宽度 + * Set editor width + * + * @param {Number|String} width 编辑器宽度值 + * @returns {editormd} 返回editormd的实例对象 + */ + + width : function(width) { + + this.editor.css("width", (typeof width === "number") ? width + "px" : width); + this.resize(); + + return this; + }, + + /** + * 设置编辑器的高度 + * Set editor height + * + * @param {Number|String} height 编辑器高度值 + * @returns {editormd} 返回editormd的实例对象 + */ + + height : function(height) { + + this.editor.css("height", (typeof height === "number") ? height + "px" : height); + this.resize(); + + return this; + }, + + /** + * 调整编辑器的尺寸和布局 + * Resize editor layout + * + * @param {Number|String} [width=null] 编辑器宽度值 + * @param {Number|String} [height=null] 编辑器高度值 + * @returns {editormd} 返回editormd的实例对象 + */ + + resize : function(width, height) { + + width = width || null; + height = height || null; + + var state = this.state; + var editor = this.editor; + var preview = this.preview; + var toolbar = this.toolbar; + var settings = this.settings; + var codeMirror = this.codeMirror; + + if (width) + { + editor.css("width", (typeof width === "number") ? width + "px" : width); + } + + if (settings.autoHeight && !state.fullscreen && !state.preview) + { + editor.css("height", "auto"); + codeMirror.css("height", "auto"); + } + else + { + if (height) + { + editor.css("height", (typeof height === "number") ? height + "px" : height); + } + + if (state.fullscreen) + { + editor.height($(window).height()); + } + + if (settings.toolbar && !settings.readOnly) + { + codeMirror.css("margin-top", toolbar.height() + 1).height(editor.height() - toolbar.height()); + } + else + { + codeMirror.css("margin-top", 0).height(editor.height()); + } + } + + if(settings.watch) + { + codeMirror.width(editor.width() / 2); + preview.width((!state.preview) ? editor.width() / 2 : editor.width()); + + this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px"); + + if (settings.toolbar && !settings.readOnly) + { + preview.css("top", toolbar.height() + 1); + } + else + { + preview.css("top", 0); + } + + if (settings.autoHeight && !state.fullscreen && !state.preview) + { + preview.height(""); + } + else + { + var previewHeight = (settings.toolbar && !settings.readOnly) ? editor.height() - toolbar.height() : editor.height(); + + preview.height(previewHeight); + } + } + else + { + codeMirror.width(editor.width()); + preview.hide(); + } + + if (state.loaded) + { + $.proxy(settings.onresize, this)(); + } + + return this; + }, + + /** + * 解析和保存Markdown代码 + * Parse & Saving Markdown source code + * + * @returns {editormd} 返回editormd的实例对象 + */ + + save : function() { + + var _this = this; + var state = this.state; + var settings = this.settings; + + if (timer === null && !(!settings.watch && state.preview)) + { + return this; + } + + var cm = this.cm; + var cmValue = cm.getValue(); + var previewContainer = this.previewContainer; + + if (settings.mode !== "gfm" && settings.mode !== "markdown") + { + this.markdownTextarea.val(cmValue); + + return this; + } + + var marked = editormd.$marked; + var markdownToC = this.markdownToC = []; + var rendererOptions = this.markedRendererOptions = { + toc : settings.toc, + tocm : settings.tocm, + tocStartLevel : settings.tocStartLevel, + pageBreak : settings.pageBreak, + taskList : settings.taskList, + emoji : settings.emoji, + tex : settings.tex, + atLink : settings.atLink, // for @link + emailLink : settings.emailLink, // for mail address auto link + flowChart : settings.flowChart, + sequenceDiagram : settings.sequenceDiagram, + previewCodeHighlight : settings.previewCodeHighlight, + }; + + var markedOptions = this.markedOptions = { + renderer : editormd.markedRenderer(markdownToC, rendererOptions), + gfm : true, + tables : true, + breaks : true, + pedantic : false, + sanitize : (settings.htmlDecode) ? false : true, // 关闭忽略HTML标签,即开启识别HTML标签,默认为false + smartLists : true, + smartypants : true + }; + + marked.setOptions(markedOptions); + + var newMarkdownDoc = editormd.$marked(cmValue, markedOptions); + + //console.info("cmValue", cmValue, newMarkdownDoc); + + newMarkdownDoc = editormd.filterHTMLTags(newMarkdownDoc, settings.htmlDecode); + + //console.error("cmValue", cmValue, newMarkdownDoc); + + this.markdownTextarea.text(cmValue); + + cm.save(); + + if (settings.saveHTMLToTextarea) + { + this.htmlTextarea.text(newMarkdownDoc); + } + + if(settings.watch || (!settings.watch && state.preview)) + { + previewContainer.html(newMarkdownDoc); + + this.previewCodeHighlight(); + + if (settings.toc) + { + var tocContainer = (settings.tocContainer === "") ? previewContainer : $(settings.tocContainer); + var tocMenu = tocContainer.find("." + this.classPrefix + "toc-menu"); + + tocContainer.attr("previewContainer", (settings.tocContainer === "") ? "true" : "false"); + + if (settings.tocContainer !== "" && tocMenu.length > 0) + { + tocMenu.remove(); + } + + editormd.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel); + + if (settings.tocDropdown || tocContainer.find("." + this.classPrefix + "toc-menu").length > 0) + { + editormd.tocDropdownMenu(tocContainer, (settings.tocTitle !== "") ? settings.tocTitle : this.lang.tocTitle); + } + + if (settings.tocContainer !== "") + { + previewContainer.find(".markdown-toc").css("border", "none"); + } + } + + if (settings.tex) + { + if (!editormd.kaTeXLoaded && settings.autoLoadModules) + { + editormd.loadKaTeX(function() { + editormd.$katex = katex; + editormd.kaTeXLoaded = true; + _this.katexRender(); + }); + } + else + { + editormd.$katex = katex; + this.katexRender(); + } + } + + if (settings.flowChart || settings.sequenceDiagram) + { + flowchartTimer = setTimeout(function(){ + clearTimeout(flowchartTimer); + _this.flowChartAndSequenceDiagramRender(); + flowchartTimer = null; + }, 10); + } + + if (state.loaded) + { + $.proxy(settings.onchange, this)(); + } + } + + return this; + }, + + /** + * 聚焦光标位置 + * Focusing the cursor position + * + * @returns {editormd} 返回editormd的实例对象 + */ + + focus : function() { + this.cm.focus(); + + return this; + }, + + /** + * 设置光标的位置 + * Set cursor position + * + * @param {Object} cursor 要设置的光标位置键值对象,例:{line:1, ch:0} + * @returns {editormd} 返回editormd的实例对象 + */ + + setCursor : function(cursor) { + this.cm.setCursor(cursor); + + return this; + }, + + /** + * 获取当前光标的位置 + * Get the current position of the cursor + * + * @returns {Cursor} 返回一个光标Cursor对象 + */ + + getCursor : function() { + return this.cm.getCursor(); + }, + + /** + * 设置光标选中的范围 + * Set cursor selected ranges + * + * @param {Object} from 开始位置的光标键值对象,例:{line:1, ch:0} + * @param {Object} to 结束位置的光标键值对象,例:{line:1, ch:0} + * @returns {editormd} 返回editormd的实例对象 + */ + + setSelection : function(from, to) { + + this.cm.setSelection(from, to); + + return this; + }, + + /** + * 获取光标选中的文本 + * Get the texts from cursor selected + * + * @returns {String} 返回选中文本的字符串形式 + */ + + getSelection : function() { + return this.cm.getSelection(); + }, + + /** + * 设置光标选中的文本范围 + * Set the cursor selection ranges + * + * @param {Array} ranges cursor selection ranges array + * @returns {Array} return this + */ + + setSelections : function(ranges) { + this.cm.setSelections(ranges); + + return this; + }, + + /** + * 获取光标选中的文本范围 + * Get the cursor selection ranges + * + * @returns {Array} return selection ranges array + */ + + getSelections : function() { + return this.cm.getSelections(); + }, + + /** + * 替换当前光标选中的文本或在当前光标处插入新字符 + * Replace the text at the current cursor selected or insert a new character at the current cursor position + * + * @param {String} value 要插入的字符值 + * @returns {editormd} 返回editormd的实例对象 + */ + + replaceSelection : function(value) { + this.cm.replaceSelection(value); + + return this; + }, + + /** + * 在当前光标处插入新字符 + * Insert a new character at the current cursor position + * + * 同replaceSelection()方法 + * With the replaceSelection() method + * + * @param {String} value 要插入的字符值 + * @returns {editormd} 返回editormd的实例对象 + */ + + insertValue : function(value) { + this.replaceSelection(value); + + return this; + }, + + /** + * 追加markdown + * append Markdown to editor + * + * @param {String} md 要追加的markdown源文档 + * @returns {editormd} 返回editormd的实例对象 + */ + + appendMarkdown : function(md) { + var settings = this.settings; + var cm = this.cm; + + cm.setValue(cm.getValue() + md); + + return this; + }, + + /** + * 设置和传入编辑器的markdown源文档 + * Set Markdown source document + * + * @param {String} md 要传入的markdown源文档 + * @returns {editormd} 返回editormd的实例对象 + */ + + setMarkdown : function(md) { + this.cm.setValue(md || this.settings.markdown); + + return this; + }, + + /** + * 获取编辑器的markdown源文档 + * Set Editor.md markdown/CodeMirror value + * + * @returns {editormd} 返回editormd的实例对象 + */ + + getMarkdown : function() { + return this.cm.getValue(); + }, + + /** + * 获取编辑器的源文档 + * Get CodeMirror value + * + * @returns {editormd} 返回editormd的实例对象 + */ + + getValue : function() { + return this.cm.getValue(); + }, + + /** + * 设置编辑器的源文档 + * Set CodeMirror value + * + * @param {String} value set code/value/string/text + * @returns {editormd} 返回editormd的实例对象 + */ + + setValue : function(value) { + this.cm.setValue(value); + + return this; + }, + + /** + * 清空编辑器 + * Empty CodeMirror editor container + * + * @returns {editormd} 返回editormd的实例对象 + */ + + clear : function() { + this.cm.setValue(""); + + return this; + }, + + /** + * 获取解析后存放在Textarea的HTML源码 + * Get parsed html code from Textarea + * + * @returns {String} 返回HTML源码 + */ + + getHTML : function() { + if (!this.settings.saveHTMLToTextarea) + { + alert("Error: settings.saveHTMLToTextarea == false"); + + return false; + } + + return this.htmlTextarea.val(); + }, + + /** + * getHTML()的别名 + * getHTML (alias) + * + * @returns {String} Return html code 返回HTML源码 + */ + + getTextareaSavedHTML : function() { + return this.getHTML(); + }, + + /** + * 获取预览窗口的HTML源码 + * Get html from preview container + * + * @returns {editormd} 返回editormd的实例对象 + */ + + getPreviewedHTML : function() { + if (!this.settings.watch) + { + alert("Error: settings.watch == false"); + + return false; + } + + return this.previewContainer.html(); + }, + + /** + * 开启实时预览 + * Enable real-time watching + * + * @returns {editormd} 返回editormd的实例对象 + */ + + watch : function(callback) { + var settings = this.settings; + + if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0) + { + return this; + } + + this.state.watching = settings.watch = true; + this.preview.show(); + + if (this.toolbar) + { + var watchIcon = settings.toolbarIconsClass.watch; + var unWatchIcon = settings.toolbarIconsClass.unwatch; + + var icon = this.toolbar.find(".fa[name=watch]"); + icon.parent().attr("title", settings.lang.toolbar.watch); + icon.removeClass(unWatchIcon).addClass(watchIcon); + } + + this.codeMirror.css("border-right", "1px solid #ddd").width(this.editor.width() / 2); + + timer = 0; + + this.save().resize(); + + if (!settings.onwatch) + { + settings.onwatch = callback || function() {}; + } + + $.proxy(settings.onwatch, this)(); + + return this; + }, + + /** + * 关闭实时预览 + * Disable real-time watching + * + * @returns {editormd} 返回editormd的实例对象 + */ + + unwatch : function(callback) { + var settings = this.settings; + this.state.watching = settings.watch = false; + this.preview.hide(); + + if (this.toolbar) + { + var watchIcon = settings.toolbarIconsClass.watch; + var unWatchIcon = settings.toolbarIconsClass.unwatch; + + var icon = this.toolbar.find(".fa[name=watch]"); + icon.parent().attr("title", settings.lang.toolbar.unwatch); + icon.removeClass(watchIcon).addClass(unWatchIcon); + } + + this.codeMirror.css("border-right", "none").width(this.editor.width()); + + this.resize(); + + if (!settings.onunwatch) + { + settings.onunwatch = callback || function() {}; + } + + $.proxy(settings.onunwatch, this)(); + + return this; + }, + + /** + * 显示编辑器 + * Show editor + * + * @param {Function} [callback=function()] 回调函数 + * @returns {editormd} 返回editormd的实例对象 + */ + + show : function(callback) { + callback = callback || function() {}; + + var _this = this; + this.editor.show(0, function() { + $.proxy(callback, _this)(); + }); + + return this; + }, + + /** + * 隐藏编辑器 + * Hide editor + * + * @param {Function} [callback=function()] 回调函数 + * @returns {editormd} 返回editormd的实例对象 + */ + + hide : function(callback) { + callback = callback || function() {}; + + var _this = this; + this.editor.hide(0, function() { + $.proxy(callback, _this)(); + }); + + return this; + }, + + /** + * 隐藏编辑器部分,只预览HTML + * Enter preview html state + * + * @returns {editormd} 返回editormd的实例对象 + */ + + previewing : function() { + + var _this = this; + var editor = this.editor; + var preview = this.preview; + var toolbar = this.toolbar; + var settings = this.settings; + var codeMirror = this.codeMirror; + var previewContainer = this.previewContainer; + + if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0) { + return this; + } + + if (settings.toolbar && toolbar) { + toolbar.toggle(); + toolbar.find(".fa[name=preview]").toggleClass("active"); + } + + codeMirror.toggle(); + + var escHandle = function(event) { + if (event.shiftKey && event.keyCode === 27) { + _this.previewed(); + } + }; + + if (codeMirror.css("display") === "none") // 为了兼容Zepto,而不使用codeMirror.is(":hidden") + { + this.state.preview = true; + + if (this.state.fullscreen) { + preview.css("background", "#fff"); + } + + editor.find("." + this.classPrefix + "preview-close-btn").show().bind(editormd.mouseOrTouch("click", "touchend"), function(){ + _this.previewed(); + }); + + if (!settings.watch) + { + this.save(); + } + else + { + previewContainer.css("padding", ""); + } + + previewContainer.addClass(this.classPrefix + "preview-active"); + + preview.show().css({ + position : "", + top : 0, + width : editor.width(), + height : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height() + }); + + if (this.state.loaded) + { + $.proxy(settings.onpreviewing, this)(); + } + + $(window).bind("keyup", escHandle); + } + else + { + $(window).unbind("keyup", escHandle); + this.previewed(); + } + }, + + /** + * 显示编辑器部分,退出只预览HTML + * Exit preview html state + * + * @returns {editormd} 返回editormd的实例对象 + */ + + previewed : function() { + + var editor = this.editor; + var preview = this.preview; + var toolbar = this.toolbar; + var settings = this.settings; + var previewContainer = this.previewContainer; + var previewCloseBtn = editor.find("." + this.classPrefix + "preview-close-btn"); + + this.state.preview = false; + + this.codeMirror.show(); + + if (settings.toolbar) { + toolbar.show(); + } + + preview[(settings.watch) ? "show" : "hide"](); + + previewCloseBtn.hide().unbind(editormd.mouseOrTouch("click", "touchend")); + + previewContainer.removeClass(this.classPrefix + "preview-active"); + + if (settings.watch) + { + previewContainer.css("padding", "20px"); + } + + preview.css({ + background : null, + position : "absolute", + width : editor.width() / 2, + height : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height() - toolbar.height(), + top : (settings.toolbar) ? toolbar.height() : 0 + }); + + if (this.state.loaded) + { + $.proxy(settings.onpreviewed, this)(); + } + + return this; + }, + + /** + * 编辑器全屏显示 + * Fullscreen show + * + * @returns {editormd} 返回editormd的实例对象 + */ + + fullscreen : function() { + + var _this = this; + var state = this.state; + var editor = this.editor; + var preview = this.preview; + var toolbar = this.toolbar; + var settings = this.settings; + var fullscreenClass = this.classPrefix + "fullscreen"; + + if (toolbar) { + toolbar.find(".fa[name=fullscreen]").parent().toggleClass("active"); + } + + var escHandle = function(event) { + if (!event.shiftKey && event.keyCode === 27) + { + if (state.fullscreen) + { + _this.fullscreenExit(); + } + } + }; + + if (!editor.hasClass(fullscreenClass)) + { + state.fullscreen = true; + + $("html,body").css("overflow", "hidden"); + + editor.css({ + width : $(window).width(), + height : $(window).height() + }).addClass(fullscreenClass); + + this.resize(); + + $.proxy(settings.onfullscreen, this)(); + + $(window).bind("keyup", escHandle); + } + else + { + $(window).unbind("keyup", escHandle); + this.fullscreenExit(); + } + + return this; + }, + + /** + * 编辑器退出全屏显示 + * Exit fullscreen state + * + * @returns {editormd} 返回editormd的实例对象 + */ + + fullscreenExit : function() { + + var editor = this.editor; + var settings = this.settings; + var toolbar = this.toolbar; + var fullscreenClass = this.classPrefix + "fullscreen"; + + this.state.fullscreen = false; + + if (toolbar) { + toolbar.find(".fa[name=fullscreen]").parent().removeClass("active"); + } + + $("html,body").css("overflow", ""); + + editor.css({ + width : editor.data("oldWidth"), + height : editor.data("oldHeight") + }).removeClass(fullscreenClass); + + this.resize(); + + $.proxy(settings.onfullscreenExit, this)(); + + return this; + }, + + /** + * 加载并执行插件 + * Load and execute the plugin + * + * @param {String} name plugin name / function name + * @param {String} path plugin load path + * @returns {editormd} 返回editormd的实例对象 + */ + + executePlugin : function(name, path) { + + var _this = this; + var cm = this.cm; + var settings = this.settings; + + path = settings.pluginPath + path; + + if (typeof define === "function") + { + if (typeof this[name] === "undefined") + { + alert("Error: " + name + " plugin is not found, you are not load this plugin."); + + return this; + } + + this[name](cm); + + return this; + } + + if ($.inArray(path, editormd.loadFiles.plugin) < 0) + { + editormd.loadPlugin(path, function() { + editormd.loadPlugins[name] = _this[name]; + _this[name](cm); + }); + } + else + { + $.proxy(editormd.loadPlugins[name], this)(cm); + } + + return this; + }, + + /** + * 搜索替换 + * Search & replace + * + * @param {String} command CodeMirror serach commands, "find, fintNext, fintPrev, clearSearch, replace, replaceAll" + * @returns {editormd} return this + */ + + search : function(command) { + var settings = this.settings; + + if (!settings.searchReplace) + { + alert("Error: settings.searchReplace == false"); + return this; + } + + if (!settings.readOnly) + { + this.cm.execCommand(command || "find"); + } + + return this; + }, + + searchReplace : function() { + this.search("replace"); + + return this; + }, + + searchReplaceAll : function() { + this.search("replaceAll"); + + return this; + } + }; + + editormd.fn.init.prototype = editormd.fn; + + /** + * 锁屏 + * lock screen when dialog opening + * + * @returns {void} + */ + + editormd.dialogLockScreen = function() { + var settings = this.settings || {dialogLockScreen : true}; + + if (settings.dialogLockScreen) + { + $("html,body").css("overflow", "hidden"); + this.resize(); + } + }; + + /** + * 显示透明背景层 + * Display mask layer when dialog opening + * + * @param {Object} dialog dialog jQuery object + * @returns {void} + */ + + editormd.dialogShowMask = function(dialog) { + var editor = this.editor; + var settings = this.settings || {dialogShowMask : true}; + + dialog.css({ + top : ($(window).height() - dialog.height()) / 2 + "px", + left : ($(window).width() - dialog.width()) / 2 + "px" + }); + + if (settings.dialogShowMask) { + editor.children("." + this.classPrefix + "mask").css("z-index", parseInt(dialog.css("z-index")) - 1).show(); + } + }; + + editormd.toolbarHandlers = { + undo : function() { + this.cm.undo(); + }, + + redo : function() { + this.cm.redo(); + }, + + bold : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("**" + selection + "**"); + + if(selection === "") { + cm.setCursor(cursor.line, cursor.ch + 2); + } + }, + + del : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("~~" + selection + "~~"); + + if(selection === "") { + cm.setCursor(cursor.line, cursor.ch + 2); + } + }, + + italic : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("*" + selection + "*"); + + if(selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + + quote : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("> " + selection); + cm.setCursor(cursor.line, cursor.ch + 2); + } + else + { + cm.replaceSelection("> " + selection); + } + + //cm.replaceSelection("> " + selection); + //cm.setCursor(cursor.line, (selection === "") ? cursor.ch + 2 : cursor.ch + selection.length + 2); + }, + + ucfirst : function() { + var cm = this.cm; + var selection = cm.getSelection(); + var selections = cm.listSelections(); + + cm.replaceSelection(editormd.firstUpperCase(selection)); + cm.setSelections(selections); + }, + + ucwords : function() { + var cm = this.cm; + var selection = cm.getSelection(); + var selections = cm.listSelections(); + + cm.replaceSelection(editormd.wordsFirstUpperCase(selection)); + cm.setSelections(selections); + }, + + uppercase : function() { + var cm = this.cm; + var selection = cm.getSelection(); + var selections = cm.listSelections(); + + cm.replaceSelection(selection.toUpperCase()); + cm.setSelections(selections); + }, + + lowercase : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + var selections = cm.listSelections(); + + cm.replaceSelection(selection.toLowerCase()); + cm.setSelections(selections); + }, + + h1 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("# " + selection); + cm.setCursor(cursor.line, cursor.ch + 2); + } + else + { + cm.replaceSelection("# " + selection); + } + }, + + h2 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("## " + selection); + cm.setCursor(cursor.line, cursor.ch + 3); + } + else + { + cm.replaceSelection("## " + selection); + } + }, + + h3 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("### " + selection); + cm.setCursor(cursor.line, cursor.ch + 4); + } + else + { + cm.replaceSelection("### " + selection); + } + }, + + h4 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("#### " + selection); + cm.setCursor(cursor.line, cursor.ch + 5); + } + else + { + cm.replaceSelection("#### " + selection); + } + }, + + h5 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("##### " + selection); + cm.setCursor(cursor.line, cursor.ch + 6); + } + else + { + cm.replaceSelection("##### " + selection); + } + }, + + h6 : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (cursor.ch !== 0) + { + cm.setCursor(cursor.line, 0); + cm.replaceSelection("###### " + selection); + cm.setCursor(cursor.line, cursor.ch + 7); + } + else + { + cm.replaceSelection("###### " + selection); + } + }, + + "list-ul" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (selection === "") + { + cm.replaceSelection("- " + selection); + } + else + { + var selectionText = selection.split("\n"); + + for (var i = 0, len = selectionText.length; i < len; i++) + { + selectionText[i] = (selectionText[i] === "") ? "" : "- " + selectionText[i]; + } + + cm.replaceSelection(selectionText.join("\n")); + } + }, + + "list-ol" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if(selection === "") + { + cm.replaceSelection("1. " + selection); + } + else + { + var selectionText = selection.split("\n"); + + for (var i = 0, len = selectionText.length; i < len; i++) + { + selectionText[i] = (selectionText[i] === "") ? "" : (i+1) + ". " + selectionText[i]; + } + + cm.replaceSelection(selectionText.join("\n")); + } + }, + + hr : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection(((cursor.ch !== 0) ? "\n\n" : "\n") + "------------\n\n"); + }, + + tex : function() { + if (!this.settings.tex) + { + alert("settings.tex === false"); + return this; + } + + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("$$" + selection + "$$"); + + if(selection === "") { + cm.setCursor(cursor.line, cursor.ch + 2); + } + }, + + link : function() { + this.executePlugin("linkDialog", "link-dialog/link-dialog"); + }, + + "reference-link" : function() { + this.executePlugin("referenceLinkDialog", "reference-link-dialog/reference-link-dialog"); + }, + + pagebreak : function() { + if (!this.settings.pageBreak) + { + alert("settings.pageBreak === false"); + return this; + } + + var cm = this.cm; + var selection = cm.getSelection(); + + cm.replaceSelection("\r\n[========]\r\n"); + }, + + image : function() { + this.executePlugin("imageDialog", "image-dialog/image-dialog"); + }, + + code : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection("`" + selection + "`"); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + + "code-block" : function() { + this.executePlugin("codeBlockDialog", "code-block-dialog/code-block-dialog"); + }, + + "preformatted-text" : function() { + this.executePlugin("preformattedTextDialog", "preformatted-text-dialog/preformatted-text-dialog"); + }, + + table : function() { + this.executePlugin("tableDialog", "table-dialog/table-dialog"); + }, + + datetime : function() { + var cm = this.cm; + var selection = cm.getSelection(); + var date = new Date(); + var langName = this.settings.lang.name; + var datefmt = editormd.dateFormat() + " " + editormd.dateFormat((langName === "zh-cn" || langName === "zh-tw") ? "cn-week-day" : "week-day"); + + cm.replaceSelection(datefmt); + }, + + emoji : function() { + this.executePlugin("emojiDialog", "emoji-dialog/emoji-dialog"); + }, + + "html-entities" : function() { + this.executePlugin("htmlEntitiesDialog", "html-entities-dialog/html-entities-dialog"); + }, + + "goto-line" : function() { + this.executePlugin("gotoLineDialog", "goto-line-dialog/goto-line-dialog"); + }, + + watch : function() { + this[this.settings.watch ? "unwatch" : "watch"](); + }, + + preview : function() { + this.previewing(); + }, + + fullscreen : function() { + this.fullscreen(); + }, + + clear : function() { + this.clear(); + }, + + search : function() { + this.search(); + }, + + help : function() { + this.executePlugin("helpDialog", "help-dialog/help-dialog"); + }, + + info : function() { + this.showInfoDialog(); + } + }; + + editormd.keyMaps = { + "Ctrl-1" : "h1", + "Ctrl-2" : "h2", + "Ctrl-3" : "h3", + "Ctrl-4" : "h4", + "Ctrl-5" : "h5", + "Ctrl-6" : "h6", + "Ctrl-B" : "bold", // if this is string == editormd.toolbarHandlers.xxxx + "Ctrl-D" : "datetime", + + "Ctrl-E" : function() { // emoji + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (!this.settings.emoji) + { + alert("Error: settings.emoji == false"); + return ; + } + + cm.replaceSelection(":" + selection + ":"); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + "Ctrl-Alt-G" : "goto-line", + "Ctrl-H" : "hr", + "Ctrl-I" : "italic", + "Ctrl-K" : "code", + + "Ctrl-L" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + var title = (selection === "") ? "" : " \""+selection+"\""; + + cm.replaceSelection("[" + selection + "]("+title+")"); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + "Ctrl-U" : "list-ul", + + "Shift-Ctrl-A" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + if (!this.settings.atLink) + { + alert("Error: settings.atLink == false"); + return ; + } + + cm.replaceSelection("@" + selection); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 1); + } + }, + + "Shift-Ctrl-C" : "code", + "Shift-Ctrl-Q" : "quote", + "Shift-Ctrl-S" : "del", + "Shift-Ctrl-K" : "tex", // KaTeX + + "Shift-Alt-C" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + cm.replaceSelection(["```", selection, "```"].join("\n")); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 3); + } + }, + + "Shift-Ctrl-Alt-C" : "code-block", + "Shift-Ctrl-H" : "html-entities", + "Shift-Alt-H" : "help", + "Shift-Ctrl-E" : "emoji", + "Shift-Ctrl-U" : "uppercase", + "Shift-Alt-U" : "ucwords", + "Shift-Ctrl-Alt-U" : "ucfirst", + "Shift-Alt-L" : "lowercase", + + "Shift-Ctrl-I" : function() { + var cm = this.cm; + var cursor = cm.getCursor(); + var selection = cm.getSelection(); + + var title = (selection === "") ? "" : " \""+selection+"\""; + + cm.replaceSelection("![" + selection + "]("+title+")"); + + if (selection === "") { + cm.setCursor(cursor.line, cursor.ch + 4); + } + }, + + "Shift-Ctrl-Alt-I" : "image", + "Shift-Ctrl-L" : "link", + "Shift-Ctrl-O" : "list-ol", + "Shift-Ctrl-P" : "preformatted-text", + "Shift-Ctrl-T" : "table", + "Shift-Alt-P" : "pagebreak", + "F9" : "watch", + "F10" : "preview", + "F11" : "fullscreen", + }; + + /** + * 清除字符串两边的空格 + * Clear the space of strings both sides. + * + * @param {String} str string + * @returns {String} trimed string + */ + + var trim = function(str) { + return (!String.prototype.trim) ? str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") : str.trim(); + }; + + editormd.trim = trim; + + /** + * 所有单词首字母大写 + * Words first to uppercase + * + * @param {String} str string + * @returns {String} string + */ + + var ucwords = function (str) { + return str.toLowerCase().replace(/\b(\w)|\s(\w)/g, function($1) { + return $1.toUpperCase(); + }); + }; + + editormd.ucwords = editormd.wordsFirstUpperCase = ucwords; + + /** + * 字符串首字母大写 + * Only string first char to uppercase + * + * @param {String} str string + * @returns {String} string + */ + + var firstUpperCase = function(str) { + return str.toLowerCase().replace(/\b(\w)/, function($1){ + return $1.toUpperCase(); + }); + }; + + var ucfirst = firstUpperCase; + + editormd.firstUpperCase = editormd.ucfirst = firstUpperCase; + + editormd.urls = { + atLinkBase : "https://github.com/" + }; + + editormd.regexs = { + atLink : /@(\w+)/g, + email : /(\w+)@(\w+)\.(\w+)\.?(\w+)?/g, + emailLink : /(mailto:)?([\w\.\_]+)@(\w+)\.(\w+)\.?(\w+)?/g, + emoji : /:([\w\+-]+):/g, + emojiDatetime : /(\d{2}:\d{2}:\d{2})/g, + twemoji : /:(tw-([\w]+)-?(\w+)?):/g, + fontAwesome : /:(fa-([\w]+)(-(\w+)){0,}):/g, + editormdLogo : /:(editormd-logo-?(\w+)?):/g, + pageBreak : /^\[[=]{8,}\]$/ + }; + + // Emoji graphics files url path + editormd.emoji = { + path : "http://www.emoji-cheat-sheet.com/graphics/emojis/", + ext : ".png" + }; + + // Twitter Emoji (Twemoji) graphics files url path + editormd.twemoji = { + path : "http://twemoji.maxcdn.com/36x36/", + ext : ".png" + }; + + /** + * 自定义marked的解析器 + * Custom Marked renderer rules + * + * @param {Array} markdownToC 传入用于接收TOC的数组 + * @returns {Renderer} markedRenderer 返回marked的Renderer自定义对象 + */ + + editormd.markedRenderer = function(markdownToC, options) { + var defaults = { + toc : true, // Table of contents + tocm : false, + tocStartLevel : 1, // Said from H1 to create ToC + pageBreak : true, + atLink : true, // for @link + emailLink : true, // for mail address auto link + taskList : false, // Enable Github Flavored Markdown task lists + emoji : false, // :emoji: , Support Twemoji, fontAwesome, Editor.md logo emojis. + tex : false, // TeX(LaTeX), based on KaTeX + flowChart : false, // flowChart.js only support IE9+ + sequenceDiagram : false, // sequenceDiagram.js only support IE9+ + }; + + var settings = $.extend(defaults, options || {}); + var marked = editormd.$marked; + var markedRenderer = new marked.Renderer(); + markdownToC = markdownToC || []; + + var regexs = editormd.regexs; + var atLinkReg = regexs.atLink; + var emojiReg = regexs.emoji; + var emailReg = regexs.email; + var emailLinkReg = regexs.emailLink; + var twemojiReg = regexs.twemoji; + var faIconReg = regexs.fontAwesome; + var editormdLogoReg = regexs.editormdLogo; + var pageBreakReg = regexs.pageBreak; + + markedRenderer.emoji = function(text) { + + text = text.replace(editormd.regexs.emojiDatetime, function($1) { + return $1.replace(/:/g, ":"); + }); + + var matchs = text.match(emojiReg); + + if (!matchs || !settings.emoji) { + return text; + } + + for (var i = 0, len = matchs.length; i < len; i++) + { + if (matchs[i] === ":+1:") { + matchs[i] = ":\\+1:"; + } + + text = text.replace(new RegExp(matchs[i]), function($1, $2){ + var faMatchs = $1.match(faIconReg); + var name = $1.replace(/:/g, ""); + + if (faMatchs) + { + for (var fa = 0, len1 = faMatchs.length; fa < len1; fa++) + { + var faName = faMatchs[fa].replace(/:/g, ""); + + return ""; + } + } + else + { + var emdlogoMathcs = $1.match(editormdLogoReg); + var twemojiMatchs = $1.match(twemojiReg); + + if (emdlogoMathcs) + { + for (var x = 0, len2 = emdlogoMathcs.length; x < len2; x++) + { + var logoName = emdlogoMathcs[x].replace(/:/g, ""); + return ""; + } + } + else if (twemojiMatchs) + { + for (var t = 0, len3 = twemojiMatchs.length; t < len3; t++) + { + var twe = twemojiMatchs[t].replace(/:/g, "").replace("tw-", ""); + return "\"twemoji-""; + } + } + else + { + var src = (name === "+1") ? "plus1" : name; + src = (src === "black_large_square") ? "black_square" : src; + src = (src === "moon") ? "waxing_gibbous_moon" : src; + + return "\":""; + } + } + }); + } + + return text; + }; + + markedRenderer.atLink = function(text) { + + if (atLinkReg.test(text)) + { + if (settings.atLink) + { + text = text.replace(emailReg, function($1, $2, $3, $4) { + return $1.replace(/@/g, "_#_@_#_"); + }); + + text = text.replace(atLinkReg, function($1, $2) { + return "" + $1 + ""; + }).replace(/_#_@_#_/g, "@"); + } + + if (settings.emailLink) + { + text = text.replace(emailLinkReg, function($1, $2, $3, $4, $5) { + return (!$2 && $.inArray($5, "jpg|jpeg|png|gif|webp|ico|icon|pdf".split("|")) < 0) ? ""+$1+"" : $1; + }); + } + + return text; + } + + return text; + }; + + markedRenderer.link = function (href, title, text) { + + if (this.options.sanitize) { + try { + var prot = decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase(); + } catch(e) { + return ""; + } + + if (prot.indexOf("javascript:") === 0) { + return ""; + } + } + + var out = "" + text.replace(/@/g, "@") + ""; + } + + if (title) { + out += " title=\"" + title + "\""; + } + + out += ">" + text + ""; + + return out; + }; + + markedRenderer.heading = function(text, level, raw) { + + var linkText = text; + var hasLinkReg = /\s*\]*)\>(.*)\<\/a\>\s*/; + var getLinkTextReg = /\s*\]+)\>([^\>]*)\<\/a\>\s*/g; + + if (hasLinkReg.test(text)) + { + var tempText = []; + text = text.split(/\]+)\>([^\>]*)\<\/a\>/); + + for (var i = 0, len = text.length; i < len; i++) + { + tempText.push(text[i].replace(/\s*href\=\"(.*)\"\s*/g, "")); + } + + text = tempText.join(" "); + } + + text = trim(text); + + var escapedText = text.toLowerCase().replace(/[^\w]+/g, "-"); + var toc = { + text : text, + level : level, + slug : escapedText + }; + + var isChinese = /^[\u4e00-\u9fa5]+$/.test(text); + var id = (isChinese) ? escape(text).replace(/\%/g, "") : text.toLowerCase().replace(/[^\w]+/g, "-"); + + markdownToC.push(toc); + + var headingHTML = ""; + + headingHTML += ""; + headingHTML += ""; + headingHTML += (hasLinkReg) ? this.atLink(this.emoji(linkText)) : this.atLink(this.emoji(text)); + headingHTML += ""; + + return headingHTML; + }; + + markedRenderer.pageBreak = function(text) { + if (pageBreakReg.test(text) && settings.pageBreak) + { + text = "
                "; + } + + return text; + }; + + markedRenderer.paragraph = function(text) { + var isTeXInline = /\$\$(.*)\$\$/g.test(text); + var isTeXLine = /^\$\$(.*)\$\$$/.test(text); + var isTeXAddClass = (isTeXLine) ? " class=\"" + editormd.classNames.tex + "\"" : ""; + var isToC = (settings.tocm) ? /^(\[TOC\]|\[TOCM\])$/.test(text) : /^\[TOC\]$/.test(text); + var isToCMenu = /^\[TOCM\]$/.test(text); + + if (!isTeXLine && isTeXInline) + { + text = text.replace(/(\$\$([^\$]*)\$\$)+/g, function($1, $2) { + return "" + $2.replace(/\$/g, "") + ""; + }); + } + else + { + text = (isTeXLine) ? text.replace(/\$/g, "") : text; + } + + var tocHTML = "
                " + text + "
                "; + + return (isToC) ? ( (isToCMenu) ? "
                " + tocHTML + "

                " : tocHTML ) + : ( (pageBreakReg.test(text)) ? this.pageBreak(text) : "" + this.atLink(this.emoji(text)) + "

                \n" ); + }; + + markedRenderer.code = function (code, lang, escaped) { + + if (lang === "seq" || lang === "sequence") + { + return "
                " + code + "
                "; + } + else if ( lang === "flow") + { + return "
                " + code + "
                "; + } + else if ( lang === "math" || lang === "latex" || lang === "katex") + { + return "

                " + code + "

                "; + } + else + { + + return marked.Renderer.prototype.code.apply(this, arguments); + } + }; + + markedRenderer.tablecell = function(content, flags) { + var type = (flags.header) ? "th" : "td"; + var tag = (flags.align) ? "<" + type +" style=\"text-align:" + flags.align + "\">" : "<" + type + ">"; + + return tag + this.atLink(this.emoji(content)) + "\n"; + }; + + markedRenderer.listitem = function(text) { + if (settings.taskList && /^\s*\[[x\s]\]\s*/.test(text)) + { + text = text.replace(/^\s*\[\s\]\s*/, " ") + .replace(/^\s*\[x\]\s*/, " "); + + return "
              • " + this.atLink(this.emoji(text)) + "
              • "; + } + else + { + return "
              • " + this.atLink(this.emoji(text)) + "
              • "; + } + }; + + return markedRenderer; + }; + + /** + * + * 生成TOC(Table of Contents) + * Creating ToC (Table of Contents) + * + * @param {Array} toc 从marked获取的TOC数组列表 + * @param {Element} container 插入TOC的容器元素 + * @param {Integer} startLevel Hx 起始层级 + * @returns {Object} tocContainer 返回ToC列表容器层的jQuery对象元素 + */ + + editormd.markdownToCRenderer = function(toc, container, tocDropdown, startLevel) { + + var html = ""; + var lastLevel = 0; + var classPrefix = this.classPrefix; + + startLevel = startLevel || 1; + + for (var i = 0, len = toc.length; i < len; i++) + { + var text = toc[i].text; + var level = toc[i].level; + + if (level < startLevel) { + continue; + } + + if (level > lastLevel) + { + html += ""; + } + else if (level < lastLevel) + { + html += (new Array(lastLevel - level + 2)).join("
            • "); + } + else + { + html += ""; + } + + html += "
            • " + text + "
                "; + lastLevel = level; + } + + var tocContainer = container.find(".markdown-toc"); + + if ((tocContainer.length < 1 && container.attr("previewContainer") === "false")) + { + var tocHTML = "
                "; + + tocHTML = (tocDropdown) ? "
                " + tocHTML + "
                " : tocHTML; + + container.html(tocHTML); + + tocContainer = container.find(".markdown-toc"); + } + + if (tocDropdown) + { + tocContainer.wrap("

                "); + } + + tocContainer.html("
                  ").children(".markdown-toc-list").html(html.replace(/\r?\n?\\<\/ul\>/g, "")); + + return tocContainer; + }; + + /** + * + * 生成TOC下拉菜单 + * Creating ToC dropdown menu + * + * @param {Object} container 插入TOC的容器jQuery对象元素 + * @param {String} tocTitle ToC title + * @returns {Object} return toc-menu object + */ + + editormd.tocDropdownMenu = function(container, tocTitle) { + + tocTitle = tocTitle || "Table of Contents"; + + var zindex = 400; + var tocMenus = container.find("." + this.classPrefix + "toc-menu"); + + tocMenus.each(function() { + var $this = $(this); + var toc = $this.children(".markdown-toc"); + var icon = ""; + var btn = "" + icon + tocTitle + ""; + var menu = toc.children("ul"); + var list = menu.find("li"); + + toc.append(btn); + + list.first().before("
                • " + tocTitle + " " + icon + "

                • "); + + $this.mouseover(function(){ + menu.show(); + + list.each(function(){ + var li = $(this); + var ul = li.children("ul"); + + if (ul.html() === "") + { + ul.remove(); + } + + if (ul.length > 0 && ul.html() !== "") + { + var firstA = li.children("a").first(); + + if (firstA.children(".fa").length < 1) + { + firstA.append( $(icon).css({ float:"right", paddingTop:"4px" }) ); + } + } + + li.mouseover(function(){ + ul.css("z-index", zindex).show(); + zindex += 1; + }).mouseleave(function(){ + ul.hide(); + }); + }); + }).mouseleave(function(){ + menu.hide(); + }); + }); + + return tocMenus; + }; + + /** + * 简单地过滤指定的HTML标签 + * Filter custom html tags + * + * @param {String} html 要过滤HTML + * @param {String} filters 要过滤的标签 + * @returns {String} html 返回过滤的HTML + */ + + editormd.filterHTMLTags = function(html, filters) { + + if (typeof html !== "string") { + html = new String(html); + } + + if (typeof filters !== "string") { + return html; + } + + var expression = filters.split("|"); + var filterTags = expression[0].split(","); + var attrs = expression[1]; + + for (var i = 0, len = filterTags.length; i < len; i++) + { + var tag = filterTags[i]; + + html = html.replace(new RegExp("\<\s*" + tag + "\s*([^\>]*)\>([^\>]*)\<\s*\/" + tag + "\s*\>", "igm"), ""); + } + + //return html; + + if (typeof attrs !== "undefined") + { + var htmlTagRegex = /\<(\w+)\s*([^\>]*)\>([^\>]*)\<\/(\w+)\>/ig; + + if (attrs === "*") + { + html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) { + return "<" + $2 + ">" + $4 + ""; + }); + } + else if (attrs === "on*") + { + html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) { + var el = $("<" + $2 + ">" + $4 + ""); + var _attrs = $($1)[0].attributes; + var $attrs = {}; + + $.each(_attrs, function(i, e) { + if (e.nodeName !== '"') $attrs[e.nodeName] = e.nodeValue; + }); + + $.each($attrs, function(i) { + if (i.indexOf("on") === 0) { + delete $attrs[i]; + } + }); + + el.attr($attrs); + + var text = (typeof el[1] !== "undefined") ? $(el[1]).text() : ""; + + return el[0].outerHTML + text; + }); + } + else + { + html = html.replace(htmlTagRegex, function($1, $2, $3, $4) { + var filterAttrs = attrs.split(","); + var el = $($1); + el.html($4); + + $.each(filterAttrs, function(i) { + el.attr(filterAttrs[i], null); + }); + + return el[0].outerHTML; + }); + } + } + + return html; + }; + + /** + * 将Markdown文档解析为HTML用于前台显示 + * Parse Markdown to HTML for Font-end preview. + * + * @param {String} id 用于显示HTML的对象ID + * @param {Object} [options={}] 配置选项,可选 + * @returns {Object} div 返回jQuery对象元素 + */ + + editormd.markdownToHTML = function(id, options) { + var defaults = { + gfm : true, + toc : true, + tocm : false, + tocStartLevel : 1, + tocTitle : "目录", + tocDropdown : false, + tocContainer : "", + markdown : "", + markdownSourceCode : false, + htmlDecode : false, + autoLoadKaTeX : true, + pageBreak : true, + atLink : true, // for @link + emailLink : true, // for mail address auto link + tex : false, + taskList : false, // Github Flavored Markdown task lists + emoji : false, + flowChart : false, + sequenceDiagram : false, + previewCodeHighlight : true + }; + + editormd.$marked = marked; + + var div = $("#" + id); + var settings = div.settings = $.extend(true, defaults, options || {}); + var saveTo = div.find("textarea"); + + if (saveTo.length < 1) + { + div.append(""); + saveTo = div.find("textarea"); + } + + var markdownDoc = (settings.markdown === "") ? saveTo.val() : settings.markdown; + var markdownToC = []; + + var rendererOptions = { + toc : settings.toc, + tocm : settings.tocm, + tocStartLevel : settings.tocStartLevel, + taskList : settings.taskList, + emoji : settings.emoji, + tex : settings.tex, + pageBreak : settings.pageBreak, + atLink : settings.atLink, // for @link + emailLink : settings.emailLink, // for mail address auto link + flowChart : settings.flowChart, + sequenceDiagram : settings.sequenceDiagram, + previewCodeHighlight : settings.previewCodeHighlight, + }; + + var markedOptions = { + renderer : editormd.markedRenderer(markdownToC, rendererOptions), + gfm : settings.gfm, + tables : true, + breaks : true, + pedantic : false, + sanitize : (settings.htmlDecode) ? false : true, // 是否忽略HTML标签,即是否开启HTML标签解析,为了安全性,默认不开启 + smartLists : true, + smartypants : true + }; + + markdownDoc = new String(markdownDoc); + + var markdownParsed = marked(markdownDoc, markedOptions); + + markdownParsed = editormd.filterHTMLTags(markdownParsed, settings.htmlDecode); + + if (settings.markdownSourceCode) { + saveTo.text(markdownDoc); + } else { + saveTo.remove(); + } + + div.addClass("markdown-body " + this.classPrefix + "html-preview").append(markdownParsed); + + var tocContainer = (settings.tocContainer !== "") ? $(settings.tocContainer) : div; + + if (settings.tocContainer !== "") + { + tocContainer.attr("previewContainer", false); + } + + if (settings.toc) + { + div.tocContainer = this.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel); + + if (settings.tocDropdown || div.find("." + this.classPrefix + "toc-menu").length > 0) + { + this.tocDropdownMenu(div, settings.tocTitle); + } + + if (settings.tocContainer !== "") + { + div.find(".editormd-toc-menu, .editormd-markdown-toc").remove(); + } + } + + if (settings.previewCodeHighlight) + { + div.find("pre").addClass("prettyprint linenums"); + prettyPrint(); + } + + if (!editormd.isIE8) + { + if (settings.flowChart) { + div.find(".flowchart").flowChart(); + } + + if (settings.sequenceDiagram) { + div.find(".sequence-diagram").sequenceDiagram({theme: "simple"}); + } + } + + if (settings.tex) + { + var katexHandle = function() { + div.find("." + editormd.classNames.tex).each(function(){ + var tex = $(this); + katex.render(tex.html().replace(/</g, "<").replace(/>/g, ">"), tex[0]); + tex.find(".katex").css("font-size", "1.6em"); + }); + }; + + if (settings.autoLoadKaTeX && !editormd.$katex && !editormd.kaTeXLoaded) + { + this.loadKaTeX(function() { + editormd.$katex = katex; + editormd.kaTeXLoaded = true; + katexHandle(); + }); + } + else + { + katexHandle(); + } + } + + div.getMarkdown = function() { + return saveTo.val(); + }; + + return div; + }; + + // Editor.md themes, change toolbar themes etc. + // added @1.5.0 + editormd.themes = ["default", "dark"]; + + // Preview area themes + // added @1.5.0 + editormd.previewThemes = ["default", "dark"]; + + // CodeMirror / editor area themes + // @1.5.0 rename -> editorThemes, old version -> themes + editormd.editorThemes = [ + "default", "3024-day", "3024-night", + "ambiance", "ambiance-mobile", + "base16-dark", "base16-light", "blackboard", + "cobalt", + "eclipse", "elegant", "erlang-dark", + "lesser-dark", + "mbo", "mdn-like", "midnight", "monokai", + "neat", "neo", "night", + "paraiso-dark", "paraiso-light", "pastel-on-dark", + "rubyblue", + "solarized", + "the-matrix", "tomorrow-night-eighties", "twilight", + "vibrant-ink", + "xq-dark", "xq-light" + ]; + + editormd.loadPlugins = {}; + + editormd.loadFiles = { + js : [], + css : [], + plugin : [] + }; + + /** + * 动态加载Editor.md插件,但不立即执行 + * Load editor.md plugins + * + * @param {String} fileName 插件文件路径 + * @param {Function} [callback=function()] 加载成功后执行的回调函数 + * @param {String} [into="head"] 嵌入页面的位置 + */ + + editormd.loadPlugin = function(fileName, callback, into) { + callback = callback || function() {}; + + this.loadScript(fileName, function() { + editormd.loadFiles.plugin.push(fileName); + callback(); + }, into); + }; + + /** + * 动态加载CSS文件的方法 + * Load css file method + * + * @param {String} fileName CSS文件名 + * @param {Function} [callback=function()] 加载成功后执行的回调函数 + * @param {String} [into="head"] 嵌入页面的位置 + */ + + editormd.loadCSS = function(fileName, callback, into) { + into = into || "head"; + callback = callback || function() {}; + + var css = document.createElement("link"); + css.type = "text/css"; + css.rel = "stylesheet"; + css.onload = css.onreadystatechange = function() { + editormd.loadFiles.css.push(fileName); + callback(); + }; + + css.href = fileName + ".css"; + + if(into === "head") { + document.getElementsByTagName("head")[0].appendChild(css); + } else { + document.body.appendChild(css); + } + }; + + editormd.isIE = (navigator.appName == "Microsoft Internet Explorer"); + editormd.isIE8 = (editormd.isIE && navigator.appVersion.match(/8./i) == "8."); + + /** + * 动态加载JS文件的方法 + * Load javascript file method + * + * @param {String} fileName JS文件名 + * @param {Function} [callback=function()] 加载成功后执行的回调函数 + * @param {String} [into="head"] 嵌入页面的位置 + */ + + editormd.loadScript = function(fileName, callback, into) { + + into = into || "head"; + callback = callback || function() {}; + + var script = null; + script = document.createElement("script"); + script.id = fileName.replace(/[\./]+/g, "-"); + script.type = "text/javascript"; + script.src = fileName + ".js"; + + if (editormd.isIE8) + { + script.onreadystatechange = function() { + if(script.readyState) + { + if (script.readyState === "loaded" || script.readyState === "complete") + { + script.onreadystatechange = null; + editormd.loadFiles.js.push(fileName); + callback(); + } + } + }; + } + else + { + script.onload = function() { + editormd.loadFiles.js.push(fileName); + callback(); + }; + } + + if (into === "head") { + document.getElementsByTagName("head")[0].appendChild(script); + } else { + document.body.appendChild(script); + } + }; + + // 使用国外的CDN,加载速度有时会很慢,或者自定义URL + // You can custom KaTeX load url. + editormd.katexURL = { + css : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min", + js : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min" + }; + + editormd.kaTeXLoaded = false; + + /** + * 加载KaTeX文件 + * load KaTeX files + * + * @param {Function} [callback=function()] 加载成功后执行的回调函数 + */ + + editormd.loadKaTeX = function (callback) { + editormd.loadCSS(editormd.katexURL.css, function(){ + editormd.loadScript(editormd.katexURL.js, callback || function(){}); + }); + }; + + /** + * 锁屏 + * lock screen + * + * @param {Boolean} lock Boolean 布尔值,是否锁屏 + * @returns {void} + */ + + editormd.lockScreen = function(lock) { + $("html,body").css("overflow", (lock) ? "hidden" : ""); + }; + + /** + * 动态创建对话框 + * Creating custom dialogs + * + * @param {Object} options 配置项键值对 Key/Value + * @returns {dialog} 返回创建的dialog的jQuery实例对象 + */ + + editormd.createDialog = function(options) { + var defaults = { + name : "", + width : 420, + height: 240, + title : "", + drag : true, + closed : true, + content : "", + mask : true, + maskStyle : { + backgroundColor : "#fff", + opacity : 0.1 + }, + lockScreen : true, + footer : true, + buttons : false + }; + + options = $.extend(true, defaults, options); + + var $this = this; + var editor = this.editor; + var classPrefix = editormd.classPrefix; + var guid = (new Date()).getTime(); + var dialogName = ( (options.name === "") ? classPrefix + "dialog-" + guid : options.name); + var mouseOrTouch = editormd.mouseOrTouch; + + var html = "
                  "; + + if (options.title !== "") + { + html += "
                  "; + html += "" + options.title + ""; + html += "
                  "; + } + + if (options.closed) + { + html += ""; + } + + html += "
                  " + options.content; + + if (options.footer || typeof options.footer === "string") + { + html += "
                  " + ( (typeof options.footer === "boolean") ? "" : options.footer) + "
                  "; + } + + html += "
                  "; + + html += "
                  "; + html += "
                  "; + html += "
                  "; + + editor.append(html); + + var dialog = editor.find("." + dialogName); + + dialog.lockScreen = function(lock) { + if (options.lockScreen) + { + $("html,body").css("overflow", (lock) ? "hidden" : ""); + $this.resize(); + } + + return dialog; + }; + + dialog.showMask = function() { + if (options.mask) + { + editor.find("." + classPrefix + "mask").css(options.maskStyle).css("z-index", editormd.dialogZindex - 1).show(); + } + return dialog; + }; + + dialog.hideMask = function() { + if (options.mask) + { + editor.find("." + classPrefix + "mask").hide(); + } + + return dialog; + }; + + dialog.loading = function(show) { + var loading = dialog.find("." + classPrefix + "dialog-mask"); + loading[(show) ? "show" : "hide"](); + + return dialog; + }; + + dialog.lockScreen(true).showMask(); + + dialog.show().css({ + zIndex : editormd.dialogZindex, + border : (editormd.isIE8) ? "1px solid #ddd" : "", + width : (typeof options.width === "number") ? options.width + "px" : options.width, + height : (typeof options.height === "number") ? options.height + "px" : options.height + }); + + var dialogPosition = function(){ + dialog.css({ + top : ($(window).height() - dialog.height()) / 2 + "px", + left : ($(window).width() - dialog.width()) / 2 + "px" + }); + }; + + dialogPosition(); + + $(window).resize(dialogPosition); + + dialog.children("." + classPrefix + "dialog-close").bind(mouseOrTouch("click", "touchend"), function() { + dialog.hide().lockScreen(false).hideMask(); + }); + + if (typeof options.buttons === "object") + { + var footer = dialog.footer = dialog.find("." + classPrefix + "dialog-footer"); + + for (var key in options.buttons) + { + var btn = options.buttons[key]; + var btnClassName = classPrefix + key + "-btn"; + + footer.append(""); + btn[1] = $.proxy(btn[1], dialog); + footer.children("." + btnClassName).bind(mouseOrTouch("click", "touchend"), btn[1]); + } + } + + if (options.title !== "" && options.drag) + { + var posX, posY; + var dialogHeader = dialog.children("." + classPrefix + "dialog-header"); + + if (!options.mask) { + dialogHeader.bind(mouseOrTouch("click", "touchend"), function(){ + editormd.dialogZindex += 2; + dialog.css("z-index", editormd.dialogZindex); + }); + } + + dialogHeader.mousedown(function(e) { + e = e || window.event; //IE + posX = e.clientX - parseInt(dialog[0].style.left); + posY = e.clientY - parseInt(dialog[0].style.top); + + document.onmousemove = moveAction; + }); + + var userCanSelect = function (obj) { + obj.removeClass(classPrefix + "user-unselect").off("selectstart"); + }; + + var userUnselect = function (obj) { + obj.addClass(classPrefix + "user-unselect").on("selectstart", function(event) { // selectstart for IE + return false; + }); + }; + + var moveAction = function (e) { + e = e || window.event; //IE + + var left, top, nowLeft = parseInt(dialog[0].style.left), nowTop = parseInt(dialog[0].style.top); + + if( nowLeft >= 0 ) { + if( nowLeft + dialog.width() <= $(window).width()) { + left = e.clientX - posX; + } else { + left = $(window).width() - dialog.width(); + document.onmousemove = null; + } + } else { + left = 0; + document.onmousemove = null; + } + + if( nowTop >= 0 ) { + top = e.clientY - posY; + } else { + top = 0; + document.onmousemove = null; + } + + + document.onselectstart = function() { + return false; + }; + + userUnselect($("body")); + userUnselect(dialog); + dialog[0].style.left = left + "px"; + dialog[0].style.top = top + "px"; + }; + + document.onmouseup = function() { + userCanSelect($("body")); + userCanSelect(dialog); + + document.onselectstart = null; + document.onmousemove = null; + }; + + dialogHeader.touchDraggable = function() { + var offset = null; + var start = function(e) { + var orig = e.originalEvent; + var pos = $(this).parent().position(); + + offset = { + x : orig.changedTouches[0].pageX - pos.left, + y : orig.changedTouches[0].pageY - pos.top + }; + }; + + var move = function(e) { + e.preventDefault(); + var orig = e.originalEvent; + + $(this).parent().css({ + top : orig.changedTouches[0].pageY - offset.y, + left : orig.changedTouches[0].pageX - offset.x + }); + }; + + this.bind("touchstart", start).bind("touchmove", move); + }; + + dialogHeader.touchDraggable(); + } + + editormd.dialogZindex += 2; + + return dialog; + }; + + /** + * 鼠标和触摸事件的判断/选择方法 + * MouseEvent or TouchEvent type switch + * + * @param {String} [mouseEventType="click"] 供选择的鼠标事件 + * @param {String} [touchEventType="touchend"] 供选择的触摸事件 + * @returns {String} EventType 返回事件类型名称 + */ + + editormd.mouseOrTouch = function(mouseEventType, touchEventType) { + mouseEventType = mouseEventType || "click"; + touchEventType = touchEventType || "touchend"; + + var eventType = mouseEventType; + + try { + document.createEvent("TouchEvent"); + eventType = touchEventType; + } catch(e) {} + + return eventType; + }; + + /** + * 日期时间的格式化方法 + * Datetime format method + * + * @param {String} [format=""] 日期时间的格式,类似PHP的格式 + * @returns {String} datefmt 返回格式化后的日期时间字符串 + */ + + editormd.dateFormat = function(format) { + format = format || ""; + + var addZero = function(d) { + return (d < 10) ? "0" + d : d; + }; + + var date = new Date(); + var year = date.getFullYear(); + var year2 = year.toString().slice(2, 4); + var month = addZero(date.getMonth() + 1); + var day = addZero(date.getDate()); + var weekDay = date.getDay(); + var hour = addZero(date.getHours()); + var min = addZero(date.getMinutes()); + var second = addZero(date.getSeconds()); + var ms = addZero(date.getMilliseconds()); + var datefmt = ""; + + var ymd = year2 + "-" + month + "-" + day; + var fymd = year + "-" + month + "-" + day; + var hms = hour + ":" + min + ":" + second; + + switch (format) + { + case "UNIX Time" : + datefmt = date.getTime(); + break; + + case "UTC" : + datefmt = date.toUTCString(); + break; + + case "yy" : + datefmt = year2; + break; + + case "year" : + case "yyyy" : + datefmt = year; + break; + + case "month" : + case "mm" : + datefmt = month; + break; + + case "cn-week-day" : + case "cn-wd" : + var cnWeekDays = ["日", "一", "二", "三", "四", "五", "六"]; + datefmt = "星期" + cnWeekDays[weekDay]; + break; + + case "week-day" : + case "wd" : + var weekDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; + datefmt = weekDays[weekDay]; + break; + + case "day" : + case "dd" : + datefmt = day; + break; + + case "hour" : + case "hh" : + datefmt = hour; + break; + + case "min" : + case "ii" : + datefmt = min; + break; + + case "second" : + case "ss" : + datefmt = second; + break; + + case "ms" : + datefmt = ms; + break; + + case "yy-mm-dd" : + datefmt = ymd; + break; + + case "yyyy-mm-dd" : + datefmt = fymd; + break; + + case "yyyy-mm-dd h:i:s ms" : + case "full + ms" : + datefmt = fymd + " " + hms + " " + ms; + break; + + case "full" : + case "yyyy-mm-dd h:i:s" : + default: + datefmt = fymd + " " + hms; + break; + } + + return datefmt; + }; + + return editormd; + +})); diff --git a/src/main/resources/static/admin/editormd/editormd.min.js b/src/main/resources/static/admin/editormd/editormd.min.js new file mode 100755 index 00000000..f810e34f --- /dev/null +++ b/src/main/resources/static/admin/editormd/editormd.min.js @@ -0,0 +1,3 @@ +/*! Editor.md v1.5.0 | editormd.min.js | Open source online markdown editor. | MIT License | By: Pandao | https://github.com/pandao/editor.md | 2015-06-09 */ +!function(e){"use strict";"function"==typeof require&&"object"==typeof exports&&"object"==typeof module?module.exports=e:"function"==typeof define?define.amd||define(["jquery"],e):window.editormd=e()}(function(){"use strict";var e="undefined"!=typeof jQuery?jQuery:Zepto;if("undefined"!=typeof e){var t=function(e,i){return new t.fn.init(e,i)};t.title=t.$name="Editor.md",t.version="1.5.0",t.homePage="https://pandao.github.io/editor.md/",t.classPrefix="editormd-",t.toolbarModes={full:["undo","redo","|","bold","del","italic","quote","ucwords","uppercase","lowercase","|","h1","h2","h3","h4","h5","h6","|","list-ul","list-ol","hr","|","link","reference-link","image","code","preformatted-text","code-block","table","datetime","emoji","html-entities","pagebreak","|","goto-line","watch","preview","fullscreen","clear","search","|","help","info"],simple:["undo","redo","|","bold","del","italic","quote","uppercase","lowercase","|","h1","h2","h3","h4","h5","h6","|","list-ul","list-ol","hr","|","watch","preview","fullscreen","|","help","info"],mini:["undo","redo","|","watch","preview","|","help","info"]},t.defaults={mode:"gfm",name:"",value:"",theme:"",editorTheme:"default",previewTheme:"",markdown:"",appendMarkdown:"",width:"100%",height:"100%",path:"./lib/",pluginPath:"",delay:300,autoLoadModules:!0,watch:!0,placeholder:"Enjoy Markdown! coding now...",gotoLine:!0,codeFold:!1,autoHeight:!1,autoFocus:!0,autoCloseTags:!0,searchReplace:!0,syncScrolling:!0,readOnly:!1,tabSize:4,indentUnit:4,lineNumbers:!0,lineWrapping:!0,autoCloseBrackets:!0,showTrailingSpace:!0,matchBrackets:!0,indentWithTabs:!0,styleSelectedText:!0,matchWordHighlight:!0,styleActiveLine:!0,dialogLockScreen:!0,dialogShowMask:!0,dialogDraggable:!0,dialogMaskBgColor:"#fff",dialogMaskOpacity:.1,fontSize:"13px",saveHTMLToTextarea:!1,disabledKeyMaps:[],onload:function(){},onresize:function(){},onchange:function(){},onwatch:null,onunwatch:null,onpreviewing:function(){},onpreviewed:function(){},onfullscreen:function(){},onfullscreenExit:function(){},onscroll:function(){},onpreviewscroll:function(){},imageUpload:!1,imageFormats:["jpg","jpeg","gif","png","bmp","webp"],imageUploadURL:"",crossDomainUpload:!1,uploadCallbackURL:"",toc:!0,tocm:!1,tocTitle:"",tocDropdown:!1,tocContainer:"",tocStartLevel:1,htmlDecode:!1,pageBreak:!0,atLink:!0,emailLink:!0,taskList:!1,emoji:!1,tex:!1,flowChart:!1,sequenceDiagram:!1,previewCodeHighlight:!0,toolbar:!0,toolbarAutoFixed:!0,toolbarIcons:"full",toolbarTitles:{},toolbarHandlers:{ucwords:function(){return t.toolbarHandlers.ucwords},lowercase:function(){return t.toolbarHandlers.lowercase}},toolbarCustomIcons:{lowercase:'a',ucwords:'Aa'},toolbarIconsClass:{undo:"fa-undo",redo:"fa-repeat",bold:"fa-bold",del:"fa-strikethrough",italic:"fa-italic",quote:"fa-quote-left",uppercase:"fa-font",h1:t.classPrefix+"bold",h2:t.classPrefix+"bold",h3:t.classPrefix+"bold",h4:t.classPrefix+"bold",h5:t.classPrefix+"bold",h6:t.classPrefix+"bold","list-ul":"fa-list-ul","list-ol":"fa-list-ol",hr:"fa-minus",link:"fa-link","reference-link":"fa-anchor",image:"fa-picture-o",code:"fa-code","preformatted-text":"fa-file-code-o","code-block":"fa-file-code-o",table:"fa-table",datetime:"fa-clock-o",emoji:"fa-smile-o","html-entities":"fa-copyright",pagebreak:"fa-newspaper-o","goto-line":"fa-terminal",watch:"fa-eye-slash",unwatch:"fa-eye",preview:"fa-desktop",search:"fa-search",fullscreen:"fa-arrows-alt",clear:"fa-eraser",help:"fa-question-circle",info:"fa-info-circle"},toolbarIconTexts:{},lang:{name:"zh-cn",description:"开源在线Markdown编辑器
                  Open source online Markdown editor.",tocTitle:"目录",toolbar:{undo:"撤销(Ctrl+Z)",redo:"重做(Ctrl+Y)",bold:"粗体",del:"删除线",italic:"斜体",quote:"引用",ucwords:"将每个单词首字母转成大写",uppercase:"将所选转换成大写",lowercase:"将所选转换成小写",h1:"标题1",h2:"标题2",h3:"标题3",h4:"标题4",h5:"标题5",h6:"标题6","list-ul":"无序列表","list-ol":"有序列表",hr:"横线",link:"链接","reference-link":"引用链接",image:"添加图片",code:"行内代码","preformatted-text":"预格式文本 / 代码块(缩进风格)","code-block":"代码块(多语言风格)",table:"添加表格",datetime:"日期时间",emoji:"Emoji表情","html-entities":"HTML实体字符",pagebreak:"插入分页符","goto-line":"跳转到行",watch:"关闭实时预览",unwatch:"开启实时预览",preview:"全窗口预览HTML(按 Shift + ESC还原)",fullscreen:"全屏(按ESC还原)",clear:"清空",search:"搜索",help:"使用帮助",info:"关于"+t.title},buttons:{enter:"确定",cancel:"取消",close:"关闭"},dialog:{link:{title:"添加链接",url:"链接地址",urlTitle:"链接标题",urlEmpty:"错误:请填写链接地址。"},referenceLink:{title:"添加引用链接",name:"引用名称",url:"链接地址",urlId:"链接ID",urlTitle:"链接标题",nameEmpty:"错误:引用链接的名称不能为空。",idEmpty:"错误:请填写引用链接的ID。",urlEmpty:"错误:请填写引用链接的URL地址。"},image:{title:"添加图片",url:"图片地址",link:"图片链接",alt:"图片描述",uploadButton:"本地上传",imageURLEmpty:"错误:图片地址不能为空。",uploadFileEmpty:"错误:上传的图片不能为空。",formatNotAllowed:"错误:只允许上传图片文件,允许上传的图片文件格式有:"},preformattedText:{title:"添加预格式文本或代码块",emptyAlert:"错误:请填写预格式文本或代码的内容。"},codeBlock:{title:"添加代码块",selectLabel:"代码语言:",selectDefaultText:"请选择代码语言",otherLanguage:"其他语言",unselectedLanguageAlert:"错误:请选择代码所属的语言类型。",codeEmptyAlert:"错误:请填写代码内容。"},htmlEntities:{title:"HTML 实体字符"},help:{title:"使用帮助"}}}},t.classNames={tex:t.classPrefix+"tex"},t.dialogZindex=99999,t.$katex=null,t.$marked=null,t.$CodeMirror=null,t.$prettyPrint=null;var i,o;t.prototype=t.fn={state:{watching:!1,loaded:!1,preview:!1,fullscreen:!1},init:function(i,o){o=o||{},"object"==typeof i&&(o=i);var r=this.classPrefix=t.classPrefix,n=this.settings=e.extend(!0,t.defaults,o);i="object"==typeof i?n.id:i;var a=this.editor=e("#"+i);this.id=i,this.lang=n.lang;var s=this.classNames={textarea:{html:r+"html-textarea",markdown:r+"markdown-textarea"}};n.pluginPath=""===n.pluginPath?n.path+"../plugins/":n.pluginPath,this.state.watching=n.watch?!0:!1,a.hasClass("editormd")||a.addClass("editormd"),a.css({width:"number"==typeof n.width?n.width+"px":n.width,height:"number"==typeof n.height?n.height+"px":n.height}),n.autoHeight&&a.css("height","auto");var l=this.markdownTextarea=a.children("textarea");l.length<1&&(a.append(""),l=this.markdownTextarea=a.children("textarea")),l.addClass(s.textarea.markdown).attr("placeholder",n.placeholder),("undefined"==typeof l.attr("name")||""===l.attr("name"))&&l.attr("name",""!==n.name?n.name:i+"-markdown-doc");var c=[n.readOnly?"":'',n.saveHTMLToTextarea?'':"",'
                  ','
                  ','
                  '].join("\n");return a.append(c).addClass(r+"vertical"),""!==n.theme&&a.addClass(r+"theme-"+n.theme),this.mask=a.children("."+r+"mask"),this.containerMask=a.children("."+r+"container-mask"),""!==n.markdown&&l.val(n.markdown),""!==n.appendMarkdown&&l.val(l.val()+n.appendMarkdown),this.htmlTextarea=a.children("."+s.textarea.html),this.preview=a.children("."+r+"preview"),this.previewContainer=this.preview.children("."+r+"preview-container"),""!==n.previewTheme&&this.preview.addClass(r+"preview-theme-"+n.previewTheme),"function"==typeof define&&define.amd&&("undefined"!=typeof katex&&(t.$katex=katex),n.searchReplace&&!n.readOnly&&(t.loadCSS(n.path+"codemirror/addon/dialog/dialog"),t.loadCSS(n.path+"codemirror/addon/search/matchesonscrollbar"))),"function"==typeof define&&define.amd||!n.autoLoadModules?("undefined"!=typeof CodeMirror&&(t.$CodeMirror=CodeMirror),"undefined"!=typeof marked&&(t.$marked=marked),this.setCodeMirror().setToolbar().loadedDisplay()):this.loadQueues(),this},loadQueues:function(){var e=this,i=this.settings,o=i.path,r=function(){return t.isIE8?void e.loadedDisplay():void(i.flowChart||i.sequenceDiagram?t.loadScript(o+"raphael.min",function(){t.loadScript(o+"underscore.min",function(){!i.flowChart&&i.sequenceDiagram?t.loadScript(o+"sequence-diagram.min",function(){e.loadedDisplay()}):i.flowChart&&!i.sequenceDiagram?t.loadScript(o+"flowchart.min",function(){t.loadScript(o+"jquery.flowchart.min",function(){e.loadedDisplay()})}):i.flowChart&&i.sequenceDiagram&&t.loadScript(o+"flowchart.min",function(){t.loadScript(o+"jquery.flowchart.min",function(){t.loadScript(o+"sequence-diagram.min",function(){e.loadedDisplay()})})})})}):e.loadedDisplay())};return t.loadCSS(o+"codemirror/codemirror.min"),i.searchReplace&&!i.readOnly&&(t.loadCSS(o+"codemirror/addon/dialog/dialog"),t.loadCSS(o+"codemirror/addon/search/matchesonscrollbar")),i.codeFold&&t.loadCSS(o+"codemirror/addon/fold/foldgutter"),t.loadScript(o+"codemirror/codemirror.min",function(){t.$CodeMirror=CodeMirror,t.loadScript(o+"codemirror/modes.min",function(){t.loadScript(o+"codemirror/addons.min",function(){return e.setCodeMirror(),"gfm"!==i.mode&&"markdown"!==i.mode?(e.loadedDisplay(),!1):(e.setToolbar(),void t.loadScript(o+"marked.min",function(){t.$marked=marked,i.previewCodeHighlight?t.loadScript(o+"prettify.min",function(){r()}):r()}))})})}),this},setTheme:function(e){var t=this.editor,i=this.settings.theme,o=this.classPrefix+"theme-";return t.removeClass(o+i).addClass(o+e),this.settings.theme=e,this},setEditorTheme:function(e){var i=this.settings;return i.editorTheme=e,"default"!==e&&t.loadCSS(i.path+"codemirror/theme/"+i.editorTheme),this.cm.setOption("theme",e),this},setCodeMirrorTheme:function(e){return this.setEditorTheme(e),this},setPreviewTheme:function(e){var t=this.preview,i=this.settings.previewTheme,o=this.classPrefix+"preview-theme-";return t.removeClass(o+i).addClass(o+e),this.settings.previewTheme=e,this},setCodeMirror:function(){var e=this.settings,i=this.editor;"default"!==e.editorTheme&&t.loadCSS(e.path+"codemirror/theme/"+e.editorTheme);var o={mode:e.mode,theme:e.editorTheme,tabSize:e.tabSize,dragDrop:!1,autofocus:e.autoFocus,autoCloseTags:e.autoCloseTags,readOnly:e.readOnly?"nocursor":!1,indentUnit:e.indentUnit,lineNumbers:e.lineNumbers,lineWrapping:e.lineWrapping,extraKeys:{"Ctrl-Q":function(e){e.foldCode(e.getCursor())}},foldGutter:e.codeFold,gutters:["CodeMirror-linenumbers","CodeMirror-foldgutter"],matchBrackets:e.matchBrackets,indentWithTabs:e.indentWithTabs,styleActiveLine:e.styleActiveLine,styleSelectedText:e.styleSelectedText,autoCloseBrackets:e.autoCloseBrackets,showTrailingSpace:e.showTrailingSpace,highlightSelectionMatches:e.matchWordHighlight?{showToken:"onselected"===e.matchWordHighlight?!1:/\w/}:!1};return this.codeEditor=this.cm=t.$CodeMirror.fromTextArea(this.markdownTextarea[0],o),this.codeMirror=this.cmElement=i.children(".CodeMirror"),""!==e.value&&this.cm.setValue(e.value),this.codeMirror.css({fontSize:e.fontSize,width:e.watch?"50%":"100%"}),e.autoHeight&&(this.codeMirror.css("height","auto"),this.cm.setOption("viewportMargin",1/0)),e.lineNumbers||this.codeMirror.find(".CodeMirror-gutters").css("border-right","none"),this},getCodeMirrorOption:function(e){return this.cm.getOption(e)},setCodeMirrorOption:function(e,t){return this.cm.setOption(e,t),this},addKeyMap:function(e,t){return this.cm.addKeyMap(e,t),this},removeKeyMap:function(e){return this.cm.removeKeyMap(e),this},gotoLine:function(t){var i=this.settings;if(!i.gotoLine)return this;var o=this.cm,r=(this.editor,o.lineCount()),n=this.preview;if("string"==typeof t&&("last"===t&&(t=r),"first"===t&&(t=1)),"number"!=typeof t)return alert("Error: The line number must be an integer."),this;if(t=parseInt(t)-1,t>r)return alert("Error: The line number range 1-"+r),this;o.setCursor({line:t,ch:0});var a=o.getScrollInfo(),s=a.clientHeight,l=o.charCoords({line:t,ch:0},"local");if(o.scrollTo(null,(l.top+l.bottom-s)/2),i.watch){var c=this.codeMirror.find(".CodeMirror-scroll")[0],h=e(c).height(),d=c.scrollTop,u=d/c.scrollHeight;n.scrollTop(0===d?0:d+h>=c.scrollHeight-16?n[0].scrollHeight:n[0].scrollHeight*u)}return o.focus(),this},extend:function(){return"undefined"!=typeof arguments[1]&&("function"==typeof arguments[1]&&(arguments[1]=e.proxy(arguments[1],this)),this[arguments[0]]=arguments[1]),"object"==typeof arguments[0]&&"undefined"==typeof arguments[0].length&&e.extend(!0,this,arguments[0]),this},set:function(t,i){return"undefined"!=typeof i&&"function"==typeof i&&(i=e.proxy(i,this)),this[t]=i,this},config:function(t,i){var o=this.settings;return"object"==typeof t&&(o=e.extend(!0,o,t)),"string"==typeof t&&(o[t]=i),this.settings=o,this.recreate(),this},on:function(t,i){var o=this.settings;return"undefined"!=typeof o["on"+t]&&(o["on"+t]=e.proxy(i,this)),this},off:function(e){var t=this.settings;return"undefined"!=typeof t["on"+e]&&(t["on"+e]=function(){}),this},showToolbar:function(t){var i=this.settings;return i.readOnly?this:(i.toolbar&&(this.toolbar.length<1||""===this.toolbar.find("."+this.classPrefix+"menu").html())&&this.setToolbar(),i.toolbar=!0,this.toolbar.show(),this.resize(),e.proxy(t||function(){},this)(),this)},hideToolbar:function(t){var i=this.settings;return i.toolbar=!1,this.toolbar.hide(),this.resize(),e.proxy(t||function(){},this)(),this},setToolbarAutoFixed:function(t){var i=this.state,o=this.editor,r=this.toolbar,n=this.settings;"undefined"!=typeof t&&(n.toolbarAutoFixed=t);var a=function(){var t=e(window),i=t.scrollTop();return n.toolbarAutoFixed?void r.css(i-o.offset().top>10&&i
                    ';i.append(n),r=this.toolbar=i.children("."+o+"toolbar")}if(!e.toolbar)return r.hide(),this;r.show();for(var a="function"==typeof e.toolbarIcons?e.toolbarIcons():"string"==typeof e.toolbarIcons?t.toolbarModes[e.toolbarIcons]:e.toolbarIcons,s=r.find("."+this.classPrefix+"menu"),l="",c=!1,h=0,d=a.length;d>h;h++){var u=a[h];if("||"===u)c=!0;else if("|"===u)l+='
                  • |
                  • ';else{var f=/h(\d)/.test(u),g=u;"watch"!==u||e.watch||(g="unwatch");var p=e.lang.toolbar[g],m=e.toolbarIconTexts[g],w=e.toolbarIconsClass[g];p="undefined"==typeof p?"":p,m="undefined"==typeof m?"":m,w="undefined"==typeof w?"":w;var v=c?'
                  • ':"
                  • ";"undefined"!=typeof e.toolbarCustomIcons[u]&&"function"!=typeof e.toolbarCustomIcons[u]?v+=e.toolbarCustomIcons[u]:(v+='',v+=''+(f?u.toUpperCase():""===w?m:"")+"",v+=""),v+="
                  • ",l=c?v+l:l+v}}return s.html(l),s.find('[title="Lowercase"]').attr("title",e.lang.toolbar.lowercase),s.find('[title="ucwords"]').attr("title",e.lang.toolbar.ucwords),this.setToolbarHandler(),this.setToolbarAutoFixed(),this},dialogLockScreen:function(){return e.proxy(t.dialogLockScreen,this)(),this},dialogShowMask:function(i){return e.proxy(t.dialogShowMask,this)(i),this},getToolbarHandles:function(e){var i=this.toolbarHandlers=t.toolbarHandlers;return e&&"undefined"!=typeof toolbarIconHandlers[e]?i[e]:i},setToolbarHandler:function(){var i=this,o=this.settings;if(!o.toolbar||o.readOnly)return this;var r=this.toolbar,n=this.cm,a=this.classPrefix,s=this.toolbarIcons=r.find("."+a+"menu > li > a"),l=this.getToolbarHandles();return s.bind(t.mouseOrTouch("click","touchend"),function(t){var r=e(this).children(".fa"),a=r.attr("name"),s=n.getCursor(),c=n.getSelection();return""!==a?(i.activeIcon=r,"undefined"!=typeof l[a]?e.proxy(l[a],i)(n):"undefined"!=typeof o.toolbarHandlers[a]&&e.proxy(o.toolbarHandlers[a],i)(n,r,s,c),"link"!==a&&"reference-link"!==a&&"image"!==a&&"code-block"!==a&&"preformatted-text"!==a&&"watch"!==a&&"preview"!==a&&"search"!==a&&"fullscreen"!==a&&"info"!==a&&n.focus(),!1):void 0}),this},createDialog:function(i){return e.proxy(t.createDialog,this)(i)},createInfoDialog:function(){var e=this,i=this.editor,o=this.classPrefix,r=['
                    ','
                    ','

                    '+t.title+"v"+t.version+"

                    ","

                    "+this.lang.description+"

                    ",'

                    '+t.homePage+'

                    ','

                    Copyright © 2015 Pandao, The MIT License.

                    ',"
                    ",'',"
                    "].join("\n");i.append(r);var n=this.infoDialog=i.children("."+o+"dialog-info");return n.find("."+o+"dialog-close").bind(t.mouseOrTouch("click","touchend"),function(){e.hideInfoDialog()}),n.css("border",t.isIE8?"1px solid #ddd":"").css("z-index",t.dialogZindex).show(),this.infoDialogPosition(),this},infoDialogPosition:function(){var t=this.infoDialog,i=function(){t.css({top:(e(window).height()-t.height())/2+"px",left:(e(window).width()-t.width())/2+"px"})};return i(),e(window).resize(i),this},showInfoDialog:function(){e("html,body").css("overflow-x","hidden");var i=this.editor,o=this.settings,r=this.infoDialog=i.children("."+this.classPrefix+"dialog-info");return r.length<1&&this.createInfoDialog(),this.lockScreen(!0),this.mask.css({opacity:o.dialogMaskOpacity,backgroundColor:o.dialogMaskBgColor}).show(),r.css("z-index",t.dialogZindex).show(),this.infoDialogPosition(),this},hideInfoDialog:function(){return e("html,body").css("overflow-x",""),this.infoDialog.hide(),this.mask.hide(),this.lockScreen(!1),this},lockScreen:function(e){return t.lockScreen(e),this.resize(),this},recreate:function(){var e=this.editor,t=this.settings;return this.codeMirror.remove(),this.setCodeMirror(),t.readOnly||(e.find(".editormd-dialog").length>0&&e.find(".editormd-dialog").remove(),t.toolbar&&(this.getToolbarHandles(),this.setToolbar())),this.loadedDisplay(!0),this},previewCodeHighlight:function(){var e=this.settings,t=this.previewContainer;return e.previewCodeHighlight&&(t.find("pre").addClass("prettyprint linenums"),"undefined"!=typeof prettyPrint&&prettyPrint()),this},katexRender:function(){return null===i?this:(this.previewContainer.find("."+t.classNames.tex).each(function(){var i=e(this);t.$katex.render(i.text(),i[0]),i.find(".katex").css("font-size","1.6em")}),this)},flowChartAndSequenceDiagramRender:function(){var i=this,r=this.settings,n=this.previewContainer;if(t.isIE8)return this;if(r.flowChart){if(null===o)return this;n.find(".flowchart").flowChart()}r.sequenceDiagram&&n.find(".sequence-diagram").sequenceDiagram({theme:"simple"});var a=i.preview,s=i.codeMirror,l=s.find(".CodeMirror-scroll"),c=l.height(),h=l.scrollTop(),d=h/l[0].scrollHeight,u=0;a.find(".markdown-toc-list").each(function(){u+=e(this).height()});var f=a.find(".editormd-toc-menu").height();return f=f?f:0,a.scrollTop(0===h?0:h+c>=l[0].scrollHeight-16?a[0].scrollHeight:(a[0].scrollHeight+u+f)*d),this},registerKeyMaps:function(i){var o=this,r=this.cm,n=this.settings,a=t.toolbarHandlers,s=n.disabledKeyMaps;if(i=i||null){for(var l in i)if(e.inArray(l,s)<0){var c={};c[l]=i[l],r.addKeyMap(i)}}else{for(var h in t.keyMaps){var d=t.keyMaps[h],u="string"==typeof d?e.proxy(a[d],o):e.proxy(d,o);if(e.inArray(h,["F9","F10","F11"])<0&&e.inArray(h,s)<0){var f={};f[h]=u,r.addKeyMap(f)}}e(window).keydown(function(t){var i={120:"F9",121:"F10",122:"F11"};if(e.inArray(i[t.keyCode],s)<0)switch(t.keyCode){case 120:return e.proxy(a.watch,o)(),!1;case 121:return e.proxy(a.preview,o)(),!1;case 122:return e.proxy(a.fullscreen,o)(),!1}})}return this},bindScrollEvent:function(){var i=this,o=this.preview,r=this.settings,n=this.codeMirror,a=t.mouseOrTouch;if(!r.syncScrolling)return this;var s=function(){n.find(".CodeMirror-scroll").bind(a("scroll","touchmove"),function(t){var n=e(this).height(),a=e(this).scrollTop(),s=a/e(this)[0].scrollHeight,l=0;o.find(".markdown-toc-list").each(function(){l+=e(this).height()});var c=o.find(".editormd-toc-menu").height();c=c?c:0,o.scrollTop(0===a?0:a+n>=e(this)[0].scrollHeight-16?o[0].scrollHeight:(o[0].scrollHeight+l+c)*s),e.proxy(r.onscroll,i)(t)})},l=function(){n.find(".CodeMirror-scroll").unbind(a("scroll","touchmove"))},c=function(){o.bind(a("scroll","touchmove"),function(t){var o=e(this).height(),a=e(this).scrollTop(),s=a/e(this)[0].scrollHeight,l=n.find(".CodeMirror-scroll");l.scrollTop(0===a?0:a+o>=e(this)[0].scrollHeight?l[0].scrollHeight:l[0].scrollHeight*s),e.proxy(r.onpreviewscroll,i)(t)})},h=function(){o.unbind(a("scroll","touchmove"))};return n.bind({mouseover:s,mouseout:l,touchstart:s,touchend:l}),"single"===r.syncScrolling?this:(o.bind({mouseover:c,mouseout:h,touchstart:c,touchend:h}),this)},bindChangeEvent:function(){var e=this,t=this.cm,o=this.settings;return o.syncScrolling?(t.on("change",function(t,r){o.watch&&e.previewContainer.css("padding",o.autoHeight?"20px 20px 50px 40px":"20px"),i=setTimeout(function(){clearTimeout(i),e.save(),i=null},o.delay)}),this):this},loadedDisplay:function(t){t=t||!1;var i=this,o=this.editor,r=this.preview,n=this.settings;return this.containerMask.hide(),this.save(),n.watch&&r.show(),o.data("oldWidth",o.width()).data("oldHeight",o.height()),this.resize(),this.registerKeyMaps(),e(window).resize(function(){i.resize()}),this.bindScrollEvent().bindChangeEvent(),t||e.proxy(n.onload,this)(),this.state.loaded=!0,this},width:function(e){return this.editor.css("width","number"==typeof e?e+"px":e),this.resize(),this},height:function(e){return this.editor.css("height","number"==typeof e?e+"px":e),this.resize(),this},resize:function(t,i){t=t||null,i=i||null;var o=this.state,r=this.editor,n=this.preview,a=this.toolbar,s=this.settings,l=this.codeMirror;if(t&&r.css("width","number"==typeof t?t+"px":t),!s.autoHeight||o.fullscreen||o.preview?(i&&r.css("height","number"==typeof i?i+"px":i),o.fullscreen&&r.height(e(window).height()),s.toolbar&&!s.readOnly?l.css("margin-top",a.height()+1).height(r.height()-a.height()):l.css("margin-top",0).height(r.height())):(r.css("height","auto"),l.css("height","auto")),s.watch)if(l.width(r.width()/2),n.width(o.preview?r.width():r.width()/2),this.previewContainer.css("padding",s.autoHeight?"20px 20px 50px 40px":"20px"),s.toolbar&&!s.readOnly?n.css("top",a.height()+1):n.css("top",0),!s.autoHeight||o.fullscreen||o.preview){var c=s.toolbar&&!s.readOnly?r.height()-a.height():r.height();n.height(c)}else n.height("");else l.width(r.width()),n.hide();return o.loaded&&e.proxy(s.onresize,this)(),this},save:function(){if(null===i)return this;var r=this,n=this.state,a=this.settings,s=this.cm,l=s.getValue(),c=this.previewContainer;if("gfm"!==a.mode&&"markdown"!==a.mode)return this.markdownTextarea.val(l),this;var h=t.$marked,d=this.markdownToC=[],u=this.markedRendererOptions={toc:a.toc,tocm:a.tocm,tocStartLevel:a.tocStartLevel,pageBreak:a.pageBreak,taskList:a.taskList,emoji:a.emoji,tex:a.tex,atLink:a.atLink,emailLink:a.emailLink,flowChart:a.flowChart,sequenceDiagram:a.sequenceDiagram,previewCodeHighlight:a.previewCodeHighlight},f=this.markedOptions={renderer:t.markedRenderer(d,u),gfm:!0,tables:!0,breaks:!0,pedantic:!1,sanitize:a.htmlDecode?!1:!0,smartLists:!0,smartypants:!0};h.setOptions(f);var g=t.$marked(l,f);if(g=t.filterHTMLTags(g,a.htmlDecode),this.markdownTextarea.text(l),s.save(),a.saveHTMLToTextarea&&this.htmlTextarea.text(g),a.watch||!a.watch&&n.preview){if(c.html(g),this.previewCodeHighlight(),a.toc){var p=""===a.tocContainer?c:e(a.tocContainer),m=p.find("."+this.classPrefix+"toc-menu");p.attr("previewContainer",""===a.tocContainer?"true":"false"),""!==a.tocContainer&&m.length>0&&m.remove(),t.markdownToCRenderer(d,p,a.tocDropdown,a.tocStartLevel),(a.tocDropdown||p.find("."+this.classPrefix+"toc-menu").length>0)&&t.tocDropdownMenu(p,""!==a.tocTitle?a.tocTitle:this.lang.tocTitle),""!==a.tocContainer&&c.find(".markdown-toc").css("border","none")}a.tex&&(!t.kaTeXLoaded&&a.autoLoadModules?t.loadKaTeX(function(){t.$katex=katex,t.kaTeXLoaded=!0,r.katexRender()}):(t.$katex=katex,this.katexRender())),(a.flowChart||a.sequenceDiagram)&&(o=setTimeout(function(){clearTimeout(o),r.flowChartAndSequenceDiagramRender(),o=null},10)),n.loaded&&e.proxy(a.onchange,this)()}return this},focus:function(){return this.cm.focus(),this},setCursor:function(e){return this.cm.setCursor(e),this},getCursor:function(){return this.cm.getCursor()},setSelection:function(e,t){return this.cm.setSelection(e,t),this},getSelection:function(){return this.cm.getSelection()},setSelections:function(e){return this.cm.setSelections(e),this},getSelections:function(){return this.cm.getSelections()},replaceSelection:function(e){return this.cm.replaceSelection(e),this},insertValue:function(e){return this.replaceSelection(e),this},appendMarkdown:function(e){var t=(this.settings,this.cm);return t.setValue(t.getValue()+e),this},setMarkdown:function(e){return this.cm.setValue(e||this.settings.markdown),this},getMarkdown:function(){return this.cm.getValue()},getValue:function(){return this.cm.getValue()},setValue:function(e){return this.cm.setValue(e),this},clear:function(){return this.cm.setValue(""),this},getHTML:function(){return this.settings.saveHTMLToTextarea?this.htmlTextarea.val():(alert("Error: settings.saveHTMLToTextarea == false"),!1)},getTextareaSavedHTML:function(){return this.getHTML()},getPreviewedHTML:function(){return this.settings.watch?this.previewContainer.html():(alert("Error: settings.watch == false"),!1)},watch:function(t){var o=this.settings;if(e.inArray(o.mode,["gfm","markdown"])<0)return this;if(this.state.watching=o.watch=!0,this.preview.show(),this.toolbar){var r=o.toolbarIconsClass.watch,n=o.toolbarIconsClass.unwatch,a=this.toolbar.find(".fa[name=watch]");a.parent().attr("title",o.lang.toolbar.watch),a.removeClass(n).addClass(r)}return this.codeMirror.css("border-right","1px solid #ddd").width(this.editor.width()/2),i=0,this.save().resize(),o.onwatch||(o.onwatch=t||function(){}),e.proxy(o.onwatch,this)(),this},unwatch:function(t){var i=this.settings;if(this.state.watching=i.watch=!1,this.preview.hide(),this.toolbar){var o=i.toolbarIconsClass.watch,r=i.toolbarIconsClass.unwatch,n=this.toolbar.find(".fa[name=watch]");n.parent().attr("title",i.lang.toolbar.unwatch),n.removeClass(o).addClass(r)}return this.codeMirror.css("border-right","none").width(this.editor.width()),this.resize(),i.onunwatch||(i.onunwatch=t||function(){}),e.proxy(i.onunwatch,this)(),this},show:function(t){t=t||function(){};var i=this;return this.editor.show(0,function(){e.proxy(t,i)()}),this},hide:function(t){t=t||function(){};var i=this;return this.editor.hide(0,function(){e.proxy(t,i)()}),this},previewing:function(){var i=this,o=this.editor,r=this.preview,n=this.toolbar,a=this.settings,s=this.codeMirror,l=this.previewContainer;if(e.inArray(a.mode,["gfm","markdown"])<0)return this;a.toolbar&&n&&(n.toggle(),n.find(".fa[name=preview]").toggleClass("active")),s.toggle();var c=function(e){e.shiftKey&&27===e.keyCode&&i.previewed()};"none"===s.css("display")?(this.state.preview=!0,this.state.fullscreen&&r.css("background","#fff"),o.find("."+this.classPrefix+"preview-close-btn").show().bind(t.mouseOrTouch("click","touchend"),function(){i.previewed()}),a.watch?l.css("padding",""):this.save(),l.addClass(this.classPrefix+"preview-active"),r.show().css({position:"",top:0,width:o.width(),height:a.autoHeight&&!this.state.fullscreen?"auto":o.height()}),this.state.loaded&&e.proxy(a.onpreviewing,this)(),e(window).bind("keyup",c)):(e(window).unbind("keyup",c),this.previewed())},previewed:function(){var i=this.editor,o=this.preview,r=this.toolbar,n=this.settings,a=this.previewContainer,s=i.find("."+this.classPrefix+"preview-close-btn");return this.state.preview=!1,this.codeMirror.show(),n.toolbar&&r.show(),o[n.watch?"show":"hide"](),s.hide().unbind(t.mouseOrTouch("click","touchend")),a.removeClass(this.classPrefix+"preview-active"),n.watch&&a.css("padding","20px"),o.css({background:null,position:"absolute",width:i.width()/2,height:n.autoHeight&&!this.state.fullscreen?"auto":i.height()-r.height(),top:n.toolbar?r.height():0}),this.state.loaded&&e.proxy(n.onpreviewed,this)(),this},fullscreen:function(){var t=this,i=this.state,o=this.editor,r=(this.preview,this.toolbar),n=this.settings,a=this.classPrefix+"fullscreen";r&&r.find(".fa[name=fullscreen]").parent().toggleClass("active");var s=function(e){e.shiftKey||27!==e.keyCode||i.fullscreen&&t.fullscreenExit()};return o.hasClass(a)?(e(window).unbind("keyup",s),this.fullscreenExit()):(i.fullscreen=!0,e("html,body").css("overflow","hidden"),o.css({width:e(window).width(),height:e(window).height()}).addClass(a),this.resize(),e.proxy(n.onfullscreen,this)(),e(window).bind("keyup",s)),this},fullscreenExit:function(){var t=this.editor,i=this.settings,o=this.toolbar,r=this.classPrefix+"fullscreen";return this.state.fullscreen=!1,o&&o.find(".fa[name=fullscreen]").parent().removeClass("active"),e("html,body").css("overflow",""),t.css({width:t.data("oldWidth"),height:t.data("oldHeight")}).removeClass(r),this.resize(),e.proxy(i.onfullscreenExit,this)(),this},executePlugin:function(i,o){var r=this,n=this.cm,a=this.settings;return o=a.pluginPath+o,"function"==typeof define?"undefined"==typeof this[i]?(alert("Error: "+i+" plugin is not found, you are not load this plugin."),this):(this[i](n),this):(e.inArray(o,t.loadFiles.plugin)<0?t.loadPlugin(o,function(){t.loadPlugins[i]=r[i],r[i](n)}):e.proxy(t.loadPlugins[i],this)(n),this)},search:function(e){var t=this.settings;return t.searchReplace?(t.readOnly||this.cm.execCommand(e||"find"),this):(alert("Error: settings.searchReplace == false"),this)},searchReplace:function(){return this.search("replace"),this},searchReplaceAll:function(){return this.search("replaceAll"),this}},t.fn.init.prototype=t.fn,t.dialogLockScreen=function(){var t=this.settings||{dialogLockScreen:!0};t.dialogLockScreen&&(e("html,body").css("overflow","hidden"),this.resize())},t.dialogShowMask=function(t){var i=this.editor,o=this.settings||{dialogShowMask:!0};t.css({top:(e(window).height()-t.height())/2+"px",left:(e(window).width()-t.width())/2+"px"}),o.dialogShowMask&&i.children("."+this.classPrefix+"mask").css("z-index",parseInt(t.css("z-index"))-1).show()},t.toolbarHandlers={undo:function(){this.cm.undo()},redo:function(){this.cm.redo()},bold:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection("**"+i+"**"),""===i&&e.setCursor(t.line,t.ch+2)},del:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection("~~"+i+"~~"),""===i&&e.setCursor(t.line,t.ch+2)},italic:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection("*"+i+"*"),""===i&&e.setCursor(t.line,t.ch+1)},quote:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("> "+i),e.setCursor(t.line,t.ch+2)):e.replaceSelection("> "+i)},ucfirst:function(){var e=this.cm,i=e.getSelection(),o=e.listSelections();e.replaceSelection(t.firstUpperCase(i)),e.setSelections(o)},ucwords:function(){var e=this.cm,i=e.getSelection(),o=e.listSelections();e.replaceSelection(t.wordsFirstUpperCase(i)),e.setSelections(o)},uppercase:function(){var e=this.cm,t=e.getSelection(),i=e.listSelections();e.replaceSelection(t.toUpperCase()),e.setSelections(i)},lowercase:function(){var e=this.cm,t=(e.getCursor(),e.getSelection()),i=e.listSelections();e.replaceSelection(t.toLowerCase()),e.setSelections(i)},h1:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("# "+i),e.setCursor(t.line,t.ch+2)):e.replaceSelection("# "+i)},h2:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0), +e.replaceSelection("## "+i),e.setCursor(t.line,t.ch+3)):e.replaceSelection("## "+i)},h3:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("### "+i),e.setCursor(t.line,t.ch+4)):e.replaceSelection("### "+i)},h4:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("#### "+i),e.setCursor(t.line,t.ch+5)):e.replaceSelection("#### "+i)},h5:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("##### "+i),e.setCursor(t.line,t.ch+6)):e.replaceSelection("##### "+i)},h6:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();0!==t.ch?(e.setCursor(t.line,0),e.replaceSelection("###### "+i),e.setCursor(t.line,t.ch+7)):e.replaceSelection("###### "+i)},"list-ul":function(){var e=this.cm,t=(e.getCursor(),e.getSelection());if(""===t)e.replaceSelection("- "+t);else{for(var i=t.split("\n"),o=0,r=i.length;r>o;o++)i[o]=""===i[o]?"":"- "+i[o];e.replaceSelection(i.join("\n"))}},"list-ol":function(){var e=this.cm,t=(e.getCursor(),e.getSelection());if(""===t)e.replaceSelection("1. "+t);else{for(var i=t.split("\n"),o=0,r=i.length;r>o;o++)i[o]=""===i[o]?"":o+1+". "+i[o];e.replaceSelection(i.join("\n"))}},hr:function(){{var e=this.cm,t=e.getCursor();e.getSelection()}e.replaceSelection((0!==t.ch?"\n\n":"\n")+"------------\n\n")},tex:function(){if(!this.settings.tex)return alert("settings.tex === false"),this;var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection("$$"+i+"$$"),""===i&&e.setCursor(t.line,t.ch+2)},link:function(){this.executePlugin("linkDialog","link-dialog/link-dialog")},"reference-link":function(){this.executePlugin("referenceLinkDialog","reference-link-dialog/reference-link-dialog")},pagebreak:function(){if(!this.settings.pageBreak)return alert("settings.pageBreak === false"),this;{var e=this.cm;e.getSelection()}e.replaceSelection("\r\n[========]\r\n")},image:function(){this.executePlugin("imageDialog","image-dialog/image-dialog")},code:function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection("`"+i+"`"),""===i&&e.setCursor(t.line,t.ch+1)},"code-block":function(){this.executePlugin("codeBlockDialog","code-block-dialog/code-block-dialog")},"preformatted-text":function(){this.executePlugin("preformattedTextDialog","preformatted-text-dialog/preformatted-text-dialog")},table:function(){this.executePlugin("tableDialog","table-dialog/table-dialog")},datetime:function(){var e=this.cm,i=(e.getSelection(),new Date,this.settings.lang.name),o=t.dateFormat()+" "+t.dateFormat("zh-cn"===i||"zh-tw"===i?"cn-week-day":"week-day");e.replaceSelection(o)},emoji:function(){this.executePlugin("emojiDialog","emoji-dialog/emoji-dialog")},"html-entities":function(){this.executePlugin("htmlEntitiesDialog","html-entities-dialog/html-entities-dialog")},"goto-line":function(){this.executePlugin("gotoLineDialog","goto-line-dialog/goto-line-dialog")},watch:function(){this[this.settings.watch?"unwatch":"watch"]()},preview:function(){this.previewing()},fullscreen:function(){this.fullscreen()},clear:function(){this.clear()},search:function(){this.search()},help:function(){this.executePlugin("helpDialog","help-dialog/help-dialog")},info:function(){this.showInfoDialog()}},t.keyMaps={"Ctrl-1":"h1","Ctrl-2":"h2","Ctrl-3":"h3","Ctrl-4":"h4","Ctrl-5":"h5","Ctrl-6":"h6","Ctrl-B":"bold","Ctrl-D":"datetime","Ctrl-E":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();return this.settings.emoji?(e.replaceSelection(":"+i+":"),void(""===i&&e.setCursor(t.line,t.ch+1))):void alert("Error: settings.emoji == false")},"Ctrl-Alt-G":"goto-line","Ctrl-H":"hr","Ctrl-I":"italic","Ctrl-K":"code","Ctrl-L":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection(),o=""===i?"":' "'+i+'"';e.replaceSelection("["+i+"]("+o+")"),""===i&&e.setCursor(t.line,t.ch+1)},"Ctrl-U":"list-ul","Shift-Ctrl-A":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();return this.settings.atLink?(e.replaceSelection("@"+i),void(""===i&&e.setCursor(t.line,t.ch+1))):void alert("Error: settings.atLink == false")},"Shift-Ctrl-C":"code","Shift-Ctrl-Q":"quote","Shift-Ctrl-S":"del","Shift-Ctrl-K":"tex","Shift-Alt-C":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection();e.replaceSelection(["```",i,"```"].join("\n")),""===i&&e.setCursor(t.line,t.ch+3)},"Shift-Ctrl-Alt-C":"code-block","Shift-Ctrl-H":"html-entities","Shift-Alt-H":"help","Shift-Ctrl-E":"emoji","Shift-Ctrl-U":"uppercase","Shift-Alt-U":"ucwords","Shift-Ctrl-Alt-U":"ucfirst","Shift-Alt-L":"lowercase","Shift-Ctrl-I":function(){var e=this.cm,t=e.getCursor(),i=e.getSelection(),o=""===i?"":' "'+i+'"';e.replaceSelection("!["+i+"]("+o+")"),""===i&&e.setCursor(t.line,t.ch+4)},"Shift-Ctrl-Alt-I":"image","Shift-Ctrl-L":"link","Shift-Ctrl-O":"list-ol","Shift-Ctrl-P":"preformatted-text","Shift-Ctrl-T":"table","Shift-Alt-P":"pagebreak",F9:"watch",F10:"preview",F11:"fullscreen"};var r=function(e){return String.prototype.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")};t.trim=r;var n=function(e){return e.toLowerCase().replace(/\b(\w)|\s(\w)/g,function(e){return e.toUpperCase()})};t.ucwords=t.wordsFirstUpperCase=n;var a=function(e){return e.toLowerCase().replace(/\b(\w)/,function(e){return e.toUpperCase()})};return t.firstUpperCase=t.ucfirst=a,t.urls={atLinkBase:"https://github.com/"},t.regexs={atLink:/@(\w+)/g,email:/(\w+)@(\w+)\.(\w+)\.?(\w+)?/g,emailLink:/(mailto:)?([\w\.\_]+)@(\w+)\.(\w+)\.?(\w+)?/g,emoji:/:([\w\+-]+):/g,emojiDatetime:/(\d{2}:\d{2}:\d{2})/g,twemoji:/:(tw-([\w]+)-?(\w+)?):/g,fontAwesome:/:(fa-([\w]+)(-(\w+)){0,}):/g,editormdLogo:/:(editormd-logo-?(\w+)?):/g,pageBreak:/^\[[=]{8,}\]$/},t.emoji={path:"http://www.emoji-cheat-sheet.com/graphics/emojis/",ext:".png"},t.twemoji={path:"http://twemoji.maxcdn.com/36x36/",ext:".png"},t.markedRenderer=function(i,o){var n={toc:!0,tocm:!1,tocStartLevel:1,pageBreak:!0,atLink:!0,emailLink:!0,taskList:!1,emoji:!1,tex:!1,flowChart:!1,sequenceDiagram:!1},a=e.extend(n,o||{}),s=t.$marked,l=new s.Renderer;i=i||[];var c=t.regexs,h=c.atLink,d=c.emoji,u=c.email,f=c.emailLink,g=c.twemoji,p=c.fontAwesome,m=c.editormdLogo,w=c.pageBreak;return l.emoji=function(e){e=e.replace(t.regexs.emojiDatetime,function(e){return e.replace(/:/g,":")});var i=e.match(d);if(!i||!a.emoji)return e;for(var o=0,r=i.length;r>o;o++)":+1:"===i[o]&&(i[o]=":\\+1:"),e=e.replace(new RegExp(i[o]),function(e,i){var o=e.match(p),r=e.replace(/:/g,"");if(o)for(var n=0,a=o.length;a>n;n++){var s=o[n].replace(/:/g,"");return''}else{var l=e.match(m),c=e.match(g);if(l)for(var h=0,d=l.length;d>h;h++){var u=l[h].replace(/:/g,"");return''}else{if(!c){var f="+1"===r?"plus1":r;return f="black_large_square"===f?"black_square":f,f="moon"===f?"waxing_gibbous_moon":f,':'+r+':'}for(var w=0,v=c.length;v>w;w++){var k=c[w].replace(/:/g,"").replace("tw-","");return'twemoji-'+k+''}}}});return e},l.atLink=function(i){return h.test(i)?(a.atLink&&(i=i.replace(u,function(e,t,i,o){return e.replace(/@/g,"_#_@_#_")}),i=i.replace(h,function(e,i){return''+e+""}).replace(/_#_@_#_/g,"@")),a.emailLink&&(i=i.replace(f,function(t,i,o,r,n){return!i&&e.inArray(n,"jpg|jpeg|png|gif|webp|ico|icon|pdf".split("|"))<0?''+t+"":t})),i):i},l.link=function(e,t,i){if(this.options.sanitize){try{var o=decodeURIComponent(unescape(e)).replace(/[^\w:]/g,"").toLowerCase()}catch(r){return""}if(0===o.indexOf("javascript:"))return""}var n=''+i.replace(/@/g,"@")+""):(t&&(n+=' title="'+t+'"'),n+=">"+i+"")},l.heading=function(e,t,o){var n=e,a=/\s*\]*)\>(.*)\<\/a\>\s*/;if(a.test(e)){var s=[];e=e.split(/\]+)\>([^\>]*)\<\/a\>/);for(var l=0,c=e.length;c>l;l++)s.push(e[l].replace(/\s*href\=\"(.*)\"\s*/g,""));e=s.join(" ")}e=r(e);var h=e.toLowerCase().replace(/[^\w]+/g,"-"),d={text:e,level:t,slug:h},u=/^[\u4e00-\u9fa5]+$/.test(e),f=u?escape(e).replace(/\%/g,""):e.toLowerCase().replace(/[^\w]+/g,"-");i.push(d);var g="';return g+='',g+='',g+=this.atLink(a?this.emoji(n):this.emoji(e)),g+=""},l.pageBreak=function(e){return w.test(e)&&a.pageBreak&&(e='
                    '),e},l.paragraph=function(e){var i=/\$\$(.*)\$\$/g.test(e),o=/^\$\$(.*)\$\$$/.test(e),r=o?' class="'+t.classNames.tex+'"':"",n=a.tocm?/^(\[TOC\]|\[TOCM\])$/.test(e):/^\[TOC\]$/.test(e),s=/^\[TOCM\]$/.test(e);e=!o&&i?e.replace(/(\$\$([^\$]*)\$\$)+/g,function(e,i){return''+i.replace(/\$/g,"")+""}):o?e.replace(/\$/g,""):e;var l='
                    '+e+"
                    ";return n?s?'
                    '+l+"

                    ":l:w.test(e)?this.pageBreak(e):""+this.atLink(this.emoji(e))+"

                    \n"},l.code=function(e,i,o){return"seq"===i||"sequence"===i?'
                    '+e+"
                    ":"flow"===i?'
                    '+e+"
                    ":"math"===i||"latex"===i||"katex"===i?'

                    '+e+"

                    ":s.Renderer.prototype.code.apply(this,arguments)},l.tablecell=function(e,t){var i=t.header?"th":"td",o=t.align?"<"+i+' style="text-align:'+t.align+'">':"<"+i+">";return o+this.atLink(this.emoji(e))+"\n"},l.listitem=function(e){return a.taskList&&/^\s*\[[x\s]\]\s*/.test(e)?(e=e.replace(/^\s*\[\s\]\s*/,' ').replace(/^\s*\[x\]\s*/,' '),'
                  • '+this.atLink(this.emoji(e))+"
                  • "):"
                  • "+this.atLink(this.emoji(e))+"
                  • "},l},t.markdownToCRenderer=function(e,t,i,o){var r="",n=0,a=this.classPrefix;o=o||1;for(var s=0,l=e.length;l>s;s++){var c=e[s].text,h=e[s].level;o>h||(r+=h>n?"":n>h?new Array(n-h+2).join("
                • "):"",r+='
                • '+c+"
                • "); + } + else + { + html += ""; + } + + html += "
                • " + text + "