add latst sources of cbsn platform archive.
@@ -21,3 +21,11 @@
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# database contents
|
||||
/public/database/items.json
|
||||
/public/database/file
|
||||
/src/database/assets/tree.json
|
||||
/src/database/assets/rankings.json
|
||||
/src/database/assets/recent-contents.json
|
||||
/src/forum/assets/forum.json
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/config.inc.php';
|
||||
|
||||
define('PROTECTOR_SKIP_DOS_CHECK', 1);
|
||||
define('PROTECTOR_SKIP_FILESCHECKER', 1);
|
||||
|
||||
// disable query logger
|
||||
define('XOOPS_LOGGER_ADDQUERY_DISABLED', true);
|
||||
|
||||
// ----
|
||||
if (!file_exists($mainfile)) {
|
||||
echo 'ERROR : mainfile.php not found'.PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
$xoops_path = '';
|
||||
$xoops_url = '';
|
||||
|
||||
foreach (file($mainfile) as $line) {
|
||||
if (preg_match('/^\s*define\s*\(\s*[\'"]XOOPS_ROOT_PATH[\'"]\s*,\s*[\'"](.+)[\'"]\)\s*;\s*$/', $line, $matches)) {
|
||||
$xoops_path = $matches[1];
|
||||
}
|
||||
if (preg_match('/^\s*define\s*\(\s*[\'"]XOOPS_URL[\'"]\s*,\s*[\'"](.+)[\'"]\)\s*;\s*$/', $line, $matches)) {
|
||||
$xoops_url = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($method) && 'POST' == strtoupper($method)) {
|
||||
$method = 'POST';
|
||||
} else {
|
||||
$method = 'GET';
|
||||
}
|
||||
$_SERVER['HTTP_USER_AGENT'] = 'php-cli';
|
||||
$_SERVER['REQUEST_METHOD'] = $method;
|
||||
$_ENV['HTTP_REFERER'] = $xoops_url.'/index.php';
|
||||
$_SERVER['QUERY_STRING'] = '/index.php';
|
||||
$_SERVER['REMOTE_ADDR'] = '192.168.0.1';
|
||||
|
||||
if (file_exists($xoops_path.'/modules/xoonips/include/common.inc.php')) {
|
||||
require_once $xoops_path.'/modules/xoonips/include/common.inc.php';
|
||||
} else {
|
||||
require_once $xoops_path.'/mainfile.php';
|
||||
require_once $xoops_path.'/modules/xoonips/condefs.php';
|
||||
require_once $xoops_path.'/modules/xoonips/include/functions.php';
|
||||
}
|
||||
|
||||
if (defined('XOOPS_DB_PROXY') && XOOPS_DB_PROXY == 1 && 'POST' == $method) {
|
||||
die('Error: not accept POST request'."\n");
|
||||
}
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
define('MYDUMPTOOL_OUTPUTDIR', __DIR__.'/data');
|
||||
if (!is_dir(MYDUMPTOOL_OUTPUTDIR)) {
|
||||
if (!@mkdir(MYDUMPTOOL_OUTPUTDIR)) {
|
||||
exit('Failed to create output directory : '.MYDUMPTOOL_OUTPUTDIR.PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
function myDumpToolDecode(&$data)
|
||||
{
|
||||
$textutil = xoonips_getutility('text');
|
||||
foreach ($data as $k => $v) {
|
||||
$v = $textutil->html_numeric_entities($v);
|
||||
$v = mb_decode_numericentity($v, array(0x0, 0x10000, 0, 0xfffff), 'UTF-8');
|
||||
$data[$k] = \Normalizer::normalize(trim($v), \Normalizer::FORM_C);
|
||||
}
|
||||
}
|
||||
|
||||
function myDumpToolConvertToInt(&$data, $keys, $zerofill = false)
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (isset($data[$key])) {
|
||||
$data[$key] = '' === $data[$key] ? ($zerofill ? 0 : null) : (int) $data[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function myDumpToolDropColumn(&$data, $keys)
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (isset($data[$key])) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
$mainfile = '/data/www/html/mainfile.php';
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/common.inc.php';
|
||||
|
||||
$prefix = $xoopsDB->prefix();
|
||||
|
||||
$d3forum = 'forum';
|
||||
|
||||
$gidGuest = XOOPS_GROUP_ANONYMOUS;
|
||||
|
||||
// get anonymous readable categories
|
||||
$categories = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
`c`.*
|
||||
FROM `${prefix}_${d3forum}_categories` AS `c`
|
||||
INNER JOIN `${prefix}_${d3forum}_category_access` AS `ca` ON `c`.`cat_id`=`ca`.`cat_id`
|
||||
WHERE `ca`.`groupid`=${gidGuest}
|
||||
GROUP BY `c`.`cat_id`
|
||||
ORDER BY `c`.`cat_order_in_tree` ASC, `c`.`cat_weight` ASC
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
myDumpToolDecode($row);
|
||||
myDumpToolConvertToInt($row, ['cat_id', 'pid', 'cat_topics_count', 'cat_posts_count', 'cat_last_post_id', 'cat_last_post_time', 'cat_topics_count_in_tree', 'cat_posts_count_in_tree', 'cat_last_post_id_in_tree', 'cat_last_post_time_in_tree', 'cat_depth_in_tree', 'cat_order_in_tree', 'cat_weight']);
|
||||
$row['cat_path_in_tree'] = unserialize($row['cat_path_in_tree']);
|
||||
$row['cat_options'] = unserialize($row['cat_options']);
|
||||
$categories[] = $row;
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
//var_dump($categories);
|
||||
|
||||
// get anonymous readable forums
|
||||
$forums = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
`f`.*
|
||||
FROM `${prefix}_${d3forum}_forums` AS `f`
|
||||
INNER JOIN `${prefix}_${d3forum}_forum_access` AS `fa` ON `f`.`forum_id`=`fa`.`forum_id`
|
||||
INNER JOIN `${prefix}_${d3forum}_category_access` AS `ca` ON `f`.`cat_id`=`ca`.`cat_id`
|
||||
WHERE `fa`.`groupid`=${gidGuest}
|
||||
AND `ca`.`groupid`=${gidGuest}
|
||||
GROUP BY `f`.`forum_id`
|
||||
ORDER BY `f`.`cat_id` ASC, `f`.`forum_weight` ASC
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
myDumpToolDecode($row);
|
||||
myDumpToolConvertToInt($row, ['forum_id', 'cat_id', 'forum_topics_count', 'forum_posts_count', 'forum_last_post_id', 'forum_last_post_time', 'forum_weight'], true);
|
||||
$row['forum_options'] = unserialize($row['forum_options']);
|
||||
$forums[] = $row;
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
//var_dump($forums);
|
||||
|
||||
// get anonymous readable topics
|
||||
$topics = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
`t`.*,
|
||||
`uf`.`name` AS `topic_first_uid_name`, `uf`.`uname` AS `topic_first_uid_uname`,
|
||||
`ul`.`name` AS `topic_last_uid_name`, `ul`.`uname` AS `topic_last_uid_uname`
|
||||
FROM `${prefix}_${d3forum}_topics` AS `t`
|
||||
INNER JOIN `${prefix}_${d3forum}_forums` AS `f` ON `t`.`forum_id`=`f`.`forum_id`
|
||||
INNER JOIN `${prefix}_${d3forum}_forum_access` AS `fa` ON `f`.`forum_id`=`fa`.`forum_id`
|
||||
INNER JOIN `${prefix}_${d3forum}_category_access` AS `ca` ON `f`.`cat_id`=`ca`.`cat_id`
|
||||
LEFT JOIN `${prefix}_users` AS `uf` ON `t`.`topic_first_uid`=`uf`.`uid`
|
||||
LEFT JOIN `${prefix}_users` AS `ul` ON `t`.`topic_last_uid`=`ul`.`uid`
|
||||
WHERE `fa`.`groupid`=${gidGuest}
|
||||
AND `ca`.`groupid`=${gidGuest}
|
||||
GROUP BY `t`.`topic_id`
|
||||
ORDER BY `t`.`forum_id` ASC, `t`.`topic_id` ASC
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
myDumpToolDecode($row);
|
||||
myDumpToolConvertToInt($row, ['topic_id', 'forum_id', 'topic_first_uid', 'topic_first_post_id', 'topic_first_post_time', 'topic_last_uid', 'topic_last_post_id', 'topic_last_post_time', 'topic_views', 'topic_posts_count', 'topic_locked', 'topic_sticky', 'topic_solved', 'topic_invisible', 'topic_votes_sum', 'topic_votes_count'], true);
|
||||
$topics[] = $row;
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
//var_dump($topics);
|
||||
|
||||
// get anonymous readable posts
|
||||
$posts = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
`p`.*,
|
||||
`u`.`name` AS `uid_name`, `u`.`uname` AS `uid_uname`, `u`.`rank` AS `uid_rank`, `u`.`posts` AS `uid_posts`
|
||||
FROM `${prefix}_${d3forum}_posts` AS `p`
|
||||
INNER JOIN `${prefix}_${d3forum}_topics` AS `t` ON `p`.`topic_id`=`t`.`topic_id`
|
||||
INNER JOIN `${prefix}_${d3forum}_forums` AS `f` ON `t`.`forum_id`=`f`.`forum_id`
|
||||
INNER JOIN `${prefix}_${d3forum}_forum_access` AS `fa` ON `f`.`forum_id`=`fa`.`forum_id`
|
||||
INNER JOIN `${prefix}_${d3forum}_category_access` AS `ca` ON `f`.`cat_id`=`ca`.`cat_id`
|
||||
LEFT JOIN `${prefix}_users` AS `u` ON `p`.`uid`=`u`.`uid`
|
||||
WHERE `fa`.`groupid`=${gidGuest}
|
||||
AND `ca`.`groupid`=${gidGuest}
|
||||
GROUP BY `p`.`post_id`
|
||||
ORDER BY `t`.`topic_id` ASC, `p`.`post_id` ASC
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
myDumpToolDecode($row);
|
||||
myDumpToolConvertToInt($row, ['post_id', 'pid', 'topic_id', 'post_time', 'modified_time', 'uid', 'uid_hidden', 'html', 'smiley', 'xcode', 'br', 'number_entity', 'special_entity', 'icon', 'attachsig', 'invisible', 'approval', 'votes_sum', 'votes_count', 'depth_in_tree', 'order_in_tree', 'uid_rank', 'uid_posts'], true);
|
||||
myDumpToolDropColumn($row, ['guest_email', 'guest_url', 'guest_pass_md5', 'guest_trip']);
|
||||
$posts[] = $row;
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
//var_dump($posts);
|
||||
var_dump(count($posts));
|
||||
|
||||
|
||||
$data = [
|
||||
'categories' => $categories,
|
||||
'forums' => $forums,
|
||||
'topics' => $topics,
|
||||
'posts' => $posts,
|
||||
];
|
||||
|
||||
file_put_contents(MYDUMPTOOL_OUTPUTDIR.'/forum.json', json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
@@ -1,15 +1,36 @@
|
||||
{
|
||||
"name": "cbsn",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@types/async-lock": "^1.1.1",
|
||||
"@types/jest": "24.0.15",
|
||||
"@types/lokijs": "^1.5.2",
|
||||
"@types/node": "12.0.8",
|
||||
"@types/rc-tree": "^1.11.3",
|
||||
"@types/react": "16.8.20",
|
||||
"@types/react-dom": "16.8.4",
|
||||
"@types/react-helmet": "^5.0.8",
|
||||
"@types/react-overlays": "^1.1.2",
|
||||
"@types/react-router-dom": "^4.3.4",
|
||||
"@types/react-router-hash-link": "^1.2.1",
|
||||
"async-lock": "^1.2.0",
|
||||
"axios": "^0.19.0",
|
||||
"lokijs": "^1.5.6",
|
||||
"moment": "^2.24.0",
|
||||
"rc-tree": "^2.1.0",
|
||||
"react": "^16.8.6",
|
||||
"react-app-polyfill": "^1.0.1",
|
||||
"react-cookie": "^4.0.0",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-helmet": "^5.2.1",
|
||||
"react-image-lightbox": "^5.1.0",
|
||||
"react-overlays": "^1.2.0",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-router-hash-link": "^1.2.1",
|
||||
"react-scripts": "3.0.1",
|
||||
"react-spinner-material": "^1.1.1",
|
||||
"typescript": "3.5.2"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -25,12 +46,14 @@
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
"not op_mini all",
|
||||
"ie 11"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
"last 1 safari version",
|
||||
"ie 11"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
RewriteEngine on
|
||||
RewriteBase /
|
||||
|
||||
RewriteCond %{QUERY_STRING} (^|&)file_id=([0-9]+)($|&)
|
||||
RewriteRule ^modules/xoonips/download.php /database/file/%2? [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule . /index.html [L]
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -5,34 +5,15 @@
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<meta name="robots" content="index,follow" />
|
||||
<meta name="keywords" content="neuroinformatics, xoonips, database, cbsn platform, Comprehensive Brain Science Network" />
|
||||
<meta name="author" content="Neuroinformatics Unit, RIKEN Center for Brain Science" />
|
||||
<meta name="copyright" content="Copyright © 2019" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
<title>CBSN platform - XooNIps for CBSN</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"short_name": "CBSN Platform",
|
||||
"name": "Comprehensive Brain Science Network Platform",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"sizes": "16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,33 +1,286 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
/* HTML TAG Re-definition */
|
||||
body {
|
||||
background: #fff;
|
||||
font-family: Verdana, Arial, Helvetica, sans-serif, "MS Pゴシック", Osaka, "ヒラギノ角ゴ Pro W3";
|
||||
color: #666666;
|
||||
margin: 0px;
|
||||
padding: 0px 15px 15px;
|
||||
font-size: 80%;
|
||||
min-width: 880px;
|
||||
max-width: 1280px;
|
||||
}
|
||||
a {
|
||||
color: #6075aa;
|
||||
}
|
||||
a:hover {
|
||||
color: #ff9900;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
color: #286367;
|
||||
}
|
||||
h4,
|
||||
h5 {
|
||||
color: #6075aa;
|
||||
}
|
||||
ul {
|
||||
margin: 2px;
|
||||
padding: 2px;
|
||||
list-style: decimal inside;
|
||||
text-align: left;
|
||||
}
|
||||
li {
|
||||
margin-left: 2px;
|
||||
list-style: square inside;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
th {
|
||||
color: #6075aa;
|
||||
background-color: #e3e7f0;
|
||||
padding: 4px;
|
||||
vertical-align: middle;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #c7c7c7;
|
||||
}
|
||||
td {
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||
}
|
||||
legend {
|
||||
padding: 3px;
|
||||
color: #990000;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
}
|
||||
form {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
textarea {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
.clearfix::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
/* XOOPS Newbb & tabular work */
|
||||
.outer {
|
||||
border: 0px solid #cc0000;
|
||||
}
|
||||
.head,
|
||||
tr.head td {
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #c7c7c7;
|
||||
background-color: #f0f2f7;
|
||||
}
|
||||
.even {
|
||||
padding: 2px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #c7c7c7;
|
||||
background-color: #f8fafe;
|
||||
}
|
||||
.odd {
|
||||
padding: 2px;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #c7c7c7;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
tr.even td {
|
||||
padding: 2px;
|
||||
background-color: #f8fafe;
|
||||
}
|
||||
tr.odd td {
|
||||
padding: 2px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.foot {
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
.comTitle {
|
||||
font-weight: bold;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.comText {
|
||||
padding: 2px;
|
||||
}
|
||||
.comUserStat {
|
||||
font-size: 90%;
|
||||
color: #2f5376;
|
||||
font-weight: bold;
|
||||
border: 1px solid silver;
|
||||
background-color: #ffffff;
|
||||
margin: 2px;
|
||||
padding: 2px;
|
||||
}
|
||||
.comUserStatCaption {
|
||||
font-weight: normal;
|
||||
}
|
||||
.comUserStatus {
|
||||
margin-left: 2px;
|
||||
margin-top: 10px;
|
||||
color: #2f5376;
|
||||
font-weight: bold;
|
||||
font-size: 10px;
|
||||
}
|
||||
.comUserRank {
|
||||
margin: 2px;
|
||||
}
|
||||
.comUserRankText {
|
||||
font-size: 90%;
|
||||
font-weight: bold;
|
||||
}
|
||||
.comUserRankImg {
|
||||
border: 0;
|
||||
}
|
||||
.comUserImg {
|
||||
margin: 2px;
|
||||
}
|
||||
.comDate {
|
||||
font-style: normal;
|
||||
font-size: 85%;
|
||||
}
|
||||
.comDateCaption {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* XOOPS News */
|
||||
.itemHead {
|
||||
padding: 3px;
|
||||
}
|
||||
.itemInfo {
|
||||
text-align: right;
|
||||
padding: 3px;
|
||||
margin-bottom: 5px;
|
||||
border-bottom: 1px solid silver;
|
||||
font-style: italic;
|
||||
}
|
||||
.itemTitle {
|
||||
padding: 3px;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
color: #6075aa;
|
||||
}
|
||||
.itemTitle a {
|
||||
color: #286367;
|
||||
text-decoration: none;
|
||||
}
|
||||
.itemTitle a:hover {
|
||||
color: #ff9900;
|
||||
}
|
||||
.itemPoster {
|
||||
font-size: 90%;
|
||||
font-style: italic;
|
||||
}
|
||||
.itemPostDate {
|
||||
font-size: 90%;
|
||||
font-style: italic;
|
||||
}
|
||||
.itemStats {
|
||||
font-size: 80%;
|
||||
}
|
||||
.itemBody {
|
||||
padding: 3px;
|
||||
}
|
||||
.itemBody img {
|
||||
padding: 5px;
|
||||
}
|
||||
.itemTopicImage {
|
||||
float: left;
|
||||
}
|
||||
.itemText {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
.itemText:first-letter {
|
||||
font-size: 133%;
|
||||
font-weight: bold;
|
||||
}
|
||||
.itemFoot {
|
||||
text-align: right;
|
||||
padding: 3px;
|
||||
margin-top: 5px;
|
||||
background-color: #efefef;
|
||||
border: 1px solid silver;
|
||||
}
|
||||
.itemAdminLink {
|
||||
font-size: 90%;
|
||||
}
|
||||
.itemPermaLink {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* XOOPS Error Msg */
|
||||
div.errorMsg {
|
||||
background-color: #ffcccc;
|
||||
text-align: center;
|
||||
border-top: 1px solid #ddddff;
|
||||
border-left: 1px solid #ddddff;
|
||||
border-right: 1px solid #aaaaaa;
|
||||
border-bottom: 1px solid #aaaaaa;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
}
|
||||
div.confirmMsg {
|
||||
background-color: #ddffdf;
|
||||
color: #136c99;
|
||||
text-align: center;
|
||||
border-top: 1px solid #ddddff;
|
||||
border-left: 1px solid #ddddff;
|
||||
border-right: 1px solid #aaaaaa;
|
||||
border-bottom: 1px solid #aaaaaa;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
}
|
||||
div.resultMsg {
|
||||
background-color: #cccccc;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
border-top: 1px solid silver;
|
||||
border-left: 1px solid silver;
|
||||
font-weight: bold;
|
||||
border-right: 1px solid #666666;
|
||||
border-bottom: 1px solid #666666;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* XOOPS Code &Quote */
|
||||
div.xoopsCode {
|
||||
background: #ffffff url(./common/assets/images/theme/xoops_quote.jpg) no-repeat right top;
|
||||
border: 1px inset #000080;
|
||||
overflow: auto;
|
||||
max-height: 300px;
|
||||
max-width: 600px;
|
||||
}
|
||||
div.xoopsQuote {
|
||||
background: #ffffff url(./common/assets/images/theme/xoops_quote.jpg) no-repeat right top;
|
||||
border: 1px inset #000080;
|
||||
overflow: auto;
|
||||
max-height: 300px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
/* other */
|
||||
table.report {
|
||||
background-color: #ff8;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
import React from 'react';
|
||||
import logo from './logo.svg';
|
||||
import React, { Component } from 'react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { CookiesProvider } from 'react-cookie';
|
||||
import AppRoot from './common/AppRoot';
|
||||
import './App.css';
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/App.tsx</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
class App extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<CookiesProvider>
|
||||
<BrowserRouter>
|
||||
<AppRoot />
|
||||
</BrowserRouter >
|
||||
</CookiesProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
.notice {
|
||||
color: red;
|
||||
text-align: center;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.title {
|
||||
margin-top: 25px;
|
||||
border-bottom: solid 1px #cccccc;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import styles from './About.module.css';
|
||||
import Announce from './blocks/Announce';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const About = (props: Props) => {
|
||||
const {lang} = props;
|
||||
const title = Functions.mlang('[en]Announce[/en][ja]ご案内[/ja]', lang);
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>{title} - {Functions.mlang(Config.SITE_TITLE, lang)}</title>
|
||||
</Helmet>
|
||||
<h1>{title}</h1>
|
||||
<div className={styles.content}>
|
||||
<Announce lang={props.lang} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default About;
|
||||
@@ -0,0 +1,79 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ReactCookieProps, withCookies } from 'react-cookie';
|
||||
import ReactGA from 'react-ga';
|
||||
import Helmet from 'react-helmet';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import Container from './Container';
|
||||
import Footer from './Footer';
|
||||
import Header from './Header';
|
||||
|
||||
interface Props extends RouteComponentProps, ReactCookieProps { }
|
||||
interface State {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class AppRoot extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { lang: 'en' };
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const params = new URLSearchParams(nextProps.location.search);
|
||||
const param_lang = params.get('ml_lang');
|
||||
const cookie_lang = typeof nextProps.cookies !== 'undefined' ? nextProps.cookies.get('ml_lang') : null;
|
||||
let lang = param_lang || (typeof cookie_lang === 'string' ? cookie_lang : null) || prevState.lang;
|
||||
if (lang !== 'en' && lang !== 'ja') {
|
||||
lang = 'en';
|
||||
}
|
||||
if (cookie_lang !== lang) {
|
||||
if (typeof nextProps.cookies !== 'undefined') {
|
||||
nextProps.cookies.set('ml_lang', lang, { path: '/' });
|
||||
}
|
||||
}
|
||||
if (prevState.lang !== lang) {
|
||||
return { lang };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { pathname } = this.props.location;
|
||||
if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') {
|
||||
ReactGA.set({ page: pathname });
|
||||
ReactGA.pageview(pathname);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const { pathname, search } = this.props.location;
|
||||
if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') {
|
||||
ReactGA.set({ page: pathname });
|
||||
ReactGA.pageview(pathname);
|
||||
}
|
||||
if (pathname !== prevProps.location.pathname || search !== prevProps.location.search) {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.state;
|
||||
const title = Functions.mlang(Config.SITE_TITLE, lang);
|
||||
const slogan = Functions.mlang(Config.SITE_SLOGAN, lang);
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{title} - {slogan}</title>
|
||||
</Helmet>
|
||||
<Header lang={lang} />
|
||||
<Container lang={lang} />
|
||||
<Footer lang={lang} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withCookies(withRouter(AppRoot));
|
||||
@@ -0,0 +1,94 @@
|
||||
.centerColumn {
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
line-height: 150%;
|
||||
padding: 0 10px 0 15px;
|
||||
}
|
||||
|
||||
.centerBlocks {
|
||||
margin-bottom: 2em;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.centerCenterColumn {
|
||||
width: 100%;
|
||||
margin: 0 6px 20px;
|
||||
}
|
||||
|
||||
.centerCenterBlock {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.centerCenterBlockTitle {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 28px;
|
||||
font-weight: bold;
|
||||
color: #6075aa;
|
||||
text-indent: 15px;
|
||||
font-size: 11pt;
|
||||
line-height: 28px;
|
||||
border: 1px solid #b7d0d5;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px -5px 10px -5px #b7d0d5 inset;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.centerCenterBlockContent {
|
||||
margin: 10px 5px;
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
.centerLeftColumn {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.centerLeftBlock {
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.centerLeftBlockTitle {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-indent: 24px;
|
||||
font-size: 11pt;
|
||||
font-weight: bold;
|
||||
background-image: url(./assets/images/theme/title_indent2.gif);
|
||||
background-repeat: no-repeat;
|
||||
color: #365196;
|
||||
}
|
||||
|
||||
.centerLeftBlockContent {
|
||||
margin: 10px 5px;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
.centerRightColumn {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.centerRightBlock {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.centerRightBlockTitle {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-indent: 24px;
|
||||
font-size: 11pt;
|
||||
font-weight: bold;
|
||||
background-image: url(./assets/images/theme/title_indent3.gif);
|
||||
background-repeat: no-repeat;
|
||||
color: #984068;
|
||||
}
|
||||
|
||||
.centerRightBlockContent {
|
||||
margin: 10px 5px;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
.centerMainContent {
|
||||
padding: 3px 3px 10px 3px;
|
||||
line-height: 150%;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { MultiLang } from '../config';
|
||||
import Rankings from '../database/blocks/Rankings';
|
||||
import RecentContents from '../database/blocks/RecentContents';
|
||||
import DatabaseTop from '../database/DatabaseTop';
|
||||
import Announce from './blocks/Announce';
|
||||
import styles from './CenterColumn.module.css';
|
||||
import MainContent from './MainContent';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const CenterBlocks = (props: Props) => {
|
||||
const titles = props.lang === 'en' ?
|
||||
{
|
||||
announce: 'Announce',
|
||||
databaseTop: 'Item Types',
|
||||
ranking: 'Ranking',
|
||||
recentContents: 'Recent Contents',
|
||||
} : {
|
||||
announce: 'ご案内',
|
||||
databaseTop: 'アイテムタイプ一覧',
|
||||
ranking: 'ランキング',
|
||||
recentContents: '新着コンテンツ',
|
||||
};
|
||||
return (
|
||||
<table className={styles.centerBlocks}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={2} className={styles.centerCenterColumn}>
|
||||
<div className={styles.centerCenterBlock}>
|
||||
<h2 className={styles.centerCenterBlockTitle}>{titles.announce}</h2>
|
||||
<div className={styles.centerCenterBlockContent}>
|
||||
<Announce lang={props.lang} />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={2} className={styles.centerCenterColumn}>
|
||||
<div className={styles.centerCenterBlock}>
|
||||
<h2 className={styles.centerCenterBlockTitle}>{titles.databaseTop}</h2>
|
||||
<div className={styles.centerCenterBlockContent}>
|
||||
<DatabaseTop lang={props.lang} />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className={styles.centerLeftColumn}>
|
||||
<div className={styles.centerLeftBlock}>
|
||||
<h2 className={styles.centerLeftBlockTitle}>{titles.ranking}</h2>
|
||||
<div className={styles.centerLeftBlockContent}>
|
||||
<Rankings lang={props.lang} />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className={styles.centerRightColumn}>
|
||||
<div className={styles.centerRightBlock}>
|
||||
<h2 className={styles.centerRightBlockTitle}>{titles.recentContents}</h2>
|
||||
<div className={styles.centerRightBlockContent}>
|
||||
<RecentContents lang={props.lang} />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
const CenterColumn = (props: Props) => {
|
||||
return (
|
||||
<td className={styles.centerColumn}>
|
||||
<Switch>
|
||||
<Route exact path="/" render={() => <CenterBlocks lang={props.lang} />} />
|
||||
</Switch>
|
||||
<MainContent lang={props.lang} />
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
export default CenterColumn;
|
||||
@@ -0,0 +1,25 @@
|
||||
.container {
|
||||
margin: 15px 5px 30px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.wrapper td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.centerColumn {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.centerColumn table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rightColumn {
|
||||
width: 200px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../config';
|
||||
import CenterColumn from './CenterColumn';
|
||||
import styles from './Container.module.css';
|
||||
import LeftColumn from './LeftColumn';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Container = (props: Props) => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<table className={styles.wrapper}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<LeftColumn lang={props.lang} />
|
||||
<CenterColumn lang={props.lang} />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Container;
|
||||
@@ -0,0 +1,12 @@
|
||||
.footer {
|
||||
margin: 20px 0 30px;
|
||||
font-size: 87%;
|
||||
line-height: 150%;
|
||||
padding-top: 10px;
|
||||
border-top: 1px dotted #cccccc;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
padding-left: 10px;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import bannerCBSN from './assets/images/theme/banner_cbsn.gif';
|
||||
import bannerIBR from './assets/images/theme/banner_ibr.gif';
|
||||
import bannerJapanNode from './assets/images/theme/banner_incf-japan-node.gif';
|
||||
import bannerCBS from './assets/images/theme/banner_riken-cbs.gif';
|
||||
import bannerRIKEN from './assets/images/theme/banner_riken.gif';
|
||||
import bannerXooNIps from './assets/images/theme/banner_xoonips.gif';
|
||||
import { MultiLang } from '../config';
|
||||
import styles from './Footer.module.css';
|
||||
import Functions from '../functions';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Footer = (props: Props) => {
|
||||
const links = [
|
||||
{
|
||||
title: '[en]INCF Japan Node[/en][ja]INCF 日本ノード[/ja]',
|
||||
image: bannerJapanNode,
|
||||
url: 'https://www.neuroinf.jp/',
|
||||
},
|
||||
{
|
||||
title: '[en]RIKEN[/en][ja]理化学研究所[/ja]',
|
||||
image: bannerRIKEN,
|
||||
url: 'http://www.riken.jp/',
|
||||
},
|
||||
{
|
||||
title: '[en]RIKEN Center for Brain Science[/en][ja]理化学研究所 脳神経科学研究センター[/ja]',
|
||||
image: bannerCBS,
|
||||
url: 'https://cbs.riken.jp/',
|
||||
},
|
||||
{
|
||||
title: '[en]The XooNIps Project[/en][ja]XooNIps 公式サイト[/ja]',
|
||||
image: bannerXooNIps,
|
||||
url: 'https://xoonips.osdn.jp/',
|
||||
},
|
||||
{
|
||||
title: '[en]Integrative Brain Research[/en][ja]統合脳プロジェクト[/ja]',
|
||||
image: bannerIBR,
|
||||
url: 'http://www.togo-nou.nips.ac.jp/',
|
||||
},
|
||||
{
|
||||
title: '[en]Comprehensive Brain Science Network[/en][ja]包括脳プロジェクト[/ja]',
|
||||
image: bannerCBSN,
|
||||
url: 'https://www.hokatsu-nou.neuroinf.jp/',
|
||||
},
|
||||
];
|
||||
return (
|
||||
<footer className={styles.footer}>
|
||||
{links.map((link, idx) => {
|
||||
const title = Functions.mlang(link.title, props.lang);
|
||||
return <a key={idx} href={link.url} target="_blank" rel="noopener noreferrer"><img src={link.image} alt={title} title={title} /></a>
|
||||
})}
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
@@ -0,0 +1,64 @@
|
||||
.header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
vertical-align: middle;
|
||||
padding-left: 15px;
|
||||
float: left;
|
||||
height: 128px;
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
top: 0px;
|
||||
right: 15px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
float: right;
|
||||
height: 128px;
|
||||
}
|
||||
|
||||
.titleJapanese {
|
||||
font-size: 40px;
|
||||
line-height: 120px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.titleEnglish {
|
||||
font-size: 48px;
|
||||
line-height: 120px;
|
||||
}
|
||||
|
||||
.titleLink,
|
||||
.titleLink:hover,
|
||||
.titleLink:visited {
|
||||
color: #666666;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.menuBar {
|
||||
border: 1px solid #b7d0d5;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px -5px 10px -5px #b7d0d5 inset;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.menuLink {
|
||||
margin: 8px 10px;
|
||||
}
|
||||
|
||||
.menuLink a {
|
||||
color:#6075aa;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
background-image: url(./assets/images/theme/menu_indent.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
padding: 0px 10px 0px 16px;
|
||||
}
|
||||
|
||||
.menuLink a:hover {
|
||||
color:#286367;
|
||||
background-image:url(./assets/images/theme/menu_indent_h.gif);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import iconHome from './assets/images/theme/icon_home.gif';
|
||||
import iconHomeHover from './assets/images/theme/icon_home_h.gif';
|
||||
import iconInquiry from './assets/images/theme/icon_inquiry.gif';
|
||||
import iconInquiryHover from './assets/images/theme/icon_inquiry_h.gif';
|
||||
import logo from './assets/images/theme/logo.png';
|
||||
import mainMenus from './assets/main-menu.json';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import styles from './Header.module.css';
|
||||
import Functions from '../functions';
|
||||
import LangFlag from './lib/LangFlag';
|
||||
import LinkImage from './lib/LinkImage';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Header = (props: Props) => {
|
||||
const title = Functions.mlang(Config.SITE_TITLE, props.lang);
|
||||
const titleStyle = props.lang === 'en' ? styles.titleEnglish : styles.titleJapanese;
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
<div className="clearfix">
|
||||
<div className={styles.headerLeft}>
|
||||
<Link className={styles.titleLink} to="/"><span className={titleStyle}>{title}</span></Link>
|
||||
</div>
|
||||
<div className={styles.headerRight}>
|
||||
<div>
|
||||
<img src={logo} alt="Comprehensive Brain Science Network" />
|
||||
</div>
|
||||
<div className={styles.shortcut}>
|
||||
<LangFlag lang={props.lang} />
|
||||
|
||||
<LinkImage url="/" imageNormal={iconHome} imageHover={iconHomeHover} alt="Home" />
|
||||
|
||||
<LinkImage url="/about" imageNormal={iconInquiry} imageHover={iconInquiryHover} alt="Contact Us" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.menuBar}>
|
||||
<div className={styles.menuLink}>
|
||||
{mainMenus.map((item, idx) => {
|
||||
return <Link key={idx} to={item.link}>{Functions.mlang(item.title, props.lang)}</Link>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@@ -0,0 +1,28 @@
|
||||
.leftColumn {
|
||||
width: 200px;
|
||||
border-right-width: 1px;
|
||||
border-right-style: dotted;
|
||||
border-right-color: #999999;
|
||||
padding: 0 15px 0 0;
|
||||
}
|
||||
|
||||
.leftBlock {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.leftBlockTitle {
|
||||
margin: 0;
|
||||
padding-left: 24px;
|
||||
font-weight: bold;
|
||||
background-image: url(./assets/images/theme/title_indent.gif);
|
||||
background-repeat: no-repeat;
|
||||
color: #286367;
|
||||
font-size: 11pt;
|
||||
line-height: 28px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.leftBlockContent {
|
||||
margin: 5px 0 10px 0;
|
||||
line-height: 120%;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../config';
|
||||
import IndexTree from '../database/blocks/IndexTree';
|
||||
import MainMenu from './blocks/MainMenu';
|
||||
import Search from '../database/blocks/Search';
|
||||
import styles from './LeftColumn.module.css';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const LeftColumn = (props: Props) => {
|
||||
const titles = props.lang === 'en' ? {
|
||||
mainMenu: 'Main Menu',
|
||||
indexTree: 'Index Tree',
|
||||
search: 'Search',
|
||||
} : {
|
||||
mainMenu: 'メインメニュー',
|
||||
indexTree: 'インデックスツリー',
|
||||
search: '検索',
|
||||
};
|
||||
return (
|
||||
<td className={styles.leftColumn}>
|
||||
<div className={styles.leftBlock}>
|
||||
<h2 className={styles.leftBlockTitle}>{titles.mainMenu}</h2>
|
||||
<div className={styles.leftBlockContent}>
|
||||
<MainMenu lang={props.lang} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.leftBlock}>
|
||||
<h2 className={styles.leftBlockTitle}>{titles.indexTree}</h2>
|
||||
<div className={styles.leftBlockContent}>
|
||||
<IndexTree lang={props.lang} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.leftBlock}>
|
||||
<h2 className={styles.leftBlockTitle}>{titles.search}</h2>
|
||||
<div className={styles.leftBlockContent}>
|
||||
<Search lang={props.lang} />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
export default LeftColumn;
|
||||
@@ -0,0 +1,9 @@
|
||||
.mainContent {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.mainContent hr {
|
||||
color: #996600;
|
||||
height: 2px;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { MultiLang } from '../config';
|
||||
import About from './About';
|
||||
import Database from '../database/Database';
|
||||
import Forum from '../forum/Forum';
|
||||
import styles from './MainContent.module.css';
|
||||
import XoopsPathRedirect from './XoopsPathRedirect';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const MainContent = (props: Props) => {
|
||||
return (
|
||||
<div className={styles.mainContent}>
|
||||
<Switch>
|
||||
<Route path="/database" render={() => <Database {...props} />} />
|
||||
<Route path="/forum" render={() => <Forum {...props} />} />
|
||||
<Route exact path="/about" render={() => <About {...props} />} />
|
||||
<Route component={XoopsPathRedirect} />
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MainContent;
|
||||
@@ -0,0 +1,184 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect, RouteComponentProps } from 'react-router';
|
||||
import { MultiLang } from '../config';
|
||||
import funcs from '../functions';
|
||||
import PageNotFound from './lib/PageNotFound';
|
||||
|
||||
interface Params {
|
||||
module: string;
|
||||
pathname: string;
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<Params> {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class XoopsPathRedirect extends Component<Props> {
|
||||
|
||||
getRedirectUrl() {
|
||||
const { pathname, hash } = this.props.location;
|
||||
const query = new URLSearchParams(this.props.location.search);
|
||||
switch (pathname || '') {
|
||||
case '/index.php': {
|
||||
return '/';
|
||||
}
|
||||
case '/modules/xoonips':
|
||||
case '/modules/xoonips/index.php': {
|
||||
return '/database';
|
||||
}
|
||||
case '/modules/xoonips/detail.php': {
|
||||
const id = query.get('id');
|
||||
if (id !== null) {
|
||||
return '/database/item/id/' + funcs.escape(id);
|
||||
}
|
||||
const itemId = query.get('item_id');
|
||||
if (itemId !== null && itemId.match(/^\d+$/) !== null) {
|
||||
return '/database/item/' + funcs.escape(itemId);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
case '/modules/xoonips/listitem.php': {
|
||||
const indexId = query.get('index_id');
|
||||
if (indexId !== null && indexId.match(/^\d+$/) !== null) {
|
||||
const params = new URLSearchParams();
|
||||
const map: any = {
|
||||
orderby: { key: 'orderby', isNumber: false },
|
||||
order_dir: { key: 'order_dir', isNumber: true },
|
||||
itemcount: { key: 'itemcount', isNumber: true },
|
||||
page: { key: 'page', isNumber: true }
|
||||
};
|
||||
for (let k in map) {
|
||||
const v = query.get(k);
|
||||
if (v === null || v.length === 0) {
|
||||
continue;
|
||||
}
|
||||
if (map[k].isNumber && v.match(/^\d+$/) !== null) {
|
||||
continue;
|
||||
}
|
||||
params.set(map[k].key, v);
|
||||
}
|
||||
const paramStr = params.toString();
|
||||
return '/database/list/' + funcs.escape(indexId) + (paramStr.length > 0 ? '?' + paramStr : '');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '/modules/xoonips/itemselect.php': {
|
||||
const op = query.get('op');
|
||||
if (op === null) {
|
||||
break;
|
||||
}
|
||||
switch (op) {
|
||||
case 'quicksearch': {
|
||||
const keyword = query.get('keyword');
|
||||
const itemType = query.get('search_itemtype');
|
||||
if (keyword === null || itemType === null || keyword === '') {
|
||||
return '';
|
||||
}
|
||||
const type = itemType.replace('xnp', '');
|
||||
if (itemType !== 'basic' && itemType !== 'all' && itemType.match(/^xnp.+/) === null) {
|
||||
return '';
|
||||
}
|
||||
const params = new URLSearchParams({ type, keyword });
|
||||
const map: any = {
|
||||
orderby: { key: 'orderby', isNumber: false },
|
||||
orderdir: { key: 'order_dir', isNumber: true },
|
||||
item_per_page: { key: 'itemcount', isNumber: true },
|
||||
page: { key: 'page', isNumber: true }
|
||||
};
|
||||
for (let k in map) {
|
||||
const v = query.get(k);
|
||||
if (v === null || v.length === 0) {
|
||||
continue;
|
||||
}
|
||||
if (map[k].isNumber && v.match(/^\d+$/) !== null) {
|
||||
continue;
|
||||
}
|
||||
params.set(map[k].key, v);
|
||||
}
|
||||
return '/database/search?' + params.toString();
|
||||
}
|
||||
case 'itemtypesearch': {
|
||||
const itemType = query.get('search_itemtype');
|
||||
if (itemType === null || itemType.match(/^xnp.+/) === null) {
|
||||
return '';
|
||||
}
|
||||
const type = itemType.replace('xnp', '');
|
||||
return '/database/search/itemtype/' + funcs.escape(type);
|
||||
}
|
||||
case 'itemsubtypesearch': {
|
||||
let type = '';
|
||||
let subtype = '';
|
||||
query.forEach((v, k) => {
|
||||
if (k.match(/^xnp[a-z]+$/) !== null && !!v) {
|
||||
type = k.replace('xnp', '');
|
||||
return;
|
||||
}
|
||||
})
|
||||
if (type === '') {
|
||||
return '';
|
||||
}
|
||||
query.forEach((v, k) => {
|
||||
if (k.match(`^xnp${type}_.+$`) !== null && !!v) {
|
||||
subtype = v;
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (subtype === '') {
|
||||
return '';
|
||||
}
|
||||
return '/database/search/itemtype/' + funcs.escape(type) + '/' + funcs.escape(subtype);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
case '/modules/xoonips/advanced_search.php': {
|
||||
return '/database/advanced';
|
||||
}
|
||||
case '/modules/forum':
|
||||
case '/modules/forum/index.php': {
|
||||
const catId = query.get('cat_id');
|
||||
if (catId !== null && catId.match(/^\d+$/) !== null) {
|
||||
return '/forum/category/' + funcs.escape(catId);
|
||||
}
|
||||
const forumId = query.get('forum_id');
|
||||
if (forumId !== null && forumId.match(/^\d+$/) !== null) {
|
||||
return '/forum/forum/' + funcs.escape(forumId);
|
||||
}
|
||||
const topicId = query.get('topic_id');
|
||||
if (topicId !== null && topicId.match(/^\d+$/) !== null) {
|
||||
const matches = hash.match(/^#post_id(\d+)$/);
|
||||
return '/forum/topic/' + funcs.escape(topicId) + (matches === null ? '' : '#postId' + matches[1]);
|
||||
}
|
||||
const postId = query.get('post_id');
|
||||
if (postId !== null && postId.match(/^\d+$/) !== null) {
|
||||
return '/forum/post/' + funcs.escape(postId);
|
||||
}
|
||||
const catIds = query.get('cat_ids');
|
||||
if (catIds !== null && catIds.match(/^\d+$/) !== null) {
|
||||
return '/forum/category/' + funcs.escape(catIds);
|
||||
}
|
||||
return '/forum/';
|
||||
}
|
||||
case '/modules/contact':
|
||||
case '/modules/mailform':
|
||||
case '/modules/mailform/index.php': {
|
||||
return '/about';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
if (this.props.location.pathname === '/') {
|
||||
return null;
|
||||
}
|
||||
const url = this.getRedirectUrl();
|
||||
if (url === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <Redirect to={url} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default XoopsPathRedirect;
|
||||
|
After Width: | Height: | Size: 175 B |
|
After Width: | Height: | Size: 126 B |
|
After Width: | Height: | Size: 985 B |
|
After Width: | Height: | Size: 121 B |
|
After Width: | Height: | Size: 121 B |
|
After Width: | Height: | Size: 121 B |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 894 B |
|
After Width: | Height: | Size: 856 B |
|
After Width: | Height: | Size: 789 B |
|
After Width: | Height: | Size: 779 B |
|
After Width: | Height: | Size: 475 B |
|
After Width: | Height: | Size: 210 B |
|
After Width: | Height: | Size: 204 B |
|
After Width: | Height: | Size: 174 B |
|
After Width: | Height: | Size: 204 B |
|
After Width: | Height: | Size: 176 B |
|
After Width: | Height: | Size: 207 B |
|
After Width: | Height: | Size: 170 B |
|
After Width: | Height: | Size: 210 B |
|
After Width: | Height: | Size: 278 B |
|
After Width: | Height: | Size: 202 B |
|
After Width: | Height: | Size: 678 B |
|
After Width: | Height: | Size: 263 B |
|
After Width: | Height: | Size: 209 B |
|
After Width: | Height: | Size: 492 B |
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 736 B |
|
After Width: | Height: | Size: 273 B |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 105 B |
|
After Width: | Height: | Size: 300 B |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 273 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 272 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"title": "[en]Home[/en][ja]ホーム[/ja]",
|
||||
"link": "/"
|
||||
},
|
||||
{
|
||||
"title": "[en]Database[/en][ja]データベース[/ja]",
|
||||
"link": "/database"
|
||||
},
|
||||
{
|
||||
"title": "[en]Forum[/en][ja]フォーラム[/ja]",
|
||||
"link": "/forum"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
img {border: 0;}
|
||||
|
||||
#xoopsHiddenText {visibility: hidden; color: #000000; font-weight: normal; font-style: normal; text-decoration: none;}
|
||||
|
||||
.pagneutral {font-size: 10px; width: 16px; height: 19px;text-align: center; background-image: url(./images/pagneutral.gif);}
|
||||
.pagact {font-size: 10px; width: 16px; height: 19px;text-align: center; background-image: url(./images/pagact.gif);}
|
||||
.paginact {font-size: 10px; width: 16px; height: 19px;text-align: center; background-image: url(./images/paginact.gif);}
|
||||
|
||||
|
||||
#mainmenu a {text-align:left; display: block; margin: 0; padding: 4px;}
|
||||
#mainmenu a.menuTop {padding-left: 3px;}
|
||||
#mainmenu a.menuMain {padding-left: 3px;}
|
||||
#mainmenu a.menuSub {padding-left: 9px;}
|
||||
|
||||
#usermenu a {text-align:left; display: block; margin: 0; padding: 4px;}
|
||||
#usermenu a.menuTop {}
|
||||
#usermenu a.highlight {color: #0000ff; background-color: #fcc;}
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../config';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Annuonce = (props: Props) => {
|
||||
const noticeStyle = {
|
||||
color: 'red',
|
||||
}
|
||||
const announce = {
|
||||
'en':
|
||||
<>
|
||||
<p style={noticeStyle}>This site has been archived since FY2019 and is no longer updated.</p>
|
||||
<p>This site is supported by a Grant-in-Aid for Scientific Research on Innovative Areas (Comprehensive Brain Science Network) from the Ministry of Education, Science, Sports and Culture of Japan.</p>
|
||||
</>,
|
||||
'ja':
|
||||
<>
|
||||
<p style={noticeStyle}>このサイトは、2019年度よりアーカイブサイトとして運用されています。</p>
|
||||
<p>
|
||||
当サイトは、新学術領域研究 <a href="https://www.hokatsu-nou.neuroinf.jp/" target="_blank" rel="noopener noreferrer">包括脳ネットワーク</a> がサポートしています。包括脳ネットワークのメンバーはユーザー登録後、このサイトに各種ファイルをアップロードし、公開・共有することができます。 当サイトは統合脳プラットフォームを引き継ぎ、包括脳ネットワークデータベース委員会により運営されています。
|
||||
</p>
|
||||
</>,
|
||||
}
|
||||
return announce[props.lang];
|
||||
}
|
||||
|
||||
export default Annuonce;
|
||||
@@ -0,0 +1,27 @@
|
||||
.mainmenu li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.mainmenu a {
|
||||
font-size: 95%;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
border-bottom: 1px dotted #cdcdcd;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.menuTop {
|
||||
padding-left: 0.5em;
|
||||
border-bottom: 1px dotted #cdcdcd;
|
||||
}
|
||||
|
||||
.menuMain {
|
||||
padding-left: 0.5em;
|
||||
border-bottom: 1px dotted #cdcdcd;
|
||||
}
|
||||
|
||||
.menuSub {
|
||||
padding-left: 15px;
|
||||
border-bottom: 1px dotted #cdcdcd;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import mainMenus from '../assets/main-menu.json';
|
||||
import { MultiLang } from '../../config.js';
|
||||
import Functions from '../../functions';
|
||||
import styles from './MainMenu.module.css';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const MainMenu = (props: Props) => {
|
||||
const links = mainMenus.map((item, idx) => {
|
||||
const title = Functions.mlang(item.title, props.lang);
|
||||
const style = idx === 0 ? styles.menuTop : styles.menuMain;
|
||||
return <li key={idx}><Link className={style} to={item.link}>{title}</Link></li>
|
||||
});
|
||||
return (
|
||||
<ul className={styles.mainmenu}>
|
||||
{links}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export default MainMenu;
|
||||
@@ -0,0 +1,37 @@
|
||||
import React, { Component } from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import mlangEnglish from '../assets/images/mlang_english.gif';
|
||||
import mlangJapanese from '../assets/images/mlang_japanese.gif';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
lang: string;
|
||||
}
|
||||
|
||||
class LangFlag extends Component<Props> {
|
||||
|
||||
render() {
|
||||
const styleLink = {
|
||||
fontSize: '8px',
|
||||
};
|
||||
const styleImage = {
|
||||
verticalAlign: 'middle',
|
||||
border: '1px solid #000',
|
||||
};
|
||||
const params = new URLSearchParams(this.props.location.search);
|
||||
const lang = params.get('ml_lang') || this.props.lang;
|
||||
const linkLang = lang !== 'en' ? 'en' : 'ja';
|
||||
params.set('ml_lang', linkLang);
|
||||
const url = this.props.location.pathname + '?' + params.toString();
|
||||
const image = linkLang === 'en' ? mlangEnglish : mlangJapanese;
|
||||
const alt = linkLang === 'en' ? 'English' : 'Japanese';
|
||||
return (
|
||||
<Link style={styleLink} to={url}>
|
||||
<img style={styleImage} src={image} alt={alt} />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default withRouter(LangFlag);
|
||||
@@ -0,0 +1,48 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
interface Props {
|
||||
url: string;
|
||||
alt: string;
|
||||
imageNormal: string;
|
||||
imageHover: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
image: string;
|
||||
}
|
||||
|
||||
class LinkImage extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
image: props.imageNormal,
|
||||
};
|
||||
this.handleMouseOver = this.handleMouseOver.bind(this);
|
||||
this.handleMouseOut = this.handleMouseOut.bind(this);
|
||||
}
|
||||
|
||||
handleMouseOver() {
|
||||
const image = this.props.imageHover;
|
||||
this.setState({image})
|
||||
}
|
||||
|
||||
handleMouseOut() {
|
||||
const image = this.props.imageNormal;
|
||||
this.setState({image})
|
||||
}
|
||||
|
||||
render() {
|
||||
const imgStyle = {
|
||||
verticalAlign: 'middle',
|
||||
}
|
||||
return (
|
||||
<Link to={this.props.url} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
||||
<img style={imgStyle} src={this.state.image} alt={this.props.alt} />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default LinkImage;
|
||||
@@ -0,0 +1,6 @@
|
||||
.loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
margin: 100px 0 0 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import Spinner from 'react-spinner-material';
|
||||
import styles from './Loading.module.css';
|
||||
|
||||
const Loading = () => {
|
||||
return (
|
||||
<div className={styles.loading}>
|
||||
<Spinner size={70} spinnerColor={'#cccccc'} spinnerWidth={8} visible={true} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Loading;
|
||||
@@ -0,0 +1,48 @@
|
||||
import React, { Component } from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import Config, { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class PageNotFound extends Component<Props> {
|
||||
|
||||
private url = '/';
|
||||
private timer: NodeJS.Timer | null = null;
|
||||
|
||||
goToTopPage() {
|
||||
if (this.props.location.pathname !== '/') {
|
||||
this.timer = setTimeout(() => {
|
||||
this.props.history.push(this.url);
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const siteTitle = Functions.mlang(Config.SITE_TITLE, this.props.lang);
|
||||
this.goToTopPage();
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>Page Not Found - {siteTitle}</title>
|
||||
</Helmet>
|
||||
<h1>Page Not Found</h1>
|
||||
<section>
|
||||
<p>The page you were trying to access doesn't exist.</p>
|
||||
<p>If the page does not automatically reload, please click <a href={this.url}>here</a></p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(PageNotFound);
|
||||
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import imageRank2 from '../assets/images/rank3dbf8e94a6f72.gif';
|
||||
import imageRank3 from '../assets/images/rank3dbf8e9e7d88d.gif';
|
||||
import imageRank4 from '../assets/images/rank3dbf8ea81e642.gif';
|
||||
import imageRank5 from '../assets/images/rank3dbf8eb1a72e7.gif';
|
||||
import imageRank6 from '../assets/images/rank3dbf8edf15093.gif';
|
||||
import imageRank7 from '../assets/images/rank3dbf8ee8681cd.gif';
|
||||
import imageRank1 from '../assets/images/rank3e632f95e81ca.gif';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
rank: number;
|
||||
posts: number;
|
||||
}
|
||||
|
||||
const userRanks = [
|
||||
{ title: '[en]Just popping in[/en][ja]新米[/ja]', min: 0, max: 20, special: false, image: imageRank1 },
|
||||
{ title: '[en]Not too shy to talk[/en][ja]半人前[/ja]', min: 21, max: 40, special: false, image: imageRank2 },
|
||||
{ title: '[en]Quite a regular[/en][ja]常連[/ja]', min: 41, max: 70, special: false, image: imageRank3 },
|
||||
{ title: '[en]Just can\'t stay away[/en][ja]一人前[/ja]', min: 71, max: 150, special: false, image: imageRank4 },
|
||||
{ title: '[en]Home away from home[/en][ja]長老[/ja]', min: 151, max: 10000, special: false, image: imageRank5 },
|
||||
{ title: '[en]Moderator[/en][ja]モデレータ[/ja]', min: 0, max: 0, special: true, image: imageRank6 },
|
||||
{ title: '[en]Webmaster[/en][ja]管理人[/ja]', min: 0, max: 0, special: true, image: imageRank7 },
|
||||
];
|
||||
|
||||
const UserRankStarImage = (props: Props) => {
|
||||
const { lang, rank, posts } = props;
|
||||
const findRank = (posts: number) => {
|
||||
const rank = userRanks.find((userRank) => {
|
||||
if (posts >= userRank.min && posts <= userRank.max) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return typeof rank !== 'undefined' ? rank : userRanks[0];
|
||||
|
||||
}
|
||||
const userRank = rank > 0 && rank <= 7 ? userRanks[rank - 1] : findRank(posts);
|
||||
const title = Functions.mlang(userRank.title, lang);
|
||||
return <img src={userRank.image} alt={title} title={title} />;
|
||||
}
|
||||
|
||||
export default UserRankStarImage;
|
||||
@@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
text: string;
|
||||
dohtml?: boolean;
|
||||
dosmiley?: boolean;
|
||||
doxcode?: boolean;
|
||||
doimage?: boolean;
|
||||
dobr?: boolean;
|
||||
}
|
||||
|
||||
const preConvertXCode = (text: string, doxcode: boolean): string => {
|
||||
if (doxcode) {
|
||||
return text.replace(/\[code\](.*)\[\/code\]/sg, (m0, m1) => {
|
||||
return '[code]' + Functions.base64_encode(m1) + '[/code]';
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
const postConvertXCode = (text: string, doxcode: boolean, doimage: boolean): string => {
|
||||
if (doxcode) {
|
||||
return text.replace(/\[code\](.*)\[\/code\]/sg, (m0, m1) => {
|
||||
const text = convertXCode(Functions.htmlspecialchars(Functions.base64_decode(m1)), doimage);
|
||||
return '<div class="xoopsCode"><pre><code>' + text + '</code></pre></div>';
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
const convertClickable = (text: string) => {
|
||||
text = text.replace(/(^|[^\]_a-zA-Z0-9-="'/]+)((?:https?|ftp)(?::\/\/[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+))/g, (m0, m1, m2) => {
|
||||
return m1 + '<a href="' + m2 + '" rel="external">' + m2 + '</a>';
|
||||
});
|
||||
text = text.replace(/(^|[^\]_a-zA-Z0-9-="'/:.]+)([a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+)/g, (m0, m1, m2) => {
|
||||
return m1 + '<a href="mailto:' + m2 + '">' + m2 + '</a>';
|
||||
});
|
||||
return text;
|
||||
}
|
||||
|
||||
const convertXCode = (text: string, doimage: boolean): string => {
|
||||
// TODO: implement
|
||||
return text;
|
||||
}
|
||||
|
||||
const convertSmiley = (text: string) => {
|
||||
// TODO: implement
|
||||
return text;
|
||||
}
|
||||
|
||||
const convertBr = (text: string): string => {
|
||||
return text.replace(/(\r?\n|\r)/g, '<br />');
|
||||
}
|
||||
|
||||
const XoopsCode = (props: Props) => {
|
||||
const { lang } = props;
|
||||
let text = props.text;
|
||||
const dohtml = !!props.dohtml;
|
||||
const dosmiley = !!props.dosmiley;
|
||||
const doxcode = !!props.doxcode;
|
||||
const doimage = !!props.doimage;
|
||||
const dobr = !!props.dobr;
|
||||
text = preConvertXCode(text, doxcode);
|
||||
if (!dohtml) {
|
||||
text = Functions.htmlspecialchars(text);
|
||||
}
|
||||
text = convertClickable(text);
|
||||
if (dosmiley) {
|
||||
text = convertSmiley(text);
|
||||
}
|
||||
if (doxcode) {
|
||||
text = convertXCode(text, doimage);
|
||||
}
|
||||
if (dobr) {
|
||||
text = convertBr(text);
|
||||
}
|
||||
text = postConvertXCode(text, doxcode, doimage);
|
||||
text = Functions.mlang(text, lang);
|
||||
return <div dangerouslySetInnerHTML={{ __html: text }} />;
|
||||
}
|
||||
|
||||
export default XoopsCode;
|
||||
@@ -0,0 +1,13 @@
|
||||
const SITE_TITLE = '[en]CBSN platform[/en][ja]包括脳プラットフォーム[/ja]';
|
||||
const SITE_SLOGAN = 'XooNIps for CBSN';
|
||||
const GOOGLE_ANALYTICS_TRACKING_ID = 'UA-2780809-1';
|
||||
|
||||
export type MultiLang = 'en' | 'ja';
|
||||
|
||||
const Config = {
|
||||
SITE_TITLE,
|
||||
SITE_SLOGAN,
|
||||
GOOGLE_ANALYTICS_TRACKING_ID,
|
||||
}
|
||||
|
||||
export default Config;
|
||||
@@ -0,0 +1,52 @@
|
||||
.database {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.database :global(.list) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.database :global(.list .listTable) {
|
||||
width: 100%;
|
||||
border-spacing: 5px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.database :global(.list .listTable .listIcon),
|
||||
.database :global(.list .listTable .listExtra) {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
width: 65px;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.database :global(.itemDetail .head) {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.database :global(.itemDetail .readme),
|
||||
.database :global(.itemDetail .rights) {
|
||||
background-color: #ffffff;
|
||||
width: 100%;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.database :global(.advancedSearch .head) {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.database :global(.advancedSearch .search) {
|
||||
text-align: center;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.database :global(.advancedSearch .fieldDateLabel) {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.database :global(.advancedSearch .fieldDate select),
|
||||
.database :global(.advancedSearch .fieldDate input) {
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { Route, Switch, RouteComponentProps } from 'react-router-dom';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import styles from './Database.module.css';
|
||||
import DatabaseAdvancedSearch from './DatabaseAdvancedSearch';
|
||||
import DatabaseDetailItem from './DatabaseDetailItem';
|
||||
import DatabaseSearchByAdvancedKeyword from './DatabaseSearchByAdvancedKeyword';
|
||||
import DatabaseSearchByIndexId from './DatabaseSearchByIndexId';
|
||||
import DatabaseSearchByItemType from './DatabaseSearchByItemType';
|
||||
import DatabaseSearchByKeyword from './DatabaseSearchByKeyword';
|
||||
import DatabaseTop from './DatabaseTop';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Database = (props: Props) => {
|
||||
const { lang } = props;
|
||||
return (
|
||||
<div className={styles.database}>
|
||||
<Helmet>
|
||||
<title>{Functions.mlang('[en]Database[/en][ja]データベース[/ja]', lang)} - {Functions.mlang(Config.SITE_TITLE, lang)}</title>
|
||||
</Helmet>
|
||||
<Switch>
|
||||
<Route exact path="/database" render={(props: RouteComponentProps) => <DatabaseTop lang={lang} {...props} />} />
|
||||
<Route exact path="/database/item/:id" render={(props: RouteComponentProps<{ id: string; doi: string }>) => <DatabaseDetailItem lang={lang} {...props} />} />
|
||||
<Route exact path="/database/item/id/:doi" render={(props: RouteComponentProps<{ id: string; doi: string }>) => <DatabaseDetailItem lang={lang} {...props} />} />
|
||||
<Route exact path="/database/advanced" render={(props: RouteComponentProps) => <DatabaseAdvancedSearch lang={lang} {...props} />} />
|
||||
<Route exact path="/database/list" render={(props: RouteComponentProps<{ id: string }>) => <DatabaseSearchByIndexId lang={lang} {...props} />} />
|
||||
<Route exact path="/database/list/:id" render={(props: RouteComponentProps<{ id: string }>) => <DatabaseSearchByIndexId lang={lang} {...props} />} />
|
||||
<Route exact path="/database/search" render={(props: RouteComponentProps) => <DatabaseSearchByKeyword lang={lang} {...props} />} />
|
||||
<Route exact path="/database/search/advanced" render={(props: RouteComponentProps) => <DatabaseSearchByAdvancedKeyword lang={lang} {...props} />} />
|
||||
<Route exact path="/database/search/itemtype/:itemType" render={(props: RouteComponentProps<{ itemType: string; subItemType: string }>) => <DatabaseSearchByItemType lang={lang} {...props} />} />
|
||||
<Route exact path="/database/search/itemtype/:itemType/:subItemType" render={(props: RouteComponentProps<{ itemType: string; subItemType: string }>) => <DatabaseSearchByItemType lang={lang} {...props} />} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Database;
|
||||
@@ -0,0 +1,49 @@
|
||||
import React, { Component } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import ItemType from './item-type';
|
||||
import AdvancedSearchQuery from './lib/AdvancedSearchQuery';
|
||||
import ItemUtil from './lib/ItemUtil';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class DatabaseAdvancedSearch extends Component<Props> {
|
||||
|
||||
private query: AdvancedSearchQuery = new AdvancedSearchQuery();
|
||||
private itemTypes = ['tool', 'paper', 'presentation', 'book', 'data', 'url', 'files'];
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.handleClickSearchButton = this.handleClickSearchButton.bind(this);
|
||||
}
|
||||
|
||||
handleClickSearchButton() {
|
||||
if (!this.query.empty()) {
|
||||
const url = ItemUtil.getSearchByAdvancedKeywordsUrl(this.query);
|
||||
this.props.history.push(url);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
return (
|
||||
<div className="advancedSearch">
|
||||
<h3>{Functions.mlang('[en]Search Items[/en][ja]アイテム検索[/ja]', lang)}</h3>
|
||||
<div className="search">
|
||||
<button className="formButton" onClick={this.handleClickSearchButton}>Search</button>
|
||||
</div>
|
||||
{this.itemTypes.map((type, i) => {
|
||||
return <ItemType.AdvancedSearch key={i} type={'xnp' + type} lang={lang} query={this.query} />;
|
||||
})}
|
||||
<div className="search">
|
||||
<button className="formButton" onClick={this.handleClickSearchButton}>Search</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DatabaseAdvancedSearch;
|
||||
@@ -0,0 +1,89 @@
|
||||
import React, { Component } from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import ItemType from './item-type';
|
||||
import ItemUtil, { Item } from './lib/ItemUtil';
|
||||
|
||||
interface Params {
|
||||
id: string;
|
||||
doi: string;
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<Params> {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
item: Item | null;
|
||||
}
|
||||
|
||||
class DatabaseDetailItem extends Component<Props, State> {
|
||||
|
||||
private id: number;
|
||||
private doi: string;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
item: null,
|
||||
};
|
||||
const { params } = this.props.match;
|
||||
this.id = typeof params.id !== 'undefined' ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : 0;
|
||||
this.doi = typeof params.doi !== 'undefined' ? params.doi : '';
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateItem();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { params } = this.props.match;
|
||||
const id = typeof params.id !== 'undefined' ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : 0;
|
||||
const doi = typeof params.doi !== 'undefined' ? params.doi : '';
|
||||
if (this.id !== id || this.doi !== doi) {
|
||||
this.id = id;
|
||||
this.doi = doi;
|
||||
this.updateItem();
|
||||
}
|
||||
}
|
||||
|
||||
updateItem() {
|
||||
if (this.doi !== '') {
|
||||
ItemUtil.getByDoi(this.doi, (item) => {
|
||||
this.setState({ loading: false, item });
|
||||
});
|
||||
} else if (this.id !== 0) {
|
||||
ItemUtil.get(this.id, (item) => {
|
||||
this.setState({ loading: false, item });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
if (this.state.loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (this.state.item === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{Functions.mlang(this.state.item.title, lang)} - {Functions.mlang('[en]Database[/en][ja]データベース[/ja]', lang)} - {Functions.mlang(Config.SITE_TITLE, lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{Functions.mlang('[en]Detail[/en][ja]詳細[/ja]', lang)}</h3>
|
||||
<br />
|
||||
<ItemType.Detail lang={lang} item={this.state.item} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DatabaseDetailItem;
|
||||
@@ -0,0 +1,56 @@
|
||||
import React, { Component } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { MultiLang } from '../config';
|
||||
import AdvancedSearchQuery from './lib/AdvancedSearchQuery';
|
||||
import DatabaseListItem from './lib/DatabaseListItem';
|
||||
import ItemUtil, { SearchCallbackFunc, SortCondition } from './lib/ItemUtil';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
lang: MultiLang
|
||||
}
|
||||
|
||||
interface State {
|
||||
search: string;
|
||||
query: AdvancedSearchQuery;
|
||||
}
|
||||
|
||||
class DatabaseSearchByAdvancedKeyword extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const search = props.location.search;
|
||||
const query = ItemUtil.getAdvancedSearchQueryByQuery(search);
|
||||
this.state = { search, query };
|
||||
this.searchFunc = this.searchFunc.bind(this);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const search = nextProps.location.search;
|
||||
if (prevState.search !== search) {
|
||||
const query = ItemUtil.getAdvancedSearchQueryByQuery(search);
|
||||
return { search, query };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
return ItemUtil.getSearchByAdvancedKeywordsUrl(this.state.query);
|
||||
}
|
||||
|
||||
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
|
||||
ItemUtil.getListByAdvancedSearchQuery(this.state.query, condition, func);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const baseUrl = this.getUrl();
|
||||
return (
|
||||
<div className="list">
|
||||
<h3>Listing item</h3>
|
||||
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DatabaseSearchByAdvancedKeyword;
|
||||
@@ -0,0 +1,88 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import DatabaseListIndex from './lib/DatabaseListIndex';
|
||||
import DatabaseListItem from './lib/DatabaseListItem';
|
||||
import IndexUtil, { Index, INDEX_ID_PUBLIC } from './lib/IndexUtil';
|
||||
import ItemUtil, { SearchCallbackFunc, SortCondition } from './lib/ItemUtil';
|
||||
|
||||
interface Params {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface Props extends RouteComponentProps<Params> {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
indexId: number;
|
||||
}
|
||||
|
||||
class DatabaseSearchByIndexId extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const { params } = props.match;
|
||||
this.state = {
|
||||
indexId: params.id ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : INDEX_ID_PUBLIC,
|
||||
}
|
||||
this.searchFunc = this.searchFunc.bind(this);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const { params } = nextProps.match;
|
||||
const indexId = params.id ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : INDEX_ID_PUBLIC;
|
||||
if (prevState.indexId !== indexId) {
|
||||
return { indexId };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
if (this.state.indexId === 0) {
|
||||
return '/';
|
||||
}
|
||||
return IndexUtil.getUrl(this.state.indexId);
|
||||
}
|
||||
|
||||
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
|
||||
if (this.state.indexId === 0) {
|
||||
const res = { total: 0, data: [] };
|
||||
func(res);
|
||||
} else {
|
||||
ItemUtil.getListByIndexId(this.state.indexId, condition, func);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const index = IndexUtil.get(this.state.indexId);
|
||||
if (index === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const baseUrl = this.getUrl();
|
||||
const pIndexes = IndexUtil.getParents(this.state.indexId);
|
||||
const parents = pIndexes.map((value: Index) => {
|
||||
const url: string = IndexUtil.getUrl(value.id);
|
||||
return <Fragment key={value.id}>/ <Link to={url}>{value.title}</Link> </Fragment>;
|
||||
});
|
||||
const title = pIndexes.map((value) => { return '/' + value.title; }).join('');
|
||||
return (
|
||||
<div className="list">
|
||||
<Helmet>
|
||||
<title>{Functions.mlang(title, lang)} - {Functions.mlang('[en]Database[/en][ja]データベース[/ja]', lang)} - {Functions.mlang(Config.SITE_TITLE, lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{Functions.mlang('[en]Listing item[/en][ja]アイテム一覧[/ja]', lang)}</h3>
|
||||
<div>{parents}</div>
|
||||
<DatabaseListIndex lang={lang} index={index} />
|
||||
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DatabaseSearchByIndexId;
|
||||
@@ -0,0 +1,73 @@
|
||||
import React, { Component } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import DatabaseListItem from './lib/DatabaseListItem';
|
||||
import ItemUtil, { SearchCallbackFunc, SortCondition } from './lib/ItemUtil';
|
||||
|
||||
interface Params {
|
||||
itemType: string;
|
||||
subItemType: string;
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<Params> {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
itemType: string;
|
||||
subItemType: string;
|
||||
}
|
||||
|
||||
class DatabaseSearchByItemType extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const { params } = this.props.match;
|
||||
this.state = {
|
||||
itemType: params.itemType ? params.itemType : '',
|
||||
subItemType: params.subItemType ? params.subItemType : '',
|
||||
};
|
||||
this.searchFunc = this.searchFunc.bind(this);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const { params } = nextProps.match;
|
||||
const item_type = params.itemType ? params.itemType : '';
|
||||
const sub_item_type = params.subItemType ? params.subItemType : '';
|
||||
if (prevState.itemType !== item_type || prevState.subItemType !== sub_item_type) {
|
||||
return { item_type, sub_item_type };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
|
||||
if (this.state.itemType === '') {
|
||||
const res = { total: 0, data: [] };
|
||||
func(res);
|
||||
} else {
|
||||
ItemUtil.getListByItemType(this.state.itemType, this.state.subItemType, condition, func);
|
||||
}
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
let url = ItemUtil.getItemTypeSearchUrl(this.state.itemType);
|
||||
if (this.state.subItemType !== '') {
|
||||
url += '/' + this.state.subItemType;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const baseUrl = this.getUrl();
|
||||
return (
|
||||
<div className="list">
|
||||
<h3>{Functions.mlang('[en]Listing item[/en][ja]アイテム一覧[/ja]', lang)}</h3>
|
||||
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DatabaseSearchByItemType;
|
||||