import React from 'react';
import ReactDOM from 'react-dom';
import { I18nextProvider } from 'react-i18next';
import * as toastr from 'toastr';
import io from 'socket.io-client';
import i18nFactory from './i18n';
import Xss from '../../lib/util/xss';
import Crowi from './util/Crowi';
// import CrowiRenderer from './util/CrowiRenderer';
import GrowiRenderer from './util/GrowiRenderer';
import HeaderSearchBox from './components/HeaderSearchBox';
import SearchPage from './components/SearchPage';
import PageEditor from './components/PageEditor';
import OptionsSelector from './components/PageEditor/OptionsSelector';
import { EditorOptions, PreviewOptions } from './components/PageEditor/OptionsSelector';
import SavePageControls from './components/SavePageControls';
import PageEditorByHackmd from './components/PageEditorByHackmd';
import Page from './components/Page';
import PageListSearch from './components/PageListSearch';
import PageHistory from './components/PageHistory';
import PageComments from './components/PageComments';
import CommentForm from './components/PageComment/CommentForm';
import PageAttachment from './components/PageAttachment';
import PageStatusAlert from './components/PageStatusAlert';
import SeenUserList from './components/SeenUserList';
import RevisionPath from './components/Page/RevisionPath';
import RevisionUrl from './components/Page/RevisionUrl';
import BookmarkButton from './components/BookmarkButton';
import NewPageNameInput from './components/NewPageNameInput';
import CustomCssEditor from './components/Admin/CustomCssEditor';
import CustomScriptEditor from './components/Admin/CustomScriptEditor';
import CustomHeaderEditor from './components/Admin/CustomHeaderEditor';
import * as entities from 'entities';
if (!window) {
window = {};
}
const userlang = $('body').data('userlang');
const i18n = i18nFactory(userlang);
// setup xss library
const xss = new Xss();
window.xss = xss;
const mainContent = document.querySelector('#content-main');
let pageId = null;
let pageRevisionId = null;
let pageRevisionCreatedAt = null;
let pageRevisionIdHackmdSynced = null;
let hasDraftOnHackmd = false;
let pageIdOnHackmd = null;
let pagePath;
let pageContent = '';
let markdown = '';
let slackChannels;
if (mainContent !== null) {
pageId = mainContent.getAttribute('data-page-id');
pageRevisionId = mainContent.getAttribute('data-page-revision-id');
pageRevisionCreatedAt = +mainContent.getAttribute('data-page-revision-created');
pageRevisionIdHackmdSynced = mainContent.getAttribute('data-page-revision-id-hackmd-synced') || null;
pageIdOnHackmd = mainContent.getAttribute('data-page-id-on-hackmd') || null;
hasDraftOnHackmd = !!mainContent.getAttribute('data-page-has-draft-on-hackmd');
pagePath = mainContent.attributes['data-path'].value;
slackChannels = mainContent.getAttribute('data-slack-channels') || '';
const rawText = document.getElementById('raw-text-original');
if (rawText) {
pageContent = rawText.innerHTML;
}
markdown = entities.decodeHTML(pageContent);
}
const isLoggedin = document.querySelector('.main-container.nologin') == null;
// FIXME
const crowi = new Crowi({
me: $('body').data('current-username'),
isAdmin: $('body').data('is-admin'),
csrfToken: $('body').data('csrftoken'),
}, window);
window.crowi = crowi;
crowi.setConfig(JSON.parse(document.getElementById('crowi-context-hydrate').textContent || '{}'));
if (isLoggedin) {
crowi.fetchUsers();
}
const crowiRenderer = new GrowiRenderer(crowi, null, {
mode: 'page',
isAutoSetup: false, // manually setup because plugins may configure it
renderToc: crowi.getCrowiForJquery().renderTocContent, // function for rendering Table Of Contents
});
window.crowiRenderer = crowiRenderer;
// FIXME
const isEnabledPlugins = $('body').data('plugin-enabled');
if (isEnabledPlugins) {
const crowiPlugin = window.crowiPlugin;
crowiPlugin.installAll(crowi, crowiRenderer);
}
// setup renderer after plugins are installed
crowiRenderer.setup();
// restore draft when the first time to edit
const draft = crowi.findDraft(pagePath);
if (!pageRevisionId && draft != null) {
markdown = draft;
}
/**
* define components
* key: id of element
* value: React Element
*/
const componentMappings = {
'search-top': ,
'search-sidebar': ,
'search-page': ,
'page-list-search': ,
//'revision-history': ,
'seen-user-list': ,
'bookmark-button': ,
'bookmark-button-lg': ,
'page-name-input': ,
};
// additional definitions if data exists
if (pageId) {
componentMappings['page-comments-list'] = ;
componentMappings['page-attachment'] = ;
}
if (pagePath) {
componentMappings['page'] = ;
componentMappings['revision-path'] = ;
componentMappings['revision-url'] = ;
}
let componentInstances = {};
Object.keys(componentMappings).forEach((key) => {
const elem = document.getElementById(key);
if (elem) {
componentInstances[key] = ReactDOM.render(componentMappings[key], elem);
}
});
/**
* save success handler when reloading is not needed
* @param {object} page Page instance
*/
const saveWithShortcutSuccessHandler = function(page) {
const editorMode = crowi.getCrowiForJquery().getCurrentEditorMode();
// show toastr
toastr.success(undefined, 'Saved successfully', {
closeButton: true,
progressBar: true,
newestOnTop: false,
showDuration: '100',
hideDuration: '100',
timeOut: '1200',
extendedTimeOut: '150',
});
pageId = page._id;
pageRevisionId = page.revision._id;
// set page id to SavePageControls
componentInstances.savePageControls.setPageId(pageId); // TODO fix this line failed because of i18next
// re-render Page component
if (componentInstances.page != null) {
componentInstances.page.setMarkdown(page.revision.body);
}
// re-render PageEditor component
if (componentInstances.pageEditor != null) {
const updateEditorValue = (editorMode !== 'builtin');
componentInstances.pageEditor.setMarkdown(page.revision.body, updateEditorValue);
}
// set revision id to PageEditorByHackmd
if (componentInstances.pageEditorByHackmd != null) {
componentInstances.pageEditorByHackmd.setRevisionId(pageRevisionId);
const updateEditorValue = (editorMode !== 'hackmd');
componentInstances.pageEditorByHackmd.setMarkdown(page.revision.body, updateEditorValue);
}
// clear state of PageStatusAlert
const pageStatusAlert = componentInstances.pageStatusAlert;
if (componentInstances.pageStatusAlert != null) {
pageStatusAlert.clearStatus(pageRevisionId);
}
};
const errorHandler = function(error) {
toastr.error(error.message, 'Error occured', {
closeButton: true,
progressBar: true,
newestOnTop: false,
showDuration: '100',
hideDuration: '100',
timeOut: '3000',
});
};
const saveWithShortcut = function(markdown) {
const editorMode = crowi.getCrowiForJquery().getCurrentEditorMode();
if (editorMode == null) {
// do nothing
return;
}
// get options
const options = componentInstances.savePageControls.getCurrentOptionsToSave();
let promise = undefined;
if (pageId == null) {
promise = crowi.createPage(pagePath, markdown, options);
}
else {
promise = crowi.updatePage(pageId, pageRevisionId, markdown, options);
}
promise
.then(saveWithShortcutSuccessHandler)
.catch(errorHandler);
};
const saveWithSubmitButtonSuccessHandler = function() {
crowi.clearDraft(pagePath);
location.href = pagePath;
};
const saveWithSubmitButton = function() {
const editorMode = crowi.getCrowiForJquery().getCurrentEditorMode();
if (editorMode == null) {
// do nothing
return;
}
// get options
const options = componentInstances.savePageControls.getCurrentOptionsToSave();
let promise = undefined;
// get markdown
if (editorMode === 'builtin') {
promise = Promise.resolve(componentInstances.pageEditor.getMarkdown());
}
else {
promise = componentInstances.pageEditorByHackmd.getMarkdown();
}
// create or update
if (pageId == null) {
promise = promise.then(markdown => {
return crowi.createPage(pagePath, markdown, options);
});
}
else {
promise = promise.then(markdown => {
return crowi.updatePage(pageId, pageRevisionId, markdown, options);
});
}
promise
.then(saveWithSubmitButtonSuccessHandler)
.catch(errorHandler);
};
// render SavePageControls
let savePageControls = null;
const savePageControlsElem = document.getElementById('save-page-controls');
if (savePageControlsElem) {
const grant = +savePageControlsElem.dataset.grant;
const grantGroupId = savePageControlsElem.dataset.grantGroup;
const grantGroupName = savePageControlsElem.dataset.grantGroupName;
ReactDOM.render(
{
if (savePageControls == null) {
savePageControls = elem.getWrappedInstance();
}
}}
pageId={pageId} pagePath={pagePath} slackChannels={slackChannels}
grant={grant} grantGroupId={grantGroupId} grantGroupName={grantGroupName} />
,
savePageControlsElem
);
componentInstances.savePageControls = savePageControls;
}
/*
* HackMD Editor
*/
// render PageEditorWithHackmd
let pageEditorByHackmd = null;
const pageEditorWithHackmdElem = document.getElementById('page-editor-with-hackmd');
if (pageEditorWithHackmdElem) {
pageEditorByHackmd = ReactDOM.render(
,
pageEditorWithHackmdElem
);
componentInstances.pageEditorByHackmd = pageEditorByHackmd;
}
/*
* PageEditor
*/
let pageEditor = null;
const editorOptions = new EditorOptions(crowi.editorOptions);
const previewOptions = new PreviewOptions(crowi.previewOptions);
// render PageEditor
const pageEditorElem = document.getElementById('page-editor');
if (pageEditorElem) {
pageEditor = ReactDOM.render(
,
pageEditorElem
);
componentInstances.pageEditor = pageEditor;
// set refs for setCaretLine/forceToFocus when tab is changed
crowi.setPageEditor(pageEditor);
}
// render comment form
const writeCommentElem = document.getElementById('page-comment-write');
if (writeCommentElem) {
const pageCommentsElem = componentInstances['page-comments-list'];
const postCompleteHandler = (comment) => {
if (pageCommentsElem != null) {
pageCommentsElem.retrieveData();
}
};
ReactDOM.render(
,
writeCommentElem);
}
// render OptionsSelector
const pageEditorOptionsSelectorElem = document.getElementById('page-editor-options-selector');
if (pageEditorOptionsSelectorElem) {
ReactDOM.render(
{ // set onChange event handler
// set options
pageEditor.setEditorOptions(newEditorOptions);
pageEditor.setPreviewOptions(newPreviewOptions);
// save
crowi.saveEditorOptions(newEditorOptions);
crowi.savePreviewOptions(newPreviewOptions);
}} />,
pageEditorOptionsSelectorElem
);
}
// render PageStatusAlert
let pageStatusAlert = null;
const pageStatusAlertElem = document.getElementById('page-status-alert');
if (pageStatusAlertElem) {
ReactDOM.render(
{
if (pageStatusAlert == null) {
pageStatusAlert = elem.getWrappedInstance();
}
}}
revisionId={pageRevisionId} hasDraftOnHackmd={hasDraftOnHackmd} />
,
pageStatusAlertElem
);
componentInstances.pageStatusAlert = pageStatusAlert;
}
// render for admin
const customCssEditorElem = document.getElementById('custom-css-editor');
if (customCssEditorElem != null) {
// get input[type=hidden] element
const customCssInputElem = document.getElementById('inputCustomCss');
ReactDOM.render(
,
customCssEditorElem
);
}
const customScriptEditorElem = document.getElementById('custom-script-editor');
if (customScriptEditorElem != null) {
// get input[type=hidden] element
const customScriptInputElem = document.getElementById('inputCustomScript');
ReactDOM.render(
,
customScriptEditorElem
);
}
const customHeaderEditorElem = document.getElementById('custom-header-editor');
if (customHeaderEditorElem != null) {
// get input[type=hidden] element
const customHeaderInputElem = document.getElementById('inputCustomHeader');
ReactDOM.render(
,
customHeaderEditorElem
);
}
// notification from websocket
const socket = io();
socket.on('page:update', function(data) {
// skip own trigger
if (data.user.username === crowi.me) {
return;
}
if (data.page.path == pagePath) {
// update PageStatusAlert
const pageStatusAlert = componentInstances.pageStatusAlert;
if (pageStatusAlert != null) {
pageStatusAlert.setLatestRevisionId(data.page._id.toString());
pageStatusAlert.setLastUpdateUsername(data.user.name);
}
// update PageEditorByHackmd
const pageEditorByHackmd = componentInstances.pageEditorByHackmd;
if (pageEditorByHackmd != null) {
pageEditorByHackmd.setHasDraftOnHackmd(data.page.hasDraftOnHackmd);
}
}
});
socket.on('page:editingWithHackmd', function(data) {
if (data.page.path == pagePath) {
// update PageStatusAlert
const pageStatusAlert = componentInstances.pageStatusAlert;
if (pageStatusAlert != null) {
pageStatusAlert.setHasDraftOnHackmd(data.page.hasDraftOnHackmd);
}
// update PageEditorByHackmd
const pageEditorByHackmd = componentInstances.pageEditorByHackmd;
if (pageEditorByHackmd != null) {
pageEditorByHackmd.setHasDraftOnHackmd(data.page.hasDraftOnHackmd);
}
}
});
// うわーもうー (commented by Crowi team -- 2018.03.23 Yuki Takei)
$('a[data-toggle="tab"][href="#revision-history"]').on('show.bs.tab', function() {
ReactDOM.render(, document.getElementById('revision-history'));
});