Yuki Takei 8 лет назад
Родитель
Сommit
cef819b7b6
3 измененных файлов с 55 добавлено и 27 удалено
  1. 2 0
      package.json
  2. 45 27
      resource/js/components/PageEditor/Editor.js
  3. 8 0
      yarn.lock

+ 2 - 0
package.json

@@ -97,6 +97,7 @@
     "jquery-slimscroll": "^1.3.8",
     "jquery-ui": "^1.12.1",
     "jquery.cookie": "~1.4.1",
+    "load-css-file": "^1.0.0",
     "markdown-it": "^8.4.0",
     "markdown-it-emoji": "^1.4.0",
     "markdown-it-footnote": "^3.0.1",
@@ -136,6 +137,7 @@
     "reveal.js": "^3.5.0",
     "rimraf": "^2.6.1",
     "sass-loader": "^7.0.1",
+    "simple-load-script": "^1.0.2",
     "slack-node": "^0.1.8",
     "socket.io": "^2.0.3",
     "socket.io-client": "^2.0.3",

+ 45 - 27
resource/js/components/PageEditor/Editor.js

@@ -1,6 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
+import urljoin from 'url-join';
+const loadScripts = require('simple-load-script').all;
+const loadCss = require('load-css-file');
+
 import * as codemirror from 'codemirror';
 
 import { UnControlled as ReactCodeMirror } from 'react-codemirror2';
@@ -23,9 +27,6 @@ require('codemirror/addon/fold/markdown-fold');
 require('codemirror/addon/fold/brace-fold');
 require('codemirror/mode/gfm/gfm');
 
-require('codemirror/addon/dialog/dialog');
-require('codemirror/addon/dialog/dialog.css');
-
 require('codemirror/theme/elegant.css');
 require('codemirror/theme/neo.css');
 require('codemirror/theme/mdn-like.css');
@@ -49,6 +50,8 @@ export default class Editor extends React.Component {
   constructor(props) {
     super(props);
 
+    this.cmCdnRoot = 'https://cdn.jsdelivr.net/npm/codemirror@5.37.0';
+
     this.interceptorManager = new InterceptorManager();
     this.interceptorManager.addInterceptors([
       new MarkdownListInterceptor(),
@@ -80,22 +83,6 @@ export default class Editor extends React.Component {
     this.renderOverlay = this.renderOverlay.bind(this);
   }
 
-  // TODO use jsdom
-  fetchJsFromCDN(src, externals) {
-    return new Promise((resolve, reject) => {
-      const script = document.createElement('script');
-      script.setAttribute('src', src);
-      script.addEventListener('load', () => {
-        resolve(externals.map(key => {
-          const ext = window[key]
-          typeof ext === 'undefined' && console.warn(`No external named '${key}' in window`);
-          return ext;
-        }));
-      });
-      script.addEventListener('error', reject);
-      document.body.appendChild(script);
-    });
-  }
 
   componentDidMount() {
     // initialize caret line
@@ -103,21 +90,24 @@ export default class Editor extends React.Component {
     // set save handler
     codemirror.commands.save = this.dispatchSave;
 
-    // FIXME debug code -- 2018.05.07 Yuki Takei
-    // vim mode
+    // set CodeMirror instance as 'CodeMirror' so that CDN addons can reference
     window.CodeMirror = require('codemirror');
-    Promise.all([
-      this.fetchJsFromCDN('https://cdn.jsdelivr.net/npm/codemirror@5.37.0/keymap/vim.min.js', ['vim']),
-    ]).then(() => {
-      console.log('loaded vim');
-      this.getCodeMirror().setOption('keyMap', 'vim');
-    });
+
+    // TODO apply indivisual settings
+    this.setKeymapMode('vim');
   }
 
   getCodeMirror() {
     return this.refs.cm.editor;
   }
 
+  loadCssAsync(source) {
+    return new Promise((resolve) => {
+      loadCss(source);
+      resolve();
+    });
+  }
+
   forceToFocus() {
     const editor = this.getCodeMirror();
     // use setInterval with reluctance -- 2018.01.11 Yuki Takei
@@ -160,6 +150,34 @@ export default class Editor extends React.Component {
     editor.scrollTo(null, top);
   }
 
+  /**
+   * set Key Maps
+   * @see https://codemirror.net/doc/manual.html#keymaps
+   *
+   * @param {string} keymapMode 'vim' or 'emacs' or 'sublime'
+   */
+  setKeymapMode(keymapMode) {
+    const loadCssAsync = this.loadCssAsync;
+
+    if (!keymapMode.match(/^(vim|emacs|sublime)$/)) {
+      // reset keymap
+      this.getCodeMirror().setOption('keyMap', 'default');
+      return;
+    }
+
+    Promise.all([
+      loadScripts(
+        urljoin(this.cmCdnRoot, 'addon/dialog/dialog.min.js'),
+        urljoin(this.cmCdnRoot, `keymap/${keymapMode}.min.js`)
+      ),
+      loadCssAsync(
+        urljoin(this.cmCdnRoot, 'addon/dialog/dialog.min.css')
+      )
+    ]).then(() => {
+      this.getCodeMirror().setOption('keyMap', keymapMode);
+    });
+  }
+
   /**
    * remove overlay and set isUploading to false
    */

+ 8 - 0
yarn.lock

@@ -4039,6 +4039,10 @@ linkify-it@^2.0.0:
   dependencies:
     uc.micro "^1.0.1"
 
+load-css-file@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/load-css-file/-/load-css-file-1.0.0.tgz#dac097ead6470f4c3f23d4bc5b9ff2c3decb212f"
+
 load-json-file@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@@ -6693,6 +6697,10 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
 
+simple-load-script@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/simple-load-script/-/simple-load-script-1.0.2.tgz#d92951fe7b601ad90af8c9429bd4b2ee127ab8a3"
+
 sinon-chai@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.0.0.tgz#d5cbd70fa71031edd96b528e0eed4038fcc99f29"