Passed
Push — master ( 3324e2...2ba76b )
by Ralf
13:44
created

Resources/Public/JavaScript/editormd/editormd.js   F

Complexity

Total Complexity 585
Complexity/F 2.29

Size

Lines of Code 4586
Function Count 256

Duplication

Duplicated Lines 68
Ratio 1.48 %

Importance

Changes 0
Metric Value
wmc 585
eloc 2452
c 0
b 0
f 0
dl 68
loc 4586
rs 0.8
mnd 329
bc 329
fnc 256
bpm 1.2851
cpm 2.2851
noi 81

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Resources/Public/JavaScript/editormd/editormd.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*
2
 * Editor.md
3
 *
4
 * @file        editormd.js 
5
 * @version     v1.5.0 
6
 * @description Open source online markdown editor.
7
 * @license     MIT License
8
 * @author      Pandao
9
 * {@link       https://github.com/pandao/editor.md}
10
 * @updateTime  2015-06-09
11
 */
12
13
;(function(factory) {
14
    "use strict";
15
    
16
	// CommonJS/Node.js
17
	if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
18
    { 
19
        module.exports = factory;
20
    }
21
	else if (typeof define === "function")  // AMD/CMD/Sea.js
0 ignored issues
show
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
22
	{
23
        if (define.amd) // for Require.js
24
        {
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
25
            /* Require.js define replace */
26
        } 
27
        else 
28
        {
29
		    define(["jquery"], factory);  // for Sea.js
30
        }
31
	} 
32
	else
33
	{ 
34
        window.editormd = factory();
35
	}
36
    
37
}(function() {    
38
39
    /* Require.js assignment replace */
40
    
41
    "use strict";
42
    
43
    var $ = (typeof (jQuery) !== "undefined") ? jQuery : Zepto;
0 ignored issues
show
Bug introduced by
The variable Zepto seems to be never declared. If this is a global, consider adding a /** global: Zepto */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
44
45
	if (typeof ($) === "undefined") {
46
		return ;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
47
	}
48
    
49
    /**
50
     * editormd
51
     * 
52
     * @param   {String} id           编辑器的ID
53
     * @param   {Object} options      配置选项 Key/Value
54
     * @returns {Object} editormd     返回editormd对象
55
     */
56
    
57
    var editormd         = function (id, options) {
58
        return new editormd.fn.init(id, options);
59
    };
60
    
61
    editormd.title        = editormd.$name = "Editor.md";
62
    editormd.version      = "1.5.0";
63
    editormd.homePage     = "https://pandao.github.io/editor.md/";
64
    editormd.classPrefix  = "editormd-";
65
    
66
    editormd.toolbarModes = {
67
        full : [
68
            "undo", "redo", "|", 
69
            "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|", 
70
            "h1", "h2", "h3", "h4", "h5", "h6", "|", 
71
            "list-ul", "list-ol", "hr", "|",
72
            "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|",
73
            "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|",
74
            "help", "info"
75
        ],
76
        simple : [
77
            "undo", "redo", "|", 
78
            "bold", "del", "italic", "quote", "uppercase", "lowercase", "|", 
79
            "h1", "h2", "h3", "h4", "h5", "h6", "|", 
80
            "list-ul", "list-ol", "hr", "|",
81
            "watch", "preview", "fullscreen", "|",
82
            "help", "info"
83
        ],
84
        mini : [
85
            "undo", "redo", "|",
86
            "watch", "preview", "|",
87
            "help", "info"
88
        ]
89
    };
90
    
91
    editormd.defaults     = {
92
        mode                 : "gfm",          //gfm or markdown
93
        name                 : "",             // Form element name
94
        value                : "",             // value for CodeMirror, if mode not gfm/markdown
95
        theme                : "",             // Editor.md self themes, before v1.5.0 is CodeMirror theme, default empty
96
        editorTheme          : "default",      // Editor area, this is CodeMirror theme at v1.5.0
97
        previewTheme         : "",             // Preview area theme, default empty
98
        markdown             : "",             // Markdown source code
99
        appendMarkdown       : "",             // if in init textarea value not empty, append markdown to textarea
100
        width                : "100%",
101
        height               : "100%",
102
        path                 : "./lib/",       // Dependents module file directory
103
        pluginPath           : "",             // If this empty, default use settings.path + "../plugins/"
104
        delay                : 300,            // Delay parse markdown to html, Uint : ms
105
        autoLoadModules      : true,           // Automatic load dependent module files
106
        watch                : true,
107
        placeholder          : "Enjoy Markdown! coding now...",
108
        gotoLine             : true,
109
        codeFold             : false,
110
        autoHeight           : false,
111
		autoFocus            : true,
112
        autoCloseTags        : true,
113
        searchReplace        : true,
114
        syncScrolling        : true,           // true | false | "single", default true
115
        readOnly             : false,
116
        tabSize              : 4,
117
		indentUnit           : 4,
118
        lineNumbers          : true,
119
		lineWrapping         : true,
120
		autoCloseBrackets    : true,
121
		showTrailingSpace    : true,
122
		matchBrackets        : true,
123
		indentWithTabs       : true,
124
		styleSelectedText    : true,
125
        matchWordHighlight   : true,           // options: true, false, "onselected"
126
        styleActiveLine      : true,           // Highlight the current line
127
        dialogLockScreen     : true,
128
        dialogShowMask       : true,
129
        dialogDraggable      : true,
130
        dialogMaskBgColor    : "#fff",
131
        dialogMaskOpacity    : 0.1,
132
        fontSize             : "13px",
133
        saveHTMLToTextarea   : false,
134
        disabledKeyMaps      : [],
135
        
136
        onload               : function() {},
137
        onresize             : function() {},
138
        onchange             : function() {},
139
        onwatch              : null,
140
        onunwatch            : null,
141
        onpreviewing         : function() {},
142
        onpreviewed          : function() {},
143
        onfullscreen         : function() {},
144
        onfullscreenExit     : function() {},
145
        onscroll             : function() {},
146
        onpreviewscroll      : function() {},
147
        
148
        imageUpload          : false,
149
        imageFormats         : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
150
        imageUploadURL       : "",
151
        crossDomainUpload    : false,
152
        uploadCallbackURL    : "",
153
        
154
        toc                  : true,           // Table of contents
155
        tocm                 : false,           // Using [TOCM], auto create ToC dropdown menu
156
        tocTitle             : "",             // for ToC dropdown menu btn
157
        tocDropdown          : false,
158
        tocContainer         : "",
159
        tocStartLevel        : 1,              // Said from H1 to create ToC
160
        htmlDecode           : false,          // Open the HTML tag identification 
161
        pageBreak            : true,           // Enable parse page break [========]
162
        atLink               : true,           // for @link
163
        emailLink            : true,           // for email address auto link
164
        taskList             : false,          // Enable Github Flavored Markdown task lists
165
        emoji                : false,          // :emoji: , Support Github emoji, Twitter Emoji (Twemoji);
166
                                               // Support FontAwesome icon emoji :fa-xxx: > Using fontAwesome icon web fonts;
167
                                               // Support Editor.md logo icon emoji :editormd-logo: :editormd-logo-1x: > 1~8x;
168
        tex                  : false,          // TeX(LaTeX), based on KaTeX
169
        flowChart            : false,          // flowChart.js only support IE9+
170
        sequenceDiagram      : false,          // sequenceDiagram.js only support IE9+
171
        previewCodeHighlight : true,
172
                
173
        toolbar              : true,           // show/hide toolbar
174
        toolbarAutoFixed     : true,           // on window scroll auto fixed position
175
        toolbarIcons         : "full",
176
        toolbarTitles        : {},
177
        toolbarHandlers      : {
178
            ucwords : function() {
179
                return editormd.toolbarHandlers.ucwords;
180
            },
181
            lowercase : function() {
182
                return editormd.toolbarHandlers.lowercase;
183
            }
184
        },
185
        toolbarCustomIcons   : {               // using html tag create toolbar icon, unused default <a> tag.
186
            lowercase        : "<a href=\"javascript:;\" title=\"Lowercase\" unselectable=\"on\"><i class=\"fa\" name=\"lowercase\" style=\"font-size:24px;margin-top: -10px;\">a</i></a>",
187
            "ucwords"        : "<a href=\"javascript:;\" title=\"ucwords\" unselectable=\"on\"><i class=\"fa\" name=\"ucwords\" style=\"font-size:20px;margin-top: -3px;\">Aa</i></a>"
188
        }, 
189
        toolbarIconsClass    : {
190
            undo             : "fa-undo",
191
            redo             : "fa-repeat",
192
            bold             : "fa-bold",
193
            del              : "fa-strikethrough",
194
            italic           : "fa-italic",
195
            quote            : "fa-quote-left",
196
            uppercase        : "fa-font",
197
            h1               : editormd.classPrefix + "bold",
198
            h2               : editormd.classPrefix + "bold",
199
            h3               : editormd.classPrefix + "bold",
200
            h4               : editormd.classPrefix + "bold",
201
            h5               : editormd.classPrefix + "bold",
202
            h6               : editormd.classPrefix + "bold",
203
            "list-ul"        : "fa-list-ul",
204
            "list-ol"        : "fa-list-ol",
205
            hr               : "fa-minus",
206
            link             : "fa-link",
207
            "reference-link" : "fa-anchor",
208
            image            : "fa-picture-o",
209
            code             : "fa-code",
210
            "preformatted-text" : "fa-file-code-o",
211
            "code-block"     : "fa-file-code-o",
212
            table            : "fa-table",
213
            datetime         : "fa-clock-o",
214
            emoji            : "fa-smile-o",
215
            "html-entities"  : "fa-copyright",
216
            pagebreak        : "fa-newspaper-o",
217
            "goto-line"      : "fa-terminal", // fa-crosshairs
218
            watch            : "fa-eye-slash",
219
            unwatch          : "fa-eye",
220
            preview          : "fa-desktop",
221
            search           : "fa-search",
222
            fullscreen       : "fa-arrows-alt",
223
            clear            : "fa-eraser",
224
            help             : "fa-question-circle",
225
            info             : "fa-info-circle"
226
        },        
227
        toolbarIconTexts     : {},
228
        
229
        lang : {
230
            name        : "zh-cn",
231
            description : "开源在线Markdown编辑器<br/>Open source online Markdown editor.",
232
            tocTitle    : "目录",
233
            toolbar     : {
234
                undo             : "撤销(Ctrl+Z)",
235
                redo             : "重做(Ctrl+Y)",
236
                bold             : "粗体",
237
                del              : "删除线",
238
                italic           : "斜体",
239
                quote            : "引用",
240
                ucwords          : "将每个单词首字母转成大写",
241
                uppercase        : "将所选转换成大写",
242
                lowercase        : "将所选转换成小写",
243
                h1               : "标题1",
244
                h2               : "标题2",
245
                h3               : "标题3",
246
                h4               : "标题4",
247
                h5               : "标题5",
248
                h6               : "标题6",
249
                "list-ul"        : "无序列表",
250
                "list-ol"        : "有序列表",
251
                hr               : "横线",
252
                link             : "链接",
253
                "reference-link" : "引用链接",
254
                image            : "添加图片",
255
                code             : "行内代码",
256
                "preformatted-text" : "预格式文本 / 代码块(缩进风格)",
257
                "code-block"     : "代码块(多语言风格)",
258
                table            : "添加表格",
259
                datetime         : "日期时间",
260
                emoji            : "Emoji表情",
261
                "html-entities"  : "HTML实体字符",
262
                pagebreak        : "插入分页符",
263
                "goto-line"      : "跳转到行",
264
                watch            : "关闭实时预览",
265
                unwatch          : "开启实时预览",
266
                preview          : "全窗口预览HTML(按 Shift + ESC还原)",
267
                fullscreen       : "全屏(按ESC还原)",
268
                clear            : "清空",
269
                search           : "搜索",
270
                help             : "使用帮助",
271
                info             : "关于" + editormd.title
272
            },
273
            buttons : {
274
                enter  : "确定",
275
                cancel : "取消",
276
                close  : "关闭"
277
            },
278
            dialog : {
279
                link : {
280
                    title    : "添加链接",
281
                    url      : "链接地址",
282
                    urlTitle : "链接标题",
283
                    urlEmpty : "错误:请填写链接地址。"
284
                },
285
                referenceLink : {
286
                    title    : "添加引用链接",
287
                    name     : "引用名称",
288
                    url      : "链接地址",
289
                    urlId    : "链接ID",
290
                    urlTitle : "链接标题",
291
                    nameEmpty: "错误:引用链接的名称不能为空。",
292
                    idEmpty  : "错误:请填写引用链接的ID。",
293
                    urlEmpty : "错误:请填写引用链接的URL地址。"
294
                },
295
                image : {
296
                    title    : "添加图片",
297
                    url      : "图片地址",
298
                    link     : "图片链接",
299
                    alt      : "图片描述",
300
                    uploadButton     : "本地上传",
301
                    imageURLEmpty    : "错误:图片地址不能为空。",
302
                    uploadFileEmpty  : "错误:上传的图片不能为空。",
303
                    formatNotAllowed : "错误:只允许上传图片文件,允许上传的图片文件格式有:"
304
                },
305
                preformattedText : {
306
                    title             : "添加预格式文本或代码块", 
307
                    emptyAlert        : "错误:请填写预格式文本或代码的内容。"
308
                },
309
                codeBlock : {
310
                    title             : "添加代码块",                    
311
                    selectLabel       : "代码语言:",
312
                    selectDefaultText : "请选择代码语言",
313
                    otherLanguage     : "其他语言",
314
                    unselectedLanguageAlert : "错误:请选择代码所属的语言类型。",
315
                    codeEmptyAlert    : "错误:请填写代码内容。"
316
                },
317
                htmlEntities : {
318
                    title : "HTML 实体字符"
319
                },
320
                help : {
321
                    title : "使用帮助"
322
                }
323
            }
324
        }
325
    };
326
    
327
    editormd.classNames  = {
328
        tex : editormd.classPrefix + "tex"
329
    };
330
331
    editormd.dialogZindex = 99999;
332
    
333
    editormd.$katex       = null;
334
    editormd.$marked      = null;
335
    editormd.$CodeMirror  = null;
336
    editormd.$prettyPrint = null;
337
    
338
    var timer, flowchartTimer;
339
340
    editormd.prototype    = editormd.fn = {
341
        state : {
342
            watching   : false,
343
            loaded     : false,
344
            preview    : false,
345
            fullscreen : false
346
        },
347
        
348
        /**
349
         * 构造函数/实例初始化
350
         * Constructor / instance initialization
351
         * 
352
         * @param   {String}   id            编辑器的ID
353
         * @param   {Object}   [options={}]  配置选项 Key/Value
354
         * @returns {editormd}               返回editormd的实例对象
355
         */
356
        
357
        init : function (id, options) {
358
            
359
            options              = options || {};
360
            
361
            if (typeof id === "object")
362
            {
363
                options = id;
364
            }
365
            
366
            var _this            = this;
0 ignored issues
show
Unused Code introduced by
The variable _this seems to be never used. Consider removing it.
Loading history...
367
            var classPrefix      = this.classPrefix  = editormd.classPrefix; 
368
            var settings         = this.settings     = $.extend(true, {}, editormd.defaults, options);
369
            
370
            id                   = (typeof id === "object") ? settings.id : id;
371
            
372
            var editor           = this.editor       = $("#" + id);
373
            
374
            this.id              = id;
375
            this.lang            = settings.lang;
376
            
377
            var classNames       = this.classNames   = {
378
                textarea : {
379
                    html     : classPrefix + "html-textarea",
380
                    markdown : classPrefix + "markdown-textarea"
381
                }
382
            };
383
            
384
            settings.pluginPath = (settings.pluginPath === "") ? settings.path + "../plugins/" : settings.pluginPath; 
385
            
386
            this.state.watching = (settings.watch) ? true : false;
387
            
388
            if ( !editor.hasClass("editormd") ) {
389
                editor.addClass("editormd");
390
            }
391
            
392
            editor.css({
393
                width  : (typeof settings.width  === "number") ? settings.width  + "px" : settings.width,
394
                height : (typeof settings.height === "number") ? settings.height + "px" : settings.height
395
            });
396
            
397
            if (settings.autoHeight)
398
            {
399
                editor.css("height", "auto");
400
            }
401
                        
402
            var markdownTextarea = this.markdownTextarea = editor.children("textarea");
403
            
404
            if (markdownTextarea.length < 1)
405
            {
406
                editor.append("<textarea></textarea>");
407
                markdownTextarea = this.markdownTextarea = editor.children("textarea");
408
            }
409
            
410
            markdownTextarea.addClass(classNames.textarea.markdown).attr("placeholder", settings.placeholder);
411
            
412
            if (typeof markdownTextarea.attr("name") === "undefined" || markdownTextarea.attr("name") === "")
413
            {
414
                markdownTextarea.attr("name", (settings.name !== "") ? settings.name : id + "-markdown-doc");
415
            }
416
            
417
            var appendElements = [
418
                (!settings.readOnly) ? "<a href=\"javascript:;\" class=\"fa fa-close " + classPrefix + "preview-close-btn\"></a>" : "",
419
                ( (settings.saveHTMLToTextarea) ? "<textarea class=\"" + classNames.textarea.html + "\" name=\"" + id + "-html-code\"></textarea>" : "" ),
420
                "<div class=\"" + classPrefix + "preview\"><div class=\"markdown-body " + classPrefix + "preview-container\"></div></div>",
421
                "<div class=\"" + classPrefix + "container-mask\" style=\"display:block;\"></div>",
422
                "<div class=\"" + classPrefix + "mask\"></div>"
423
            ].join("\n");
424
            
425
            editor.append(appendElements).addClass(classPrefix + "vertical");
426
            
427
            if (settings.theme !== "") 
428
            {
429
                editor.addClass(classPrefix + "theme-" + settings.theme);
430
            }
431
            
432
            this.mask          = editor.children("." + classPrefix + "mask");    
433
            this.containerMask = editor.children("." + classPrefix  + "container-mask");
434
            
435
            if (settings.markdown !== "")
436
            {
437
                markdownTextarea.val(settings.markdown);
438
            }
439
            
440
            if (settings.appendMarkdown !== "")
441
            {
442
                markdownTextarea.val(markdownTextarea.val() + settings.appendMarkdown);
443
            }
444
            
445
            this.htmlTextarea     = editor.children("." + classNames.textarea.html);            
446
            this.preview          = editor.children("." + classPrefix + "preview");
447
            this.previewContainer = this.preview.children("." + classPrefix + "preview-container");
448
            
449
            if (settings.previewTheme !== "") 
450
            {
451
                this.preview.addClass(classPrefix + "preview-theme-" + settings.previewTheme);
452
            }
453
            
454
            if (typeof define === "function" && define.amd)
0 ignored issues
show
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
455
            {
456
                if (typeof katex !== "undefined") 
0 ignored issues
show
Bug introduced by
The variable katex seems to be never declared. If this is a global, consider adding a /** global: katex */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
457
                {
458
                    editormd.$katex = katex;
459
                }
460
                
461
                if (settings.searchReplace && !settings.readOnly) 
462
                {
463
                    editormd.loadCSS(settings.path + "codemirror/addon/dialog/dialog");
464
                    editormd.loadCSS(settings.path + "codemirror/addon/search/matchesonscrollbar");
465
                }
466
            }
467
            
468
            if ((typeof define === "function" && define.amd) || !settings.autoLoadModules)
469
            {
470
                if (typeof CodeMirror !== "undefined") {
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
471
                    editormd.$CodeMirror = CodeMirror;
472
                }
473
                
474
                if (typeof marked     !== "undefined") {
0 ignored issues
show
Bug introduced by
The variable marked seems to be never declared. If this is a global, consider adding a /** global: marked */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
475
                    editormd.$marked     = marked;
476
                }
477
                
478
                this.setCodeMirror().setToolbar().loadedDisplay();
479
            } 
480
            else 
481
            {
482
                this.loadQueues();
483
            }
484
485
            return this;
486
        },
487
        
488
        /**
489
         * 所需组件加载队列
490
         * Required components loading queue
491
         * 
492
         * @returns {editormd}  返回editormd的实例对象
493
         */
494
        
495
        loadQueues : function() {
496
            var _this        = this;
497
            var settings     = this.settings;
498
            var loadPath     = settings.path;
499
                                
500
            var loadFlowChartOrSequenceDiagram = function() {
501
                
502
                if (editormd.isIE8) 
503
                {
504
                    _this.loadedDisplay();
505
                    
506
                    return ;
507
                }
508
509
                if (settings.flowChart || settings.sequenceDiagram) 
510
                {
511
                    editormd.loadScript(loadPath + "raphael.min", function() {
512
513
                        editormd.loadScript(loadPath + "underscore.min", function() {  
514
515
                            if (!settings.flowChart && settings.sequenceDiagram) 
516
                            {
517
                                editormd.loadScript(loadPath + "sequence-diagram.min", function() {
518
                                    _this.loadedDisplay();
519
                                });
520
                            }
521
                            else if (settings.flowChart && !settings.sequenceDiagram) 
522
                            {      
523
                                editormd.loadScript(loadPath + "flowchart.min", function() {  
524
                                    editormd.loadScript(loadPath + "jquery.flowchart.min", function() {
525
                                        _this.loadedDisplay();
526
                                    });
527
                                });
528
                            }
529
                            else if (settings.flowChart && settings.sequenceDiagram) 
530
                            {  
531
                                editormd.loadScript(loadPath + "flowchart.min", function() {  
532
                                    editormd.loadScript(loadPath + "jquery.flowchart.min", function() {
533
                                        editormd.loadScript(loadPath + "sequence-diagram.min", function() {
534
                                            _this.loadedDisplay();
535
                                        });
536
                                    });
537
                                });
538
                            }
539
                        });
540
541
                    });
542
                } 
543
                else
544
                {
545
                    _this.loadedDisplay();
546
                }
547
            }; 
548
549
            editormd.loadCSS(loadPath + "codemirror/codemirror.min");
550
            
551
            if (settings.searchReplace && !settings.readOnly)
552
            {
553
                editormd.loadCSS(loadPath + "codemirror/addon/dialog/dialog");
554
                editormd.loadCSS(loadPath + "codemirror/addon/search/matchesonscrollbar");
555
            }
556
            
557
            if (settings.codeFold)
558
            {
559
                editormd.loadCSS(loadPath + "codemirror/addon/fold/foldgutter");            
560
            }
561
            
562
            editormd.loadScript(loadPath + "codemirror/codemirror.min", function() {
563
                editormd.$CodeMirror = CodeMirror;
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
564
                
565
                editormd.loadScript(loadPath + "codemirror/modes.min", function() {
566
                    
567
                    editormd.loadScript(loadPath + "codemirror/addons.min", function() {
568
                        
569
                        _this.setCodeMirror();
570
                        
571
                        if (settings.mode !== "gfm" && settings.mode !== "markdown") 
572
                        {
573
                            _this.loadedDisplay();
574
                            
575
                            return false;
576
                        }
577
                        
578
                        _this.setToolbar();
579
580
                        editormd.loadScript(loadPath + "marked.min", function() {
581
582
                            editormd.$marked = marked;
0 ignored issues
show
Bug introduced by
The variable marked seems to be never declared. If this is a global, consider adding a /** global: marked */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
583
                                
584
                            if (settings.previewCodeHighlight) 
585
                            {
586
                                editormd.loadScript(loadPath + "prettify.min", function() {
587
                                    loadFlowChartOrSequenceDiagram();
588
                                });
589
                            } 
590
                            else
591
                            {                  
592
                                loadFlowChartOrSequenceDiagram();
593
                            }
594
                        });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
595
                        
596
                    });
597
                    
598
                });
599
                
600
            });
601
602
            return this;
603
        },
604
        
605
        /**
606
         * 设置 Editor.md 的整体主题,主要是工具栏
607
         * Setting Editor.md theme
608
         * 
609
         * @returns {editormd}  返回editormd的实例对象
610
         */
611
        
612
        setTheme : function(theme) {
613
            var editor      = this.editor;
614
            var oldTheme    = this.settings.theme;
615
            var themePrefix = this.classPrefix + "theme-";
616
            
617
            editor.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme);
618
            
619
            this.settings.theme = theme;
620
            
621
            return this;
622
        },
623
        
624
        /**
625
         * 设置 CodeMirror(编辑区)的主题
626
         * Setting CodeMirror (Editor area) theme
627
         * 
628
         * @returns {editormd}  返回editormd的实例对象
629
         */
630
        
631
        setEditorTheme : function(theme) {  
632
            var settings   = this.settings;  
633
            settings.editorTheme = theme;  
634
            
635
            if (theme !== "default")
636
            {
637
                editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme);
638
            }
639
            
640
            this.cm.setOption("theme", theme);
641
            
642
            return this;
643
        },
644
        
645
        /**
646
         * setEditorTheme() 的别名
647
         * setEditorTheme() alias
648
         * 
649
         * @returns {editormd}  返回editormd的实例对象
650
         */
651
        
652
        setCodeMirrorTheme : function (theme) {            
653
            this.setEditorTheme(theme);
654
            
655
            return this;
656
        },
657
        
658
        /**
659
         * 设置 Editor.md 的主题
660
         * Setting Editor.md theme
661
         * 
662
         * @returns {editormd}  返回editormd的实例对象
663
         */
664
        
665
        setPreviewTheme : function(theme) {  
666
            var preview     = this.preview;
667
            var oldTheme    = this.settings.previewTheme;
668
            var themePrefix = this.classPrefix + "preview-theme-";
669
            
670
            preview.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme);
671
            
672
            this.settings.previewTheme = theme;
673
            
674
            return this;
675
        },
676
        
677
        /**
678
         * 配置和初始化CodeMirror组件
679
         * CodeMirror initialization
680
         * 
681
         * @returns {editormd}  返回editormd的实例对象
682
         */
683
        
684
        setCodeMirror : function() { 
685
            var settings         = this.settings;
686
            var editor           = this.editor;
687
            
688
            if (settings.editorTheme !== "default")
689
            {
690
                editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme);
691
            }
692
            
693
            var codeMirrorConfig = {
694
                mode                      : settings.mode,
695
                theme                     : settings.editorTheme,
696
                tabSize                   : settings.tabSize,
697
                dragDrop                  : false,
698
                autofocus                 : settings.autoFocus,
699
                autoCloseTags             : settings.autoCloseTags,
700
                readOnly                  : (settings.readOnly) ? "nocursor" : false,
701
                indentUnit                : settings.indentUnit,
702
                lineNumbers               : settings.lineNumbers,
703
                lineWrapping              : settings.lineWrapping,
704
                extraKeys                 : {
705
                                                "Ctrl-Q": function(cm) { 
706
                                                    cm.foldCode(cm.getCursor()); 
707
                                                }
708
                                            },
709
                foldGutter                : settings.codeFold,
710
                gutters                   : ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
711
                matchBrackets             : settings.matchBrackets,
712
                indentWithTabs            : settings.indentWithTabs,
713
                styleActiveLine           : settings.styleActiveLine,
714
                styleSelectedText         : settings.styleSelectedText,
715
                autoCloseBrackets         : settings.autoCloseBrackets,
716
                showTrailingSpace         : settings.showTrailingSpace,
717
                highlightSelectionMatches : ( (!settings.matchWordHighlight) ? false : { showToken: (settings.matchWordHighlight === "onselected") ? false : /\w/ } )
718
            };
719
            
720
            this.codeEditor = this.cm        = editormd.$CodeMirror.fromTextArea(this.markdownTextarea[0], codeMirrorConfig);
721
            this.codeMirror = this.cmElement = editor.children(".CodeMirror");
722
            
723
            if (settings.value !== "")
724
            {
725
                this.cm.setValue(settings.value);
726
            }
727
728
            this.codeMirror.css({
729
                fontSize : settings.fontSize,
730
                width    : (!settings.watch) ? "100%" : "50%"
731
            });
732
            
733
            if (settings.autoHeight)
734
            {
735
                this.codeMirror.css("height", "auto");
736
                this.cm.setOption("viewportMargin", Infinity);
737
            }
738
            
739
            if (!settings.lineNumbers)
740
            {
741
                this.codeMirror.find(".CodeMirror-gutters").css("border-right", "none");
742
            }
743
744
            return this;
745
        },
746
        
747
        /**
748
         * 获取CodeMirror的配置选项
749
         * Get CodeMirror setting options
750
         * 
751
         * @returns {Mixed}                  return CodeMirror setting option value
752
         */
753
        
754
        getCodeMirrorOption : function(key) {            
755
            return this.cm.getOption(key);
756
        },
757
        
758
        /**
759
         * 配置和重配置CodeMirror的选项
760
         * CodeMirror setting options / resettings
761
         * 
762
         * @returns {editormd}  返回editormd的实例对象
763
         */
764
        
765
        setCodeMirrorOption : function(key, value) {
766
            
767
            this.cm.setOption(key, value);
768
            
769
            return this;
770
        },
771
        
772
        /**
773
         * 添加 CodeMirror 键盘快捷键
774
         * Add CodeMirror keyboard shortcuts key map
775
         * 
776
         * @returns {editormd}  返回editormd的实例对象
777
         */
778
        
779
        addKeyMap : function(map, bottom) {
780
            this.cm.addKeyMap(map, bottom);
781
            
782
            return this;
783
        },
784
        
785
        /**
786
         * 移除 CodeMirror 键盘快捷键
787
         * Remove CodeMirror keyboard shortcuts key map
788
         * 
789
         * @returns {editormd}  返回editormd的实例对象
790
         */
791
        
792
        removeKeyMap : function(map) {
793
            this.cm.removeKeyMap(map);
794
            
795
            return this;
796
        },
797
        
798
        /**
799
         * 跳转到指定的行
800
         * Goto CodeMirror line
801
         * 
802
         * @param   {String|Intiger}   line      line number or "first"|"last"
803
         * @returns {editormd}                   返回editormd的实例对象
804
         */
805
        
806
        gotoLine : function (line) {
807
            
808
            var settings = this.settings;
809
            
810
            if (!settings.gotoLine)
811
            {
812
                return this;
813
            }
814
            
815
            var cm       = this.cm;
816
            var editor   = this.editor;
0 ignored issues
show
Unused Code introduced by
The assignment to variable editor seems to be never used. Consider removing it.
Loading history...
817
            var count    = cm.lineCount();
818
            var preview  = this.preview;
819
            
820
            if (typeof line === "string")
821
            {
822
                if(line === "last")
823
                {
824
                    line = count;
825
                }
826
            
827
                if (line === "first")
828
                {
829
                    line = 1;
830
                }
831
            }
832
            
833
            if (typeof line !== "number") 
834
            {  
835
                alert("Error: The line number must be an integer.");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
836
                return this;
837
            }
838
            
839
            line  = parseInt(line) - 1;
840
            
841
            if (line > count)
842
            {
843
                alert("Error: The line number range 1-" + count);
844
                
845
                return this;
846
            }
847
            
848
            cm.setCursor( {line : line, ch : 0} );
849
            
850
            var scrollInfo   = cm.getScrollInfo();
851
            var clientHeight = scrollInfo.clientHeight; 
852
            var coords       = cm.charCoords({line : line, ch : 0}, "local");
853
            
854
            cm.scrollTo(null, (coords.top + coords.bottom - clientHeight) / 2);
855
            
856
            if (settings.watch)
857
            {            
858
                var cmScroll  = this.codeMirror.find(".CodeMirror-scroll")[0];
859
                var height    = $(cmScroll).height(); 
860
                var scrollTop = cmScroll.scrollTop;         
861
                var percent   = (scrollTop / cmScroll.scrollHeight);
862
863
                if (scrollTop === 0)
864
                {
865
                    preview.scrollTop(0);
866
                } 
867
                else if (scrollTop + height >= cmScroll.scrollHeight - 16)
868
                { 
869
                    preview.scrollTop(preview[0].scrollHeight);                    
870
                } 
871
                else
872
                {                    
873
                    preview.scrollTop(preview[0].scrollHeight * percent);
874
                }
875
            }
876
877
            cm.focus();
878
            
879
            return this;
880
        },
881
        
882
        /**
883
         * 扩展当前实例对象,可同时设置多个或者只设置一个
884
         * Extend editormd instance object, can mutil setting.
885
         * 
886
         * @returns {editormd}                  this(editormd instance object.)
887
         */
888
        
889
        extend : function() {
890
            if (typeof arguments[1] !== "undefined")
891
            {
892
                if (typeof arguments[1] === "function")
893
                {
894
                    arguments[1] = $.proxy(arguments[1], this);
895
                }
896
897
                this[arguments[0]] = arguments[1];
898
            }
899
            
900
            if (typeof arguments[0] === "object" && typeof arguments[0].length === "undefined")
901
            {
902
                $.extend(true, this, arguments[0]);
903
            }
904
905
            return this;
906
        },
907
        
908
        /**
909
         * 设置或扩展当前实例对象,单个设置
910
         * Extend editormd instance object, one by one
911
         * 
912
         * @param   {String|Object}   key       option key
913
         * @param   {String|Object}   value     option value
914
         * @returns {editormd}                  this(editormd instance object.)
915
         */
916
        
917
        set : function (key, value) {
918
            
919
            if (typeof value !== "undefined" && typeof value === "function")
920
            {
921
                value = $.proxy(value, this);
922
            }
923
            
924
            this[key] = value;
925
926
            return this;
927
        },
928
        
929
        /**
930
         * 重新配置
931
         * Resetting editor options
932
         * 
933
         * @param   {String|Object}   key       option key
934
         * @param   {String|Object}   value     option value
935
         * @returns {editormd}                  this(editormd instance object.)
936
         */
937
        
938
        config : function(key, value) {
939
            var settings = this.settings;
940
            
941
            if (typeof key === "object")
942
            {
943
                settings = $.extend(true, settings, key);
944
            }
945
            
946
            if (typeof key === "string")
947
            {
948
                settings[key] = value;
949
            }
950
            
951
            this.settings = settings;
952
            this.recreate();
953
            
954
            return this;
955
        },
956
        
957
        /**
958
         * 注册事件处理方法
959
         * Bind editor event handle
960
         * 
961
         * @param   {String}     eventType      event type
962
         * @param   {Function}   callback       回调函数
963
         * @returns {editormd}                  this(editormd instance object.)
964
         */
965
        
966
        on : function(eventType, callback) {
967
            var settings = this.settings;
968
            
969
            if (typeof settings["on" + eventType] !== "undefined") 
970
            {                
971
                settings["on" + eventType] = $.proxy(callback, this);      
972
            }
973
974
            return this;
975
        },
976
        
977
        /**
978
         * 解除事件处理方法
979
         * Unbind editor event handle
980
         * 
981
         * @param   {String}   eventType          event type
982
         * @returns {editormd}                    this(editormd instance object.)
983
         */
984
        
985
        off : function(eventType) {
986
            var settings = this.settings;
987
            
988
            if (typeof settings["on" + eventType] !== "undefined") 
989
            {
990
                settings["on" + eventType] = function(){};
991
            }
992
            
993
            return this;
994
        },
995
        
996
        /**
997
         * 显示工具栏
998
         * Display toolbar
999
         * 
1000
         * @param   {Function} [callback=function(){}] 回调函数
1001
         * @returns {editormd}  返回editormd的实例对象
1002
         */
1003
        
1004
        showToolbar : function(callback) {
1005
            var settings = this.settings;
1006
            
1007
            if(settings.readOnly) {
1008
                return this;
1009
            }
1010
            
1011
            if (settings.toolbar && (this.toolbar.length < 1 || this.toolbar.find("." + this.classPrefix + "menu").html() === "") )
1012
            {
1013
                this.setToolbar();
1014
            }
1015
            
1016
            settings.toolbar = true; 
1017
            
1018
            this.toolbar.show();
1019
            this.resize();
1020
            
1021
            $.proxy(callback || function(){}, this)();
1022
1023
            return this;
1024
        },
1025
        
1026
        /**
1027
         * 隐藏工具栏
1028
         * Hide toolbar
1029
         * 
1030
         * @param   {Function} [callback=function(){}] 回调函数
1031
         * @returns {editormd}                         this(editormd instance object.)
1032
         */
1033
        
1034
        hideToolbar : function(callback) { 
1035
            var settings = this.settings;
1036
            
1037
            settings.toolbar = false;  
1038
            this.toolbar.hide();
1039
            this.resize();
1040
            
1041
            $.proxy(callback || function(){}, this)();
1042
1043
            return this;
1044
        },
1045
        
1046
        /**
1047
         * 页面滚动时工具栏的固定定位
1048
         * Set toolbar in window scroll auto fixed position
1049
         * 
1050
         * @returns {editormd}  返回editormd的实例对象
1051
         */
1052
        
1053
        setToolbarAutoFixed : function(fixed) {
1054
            
1055
            var state    = this.state;
1056
            var editor   = this.editor;
1057
            var toolbar  = this.toolbar;
1058
            var settings = this.settings;
1059
            
1060
            if (typeof fixed !== "undefined")
1061
            {
1062
                settings.toolbarAutoFixed = fixed;
1063
            }
1064
            
1065
            var autoFixedHandle = function(){
1066
                var $window = $(window);
1067
                var top     = $window.scrollTop();
1068
                
1069
                if (!settings.toolbarAutoFixed)
1070
                {
1071
                    return false;
1072
                }
1073
1074
                if (top - editor.offset().top > 10 && top < editor.height())
1075
                {
1076
                    toolbar.css({
1077
                        position : "fixed",
1078
                        width    : editor.width() + "px",
1079
                        left     : ($window.width() - editor.width()) / 2 + "px"
1080
                    });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1081
                }
1082
                else
1083
                {
1084
                    toolbar.css({
1085
                        position : "absolute",
1086
                        width    : "100%",
1087
                        left     : 0
1088
                    });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1089
                }
1090
            };
1091
            
1092
            if (!state.fullscreen && !state.preview && settings.toolbar && settings.toolbarAutoFixed)
1093
            {
1094
                $(window).bind("scroll", autoFixedHandle);
1095
            }
1096
1097
            return this;
1098
        },
1099
        
1100
        /**
1101
         * 配置和初始化工具栏
1102
         * Set toolbar and Initialization
1103
         * 
1104
         * @returns {editormd}  返回editormd的实例对象
1105
         */
1106
        
1107
        setToolbar : function() {
1108
            var settings    = this.settings;  
1109
            
1110
            if(settings.readOnly) {
1111
                return this;
1112
            }
1113
            
1114
            var editor      = this.editor;
1115
            var preview     = this.preview;
0 ignored issues
show
Unused Code introduced by
The assignment to variable preview seems to be never used. Consider removing it.
Loading history...
1116
            var classPrefix = this.classPrefix;
1117
            
1118
            var toolbar     = this.toolbar = editor.children("." + classPrefix + "toolbar");
1119
            
1120
            if (settings.toolbar && toolbar.length < 1)
1121
            {            
1122
                var toolbarHTML = "<div class=\"" + classPrefix + "toolbar\"><div class=\"" + classPrefix + "toolbar-container\"><ul class=\"" + classPrefix + "menu\"></ul></div></div>";
1123
                
1124
                editor.append(toolbarHTML);
1125
                toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar");
1126
            }
1127
            
1128
            if (!settings.toolbar) 
1129
            {
1130
                toolbar.hide();
1131
                
1132
                return this;
1133
            }
1134
            
1135
            toolbar.show();
1136
            
1137
            var icons       = (typeof settings.toolbarIcons === "function") ? settings.toolbarIcons() 
1138
                            : ((typeof settings.toolbarIcons === "string")  ? editormd.toolbarModes[settings.toolbarIcons] : settings.toolbarIcons);
1139
            
1140
            var toolbarMenu = toolbar.find("." + this.classPrefix + "menu"), menu = "";
1141
            var pullRight   = false;
1142
            
1143
            for (var i = 0, len = icons.length; i < len; i++)
1144
            {
1145
                var name = icons[i];
1146
1147
                if (name === "||") 
1148
                { 
1149
                    pullRight = true;
1150
                } 
1151
                else if (name === "|")
1152
                {
1153
                    menu += "<li class=\"divider\" unselectable=\"on\">|</li>";
1154
                }
1155
                else
1156
                {
1157
                    var isHeader = (/h(\d)/.test(name));
1158
                    var index    = name;
1159
                    
1160
                    if (name === "watch" && !settings.watch) {
1161
                        index = "unwatch";
1162
                    }
1163
                    
1164
                    var title     = settings.lang.toolbar[index];
1165
                    var iconTexts = settings.toolbarIconTexts[index];
1166
                    var iconClass = settings.toolbarIconsClass[index];
1167
                    
1168
                    title     = (typeof title     === "undefined") ? "" : title;
1169
                    iconTexts = (typeof iconTexts === "undefined") ? "" : iconTexts;
1170
                    iconClass = (typeof iconClass === "undefined") ? "" : iconClass;
1171
1172
                    var menuItem = pullRight ? "<li class=\"pull-right\">" : "<li>";
1173
                    
1174
                    if (typeof settings.toolbarCustomIcons[name] !== "undefined" && typeof settings.toolbarCustomIcons[name] !== "function")
1175
                    {
1176
                        menuItem += settings.toolbarCustomIcons[name];
1177
                    }
1178
                    else 
1179
                    {
1180
                        menuItem += "<a href=\"javascript:;\" title=\"" + title + "\" unselectable=\"on\">";
1181
                        menuItem += "<i class=\"fa " + iconClass + "\" name=\""+name+"\" unselectable=\"on\">"+((isHeader) ? name.toUpperCase() : ( (iconClass === "") ? iconTexts : "") ) + "</i>";
1182
                        menuItem += "</a>";
1183
                    }
1184
1185
                    menuItem += "</li>";
1186
1187
                    menu = pullRight ? menuItem + menu : menu + menuItem;
1188
                }
1189
            }
1190
1191
            toolbarMenu.html(menu);
1192
            
1193
            toolbarMenu.find("[title=\"Lowercase\"]").attr("title", settings.lang.toolbar.lowercase);
1194
            toolbarMenu.find("[title=\"ucwords\"]").attr("title", settings.lang.toolbar.ucwords);
1195
            
1196
            this.setToolbarHandler();
1197
            this.setToolbarAutoFixed();
1198
1199
            return this;
1200
        },
1201
        
1202
        /**
1203
         * 工具栏图标事件处理对象序列
1204
         * Get toolbar icons event handlers
1205
         * 
1206
         * @param   {Object}   cm    CodeMirror的实例对象
1207
         * @param   {String}   name  要获取的事件处理器名称
1208
         * @returns {Object}         返回处理对象序列
1209
         */
1210
            
1211
        dialogLockScreen : function() {
1212
            $.proxy(editormd.dialogLockScreen, this)();
1213
            
1214
            return this;
1215
        },
1216
1217
        dialogShowMask : function(dialog) {
1218
            $.proxy(editormd.dialogShowMask, this)(dialog);
1219
            
1220
            return this;
1221
        },
1222
        
1223
        getToolbarHandles : function(name) {  
1224
            var toolbarHandlers = this.toolbarHandlers = editormd.toolbarHandlers;
1225
            
1226
            return (name && typeof toolbarIconHandlers[name] !== "undefined") ? toolbarHandlers[name] : toolbarHandlers;
0 ignored issues
show
Bug introduced by
The variable toolbarIconHandlers seems to be never declared. If this is a global, consider adding a /** global: toolbarIconHandlers */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1227
        },
1228
        
1229
        /**
1230
         * 工具栏图标事件处理器
1231
         * Bind toolbar icons event handle
1232
         * 
1233
         * @returns {editormd}  返回editormd的实例对象
1234
         */
1235
        
1236
        setToolbarHandler : function() {
1237
            var _this               = this;
1238
            var settings            = this.settings;
1239
            
1240
            if (!settings.toolbar || settings.readOnly) {
1241
                return this;
1242
            }
1243
            
1244
            var toolbar             = this.toolbar;
1245
            var cm                  = this.cm;
1246
            var classPrefix         = this.classPrefix;           
1247
            var toolbarIcons        = this.toolbarIcons = toolbar.find("." + classPrefix + "menu > li > a");  
1248
            var toolbarIconHandlers = this.getToolbarHandles();  
1249
                
1250
            toolbarIcons.bind(editormd.mouseOrTouch("click", "touchend"), function(event) {
0 ignored issues
show
Unused Code introduced by
The parameter event is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1251
1252
                var icon                = $(this).children(".fa");
1253
                var name                = icon.attr("name");
1254
                var cursor              = cm.getCursor();
1255
                var selection           = cm.getSelection();
1256
1257
                if (name === "") {
1258
                    return ;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
1259
                }
1260
                
1261
                _this.activeIcon = icon;
1262
1263
                if (typeof toolbarIconHandlers[name] !== "undefined") 
1264
                {
1265
                    $.proxy(toolbarIconHandlers[name], _this)(cm);
1266
                }
1267
                else 
1268
                {
1269
                    if (typeof settings.toolbarHandlers[name] !== "undefined") 
1270
                    {
1271
                        $.proxy(settings.toolbarHandlers[name], _this)(cm, icon, cursor, selection);
1272
                    }
1273
                }
1274
                
1275
                if (name !== "link" && name !== "reference-link" && name !== "image" && name !== "code-block" && 
1276
                    name !== "preformatted-text" && name !== "watch" && name !== "preview" && name !== "search" && name !== "fullscreen" && name !== "info") 
1277
                {
1278
                    cm.focus();
1279
                }
1280
1281
                return false;
1282
1283
            });
1284
1285
            return this;
1286
        },
1287
        
1288
        /**
1289
         * 动态创建对话框
1290
         * Creating custom dialogs
1291
         * 
1292
         * @param   {Object} options  配置项键值对 Key/Value
1293
         * @returns {dialog}          返回创建的dialog的jQuery实例对象
1294
         */
1295
        
1296
        createDialog : function(options) {            
1297
            return $.proxy(editormd.createDialog, this)(options);
1298
        },
1299
        
1300
        /**
1301
         * 创建关于Editor.md的对话框
1302
         * Create about Editor.md dialog
1303
         * 
1304
         * @returns {editormd}  返回editormd的实例对象
1305
         */
1306
        
1307
        createInfoDialog : function() {
1308
            var _this        = this;
1309
			var editor       = this.editor;
1310
            var classPrefix  = this.classPrefix;  
1311
            
1312
            var infoDialogHTML = [
1313
                "<div class=\"" + classPrefix + "dialog " + classPrefix + "dialog-info\" style=\"\">",
1314
                "<div class=\"" + classPrefix + "dialog-container\">",
1315
                "<h1><i class=\"editormd-logo editormd-logo-lg editormd-logo-color\"></i> " + editormd.title + "<small>v" + editormd.version + "</small></h1>",
1316
                "<p>" + this.lang.description + "</p>",
1317
                "<p style=\"margin: 10px 0 20px 0;\"><a href=\"" + editormd.homePage + "\" target=\"_blank\">" + editormd.homePage + " <i class=\"fa fa-external-link\"></i></a></p>",
1318
                "<p style=\"font-size: 0.85em;\">Copyright &copy; 2015 <a href=\"https://github.com/pandao\" target=\"_blank\" class=\"hover-link\">Pandao</a>, The <a href=\"https://github.com/pandao/editor.md/blob/master/LICENSE\" target=\"_blank\" class=\"hover-link\">MIT</a> License.</p>",
1319
                "</div>",
1320
                "<a href=\"javascript:;\" class=\"fa fa-close " + classPrefix + "dialog-close\"></a>",
1321
                "</div>"
1322
            ].join("\n");
1323
1324
            editor.append(infoDialogHTML);
1325
            
1326
            var infoDialog  = this.infoDialog = editor.children("." + classPrefix + "dialog-info");
1327
1328
            infoDialog.find("." + classPrefix + "dialog-close").bind(editormd.mouseOrTouch("click", "touchend"), function() {
1329
                _this.hideInfoDialog();
1330
            });
1331
            
1332
            infoDialog.css("border", (editormd.isIE8) ? "1px solid #ddd" : "").css("z-index", editormd.dialogZindex).show();
1333
            
1334
            this.infoDialogPosition();
1335
1336
            return this;
1337
        },
1338
        
1339
        /**
1340
         * 关于Editor.md对话居中定位
1341
         * Editor.md dialog position handle
1342
         * 
1343
         * @returns {editormd}  返回editormd的实例对象
1344
         */
1345
        
1346
        infoDialogPosition : function() {
1347
            var infoDialog = this.infoDialog;
1348
            
1349
			var _infoDialogPosition = function() {
1350
				infoDialog.css({
1351
					top  : ($(window).height() - infoDialog.height()) / 2 + "px",
1352
					left : ($(window).width()  - infoDialog.width()) / 2  + "px"
1353
				});
1354
			};
1355
1356
			_infoDialogPosition();
1357
1358
			$(window).resize(_infoDialogPosition);
1359
            
1360
            return this;
1361
        },
1362
        
1363
        /**
1364
         * 显示关于Editor.md
1365
         * Display about Editor.md dialog
1366
         * 
1367
         * @returns {editormd}  返回editormd的实例对象
1368
         */
1369
        
1370
        showInfoDialog : function() {
1371
1372
            $("html,body").css("overflow-x", "hidden");
1373
            
1374
            var _this       = this;
0 ignored issues
show
Unused Code introduced by
The variable _this seems to be never used. Consider removing it.
Loading history...
1375
			var editor      = this.editor;
1376
            var settings    = this.settings;         
1377
			var infoDialog  = this.infoDialog = editor.children("." + this.classPrefix + "dialog-info");
1378
            
1379
            if (infoDialog.length < 1)
1380
            {
1381
                this.createInfoDialog();
1382
            }
1383
            
1384
            this.lockScreen(true);
1385
            
1386
            this.mask.css({
1387
						opacity         : settings.dialogMaskOpacity,
1388
						backgroundColor : settings.dialogMaskBgColor
1389
					}).show();
1390
1391
			infoDialog.css("z-index", editormd.dialogZindex).show();
1392
1393
			this.infoDialogPosition();
1394
1395
            return this;
1396
        },
1397
        
1398
        /**
1399
         * 隐藏关于Editor.md
1400
         * Hide about Editor.md dialog
1401
         * 
1402
         * @returns {editormd}  返回editormd的实例对象
1403
         */
1404
        
1405
        hideInfoDialog : function() {            
1406
            $("html,body").css("overflow-x", "");
1407
            this.infoDialog.hide();
1408
            this.mask.hide();
1409
            this.lockScreen(false);
1410
1411
            return this;
1412
        },
1413
        
1414
        /**
1415
         * 锁屏
1416
         * lock screen
1417
         * 
1418
         * @param   {Boolean}    lock    Boolean 布尔值,是否锁屏
1419
         * @returns {editormd}           返回editormd的实例对象
1420
         */
1421
        
1422
        lockScreen : function(lock) {
1423
            editormd.lockScreen(lock);
1424
            this.resize();
1425
1426
            return this;
1427
        },
1428
        
1429
        /**
1430
         * 编辑器界面重建,用于动态语言包或模块加载等
1431
         * Recreate editor
1432
         * 
1433
         * @returns {editormd}  返回editormd的实例对象
1434
         */
1435
        
1436
        recreate : function() {
1437
            var _this            = this;
0 ignored issues
show
Unused Code introduced by
The variable _this seems to be never used. Consider removing it.
Loading history...
1438
            var editor           = this.editor;
1439
            var settings         = this.settings;
1440
            
1441
            this.codeMirror.remove();
1442
            
1443
            this.setCodeMirror();
1444
1445
            if (!settings.readOnly) 
1446
            {
1447
                if (editor.find(".editormd-dialog").length > 0) {
1448
                    editor.find(".editormd-dialog").remove();
1449
                }
1450
                
1451
                if (settings.toolbar) 
1452
                {  
1453
                    this.getToolbarHandles();                  
1454
                    this.setToolbar();
1455
                }
1456
            }
1457
            
1458
            this.loadedDisplay(true);
1459
1460
            return this;
1461
        },
1462
        
1463
        /**
1464
         * 高亮预览HTML的pre代码部分
1465
         * highlight of preview codes
1466
         * 
1467
         * @returns {editormd}             返回editormd的实例对象
1468
         */
1469
        
1470
        previewCodeHighlight : function() {    
1471
            var settings         = this.settings;
1472
            var previewContainer = this.previewContainer;
1473
            
1474
            if (settings.previewCodeHighlight) 
1475
            {
1476
                previewContainer.find("pre").addClass("prettyprint linenums");
1477
                
1478
                if (typeof prettyPrint !== "undefined")
0 ignored issues
show
Bug introduced by
The variable prettyPrint seems to be never declared. If this is a global, consider adding a /** global: prettyPrint */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1479
                {                    
1480
                    prettyPrint();
1481
                }
1482
            }
1483
1484
            return this;
1485
        },
1486
        
1487
        /**
1488
         * 解析TeX(KaTeX)科学公式
1489
         * TeX(KaTeX) Renderer
1490
         * 
1491
         * @returns {editormd}             返回editormd的实例对象
1492
         */
1493
        
1494
        katexRender : function() {
1495
            
1496
            if (timer === null)
1497
            {
1498
                return this;
1499
            }
1500
            
1501
            this.previewContainer.find("." + editormd.classNames.tex).each(function(){
1502
                var tex  = $(this);
1503
                editormd.$katex.render(tex.text(), tex[0]);
1504
                
1505
                tex.find(".katex").css("font-size", "1.6em");
1506
            });   
1507
1508
            return this;
1509
        },
1510
        
1511
        /**
1512
         * 解析和渲染流程图及时序图
1513
         * FlowChart and SequenceDiagram Renderer
1514
         * 
1515
         * @returns {editormd}             返回editormd的实例对象
1516
         */
1517
        
1518
        flowChartAndSequenceDiagramRender : function() {
1519
            var $this            = this;
1520
            var settings         = this.settings;
1521
            var previewContainer = this.previewContainer;
1522
            
1523
            if (editormd.isIE8) {
1524
                return this;
1525
            }
1526
1527
            if (settings.flowChart) {
1528
                if (flowchartTimer === null) {
1529
                    return this;
1530
                }
1531
                
1532
                previewContainer.find(".flowchart").flowChart(); 
1533
            }
1534
1535
            if (settings.sequenceDiagram) {
1536
                previewContainer.find(".sequence-diagram").sequenceDiagram({theme: "simple"});
1537
            }
1538
                    
1539
            var preview    = $this.preview;
1540
            var codeMirror = $this.codeMirror;
1541
            var codeView   = codeMirror.find(".CodeMirror-scroll");
1542
1543
            var height    = codeView.height();
1544
            var scrollTop = codeView.scrollTop();                    
1545
            var percent   = (scrollTop / codeView[0].scrollHeight);
1546
            var tocHeight = 0;
1547
1548
            preview.find(".markdown-toc-list").each(function(){
1549
                tocHeight += $(this).height();
1550
            });
1551
1552
            var tocMenuHeight = preview.find(".editormd-toc-menu").height(); 
1553
            tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight;
1554
1555
            if (scrollTop === 0) 
1556
            {
1557
                preview.scrollTop(0);
1558
            } 
1559
            else if (scrollTop + height >= codeView[0].scrollHeight - 16)
1560
            { 
1561
                preview.scrollTop(preview[0].scrollHeight);                        
1562
            } 
1563
            else
1564
            {                  
1565
                preview.scrollTop((preview[0].scrollHeight + tocHeight + tocMenuHeight) * percent);
1566
            }
1567
1568
            return this;
1569
        },
1570
        
1571
        /**
1572
         * 注册键盘快捷键处理
1573
         * Register CodeMirror keyMaps (keyboard shortcuts).
1574
         * 
1575
         * @param   {Object}    keyMap      KeyMap key/value {"(Ctrl/Shift/Alt)-Key" : function(){}}
1576
         * @returns {editormd}              return this
1577
         */
1578
        
1579
        registerKeyMaps : function(keyMap) {
1580
            
1581
            var _this           = this;
1582
            var cm              = this.cm;
1583
            var settings        = this.settings;
1584
            var toolbarHandlers = editormd.toolbarHandlers;
1585
            var disabledKeyMaps = settings.disabledKeyMaps;
1586
            
1587
            keyMap              = keyMap || null;
1588
            
1589
            if (keyMap)
1590
            {
1591
                for (var i in keyMap)
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
1592
                {
1593
                    if ($.inArray(i, disabledKeyMaps) < 0)
1594
                    {
1595
                        var map = {};
1596
                        map[i]  = keyMap[i];
1597
1598
                        cm.addKeyMap(keyMap);
1599
                    }
1600
                }
1601
            }
1602
            else
1603
            {
1604
                for (var k in editormd.keyMaps)
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
1605
                {
1606
                    var _keyMap = editormd.keyMaps[k];
1607
                    var handle = (typeof _keyMap === "string") ? $.proxy(toolbarHandlers[_keyMap], _this) : $.proxy(_keyMap, _this);
1608
                    
1609
                    if ($.inArray(k, ["F9", "F10", "F11"]) < 0 && $.inArray(k, disabledKeyMaps) < 0)
1610
                    {
1611
                        var _map = {};
1612
                        _map[k] = handle;
1613
1614
                        cm.addKeyMap(_map);
1615
                    }
1616
                }
1617
                
1618
                $(window).keydown(function(event) {
1619
                    
1620
                    var keymaps = {
1621
                        "120" : "F9",
1622
                        "121" : "F10",
1623
                        "122" : "F11"
1624
                    };
1625
                    
1626
                    if ( $.inArray(keymaps[event.keyCode], disabledKeyMaps) < 0 )
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if $.inArray(keymaps.event....e, disabledKeyMaps) < 0 is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
1627
                    {
1628
                        switch (event.keyCode)
1629
                        {
1630
                            case 120:
1631
                                    $.proxy(toolbarHandlers["watch"], _this)();
1632
                                    return false;
1633
                                break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
1634
                                
1635
                            case 121:
1636
                                    $.proxy(toolbarHandlers["preview"], _this)();
1637
                                    return false;
1638
                                break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
1639
                                
1640
                            case 122:
1641
                                    $.proxy(toolbarHandlers["fullscreen"], _this)();                        
1642
                                    return false;
1643
                                break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
1644
                                
1645
                            default:
1646
                                break;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1647
                        }
1648
                    }
1649
                });
1650
            }
1651
1652
            return this;
1653
        },
1654
        
1655
        /**
1656
         * 绑定同步滚动
1657
         * 
1658
         * @returns {editormd} return this
1659
         */
1660
        
1661
        bindScrollEvent : function() {
1662
            
1663
            var _this            = this;
1664
            var preview          = this.preview;
1665
            var settings         = this.settings;
1666
            var codeMirror       = this.codeMirror;
1667
            var mouseOrTouch     = editormd.mouseOrTouch;
1668
            
1669
            if (!settings.syncScrolling) {
1670
                return this;
1671
            }
1672
                
1673
            var cmBindScroll = function() {    
1674
                codeMirror.find(".CodeMirror-scroll").bind(mouseOrTouch("scroll", "touchmove"), function(event) {
1675
                    var height    = $(this).height();
1676
                    var scrollTop = $(this).scrollTop();                    
1677
                    var percent   = (scrollTop / $(this)[0].scrollHeight);
1678
                    
1679
                    var tocHeight = 0;
1680
                    
1681
                    preview.find(".markdown-toc-list").each(function(){
1682
                        tocHeight += $(this).height();
1683
                    });
1684
                    
1685
                    var tocMenuHeight = preview.find(".editormd-toc-menu").height();
1686
                    tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight;
1687
1688
                    if (scrollTop === 0) 
1689
                    {
1690
                        preview.scrollTop(0);
1691
                    } 
1692
                    else if (scrollTop + height >= $(this)[0].scrollHeight - 16)
1693
                    { 
1694
                        preview.scrollTop(preview[0].scrollHeight);                        
1695
                    } 
1696
                    else
1697
                    {
1698
                        preview.scrollTop((preview[0].scrollHeight  + tocHeight + tocMenuHeight) * percent);
1699
                    }
1700
                    
1701
                    $.proxy(settings.onscroll, _this)(event);
1702
                });
1703
            };
1704
1705
            var cmUnbindScroll = function() {
1706
                codeMirror.find(".CodeMirror-scroll").unbind(mouseOrTouch("scroll", "touchmove"));
1707
            };
1708
1709
            var previewBindScroll = function() {
1710
                
1711
                preview.bind(mouseOrTouch("scroll", "touchmove"), function(event) {
1712
                    var height    = $(this).height();
1713
                    var scrollTop = $(this).scrollTop();         
1714
                    var percent   = (scrollTop / $(this)[0].scrollHeight);
1715
                    var codeView  = codeMirror.find(".CodeMirror-scroll");
1716
1717
                    if(scrollTop === 0) 
1718
                    {
1719
                        codeView.scrollTop(0);
1720
                    }
1721
                    else if (scrollTop + height >= $(this)[0].scrollHeight)
1722
                    {
1723
                        codeView.scrollTop(codeView[0].scrollHeight);                        
1724
                    }
1725
                    else 
1726
                    {
1727
                        codeView.scrollTop(codeView[0].scrollHeight * percent);
1728
                    }
1729
                    
1730
                    $.proxy(settings.onpreviewscroll, _this)(event);
1731
                });
1732
1733
            };
1734
1735
            var previewUnbindScroll = function() {
1736
                preview.unbind(mouseOrTouch("scroll", "touchmove"));
1737
            }; 
1738
1739
			codeMirror.bind({
1740
				mouseover  : cmBindScroll,
1741
				mouseout   : cmUnbindScroll,
1742
				touchstart : cmBindScroll,
1743
				touchend   : cmUnbindScroll
1744
			});
1745
            
1746
            if (settings.syncScrolling === "single") {
1747
                return this;
1748
            }
1749
            
1750
			preview.bind({
1751
				mouseover  : previewBindScroll,
1752
				mouseout   : previewUnbindScroll,
1753
				touchstart : previewBindScroll,
1754
				touchend   : previewUnbindScroll
1755
			});
1756
1757
            return this;
1758
        },
1759
        
1760
        bindChangeEvent : function() {
1761
            
1762
            var _this            = this;
1763
            var cm               = this.cm;
1764
            var settings         = this.settings;
1765
            
1766
            if (!settings.syncScrolling) {
1767
                return this;
1768
            }
1769
            
1770
            cm.on("change", function(_cm, changeObj) {
0 ignored issues
show
Unused Code introduced by
The parameter changeObj is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter _cm is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1771
                
1772
                if (settings.watch)
1773
                {
1774
                    _this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px");
1775
                }
1776
                
1777
                timer = setTimeout(function() {
1778
                    clearTimeout(timer);
1779
                    _this.save();
1780
                    timer = null;
1781
                }, settings.delay);
1782
            });
1783
1784
            return this;
1785
        },
1786
        
1787
        /**
1788
         * 加载队列完成之后的显示处理
1789
         * Display handle of the module queues loaded after.
1790
         * 
1791
         * @param   {Boolean}   recreate   是否为重建编辑器
1792
         * @returns {editormd}             返回editormd的实例对象
1793
         */
1794
        
1795
        loadedDisplay : function(recreate) {
1796
            
1797
            recreate             = recreate || false;
1798
            
1799
            var _this            = this;
1800
            var editor           = this.editor;
1801
            var preview          = this.preview;
1802
            var settings         = this.settings;
1803
            
1804
            this.containerMask.hide();
1805
            
1806
            this.save();
1807
            
1808
            if (settings.watch) {
1809
                preview.show();
1810
            }
1811
            
1812
            editor.data("oldWidth", editor.width()).data("oldHeight", editor.height()); // 为了兼容Zepto
1813
            
1814
            this.resize();
1815
            this.registerKeyMaps();
1816
            
1817
            $(window).resize(function(){
1818
                _this.resize();
1819
            });
1820
            
1821
            this.bindScrollEvent().bindChangeEvent();
1822
            
1823
            if (!recreate)
1824
            {
1825
                $.proxy(settings.onload, this)();
1826
            }
1827
            
1828
            this.state.loaded = true;
1829
1830
            return this;
1831
        },
1832
        
1833
        /**
1834
         * 设置编辑器的宽度
1835
         * Set editor width
1836
         * 
1837
         * @param   {Number|String} width  编辑器宽度值
1838
         * @returns {editormd}             返回editormd的实例对象
1839
         */
1840
        
1841
        width : function(width) {
1842
                
1843
            this.editor.css("width", (typeof width === "number") ? width  + "px" : width);            
1844
            this.resize();
1845
            
1846
            return this;
1847
        },
1848
        
1849
        /**
1850
         * 设置编辑器的高度
1851
         * Set editor height
1852
         * 
1853
         * @param   {Number|String} height  编辑器高度值
1854
         * @returns {editormd}              返回editormd的实例对象
1855
         */
1856
        
1857
        height : function(height) {
1858
                
1859
            this.editor.css("height", (typeof height === "number")  ? height  + "px" : height);            
1860
            this.resize();
1861
            
1862
            return this;
1863
        },
1864
        
1865
        /**
1866
         * 调整编辑器的尺寸和布局
1867
         * Resize editor layout
1868
         * 
1869
         * @param   {Number|String} [width=null]  编辑器宽度值
1870
         * @param   {Number|String} [height=null] 编辑器高度值
1871
         * @returns {editormd}                    返回editormd的实例对象
1872
         */
1873
        
1874
        resize : function(width, height) {
1875
            
1876
            width  = width  || null;
1877
            height = height || null;
1878
            
1879
            var state      = this.state;
1880
            var editor     = this.editor;
1881
            var preview    = this.preview;
1882
            var toolbar    = this.toolbar;
1883
            var settings   = this.settings;
1884
            var codeMirror = this.codeMirror;
1885
            
1886
            if (width)
1887
            {
1888
                editor.css("width", (typeof width  === "number") ? width  + "px" : width);
1889
            }
1890
            
1891
            if (settings.autoHeight && !state.fullscreen && !state.preview)
1892
            {
1893
                editor.css("height", "auto");
1894
                codeMirror.css("height", "auto");
1895
            } 
1896
            else 
1897
            {
1898
                if (height) 
1899
                {
1900
                    editor.css("height", (typeof height === "number") ? height + "px" : height);
1901
                }
1902
                
1903
                if (state.fullscreen)
1904
                {
1905
                    editor.height($(window).height());
1906
                }
1907
1908
                if (settings.toolbar && !settings.readOnly) 
1909
                {
1910
                    codeMirror.css("margin-top", toolbar.height() + 1).height(editor.height() - toolbar.height());
1911
                } 
1912
                else
1913
                {
1914
                    codeMirror.css("margin-top", 0).height(editor.height());
1915
                }
1916
            }
1917
            
1918
            if(settings.watch) 
1919
            {
1920
                codeMirror.width(editor.width() / 2);
1921
                preview.width((!state.preview) ? editor.width() / 2 : editor.width());
1922
                
1923
                this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px");
1924
                
1925
                if (settings.toolbar && !settings.readOnly) 
1926
                {
1927
                    preview.css("top", toolbar.height() + 1);
1928
                } 
1929
                else 
1930
                {
1931
                    preview.css("top", 0);
1932
                }
1933
                
1934
                if (settings.autoHeight && !state.fullscreen && !state.preview)
1935
                {
1936
                    preview.height("");
1937
                }
1938
                else
1939
                {                
1940
                    var previewHeight = (settings.toolbar && !settings.readOnly) ? editor.height() - toolbar.height() : editor.height();
1941
                    
1942
                    preview.height(previewHeight);
1943
                }
1944
            } 
1945
            else 
1946
            {
1947
                codeMirror.width(editor.width());
1948
                preview.hide();
1949
            }
1950
            
1951
            if (state.loaded) 
1952
            {
1953
                $.proxy(settings.onresize, this)();
1954
            }
1955
1956
            return this;
1957
        },
1958
        
1959
        /**
1960
         * 解析和保存Markdown代码
1961
         * Parse & Saving Markdown source code
1962
         * 
1963
         * @returns {editormd}     返回editormd的实例对象
1964
         */
1965
        
1966
        save : function() {
1967
            
1968
            var _this            = this;
1969
            var state            = this.state;
1970
            var settings         = this.settings;
1971
1972
            if (timer === null && !(!settings.watch && state.preview))
1973
            {
1974
                return this;
1975
            }
1976
            
1977
            var cm               = this.cm;            
1978
            var cmValue          = cm.getValue();
1979
            var previewContainer = this.previewContainer;
1980
1981
            if (settings.mode !== "gfm" && settings.mode !== "markdown") 
1982
            {
1983
                this.markdownTextarea.val(cmValue);
1984
                
1985
                return this;
1986
            }
1987
            
1988
            var marked          = editormd.$marked;
1989
            var markdownToC     = this.markdownToC = [];            
1990
            var rendererOptions = this.markedRendererOptions = {  
1991
                toc                  : settings.toc,
1992
                tocm                 : settings.tocm,
1993
                tocStartLevel        : settings.tocStartLevel,
1994
                pageBreak            : settings.pageBreak,
1995
                taskList             : settings.taskList,
1996
                emoji                : settings.emoji,
1997
                tex                  : settings.tex,
1998
                atLink               : settings.atLink,           // for @link
1999
                emailLink            : settings.emailLink,        // for mail address auto link
2000
                flowChart            : settings.flowChart,
2001
                sequenceDiagram      : settings.sequenceDiagram,
2002
                previewCodeHighlight : settings.previewCodeHighlight,
2003
            };
2004
            
2005
            var markedOptions = this.markedOptions = {
2006
                renderer    : editormd.markedRenderer(markdownToC, rendererOptions),
2007
                gfm         : true,
2008
                tables      : true,
2009
                breaks      : true,
2010
                pedantic    : false,
2011
                sanitize    : (settings.htmlDecode) ? false : true,  // 关闭忽略HTML标签,即开启识别HTML标签,默认为false
2012
                smartLists  : true,
2013
                smartypants : true
2014
            };
2015
            
2016
            marked.setOptions(markedOptions);
2017
                    
2018
            var newMarkdownDoc = editormd.$marked(cmValue, markedOptions);
2019
            
2020
            //console.info("cmValue", cmValue, newMarkdownDoc);
2021
            
2022
            newMarkdownDoc = editormd.filterHTMLTags(newMarkdownDoc, settings.htmlDecode);
2023
            
2024
            //console.error("cmValue", cmValue, newMarkdownDoc);
2025
            
2026
            this.markdownTextarea.text(cmValue);
2027
            
2028
            cm.save();
2029
            
2030
            if (settings.saveHTMLToTextarea) 
2031
            {
2032
                this.htmlTextarea.text(newMarkdownDoc);
2033
            }
2034
            
2035
            if(settings.watch || (!settings.watch && state.preview))
2036
            {
2037
                previewContainer.html(newMarkdownDoc);
2038
2039
                this.previewCodeHighlight();
2040
                
2041
                if (settings.toc) 
2042
                {
2043
                    var tocContainer = (settings.tocContainer === "") ? previewContainer : $(settings.tocContainer);
2044
                    var tocMenu      = tocContainer.find("." + this.classPrefix + "toc-menu");
2045
                    
2046
                    tocContainer.attr("previewContainer", (settings.tocContainer === "") ? "true" : "false");
2047
                    
2048
                    if (settings.tocContainer !== "" && tocMenu.length > 0)
2049
                    {
2050
                        tocMenu.remove();
2051
                    }
2052
                    
2053
                    editormd.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel);
2054
            
2055
                    if (settings.tocDropdown || tocContainer.find("." + this.classPrefix + "toc-menu").length > 0)
2056
                    {
2057
                        editormd.tocDropdownMenu(tocContainer, (settings.tocTitle !== "") ? settings.tocTitle : this.lang.tocTitle);
2058
                    }
2059
            
2060
                    if (settings.tocContainer !== "")
2061
                    {
2062
                        previewContainer.find(".markdown-toc").css("border", "none");
2063
                    }
2064
                }
2065
                
2066
                if (settings.tex)
2067
                {
2068
                    if (!editormd.kaTeXLoaded && settings.autoLoadModules) 
2069
                    {
2070
                        editormd.loadKaTeX(function() {
2071
                            editormd.$katex = katex;
0 ignored issues
show
Bug introduced by
The variable katex seems to be never declared. If this is a global, consider adding a /** global: katex */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2072
                            editormd.kaTeXLoaded = true;
2073
                            _this.katexRender();
2074
                        });
2075
                    } 
2076
                    else 
2077
                    {
2078
                        editormd.$katex = katex;
0 ignored issues
show
Bug introduced by
The variable katex seems to be never declared. If this is a global, consider adding a /** global: katex */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2079
                        this.katexRender();
2080
                    }
2081
                }                
2082
                
2083
                if (settings.flowChart || settings.sequenceDiagram)
2084
                {
2085
                    flowchartTimer = setTimeout(function(){
2086
                        clearTimeout(flowchartTimer);
2087
                        _this.flowChartAndSequenceDiagramRender();
2088
                        flowchartTimer = null;
2089
                    }, 10);
2090
                }
2091
2092
                if (state.loaded) 
2093
                {
2094
                    $.proxy(settings.onchange, this)();
2095
                }
2096
            }
2097
2098
            return this;
2099
        },
2100
        
2101
        /**
2102
         * 聚焦光标位置
2103
         * Focusing the cursor position
2104
         * 
2105
         * @returns {editormd}         返回editormd的实例对象
2106
         */
2107
        
2108
        focus : function() {
2109
            this.cm.focus();
2110
2111
            return this;
2112
        },
2113
        
2114
        /**
2115
         * 设置光标的位置
2116
         * Set cursor position
2117
         * 
2118
         * @param   {Object}    cursor 要设置的光标位置键值对象,例:{line:1, ch:0}
2119
         * @returns {editormd}         返回editormd的实例对象
2120
         */
2121
        
2122
        setCursor : function(cursor) {
2123
            this.cm.setCursor(cursor);
2124
2125
            return this;
2126
        },
2127
        
2128
        /**
2129
         * 获取当前光标的位置
2130
         * Get the current position of the cursor
2131
         * 
2132
         * @returns {Cursor}         返回一个光标Cursor对象
2133
         */
2134
        
2135
        getCursor : function() {
2136
            return this.cm.getCursor();
2137
        },
2138
        
2139
        /**
2140
         * 设置光标选中的范围
2141
         * Set cursor selected ranges
2142
         * 
2143
         * @param   {Object}    from   开始位置的光标键值对象,例:{line:1, ch:0}
2144
         * @param   {Object}    to     结束位置的光标键值对象,例:{line:1, ch:0}
2145
         * @returns {editormd}         返回editormd的实例对象
2146
         */
2147
        
2148
        setSelection : function(from, to) {
2149
        
2150
            this.cm.setSelection(from, to);
2151
        
2152
            return this;
2153
        },
2154
        
2155
        /**
2156
         * 获取光标选中的文本
2157
         * Get the texts from cursor selected
2158
         * 
2159
         * @returns {String}         返回选中文本的字符串形式
2160
         */
2161
        
2162
        getSelection : function() {
2163
            return this.cm.getSelection();
2164
        },
2165
        
2166
        /**
2167
         * 设置光标选中的文本范围
2168
         * Set the cursor selection ranges
2169
         * 
2170
         * @param   {Array}    ranges  cursor selection ranges array
2171
         * @returns {Array}            return this
2172
         */
2173
        
2174
        setSelections : function(ranges) {
2175
            this.cm.setSelections(ranges);
2176
            
2177
            return this;
2178
        },
2179
        
2180
        /**
2181
         * 获取光标选中的文本范围
2182
         * Get the cursor selection ranges
2183
         * 
2184
         * @returns {Array}         return selection ranges array
2185
         */
2186
        
2187
        getSelections : function() {
2188
            return this.cm.getSelections();
2189
        },
2190
        
2191
        /**
2192
         * 替换当前光标选中的文本或在当前光标处插入新字符
2193
         * Replace the text at the current cursor selected or insert a new character at the current cursor position
2194
         * 
2195
         * @param   {String}    value  要插入的字符值
2196
         * @returns {editormd}         返回editormd的实例对象
2197
         */
2198
        
2199
        replaceSelection : function(value) {
2200
            this.cm.replaceSelection(value);
2201
2202
            return this;
2203
        },
2204
        
2205
        /**
2206
         * 在当前光标处插入新字符
2207
         * Insert a new character at the current cursor position
2208
         *
2209
         * 同replaceSelection()方法
2210
         * With the replaceSelection() method
2211
         * 
2212
         * @param   {String}    value  要插入的字符值
2213
         * @returns {editormd}         返回editormd的实例对象
2214
         */
2215
        
2216
        insertValue : function(value) {
2217
            this.replaceSelection(value);
2218
2219
            return this;
2220
        },
2221
        
2222
        /**
2223
         * 追加markdown
2224
         * append Markdown to editor
2225
         * 
2226
         * @param   {String}    md     要追加的markdown源文档
2227
         * @returns {editormd}         返回editormd的实例对象
2228
         */
2229
        
2230
        appendMarkdown : function(md) {
2231
            var settings = this.settings;
0 ignored issues
show
Unused Code introduced by
The assignment to variable settings seems to be never used. Consider removing it.
Loading history...
2232
            var cm       = this.cm;
2233
            
2234
            cm.setValue(cm.getValue() + md);
2235
            
2236
            return this;
2237
        },
2238
        
2239
        /**
2240
         * 设置和传入编辑器的markdown源文档
2241
         * Set Markdown source document
2242
         * 
2243
         * @param   {String}    md     要传入的markdown源文档
2244
         * @returns {editormd}         返回editormd的实例对象
2245
         */
2246
        
2247
        setMarkdown : function(md) {
2248
            this.cm.setValue(md || this.settings.markdown);
2249
            
2250
            return this;
2251
        },
2252
        
2253
        /**
2254
         * 获取编辑器的markdown源文档
2255
         * Set Editor.md markdown/CodeMirror value
2256
         * 
2257
         * @returns {editormd}         返回editormd的实例对象
2258
         */
2259
        
2260
        getMarkdown : function() {
2261
            return this.cm.getValue();
2262
        },
2263
        
2264
        /**
2265
         * 获取编辑器的源文档
2266
         * Get CodeMirror value
2267
         * 
2268
         * @returns {editormd}         返回editormd的实例对象
2269
         */
2270
        
2271
        getValue : function() {
2272
            return this.cm.getValue();
2273
        },
2274
        
2275
        /**
2276
         * 设置编辑器的源文档
2277
         * Set CodeMirror value
2278
         * 
2279
         * @param   {String}     value   set code/value/string/text
2280
         * @returns {editormd}           返回editormd的实例对象
2281
         */
2282
        
2283
        setValue : function(value) {
2284
            this.cm.setValue(value);
2285
            
2286
            return this;
2287
        },
2288
        
2289
        /**
2290
         * 清空编辑器
2291
         * Empty CodeMirror editor container
2292
         * 
2293
         * @returns {editormd}         返回editormd的实例对象
2294
         */
2295
        
2296
        clear : function() {
2297
            this.cm.setValue("");
2298
            
2299
            return this;
2300
        },
2301
        
2302
        /**
2303
         * 获取解析后存放在Textarea的HTML源码
2304
         * Get parsed html code from Textarea
2305
         * 
2306
         * @returns {String}               返回HTML源码
2307
         */
2308
        
2309
        getHTML : function() {
2310
            if (!this.settings.saveHTMLToTextarea)
2311
            {
2312
                alert("Error: settings.saveHTMLToTextarea == false");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
2313
2314
                return false;
2315
            }
2316
            
2317
            return this.htmlTextarea.val();
2318
        },
2319
        
2320
        /**
2321
         * getHTML()的别名
2322
         * getHTML (alias)
2323
         * 
2324
         * @returns {String}           Return html code 返回HTML源码
2325
         */
2326
        
2327
        getTextareaSavedHTML : function() {
2328
            return this.getHTML();
2329
        },
2330
        
2331
        /**
2332
         * 获取预览窗口的HTML源码
2333
         * Get html from preview container
2334
         * 
2335
         * @returns {editormd}         返回editormd的实例对象
2336
         */
2337
        
2338
        getPreviewedHTML : function() {
2339
            if (!this.settings.watch)
2340
            {
2341
                alert("Error: settings.watch == false");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
2342
2343
                return false;
2344
            }
2345
            
2346
            return this.previewContainer.html();
2347
        },
2348
        
2349
        /**
2350
         * 开启实时预览
2351
         * Enable real-time watching
2352
         * 
2353
         * @returns {editormd}         返回editormd的实例对象
2354
         */
2355
        
2356
        watch : function(callback) {     
2357
            var settings        = this.settings;
2358
            
2359
            if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0)
2360
            {
2361
                return this;
2362
            }
2363
            
2364
            this.state.watching = settings.watch = true;
2365
            this.preview.show();
2366
            
2367
            if (this.toolbar)
2368
            {
2369
                var watchIcon   = settings.toolbarIconsClass.watch;
2370
                var unWatchIcon = settings.toolbarIconsClass.unwatch;
2371
                
2372
                var icon        = this.toolbar.find(".fa[name=watch]");
2373
                icon.parent().attr("title", settings.lang.toolbar.watch);
2374
                icon.removeClass(unWatchIcon).addClass(watchIcon);
2375
            }
2376
            
2377
            this.codeMirror.css("border-right", "1px solid #ddd").width(this.editor.width() / 2); 
2378
            
2379
            timer = 0;
2380
            
2381
            this.save().resize();
2382
            
2383
            if (!settings.onwatch)
2384
            {
2385
                settings.onwatch = callback || function() {};
2386
            }
2387
            
2388
            $.proxy(settings.onwatch, this)();
2389
            
2390
            return this;
2391
        },
2392
        
2393
        /**
2394
         * 关闭实时预览
2395
         * Disable real-time watching
2396
         * 
2397
         * @returns {editormd}         返回editormd的实例对象
2398
         */
2399
        
2400
        unwatch : function(callback) {
2401
            var settings        = this.settings;
2402
            this.state.watching = settings.watch = false;
2403
            this.preview.hide();
2404
            
2405
            if (this.toolbar) 
2406
            {
2407
                var watchIcon   = settings.toolbarIconsClass.watch;
2408
                var unWatchIcon = settings.toolbarIconsClass.unwatch;
2409
                
2410
                var icon    = this.toolbar.find(".fa[name=watch]");
2411
                icon.parent().attr("title", settings.lang.toolbar.unwatch);
2412
                icon.removeClass(watchIcon).addClass(unWatchIcon);
2413
            }
2414
            
2415
            this.codeMirror.css("border-right", "none").width(this.editor.width());
2416
            
2417
            this.resize();
2418
            
2419
            if (!settings.onunwatch)
2420
            {
2421
                settings.onunwatch = callback || function() {};
2422
            }
2423
            
2424
            $.proxy(settings.onunwatch, this)();
2425
            
2426
            return this;
2427
        },
2428
        
2429
        /**
2430
         * 显示编辑器
2431
         * Show editor
2432
         * 
2433
         * @param   {Function} [callback=function()] 回调函数
2434
         * @returns {editormd}                       返回editormd的实例对象
2435
         */
2436
        
2437
        show : function(callback) {
2438
            callback  = callback || function() {};
2439
            
2440
            var _this = this;
2441
            this.editor.show(0, function() {
2442
                $.proxy(callback, _this)();
2443
            });
2444
            
2445
            return this;
2446
        },
2447
        
2448
        /**
2449
         * 隐藏编辑器
2450
         * Hide editor
2451
         * 
2452
         * @param   {Function} [callback=function()] 回调函数
2453
         * @returns {editormd}                       返回editormd的实例对象
2454
         */
2455
        
2456
        hide : function(callback) {
2457
            callback  = callback || function() {};
2458
            
2459
            var _this = this;
2460
            this.editor.hide(0, function() {
2461
                $.proxy(callback, _this)();
2462
            });
2463
            
2464
            return this;
2465
        },
2466
        
2467
        /**
2468
         * 隐藏编辑器部分,只预览HTML
2469
         * Enter preview html state
2470
         * 
2471
         * @returns {editormd}         返回editormd的实例对象
2472
         */
2473
        
2474
        previewing : function() {
2475
            
2476
            var _this            = this;
2477
            var editor           = this.editor;
2478
            var preview          = this.preview;
2479
            var toolbar          = this.toolbar;
2480
            var settings         = this.settings;
2481
            var codeMirror       = this.codeMirror;
2482
            var previewContainer = this.previewContainer;
2483
            
2484
            if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0) {
2485
                return this;
2486
            }
2487
            
2488
            if (settings.toolbar && toolbar) {
2489
                toolbar.toggle();
2490
                toolbar.find(".fa[name=preview]").toggleClass("active");
2491
            }
2492
            
2493
            codeMirror.toggle();
2494
            
2495
            var escHandle = function(event) {
2496
                if (event.shiftKey && event.keyCode === 27) {
2497
                    _this.previewed();
2498
                }
2499
            };
2500
2501
            if (codeMirror.css("display") === "none") // 为了兼容Zepto,而不使用codeMirror.is(":hidden")
2502
            {
2503
                this.state.preview = true;
2504
2505
                if (this.state.fullscreen) {
2506
                    preview.css("background", "#fff");
2507
                }
2508
                
2509
                editor.find("." + this.classPrefix + "preview-close-btn").show().bind(editormd.mouseOrTouch("click", "touchend"), function(){
2510
                    _this.previewed();
2511
                });
2512
            
2513
                if (!settings.watch)
2514
                {
2515
                    this.save();
2516
                } 
2517
                else 
2518
                {
2519
                    previewContainer.css("padding", "");
2520
                }
2521
                
2522
                previewContainer.addClass(this.classPrefix + "preview-active");
2523
2524
                preview.show().css({
2525
                    position  : "",
2526
                    top       : 0,
2527
                    width     : editor.width(),
2528
                    height    : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height()
2529
                });
2530
                
2531
                if (this.state.loaded)
2532
                {
2533
                    $.proxy(settings.onpreviewing, this)();
2534
                }
2535
2536
                $(window).bind("keyup", escHandle);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
2537
            } 
2538
            else 
2539
            {
2540
                $(window).unbind("keyup", escHandle);
2541
                this.previewed();
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
2542
            }
2543
        },
2544
        
2545
        /**
2546
         * 显示编辑器部分,退出只预览HTML
2547
         * Exit preview html state
2548
         * 
2549
         * @returns {editormd}         返回editormd的实例对象
2550
         */
2551
        
2552
        previewed : function() {
2553
            
2554
            var editor           = this.editor;
2555
            var preview          = this.preview;
2556
            var toolbar          = this.toolbar;
2557
            var settings         = this.settings;
2558
            var previewContainer = this.previewContainer;
2559
            var previewCloseBtn  = editor.find("." + this.classPrefix + "preview-close-btn");
2560
2561
            this.state.preview   = false;
2562
            
2563
            this.codeMirror.show();
2564
            
2565
            if (settings.toolbar) {
2566
                toolbar.show();
2567
            }
2568
            
2569
            preview[(settings.watch) ? "show" : "hide"]();
2570
            
2571
            previewCloseBtn.hide().unbind(editormd.mouseOrTouch("click", "touchend"));
2572
                
2573
            previewContainer.removeClass(this.classPrefix + "preview-active");
2574
                
2575
            if (settings.watch)
2576
            {
2577
                previewContainer.css("padding", "20px");
2578
            }
2579
            
2580
            preview.css({ 
2581
                background : null,
2582
                position   : "absolute",
2583
                width      : editor.width() / 2,
2584
                height     : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height() - toolbar.height(),
2585
                top        : (settings.toolbar)    ? toolbar.height() : 0
2586
            });
2587
2588
            if (this.state.loaded)
2589
            {
2590
                $.proxy(settings.onpreviewed, this)();
2591
            }
2592
            
2593
            return this;
2594
        },
2595
        
2596
        /**
2597
         * 编辑器全屏显示
2598
         * Fullscreen show
2599
         * 
2600
         * @returns {editormd}         返回editormd的实例对象
2601
         */
2602
        
2603
        fullscreen : function() {
2604
            
2605
            var _this            = this;
2606
            var state            = this.state;
2607
            var editor           = this.editor;
2608
            var preview          = this.preview;
0 ignored issues
show
Unused Code introduced by
The assignment to variable preview seems to be never used. Consider removing it.
Loading history...
2609
            var toolbar          = this.toolbar;
2610
            var settings         = this.settings;
2611
            var fullscreenClass  = this.classPrefix + "fullscreen";
2612
            
2613
            if (toolbar) {
2614
                toolbar.find(".fa[name=fullscreen]").parent().toggleClass("active"); 
2615
            }
2616
            
2617
            var escHandle = function(event) {
2618
                if (!event.shiftKey && event.keyCode === 27) 
2619
                {
2620
                    if (state.fullscreen)
2621
                    {
2622
                        _this.fullscreenExit();
2623
                    }
2624
                }
2625
            };
2626
2627
            if (!editor.hasClass(fullscreenClass)) 
2628
            {
2629
                state.fullscreen = true;
2630
2631
                $("html,body").css("overflow", "hidden");
2632
                
2633
                editor.css({
2634
                    width    : $(window).width(),
2635
                    height   : $(window).height()
2636
                }).addClass(fullscreenClass);
2637
2638
                this.resize();
2639
    
2640
                $.proxy(settings.onfullscreen, this)();
2641
2642
                $(window).bind("keyup", escHandle);
2643
            }
2644
            else
2645
            {           
2646
                $(window).unbind("keyup", escHandle); 
2647
                this.fullscreenExit();
2648
            }
2649
2650
            return this;
2651
        },
2652
        
2653
        /**
2654
         * 编辑器退出全屏显示
2655
         * Exit fullscreen state
2656
         * 
2657
         * @returns {editormd}         返回editormd的实例对象
2658
         */
2659
        
2660
        fullscreenExit : function() {
2661
            
2662
            var editor            = this.editor;
2663
            var settings          = this.settings;
2664
            var toolbar           = this.toolbar;
2665
            var fullscreenClass   = this.classPrefix + "fullscreen";  
2666
            
2667
            this.state.fullscreen = false;
2668
            
2669
            if (toolbar) {
2670
                toolbar.find(".fa[name=fullscreen]").parent().removeClass("active"); 
2671
            }
2672
2673
            $("html,body").css("overflow", "");
2674
2675
            editor.css({
2676
                width    : editor.data("oldWidth"),
2677
                height   : editor.data("oldHeight")
2678
            }).removeClass(fullscreenClass);
2679
2680
            this.resize();
2681
            
2682
            $.proxy(settings.onfullscreenExit, this)();
2683
2684
            return this;
2685
        },
2686
        
2687
        /**
2688
         * 加载并执行插件
2689
         * Load and execute the plugin
2690
         * 
2691
         * @param   {String}     name    plugin name / function name
2692
         * @param   {String}     path    plugin load path
2693
         * @returns {editormd}           返回editormd的实例对象
2694
         */
2695
        
2696
        executePlugin : function(name, path) {
2697
            
2698
            var _this    = this;
2699
            var cm       = this.cm;
2700
            var settings = this.settings;
2701
            
2702
            path = settings.pluginPath + path;
2703
            
2704
            if (typeof define === "function") 
0 ignored issues
show
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2705
            {            
2706
                if (typeof this[name] === "undefined")
2707
                {
2708
                    alert("Error: " + name + " plugin is not found, you are not load this plugin.");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
2709
                    
2710
                    return this;
2711
                }
2712
                
2713
                this[name](cm);
2714
                
2715
                return this;
2716
            }
2717
            
2718
            if ($.inArray(path, editormd.loadFiles.plugin) < 0)
2719
            {
2720
                editormd.loadPlugin(path, function() {
2721
                    editormd.loadPlugins[name] = _this[name];
2722
                    _this[name](cm);
2723
                });
2724
            }
2725
            else
2726
            {
2727
                $.proxy(editormd.loadPlugins[name], this)(cm);
2728
            }
2729
            
2730
            return this;
2731
        },
2732
                
2733
        /**
2734
         * 搜索替换
2735
         * Search & replace
2736
         * 
2737
         * @param   {String}     command    CodeMirror serach commands, "find, fintNext, fintPrev, clearSearch, replace, replaceAll"
2738
         * @returns {editormd}              return this
2739
         */
2740
        
2741
        search : function(command) {
2742
            var settings = this.settings;
2743
            
2744
            if (!settings.searchReplace)
2745
            {
2746
                alert("Error: settings.searchReplace == false");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
2747
                return this;
2748
            }
2749
            
2750
            if (!settings.readOnly)
2751
            {
2752
                this.cm.execCommand(command || "find");
2753
            }
2754
            
2755
            return this;
2756
        },
2757
        
2758
        searchReplace : function() {            
2759
            this.search("replace");
2760
            
2761
            return this;
2762
        },
2763
        
2764
        searchReplaceAll : function() {          
2765
            this.search("replaceAll");
2766
            
2767
            return this;
2768
        }
2769
    };
2770
    
2771
    editormd.fn.init.prototype = editormd.fn; 
2772
   
2773
    /**
2774
     * 锁屏
2775
     * lock screen when dialog opening
2776
     * 
2777
     * @returns {void}
2778
     */
2779
2780
    editormd.dialogLockScreen = function() {
2781
        var settings = this.settings || {dialogLockScreen : true};
2782
        
2783
        if (settings.dialogLockScreen) 
2784
        {            
2785
            $("html,body").css("overflow", "hidden");
2786
            this.resize();
2787
        }
2788
    };
2789
   
2790
    /**
2791
     * 显示透明背景层
2792
     * Display mask layer when dialog opening
2793
     * 
2794
     * @param   {Object}     dialog    dialog jQuery object
2795
     * @returns {void}
2796
     */
2797
    
2798
    editormd.dialogShowMask = function(dialog) {
2799
        var editor   = this.editor;
2800
        var settings = this.settings || {dialogShowMask : true};
2801
        
2802
        dialog.css({
2803
            top  : ($(window).height() - dialog.height()) / 2 + "px",
2804
            left : ($(window).width()  - dialog.width())  / 2 + "px"
2805
        });
2806
2807
        if (settings.dialogShowMask) {
2808
            editor.children("." + this.classPrefix + "mask").css("z-index", parseInt(dialog.css("z-index")) - 1).show();
2809
        }
2810
    };
2811
2812
    editormd.toolbarHandlers = {
2813
        undo : function() {
2814
            this.cm.undo();
2815
        },
2816
        
2817
        redo : function() {
2818
            this.cm.redo();
2819
        },
2820
        
2821
        bold : function() {
2822
            var cm        = this.cm;
2823
            var cursor    = cm.getCursor();
2824
            var selection = cm.getSelection();
2825
2826
            cm.replaceSelection("**" + selection + "**");
2827
2828
            if(selection === "") {
2829
                cm.setCursor(cursor.line, cursor.ch + 2);
2830
            }
2831
        },
2832
        
2833
        del : function() {
2834
            var cm        = this.cm;
2835
            var cursor    = cm.getCursor();
2836
            var selection = cm.getSelection();
2837
2838
            cm.replaceSelection("~~" + selection + "~~");
2839
2840
            if(selection === "") {
2841
                cm.setCursor(cursor.line, cursor.ch + 2);
2842
            }
2843
        },
2844
2845
        italic : function() {
2846
            var cm        = this.cm;
2847
            var cursor    = cm.getCursor();
2848
            var selection = cm.getSelection();
2849
2850
            cm.replaceSelection("*" + selection + "*");
2851
2852
            if(selection === "") {
2853
                cm.setCursor(cursor.line, cursor.ch + 1);
2854
            }
2855
        },
2856
2857
        quote : function() {
2858
            var cm        = this.cm;
2859
            var cursor    = cm.getCursor();
2860
            var selection = cm.getSelection();
2861
2862
            if (cursor.ch !== 0)
2863
            {
2864
                cm.setCursor(cursor.line, 0);
2865
                cm.replaceSelection("> " + selection);
2866
                cm.setCursor(cursor.line, cursor.ch + 2);
2867
            }
2868
            else
2869
            {
2870
                cm.replaceSelection("> " + selection);
2871
            }
2872
2873
            //cm.replaceSelection("> " + selection);
2874
            //cm.setCursor(cursor.line, (selection === "") ? cursor.ch + 2 : cursor.ch + selection.length + 2);
2875
        },
2876
        
2877
        ucfirst : function() {
2878
            var cm         = this.cm;
2879
            var selection  = cm.getSelection();
2880
            var selections = cm.listSelections();
2881
2882
            cm.replaceSelection(editormd.firstUpperCase(selection));
2883
            cm.setSelections(selections);
2884
        },
2885
        
2886
        ucwords : function() {
2887
            var cm         = this.cm;
2888
            var selection  = cm.getSelection();
2889
            var selections = cm.listSelections();
2890
2891
            cm.replaceSelection(editormd.wordsFirstUpperCase(selection));
2892
            cm.setSelections(selections);
2893
        },
2894
        
2895
        uppercase : function() {
2896
            var cm         = this.cm;
2897
            var selection  = cm.getSelection();
2898
            var selections = cm.listSelections();
2899
2900
            cm.replaceSelection(selection.toUpperCase());
2901
            cm.setSelections(selections);
2902
        },
2903
        
2904
        lowercase : function() {
2905
            var cm         = this.cm;
2906
            var cursor     = cm.getCursor();
0 ignored issues
show
Unused Code introduced by
The variable cursor seems to be never used. Consider removing it.
Loading history...
2907
            var selection  = cm.getSelection();
2908
            var selections = cm.listSelections();
2909
            
2910
            cm.replaceSelection(selection.toLowerCase());
2911
            cm.setSelections(selections);
2912
        },
2913
2914
        h1 : function() {
2915
            var cm        = this.cm;
2916
            var cursor    = cm.getCursor();
2917
            var selection = cm.getSelection();
2918
2919
            if (cursor.ch !== 0)
2920
            {
2921
                cm.setCursor(cursor.line, 0);
2922
                cm.replaceSelection("# " + selection);
2923
                cm.setCursor(cursor.line, cursor.ch + 2);
2924
            }
2925
            else
2926
            {
2927
                cm.replaceSelection("# " + selection);
2928
            }
2929
        },
2930
2931
        h2 : function() {
2932
            var cm        = this.cm;
2933
            var cursor    = cm.getCursor();
2934
            var selection = cm.getSelection();
2935
2936
            if (cursor.ch !== 0)
2937
            {
2938
                cm.setCursor(cursor.line, 0);
2939
                cm.replaceSelection("## " + selection);
2940
                cm.setCursor(cursor.line, cursor.ch + 3);
2941
            }
2942
            else
2943
            {
2944
                cm.replaceSelection("## " + selection);
2945
            }
2946
        },
2947
2948
        h3 : function() {
2949
            var cm        = this.cm;
2950
            var cursor    = cm.getCursor();
2951
            var selection = cm.getSelection();
2952
2953
            if (cursor.ch !== 0)
2954
            {
2955
                cm.setCursor(cursor.line, 0);
2956
                cm.replaceSelection("### " + selection);
2957
                cm.setCursor(cursor.line, cursor.ch + 4);
2958
            }
2959
            else
2960
            {
2961
                cm.replaceSelection("### " + selection);
2962
            }
2963
        },
2964
2965
        h4 : function() {
2966
            var cm        = this.cm;
2967
            var cursor    = cm.getCursor();
2968
            var selection = cm.getSelection();
2969
2970
            if (cursor.ch !== 0)
2971
            {
2972
                cm.setCursor(cursor.line, 0);
2973
                cm.replaceSelection("#### " + selection);
2974
                cm.setCursor(cursor.line, cursor.ch + 5);
2975
            }
2976
            else
2977
            {
2978
                cm.replaceSelection("#### " + selection);
2979
            }
2980
        },
2981
2982
        h5 : function() {
2983
            var cm        = this.cm;
2984
            var cursor    = cm.getCursor();
2985
            var selection = cm.getSelection();
2986
2987
            if (cursor.ch !== 0)
2988
            {
2989
                cm.setCursor(cursor.line, 0);
2990
                cm.replaceSelection("##### " + selection);
2991
                cm.setCursor(cursor.line, cursor.ch + 6);
2992
            }
2993
            else
2994
            {
2995
                cm.replaceSelection("##### " + selection);
2996
            }
2997
        },
2998
2999
        h6 : function() {
3000
            var cm        = this.cm;
3001
            var cursor    = cm.getCursor();
3002
            var selection = cm.getSelection();
3003
3004
            if (cursor.ch !== 0)
3005
            {
3006
                cm.setCursor(cursor.line, 0);
3007
                cm.replaceSelection("###### " + selection);
3008
                cm.setCursor(cursor.line, cursor.ch + 7);
3009
            }
3010
            else
3011
            {
3012
                cm.replaceSelection("###### " + selection);
3013
            }
3014
        },
3015
3016 View Code Duplication
        "list-ul" : function() {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3017
            var cm        = this.cm;
3018
            var cursor    = cm.getCursor();
0 ignored issues
show
Unused Code introduced by
The variable cursor seems to be never used. Consider removing it.
Loading history...
3019
            var selection = cm.getSelection();
3020
3021
            if (selection === "") 
3022
            {
3023
                cm.replaceSelection("- " + selection);
3024
            } 
3025
            else 
3026
            {
3027
                var selectionText = selection.split("\n");
3028
3029
                for (var i = 0, len = selectionText.length; i < len; i++) 
3030
                {
3031
                    selectionText[i] = (selectionText[i] === "") ? "" : "- " + selectionText[i];
3032
                }
3033
3034
                cm.replaceSelection(selectionText.join("\n"));
3035
            }
3036
        },
3037
3038 View Code Duplication
        "list-ol" : function() {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3039
            var cm        = this.cm;
3040
            var cursor    = cm.getCursor();
0 ignored issues
show
Unused Code introduced by
The variable cursor seems to be never used. Consider removing it.
Loading history...
3041
            var selection = cm.getSelection();
3042
3043
            if(selection === "") 
3044
            {
3045
                cm.replaceSelection("1. " + selection);
3046
            }
3047
            else
3048
            {
3049
                var selectionText = selection.split("\n");
3050
3051
                for (var i = 0, len = selectionText.length; i < len; i++) 
3052
                {
3053
                    selectionText[i] = (selectionText[i] === "") ? "" : (i+1) + ". " + selectionText[i];
3054
                }
3055
3056
                cm.replaceSelection(selectionText.join("\n"));
3057
            }
3058
        },
3059
3060
        hr : function() {
3061
            var cm        = this.cm;
3062
            var cursor    = cm.getCursor();
3063
            var selection = cm.getSelection();
0 ignored issues
show
Unused Code introduced by
The variable selection seems to be never used. Consider removing it.
Loading history...
3064
3065
            cm.replaceSelection(((cursor.ch !== 0) ? "\n\n" : "\n") + "------------\n\n");
3066
        },
3067
3068
        tex : function() {
3069
            if (!this.settings.tex)
3070
            {
3071
                alert("settings.tex === false");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
3072
                return this;
3073
            }
3074
            
3075
            var cm        = this.cm;
3076
            var cursor    = cm.getCursor();
3077
            var selection = cm.getSelection();
3078
3079
            cm.replaceSelection("$$" + selection + "$$");
3080
3081
            if(selection === "") {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if selection === "" is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
3082
                cm.setCursor(cursor.line, cursor.ch + 2);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
3083
            }
3084
        },
3085
3086
        link : function() {
3087
            this.executePlugin("linkDialog", "link-dialog/link-dialog");
3088
        },
3089
3090
        "reference-link" : function() {
3091
            this.executePlugin("referenceLinkDialog", "reference-link-dialog/reference-link-dialog");
3092
        },
3093
3094
        pagebreak : function() {
3095
            if (!this.settings.pageBreak)
3096
            {
3097
                alert("settings.pageBreak === false");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
3098
                return this;
3099
            }
3100
            
3101
            var cm        = this.cm;
3102
            var selection = cm.getSelection();
0 ignored issues
show
Unused Code introduced by
The variable selection seems to be never used. Consider removing it.
Loading history...
3103
3104
            cm.replaceSelection("\r\n[========]\r\n");
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
3105
        },
3106
3107
        image : function() {
3108
            this.executePlugin("imageDialog", "image-dialog/image-dialog");
3109
        },
3110
        
3111
        code : function() {
3112
            var cm        = this.cm;
3113
            var cursor    = cm.getCursor();
3114
            var selection = cm.getSelection();
3115
3116
            cm.replaceSelection("`" + selection + "`");
3117
3118
            if (selection === "") {
3119
                cm.setCursor(cursor.line, cursor.ch + 1);
3120
            }
3121
        },
3122
3123
        "code-block" : function() {
3124
            this.executePlugin("codeBlockDialog", "code-block-dialog/code-block-dialog");            
3125
        },
3126
3127
        "preformatted-text" : function() {
3128
            this.executePlugin("preformattedTextDialog", "preformatted-text-dialog/preformatted-text-dialog");
3129
        },
3130
        
3131
        table : function() {
3132
            this.executePlugin("tableDialog", "table-dialog/table-dialog");         
3133
        },
3134
        
3135
        datetime : function() {
3136
            var cm        = this.cm;
3137
            var selection = cm.getSelection();
0 ignored issues
show
Unused Code introduced by
The variable selection seems to be never used. Consider removing it.
Loading history...
3138
            var date      = new Date();
0 ignored issues
show
Unused Code introduced by
The variable date seems to be never used. Consider removing it.
Loading history...
3139
            var langName  = this.settings.lang.name;
3140
            var datefmt   = editormd.dateFormat() + " " + editormd.dateFormat((langName === "zh-cn" || langName === "zh-tw") ? "cn-week-day" : "week-day");
3141
3142
            cm.replaceSelection(datefmt);
3143
        },
3144
        
3145
        emoji : function() {
3146
            this.executePlugin("emojiDialog", "emoji-dialog/emoji-dialog");
3147
        },
3148
                
3149
        "html-entities" : function() {
3150
            this.executePlugin("htmlEntitiesDialog", "html-entities-dialog/html-entities-dialog");
3151
        },
3152
                
3153
        "goto-line" : function() {
3154
            this.executePlugin("gotoLineDialog", "goto-line-dialog/goto-line-dialog");
3155
        },
3156
3157
        watch : function() {    
3158
            this[this.settings.watch ? "unwatch" : "watch"]();
3159
        },
3160
3161
        preview : function() {
3162
            this.previewing();
3163
        },
3164
3165
        fullscreen : function() {
3166
            this.fullscreen();
3167
        },
3168
3169
        clear : function() {
3170
            this.clear();
3171
        },
3172
        
3173
        search : function() {
3174
            this.search();
3175
        },
3176
3177
        help : function() {
3178
            this.executePlugin("helpDialog", "help-dialog/help-dialog");
3179
        },
3180
3181
        info : function() {
3182
            this.showInfoDialog();
3183
        }
3184
    };
3185
    
3186
    editormd.keyMaps = {
3187
        "Ctrl-1"       : "h1",
3188
        "Ctrl-2"       : "h2",
3189
        "Ctrl-3"       : "h3",
3190
        "Ctrl-4"       : "h4",
3191
        "Ctrl-5"       : "h5",
3192
        "Ctrl-6"       : "h6",
3193
        "Ctrl-B"       : "bold",  // if this is string ==  editormd.toolbarHandlers.xxxx
3194
        "Ctrl-D"       : "datetime",
3195
        
3196
        "Ctrl-E"       : function() { // emoji
3197
            var cm        = this.cm;
3198
            var cursor    = cm.getCursor();
3199
            var selection = cm.getSelection();
3200
            
3201
            if (!this.settings.emoji)
3202
            {
3203
                alert("Error: settings.emoji == false");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
3204
                return ;
3205
            }
3206
3207
            cm.replaceSelection(":" + selection + ":");
3208
3209
            if (selection === "") {
3210
                cm.setCursor(cursor.line, cursor.ch + 1);
3211
            }
3212
        },
3213
        "Ctrl-Alt-G"   : "goto-line",
3214
        "Ctrl-H"       : "hr",
3215
        "Ctrl-I"       : "italic",
3216
        "Ctrl-K"       : "code",
3217
        
3218 View Code Duplication
        "Ctrl-L"        : function() {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3219
            var cm        = this.cm;
3220
            var cursor    = cm.getCursor();
3221
            var selection = cm.getSelection();
3222
            
3223
            var title = (selection === "") ? "" : " \""+selection+"\"";
3224
3225
            cm.replaceSelection("[" + selection + "]("+title+")");
3226
3227
            if (selection === "") {
3228
                cm.setCursor(cursor.line, cursor.ch + 1);
3229
            }
3230
        },
3231
        "Ctrl-U"         : "list-ul",
3232
        
3233
        "Shift-Ctrl-A"   : function() {
3234
            var cm        = this.cm;
3235
            var cursor    = cm.getCursor();
3236
            var selection = cm.getSelection();
3237
            
3238
            if (!this.settings.atLink)
3239
            {
3240
                alert("Error: settings.atLink == false");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
3241
                return ;
3242
            }
3243
3244
            cm.replaceSelection("@" + selection);
3245
3246
            if (selection === "") {
3247
                cm.setCursor(cursor.line, cursor.ch + 1);
3248
            }
3249
        },
3250
        
3251
        "Shift-Ctrl-C"     : "code",
3252
        "Shift-Ctrl-Q"     : "quote",
3253
        "Shift-Ctrl-S"     : "del",
3254
        "Shift-Ctrl-K"     : "tex",  // KaTeX
3255
        
3256
        "Shift-Alt-C"      : function() {
3257
            var cm        = this.cm;
3258
            var cursor    = cm.getCursor();
3259
            var selection = cm.getSelection();
3260
            
3261
            cm.replaceSelection(["```", selection, "```"].join("\n"));
3262
3263
            if (selection === "") {
3264
                cm.setCursor(cursor.line, cursor.ch + 3);
3265
            } 
3266
        },
3267
        
3268
        "Shift-Ctrl-Alt-C" : "code-block",
3269
        "Shift-Ctrl-H"     : "html-entities",
3270
        "Shift-Alt-H"      : "help",
3271
        "Shift-Ctrl-E"     : "emoji",
3272
        "Shift-Ctrl-U"     : "uppercase",
3273
        "Shift-Alt-U"      : "ucwords",
3274
        "Shift-Ctrl-Alt-U" : "ucfirst",
3275
        "Shift-Alt-L"      : "lowercase",
3276
        
3277 View Code Duplication
        "Shift-Ctrl-I"     : function() {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3278
            var cm        = this.cm;
3279
            var cursor    = cm.getCursor();
3280
            var selection = cm.getSelection();
3281
            
3282
            var title = (selection === "") ? "" : " \""+selection+"\"";
3283
3284
            cm.replaceSelection("![" + selection + "]("+title+")");
3285
3286
            if (selection === "") {
3287
                cm.setCursor(cursor.line, cursor.ch + 4);
3288
            }
3289
        },
3290
        
3291
        "Shift-Ctrl-Alt-I" : "image",
3292
        "Shift-Ctrl-L"     : "link",
3293
        "Shift-Ctrl-O"     : "list-ol",
3294
        "Shift-Ctrl-P"     : "preformatted-text",
3295
        "Shift-Ctrl-T"     : "table",
3296
        "Shift-Alt-P"      : "pagebreak",
3297
        "F9"               : "watch",
3298
        "F10"              : "preview",
3299
        "F11"              : "fullscreen",
3300
    };
3301
    
3302
    /**
3303
     * 清除字符串两边的空格
3304
     * Clear the space of strings both sides.
3305
     * 
3306
     * @param   {String}    str            string
3307
     * @returns {String}                   trimed string    
3308
     */
3309
    
3310
    var trim = function(str) {
3311
        return (!String.prototype.trim) ? str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") : str.trim();
3312
    };
3313
    
3314
    editormd.trim = trim;
3315
    
3316
    /**
3317
     * 所有单词首字母大写
3318
     * Words first to uppercase
3319
     * 
3320
     * @param   {String}    str            string
3321
     * @returns {String}                   string
3322
     */
3323
    
3324
    var ucwords = function (str) {
3325
        return str.toLowerCase().replace(/\b(\w)|\s(\w)/g, function($1) {  
3326
            return $1.toUpperCase();
3327
        });
3328
    };
3329
    
3330
    editormd.ucwords = editormd.wordsFirstUpperCase = ucwords;
3331
    
3332
    /**
3333
     * 字符串首字母大写
3334
     * Only string first char to uppercase
3335
     * 
3336
     * @param   {String}    str            string
3337
     * @returns {String}                   string
3338
     */
3339
    
3340
    var firstUpperCase = function(str) {        
3341
        return str.toLowerCase().replace(/\b(\w)/, function($1){
3342
            return $1.toUpperCase();
3343
        });
3344
    };
3345
    
3346
    var ucfirst = firstUpperCase;
3347
    
3348
    editormd.firstUpperCase = editormd.ucfirst = firstUpperCase;
3349
    
3350
    editormd.urls = {
3351
        atLinkBase : "https://github.com/"
3352
    };
3353
    
3354
    editormd.regexs = {
3355
        atLink        : /@(\w+)/g,
3356
        email         : /(\w+)@(\w+)\.(\w+)\.?(\w+)?/g,
3357
        emailLink     : /(mailto:)?([\w\.\_]+)@(\w+)\.(\w+)\.?(\w+)?/g,
3358
        emoji         : /:([\w\+-]+):/g,
3359
        emojiDatetime : /(\d{2}:\d{2}:\d{2})/g,
3360
        twemoji       : /:(tw-([\w]+)-?(\w+)?):/g,
3361
        fontAwesome   : /:(fa-([\w]+)(-(\w+)){0,}):/g,
3362
        editormdLogo  : /:(editormd-logo-?(\w+)?):/g,
3363
        pageBreak     : /^\[[=]{8,}\]$/
3364
    };
3365
3366
    // Emoji graphics files url path
3367
    editormd.emoji     = {
3368
        path  : "https://www.webpagefx.com/tools/emoji-cheat-sheet/graphics/emojis/",
3369
        ext   : ".png"
3370
    };
3371
3372
    // Twitter Emoji (Twemoji)  graphics files url path    
3373
    editormd.twemoji = {
3374
        path : "http://twemoji.maxcdn.com/36x36/",
3375
        ext  : ".png"
3376
    };
3377
3378
    /**
3379
     * 自定义marked的解析器
3380
     * Custom Marked renderer rules
3381
     * 
3382
     * @param   {Array}    markdownToC     传入用于接收TOC的数组
3383
     * @returns {Renderer} markedRenderer  返回marked的Renderer自定义对象
3384
     */
3385
3386
    editormd.markedRenderer = function(markdownToC, options) {
3387
        var defaults = {
3388
            toc                  : true,           // Table of contents
3389
            tocm                 : false,
3390
            tocStartLevel        : 1,              // Said from H1 to create ToC  
3391
            pageBreak            : true,
3392
            atLink               : true,           // for @link
3393
            emailLink            : true,           // for mail address auto link
3394
            taskList             : false,          // Enable Github Flavored Markdown task lists
3395
            emoji                : false,          // :emoji: , Support Twemoji, fontAwesome, Editor.md logo emojis.
3396
            tex                  : false,          // TeX(LaTeX), based on KaTeX
3397
            flowChart            : false,          // flowChart.js only support IE9+
3398
            sequenceDiagram      : false,          // sequenceDiagram.js only support IE9+
3399
        };
3400
        
3401
        var settings        = $.extend(defaults, options || {});    
3402
        var marked          = editormd.$marked;
3403
        var markedRenderer  = new marked.Renderer();
3404
        markdownToC         = markdownToC || [];        
3405
            
3406
        var regexs          = editormd.regexs;
3407
        var atLinkReg       = regexs.atLink;
3408
        var emojiReg        = regexs.emoji;
3409
        var emailReg        = regexs.email;
3410
        var emailLinkReg    = regexs.emailLink;
3411
        var twemojiReg      = regexs.twemoji;
3412
        var faIconReg       = regexs.fontAwesome;
3413
        var editormdLogoReg = regexs.editormdLogo;
3414
        var pageBreakReg    = regexs.pageBreak;
3415
3416
        markedRenderer.emoji = function(text) {
3417
            
3418
            text = text.replace(editormd.regexs.emojiDatetime, function($1) {           
3419
                return $1.replace(/:/g, "&#58;");
3420
            });
3421
            
3422
            var matchs = text.match(emojiReg);
3423
3424
            if (!matchs || !settings.emoji) {
3425
                return text;
3426
            }
3427
3428
            for (var i = 0, len = matchs.length; i < len; i++)
3429
            {            
3430
                if (matchs[i] === ":+1:") {
3431
                    matchs[i] = ":\\+1:";
3432
                }
3433
3434
                text = text.replace(new RegExp(matchs[i]), function($1, $2){
0 ignored issues
show
Unused Code introduced by
The parameter $2 is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
3435
                    var faMatchs = $1.match(faIconReg);
3436
                    var name     = $1.replace(/:/g, "");
3437
3438
                    if (faMatchs)
3439
                    {                        
3440
                        for (var fa = 0, len1 = faMatchs.length; fa < len1; fa++)
0 ignored issues
show
Bug introduced by
The variable fa seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.fa.
Loading history...
introduced by
This code is unreachable and can thus be removed without consequences.
Loading history...
3441
                        {
3442
                            var faName = faMatchs[fa].replace(/:/g, "");
3443
                            
3444
                            return "<i class=\"fa " + faName + " fa-emoji\" title=\"" + faName.replace("fa-", "") + "\"></i>";
3445
                        }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
3446
                    }
3447
                    else
3448
                    {
3449
                        var emdlogoMathcs = $1.match(editormdLogoReg);
3450
                        var twemojiMatchs = $1.match(twemojiReg);
3451
3452
                        if (emdlogoMathcs)                                        
3453
                        {                            
3454
                            for (var x = 0, len2 = emdlogoMathcs.length; x < len2; x++)
0 ignored issues
show
introduced by
This code is unreachable and can thus be removed without consequences.
Loading history...
Bug introduced by
The variable x seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.x.
Loading history...
3455
                            {
3456
                                var logoName = emdlogoMathcs[x].replace(/:/g, "");
3457
                                return "<i class=\"" + logoName + "\" title=\"Editor.md logo (" + logoName + ")\"></i>";
3458
                            }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
3459
                        }
3460
                        else if (twemojiMatchs) 
3461
                        {
3462
                            for (var t = 0, len3 = twemojiMatchs.length; t < len3; t++)
0 ignored issues
show
Bug introduced by
The variable t seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.t.
Loading history...
introduced by
This code is unreachable and can thus be removed without consequences.
Loading history...
3463
                            {
3464
                                var twe = twemojiMatchs[t].replace(/:/g, "").replace("tw-", "");
3465
                                return "<img src=\"" + editormd.twemoji.path + twe + editormd.twemoji.ext + "\" title=\"twemoji-" + twe + "\" alt=\"twemoji-" + twe + "\" class=\"emoji twemoji\" />";
3466
                            }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
3467
                        }
3468
                        else
3469
                        {
3470
                            var src = (name === "+1") ? "plus1" : name;
3471
                            src     = (src === "black_large_square") ? "black_square" : src;
3472
                            src     = (src === "moon") ? "waxing_gibbous_moon" : src;
3473
3474
                            return "<img src=\"" + editormd.emoji.path + src + editormd.emoji.ext + "\" class=\"emoji\" title=\"&#58;" + name + "&#58;\" alt=\"&#58;" + name + "&#58;\" />";
3475
                        }
3476
                    }
3477
                });
3478
            }
3479
3480
            return text;
3481
        };
3482
3483
        markedRenderer.atLink = function(text) {
3484
3485
            if (atLinkReg.test(text))
3486
            { 
3487
                if (settings.atLink) 
3488
                {
3489
                    text = text.replace(emailReg, function($1, $2, $3, $4) {
0 ignored issues
show
Unused Code introduced by
The parameter $3 is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter $2 is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter $4 is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
3490
                        return $1.replace(/@/g, "_#_&#64;_#_");
3491
                    });
3492
3493
                    text = text.replace(atLinkReg, function($1, $2) {
3494
                        return "<a href=\"" + editormd.urls.atLinkBase + "" + $2 + "\" title=\"&#64;" + $2 + "\" class=\"at-link\">" + $1 + "</a>";
3495
                    }).replace(/_#_&#64;_#_/g, "@");
3496
                }
3497
                
3498
                if (settings.emailLink)
3499
                {
3500
                    text = text.replace(emailLinkReg, function($1, $2, $3, $4, $5) {
3501
                        return (!$2 && $.inArray($5, "jpg|jpeg|png|gif|webp|ico|icon|pdf".split("|")) < 0) ? "<a href=\"mailto:" + $1 + "\">"+$1+"</a>" : $1;
3502
                    });
3503
                }
3504
3505
                return text;
3506
            }
3507
3508
            return text;
3509
        };
3510
                
3511
        markedRenderer.link = function (href, title, text) {
3512
3513
            if (this.options.sanitize) {
3514
                try {
3515
                    var prot = decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase();
3516
                } catch(e) {
3517
                    return "";
3518
                }
3519
3520
                if (prot.indexOf("javascript:") === 0) {
3521
                    return "";
3522
                }
3523
            }
3524
3525
            var out = "<a href=\"" + href + "\"";
3526
            
3527
            if (atLinkReg.test(title) || atLinkReg.test(text))
3528
            {
3529
                if (title)
3530
                {
3531
                    out += " title=\"" + title.replace(/@/g, "&#64;");
3532
                }
3533
                
3534
                return out + "\">" + text.replace(/@/g, "&#64;") + "</a>";
3535
            }
3536
3537
            if (title) {
3538
                out += " title=\"" + title + "\"";
3539
            }
3540
3541
            out += ">" + text + "</a>";
3542
3543
            return out;
3544
        };
3545
        
3546
        markedRenderer.heading = function(text, level, raw) {
0 ignored issues
show
Unused Code introduced by
The parameter raw is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
3547
                    
3548
            var linkText       = text;
3549
            var hasLinkReg     = /\s*\<a\s*href\=\"(.*)\"\s*([^\>]*)\>(.*)\<\/a\>\s*/;
3550
            var getLinkTextReg = /\s*\<a\s*([^\>]+)\>([^\>]*)\<\/a\>\s*/g;
0 ignored issues
show
Unused Code introduced by
The variable getLinkTextReg seems to be never used. Consider removing it.
Loading history...
3551
3552
            if (hasLinkReg.test(text)) 
3553
            {
3554
                var tempText = [];
3555
                text         = text.split(/\<a\s*([^\>]+)\>([^\>]*)\<\/a\>/);
3556
3557
                for (var i = 0, len = text.length; i < len; i++)
3558
                {
3559
                    tempText.push(text[i].replace(/\s*href\=\"(.*)\"\s*/g, ""));
3560
                }
3561
3562
                text = tempText.join(" ");
3563
            }
3564
            
3565
            text = trim(text);
3566
            
3567
            var escapedText    = text.toLowerCase().replace(/[^\w]+/g, "-");
3568
            var toc = {
3569
                text  : text,
3570
                level : level,
3571
                slug  : escapedText
3572
            };
3573
            
3574
            var isChinese = /^[\u4e00-\u9fa5]+$/.test(text);
3575
            var id        = (isChinese) ? escape(text).replace(/\%/g, "") : text.toLowerCase().replace(/[^\w]+/g, "-");
3576
3577
            markdownToC.push(toc);
3578
            
3579
            var headingHTML = "<h" + level + " id=\"h"+ level + "-" + this.options.headerPrefix + id +"\">";
3580
            
3581
            headingHTML    += "<a name=\"" + text + "\" class=\"reference-link\"></a>";
3582
            headingHTML    += "<span class=\"header-link octicon octicon-link\"></span>";
3583
            headingHTML    += (hasLinkReg) ? this.atLink(this.emoji(linkText)) : this.atLink(this.emoji(text));
3584
            headingHTML    += "</h" + level + ">";
3585
3586
            return headingHTML;
3587
        };
3588
        
3589
        markedRenderer.pageBreak = function(text) {
3590
            if (pageBreakReg.test(text) && settings.pageBreak)
3591
            {
3592
                text = "<hr style=\"page-break-after:always;\" class=\"page-break editormd-page-break\" />";
3593
            }
3594
            
3595
            return text;
3596
        };
3597
3598
        markedRenderer.paragraph = function(text) {
3599
            var isTeXInline     = /\$\$(.*)\$\$/g.test(text);
3600
            var isTeXLine       = /^\$\$(.*)\$\$$/.test(text);
3601
            var isTeXAddClass   = (isTeXLine)     ? " class=\"" + editormd.classNames.tex + "\"" : "";
3602
            var isToC           = (settings.tocm) ? /^(\[TOC\]|\[TOCM\])$/.test(text) : /^\[TOC\]$/.test(text);
3603
            var isToCMenu       = /^\[TOCM\]$/.test(text);
3604
            
3605
            if (!isTeXLine && isTeXInline) 
3606
            {
3607
                text = text.replace(/(\$\$([^\$]*)\$\$)+/g, function($1, $2) {
3608
                    return "<span class=\"" + editormd.classNames.tex + "\">" + $2.replace(/\$/g, "") + "</span>";
3609
                });
3610
            } 
3611
            else 
3612
            {
3613
                text = (isTeXLine) ? text.replace(/\$/g, "") : text;
3614
            }
3615
            
3616
            var tocHTML = "<div class=\"markdown-toc editormd-markdown-toc\">" + text + "</div>";
3617
            
3618
            return (isToC) ? ( (isToCMenu) ? "<div class=\"editormd-toc-menu\">" + tocHTML + "</div><br/>" : tocHTML )
3619
                           : ( (pageBreakReg.test(text)) ? this.pageBreak(text) : "<p" + isTeXAddClass + ">" + this.atLink(this.emoji(text)) + "</p>\n" );
3620
        };
3621
3622
        markedRenderer.code = function (code, lang, escaped) { 
0 ignored issues
show
Unused Code introduced by
The parameter escaped is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
3623
3624
            if (lang === "seq" || lang === "sequence")
3625
            {
3626
                return "<div class=\"sequence-diagram\">" + code + "</div>";
3627
            } 
3628
            else if ( lang === "flow")
3629
            {
3630
                return "<div class=\"flowchart\">" + code + "</div>";
3631
            } 
3632
            else if ( lang === "math" || lang === "latex" || lang === "katex")
3633
            {
3634
                return "<p class=\"" + editormd.classNames.tex + "\">" + code + "</p>";
3635
            } 
3636
            else 
3637
            {
3638
3639
                return marked.Renderer.prototype.code.apply(this, arguments);
3640
            }
3641
        };
3642
3643
        markedRenderer.tablecell = function(content, flags) {
3644
            var type = (flags.header) ? "th" : "td";
3645
            var tag  = (flags.align)  ? "<" + type +" style=\"text-align:" + flags.align + "\">" : "<" + type + ">";
3646
            
3647
            return tag + this.atLink(this.emoji(content)) + "</" + type + ">\n";
3648
        };
3649
3650
        markedRenderer.listitem = function(text) {
3651
            if (settings.taskList && /^\s*\[[x\s]\]\s*/.test(text)) 
3652
            {
3653
                text = text.replace(/^\s*\[\s\]\s*/, "<input type=\"checkbox\" class=\"task-list-item-checkbox\" /> ")
3654
                           .replace(/^\s*\[x\]\s*/,  "<input type=\"checkbox\" class=\"task-list-item-checkbox\" checked disabled /> ");
3655
3656
                return "<li style=\"list-style: none;\">" + this.atLink(this.emoji(text)) + "</li>";
3657
            }
3658
            else 
3659
            {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
3660
                return "<li>" + this.atLink(this.emoji(text)) + "</li>";
3661
            }
3662
        };
3663
        
3664
        return markedRenderer;
3665
    };
3666
    
3667
    /**
3668
     *
3669
     * 生成TOC(Table of Contents)
3670
     * Creating ToC (Table of Contents)
3671
     * 
3672
     * @param   {Array}    toc             从marked获取的TOC数组列表
3673
     * @param   {Element}  container       插入TOC的容器元素
3674
     * @param   {Integer}  startLevel      Hx 起始层级
3675
     * @returns {Object}   tocContainer    返回ToC列表容器层的jQuery对象元素
3676
     */
3677
    
3678
    editormd.markdownToCRenderer = function(toc, container, tocDropdown, startLevel) {
3679
        
3680
        var html        = "";    
3681
        var lastLevel   = 0;
3682
        var classPrefix = this.classPrefix;
3683
        
3684
        startLevel      = startLevel  || 1;
3685
        
3686
        for (var i = 0, len = toc.length; i < len; i++) 
3687
        {
3688
            var text  = toc[i].text;
3689
            var level = toc[i].level;
3690
            
3691
            if (level < startLevel) {
3692
                continue;
3693
            }
3694
            
3695
            if (level > lastLevel) 
3696
            {
3697
                html += "";
3698
            }
3699
            else if (level < lastLevel) 
3700
            {
3701
                html += (new Array(lastLevel - level + 2)).join("</ul></li>");
3702
            } 
3703
            else 
3704
            {
3705
                html += "</ul></li>";
3706
            }
3707
3708
            html += "<li><a class=\"toc-level-" + level + "\" href=\"#" + text + "\" level=\"" + level + "\">" + text + "</a><ul>";
3709
            lastLevel = level;
3710
        }
3711
        
3712
        var tocContainer = container.find(".markdown-toc");
3713
        
3714
        if ((tocContainer.length < 1 && container.attr("previewContainer") === "false"))
3715
        {
3716
            var tocHTML = "<div class=\"markdown-toc " + classPrefix + "markdown-toc\"></div>";
3717
            
3718
            tocHTML = (tocDropdown) ? "<div class=\"" + classPrefix + "toc-menu\">" + tocHTML + "</div>" : tocHTML;
3719
            
3720
            container.html(tocHTML);
3721
            
3722
            tocContainer = container.find(".markdown-toc");
3723
        }
3724
        
3725
        if (tocDropdown)
3726
        {
3727
            tocContainer.wrap("<div class=\"" + classPrefix + "toc-menu\"></div><br/>");
3728
        }
3729
        
3730
        tocContainer.html("<ul class=\"markdown-toc-list\"></ul>").children(".markdown-toc-list").html(html.replace(/\r?\n?\<ul\>\<\/ul\>/g, ""));
3731
        
3732
        return tocContainer;
3733
    };
3734
    
3735
    /**
3736
     *
3737
     * 生成TOC下拉菜单
3738
     * Creating ToC dropdown menu
3739
     * 
3740
     * @param   {Object}   container       插入TOC的容器jQuery对象元素
3741
     * @param   {String}   tocTitle        ToC title
3742
     * @returns {Object}                   return toc-menu object
3743
     */
3744
    
3745
    editormd.tocDropdownMenu = function(container, tocTitle) {
3746
        
3747
        tocTitle      = tocTitle || "Table of Contents";
3748
        
3749
        var zindex    = 400;
3750
        var tocMenus  = container.find("." + this.classPrefix + "toc-menu");
3751
3752
        tocMenus.each(function() {
3753
            var $this  = $(this);
3754
            var toc    = $this.children(".markdown-toc");
3755
            var icon   = "<i class=\"fa fa-angle-down\"></i>";
3756
            var btn    = "<a href=\"javascript:;\" class=\"toc-menu-btn\">" + icon + tocTitle + "</a>";
3757
            var menu   = toc.children("ul");            
3758
            var list   = menu.find("li");
3759
            
3760
            toc.append(btn);
3761
            
3762
            list.first().before("<li><h1>" + tocTitle + " " + icon + "</h1></li>");
3763
            
3764
            $this.mouseover(function(){
3765
                menu.show();
3766
3767
                list.each(function(){
3768
                    var li = $(this);
3769
                    var ul = li.children("ul");
3770
3771
                    if (ul.html() === "")
3772
                    {
3773
                        ul.remove();
3774
                    }
3775
3776
                    if (ul.length > 0 && ul.html() !== "")
3777
                    {
3778
                        var firstA = li.children("a").first();
3779
3780
                        if (firstA.children(".fa").length < 1)
3781
                        {
3782
                            firstA.append( $(icon).css({ float:"right", paddingTop:"4px" }) );
3783
                        }
3784
                    }
3785
3786
                    li.mouseover(function(){
3787
                        ul.css("z-index", zindex).show();
3788
                        zindex += 1;
3789
                    }).mouseleave(function(){
3790
                        ul.hide();
3791
                    });
3792
                });
3793
            }).mouseleave(function(){
3794
                menu.hide();
3795
            }); 
3796
        });       
3797
        
3798
        return tocMenus;
3799
    };
3800
    
3801
    /**
3802
     * 简单地过滤指定的HTML标签
3803
     * Filter custom html tags
3804
     * 
3805
     * @param   {String}   html          要过滤HTML
3806
     * @param   {String}   filters       要过滤的标签
3807
     * @returns {String}   html          返回过滤的HTML
3808
     */
3809
    
3810
    editormd.filterHTMLTags = function(html, filters) {
3811
        
3812
        if (typeof html !== "string") {
3813
            html = new String(html);
3814
        }
3815
            
3816
        if (typeof filters !== "string") {
3817
            return html;
3818
        }
3819
3820
        var expression = filters.split("|");
3821
        var filterTags = expression[0].split(",");
3822
        var attrs      = expression[1];
3823
3824
        for (var i = 0, len = filterTags.length; i < len; i++)
3825
        {
3826
            var tag = filterTags[i];
3827
3828
            html = html.replace(new RegExp("\<\s*" + tag + "\s*([^\>]*)\>([^\>]*)\<\s*\/" + tag + "\s*\>", "igm"), "");
3829
        }
3830
        
3831
        //return html;
3832
3833
        if (typeof attrs !== "undefined")
3834
        {
3835
            var htmlTagRegex = /\<(\w+)\s*([^\>]*)\>([^\>]*)\<\/(\w+)\>/ig;
3836
3837
            if (attrs === "*")
3838
            {
3839
                html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) {
3840
                    return "<" + $2 + ">" + $4 + "</" + $5 + ">";
3841
                });         
3842
            }
3843
            else if (attrs === "on*")
3844
            {
3845
                html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) {
3846
                    var el = $("<" + $2 + ">" + $4 + "</" + $5 + ">");
3847
                    var _attrs = $($1)[0].attributes;
3848
                    var $attrs = {};
3849
                    
3850
                    $.each(_attrs, function(i, e) {
3851
                        if (e.nodeName !== '"') $attrs[e.nodeName] = e.nodeValue;
3852
                    });
3853
                    
3854
                    $.each($attrs, function(i) {                        
3855
                        if (i.indexOf("on") === 0) {
3856
                            delete $attrs[i];
3857
                        }
3858
                    });
3859
                    
3860
                    el.attr($attrs);
3861
                    
3862
                    var text = (typeof el[1] !== "undefined") ? $(el[1]).text() : "";
3863
3864
                    return el[0].outerHTML + text;
3865
                });
3866
            }
3867
            else
3868
            {
3869
                html = html.replace(htmlTagRegex, function($1, $2, $3, $4) {
3870
                    var filterAttrs = attrs.split(",");
3871
                    var el = $($1);
3872
                    el.html($4);
3873
3874
                    $.each(filterAttrs, function(i) {
3875
                        el.attr(filterAttrs[i], null);
3876
                    });
3877
3878
                    return el[0].outerHTML;
3879
                });
3880
            }
3881
        }
3882
        
3883
        return html;
3884
    };
3885
    
3886
    /**
3887
     * 将Markdown文档解析为HTML用于前台显示
3888
     * Parse Markdown to HTML for Font-end preview.
3889
     * 
3890
     * @param   {String}   id            用于显示HTML的对象ID
3891
     * @param   {Object}   [options={}]  配置选项,可选
3892
     * @returns {Object}   div           返回jQuery对象元素
3893
     */
3894
    
3895
    editormd.markdownToHTML = function(id, options) {
3896
        var defaults = {
3897
            gfm                  : true,
3898
            toc                  : true,
3899
            tocm                 : false,
3900
            tocStartLevel        : 1,
3901
            tocTitle             : "目录",
3902
            tocDropdown          : false,
3903
            tocContainer         : "",
3904
            markdown             : "",
3905
            markdownSourceCode   : false,
3906
            htmlDecode           : false,
3907
            autoLoadKaTeX        : true,
3908
            pageBreak            : true,
3909
            atLink               : true,    // for @link
3910
            emailLink            : true,    // for mail address auto link
3911
            tex                  : false,
3912
            taskList             : false,   // Github Flavored Markdown task lists
3913
            emoji                : false,
3914
            flowChart            : false,
3915
            sequenceDiagram      : false,
3916
            previewCodeHighlight : true
3917
        };
3918
        
3919
        editormd.$marked  = marked;
0 ignored issues
show
Bug introduced by
The variable marked seems to be never declared. If this is a global, consider adding a /** global: marked */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
3920
3921
        var div           = $("#" + id);
3922
        var settings      = div.settings = $.extend(true, defaults, options || {});
3923
        var saveTo        = div.find("textarea");
3924
        
3925
        if (saveTo.length < 1)
3926
        {
3927
            div.append("<textarea></textarea>");
3928
            saveTo        = div.find("textarea");
3929
        }        
3930
        
3931
        var markdownDoc   = (settings.markdown === "") ? saveTo.val() : settings.markdown; 
3932
        var markdownToC   = [];
3933
3934
        var rendererOptions = {  
3935
            toc                  : settings.toc,
3936
            tocm                 : settings.tocm,
3937
            tocStartLevel        : settings.tocStartLevel,
3938
            taskList             : settings.taskList,
3939
            emoji                : settings.emoji,
3940
            tex                  : settings.tex,
3941
            pageBreak            : settings.pageBreak,
3942
            atLink               : settings.atLink,           // for @link
3943
            emailLink            : settings.emailLink,        // for mail address auto link
3944
            flowChart            : settings.flowChart,
3945
            sequenceDiagram      : settings.sequenceDiagram,
3946
            previewCodeHighlight : settings.previewCodeHighlight,
3947
        };
3948
3949
        var markedOptions = {
3950
            renderer    : editormd.markedRenderer(markdownToC, rendererOptions),
3951
            gfm         : settings.gfm,
3952
            tables      : true,
3953
            breaks      : true,
3954
            pedantic    : false,
3955
            sanitize    : (settings.htmlDecode) ? false : true, // 是否忽略HTML标签,即是否开启HTML标签解析,为了安全性,默认不开启
3956
            smartLists  : true,
3957
            smartypants : true
3958
        };
3959
        
3960
		markdownDoc = new String(markdownDoc);
3961
        
3962
        var markdownParsed = marked(markdownDoc, markedOptions);
3963
        
3964
        markdownParsed = editormd.filterHTMLTags(markdownParsed, settings.htmlDecode);
3965
        
3966
        if (settings.markdownSourceCode) {
3967
            saveTo.text(markdownDoc);
3968
        } else {
3969
            saveTo.remove();
3970
        }
3971
        
3972
        div.addClass("markdown-body " + this.classPrefix + "html-preview").append(markdownParsed);
3973
        
3974
        var tocContainer = (settings.tocContainer !== "") ? $(settings.tocContainer) : div;
3975
        
3976
        if (settings.tocContainer !== "")
3977
        {
3978
            tocContainer.attr("previewContainer", false);
3979
        }
3980
         
3981
        if (settings.toc) 
3982
        {
3983
            div.tocContainer = this.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel);
3984
            
3985
            if (settings.tocDropdown || div.find("." + this.classPrefix + "toc-menu").length > 0)
3986
            {
3987
                this.tocDropdownMenu(div, settings.tocTitle);
3988
            }
3989
            
3990
            if (settings.tocContainer !== "")
3991
            {
3992
                div.find(".editormd-toc-menu, .editormd-markdown-toc").remove();
3993
            }
3994
        }
3995
            
3996
        if (settings.previewCodeHighlight) 
3997
        {
3998
            div.find("pre").addClass("prettyprint linenums");
3999
            prettyPrint();
4000
        }
4001
        
4002
        if (!editormd.isIE8) 
4003
        {
4004
            if (settings.flowChart) {
4005
                div.find(".flowchart").flowChart(); 
4006
            }
4007
4008
            if (settings.sequenceDiagram) {
4009
                div.find(".sequence-diagram").sequenceDiagram({theme: "simple"});
4010
            }
4011
        }
4012
4013
        if (settings.tex)
4014
        {
4015
            var katexHandle = function() {
4016
                div.find("." + editormd.classNames.tex).each(function(){
4017
                    var tex  = $(this);                    
4018
                    katex.render(tex.html().replace(/&lt;/g, "<").replace(/&gt;/g, ">"), tex[0]);                    
0 ignored issues
show
Bug introduced by
The variable katex seems to be never declared. If this is a global, consider adding a /** global: katex */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
4019
                    tex.find(".katex").css("font-size", "1.6em");
4020
                });
4021
            };
4022
            
4023
            if (settings.autoLoadKaTeX && !editormd.$katex && !editormd.kaTeXLoaded)
4024
            {
4025
                this.loadKaTeX(function() {
4026
                    editormd.$katex      = katex;
0 ignored issues
show
Bug introduced by
The variable katex seems to be never declared. If this is a global, consider adding a /** global: katex */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
4027
                    editormd.kaTeXLoaded = true;
4028
                    katexHandle();
4029
                });
4030
            }
4031
            else
4032
            {
4033
                katexHandle();
4034
            }
4035
        }
4036
        
4037
        div.getMarkdown = function() {            
4038
            return saveTo.val();
4039
        };
4040
        
4041
        return div;
4042
    };
4043
    
4044
    // Editor.md themes, change toolbar themes etc.
4045
    // added @1.5.0
4046
    editormd.themes        = ["default", "dark"];
4047
    
4048
    // Preview area themes
4049
    // added @1.5.0
4050
    editormd.previewThemes = ["default", "dark"];
4051
    
4052
    // CodeMirror / editor area themes
4053
    // @1.5.0 rename -> editorThemes, old version -> themes
4054
    editormd.editorThemes = [
4055
        "default", "3024-day", "3024-night",
4056
        "ambiance", "ambiance-mobile",
4057
        "base16-dark", "base16-light", "blackboard",
4058
        "cobalt",
4059
        "eclipse", "elegant", "erlang-dark",
4060
        "lesser-dark",
4061
        "mbo", "mdn-like", "midnight", "monokai",
4062
        "neat", "neo", "night",
4063
        "paraiso-dark", "paraiso-light", "pastel-on-dark",
4064
        "rubyblue",
4065
        "solarized",
4066
        "the-matrix", "tomorrow-night-eighties", "twilight",
4067
        "vibrant-ink",
4068
        "xq-dark", "xq-light"
4069
    ];
4070
4071
    editormd.loadPlugins = {};
4072
    
4073
    editormd.loadFiles = {
4074
        js     : [],
4075
        css    : [],
4076
        plugin : []
4077
    };
4078
    
4079
    /**
4080
     * 动态加载Editor.md插件,但不立即执行
4081
     * Load editor.md plugins
4082
     * 
4083
     * @param {String}   fileName              插件文件路径
4084
     * @param {Function} [callback=function()] 加载成功后执行的回调函数
4085
     * @param {String}   [into="head"]         嵌入页面的位置
4086
     */
4087
    
4088
    editormd.loadPlugin = function(fileName, callback, into) {
4089
        callback   = callback || function() {};
4090
        
4091
        this.loadScript(fileName, function() {
4092
            editormd.loadFiles.plugin.push(fileName);
4093
            callback();
4094
        }, into);
4095
    };
4096
    
4097
    /**
4098
     * 动态加载CSS文件的方法
4099
     * Load css file method
4100
     * 
4101
     * @param {String}   fileName              CSS文件名
4102
     * @param {Function} [callback=function()] 加载成功后执行的回调函数
4103
     * @param {String}   [into="head"]         嵌入页面的位置
4104
     */
4105
    
4106
    editormd.loadCSS   = function(fileName, callback, into) {
4107
        into       = into     || "head";        
4108
        callback   = callback || function() {};
4109
        
4110
        var css    = document.createElement("link");
4111
        css.type   = "text/css";
4112
        css.rel    = "stylesheet";
4113
        css.onload = css.onreadystatechange = function() {
4114
            editormd.loadFiles.css.push(fileName);
4115
            callback();
4116
        };
4117
4118
        css.href   = fileName + ".css";
4119
4120
        if(into === "head") {
4121
            document.getElementsByTagName("head")[0].appendChild(css);
4122
        } else {
4123
            document.body.appendChild(css);
4124
        }
4125
    };
4126
    
4127
    editormd.isIE    = (navigator.appName == "Microsoft Internet Explorer");
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
4128
    editormd.isIE8   = (editormd.isIE && navigator.appVersion.match(/8./i) == "8.");
4129
4130
    /**
4131
     * 动态加载JS文件的方法
4132
     * Load javascript file method
4133
     * 
4134
     * @param {String}   fileName              JS文件名
4135
     * @param {Function} [callback=function()] 加载成功后执行的回调函数
4136
     * @param {String}   [into="head"]         嵌入页面的位置
4137
     */
4138
4139
    editormd.loadScript = function(fileName, callback, into) {
4140
        
4141
        into          = into     || "head";
4142
        callback      = callback || function() {};
4143
        
4144
        var script    = null; 
4145
        script        = document.createElement("script");
4146
        script.id     = fileName.replace(/[\./]+/g, "-");
4147
        script.type   = "text/javascript";        
4148
        script.src    = fileName + ".js";
4149
        
4150
        if (editormd.isIE8) 
4151
        {            
4152
            script.onreadystatechange = function() {
4153
                if(script.readyState) 
4154
                {
4155
                    if (script.readyState === "loaded" || script.readyState === "complete") 
4156
                    {
4157
                        script.onreadystatechange = null; 
4158
                        editormd.loadFiles.js.push(fileName);
4159
                        callback();
4160
                    }
4161
                } 
4162
            };
4163
        }
4164
        else
4165
        {
4166
            script.onload = function() {
4167
                editormd.loadFiles.js.push(fileName);
4168
                callback();
4169
            };
4170
        }
4171
4172
        if (into === "head") {
4173
            document.getElementsByTagName("head")[0].appendChild(script);
4174
        } else {
4175
            document.body.appendChild(script);
4176
        }
4177
    };
4178
    
4179
    // 使用国外的CDN,加载速度有时会很慢,或者自定义URL
4180
    // You can custom KaTeX load url.
4181
    editormd.katexURL  = {
4182
        css : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min",
4183
        js  : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min"
4184
    };
4185
    
4186
    editormd.kaTeXLoaded = false;
4187
    
4188
    /**
4189
     * 加载KaTeX文件
4190
     * load KaTeX files
4191
     * 
4192
     * @param {Function} [callback=function()]  加载成功后执行的回调函数
4193
     */
4194
    
4195
    editormd.loadKaTeX = function (callback) {
4196
        editormd.loadCSS(editormd.katexURL.css, function(){
4197
            editormd.loadScript(editormd.katexURL.js, callback || function(){});
4198
        });
4199
    };
4200
        
4201
    /**
4202
     * 锁屏
4203
     * lock screen
4204
     * 
4205
     * @param   {Boolean}   lock   Boolean 布尔值,是否锁屏
4206
     * @returns {void}
4207
     */
4208
    
4209
    editormd.lockScreen = function(lock) {
4210
        $("html,body").css("overflow", (lock) ? "hidden" : "");
4211
    };
4212
        
4213
    /**
4214
     * 动态创建对话框
4215
     * Creating custom dialogs
4216
     * 
4217
     * @param   {Object} options 配置项键值对 Key/Value
4218
     * @returns {dialog} 返回创建的dialog的jQuery实例对象
4219
     */
4220
4221
    editormd.createDialog = function(options) {
4222
        var defaults = {
4223
            name : "",
4224
            width : 420,
4225
            height: 240,
4226
            title : "",
4227
            drag  : true,
4228
            closed : true,
4229
            content : "",
4230
            mask : true,
4231
            maskStyle : {
4232
                backgroundColor : "#fff",
4233
                opacity : 0.1
4234
            },
4235
            lockScreen : true,
4236
            footer : true,
4237
            buttons : false
4238
        };
4239
4240
        options          = $.extend(true, defaults, options);
4241
        
4242
        var $this        = this;
4243
        var editor       = this.editor;
4244
        var classPrefix  = editormd.classPrefix;
4245
        var guid         = (new Date()).getTime();
4246
        var dialogName   = ( (options.name === "") ? classPrefix + "dialog-" + guid : options.name);
4247
        var mouseOrTouch = editormd.mouseOrTouch;
4248
4249
        var html         = "<div class=\"" + classPrefix + "dialog " + dialogName + "\">";
4250
4251
        if (options.title !== "")
4252
        {
4253
            html += "<div class=\"" + classPrefix + "dialog-header\"" + ( (options.drag) ? " style=\"cursor: move;\"" : "" ) + ">";
4254
            html += "<strong class=\"" + classPrefix + "dialog-title\">" + options.title + "</strong>";
4255
            html += "</div>";
4256
        }
4257
4258
        if (options.closed)
4259
        {
4260
            html += "<a href=\"javascript:;\" class=\"fa fa-close " + classPrefix + "dialog-close\"></a>";
4261
        }
4262
4263
        html += "<div class=\"" + classPrefix + "dialog-container\">" + options.content;                    
4264
4265
        if (options.footer || typeof options.footer === "string") 
4266
        {
4267
            html += "<div class=\"" + classPrefix + "dialog-footer\">" + ( (typeof options.footer === "boolean") ? "" : options.footer) + "</div>";
4268
        }
4269
4270
        html += "</div>";
4271
4272
        html += "<div class=\"" + classPrefix + "dialog-mask " + classPrefix + "dialog-mask-bg\"></div>";
4273
        html += "<div class=\"" + classPrefix + "dialog-mask " + classPrefix + "dialog-mask-con\"></div>";
4274
        html += "</div>";
4275
4276
        editor.append(html);
4277
4278
        var dialog = editor.find("." + dialogName);
4279
4280
        dialog.lockScreen = function(lock) {
4281
            if (options.lockScreen)
4282
            {                
4283
                $("html,body").css("overflow", (lock) ? "hidden" : "");
4284
                $this.resize();
4285
            }
4286
4287
            return dialog;
4288
        };
4289
4290
        dialog.showMask = function() {
4291
            if (options.mask)
4292
            {
4293
                editor.find("." + classPrefix + "mask").css(options.maskStyle).css("z-index", editormd.dialogZindex - 1).show();
4294
            }
4295
            return dialog;
4296
        };
4297
4298
        dialog.hideMask = function() {
4299
            if (options.mask)
4300
            {
4301
                editor.find("." + classPrefix + "mask").hide();
4302
            }
4303
4304
            return dialog;
4305
        };
4306
4307
        dialog.loading = function(show) {                        
4308
            var loading = dialog.find("." + classPrefix + "dialog-mask");
4309
            loading[(show) ? "show" : "hide"]();
4310
4311
            return dialog;
4312
        };
4313
4314
        dialog.lockScreen(true).showMask();
4315
4316
        dialog.show().css({
4317
            zIndex : editormd.dialogZindex,
4318
            border : (editormd.isIE8) ? "1px solid #ddd" : "",
4319
            width  : (typeof options.width  === "number") ? options.width + "px"  : options.width,
4320
            height : (typeof options.height === "number") ? options.height + "px" : options.height
4321
        });
4322
4323
        var dialogPosition = function(){
4324
            dialog.css({
4325
                top    : ($(window).height() - dialog.height()) / 2 + "px",
4326
                left   : ($(window).width() - dialog.width()) / 2 + "px"
4327
            });
4328
        };
4329
4330
        dialogPosition();
4331
4332
        $(window).resize(dialogPosition);
4333
4334
        dialog.children("." + classPrefix + "dialog-close").bind(mouseOrTouch("click", "touchend"), function() {
4335
            dialog.hide().lockScreen(false).hideMask();
4336
        });
4337
4338
        if (typeof options.buttons === "object")
4339
        {
4340
            var footer = dialog.footer = dialog.find("." + classPrefix + "dialog-footer");
4341
4342
            for (var key in options.buttons)
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
4343
            {
4344
                var btn = options.buttons[key];
4345
                var btnClassName = classPrefix + key + "-btn";
4346
4347
                footer.append("<button class=\"" + classPrefix + "btn " + btnClassName + "\">" + btn[0] + "</button>");
4348
                btn[1] = $.proxy(btn[1], dialog);
4349
                footer.children("." + btnClassName).bind(mouseOrTouch("click", "touchend"), btn[1]);
4350
            }
4351
        }
4352
4353
        if (options.title !== "" && options.drag)
4354
        {                        
4355
            var posX, posY;
4356
            var dialogHeader = dialog.children("." + classPrefix + "dialog-header");
4357
4358
            if (!options.mask) {
4359
                dialogHeader.bind(mouseOrTouch("click", "touchend"), function(){
4360
                    editormd.dialogZindex += 2;
4361
                    dialog.css("z-index", editormd.dialogZindex);
4362
                });
4363
            }
4364
4365
            dialogHeader.mousedown(function(e) {
4366
                e = e || window.event;  //IE
4367
                posX = e.clientX - parseInt(dialog[0].style.left);
4368
                posY = e.clientY - parseInt(dialog[0].style.top);
4369
4370
                document.onmousemove = moveAction;                   
4371
            });
4372
4373
            var userCanSelect = function (obj) {
4374
                obj.removeClass(classPrefix + "user-unselect").off("selectstart");
4375
            };
4376
4377
            var userUnselect = function (obj) {
4378
                obj.addClass(classPrefix + "user-unselect").on("selectstart", function(event) { // selectstart for IE                        
0 ignored issues
show
Unused Code introduced by
The parameter event is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
4379
                    return false;
4380
                });
4381
            };
4382
4383
            var moveAction = function (e) {
4384
                e = e || window.event;  //IE
4385
4386
                var left, top, nowLeft = parseInt(dialog[0].style.left), nowTop = parseInt(dialog[0].style.top);
4387
4388
                if( nowLeft >= 0 ) {
4389
                    if( nowLeft + dialog.width() <= $(window).width()) {
4390
                        left = e.clientX - posX;
4391
                    } else {	
4392
                        left = $(window).width() - dialog.width();
4393
                        document.onmousemove = null;
4394
                    }
4395
                } else {
4396
                    left = 0;
4397
                    document.onmousemove = null;
4398
                }
4399
4400
                if( nowTop >= 0 ) {
4401
                    top = e.clientY - posY;
4402
                } else {
4403
                    top = 0;
4404
                    document.onmousemove = null;
4405
                }
4406
4407
4408
                document.onselectstart = function() {
4409
                    return false;
4410
                };
4411
4412
                userUnselect($("body"));
4413
                userUnselect(dialog);
4414
                dialog[0].style.left = left + "px";
4415
                dialog[0].style.top  = top + "px";
4416
            };
4417
4418
            document.onmouseup = function() {                            
4419
                userCanSelect($("body"));
4420
                userCanSelect(dialog);
4421
4422
                document.onselectstart = null;         
4423
                document.onmousemove = null;
4424
            };
4425
4426
            dialogHeader.touchDraggable = function() {
4427
                var offset = null;
4428
                var start  = function(e) {
4429
                    var orig = e.originalEvent; 
4430
                    var pos  = $(this).parent().position();
4431
4432
                    offset = {
4433
                        x : orig.changedTouches[0].pageX - pos.left,
4434
                        y : orig.changedTouches[0].pageY - pos.top
4435
                    };
4436
                };
4437
4438
                var move = function(e) {
4439
                    e.preventDefault();
4440
                    var orig = e.originalEvent;
4441
4442
                    $(this).parent().css({
4443
                        top  : orig.changedTouches[0].pageY - offset.y,
4444
                        left : orig.changedTouches[0].pageX - offset.x
4445
                    });
4446
                };
4447
4448
                this.bind("touchstart", start).bind("touchmove", move);
4449
            };
4450
4451
            dialogHeader.touchDraggable();
4452
        }
4453
4454
        editormd.dialogZindex += 2;
4455
4456
        return dialog;
4457
    };
4458
    
4459
    /**
4460
     * 鼠标和触摸事件的判断/选择方法
4461
     * MouseEvent or TouchEvent type switch
4462
     * 
4463
     * @param   {String} [mouseEventType="click"]    供选择的鼠标事件
4464
     * @param   {String} [touchEventType="touchend"] 供选择的触摸事件
4465
     * @returns {String} EventType                   返回事件类型名称
4466
     */
4467
    
4468
    editormd.mouseOrTouch = function(mouseEventType, touchEventType) {
4469
        mouseEventType = mouseEventType || "click";
4470
        touchEventType = touchEventType || "touchend";
4471
        
4472
        var eventType  = mouseEventType;
4473
4474
        try {
4475
            document.createEvent("TouchEvent");
4476
            eventType = touchEventType;
4477
        } catch(e) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
4478
4479
        return eventType;
4480
    };
4481
    
4482
    /**
4483
     * 日期时间的格式化方法
4484
     * Datetime format method
4485
     * 
4486
     * @param   {String}   [format=""]  日期时间的格式,类似PHP的格式
4487
     * @returns {String}   datefmt      返回格式化后的日期时间字符串
4488
     */
4489
    
4490
    editormd.dateFormat = function(format) {                
4491
        format      = format || "";
4492
4493
        var addZero = function(d) {
4494
            return (d < 10) ? "0" + d : d;
4495
        };
4496
4497
        var date    = new Date(); 
4498
        var year    = date.getFullYear();
4499
        var year2   = year.toString().slice(2, 4);
4500
        var month   = addZero(date.getMonth() + 1);
4501
        var day     = addZero(date.getDate());
4502
        var weekDay = date.getDay();
4503
        var hour    = addZero(date.getHours());
4504
        var min     = addZero(date.getMinutes());
4505
        var second  = addZero(date.getSeconds());
4506
        var ms      = addZero(date.getMilliseconds()); 
4507
        var datefmt = "";
4508
4509
        var ymd     = year2 + "-" + month + "-" + day;
4510
        var fymd    = year  + "-" + month + "-" + day;
4511
        var hms     = hour  + ":" + min   + ":" + second;
4512
4513
        switch (format) 
4514
        {
4515
            case "UNIX Time" :
4516
                    datefmt = date.getTime();
4517
                break;
4518
4519
            case "UTC" :
4520
                    datefmt = date.toUTCString();
4521
                break;	
4522
4523
            case "yy" :
4524
                    datefmt = year2;
4525
                break;	
4526
4527
            case "year" :
4528
            case "yyyy" :
4529
                    datefmt = year;
4530
                break;
4531
4532
            case "month" :
4533
            case "mm" :
4534
                    datefmt = month;
4535
                break;                        
4536
4537
            case "cn-week-day" :
4538
            case "cn-wd" :
4539
                    var cnWeekDays = ["日", "一", "二", "三", "四", "五", "六"];
4540
                    datefmt = "星期" + cnWeekDays[weekDay];
4541
                break;
4542
4543
            case "week-day" :
4544
            case "wd" :
4545
                    var weekDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
4546
                    datefmt = weekDays[weekDay];
4547
                break;
4548
4549
            case "day" :
4550
            case "dd" :
4551
                    datefmt = day;
4552
                break;
4553
4554
            case "hour" :
4555
            case "hh" :
4556
                    datefmt = hour;
4557
                break;
4558
4559
            case "min" :
4560
            case "ii" :
4561
                    datefmt = min;
4562
                break;
4563
4564
            case "second" :
4565
            case "ss" :
4566
                    datefmt = second;
4567
                break;
4568
4569
            case "ms" :
4570
                    datefmt = ms;
4571
                break;
4572
4573
            case "yy-mm-dd" :
4574
                    datefmt = ymd;
4575
                break;
4576
4577
            case "yyyy-mm-dd" :
4578
                    datefmt = fymd;
4579
                break;
4580
4581
            case "yyyy-mm-dd h:i:s ms" :
4582
            case "full + ms" : 
4583
                    datefmt = fymd + " " + hms + " " + ms;
4584
                break;
4585
4586
            case "full" :
4587
            case "yyyy-mm-dd h:i:s" :
4588
                default:
4589
                    datefmt = fymd + " " + hms;
4590
                break;
4591
        }
4592
4593
        return datefmt;
4594
    };
4595
4596
    return editormd;
4597
4598
}));
4599