Просмотр исходного кода

fix #22 Imprv: detach code blocks before preProcess

add DetachCodeBlock.js
Yuki Takei 9 лет назад
Родитель
Сommit
2c521dd850

+ 1 - 1
package.json

@@ -63,6 +63,7 @@
     "consolidate": "~0.14.0",
     "cookie-parser": "~1.3.4",
     "copy-webpack-plugin": "^4.0.0",
+    "crowi-pluginkit": "^1.1.0",
     "csrf": "~3.0.3",
     "css-loader": "^0.27.3",
     "debug": "~2.6.0",
@@ -118,7 +119,6 @@
   "devDependencies": {
     "chai": "^3.5.0",
     "concurrently": "^3.4.0",
-    "crowi-pluginkit": "^1.1.0",
     "easy-livereload": "^1.2.0",
     "mocha": "^3.2.0",
     "node-dev": "^3.1.3",

+ 13 - 5
resource/js/legacy/crowi-form.js

@@ -75,17 +75,25 @@ $(function() {
 
   function renderPreview() {
     var markdown = $('#form-body').val();
-    var parsedHTML = crowiRenderer.render(markdown, rendererOptions);
 
     // create context object
     var context = {
       markdown,
-      parsedHTML,
       currentPagePath: decodeURIComponent(location.pathname)
     };
 
-    // process interceptors for pre rendering
-    crowi.interceptorManager.process('preRenderPreview', context)   // process with the context
+    crowi.interceptorManager.process('preRenderPreview', context)
+      .then(() => crowi.interceptorManager.process('prePreProcess', context))
+      .then(() => {
+        context.markdown = crowiRenderer.preProcess(context.markdown);
+      })
+      .then(() => crowi.interceptorManager.process('postPreProcess', context))
+      .then(() => {
+        var parsedHTML = crowiRenderer.render(context.markdown, rendererOptions);
+        context['parsedHTML'] = parsedHTML;
+      })
+      .then(() => crowi.interceptorManager.process('postRenderPreview', context))
+      .then(() => crowi.interceptorManager.process('preRenderPreviewHtml', context))
       // render HTML with jQuery
       .then(() => {
         $('#preview-body').html(context.parsedHTML);
@@ -94,7 +102,7 @@ $(function() {
       // process interceptors for post rendering
       .then((bodyElement) => {
         context = Object.assign(context, {bodyElement})
-        return crowi.interceptorManager.process('postRenderPreview', context);
+        return crowi.interceptorManager.process('postRenderPreviewHtml', context);
       });
   }
 

+ 14 - 5
resource/js/legacy/crowi.js

@@ -426,17 +426,26 @@ $(function() {
     var $rawTextOriginal = $('#raw-text-original');
     if ($rawTextOriginal.length > 0) {
       var markdown = Crowi.unescape($('#raw-text-original').html());
-      var parsedHTML = crowiRenderer.render(markdown, rendererOptions);
 
       // create context object
       var context = {
         markdown,
-        parsedHTML,
         currentPagePath: decodeURIComponent(location.pathname)
       };
 
-      // process interceptors for pre rendering
-      crowi.interceptorManager.process('preRender', context)   // process with the context
+      crowi.interceptorManager.process('preRender', context)
+        .then(() => crowi.interceptorManager.process('prePreProcess', context))
+        .then(() => {
+          context.markdown = crowiRenderer.preProcess(context.markdown);
+        })
+        .then(() => crowi.interceptorManager.process('postPreProcess', context))
+        .then(() => {
+          var parsedHTML = crowiRenderer.parseMarkdown(context.markdown, rendererOptions);
+          context.parsedHTML = parsedHTML;
+          Promise.resolve(context);
+        })
+        .then(() => crowi.interceptorManager.process('postRender', context))
+        .then(() => crowi.interceptorManager.process('preRenderHtml', context))
         // render HTML with jQuery
         .then(() => {
           $('#revision-body-content').html(context.parsedHTML);
@@ -458,7 +467,7 @@ $(function() {
         // process interceptors for post rendering
         .then((bodyElement) => {
           context = Object.assign(context, {bodyElement})
-          return crowi.interceptorManager.process('postRender', context);
+          return crowi.interceptorManager.process('postRenderHtml', context);
         });
 
 

+ 8 - 0
resource/js/util/Crowi.js

@@ -4,6 +4,10 @@
 
 import axios from 'axios'
 import InterceptorManager from '../../../lib/util/interceptorManager';
+import {
+  DetachCodeBlockInterceptor,
+  RestoreCodeBlockInterceptor,
+} from './Interceptor/DetachCodeBlock';
 
 export default class Crowi {
   constructor(context, window) {
@@ -19,6 +23,10 @@ export default class Crowi {
     this.apiRequest = this.apiRequest.bind(this);
 
     this.interceptorManager = new InterceptorManager();
+    this.interceptorManager.addInterceptors([
+      new DetachCodeBlockInterceptor(this),
+      new RestoreCodeBlockInterceptor(this),
+    ]);
 
     // FIXME
     this.me = context.me;

+ 0 - 1
resource/js/util/CrowiRenderer.js

@@ -131,7 +131,6 @@ export default class CrowiRenderer {
   render(markdown, rendererOptions) {
     let html = '';
 
-    markdown = this.preProcess(markdown);
     html = this.parseMarkdown(markdown, rendererOptions.marked || {});
 
     return html;

+ 116 - 0
resource/js/util/Interceptor/DetachCodeBlock.js

@@ -0,0 +1,116 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+import { BasicInterceptor } from 'crowi-pluginkit';
+
+
+class DetachCodeBlockUtil {
+  static createReplaceStr(replaceId) {
+    return `<pre>${replaceId}</pre>`;
+  }
+}
+
+/**
+ * The interceptor that detach code blocks
+ */
+export class DetachCodeBlockInterceptor extends BasicInterceptor {
+
+  constructor(crowi) {
+    super();
+    this.crowi = crowi;
+    this.crowiForJquery = crowi.getCrowiForJquery();
+  }
+
+  /**
+   * @inheritdoc
+   */
+  isInterceptWhen(contextName) {
+    return (
+      contextName === 'prePreProcess'
+    );
+  }
+
+  /**
+   * @inheritdoc
+   */
+  process(contextName, ...args) {
+    const context = Object.assign(args[0]);   // clone
+    const markdown = context.markdown;
+    const currentPagePath = context.currentPagePath;
+
+    context.dcbContextMap = {};
+
+    // see: https://regex101.com/r/8PAEcC/1
+    context.markdown = markdown.replace(/```(.|[\r\n])*?```/gm, (all) => {
+      // create ID
+      const replaceId = 'dcb-' + this.createRandomStr(8);
+
+      // register to context
+      let dcbContext = {};
+      dcbContext.content = all;
+      dcbContext.substituteContent = DetachCodeBlockUtil.createReplaceStr(replaceId);
+      context.dcbContextMap[replaceId] = dcbContext;
+
+      // return substituteContent
+      return dcbContext.substituteContent;
+    });
+
+    // resolve
+    return Promise.resolve(context);
+  }
+
+  /**
+   * @see http://qiita.com/ryounagaoka/items/4736c225bdd86a74d59c
+   *
+   * @param {number} length
+   * @return random strings
+   */
+  createRandomStr(length) {
+    const bag = "abcdefghijklmnopqrstuvwxyz0123456789";
+    let generated = "";
+    for (var i = 0; i < length; i++) {
+      generated += bag[Math.floor(Math.random() * bag.length)];
+    }
+    return generated;
+  }
+}
+
+
+/**
+ * The interceptor that restore detached code blocks
+ */
+export class RestoreCodeBlockInterceptor extends BasicInterceptor {
+
+  constructor(crowi) {
+    super();
+    this.crowi = crowi;
+    this.crowiForJquery = crowi.getCrowiForJquery();
+  }
+
+  /**
+   * @inheritdoc
+   */
+  isInterceptWhen(contextName) {
+    return (
+      contextName === 'postPreProcess'
+    );
+  }
+
+  /**
+   * @inheritdoc
+   */
+  process(contextName, ...args) {
+    const context = Object.assign(args[0]);   // clone
+
+    // forEach keys of dcbContextMap
+    Object.keys(context.dcbContextMap).forEach((replaceId) => {
+      // get context object from context
+      let dcbContext = context.dcbContextMap[replaceId];
+
+      context.markdown = context.markdown.replace(dcbContext.substituteContent, dcbContext.content);
+    });
+
+    // resolve
+    return Promise.resolve(context);
+  }
+}