utsushiiro 7 лет назад
Родитель
Сommit
5dfc3bc745
24 измененных файлов с 585 добавлено и 394 удалено
  1. 7 3
      src/client/js/components/Admin/AdminRebuildSearch.jsx
  2. 23 18
      src/client/js/components/BookmarkButton.jsx
  3. 6 5
      src/client/js/components/Common/UserPictureList.jsx
  4. 11 9
      src/client/js/components/HeaderSearchBox.jsx
  5. 57 19
      src/client/js/components/InstallerForm.jsx
  6. 15 10
      src/client/js/components/LikeButton.jsx
  7. 13 9
      src/client/js/components/Page.jsx
  8. 13 9
      src/client/js/components/Page/RevisionLoader.jsx
  9. 14 13
      src/client/js/components/Page/RevisionRenderer.jsx
  10. 80 63
      src/client/js/components/PageComment/CommentForm.jsx
  11. 33 23
      src/client/js/components/PageEditor/Editor.jsx
  12. 46 38
      src/client/js/components/PageEditor/HandsontableModal.jsx
  13. 19 10
      src/client/js/components/PageEditor/MarkdownTableDataImportForm.jsx
  14. 32 18
      src/client/js/components/PageEditorByHackmd.jsx
  15. 6 5
      src/client/js/components/PageEditorByHackmd/HackmdEditor.jsx
  16. 24 18
      src/client/js/components/PageHistory/PageRevisionList.jsx
  17. 16 14
      src/client/js/components/PageHistory/Revision.jsx
  18. 3 2
      src/client/js/components/PagePathAutoComplete.jsx
  19. 7 8
      src/client/js/components/PageStatusAlert.jsx
  20. 10 5
      src/client/js/components/PageTagForm.jsx
  21. 29 26
      src/client/js/components/RecentCreated/RecentCreated.jsx
  22. 30 17
      src/client/js/components/SavePageControls.jsx
  23. 77 43
      src/client/js/components/SavePageControls/GrantSelector.jsx
  24. 14 9
      src/client/js/components/SlackNotification.jsx

+ 7 - 3
src/client/js/components/Admin/AdminRebuildSearch.jsx

@@ -2,6 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 
 export default class AdminRebuildSearch extends React.Component {
+
   constructor(props) {
     super(props);
 
@@ -16,19 +17,21 @@ export default class AdminRebuildSearch extends React.Component {
   componentDidMount() {
     const socket = this.props.crowi.getWebSocket();
 
-    socket.on('admin:addPageProgress', data => {
+    socket.on('admin:addPageProgress', (data) => {
       const newStates = Object.assign(data, { isCompleted: false });
       this.setState(newStates);
     });
 
-    socket.on('admin:finishAddPage', data => {
+    socket.on('admin:finishAddPage', (data) => {
       const newStates = Object.assign(data, { isCompleted: true });
       this.setState(newStates);
     });
   }
 
   render() {
-    const { total, current, skip, isCompleted } = this.state;
+    const {
+      total, current, skip, isCompleted,
+    } = this.state;
     if (total === 0) {
       return null;
     }
@@ -59,6 +62,7 @@ export default class AdminRebuildSearch extends React.Component {
       </div>
     );
   }
+
 }
 
 AdminRebuildSearch.propTypes = {

+ 23 - 18
src/client/js/components/BookmarkButton.jsx

@@ -20,12 +20,12 @@ export default class BookmarkButton extends React.Component {
       return;
     }
 
-    this.props.crowi.apiGet('/bookmarks.get', {page_id: this.props.pageId})
-    .then(res => {
-      if (res.bookmark) {
-        this.markBookmarked();
-      }
-    });
+    this.props.crowi.apiGet('/bookmarks.get', { page_id: this.props.pageId })
+      .then((res) => {
+        if (res.bookmark) {
+          this.markBookmarked();
+        }
+      });
   }
 
   handleClick(event) {
@@ -34,25 +34,25 @@ export default class BookmarkButton extends React.Component {
     const pageId = this.props.pageId;
 
     if (!this.state.bookmarked) {
-      this.props.crowi.apiPost('/bookmarks.add', {page_id: pageId})
-      .then(res => {
-        this.markBookmarked();
-      });
+      this.props.crowi.apiPost('/bookmarks.add', { page_id: pageId })
+        .then((res) => {
+          this.markBookmarked();
+        });
     }
     else {
-      this.props.crowi.apiPost('/bookmarks.remove', {page_id: pageId})
-      .then(res => {
-        this.markUnBookmarked();
-      });
+      this.props.crowi.apiPost('/bookmarks.remove', { page_id: pageId })
+        .then((res) => {
+          this.markUnBookmarked();
+        });
     }
   }
 
   markBookmarked() {
-    this.setState({bookmarked: true});
+    this.setState({ bookmarked: true });
   }
 
   markUnBookmarked() {
-    this.setState({bookmarked: false});
+    this.setState({ bookmarked: false });
   }
 
   isUserLoggedIn() {
@@ -73,12 +73,17 @@ export default class BookmarkButton extends React.Component {
     const addedClassName = addedClassNames.join(' ');
 
     return (
-      <button href="#" title="Bookmark" onClick={this.handleClick}
-          className={`btn-bookmark btn btn-default btn-circle btn-outline ${addedClassName}`}>
+      <button
+        href="#"
+        title="Bookmark"
+        onClick={this.handleClick}
+        className={`btn-bookmark btn btn-default btn-circle btn-outline ${addedClassName}`}
+      >
         <i className="icon-star"></i>
       </button>
     );
   }
+
 }
 
 BookmarkButton.propTypes = {

+ 6 - 5
src/client/js/components/Common/UserPictureList.jsx

@@ -15,24 +15,24 @@ export default class UserPictureList extends React.Component {
 
     const users = this.props.users.concat(
       // FIXME: user data cache
-      this.props.crowi.findUserByIds(userIds)
+      this.props.crowi.findUserByIds(userIds),
     );
 
     this.state = {
-      users: users,
+      users,
       tooltipUsername: '',
     };
 
   }
 
   render() {
-    const users = this.state.users.map(user => {
+    const users = this.state.users.map((user) => {
       // create Tooltip
       const tooltip = <Tooltip id={`tooltip-${user._id}`}>{user.username}</Tooltip>;
 
       return (
-        <a key={user._id} data-user-id={user._id} href={'/user/' + user.username}>
-          <OverlayTrigger overlay={tooltip} placement='bottom'>
+        <a key={user._id} data-user-id={user._id} href={`/user/${user.username}`}>
+          <OverlayTrigger overlay={tooltip} placement="bottom">
             <span key={`span-${user._id}`}>{/* workaround from https://github.com/react-bootstrap/react-bootstrap/issues/2208#issuecomment-301737531 */}
               <UserPicture user={user} size="xs" ref={`userPicture-${user._id}`} />
             </span>
@@ -47,6 +47,7 @@ export default class UserPictureList extends React.Component {
       </span>
     );
   }
+
 }
 
 UserPictureList.propTypes = {

+ 11 - 9
src/client/js/components/HeaderSearchBox.jsx

@@ -68,13 +68,14 @@ class HeaderSearchBox extends React.Component {
     return (
       <FormGroup>
         <InputGroup>
-        <InputGroup.Button className="btn-group-dropdown-scope">
-          <DropdownButton id="dbScope" title={scopeLabel}>
-            <MenuItem onClick={this.onClickAllPages}>All pages</MenuItem>
-            <MenuItem onClick={this.onClickChildren}>{ t('header_search_box.item_label.This tree') }</MenuItem>
-          </DropdownButton>
-        </InputGroup.Button>
-          <SearchForm t={this.props.t}
+          <InputGroup.Button className="btn-group-dropdown-scope">
+            <DropdownButton id="dbScope" title={scopeLabel}>
+              <MenuItem onClick={this.onClickAllPages}>All pages</MenuItem>
+              <MenuItem onClick={this.onClickChildren}>{ t('header_search_box.item_label.This tree') }</MenuItem>
+            </DropdownButton>
+          </InputGroup.Button>
+          <SearchForm
+            t={this.props.t}
             crowi={this.props.crowi}
             onInputChange={this.onInputChange}
             onSubmit={this.search}
@@ -83,16 +84,17 @@ class HeaderSearchBox extends React.Component {
           <InputGroup.Button className="btn-group-submit-search">
             <Button bsStyle="link" onClick={this.search}>
               <i className="icon-magnifier"></i>
-            </Button >
+            </Button>
           </InputGroup.Button>
         </InputGroup>
       </FormGroup>
     );
   }
+
 }
 
 HeaderSearchBox.propTypes = {
-  t: PropTypes.func.isRequired,               // i18next
+  t: PropTypes.func.isRequired, // i18next
   crowi: PropTypes.object.isRequired,
 };
 

+ 57 - 19
src/client/js/components/InstallerForm.jsx

@@ -4,6 +4,7 @@ import i18next from 'i18next';
 import { translate } from 'react-i18next';
 
 class InstallerForm extends React.Component {
+
   constructor(props) {
     super(props);
 
@@ -21,12 +22,12 @@ class InstallerForm extends React.Component {
     const axios = require('axios').create({
       headers: {
         'Content-Type': 'application/json',
-        'X-Requested-With': 'XMLHttpRequest'
+        'X-Requested-With': 'XMLHttpRequest',
       },
-      responseType: 'json'
+      responseType: 'json',
     });
-    axios.get('/_api/check_username', {params: {username: event.target.value}})
-      .then((res) => this.setState({ isValidUserName: res.data.valid }));
+    axios.get('/_api/check_username', { params: { username: event.target.value } })
+      .then((res) => { return this.setState({ isValidUserName: res.data.valid }) });
   }
 
   changeLanguage(locale) {
@@ -37,7 +38,7 @@ class InstallerForm extends React.Component {
     const hasErrorClass = this.state.isValidUserName ? '' : ' has-error';
     const unavailableUserId = this.state.isValidUserName ? '' : <span><i className="icon-fw icon-ban" />{ this.props.t('installer.unavaliable_user_id') }</span>;
     return (
-      <div className={'login-dialog p-t-10 p-b-10 col-sm-offset-4 col-sm-4' + hasErrorClass}>
+      <div className={`login-dialog p-t-10 p-b-10 col-sm-offset-4 col-sm-4${hasErrorClass}`}>
         <p className="alert alert-success">
           <strong>{ this.props.t('installer.create_initial_account') }</strong><br />
           <small>{ this.props.t('installer.initial_account_will_be_administrator_automatically') }</small>
@@ -46,43 +47,79 @@ class InstallerForm extends React.Component {
         <form role="form" action="/installer" method="post" id="register-form">
           <div className="input-group m-t-20 m-b-20 mx-auto">
             <div className="radio radio-primary radio-inline">
-              <input type="radio" id="radioLangEn" name="registerForm[app:globalLang]" value="en-US"
-                     defaultChecked={ true } onClick={() => this.changeLanguage('en-US')} />
+              <input
+                type="radio"
+                id="radioLangEn"
+                name="registerForm[app:globalLang]"
+                value="en-US"
+                defaultChecked
+                onClick={() => { return this.changeLanguage('en-US') }}
+              />
               <label htmlFor="radioLangEn">English</label>
             </div>
             <div className="radio radio-primary radio-inline">
-              <input type="radio" id="radioLangJa" name="registerForm[app:globalLang]" value="ja"
-                     defaultChecked={ false } onClick={() => this.changeLanguage('ja')} />
+              <input
+                type="radio"
+                id="radioLangJa"
+                name="registerForm[app:globalLang]"
+                value="ja"
+                defaultChecked={false}
+                onClick={() => { return this.changeLanguage('ja') }}
+              />
               <label htmlFor="radioLangJa">日本語</label>
             </div>
           </div>
 
-          <div className={'input-group' + hasErrorClass}>
+          <div className={`input-group${hasErrorClass}`}>
             <span className="input-group-addon"><i className="icon-user" /></span>
-            <input type="text" className="form-control" placeholder={ this.props.t('User ID') }
-              name="registerForm[username]" defaultValue={this.props.userName} onBlur={this.checkUserName} required />
+            <input
+              type="text"
+              className="form-control"
+              placeholder={this.props.t('User ID')}
+              name="registerForm[username]"
+              defaultValue={this.props.userName}
+              onBlur={this.checkUserName}
+              required
+            />
           </div>
           <p className="help-block">{ unavailableUserId }</p>
 
           <div className="input-group">
             <span className="input-group-addon"><i className="icon-tag" /></span>
-            <input type="text" className="form-control" placeholder={ this.props.t('Name') }
-                   name="registerForm[name]" defaultValue={ this.props.name } required />
+            <input
+              type="text"
+              className="form-control"
+              placeholder={this.props.t('Name')}
+              name="registerForm[name]"
+              defaultValue={this.props.name}
+              required
+            />
           </div>
 
           <div className="input-group">
             <span className="input-group-addon"><i className="icon-envelope" /></span>
-            <input type="email" className="form-control" placeholder={ this.props.t('Email') }
-                   name="registerForm[email]" defaultValue={ this.props.email } required />
+            <input
+              type="email"
+              className="form-control"
+              placeholder={this.props.t('Email')}
+              name="registerForm[email]"
+              defaultValue={this.props.email}
+              required
+            />
           </div>
 
           <div className="input-group">
             <span className="input-group-addon"><i className="icon-lock" /></span>
-            <input type="password" className="form-control" placeholder={ this.props.t('Password') }
-                   name="registerForm[password]" required />
+            <input
+              type="password"
+              className="form-control"
+              placeholder={this.props.t('Password')}
+              name="registerForm[password]"
+              required
+            />
           </div>
 
-          <input type="hidden" name="_csrf" value={ this.props.csrf } />
+          <input type="hidden" name="_csrf" value={this.props.csrf} />
 
           <div className="input-group m-t-30 m-b-20 d-flex justify-content-center">
             <button type="submit" className="fcbtn btn btn-success btn-1b btn-register">
@@ -100,6 +137,7 @@ class InstallerForm extends React.Component {
       </div>
     );
   }
+
 }
 
 InstallerForm.propTypes = {

+ 15 - 10
src/client/js/components/LikeButton.jsx

@@ -19,16 +19,16 @@ export default class LikeButton extends React.Component {
     const pageId = this.props.pageId;
 
     if (!this.state.isLiked) {
-      this.props.crowi.apiPost('/likes.add', {page_id: pageId})
-      .then(res => {
-        this.setState({isLiked: true});
-      });
+      this.props.crowi.apiPost('/likes.add', { page_id: pageId })
+        .then((res) => {
+          this.setState({ isLiked: true });
+        });
     }
     else {
-      this.props.crowi.apiPost('/likes.remove', {page_id: pageId})
-      .then(res => {
-        this.setState({isLiked: false});
-      });
+      this.props.crowi.apiPost('/likes.remove', { page_id: pageId })
+        .then((res) => {
+          this.setState({ isLiked: false });
+        });
     }
   }
 
@@ -50,12 +50,17 @@ export default class LikeButton extends React.Component {
     const addedClassName = addedClassNames.join(' ');
 
     return (
-      <button href="#" title="Like" onClick={this.handleClick}
-          className={`btn-like btn btn-default btn-circle btn-outline ${addedClassName}`}>
+      <button
+        href="#"
+        title="Like"
+        onClick={this.handleClick}
+        className={`btn-like btn btn-default btn-circle btn-outline ${addedClassName}`}
+      >
         <i className="icon-like"></i>
       </button>
     );
   }
+
 }
 
 LikeButton.propTypes = {

+ 13 - 9
src/client/js/components/Page.jsx

@@ -13,7 +13,7 @@ export default class Page extends React.Component {
 
     this.state = {
       markdown: this.props.markdown,
-      currentTargetTableArea: null
+      currentTargetTableArea: null,
     };
 
     this.saveHandlerForHandsontableModal = this.saveHandlerForHandsontableModal.bind(this);
@@ -30,28 +30,32 @@ export default class Page extends React.Component {
    */
   launchHandsontableModal(beginLineNumber, endLineNumber) {
     const tableLines = this.state.markdown.split(/\r\n|\r|\n/).slice(beginLineNumber - 1, endLineNumber).join('\n');
-    this.setState({currentTargetTableArea: {beginLineNumber, endLineNumber}});
+    this.setState({ currentTargetTableArea: { beginLineNumber, endLineNumber } });
     this.refs.handsontableModal.show(MarkdownTable.fromMarkdownString(tableLines));
   }
 
   saveHandlerForHandsontableModal(markdownTable) {
     const newMarkdown = mtu.replaceMarkdownTableInMarkdown(markdownTable, this.state.markdown, this.state.currentTargetTableArea.beginLineNumber, this.state.currentTargetTableArea.endLineNumber);
     this.props.onSaveWithShortcut(newMarkdown);
-    this.setState({currentTargetTableArea: null});
+    this.setState({ currentTargetTableArea: null });
   }
 
   render() {
     const isMobile = this.props.crowi.isMobile;
 
-    return <div className={isMobile ? 'page-mobile' : ''}>
-      <RevisionRenderer
-          crowi={this.props.crowi} crowiRenderer={this.props.crowiRenderer}
+    return (
+      <div className={isMobile ? 'page-mobile' : ''}>
+        <RevisionRenderer
+          crowi={this.props.crowi}
+          crowiRenderer={this.props.crowiRenderer}
           markdown={this.state.markdown}
           pagePath={this.props.pagePath}
-      />
-      <HandsontableModal ref='handsontableModal' onSave={this.saveHandlerForHandsontableModal} />
-    </div>;
+        />
+        <HandsontableModal ref="handsontableModal" onSave={this.saveHandlerForHandsontableModal} />
+      </div>
+    );
   }
+
 }
 
 Page.propTypes = {

+ 13 - 9
src/client/js/components/Page/RevisionLoader.jsx

@@ -43,7 +43,7 @@ export default class RevisionLoader extends React.Component {
 
     // load data with REST API
     this.props.crowi.apiGet('/revisions.get', requestData)
-      .then(res => {
+      .then((res) => {
         if (!res.ok) {
           throw new Error(res.error);
         }
@@ -53,7 +53,7 @@ export default class RevisionLoader extends React.Component {
           error: null,
         });
       })
-      .catch(err => {
+      .catch((err) => {
         this.setState({ error: err });
       })
       .finally(() => {
@@ -70,9 +70,11 @@ export default class RevisionLoader extends React.Component {
   render() {
     // ----- before load -----
     if (this.props.lazy && !this.state.isLoaded) {
-      return <Waypoint onPositionChange={this.onWaypointChange} bottomOffset='-100px'>
-        <div className="wiki"></div>
-      </Waypoint>;
+      return (
+        <Waypoint onPositionChange={this.onWaypointChange} bottomOffset="-100px">
+          <div className="wiki"></div>
+        </Waypoint>
+      );
     }
 
     // ----- loading -----
@@ -94,13 +96,15 @@ export default class RevisionLoader extends React.Component {
 
     return (
       <RevisionRenderer
-          crowi={this.props.crowi} crowiRenderer={this.props.crowiRenderer}
-          pagePath={this.props.pagePath}
-          markdown={markdown}
-          highlightKeywords={this.props.highlightKeywords}
+        crowi={this.props.crowi}
+        crowiRenderer={this.props.crowiRenderer}
+        pagePath={this.props.pagePath}
+        markdown={markdown}
+        highlightKeywords={this.props.highlightKeywords}
       />
     );
   }
+
 }
 
 RevisionLoader.propTypes = {

+ 14 - 13
src/client/js/components/Page/RevisionRenderer.jsx

@@ -39,8 +39,8 @@ export default class RevisionRenderer extends React.Component {
         return;
       }
       const k = keyword
-            .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
-            .replace(/(^"|"$)/g, ''); // for phrase (quoted) keyword
+        .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+        .replace(/(^"|"$)/g, ''); // for phrase (quoted) keyword
       const keywordExp = new RegExp(`(${k}(?!(.*?")))`, 'ig');
       returnBody = returnBody.replace(keywordExp, '<em class="highlighted">$&</em>');
     });
@@ -49,7 +49,7 @@ export default class RevisionRenderer extends React.Component {
   }
 
   renderHtml(markdown, highlightKeywords) {
-    let context = {
+    const context = {
       markdown,
       currentPagePath: this.props.pagePath,
     };
@@ -57,15 +57,15 @@ export default class RevisionRenderer extends React.Component {
     const crowiRenderer = this.props.crowiRenderer;
     const interceptorManager = this.props.crowi.interceptorManager;
     interceptorManager.process('preRender', context)
-      .then(() => interceptorManager.process('prePreProcess', context))
+      .then(() => { return interceptorManager.process('prePreProcess', context) })
       .then(() => {
         context.markdown = crowiRenderer.preProcess(context.markdown);
       })
-      .then(() => interceptorManager.process('postPreProcess', context))
+      .then(() => { return interceptorManager.process('postPreProcess', context) })
       .then(() => {
-        context['parsedHTML'] = crowiRenderer.process(context.markdown);
+        context.parsedHTML = crowiRenderer.process(context.markdown);
       })
-      .then(() => interceptorManager.process('prePostProcess', context))
+      .then(() => { return interceptorManager.process('prePostProcess', context) })
       .then(() => {
         context.parsedHTML = crowiRenderer.postProcess(context.parsedHTML);
 
@@ -74,13 +74,13 @@ export default class RevisionRenderer extends React.Component {
           context.parsedHTML = this.getHighlightedBody(context.parsedHTML, highlightKeywords);
         }
       })
-      .then(() => interceptorManager.process('postPostProcess', context))
-      .then(() => interceptorManager.process('preRenderHtml', context))
+      .then(() => { return interceptorManager.process('postPostProcess', context) })
+      .then(() => { return interceptorManager.process('preRenderHtml', context) })
       .then(() => {
         this.setState({ html: context.parsedHTML, markdown });
       })
       // process interceptors for post rendering
-      .then(() => interceptorManager.process('postRenderHtml', context));
+      .then(() => { return interceptorManager.process('postRenderHtml', context) });
 
   }
 
@@ -90,12 +90,13 @@ export default class RevisionRenderer extends React.Component {
 
     return (
       <RevisionBody
-          html={this.state.html}
-          isMathJaxEnabled={isMathJaxEnabled}
-          renderMathJaxOnInit={true}
+        html={this.state.html}
+        isMathJaxEnabled={isMathJaxEnabled}
+        renderMathJaxOnInit
       />
     );
   }
+
 }
 
 RevisionRenderer.propTypes = {

+ 80 - 63
src/client/js/components/PageComment/CommentForm.jsx

@@ -1,12 +1,12 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import ReactUtils from '../ReactUtils';
 
 import Button from 'react-bootstrap/es/Button';
 import Tab from 'react-bootstrap/es/Tab';
 import Tabs from 'react-bootstrap/es/Tabs';
-import UserPicture from '../User/UserPicture';
 import * as toastr from 'toastr';
+import UserPicture from '../User/UserPicture';
+import ReactUtils from '../ReactUtils';
 
 import GrowiRenderer from '../../util/GrowiRenderer';
 
@@ -47,7 +47,7 @@ export default class CommentForm extends React.Component {
       slackChannels: this.props.slackChannels,
     };
 
-    this.growiRenderer = new GrowiRenderer(this.props.crowi, this.props.crowiOriginRenderer, {mode: 'comment'});
+    this.growiRenderer = new GrowiRenderer(this.props.crowi, this.props.crowiOriginRenderer, { mode: 'comment' });
 
     this.updateState = this.updateState.bind(this);
     this.updateStateCheckbox = this.updateStateCheckbox.bind(this);
@@ -70,17 +70,17 @@ export default class CommentForm extends React.Component {
       return;
     }
 
-    const layoutType = this.props.crowi.getConfig()['layoutType'];
-    this.setState({isLayoutTypeGrowi: 'crowi-plus' === layoutType || 'growi' === layoutType});
+    const layoutType = this.props.crowi.getConfig().layoutType;
+    this.setState({ isLayoutTypeGrowi: layoutType === 'crowi-plus' || layoutType === 'growi' });
   }
 
   updateState(value) {
-    this.setState({comment: value});
+    this.setState({ comment: value });
   }
 
   updateStateCheckbox(event) {
     const value = event.target.checked;
-    this.setState({isMarkdown: value});
+    this.setState({ isMarkdown: value });
     // changeMode
     this.refs.editor.setGfmMode(value);
   }
@@ -91,11 +91,11 @@ export default class CommentForm extends React.Component {
   }
 
   onSlackEnabledFlagChange(value) {
-    this.setState({isSlackEnabled: value});
+    this.setState({ isSlackEnabled: value });
   }
 
   onSlackChannelsChange(value) {
-    this.setState({slackChannels: value});
+    this.setState({ slackChannels: value });
   }
 
   /**
@@ -117,34 +117,35 @@ export default class CommentForm extends React.Component {
       slackNotificationForm: {
         isSlackEnabled: this.state.isSlackEnabled,
         slackChannels: this.state.slackChannels,
-      }
+      },
     })
-    .then((res) => {
-      if (this.props.onPostComplete != null) {
-        this.props.onPostComplete(res.comment);
-      }
-      this.setState({
-        comment: '',
-        isMarkdown: true,
-        html: '',
-        key: 1,
-        errorMessage: undefined,
-        isSlackEnabled: false,
+      .then((res) => {
+        if (this.props.onPostComplete != null) {
+          this.props.onPostComplete(res.comment);
+        }
+        this.setState({
+          comment: '',
+          isMarkdown: true,
+          html: '',
+          key: 1,
+          errorMessage: undefined,
+          isSlackEnabled: false,
+        });
+        // reset value
+        this.refs.editor.setValue('');
+      })
+      .catch((err) => {
+        const errorMessage = err.message || 'An unknown error occured when posting comment';
+        this.setState({ errorMessage });
       });
-      // reset value
-      this.refs.editor.setValue('');
-    })
-    .catch(err => {
-      const errorMessage = err.message || 'An unknown error occured when posting comment';
-      this.setState({ errorMessage });
-    });
   }
 
   getCommentHtml() {
     return (
       <CommentPreview
-        inputRef={el => this.previewElement = el}
-        html={this.state.html} />
+        inputRef={(el) => { return this.previewElement = el }}
+        html={this.state.html}
+      />
     );
   }
 
@@ -156,30 +157,30 @@ export default class CommentForm extends React.Component {
     const growiRenderer = this.growiRenderer;
     const interceptorManager = this.props.crowi.interceptorManager;
     interceptorManager.process('preRenderCommnetPreview', context)
-      .then(() => interceptorManager.process('prePreProcess', context))
+      .then(() => { return interceptorManager.process('prePreProcess', context) })
       .then(() => {
         context.markdown = growiRenderer.preProcess(context.markdown);
       })
-      .then(() => interceptorManager.process('postPreProcess', context))
+      .then(() => { return interceptorManager.process('postPreProcess', context) })
       .then(() => {
         const parsedHTML = growiRenderer.process(context.markdown);
-        context['parsedHTML'] = parsedHTML;
+        context.parsedHTML = parsedHTML;
       })
-      .then(() => interceptorManager.process('prePostProcess', context))
+      .then(() => { return interceptorManager.process('prePostProcess', context) })
       .then(() => {
         context.parsedHTML = growiRenderer.postProcess(context.parsedHTML);
       })
-      .then(() => interceptorManager.process('postPostProcess', context))
-      .then(() => interceptorManager.process('preRenderCommentPreviewHtml', context))
+      .then(() => { return interceptorManager.process('postPostProcess', context) })
+      .then(() => { return interceptorManager.process('preRenderCommentPreviewHtml', context) })
       .then(() => {
         this.setState({ html: context.parsedHTML });
       })
       // process interceptors for post rendering
-      .then(() => interceptorManager.process('postRenderCommentPreviewHtml', context));
+      .then(() => { return interceptorManager.process('postRenderCommentPreviewHtml', context) });
   }
 
   generateInnerHtml(html) {
-    return {__html: html};
+    return { __html: html };
   }
 
   onUpload(file) {
@@ -202,7 +203,7 @@ export default class CommentForm extends React.Component {
         // when image
         if (attachment.fileFormat.startsWith('image/')) {
           // modify to "![fileName](url)" syntax
-          insertText = '!' + insertText;
+          insertText = `!${insertText}`;
         }
         this.refs.editor.insertText(insertText);
       })
@@ -238,7 +239,7 @@ export default class CommentForm extends React.Component {
     const user = crowi.findUser(username);
     const creatorsPage = `/user/${username}`;
     const comment = this.state.comment;
-    const commentPreview = this.state.isMarkdown ? this.getCommentHtml(): ReactUtils.nl2br(comment);
+    const commentPreview = this.state.isMarkdown ? this.getCommentHtml() : ReactUtils.nl2br(comment);
     const emojiStrategy = this.props.crowi.getEmojiStrategy();
 
     const isLayoutTypeGrowi = this.state.isLayoutTypeGrowi;
@@ -254,61 +255,73 @@ export default class CommentForm extends React.Component {
       <div>
 
         <form className="form page-comment-form" id="page-comment-form" onSubmit={this.postComment}>
-          { username &&
+          { username
+            && (
             <div className="comment-form">
-              { isLayoutTypeGrowi &&
+              { isLayoutTypeGrowi
+                && (
                 <div className="comment-form-user">
                   <a href={creatorsPage}>
                     <UserPicture user={user} />
                   </a>
                 </div>
+)
               }
               <div className="comment-form-main">
                 {/* Add Comment Button */}
-                { !this.state.isFormShown &&
+                { !this.state.isFormShown
+                  && (
                   <button className={`btn btn-lg ${isLayoutTypeGrowi ? 'btn-link' : 'btn-primary'} center-block`} onClick={this.showCommentFormBtnClickHandler}>
                     <i className="icon-bubble"></i> Add Comment
                   </button>
+)
                 }
                 {/* Editor */}
-                { this.state.isFormShown && <React.Fragment>
+                { this.state.isFormShown && (
+                <React.Fragment>
                   <div className="comment-write">
                     <Tabs activeKey={this.state.key} id="comment-form-tabs" onSelect={this.handleSelect} animation={false}>
                       <Tab eventKey={1} title="Write">
-                        <Editor ref="editor"
-                          value={this.state.comment}
-                          isGfmMode={this.state.isMarkdown}
-                          editorOptions={this.props.editorOptions}
-                          lineNumbers={false}
-                          isMobile={this.props.crowi.isMobile}
-                          isUploadable={this.state.isUploadable && this.state.isLayoutTypeGrowi}  // enable only when GROWI layout
-                          isUploadableFile={this.state.isUploadableFile}
-                          emojiStrategy={emojiStrategy}
-                          onChange={this.updateState}
-                          onUpload={this.onUpload}
-                          onCtrlEnter={this.postComment}
-                        />
+                        <Editor
+              ref="editor"
+              value={this.state.comment}
+              isGfmMode={this.state.isMarkdown}
+              editorOptions={this.props.editorOptions}
+              lineNumbers={false}
+              isMobile={this.props.crowi.isMobile}
+              isUploadable={this.state.isUploadable && this.state.isLayoutTypeGrowi} // enable only when GROWI layout
+              isUploadableFile={this.state.isUploadableFile}
+              emojiStrategy={emojiStrategy}
+              onChange={this.updateState}
+              onUpload={this.onUpload}
+              onCtrlEnter={this.postComment}
+            />
                       </Tab>
-                      { this.state.isMarkdown == true &&
+                      { this.state.isMarkdown == true
+                      && (
                       <Tab eventKey={2} title="Preview">
                         <div className="comment-form-preview">
-                        {commentPreview}
+                          {commentPreview}
                         </div>
                       </Tab>
+)
                       }
                     </Tabs>
                   </div>
                   <div className="comment-submit">
                     <div className="d-flex">
-                      <label style={{flex: 1}}>
-                      { isLayoutTypeGrowi && this.state.key == 1 &&
+                      <label style={{ flex: 1 }}>
+                        { isLayoutTypeGrowi && this.state.key == 1
+                        && (
                         <span>
                           <input type="checkbox" id="comment-form-is-markdown" name="isMarkdown" checked={this.state.isMarkdown} value="1" onChange={this.updateStateCheckbox} /> Markdown
                         </span>
+)
                       }
                       </label>
                       <span className="hidden-xs">{ this.state.errorMessage && errorMessage }</span>
-                      { this.state.hasSlackConfig &&
+                      { this.state.hasSlackConfig
+                        && (
                         <div className="form-inline align-self-center mr-md-2">
                           <SlackNotification
                             crowi={this.props.crowi}
@@ -320,6 +333,7 @@ export default class CommentForm extends React.Component {
                             onChannelChange={this.onSlackChannelsChange}
                           />
                         </div>
+)
                       }
                       <div className="hidden-xs">{submitButton}</div>
                     </div>
@@ -330,15 +344,18 @@ export default class CommentForm extends React.Component {
                       </div>
                     </div>
                   </div>
-                </React.Fragment>}
+                </React.Fragment>
+)}
               </div>
             </div>
+)
           }
         </form>
 
       </div>
     );
   }
+
 }
 
 CommentForm.propTypes = {

+ 33 - 23
src/client/js/components/PageEditor/Editor.jsx

@@ -1,11 +1,11 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
+import Dropzone from 'react-dropzone';
 import AbstractEditor from './AbstractEditor';
 import CodeMirrorEditor from './CodeMirrorEditor';
 import TextAreaEditor from './TextAreaEditor';
 
-import Dropzone from 'react-dropzone';
 
 import pasteHelper from './PasteHelper';
 
@@ -117,9 +117,9 @@ export default class Editor extends AbstractEditor {
       try {
         const file = items[i].getAsFile();
         // check type and size
-        if (file != null &&
-            pasteHelper.fileAccepted(file, dropzone.props.accept) &&
-            pasteHelper.fileMatchSize(file, dropzone.props.maxSize, dropzone.props.minSize)) {
+        if (file != null
+            && pasteHelper.fileAccepted(file, dropzone.props.accept)
+            && pasteHelper.fileMatchSize(file, dropzone.props.maxSize, dropzone.props.minSize)) {
 
           this.dispatchUpload(file);
           this.setState({ isUploading: true });
@@ -159,13 +159,13 @@ export default class Editor extends AbstractEditor {
   }
 
   getDropzoneAccept() {
-    let accept = 'null';    // reject all
+    let accept = 'null'; // reject all
     if (this.props.isUploadable) {
       if (!this.props.isUploadableFile) {
         accept = 'image/*'; // image only
       }
       else {
-        accept = '';        // allow all
+        accept = ''; // allow all
       }
     }
 
@@ -196,11 +196,13 @@ export default class Editor extends AbstractEditor {
   renderDropzoneOverlay() {
     return (
       <div className="overlay overlay-dropzone-active">
-        {this.state.isUploading &&
+        {this.state.isUploading
+          && (
           <span className="overlay-content">
             <div className="speeding-wheel d-inline-block"></div>
             <span className="sr-only">Uploading...</span>
           </span>
+)
         }
         {!this.state.isUploading && <span className="overlay-content"></span>}
       </div>
@@ -239,46 +241,54 @@ export default class Editor extends AbstractEditor {
     return (
       <div style={flexContainer} className="editor-container">
         <Dropzone
-            ref="dropzone"
-            disableClick
-            accept={this.getDropzoneAccept()}
-            className={this.getDropzoneClassName()}
-            acceptClassName="dropzone-accepted"
-            rejectClassName="dropzone-rejected"
-            multiple={false}
-            onDragLeave={this.dragLeaveHandler}
-            onDrop={this.dropHandler}
-          >
+          ref="dropzone"
+          disableClick
+          accept={this.getDropzoneAccept()}
+          className={this.getDropzoneClassName()}
+          acceptClassName="dropzone-accepted"
+          rejectClassName="dropzone-rejected"
+          multiple={false}
+          onDragLeave={this.dragLeaveHandler}
+          onDrop={this.dropHandler}
+        >
 
           { this.state.dropzoneActive && this.renderDropzoneOverlay() }
 
           { this.state.isComponentDidMount && this.renderNavbar() }
 
           {/* for PC */}
-          { !isMobile &&
+          { !isMobile
+            && (
             <CodeMirrorEditor
               ref="cmEditor"
               onPasteFiles={this.pasteFilesHandler}
               onDragEnter={this.dragEnterHandler}
               {...this.props}
             />
+)
           }
 
           {/* for mobile */}
-          { isMobile &&
+          { isMobile
+            && (
             <TextAreaEditor
               ref="taEditor"
               onPasteFiles={this.pasteFilesHandler}
               onDragEnter={this.dragEnterHandler}
               {...this.props}
             />
+)
           }
 
         </Dropzone>
 
-        { this.props.isUploadable &&
-          <button type="button" className="btn btn-default btn-block btn-open-dropzone"
-            onClick={() => {this.refs.dropzone.open()}}>
+        { this.props.isUploadable
+          && (
+          <button
+            type="button"
+            className="btn btn-default btn-block btn-open-dropzone"
+            onClick={() => { this.refs.dropzone.open() }}
+          >
 
             <i className="icon-paper-clip" aria-hidden="true"></i>&nbsp;
             Attach files
@@ -288,6 +298,7 @@ export default class Editor extends AbstractEditor {
               or pasting from the clipboard.
             </span>
           </button>
+)
         }
       </div>
     );
@@ -304,4 +315,3 @@ Editor.propTypes = Object.assign({
   onChange: PropTypes.func,
   onUpload: PropTypes.func,
 }, AbstractEditor.propTypes);
-

+ 46 - 38
src/client/js/components/PageEditor/HandsontableModal.jsx

@@ -13,10 +13,10 @@ import MarkdownTable from '../../models/MarkdownTable';
 
 const DEFAULT_HOT_HEIGHT = 300;
 const MARKDOWNTABLE_TO_HANDSONTABLE_ALIGNMENT_SYMBOL_MAPPING = {
-  'r': 'htRight',
-  'c': 'htCenter',
-  'l': 'htLeft',
-  '': ''
+  r: 'htRight',
+  c: 'htCenter',
+  l: 'htLeft',
+  '': '',
 };
 
 export default class HandsontableModal extends React.PureComponent {
@@ -81,7 +81,7 @@ export default class HandsontableModal extends React.PureComponent {
       {
         markdownTableOnInit: initMarkdownTable,
         markdownTable: initMarkdownTable.clone(),
-      }
+      },
     );
 
     this.manuallyResizedColumnIndicesSet.clear();
@@ -90,11 +90,15 @@ export default class HandsontableModal extends React.PureComponent {
   createCustomizedContextMenu() {
     return {
       items: {
-        'row_above': {}, 'row_below': {}, 'col_left': {}, 'col_right': {},
-        'separator1': Handsontable.plugins.ContextMenu.SEPARATOR,
-        'remove_row': {}, 'remove_col': {},
-        'separator2': Handsontable.plugins.ContextMenu.SEPARATOR,
-        'custom_alignment': {
+        row_above: {},
+        row_below: {},
+        col_left: {},
+        col_right: {},
+        separator1: Handsontable.plugins.ContextMenu.SEPARATOR,
+        remove_row: {},
+        remove_col: {},
+        separator2: Handsontable.plugins.ContextMenu.SEPARATOR,
+        custom_alignment: {
           name: 'Align columns',
           key: 'align_columns',
           submenu: {
@@ -102,20 +106,20 @@ export default class HandsontableModal extends React.PureComponent {
               {
                 name: 'Left',
                 key: 'align_columns:1',
-                callback: (key, selection) => {this.align('l', selection[0].start.col, selection[0].end.col)}
+                callback: (key, selection) => { this.align('l', selection[0].start.col, selection[0].end.col) },
               }, {
                 name: 'Center',
                 key: 'align_columns:2',
-                callback: (key, selection) => {this.align('c', selection[0].start.col, selection[0].end.col)}
+                callback: (key, selection) => { this.align('c', selection[0].start.col, selection[0].end.col) },
               }, {
                 name: 'Right',
                 key: 'align_columns:3',
-                callback: (key, selection) => {this.align('r', selection[0].start.col, selection[0].end.col)}
-              }
-            ]
-          }
-        }
-      }
+                callback: (key, selection) => { this.align('r', selection[0].start.col, selection[0].end.col) },
+              },
+            ],
+          },
+        },
+      },
     };
   }
 
@@ -150,7 +154,7 @@ export default class HandsontableModal extends React.PureComponent {
   save() {
     const markdownTable = new MarkdownTable(
       this.refs.hotTable.hotInstance.getData(),
-      {align: [].concat(this.state.markdownTable.options.align)}
+      { align: [].concat(this.state.markdownTable.options.align) },
     ).normalizeCells();
 
     if (this.props.onSave != null) {
@@ -284,11 +288,11 @@ export default class HandsontableModal extends React.PureComponent {
     else if (columns[columns.length - 1] < target) {
       insertPosition = target - columns.length;
     }
-    align.splice.apply(align, [insertPosition, 0].concat(removed));
+    align.splice(...[insertPosition, 0].concat(removed));
 
     this.setState((prevState) => {
       // change only align info, so share table data to avoid redundant copy
-      const newMarkdownTable = new MarkdownTable(prevState.markdownTable.table, {align: align});
+      const newMarkdownTable = new MarkdownTable(prevState.markdownTable.table, { align });
       return { markdownTable: newMarkdownTable };
     }, () => {
       this.synchronizeAlignment();
@@ -301,8 +305,8 @@ export default class HandsontableModal extends React.PureComponent {
   align(direction, startCol, endCol) {
     this.setState((prevState) => {
       // change only align info, so share table data to avoid redundant copy
-      const newMarkdownTable = new MarkdownTable(prevState.markdownTable.table, {align: [].concat(prevState.markdownTable.options.align)});
-      for (let i = startCol; i <= endCol ; i++) {
+      const newMarkdownTable = new MarkdownTable(prevState.markdownTable.table, { align: [].concat(prevState.markdownTable.options.align) });
+      for (let i = startCol; i <= endCol; i++) {
         newMarkdownTable.options.align[i] = direction;
       }
       return { markdownTable: newMarkdownTable };
@@ -415,7 +419,7 @@ export default class HandsontableModal extends React.PureComponent {
         <Modal.Body className="p-0 d-flex flex-column">
           <div className="px-4 py-3 modal-navbar">
             <Button className="m-r-20 data-import-button" onClick={this.toggleDataImportArea}>
-              Data Import<i className={this.state.isDataImportAreaExpanded ? 'fa fa-angle-up' : 'fa fa-angle-down' }></i>
+              Data Import<i className={this.state.isDataImportAreaExpanded ? 'fa fa-angle-up' : 'fa fa-angle-down'}></i>
             </Button>
             <ButtonGroup>
               <Button onClick={() => { this.alignButtonHandler('l') }}><i className="ti-align-left"></i></Button>
@@ -424,20 +428,23 @@ export default class HandsontableModal extends React.PureComponent {
             </ButtonGroup>
             <Collapse in={this.state.isDataImportAreaExpanded}>
               <div> {/* This div is necessary for smoothing animations. (https://react-bootstrap.github.io/utilities/transitions/#transitions-collapse) */}
-                <MarkdownTableDataImportForm onCancel={this.toggleDataImportArea} onImport={this.importData}/>
+                <MarkdownTableDataImportForm onCancel={this.toggleDataImportArea} onImport={this.importData} />
               </div>
             </Collapse>
           </div>
           <div ref="hotTableContainer" className="m-4 hot-table-container">
-            <HotTable ref='hotTable' data={this.state.markdownTable.table}
-                settings={this.handsontableSettings} height={this.state.handsontableHeight}
-                afterLoadData={this.afterLoadDataHandler}
-                modifyColWidth={this.modifyColWidthHandler}
-                beforeColumnMove={this.beforeColumnMoveHandler}
-                beforeColumnResize={this.beforeColumnResizeHandler}
-                afterColumnResize={this.afterColumnResizeHandler}
-                afterColumnMove={this.afterColumnMoveHandler}
-              />
+            <HotTable
+              ref="hotTable"
+              data={this.state.markdownTable.table}
+              settings={this.handsontableSettings}
+              height={this.state.handsontableHeight}
+              afterLoadData={this.afterLoadDataHandler}
+              modifyColWidth={this.modifyColWidthHandler}
+              beforeColumnMove={this.beforeColumnMoveHandler}
+              beforeColumnResize={this.beforeColumnResizeHandler}
+              afterColumnResize={this.afterColumnResizeHandler}
+              afterColumnMove={this.afterColumnMoveHandler}
+            />
           </div>
         </Modal.Body>
         <Modal.Footer>
@@ -461,8 +468,8 @@ export default class HandsontableModal extends React.PureComponent {
         ['', '', ''],
       ],
       {
-        align: ['', '', '']
-      }
+        align: ['', '', ''],
+      },
     );
   }
 
@@ -475,11 +482,12 @@ export default class HandsontableModal extends React.PureComponent {
       manualColumnMove: true,
       manualColumnResize: true,
       selectionMode: 'multiple',
-      outsideClickDeselects: false
+      outsideClickDeselects: false,
     };
   }
+
 }
 
 HandsontableModal.propTypes = {
-  onSave: PropTypes.func
+  onSave: PropTypes.func,
 };

+ 19 - 10
src/client/js/components/PageEditor/MarkdownTableDataImportForm.jsx

@@ -4,8 +4,8 @@ import FormGroup from 'react-bootstrap/es/FormGroup';
 import ControlLabel from 'react-bootstrap/es/ControlLabel';
 import FormControl from 'react-bootstrap/es/FormControl';
 import Button from 'react-bootstrap/es/Button';
-import MarkdownTable from '../../models/MarkdownTable';
 import Collapse from 'react-bootstrap/es/Collapse';
+import MarkdownTable from '../../models/MarkdownTable';
 
 export default class MarkdownTableDataImportForm extends React.Component {
 
@@ -15,7 +15,7 @@ export default class MarkdownTableDataImportForm extends React.Component {
     this.state = {
       dataFormat: 'csv',
       data: '',
-      parserErrorMessage: null
+      parserErrorMessage: null,
     };
 
     this.importButtonHandler = this.importButtonHandler.bind(this);
@@ -25,10 +25,10 @@ export default class MarkdownTableDataImportForm extends React.Component {
     try {
       const markdownTable = this.convertFormDataToMarkdownTable();
       this.props.onImport(markdownTable);
-      this.setState({parserErrorMessage: null});
+      this.setState({ parserErrorMessage: null });
     }
     catch (e) {
-      this.setState({parserErrorMessage: e.message});
+      this.setState({ parserErrorMessage: e.message });
     }
   }
 
@@ -53,8 +53,12 @@ export default class MarkdownTableDataImportForm extends React.Component {
       <form action="" className="data-import-form pt-5">
         <FormGroup>
           <ControlLabel>Select Data Format</ControlLabel>
-          <FormControl componentClass="select" placeholder="select"
-                       value={this.state.dataFormat} onChange={e => this.setState({dataFormat: e.target.value})}>
+          <FormControl
+            componentClass="select"
+            placeholder="select"
+            value={this.state.dataFormat}
+            onChange={(e) => { return this.setState({ dataFormat: e.target.value }) }}
+          >
             <option value="csv">CSV</option>
             <option value="tsv">TSV</option>
             <option value="html">HTML</option>
@@ -62,13 +66,17 @@ export default class MarkdownTableDataImportForm extends React.Component {
         </FormGroup>
         <FormGroup>
           <ControlLabel>Import Data</ControlLabel>
-          <FormControl componentClass="textarea" placeholder="Paste table data" style={{ height: 200 }}
-                       onChange={e => this.setState({data: e.target.value})}/>
+          <FormControl
+            componentClass="textarea"
+            placeholder="Paste table data"
+            style={{ height: 200 }}
+            onChange={(e) => { return this.setState({ data: e.target.value }) }}
+          />
         </FormGroup>
         <Collapse in={this.state.parserErrorMessage != null}>
           <FormGroup>
             <ControlLabel>Parse Error</ControlLabel>
-            <FormControl componentClass="textarea" style={{ height: 100 }} value={this.state.parserErrorMessage || ''} readOnly/>
+            <FormControl componentClass="textarea" style={{ height: 100 }} value={this.state.parserErrorMessage || ''} readOnly />
           </FormGroup>
         </Collapse>
         <div className="d-flex justify-content-end">
@@ -78,9 +86,10 @@ export default class MarkdownTableDataImportForm extends React.Component {
       </form>
     );
   }
+
 }
 
 MarkdownTableDataImportForm.propTypes = {
   onCancel: PropTypes.func,
-  onImport: PropTypes.func
+  onImport: PropTypes.func,
 };

+ 32 - 18
src/client/js/components/PageEditorByHackmd.jsx

@@ -45,7 +45,7 @@ export default class PageEditorByHackmd extends React.PureComponent {
     }
 
     return this.refs.hackmdEditor.getValue()
-      .then(document => {
+      .then((document) => {
         this.setState({ markdown: document });
         return document;
       });
@@ -123,7 +123,7 @@ export default class PageEditorByHackmd extends React.PureComponent {
       pageId: this.props.pageId,
     };
     this.props.crowi.apiPost('/hackmd.integrate', params)
-      .then(res => {
+      .then((res) => {
         if (!res.ok) {
           throw new Error(res.error);
         }
@@ -136,7 +136,7 @@ export default class PageEditorByHackmd extends React.PureComponent {
       })
       .catch(this.apiErrorHandler)
       .then(() => {
-        this.setState({isInitializing: false});
+        this.setState({ isInitializing: false });
       });
   }
 
@@ -144,14 +144,14 @@ export default class PageEditorByHackmd extends React.PureComponent {
    * Start to edit w/o any api request
    */
   resumeToEdit() {
-    this.setState({isInitialized: true});
+    this.setState({ isInitialized: true });
   }
 
   /**
    * Reset draft
    */
   discardChanges() {
-    this.setState({hasDraftOnHackmd: false});
+    this.setState({ hasDraftOnHackmd: false });
   }
 
   /**
@@ -174,10 +174,10 @@ export default class PageEditorByHackmd extends React.PureComponent {
       pageId: this.props.pageId,
     };
     this.props.crowi.apiPost('/hackmd.saveOnHackmd', params)
-      .then(res => {
+      .then((res) => {
         // do nothing
       })
-      .catch(err => {
+      .catch((err) => {
         // do nothing
       });
   }
@@ -202,7 +202,7 @@ export default class PageEditorByHackmd extends React.PureComponent {
     if (this.state.isInitialized) {
       return (
         <HackmdEditor
-          ref='hackmdEditor'
+          ref="hackmdEditor"
           hackmdUri={hackmdUri}
           pageIdOnHackmd={this.state.pageIdOnHackmd}
           initializationMarkdown={isResume ? null : this.state.markdown}
@@ -218,7 +218,7 @@ export default class PageEditorByHackmd extends React.PureComponent {
     const isRevisionOutdated = this.state.initialRevisionId !== this.state.revisionId;
     const isHackmdDocumentOutdated = this.state.revisionId !== this.state.revisionIdHackmdSynced;
 
-    let content = undefined;
+    let content;
     /*
      * HackMD is not setup
      */
@@ -238,31 +238,40 @@ export default class PageEditorByHackmd extends React.PureComponent {
         <React.Fragment>
           <span className="btn-label"><i className="icon-control-end"></i></span>
           Resume to edit with HackMD
-        </React.Fragment>);
+        </React.Fragment>
+      );
       content = (
         <div>
           <p className="text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
           <div className="text-center hackmd-resume-button-container mb-3">
-            <SplitButton id='split-button-resume-hackmd' title={title} bsStyle="success" bsSize="large"
-                className="btn-resume waves-effect waves-light" onClick={() => this.resumeToEdit()}>
-              <MenuItem className="text-center" onClick={() => this.discardChanges()}>
+            <SplitButton
+              id="split-button-resume-hackmd"
+              title={title}
+              bsStyle="success"
+              bsSize="large"
+              className="btn-resume waves-effect waves-light"
+              onClick={() => { return this.resumeToEdit() }}
+            >
+              <MenuItem className="text-center" onClick={() => { return this.discardChanges() }}>
                 <i className="icon-control-rewind"></i> Discard changes
               </MenuItem>
             </SplitButton>
           </div>
           <p className="text-center">
             Click to edit from the previous continuation<br />
-            or <button className="btn btn-link text-danger p-0 hackmd-discard-button" onClick={() => this.discardChanges()}>Discard changes</button>.
+            or <button className="btn btn-link text-danger p-0 hackmd-discard-button" onClick={() => { return this.discardChanges() }}>Discard changes</button>.
           </p>
-          { isHackmdDocumentOutdated &&
+          { isHackmdDocumentOutdated
+            && (
             <div className="panel panel-warning mt-5">
               <div className="panel-heading"><i className="icon-fw icon-info"></i> DRAFT MAY BE OUTDATED</div>
               <div className="panel-body text-center">
                 The current draft on HackMD is based on&nbsp;
                 <a href={`?revision=${revisionIdHackmdSynced}`}><span className="label label-default">{revisionIdHackmdSynced.substr(-8)}</span></a>.<br />
-                <button className="btn btn-link text-danger p-0 hackmd-discard-button" onClick={() => this.discardChanges()}>Discard it</button> to start to edit with current revision.
+                <button className="btn btn-link text-danger p-0 hackmd-discard-button" onClick={() => { return this.discardChanges() }}>Discard it</button> to start to edit with current revision.
               </div>
             </div>
+)
           }
         </div>
       );
@@ -275,8 +284,12 @@ export default class PageEditorByHackmd extends React.PureComponent {
         <div>
           <p className="text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
           <div className="text-center hackmd-start-button-container mb-3">
-            <button className="btn btn-info btn-lg waves-effect waves-light" type="button" disabled={isRevisionOutdated || this.state.isInitializing}
-                onClick={() => this.startToEdit()}>
+            <button
+              className="btn btn-info btn-lg waves-effect waves-light"
+              type="button"
+              disabled={isRevisionOutdated || this.state.isInitializing}
+              onClick={() => { return this.startToEdit() }}
+            >
               <span className="btn-label"><i className="icon-paper-plane"></i></span>
               Start to edit with HackMD
             </button>
@@ -292,6 +305,7 @@ export default class PageEditorByHackmd extends React.PureComponent {
       </div>
     );
   }
+
 }
 
 PageEditorByHackmd.propTypes = {

+ 6 - 5
src/client/js/components/PageEditorByHackmd/HackmdEditor.jsx

@@ -26,23 +26,23 @@ export default class HackmdEditor extends React.PureComponent {
   }
 
   initHackmdWithPenpal() {
-    const _this = this;   // for in methods scope
+    const _this = this; // for in methods scope
 
     const url = `${this.props.hackmdUri}/${this.props.pageIdOnHackmd}?both`;
 
     const connection = Penpal.connectToChild({
       url,
       appendTo: this.refs.iframeContainer,
-      methods: {  // expose methods to HackMD
+      methods: { // expose methods to HackMD
         notifyBodyChanges(document) {
           _this.notifyBodyChangesHandler(document);
         },
         saveWithShortcut(document) {
           _this.saveWithShortcutHandler(document);
-        }
+        },
       },
     });
-    connection.promise.then(child => {
+    connection.promise.then((child) => {
       this.hackmd = child;
       if (this.props.initializationMarkdown != null) {
         child.setValueOnInit(this.props.initializationMarkdown);
@@ -78,9 +78,10 @@ export default class HackmdEditor extends React.PureComponent {
   render() {
     return (
       // will be rendered in componentDidMount
-      <div id='iframe-hackmd-container' ref='iframeContainer'></div>
+      <div id="iframe-hackmd-container" ref="iframeContainer"></div>
     );
   }
+
 }
 
 HackmdEditor.propTypes = {

+ 24 - 18
src/client/js/components/PageHistory/PageRevisionList.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import Revision     from './Revision';
+import Revision from './Revision';
 import RevisionDiff from './RevisionDiff';
 
 export default class PageRevisionList extends React.Component {
@@ -46,14 +46,16 @@ export default class PageRevisionList extends React.Component {
           isCompactNodiffRevisions={this.state.isCompactNodiffRevisions}
           onDiffOpenClicked={this.props.onDiffOpenClicked}
           key={`revision-history-rev-${revisionId}`}
-          />
-        { hasDiff &&
+        />
+        { hasDiff
+          && (
           <RevisionDiff
             revisionDiffOpened={revisionDiffOpened}
             currentRevision={revision}
             previousRevision={previousRevision}
             key={`revision-deff-${revisionId}`}
           />
+)
         }
       </div>
     );
@@ -62,14 +64,16 @@ export default class PageRevisionList extends React.Component {
   render() {
     const { t } = this.props;
 
-    const revisions = this.props.revisions,
-      revisionCount = this.props.revisions.length;
+    const revisions = this.props.revisions;
+
+
+    const revisionCount = this.props.revisions.length;
 
     let hasDiffPrev;
 
     const revisionList = this.props.revisions.map((revision, idx) => {
       let previousRevision;
-      if (idx+1 < revisionCount) {
+      if (idx + 1 < revisionCount) {
         previousRevision = revisions[idx + 1];
       }
       else {
@@ -89,23 +93,25 @@ export default class PageRevisionList extends React.Component {
       classNames.push('revision-history-list-compact');
     }
 
-    return <React.Fragment>
-      <div className='checkbox checkbox-info pull-right'>
-        <input id='cbCompactize' type='checkbox' value={true} checked={this.state.isCompactNodiffRevisions} onChange={this.cbCompactizeChangeHandler}></input>
-        <label htmlFor='cbCompactize'>{ t('Shrink versions that have no diffs') }</label>
-      </div>
-      <div className="clearfix"></div>
-      <div className={classNames.join(' ')}>
-        {revisionList}
-      </div>
-    </React.Fragment>;
+    return (
+      <React.Fragment>
+        <div className="checkbox checkbox-info pull-right">
+          <input id="cbCompactize" type="checkbox" value checked={this.state.isCompactNodiffRevisions} onChange={this.cbCompactizeChangeHandler}></input>
+          <label htmlFor="cbCompactize">{ t('Shrink versions that have no diffs') }</label>
+        </div>
+        <div className="clearfix"></div>
+        <div className={classNames.join(' ')}>
+          {revisionList}
+        </div>
+      </React.Fragment>
+    );
   }
+
 }
 
 PageRevisionList.propTypes = {
-  t: PropTypes.func.isRequired,               // i18next
+  t: PropTypes.func.isRequired, // i18next
   revisions: PropTypes.array,
   diffOpened: PropTypes.object,
   onDiffOpenClicked: PropTypes.func.isRequired,
 };
-

+ 16 - 14
src/client/js/components/PageHistory/Revision.jsx

@@ -1,8 +1,8 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import UserDate     from '../Common/UserDate';
-import UserPicture  from '../User/UserPicture';
+import UserDate from '../Common/UserDate';
+import UserPicture from '../User/UserPicture';
 
 export default class Revision extends React.Component {
 
@@ -26,7 +26,7 @@ export default class Revision extends React.Component {
 
     let pic = '';
     if (typeof author === 'object') {
-      pic = <UserPicture user={author} size='sm' />;
+      pic = <UserPicture user={author} size="sm" />;
     }
 
     return (
@@ -52,7 +52,7 @@ export default class Revision extends React.Component {
 
     let pic = '';
     if (typeof author === 'object') {
-      pic = <UserPicture user={author} size='lg' />;
+      pic = <UserPicture user={author} size="lg" />;
     }
 
     const iconClass = this.props.revisionDiffOpened ? 'caret caret-opened' : 'caret';
@@ -70,17 +70,19 @@ export default class Revision extends React.Component {
               <UserDate dateTime={revision.createdAt} />
             </p>
             <p>
-              <span className='d-inline-block' style={{ minWidth: '90px' }}>
-                { !this.props.hasDiff &&
-                  <span className='text-muted'>{ t('No diff') }</span>
+              <span className="d-inline-block" style={{ minWidth: '90px' }}>
+                { !this.props.hasDiff
+                  && <span className="text-muted">{ t('No diff') }</span>
                 }
-                { this.props.hasDiff &&
+                { this.props.hasDiff
+                  && (
                   <a className="diff-view" onClick={this._onDiffOpenClicked}>
                     <i className={iconClass}></i> { t('View diff') }
                   </a>
+)
                 }
               </span>
-              <a href={'?revision=' + revision._id } className="m-l-10">
+              <a href={`?revision=${revision._id}`} className="m-l-10">
                 <i className="icon-login"></i> { t('Go to this version') }
               </a>
             </p>
@@ -96,18 +98,18 @@ export default class Revision extends React.Component {
     if (this.props.isCompactNodiffRevisions && !this.props.hasDiff) {
       return this.renderSimplifiedNodiff(revision);
     }
-    else {
-      return this.renderFull(revision);
-    }
+
+    return this.renderFull(revision);
+
   }
+
 }
 
 Revision.propTypes = {
-  t: PropTypes.func.isRequired,               // i18next
+  t: PropTypes.func.isRequired, // i18next
   revision: PropTypes.object,
   revisionDiffOpened: PropTypes.bool.isRequired,
   hasDiff: PropTypes.bool.isRequired,
   isCompactNodiffRevisions: PropTypes.bool.isRequired,
   onDiffOpenClicked: PropTypes.func.isRequired,
 };
-

+ 3 - 2
src/client/js/components/PagePathAutoComplete.jsx

@@ -41,12 +41,12 @@ export default class PagePathAutoComplete extends React.Component {
 
   render() {
     return (
-      <div ref='rootDom'>
+      <div ref="rootDom">
         <SearchTypeahead
           ref={this.searchTypeaheadDom}
           crowi={this.crowi}
           onSubmit={this.onSubmit}
-          inputName='new_path'
+          inputName="new_path"
           emptyLabelExceptError={null}
           placeholder="Input page path"
           keywordOnInit={this.getKeywordOnInit(this.props.initializedPath)}
@@ -54,6 +54,7 @@ export default class PagePathAutoComplete extends React.Component {
       </div>
     );
   }
+
 }
 
 PagePathAutoComplete.propTypes = {

+ 7 - 8
src/client/js/components/PageStatusAlert.jsx

@@ -116,21 +116,20 @@ class PageStatusAlert extends React.Component {
     if (isHackmdDocumentOutdated && isRevisionOutdated) {
       content = this.renderUpdatedAlert();
     }
-    else {
-      if (this.state.isDraftUpdatingInRealtime) {
-        content = this.renderSomeoneEditingAlert();
-      }
-      else if (this.state.hasDraftOnHackmd) {
-        content = this.renderDraftExistsAlert();
-      }
+    else if (this.state.isDraftUpdatingInRealtime) {
+      content = this.renderSomeoneEditingAlert();
+    }
+    else if (this.state.hasDraftOnHackmd) {
+      content = this.renderDraftExistsAlert();
     }
 
     return content;
   }
+
 }
 
 PageStatusAlert.propTypes = {
-  t: PropTypes.func.isRequired,               // i18next
+  t: PropTypes.func.isRequired, // i18next
   crowi: PropTypes.object.isRequired,
   hasDraftOnHackmd: PropTypes.bool.isRequired,
   revisionId: PropTypes.string,

+ 10 - 5
src/client/js/components/PageTagForm.jsx

@@ -25,7 +25,7 @@ export default class PageTagForm extends React.Component {
 
   componentWillReceiveProps(nextProps) {
     this.setState({
-      pageTags: nextProps.pageTags
+      pageTags: nextProps.pageTags,
     });
   }
 
@@ -34,24 +34,29 @@ export default class PageTagForm extends React.Component {
   }
 
   updateState(value) {
-    this.setState({pageTags: value});
+    this.setState({ pageTags: value });
   }
 
   render() {
     return (
       <div className="input-group-sm mx-1">
-        <input className="form-control page-tag-form" type="text" value={this.state.pageTags} placeholder="tag name"
+        <input
+          className="form-control page-tag-form"
+          type="text"
+          value={this.state.pageTags}
+          placeholder="tag name"
           data-toggle="popover"
           title="タグ"
           data-content="タグ付けによりページをカテゴライズすることができます。"
           data-trigger="focus"
           data-placement="right"
-          onChange={e => this.updateState(e.target.value)}
+          onChange={(e) => { return this.updateState(e.target.value) }}
           onBlur={this.handleSubmit}
-          />
+        />
       </div>
     );
   }
+
 }
 
 PageTagForm.propTypes = {

+ 29 - 26
src/client/js/components/RecentCreated/RecentCreated.jsx

@@ -1,8 +1,9 @@
 import React from 'react';
-import Page from '../PageList/Page';
 
 import PropTypes from 'prop-types';
 import Pagination from 'react-bootstrap/lib/Pagination';
+import Page from '../PageList/Page';
+
 export default class RecentCreated extends React.Component {
 
   constructor(props) {
@@ -28,8 +29,10 @@ export default class RecentCreated extends React.Component {
     const offset = (selectPageNumber - 1) * limit;
 
     // pagesList get and pagination calculate
-    this.props.crowi.apiGet('/pages.recentCreated', { page_id: pageId, user: userId, limit, offset })
-      .then(res => {
+    this.props.crowi.apiGet('/pages.recentCreated', {
+      page_id: pageId, user: userId, limit, offset,
+    })
+      .then((res) => {
         const totalCount = res.totalCount;
         const pages = res.pages;
         const activePage = selectPageNumber;
@@ -45,18 +48,18 @@ export default class RecentCreated extends React.Component {
 
   calculatePagination(limit, totalCount, activePage) {
     // calc totalPageNumber
-    const totalPage = Math.floor(totalCount / limit) + (totalCount % limit === 0 ? 0  : 1);
+    const totalPage = Math.floor(totalCount / limit) + (totalCount % limit === 0 ? 0 : 1);
 
     let paginationStart = activePage - 2;
-    let maxViewPageNum =  activePage + 2;
+    let maxViewPageNum = activePage + 2;
     // pagiNation Number area size = 5 , pageNuber calculate in here
     // activePage Position calculate ex. 4 5 [6] 7 8 (Page8 over is Max), 3 4 5 [6] 7 (Page7 is Max)
-    if ( paginationStart < 1 ) {
+    if (paginationStart < 1) {
       const diff = 1 - paginationStart;
       paginationStart += diff;
       maxViewPageNum = Math.min(totalPage, maxViewPageNum + diff);
     }
-    if ( maxViewPageNum > totalPage ) {
+    if (maxViewPageNum > totalPage) {
       const diff = maxViewPageNum - totalPage;
       maxViewPageNum -= diff;
       paginationStart = Math.max(1, paginationStart - diff);
@@ -68,6 +71,7 @@ export default class RecentCreated extends React.Component {
       maxViewPageNum,
     };
   }
+
   /**
    * generate Elements of Page
    *
@@ -75,8 +79,8 @@ export default class RecentCreated extends React.Component {
    *
    */
   generatePageList(pages) {
-    return pages.map(page => {
-      return <Page page={page} key={'recent-created:list-view:' + page._id} />;
+    return pages.map((page) => {
+      return <Page page={page} key={`recent-created:list-view:${page._id}`} />;
     });
 
   }
@@ -87,21 +91,21 @@ export default class RecentCreated extends React.Component {
    * this function set << & <
    */
   generateFirstPrev(activePage) {
-    let paginationItems = [];
-    if (1 != activePage) {
+    const paginationItems = [];
+    if (activePage != 1) {
       paginationItems.push(
-        <Pagination.First key="first" onClick={() => this.getRecentCreatedList(1)} />
+        <Pagination.First key="first" onClick={() => { return this.getRecentCreatedList(1) }} />,
       );
       paginationItems.push(
-        <Pagination.Prev key="prev" onClick={() => this.getRecentCreatedList(this.state.activePage - 1)} />
+        <Pagination.Prev key="prev" onClick={() => { return this.getRecentCreatedList(this.state.activePage - 1) }} />,
       );
     }
     else {
       paginationItems.push(
-        <Pagination.First key="first" disabled />
+        <Pagination.First key="first" disabled />,
       );
       paginationItems.push(
-        <Pagination.Prev key="prev" disabled />
+        <Pagination.Prev key="prev" disabled />,
       );
 
     }
@@ -114,10 +118,10 @@ export default class RecentCreated extends React.Component {
    * this function set  numbers
    */
   generatePaginations(activePage, paginationStart, maxViewPageNum) {
-    let paginationItems = [];
+    const paginationItems = [];
     for (let number = paginationStart; number <= maxViewPageNum; number++) {
       paginationItems.push(
-        <Pagination.Item key={number} active={number === activePage} onClick={ () => this.getRecentCreatedList(number)}>{number}</Pagination.Item>
+        <Pagination.Item key={number} active={number === activePage} onClick={() => { return this.getRecentCreatedList(number) }}>{number}</Pagination.Item>,
       );
     }
     return paginationItems;
@@ -129,21 +133,21 @@ export default class RecentCreated extends React.Component {
    * this function set > & >>
    */
   generateNextLast(activePage, totalPage) {
-    let paginationItems = [];
+    const paginationItems = [];
     if (totalPage != activePage) {
       paginationItems.push(
-        <Pagination.Next key="next" onClick={() => this.getRecentCreatedList(this.state.activePage + 1)} />
+        <Pagination.Next key="next" onClick={() => { return this.getRecentCreatedList(this.state.activePage + 1) }} />,
       );
       paginationItems.push(
-        <Pagination.Last key="last" onClick={() => this.getRecentCreatedList(totalPage)} />
+        <Pagination.Last key="last" onClick={() => { return this.getRecentCreatedList(totalPage) }} />,
       );
     }
     else {
       paginationItems.push(
-        <Pagination.Next key="next" disabled />
+        <Pagination.Next key="next" disabled />,
       );
       paginationItems.push(
-        <Pagination.Last key="last" disabled />
+        <Pagination.Last key="last" disabled />,
       );
 
     }
@@ -154,12 +158,12 @@ export default class RecentCreated extends React.Component {
   render() {
     const pageList = this.generatePageList(this.state.pages);
 
-    let paginationItems = [];
+    const paginationItems = [];
 
     const activePage = this.state.activePage;
     const totalPage = this.state.paginationNumbers.totalPage;
     const paginationStart = this.state.paginationNumbers.paginationStart;
-    const maxViewPageNum =  this.state.paginationNumbers.maxViewPageNum;
+    const maxViewPageNum = this.state.paginationNumbers.maxViewPageNum;
     const firstPrevItems = this.generateFirstPrev(activePage);
     paginationItems.push(firstPrevItems);
     const paginations = this.generatePaginations(activePage, paginationStart, maxViewPageNum);
@@ -176,8 +180,8 @@ export default class RecentCreated extends React.Component {
       </div>
     );
   }
-}
 
+}
 
 
 RecentCreated.propTypes = {
@@ -188,4 +192,3 @@ RecentCreated.propTypes = {
 
 RecentCreated.defaultProps = {
 };
-

+ 30 - 17
src/client/js/components/SavePageControls.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import { translate } from 'react-i18next';
 
 import ButtonToolbar from 'react-bootstrap/es/ButtonToolbar';
-import SplitButton  from 'react-bootstrap/es/SplitButton';
+import SplitButton from 'react-bootstrap/es/SplitButton';
 import MenuItem from 'react-bootstrap/es/MenuItem';
 
 import SlackNotification from './SlackNotification';
@@ -37,7 +37,7 @@ class SavePageControls extends React.PureComponent {
    * @param {string} pageId
    */
   setPageId(pageId) {
-    this.setState({pageId});
+    this.setState({ pageId });
   }
 
   submit() {
@@ -60,31 +60,43 @@ class SavePageControls extends React.PureComponent {
       <div className="d-flex align-items-center form-inline">
         <div className="mr-2">
           <SlackNotification
-              ref='slackNotification'
-              crowi={this.props.crowi}
-              pageId={this.props.pageId}
-              pagePath={this.props.pagePath}
-              isSlackEnabled={false}
-              slackChannels={this.props.slackChannels} />
+            ref="slackNotification"
+            crowi={this.props.crowi}
+            pageId={this.props.pageId}
+            pagePath={this.props.pagePath}
+            isSlackEnabled={false}
+            slackChannels={this.props.slackChannels}
+          />
         </div>
 
-        {isAclEnabled &&
+        {isAclEnabled
+          && (
           <div className="mr-2">
-            <GrantSelector crowi={this.props.crowi}
-                ref={(elem) => {
+            <GrantSelector
+              crowi={this.props.crowi}
+              ref={(elem) => {
                   if (this.refs.grantSelector == null) {
                     this.refs.grantSelector = elem.getWrappedInstance();
                   }
                 }}
-                grant={this.props.grant}
-                grantGroupId={this.props.grantGroupId}
-                grantGroupName={this.props.grantGroupName} />
+              grant={this.props.grant}
+              grantGroupId={this.props.grantGroupId}
+              grantGroupName={this.props.grantGroupName}
+            />
           </div>
+)
         }
 
         <ButtonToolbar>
-          <SplitButton id="spl-btn-submit" bsStyle="primary" className="btn-submit" dropup pullRight onClick={this.submit}
-              title={labelSubmitButton}>
+          <SplitButton
+            id="spl-btn-submit"
+            bsStyle="primary"
+            className="btn-submit"
+            dropup
+            pullRight
+            onClick={this.submit}
+            title={labelSubmitButton}
+          >
             <MenuItem eventKey="1" onClick={this.submitAndOverwriteScopesOfDescendants}>{labelOverwriteScopes}</MenuItem>
             {/* <MenuItem divider /> */}
           </SplitButton>
@@ -92,10 +104,11 @@ class SavePageControls extends React.PureComponent {
       </div>
     );
   }
+
 }
 
 SavePageControls.propTypes = {
-  t: PropTypes.func.isRequired,               // i18next
+  t: PropTypes.func.isRequired, // i18next
   crowi: PropTypes.object.isRequired,
   onSubmit: PropTypes.func.isRequired,
   pageId: PropTypes.string,

+ 77 - 43
src/client/js/components/SavePageControls/GrantSelector.jsx

@@ -23,23 +23,33 @@ class GrantSelector extends React.Component {
     super(props);
 
     this.availableGrants = [
-      { grant: 1, iconClass: 'icon-people', styleClass: '', label: 'Public' },
-      { grant: 2, iconClass: 'icon-link', styleClass: 'text-info', label: 'Anyone with the link' },
+      {
+        grant: 1, iconClass: 'icon-people', styleClass: '', label: 'Public',
+      },
+      {
+        grant: 2, iconClass: 'icon-link', styleClass: 'text-info', label: 'Anyone with the link',
+      },
       // { grant: 3, iconClass: '', label: 'Specified users only' },
-      { grant: 4, iconClass: 'icon-lock', styleClass: 'text-danger', label: 'Just me' },
-      { grant: 5, iconClass: 'icon-options', styleClass: '', label: 'Only inside the group' },  // appeared only one of these 'grant: 5'
-      { grant: 5, iconClass: 'icon-options', styleClass: '', label: 'Reselect the group' },     // appeared only one of these 'grant: 5'
+      {
+        grant: 4, iconClass: 'icon-lock', styleClass: 'text-danger', label: 'Just me',
+      },
+      {
+        grant: 5, iconClass: 'icon-options', styleClass: '', label: 'Only inside the group',
+      }, // appeared only one of these 'grant: 5'
+      {
+        grant: 5, iconClass: 'icon-options', styleClass: '', label: 'Reselect the group',
+      }, // appeared only one of these 'grant: 5'
     ];
 
     this.state = {
-      grant: this.props.grant || 1,  // default: 1
+      grant: this.props.grant || 1, // default: 1
       userRelatedGroups: [],
       isSelectGroupModalShown: false,
     };
     if (this.props.grantGroupId !== '') {
       this.state.grantGroup = {
         _id: this.props.grantGroupId,
-        name: this.props.grantGroupName
+        name: this.props.grantGroupName,
       };
     }
 
@@ -68,7 +78,7 @@ class GrantSelector extends React.Component {
     // refresh bootstrap-select
     // see https://silviomoreto.github.io/bootstrap-select/methods/#selectpickerrefresh
     $('.grant-selector .selectpicker').selectpicker('refresh');
-    //// DIRTY HACK -- 2018.05.25 Yuki Takei
+    // // DIRTY HACK -- 2018.05.25 Yuki Takei
     // set group name to the bootstrap-select options
     //  cz: .selectpicker('refresh') doesn't replace data-content
     $('.grant-selector .group-name').text(this.getGroupName());
@@ -77,7 +87,7 @@ class GrantSelector extends React.Component {
 
   getCurrentOptionsToSave() {
     const options = {
-      grant: this.state.grant
+      grant: this.state.grant,
     };
     if (this.state.grantGroup != null) {
       options.grantUserGroupId = this.state.grantGroup._id;
@@ -89,6 +99,7 @@ class GrantSelector extends React.Component {
     this.retrieveUserGroupRelations();
     this.setState({ isSelectGroupModalShown: true });
   }
+
   hideSelectGroupModal() {
     this.setState({ isSelectGroupModalShown: false });
   }
@@ -103,14 +114,14 @@ class GrantSelector extends React.Component {
    */
   retrieveUserGroupRelations() {
     this.props.crowi.apiGet('/me/user-group-relations')
-      .then(res => {
+      .then((res) => {
         return res.userGroupRelations;
       })
-      .then(userGroupRelations => {
-        const userRelatedGroups = userGroupRelations.map(relation => {
+      .then((userGroupRelations) => {
+        const userRelatedGroups = userGroupRelations.map((relation) => {
           return relation.relatedGroup;
         });
-        this.setState({userRelatedGroups});
+        this.setState({ userRelatedGroups });
       });
   }
 
@@ -177,18 +188,29 @@ class GrantSelector extends React.Component {
 
     // add specified group option
     grantElems.push(
-      <option ref="specifiedGroupOption" key="specifiedGroupKey" value={SPECIFIED_GROUP_VALUE} style={{ display: grantGroup ? 'inherit' : 'none' }}
-          data-content={`<i class="icon icon-fw icon-organization text-success"></i> <span class="group-name text-success">${this.getGroupName()}</span>`}>
+      <option
+        ref="specifiedGroupOption"
+        key="specifiedGroupKey"
+        value={SPECIFIED_GROUP_VALUE}
+        style={{ display: grantGroup ? 'inherit' : 'none' }}
+        data-content={`<i class="icon icon-fw icon-organization text-success"></i> <span class="group-name text-success">${this.getGroupName()}</span>`}
+      >
         {this.getGroupName()}
-      </option>
+      </option>,
     );
 
     const bsClassName = 'form-control-dummy'; // set form-control* to shrink width
     return (
       <FormGroup className="grant-selector m-b-0">
-        <FormControl componentClass="select" placeholder="select" defaultValue={selectedValue} bsClass={bsClassName} className="btn-group-sm selectpicker"
+        <FormControl
+          componentClass="select"
+          placeholder="select"
+          defaultValue={selectedValue}
+          bsClass={bsClassName}
+          className="btn-group-sm selectpicker"
           onChange={this.changeGrantHandler}
-          inputRef={ el => this.grantSelectorInputEl=el }>
+          inputRef={(el) => { return this.grantSelectorInputEl = el }}
+        >
 
           {grantElems}
 
@@ -206,49 +228,61 @@ class GrantSelector extends React.Component {
   renderSelectGroupModal() {
     const generateGroupListItems = () => {
       return this.state.userRelatedGroups.map((group) => {
-        return <ListGroupItem key={group._id} header={group.name} onClick={() => { this.groupListItemClickHandler(group) }}>
+        return (
+          <ListGroupItem key={group._id} header={group.name} onClick={() => { this.groupListItemClickHandler(group) }}>
             (TBD) List group members
-          </ListGroupItem>;
+          </ListGroupItem>
+        );
       });
     };
 
-    let content = this.state.userRelatedGroups.length === 0
-      ? <div>
+    const content = this.state.userRelatedGroups.length === 0
+      ? (
+        <div>
           <h4>There is no group to which you belong.</h4>
-          { this.props.crowi.isAdmin &&
-            <p><a href="/admin/user-groups"><i className="icon icon-fw icon-login"></i> Manage Groups</a></p>
+          { this.props.crowi.isAdmin
+            && <p><a href="/admin/user-groups"><i className="icon icon-fw icon-login"></i> Manage Groups</a></p>
           }
         </div>
-      : <ListGroup>
-        {generateGroupListItems()}
-      </ListGroup>;
+      )
+      : (
+        <ListGroup>
+          {generateGroupListItems()}
+        </ListGroup>
+      );
 
     return (
-        <Modal className="select-grant-group"
-          container={this} show={this.state.isSelectGroupModalShown} onHide={this.hideSelectGroupModal}
-        >
-          <Modal.Header closeButton>
-            <Modal.Title>
+      <Modal
+        className="select-grant-group"
+        container={this}
+        show={this.state.isSelectGroupModalShown}
+        onHide={this.hideSelectGroupModal}
+      >
+        <Modal.Header closeButton>
+          <Modal.Title>
               Select a Group
-            </Modal.Title>
-          </Modal.Header>
-          <Modal.Body>
-            {content}
-          </Modal.Body>
-        </Modal>
+          </Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          {content}
+        </Modal.Body>
+      </Modal>
     );
   }
 
   render() {
-    return <React.Fragment>
-      {this.renderGrantSelector()}
-      {this.renderSelectGroupModal()}
-    </React.Fragment>;
+    return (
+      <React.Fragment>
+        {this.renderGrantSelector()}
+        {this.renderSelectGroupModal()}
+      </React.Fragment>
+    );
   }
+
 }
 
 GrantSelector.propTypes = {
-  t: PropTypes.func.isRequired,               // i18next
+  t: PropTypes.func.isRequired, // i18next
   crowi: PropTypes.object.isRequired,
   grant: PropTypes.number,
   grantGroupId: PropTypes.string,

+ 14 - 9
src/client/js/components/SlackNotification.jsx

@@ -27,7 +27,7 @@ export default class SlackNotification extends React.Component {
   componentWillReceiveProps(nextProps) {
     this.setState({
       isSlackEnabled: nextProps.isSlackEnabled,
-      slackChannels: nextProps.slackChannels
+      slackChannels: nextProps.slackChannels,
     });
   }
 
@@ -36,7 +36,7 @@ export default class SlackNotification extends React.Component {
   }
 
   updateState(value) {
-    this.setState({slackChannels: value});
+    this.setState({ slackChannels: value });
     // dispatch event
     if (this.props.onChannelChange != null) {
       this.props.onChannelChange(value);
@@ -45,7 +45,7 @@ export default class SlackNotification extends React.Component {
 
   updateStateCheckbox(event) {
     const value = event.target.checked;
-    this.setState({isSlackEnabled: value});
+    this.setState({ isSlackEnabled: value });
     // dispatch event
     if (this.props.onEnabledFlagChange != null) {
       this.props.onEnabledFlagChange(value);
@@ -56,21 +56,26 @@ export default class SlackNotification extends React.Component {
     return (
       <div className="input-group input-group-sm input-group-slack extended-setting">
         <label className="input-group-addon">
-          <img id="slack-mark-white" src="/images/icons/slack/mark-monochrome_white.svg" width="18" height="18"/>
-          <img id="slack-mark-black" src="/images/icons/slack/mark-monochrome_black.svg" width="18" height="18"/>
-          <input type="checkbox" value="1" checked={this.state.isSlackEnabled} onChange={this.updateStateCheckbox}/>
+          <img id="slack-mark-white" src="/images/icons/slack/mark-monochrome_white.svg" width="18" height="18" />
+          <img id="slack-mark-black" src="/images/icons/slack/mark-monochrome_black.svg" width="18" height="18" />
+          <input type="checkbox" value="1" checked={this.state.isSlackEnabled} onChange={this.updateStateCheckbox} />
         </label>
-        <input className="form-control" type="text" value={this.state.slackChannels} placeholder="slack channel name"
+        <input
+          className="form-control"
+          type="text"
+          value={this.state.slackChannels}
+          placeholder="slack channel name"
           data-toggle="popover"
           title="Slack通知"
           data-content="通知するにはチェックを入れてください。カンマ区切りで複数チャンネルに通知することができます。"
           data-trigger="focus"
           data-placement="top"
-          onChange={e => this.updateState(e.target.value)}
-          />
+          onChange={(e) => { return this.updateState(e.target.value) }}
+        />
       </div>
     );
   }
+
 }
 
 SlackNotification.propTypes = {