55 *
66 * @package EditorMD
77 * @author DT27
8- * @version 1.4.0
8+ * @version 1.5.0
9+ * @dependence 1.2.0-*
910 * @link https://dt27.cn/php/editormd-for-typecho/
1011 */
1112class EditorMD_Plugin implements Typecho_Plugin_Interface
@@ -111,16 +112,28 @@ public static function Editor()
111112 $ options = Helper::options ();
112113 $ cssUrl = $ options ->pluginUrl .'/EditorMD/css/editormd.min.css ' ;
113114 $ jsUrl = $ options ->pluginUrl .'/EditorMD/js/editormd.min.js ' ;
115+ $ markedUrl = $ options ->pluginUrl .'/EditorMD/lib/marked.min.js ' ;
116+ $ purifyUrl = $ options ->pluginUrl .'/EditorMD/lib/purify.min.js ' ;
117+ $ safeMarkedUrl = $ options ->pluginUrl .'/EditorMD/js/safe-marked.js ' ;
114118 $ editormd = Typecho_Widget::widget ('Widget_Options ' )->plugin ('EditorMD ' );
115119 ?>
116120 <link rel="stylesheet" href="<?php echo $ cssUrl ; ?> " />
117121 <script>
118122 var emojiPath = '<?php echo $ options ->pluginUrl ; ?> ';
119123 var uploadURL = '<?php Helper::security ()->index ('/action/upload?cid=CID ' ); ?> ';
120124 </script>
125+ <script type="text/javascript" src="<?php echo $ markedUrl ; ?> "></script>
126+ <script type="text/javascript" src="<?php echo $ purifyUrl ; ?> "></script>
121127 <script type="text/javascript" src="<?php echo $ jsUrl ; ?> "></script>
128+ <script type="text/javascript" src="<?php echo $ safeMarkedUrl ; ?> "></script>
122129 <script>
123130 $(document).ready(function() {
131+ if (!window.EditorMDSanitizeMarked
132+ || !window.EditorMDSanitizeMarked()
133+ || !window.EditorMDGuardMarkedLoader
134+ || !window.EditorMDGuardMarkedLoader()) {
135+ return;
136+ }
124137
125138 var textarea = $('#text').parent("p");
126139 var isMarkdown = $('[name=markdown]').val()?1:0;
@@ -145,7 +158,7 @@ public static function Editor()
145158 height: 640,
146159 path: '<?php echo $ options ->pluginUrl ?> /EditorMD/lib/',
147160 toolbarAutoFixed: false,
148- htmlDecode: true ,
161+ htmlDecode: false ,
149162 emoji: <?php echo $ editormd ->emoji ? 'true ' : 'false ' ; ?> ,
150163 tex: <?php echo $ editormd ->isTex ? 'true ' : 'false ' ; ?> ,
151164 toc: <?php echo $ editormd ->isToc ? 'true ' : 'false ' ; ?> ,
@@ -221,87 +234,109 @@ public static function Editor()
221234
222235 // 优化图片及文件附件插入 Thanks to Markxuxiao
223236 Typecho.insertFileToEditor = function (file, url, isImage) {
224- html = isImage ? ''
237+ var html = isImage ? ''
225238 : '[' + file + '](' + url + ')';
226239 postEditormd.insertValue(html);
227240 };
228241
229242 // 支持黏贴图片直接上传
230- $(document).on('paste', function(event) {
231- event = event.originalEvent;
232- var cbd = event.clipboardData;
233- var ua = window.navigator.userAgent;
234- if (!(event.clipboardData && event.clipboardData.items)) {
243+ var pasteUploadIndex = 0;
244+ var uploadClipboardImage = function(blob) {
245+ if (!blob) {
235246 return;
236247 }
237248
238- if (cbd.items && cbd.items.length === 2 && cbd.items[0].kind === "string" && cbd.items[1].kind === "file" &&
239- cbd.types && cbd.types.length === 2 && cbd.types[0] === "text/plain" && cbd.types[1] === "Files" &&
240- ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49){
249+ var extensions = {
250+ 'image/jpeg': 'jpg',
251+ 'image/pjpeg': 'jpg',
252+ 'image/png': 'png',
253+ 'image/gif': 'gif',
254+ 'image/webp': 'webp'
255+ };
256+ var ext = extensions[blob.type];
257+ if (!ext || !blob.size) {
241258 return;
242259 }
243260
244- var itemLength = cbd.items.length;
261+ var uploadId = pasteUploadIndex++;
262+ var formData = new FormData();
263+ formData.append(
264+ 'blob',
265+ blob,
266+ new Date().getTime() + '-' + uploadId + '.' + ext
267+ );
245268
246- if (itemLength == 0) {
247- return ;
248- }
269+ var uploadingText = '![图片上传中(' + uploadId + ')...]';
270+ var uploadFailText = '![图片上传失败(' + uploadId + ')]' ;
271+ postEditormd.insertValue(uploadingText);
249272
250- if (itemLength == 1 && cbd.items[0].kind == 'string') {
251- return;
252- }
273+ $.ajax({
274+ method: 'post',
275+ url: uploadURL.replace(
276+ 'CID',
277+ encodeURIComponent($('input[name="cid"]').val() || '')
278+ ),
279+ data: formData,
280+ dataType: 'json',
281+ contentType: false,
282+ processData: false,
283+ success: function(data) {
284+ if (data && data[0]) {
285+ var attachment = data[1] || {};
286+ var attachmentId = parseInt(attachment.cid, 10);
287+ var imageUrl = attachment.url || data[0];
253288
254- if ((itemLength == 1 && cbd.items[0].kind == 'file')
255- || itemLength > 1
256- ) {
257- for (var i = 0; i < cbd.items.length; i++) {
258- var item = cbd.items[i];
289+ postEditormd.setValue(
290+ postEditormd.getValue().replace(
291+ uploadingText,
292+ ''
293+ )
294+ );
259295
260- if(item.kind == "file") {
261- var blob = item.getAsFile();
262- if (blob.size === 0) {
263- return;
296+ if (
297+ attachmentId > 0
298+ && !$('input[name="attachment[]"][value="'
299+ + attachmentId + '"]').length
300+ ) {
301+ $('<input>', {
302+ type: 'hidden',
303+ name: 'attachment[]',
304+ value: attachmentId
305+ }).appendTo($('#text').closest('form'));
264306 }
265- var ext = 'jpg';
266- switch(blob.type) {
267- case 'image/jpeg':
268- case 'image/pjpeg':
269- ext = 'jpg';
270- break;
271- case 'image/png':
272- ext = 'png';
273- break;
274- case 'image/gif':
275- ext = 'gif';
276- break;
277- }
278- var formData = new FormData();
279- formData.append('blob', blob, Math.floor(new Date().getTime() / 1000) + '.' + ext);
280- var uploadingText = '![图片上传中(' + i + ')...]';
281- var uploadFailText = '![图片上传失败(' + i + ')]'
282- postEditormd.insertValue(uploadingText);
283- $.ajax({
284- method: 'post',
285- url: uploadURL.replace('CID', $('input[name="cid"]').val()),
286- data: formData,
287- contentType: false,
288- processData: false,
289- success: function(data) {
290- if (data[0]) {
291- postEditormd.setValue(postEditormd.getValue().replace(uploadingText, ''));
292- } else {
293- postEditormd.setValue(postEditormd.getValue().replace(uploadingText, uploadFailText));
294- }
295- },
296- error: function() {
297- postEditormd.setValue(postEditormd.getValue().replace(uploadingText, uploadFailText));
298- }
299- });
307+ } else {
308+ postEditormd.setValue(
309+ postEditormd.getValue().replace(
310+ uploadingText,
311+ uploadFailText
312+ )
313+ );
300314 }
315+ },
316+ error: function() {
317+ postEditormd.setValue(
318+ postEditormd.getValue().replace(
319+ uploadingText,
320+ uploadFailText
321+ )
322+ );
301323 }
324+ });
325+ };
302326
327+ $(document).on('paste', function(event) {
328+ event = event.originalEvent;
329+ var cbd = event.clipboardData;
330+ if (!(event.clipboardData && event.clipboardData.items)) {
331+ return;
303332 }
304333
334+ for (var i = 0; i < cbd.items.length; i++) {
335+ var item = cbd.items[i];
336+ if (item.kind === 'file') {
337+ uploadClipboardImage(item.getAsFile());
338+ }
339+ }
305340 });
306341
307342 });
@@ -311,20 +346,23 @@ public static function Editor()
311346 /**
312347 * emoji 解析器
313348 */
314- public static function footerJS ($ conent )
349+ public static function footerJS ($ content )
315350 {
316351 $ options = Helper::options ();
317352 $ pluginUrl = $ options ->pluginUrl .'/EditorMD ' ;
318353 $ editormd = Typecho_Widget::widget ('Widget_Options ' )->plugin ('EditorMD ' );
354+ $ needsMarkdown = $ editormd ->isActive == 1 && self ::$ count > 0 ;
319355 if ($ editormd ->emoji ){
320356?>
321357<link rel="stylesheet" href="<?php echo $ pluginUrl ; ?> /css/emojify.min.css" />
322- <?php }if ($ editormd ->emoji || ( $ editormd -> isActive == 1 && $ conent -> isMarkdown ) ){ ?>
358+ <?php }if ($ editormd ->emoji || $ needsMarkdown ){ ?>
323359<script type="text/javascript">
324360 window.jQuery || document.write(unescape('%3Cscript%20type%3D%22text/javascript%22%20src%3D%22<?php echo $ pluginUrl ; ?> /lib/jquery.min.js%22%3E%3C/script%3E'));
325361</script>
326- <?php }if ($ editormd -> isActive == 1 && $ conent -> isMarkdown ){ ?>
362+ <?php }if ($ needsMarkdown ){ ?>
327363<script src="<?php echo $ pluginUrl ; ?> /lib/marked.min.js"></script>
364+ <script src="<?php echo $ pluginUrl ; ?> /lib/purify.min.js"></script>
365+ <script src="<?php echo $ pluginUrl ; ?> /js/safe-marked.js"></script>
328366<script src="<?php echo $ pluginUrl ; ?> /js/editormd.min.js"></script>
329367<?php if ($ editormd ->isSeq == 1 ||$ editormd ->isFlow == 1 ){ ?>
330368<script src="<?php echo $ pluginUrl ; ?> /lib/raphael.min.js"></script>
@@ -336,20 +374,31 @@ public static function footerJS($conent)
336374<script src="<?php echo $ pluginUrl ; ?> /lib/sequence-diagram.min.js"></script>
337375<?php }}if ($ editormd ->emoji ){ ?>
338376<script src="<?php echo $ pluginUrl ; ?> /js/emojify.min.js"></script>
339- <?php }if ($ editormd ->emoji ||( $ editormd -> isActive == 1 && $ conent -> isMarkdown ) ){?>
377+ <?php }if ($ editormd ->emoji ||$ needsMarkdown ){?>
340378<script type="text/javascript">
341379$(function() {
342- <?php if ($ editormd ->isActive == 1 && $ conent ->isMarkdown ){ ?>
380+ <?php if ($ needsMarkdown ){ ?>
381+ if (!window.EditorMDSanitizeMarked
382+ || !window.EditorMDSanitizeMarked()) {
383+ return;
384+ }
385+
343386 var parseMarkdown = function () {
344387 var markdowns = document.getElementsByClassName("md_content");
345388 $(markdowns).each(function () {
346- var markdown = $(this).children("#append-test").text();
389+ var source = $(this).children(".editormd-source");
390+ if (!source.length) {
391+ return;
392+ }
393+ var markdown = source.map(function () {
394+ return $(this).val();
395+ }).get().join('\n\n');
347396 //$('#md_content_'+i).text('');
348397 var editormdView;
349398 editormdView = editormd.markdownToHTML($(this).attr("id"), {
350399 markdown: markdown,//+ "\r\n" + $("#append-test").text(),
351400 toolbarAutoFixed: false,
352- htmlDecode: true ,
401+ htmlDecode: false ,
353402 emoji: <?php echo $ editormd ->emoji ? 'true ' : 'false ' ; ?> ,
354403 tex: <?php echo $ editormd ->isTex ? 'true ' : 'false ' ; ?> ,
355404 toc: <?php echo $ editormd ->isToc ? 'true ' : 'false ' ; ?> ,
@@ -362,7 +411,7 @@ public static function footerJS($conent)
362411 };
363412 parseMarkdown();
364413 $(document).on('pjax:complete', function () {
365- parseMarkdown()
414+ parseMarkdown();
366415 });
367416<?php }if ($ editormd ->emoji ){ ?>
368417 emojify.setConfig({
@@ -397,20 +446,83 @@ public static function footerJS($conent)
397446<?php
398447}
399448 }
400- public static function content ($ text , $ conent ){
401- self ::$ count ++;
449+ public static function content ($ text , $ content ){
402450 $ editormd = Typecho_Widget::widget ('Widget_Options ' )->plugin ('EditorMD ' );
403- $ text = $ conent ->isMarkdown ? ($ editormd ->isActive == 1 ?$ text :$ conent ->markdown ($ text ))
404- : $ conent ->autoP ($ text );
405- if ($ editormd ->isActive == 1 && $ conent ->isMarkdown )
406- return '<div id="md_content_ ' .self ::$ count .'" class="md_content" style="min-height: 50px;"><textarea id="append-test" style="display:none;"> ' .$ text .'</textarea></div> ' ;
407- else
451+ if ($ content ->isMarkdown ) {
452+ return $ editormd ->isActive == 1
453+ ? self ::renderMarkdown ($ text )
454+ : self ::markdown ($ text );
455+ }
456+
457+ return self ::autoP ($ text );
458+ }
459+
460+ public static function excerpt ($ text , $ content ){
461+ if (self ::isTypecho13 ()) {
408462 return $ text ;
463+ }
464+
465+ return self ::content ($ text , $ content );
409466 }
410- public static function excerpt ($ text , $ conent ){
467+
468+ private static function renderMarkdown ($ text )
469+ {
470+ $ segments = explode ('<!--more--> ' , (string ) $ text );
471+ foreach ($ segments as &$ segment ) {
472+ $ segment = '<textarea class="editormd-source" style="display:none;"> '
473+ . htmlspecialchars ($ segment , ENT_NOQUOTES | ENT_SUBSTITUTE , 'UTF-8 ' )
474+ . '</textarea> ' ;
475+ }
476+ unset($ segment );
477+
411478 self ::$ count ++;
412- $ text = $ conent ->isMarkdown ? $ conent ->markdown ($ text )
413- : $ conent ->autoP ($ text );
414- return $ text ;
479+ return '<div id="md_content_ ' . self ::$ count
480+ . '" class="md_content" style="min-height: 50px;"> '
481+ . implode ('<!--more--> ' , $ segments )
482+ . '</div> ' ;
483+ }
484+
485+ private static function markdown ($ text )
486+ {
487+ $ handle = Typecho_Plugin::factory ('Widget_Abstract_Contents ' );
488+ if (self ::isTypecho13 ()) {
489+ $ result = $ handle ->trigger ($ parsed )->filter ('markdown ' , (string ) $ text );
490+ } else {
491+ $ result = $ handle ->trigger ($ parsed )->markdown ((string ) $ text );
492+ }
493+
494+ return $ parsed ? $ result : Markdown::convert ((string ) $ text );
495+ }
496+
497+ private static function autoP ($ text )
498+ {
499+ if (!$ text ) {
500+ return $ text ;
501+ }
502+
503+ $ handle = Typecho_Plugin::factory ('Widget_Abstract_Contents ' );
504+ if (self ::isTypecho13 ()) {
505+ $ result = $ handle ->trigger ($ parsed )->filter ('autoP ' , (string ) $ text );
506+ } else {
507+ $ result = $ handle ->trigger ($ parsed )->autoP ((string ) $ text );
508+ }
509+ if ($ parsed ) {
510+ return $ result ;
511+ }
512+
513+ static $ parser ;
514+ if (!$ parser ) {
515+ $ parser = new AutoP ();
516+ }
517+
518+ return $ parser ->parse ((string ) $ text );
519+ }
520+
521+ private static function isTypecho13 ()
522+ {
523+ return method_exists (
524+ Typecho_Plugin::factory ('Widget_Abstract_Contents ' ),
525+ 'filter '
526+ );
415527 }
416528}
0 commit comments