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

apply to src/client/js/components

Yuki Takei 7 лет назад
Родитель
Сommit
6ae3812b65
24 измененных файлов с 515 добавлено и 416 удалено
  1. 174 120
      src/client/js/components/PageEditor/CodeMirrorEditor.js
  2. 17 17
      src/client/js/components/PageEditor/EmojiAutoCompleteHelper.js
  3. 1 1
      src/client/js/components/PageEditor/MarkdownListUtil.js
  4. 3 6
      src/client/js/components/PageEditor/MarkdownTableInterceptor.js
  5. 13 13
      src/client/js/components/PageEditor/MarkdownTableUtil.js
  6. 68 42
      src/client/js/components/PageEditor/OptionsSelector.js
  7. 3 1
      src/client/js/components/PageEditor/PasteHelper.js
  8. 3 6
      src/client/js/components/PageEditor/PreventMarkdownListInterceptor.js
  9. 8 9
      src/client/js/components/PageEditor/Preview.js
  10. 38 36
      src/client/js/components/PageEditor/ScrollSyncHelper.js
  11. 10 8
      src/client/js/components/PageEditor/SimpleCheatsheet.js
  12. 23 18
      src/client/js/components/PageEditor/TextAreaEditor.js
  13. 8 5
      src/client/js/components/PageHistory/RevisionDiff.js
  14. 3 3
      src/client/js/components/PageList/ListView.js
  15. 3 4
      src/client/js/components/PageList/Page.js
  16. 2 3
      src/client/js/components/PageList/PageListMeta.js
  17. 4 6
      src/client/js/components/PageList/PagePath.js
  18. 4 8
      src/client/js/components/SearchPage/DeletePageListModal.js
  19. 23 19
      src/client/js/components/SearchPage/SearchPageForm.js
  20. 88 70
      src/client/js/components/SearchPage/SearchResult.js
  21. 3 4
      src/client/js/components/SearchPage/SearchResultList.js
  22. 6 7
      src/client/js/components/User/User.js
  23. 9 9
      src/client/js/components/User/UserPicture.js
  24. 1 1
      src/client/styles/scss/_editor-overlay.scss

+ 174 - 120
src/client/js/components/PageEditor/CodeMirrorEditor.js

@@ -3,14 +3,25 @@ import PropTypes from 'prop-types';
 
 import Modal from 'react-bootstrap/es/Modal';
 import Button from 'react-bootstrap/es/Button';
+import urljoin from 'url-join';
+import * as codemirror from 'codemirror';
+
+import { UnControlled as ReactCodeMirror } from 'react-codemirror2';
 
 import InterceptorManager from '@commons/service/interceptor-manager';
 
-import urljoin from 'url-join';
+import AbstractEditor from './AbstractEditor';
+import SimpleCheatsheet from './SimpleCheatsheet';
+import Cheatsheet from './Cheatsheet';
+import pasteHelper from './PasteHelper';
+import EmojiAutoCompleteHelper from './EmojiAutoCompleteHelper';
+import PreventMarkdownListInterceptor from './PreventMarkdownListInterceptor';
+import MarkdownTableInterceptor from './MarkdownTableInterceptor';
+import mtu from './MarkdownTableUtil';
+import HandsontableModal from './HandsontableModal';
+
 const loadScript = require('simple-load-script');
 const loadCssSync = require('load-css-file');
-
-import * as codemirror from 'codemirror';
 // set save handler
 codemirror.commands.save = (instance) => {
   if (instance.codeMirrorEditor != null) {
@@ -19,9 +30,6 @@ codemirror.commands.save = (instance) => {
 };
 // set CodeMirror instance as 'CodeMirror' so that CDN addons can reference
 window.CodeMirror = require('codemirror');
-
-
-import { UnControlled as ReactCodeMirror } from 'react-codemirror2';
 require('codemirror/addon/display/placeholder');
 require('codemirror/addon/edit/matchbrackets');
 require('codemirror/addon/edit/matchtags');
@@ -42,19 +50,6 @@ require('codemirror/addon/display/placeholder');
 require('codemirror/mode/gfm/gfm');
 require('../../util/codemirror/autorefresh.ext');
 
-import AbstractEditor from './AbstractEditor';
-
-import SimpleCheatsheet from './SimpleCheatsheet';
-import Cheatsheet from './Cheatsheet';
-
-import pasteHelper from './PasteHelper';
-import EmojiAutoCompleteHelper from './EmojiAutoCompleteHelper';
-
-import PreventMarkdownListInterceptor from './PreventMarkdownListInterceptor';
-import MarkdownTableInterceptor from './MarkdownTableInterceptor';
-import mtu from './MarkdownTableUtil';
-import HandsontableModal from './HandsontableModal';
-
 export default class CodeMirrorEditor extends AbstractEditor {
 
   constructor(props) {
@@ -110,14 +105,14 @@ export default class CodeMirrorEditor extends AbstractEditor {
       new MarkdownTableInterceptor(),
     ]);
 
-    this.loadedThemeSet = new Set(['eclipse', 'elegant']);   // themes imported in _vendor.scss
+    this.loadedThemeSet = new Set(['eclipse', 'elegant']); // themes imported in _vendor.scss
     this.loadedKeymapSet = new Set();
   }
 
   componentWillMount() {
     if (this.props.emojiStrategy != null) {
       this.emojiAutoCompleteHelper = new EmojiAutoCompleteHelper(this.props.emojiStrategy);
-      this.setState({isEnabledEmojiAutoComplete: true});
+      this.setState({ isEnabledEmojiAutoComplete: true });
     }
   }
 
@@ -137,7 +132,7 @@ export default class CodeMirrorEditor extends AbstractEditor {
   }
 
   getCodeMirror() {
-    return this.refs.cm.editor;
+    return this.cm.editor;
   }
 
   /**
@@ -185,14 +180,14 @@ export default class CodeMirrorEditor extends AbstractEditor {
    * @inheritDoc
    */
   setCaretLine(line) {
-    if (isNaN(line)) {
+    if (Number.isNaN(line)) {
       return;
     }
 
     const editor = this.getCodeMirror();
     const linePosition = Math.max(0, line);
 
-    editor.setCursor({line: linePosition});   // leave 'ch' field as null/undefined to indicate the end of line
+    editor.setCursor({ line: linePosition }); // leave 'ch' field as null/undefined to indicate the end of line
     this.setScrollTopByLine(linePosition);
   }
 
@@ -200,13 +195,13 @@ export default class CodeMirrorEditor extends AbstractEditor {
    * @inheritDoc
    */
   setScrollTopByLine(line) {
-    if (isNaN(line)) {
+    if (Number.isNaN(line)) {
       return;
     }
 
     const editor = this.getCodeMirror();
     // get top position of the line
-    const top = editor.charCoords({line, ch: 0}, 'local').top;
+    const top = editor.charCoords({ line, ch: 0 }, 'local').top;
     editor.scrollTo(null, top);
   }
 
@@ -331,11 +326,11 @@ export default class CodeMirrorEditor extends AbstractEditor {
    */
   loadKeymapMode(keymapMode) {
     const loadCss = this.loadCss;
-    let scriptList = [];
-    let cssList = [];
+    const scriptList = [];
+    const cssList = [];
 
     // add dependencies
-    if (this.loadedKeymapSet.size == 0) {
+    if (this.loadedKeymapSet.size === 0) {
       const dialogScriptUrl = this.props.noCdn
         ? urljoin(this.cmNoCdnScriptRoot, 'codemirror-dialog.js')
         : urljoin(this.cmCdnRoot, 'addon/dialog/dialog.min.js');
@@ -394,14 +389,14 @@ export default class CodeMirrorEditor extends AbstractEditor {
     }
 
     const context = {
-      handlers: [],  // list of handlers which process enter key
+      handlers: [], // list of handlers which process enter key
       editor: this,
     };
 
     const interceptorManager = this.interceptorManager;
     interceptorManager.process('preHandleEnter', context)
       .then(() => {
-        if (context.handlers.length == 0) {
+        if (context.handlers.length === 0) {
           codemirror.commands.newlineAndIndentContinueMarkdownList(this.getCodeMirror());
         }
       });
@@ -432,13 +427,14 @@ export default class CodeMirrorEditor extends AbstractEditor {
     if (mtu.isEndOfLine(editor) && mtu.linePartOfTableRE.test(strFromBol)) {
       if (!hasCustomClass) {
         additionalClassSet.add(autoformatTableClass);
-        this.setState({additionalClassSet});
+        this.setState({ additionalClassSet });
       }
     }
     else {
+      // eslint-disable-next-line no-lonely-if
       if (hasCustomClass) {
         additionalClassSet.delete(autoformatTableClass);
-        this.setState({additionalClassSet});
+        this.setState({ additionalClassSet });
       }
     }
   }
@@ -479,16 +475,13 @@ export default class CodeMirrorEditor extends AbstractEditor {
 
   /**
    * update states which related to cheatsheet
-   * @param {boolean} isGfmMode (use state.isGfmMode if null is set)
-   * @param {string} value (get value from codemirror if null is set)
+   * @param {boolean} isGfmModeTmp (use state.isGfmMode if null is set)
+   * @param {string} valueTmp (get value from codemirror if null is set)
    */
-  updateCheatsheetStates(isGfmMode, value) {
-    if (isGfmMode == null) {
-      isGfmMode = this.state.isGfmMode;
-    }
-    if (value == null) {
-      value = this.getCodeMirror().getDoc().getValue();
-    }
+  updateCheatsheetStates(isGfmModeTmp, valueTmp) {
+    const isGfmMode = isGfmModeTmp || this.state.isGfmMode;
+    const value = valueTmp || this.getCodeMirror().getDoc().getValue();
+
     // update isSimpleCheatsheetShown, isCheatsheetModalButtonShown
     const isSimpleCheatsheetShown = isGfmMode && value.length === 0;
     const isCheatsheetModalButtonShown = isGfmMode && value.length > 0;
@@ -505,11 +498,13 @@ export default class CodeMirrorEditor extends AbstractEditor {
     };
 
     return this.state.isLoadingKeymap
-      ? <div className="overlay overlay-loading-keymap">
+      ? (
+        <div className="overlay overlay-loading-keymap">
           <span style={style} className="overlay-content">
             <div className="speeding-wheel d-inline-block"></div> Loading Keymap ...
           </span>
         </div>
+      )
       : '';
   }
 
@@ -523,27 +518,27 @@ export default class CodeMirrorEditor extends AbstractEditor {
 
   renderCheatsheetModalButton() {
     const showCheatsheetModal = () => {
-      this.setState({isCheatsheetModalShown: true});
+      this.setState({ isCheatsheetModalShown: true });
     };
 
     const hideCheatsheetModal = () => {
-      this.setState({isCheatsheetModalShown: false});
+      this.setState({ isCheatsheetModalShown: false });
     };
 
     return (
       <React.Fragment>
         <Modal className="modal-gfm-cheatsheet" show={this.state.isCheatsheetModalShown} onHide={() => { hideCheatsheetModal() }}>
           <Modal.Header closeButton>
-            <Modal.Title><i className="icon-fw icon-question"/>Markdown Help</Modal.Title>
+            <Modal.Title><i className="icon-fw icon-question" />Markdown Help</Modal.Title>
           </Modal.Header>
           <Modal.Body className="pt-1">
             { this.renderCheatsheetModalBody() }
           </Modal.Body>
         </Modal>
 
-        <a className="gfm-cheatsheet-modal-link text-muted small" onClick={() => { showCheatsheetModal() }}>
+        <button type="button" className="btn-link gfm-cheatsheet-modal-link text-muted small mr-3" onClick={() => { showCheatsheetModal() }}>
           <i className="icon-question" /> Markdown
-        </a>
+        </button>
       </React.Fragment>
     );
   }
@@ -590,8 +585,8 @@ export default class CodeMirrorEditor extends AbstractEditor {
       for (let i = startLineNum; i <= endLineNum; i++) {
         lines.push(prefix + cm.getDoc().getLine(i));
       }
-      const replacement = lines.join('\n') + '\n';
-      cm.getDoc().replaceRange(replacement, {line: startLineNum, ch: 0}, {line: endLineNum + 1, ch: 0});
+      const replacement = `${lines.join('\n')}\n`;
+      cm.getDoc().replaceRange(replacement, { line: startLineNum, ch: 0 }, { line: endLineNum + 1, ch: 0 });
 
       cm.setCursor(endLineNum, cm.getDoc().getLine(endLineNum).length);
       cm.focus();
@@ -611,69 +606,123 @@ export default class CodeMirrorEditor extends AbstractEditor {
     if (!line.startsWith('#')) {
       prefix += ' ';
     }
-    cm.getDoc().replaceRange(prefix, {line: lineNum, ch: 0}, {line: lineNum, ch: 0});
+    cm.getDoc().replaceRange(prefix, { line: lineNum, ch: 0 }, { line: lineNum, ch: 0 });
     cm.focus();
   }
 
   showHandsonTableHandler() {
-    this.refs.handsontableModal.show(mtu.getMarkdownTable(this.getCodeMirror()));
+    this.handsontableModal.show(mtu.getMarkdownTable(this.getCodeMirror()));
   }
 
   getNavbarItems() {
     // The following styles will be removed after creating icons for the editor navigation bar.
-    const paddingTopBottom54 = {'paddingTop': '6px', 'paddingBottom': '5px'};
-    const paddingBottom6 = {'paddingBottom': '7px'};
-    const fontSize18 = {'fontSize': '18px'};
+    const paddingTopBottom54 = { paddingTop: '6px', paddingBottom: '5px' };
+    const paddingBottom6 = { paddingBottom: '7px' };
+    const fontSize18 = { fontSize: '18px' };
 
     return [
-      <Button key='nav-item-bold' bsSize="small" title={'Bold'}
-              onClick={ this.createReplaceSelectionHandler('**', '**') }>
-        <i className={'fa fa-bold'}></i>
+      <Button
+        key="nav-item-bold"
+        bsSize="small"
+        title="Bold"
+        onClick={this.createReplaceSelectionHandler('**', '**')}
+      >
+        <i className="fa fa-bold"></i>
       </Button>,
-      <Button key='nav-item-italic' bsSize="small" title={'Italic'}
-              onClick={ this.createReplaceSelectionHandler('*', '*') }>
-        <i className={'fa fa-italic'}></i>
+      <Button
+        key="nav-item-italic"
+        bsSize="small"
+        title="Italic"
+        onClick={this.createReplaceSelectionHandler('*', '*')}
+      >
+        <i className="fa fa-italic"></i>
       </Button>,
-      <Button key='nav-item-strikethough' bsSize="small" title={'Strikethrough'}
-              onClick={ this.createReplaceSelectionHandler('~~', '~~') }>
-        <i className={'fa fa-strikethrough'}></i>
+      <Button
+        key="nav-item-strikethough"
+        bsSize="small"
+        title="Strikethrough"
+        onClick={this.createReplaceSelectionHandler('~~', '~~')}
+      >
+        <i className="fa fa-strikethrough"></i>
       </Button>,
-      <Button key='nav-item-header' bsSize="small" title={'Heading'}
-              onClick={ this.makeHeaderHandler }>
-        <i className={'fa fa-header'}></i>
+      <Button
+        key="nav-item-header"
+        bsSize="small"
+        title="Heading"
+        onClick={this.makeHeaderHandler}
+      >
+        <i className="fa fa-header"></i>
       </Button>,
-      <Button key='nav-item-code' bsSize="small" title={'Inline Code'}
-              onClick={ this.createReplaceSelectionHandler('`', '`') }>
-        <i className={'fa fa-code'}></i>
+      <Button
+        key="nav-item-code"
+        bsSize="small"
+        title="Inline Code"
+        onClick={this.createReplaceSelectionHandler('`', '`')}
+      >
+        <i className="fa fa-code"></i>
       </Button>,
-      <Button key='nav-item-quote' bsSize="small" title={'Quote'}
-              onClick={ this.createAddPrefixToEachLinesHandler('> ') } style={paddingBottom6}>
-        <i className={'ti-quote-right'}></i>
+      <Button
+        key="nav-item-quote"
+        bsSize="small"
+        title="Quote"
+        onClick={this.createAddPrefixToEachLinesHandler('> ')}
+        style={paddingBottom6}
+      >
+        <i className="ti-quote-right"></i>
       </Button>,
-      <Button key='nav-item-ul' bsSize="small" title={'List'}
-              onClick={ this.createAddPrefixToEachLinesHandler('- ') } style={paddingTopBottom54}>
-        <i className={'ti-list'} style={fontSize18}></i>
+      <Button
+        key="nav-item-ul"
+        bsSize="small"
+        title="List"
+        onClick={this.createAddPrefixToEachLinesHandler('- ')}
+        style={paddingTopBottom54}
+      >
+        <i className="ti-list" style={fontSize18}></i>
       </Button>,
-      <Button key='nav-item-ol' bsSize="small" title={'Numbered List'}
-              onClick={ this.createAddPrefixToEachLinesHandler('1. ') } style={paddingTopBottom54}>
-        <i className={'ti-list-ol'} style={fontSize18}></i>
+      <Button
+        key="nav-item-ol"
+        bsSize="small"
+        title="Numbered List"
+        onClick={this.createAddPrefixToEachLinesHandler('1. ')}
+        style={paddingTopBottom54}
+      >
+        <i className="ti-list-ol" style={fontSize18}></i>
       </Button>,
-      <Button key='nav-item-checkbox' bsSize="small" title={'Check List'}
-              onClick={ this.createAddPrefixToEachLinesHandler('- [ ] ') } style={paddingBottom6}>
-        <i className={'ti-check-box'}></i>
+      <Button
+        key="nav-item-checkbox"
+        bsSize="small"
+        title="Check List"
+        onClick={this.createAddPrefixToEachLinesHandler('- [ ] ')}
+        style={paddingBottom6}
+      >
+        <i className="ti-check-box"></i>
       </Button>,
-      <Button key='nav-item-link' bsSize="small" title={'Link'}
-              onClick={ this.createReplaceSelectionHandler('[', ']()') } style={paddingBottom6}>
-        <i className={'icon-link'}></i>
+      <Button
+        key="nav-item-link"
+        bsSize="small"
+        title="Link"
+        onClick={this.createReplaceSelectionHandler('[', ']()')}
+        style={paddingBottom6}
+      >
+        <i className="icon-link"></i>
       </Button>,
-      <Button key='nav-item-image' bsSize="small" title={'Image'}
-              onClick={ this.createReplaceSelectionHandler('![', ']()') } style={paddingBottom6}>
-        <i className={'icon-picture'}></i>
+      <Button
+        key="nav-item-image"
+        bsSize="small"
+        title="Image"
+        onClick={this.createReplaceSelectionHandler('![', ']()')}
+        style={paddingBottom6}
+      >
+        <i className="icon-picture"></i>
+      </Button>,
+      <Button
+        key="nav-item-table"
+        bsSize="small"
+        title="Table"
+        onClick={this.showHandsonTableHandler}
+      >
+        <img src="/images/icons/editor/table.svg" alt="icon-table" width="14" height="14" />
       </Button>,
-      <Button key='nav-item-table' bsSize="small" title={'Table'}
-              onClick={ this.showHandsonTableHandler }>
-        <img src="/images/icons/editor/table.svg" width="14" height="14" />
-      </Button>
     ];
   }
 
@@ -688,50 +737,51 @@ export default class CodeMirrorEditor extends AbstractEditor {
 
     const placeholder = this.state.isGfmMode ? 'Input with Markdown..' : 'Input with Plane Text..';
 
-    return <React.Fragment>
+    return (
+      <React.Fragment>
 
-      <ReactCodeMirror
-        ref="cm"
-        className={additionalClasses}
-        placeholder="search"
-        editorDidMount={(editor) => {
+        <ReactCodeMirror
+          ref={(c) => { this.cm = c }}
+          className={additionalClasses}
+          placeholder="search"
+          editorDidMount={(editor) => {
           // add event handlers
           editor.on('paste', this.pasteHandler);
           editor.on('scrollCursorIntoView', this.scrollCursorIntoViewHandler);
         }}
-        value={this.state.value}
-        options={{
-          mode: mode,
+          value={this.state.value}
+          options={{
+          mode,
           theme: editorOptions.theme,
           styleActiveLine: editorOptions.styleActiveLine,
           lineNumbers: this.props.lineNumbers,
           tabSize: 4,
           indentUnit: 4,
           lineWrapping: true,
-          autoRefresh: {force: true},   // force option is enabled by autorefresh.ext.js -- Yuki Takei
+          autoRefresh: { force: true }, // force option is enabled by autorefresh.ext.js -- Yuki Takei
           autoCloseTags: true,
-          placeholder: placeholder,
+          placeholder,
           matchBrackets: true,
-          matchTags: {bothTags: true},
+          matchTags: { bothTags: true },
           // folding
           foldGutter: this.props.lineNumbers,
           gutters: this.props.lineNumbers ? ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'] : [],
           // match-highlighter, matchesonscrollbar, annotatescrollbar options
-          highlightSelectionMatches: {annotateScrollbar: true},
+          highlightSelectionMatches: { annotateScrollbar: true },
           // markdown mode options
           highlightFormatting: true,
           // continuelist, indentlist
           extraKeys: {
-            'Enter': this.handleEnterKey,
+            Enter: this.handleEnterKey,
             'Ctrl-Enter': this.handleCtrlEnterKey,
             'Cmd-Enter': this.handleCtrlEnterKey,
-            'Tab': 'indentMore',
+            Tab: 'indentMore',
             'Shift-Tab': 'indentLess',
             'Ctrl-Q': (cm) => { cm.foldCode(cm.getCursor()) },
-          }
+          },
         }}
-        onCursor={this.cursorHandler}
-        onScroll={(editor, data) => {
+          onCursor={this.cursorHandler}
+          onScroll={(editor, data) => {
           if (this.props.onScroll != null) {
             // add line data
             const line = editor.lineAtHeight(data.top, 'local');
@@ -739,23 +789,27 @@ export default class CodeMirrorEditor extends AbstractEditor {
             this.props.onScroll(data);
           }
         }}
-        onChange={this.changeHandler}
-        onDragEnter={(editor, event) => {
+          onChange={this.changeHandler}
+          onDragEnter={(editor, event) => {
           if (this.props.onDragEnter != null) {
             this.props.onDragEnter(event);
           }
         }}
-      />
+        />
 
-      { this.renderLoadingKeymapOverlay() }
+        { this.renderLoadingKeymapOverlay() }
 
-      <div className="overlay overlay-gfm-cheatsheet mt-1 p-3 pt-3">
-        { this.state.isSimpleCheatsheetShown && this.renderSimpleCheatsheet() }
-        { this.state.isCheatsheetModalButtonShown && this.renderCheatsheetModalButton() }
-      </div>
+        <div className="overlay overlay-gfm-cheatsheet mt-1 p-3 pt-3">
+          { this.state.isSimpleCheatsheetShown && this.renderSimpleCheatsheet() }
+          { this.state.isCheatsheetModalButtonShown && this.renderCheatsheetModalButton() }
+        </div>
 
-      <HandsontableModal ref='handsontableModal' onSave={ table => mtu.replaceFocusedMarkdownTableWithEditor(this.getCodeMirror(), table) }/>
-    </React.Fragment>;
+        <HandsontableModal
+          ref={(c) => { this.handsontableModal = c }}
+          onSave={(table) => { return mtu.replaceFocusedMarkdownTableWithEditor(this.getCodeMirror(), table) }}
+        />
+      </React.Fragment>
+    );
   }
 
 }

+ 17 - 17
src/client/js/components/PageEditor/EmojiAutoCompleteHelper.js

@@ -14,8 +14,7 @@ class EmojiAutoCompleteHelper {
   }
 
   initEmojiImageMap() {
-    for (let unicode in this.emojiStrategy) {
-      const data = this.emojiStrategy[unicode];
+    for (const data of Object.values(this.emojiStrategy)) {
       const shortname = data.shortname;
       // add image tag
       this.emojiShortnameImageMap[shortname] = emojione.shortnameToImage(shortname);
@@ -62,7 +61,7 @@ class EmojiAutoCompleteHelper {
       // closeOnUnfocus: false,  // for debug
       hint: () => {
         const matched = editor.getDoc().getRange(sc.from(), sc.to());
-        const term = matched.replace(':', '');  // remove ':' in the head
+        const term = matched.replace(':', ''); // remove ':' in the head
 
         // get a list of shortnames
         const shortnames = this.searchEmojiShortnames(term);
@@ -87,10 +86,9 @@ class EmojiAutoCompleteHelper {
         text: shortname,
         className: 'crowi-emoji-autocomplete',
         render: (element) => {
-          element.innerHTML =
-            `<div class="img-container">${this.emojiShortnameImageMap[shortname]}</div>` +
-            `<span class="shortname-container">${shortname}</span>`;
-        }
+          element.innerHTML = `<div class="img-container">${this.emojiShortnameImageMap[shortname]}</div>`
+            + `<span class="shortname-container">${shortname}</span>`;
+        },
       };
     });
   }
@@ -103,16 +101,18 @@ class EmojiAutoCompleteHelper {
   searchEmojiShortnames(term) {
     const maxLength = 12;
 
-    let results1 = [], results2 = [], results3 = [], results4 = [];
-    const countLen1 = () => { results1.length };
-    const countLen2 = () => { countLen1() + results2.length };
-    const countLen3 = () => { countLen2() + results3.length };
-    const countLen4 = () => { countLen3() + results4.length };
+    const results1 = [];
+    const results2 = [];
+    const results3 = [];
+    const results4 = [];
+    const countLen1 = () => { return results1.length };
+    const countLen2 = () => { return countLen1() + results2.length };
+    const countLen3 = () => { return countLen2() + results3.length };
+    const countLen4 = () => { return countLen3() + results4.length };
+
     // TODO performance tune
     // when total length of all results is less than `maxLength`
-    for (let unicode in this.emojiStrategy) {
-      const data = this.emojiStrategy[unicode];
-
+    for (const data of Object.values(this.emojiStrategy)) {
       if (maxLength <= countLen1()) { break }
       // prefix match to shortname
       else if (data.shortname.indexOf(`:${term}`) > -1) {
@@ -127,13 +127,13 @@ class EmojiAutoCompleteHelper {
       }
       else if (maxLength <= countLen3()) { continue }
       // partial match to elements of aliases
-      else if ((data.aliases != null) && data.aliases.find(elem => elem.indexOf(term) > -1)) {
+      else if ((data.aliases != null) && data.aliases.find((elem) => { return elem.indexOf(term) > -1 })) {
         results3.push(data.shortname);
         continue;
       }
       else if (maxLength <= countLen4()) { continue }
       // partial match to elements of keywords
-      else if ((data.keywords != null) && data.keywords.find(elem => elem.indexOf(term) > -1)) {
+      else if ((data.keywords != null) && data.keywords.find((elem) => { return elem.indexOf(term) > -1 })) {
         results4.push(data.shortname);
       }
     }

+ 1 - 1
src/client/js/components/PageEditor/MarkdownListUtil.js

@@ -88,7 +88,7 @@ class MarkdownListUtil {
     // not listful data
     else {
       // append `indentAndMark` at the beginning of all lines (except the first line)
-      const replacedText = text.replace(/(\r\n|\r|\n)/g, '$1' + indentAndMark);
+      const replacedText = text.replace(/(\r\n|\r|\n)/g, `$1${indentAndMark}`);
       // append `indentAndMark` to the first line
       adjusted = indentAndMark + replacedText;
     }

+ 3 - 6
src/client/js/components/PageEditor/MarkdownTableInterceptor.js

@@ -8,10 +8,6 @@ import MarkdownTable from '../../models/MarkdownTable';
  */
 export default class MarkdownTableInterceptor extends BasicInterceptor {
 
-  constructor() {
-    super();
-  }
-
   /**
    * @inheritdoc
    */
@@ -32,8 +28,8 @@ export default class MarkdownTableInterceptor extends BasicInterceptor {
    * @inheritdoc
    */
   process(contextName, ...args) {
-    const context = Object.assign(args[0]);   // clone
-    const editor = context.editor;            // AbstractEditor instance
+    const context = Object.assign(args[0]); // clone
+    const editor = context.editor; // AbstractEditor instance
 
     // do nothing if editor is not a CodeMirrorEditor
     if (editor == null || editor.getCodeMirror() == null) {
@@ -67,4 +63,5 @@ export default class MarkdownTableInterceptor extends BasicInterceptor {
     // resolve
     return Promise.resolve(context);
   }
+
 }

+ 13 - 13
src/client/js/components/PageEditor/MarkdownTableUtil.js

@@ -9,7 +9,7 @@ class MarkdownTableUtil {
     // https://github.com/markdown-it/markdown-it/blob/d29f421927e93e88daf75f22089a3e732e195bd2/lib/rules_block/table.js#L83
     // https://regex101.com/r/7BN2fR/7
     this.tableAlignmentLineRE = /^[-:|][-:|\s]*$/;
-    this.tableAlignmentLineNegRE = /^[^-:]*$/;  // it is need to check to ignore empty row which is matched above RE
+    this.tableAlignmentLineNegRE = /^[^-:]*$/; // it is need to check to ignore empty row which is matched above RE
     this.linePartOfTableRE = /^\|[^\r\n]*|[^\r\n]*\|$|([^|\r\n]+\|[^|\r\n]*)+/; // own idea
 
     this.getBot = this.getBot.bind(this);
@@ -29,7 +29,7 @@ class MarkdownTableUtil {
   getBot(editor) {
     const curPos = editor.getCursor();
     if (!this.isInTable(editor)) {
-      return { line: curPos.line, ch: curPos.ch};
+      return { line: curPos.line, ch: curPos.ch };
     }
 
     const firstLine = editor.getDoc().firstLine();
@@ -51,7 +51,7 @@ class MarkdownTableUtil {
   getEot(editor) {
     const curPos = editor.getCursor();
     if (!this.isInTable(editor)) {
-      return { line: curPos.line, ch: curPos.ch};
+      return { line: curPos.line, ch: curPos.ch };
     }
 
     const lastLine = editor.getDoc().lastLine();
@@ -109,7 +109,7 @@ class MarkdownTableUtil {
    */
   isEndOfLine(editor) {
     const curPos = editor.getCursor();
-    return (curPos.ch == editor.getDoc().getLine(curPos.line).length);
+    return (curPos.ch === editor.getDoc().getLine(curPos.line).length);
   }
 
   /**
@@ -127,8 +127,8 @@ class MarkdownTableUtil {
    */
   addRowToMarkdownTable(mdtable) {
     const numCol = mdtable.table.length > 0 ? mdtable.table[0].length : 1;
-    let newRow = [];
-    (new Array(numCol)).forEach(() => newRow.push('')); // create cols
+    const newRow = [];
+    (new Array(numCol)).forEach(() => { return newRow.push('') }); // create cols
     mdtable.table.push(newRow);
   }
 
@@ -137,15 +137,14 @@ class MarkdownTableUtil {
    * (The merged markdown table options are used for the first markdown table.)
    * @param {Array} array of markdown table
    */
-  mergeMarkdownTable(mdtable_list) {
-    if (mdtable_list == undefined
-        || !(mdtable_list instanceof Array)) {
+  mergeMarkdownTable(mdtableList) {
+    if (mdtableList == null || !(mdtableList instanceof Array)) {
       return undefined;
     }
 
     let newTable = [];
-    const options = mdtable_list[0].options; // use option of first markdown-table
-    mdtable_list.forEach((mdtable) => {
+    const options = mdtableList[0].options; // use option of first markdown-table
+    mdtableList.forEach((mdtable) => {
       newTable = newTable.concat(mdtable.table);
     });
     return (new MarkdownTable(newTable, options));
@@ -176,15 +175,16 @@ class MarkdownTableUtil {
 
     let newMarkdown = '';
     if (markdownBeforeTable.length > 0) {
-      newMarkdown += markdownBeforeTable.join('\n') + '\n';
+      newMarkdown += `${markdownBeforeTable.join('\n')}\n`;
     }
     newMarkdown += table;
     if (markdownAfterTable.length > 0) {
-      newMarkdown += '\n' + markdownAfterTable.join('\n');
+      newMarkdown += `\n${markdownAfterTable.join('\n')}`;
     }
 
     return newMarkdown;
   }
+
 }
 
 // singleton pattern

+ 68 - 42
src/client/js/components/PageEditor/OptionsSelector.js

@@ -9,6 +9,28 @@ import ControlLabel from 'react-bootstrap/es/ControlLabel';
 import Dropdown from 'react-bootstrap/es/Dropdown';
 import MenuItem from 'react-bootstrap/es/MenuItem';
 
+export class EditorOptions {
+
+  constructor(props) {
+    this.theme = 'elegant';
+    this.keymapMode = 'default';
+    this.styleActiveLine = false;
+
+    Object.assign(this, props);
+  }
+
+}
+
+export class PreviewOptions {
+
+  constructor(props) {
+    this.renderMathJaxInRealtime = false;
+
+    Object.assign(this, props);
+  }
+
+}
+
 class OptionsSelector extends React.Component {
 
   constructor(props) {
@@ -25,7 +47,7 @@ class OptionsSelector extends React.Component {
     };
 
     this.availableThemes = [
-      'eclipse', 'elegant', 'neo', 'mdn-like', 'material', 'dracula', 'monokai', 'twilight'
+      'eclipse', 'elegant', 'neo', 'mdn-like', 'material', 'dracula', 'monokai', 'twilight',
     ];
     this.keymapModes = {
       default: 'Default',
@@ -52,8 +74,8 @@ class OptionsSelector extends React.Component {
 
   onChangeTheme() {
     const newValue = this.themeSelectorInputEl.value;
-    const newOpts = Object.assign(this.state.editorOptions, {theme: newValue});
-    this.setState({editorOptions: newOpts});
+    const newOpts = Object.assign(this.state.editorOptions, { theme: newValue });
+    this.setState({ editorOptions: newOpts });
 
     // dispatch event
     this.dispatchOnChange();
@@ -61,8 +83,8 @@ class OptionsSelector extends React.Component {
 
   onChangeKeymapMode() {
     const newValue = this.keymapModeSelectorInputEl.value;
-    const newOpts = Object.assign(this.state.editorOptions, {keymapMode: newValue});
-    this.setState({editorOptions: newOpts});
+    const newOpts = Object.assign(this.state.editorOptions, { keymapMode: newValue });
+    this.setState({ editorOptions: newOpts });
 
     // dispatch event
     this.dispatchOnChange();
@@ -73,8 +95,8 @@ class OptionsSelector extends React.Component {
     this._cddForceOpen = true;
 
     const newValue = !this.state.editorOptions.styleActiveLine;
-    const newOpts = Object.assign(this.state.editorOptions, {styleActiveLine: newValue});
-    this.setState({editorOptions: newOpts});
+    const newOpts = Object.assign(this.state.editorOptions, { styleActiveLine: newValue });
+    this.setState({ editorOptions: newOpts });
 
     // dispatch event
     this.dispatchOnChange();
@@ -85,8 +107,8 @@ class OptionsSelector extends React.Component {
     this._cddForceOpen = true;
 
     const newValue = !this.state.previewOptions.renderMathJaxInRealtime;
-    const newOpts = Object.assign(this.state.previewOptions, {renderMathJaxInRealtime: newValue});
-    this.setState({previewOptions: newOpts});
+    const newOpts = Object.assign(this.state.previewOptions, { renderMathJaxInRealtime: newValue });
+    this.setState({ previewOptions: newOpts });
 
     // dispatch event
     this.dispatchOnChange();
@@ -122,9 +144,15 @@ class OptionsSelector extends React.Component {
     return (
       <FormGroup controlId="formControlsSelect" className="my-0">
         <ControlLabel>Theme:</ControlLabel>
-        <FormControl componentClass="select" placeholder="select" bsClass={bsClassName} className="btn-group-sm selectpicker"
-            onChange={this.onChangeTheme}
-            inputRef={ el => this.themeSelectorInputEl=el }>
+        <FormControl
+          componentClass="select"
+          placeholder="select"
+          bsClass={bsClassName}
+          className="btn-group-sm selectpicker"
+          onChange={this.onChangeTheme}
+          // eslint-disable-next-line no-return-assign
+          inputRef={(el) => { return this.themeSelectorInputEl = el }}
+        >
 
           {optionElems}
 
@@ -135,13 +163,14 @@ class OptionsSelector extends React.Component {
 
   renderKeymapModeSelector() {
     const optionElems = [];
-    for (let mode in this.keymapModes) {
+    // eslint-disable-next-line guard-for-in, no-restricted-syntax
+    for (const mode in this.keymapModes) {
       const label = this.keymapModes[mode];
       const dataContent = (mode === 'default')
         ? label
         : `<img src='/images/icons/${mode}.png' width='16px' class='m-r-5'></img> ${label}`;
       optionElems.push(
-        <option key={mode} value={mode} data-content={dataContent}>{label}</option>
+        <option key={mode} value={mode} data-content={dataContent}>{label}</option>,
       );
     }
 
@@ -150,9 +179,15 @@ class OptionsSelector extends React.Component {
     return (
       <FormGroup controlId="formControlsSelect" className="my-0">
         <ControlLabel>Keymap:</ControlLabel>
-        <FormControl componentClass="select" placeholder="select" bsClass={bsClassName} className="btn-group-sm selectpicker"
-            onChange={this.onChangeKeymapMode}
-            inputRef={ el => this.keymapModeSelectorInputEl=el }>
+        <FormControl
+          componentClass="select"
+          placeholder="select"
+          bsClass={bsClassName}
+          className="btn-group-sm selectpicker"
+          onChange={this.onChangeKeymapMode}
+          // eslint-disable-next-line no-return-assign
+          inputRef={(el) => { return this.keymapModeSelectorInputEl = el }}
+        >
 
           {optionElems}
 
@@ -165,8 +200,13 @@ class OptionsSelector extends React.Component {
     return (
       <FormGroup controlId="formControlsSelect" className="my-0">
 
-        <Dropdown dropup id="configurationDropdown" className="configuration-dropdown"
-            open={this.state.isCddMenuOpened} onToggle={this.onToggleConfigurationDropdown}>
+        <Dropdown
+          dropup
+          id="configurationDropdown"
+          className="configuration-dropdown"
+          open={this.state.isCddMenuOpened}
+          onToggle={this.onToggleConfigurationDropdown}
+        >
 
           <Dropdown.Toggle bsSize="sm">
             <i className="icon-settings"></i>
@@ -219,7 +259,7 @@ class OptionsSelector extends React.Component {
 
     return (
       <MenuItem onClick={this.onClickRenderMathJaxInRealtime}>
-        <span className="icon-container"><img src="/images/icons/fx.svg" width="14px"></img></span>
+        <span className="icon-container"><img src="/images/icons/fx.svg" width="14px" alt="fx"></img></span>
         <span className="menuitem-label">MathJax Rendering</span>
         <i className={iconClassName}></i>
       </MenuItem>
@@ -227,34 +267,20 @@ class OptionsSelector extends React.Component {
   }
 
   render() {
-    return <div className="d-flex flex-row">
-      <span className="m-l-5">{this.renderThemeSelector()}</span>
-      <span className="m-l-5">{this.renderKeymapModeSelector()}</span>
-      <span className="m-l-5">{this.renderConfigurationDropdown()}</span>
-    </div>;
+    return (
+      <div className="d-flex flex-row">
+        <span className="m-l-5">{this.renderThemeSelector()}</span>
+        <span className="m-l-5">{this.renderKeymapModeSelector()}</span>
+        <span className="m-l-5">{this.renderConfigurationDropdown()}</span>
+      </div>
+    );
   }
-}
-
-export class EditorOptions {
-  constructor(props) {
-    this.theme = 'elegant';
-    this.keymapMode = 'default';
-    this.styleActiveLine = false;
 
-    Object.assign(this, props);
-  }
 }
 
-export class PreviewOptions {
-  constructor(props) {
-    this.renderMathJaxInRealtime = false;
-
-    Object.assign(this, props);
-  }
-}
 
 OptionsSelector.propTypes = {
-  t: PropTypes.func.isRequired,               // i18next
+  t: PropTypes.func.isRequired, // i18next
   crowi: PropTypes.object.isRequired,
   editorOptions: PropTypes.instanceOf(EditorOptions).isRequired,
   previewOptions: PropTypes.instanceOf(PreviewOptions).isRequired,

+ 3 - 1
src/client/js/components/PageEditor/PasteHelper.js

@@ -17,7 +17,7 @@ class PasteHelper {
     // get data in clipboard
     const text = event.clipboardData.getData('text/plain');
 
-    if (text.length == 0) {
+    if (text.length === 0) {
       return;
     }
 
@@ -35,6 +35,7 @@ class PasteHelper {
   fileAccepted(file, accept) {
     return file.type === 'application/x-moz-file' || accepts(file, accept);
   }
+
   /**
    * transplanted from react-dropzone
    * @see https://github.com/react-dropzone/react-dropzone/blob/master/src/utils/index.js
@@ -46,6 +47,7 @@ class PasteHelper {
   fileMatchSize(file, maxSize, minSize) {
     return file.size <= maxSize && file.size >= minSize;
   }
+
 }
 
 // singleton pattern

+ 3 - 6
src/client/js/components/PageEditor/PreventMarkdownListInterceptor.js

@@ -3,10 +3,6 @@ import mlu from './MarkdownListUtil';
 
 export default class PreventMarkdownListInterceptor extends BasicInterceptor {
 
-  constructor() {
-    super();
-  }
-
   /**
    * @inheritdoc
    */
@@ -27,8 +23,8 @@ export default class PreventMarkdownListInterceptor extends BasicInterceptor {
    * @inheritdoc
    */
   process(contextName, ...args) {
-    const context = Object.assign(args[0]);   // clone
-    const editor = context.editor;            // AbstractEditor instance
+    const context = Object.assign(args[0]); // clone
+    const editor = context.editor; // AbstractEditor instance
 
     // get strings from current position to EOL(end of line) before break the line
     const strToEol = editor.getStrToEol();
@@ -43,4 +39,5 @@ export default class PreventMarkdownListInterceptor extends BasicInterceptor {
     // resolve
     return Promise.resolve(context);
   }
+
 }

+ 8 - 9
src/client/js/components/PageEditor/Preview.js

@@ -10,24 +10,22 @@ import { PreviewOptions } from './OptionsSelector';
  */
 export default class Preview extends React.Component {
 
-  constructor(props) {
-    super(props);
-  }
-
   render() {
     const renderMathJaxInRealtime = this.props.previewOptions.renderMathJaxInRealtime;
 
     return (
-      <div className="page-editor-preview-body"
-          ref={(elm) => {
+      <div
+        className="page-editor-preview-body"
+        ref={(elm) => {
             this.previewElement = elm;
             this.props.inputRef(elm);
           }}
-          onScroll={(event) => {
+        onScroll={(event) => {
             if (this.props.onScroll != null) {
               this.props.onScroll(event.target.scrollTop);
             }
-          }}>
+          }}
+      >
 
         <RevisionBody
           {...this.props}
@@ -36,11 +34,12 @@ export default class Preview extends React.Component {
       </div>
     );
   }
+
 }
 
 Preview.propTypes = {
   html: PropTypes.string,
-  inputRef: PropTypes.func.isRequired,  // for getting div element
+  inputRef: PropTypes.func.isRequired, // for getting div element
   isMathJaxEnabled: PropTypes.bool,
   renderMathJaxOnInit: PropTypes.bool,
   previewOptions: PropTypes.instanceOf(PreviewOptions),

+ 38 - 36
src/client/js/components/PageEditor/ScrollSyncHelper.js

@@ -5,11 +5,8 @@
 class ScrollSyncHelper {
 
   /**
-	 * @typedef {{ element: Element, line: number }} CodeLineElement
-	 */
-
-  constructor() {
-  }
+   * @typedef {{ element: Element, line: number }} CodeLineElement
+   */
 
   getCodeLineElements(parentElement) {
     /** @type {CodeLineElement[]} */
@@ -17,26 +14,27 @@ class ScrollSyncHelper {
     if (!elements) {
       elements = Array.prototype.map.call(
         parentElement.getElementsByClassName('code-line'),
-        element => {
+        (element) => {
           const line = +element.getAttribute('data-line');
           return { element, line };
-        })
-        .filter(x => !isNaN(x.line));
+        },
+      )
+        .filter((x) => { return !Number.isNaN(x.line) });
     }
     return elements;
   }
 
   /**
-	 * Find the html elements that map to a specific target line in the editor.
-	 *
-	 * If an exact match, returns a single element. If the line is between elements,
-	 * returns the element prior to and the element after the given line.
-	 *
+   * Find the html elements that map to a specific target line in the editor.
+   *
+   * If an exact match, returns a single element. If the line is between elements,
+   * returns the element prior to and the element after the given line.
+   *
    * @param {Element} element
-	 * @param {number} targetLine
-	 *
-	 * @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
-	 */
+   * @param {number} targetLine
+   *
+   * @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
+   */
   getElementsForSourceLine(element, targetLine) {
     const lines = this.getCodeLineElements(element);
     let previous = lines[0] || null;
@@ -44,7 +42,7 @@ class ScrollSyncHelper {
       if (entry.line === targetLine) {
         return { previous: entry, next: null };
       }
-      else if (entry.line > targetLine) {
+      if (entry.line > targetLine) {
         return { previous, next: entry };
       }
       previous = entry;
@@ -53,13 +51,13 @@ class ScrollSyncHelper {
   }
 
   /**
-	 * Find the html elements that are at a specific pixel offset on the page.
-	 *
+   * Find the html elements that are at a specific pixel offset on the page.
+   *
    * @param {Element} parentElement
-	 * @param {number} offset
+   * @param {number} offset
    *
-	 * @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
-	 */
+   * @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
+   */
   getLineElementsAtPageOffset(parentElement, offset) {
     const lines = this.getCodeLineElements(parentElement);
 
@@ -82,7 +80,7 @@ class ScrollSyncHelper {
     if (hi >= 1 && hiElement.element.getBoundingClientRect().top > position) {
       const loElement = lines[lo];
       const bounds = loElement.element.getBoundingClientRect();
-      let previous = { element: loElement.element, line: loElement.line };
+      const previous = { element: loElement.element, line: loElement.line };
       if (bounds.height > 0) {
         previous.line += (position - bounds.top) / (bounds.height);
       }
@@ -99,12 +97,14 @@ class ScrollSyncHelper {
     const { previous, next } = this.getLineElementsAtPageOffset(parentElement, offset);
     if (previous) {
       if (next) {
-        const betweenProgress = (offset - parentElement.scrollTop - previous.element.getBoundingClientRect().top) / (next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top);
+        const betweenProgress = (
+          offset - parentElement.scrollTop - previous.element.getBoundingClientRect().top)
+          / (next.element.getBoundingClientRect().top - previous.element.getBoundingClientRect().top);
         return previous.line + betweenProgress * (next.line - previous.line);
       }
-      else {
-        return previous.line;
-      }
+
+      return previous.line;
+
     }
     return null;
   }
@@ -123,11 +123,11 @@ class ScrollSyncHelper {
   }
 
   /**
-	 * Attempt to scroll preview element for a source line in the editor.
-	 *
+   * Attempt to scroll preview element for a source line in the editor.
+   *
    * @param {Element} previewElement
-	 * @param {number} line
-	 */
+   * @param {number} line
+   */
   scrollPreview(previewElement, line) {
     const { previous, next } = this.getElementsForSourceLine(previewElement, line);
     if (previous) {
@@ -149,12 +149,13 @@ class ScrollSyncHelper {
   }
 
   /**
-	 * Attempt to reveal the element that is overflowing from previewElement.
-	 *
+   * Attempt to reveal the element that is overflowing from previewElement.
+   *
    * @param {Element} previewElement
-	 * @param {number} line
-	 */
+   * @param {number} line
+   */
   scrollPreviewToRevealOverflowing(previewElement, line) {
+    // eslint-disable-next-line no-unused-vars
     const { previous, next } = this.getElementsForSourceLine(previewElement, line);
     if (previous) {
       const parentElementOffset = this.getParentElementOffset(previewElement);
@@ -191,6 +192,7 @@ class ScrollSyncHelper {
     line = Math.floor(line);
     editor.setScrollTopByLine(line);
   }
+
 }
 
 // singleton pattern

+ 10 - 8
src/client/js/components/PageEditor/SimpleCheatsheet.js

@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import { translate } from 'react-i18next';
 
 class SimpleCheatsheet extends React.Component {
+
   render() {
     const { t } = this.props;
 
@@ -12,8 +13,8 @@ class SimpleCheatsheet extends React.Component {
           <div className="row">
             <div className="col-xs-6">
               <p>
-                # {t('sandbox.header_x', {index: '1'})}<br />
-                ## {t('sandbox.header_x', {index: '2'})}
+                # {t('sandbox.header_x', { index: '1' })}<br />
+                ## {t('sandbox.header_x', { index: '2' })}
               </p>
               <p><i>*{t('sandbox.italics')}*</i>&nbsp;&nbsp;<b>**{t('sandbox.bold')}**</b></p>
               <p>
@@ -28,11 +29,11 @@ class SimpleCheatsheet extends React.Component {
             </div>
             <div className="col-xs-6">
               <p>
-                - {t('sandbox.unordered_list_x', {index: '1'})}<br />
-                &nbsp;&nbsp;&nbsp;- {t('sandbox.unordered_list_x', {index: '1.1'})}<br />
-                - {t('sandbox.unordered_list_x', {index: '2'})}<br />
-                1. {t('sandbox.ordered_list_x', {index: '1'})}<br />
-                1. {t('sandbox.ordered_list_x', {index: '2'})}
+                - {t('sandbox.unordered_list_x', { index: '1' })}<br />
+                &nbsp;&nbsp;&nbsp;- {t('sandbox.unordered_list_x', { index: '1.1' })}<br />
+                - {t('sandbox.unordered_list_x', { index: '2' })}<br />
+                1. {t('sandbox.ordered_list_x', { index: '1' })}<br />
+                1. {t('sandbox.ordered_list_x', { index: '2' })}
               </p>
               <hr />
               <p>[ ][ ] {t('sandbox.block_detail')}</p>
@@ -42,10 +43,11 @@ class SimpleCheatsheet extends React.Component {
       </div>
     );
   }
+
 }
 
 SimpleCheatsheet.propTypes = {
-  t: PropTypes.func.isRequired,               // i18next
+  t: PropTypes.func.isRequired, // i18next
 };
 
 export default translate()(SimpleCheatsheet);

+ 23 - 18
src/client/js/components/PageEditor/TextAreaEditor.js

@@ -79,18 +79,20 @@ export default class TextAreaEditor extends AbstractEditor {
    * @inheritDoc
    */
   setCaretLine(line) {
-    if (isNaN(line)) {
+    if (Number.isNaN(line)) {
       return;
     }
 
     // scroll to bottom
     this.textarea.scrollTop = this.textarea.scrollHeight;
 
-    const lines = this.textarea.value.split('\n').slice(0, line+1);
+    const lines = this.textarea.value.split('\n').slice(0, line + 1);
+    /* eslint-disable no-param-reassign, no-return-assign */
     const pos = lines
-        .map(lineStr => lineStr.length + 1) // correct length+1 of each lines
-        .reduce((a, x) => a += x, 0)        // sum
-        - 1;                                // -1
+      .map((lineStr) => { return lineStr.length + 1 }) // correct length+1 of each lines
+      .reduce((a, x) => { return a += x }, 0) //          sum
+        - 1; //                                           -1
+    /* eslint-enable no-param-reassign, no-return-assign */
 
     this.textarea.setSelectionRange(pos, pos);
   }
@@ -149,13 +151,13 @@ export default class TextAreaEditor extends AbstractEditor {
 
   getBolPos() {
     const currentPos = this.textarea.selectionStart;
-    return this.textarea.value.lastIndexOf('\n', currentPos-1) + 1;
+    return this.textarea.value.lastIndexOf('\n', currentPos - 1) + 1;
   }
 
   getEolPos() {
     const currentPos = this.textarea.selectionStart;
     const pos = this.textarea.value.indexOf('\n', currentPos);
-    if (pos < 0) {  // not found but EOF
+    if (pos < 0) { // not found but EOF
       return this.textarea.value.length;
     }
     return pos;
@@ -197,7 +199,7 @@ export default class TextAreaEditor extends AbstractEditor {
     }
 
     const context = {
-      handlers: [],  // list of handlers which process enter key
+      handlers: [], // list of handlers which process enter key
       editor: this,
     };
 
@@ -205,7 +207,7 @@ export default class TextAreaEditor extends AbstractEditor {
     interceptorManager.process('preHandleEnter', context)
       .then(() => {
         event.preventDefault();
-        if (context.handlers.length == 0) {
+        if (context.handlers.length === 0) {
           mlu.newlineAndIndentContinueMarkdownList(this);
         }
       });
@@ -240,21 +242,24 @@ export default class TextAreaEditor extends AbstractEditor {
   }
 
   render() {
-    return <React.Fragment>
-      <FormControl
-        componentClass="textarea" className="textarea-editor"
-        inputRef={ref => { this.textarea = ref }}
-        defaultValue={this.state.value}
-        onChange={(e) => {
+    return (
+      <React.Fragment>
+        <FormControl
+          componentClass="textarea"
+          className="textarea-editor"
+          inputRef={(ref) => { this.textarea = ref }}
+          defaultValue={this.state.value}
+          onChange={(e) => {
           if (this.props.onChange != null) {
             this.props.onChange(e.target.value);
           }
-        }} />
-    </React.Fragment>;
+        }}
+        />
+      </React.Fragment>
+    );
   }
 
 }
 
 TextAreaEditor.propTypes = Object.assign({
 }, AbstractEditor.propTypes);
-

+ 8 - 5
src/client/js/components/PageHistory/RevisionDiff.js

@@ -7,9 +7,9 @@ import { Diff2Html } from 'diff2html';
 export default class RevisionDiff extends React.Component {
 
   render() {
-    const currentRevision = this.props.currentRevision,
-      previousRevision = this.props.previousRevision,
-      revisionDiffOpened = this.props.revisionDiffOpened;
+    const currentRevision = this.props.currentRevision;
+    const previousRevision = this.props.previousRevision;
+    const revisionDiffOpened = this.props.revisionDiffOpened;
 
 
     let diffViewHTML = '';
@@ -18,6 +18,8 @@ export default class RevisionDiff extends React.Component {
       && revisionDiffOpened) {
 
       let previousText = previousRevision.body;
+      // comparing ObjectId
+      // eslint-disable-next-line eqeqeq
       if (currentRevision._id == previousRevision._id) {
         previousText = '';
       }
@@ -25,15 +27,16 @@ export default class RevisionDiff extends React.Component {
       const patch = createPatch(
         currentRevision.path,
         previousText,
-        currentRevision.body
+        currentRevision.body,
       );
 
       diffViewHTML = Diff2Html.getPrettyHtml(patch);
     }
 
-    const diffView = {__html: diffViewHTML};
+    const diffView = { __html: diffViewHTML };
     return <div className="revision-history-diff" dangerouslySetInnerHTML={diffView} />;
   }
+
 }
 
 RevisionDiff.propTypes = {

+ 3 - 3
src/client/js/components/PageList/ListView.js

@@ -7,17 +7,18 @@ export default class ListView extends React.Component {
 
   render() {
     const listView = this.props.pages.map((page) => {
-      return <Page page={page} key={'page-list:list-view:' + page._id} />;
+      return <Page page={page} key={`page-list:list-view:${page._id}`} />;
     });
 
     return (
       <div className="page-list">
         <ul className="page-list-ul page-list-ul-flat">
-        {listView}
+          {listView}
         </ul>
       </div>
     );
   }
+
 }
 
 ListView.propTypes = {
@@ -25,5 +26,4 @@ ListView.propTypes = {
 };
 
 ListView.defaultProps = {
-  pages: [],
 };

+ 3 - 4
src/client/js/components/PageList/Page.js

@@ -15,7 +15,7 @@ export default class Page extends React.Component {
     }
 
     const styleFlex = {
-      flex: 1
+      flex: 1,
     };
 
     return (
@@ -26,20 +26,19 @@ export default class Page extends React.Component {
         </a>
         <PageListMeta page={page} />
         <div style={styleFlex}></div>
-        {this.props.children}
       </li>
     );
   }
+
 }
 
 Page.propTypes = {
   page: PropTypes.object.isRequired,
   linkTo: PropTypes.string,
+  excludePathString: PropTypes.string,
 };
 
 Page.defaultProps = {
-  page: {},
   linkTo: '',
   excludePathString: '',
 };
-

+ 2 - 3
src/client/js/components/PageList/PageListMeta.js

@@ -39,7 +39,7 @@ export default class PageListMeta extends React.Component {
     }
 
     let locked;
-    if (page.grant != 1) {
+    if (page.grant !== 1) {
       locked = <span><i className="icon-lock" /></span>;
     }
 
@@ -53,6 +53,7 @@ export default class PageListMeta extends React.Component {
       </span>
     );
   }
+
 }
 
 PageListMeta.propTypes = {
@@ -60,6 +61,4 @@ PageListMeta.propTypes = {
 };
 
 PageListMeta.defaultProps = {
-  page: {},
 };
-

+ 4 - 6
src/client/js/components/PageList/PagePath.js

@@ -6,7 +6,7 @@ import escapeStringRegexp from 'escape-string-regexp';
 export default class PagePath extends React.Component {
 
   getShortPath(path) {
-    let name = path.replace(/(\/)$/, '');
+    const name = path.replace(/(\/)$/, '');
 
     // /.../hoge/YYYY/MM/DD 形式のページ
     if (name.match(/.+\/([^/]+\/\d{4}\/\d{2}\/\d{2})$/)) {
@@ -34,7 +34,7 @@ export default class PagePath extends React.Component {
     const shortPath = this.getShortPath(pagePath);
 
     const shortPathEscaped = escapeStringRegexp(shortPath);
-    const pathPrefix = pagePath.replace(new RegExp(shortPathEscaped + '(/)?$'), '');
+    const pathPrefix = pagePath.replace(new RegExp(`${shortPathEscaped}(/)?$`), '');
 
     let classNames = ['page-path'];
     classNames = classNames.concat(this.props.additionalClassNames);
@@ -42,11 +42,10 @@ export default class PagePath extends React.Component {
     if (isShortPathOnly) {
       return <span className={classNames.join(' ')}>{shortPath}</span>;
     }
-    else {
-      return <span className={classNames.join(' ')}>{pathPrefix}<strong>{shortPath}</strong></span>;
-    }
 
+    return <span className={classNames.join(' ')}>{pathPrefix}<strong>{shortPath}</strong></span>;
   }
+
 }
 
 PagePath.propTypes = {
@@ -57,7 +56,6 @@ PagePath.propTypes = {
 };
 
 PagePath.defaultProps = {
-  page: {},
   additionalClassNames: [],
   excludePathString: '',
 };

+ 4 - 8
src/client/js/components/SearchPage/DeletePageListModal.js

@@ -12,15 +12,11 @@ export default class DeletePageListModal extends React.Component {
    */
   static get OMIT_BODY_THRES() { return 400 }
 
-  constructor(props) {
-    super(props);
-  }
-
   componentWillMount() {
   }
 
   render() {
-    if (this.props.pages === undefined || this.props.pages.length == 0) {
+    if (this.props.pages == null || this.props.pages.length === 0) {
       return <div></div>;
     }
 
@@ -44,7 +40,7 @@ export default class DeletePageListModal extends React.Component {
           <div className="d-flex justify-content-between">
             <span className="text-danger">{this.props.errorMessage}</span>
             <span className="d-flex align-items-center">
-              <Checkbox className="text-danger" onClick={this.props.toggleDeleteCompletely} inline={true}>Delete completely</Checkbox>
+              <Checkbox className="text-danger" onClick={this.props.toggleDeleteCompletely} inline>Delete completely</Checkbox>
               <span className="m-l-10">
                 <Button onClick={this.props.confirmedToDelete}><i className="icon-trash"></i>Delete</Button>
               </span>
@@ -61,7 +57,7 @@ DeletePageListModal.propTypes = {
   isShown: PropTypes.bool.isRequired,
   pages: PropTypes.array,
   errorMessage: PropTypes.string,
-  cancel: PropTypes.func.isRequired,                 // for cancel evnet handling
-  confirmedToDelete: PropTypes.func.isRequired,      // for confirmed event handling
+  cancel: PropTypes.func.isRequired, //                 for cancel evnet handling
+  confirmedToDelete: PropTypes.func.isRequired, //      for confirmed event handling
   toggleDeleteCompletely: PropTypes.func.isRequired, // for delete completely check event handling
 };

+ 23 - 19
src/client/js/components/SearchPage/SearchPageForm.js

@@ -24,35 +24,39 @@ export default class SearchPageForm extends React.Component {
 
   search() {
     const keyword = this.state.keyword;
-    this.props.onSearchFormChanged({keyword: keyword});
-    this.setState({searchedKeyword: keyword});
+    this.props.onSearchFormChanged({ keyword });
+    this.setState({ searchedKeyword: keyword });
   }
 
   onInputChange(input) { // for only submitting with button
-    this.setState({keyword: input});
+    this.setState({ keyword: input });
   }
 
   render() {
-    return <FormGroup>
-      <InputGroup>
-        <SearchForm t={this.props.t}
-          crowi={this.props.crowi}
-          onSubmit={this.search}
-          keyword={this.state.searchedKeyword}
-          onInputChange={this.onInputChange}
-        />
-        <InputGroup.Button className="">
-          <Button onClick={this.search}>
-            <i className="icon-magnifier"></i>
-          </Button >
-        </InputGroup.Button>
-      </InputGroup>
-    </FormGroup>;
+    return (
+      <FormGroup>
+        <InputGroup>
+          <SearchForm
+            t={this.props.t}
+            crowi={this.props.crowi}
+            onSubmit={this.search}
+            keyword={this.state.searchedKeyword}
+            onInputChange={this.onInputChange}
+          />
+          <InputGroup.Button className="">
+            <Button onClick={this.search}>
+              <i className="icon-magnifier"></i>
+            </Button>
+          </InputGroup.Button>
+        </InputGroup>
+      </FormGroup>
+    );
   }
+
 }
 
 SearchPageForm.propTypes = {
-  t: PropTypes.func.isRequired,               // i18next
+  t: PropTypes.func.isRequired, // i18next
   crowi: PropTypes.object.isRequired,
   keyword: PropTypes.string,
   onSearchFormChanged: PropTypes.func.isRequired,

+ 88 - 70
src/client/js/components/SearchPage/SearchResult.js

@@ -1,3 +1,5 @@
+/* eslint-disable jsx-a11y/label-has-associated-control */
+/* eslint-disable jsx-a11y/label-has-for */
 import React from 'react';
 import PropTypes from 'prop-types';
 import * as toastr from 'toastr';
@@ -8,6 +10,7 @@ import DeletePageListModal from './DeletePageListModal';
 
 // Search.SearchResult
 export default class SearchResult extends React.Component {
+
   constructor(props) {
     super(props);
     this.state = {
@@ -50,8 +53,8 @@ export default class SearchResult extends React.Component {
     else {
       this.state.selectedPages.add(page);
     }
-    this.setState({isDeleteConfirmModalShown: false});
-    this.setState({selectedPages: this.state.selectedPages});
+    this.setState({ isDeleteConfirmModalShown: false });
+    this.setState({ selectedPages: this.state.selectedPages });
   }
 
   /**
@@ -61,7 +64,7 @@ export default class SearchResult extends React.Component {
    * @memberof SearchResult
    */
   isAllSelected() {
-    return this.state.selectedPages.size == this.props.pages.length;
+    return this.state.selectedPages.size === this.props.pages.length;
   }
 
   /**
@@ -77,10 +80,12 @@ export default class SearchResult extends React.Component {
       this.state.selectedPages.clear();
       this.props.pages.map((page) => {
         this.state.selectedPages.add(page);
+        return;
       });
     }
-    this.setState({selectedPages: this.state.selectedPages});
+    this.setState({ selectedPages: this.state.selectedPages });
   }
+
   /**
    * change deletion mode
    *
@@ -88,7 +93,7 @@ export default class SearchResult extends React.Component {
    */
   handleDeletionModeChange() {
     this.state.selectedPages.clear();
-    this.setState({deletionMode: !this.state.deletionMode});
+    this.setState({ deletionMode: !this.state.deletionMode });
   }
 
   /**
@@ -98,7 +103,7 @@ export default class SearchResult extends React.Component {
    */
   toggleDeleteCompletely() {
     // request で completely が undefined でないと指定アリと見なされるため
-    this.setState({isDeleteCompletely: this.state.isDeleteCompletely? undefined : true});
+    this.setState({ isDeleteCompletely: this.state.isDeleteCompletely ? undefined : true });
   }
 
   /**
@@ -107,41 +112,41 @@ export default class SearchResult extends React.Component {
    * @memberof SearchResult
    */
   deleteSelectedPages() {
-    let deleteCompletely = this.state.isDeleteCompletely;
+    const deleteCompletely = this.state.isDeleteCompletely;
     Promise.all(Array.from(this.state.selectedPages).map((page) => {
       return new Promise((resolve, reject) => {
         const pageId = page._id;
         const revisionId = page.revision._id;
-        this.props.crowi.apiPost('/pages.remove', {page_id: pageId, revision_id: revisionId, completely: deleteCompletely})
-          .then(res => {
+        this.props.crowi.apiPost('/pages.remove', { page_id: pageId, revision_id: revisionId, completely: deleteCompletely })
+          .then((res) => {
             if (res.ok) {
               this.state.selectedPages.delete(page);
               return resolve();
             }
-            else {
-              return reject();
-            }
+
+            return reject();
+
           })
-          .catch(err => {
-            console.log(err.message);   // eslint-disable-line no-console
-            this.setState({errorMessageForDeleting: err.message});
+          .catch((err) => {
+            console.log(err.message); // eslint-disable-line no-console
+            this.setState({ errorMessageForDeleting: err.message });
             return reject();
           });
       });
     }))
-    .then(() => {
-      window.location.reload();
-    })
-    .catch(err => {
-      toastr.error(err, 'Error occured', {
-        closeButton: true,
-        progressBar: true,
-        newestOnTop: false,
-        showDuration: '100',
-        hideDuration: '100',
-        timeOut: '3000',
+      .then(() => {
+        window.location.reload();
+      })
+      .catch((err) => {
+        toastr.error(err, 'Error occured', {
+          closeButton: true,
+          progressBar: true,
+          newestOnTop: false,
+          showDuration: '100',
+          hideDuration: '100',
+          timeOut: '3000',
+        });
       });
-    });
   }
 
   /**
@@ -150,7 +155,7 @@ export default class SearchResult extends React.Component {
    * @memberof SearchResult
    */
   showDeleteConfirmModal() {
-    this.setState({isDeleteConfirmModalShown: true});
+    this.setState({ isDeleteConfirmModalShown: true });
   }
 
   /**
@@ -168,8 +173,8 @@ export default class SearchResult extends React.Component {
   render() {
     const excludePathString = this.props.tree;
 
-    //console.log(this.props.searchError);
-    //console.log(this.isError());
+    // console.log(this.props.searchError);
+    // console.log(this.isError());
     if (this.isError()) {
       return (
         <div className="content-main">
@@ -189,7 +194,7 @@ export default class SearchResult extends React.Component {
       }
       return (
         <div className="content-main">
-            <i className="icon-fw icon-info" /> No page found with &quot;{this.props.searchingKeyword}&quot;{under}
+          <i className="icon-fw icon-info" /> No page found with &quot;{this.props.searchingKeyword}&quot;{under}
         </div>
       );
 
@@ -199,48 +204,63 @@ export default class SearchResult extends React.Component {
     let allSelectCheck = '';
 
     if (this.state.deletionMode) {
-      deletionModeButtons =
-      <div className="btn-group">
-        <button type="button" className="btn btn-rounded btn-default btn-xs" onClick={() => this.handleDeletionModeChange()}>
-          <i className="icon-ban"/> Cancel
-        </button>
-        <button type="button" className="btn btn-rounded btn-danger btn-xs" onClick={() => this.showDeleteConfirmModal()} disabled={this.state.selectedPages.size == 0}>
-          <i className="icon-trash"/> Delete
-        </button>
-      </div>;
-      allSelectCheck =
-      <div>
-        <label>
-          <input
-            type="checkbox"
-            onClick={() => this.handleAllSelect()}
-            checked={this.isAllSelected()} />
+      deletionModeButtons = (
+        <div className="btn-group">
+          <button type="button" className="btn btn-rounded btn-default btn-xs" onClick={() => { return this.handleDeletionModeChange() }}>
+            <i className="icon-ban" /> Cancel
+          </button>
+          <button
+            type="button"
+            className="btn btn-rounded btn-danger btn-xs"
+            onClick={() => { return this.showDeleteConfirmModal() }}
+            disabled={this.state.selectedPages.size === 0}
+          >
+            <i className="icon-trash" /> Delete
+          </button>
+        </div>
+      );
+      allSelectCheck = (
+        <div>
+          <label>
+            <input
+              type="checkbox"
+              onClick={() => { return this.handleAllSelect() }}
+              checked={this.isAllSelected()}
+            />
             &nbsp;Check All
-        </label>
-      </div>;
+          </label>
+        </div>
+      );
     }
     else {
-      deletionModeButtons =
-      <div className="btn-group">
-        <button type="button" className="btn btn-default btn-rounded btn-xs" onClick={() => this.handleDeletionModeChange()}>
-          <i className="ti-check-box"/> DeletionMode
-        </button>
-      </div>;
+      deletionModeButtons = (
+        <div className="btn-group">
+          <button type="button" className="btn btn-default btn-rounded btn-xs" onClick={() => { return this.handleDeletionModeChange() }}>
+            <i className="ti-check-box" /> DeletionMode
+          </button>
+        </div>
+      );
     }
 
     const listView = this.props.pages.map((page) => {
-      const pageId = '#' + page._id;
+      const pageId = `#${page._id}`;
       return (
-        <Page page={page}
+        <Page
+          page={page}
           linkTo={pageId}
           key={page._id}
           excludePathString={excludePathString}
-          >
-          { this.state.deletionMode &&
-            <input type="checkbox" className="search-result-list-delete-checkbox"
+        >
+          { this.state.deletionMode
+            && (
+            <input
+              type="checkbox"
+              className="search-result-list-delete-checkbox"
               value={pageId}
               checked={this.state.selectedPages.has(page)}
-              onClick={() => this.toggleCheckbox(page)} />
+              onClick={() => { return this.toggleCheckbox(page) }}
+            />
+)
             }
           <div className="page-list-option">
             <a href={page.path}><i className="icon-login" /></a>
@@ -251,7 +271,7 @@ export default class SearchResult extends React.Component {
 
     // TODO あとでなんとかする
     setTimeout(() => {
-      $('#search-result-list > nav').affix({ offset: { top: 50 }});
+      $('#search-result-list > nav').affix({ offset: { top: 50 } });
     }, 1200);
 
     /*
@@ -280,10 +300,11 @@ export default class SearchResult extends React.Component {
           </div>
           <div className="col-md-8 search-result-content" id="search-result-content">
             <SearchResultList
-              crowi={this.props.crowi} crowiRenderer={this.props.crowiRenderer}
+              crowi={this.props.crowi}
+              crowiRenderer={this.props.crowiRenderer}
               pages={this.props.pages}
               searchingKeyword={this.props.searchingKeyword}
-              />
+            />
           </div>
         </div>
         <DeletePageListModal
@@ -295,9 +316,10 @@ export default class SearchResult extends React.Component {
           toggleDeleteCompletely={this.toggleDeleteCompletely}
         />
 
-      </div>//content-main
+      </div>// content-main
     );
   }
+
 }
 
 SearchResult.propTypes = {
@@ -307,12 +329,8 @@ SearchResult.propTypes = {
   pages: PropTypes.array.isRequired,
   searchingKeyword: PropTypes.string.isRequired,
   searchResultMeta: PropTypes.object.isRequired,
-  searchError: PropTypes.object
+  searchError: PropTypes.object,
 };
 SearchResult.defaultProps = {
-  tree: '',
-  pages: [],
-  searchingKeyword: '',
-  searchResultMeta: {},
   searchError: null,
 };

+ 3 - 4
src/client/js/components/SearchPage/SearchResultList.js

@@ -10,7 +10,7 @@ export default class SearchResultList extends React.Component {
   constructor(props) {
     super(props);
 
-    this.growiRenderer = new GrowiRenderer(this.props.crowi, this.props.crowiRenderer, {mode: 'searchresult'});
+    this.growiRenderer = new GrowiRenderer(this.props.crowi, this.props.crowiRenderer, { mode: 'searchresult' });
   }
 
   render() {
@@ -32,10 +32,11 @@ export default class SearchResultList extends React.Component {
 
     return (
       <div>
-      {resultList}
+        {resultList}
       </div>
     );
   }
+
 }
 
 SearchResultList.propTypes = {
@@ -46,6 +47,4 @@ SearchResultList.propTypes = {
 };
 
 SearchResultList.defaultProps = {
-  pages: [],
-  searchingKeyword: '',
 };

+ 6 - 7
src/client/js/components/User/User.js

@@ -7,7 +7,7 @@ export default class User extends React.Component {
 
   render() {
     const user = this.props.user;
-    const userLink = '/user/' + user.username;
+    const userLink = `/user/${user.username}`;
 
     const username = this.props.username;
     const name = this.props.name;
@@ -17,16 +17,17 @@ export default class User extends React.Component {
         <a href={userLink}>
           <UserPicture user={user} />
 
-          {username &&
-              <span className="user-component-username">@{user.username}</span>
+          {username
+              && <span className="user-component-username">@{user.username}</span>
           }
-          {name &&
-              <span className="user-component-name">({user.name})</span>
+          {name
+              && <span className="user-component-name">({user.name})</span>
           }
         </a>
       </span>
     );
   }
+
 }
 
 User.propTypes = {
@@ -36,6 +37,4 @@ User.propTypes = {
 };
 
 User.defaultProps = {
-  name: false,
-  username: false,
 };

+ 9 - 9
src/client/js/components/User/UserPicture.js

@@ -11,15 +11,15 @@ export default class UserPicture extends React.Component {
       return this.generateGravatarSrc(user);
     }
     // uploaded image
-    else if (user.image != null) {
+    if (user.image != null) {
       return user.image;
     }
-    else if (user.imageAttachment != null) {
+    if (user.imageAttachment != null) {
       return user.imageAttachment.filePathProxied;
     }
-    else {
-      return '/images/icons/user.svg';
-    }
+
+    return '/images/icons/user.svg';
+
   }
 
   generateGravatarSrc(user) {
@@ -29,10 +29,10 @@ export default class UserPicture extends React.Component {
   }
 
   getClassName() {
-    let className = ['img-circle', 'picture'];
+    const className = ['img-circle', 'picture'];
     // size
     if (this.props.size) {
-      className.push('picture-' + this.props.size);
+      className.push(`picture-${this.props.size}`);
     }
 
     return className.join(' ');
@@ -46,9 +46,10 @@ export default class UserPicture extends React.Component {
         src={this.getUserPicture(user)}
         alt={user.username}
         className={this.getClassName()}
-        />
+      />
     );
   }
+
 }
 
 UserPicture.propTypes = {
@@ -57,6 +58,5 @@ UserPicture.propTypes = {
 };
 
 UserPicture.defaultProps = {
-  user: {},
   size: null,
 };

+ 1 - 1
src/client/styles/scss/_editor-overlay.scss

@@ -47,7 +47,7 @@
       }
     }
 
-    a.gfm-cheatsheet-modal-link {
+    .gfm-cheatsheet-modal-link {
       color: $text-muted;
       pointer-events: all;
       cursor: pointer;