Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ccf1b37e1
|
||
|
|
79e0e2295f
|
||
|
|
480c1aeed8
|
+21
-26
@@ -1,30 +1,25 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.eslintcache
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# database contents
|
||||
/public/static
|
||||
/public/modules
|
||||
/public/uploads
|
||||
/dl-limit-items.csv
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Keep @types/node on the 24.x line to match the deployment target. Its
|
||||
// "latest" dist-tag points at the 22.x line, so the default target would
|
||||
// report no updates at all instead of offering 24.x patches.
|
||||
export default {
|
||||
target: (name) => (name === '@types/node' ? 'minor' : 'latest'),
|
||||
};
|
||||
@@ -1,44 +1,149 @@
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
# Invertebrate Brain Platform
|
||||
|
||||
## Available Scripts
|
||||
Static front-end for the archived [Invertebrate Brain Platform](https://invbrain.neuroinf.jp/) site,
|
||||
built with [Vite](https://vite.dev/), React and TypeScript.
|
||||
|
||||
In the project directory, you can run:
|
||||
## Requirements
|
||||
|
||||
### `yarn start`
|
||||
- Node.js 20.19+ / 22.12+ (Vite 8)
|
||||
- npm
|
||||
|
||||
Runs the app in the development mode.<br />
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
## Setup
|
||||
|
||||
The page will reload if you make edits.<br />
|
||||
You will also see any lint errors in the console.
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### `yarn test`
|
||||
## Site data
|
||||
|
||||
Launches the test runner in the interactive watch mode.<br />
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
The pages are rendered from JSON dumped out of the original XOOPS installation
|
||||
by [xoops-static-exporter](https://git.ni.riken.jp/niu/xoops-static-exporter)
|
||||
(`sites/invbrain.neuroinf.jp/`).
|
||||
|
||||
### `yarn build`
|
||||
All of it lives in `static-contents/`, which sits next to the repository rather
|
||||
than inside it:
|
||||
|
||||
Builds the app for production to the `build` folder.<br />
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
```
|
||||
invbrain.neuroinf.jp/
|
||||
├── invbrain.neuroinf.jp/ this repository
|
||||
├── deploy.sh
|
||||
└── static-contents/
|
||||
├── modules/ one directory per source XOOPS module
|
||||
│ ├── credits/ index.json, <page id>.json
|
||||
│ ├── documents/ pico: index.json, <content id>.json, images/
|
||||
│ ├── htmldocs/ pico: index.json, <content id>.json, IVBPF/
|
||||
│ ├── newdb*/ CosmoDB: index.json, <item id>.json, extract/, images/
|
||||
│ ├── PubMedPDF/ index.json
|
||||
│ ├── xfileuploader/ files linked from the pages
|
||||
│ └── xwords/ index.json
|
||||
├── static/ static HTML trees linked from the pages
|
||||
├── uploads/ files linked from the pages
|
||||
├── common/ files linked from the pages
|
||||
└── rss.xml
|
||||
```
|
||||
|
||||
The build is minified and the filenames include the hashes.<br />
|
||||
Your app is ready to be deployed!
|
||||
The app keeps the original XOOPS addresses (`/modules/<module>/...`), so its
|
||||
routes share `modules/` with the data. That works because the data files are
|
||||
all `index.json`, `<id>.json` and linked assets, none of which is a page URL, and
|
||||
a real file always wins over the SPA fallback.
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
Each module fetches its own `index.json` the first time one of its pages is
|
||||
shown, rather than all of them at start up.
|
||||
|
||||
### `yarn eject`
|
||||
The dev and preview servers mount `static-contents/` at the site root. It is
|
||||
deliberately kept out of `dist/`: it runs to about 68 GB, and the web server
|
||||
maps it in directly. It is generated content and is not tracked in git.
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
## Scripts
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
| Command | Description |
|
||||
| --- | --- |
|
||||
| `npm run dev` | Start the dev server on http://localhost:5173 |
|
||||
| `npm run build` | Type-check and build into `dist/` |
|
||||
| `npm run preview` | Serve the production build on http://localhost:4173 |
|
||||
| `npm run lint` | Lint with [Biome](https://biomejs.dev/) |
|
||||
| `npm run format` | Format with Biome |
|
||||
| `npm run check` | Lint, format and organize imports, applying the safe fixes |
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
`@types/node` is pinned to the major that matches the Node runtime in use (24);
|
||||
`.ncurc.js` keeps `npm-check-updates` from bumping it past that.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
There is currently no test suite.
|
||||
|
||||
## Learn More
|
||||
## Deployment
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
Build locally, then run `../deploy.sh`, which does steps 2 to 4 below against
|
||||
the `archive` host. The server does not need a toolchain.
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
### 1. Build and check
|
||||
|
||||
```sh
|
||||
npm ci
|
||||
npm run check # biome, writes fixes
|
||||
npm run build # runs tsc -b, then vite build
|
||||
npm run preview # localhost:4173 , serves dist/ + static-contents/
|
||||
```
|
||||
|
||||
### 2. Send up the site data
|
||||
|
||||
About 68 GB, most of it under `modules/newdb5/extract/`. The server mirrors the
|
||||
local layout: `static-contents/` sits next to the document root.
|
||||
|
||||
```
|
||||
/data/www/invbrain.neuroinf.jp/
|
||||
├── html/ document root
|
||||
└── static-contents/
|
||||
```
|
||||
|
||||
```sh
|
||||
rsync -a --delete static-contents/ \
|
||||
user@server:/data/www/invbrain.neuroinf.jp/static-contents/
|
||||
```
|
||||
|
||||
### 3. Push the build
|
||||
|
||||
```sh
|
||||
rsync -a --delete dist/ user@server:/data/www/invbrain.neuroinf.jp/html/
|
||||
```
|
||||
|
||||
`--delete` clears out the previous build, and with it the data symlinks from
|
||||
step 4; they are recreated right after.
|
||||
|
||||
### 4. Link the site data into the document root
|
||||
|
||||
```sh
|
||||
cd /data/www/invbrain.neuroinf.jp/html
|
||||
ln -sfn ../static-contents/* .
|
||||
```
|
||||
|
||||
That links every top-level entry of `static-contents/` — currently `modules/`,
|
||||
`static/`, `uploads/`, `common/` and `rss.xml`. The links are relative, so the
|
||||
whole tree can be moved without rewriting them.
|
||||
|
||||
Apache needs `Options FollowSymLinks`, `AllowOverride All` and
|
||||
`Require all granted` on the document root, and `Options FollowSymLinks` with
|
||||
`Require all granted` on `static-contents/`. Use symlinks rather than `Alias`:
|
||||
an aliased `/modules` is outside the document root, so `.htaccess` no longer
|
||||
sends the page URLs under it to `index.html`.
|
||||
|
||||
### 5. Check the deployed site
|
||||
|
||||
```sh
|
||||
curl -I https://invbrain.neuroinf.jp/rss.xml # 200
|
||||
curl -I https://invbrain.neuroinf.jp/modules/htmldocs/index.json # 200
|
||||
```
|
||||
|
||||
Then load `/`, a CosmoDB list and detail page (`/modules/newdb5/list.php?id=2`),
|
||||
`/modules/PubMedPDF/`, `/modules/xwords/` and `/modules/credits/` in a browser
|
||||
and confirm the images resolve.
|
||||
|
||||
### Upgrading from v1.0.0
|
||||
|
||||
v1.0.0 already served the data from the same paths, so the data layout is
|
||||
unchanged and step 2 only has to transfer what differs.
|
||||
|
||||
## Google Analytics
|
||||
|
||||
`GOOGLE_ANALYTICS_TRACKING_ID` in `src/config.ts` carries the GA4 measurement
|
||||
ID that replaced the Universal Analytics property shut down in 2023. Emptying
|
||||
it disables tracking.
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.5.13/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"includes": ["**", "!**/dist", "!src/*/assets"]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 4,
|
||||
"lineWidth": 120
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "single",
|
||||
"jsxQuoteStyle": "double",
|
||||
"semicolons": "always",
|
||||
"trailingCommas": "es5",
|
||||
"arrowParentheses": "always"
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"preset": "recommended"
|
||||
}
|
||||
},
|
||||
"assist": {
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,316 +0,0 @@
|
||||
<?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);
|
||||
|
||||
// remove ob fileters
|
||||
while (ob_get_level()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
define('MYDUMPTOOL_OUTPUTDIR', __DIR__.'/data');
|
||||
MyDumpTool::makeDirectory('');
|
||||
MyDumpTool::makeDirectory('src');
|
||||
MyDumpTool::makeDirectory('public');
|
||||
|
||||
class MyDumpTool
|
||||
{
|
||||
public static function decode(&$data)
|
||||
{
|
||||
$textutil = xoonips_getutility('text');
|
||||
foreach ($data as $k => $v) {
|
||||
if (is_string($v)) {
|
||||
$v = $textutil->html_numeric_entities($v);
|
||||
$v = mb_decode_numericentity($v, [0x0, 0x10000, 0, 0xfffff], 'UTF-8');
|
||||
$data[$k] = \Normalizer::normalize(trim($v), \Normalizer::FORM_C);
|
||||
} elseif (null === $v) {
|
||||
$data[$k] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function convertToInt(&$data, $keys, $zerofill = false)
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (isset($data[$key])) {
|
||||
$data[$key] = '' === $data[$key] ? ($zerofill ? 0 : null) : (int) $data[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function dropColumn(&$data, $keys)
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (isset($data[$key])) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function fileCopy($from, $to)
|
||||
{
|
||||
system('rsync -aq '.escapeshellarg($from).' '.escapeshellarg($to), $ret);
|
||||
|
||||
return 0 === $ret;
|
||||
}
|
||||
|
||||
public static function fixHtml($text)
|
||||
{
|
||||
static $config = [
|
||||
//'clean' => true,
|
||||
'hide-comments' => true,
|
||||
'indent' => true,
|
||||
'output-xhtml' => true,
|
||||
'preserve-entities' => true,
|
||||
'show-body-only' => true,
|
||||
'drop-proprietary-attributes' => true,
|
||||
'logical-emphasis' => true,
|
||||
'wrap' => 0,
|
||||
'input-encoding' => 'utf8',
|
||||
'output-encoding' => 'utf8',
|
||||
'output-bom' => false,
|
||||
];
|
||||
$text = tidy_repair_string($text, $config);
|
||||
$text = preg_replace('/<table([^>]*)>((?:\s*<caption[^>]*>.*<\/caption>)?\s*)<tr([^>]*)>/Us', '<table\1>\2<tbody><tr\3>', $text);
|
||||
$text = preg_replace('/<\/tr>(\s*)<\/table>/s', '</tr></tbody>\1</table>', $text);
|
||||
$text = tidy_repair_string($text, $config);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
public static function tableExists($name)
|
||||
{
|
||||
global $xoopsDB;
|
||||
$prefix = $xoopsDB->prefix();
|
||||
$sql = <<< SQL
|
||||
SELECT 1
|
||||
FROM `${prefix}_${name}`
|
||||
LIMIT 1
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
return false;
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function makeDirectory($dirname)
|
||||
{
|
||||
$path = MYDUMPTOOL_OUTPUTDIR.('' !== $dirname ? '/'.$dirname : '');
|
||||
if (!is_dir($path)) {
|
||||
if (!@mkdir($path, 0755, true)) {
|
||||
exit('Failed to create directory: '.$path.PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function saveJson($fname, $data)
|
||||
{
|
||||
$path = MYDUMPTOOL_OUTPUTDIR.'/'.$fname;
|
||||
if (false === file_put_contents($path, json_encode($data, JSON_UNESCAPED_UNICODE))) {
|
||||
exit('Failed to save json file: '.$path.PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
public static function fileDownload($url, $fpath)
|
||||
{
|
||||
$fp = fopen($fpath, 'w');
|
||||
if (false === $fp) {
|
||||
exit('Failed to open file: '.$fpath.PHP_EOL);
|
||||
}
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($curl, CURLOPT_AUTOREFERER, true);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_FAILONERROR, true);
|
||||
curl_setopt($curl, CURLOPT_FILE, $fp);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
|
||||
$ret = curl_exec($curl);
|
||||
fclose($fp);
|
||||
if (false === $ret) {
|
||||
unlink($fpath);
|
||||
exit('Failed to download file, unexpected error: '.$url.PHP_EOL);
|
||||
}
|
||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
if (200 !== $code) {
|
||||
unlink($fpath);
|
||||
exit('Failed to download file, invalid status code('.$code.'): '.$url.PHP_EOL);
|
||||
}
|
||||
curl_close($curl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getMimeType($fpath)
|
||||
{
|
||||
$finfo = finfo_open(FILEINFO_MIME);
|
||||
$mime = finfo_file($finfo, $fpath);
|
||||
finfo_close($finfo);
|
||||
$mime = preg_replace('/(;.*)$/', '', $mime);
|
||||
|
||||
return $mime;
|
||||
}
|
||||
|
||||
public static function getModule($dirname)
|
||||
{
|
||||
$moduleHandler = xoops_gethandler('module');
|
||||
$moduleObj = $moduleHandler->getByDirname($dirname);
|
||||
|
||||
return [
|
||||
'id' => (int) $moduleObj->get('mid'),
|
||||
'name' => $moduleObj->get('name'),
|
||||
'dirname' => $moduleObj->get('dirname'),
|
||||
'version' => (int) $moduleObj->get('version'),
|
||||
'lastupdate' => (int) $moduleObj->get('last_update'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function canModuleRead($dirname, $uid)
|
||||
{
|
||||
$moduleHandler = xoops_gethandler('module');
|
||||
$moduleObj = $moduleHandler->getByDirname($dirname);
|
||||
$moduleId = (int) $moduleObj->get('mid');
|
||||
$memberHandler = xoops_gethandler('member');
|
||||
$groupIds = 0 === $uid ? [XOOPS_GROUP_ANONYMOUS] : $memberHandler->getGroupsByUser($uid);
|
||||
$groupPermHandler = xoops_gethandler('groupperm');
|
||||
$ret = $groupPermHandler->checkRight('module_read', $moduleId, $groupIds);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function mlang($s, $url, $lang)
|
||||
{
|
||||
if (!defined('CUBE_UTILS_ML_LANGS')) {
|
||||
exit('Error: cubeUtils is not loaded'.PHP_EOL);
|
||||
}
|
||||
$mLanguage = $lang;
|
||||
$mLanguages = explode(',', CUBE_UTILS_ML_LANGS);
|
||||
$mLanguageNames = explode(',', CUBE_UTILS_ML_LANGNAMES);
|
||||
// escape brackets inside of <input type="text" value="...">
|
||||
$s = preg_replace_callback('/(\<input)([^>]*)(\>)/isU', 'self::mlang_escapeBracketTextBox', $s);
|
||||
// escape brackets inside of <textarea></textarea>
|
||||
$s = preg_replace_callback('/(\<textarea[^>]*\>)(.*)(<\/textarea\>)/isU', 'self::mlang_escapeBracket', $s);
|
||||
// multilanguage image tag
|
||||
$langimages = explode(',', CUBE_UTILS_ML_LANGIMAGES);
|
||||
$langnames = explode(',', CUBE_UTILS_ML_LANGNAMES);
|
||||
@list($url, $query) = explode('?', $url);
|
||||
if (empty($query)) {
|
||||
$link_base = '?'.CUBE_UTILS_ML_PARAM_NAME.'=';
|
||||
} elseif (false === ($pos = strpos($query, CUBE_UTILS_ML_PARAM_NAME.'='))) {
|
||||
$link_base = '?'.htmlspecialchars($query, ENT_QUOTES).'&'.CUBE_UTILS_ML_PARAM_NAME.'=';
|
||||
} elseif ($pos < 2) {
|
||||
$link_base = '?'.CUBE_UTILS_ML_PARAM_NAME.'=';
|
||||
} else {
|
||||
$link_base = '?'.htmlspecialchars(substr($query, 0, $pos - 1), ENT_QUOTES).'&'.CUBE_UTILS_ML_PARAM_NAME.'=';
|
||||
}
|
||||
$link_base = $url.$link_base;
|
||||
$langimage_html = '';
|
||||
foreach ($mLanguages as $l => $lang) {
|
||||
$langimage_html .= '<a rel="nofollow" href="'.$link_base.$lang.'"><img src="'.XOOPS_URL.'/'.$langimages[$l].'" alt="flag" title="'.$langnames[$l].'" /></a>';
|
||||
}
|
||||
$s = preg_replace('/\['.CUBE_UTILS_ML_IMAGETAG.'\]/', $langimage_html, $s);
|
||||
|
||||
$s = preg_replace('/\['.CUBE_UTILS_ML_URLTAG.':([^\]]*?)\]/', $link_base.'$1', $s);
|
||||
|
||||
// simple pattern to strip selected lang_tags
|
||||
$s = preg_replace('/\[(\/)?([^\]]+\|)?'.preg_quote($mLanguage).'(\|[^\]]+)?\](\<br \/\>)?/i', '', $s);
|
||||
|
||||
// eliminate description between the other language tags.
|
||||
foreach ($mLanguages as $lang) {
|
||||
if ($mLanguage == $lang) {
|
||||
continue;
|
||||
}
|
||||
$s = preg_replace_callback('/\[(?:^\/[^\]]+\|)?'.preg_quote($lang).'(?:\|[^\]]+)?\].*\[\/(?:^\/[^\]]+\|)?'.preg_quote($lang).'(?:\|[^\]]+)?(?:\]\<br \/\>|\])/isU', 'self::mlang_checkNeverCross', $s);
|
||||
}
|
||||
|
||||
// escape brackets inside of <input type="text" value="...">
|
||||
$s = preg_replace_callback('/(\<input)([^>]*)(\>)/isU', 'self::mlang_unEscapeBracketTextBox', $s);
|
||||
|
||||
// escape brackets inside of <textarea></textarea>
|
||||
$s = preg_replace_callback('/(\<textarea[^>]*\>)(.*)(<\/textarea\>)/isU', 'self::unEscapeBracket', $s);
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
public static function mlang_escapeBracketTextBox($matches)
|
||||
{
|
||||
if (preg_match('/type=["\']?(?=text|hidden)["\']?/i', $matches[2])) {
|
||||
return $matches[1].str_replace('[', '__ml[ml__', $matches[2]).$matches[3];
|
||||
} else {
|
||||
return $matches[1].$matches[2].$matches[3];
|
||||
}
|
||||
}
|
||||
|
||||
public static function mlang_escapeBracket($matches)
|
||||
{
|
||||
return $matches[1].str_replace('[', '__ml[ml__', $matches[2]).$matches[3];
|
||||
}
|
||||
|
||||
public static function mlang_unEscapeBracketTextBox($matches)
|
||||
{
|
||||
if (preg_match('/type=["\']?(?=text|hidden)["\']?/i', $matches[2])) {
|
||||
return $matches[1].str_replace('__ml[ml__', '[', $matches[2]).$matches[3];
|
||||
} else {
|
||||
return $matches[1].$matches[2].$matches[3];
|
||||
}
|
||||
}
|
||||
|
||||
public static function mlang_unEscapeBracket($matches)
|
||||
{
|
||||
return $matches[1].str_replace('__ml[ml__', '[', $matches[2]).$matches[3];
|
||||
}
|
||||
|
||||
public static function mlang_checkNeverCross($matches)
|
||||
{
|
||||
return preg_match(CUBE_UTILS_ML_NEVERCROSSREGEX, $matches[0]) ? $matches[0] : '';
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
$mainfile = '/data/www/html/mainfile.php';
|
||||
|
||||
//$XOOPS_MODULE_D3FORUM = ['forum'];
|
||||
$XOOPS_MODULE_PICO = ['documents', 'htmldocs'];
|
||||
$XOOPS_MODULE_COSMODB = ['newdb', 'newdb1', 'newdb2', 'newdb3', 'newdb4', 'newdb5', 'newdb6', 'newdb7', 'newdb8', 'newdb9', 'newdb10', 'newdb112', 'newdb12', 'newdb13'];
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
DESTDIR=$(dirname $0)/static-contents
|
||||
XOOPS_ROOT_PATH=/data/www/html
|
||||
XOOPS_MODULE_PATH=${XOOPS_ROOT_PATH}/modules
|
||||
|
||||
COSMODBS="
|
||||
newdb1
|
||||
newdb2
|
||||
newdb3
|
||||
newdb5
|
||||
newdb7
|
||||
newdb8
|
||||
newdb9
|
||||
newdb10
|
||||
newdb112
|
||||
newdb12
|
||||
newdb13"
|
||||
|
||||
for i in ${COSMODBS}; do
|
||||
SRC=${XOOPS_MODULE_PATH}/${i}/extract
|
||||
DEST=${DESTDIR}/modules/${i}/extract
|
||||
if test ! -d ${DEST}; then
|
||||
mkdir -p ${DEST}
|
||||
fi
|
||||
rsync -av --delete ${SRC}/ ${DEST}/
|
||||
done
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
DESTDIR=$(dirname $0)/static-contents
|
||||
|
||||
rsync -av --delete /data/www/static/ $DESTDIR/static/
|
||||
@@ -1,193 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/common.inc.php';
|
||||
|
||||
if (!isset($XOOPS_MODULE_COSMODB) || empty($XOOPS_MODULE_COSMODB)) {
|
||||
exit('Not configured for cosmodb module'.PHP_EOL);
|
||||
}
|
||||
|
||||
foreach ($XOOPS_MODULE_COSMODB as $dirname) {
|
||||
dumpCosmoDB($dirname);
|
||||
}
|
||||
|
||||
function dumpCosmoDB($dirname)
|
||||
{
|
||||
global $xoopsDB;
|
||||
$config = [];
|
||||
if (!MyDumpTool::canModuleRead($dirname, 0)) {
|
||||
echo 'No read permission for Guest user: '.$dirname.PHP_EOL;
|
||||
return false;
|
||||
}
|
||||
echo $dirname.PHP_EOL;
|
||||
MyDumpTool::makeDirectory('public/modules/'.$dirname);
|
||||
// copy noimage
|
||||
MyDumpTool::makeDirectory('public/modules/'.$dirname.'/images');
|
||||
$spath = XOOPS_ROOT_PATH.'/modules/'.$dirname.'/images/noimage.gif';
|
||||
$dpath = MYDUMPTOOL_OUTPUTDIR.'/public/modules/'.$dirname.'/images/noimage.gif';
|
||||
MyDumpTool::fileCopy($spath, $dpath);
|
||||
// component_master table
|
||||
$sql = sprintf('SELECT * FROM `%s` ORDER BY `sort`,`comp_id`', $xoopsDB->prefix($dirname.'_component_master'));
|
||||
$modKeys = ['comp_id' => 'i', 'type' => 'i', 'onoff' => 'i', 'sort' => 'i', 'nonull' => 'i', 'textmax' => 'i', 'onoff_refine' => 'i'];
|
||||
// onoff: use for sort, onoff_refine: use for refine search, 0: means yes, 1: means no
|
||||
$config['component'] = queryDatabase($sql, $modKeys);
|
||||
// keyword table
|
||||
$sql = sprintf('SELECT * FROM `%s` ORDER BY `kw_id`', $xoopsDB->prefix($dirname.'_keyword'));
|
||||
$modKeys = ['kw_id' => 'i', 'sort' => 'i'];
|
||||
$res = queryDatabase($sql, $modKeys);
|
||||
foreach ($res as $idx => $row) {
|
||||
$path = explode('/', trim($row['path'], '/'));
|
||||
$parent = array_pop($path);
|
||||
$res[$idx]['pid'] = ($parent == '') ? 0 : intval($parent);
|
||||
unset($res[$idx]['path']);
|
||||
}
|
||||
$config['keyword'] = $res;
|
||||
// detail table
|
||||
$sql = sprintf('SELECT * FROM `%s` WHERE `detail_id`=1', $xoopsDB->prefix($dirname.'_detail'));
|
||||
$modKeys = ['detail_id' => 'd', 'template' => 'h'];
|
||||
$res = queryDatabase($sql, $modKeys);
|
||||
$config['detail'] = $res[0]['template'];
|
||||
// list table
|
||||
$sql = sprintf('SELECT * FROM `%s` WHERE `onoff`=0 ORDER BY `sort`,`list_id`', $xoopsDB->prefix($dirname.'_list'));
|
||||
$modKeys = ['list_id' => 'i', 'type' => 'i', 'onoff' => 'd', 'sort' => 'i'];
|
||||
$config['list'] = queryDatabase($sql, $modKeys);
|
||||
|
||||
//var_dump($config);
|
||||
$dHandler = Cosmoapi_Utils::getTrustModuleHandler('data', 'cosmoapi');
|
||||
$dHandler->setDirname($dirname);
|
||||
$ids = $dHandler->getIds();
|
||||
foreach ($ids as $id => $label) {
|
||||
$obj = $dHandler->get($id);
|
||||
$keywords = [];
|
||||
foreach ($obj->mKeywords as $kObj) {
|
||||
$keywords[] = intval($kObj->mKeywordId);
|
||||
}
|
||||
$components = [];
|
||||
foreach ($obj->mComponents as $cObj) {
|
||||
$components[] = [
|
||||
'name' => $cObj->mName,
|
||||
'value' => $cObj->mValue,
|
||||
];
|
||||
}
|
||||
$items = [];
|
||||
foreach ($obj->mItems as $iObj) {
|
||||
$items[] = [
|
||||
'type' => $iObj->mType,
|
||||
'name' => $iObj->mName,
|
||||
'path' => $iObj->mPath,
|
||||
'reg_date' => intval($iObj->mRegDate),
|
||||
'reg_user' => getUserInfo($iObj->mRegUserId),
|
||||
];
|
||||
}
|
||||
$thumbnails = [];
|
||||
foreach ($obj->mThumbnails as $thumbnail) {
|
||||
$thumbnails[] = [
|
||||
'url' => preg_replace('/^'.preg_quote(XOOPS_URL.'/modules/'.$dirname.'/extract/'.$id.'/thumbnail/','/').'/', '', $thumbnail['url']),
|
||||
'caption' => $thumbnail['caption'],
|
||||
];
|
||||
}
|
||||
$commentTopics = [];
|
||||
foreach ($obj->mComments as $ctObj) {
|
||||
$comment = [
|
||||
'subject' => $ctObj->mComment->mSubject,
|
||||
'message' => $ctObj->mComment->mMessage,
|
||||
'reg_date' => intval($ctObj->mComment->mRegDate),
|
||||
'reg_user' => getUserInfo($ctObj->mComment->mRegUser),
|
||||
];
|
||||
$replies = [];
|
||||
foreach ($ctObj->mReplies as $cObj) {
|
||||
$replies[] = [
|
||||
'subject' => $cObj->mSubject,
|
||||
'message' => $cObj->mMessage,
|
||||
'reg_date' => intval($cObj->mRegDate),
|
||||
'reg_user' => getUserInfo($cObj->mRegUser),
|
||||
];
|
||||
}
|
||||
$commentTopics[] = [
|
||||
'topic_id' => intval($ctObj->mTopicId),
|
||||
'com_id' => intval($ctObj->mComId),
|
||||
'type' => $ctObj->mType,
|
||||
'comment' => $comment,
|
||||
'replies' => $replies,
|
||||
];
|
||||
}
|
||||
$links = [];
|
||||
foreach ($obj->mLinks as $lObj) {
|
||||
$href = preg_replace(preg_replace('/https/', 'https?', '/'.preg_quote(XOOPS_URL, '/').'/'), '', $lObj->mHref);
|
||||
if (strpos($href, 'invbrain') !== false) {
|
||||
var_dump($href);
|
||||
}
|
||||
$links[] = [
|
||||
'type' => (int) $lObj->mType,
|
||||
'user' => getUserInfo($lObj->mUid),
|
||||
'name' => $lObj->mName,
|
||||
'href' => $href,
|
||||
'note' => $lObj->mNote,
|
||||
];
|
||||
}
|
||||
$basic = [
|
||||
'id' => $id,
|
||||
'label' => $obj->mLabel,
|
||||
'reg_date' => intval($obj->mRegDate),
|
||||
'author' => getUserInfo($obj->mAuthor),
|
||||
'views' => intval($obj->mViews),
|
||||
'components' => $components,
|
||||
'thumbnails' => $thumbnails,
|
||||
'items' => $items,
|
||||
];
|
||||
$detail = [
|
||||
'id' => $id,
|
||||
'users' => array_map('getUserInfo', $obj->mUserIds),
|
||||
'keywords' => $keywords,
|
||||
'comment_topics' => $commentTopics,
|
||||
'links' => $links,
|
||||
];
|
||||
MyDumpTool::saveJson('public/modules/'.$dirname.'/'.$id.'.json', $detail);
|
||||
$config['data'][] = $basic;
|
||||
}
|
||||
MyDumpTool::saveJson('public/modules/'.$dirname.'/index.json', $config);
|
||||
}
|
||||
|
||||
function getUserInfo($uid) {
|
||||
static $users = [];
|
||||
if (!isset($users[$uid])) {
|
||||
$memberHandler = xoops_gethandler('member');
|
||||
$userObj = $memberHandler->getUser($uid);
|
||||
if (is_object($userObj)) {
|
||||
$name = trim($userObj->get('name'));
|
||||
$uname = trim($userObj->get('uname'));
|
||||
$users[$uid] = $uname; // $name !== '' ? $name.' ('.$uname.')' : $uname;
|
||||
} else {
|
||||
$users[$uid] = 'unknown';
|
||||
}
|
||||
}
|
||||
return $users[$uid];
|
||||
}
|
||||
|
||||
function queryDatabase($sql, $modKeys = []) {
|
||||
global $xoopsDB;
|
||||
$ret = [];
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
MyDumpTool::decode($row);
|
||||
foreach ($modKeys as $key => $type) {
|
||||
switch ($type) {
|
||||
case 'i':
|
||||
$row[$key] = intval($row[$key]);
|
||||
break;
|
||||
case 'd':
|
||||
unset($row[$key]);
|
||||
break;
|
||||
case 'h':
|
||||
$row[$key] = preg_replace('/\s+([a-z]+)=\'([^\']*)\'/U', ' \1="\2"', MyDumpTool::fixHtml($row[$key]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
$ret[] = $row;
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/common.inc.php';
|
||||
|
||||
$prefix = $xoopsDB->prefix();
|
||||
|
||||
$table = 'nipfcredits_organization';
|
||||
if (!MyDumpTool::tableExists($table)) {
|
||||
exit('credits module not found'.PHP_EOL);
|
||||
}
|
||||
MyDumpTool::makeDirectory('public/modules/credits');
|
||||
|
||||
// module
|
||||
$module = MyDumpTool::getModule('credits');
|
||||
|
||||
// organization
|
||||
$organization = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
`o`.*
|
||||
FROM `${prefix}_nipfcredits_organization` AS `o`
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
MyDumpTool::decode($row);
|
||||
$organization = $row;
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
$organization['com_email'] = str_replace('@', ' (at) ', $organization['com_email']);
|
||||
$organization['com_url'] = XOOPS_URL;
|
||||
$organization['institute'] = '[en]Neuroinformatics Unit, RIKEN Center for Brain Science[/en][ja]理化学研究所 脳神経科学研究センター 神経情報基盤ユニット[/ja]';
|
||||
$organization['url'] = 'https://www.ni.riken.jp/';
|
||||
$organization['email'] = 'office (at) ni.riken.jp';
|
||||
$organization['address'] = '[en]Hirosawa 2-1, Wako, Saitama, 351-0198 Japan[/en][ja]埼玉県和光市広沢 2-1[/ja]';
|
||||
$organization['tel'] = '+81 48 (462) 1111';
|
||||
|
||||
// member
|
||||
$member = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
`m`.*
|
||||
FROM `${prefix}_nipfcredits_member` AS `m`
|
||||
ORDER BY `m`.`weight`
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
MyDumpTool::decode($row);
|
||||
$member[] = $row;
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
$opage = getOrganizationPage($organization, $member, $module);
|
||||
MyDumpTool::saveJson('public/modules/credits/0.json', $opage);
|
||||
//var_dump($opage);
|
||||
|
||||
// pages
|
||||
$pages = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
`p`.*
|
||||
FROM `${prefix}_nipfcredits_page` AS `p`
|
||||
ORDER BY `p`.`weight`
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
MyDumpTool::decode($row);
|
||||
myDumpTool::convertToInt($row, ['pid', 'weight', 'lastupdate']);
|
||||
$content = replaceOrganization($row['content'], $organization);
|
||||
$content = MyDumpTool::fixHtml($content);
|
||||
$content = preg_replace('/href="(?:\.\/|'.preg_quote(XOOPS_URL, '/').'\/modules\/credits\/)?index\.php\?id=(\d+)"/', 'href="/credits/\1"', $content);
|
||||
$imageUrl = [];
|
||||
$content = preg_replace_callback('/<img ([^>]*)src="([^"]+)"([^>]*)\/>/Usi', function ($matches) use (&$imageUrl, $row) {
|
||||
$url = $matches[2];
|
||||
if (preg_match('/^(.+)\/([^\/]+\.(:?png|jpg|gif|svg))(\?.+)?$/i', $url, $m)) {
|
||||
if (!isset($imageUrl[$m[1]])) {
|
||||
$imageUrl[$m[1]] = count($imageUrl) + 1;
|
||||
}
|
||||
$id = $imageUrl[$m[1]];
|
||||
$fname = '/modules/credits/images/'.$row['pid'].'/'.$id.'/'.urldecode($m[2]);
|
||||
$fpath = MYDUMPTOOL_OUTPUTDIR.'/public'.$fname;
|
||||
if (!file_exists($fpath)) {
|
||||
MyDumpTool::makeDirectory('public/modules/credits/images');
|
||||
MyDumpTool::makeDirectory('public/modules/credits/images/'.$row['pid']);
|
||||
MyDumpTool::makeDirectory('public/modules/credits/images/'.$row['pid'].'/'.$id);
|
||||
if (preg_match('/^\//', $url)) {
|
||||
$url = XOOPS_URL.$url;
|
||||
}
|
||||
MyDumpTool::fileDownload($url, $fpath);
|
||||
}
|
||||
$url = 'images/'.$row['pid'].'/'.$id.'/'.$m[2];
|
||||
}
|
||||
|
||||
return '<img '.$matches[1].'src="'.$url.'"'.$matches[3].'/>';
|
||||
}, $content);
|
||||
$content = preg_replace('/<a (?:[^>]*)href="(?:'.preg_quote(XOOPS_URL, '/').')?\/modules\/xoonips\/(?:register|edit|user)[^"]*"(?:[^>]*)>(.*)<\/a>/Usi', '<s>\1</s>(closed)', $content);
|
||||
$row['content'] = $content;
|
||||
MyDumpTool::dropColumn($row, ['weight']);
|
||||
$pages[] = [
|
||||
'id' => $row['pid'],
|
||||
'title' => $row['title'],
|
||||
];
|
||||
MyDumpTool::saveJson('public/modules/credits/'.$row['pid'].'.json', $row);
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
//var_dump($pages);
|
||||
|
||||
$pages[] = [
|
||||
'id' => $opage['id'],
|
||||
'title' => $opage['title'],
|
||||
];
|
||||
|
||||
$data = [
|
||||
'name' => $module['name'],
|
||||
'pages' => $pages,
|
||||
];
|
||||
MyDumpTool::saveJson('public/modules/credits/index.json', $data);
|
||||
|
||||
function replaceOrganization($text, $org)
|
||||
{
|
||||
$textutil = xoonips_getutility('text');
|
||||
$map = [
|
||||
'{COMMITTEE_NAME}' => $textutil->html_special_chars($org['committee']),
|
||||
'{COMMITTEE_EMAIL}' => '<s>'.$textutil->html_special_chars($org['com_email']).'</s>(committee email has been closed)',
|
||||
'{INSTITUTE_NAME}' => $textutil->html_special_chars($org['institute']),
|
||||
'{INSTITUTE_URL}' => $textutil->html_special_chars($org['url']),
|
||||
'{INSTITUTE_EMAIL}' => $textutil->html_special_chars($org['email']),
|
||||
'{INSTITUTE_ADDRESS}' => $textutil->html_special_chars($org['address']),
|
||||
'{INSTITUTE_TEL}' => $textutil->html_special_chars($org['tel']),
|
||||
'{PLATFORM_URL}' => XOOPS_URL,
|
||||
];
|
||||
|
||||
return str_replace(array_keys($map), $map, $text);
|
||||
}
|
||||
|
||||
function getOrganizationPage($org, $member, $module)
|
||||
{
|
||||
$textutil = xoonips_getutility('text');
|
||||
$members = [];
|
||||
if (!empty($member)) {
|
||||
$members[] = ' <li>[en]Members[/en][ja]委員一覧[/ja]';
|
||||
$members[] = ' <ul>';
|
||||
foreach ($member as $m) {
|
||||
$members[] = ' <li>'.$textutil->html_special_chars($m['name'].' : '.$m['division']).'</li>';
|
||||
}
|
||||
$members[] = ' </ul>';
|
||||
$members[] = ' </li>';
|
||||
}
|
||||
$members = implode("\n", $members);
|
||||
$committee = $textutil->html_special_chars($org['committee']);
|
||||
$institute = $textutil->html_special_chars($org['institute']);
|
||||
$address = $textutil->html_special_chars($org['address']);
|
||||
$url = $textutil->html_special_chars($org['url']);
|
||||
$email = $textutil->html_special_chars($org['com_email']);
|
||||
$html = <<< HTML
|
||||
<h4>${committee}</h4>
|
||||
<ul>
|
||||
<li>E-Mail: <strike>${email}</strike></li>
|
||||
${members}
|
||||
</ul>
|
||||
<h4>${institute}</h4>
|
||||
<ul>
|
||||
<li>[en]Address[/en][ja]所在地[/ja]: ${address}</li>
|
||||
<li>URL: <a href="${url}" target="_blank" rel=”noopener noreferrer”>${url}</a></li>
|
||||
</ul>
|
||||
HTML;
|
||||
|
||||
return [
|
||||
'id' => 0,
|
||||
'title' => '[en]Organization[/en][ja]運用母体[/ja]',
|
||||
'content' => $html,
|
||||
'lastupdate' => 0,
|
||||
];
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/common.inc.php';
|
||||
|
||||
if (!isset($XOOPS_MODULE_PICO) || empty($XOOPS_MODULE_PICO)) {
|
||||
exit('Not configured for pico module'.PHP_EOL);
|
||||
}
|
||||
|
||||
error_reporting(0);
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/include/main_functions.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/include/common_functions.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/pico.textsanitizer.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/PicoUriMapper.class.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/PicoPermission.class.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/PicoModelCategory.class.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/PicoModelContent.class.php';
|
||||
require_once XOOPS_TRUST_PATH.'/libs/altsys/class/AltsysBreadcrumbs.class.php';
|
||||
error_reporting(E_ALL);
|
||||
|
||||
foreach ($XOOPS_MODULE_PICO as $pico) {
|
||||
dumpPico($pico);
|
||||
}
|
||||
|
||||
function dumpPico($pico)
|
||||
{
|
||||
global $xoopsDB;
|
||||
$prefix = $xoopsDB->prefix();
|
||||
$table = $pico.'_contents';
|
||||
if (!MyDumpTool::tableExists($table)) {
|
||||
exit('pico('.$pico.') module not found'.PHP_EOL);
|
||||
}
|
||||
MyDumpTool::makeDirectory('public/modules/'.$pico);
|
||||
$moduleHandler = xoops_gethandler('module');
|
||||
$module = $moduleHandler->getByDirname($pico);
|
||||
$moduleConfigHandler = xoops_gethandler('config');
|
||||
$moduleConfig = $moduleConfigHandler->getConfigsByDirname($pico);
|
||||
$permObj = PicoPermission::getInstance();
|
||||
$perms = $permObj->getPermissions($pico);
|
||||
$categoryHandler = new PicoCategoryHandler($pico, $perms);
|
||||
$contentHandler = new PicoContentHandler($pico);
|
||||
$cats = $categoryHandler->getAllCategories();
|
||||
$confContents = [];
|
||||
$confCategories = [];
|
||||
$dataMap = [];
|
||||
$dataSeq = 1;
|
||||
foreach ($cats as $cat) {
|
||||
$catData = $cat->getData();
|
||||
$modConfig = $cat->getOverriddenModConfig();
|
||||
$link = pico_common_make_category_link4html($modConfig, $catData, $pico);
|
||||
MyDumpTool::decode($catData);
|
||||
$confCategories[] = [
|
||||
'id' => $catData['id'],
|
||||
'title' => $catData['cat_title'],
|
||||
'desc' => (string) $catData['cat_desc'],
|
||||
'pid' => (int) $catData['pid'],
|
||||
'weight' => (int) $catData['cat_weight'],
|
||||
'link' => $link,
|
||||
];
|
||||
$contents = $contentHandler->getCategoryContents($cat);
|
||||
foreach ($contents as $content) {
|
||||
$data = $content->getData();
|
||||
$link = pico_common_make_content_link4html($modConfig, $data);
|
||||
MyDumpTool::decode($data);
|
||||
$url = XOOPS_URL.'/modules/'.$pico.'/'.$link;
|
||||
$content_en = normalizeHtml(MyDumpTool::mlang($data['body_cached'], $url, 'en'), $url);
|
||||
$content_ja = normalizeHtml(MyDumpTool::mlang($data['body_cached'], $url, 'ja'), $url);
|
||||
$item = [
|
||||
'id' => $data['id'],
|
||||
'title' => $data['subject_raw'],
|
||||
'content' => [
|
||||
'en' => $content_en,
|
||||
'ja' => $content_ja,
|
||||
],
|
||||
];
|
||||
MyDumpTool::saveJson('public/modules/'.$pico.'/'.$data['id'].'.json', $item);
|
||||
$confContents[] = [
|
||||
'id' => $item['id'],
|
||||
'title' => $data['subject_raw'],
|
||||
'cat_id' => (int) $data['cat_id'],
|
||||
'weight' => (int) $data['weight'],
|
||||
'link' => $link,
|
||||
];
|
||||
}
|
||||
}
|
||||
$moduleinfo = [
|
||||
'name' => $module->get('name'),
|
||||
'dirname' => $pico,
|
||||
'message' => $moduleConfig['top_message'],
|
||||
'show_menuinmoduletop' => (int) $moduleConfig['show_menuinmoduletop'],
|
||||
'show_listasindex' => (int) $moduleConfig['show_listasindex'],
|
||||
'show_breadcrumbs' => (int) $moduleConfig['show_breadcrumbs'],
|
||||
'show_pagenavi' => (int) $moduleConfig['show_pagenavi'],
|
||||
];
|
||||
MyDumpTool::decode($moduleinfo);
|
||||
$config = [
|
||||
'module' => $moduleinfo,
|
||||
'categories' => $confCategories,
|
||||
'contents' => $confContents,
|
||||
];
|
||||
MyDumpTool::saveJson('public/modules/'.$pico.'/index.json', $config);
|
||||
}
|
||||
|
||||
function normalizeUrl($url, $base)
|
||||
{
|
||||
$localpath = function ($url) {
|
||||
$purl1 = parse_url($url);
|
||||
$purl2 = parse_url(XOOPS_URL);
|
||||
if ($purl1['host'] === $purl2['host']) {
|
||||
$url = preg_replace('/^http:/', 'https:', $url);
|
||||
}
|
||||
|
||||
return $url;
|
||||
};
|
||||
if ('' === $url) {
|
||||
return $localpath($base);
|
||||
}
|
||||
$purl = parse_url($url);
|
||||
if (isset($purl['scheme'])) {
|
||||
return $localpath($url);
|
||||
}
|
||||
$pburl = parse_url($base);
|
||||
$path = $pburl['path'];
|
||||
if ('/' == $url[0]) {
|
||||
$path = '';
|
||||
} elseif ('#' !== $url[0] && '?' !== $url[0]) {
|
||||
$path = preg_replace('#/[^/]*$#', '', $path).'/';
|
||||
}
|
||||
$path .= $url;
|
||||
$pattern = ['#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'];
|
||||
do {
|
||||
$path = preg_replace($pattern, '/', $path, -1, $count);
|
||||
} while (0 < $count);
|
||||
|
||||
return $localpath($pburl['scheme'].'://'.$pburl['host'].$path);
|
||||
}
|
||||
|
||||
function downloadLocalFile($url)
|
||||
{
|
||||
$url2 = preg_replace('/'.preg_quote(XOOPS_URL, '/').'/', '', $url);
|
||||
if (preg_match('/^\//', $url2)) {
|
||||
if (preg_match('/\/[^\/]+\.(zip|pdf|doc|docx|xls|xlsx|ppt|pptx|txt|gif|jpg|jpeg|png|wmv|tar\.gz|mpg|obj|mtl|tif|avi)(\?.+)?$/i', $url2)) {
|
||||
$spath = urldecode($url2);
|
||||
$opath = MYDUMPTOOL_OUTPUTDIR.'/public'.$spath;
|
||||
if (!file_exists($opath)) {
|
||||
$odir = dirname($opath);
|
||||
mkdir($odir, 0755, true);
|
||||
MyDumpTool::fileDownload($url, $opath);
|
||||
}
|
||||
} elseif (!preg_match('/\/([^\/]+\.(php|html))?(\?.+)?(#.+)?$/i', $url2)) {
|
||||
var_dump($url2);
|
||||
}
|
||||
$url = $url2;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
function normalizeHtml($text, $path)
|
||||
{
|
||||
$text = MyDumpTool::fixHtml($text);
|
||||
$text = preg_replace_callback('/<img ([^>]*)src="(.*)"([^>]*)\/>/Us', function ($matches) use ($path) {
|
||||
$url = htmlspecialchars_decode($matches[2], ENT_QUOTES);
|
||||
$url = normalizeUrl($url, $path);
|
||||
$url = downloadLocalFile($url);
|
||||
|
||||
return '<img '.$matches[1].'src="'.htmlspecialchars($url, ENT_QUOTES).'"'.$matches[3].'/>';
|
||||
}, $text);
|
||||
$text = preg_replace_callback('/<a ([^>]*)href="([^"]+)"([^>]*)>/Us', function ($matches) use ($path) {
|
||||
$url = htmlspecialchars_decode($matches[2], ENT_QUOTES);
|
||||
$url = normalizeUrl($url, $path);
|
||||
$url = downloadLocalFile($url);
|
||||
|
||||
return '<a '.$matches[1].'href="'.htmlspecialchars($url, ENT_QUOTES).'"'.$matches[3].'>';
|
||||
}, $text);
|
||||
$text = preg_replace_callback('/<embed ([^>]*)src="(.*)"([^>]*)\/>/Us', function ($matches) use ($path) {
|
||||
$url = htmlspecialchars_decode($matches[2], ENT_QUOTES);
|
||||
$url = normalizeUrl($url, $path);
|
||||
$url = downloadLocalFile($url);
|
||||
|
||||
return '<embed '.$matches[1].'src="'.htmlspecialchars($url, ENT_QUOTES).'"'.$matches[3].'/>';
|
||||
}, $text);
|
||||
$text = preg_replace('/ lang="([^"]+)"/Us', '', $text);
|
||||
$text = preg_replace('/ xml:lang="([^"]+)"/Us', '', $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/common.inc.php';
|
||||
|
||||
MyDumpTool::makeDirectory('public/modules');
|
||||
MyDumpTool::makeDirectory('public/modules/PubMedPDF');
|
||||
// MyDumpTool::makeDirectory('public/modules/PubMedPDF/detail');
|
||||
|
||||
dumpPubMedPDF();
|
||||
|
||||
function dumpPubMedPDF()
|
||||
{
|
||||
global $xoopsDB;
|
||||
$prefix = $xoopsDB->prefix();
|
||||
|
||||
// module
|
||||
$module = MyDumpTool::getModule('PubMedPDF');
|
||||
|
||||
// paper
|
||||
$paper = [];
|
||||
$authorIdMap = [];
|
||||
$journalIdMap = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
*
|
||||
FROM `${prefix}_pmid_id`
|
||||
ORDER BY `id` ASC
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
MyDumpTool::decode($row);
|
||||
MyDumpTool::convertToInt($row, ['id', 'PMID', 'Journal', 'Year']);
|
||||
$authors = array_map('trim', explode(',', rtrim(trim($row['Author']), ',')));
|
||||
$authorIds = [];
|
||||
foreach ($authors as $_author) {
|
||||
$authorId = (int) substr($_author, 1, -1);
|
||||
$authorIds[] = $authorId;
|
||||
$authorIdMap[$authorId][] = $row['id'];
|
||||
}
|
||||
$journalIdMap[$row['Journal']][] = $row['id'];
|
||||
$paper[] = [
|
||||
'id' => $row['id'],
|
||||
'pmid' => $row['PMID'],
|
||||
'journal_id' => $row['Journal'],
|
||||
'year' => $row['Year'],
|
||||
'volume' => $row['Volume'],
|
||||
'page' => $row['Page'],
|
||||
'author_ids' => $authorIds,
|
||||
'title' => $row['Title'],
|
||||
];
|
||||
$paperDetail = [
|
||||
'id' => $row['id'],
|
||||
'abstract' => $row['Abstract'],
|
||||
'reg_user' => $row['R_usr'],
|
||||
'reg_date' => $row['R_date'],
|
||||
];
|
||||
//MyDumpTool::saveJson('public/modules/PubMedPDF/detail/'.$row['id'].'.json', $paperDetail);
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
|
||||
// author
|
||||
$author = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
*
|
||||
FROM `${prefix}_pmid_author`
|
||||
ORDER BY `Author` ASC
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
MyDumpTool::decode($row);
|
||||
MyDumpTool::convertToInt($row, ['id']);
|
||||
$author[] = [
|
||||
'id' => $row['id'],
|
||||
'name' => $row['Author'],
|
||||
'count' => count($authorIdMap[$row['id']]),
|
||||
];
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
|
||||
// journal
|
||||
$journal = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
*
|
||||
FROM `${prefix}_pmid_journal`
|
||||
ORDER BY `Journal` ASC
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
MyDumpTool::decode($row);
|
||||
MyDumpTool::convertToInt($row, ['id']);
|
||||
$journal[] = [
|
||||
'id' => $row['id'],
|
||||
'name' => $row['Journal'],
|
||||
'count' => count($journalIdMap[$row['id']]),
|
||||
];
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
|
||||
$data = [
|
||||
'name' => $module['name'],
|
||||
'author' => $author,
|
||||
'journal' => $journal,
|
||||
'paper' => $paper,
|
||||
];
|
||||
MyDumpTool::saveJson('public/modules/PubMedPDF/index.json', $data);
|
||||
}
|
||||
-1176
File diff suppressed because it is too large
Load Diff
@@ -1,106 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/common.inc.php';
|
||||
|
||||
$data = dumpXwords();
|
||||
|
||||
MyDumpTool::makeDirectory('public/modules/xwords');
|
||||
MyDumpTool::saveJson('public/modules/xwords/index.json', $data);
|
||||
|
||||
function dumpXwords()
|
||||
{
|
||||
global $xoopsDB;
|
||||
$prefix = $xoopsDB->prefix();
|
||||
|
||||
// module
|
||||
$module = MyDumpTool::getModule('xwords');
|
||||
|
||||
// category
|
||||
$category = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
*
|
||||
FROM `${prefix}_xwords_cat`
|
||||
ORDER BY `weight` ASC
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
MyDumpTool::decode($row);
|
||||
MyDumpTool::convertToInt($row, ['categoryID', 'total', 'weight']);
|
||||
$category[] = [
|
||||
'id' => $row['categoryID'],
|
||||
'name' => $row['name'],
|
||||
'total' => $row['total'],
|
||||
];
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
|
||||
// entry
|
||||
$entry = [];
|
||||
$sql = <<< SQL
|
||||
SELECT
|
||||
*
|
||||
FROM `${prefix}_xwords_ent`
|
||||
WHERE `offline` = 0
|
||||
SQL;
|
||||
if (!($res = $xoopsDB->query($sql))) {
|
||||
var_dump($xoopsDB);
|
||||
exit();
|
||||
}
|
||||
while ($row = $xoopsDB->fetchArray($res)) {
|
||||
MyDumpTool::decode($row);
|
||||
MyDumpTool::convertToInt($row, ['entryID', 'categoryID', 'datesub', 'counter']);
|
||||
$procs = explode(',', $row['proc']);
|
||||
$term = $row['term'];
|
||||
$yomi = $procs[1];
|
||||
$initTerm = mb_substr($term, 0, 1, 'UTF-8');
|
||||
$initTerm = mb_convert_kana($initTerm, 'asHc', 'UTF-8');
|
||||
$initTerm = strtoupper($initTerm);
|
||||
$initYomi = mb_substr($yomi, 0, 1, 'UTF-8');
|
||||
$initYomi = mb_convert_kana($initYomi, 'asHc', 'UTF-8');
|
||||
$initYomi = strtoupper($initYomi);
|
||||
$kana = [
|
||||
'01' => '[あ-お]',
|
||||
'02' => '[か-こが-ご]',
|
||||
'03' => '[さ-そざ-ぞ]',
|
||||
'04' => '[た-とだ-ど]',
|
||||
'05' => '[な-の]',
|
||||
'06' => '[は-ほば-ぼぱ-ぽ]',
|
||||
'07' => '[ま-も]',
|
||||
'08' => '[や-よ]',
|
||||
'09' => '[ら-ろ]',
|
||||
'10' => '[わ-ん]',
|
||||
'11' => '.*',
|
||||
];
|
||||
if (preg_match('/^[A-Z]$/', $initTerm)) {
|
||||
$init = $initTerm;
|
||||
} else {
|
||||
foreach ($kana as $index => $pattern) {
|
||||
if (preg_match('/^'.$pattern.'/u', $initYomi)) {
|
||||
$init = $index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$entry[] = [
|
||||
'id' => $row['entryID'],
|
||||
'cat_id' => $row['categoryID'],
|
||||
'term' => $row['term'],
|
||||
'yomi' => $yomi,
|
||||
'initial' => $init,
|
||||
'definition' => $row['definition'],
|
||||
'timestamp' => $row['datesub'],
|
||||
'counter' => $row['counter'],
|
||||
];
|
||||
}
|
||||
$xoopsDB->freeRecordSet($res);
|
||||
|
||||
return [
|
||||
'name' => $module['name'],
|
||||
'category' => $category,
|
||||
'entry' => $entry,
|
||||
];
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
if ($argc !== 2 || !preg_match('/\.html?$/', $argv[1]) || !file_exists($argv[1])) {
|
||||
exit('Error: html file not found.'.PHP_EOL);
|
||||
}
|
||||
$fpath = $argv[1];
|
||||
|
||||
$timestamp = filemtime($fpath);
|
||||
$text = file_get_contents($fpath);
|
||||
$encoding = mb_detect_encoding($text);
|
||||
if (!in_array($encoding, ['UTF-8', 'ASCII'])) {
|
||||
if (in_array($encoding, ['SJIS'])) {
|
||||
$encoding = 'CP932';
|
||||
}
|
||||
$text = mb_convert_encoding($text, 'UTF-8', $encoding);
|
||||
$text = preg_replace('/(<meta\s+http-equiv=(?:"|\')content-type(?:"|\')\s+content=(?:"|\')text\/html;\s*charset=)(?:[^"\']+)((?:"|\')\s*>)/is', '\1UTF-8\2', $text);
|
||||
}
|
||||
$text = \Normalizer::normalize(trim($text), \Normalizer::FORM_C);
|
||||
static $config = [
|
||||
//'clean' => true,
|
||||
'hide-comments' => true,
|
||||
'indent' => true,
|
||||
'output-xhtml' => true,
|
||||
'preserve-entities' => true,
|
||||
//'show-body-only' => true,
|
||||
'wrap' => 0,
|
||||
'input-encoding' => 'utf8',
|
||||
'output-encoding' => 'utf8',
|
||||
'output-bom' => false,
|
||||
];
|
||||
$text = tidy_repair_string($text, $config);
|
||||
$text = preg_replace('/<table([^>]*)>((?:\s*<caption[^>]*>.*<\/caption>)?\s*)<tr([^>]*)>/Us', '<table\1>\2<tbody><tr\3>', $text);
|
||||
$text = preg_replace('/<\/tr>(\s*)<\/table>/s', '</tr></tbody>\1</table>', $text);
|
||||
|
||||
file_put_contents($fpath, $text);
|
||||
touch($fpath, $timestamp);
|
||||
@@ -1,311 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/common.inc.php';
|
||||
|
||||
if (!isset($XOOPS_MODULE_PICO) || empty($XOOPS_MODULE_PICO)) {
|
||||
exit('Not configured for pico module'.PHP_EOL);
|
||||
}
|
||||
|
||||
error_reporting(0);
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/include/main_functions.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/include/common_functions.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/include/transact_functions.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/pico.textsanitizer.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/PicoUriMapper.class.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/PicoPermission.class.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/PicoModelCategory.class.php';
|
||||
require_once XOOPS_TRUST_PATH.'/modules/pico/class/PicoModelContent.class.php';
|
||||
require_once XOOPS_TRUST_PATH.'/libs/altsys/class/AltsysBreadcrumbs.class.php';
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
foreach ($XOOPS_MODULE_PICO as $dirname) {
|
||||
$pico = new FixPicoWraps($dirname);
|
||||
if ($pico->isWrapsMode()) {
|
||||
$pico->fixCategories();
|
||||
$pico->fixContents();
|
||||
}
|
||||
}
|
||||
|
||||
class FixPicoWraps
|
||||
{
|
||||
private $mDirname;
|
||||
private $mModule;
|
||||
private $mModuleConfig;
|
||||
private $mWrapsPath;
|
||||
|
||||
public function __construct($dirname)
|
||||
{
|
||||
$this->mDirname = $dirname;
|
||||
$moduleHandler = xoops_gethandler('module');
|
||||
$this->mModule = $moduleHandler->getByDirname($dirname);
|
||||
$moduleConfigHandler = xoops_gethandler('config');
|
||||
$this->mModuleConfig = $moduleConfigHandler->getConfigsByDirname($dirname);
|
||||
$this->mWrapsPath = XOOPS_TRUST_PATH.'/wraps/'.$dirname;
|
||||
}
|
||||
|
||||
public function isWrapsMode()
|
||||
{
|
||||
$key = 'use_wraps_mode';
|
||||
|
||||
return isset($this->mModuleConfig[$key]) && '1' === $this->mModuleConfig[$key];
|
||||
}
|
||||
|
||||
public function getWrapsFiles()
|
||||
{
|
||||
exec(sprintf('find "%s" -type f -name "*.html" -o -name "*.htm"', $this->mWrapsPath), $fpaths);
|
||||
$ret = [];
|
||||
foreach ($fpaths as $fpath) {
|
||||
$ret[] = preg_replace('/^'.preg_quote($this->mWrapsPath, '/').'/', '', $fpath);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getWrapsFilesDirectory()
|
||||
{
|
||||
$files = $this->getWrapsFiles();
|
||||
$dirs = array_map('dirname', $files);
|
||||
$ret = array_unique($dirs);
|
||||
sort($ret);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function fixCategories()
|
||||
{
|
||||
$catVpaths = $this->getWrapsFilesDirectory();
|
||||
$registeredCatVpaths = ['/' => 0];
|
||||
$categories = $this->getAllCategories();
|
||||
foreach ($categories as $category) {
|
||||
$contents = $category->getContents(true);
|
||||
$data = $category->getData();
|
||||
if (empty($contents)) {
|
||||
// remove empty category.
|
||||
echo "empty category found. deleting...\n";
|
||||
$this->deleteCategory($category);
|
||||
continue;
|
||||
}
|
||||
if (null !== $data['cat_vpath']) {
|
||||
if (!in_array($data['cat_vpath'], $catVpaths)) {
|
||||
echo 'category vpath not found. '.$data['cat_vpath'].' need to confirm by manual.'."\n";
|
||||
} else {
|
||||
$registeredCatVpaths[$data['cat_vpath']] = intval($data['cat_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
// register not registered wraps directory.
|
||||
$notRegisteredCatVpaths = array_diff($catVpaths, array_keys($registeredCatVpaths));
|
||||
foreach ($notRegisteredCatVpaths as $catVpath) {
|
||||
$title = basename($catVpath);
|
||||
$this->createCategory($title, '', 0, $catVpath, 0);
|
||||
}
|
||||
// update parent directory
|
||||
$categories = $this->getAllCategories();
|
||||
$cmap = ['/' => ['cat_id' => '0', 'cat_vpath' => '/']];
|
||||
foreach ($categories as $category) {
|
||||
$data = $category->getData();
|
||||
if (null !== $data['cat_vpath']) {
|
||||
$cmap[$data['cat_vpath']] = $data;
|
||||
}
|
||||
}
|
||||
foreach ($cmap as $cvpath => $data) {
|
||||
if ('0' === $data['cat_id']) {
|
||||
continue;
|
||||
}
|
||||
$pcvpath = dirname($cvpath);
|
||||
if (!isset($cmap[$pcvpath])) {
|
||||
echo 'parent category vpath not found: '.$pcvpath.PHP_EOL;
|
||||
} else {
|
||||
$pdata = $cmap[$pcvpath];
|
||||
if ($data['pid'] !== $pdata['cat_id']) {
|
||||
echo 'parent category id mismatched: '.$data['cat_vpath'].' need to confirm by manual.'."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function fixContents()
|
||||
{
|
||||
$wrapFiles = $this->getWrapsFiles();
|
||||
$foundFiles = [];
|
||||
foreach ($this->getAllCategories() as $category) {
|
||||
$cdata = $category->getData();
|
||||
$cvpath = null === $cdata['cat_vpath'] ? '/' : $cdata['cat_vpath'];
|
||||
$contents = $category->getContents(true);
|
||||
foreach ($contents as $content) {
|
||||
$data = $content->getData4Html();
|
||||
$vpath = $data['vpath'];
|
||||
if ($cvpath !== dirname($vpath)) {
|
||||
echo 'vpath and cat_vpath is not matched..:'.$vpath."\n";
|
||||
}
|
||||
$filters = array_map('trim', explode('|', $data['filters']));
|
||||
if (in_array('wraps', $filters)) {
|
||||
$fpath = $this->mWrapsPath.$data['vpath'];
|
||||
if (!file_exists($fpath)) {
|
||||
echo 'wraps file not found: '.$data['vpath'].PHP_EOL;
|
||||
}
|
||||
}
|
||||
$html = trim($data['body_cached']);
|
||||
if (0 === strlen($html)) {
|
||||
echo 'Empty file found: '.$data['vpath'].PHP_EOL;
|
||||
}
|
||||
if (in_array($data['vpath'], $wrapFiles)) {
|
||||
$foundFiles[] = $data['vpath'];
|
||||
} else {
|
||||
echo 'Real wraps file not found: '.$data['vpath'].PHP_EOL;
|
||||
}
|
||||
if (preg_match('/<script/', $html)) {
|
||||
echo 'Javascript found : '.$data['vpath'].PHP_EOL;
|
||||
var_dump($html);
|
||||
exit();
|
||||
}
|
||||
if (preg_match('/onclick=/', $html)) {
|
||||
echo 'Javascript found : '.$data['vpath'].PHP_EOL;
|
||||
var_dump($html);
|
||||
exit();
|
||||
}
|
||||
$hasError = false;
|
||||
$html = $this->checkMlang($html, $hasError);
|
||||
if ($hasError) {
|
||||
echo 'mlang error detected: '.$data['vpath'].PHP_EOL;
|
||||
var_dump($html);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
sort($foundFiles);
|
||||
$missingFiles = array_diff($wrapFiles, $foundFiles);
|
||||
if (!empty($missingFiles)) {
|
||||
echo 'Missing file found: '.implode(', ', $missingFiles).PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
private function getAllCategories()
|
||||
{
|
||||
$permObj = PicoPermission::getInstance();
|
||||
$perms = $permObj->getPermissions($this->mDirname);
|
||||
$categoryHandler = new PicoCategoryHandler($this->mDirname, $perms);
|
||||
|
||||
return $categoryHandler->getAllCategories(true);
|
||||
}
|
||||
|
||||
private function createCategory($title, $desc, $weight, $vpath, $pid)
|
||||
{
|
||||
$db = XoopsDatabaseFactory::getDatabaseConnection();
|
||||
list($cid) = $db->fetchRow($db->query('SELECT MAX(cat_id)+1 FROM '.$db->prefix($this->mDirname.'_categories')));
|
||||
list($permId) = $db->fetchRow($db->query('SELECT cat_permission_id FROM '.$db->prefix($this->mDirname.'_categories').' WHERE cat_id='.intval($pid)));
|
||||
$vars = [
|
||||
'cat_id' => intval($cid),
|
||||
'cat_permission_id' => intval($permId),
|
||||
'cat_vpath' => ('' === $vpath ? 'NULL' : $db->quoteString($vpath)),
|
||||
'pid' => intval($pid),
|
||||
'cat_title' => $db->quoteString($title),
|
||||
'cat_desc' => $db->quoteString($desc),
|
||||
'cat_path_in_tree' => $db->quoteString(''),
|
||||
'cat_unique_path' => $db->quoteString(''),
|
||||
'cat_weight' => intval($weight),
|
||||
'cat_options' => $db->quoteString(pico_common_serialize([])),
|
||||
];
|
||||
$set = [];
|
||||
foreach ($vars as $key => $value) {
|
||||
$set[] = sprintf('`%s`=%s', $key, $value);
|
||||
}
|
||||
$sql = sprintf('INSERT INTO `%s` SET %s', $db->prefix($this->mDirname.'_categories'), implode(', ', $set));
|
||||
if (!$db->queryF($sql)) {
|
||||
var_dump($db);
|
||||
die(_MD_PICO_ERR_DUPLICATEDVPATH.' or '._MD_PICO_ERR_SQL.__LINE__);
|
||||
}
|
||||
pico_sync_cattree($this->mDirname);
|
||||
echo 'category created: '.$title."\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function deleteCategory($category)
|
||||
{
|
||||
global $xoopsModule;
|
||||
$data = $category->getData();
|
||||
if (0 == $data['id']) {
|
||||
echo "cannot delete root(Top) category\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!empty($data['redundants']['subcattree_raw'])) {
|
||||
echo "child categories exist\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
var_dump($data['cat_id'], $data['cat_vpath']);
|
||||
$backupModule = $xoopsModule;
|
||||
$xoopsModule = $this->mModule;
|
||||
pico_delete_category($this->mDirname, $data['cat_id']);
|
||||
$xoopsModule = $backupModule;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkMlang($text, &$hasError)
|
||||
{
|
||||
$hasError = false;
|
||||
if (!isset($GLOBALS['cubeUtilMlang'])) {
|
||||
return $text;
|
||||
}
|
||||
$errtags = [];
|
||||
$hls = '<span style="font-weight:bold; color:red">';
|
||||
$hle = '</span>';
|
||||
foreach ($GLOBALS['cubeUtilMlang']->mLanguages as $lang) {
|
||||
$tags = '['.$lang.']';
|
||||
$tage = '[/'.$lang.']';
|
||||
$pos = 0;
|
||||
$is_error = false;
|
||||
while ($pos < strlen($text)) {
|
||||
$poss = strpos($text, $tags, $pos);
|
||||
$pose = strpos($text, $tage, $pos);
|
||||
if (false === $poss && false === $pose) {
|
||||
// finished
|
||||
break;
|
||||
} elseif (false === $poss && false !== $pose) {
|
||||
// end tag only found.
|
||||
$text = substr($text, 0, $pose).$hls.$tage.$hle.substr($text, $pose + strlen($tage));
|
||||
$is_error = true;
|
||||
break;
|
||||
} elseif (false !== $poss && false === $pose) {
|
||||
// end tag not found.
|
||||
$text = substr($text, 0, $poss).$hls.$tags.$hle.substr($text, $poss + strlen($tags));
|
||||
$is_error = true;
|
||||
break;
|
||||
}
|
||||
if ($poss > $pose) {
|
||||
// end tag found before start tag.
|
||||
$text = substr($text, 0, $pose).$hls.$tage.$hle.substr($text, $pose + strlen($tage));
|
||||
$is_error = true;
|
||||
break;
|
||||
}
|
||||
$poss2 = strpos($text, $tags, $poss + strlen($tags));
|
||||
if (false === $poss2) {
|
||||
// finished
|
||||
break;
|
||||
}
|
||||
if ($pose > $poss2) {
|
||||
// next start tag found before end tag
|
||||
$text = substr($text, 0, $poss2).$hls.$tags.$hle.substr($text, $poss2 + strlen($tags));
|
||||
$is_error = true;
|
||||
break;
|
||||
}
|
||||
$pos = $pose + strlen($tage);
|
||||
}
|
||||
if ($is_error) {
|
||||
$errtags[] = $lang;
|
||||
$text = $error.$text;
|
||||
$text = str_replace([$tags, $tage], ['['.$lang.']', '[/'.$lang.']'], $text);
|
||||
}
|
||||
}
|
||||
if (!empty($errtags)) {
|
||||
$text = '<div class="error">'.$hls.'Unmatched ml tag(s) found: '.implode(', ', $errtags).$hle.'</div>'.$text;
|
||||
$hasError = true;
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
$SITENAME = 'Invertebrate Brain Platform';
|
||||
$HOSTNAME = 'invbrain';
|
||||
$FQDN = $HOSTNAME.'.neuroinf.jp';
|
||||
$URL = 'https://'.$FQDN;
|
||||
$XOOPS_URL = $URL;
|
||||
$JSONS = glob(__DIR__.'/../../static-contents/modules/newdb*/index.json');
|
||||
$items = [];
|
||||
foreach ($JSONS as $JSON) {
|
||||
$data = json_decode(file_get_contents($JSON), true);
|
||||
$mydirname = basename(dirname($JSON));
|
||||
foreach ($data['data'] as $item) {
|
||||
$items[] = [
|
||||
'title' => mlang($item['label']),
|
||||
'url' => $XOOPS_URL.'/modules/'.$mydirname.'/detail.php?id='.$item['id'],
|
||||
'date' => $item['reg_date'],
|
||||
'category' => $mydirname,
|
||||
];
|
||||
}
|
||||
usort($items, function ($a, $b) { return $a['date'] === $b['date'] ? 0 : ($a['date'] > $b['date'] ? -1 : 1);});
|
||||
$items = array_slice($items, 0, 10);
|
||||
}
|
||||
|
||||
function mlang($text) {
|
||||
$text = preg_replace('/\[ja\].*\[\/ja\]/Us', '', $text);
|
||||
$text = preg_replace('/\[\/?en\]/', '', $text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
//var_dump($items);
|
||||
|
||||
$xml = <<< HEAD
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<atom:link href="${XOOPS_URL}/rss.xml" rel="self" type="application/rss+xml" />
|
||||
<title>${SITENAME}</title>
|
||||
<link>${URL}</link>
|
||||
<description>${SITENAME}</description>
|
||||
<language>en</language>
|
||||
|
||||
HEAD;
|
||||
|
||||
foreach($items as $item) {
|
||||
$title = htmlspecialchars($item['title'], ENT_QUOTES);
|
||||
$url = htmlspecialchars($item['url'], ENT_QUOTES);
|
||||
$pubDate = gmdate(DATE_RSS, $item['date']);
|
||||
$category = htmlspecialchars($item['category'], ENT_QUOTES);
|
||||
$xml .= <<< ITEM
|
||||
<item>
|
||||
<title>$title</title>
|
||||
<link>$url</link>
|
||||
<pubDate>$pubDate</pubDate>
|
||||
<guid>$url</guid>
|
||||
<description></description>
|
||||
<category>$category</category>
|
||||
</item>
|
||||
|
||||
ITEM;
|
||||
}
|
||||
|
||||
$xml .= <<< FOOTER
|
||||
</channel>
|
||||
</rss>
|
||||
FOOTER;
|
||||
|
||||
file_put_contents(__DIR__.'/rss.xml', $xml);
|
||||
@@ -1,465 +0,0 @@
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=6045,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=25
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=909,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=27
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=1648,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=28
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=913,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=29
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=1623,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=30
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=1620,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=32
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=896,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=33
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=2434,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=34
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=2441,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=35
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=5307,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=37
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?id=943,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=38
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=403,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=39
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=398,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=40
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=350,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=41
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=366,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=43
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=370,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=44
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=361,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=45
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?id=4449054b,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=46
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=6110,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=47
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?id=7252867b,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=48
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=6105,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=49
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=2861,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=50
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?id=16625198b,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=51
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=6422,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=52
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?id=19281836b,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=53
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?id=7941380b,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=54
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=6448,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=55
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=6106,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=56
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1027,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=57
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1030,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=58
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1031,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=59
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1033,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=61
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1034,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=62
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1035,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=63
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1036,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=64
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1037,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=65
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1038,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=66
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1041,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=67
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1042,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=68
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1043,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=69
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1044,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=70
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1045,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=71
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1046,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=72
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1047,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=73
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1048,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=74
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1051,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=76
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1052,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=77
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1054,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=78
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1056,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=79
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1057,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=80
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1059,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=81
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1060,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=82
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1061,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=83
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1062,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=84
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1064,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=85
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1065,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=86
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1066,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=87
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1067,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=114
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1068,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=116
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1069,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=117
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1071,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=119
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1073,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=121
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1076,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=124
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1077,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=125
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1078,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=126
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1079,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=127
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=296,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=128
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=5586,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=129
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=297,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=130
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1080,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=131
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1081,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=132
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1082,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=133
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1084,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=134
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1085,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=135
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1086,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=136
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1087,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=137
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1088,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=138
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1089,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=139
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1098,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=140
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1099,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=141
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1100,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=142
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1101,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=143
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1102,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=144
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1103,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=145
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1105,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=146
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1106,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=147
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1107,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=148
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1108,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=149
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1109,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=150
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1110,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=151
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1111,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=152
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1112,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=153
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1113,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=154
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1114,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=155
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1115,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=156
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1119,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=159
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1120,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=160
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1121,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=161
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1122,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=162
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1123,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=163
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1124,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=164
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1125,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=165
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1127,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=167
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1128,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=168
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1129,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=169
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1130,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=170
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1131,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=171
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1132,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=172
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1134,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=174
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1135,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=175
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1136,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=176
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1137,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=177
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1138,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=178
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1139,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=179
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1140,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=180
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1141,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=181
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1142,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=182
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1143,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=183
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1144,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=184
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1146,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=186
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1147,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=187
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1148,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=188
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1149,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=189
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1150,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=190
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1152,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=192
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1153,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=193
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1154,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=194
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1156,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=195
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1157,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=196
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1159,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=198
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1160,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=199
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1161,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=200
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1162,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=201
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1163,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=202
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1164,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=203
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1165,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=204
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1166,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=205
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1167,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=206
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1168,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=207
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1169,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=208
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1170,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=209
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1171,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=210
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1173,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=212
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1174,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=213
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1175,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=214
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1176,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=215
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1177,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=216
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1178,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=217
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1179,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=218
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1180,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=219
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1181,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=220
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1182,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=221
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1183,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=222
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1184,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=223
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1185,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=224
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1186,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=225
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1187,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=226
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1188,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=227
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1189,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=228
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1190,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=229
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1191,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=230
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1192,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=231
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1193,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=232
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?id=15929656d,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=233
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=376,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=234
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=298,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=235
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=1629,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=236
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=1647,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=237
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1209,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=239
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1210,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=240
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1211,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=241
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1212,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=242
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1213,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=243
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1214,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=244
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1215,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=245
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1216,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=246
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1218,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=248
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1219,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=249
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1220,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=250
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1222,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=251
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1223,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=252
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1224,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=253
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1225,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=254
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1226,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=255
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1227,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=256
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1228,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=257
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1230,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=258
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1231,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=259
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1232,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=260
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1233,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=261
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1234,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=262
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1235,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=263
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1236,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=264
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1237,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=265
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1238,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=266
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1239,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=267
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1240,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=268
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1241,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=269
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1242,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=270
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1243,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=271
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1244,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=272
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1245,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=273
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1246,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=274
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1247,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=275
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1248,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=276
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1251,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=279
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1252,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=280
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1255,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=281
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1256,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=282
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1257,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=283
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1258,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=284
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1262,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=286
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1263,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=287
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1264,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=288
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1265,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=289
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1266,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=290
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1268,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=292
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1269,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=293
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1270,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=294
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1271,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=295
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1272,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=296
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1273,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=297
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1278,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=302
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1279,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=303
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1280,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=304
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1281,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=305
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1282,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=306
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1283,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=307
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1284,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=308
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1285,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=309
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1286,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=310
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1287,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=311
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1288,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=312
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1289,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=313
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1291,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=315
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1292,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=316
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1293,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=317
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1294,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=318
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1295,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=319
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1296,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=320
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1298,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=322
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1299,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=323
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1300,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=324
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1302,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=326
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1306,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=330
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1312,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=336
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1313,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=337
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1314,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=338
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1315,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=339
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=162,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=341
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=5936,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=342
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/558/weaverWearne06.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=360
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?id=4449054c,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=367
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1039,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=416
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1040,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=417
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=7051,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=444
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1427,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=485
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1425,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=486
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1424,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=487
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1423,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=488
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1422,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=489
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1421,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=490
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1420,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=491
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1419,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=492
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1418,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=493
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1417,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=494
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1416,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=496
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1415,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=497
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1362,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=498
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1414,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=499
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1363,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=500
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1413,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=501
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1411,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=502
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1409,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=503
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1365,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=504
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1369,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=505
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1408,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=506
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1406,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=507
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1362,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=511
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1364,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=514
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1367,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=515
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1366,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=516
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1405,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=517
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1364,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=518
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1401,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=519
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1415,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=520
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1368,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=521
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1399,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=523
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1370,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=524
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1398,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=525
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1371,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=526
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1397,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=527
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1396,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=528
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1372,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=529
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1373,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=530
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1374,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=531
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1375,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=532
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1376,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=533
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1380,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=534
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1395,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=535
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1394,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=536
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1393,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=537
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1392,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=538
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1391,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=539
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1390,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=540
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1389,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=541
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1387,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=542
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1386,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=543
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1385,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=544
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1384,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=545
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1382,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=546
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1381,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=547
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1362,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=550
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1363,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=551
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1364,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=552
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1365,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=553
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1366,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=554
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1367,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=555
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1368,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=556
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1369,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=557
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1370,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=558
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1371,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=559
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1372,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=560
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1374,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=562
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1375,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=563
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1376,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=564
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1379,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=565
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1380,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=566
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1381,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=567
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1382,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=568
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1384,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=569
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1385,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=571
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1386,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=572
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1387,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=573
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1045,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=574
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1389,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=575
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1390,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=576
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1391,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=577
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1392,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=578
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1393,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=579
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1394,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=580
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1395,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=581
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1396,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=582
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1397,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=583
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1398,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=584
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1399,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=585
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1400,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=586
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1401,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=587
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1402,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=588
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1405,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=589
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1409,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=590
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1409,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=591
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1411,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=592
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1413,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=593
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1414,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=594
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1415,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=595
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1416,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=596
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1418,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=598
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1419,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=599
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1420,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=600
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1421,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=601
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1422,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=602
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1423,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=603
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1424,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=604
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1425,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=605
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1427,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=606
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1428,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=607
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1429,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=608
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1430,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=609
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1431,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=610
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1432,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=611
|
||||
visiome,https://visiome.neuroinf.jp/modules/xoonips/detail.php?item_id=7252,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=612
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1434,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=613
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1435,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=614
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1450,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=615
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1452,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=616
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1453,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=617
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1454,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=618
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1455,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=619
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1458,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=620
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1460,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=621
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1461,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=622
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1462,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=623
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1463,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=624
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1466,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=625
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1465,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=626
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1467,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=627
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1470,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=628
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1471,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=629
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1472,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=630
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1473,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=631
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1474,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=632
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1476,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=633
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1477,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=634
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1479,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=635
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1481,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=636
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1482,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=637
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1483,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=638
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1486,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=639
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1487,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=640
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1488,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=641
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1489,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=642
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1490,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=643
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1493,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=644
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1496,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=645
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1497,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=646
|
||||
cerebellum,https://cerebellum.neuroinf.jp/modules/xoonips/detail.php?item_id=1498,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=647
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/533/SundtEtAl2015.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=656
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/536/baker_etal_JCNS_2010.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=657
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/538/CS56PyModelDB.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=659
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/539/TCconvergenceModel.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=660
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/540/BahlEtAl2012.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=661
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/541/ACh_ModelDB.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=662
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/543/OLMmodel_r3.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=664
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/548/GentilettiEtAl2016.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=668
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/556/FUS_model.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=677
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/560/ShepherdBrayton1979.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=680
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/561/gc-1.1_r2.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=681
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/564/VladimirovTuTraub2012.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=683
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/565/xiaoshenli.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=684
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/566/kv72-R213QW-mutations_r2.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=685
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/567/Ih_current.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=686
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/569/Schizophr.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=687
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/570/SaudargieneEtAl2015.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=688
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/571/bpap.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=689
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/572/magical7.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=691
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/575/HyunEtAl2015.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=696
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/576/MasurkarChen2011_r1.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=697
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/577/Nakano_FICN_model_r1.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=698
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/582/AshhadNarayanan2013.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=699
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/583/DiFrancescoNoble1985.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=700
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/584/DGC.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=701
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/586/DG_BC.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=703
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/587/DRG_Devor.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=704
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/588/Kv72_ModelDB.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=705
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/589/SousaEtAl2014.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=706
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/590/MiglioreEtAl2015.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=707
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/592/Chloride_Model.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=708
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/593/MiglioreEJN2016_r2.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=709
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/595/Moore2015.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=711
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/597/FFI_CA1_r3.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=713
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/598/stadler2014_layerV_r1.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=714
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/600/2VN.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=716
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/602/Branch_Point_Tapering.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=718
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/604/MenonEtAl2009_r1.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=720
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/606/CA3Atrophy.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=722
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/607/Gorin_et_al_2016.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=723
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/608/Poleg-PolskyDiamond2011.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=724
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/613/Demo.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=728
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/614/ShortEtAl2016.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=729
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/618/Casaleggio2014.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=733
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/623/MiglioreMcTavish2013.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=736
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/624/KimEtAl2017.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=737
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/626/fig1b.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=738
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/627/cortex_r1.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=739
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/629/oltedal.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=741
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/632/ka_rgc.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=744
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/634/na_rgc.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=745
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/636/V1_PFC_ModelDB.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=747
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/640/moore83.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=751
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/642/SpaceClampDemo.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=753
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/644/anderson.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=755
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/645/VNO.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=756
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/645/VNO.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=757
|
||||
cns,https://cns.neuroinf.jp/modules/fmanager/index.php/view/647/Watanabe-et-al_2017_r1.zip,http://sim.neuroinf.jp/modules/xoonips/detail.php?item_id=758
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
@@ -10,14 +10,15 @@
|
||||
content="The Invertebrate Brain Platform offers you direct access to a growing database of information on nervous systems and behaviour of various species of invertebrates and a large body of ancillary material to promote the use of invertebrate systems in research and edcuation and facilitate information transfer to engineers that are looking for mechanisms that may be useful to solve a wide range of technological problems. The database is linked to explanations of the contents to allow users to familiarise themselves with the data and the context in which they were obtained. The platform has four entrance points taylored to different target user groups. The first entrance point is designed for users that are interested in using invertebrates for research purposes, in particular in the field of neuroscience, to assist them in intitiating research projects. A second entrance point is available for those that are concerned with implementations of design principles of invertebrate nervous systems and behaviour in industrial applications. The third portal is destined for providing quick access for instructors that intend to use invertebrates for educational purposes and the remaining entrance point facilitates obtaining general comparative information on sensory and central nervous systems and behaviour of invertebrates."
|
||||
/>
|
||||
<meta name="robots" content="index,follow" />
|
||||
<meta name="keywords" content="neuroinformatics, xoonips, database, dynamic brain platform, dynamic principles of brain functions" />
|
||||
<meta name="keywords" content="neuroinformatics, xoonips, database, invertebrate brain platform, invertebrate, insect" />
|
||||
<meta name="author" content="Neuroinformatics Unit, RIKEN Center for Brain Science" />
|
||||
<meta name="copyright" content="Copyright © 2018" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>Invertebrate Brain Platform</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+1799
File diff suppressed because it is too large
Load Diff
+36
-58
@@ -1,60 +1,38 @@
|
||||
{
|
||||
"name": "invbrain",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@types/async-lock": "^1.1.2",
|
||||
"@types/jest": "26.0.23",
|
||||
"@types/lokijs": "^1.5.4",
|
||||
"@types/node": "15.6.1",
|
||||
"@types/react": "17.0.8",
|
||||
"@types/react-dom": "17.0.5",
|
||||
"@types/react-helmet": "^6.1.1",
|
||||
"@types/react-html-parser": "^2.0.1",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"@types/react-router-hash-link": "^2.4.0",
|
||||
"async-lock": "^1.3.0",
|
||||
"axios": "^0.21.1",
|
||||
"lokijs": "^1.5.12",
|
||||
"moment": "^2.29.1",
|
||||
"rc-tree": "^4.1.5",
|
||||
"react": "^17.0.2",
|
||||
"react-app-polyfill": "^2.0.0",
|
||||
"react-cookie": "^4.0.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-ga": "^3.3.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-html-parser": "^2.0.2",
|
||||
"react-image-lightbox": "^5.1.1",
|
||||
"react-overlays": "^5.0.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-router-hash-link": "^2.4.3",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-spinner-material": "^1.3.1",
|
||||
"typescript": "4.3.2",
|
||||
"xregexp": "^5.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all",
|
||||
"ie 11"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version",
|
||||
"ie 11"
|
||||
]
|
||||
}
|
||||
"name": "invbrain",
|
||||
"version": "2.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "biome lint",
|
||||
"format": "biome format --write",
|
||||
"check": "biome check --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dr.pogodin/react-helmet": "^3.2.3",
|
||||
"@orrisroot/react-html-parser": "^3.0.1",
|
||||
"date-fns": "^4.4.0",
|
||||
"ky": "^2.1.0",
|
||||
"lokijs": "^1.5.12",
|
||||
"react": "^19.3.0",
|
||||
"react-cookie": "^8.1.2",
|
||||
"react-dom": "^19.3.0",
|
||||
"react-ga4": "^3.0.1",
|
||||
"react-router": "^8.3.1",
|
||||
"sanitize.css": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.5.13",
|
||||
"@types/lokijs": "^1.5.14",
|
||||
"@types/node": "^24.13.4",
|
||||
"@types/react": "^19.3.0",
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"@vitejs/plugin-react": "^6.1.1",
|
||||
"sirv": "^3.0.2",
|
||||
"typescript": "~7.0.2",
|
||||
"vite": "^8.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
+13
-13
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"short_name": "Invertebrate Brain Platform",
|
||||
"name": "Invertebrate Brain Platform",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "32x32 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
"short_name": "Invertebrate Brain Platform",
|
||||
"name": "Invertebrate Brain Platform",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "32x32 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
@import url("./common/assets/xoops.css");
|
||||
@import url("./custom/assets/style.css");
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
+8
-8
@@ -1,17 +1,17 @@
|
||||
import React from 'react';
|
||||
import { HelmetProvider } from '@dr.pogodin/react-helmet';
|
||||
import { CookiesProvider } from 'react-cookie';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import './App.css';
|
||||
import { BrowserRouter } from 'react-router';
|
||||
import AppRoot from './common/AppRoot';
|
||||
import './App.css';
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
const App = () => (
|
||||
<HelmetProvider>
|
||||
<CookiesProvider>
|
||||
<BrowserRouter>
|
||||
<AppRoot />
|
||||
</BrowserRouter >
|
||||
</BrowserRouter>
|
||||
</CookiesProvider>
|
||||
);
|
||||
}
|
||||
</HelmetProvider>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
||||
+33
-53
@@ -1,67 +1,47 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ReactCookieProps, withCookies } from 'react-cookie';
|
||||
import ReactGA from 'react-ga';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useCookies } from 'react-cookie';
|
||||
import ReactGA from 'react-ga4';
|
||||
import { useLocation } from 'react-router';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import Page from '../custom/Page';
|
||||
|
||||
interface Props extends RouteComponentProps, ReactCookieProps { }
|
||||
interface State {
|
||||
lang: MultiLang;
|
||||
const isAnalyticsEnabled = import.meta.env.PROD && Config.GOOGLE_ANALYTICS_TRACKING_ID !== '';
|
||||
|
||||
if (isAnalyticsEnabled) {
|
||||
// Page views are sent from the location effect below, the first one included;
|
||||
// gtag's own page_view on config would count the landing page twice.
|
||||
ReactGA.initialize(Config.GOOGLE_ANALYTICS_TRACKING_ID, { gtagOptions: { send_page_view: false } });
|
||||
}
|
||||
|
||||
class AppRoot extends Component<Props, State> {
|
||||
const toMultiLang = (value: unknown): MultiLang | null => (value === 'en' || value === 'ja' ? value : null);
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { lang: 'en' };
|
||||
if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') {
|
||||
ReactGA.initialize(Config.GOOGLE_ANALYTICS_TRACKING_ID);
|
||||
}
|
||||
}
|
||||
const AppRoot = () => {
|
||||
const location = useLocation();
|
||||
const [cookies, setCookie] = useCookies(['ml_lang']);
|
||||
const previousLocation = useRef<string | null>(null);
|
||||
|
||||
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;
|
||||
}
|
||||
const paramLang = toMultiLang(new URLSearchParams(location.search).get('ml_lang'));
|
||||
const cookieLang = toMultiLang(cookies.ml_lang);
|
||||
const lang: MultiLang = paramLang ?? cookieLang ?? 'en';
|
||||
|
||||
componentDidMount() {
|
||||
const { pathname } = this.props.location;
|
||||
if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') {
|
||||
ReactGA.set({ page: pathname });
|
||||
ReactGA.pageview(pathname);
|
||||
useEffect(() => {
|
||||
if (cookies.ml_lang !== lang) {
|
||||
setCookie('ml_lang', lang, { path: '/' });
|
||||
}
|
||||
}
|
||||
}, [cookies.ml_lang, lang, setCookie]);
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const { pathname, search } = this.props.location;
|
||||
if (Config.GOOGLE_ANALYTICS_TRACKING_ID !== '') {
|
||||
ReactGA.set({ page: pathname });
|
||||
ReactGA.pageview(pathname);
|
||||
useEffect(() => {
|
||||
if (isAnalyticsEnabled) {
|
||||
ReactGA.send({ hitType: 'pageview', page: location.pathname + location.search });
|
||||
}
|
||||
if (pathname !== prevProps.location.pathname || search !== prevProps.location.search) {
|
||||
const current = location.pathname + location.search;
|
||||
if (previousLocation.current !== null && previousLocation.current !== current) {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
previousLocation.current = current;
|
||||
}, [location.pathname, location.search]);
|
||||
|
||||
render() {
|
||||
const { lang } = this.state;
|
||||
return <Page lang={lang} />;
|
||||
}
|
||||
}
|
||||
return <Page lang={lang} />;
|
||||
};
|
||||
|
||||
export default withCookies(withRouter(AppRoot));
|
||||
export default AppRoot;
|
||||
|
||||
@@ -1,40 +1,30 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect, RouteComponentProps } from 'react-router';
|
||||
import { MultiLang } from '../config';
|
||||
import { Navigate, useLocation } from 'react-router';
|
||||
import type { MultiLang } from '../config';
|
||||
import PageNotFound from './lib/PageNotFound';
|
||||
|
||||
interface Params {
|
||||
module: string;
|
||||
pathname: string;
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<Params> {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class XoopsPathRedirect extends Component<Props> {
|
||||
|
||||
getRedirectUrl() {
|
||||
const { pathname } = this.props.location;
|
||||
switch (pathname || '') {
|
||||
case '/index.php': {
|
||||
return '/';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
const getRedirectUrl = (pathname: string): string => {
|
||||
switch (pathname || '') {
|
||||
case '/index.php':
|
||||
return '/';
|
||||
}
|
||||
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} />;
|
||||
const XoopsPathRedirect = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const location = useLocation();
|
||||
if (location.pathname === '/') {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const url = getRedirectUrl(location.pathname);
|
||||
if (url === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <Navigate to={url} replace />;
|
||||
};
|
||||
|
||||
export default XoopsPathRedirect;
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import React from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import { MultiLang } from '../../config';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const SelectLang = (props: Props) => {
|
||||
const { lang, history } = props;
|
||||
const langs: MultiLang[] = ['en', 'ja'];
|
||||
const titles = {
|
||||
en: 'English',
|
||||
ja: 'Japanese',
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<select value={lang} onChange={(e) => {
|
||||
const xlang = e.target.value;
|
||||
const params = new URLSearchParams(history.location.search);
|
||||
params.set('ml_lang', xlang);
|
||||
const url = history.location.pathname + '?' + params.toString();
|
||||
history.push(url);
|
||||
}}>
|
||||
{langs.map((xlang) => {
|
||||
return <option key={xlang} value={xlang}>{titles[xlang]}</option>;
|
||||
})}
|
||||
</select>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withRouter(SelectLang);
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { ComponentProps, MouseEvent } from 'react';
|
||||
import { Link, type To } from 'react-router';
|
||||
|
||||
type LinkProps = ComponentProps<typeof Link>;
|
||||
|
||||
const RETRY_INTERVAL_MS = 16;
|
||||
const RETRY_COUNT = 20;
|
||||
|
||||
const hashOf = (to: To): string => {
|
||||
if (typeof to === 'string') {
|
||||
const idx = to.indexOf('#');
|
||||
return idx === -1 ? '' : to.slice(idx + 1);
|
||||
}
|
||||
return (to.hash ?? '').replace(/^#/, '');
|
||||
};
|
||||
|
||||
const scrollToHash = (hash: string, remaining = RETRY_COUNT) => {
|
||||
if (hash === '') {
|
||||
return;
|
||||
}
|
||||
const id = decodeURIComponent(hash);
|
||||
const target = document.getElementById(id) ?? document.querySelector(`[name="${CSS.escape(id)}"]`);
|
||||
if (target !== null) {
|
||||
target.scrollIntoView();
|
||||
return;
|
||||
}
|
||||
// The target may not be in the DOM yet (pico pages fetch their content).
|
||||
if (remaining > 0) {
|
||||
setTimeout(() => {
|
||||
scrollToHash(hash, remaining - 1);
|
||||
}, RETRY_INTERVAL_MS);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A `<Link>` that also scrolls to the element named by the URL fragment.
|
||||
* Replaces `react-router-hash-link`, which is unmaintained and still targets react-router v5.
|
||||
*/
|
||||
const HashLink = ({ onClick, to, ...rest }: LinkProps) => {
|
||||
const handleClick = (e: MouseEvent<HTMLAnchorElement>) => {
|
||||
onClick?.(e);
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
// Let react-router commit the navigation before looking for the target.
|
||||
setTimeout(() => {
|
||||
scrollToHash(hashOf(to));
|
||||
}, 0);
|
||||
};
|
||||
return <Link {...rest} to={to} onClick={handleClick} />;
|
||||
};
|
||||
|
||||
export default HashLink;
|
||||
@@ -1,11 +1,9 @@
|
||||
import React from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Link, useLocation } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import mlangEnglish from '../assets/images/mlang_english.gif';
|
||||
import mlangJapanese from '../assets/images/mlang_japanese.gif';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
className?: string;
|
||||
image?: string;
|
||||
@@ -18,17 +16,17 @@ const langResources = {
|
||||
|
||||
const LangFlag = (props: Props) => {
|
||||
const { lang, className, image } = props;
|
||||
const params = new URLSearchParams(props.location.search);
|
||||
const location = useLocation();
|
||||
const params = new URLSearchParams(location.search);
|
||||
const flagLang = lang === 'en' ? 'ja' : 'en';
|
||||
params.set('ml_lang', flagLang);
|
||||
const url = props.location.pathname + '?' + params.toString();
|
||||
const url = `${location.pathname}?${params.toString()}`;
|
||||
const imageSrc = image || langResources[flagLang].image;
|
||||
return (
|
||||
<Link className={className} to={url}>
|
||||
<img src={imageSrc} alt={langResources[flagLang].title} title={langResources[flagLang].title} />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export default withRouter(LangFlag);
|
||||
export default LangFlag;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
margin: 100px 0;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 8px solid #eeeeee;
|
||||
border-top-color: #cccccc;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.spinner {
|
||||
animation-duration: 4s;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
import React from 'react';
|
||||
import Spinner from 'react-spinner-material';
|
||||
import styles from './Loading.module.css';
|
||||
|
||||
const Loading = () => {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignContent: 'center', margin: '100px 0' }}>
|
||||
<Spinner radius={60} color={'#cccccc'} stroke={8} visible={true} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const Loading = () => (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.spinner} role="status" aria-label="Loading" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Loading;
|
||||
export default Loading;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../../config';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
|
||||
interface Props {
|
||||
@@ -8,8 +7,9 @@ interface Props {
|
||||
|
||||
const NoticeSiteHasBeenArchived = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const notice = '[en]This site has been archived since FY2019 and is no longer updated.[/en][ja]このサイトは、2019年度よりアーカイブサイトとして運用されています。[/ja]';
|
||||
const notice =
|
||||
'[en]This site has been archived since FY2019 and is no longer updated.[/en][ja]このサイトは、2019年度よりアーカイブサイトとして運用されています。[/ja]';
|
||||
return <p style={{ color: 'red' }}>{Functions.mlang(notice, lang)}</p>;
|
||||
}
|
||||
};
|
||||
|
||||
export default NoticeSiteHasBeenArchived;
|
||||
export default NoticeSiteHasBeenArchived;
|
||||
|
||||
@@ -1,54 +1,44 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
class PageNotFound extends Component<Props> {
|
||||
const TOP_URL = '/';
|
||||
|
||||
private url = '/';
|
||||
private timer: NodeJS.Timer | null = null;
|
||||
const PageNotFound = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
goToTopPage() {
|
||||
if (this.props.location.pathname !== '/') {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
this.timer = setTimeout(() => {
|
||||
this.timer = null;
|
||||
this.props.history.push(this.url);
|
||||
}, 5000);
|
||||
useEffect(() => {
|
||||
if (location.pathname === TOP_URL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const timer = setTimeout(() => {
|
||||
void navigate(TOP_URL);
|
||||
}, 5000);
|
||||
return () => clearTimeout(timer);
|
||||
}, [location.pathname, navigate]);
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>Page Not Found - {Functions.siteTitle(lang)}</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 go to the <a href={TOP_URL}>top page</a>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
this.goToTopPage();
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>Page Not Found - {Functions.siteTitle(lang)}</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);
|
||||
export default PageNotFound;
|
||||
|
||||
+112
-70
@@ -1,8 +1,14 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import ReactHtmlParser, { convertNodeToElement } from 'react-html-parser';
|
||||
import { HashLink } from 'react-router-hash-link';
|
||||
import { MultiLang } from '../../config';
|
||||
import ReactHtmlParser, {
|
||||
convertNodeToElement,
|
||||
type DomElement,
|
||||
type DomNode,
|
||||
type Transform,
|
||||
} from '@orrisroot/react-html-parser';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { useLocation } from 'react-router';
|
||||
import Config, { type MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import HashLink from './HashLink';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -16,102 +22,138 @@ interface Props {
|
||||
|
||||
const preConvertXCode = (text: string, doxcode: boolean): string => {
|
||||
if (doxcode) {
|
||||
return text.replace(/\[code\](.*)\[\/code\]/sg, (m0, m1) => {
|
||||
return '[code]' + Functions.base64Encode(m1) + '[/code]';
|
||||
return text.replace(/\[code\](.*)\[\/code\]/gs, (_m0, m1) => {
|
||||
return `[code]${Functions.base64Encode(m1)}[/code]`;
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
const postConvertXCode = (text: string, doxcode: boolean, doimage: boolean): string => {
|
||||
if (doxcode) {
|
||||
return text.replace(/\[code\](.*)\[\/code\]/sg, (m0, m1) => {
|
||||
return text.replace(/\[code\](.*)\[\/code\]/gs, (_m0, m1) => {
|
||||
const text = convertXCode(Functions.htmlspecialchars(Functions.base64Decode(m1)), doimage);
|
||||
return '<div class="xoopsCode"><pre><code>' + text + '</code></pre></div>';
|
||||
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;/?:@&=+$,%#]+[a-zA-Z0-9=]))/g, (...matches) => {
|
||||
return matches[1] + '<a href="' + matches[2] + '" target="_blank" rel="external noopener noreferrer">' + matches[2] + '</a>';
|
||||
});
|
||||
text = text.replace(/(^|[^\]_a-zA-Z0-9-="'/:.]+)([a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+)/g, (...matches) => {
|
||||
return matches[1] + '<a href="mailto:' + matches[2] + '">' + matches[2] + '</a>';
|
||||
});
|
||||
text = text.replace(
|
||||
/(^|[^\]_a-zA-Z0-9-="'/]+)((?:https?|ftp)(?::\/\/[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+[a-zA-Z0-9=]))/g,
|
||||
(...matches) => {
|
||||
return (
|
||||
matches[1] +
|
||||
'<a href="' +
|
||||
matches[2] +
|
||||
'" target="_blank" rel="external noopener noreferrer">' +
|
||||
matches[2] +
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
);
|
||||
text = text.replace(
|
||||
/(^|[^\]_a-zA-Z0-9-="'/:.]+)([a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+)/g,
|
||||
(...matches) => {
|
||||
return `${matches[1]}<a href="mailto:${matches[2]}">${matches[2]}</a>`;
|
||||
}
|
||||
);
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
const convertXCode = (text: string, doimage: boolean): string => {
|
||||
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 />');
|
||||
}
|
||||
};
|
||||
|
||||
interface TransformParsedNode {
|
||||
type: string;
|
||||
next: object | null;
|
||||
prev: object | null;
|
||||
parent: object | null;
|
||||
name: string;
|
||||
attribs: any;
|
||||
children: object[];
|
||||
data: string;
|
||||
}
|
||||
|
||||
const cssConvert = (text: string): object => {
|
||||
const ret: any = {};
|
||||
const cssConvert = (text: string): CSSProperties => {
|
||||
const ret: Record<string, string> = {};
|
||||
text.split(';').forEach((line) => {
|
||||
const line_ = line.trim();
|
||||
if (line.length === 0) {
|
||||
if (line_.length === 0) {
|
||||
return;
|
||||
}
|
||||
const kv = line_.split(':');
|
||||
const key = Functions.camelCase(kv[0].trim());
|
||||
const value = kv[1].trim();
|
||||
ret[key] = value;
|
||||
})
|
||||
return ret;
|
||||
}
|
||||
|
||||
const transform = (node: object, idx: number): ReactElement | null | void => {
|
||||
const node_ = node as TransformParsedNode;
|
||||
if (node_.type === 'tag' && node_.name === 'a') {
|
||||
const url = (node_.attribs && node_.attribs['href']) || '/';
|
||||
const download = (node_.attribs && node_.attribs['download']) || '';
|
||||
const rel = (node_.attribs && node_.attribs['rel']) || '';
|
||||
const klass = (node_.attribs && node_.attribs['class']) || '';
|
||||
const target = (node_.attribs && node_.attribs['target']) || '';
|
||||
const isFile = download !== '' || /\.(zip|pdf|png|gif|jpg|gz|bz2|xz|mpg|mp3|mp4)$/i.test(url);
|
||||
const isExternal = /^(_blank|_top|_parent)$/.test(target) || /external/.test(rel) || /external/.test(klass) || /^(mailto|https?:?\/\/)/.test(url);
|
||||
if (!isFile && !isExternal) {
|
||||
const style = (node_.attribs && node_.attribs['style']) || '';
|
||||
const title = (node_.attribs && node_.attribs['title']) || null;
|
||||
return <HashLink key={idx} to={url} style={cssConvert(style)} title={title}>
|
||||
{node_.children.map((value: object, index: number) => {
|
||||
return convertNodeToElement(value, index, transform);
|
||||
})}
|
||||
</HashLink>;
|
||||
if (kv.length < 2) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (node_.type === 'tag' && node_.name === 'img') {
|
||||
const src = (node_.attribs && node_.attribs['src']) || '';
|
||||
node_.attribs['src'] = src.replace('`XOOPS_URL`', process.env.PUBLIC_URL);
|
||||
return convertNodeToElement(node_ as object, idx, transform);
|
||||
}
|
||||
}
|
||||
ret[Functions.camelCase(kv[0].trim())] = kv.slice(1).join(':').trim();
|
||||
});
|
||||
return ret as CSSProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stored content links are often written relative to the page that contains
|
||||
* them (`../General/behavior.html`, `#section`, ...), so they have to be
|
||||
* resolved the way a browser resolves an `href`. react-router would instead
|
||||
* resolve them against the route hierarchy.
|
||||
*/
|
||||
const resolveUrl = (href: string, basePath: string): string => {
|
||||
const url = new URL(href, window.location.origin + basePath);
|
||||
return url.pathname + url.search + url.hash;
|
||||
};
|
||||
|
||||
const makeTransform = (basePath: string): Transform => {
|
||||
const transform: Transform = (node: DomNode, idx: number | string) => {
|
||||
if (node.type !== 'tag') {
|
||||
return undefined;
|
||||
}
|
||||
const element = node as DomElement;
|
||||
const attribs = element.attribs ?? {};
|
||||
if (element.name === 'a') {
|
||||
const url = attribs.href || '/';
|
||||
const download = attribs.download || '';
|
||||
const rel = attribs.rel || '';
|
||||
const klass = attribs.class || '';
|
||||
const target = attribs.target || '';
|
||||
// Data files are served by the web server, not by the app.
|
||||
const isFile = download !== '' || /\.(zip|pdf|png|gif|jpg|gz|bz2|xz|mpg|mp3|mp4)$/i.test(url);
|
||||
const isExternal =
|
||||
/^(_blank|_top|_parent)$/.test(target) ||
|
||||
/external/.test(rel) ||
|
||||
/external/.test(klass) ||
|
||||
/^(mailto|https?:?\/\/)/.test(url);
|
||||
if (isFile || isExternal) {
|
||||
return undefined;
|
||||
}
|
||||
return (
|
||||
<HashLink
|
||||
key={idx}
|
||||
to={resolveUrl(url, basePath)}
|
||||
style={cssConvert(attribs.style || '')}
|
||||
title={attribs.title}
|
||||
>
|
||||
{element.children.map((child, index) => convertNodeToElement(child, index, transform))}
|
||||
</HashLink>
|
||||
);
|
||||
}
|
||||
if (element.name === 'img' && typeof attribs.src === 'string') {
|
||||
// Rewritten in place; returning undefined lets the parser render it.
|
||||
attribs.src = attribs.src.replace('`XOOPS_URL`', Config.BASE_URL);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
return transform;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders converted XOOPS text. It returns a fragment rather than a wrapper
|
||||
* element because the CosmoDB list templates are table cells placed directly
|
||||
* inside a `<tr>`.
|
||||
*/
|
||||
const XoopsCode = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const { pathname } = useLocation();
|
||||
let text = props.text;
|
||||
const dohtml = !!props.dohtml;
|
||||
const dosmiley = !!props.dosmiley;
|
||||
@@ -134,7 +176,7 @@ const XoopsCode = (props: Props) => {
|
||||
}
|
||||
text = postConvertXCode(text, doxcode, doimage);
|
||||
text = Functions.mlang(text, lang);
|
||||
return <>{ReactHtmlParser(text, { transform })}</>;
|
||||
}
|
||||
return <>{ReactHtmlParser(text, { transform: makeTransform(pathname) })}</>;
|
||||
};
|
||||
|
||||
export default XoopsCode;
|
||||
export default XoopsCode;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
/**
|
||||
* Runs a module's lazy `initialize()` and reports its outcome: `null` while it
|
||||
* is pending, then the boolean it resolved to. The modules fetch their own
|
||||
* index the first time one of their pages is shown, and cache the promise, so
|
||||
* this is cheap to call from every page.
|
||||
*/
|
||||
const useInitialized = (initialize: () => Promise<boolean>): boolean | null => {
|
||||
const [status, setStatus] = useState<boolean | null>(null);
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
initialize().then((result) => {
|
||||
if (active) {
|
||||
setStatus(result);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [initialize]);
|
||||
return status;
|
||||
};
|
||||
|
||||
export default useInitialized;
|
||||
+10
-3
@@ -1,8 +1,14 @@
|
||||
const SITE_TITLE = 'Invertebrate Brain Platform';
|
||||
const SITE_SLOGAN = '';
|
||||
const GOOGLE_ANALYTICS_TRACKING_ID = 'UA-2780881-1';
|
||||
// Replaces the Universal Analytics property (UA-2780881-1), shut down in 2023.
|
||||
const GOOGLE_ANALYTICS_TRACKING_ID = 'G-JF4NM0XJHK';
|
||||
const PICO_MODULES = ['documents', 'htmldocs'];
|
||||
|
||||
// Vite's BASE_URL always carries a trailing slash ('/' when the app is served
|
||||
// from the document root). Strip it so it can be concatenated the same way
|
||||
// CRA's PUBLIC_URL was.
|
||||
const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, '');
|
||||
|
||||
export type MultiLang = 'en' | 'ja';
|
||||
|
||||
const Config = {
|
||||
@@ -10,6 +16,7 @@ const Config = {
|
||||
SITE_SLOGAN,
|
||||
GOOGLE_ANALYTICS_TRACKING_ID,
|
||||
PICO_MODULES,
|
||||
}
|
||||
BASE_URL,
|
||||
};
|
||||
|
||||
export default Config;
|
||||
export default Config;
|
||||
|
||||
+44
-81
@@ -1,101 +1,64 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
import { Navigate, Route, Routes, useLocation } from 'react-router';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import useInitialized from '../common/lib/useInitialized';
|
||||
import type { MultiLang } from '../config';
|
||||
import './assets/style.css';
|
||||
import CosmoDbDetail from './CosmoDbDetail';
|
||||
import CosmoDbList from './CosmoDbList';
|
||||
import CosmoDbContext from './lib/CosmoDbContext';
|
||||
import type CosmoDbContext from './lib/CosmoDbContext';
|
||||
import CosmoDbUtils from './lib/CosmoDbUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
modulepath: string;
|
||||
dirname: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
context: CosmoDbContext | null;
|
||||
interface PageProps {
|
||||
lang: MultiLang;
|
||||
context: CosmoDbContext;
|
||||
}
|
||||
|
||||
class CosmoDb extends Component<Props, State> {
|
||||
const CosmoDbListPage = (props: PageProps) => {
|
||||
const { lang, context } = props;
|
||||
const { search } = useLocation();
|
||||
return <CosmoDbList lang={lang} context={context} params={new URLSearchParams(search)} />;
|
||||
};
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
public constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
context: null,
|
||||
};
|
||||
const CosmoDbDetailPage = (props: PageProps) => {
|
||||
const { lang, context } = props;
|
||||
const { search } = useLocation();
|
||||
const params = new URLSearchParams(search);
|
||||
const idStr = params.get('id') || '';
|
||||
const tabStr = params.get('tab') || '1';
|
||||
if (!/^\d+/.test(idStr) || !/^\d+/.test(tabStr)) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <CosmoDbDetail lang={lang} context={context} id={parseInt(idStr, 10)} tab={parseInt(tabStr, 10)} />;
|
||||
};
|
||||
|
||||
public componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
const CosmoDb = (props: Props) => {
|
||||
const { lang, dirname } = props;
|
||||
const context = CosmoDbUtils.getContext(dirname);
|
||||
const status = useInitialized(context.initialize);
|
||||
if (status === null) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, modulepath } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.modulepath !== modulepath) {
|
||||
this.updatePage();
|
||||
}
|
||||
if (!status) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
public async updatePage() {
|
||||
const { modulepath } = this.props;
|
||||
const context = CosmoDbUtils.getContext(modulepath);
|
||||
const status = await context.initialize();
|
||||
if (this.isActive) {
|
||||
this.setState({ loading: false, context: status ? context : null });
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { lang, modulepath } = this.props;
|
||||
const { loading, context } = this.state;
|
||||
const topUrl = modulepath + '/list.php?id=1';
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (context === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <div className="cosmodb">
|
||||
<Switch>
|
||||
<Route exact path={modulepath} render={(props: RouteComponentProps) => {
|
||||
return <Redirect to={topUrl} />
|
||||
}} />
|
||||
<Route exact path={`${modulepath}/index.php`} render={(props: RouteComponentProps) => {
|
||||
return <Redirect to={topUrl} />
|
||||
}} />
|
||||
<Route path={`${modulepath}/list.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
return <CosmoDbList lang={lang} context={context} params={params} />;
|
||||
}} />
|
||||
<Route path={`${modulepath}/detail.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const idStr = params.get('id') || '';
|
||||
const tabStr = params.get('tab') || '1';
|
||||
if (!/^\d+/.test(idStr) || !/^\d+/.test(tabStr)) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const id = parseInt(idStr);
|
||||
const tab = parseInt(tabStr);
|
||||
return <CosmoDbDetail lang={lang} context={context} id={id} tab={tab} />;
|
||||
}} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
const topUrl = context.getUrl('list.php?id=1');
|
||||
return (
|
||||
<div className="cosmodb">
|
||||
<Routes>
|
||||
<Route index element={<Navigate to={topUrl} replace />} />
|
||||
<Route path="index.php" element={<Navigate to={topUrl} replace />} />
|
||||
<Route path="list.php" element={<CosmoDbListPage lang={lang} context={context} />} />
|
||||
<Route path="detail.php" element={<CosmoDbDetailPage lang={lang} context={context} />} />
|
||||
<Route path="*" element={<PageNotFound lang={lang} />} />
|
||||
</Routes>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CosmoDb;
|
||||
|
||||
@@ -1,81 +1,65 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import XoopsCode from '../common/lib/XoopsCode';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import CosmoDbContext, { CosmoDbBasicData, CosmoDbDetailData } from './lib/CosmoDbContext';
|
||||
import type CosmoDbContext from './lib/CosmoDbContext';
|
||||
import type { CosmoDbDetailData } from './lib/CosmoDbContext';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
context: CosmoDbContext;
|
||||
id: number;
|
||||
tab: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
basic: CosmoDbBasicData | null;
|
||||
interface LoadedDetail {
|
||||
context: CosmoDbContext;
|
||||
id: number;
|
||||
detail: CosmoDbDetailData | null;
|
||||
}
|
||||
|
||||
class CosmoDbDetail extends Component<Props, State> {
|
||||
const CosmoDbDetail = (props: Props) => {
|
||||
const { lang, context, id, tab } = props;
|
||||
const basic = context.getBasic(id);
|
||||
const [loaded, setLoaded] = useState<LoadedDetail | null>(null);
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
public constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
basic: null,
|
||||
detail: null,
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
context.getDetail(id).then((detail) => {
|
||||
if (active) {
|
||||
setLoaded({ context, id, detail });
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}
|
||||
}, [context, id]);
|
||||
|
||||
public componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
if (basic === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, context, id, tab } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.context.getUrl('') !== context.getUrl('') || prevProps.id !== id || prevProps.tab !== tab) {
|
||||
this.updatePage();
|
||||
}
|
||||
// A detail loaded for the previous item must not be shown for this one.
|
||||
if (loaded === null || loaded.context !== context || loaded.id !== id) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
if (loaded.detail === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
public async updatePage() {
|
||||
const { context, id } = this.props;
|
||||
const basic = context.getBasic(id);
|
||||
const detail = basic !== null ? await context.getDetail(id) : null;
|
||||
if (this.isActive) {
|
||||
this.setState({ loading: false, basic, detail });
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { lang, context, tab } = this.props;
|
||||
if (this.state.loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (this.state.basic === null || this.state.detail === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const html = context.renderDetail(this.state.basic, this.state.detail, tab);
|
||||
return <div className="detail">
|
||||
const html = context.renderDetail(basic, loaded.detail, tab);
|
||||
return (
|
||||
<div className="detail">
|
||||
<Helmet>
|
||||
<title>{Functions.mlang(this.state.basic.label, lang)} - {Functions.mlang(context.getModuleName(), lang)} - {Functions.siteTitle(lang)}</title>
|
||||
<title>
|
||||
{Functions.mlang(basic.label, lang)} - {Functions.mlang(context.getModuleName(), lang)} -{' '}
|
||||
{Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<XoopsCode lang={lang} text={html} dohtml={true} />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(CosmoDbDetail);
|
||||
export default CosmoDbDetail;
|
||||
|
||||
+33
-23
@@ -1,46 +1,56 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import CosmoDbContext, { CosmoDbListDataType } from './lib/CosmoDbContext';
|
||||
import type CosmoDbContext from './lib/CosmoDbContext';
|
||||
import { CosmoDbListDataType } from './lib/CosmoDbContext';
|
||||
import CosmoDbListListView from './lib/CosmoDbListListView';
|
||||
import CosmoDbListPageNavi from './lib/CosmoDbListPageNavi';
|
||||
import CosmoDbListThumbnailView from './lib/CosmoDbListThumbnailView';
|
||||
import CosmoDbListViewSwitcher from './lib/CosmoDbListViewSwitcher';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
context: CosmoDbContext;
|
||||
params: URLSearchParams;
|
||||
}
|
||||
|
||||
const CosmoDbList: React.FC<Props> = (props: Props) => {
|
||||
const CosmoDbList = (props: Props) => {
|
||||
const { lang, context, params } = props;
|
||||
const paramId = params.get('id');
|
||||
if (paramId === null || !/^\d+$/.test(paramId)) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const id = parseInt(paramId);
|
||||
let list = context.getList(id);
|
||||
const id = parseInt(paramId, 10);
|
||||
const list = context.getList(id);
|
||||
if (list === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const result = context.search(params);
|
||||
return <div className="list">
|
||||
<Helmet>
|
||||
<title>{Functions.mlang(context.getModuleName(), lang)} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<CosmoDbListViewSwitcher lang={lang} context={context} params={params} />
|
||||
<CosmoDbListPageNavi lang={lang} context={context} params={params} total={result.total} />
|
||||
<hr />
|
||||
{list.type === CosmoDbListDataType.List ?
|
||||
<CosmoDbListListView lang={lang} context={context} list={list} params={params} data={result.result} /> :
|
||||
<CosmoDbListThumbnailView lang={lang} context={context} list={list} params={params} data={result.result} />
|
||||
}
|
||||
<CosmoDbListPageNavi lang={lang} context={context} params={params} total={result.total} />
|
||||
</div>;
|
||||
return (
|
||||
<div className="list">
|
||||
<Helmet>
|
||||
<title>
|
||||
{Functions.mlang(context.getModuleName(), lang)} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<CosmoDbListViewSwitcher lang={lang} context={context} params={params} />
|
||||
<CosmoDbListPageNavi lang={lang} context={context} params={params} total={result.total} />
|
||||
<hr />
|
||||
{list.type === CosmoDbListDataType.List ? (
|
||||
<CosmoDbListListView lang={lang} context={context} list={list} params={params} data={result.result} />
|
||||
) : (
|
||||
<CosmoDbListThumbnailView
|
||||
lang={lang}
|
||||
context={context}
|
||||
list={list}
|
||||
params={params}
|
||||
data={result.result}
|
||||
/>
|
||||
)}
|
||||
<CosmoDbListPageNavi lang={lang} context={context} params={params} total={result.total} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(CosmoDbList);
|
||||
export default CosmoDbList;
|
||||
|
||||
+120
-120
@@ -1,269 +1,269 @@
|
||||
.cosmodb {
|
||||
font-size: 95%;
|
||||
font-size: 95%;
|
||||
}
|
||||
|
||||
.cosmodb th {
|
||||
color: #ffffff;
|
||||
background-color: #802d2d;
|
||||
padding: 2px;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
color: #ffffff;
|
||||
background-color: #802d2d;
|
||||
padding: 2px;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cosmodb th a {
|
||||
color: #ffffff;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.cosmodb th a:hover {
|
||||
color: #ff6600;
|
||||
color: #ff6600;
|
||||
}
|
||||
|
||||
.cosmodb hr {
|
||||
border-width: 1px 0 0 0;
|
||||
border-style: solid;
|
||||
border-color: #dedbde;
|
||||
border-width: 1px 0 0 0;
|
||||
border-style: solid;
|
||||
border-color: #dedbde;
|
||||
}
|
||||
|
||||
.cosmodb figure {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.cosmodb .list .view-switcher {
|
||||
font-size: 95%;
|
||||
text-align: right;
|
||||
font-size: 95%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.cosmodb .list .pagenavi {
|
||||
margin: 5px 0;
|
||||
font-size: 95%;
|
||||
margin: 5px 0;
|
||||
font-size: 95%;
|
||||
}
|
||||
|
||||
.cosmodb .list .pagenavi .pages {
|
||||
margin-left: 10px;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cosmodb .list .list-sortbar {
|
||||
margin: 5px 0;
|
||||
text-align: right;
|
||||
margin: 5px 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.cosmodb .list .list-sortbar select {
|
||||
margin-right: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.cosmodb .list .list-sortbar .formButton {
|
||||
background: white;
|
||||
border: 1px solid;
|
||||
background: white;
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
.cosmodb .list .list-view .list-table {
|
||||
margin: 10px 0;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #dedbde;
|
||||
text-align: left;
|
||||
margin: 10px 0;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #dedbde;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cosmodb .list .list-view .list-table th {
|
||||
padding: 2px;
|
||||
vertical-align: middle;
|
||||
padding: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.cosmodb .list .list-view .list-table td {
|
||||
padding: 3px;
|
||||
border: 1px solid #dedbde;
|
||||
padding: 3px;
|
||||
border: 1px solid #dedbde;
|
||||
}
|
||||
|
||||
.cosmodb .list .thumbnail-view .thumbnail-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.cosmodb .list .thumbnail-view .thumbnail-table td {
|
||||
padding: 0px;
|
||||
border-width: 0px;
|
||||
vertical-align: top;
|
||||
padding: 0px;
|
||||
border-width: 0px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.cosmodb .list .thumbnail-view .thumbnail-table td .list_table th a {
|
||||
color: #ffffff;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.cosmodb .list .thumbnail-view .thumbnail-table td .list_table th a:hover {
|
||||
color: #ff6600;
|
||||
text-decoration: none;
|
||||
color: #ff6600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cosmodb .list .thumbnail-view .thumbnail-table td .list_table {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #dedbde;
|
||||
text-align: left;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #dedbde;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cosmodb .list .thumbnail-view .thumbnail-table td .list_table td {
|
||||
padding: 3px;
|
||||
border: 1px solid #dedbde;
|
||||
padding: 3px;
|
||||
border: 1px solid #dedbde;
|
||||
}
|
||||
|
||||
.cosmodb .list .thumbnail-view .thumbnail-table td .list_table td img {
|
||||
border: 2px solid transparent;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.cosmodb .list .thumbnail-view .thumbnail-table td .list_table td img:hover {
|
||||
border-color: #ff6600;
|
||||
border-color: #ff6600;
|
||||
}
|
||||
|
||||
.cosmodb .detail table td {
|
||||
padding: 3px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.cosmodb .detail .label_header {
|
||||
color: #db4d4d;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
color: #db4d4d;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
.cosmodb .detail .h_under {
|
||||
margin: 10px 0 30px;
|
||||
font-weight: bold;
|
||||
margin: 10px 0 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cosmodb .detail .images {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cosmodb .detail .images td img {
|
||||
border: 2px solid transparent;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.cosmodb .detail .images td img:hover {
|
||||
border-color: #ff6600;
|
||||
border-color: #ff6600;
|
||||
}
|
||||
|
||||
.cosmodb .detail .list_table td.even {
|
||||
width: 120px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.cosmodb .detail .h {
|
||||
width: 100%;
|
||||
font-size: 120%;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
margin-top: 40px;
|
||||
color: #db4d4d;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
font-size: 120%;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
margin-top: 40px;
|
||||
color: #db4d4d;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cosmodb .detail .keywords>ul {
|
||||
padding-left: 0;
|
||||
.cosmodb .detail .keywords > ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.cosmodb .detail .keywords>ul>li {
|
||||
list-style-type: none;
|
||||
font-style: italic;
|
||||
.cosmodb .detail .keywords > ul > li {
|
||||
list-style-type: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.cosmodb .detail .keywords>ul>li>ul {
|
||||
padding-left: 20px;
|
||||
font-style: normal;
|
||||
.cosmodb .detail .keywords > ul > li > ul {
|
||||
padding-left: 20px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.cosmodb .detail .keywords>ul>li>ul li {
|
||||
list-style: square inside;
|
||||
color: #2F5376;
|
||||
.cosmodb .detail .keywords > ul > li > ul li {
|
||||
list-style: square inside;
|
||||
color: #2f5376;
|
||||
}
|
||||
|
||||
.cosmodb .detail .datatree ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cosmodb .detail .datatree ul li {
|
||||
margin: 0 0 0 14px;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
white-space: nowrap;
|
||||
border-left: 1px solid #cccccc;
|
||||
margin: 0 0 0 14px;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
white-space: nowrap;
|
||||
border-left: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
.cosmodb .detail .datatree ul li div {
|
||||
padding-left: 10px;
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cosmodb .detail .datatree ul li div::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: -1px;
|
||||
bottom: 50%;
|
||||
width: 7px;
|
||||
border: 1px solid #cccccc;
|
||||
border-top: 0 none transparent;
|
||||
border-right: 0 none transparent;
|
||||
border-radius: 0 0 0 0px;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: -1px;
|
||||
bottom: 50%;
|
||||
width: 7px;
|
||||
border: 1px solid #cccccc;
|
||||
border-top: 0 none transparent;
|
||||
border-right: 0 none transparent;
|
||||
border-radius: 0 0 0 0px;
|
||||
}
|
||||
|
||||
.cosmodb .detail .datatree ul>li:last-child {
|
||||
border-left: 1px solid transparent;
|
||||
.cosmodb .detail .datatree ul > li:last-child {
|
||||
border-left: 1px solid transparent;
|
||||
}
|
||||
|
||||
.cosmodb .detail .datatree li.root {
|
||||
background: url(images/icon-root.png) no-repeat 6px 0;
|
||||
background: url(images/icon-root.png) no-repeat 6px 0;
|
||||
}
|
||||
|
||||
.cosmodb .detail .datatree li.root>div {
|
||||
visibility: hidden;
|
||||
.cosmodb .detail .datatree li.root > div {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.cosmodb .detail .datatree li.dir>div {
|
||||
background: url(images/icon-folder-open.png) no-repeat 8px 0;
|
||||
text-indent: 18px;
|
||||
.cosmodb .detail .datatree li.dir > div {
|
||||
background: url(images/icon-folder-open.png) no-repeat 8px 0;
|
||||
text-indent: 18px;
|
||||
}
|
||||
|
||||
.cosmodb .detail .datatree li.file>div {
|
||||
background: url(images/icon-file.png) no-repeat 8px 0;
|
||||
text-indent: 14px;
|
||||
.cosmodb .detail .datatree li.file > div {
|
||||
background: url(images/icon-file.png) no-repeat 8px 0;
|
||||
text-indent: 14px;
|
||||
}
|
||||
|
||||
.cosmodb .detail .news {
|
||||
border-bottom: 1px dashed #ddd;
|
||||
border-bottom: 1px dashed #ddd;
|
||||
}
|
||||
|
||||
.cosmodb .detail .news .date {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.cosmodb .detail .news .filename {
|
||||
display: inline-block;
|
||||
width: calc(100% - 200px);
|
||||
display: inline-block;
|
||||
width: calc(100% - 200px);
|
||||
}
|
||||
|
||||
.cosmodb .detail .news .user {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.cosmodb .detail .links .title {
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cosmodb .detail .links .link span {
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.cosmodb .detail .links .link .name {
|
||||
width: 50%;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.cosmodb .detail .links .link .note {
|
||||
width: 30%;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.cosmodb .detail .links .link .user {
|
||||
width: 10%;
|
||||
}
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
+209
-121
@@ -1,14 +1,17 @@
|
||||
import axios from 'axios';
|
||||
import { format } from 'date-fns';
|
||||
import ky from 'ky';
|
||||
import loki from 'lokijs';
|
||||
import XRegExp from 'xregexp';
|
||||
import Config from '../../config';
|
||||
import Functions from '../../functions';
|
||||
|
||||
const formatDate = (timestamp: number, pattern: string): string => format(new Date(timestamp * 1000), pattern);
|
||||
|
||||
export enum CosmoDbComponentDataType {
|
||||
System = 1,
|
||||
RadioButtom,
|
||||
CheckBox,
|
||||
Text,
|
||||
Select
|
||||
Select,
|
||||
}
|
||||
|
||||
export interface CosmoDbComponentData {
|
||||
@@ -35,7 +38,7 @@ export interface CosmoDbKeywordData {
|
||||
|
||||
export enum CosmoDbListDataType {
|
||||
List = 1,
|
||||
Thumbnail
|
||||
Thumbnail,
|
||||
}
|
||||
|
||||
export interface CosmoDbListData {
|
||||
@@ -98,7 +101,7 @@ export interface CosmoDbDetailCommentTopicData {
|
||||
|
||||
export interface CosmoDbDetailLinkData {
|
||||
type: number;
|
||||
user: string,
|
||||
user: string;
|
||||
name: string;
|
||||
href: string;
|
||||
note: string;
|
||||
@@ -118,8 +121,6 @@ export interface CosmoDbBasicSearchResult {
|
||||
}
|
||||
|
||||
class CosmoDbContext {
|
||||
|
||||
private isInitialized: boolean;
|
||||
private modulepath: string;
|
||||
private database: loki;
|
||||
private component: Collection<CosmoDbComponentData>;
|
||||
@@ -127,9 +128,9 @@ class CosmoDbContext {
|
||||
private detailTemplate: string | null;
|
||||
private list: Collection<CosmoDbListData>;
|
||||
private basic: Collection<CosmoDbBasicData>;
|
||||
private initializing: Promise<boolean> | null;
|
||||
|
||||
public constructor(modulepath: string) {
|
||||
this.isInitialized = false;
|
||||
this.modulepath = modulepath;
|
||||
this.database = new loki(modulepath);
|
||||
this.component = this.database.addCollection('component');
|
||||
@@ -137,6 +138,7 @@ class CosmoDbContext {
|
||||
this.detailTemplate = null;
|
||||
this.list = this.database.addCollection('list');
|
||||
this.basic = this.database.addCollection('data');
|
||||
this.initializing = null;
|
||||
}
|
||||
|
||||
public getModuleName(): string {
|
||||
@@ -147,12 +149,16 @@ class CosmoDbContext {
|
||||
return `${this.modulepath}/${path}`;
|
||||
}
|
||||
|
||||
private getJsonUrl(path: string): URL {
|
||||
return new URL(Config.BASE_URL + this.getUrl(path), window.location.href);
|
||||
}
|
||||
|
||||
public getListUrl(params: URLSearchParams): string {
|
||||
return this.getUrl('list.php?' + params.toString());
|
||||
return this.getUrl(`list.php?${params.toString()}`);
|
||||
}
|
||||
|
||||
public getDetailUrl(id: number, tab?: number): string {
|
||||
const tabParam = (typeof tab !== 'undefined' && tab !== 1) ? `&tab=${tab.toString()}` : ''
|
||||
const tabParam = typeof tab !== 'undefined' && tab !== 1 ? `&tab=${tab.toString()}` : '';
|
||||
return this.getUrl(`detail.php?id=${id}${tabParam}`);
|
||||
}
|
||||
|
||||
@@ -205,19 +211,21 @@ class CosmoDbContext {
|
||||
replace = basic.author;
|
||||
break;
|
||||
case 'Creation Date':
|
||||
replace = Functions.formatDate(basic.reg_date, 'YYYY-MM-DD');
|
||||
replace = formatDate(basic.reg_date, 'yyyy-MM-dd');
|
||||
break;
|
||||
case 'Views':
|
||||
replace = basic.views.toString();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const component = basic.components.find((c) => { return c.name === value.name; });
|
||||
const component = basic.components.find((c) => {
|
||||
return c.name === value.name;
|
||||
});
|
||||
replace = typeof component !== 'undefined' ? component.value : '';
|
||||
}
|
||||
replace = Functions.htmlspecialchars(replace);
|
||||
if (replace !== '' && linkToDetail) {
|
||||
replace = `<a href="${url}">` + replace + '</a>';
|
||||
replace = `<a href="${url}">${replace}</a>`;
|
||||
}
|
||||
text = text.replace(`{${value.name}}`, replace);
|
||||
});
|
||||
@@ -229,7 +237,7 @@ class CosmoDbContext {
|
||||
}
|
||||
dirs += value.name.substr(0, 3);
|
||||
}
|
||||
})
|
||||
});
|
||||
dirs = Functions.htmlspecialchars(dirs);
|
||||
if (dirs !== '' && linkToDetail) {
|
||||
dirs = `<a href="${url}">${dirs}</a>`;
|
||||
@@ -239,7 +247,7 @@ class CosmoDbContext {
|
||||
}
|
||||
|
||||
public renderDetail(basic: CosmoDbBasicData, detail: CosmoDbDetailData, tab: number): string {
|
||||
let template = this.detailTemplate || '';
|
||||
const template = this.detailTemplate || '';
|
||||
return this.renderBasic(basic, template, false)
|
||||
.replace(/{(AddBookmark|AddLink|Config|FileManager)}/g, '')
|
||||
.replace('{Acomment}', this.renderDetailAuthorComment(detail))
|
||||
@@ -247,10 +255,12 @@ class CosmoDbContext {
|
||||
.replace('{Keyword}', this.renderDetailKeywords(detail))
|
||||
.replace('{News}', this.renderDetailNews(basic))
|
||||
.replace('{Link}', this.renderDetailLinks(basic, detail))
|
||||
.replace(XRegExp('{Image\\s+(.*?)\\s+(.*?)\\s*}', 'g'), (whole, m1, m2) => this.renderDetailImage(basic, m1, m2))
|
||||
.replace(/\{Image\s+(.*?)\s+(.*?)\s*\}/g, (_whole, m1, m2) => this.renderDetailImage(basic, m1, m2))
|
||||
.replace('{Dtree}', this.renderDetailDataTree(basic))
|
||||
.replace(XRegExp('{href_tab(\\d+)}(.*?){/href_tab}', 'g'), (whole, m1, m2) => this.renderDetailHrefTab(basic, m1, m2))
|
||||
.replace(XRegExp('{tab(\\d+)}(.*?){/tab}', 'sg'), (whole, m1, m2) => this.renderDetailTab(tab, m1, m2));
|
||||
.replace(/\{href_tab(\d+)\}(.*?)\{\/href_tab\}/g, (_whole, m1, m2) =>
|
||||
this.renderDetailHrefTab(basic, m1, m2)
|
||||
)
|
||||
.replace(/\{tab(\d+)\}(.*?)\{\/tab\}/gs, (_whole, m1, m2) => this.renderDetailTab(tab, m1, m2));
|
||||
}
|
||||
|
||||
public search(params: URLSearchParams): CosmoDbBasicSearchResult {
|
||||
@@ -258,36 +268,88 @@ class CosmoDbContext {
|
||||
const paramN = params.get('n');
|
||||
const paramSort = params.get('sort');
|
||||
const paramSortMethod = params.get('sort_method');
|
||||
const offset = (paramItem !== null && /^\d+/.test(paramItem)) ? parseInt(paramItem) : 0;
|
||||
const limit = (paramN !== null && /^\d+/.test(paramN)) ? parseInt(paramN) : 20;
|
||||
const sort = (paramSort !== null && /^\d+/.test(paramSort)) ? parseInt(paramSort) : 1;
|
||||
const sortMethod = (paramSortMethod !== null && paramSortMethod === 'asc') ? 'asc' : 'desc';
|
||||
const component = this.component.findOne({ comp_id: sort })
|
||||
const offset = paramItem !== null && /^\d+/.test(paramItem) ? parseInt(paramItem, 10) : 0;
|
||||
const limit = paramN !== null && /^\d+/.test(paramN) ? parseInt(paramN, 10) : 20;
|
||||
const sort = paramSort !== null && /^\d+/.test(paramSort) ? parseInt(paramSort, 10) : 1;
|
||||
const sortMethod = paramSortMethod !== null && paramSortMethod === 'asc' ? 'asc' : 'desc';
|
||||
const component = this.component.findOne({ comp_id: sort });
|
||||
const sortFunc = (a: CosmoDbBasicData, b: CosmoDbBasicData) => {
|
||||
if (component !== null) {
|
||||
if (component.type === CosmoDbComponentDataType.System) {
|
||||
switch (component.name) {
|
||||
case 'ID':
|
||||
return a.id === b.id ? 0 : (a.id > b.id ? (sortMethod === 'asc' ? 1 : -1) : (sortMethod === 'asc' ? -1 : 1));
|
||||
return a.id === b.id
|
||||
? 0
|
||||
: a.id > b.id
|
||||
? sortMethod === 'asc'
|
||||
? 1
|
||||
: -1
|
||||
: sortMethod === 'asc'
|
||||
? -1
|
||||
: 1;
|
||||
case 'Data Name':
|
||||
return a.label === b.label ? 0 : (a.label > b.label ? (sortMethod === 'asc' ? 1 : -1) : (sortMethod === 'asc' ? -1 : 1));
|
||||
return a.label === b.label
|
||||
? 0
|
||||
: a.label > b.label
|
||||
? sortMethod === 'asc'
|
||||
? 1
|
||||
: -1
|
||||
: sortMethod === 'asc'
|
||||
? -1
|
||||
: 1;
|
||||
case 'Author':
|
||||
return a.author === b.author ? 0 : (a.author > b.author ? (sortMethod === 'asc' ? 1 : -1) : (sortMethod === 'asc' ? -1 : 1));
|
||||
return a.author === b.author
|
||||
? 0
|
||||
: a.author > b.author
|
||||
? sortMethod === 'asc'
|
||||
? 1
|
||||
: -1
|
||||
: sortMethod === 'asc'
|
||||
? -1
|
||||
: 1;
|
||||
case 'Creation Date':
|
||||
return a.reg_date === b.reg_date ? 0 : (a.reg_date > b.reg_date ? (sortMethod === 'asc' ? 1 : -1) : (sortMethod === 'asc' ? -1 : 1));
|
||||
return a.reg_date === b.reg_date
|
||||
? 0
|
||||
: a.reg_date > b.reg_date
|
||||
? sortMethod === 'asc'
|
||||
? 1
|
||||
: -1
|
||||
: sortMethod === 'asc'
|
||||
? -1
|
||||
: 1;
|
||||
case 'Views':
|
||||
return a.views === b.views ? 0 : (a.views > b.views ? (sortMethod === 'asc' ? 1 : -1) : (sortMethod === 'asc' ? -1 : 1));
|
||||
return a.views === b.views
|
||||
? 0
|
||||
: a.views > b.views
|
||||
? sortMethod === 'asc'
|
||||
? 1
|
||||
: -1
|
||||
: sortMethod === 'asc'
|
||||
? -1
|
||||
: 1;
|
||||
}
|
||||
} else {
|
||||
const ac = a.components.find((value) => { return value.name === component.name });
|
||||
const bc = b.components.find((value) => { return value.name === component.name });
|
||||
const ac = a.components.find((value) => {
|
||||
return value.name === component.name;
|
||||
});
|
||||
const bc = b.components.find((value) => {
|
||||
return value.name === component.name;
|
||||
});
|
||||
if (typeof ac !== 'undefined' && typeof bc !== 'undefined') {
|
||||
return ac.value === bc.value ? 0 : (ac.value > bc.value ? (sortMethod === 'asc' ? 1 : -1) : (sortMethod === 'asc' ? -1 : 1));
|
||||
return ac.value === bc.value
|
||||
? 0
|
||||
: ac.value > bc.value
|
||||
? sortMethod === 'asc'
|
||||
? 1
|
||||
: -1
|
||||
: sortMethod === 'asc'
|
||||
? -1
|
||||
: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
const resultSet = this.basic.chain().find();
|
||||
return {
|
||||
total: resultSet.count(),
|
||||
@@ -296,85 +358,91 @@ class CosmoDbContext {
|
||||
}
|
||||
|
||||
public async getDetail(contentId: number): Promise<CosmoDbDetailData | null> {
|
||||
let page: CosmoDbDetailData | null = null;
|
||||
try {
|
||||
const response = await axios.get(this.getUrl(`${contentId}.json`));
|
||||
page = response.data as CosmoDbDetailData;
|
||||
} catch (e) {
|
||||
// ignore
|
||||
return await ky.get(this.getJsonUrl(`${contentId}.json`)).json<CosmoDbDetailData>();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
public async initialize(): Promise<boolean> {
|
||||
if (!this.isInitialized) {
|
||||
try {
|
||||
const response = await axios.get(this.getUrl('index.json'));
|
||||
const index = response.data as CosmoDbIndexData;
|
||||
index.component.forEach((value) => {
|
||||
this.component.insert(value);
|
||||
});
|
||||
index.keyword.forEach((value) => {
|
||||
this.keyword.insert(value);
|
||||
});
|
||||
this.detailTemplate = index.detail;
|
||||
index.list.forEach((value) => {
|
||||
this.list.insert(value);
|
||||
/**
|
||||
* Loads the module index once. Concurrent callers share the same request,
|
||||
* so the collections are never filled twice.
|
||||
*/
|
||||
public initialize = (): Promise<boolean> => {
|
||||
if (this.initializing === null) {
|
||||
this.initializing = ky
|
||||
.get(this.getJsonUrl('index.json'))
|
||||
.json<CosmoDbIndexData>()
|
||||
.then((index) => {
|
||||
this.component.insert(index.component);
|
||||
this.keyword.insert(index.keyword);
|
||||
this.detailTemplate = index.detail;
|
||||
this.list.insert(index.list);
|
||||
this.basic.insert(index.data);
|
||||
return true;
|
||||
})
|
||||
index.data.forEach((value) => {
|
||||
this.basic.insert(value);
|
||||
})
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
this.isInitialized = true;
|
||||
.catch(() => false);
|
||||
}
|
||||
return this.isInitialized;
|
||||
}
|
||||
return this.initializing;
|
||||
};
|
||||
|
||||
private renderTextArea(text: string): string {
|
||||
const regexI = XRegExp('\\[i\\](.*?)\\[/i\\]', 'sg');
|
||||
const regexB = XRegExp('\\[b\\](.*?)\\[/b\\]', 'sg');
|
||||
text = text.replace(/\r?\n/g, '<br />')
|
||||
.replace(regexI, (whole, m1) => `<em>${m1}</em>`)
|
||||
.replace(regexB, (whole, m1) => `<strong>${m1}</strong>`);
|
||||
const regexI = /\[i\](.*?)\[\/i\]/gs;
|
||||
const regexB = /\[b\](.*?)\[\/b\]/gs;
|
||||
text = text
|
||||
.replace(/\r?\n/g, '<br />')
|
||||
.replace(regexI, (_whole, m1) => `<em>${m1}</em>`)
|
||||
.replace(regexB, (_whole, m1) => `<strong>${m1}</strong>`);
|
||||
return text;
|
||||
}
|
||||
|
||||
private renderComment(comment: CosmoDbDetailCommentData, showSubject: boolean) {
|
||||
const datetime = Functions.formatDate(comment.reg_date, 'YYYY-MM-DD HH:mm');
|
||||
const datetime = formatDate(comment.reg_date, 'yyyy-MM-dd HH:mm');
|
||||
const message = this.renderTextArea(comment.message);
|
||||
const subject = showSubject ? `<b>${this.renderTextArea(comment.subject)}</b><br />` : '';
|
||||
return `<tr>`
|
||||
+ `<td class="even" style="text-align: center">${comment.reg_user}<br />${datetime.replace(' ', '<br />')}</td>`
|
||||
+ `<td>${subject}${message}</td>`
|
||||
+ `</tr>`;
|
||||
return (
|
||||
`<tr>` +
|
||||
`<td class="even" style="text-align: center">${comment.reg_user}<br />${datetime.replace(' ', '<br />')}</td>` +
|
||||
`<td>${subject}${message}</td>` +
|
||||
`</tr>`
|
||||
);
|
||||
}
|
||||
|
||||
private renderDetailAuthorComment(detail: CosmoDbDetailData): string {
|
||||
const topics = detail.comment_topics.find((value) => { return value.type === 'auth'; });
|
||||
const topics = detail.comment_topics.find((value) => {
|
||||
return value.type === 'auth';
|
||||
});
|
||||
let result = '';
|
||||
if (typeof topics !== 'undefined') {
|
||||
result = '<table class="list_table"><tbody>'
|
||||
+ '<tr><th colspan="2">[en]Author Comment[/en][ja]オーサーコメント[/ja]</th></tr>'
|
||||
+ this.renderComment(topics.comment, false)
|
||||
+ topics.replies.map((value) => this.renderComment(value, true)).join('')
|
||||
+ '</tbody></table>';
|
||||
result =
|
||||
'<table class="list_table"><tbody>' +
|
||||
'<tr><th colspan="2">[en]Author Comment[/en][ja]オーサーコメント[/ja]</th></tr>' +
|
||||
this.renderComment(topics.comment, false) +
|
||||
topics.replies.map((value) => this.renderComment(value, true)).join('') +
|
||||
'</tbody></table>';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private renderDetailUserComment(detail: CosmoDbDetailData): string {
|
||||
const topics = detail.comment_topics.filter((value) => { return value.type === 'user'; });
|
||||
const topics = detail.comment_topics.filter((value) => {
|
||||
return value.type === 'user';
|
||||
});
|
||||
let result = '';
|
||||
if (topics.length > 0) {
|
||||
result = '<table class="list_table"><tbody>'
|
||||
+ '<tr><th colspan="2">[en]User Comment[/en][ja]ユーザーコメント[/ja]</th></tr>'
|
||||
+ topics.map((topic) => {
|
||||
return this.renderComment(topic.comment, true)
|
||||
+ topic.replies.map((comment) => this.renderComment(comment, true)).join('');
|
||||
}).join('')
|
||||
+ '</tbody></table>';
|
||||
result =
|
||||
'<table class="list_table"><tbody>' +
|
||||
'<tr><th colspan="2">[en]User Comment[/en][ja]ユーザーコメント[/ja]</th></tr>' +
|
||||
topics
|
||||
.map((topic) => {
|
||||
return (
|
||||
this.renderComment(topic.comment, true) +
|
||||
topic.replies.map((comment) => this.renderComment(comment, true)).join('')
|
||||
);
|
||||
})
|
||||
.join('') +
|
||||
'</tbody></table>';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -396,7 +464,11 @@ class CosmoDbContext {
|
||||
}
|
||||
});
|
||||
const renderChildKeywords = (id: number): string => {
|
||||
const keywords = this.keyword.chain().find({ pid: id, kw_id: { '$in': allKwIds } }).simplesort('sort').data();
|
||||
const keywords = this.keyword
|
||||
.chain()
|
||||
.find({ pid: id, kw_id: { $in: allKwIds } })
|
||||
.simplesort('sort')
|
||||
.data();
|
||||
if (keywords.length === 0) {
|
||||
return '';
|
||||
}
|
||||
@@ -404,10 +476,10 @@ class CosmoDbContext {
|
||||
rows.push('<ul>');
|
||||
keywords.forEach((keyword) => {
|
||||
rows.push(`<li>${keyword.keyword}${renderChildKeywords(keyword.kw_id)}</li>`);
|
||||
})
|
||||
});
|
||||
rows.push('</ul>');
|
||||
return rows.join('');
|
||||
}
|
||||
};
|
||||
return `<div class="keywords">${renderChildKeywords(0)}</div>`;
|
||||
}
|
||||
|
||||
@@ -415,7 +487,7 @@ class CosmoDbContext {
|
||||
let result = '';
|
||||
basic.items.forEach((item) => {
|
||||
if (item.type === 'file') {
|
||||
const date = Functions.formatDate(item.reg_date, 'YYYY-MM-DD');
|
||||
const date = formatDate(item.reg_date, 'yyyy-MM-dd');
|
||||
const fname = Functions.htmlspecialchars(item.path === '' ? item.name : `${item.path}/${item.name}`);
|
||||
const uname = Functions.htmlspecialchars(`${item.reg_user}`);
|
||||
result += `<div class="news"><span class="date">${date}</span><span class="filename">${fname}</span><span class="user">${uname}</span></div>`;
|
||||
@@ -432,20 +504,24 @@ class CosmoDbContext {
|
||||
const user = Functions.htmlspecialchars(link.user);
|
||||
if (link.type === 1) {
|
||||
const name = Functions.htmlspecialchars(basic.label);
|
||||
const href = this.getDetailUrl(parseInt(link.name));
|
||||
linkIn.push(`<div class="link"><span class="name"><a href="${href}">${name}</a></span><span class="note">${note}</span><span class="user">${user}</span></div>`);
|
||||
const href = this.getDetailUrl(parseInt(link.name, 10));
|
||||
linkIn.push(
|
||||
`<div class="link"><span class="name"><a href="${href}">${name}</a></span><span class="note">${note}</span><span class="user">${user}</span></div>`
|
||||
);
|
||||
} else {
|
||||
const name = Functions.htmlspecialchars(link.name);
|
||||
const href = Functions.htmlspecialchars(link.href);
|
||||
linkOut.push(`<div class="link"><span class="name"><a href="${href}">${name}</a></span><span class="note">${note}</span><span class="user">${user}</span></div>`);
|
||||
linkOut.push(
|
||||
`<div class="link"><span class="name"><a href="${href}">${name}</a></span><span class="note">${note}</span><span class="user">${user}</span></div>`
|
||||
);
|
||||
}
|
||||
});
|
||||
let result: string = '';
|
||||
if (linkIn.length !== 0) {
|
||||
result += `<div class="links"><div class="title">[en]Relative data[/en][ja]関連データ[/ja]</div>${linkIn.join('')}</div>`
|
||||
result += `<div class="links"><div class="title">[en]Relative data[/en][ja]関連データ[/ja]</div>${linkIn.join('')}</div>`;
|
||||
}
|
||||
if (linkOut.length !== 0) {
|
||||
result += `<div class="links"><div class="title">[en]Relative URL[/en][ja]関連URL[/ja]</div>${linkOut.join('')}</div>`
|
||||
result += `<div class="links"><div class="title">[en]Relative URL[/en][ja]関連URL[/ja]</div>${linkOut.join('')}</div>`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -453,28 +529,31 @@ class CosmoDbContext {
|
||||
private renderDetailImage(basic: CosmoDbBasicData, arg1: string, arg2: string): string {
|
||||
const dirs = arg1.split('|');
|
||||
const options = arg2.split('|');
|
||||
const images = basic.thumbnails.filter((thumbnail) => XRegExp(`^${dirs.join('|')}/`).test(thumbnail.url))
|
||||
.sort((a, b) => a.url === b.url ? 0 : (a.url > b.url ? 1 : -1));
|
||||
const images = basic.thumbnails
|
||||
.filter((thumbnail) => new RegExp(`^${dirs.join('|')}/`).test(thumbnail.url))
|
||||
.sort((a, b) => (a.url === b.url ? 0 : a.url > b.url ? 1 : -1));
|
||||
if (options.length !== 3 || images.length === 0) {
|
||||
return '';
|
||||
}
|
||||
const imgStyles: string[] = [];
|
||||
if (options[0] !== '') {
|
||||
const value = /^d+/.test(options[0]) ? `${options[0]}px` : options[0];
|
||||
const value = /^\d+$/.test(options[0]) ? `${options[0]}px` : options[0];
|
||||
imgStyles.push(`width:${value}`);
|
||||
}
|
||||
if (options[1] !== '') {
|
||||
const value = /^d+/.test(options[1]) ? `${options[1]}px` : options[1];
|
||||
const value = /^\d+$/.test(options[1]) ? `${options[1]}px` : options[1];
|
||||
imgStyles.push(`height:${value}`);
|
||||
}
|
||||
const imgStyle = imgStyles.length !== 0 ? 'style="' + imgStyles.join(';') + '" ' : '';
|
||||
const maxCol = Math.min(parseInt(options[2]), images.length);
|
||||
const imgStyle = imgStyles.length !== 0 ? `style="${imgStyles.join(';')}" ` : '';
|
||||
const maxCol = Math.min(parseInt(options[2], 10), images.length);
|
||||
const rows: string[] = [];
|
||||
let cols: string[] = [];
|
||||
images.forEach((image) => {
|
||||
const url = Functions.htmlspecialchars(this.getThumbnailUrl(basic.id, image.url));
|
||||
const caption = Functions.htmlspecialchars(image.caption);
|
||||
cols.push(`<td><figure><a href="${url}"><img src="${url}" alt="${url}" ${imgStyle}/></a><figcaption>${caption}</figcaption></figure></td>`);
|
||||
cols.push(
|
||||
`<td><figure><a href="${url}"><img src="${url}" alt="${url}" ${imgStyle}/></a><figcaption>${caption}</figcaption></figure></td>`
|
||||
);
|
||||
if (cols.length === maxCol) {
|
||||
rows.push(`<tr>${cols.join('')}</tr>`);
|
||||
cols = [];
|
||||
@@ -487,40 +566,49 @@ class CosmoDbContext {
|
||||
cols = [];
|
||||
}
|
||||
}
|
||||
let result = `<table class="images"><tbody>${rows.join('')}</tbody></table>`;
|
||||
const result = `<table class="images"><tbody>${rows.join('')}</tbody></table>`;
|
||||
return result;
|
||||
}
|
||||
|
||||
private renderDetailDataTree(basic: CosmoDbBasicData): string {
|
||||
const renderChildTree = (path: string): string => {
|
||||
return '<ul>'
|
||||
+ basic.items.filter((item) => item.path === path).sort((a, b) => {
|
||||
if (a.type === 'dir' && b.type === 'file') {
|
||||
return -1;
|
||||
} else if (a.type === 'file' && b.type === 'dir') {
|
||||
return 1;
|
||||
}
|
||||
return a.name === b.name ? 0 : (a.name > b.name ? 1 : -1)
|
||||
}).map((item) => {
|
||||
const name = Functions.htmlspecialchars(item.name);
|
||||
const path = item.path === '' ? item.name : `${item.path}/${item.name}`;
|
||||
const url = Functions.htmlspecialchars(this.getDataUrl(basic.id, `${path}`));
|
||||
const node = item.type === 'dir' ? `<div>${name}</div>${renderChildTree(`${path}`)}` : `<div><a href="${url}" download="${name}">${name}</a></div>`;
|
||||
return `<li class="${item.type}">${node}</li>`;
|
||||
}).join('')
|
||||
+ '</ul>';
|
||||
}
|
||||
return (
|
||||
'<ul>' +
|
||||
basic.items
|
||||
.filter((item) => item.path === path)
|
||||
.sort((a, b) => {
|
||||
if (a.type === 'dir' && b.type === 'file') {
|
||||
return -1;
|
||||
} else if (a.type === 'file' && b.type === 'dir') {
|
||||
return 1;
|
||||
}
|
||||
return a.name === b.name ? 0 : a.name > b.name ? 1 : -1;
|
||||
})
|
||||
.map((item) => {
|
||||
const name = Functions.htmlspecialchars(item.name);
|
||||
const path = item.path === '' ? item.name : `${item.path}/${item.name}`;
|
||||
const url = Functions.htmlspecialchars(this.getDataUrl(basic.id, `${path}`));
|
||||
const node =
|
||||
item.type === 'dir'
|
||||
? `<div>${name}</div>${renderChildTree(`${path}`)}`
|
||||
: `<div><a href="${url}" download="${name}">${name}</a></div>`;
|
||||
return `<li class="${item.type}">${node}</li>`;
|
||||
})
|
||||
.join('') +
|
||||
'</ul>'
|
||||
);
|
||||
};
|
||||
return `<div class="datatree"><ul><li class="root"><div>ROOT</div>${renderChildTree('')}</li></ul></div>`;
|
||||
}
|
||||
|
||||
private renderDetailHrefTab(basic: CosmoDbBasicData, no: string, label: string): string {
|
||||
const tab = parseInt(no);
|
||||
const tab = parseInt(no, 10);
|
||||
const href = this.getDetailUrl(basic.id, tab);
|
||||
return `<a href="${Functions.htmlspecialchars(href)}">${Functions.htmlspecialchars(label)}</a>`;
|
||||
}
|
||||
|
||||
private renderDetailTab(tab: number, no: string, data: string): string {
|
||||
return tab === parseInt(no) ? data : '';
|
||||
return tab === parseInt(no, 10) ? data : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import XoopsCode from '../../common/lib/XoopsCode';
|
||||
import { MultiLang } from '../../config';
|
||||
import CosmoDbContext, { CosmoDbBasicData, CosmoDbListData } from './CosmoDbContext';
|
||||
import type { MultiLang } from '../../config';
|
||||
import type CosmoDbContext from './CosmoDbContext';
|
||||
import type { CosmoDbBasicData, CosmoDbListData } from './CosmoDbContext';
|
||||
import CosmoDbListSortBar from './CosmoDbListSortBar';
|
||||
|
||||
interface Props {
|
||||
@@ -12,29 +12,33 @@ interface Props {
|
||||
data: CosmoDbBasicData[];
|
||||
}
|
||||
|
||||
const CosmoDbListListView: React.FC<Props> = (props: Props) => {
|
||||
const CosmoDbListListView = (props: Props) => {
|
||||
const { lang, context, list, params, data } = props;
|
||||
const components = context.getComponents();
|
||||
let rowHeader = list.list_th;
|
||||
components.forEach((value) => {
|
||||
rowHeader = rowHeader.replace(`{${value.name}}`, value.tag);
|
||||
})
|
||||
return <div className="list-view">
|
||||
<CosmoDbListSortBar lang={lang} context={context} list={list} params={params} />
|
||||
<table className="list-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<XoopsCode lang={lang} text={rowHeader} dohtml={true} />
|
||||
</tr>
|
||||
{data.map((value) => {
|
||||
let row = context.renderBasic(value, list.template, true);
|
||||
return <tr key={value.id}>
|
||||
<XoopsCode lang={lang} text={row} dohtml={true} />
|
||||
</tr>;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>;
|
||||
});
|
||||
return (
|
||||
<div className="list-view">
|
||||
<CosmoDbListSortBar lang={lang} context={context} list={list} params={params} />
|
||||
<table className="list-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<XoopsCode lang={lang} text={rowHeader} dohtml={true} />
|
||||
</tr>
|
||||
{data.map((value) => {
|
||||
const row = context.renderBasic(value, list.template, true);
|
||||
return (
|
||||
<tr key={value.id}>
|
||||
<XoopsCode lang={lang} text={row} dohtml={true} />
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CosmoDbListListView;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Fragment, type ReactNode } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import CosmoDbContext from './CosmoDbContext';
|
||||
import type CosmoDbContext from './CosmoDbContext';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -11,12 +11,12 @@ interface Props {
|
||||
total: number;
|
||||
}
|
||||
|
||||
const CosmoDbListPageNavi: React.FC<Props> = (props: Props) => {
|
||||
const CosmoDbListPageNavi = (props: Props) => {
|
||||
const { lang, context, params, total } = props;
|
||||
const paramItem = params.get('item');
|
||||
const paramN = params.get('n');
|
||||
const offset = (paramItem !== null && /^\d+$/.test(paramItem)) ? parseInt(paramItem) : 0;
|
||||
let limit = (paramN !== null && /^\d+$/.test(paramN)) ? parseInt(paramN) : 20;
|
||||
const offset = paramItem !== null && /^\d+$/.test(paramItem) ? parseInt(paramItem, 10) : 0;
|
||||
const limit = paramN !== null && /^\d+$/.test(paramN) ? parseInt(paramN, 10) : 20;
|
||||
if (offset > total) {
|
||||
return null;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ const CosmoDbListPageNavi: React.FC<Props> = (props: Props) => {
|
||||
if (rangeMax > max - 1) {
|
||||
rangeMax = max - 1;
|
||||
}
|
||||
const link = (num: number, el?: JSX.Element) => {
|
||||
const link = (num: number, el?: ReactNode) => {
|
||||
const label = el || num;
|
||||
const nParams = new URLSearchParams(params);
|
||||
if (num - 1 > 0) {
|
||||
@@ -42,33 +42,49 @@ const CosmoDbListPageNavi: React.FC<Props> = (props: Props) => {
|
||||
nParams.set('item', '0');
|
||||
}
|
||||
const url = context.getListUrl(nParams);
|
||||
return num === now ? <>({label})</> : <Link to={url}>{label}</Link>
|
||||
}
|
||||
const items: JSX.Element[] = [];
|
||||
return num === now ? <>({label})</> : <Link to={url}>{label}</Link>;
|
||||
};
|
||||
const items: { key: string; node: ReactNode }[] = [];
|
||||
if (prev !== 0) {
|
||||
items.push(link(prev, <>«</>));
|
||||
items.push({ key: 'prev', node: link(prev, <>«</>) });
|
||||
}
|
||||
items.push(link(min));
|
||||
items.push({ key: `${min}`, node: link(min) });
|
||||
if (now - min > 4) {
|
||||
items.push(<>...</>);
|
||||
items.push({ key: 'gap-prev', node: <>...</> });
|
||||
}
|
||||
for (let range = rangeMin; range <= rangeMax; range++) {
|
||||
items.push(link(range));
|
||||
items.push({ key: `${range}`, node: link(range) });
|
||||
}
|
||||
if (max - now > 4) {
|
||||
items.push(<>...</>);
|
||||
items.push({ key: 'gap-next', node: <>...</> });
|
||||
}
|
||||
items.push(link(max));
|
||||
items.push({ key: `${max}`, node: link(max) });
|
||||
if (next !== 0) {
|
||||
items.push(link(next, <>»</>));
|
||||
items.push({ key: 'next', node: link(next, <>»</>) });
|
||||
}
|
||||
const rangeS = offset + 1;
|
||||
const rangeE = (offset + limit) < total ? offset + limit : total;
|
||||
const rangeE = offset + limit < total ? offset + limit : total;
|
||||
|
||||
return <nav className="pagenavi">
|
||||
<span className="label">{Functions.mlang(`[en]${total} Hits (${rangeS}-${rangeE})[/en][ja]全${total}件 (${rangeS}-${rangeE}件目)[/ja]`, lang)}</span>
|
||||
{total > limit && <span className="pages">{items.map((el, index) => <Fragment key={index}>{index !== 0 && <> </>}{el}</Fragment>)}</span>}
|
||||
</nav>;
|
||||
}
|
||||
return (
|
||||
<nav className="pagenavi">
|
||||
<span className="label">
|
||||
{Functions.mlang(
|
||||
`[en]${total} Hits (${rangeS}-${rangeE})[/en][ja]全${total}件 (${rangeS}-${rangeE}件目)[/ja]`,
|
||||
lang
|
||||
)}
|
||||
</span>
|
||||
{total > limit && (
|
||||
<span className="pages">
|
||||
{items.map((item, index) => (
|
||||
<Fragment key={item.key}>
|
||||
{index !== 0 && <> </>}
|
||||
{item.node}
|
||||
</Fragment>
|
||||
))}
|
||||
</span>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default CosmoDbListPageNavi;
|
||||
|
||||
@@ -1,127 +1,83 @@
|
||||
import React, { ChangeEvent, Component, FormEvent } from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { type ChangeEvent, type FormEvent, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import CosmoDbContext, { CosmoDbListData, CosmoDbListDataType } from './CosmoDbContext';
|
||||
import type CosmoDbContext from './CosmoDbContext';
|
||||
import { type CosmoDbListData, CosmoDbListDataType } from './CosmoDbContext';
|
||||
import CosmoDbUtils from './CosmoDbUtils';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
context: CosmoDbContext;
|
||||
list: CosmoDbListData;
|
||||
params: URLSearchParams;
|
||||
}
|
||||
|
||||
interface State {
|
||||
pathname: string;
|
||||
listType: CosmoDbListDataType;
|
||||
sort: number;
|
||||
sort_method: 'asc' | 'desc'
|
||||
n: number;
|
||||
size: string;
|
||||
}
|
||||
type SortMethod = 'asc' | 'desc';
|
||||
|
||||
class CosmoDbListSortBar extends Component<Props, State> {
|
||||
const CosmoDbListSortBar = (props: Props) => {
|
||||
const { lang, context, list, params } = props;
|
||||
const navigate = useNavigate();
|
||||
const paramSort = params.get('sort');
|
||||
const paramN = params.get('n');
|
||||
const [sort, setSort] = useState(paramSort !== null && /^\d+$/.test(paramSort) ? parseInt(paramSort, 10) : 1);
|
||||
const [sortMethod, setSortMethod] = useState<SortMethod>(params.get('sort_method') === 'asc' ? 'asc' : 'desc');
|
||||
const [n, setN] = useState(paramN !== null && /^\d+$/.test(paramN) ? parseInt(paramN, 10) : 20);
|
||||
const [size, setSize] = useState(CosmoDbUtils.getThumbnailSize(params.get('size'), list.thumb_size)?.name ?? '');
|
||||
|
||||
public constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = CosmoDbListSortBar.props2state(props);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleChangeSort = this.handleChangeSort.bind(this);
|
||||
this.handleChangeSortMethod = this.handleChangeSortMethod.bind(this);
|
||||
this.handleChangeN = this.handleChangeN.bind(this);
|
||||
this.handleChangeSize = this.handleChangeSize.bind(this);
|
||||
}
|
||||
|
||||
public static props2state(props: Props): State {
|
||||
const { location, list, params } = props;
|
||||
const paramSort = params.get('sort');
|
||||
const paramSortMethod = params.get('sort_method');
|
||||
const paramN = params.get('n');
|
||||
const size = CosmoDbUtils.getThumbnailSize(params.get('size'), list.thumb_size);
|
||||
return {
|
||||
pathname: location.pathname,
|
||||
listType: list.type,
|
||||
sort: paramSort !== null && /^\d+$/.test(paramSort) ? parseInt(paramSort) : 1,
|
||||
sort_method: paramSortMethod !== null && paramSortMethod === 'asc' ? 'asc' : 'desc',
|
||||
n: paramN !== null && /^\d+$/.test(paramN) ? parseInt(paramN) : 20,
|
||||
size: size !== null ? size.name : ''
|
||||
};
|
||||
}
|
||||
|
||||
public static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
||||
const pathname = nextProps.location.pathname;
|
||||
if (pathname !== prevState.pathname || nextProps.list.type !== prevState.listType) {
|
||||
return CosmoDbListSortBar.props2state(nextProps);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||
const { context, list, params } = this.props;
|
||||
const nParams = new URLSearchParams(params);
|
||||
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const nParams = new URLSearchParams(params);
|
||||
if (list.type === CosmoDbListDataType.Thumbnail) {
|
||||
nParams.set('size', this.state.size);
|
||||
nParams.set('size', size);
|
||||
}
|
||||
nParams.set('sort', this.state.sort.toString());
|
||||
nParams.set('sort_method', this.state.sort_method);
|
||||
nParams.set('n', this.state.n.toString());
|
||||
const url = context.getListUrl(nParams);
|
||||
this.props.history.push(url);
|
||||
}
|
||||
nParams.set('sort', sort.toString());
|
||||
nParams.set('sort_method', sortMethod);
|
||||
nParams.set('n', n.toString());
|
||||
void navigate(context.getListUrl(nParams));
|
||||
};
|
||||
|
||||
private handleChangeSort(event: ChangeEvent<HTMLSelectElement>) {
|
||||
const sort = parseInt(event.target.value);
|
||||
this.setState({ sort });
|
||||
}
|
||||
|
||||
private handleChangeSortMethod(event: ChangeEvent<HTMLSelectElement>) {
|
||||
const sort_method = event.target.value as 'asc' | 'desc';
|
||||
this.setState({ sort_method });
|
||||
}
|
||||
|
||||
private handleChangeN(event: ChangeEvent<HTMLSelectElement>) {
|
||||
const n = parseInt(event.target.value);
|
||||
this.setState({ n });
|
||||
}
|
||||
|
||||
private handleChangeSize(event: ChangeEvent<HTMLSelectElement>) {
|
||||
const size = event.target.value;
|
||||
this.setState({ size });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { lang, context, list } = this.props;
|
||||
const components = context.getComponents();
|
||||
const optionsSort: JSX.Element[] = [];
|
||||
components.forEach((value) => {
|
||||
if (value.onoff === 0) {
|
||||
optionsSort.push(<option key={value.comp_id} value={value.comp_id}>{Functions.mlang(value.tag, lang)}</option>);
|
||||
}
|
||||
});
|
||||
const optionsSize: JSX.Element[] = list.thumb_size.split(';').map((value) => {
|
||||
const key = value.split(',')[0];
|
||||
return <option key={key}>{key}</option>
|
||||
});
|
||||
return <form className="list-sortbar" onSubmit={this.handleSubmit}>
|
||||
<select value={this.state.sort} onChange={this.handleChangeSort}>
|
||||
{optionsSort}
|
||||
const components = context.getComponents().filter((value) => value.onoff === 0);
|
||||
return (
|
||||
<form className="list-sortbar" onSubmit={handleSubmit}>
|
||||
<select
|
||||
value={sort}
|
||||
onChange={(event: ChangeEvent<HTMLSelectElement>) => setSort(parseInt(event.target.value, 10))}
|
||||
>
|
||||
{components.map((value) => (
|
||||
<option key={value.comp_id} value={value.comp_id}>
|
||||
{Functions.mlang(value.tag, lang)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select value={this.state.sort_method} onChange={this.handleChangeSortMethod}>
|
||||
<select
|
||||
value={sortMethod}
|
||||
onChange={(event: ChangeEvent<HTMLSelectElement>) => setSortMethod(event.target.value as SortMethod)}
|
||||
>
|
||||
<option value="asc">{Functions.mlang('[en]ascending order[/en][ja]昇順[/ja]', lang)}</option>
|
||||
<option value="desc">{Functions.mlang('[en]descending order[/en][ja]降順[/ja]', lang)}</option>
|
||||
</select>
|
||||
<select value={this.state.n} onChange={this.handleChangeN}>
|
||||
<select
|
||||
value={n}
|
||||
onChange={(event: ChangeEvent<HTMLSelectElement>) => setN(parseInt(event.target.value, 10))}
|
||||
>
|
||||
<option value="20">20</option>
|
||||
<option value="40">40</option>
|
||||
<option value="60">60</option>
|
||||
<option value="80">80</option>
|
||||
<option value="100">100</option>
|
||||
</select>
|
||||
{list.type === CosmoDbListDataType.Thumbnail && <select value={this.state.size} onChange={this.handleChangeSize}>{optionsSize}</select>}
|
||||
{list.type === CosmoDbListDataType.Thumbnail && (
|
||||
<select value={size} onChange={(event: ChangeEvent<HTMLSelectElement>) => setSize(event.target.value)}>
|
||||
{list.thumb_size.split(';').map((value) => {
|
||||
const key = value.split(',')[0];
|
||||
return <option key={key}>{key}</option>;
|
||||
})}
|
||||
</select>
|
||||
)}
|
||||
<input className="formButton" type="submit" value="Sort" />
|
||||
</form>;
|
||||
}
|
||||
}
|
||||
export default withRouter(CosmoDbListSortBar);
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default CosmoDbListSortBar;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import XoopsCode from '../../common/lib/XoopsCode';
|
||||
import { MultiLang } from '../../config';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import CosmoDbContext, { CosmoDbBasicData, CosmoDbListData } from './CosmoDbContext';
|
||||
import type CosmoDbContext from './CosmoDbContext';
|
||||
import type { CosmoDbBasicData, CosmoDbListData } from './CosmoDbContext';
|
||||
import CosmoDbListSortBar from './CosmoDbListSortBar';
|
||||
import CosmoDbUtils from './CosmoDbUtils';
|
||||
|
||||
@@ -14,14 +15,14 @@ interface Props {
|
||||
data: CosmoDbBasicData[];
|
||||
}
|
||||
|
||||
const CosmoDbListThumbnailView: React.FC<Props> = (props: Props) => {
|
||||
const CosmoDbListThumbnailView = (props: Props) => {
|
||||
const { lang, context, list, params, data } = props;
|
||||
const size = CosmoDbUtils.getThumbnailSize(params.get('size'), list.thumb_size);
|
||||
if (size === null) {
|
||||
return null;
|
||||
}
|
||||
const rows: JSX.Element[] = [];
|
||||
let cols: JSX.Element[] = [];
|
||||
const rows: ReactNode[] = [];
|
||||
let cols: ReactNode[] = [];
|
||||
let col = 1;
|
||||
const thumbRegex = new RegExp(`^${list.thumb_dir}/`);
|
||||
const imgStyles: string[] = [];
|
||||
@@ -31,7 +32,7 @@ const CosmoDbListThumbnailView: React.FC<Props> = (props: Props) => {
|
||||
if (size.width > 0) {
|
||||
imgStyles.push(`width:${size.width}px`);
|
||||
}
|
||||
const imgStyle = imgStyles.length !== 0 ? 'style="' + imgStyles.join(';') + '" ' : '';
|
||||
const imgStyle = imgStyles.length !== 0 ? `style="${imgStyles.join(';')}" ` : '';
|
||||
data.forEach((value) => {
|
||||
const thumbs = value.thumbnails.filter((thumbnail) => {
|
||||
return thumbRegex.test(thumbnail.url);
|
||||
@@ -40,12 +41,19 @@ const CosmoDbListThumbnailView: React.FC<Props> = (props: Props) => {
|
||||
if (thumbs.length === 0) {
|
||||
thumbs.push({ url: '', caption: '' });
|
||||
}
|
||||
thumbs.forEach((thumb, tindex) => {
|
||||
thumbs.forEach((thumb) => {
|
||||
const imgUrl = context.getThumbnailUrl(value.id, thumb.url);
|
||||
const style = thumb.url === '' ? '' : imgStyle;
|
||||
const image = `<img src="${Functions.htmlspecialchars(imgUrl)}" alt="${Functions.htmlspecialchars(thumb.url)}" ${style}/>`;
|
||||
const template = list.template.replace('{Image}', `<div ${imgStyle}><a href="${Functions.htmlspecialchars(url)}">${image}</a></div>`);
|
||||
cols.push(<td key={`${value.id}.${tindex}`}><XoopsCode lang={lang} text={context.renderBasic(value, template, true)} dohtml={true} /></td>);
|
||||
const template = list.template.replace(
|
||||
'{Image}',
|
||||
`<div ${imgStyle}><a href="${Functions.htmlspecialchars(url)}">${image}</a></div>`
|
||||
);
|
||||
cols.push(
|
||||
<td key={`${value.id}:${thumb.url}`}>
|
||||
<XoopsCode lang={lang} text={context.renderBasic(value, template, true)} dohtml={true} />
|
||||
</td>
|
||||
);
|
||||
if (cols.length === size.maxCol) {
|
||||
rows.push(<tr key={col}>{cols}</tr>);
|
||||
cols = [];
|
||||
@@ -61,14 +69,14 @@ const CosmoDbListThumbnailView: React.FC<Props> = (props: Props) => {
|
||||
}
|
||||
col++;
|
||||
}
|
||||
return <div className="thumbnail-view">
|
||||
<CosmoDbListSortBar lang={lang} context={context} list={list} params={params} />
|
||||
<table className="thumbnail-table">
|
||||
<tbody>
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>;
|
||||
return (
|
||||
<div className="thumbnail-view">
|
||||
<CosmoDbListSortBar lang={lang} context={context} list={list} params={params} />
|
||||
<table className="thumbnail-table">
|
||||
<tbody>{rows}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CosmoDbListThumbnailView;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Fragment, type ReactNode } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import PageNotFound from '../../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../../config';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import CosmoDbContext from './CosmoDbContext';
|
||||
import type CosmoDbContext from './CosmoDbContext';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -11,33 +11,34 @@ interface Props {
|
||||
params: URLSearchParams;
|
||||
}
|
||||
|
||||
const CosmoDbListViewSwitcher: React.FC<Props> = (props: Props) => {
|
||||
const CosmoDbListViewSwitcher = (props: Props) => {
|
||||
const { lang, context, params } = props;
|
||||
let viewSwitcher: JSX.Element[] = [];
|
||||
const viewSwitcher: ReactNode[] = [];
|
||||
const paramId = params.get('id');
|
||||
if (paramId === null || !/^\d+$/.test(paramId)) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const id = parseInt(paramId);
|
||||
const id = parseInt(paramId, 10);
|
||||
const lists = context.getLists();
|
||||
lists.forEach((value, index) => {
|
||||
const name = Functions.mlang(value.name, lang);
|
||||
if (index > 0) {
|
||||
viewSwitcher.push(<Fragment key={`${index}|`}> | </Fragment>);
|
||||
viewSwitcher.push(<Fragment key={`${value.list_id}|`}> | </Fragment>);
|
||||
}
|
||||
if (value.list_id === id) {
|
||||
viewSwitcher.push(<span key={value.list_id}>{name}</span>);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const nParams = new URLSearchParams(params);
|
||||
nParams.set('id', value.list_id.toString());
|
||||
const url = context.getListUrl(nParams);
|
||||
viewSwitcher.push(<Link key={value.list_id} to={url}>{name}</Link>);
|
||||
viewSwitcher.push(
|
||||
<Link key={value.list_id} to={url}>
|
||||
{name}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
});
|
||||
return <div className="view-switcher">
|
||||
{viewSwitcher}
|
||||
</div>;
|
||||
return <div className="view-switcher">{viewSwitcher}</div>;
|
||||
};
|
||||
|
||||
export default CosmoDbListViewSwitcher;
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
import CosmoDbContext from './CosmoDbContext';
|
||||
|
||||
class CosmoDbUtils {
|
||||
const contexts = new Map<string, CosmoDbContext>();
|
||||
|
||||
private static contexts: Map<string, CosmoDbContext> = new Map<string, CosmoDbContext>();
|
||||
|
||||
private constructor() {
|
||||
// blcok to access from outside of this class
|
||||
const getContext = (dirname: string): CosmoDbContext => {
|
||||
let context = contexts.get(dirname);
|
||||
if (typeof context === 'undefined') {
|
||||
context = new CosmoDbContext(`/modules/${dirname}`);
|
||||
contexts.set(dirname, context);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
public static getContext(modulepath: string): CosmoDbContext {
|
||||
let context = this.contexts.get(modulepath);
|
||||
if (typeof context === 'undefined') {
|
||||
context = new CosmoDbContext(modulepath);
|
||||
this.contexts.set(modulepath, context);
|
||||
}
|
||||
return context;
|
||||
const getThumbnailSize = (paramSize: string | null, thumbSize: string) => {
|
||||
const options = thumbSize.split(';').map((value) => value.split(','));
|
||||
if (options.length === 0 || options.filter((value) => value.length !== 4).length !== 0) {
|
||||
return null;
|
||||
}
|
||||
const size = paramSize === null ? options[0][0] : paramSize;
|
||||
const option = options.find((value) => value[0] === size);
|
||||
if (typeof option === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
const imgWidth = /^\d+$/.test(option[1]) ? parseInt(option[1], 10) : 0;
|
||||
const imgHeight = /^\d+$/.test(option[2]) ? parseInt(option[2], 10) : 0;
|
||||
const maxCol = /^\d+$/.test(option[3]) ? parseInt(option[3], 10) : 0;
|
||||
if (imgHeight < 0 || imgWidth < 0 || maxCol <= 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
name: option[0],
|
||||
height: imgHeight,
|
||||
width: imgWidth,
|
||||
maxCol: maxCol,
|
||||
};
|
||||
};
|
||||
|
||||
public static getThumbnailSize(paramSize: string | null, thumbSize: string) {
|
||||
const options = thumbSize.split(';').map((value) => { return value.split(','); });
|
||||
if (options.length === 0 || options.filter((value) => { return value.length !== 4; }).length !== 0) {
|
||||
return null;
|
||||
}
|
||||
const size = paramSize === null ? options[0][0] : paramSize;
|
||||
const option = options.find((value) => { return value[0] === size; });
|
||||
if (typeof option === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
const imgWidth = /^\d+$/.test(option[1]) ? parseInt(option[1]) : 0;
|
||||
const imgHeight = /^\d+$/.test(option[2]) ? parseInt(option[2]) : 0;
|
||||
const maxCol = /^\d+$/.test(option[3]) ? parseInt(option[3]) : 0;
|
||||
if (imgHeight < 0 || imgWidth < 0 || maxCol <= 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
name: option[0],
|
||||
height: imgHeight,
|
||||
width: imgWidth,
|
||||
maxCol: maxCol,
|
||||
}
|
||||
}
|
||||
}
|
||||
const CosmoDbUtils = {
|
||||
getContext,
|
||||
getThumbnailSize,
|
||||
};
|
||||
|
||||
export default CosmoDbUtils;
|
||||
|
||||
+27
-29
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
import { Navigate, Route, Routes, useLocation } from 'react-router';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import CreditsIndex from './CreditsIndex';
|
||||
import CreditsPage from './CreditsPage';
|
||||
|
||||
@@ -9,35 +8,34 @@ interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const CreditsTop = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const { search } = useLocation();
|
||||
const paramId = new URLSearchParams(search).get('id');
|
||||
if (paramId === null) {
|
||||
return <CreditsIndex lang={lang} />;
|
||||
}
|
||||
if (/^[1-9][0-9]*$/.test(paramId)) {
|
||||
return <CreditsPage lang={lang} id={parseInt(paramId, 10)} />;
|
||||
}
|
||||
return <PageNotFound lang={lang} />;
|
||||
};
|
||||
|
||||
const CreditsRedirect = () => {
|
||||
const { search, hash } = useLocation();
|
||||
return <Navigate to={`/modules/credits/index.php${search}${hash}`} replace />;
|
||||
};
|
||||
|
||||
const Credits = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const pathname = '/modules/credits';
|
||||
return (
|
||||
<>
|
||||
<Switch>
|
||||
<Route exact path={`${pathname}/index.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const paramId = params.get('id');
|
||||
if (null === paramId) {
|
||||
return <CreditsIndex lang={lang} />;
|
||||
}
|
||||
if (/^[1-9][0-9]*$/.test(paramId)) {
|
||||
const id = parseInt(paramId, 10);
|
||||
return <CreditsPage lang={lang} id={id} />;
|
||||
}
|
||||
return <PageNotFound lang={lang} />;
|
||||
}} />
|
||||
<Route exact path={`${pathname}/aboutus.php`} render={() => <CreditsPage lang={lang} id={0} />} />
|
||||
<Route exact path={pathname} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const url = pathname + '/index.php' + location.search + location.hash;
|
||||
return <Redirect to={url} />
|
||||
}} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
</>
|
||||
<Routes>
|
||||
<Route index element={<CreditsRedirect />} />
|
||||
<Route path="index.php" element={<CreditsTop lang={lang} />} />
|
||||
<Route path="aboutus.php" element={<CreditsPage lang={lang} id={0} />} />
|
||||
<Route path="*" element={<PageNotFound lang={lang} />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Credits;
|
||||
|
||||
@@ -1,84 +1,58 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import CreditsUtils, { CreditsIndexData } from './lib/CreditsUtils';
|
||||
import CreditsUtils, { type CreditsIndexData } from './lib/CreditsUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
index: CreditsIndexData | null;
|
||||
}
|
||||
const CreditsIndex = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const [index, setIndex] = useState<CreditsIndexData | null | undefined>(undefined);
|
||||
|
||||
class CreditsIndex extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
index: null,
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
CreditsUtils.getIndex().then((index) => {
|
||||
if (active) {
|
||||
setIndex(index);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
if (typeof index === 'undefined') {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang } = this.props;
|
||||
if (prevProps.lang !== lang) {
|
||||
this.updatePage();
|
||||
}
|
||||
if (index === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(index.name, lang);
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<ul>
|
||||
{index.pages.map((page) => (
|
||||
<li key={page.id}>
|
||||
<Link to={CreditsUtils.getPageUrl(page.id)}>{Functions.mlang(page.title, lang)}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
const index = await CreditsUtils.getIndex();
|
||||
if (this.isActive) {
|
||||
this.setState({ loading: false, index });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
if (this.state.loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (null === this.state.index) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(this.state.index.name, lang);
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<ul>
|
||||
{this.state.index.pages.map((page) => {
|
||||
const title = Functions.mlang(page.title, lang);
|
||||
const url = CreditsUtils.getPageUrl(page.id);
|
||||
return <li key={page.id}><Link to={url}>{title}</Link></li>;
|
||||
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default CreditsIndex;
|
||||
|
||||
+49
-66
@@ -1,86 +1,69 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { format } from 'date-fns';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import XoopsCode from '../common/lib/XoopsCode';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import CreditsUtils, { CreditsIndexData, CreditsPageDetailData } from './lib/CreditsUtils';
|
||||
import CreditsUtils, { type CreditsIndexData, type CreditsPageDetailData } from './lib/CreditsUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
id: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
interface LoadedPage {
|
||||
id: number;
|
||||
index: CreditsIndexData | null;
|
||||
page: CreditsPageDetailData | null;
|
||||
}
|
||||
|
||||
class CreditsPage extends Component<Props, State> {
|
||||
const CreditsPage = (props: Props) => {
|
||||
const { lang, id } = props;
|
||||
const [loaded, setLoaded] = useState<LoadedPage | null>(null);
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
index: null,
|
||||
page: null
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
Promise.all([CreditsUtils.getIndex(), CreditsUtils.getPage(id)]).then(([index, page]) => {
|
||||
if (active) {
|
||||
setLoaded({ id, index, page });
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
if (loaded === null || loaded.id !== id) {
|
||||
return <Loading />;
|
||||
}
|
||||
const { index, page } = loaded;
|
||||
if (index === null || page === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(index.name, lang);
|
||||
const title = Functions.mlang(page.title, lang);
|
||||
const lastupdate = page.lastupdate === 0 ? '' : format(new Date(page.lastupdate * 1000), 'MMMM do, yyyy');
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{title} - {mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
{lastupdate !== '' && (
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
{Functions.mlang('[en]Last Update[/en][ja]最終更新日[/ja]', lang)} : {lastupdate}
|
||||
</div>
|
||||
)}
|
||||
<hr />
|
||||
<XoopsCode lang={lang} text={page.content} dohtml={true} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, id } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.id !== id) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
const { id } = this.props;
|
||||
const index = await CreditsUtils.getIndex();
|
||||
const page = await CreditsUtils.getPage(id);
|
||||
if (this.isActive) {
|
||||
this.setState({ loading: false, index, page });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
if (this.state.loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (null === this.state.page || null === this.state.index) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(this.state.index.name, lang);
|
||||
const title = Functions.mlang(this.state.page.title, lang);
|
||||
const lastupdate = this.state.page.lastupdate === 0 ? '' : Functions.formatDate(this.state.page.lastupdate, 'MMMM Do, YYYY');
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{title} - {mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
{'' !== lastupdate && <div style={{ textAlign: 'right' }}>{Functions.mlang('[en]Last Update[/en][ja]最終更新日[/ja]', lang)} : {lastupdate}</div>}
|
||||
<hr />
|
||||
<XoopsCode lang={lang} text={this.state.page.content} dohtml={true} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default CreditsPage;
|
||||
export default CreditsPage;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import ky from 'ky';
|
||||
import Config from '../../config';
|
||||
|
||||
export interface CreditsPageData {
|
||||
id: number;
|
||||
@@ -16,46 +17,42 @@ export interface CreditsIndexData {
|
||||
}
|
||||
|
||||
class CreditsUtils {
|
||||
|
||||
private pathname: string;
|
||||
private index?: CreditsIndexData | null;
|
||||
private index: Promise<CreditsIndexData | null> | null = null;
|
||||
|
||||
constructor(path: string) {
|
||||
this.pathname = path;
|
||||
}
|
||||
|
||||
getIndexUrl(): string {
|
||||
return this.pathname + '/';
|
||||
return `${this.pathname}/`;
|
||||
}
|
||||
|
||||
getPageUrl(id: number): string {
|
||||
return this.getIndexUrl() + (0 === id ? 'aboutus.php' : ('?id=' + id));
|
||||
return this.getIndexUrl() + (0 === id ? 'aboutus.php' : `?id=${id}`);
|
||||
}
|
||||
|
||||
async getIndex(): Promise<CreditsIndexData | null> {
|
||||
if (typeof this.index === 'undefined') {
|
||||
try {
|
||||
const url = this.getIndexUrl() + 'index.json';
|
||||
const response = await axios.get(url);
|
||||
const index = response.data as CreditsIndexData;
|
||||
this.index = index;
|
||||
} catch (err) {
|
||||
this.index = null;
|
||||
}
|
||||
private getDataUrl(fname: string): URL {
|
||||
return new URL(Config.BASE_URL + this.getIndexUrl() + fname, window.location.href);
|
||||
}
|
||||
|
||||
/** Fetched once; concurrent callers share the request. */
|
||||
getIndex(): Promise<CreditsIndexData | null> {
|
||||
if (this.index === null) {
|
||||
this.index = ky
|
||||
.get(this.getDataUrl('index.json'))
|
||||
.json<CreditsIndexData>()
|
||||
.catch(() => null);
|
||||
}
|
||||
return this.index;
|
||||
}
|
||||
|
||||
async getPage(id: number): Promise<CreditsPageDetailData | null> {
|
||||
let page = null;
|
||||
try {
|
||||
const url = this.getIndexUrl() + id + '.json';
|
||||
const response = await axios.get(url);
|
||||
page = response.data as CreditsPageDetailData;
|
||||
} catch (err) {
|
||||
// ignore
|
||||
return await ky.get(this.getDataUrl(`${id}.json`)).json<CreditsPageDetailData>();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import React from 'react';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Footer: React.FC<Props> = (props: Props) => {
|
||||
const Footer = (_props: Props) => {
|
||||
return (
|
||||
<footer id="footer">
|
||||
<span className="copyright">Copyright (C) 2018 Neuroinformatics Unit, RIKEN Center for Brain Science</span>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
|
||||
+358
-129
@@ -1,140 +1,369 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Link, RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import LangFlag from '../common/lib/LangFlag';
|
||||
import Config, { MultiLang } from '../config';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import imageEnglish from './assets/images/mlang_english.png';
|
||||
import imageJapanese from './assets/images/mlang_japanese.png';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
keyword: string;
|
||||
menuid: number;
|
||||
}
|
||||
|
||||
class Header extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
keyword: '',
|
||||
menuid: 0
|
||||
};
|
||||
}
|
||||
|
||||
setMenuId(menuid: number) {
|
||||
this.setState({ menuid });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
return (
|
||||
<header id="header">
|
||||
<div className="clearfix">
|
||||
<Link className="logo" to="/"><span className="hidden">{Config.SITE_TITLE}</span></Link>
|
||||
<div className="utility">
|
||||
<LangFlag lang="en" image={imageJapanese} /> <LangFlag lang="ja" image={imageEnglish} />
|
||||
</div>
|
||||
const Header = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const [menuid, setMenuId] = useState(0);
|
||||
return (
|
||||
<header id="header">
|
||||
<div className="clearfix">
|
||||
<Link className="logo" to="/">
|
||||
<span className="hidden">{Config.SITE_TITLE}</span>
|
||||
</Link>
|
||||
<div className="utility">
|
||||
<LangFlag lang="en" image={imageJapanese} /> <LangFlag lang="ja" image={imageEnglish} />
|
||||
</div>
|
||||
<nav className="menubar">
|
||||
<ul className="mainmenu">
|
||||
<li><Link to="/">{Functions.mlang('[en]HOME[/en][ja]ホーム[/ja]', lang)}</Link></li>
|
||||
<li onMouseEnter={() => this.setMenuId(1)} onMouseLeave={() => this.setMenuId(0)}><Link to="/modules/htmldocs/IVBPF/Database/ivbpf.html">+ {Functions.mlang('[en]DATABASE[/en][ja]データベース[/ja]', lang)}</Link>
|
||||
{1 === this.state.menuid &&
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/newdb10/list.php?id=2">{Functions.mlang('[en]Invertebrte Sensory System[/en][ja]無脊椎動物の感覚器[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb1/list.php?id=2">{Functions.mlang('[en]Invertebrate Brain Gallery[/en][ja]無脊椎動物脳ギャラリー[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb5/list.php?id=2">{Functions.mlang('[en]Silkmoth Brain and Neurons[/en][ja]カイコガの脳と神経[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/BombyxStandardBrain/index.html">{Functions.mlang('[en]Silkmoth Standard Brain[/en][ja]カイコガ標準脳[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb112/list.php?id=2">{Functions.mlang('[en]Honeybee Brain and Neurons[/en][ja]ミツバチの脳と神経[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb9/list.php?id=2">{Functions.mlang('[en]Cricket Brain and Neurons[/en][ja]コオロギの脳と神経[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb12/list.php?id=2">{Functions.mlang('[en]Fly Brain and Neurons[/en][ja]ハエの脳と神経[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb8/list.php?id=2">{Functions.mlang('[en]Cockroach Brain and Neurons[/en][ja]ゴキブリの脳と神経[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb3/list.php?id=2">{Functions.mlang('[en]Crayfish Brain and Neurons[/en][ja]ザリガニの脳と神経[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb7/list.php?id=2">{Functions.mlang('[en]Invertebrate Image and Movie[/en][ja]無脊椎動物の画像と映像[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb13/list.php?id=2">{Functions.mlang('[en]Lepidoptera Antennal Lobe[/en][ja]各種鱗翅目昆虫触角葉糸球体構造[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/newdb2/list.php?id=2">{Functions.mlang('[en]Application Software[/en][ja]アプリケーションソフトウェア[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/PubMedPDF/journal.php?journal=266">{Functions.mlang('[en]Conference Posters[/en][ja]研究発表ポスター[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Database/Experimental_set_ups.html">{Functions.mlang('[en]Measurement Instruments[/en][ja]計測装置の製作方法[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Education/education.html">{Functions.mlang('[en]Documents for Student Experiments[/en][ja]生物教育と実習テキスト[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/PubMedPDF/">{Functions.mlang('[en]Reference and Textbooks[/en][ja]文献と教科書[/ja]', lang)}</Link>
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
<li onMouseEnter={() => this.setMenuId(2)} onMouseLeave={() => this.setMenuId(0)}><Link to="/modules/htmldocs/IVBPF/Science/insect_nervous_system.html">+ {Functions.mlang('[en]RESEARCH[/en][ja]研 究[/ja]', lang)}</Link>
|
||||
{2 === this.state.menuid &&
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Science/sensory-system.html">{Functions.mlang('[en]Sensory systems of insect[/en][ja]昆虫の感覚系[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/General/Insects_nervous_systems.html">{Functions.mlang('[en]Central nervous system of insects[/en][ja]昆虫の脳神経系[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/General/behavior.html">{Functions.mlang('[en]Animal and insect behavior[/en][ja]動物と昆虫の行動[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/General/methods.html">{Functions.mlang('[en]Methods for investigating neurons[/en][ja]昆虫のニューロンの調べ方[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Latest_topics/insect_robot_hybrid_system.html">{Functions.mlang('[en]Insect machine hybrid system[/en][ja]昆虫−ロボット融合システム(サイボーグ昆虫)[/ja]', lang)}</Link>
|
||||
{'ja' === lang &&
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Science/multicompartment_model.html">単一ニューロンのモデル化</Link>
|
||||
}
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Latest_topics/supercom.html">{Functions.mlang('[en]Reconstructing an insect brain using a next-generation super computer[/en][ja]昆虫脳を再構成する:次世代スーパーコンピュータ[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/kei/index.html">{Functions.mlang('[en]Resources of IOSSIM[/en][ja]IOSSIMの公開ページ[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Latest_topics/behavior_control.html">{Functions.mlang('[en]External control of insect behavior and neurorehabilitation[/en][ja]昆虫行動制御・神経回路修復[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Science/comparative-physiology.html">{Functions.mlang('[en]Comparative table of invertebrate[/en][ja]無脊椎動物脳比較表[/ja]', lang)}</Link>
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
<li onMouseEnter={() => this.setMenuId(3)} onMouseLeave={() => this.setMenuId(0)}><Link to="/modules/htmldocs/IVBPF/Engineering/Engineering_contents.html">+ {Functions.mlang('[en]APPLICATION[/en][ja]機能利用[/ja]', lang)}</Link>
|
||||
{3 === this.state.menuid &&
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Spider_strings.html">{Functions.mlang('[en]Materials: Spider Silk[/en][ja]材料:クモの糸[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Insect_kutikura.html">{Functions.mlang('[en]Materials: Insect Cuticle[/en][ja]材料:クチクラ[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Insect_wing_based_joint_robot.html">{Functions.mlang('[en]Materials: Insect Wing[/en][ja]材料:昆虫の翅の柔軟なロボット関節への応用[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/odor_sensor.html">{Functions.mlang('[en]Sensor: Odor Sensor[/en][ja]感覚システム:匂いセンサ[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/model_based_robot.html">{Functions.mlang('[en]Sensor: Odor-source Searching Robot[/en][ja]感覚システム:匂い源探索ロボット[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Artificial_sensor.html">{Functions.mlang('[en]Sensor: Flow sensor[/en][ja]感覚システム:人工感覚毛センサ[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Ant_based_optic_navigation_robot.html">{Functions.mlang('[en]Sensor: Visual navigation in the deseart ant[/en][ja]感覚システム:サバクアリの視覚ナビゲーション[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Cricket_sound_orientation_robot.html">{Functions.mlang('[en]Sensor: Sound orientation in the cricket[/en][ja]感覚システム:コオロギによる音源定位[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Fly_based_flight_robot.html">{Functions.mlang('[en]Robot control: A visually guided flight robot[/en][ja]ロボット制御:ハエにヒントを得た視覚誘導飛行ロボット[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Ocellar_based_flight_controller.html">{Functions.mlang('[en]Robot control: A flight-stabilization device based on ocelli[/en][ja]ロボット制御:単眼による飛行安定装置[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Insect_controlled_robot.html">{Functions.mlang('[en]Robot control: Insect controlled robot[/en][ja]ロボット制御:昆虫が操縦するロボット[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Insect_based_flight_robot.html">{Functions.mlang('[en]Robot control: An insect-based flying robot[/en][ja]ロボット制御:昆虫による飛行関連ロボット[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Insect_based_walking_robot.html">{Functions.mlang('[en]Robot control: Insect walking and a walking-controlled robot[/en][ja]ロボット制御:昆虫の歩行と歩行制御ロボット[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/biobot.html">{Functions.mlang('[en]Biobot[/en][ja]昆虫行動制御:昆虫の行動を遠隔で制御する[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Insect_based_construction_method.html">{Functions.mlang('[en]An architectural insecticide method based on insect traits[/en][ja]建築的防虫法:昆虫の習性に基づく建築的防虫法[/ja]', lang)}</Link>
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
<li onMouseEnter={() => this.setMenuId(4)} onMouseLeave={() => this.setMenuId(0)}><Link to="/modules/htmldocs/IVBPF/Education/education.html">+ {Functions.mlang('[en]EDUCATION[/en][ja]教 育[/ja]', lang)}</Link>
|
||||
{4 === this.state.menuid && 'ja' === lang &&
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/outreach/index.html">小中高生を対象としたサイエンス教室</Link>
|
||||
<Link className="item" to="/modules/noneLectClass/">中高教員対象研修会</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Education/experiments/index.html">中高教員から提供された生物実習テキスト</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/outreach/museum.html">大学・博物館からの資料提供による脳ギャラリーの作成</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Education/Experimental_set_ups.html">計測装置の作成法</Link>
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
<li onMouseEnter={() => this.setMenuId(5)} onMouseLeave={() => this.setMenuId(0)}><span>+ {Functions.mlang('[en]REFERENCE[/en][ja]リファレンス[/ja]', lang)}</span>
|
||||
{5 === this.state.menuid &&
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/xwords/index.php">{Functions.mlang('[en]Glossary[/en][ja]用語集[/ja]', lang)}</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Science/comparative-physiology.html">{Functions.mlang('[en]Comparative Table[/en][ja]無脊椎動物脳比較表[/ja]', lang)}</Link>
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
</div>
|
||||
<nav className="menubar">
|
||||
<ul className="mainmenu">
|
||||
<li>
|
||||
<Link to="/">{Functions.mlang('[en]HOME[/en][ja]ホーム[/ja]', lang)}</Link>
|
||||
</li>
|
||||
<li onMouseEnter={() => setMenuId(1)} onMouseLeave={() => setMenuId(0)}>
|
||||
<Link to="/modules/htmldocs/IVBPF/Database/ivbpf.html">
|
||||
+ {Functions.mlang('[en]DATABASE[/en][ja]データベース[/ja]', lang)}
|
||||
</Link>
|
||||
{1 === menuid && (
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/newdb10/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Invertebrte Sensory System[/en][ja]無脊椎動物の感覚器[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb1/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Invertebrate Brain Gallery[/en][ja]無脊椎動物脳ギャラリー[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb5/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Silkmoth Brain and Neurons[/en][ja]カイコガの脳と神経[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/BombyxStandardBrain/index.html">
|
||||
{Functions.mlang('[en]Silkmoth Standard Brain[/en][ja]カイコガ標準脳[/ja]', lang)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb112/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Honeybee Brain and Neurons[/en][ja]ミツバチの脳と神経[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb9/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Cricket Brain and Neurons[/en][ja]コオロギの脳と神経[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb12/list.php?id=2">
|
||||
{Functions.mlang('[en]Fly Brain and Neurons[/en][ja]ハエの脳と神経[/ja]', lang)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb8/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Cockroach Brain and Neurons[/en][ja]ゴキブリの脳と神経[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb3/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Crayfish Brain and Neurons[/en][ja]ザリガニの脳と神経[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb7/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Invertebrate Image and Movie[/en][ja]無脊椎動物の画像と映像[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb13/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Lepidoptera Antennal Lobe[/en][ja]各種鱗翅目昆虫触角葉糸球体構造[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/newdb2/list.php?id=2">
|
||||
{Functions.mlang(
|
||||
'[en]Application Software[/en][ja]アプリケーションソフトウェア[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/PubMedPDF/journal.php?journal=266">
|
||||
{Functions.mlang('[en]Conference Posters[/en][ja]研究発表ポスター[/ja]', lang)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Database/Experimental_set_ups.html">
|
||||
{Functions.mlang(
|
||||
'[en]Measurement Instruments[/en][ja]計測装置の製作方法[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Education/education.html">
|
||||
{Functions.mlang(
|
||||
'[en]Documents for Student Experiments[/en][ja]生物教育と実習テキスト[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/PubMedPDF/">
|
||||
{Functions.mlang('[en]Reference and Textbooks[/en][ja]文献と教科書[/ja]', lang)}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
<li onMouseEnter={() => setMenuId(2)} onMouseLeave={() => setMenuId(0)}>
|
||||
<Link to="/modules/htmldocs/IVBPF/Science/insect_nervous_system.html">
|
||||
+ {Functions.mlang('[en]RESEARCH[/en][ja]研 究[/ja]', lang)}
|
||||
</Link>
|
||||
{2 === menuid && (
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Science/sensory-system.html">
|
||||
{Functions.mlang('[en]Sensory systems of insect[/en][ja]昆虫の感覚系[/ja]', lang)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/General/Insects_nervous_systems.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Central nervous system of insects[/en][ja]昆虫の脳神経系[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/General/behavior.html">
|
||||
{Functions.mlang(
|
||||
'[en]Animal and insect behavior[/en][ja]動物と昆虫の行動[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/General/methods.html">
|
||||
{Functions.mlang(
|
||||
'[en]Methods for investigating neurons[/en][ja]昆虫のニューロンの調べ方[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Latest_topics/insect_robot_hybrid_system.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Insect machine hybrid system[/en][ja]昆虫−ロボット融合システム(サイボーグ昆虫)[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
{'ja' === lang && (
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Science/multicompartment_model.html"
|
||||
>
|
||||
単一ニューロンのモデル化
|
||||
</Link>
|
||||
)}
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Latest_topics/supercom.html">
|
||||
{Functions.mlang(
|
||||
'[en]Reconstructing an insect brain using a next-generation super computer[/en][ja]昆虫脳を再構成する:次世代スーパーコンピュータ[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/kei/index.html">
|
||||
{Functions.mlang('[en]Resources of IOSSIM[/en][ja]IOSSIMの公開ページ[/ja]', lang)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Latest_topics/behavior_control.html">
|
||||
{Functions.mlang(
|
||||
'[en]External control of insect behavior and neurorehabilitation[/en][ja]昆虫行動制御・神経回路修復[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Science/comparative-physiology.html">
|
||||
{Functions.mlang(
|
||||
'[en]Comparative table of invertebrate[/en][ja]無脊椎動物脳比較表[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
<li onMouseEnter={() => setMenuId(3)} onMouseLeave={() => setMenuId(0)}>
|
||||
<Link to="/modules/htmldocs/IVBPF/Engineering/Engineering_contents.html">
|
||||
+ {Functions.mlang('[en]APPLICATION[/en][ja]機能利用[/ja]', lang)}
|
||||
</Link>
|
||||
{3 === menuid && (
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Spider_strings.html">
|
||||
{Functions.mlang('[en]Materials: Spider Silk[/en][ja]材料:クモの糸[/ja]', lang)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Insect_kutikura.html">
|
||||
{Functions.mlang('[en]Materials: Insect Cuticle[/en][ja]材料:クチクラ[/ja]', lang)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Engineering/Insect_wing_based_joint_robot.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Materials: Insect Wing[/en][ja]材料:昆虫の翅の柔軟なロボット関節への応用[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/odor_sensor.html">
|
||||
{Functions.mlang(
|
||||
'[en]Sensor: Odor Sensor[/en][ja]感覚システム:匂いセンサ[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/model_based_robot.html">
|
||||
{Functions.mlang(
|
||||
'[en]Sensor: Odor-source Searching Robot[/en][ja]感覚システム:匂い源探索ロボット[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/Artificial_sensor.html">
|
||||
{Functions.mlang(
|
||||
'[en]Sensor: Flow sensor[/en][ja]感覚システム:人工感覚毛センサ[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Engineering/Ant_based_optic_navigation_robot.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Sensor: Visual navigation in the deseart ant[/en][ja]感覚システム:サバクアリの視覚ナビゲーション[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Engineering/Cricket_sound_orientation_robot.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Sensor: Sound orientation in the cricket[/en][ja]感覚システム:コオロギによる音源定位[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Engineering/Fly_based_flight_robot.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Robot control: A visually guided flight robot[/en][ja]ロボット制御:ハエにヒントを得た視覚誘導飛行ロボット[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Engineering/Ocellar_based_flight_controller.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Robot control: A flight-stabilization device based on ocelli[/en][ja]ロボット制御:単眼による飛行安定装置[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Engineering/Insect_controlled_robot.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Robot control: Insect controlled robot[/en][ja]ロボット制御:昆虫が操縦するロボット[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Engineering/Insect_based_flight_robot.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Robot control: An insect-based flying robot[/en][ja]ロボット制御:昆虫による飛行関連ロボット[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Engineering/Insect_based_walking_robot.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]Robot control: Insect walking and a walking-controlled robot[/en][ja]ロボット制御:昆虫の歩行と歩行制御ロボット[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Engineering/biobot.html">
|
||||
{Functions.mlang(
|
||||
'[en]Biobot[/en][ja]昆虫行動制御:昆虫の行動を遠隔で制御する[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
className="item"
|
||||
to="/modules/htmldocs/IVBPF/Engineering/Insect_based_construction_method.html"
|
||||
>
|
||||
{Functions.mlang(
|
||||
'[en]An architectural insecticide method based on insect traits[/en][ja]建築的防虫法:昆虫の習性に基づく建築的防虫法[/ja]',
|
||||
lang
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
<li onMouseEnter={() => setMenuId(4)} onMouseLeave={() => setMenuId(0)}>
|
||||
<Link to="/modules/htmldocs/IVBPF/Education/education.html">
|
||||
+ {Functions.mlang('[en]EDUCATION[/en][ja]教 育[/ja]', lang)}
|
||||
</Link>
|
||||
{4 === menuid && 'ja' === lang && (
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/outreach/index.html">
|
||||
小中高生を対象としたサイエンス教室
|
||||
</Link>
|
||||
<Link className="item" to="/modules/noneLectClass/">
|
||||
中高教員対象研修会
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Education/experiments/index.html">
|
||||
中高教員から提供された生物実習テキスト
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/outreach/museum.html">
|
||||
大学・博物館からの資料提供による脳ギャラリーの作成
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Education/Experimental_set_ups.html">
|
||||
計測装置の作成法
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
<li onMouseEnter={() => setMenuId(5)} onMouseLeave={() => setMenuId(0)}>
|
||||
<span>+ {Functions.mlang('[en]REFERENCE[/en][ja]リファレンス[/ja]', lang)}</span>
|
||||
{5 === menuid && (
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/xwords/index.php">
|
||||
{Functions.mlang('[en]Glossary[/en][ja]用語集[/ja]', lang)}
|
||||
</Link>
|
||||
<Link className="item" to="/modules/htmldocs/IVBPF/Science/comparative-physiology.html">
|
||||
{Functions.mlang('[en]Comparative Table[/en][ja]無脊椎動物脳比較表[/ja]', lang)}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
|
||||
<li onMouseEnter={() => this.setMenuId(6)} onMouseLeave={() => this.setMenuId(0)}><span>+ {Functions.mlang('[en]INFORMATION[/en][ja]情 報[/ja]', lang)}</span>
|
||||
{6 === this.state.menuid &&
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/credits/index.php">{Functions.mlang('[en]About This Site[/en][ja]このサイトについて[/ja]', lang)}</Link>
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
<li onMouseEnter={() => setMenuId(6)} onMouseLeave={() => setMenuId(0)}>
|
||||
<span>+ {Functions.mlang('[en]INFORMATION[/en][ja]情 報[/ja]', lang)}</span>
|
||||
{6 === menuid && (
|
||||
<div className="submenu">
|
||||
<Link className="item" to="/modules/credits/index.php">
|
||||
{Functions.mlang('[en]About This Site[/en][ja]このサイトについて[/ja]', lang)}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(Header);
|
||||
export default Header;
|
||||
|
||||
+72
-49
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { Navigate, Route, Routes } from 'react-router';
|
||||
import XoopsPathRedirect from '../common/XoopsPathRedirect';
|
||||
import { MultiLang } from '../config';
|
||||
import Config, { type MultiLang } from '../config';
|
||||
import CosmoDb from '../cosmodb/CosmoDb';
|
||||
import Credits from '../credits/Credits';
|
||||
import Pico from '../pico/Pico';
|
||||
@@ -15,54 +14,78 @@ interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const Page: React.FC<Props> = (props: Props) => {
|
||||
const TOP_URL = '/modules/htmldocs/IVBPF/Top/index.html';
|
||||
|
||||
const COSMODB_MODULES = [
|
||||
'newdb1',
|
||||
'newdb2',
|
||||
'newdb3',
|
||||
'newdb5',
|
||||
'newdb7',
|
||||
'newdb8',
|
||||
'newdb9',
|
||||
'newdb10',
|
||||
'newdb112',
|
||||
'newdb12',
|
||||
'newdb13',
|
||||
];
|
||||
|
||||
const Page = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const url = '/modules/htmldocs/IVBPF/Top/index.html';
|
||||
return <div id="page">
|
||||
<Header lang={lang} />
|
||||
<div id="main">
|
||||
<Switch>
|
||||
<Route path="/modules/newdb1" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb1" />} />
|
||||
<Route path="/modules/newdb2" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb2" />} />
|
||||
<Route path="/modules/newdb3" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb3" />} />
|
||||
<Route path="/modules/newdb5" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb5" />} />
|
||||
<Route path="/modules/newdb7" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb7" />} />
|
||||
<Route path="/modules/newdb8" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb8" />} />
|
||||
<Route path="/modules/newdb9" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb9" />} />
|
||||
<Route path="/modules/newdb10" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb10" />} />
|
||||
<Route path="/modules/newdb112" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb112" />} />
|
||||
<Route path="/modules/newdb12" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb12" />} />
|
||||
<Route path="/modules/newdb13" render={() => <CustomBlock lang={lang} modulepath="/modules/newdb13" />} />
|
||||
<Route path="/modules/noneLectClass" render={() => <CustomBlock lang={lang} modulepath="/modules/noneLectClass" />} />
|
||||
</Switch>
|
||||
<div className="mainContent">
|
||||
<Switch>
|
||||
<Route exact path="/" render={() => <Redirect to={url} />} />
|
||||
<Route path="/modules/htmldocs/index.html" render={() => <Redirect to={url} />} />
|
||||
<Route path="/modules/htmldocs/IVBPF/index.html" render={() => <Redirect to={url} />} />
|
||||
<Route path="/modules/htmldocs" render={() => <Pico lang={lang} modulepath="/modules/htmldocs" showTitle={false} />} />
|
||||
<Route path="/modules/documents" render={() => <Pico lang={lang} modulepath="/modules/documents" showTitle={false} />} />
|
||||
<Route path="/modules/newdb1" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb1" />} />
|
||||
<Route path="/modules/newdb2" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb2" />} />
|
||||
<Route path="/modules/newdb3" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb3" />} />
|
||||
<Route path="/modules/newdb5" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb5" />} />
|
||||
<Route path="/modules/newdb7" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb7" />} />
|
||||
<Route path="/modules/newdb8" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb8" />} />
|
||||
<Route path="/modules/newdb9" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb9" />} />
|
||||
<Route path="/modules/newdb10" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb10" />} />
|
||||
<Route path="/modules/newdb112" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb112" />} />
|
||||
<Route path="/modules/newdb12" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb12" />} />
|
||||
<Route path="/modules/newdb13" render={() => <CosmoDb lang={lang} modulepath="/modules/newdb13" />} />
|
||||
<Route path="/modules/credits" render={() => <Credits lang={lang} />} />
|
||||
<Route path="/modules/PubMedPDF" render={() => <PubmedPdf lang={lang} />} />
|
||||
<Route path="/modules/xwords" render={() => <Xwords lang={lang} />} />
|
||||
<Route path="/modules/noneLectClass" render={() => <div>神生研教員研修: <a href="http://www.brain.rcast.u-tokyo.ac.jp/others/2010-jinseiken/100131-jinseiken-text.pdf">100131-jinseiken-text.pdf</a></div>} />
|
||||
<Route component={XoopsPathRedirect} />
|
||||
</Switch>
|
||||
return (
|
||||
<div id="page">
|
||||
<Header lang={lang} />
|
||||
<div id="main">
|
||||
<Routes>
|
||||
{[...COSMODB_MODULES, 'noneLectClass'].map((dirname) => (
|
||||
<Route
|
||||
key={dirname}
|
||||
path={`/modules/${dirname}/*`}
|
||||
element={<CustomBlock lang={lang} modulepath={`/modules/${dirname}`} />}
|
||||
/>
|
||||
))}
|
||||
<Route path="*" element={null} />
|
||||
</Routes>
|
||||
<div className="mainContent">
|
||||
<Routes>
|
||||
<Route index element={<Navigate to={TOP_URL} replace />} />
|
||||
<Route path="/modules/htmldocs/index.html" element={<Navigate to={TOP_URL} replace />} />
|
||||
<Route path="/modules/htmldocs/IVBPF/index.html" element={<Navigate to={TOP_URL} replace />} />
|
||||
{Config.PICO_MODULES.map((dirname) => (
|
||||
<Route
|
||||
key={dirname}
|
||||
path={`/modules/${dirname}/*`}
|
||||
element={<Pico lang={lang} dirname={dirname} />}
|
||||
/>
|
||||
))}
|
||||
{COSMODB_MODULES.map((dirname) => (
|
||||
<Route
|
||||
key={dirname}
|
||||
path={`/modules/${dirname}/*`}
|
||||
element={<CosmoDb lang={lang} dirname={dirname} />}
|
||||
/>
|
||||
))}
|
||||
<Route path="/modules/credits/*" element={<Credits lang={lang} />} />
|
||||
<Route path="/modules/PubMedPDF/*" element={<PubmedPdf lang={lang} />} />
|
||||
<Route path="/modules/xwords/*" element={<Xwords lang={lang} />} />
|
||||
<Route
|
||||
path="/modules/noneLectClass/*"
|
||||
element={
|
||||
<div>
|
||||
神生研教員研修:{' '}
|
||||
<a href="http://www.brain.rcast.u-tokyo.ac.jp/others/2010-jinseiken/100131-jinseiken-text.pdf">
|
||||
100131-jinseiken-text.pdf
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<Route path="*" element={<XoopsPathRedirect lang={lang} />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
<Footer lang={lang} />
|
||||
</div>
|
||||
<Footer lang={lang} />
|
||||
</div>;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
+116
-117
@@ -1,22 +1,22 @@
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
width: 100%;
|
||||
min-width: 880px;
|
||||
width: 100%;
|
||||
min-width: 880px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #000000;
|
||||
background-color: #c6c6c6;
|
||||
margin: 0;
|
||||
font-size: 90%;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #000000;
|
||||
background-color: #c6c6c6;
|
||||
margin: 0;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
h1,
|
||||
@@ -25,242 +25,241 @@ h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
color: inherit;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.802em;
|
||||
font-size: 1.802em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.602em;
|
||||
font-size: 1.602em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.424em;
|
||||
font-size: 1.424em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.266em;
|
||||
font-size: 1.266em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.125em;
|
||||
font-size: 1.125em;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #ff6600;
|
||||
text-decoration: underline;
|
||||
color: #ff6600;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
padding: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
th,
|
||||
td,
|
||||
.head {
|
||||
vertical-align: top;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.foot {
|
||||
vertical-align: middle;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
th,
|
||||
.head,
|
||||
.foot {
|
||||
font-weight: bold;
|
||||
background-color: #cee1eb;
|
||||
font-weight: bold;
|
||||
background-color: #cee1eb;
|
||||
}
|
||||
|
||||
.even {
|
||||
background-color: #eff4f9;
|
||||
background-color: #eff4f9;
|
||||
}
|
||||
|
||||
.odd {
|
||||
background-color: #f5f7f7;
|
||||
background-color: #f5f7f7;
|
||||
}
|
||||
|
||||
.clearfix::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.font-weight-bold {
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.font-italic {
|
||||
font-style: italic;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.block .blockTitle {
|
||||
padding: 5px 10px;
|
||||
color: #ffffff;
|
||||
background: #802d2d;
|
||||
font-weight: bold;
|
||||
padding: 5px 10px;
|
||||
color: #ffffff;
|
||||
background: #802d2d;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.block .blockContent {
|
||||
border-left: 1px solid#cccccc;
|
||||
border-right: 1px solid #cccccc;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
padding: 3px;
|
||||
margin-bottom: 8px;
|
||||
border-left: 1px solid #cccccc;
|
||||
border-right: 1px solid #cccccc;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
padding: 3px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#page {
|
||||
margin: 5px 15px;
|
||||
margin: 5px 15px;
|
||||
}
|
||||
|
||||
#header {
|
||||
color: #ffffff;
|
||||
background-color: #802d2d;
|
||||
color: #ffffff;
|
||||
background-color: #802d2d;
|
||||
}
|
||||
|
||||
#header .logo {
|
||||
float: left;
|
||||
display: block;
|
||||
height: 40px;
|
||||
width: 180px;
|
||||
background-image: url(images/logo.png);
|
||||
float: left;
|
||||
display: block;
|
||||
height: 40px;
|
||||
width: 180px;
|
||||
background-image: url(images/logo.png);
|
||||
}
|
||||
|
||||
#header .utility {
|
||||
float: right;
|
||||
padding-top: 7px;
|
||||
padding-right: 15px;
|
||||
float: right;
|
||||
padding-top: 7px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
#header .menubar {
|
||||
height: 22px;
|
||||
border-top: 1px solid #000000;
|
||||
border-bottom: 1px solid #000000;
|
||||
background-color: #464646;
|
||||
color: #ffffff;
|
||||
height: 22px;
|
||||
border-top: 1px solid #000000;
|
||||
border-bottom: 1px solid #000000;
|
||||
background-color: #464646;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#header .mainmenu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#header .mainmenu li {
|
||||
margin: 0 10px;
|
||||
line-height: 20px;
|
||||
font-size: 0.8em;
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
margin: 0 10px;
|
||||
line-height: 20px;
|
||||
font-size: 0.8em;
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#header .mainmenu li:first-child {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#header .mainmenu li>a,
|
||||
#header .mainmenu li>span {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
color: #ffffff;
|
||||
background-color: #464646;
|
||||
text-decoration: none;
|
||||
#header .mainmenu li > a,
|
||||
#header .mainmenu li > span {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
color: #ffffff;
|
||||
background-color: #464646;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header .mainmenu li>a:hover,
|
||||
#header .mainmenu li>span:hover {
|
||||
background-color: #6f6f6f;
|
||||
#header .mainmenu li > a:hover,
|
||||
#header .mainmenu li > span:hover {
|
||||
background-color: #6f6f6f;
|
||||
}
|
||||
|
||||
#header .mainmenu .submenu {
|
||||
position: relative;
|
||||
float: left;
|
||||
background-color: lightyellow;
|
||||
border: 1px solid #000000;
|
||||
z-index: 10;
|
||||
animation: fadeIn 1s ease 0s 1 normal;
|
||||
position: relative;
|
||||
float: left;
|
||||
background-color: lightyellow;
|
||||
border: 1px solid #000000;
|
||||
z-index: 10;
|
||||
animation: fadeIn 1s ease 0s 1 normal;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#header .mainmenu .submenu .item {
|
||||
display: block;
|
||||
padding: 1px 5px;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
padding: 1px 5px;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header .mainmenu .submenu .item:not(:first-child) {
|
||||
border-top: 1px solid #000000;
|
||||
border-top: 1px solid #000000;
|
||||
}
|
||||
|
||||
#header .mainmenu .submenu .item:hover {
|
||||
background-color: yellow;
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
#footer {
|
||||
height: 42px;
|
||||
border-top: 2px solid #000000;
|
||||
color: #ffffff;
|
||||
background-color: #802d2d;
|
||||
text-align: center;
|
||||
height: 42px;
|
||||
border-top: 2px solid #000000;
|
||||
color: #ffffff;
|
||||
background-color: #802d2d;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#footer .copyright {
|
||||
line-height: 38px;
|
||||
display: block;
|
||||
font-size: 90%;
|
||||
line-height: 38px;
|
||||
display: block;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
#main {
|
||||
background-color: #ffffff;
|
||||
padding: 15px;
|
||||
min-height: 500px;
|
||||
|
||||
}
|
||||
background-color: #ffffff;
|
||||
padding: 15px;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import XoopsCode from '../../common/lib/XoopsCode';
|
||||
import { MultiLang } from '../../config';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
|
||||
interface Props {
|
||||
@@ -12,75 +11,91 @@ const contents = [
|
||||
{
|
||||
modulepath: '/modules/newdb1',
|
||||
title: '[en]Invertebrate Brain Gallery[/en][ja]無脊椎動物脳ギャラリー[/ja]',
|
||||
content: '<p>[en]Many invertebrate animals have evolved a rich variety of brains (head ganglia). Unusually large brains of squids and octopuses and small but sophisticated brains of arthropods, especially those of insects, are among the most fascinating brains created on earth by natural selection. Here, photographs of invertebrates of 6 phyla, 35 orders and 47 species and those of their central nervous systems are presented, with brief explanations of their characteristics. Enjoy seeing the pictures and consider evolution of brains in invertebrates. These images can be only used for research and education freely. Providers (Dr. Makoto Mizunami) have copyright for images and documents. [/en][ja]脳を持つ動物は脊椎動物だけではありません。多くの無脊椎動物は、実に多様な脳を発達させています。イカやタコの哺乳類に匹敵するほどの巨大な脳や、節足動物、特に昆虫の小さく精巧な脳は、自然選択が生み出した脳のなかでも最も興味深いものの1つと言えます。ここでは、6門、35目、47種の無脊椎動物とその脳および中枢神経系全体の取り出し標本の写真を展示し、解説しています。画像を見ながら、脳の進化について考えるとよいでしょう。 これらの画像は、教育、研究のためには自由に利用することができます。なお、画像、文章の著作権は、提供者(北海道大学・水波誠)にあります。 [/ja]</p>',
|
||||
content:
|
||||
'<p>[en]Many invertebrate animals have evolved a rich variety of brains (head ganglia). Unusually large brains of squids and octopuses and small but sophisticated brains of arthropods, especially those of insects, are among the most fascinating brains created on earth by natural selection. Here, photographs of invertebrates of 6 phyla, 35 orders and 47 species and those of their central nervous systems are presented, with brief explanations of their characteristics. Enjoy seeing the pictures and consider evolution of brains in invertebrates. These images can be only used for research and education freely. Providers (Dr. Makoto Mizunami) have copyright for images and documents. [/en][ja]脳を持つ動物は脊椎動物だけではありません。多くの無脊椎動物は、実に多様な脳を発達させています。イカやタコの哺乳類に匹敵するほどの巨大な脳や、節足動物、特に昆虫の小さく精巧な脳は、自然選択が生み出した脳のなかでも最も興味深いものの1つと言えます。ここでは、6門、35目、47種の無脊椎動物とその脳および中枢神経系全体の取り出し標本の写真を展示し、解説しています。画像を見ながら、脳の進化について考えるとよいでしょう。 これらの画像は、教育、研究のためには自由に利用することができます。なお、画像、文章の著作権は、提供者(北海道大学・水波誠)にあります。 [/ja]</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb2',
|
||||
title: '[en]Application software[/en][ja]アプリケーションソフトウェア[/ja]',
|
||||
content: '<p>Information and URL for application software can be used in the invertebrate neuroethology are presented.</p>',
|
||||
content:
|
||||
'<p>Information and URL for application software can be used in the invertebrate neuroethology are presented.</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb3',
|
||||
title: '[en]Crayfish Brain and Neurons[/en][ja]ザリガニの脳と神経[/ja]',
|
||||
content: '<p>Experimental data, such as confocal images by laser scanning microscope and electrical responses to the pheromone stimulus are presented. Providers (*) have copyright for images and documents.</p>',
|
||||
content:
|
||||
'<p>Experimental data, such as confocal images by laser scanning microscope and electrical responses to the pheromone stimulus are presented. Providers (*) have copyright for images and documents.</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb5',
|
||||
title: '[en]Bombyx Brain and Neurons[/en][ja]カイコガの脳・神経細胞データ[/ja]',
|
||||
content: '<p>[en]Experimental data, such as confocal images by laser scanning microscope and electrical responses to the pheromone stimulus are presented. Providers (Prof. Ryohei Kanzaki et al., The University of Tokyo) have copyright for images and documents. [/en][ja]カイコガの脳および同定ニューロンの共焦点レーザ顕微鏡画像、フェロモン刺激に対する電気応答などの実測データを提供しています。一連のデータの著作権は、東京大学・神崎亮平が管理しています。(データ取得者はファイル名末尾の記号による。 se:関洋一 rf:福島亮太 sn:並木重宏 sw:和田賢 hi:Evan Hill Ki:吉川 美都子) [/ja]</p>',
|
||||
content:
|
||||
'<p>[en]Experimental data, such as confocal images by laser scanning microscope and electrical responses to the pheromone stimulus are presented. Providers (Prof. Ryohei Kanzaki et al., The University of Tokyo) have copyright for images and documents. [/en][ja]カイコガの脳および同定ニューロンの共焦点レーザ顕微鏡画像、フェロモン刺激に対する電気応答などの実測データを提供しています。一連のデータの著作権は、東京大学・神崎亮平が管理しています。(データ取得者はファイル名末尾の記号による。 se:関洋一 rf:福島亮太 sn:並木重宏 sw:和田賢 hi:Evan Hill Ki:吉川 美都子) [/ja]</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb7',
|
||||
title: '[en]Movies and Images[/en][ja]無脊椎動物の写真と映像[/ja]',
|
||||
content: '<p>Movie and image data of invertebrate animals are presented by this database. It can be used for educational fields. Providers (*) have copyright for images and documents.</p>',
|
||||
content:
|
||||
'<p>Movie and image data of invertebrate animals are presented by this database. It can be used for educational fields. Providers (*) have copyright for images and documents.</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb8',
|
||||
title: '[en]Cockroach Brain and Neurons[/en][ja]ゴキブリの脳と神経[/ja]',
|
||||
content: '<p>Experimental data, such as confocal images by laser scanning microscope are presented. Providers (.......)have copyright for images and documents. Providers (*) have copyright for images and documents.</p>',
|
||||
content:
|
||||
'<p>Experimental data, such as confocal images by laser scanning microscope are presented. Providers (.......)have copyright for images and documents. Providers (*) have copyright for images and documents.</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb9',
|
||||
title: '[en]Cricket Brain and Neurons[/en][ja]コオロギの脳と神経[/ja]',
|
||||
content: '<p>Experimental data, sch as confocal images by laser scanning microscope are preseunted. Providers (*) have copyright for images and documents.</p>',
|
||||
content:
|
||||
'<p>Experimental data, sch as confocal images by laser scanning microscope are preseunted. Providers (*) have copyright for images and documents.</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb10',
|
||||
title: '[en]Invertebrate Sensory System[/en][ja]無脊椎動物の感覚器[/ja]',
|
||||
content: '<p>Scanning migrographs of inverbetrates of antenna. Sensory hairs for olfaction and taste are shown. Providers (*) have copyright for images and documents.</p>',
|
||||
content:
|
||||
'<p>Scanning migrographs of inverbetrates of antenna. Sensory hairs for olfaction and taste are shown. Providers (*) have copyright for images and documents.</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb112',
|
||||
title: '[en]Honeybee Brain and Neurons[/en][ja]ミツバチの脳と神経[/ja]',
|
||||
content: '<p>Experimental data, such as confocal images by laser scanning microscope and electrical responses to the pheromone stimulus are presented. Providers (*) have copyright for images and documents.</p>',
|
||||
content:
|
||||
'<p>Experimental data, such as confocal images by laser scanning microscope and electrical responses to the pheromone stimulus are presented. Providers (*) have copyright for images and documents.</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb12',
|
||||
title: '[en]Fly Brain and Neurons[/en][ja]ハエの脳と神経[/ja]',
|
||||
content: '<p>Experimental data, such as images of transmission electron microscope and fluorescence maicroscope are presented. Providers (*) have copyright for images and documents.</p>',
|
||||
content:
|
||||
'<p>Experimental data, such as images of transmission electron microscope and fluorescence maicroscope are presented. Providers (*) have copyright for images and documents.</p>',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/newdb13',
|
||||
title: '[en]Glomerular Structures of Lepidopteran Insects[/en][ja]各種鱗翅目昆虫触角葉糸球体構造[/ja]',
|
||||
content: '[en]<p>Insects have a pair of the antenna. In lepidopteran insects there are several different types of sensillum on the antennae for olfaction (i.e., sensilla torichodea, sensilla basiconica, and sensilla coeloconica). Each sensillum contains olfactory receptor neurons (ORNs). Axons of the ORNs project to the first order olfactory center, the antennal lobe (AL). The AL consists of a dorsally located male specific macroglomerular complex (MGC) and ventrally located ordinary glomeruli (OG). Pheromonal information is processed in the MGC and general odor information is processed in the OGs. Glomerular structures in different lepidopteran insects are demonstrated.</p><p><a href="/modules/htmldocs/IVBPF/General/sensory-system.html">Pheromone and general odor processing</a></p>[/en][ja]<p>昆虫はわれわれヒトを含めた哺乳類に匹敵する,あるいはそれ以上の優れた嗅覚能力を持っています.嗅覚受容細胞(匂いセンサ)で受容された匂い情報は嗅覚系一次中枢において匂いの識別処理が行われます.哺乳類ではこの脳領域を嗅球(olfactory bulb),昆虫では触角葉(antennal lobe)と言いいます.触角葉は糸球体といわれる多数の球状構造からできています.このデータベースで挙げた鱗翅目昆虫の糸球体は,触角葉の入り口にある大きい糸球体(大糸球体(macroglomerular complex; MGC))とその他の小さなたくさんの糸球体(常糸球体(ordinary glomerulus))から構成されます.前者はフェロモン情報処理に,後者は植物や食物などの一般臭の情報を処理します.</p><p><a href="/modules/htmldocs/IVBPF/General/sensory-system.html">フェロモン及び一般臭受容の詳細</a> <a href="/modules/htmldocs/IVBPF/General/general_odor_processing.html">匂い識別機構の詳細</a></p>[/ja]',
|
||||
content:
|
||||
'[en]<p>Insects have a pair of the antenna. In lepidopteran insects there are several different types of sensillum on the antennae for olfaction (i.e., sensilla torichodea, sensilla basiconica, and sensilla coeloconica). Each sensillum contains olfactory receptor neurons (ORNs). Axons of the ORNs project to the first order olfactory center, the antennal lobe (AL). The AL consists of a dorsally located male specific macroglomerular complex (MGC) and ventrally located ordinary glomeruli (OG). Pheromonal information is processed in the MGC and general odor information is processed in the OGs. Glomerular structures in different lepidopteran insects are demonstrated.</p><p><a href="/modules/htmldocs/IVBPF/General/sensory-system.html">Pheromone and general odor processing</a></p>[/en][ja]<p>昆虫はわれわれヒトを含めた哺乳類に匹敵する,あるいはそれ以上の優れた嗅覚能力を持っています.嗅覚受容細胞(匂いセンサ)で受容された匂い情報は嗅覚系一次中枢において匂いの識別処理が行われます.哺乳類ではこの脳領域を嗅球(olfactory bulb),昆虫では触角葉(antennal lobe)と言いいます.触角葉は糸球体といわれる多数の球状構造からできています.このデータベースで挙げた鱗翅目昆虫の糸球体は,触角葉の入り口にある大きい糸球体(大糸球体(macroglomerular complex; MGC))とその他の小さなたくさんの糸球体(常糸球体(ordinary glomerulus))から構成されます.前者はフェロモン情報処理に,後者は植物や食物などの一般臭の情報を処理します.</p><p><a href="/modules/htmldocs/IVBPF/General/sensory-system.html">フェロモン及び一般臭受容の詳細</a> <a href="/modules/htmldocs/IVBPF/General/general_odor_processing.html">匂い識別機構の詳細</a></p>[/ja]',
|
||||
},
|
||||
{
|
||||
modulepath: '/modules/noneLectClass',
|
||||
title: '中高教員対象研修会',
|
||||
content: '動物の行動が発現するしくみについての講義をとおして、感覚・脳・行動の成り立ち(神経・脳の役割)を学び、カイコガの匂い源探索の行動実験により、感覚・脳・行動の機能を考え、推測していきます。さらに、筋電位を計測する安価な増幅器を作製し、実際に作製した増幅器を使って、昆虫の羽ばたきをおこす筋肉の電位を計測することで、成体がつくる生物電気について学びます。また、成体が発生する電気によりロボット(チョロQ)を動かすことで、生物と工学の接点について学び、ブレインマシンインターフェース(BMI)といわれる、脳信号で車椅子を操作する最新の技術への取っ掛かりを実験によって体験します。'
|
||||
}
|
||||
content:
|
||||
'動物の行動が発現するしくみについての講義をとおして、感覚・脳・行動の成り立ち(神経・脳の役割)を学び、カイコガの匂い源探索の行動実験により、感覚・脳・行動の機能を考え、推測していきます。さらに、筋電位を計測する安価な増幅器を作製し、実際に作製した増幅器を使って、昆虫の羽ばたきをおこす筋肉の電位を計測することで、成体がつくる生物電気について学びます。また、成体が発生する電気によりロボット(チョロQ)を動かすことで、生物と工学の接点について学び、ブレインマシンインターフェース(BMI)といわれる、脳信号で車椅子を操作する最新の技術への取っ掛かりを実験によって体験します。',
|
||||
},
|
||||
];
|
||||
|
||||
const CustomBlock: React.FC<Props> = (props: Props) => {
|
||||
const CustomBlock = (props: Props) => {
|
||||
const { lang, modulepath } = props;
|
||||
const data = contents.find((value) => modulepath === value.modulepath);
|
||||
if (typeof data === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
return <div className="block">
|
||||
<div className="blockTitle">{Functions.mlang(data.title, lang)}</div>
|
||||
<div className="blockContent"><XoopsCode lang={lang} text={data.content} dohtml={true} /></div>
|
||||
</div>;
|
||||
}
|
||||
return (
|
||||
<div className="block">
|
||||
<div className="blockTitle">{Functions.mlang(data.title, lang)}</div>
|
||||
<div className="blockContent">
|
||||
<XoopsCode lang={lang} text={data.content} dohtml={true} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomBlock;
|
||||
|
||||
+44
-44
@@ -1,18 +1,16 @@
|
||||
import moment from 'moment';
|
||||
import XRegExp from 'xregexp';
|
||||
import Config, { MultiLang } from './config';
|
||||
import Config, { type MultiLang } from './config';
|
||||
|
||||
const escape = (str: string): string => {
|
||||
const escapeUrl = (str: string) => {
|
||||
return encodeURIComponent(str).replace(/[!'()*]/g, (c) => {
|
||||
return '%' + c.charCodeAt(0).toString(16);
|
||||
return `%${c.charCodeAt(0).toString(16)}`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const unescape = (str: string): string => {
|
||||
const unescapeUrl = (str: string) => {
|
||||
return decodeURIComponent(str);
|
||||
}
|
||||
};
|
||||
|
||||
const htmlspecialchars = (str: string): string => {
|
||||
const htmlspecialchars = (str: string) => {
|
||||
return str.replace(/(<|>|&|'|")/g, (match) => {
|
||||
switch (match) {
|
||||
case '<':
|
||||
@@ -21,46 +19,42 @@ const htmlspecialchars = (str: string): string => {
|
||||
return '>';
|
||||
case '&':
|
||||
return '&';
|
||||
case '\'':
|
||||
case "'":
|
||||
return ''';
|
||||
case '"':
|
||||
return '"';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const camelCase = (str: string): string => {
|
||||
const camelCase = (str: string) => {
|
||||
return str.replace(/[-_](.)/g, (...matches) => {
|
||||
return matches[1].toUpperCase();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const snakeCase = (str: string): string => {
|
||||
const snakeCase = (str: string) => {
|
||||
var camel = camelCase(str);
|
||||
return camel.replace(/[A-Z]/g, (...matches) => {
|
||||
return "_" + matches[0].charAt(0).toLowerCase();
|
||||
return `_${matches[0].charAt(0).toLowerCase()}`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const pascalCase = (str: string): string => {
|
||||
const pascalCase = (str: string) => {
|
||||
var camel = camelCase(str);
|
||||
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
||||
}
|
||||
};
|
||||
|
||||
const base64Encode = (str: string): string => {
|
||||
const base64Encode = (str: string) => {
|
||||
return btoa(unescape(encodeURIComponent(str)));
|
||||
}
|
||||
};
|
||||
|
||||
const base64Decode = (str: string): string => {
|
||||
const base64Decode = (str: string) => {
|
||||
return decodeURIComponent(escape(atob(str)));
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (timestamp: number, format: string): string => {
|
||||
return moment(new Date(timestamp * 1000)).format(format);
|
||||
}
|
||||
|
||||
const ordinal = (num: number): string => {
|
||||
const ordinal = (num: number) => {
|
||||
const hundred = num % 100;
|
||||
if (hundred >= 10 && hundred <= 20) {
|
||||
return 'th';
|
||||
@@ -68,69 +62,75 @@ const ordinal = (num: number): string => {
|
||||
const ten = num % 10;
|
||||
const suffix = ['st', 'nd', 'rd'];
|
||||
return ten > 0 && ten < 4 ? suffix[ten - 1] : 'th';
|
||||
}
|
||||
};
|
||||
|
||||
const mlang = (str: string, lang: MultiLang): string => {
|
||||
const mlang = (str: string, lang: MultiLang) => {
|
||||
const mlLangs = ['en', 'ja'];
|
||||
// escape brackets inside of <input type="text" value="...">
|
||||
let text = str.replace(/(<input)([^>]*)(>)/isg, (whole, m1, m2, m3) => {
|
||||
let text = str.replace(/(<input)([^>]*)(>)/gis, (whole, m1, m2, m3) => {
|
||||
if (m2.match(/type=["']?(?:text|hidden)["']?/is)) {
|
||||
return m1 + m2.replace(/\[/g, '__ml[ml__') + m3;
|
||||
}
|
||||
return whole;
|
||||
});
|
||||
// escape brackets inside of <textarea></textarea>
|
||||
text = text.replace(/(<textarea[^>]*>)(.*)(<\/textarea>)/isg, (whole, m1, m2, m3) => {
|
||||
text = text.replace(/(<textarea[^>]*>)(.*)(<\/textarea>)/gis, (_whole, m1, m2, m3) => {
|
||||
return m1 + m2.replace(/\[/g, '__ml[ml__') + m3;
|
||||
});
|
||||
// simple pattern to strip selected lang_tags
|
||||
const re = new RegExp('\\[/?(?:[^\\]]+\\|)?' + lang + '(?:\\|[^\\]]+)?\\](?:<br />)?', 'g');
|
||||
const re = new RegExp(`\\[/?(?:[^\\]]+\\|)?${lang}(?:\\|[^\\]]+)?\\](?:<br />)?`, 'g');
|
||||
text = text.replace(re, '');
|
||||
// eliminate description between the other language tags.
|
||||
mlLangs.forEach((mlLang) => {
|
||||
if (mlLang !== lang) {
|
||||
const re = XRegExp('\\[(?:[^/][^\\]]+\\|)?' + mlLang + '(?:\\|[^\\]]+)?\\].*?\\[/(?:[^\\]]+\\|)?' + mlLang + '(?:\\|[^\\]]+)?\\](?:<br />)?', 'isg');
|
||||
const re = new RegExp(
|
||||
'\\[(?:[^/][^\\]]+\\|)?' +
|
||||
mlLang +
|
||||
'(?:\\|[^\\]]+)?\\].*?\\[/(?:[^\\]]+\\|)?' +
|
||||
mlLang +
|
||||
'(?:\\|[^\\]]+)?\\](?:<br />)?',
|
||||
'gis'
|
||||
);
|
||||
text = text.replace(re, (whole) => {
|
||||
return whole.match(/<\/table>/) ? whole : '';
|
||||
});
|
||||
}
|
||||
});
|
||||
// unescape brackets inside of <textarea></textarea>
|
||||
text = text.replace(/(<textarea[^>]*>)(.*)(<\/textarea>)/isg, (whole, m1, m2, m3) => {
|
||||
text = text.replace(/(<textarea[^>]*>)(.*)(<\/textarea>)/gis, (_whole, m1, m2, m3) => {
|
||||
return m1 + m2.replace(/__ml\[ml__/g, '[') + m3;
|
||||
});
|
||||
// unescape brackets inside of <input type="text" value="...">
|
||||
text = text.replace(/(<input)([^>]*)(>)/isg, (whole, m1, m2, m3) => {
|
||||
text = text.replace(/(<input)([^>]*)(>)/gis, (whole, m1, m2, m3) => {
|
||||
if (m2.match(/type=["']?(?:text|hidden)["']?/is)) {
|
||||
return m1 + m2.replace(/__ml\[ml__/g, '[') + m3;
|
||||
}
|
||||
return whole;
|
||||
});
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
const siteTitle = (lang: MultiLang): string => {
|
||||
const siteTitle = (lang: MultiLang) => {
|
||||
return mlang(Config.SITE_TITLE, lang);
|
||||
}
|
||||
};
|
||||
|
||||
const siteSlogan = (lang: MultiLang): string => {
|
||||
const siteSlogan = (lang: MultiLang) => {
|
||||
return mlang(Config.SITE_SLOGAN, lang);
|
||||
}
|
||||
};
|
||||
|
||||
const Functions = {
|
||||
escape,
|
||||
unescape,
|
||||
escape: escapeUrl,
|
||||
unescape: unescapeUrl,
|
||||
htmlspecialchars,
|
||||
camelCase,
|
||||
snakeCase,
|
||||
pascalCase,
|
||||
base64Encode,
|
||||
base64Decode,
|
||||
formatDate,
|
||||
ordinal,
|
||||
mlang,
|
||||
siteTitle,
|
||||
siteSlogan,
|
||||
};
|
||||
|
||||
export default Functions;
|
||||
export default Functions;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
@import-normalize;
|
||||
@import url("./common/assets/xoops.css");
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import 'react-app-polyfill/ie11';
|
||||
import 'react-app-polyfill/stable';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
// import * as serviceWorker from './serviceWorker';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
// serviceWorker.unregister();
|
||||
@@ -0,0 +1,19 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import 'sanitize.css';
|
||||
import 'sanitize.css/forms.css';
|
||||
import 'sanitize.css/typography.css';
|
||||
import './sanitize-overrides.css';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
|
||||
const container = document.getElementById('root');
|
||||
if (container === null) {
|
||||
throw new Error('Root container #root was not found in the document.');
|
||||
}
|
||||
|
||||
createRoot(container).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
);
|
||||
+35
-96
@@ -1,10 +1,9 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
import { useLocation } from 'react-router';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import useInitialized from '../common/lib/useInitialized';
|
||||
import type { MultiLang } from '../config';
|
||||
import './assets/pico_main.css';
|
||||
import PicoContext from './lib/PicoContext';
|
||||
import PicoUtils from './lib/PicoUtils';
|
||||
import PicoCategory from './PicoCategory';
|
||||
import PicoContent from './PicoContent';
|
||||
@@ -13,105 +12,45 @@ import PicoMenu from './PicoMenu';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
modulepath: string;
|
||||
showTitle: boolean;
|
||||
dirname: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
context: PicoContext | null;
|
||||
}
|
||||
|
||||
class Pico extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
context: null,
|
||||
};
|
||||
const Pico = (props: Props) => {
|
||||
const { lang, dirname } = props;
|
||||
const { pathname, search } = useLocation();
|
||||
const context = PicoUtils.getContext(dirname);
|
||||
const status = useInitialized(context.initialize);
|
||||
if (status === null) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
if (!status) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, modulepath } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.modulepath !== modulepath) {
|
||||
this.updatePage();
|
||||
const link = pathname.replace(new RegExp(`^${context.getModulePath()}/?`), '');
|
||||
const content = context.getContentByPath(link);
|
||||
if (content !== null) {
|
||||
return <PicoContent lang={lang} context={context} contentId={content.id} />;
|
||||
}
|
||||
const category = context.getCategoryByPath(link);
|
||||
if (category !== null) {
|
||||
return <PicoCategory lang={lang} context={context} catId={category.id} />;
|
||||
}
|
||||
if (link === '' || link === 'index.php') {
|
||||
const params = new URLSearchParams(search);
|
||||
const paramCatId = params.get('cat_id');
|
||||
if (paramCatId?.match(/^\d+$/)) {
|
||||
return <PicoCategory lang={lang} context={context} catId={parseInt(paramCatId, 10)} />;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
const { modulepath, showTitle } = this.props;
|
||||
const context = PicoUtils.getContext(modulepath, showTitle);
|
||||
const status = await context.initialize();
|
||||
if (this.isActive) {
|
||||
this.setState({ loading: false, context: status ? context : null });
|
||||
const paramContentId = params.get('content_id');
|
||||
if (paramContentId?.match(/^\d+$/)) {
|
||||
return <PicoContent lang={lang} context={context} contentId={parseInt(paramContentId, 10)} />;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, modulepath } = this.props;
|
||||
const { loading, context } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
if (params.get('page') === 'menu') {
|
||||
return <PicoMenu lang={lang} context={context} />;
|
||||
}
|
||||
if (context === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <Switch>
|
||||
<Route path={`${modulepath}`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const { pathname, search } = location;
|
||||
const regex = new RegExp(`^${modulepath}/?`);
|
||||
const link = pathname.replace(regex, '');
|
||||
const content = context.getContentByPath(link);
|
||||
if (content !== null) {
|
||||
return <PicoContent lang={lang} context={context} contentId={content.id} />;
|
||||
}
|
||||
const category = context.getCategoryByPath(link);
|
||||
if (category !== null) {
|
||||
return <PicoCategory lang={lang} context={context} catId={category.id} />;
|
||||
}
|
||||
if (link === '' || link === 'index.php') {
|
||||
const params = new URLSearchParams(search);
|
||||
const paramCatId = params.get('cat_id');
|
||||
if (paramCatId !== null && paramCatId.match(/^\d+$/)) {
|
||||
const catId = parseInt(paramCatId, 10);
|
||||
return <PicoCategory lang={lang} context={context} catId={catId} />;
|
||||
}
|
||||
const paramContentId = params.get('content_id');
|
||||
if (paramContentId !== null && paramContentId.match(/^\d+$/)) {
|
||||
const contentId = parseInt(paramContentId, 10);
|
||||
return <PicoContent lang={lang} context={context} contentId={contentId} />;
|
||||
}
|
||||
const paramPage = params.get('page');
|
||||
if (paramPage !== null && paramPage === 'menu') {
|
||||
return <PicoMenu lang={lang} context={context} />;
|
||||
}
|
||||
return <PicoIndex lang={lang} context={context} />;
|
||||
}
|
||||
return <PageNotFound lang={lang} />;
|
||||
}} />
|
||||
<Route exact path={modulepath} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const url = modulepath + '/index.php' + location.search + location.hash;
|
||||
return <Redirect to={url} />
|
||||
}} />
|
||||
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>;
|
||||
return <PicoIndex lang={lang} context={context} />;
|
||||
}
|
||||
}
|
||||
return <PageNotFound lang={lang} />;
|
||||
};
|
||||
|
||||
export default Pico;
|
||||
|
||||
+28
-16
@@ -1,10 +1,11 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Fragment } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import PicoContext, { PicoCategoryData, PicoContentData } from './lib/PicoContext';
|
||||
import type PicoContext from './lib/PicoContext';
|
||||
import type { PicoCategoryData, PicoContentData } from './lib/PicoContext';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -28,7 +29,10 @@ const PicoCategory = (props: Props) => {
|
||||
return (
|
||||
<div className="pico_container">
|
||||
<Helmet>
|
||||
<title>{Functions.mlang(category.title, lang)} - {Functions.mlang(pico.name, lang)} - {Functions.siteTitle(lang)}</title>
|
||||
<title>
|
||||
{Functions.mlang(category.title, lang)} - {Functions.mlang(pico.name, lang)} -{' '}
|
||||
{Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
{pico.show_breadcrumbs !== 0 && (
|
||||
<div className="pico_breadcrumbs">
|
||||
@@ -36,7 +40,9 @@ const PicoCategory = (props: Props) => {
|
||||
return (
|
||||
<Fragment key={parentCategory.id}>
|
||||
{idx > 0 && <> > </>}
|
||||
<Link key={parentCategory.id} to={context.getUrl(parentCategory.link)}>{Functions.mlang(parentCategory.title, lang)}</Link>
|
||||
<Link key={parentCategory.id} to={context.getUrl(parentCategory.link)}>
|
||||
{Functions.mlang(parentCategory.title, lang)}
|
||||
</Link>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
@@ -44,20 +50,20 @@ const PicoCategory = (props: Props) => {
|
||||
{Functions.mlang(category.title, lang)}
|
||||
</div>
|
||||
)}
|
||||
{catId === 0 && pico.message !== '' && (
|
||||
<p>{Functions.mlang(pico.message, lang)}</p>
|
||||
)}
|
||||
{catId === 0 && pico.message !== '' && <p>{Functions.mlang(pico.message, lang)}</p>}
|
||||
<h1>{Functions.mlang(category.title, lang)}</h1>
|
||||
{category.desc !== '' && (
|
||||
<p>{Functions.mlang(category.desc, lang)}</p>
|
||||
)}
|
||||
{category.desc !== '' && <p>{Functions.mlang(category.desc, lang)}</p>}
|
||||
{subCategories.length > 0 && (
|
||||
<>
|
||||
<h2>{Functions.mlang('[en]Subcageroies[/en][ja]サブカテゴリー[/ja]', lang)}</h2>
|
||||
{subCategories.map((subCategory: PicoCategoryData) => {
|
||||
return (
|
||||
<dl key={subCategory.id} className="pico_subcategory">
|
||||
<dt><Link to={context.getUrl(subCategory.link)}>{Functions.mlang(subCategory.title, lang)}</Link></dt>
|
||||
<dt>
|
||||
<Link to={context.getUrl(subCategory.link)}>
|
||||
{Functions.mlang(subCategory.title, lang)}
|
||||
</Link>
|
||||
</dt>
|
||||
<dd>{Functions.mlang(subCategory.desc, lang)}</dd>
|
||||
</dl>
|
||||
);
|
||||
@@ -69,7 +75,13 @@ const PicoCategory = (props: Props) => {
|
||||
<h2>{Functions.mlang('[en]Contents[/en][ja]コンテンツ[/ja]', lang)}</h2>
|
||||
<ul className="pico_list_contents">
|
||||
{contents.map((content: PicoContentData) => {
|
||||
return <li key={content.id}><Link to={context.getUrl(content.link)}>{Functions.mlang(content.title, lang)}</Link></li>;
|
||||
return (
|
||||
<li key={content.id}>
|
||||
<Link to={context.getUrl(content.link)}>
|
||||
{Functions.mlang(content.title, lang)}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
@@ -78,4 +90,4 @@ const PicoCategory = (props: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default PicoCategory;
|
||||
export default PicoCategory;
|
||||
|
||||
+61
-88
@@ -1,14 +1,14 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { HashLink } from 'react-router-hash-link';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import HashLink from '../common/lib/HashLink';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import XoopsCode from '../common/lib/XoopsCode';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import PicoContext, { PicoCategoryData, PicoPageData } from './lib/PicoContext';
|
||||
import type PicoContext from './lib/PicoContext';
|
||||
import type { PicoCategoryData, PicoPageData } from './lib/PicoContext';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -16,97 +16,70 @@ interface Props {
|
||||
contentId: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
interface LoadedPage {
|
||||
context: PicoContext;
|
||||
contentId: number;
|
||||
page: PicoPageData | null;
|
||||
}
|
||||
|
||||
class PicoContent extends Component<Props, State> {
|
||||
const PicoContent = (props: Props) => {
|
||||
const { lang, context, contentId } = props;
|
||||
const [loaded, setLoaded] = useState<LoadedPage | null>(null);
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
page: null,
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
context.getPage(contentId).then((page) => {
|
||||
if (active) {
|
||||
setLoaded({ context, contentId, page });
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
this.isActive = false;
|
||||
}
|
||||
}, [context, contentId]);
|
||||
|
||||
componentDidMount() {
|
||||
const { contentId } = this.props;
|
||||
this.isActive = true;
|
||||
this.updatePage(contentId);
|
||||
// A page loaded for the previous content must not be shown for this one.
|
||||
if (loaded === null || loaded.context !== context || loaded.contentId !== contentId) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { context, contentId } = this.props;
|
||||
const prevContext = prevProps.context;
|
||||
const prevContentId = prevProps.contentId;
|
||||
if (context !== prevContext || contentId !== prevContentId) {
|
||||
this.setState({ loading: true, page: null });
|
||||
this.updatePage(contentId);
|
||||
}
|
||||
const { page } = loaded;
|
||||
if (page === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
const pico = context.getModule();
|
||||
if (pico === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
async updatePage(contentId: number) {
|
||||
const { context } = this.props;
|
||||
const page = await context.getPage(contentId);
|
||||
if (this.isActive) {
|
||||
this.setState({ loading: false, page });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, context, contentId } = this.props;
|
||||
const { loading, page } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (page === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const pico = context.getModule();
|
||||
if (pico === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const categories = context.getContentCategery(contentId);
|
||||
return (
|
||||
<div className="pico_container">
|
||||
<Helmet>
|
||||
<title>{Functions.mlang(page.title, lang)} - {Functions.mlang(pico.name, lang)} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
{context.isShowTitle() && <>
|
||||
<h2>{Functions.mlang(pico.name, lang)}</h2>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
</>}
|
||||
{pico.show_breadcrumbs !== 0 && (
|
||||
<div className="pico_breadcrumbs">
|
||||
{categories.map((category: PicoCategoryData, idx: number) => {
|
||||
return (
|
||||
<Fragment key={category.id}>
|
||||
{idx > 0 && <> > </>}
|
||||
<Link key={category.id} to={context.getUrl(category.link)}>{Functions.mlang(category.title, lang)}</Link>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
> {Functions.mlang(page.title, lang)}
|
||||
</div>
|
||||
)}
|
||||
<div id="top_of_pico_body" className="pico_body">
|
||||
<XoopsCode lang={lang} text={page.content[lang]} dohtml={true} />
|
||||
</div>
|
||||
<div className="bottom_of_content_body">
|
||||
<HashLink to="#top_of_pico_body">{Functions.mlang('[en]Jump to the top[/en][ja]この記事の1行目に飛ぶ[/ja]', lang)}</HashLink>
|
||||
const categories = context.getContentCategery(contentId);
|
||||
return (
|
||||
<div className="pico_container">
|
||||
<Helmet>
|
||||
<title>
|
||||
{Functions.mlang(page.title, lang)} - {Functions.mlang(pico.name, lang)} -{' '}
|
||||
{Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
{pico.show_breadcrumbs !== 0 && (
|
||||
<div className="pico_breadcrumbs">
|
||||
{categories.map((category: PicoCategoryData, idx: number) => (
|
||||
<Fragment key={category.id}>
|
||||
{idx > 0 && <> > </>}
|
||||
<Link to={context.getUrl(category.link)}>{Functions.mlang(category.title, lang)}</Link>
|
||||
</Fragment>
|
||||
))}
|
||||
> {Functions.mlang(page.title, lang)}
|
||||
</div>
|
||||
)}
|
||||
<div id="top_of_pico_body" className="pico_body">
|
||||
<XoopsCode lang={lang} text={page.content[lang]} dohtml={true} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
<div className="bottom_of_content_body">
|
||||
<HashLink to="#top_of_pico_body">
|
||||
{Functions.mlang('[en]Jump to the top[/en][ja]この記事の1行目に飛ぶ[/ja]', lang)}
|
||||
</HashLink>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PicoContent;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import PicoContext from './lib/PicoContext';
|
||||
import type { MultiLang } from '../config';
|
||||
import type PicoContext from './lib/PicoContext';
|
||||
import PicoCategory from './PicoCategory';
|
||||
import PicoContent from './PicoContent';
|
||||
import PicoMenu from './PicoMenu';
|
||||
@@ -18,17 +17,16 @@ const PicoIndex = (props: Props) => {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
if (pico.show_menuinmoduletop !== 0) {
|
||||
return <PicoMenu lang={lang} context={context} />
|
||||
return <PicoMenu lang={lang} context={context} />;
|
||||
}
|
||||
if (pico.show_listasindex !== 0) {
|
||||
return <PicoCategory lang={lang} context={context} catId={0} />
|
||||
return <PicoCategory lang={lang} context={context} catId={0} />;
|
||||
}
|
||||
const content = context.getFirstContent(0);
|
||||
if (content === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <PicoContent lang={lang} context={context} contentId={content.id} />;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
export default PicoIndex;
|
||||
|
||||
+32
-20
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Link } from 'react-router';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import PicoContext, { PicoCategoryData, PicoContentData } from './lib/PicoContext';
|
||||
import type PicoContext from './lib/PicoContext';
|
||||
import type { PicoCategoryData, PicoContentData } from './lib/PicoContext';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -22,7 +22,7 @@ const PicoMenuCategory = (props: PicoMenuCategoryProps) => {
|
||||
const { lang, context, category, depth } = props;
|
||||
const subCategories = context.getSubCategories(category.id);
|
||||
const contents = context.getCategoryContents(category.id);
|
||||
const level = 'level' + depth;
|
||||
const level = `level${depth}`;
|
||||
const link = <Link to={context.getUrl(category.link)}>{Functions.mlang(category.title, lang)}</Link>;
|
||||
let title = <h6>{link}</h6>;
|
||||
switch (depth) {
|
||||
@@ -49,21 +49,32 @@ const PicoMenuCategory = (props: PicoMenuCategoryProps) => {
|
||||
<div className={level}>
|
||||
<ul className="pico_list_contents_in_menu">
|
||||
{contents.map((content: PicoContentData) => {
|
||||
return <li key={content.id}><Link to={context.getUrl(content.link)}>{Functions.mlang(content.title, lang)}</Link></li>;
|
||||
return (
|
||||
<li key={content.id}>
|
||||
<Link to={context.getUrl(content.link)}>
|
||||
{Functions.mlang(content.title, lang)}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
{subCategories.length > 0 && (
|
||||
<>
|
||||
{subCategories.map((subCategory: PicoCategoryData) => {
|
||||
return <PicoMenuCategory key={subCategory.id} lang={lang} context={context} category={subCategory} depth={depth + 1} />;
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
{subCategories.length > 0 &&
|
||||
subCategories.map((subCategory: PicoCategoryData) => {
|
||||
return (
|
||||
<PicoMenuCategory
|
||||
key={subCategory.id}
|
||||
lang={lang}
|
||||
context={context}
|
||||
category={subCategory}
|
||||
depth={depth + 1}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const PicoMenu = (props: Props) => {
|
||||
const { lang, context } = props;
|
||||
@@ -78,14 +89,15 @@ const PicoMenu = (props: Props) => {
|
||||
return (
|
||||
<div className="pico_container pico_menu">
|
||||
<Helmet>
|
||||
<title>{Functions.mlang('[en]Menu[/en][ja]メニュー[/ja]', lang)} - {Functions.mlang(pico.name, lang)} - {Functions.siteTitle(lang)}</title>
|
||||
<title>
|
||||
{Functions.mlang('[en]Menu[/en][ja]メニュー[/ja]', lang)} - {Functions.mlang(pico.name, lang)} -{' '}
|
||||
{Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
{pico.message !== '' && (
|
||||
<p>{Functions.mlang(pico.message, lang)}</p>
|
||||
)}
|
||||
{pico.message !== '' && <p>{Functions.mlang(pico.message, lang)}</p>}
|
||||
<PicoMenuCategory lang={lang} context={context} category={category} depth={1} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PicoMenu;
|
||||
export default PicoMenu;
|
||||
|
||||
@@ -103,7 +103,7 @@ table.pico_form_table td.pico_waiting {
|
||||
table.pico_form_table td,
|
||||
table.pico_form_table th {
|
||||
color: black;
|
||||
vertical-align: top !important;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
p.pico_submit {
|
||||
|
||||
+38
-39
@@ -1,5 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import ky from 'ky';
|
||||
import loki from 'lokijs';
|
||||
import Config from '../../config';
|
||||
|
||||
export interface PicoModuleData {
|
||||
name: string;
|
||||
@@ -40,37 +41,38 @@ export interface PicoPageData {
|
||||
content: {
|
||||
en: string;
|
||||
ja: string;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const PICO_CATEGORY_ID_ROOT = 65535;
|
||||
|
||||
class PicoContext {
|
||||
|
||||
private isInitialized: boolean;
|
||||
private modulepath: string;
|
||||
private showTitle: boolean;
|
||||
private dirname: string;
|
||||
private database: loki;
|
||||
private module: PicoModuleData | null;
|
||||
private categories: Collection<PicoCategoryData>;
|
||||
private contents: Collection<PicoContentData>;
|
||||
private initializing: Promise<boolean> | null;
|
||||
|
||||
constructor(modulepath: string, showTitle: boolean) {
|
||||
this.isInitialized = false;
|
||||
this.modulepath = modulepath;
|
||||
this.showTitle = showTitle;
|
||||
this.database = new loki(modulepath);
|
||||
constructor(dirname: string) {
|
||||
this.dirname = dirname;
|
||||
this.database = new loki(dirname);
|
||||
this.module = null;
|
||||
this.categories = this.database.addCollection('categories');
|
||||
this.contents = this.database.addCollection('contents');
|
||||
this.initializing = null;
|
||||
}
|
||||
|
||||
isShowTitle(): boolean {
|
||||
return this.showTitle;
|
||||
getModulePath(): string {
|
||||
return `/modules/${this.dirname}`;
|
||||
}
|
||||
|
||||
getUrl(path: string): string {
|
||||
return `${this.modulepath}/${path}`;
|
||||
return `${this.getModulePath()}/${path}`;
|
||||
}
|
||||
|
||||
private getDataUrl(path: string): URL {
|
||||
return new URL(Config.BASE_URL + this.getUrl(path), window.location.href);
|
||||
}
|
||||
|
||||
getModule(): PicoModuleData | null {
|
||||
@@ -127,35 +129,32 @@ class PicoContext {
|
||||
}
|
||||
|
||||
async getPage(contentId: number): Promise<PicoPageData | null> {
|
||||
let page: PicoPageData | null = null;
|
||||
try {
|
||||
const response = await axios.get(this.getUrl(`${contentId}.json`));
|
||||
page = response.data as PicoPageData;
|
||||
} catch (e) {
|
||||
// ignore
|
||||
return await ky.get(this.getDataUrl(`${contentId}.json`)).json<PicoPageData>();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
async initialize(): Promise<boolean> {
|
||||
if (!this.isInitialized) {
|
||||
try {
|
||||
const response = await axios.get(this.getUrl('index.json'));
|
||||
const index = response.data as PicoIndexData;
|
||||
this.module = index.module;
|
||||
index.categories.forEach((value) => {
|
||||
this.categories.insert(value);
|
||||
});
|
||||
index.contents.forEach((value) => {
|
||||
this.contents.insert(value);
|
||||
});
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
this.isInitialized = true;
|
||||
/**
|
||||
* Loads the module index once. Concurrent callers share the same request,
|
||||
* so the collections are never filled twice.
|
||||
*/
|
||||
initialize = (): Promise<boolean> => {
|
||||
if (this.initializing === null) {
|
||||
this.initializing = ky
|
||||
.get(this.getDataUrl('index.json'))
|
||||
.json<PicoIndexData>()
|
||||
.then((index) => {
|
||||
this.module = index.module;
|
||||
this.categories.insert(index.categories);
|
||||
this.contents.insert(index.contents);
|
||||
return true;
|
||||
})
|
||||
.catch(() => false);
|
||||
}
|
||||
return this.isInitialized;
|
||||
}
|
||||
return this.initializing;
|
||||
};
|
||||
}
|
||||
|
||||
export default PicoContext;
|
||||
export default PicoContext;
|
||||
|
||||
+11
-15
@@ -1,22 +1,18 @@
|
||||
import PicoContext from './PicoContext';
|
||||
|
||||
class PicoUtils {
|
||||
const contexts = new Map<string, PicoContext>();
|
||||
|
||||
private static contexts: Map<string, PicoContext> = new Map<string, PicoContext>();
|
||||
|
||||
private constructor() {
|
||||
// blcok to access from outside of this class
|
||||
const getContext = (dirname: string): PicoContext => {
|
||||
let context = contexts.get(dirname);
|
||||
if (typeof context === 'undefined') {
|
||||
context = new PicoContext(dirname);
|
||||
contexts.set(dirname, context);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
public static getContext(modulepath: string, showTitle: boolean): PicoContext {
|
||||
let context = this.contexts.get(modulepath);
|
||||
if (typeof context === 'undefined') {
|
||||
context = new PicoContext(modulepath, showTitle);
|
||||
this.contexts.set(modulepath, context);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
const PicoUtils = {
|
||||
getContext,
|
||||
};
|
||||
|
||||
export default PicoUtils;
|
||||
|
||||
+82
-71
@@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
import { Navigate, Route, Routes, useLocation } from 'react-router';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import useInitialized from '../common/lib/useInitialized';
|
||||
import type { MultiLang } from '../config';
|
||||
import PubmedPdfUtils from './lib/PubmedPdfUtils';
|
||||
import PubmedPdfAuthorsByInitial from './PubmedPdfAuthorsByInitial';
|
||||
import PubmedPdfIndex from './PubmedPdfIndex';
|
||||
import PubmedPdfJournalsByYear from './PubmedPdfJournalsByYear';
|
||||
@@ -14,77 +16,86 @@ interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const useParams = () => new URLSearchParams(useLocation().search);
|
||||
|
||||
const AuthorPage = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const params = useParams();
|
||||
const initial = params.get('initial');
|
||||
if (initial !== null && /^[A-Z]/.test(initial)) {
|
||||
return <PubmedPdfAuthorsByInitial lang={lang} initial={initial} />;
|
||||
}
|
||||
const authorIdStr = params.get('author');
|
||||
if (authorIdStr !== null && /^[0-9]+/.test(authorIdStr)) {
|
||||
const yearStr = params.get('year');
|
||||
const year = yearStr !== null && /^[0-9]+/.test(yearStr) ? parseInt(yearStr, 10) : 0;
|
||||
return <PubmedPdfPapersByAuthor lang={lang} authorId={parseInt(authorIdStr, 10)} year={year} />;
|
||||
}
|
||||
return <PageNotFound lang={lang} />;
|
||||
};
|
||||
|
||||
const YearPage = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const params = useParams();
|
||||
const yearStr = params.get('year');
|
||||
const journalIdStr = params.get('journal');
|
||||
if (yearStr === null || !/^[0-9]+/.test(yearStr) || (journalIdStr !== null && !/^[0-9]+/.test(journalIdStr))) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const year = parseInt(yearStr, 10);
|
||||
const journalId = journalIdStr !== null ? parseInt(journalIdStr, 10) : 0;
|
||||
if (journalId === 0) {
|
||||
return <PubmedPdfJournalsByYear lang={lang} year={year} />;
|
||||
}
|
||||
return <PubmedPdfPapersByYear lang={lang} year={year} journalId={journalId} />;
|
||||
};
|
||||
|
||||
const JournalPage = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const params = useParams();
|
||||
const journalIdStr = params.get('journal');
|
||||
const yearStr = params.get('year');
|
||||
if (journalIdStr === null || !/^[0-9]+/.test(journalIdStr) || (yearStr !== null && !/^[0-9]+/.test(yearStr))) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const year = yearStr !== null ? parseInt(yearStr, 10) : 0;
|
||||
return <PubmedPdfPapersByJournal lang={lang} journalId={parseInt(journalIdStr, 10)} year={year} />;
|
||||
};
|
||||
|
||||
const PaperDescPage = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const idStr = useParams().get('id');
|
||||
if (idStr === null || !/^[0-9]+/.test(idStr)) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return <PubmedPdfPaperDesc lang={lang} id={parseInt(idStr, 10)} />;
|
||||
};
|
||||
|
||||
const IndexRedirect = () => {
|
||||
const { search, hash } = useLocation();
|
||||
return <Navigate to={`${PubmedPdfUtils.getIndexUrl()}${search}${hash}`} replace />;
|
||||
};
|
||||
|
||||
const PubmedPdf = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const pathname = '/modules/PubMedPDF';
|
||||
const status = useInitialized(PubmedPdfUtils.initialize);
|
||||
if (status === null) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (!status) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Switch>
|
||||
<Route exact path={`${pathname}/index.php`} render={() => {
|
||||
return <PubmedPdfIndex lang={lang} />;
|
||||
}} />
|
||||
<Route exact path={`${pathname}/author.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const initial = params.get('initial');
|
||||
if (initial !== null && /^[A-Z]/.test(initial)) {
|
||||
return <PubmedPdfAuthorsByInitial lang={lang} initial={initial} />;
|
||||
}
|
||||
const authorIdStr = params.get('author');
|
||||
if (authorIdStr !== null && /^[0-9]+/.test(authorIdStr)) {
|
||||
const authorId = parseInt(authorIdStr);
|
||||
const yearStr = params.get('year');
|
||||
const year = (yearStr !== null && /^[0-9]+/.test(yearStr)) ? parseInt(yearStr) : 0;
|
||||
return <PubmedPdfPapersByAuthor lang={lang} authorId={authorId} year={year} />;
|
||||
}
|
||||
return <PageNotFound lang={lang} />
|
||||
}} />
|
||||
<Route exact path={`${pathname}/year.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const yearStr = params.get('year');
|
||||
const journalIdStr = params.get('journal');
|
||||
if (yearStr === null || !(/^[0-9]+/.test(yearStr)) || (journalIdStr !== null && !(/^[0-9]+/.test(journalIdStr)))) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const year = parseInt(yearStr);
|
||||
const journalId = journalIdStr !== null ? parseInt(journalIdStr) : 0;
|
||||
if (journalId === 0) {
|
||||
return <PubmedPdfJournalsByYear lang={lang} year={year} />;
|
||||
}
|
||||
return <PubmedPdfPapersByYear lang={lang} year={year} journalId={journalId} />;
|
||||
}} />
|
||||
<Route exact path={`${pathname}/journal.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const journalIdStr = params.get('journal');
|
||||
const yearStr = params.get('year');
|
||||
if (journalIdStr === null || !(/^[0-9]+/.test(journalIdStr)) || (yearStr !== null && !(/^[0-9]+/.test(yearStr)))) {
|
||||
return <PageNotFound lang={lang} />
|
||||
}
|
||||
const journalId = parseInt(journalIdStr);
|
||||
const year = yearStr !== null ? parseInt(yearStr) : 0;
|
||||
return <PubmedPdfPapersByJournal lang={lang} journalId={journalId} year={year} />;
|
||||
}} />
|
||||
<Route exact path={`${pathname}/paper_desc.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const idStr = params.get('id');
|
||||
if (idStr === null || !(/^[0-9]+/.test(idStr))) {
|
||||
return <PageNotFound lang={lang} />
|
||||
}
|
||||
const id = parseInt(idStr);
|
||||
return <PubmedPdfPaperDesc lang={lang} id={id} />;
|
||||
}} />
|
||||
<Route exact path={pathname} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const url = pathname + '/index.php' + location.search + location.hash;
|
||||
return <Redirect to={url} />;
|
||||
}} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
</>
|
||||
<Routes>
|
||||
<Route index element={<IndexRedirect />} />
|
||||
<Route path="index.php" element={<PubmedPdfIndex lang={lang} />} />
|
||||
<Route path="author.php" element={<AuthorPage lang={lang} />} />
|
||||
<Route path="year.php" element={<YearPage lang={lang} />} />
|
||||
<Route path="journal.php" element={<JournalPage lang={lang} />} />
|
||||
<Route path="paper_desc.php" element={<PaperDescPage lang={lang} />} />
|
||||
<Route path="*" element={<PageNotFound lang={lang} />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default PubmedPdf;
|
||||
|
||||
@@ -1,97 +1,53 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Link } from 'react-router';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/pubmedpdf.css';
|
||||
import PubmedPdfBreadcrumbs from './lib/PubmedPdfBreadcrumbs';
|
||||
import PubmedPdfUtils, { PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
|
||||
import PubmedPdfUtils, { type PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
initial: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class PubmedPdfAuthorsByInitial extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, initial } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.initial !== initial) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await PubmedPdfUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = PubmedPdfUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, initial } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if ('' === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const authors = PubmedPdfUtils.getAuthors(initial);
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: `a-${initial}`, name: initial, link: PubmedPdfUtils.getAuthorInitialUrl(initial) }
|
||||
];
|
||||
return (
|
||||
<div className="pubmedpdf" >
|
||||
<Helmet>
|
||||
<title>{mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
<div className="authors">
|
||||
<div className="author-group">
|
||||
{authors.map((author) => {
|
||||
return <span className="author" key={author.id}><Link to={PubmedPdfUtils.getAuthorUrl(author.id, 0)}>{Functions.mlang(author.name, lang)}</Link> ({author.count})</span>;
|
||||
})}
|
||||
</div>
|
||||
const PubmedPdfAuthorsByInitial = (props: Props) => {
|
||||
const { lang, initial } = props;
|
||||
const name = PubmedPdfUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const authors = PubmedPdfUtils.getAuthors(initial);
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: `a-${initial}`, name: initial, link: PubmedPdfUtils.getAuthorInitialUrl(initial) },
|
||||
];
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>
|
||||
{mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
<div className="authors">
|
||||
<div className="author-group">
|
||||
{authors.map((author) => {
|
||||
return (
|
||||
<span className="author" key={author.id}>
|
||||
<Link to={PubmedPdfUtils.getAuthorUrl(author.id, 0)}>
|
||||
{Functions.mlang(author.name, lang)}
|
||||
</Link>{' '}
|
||||
({author.count})
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubmedPdfAuthorsByInitial;
|
||||
|
||||
@@ -1,123 +1,94 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { HashLink } from 'react-router-hash-link';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Link } from 'react-router';
|
||||
import HashLink from '../common/lib/HashLink';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/pubmedpdf.css';
|
||||
import PubmedPdfUtils from './lib/PubmedPdfUtils';
|
||||
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class PubmedPdfIndex extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang } = this.props;
|
||||
if (prevProps.lang !== lang) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await PubmedPdfUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = PubmedPdfUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if ('' === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const authorInitials = PubmedPdfUtils.getAuthorInitials();
|
||||
const years = PubmedPdfUtils.getYears();
|
||||
const journalInitials = PubmedPdfUtils.getJournalInitials();
|
||||
return (
|
||||
<div className="pubmedpdf" >
|
||||
<Helmet>
|
||||
<title>{mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<nav className="header">
|
||||
<div className="count">
|
||||
<strong>{PubmedPdfUtils.getCountPapers()}</strong> papers registered
|
||||
</div>
|
||||
<div className="link">
|
||||
<a href="https://www.ncbi.nlm.nih.gov/pubmed" target="_blank" rel="noopener noreferrer">[PubMed]</a>
|
||||
</div>
|
||||
</nav>
|
||||
<h4 className="pt">{Functions.mlang('[en]Author[/en][ja]著者[/ja]', lang)}</h4>
|
||||
<div className="initials">
|
||||
{authorInitials.map((initial) => {
|
||||
return <span className="initial" key={initial}><Link to={PubmedPdfUtils.getAuthorInitialUrl(initial)}>{initial}</Link></span>
|
||||
})}
|
||||
const PubmedPdfIndex = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const name = PubmedPdfUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const authorInitials = PubmedPdfUtils.getAuthorInitials();
|
||||
const years = PubmedPdfUtils.getYears();
|
||||
const journalInitials = PubmedPdfUtils.getJournalInitials();
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>
|
||||
{mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<nav className="header">
|
||||
<div className="count">
|
||||
<strong>{PubmedPdfUtils.getCountPapers()}</strong> papers registered
|
||||
</div>
|
||||
<h4 className="pt">{Functions.mlang('[en]Year[/en][ja]発行年[/ja]', lang)}</h4>
|
||||
<div className="years">
|
||||
{years.map((year) => {
|
||||
return <div className="year" key={year.year}><Link to={PubmedPdfUtils.getYearUrl(year.year, 0)}>{year.year}</Link> ({year.count})</div>;
|
||||
})}
|
||||
<div className="link">
|
||||
<a href="https://www.ncbi.nlm.nih.gov/pubmed" target="_blank" rel="noopener noreferrer">
|
||||
[PubMed]
|
||||
</a>
|
||||
</div>
|
||||
<h4 className="pt">{Functions.mlang('[en]Journal/Book[/en][ja]ジャーナル/書籍[/ja]', lang)}</h4>
|
||||
<div className="journals">
|
||||
{journalInitials.map((initial) => {
|
||||
return <span className="initial" key={initial}><HashLink to={PubmedPdfUtils.getIndexUrl() + `#${initial}`}>{initial}</HashLink></span>
|
||||
})}
|
||||
{journalInitials.map((initial) => {
|
||||
const journals = PubmedPdfUtils.getJournalsByInitial(initial);
|
||||
return <div id={initial} className="journal-group" key={initial}>
|
||||
</nav>
|
||||
<h4 className="pt">{Functions.mlang('[en]Author[/en][ja]著者[/ja]', lang)}</h4>
|
||||
<div className="initials">
|
||||
{authorInitials.map((initial) => {
|
||||
return (
|
||||
<span className="initial" key={initial}>
|
||||
<Link to={PubmedPdfUtils.getAuthorInitialUrl(initial)}>{initial}</Link>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<h4 className="pt">{Functions.mlang('[en]Year[/en][ja]発行年[/ja]', lang)}</h4>
|
||||
<div className="years">
|
||||
{years.map((year) => {
|
||||
return (
|
||||
<div className="year" key={year.year}>
|
||||
<Link to={PubmedPdfUtils.getYearUrl(year.year, 0)}>{year.year}</Link> ({year.count})
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<h4 className="pt">{Functions.mlang('[en]Journal/Book[/en][ja]ジャーナル/書籍[/ja]', lang)}</h4>
|
||||
<div className="journals">
|
||||
{journalInitials.map((initial) => {
|
||||
return (
|
||||
<span className="initial" key={initial}>
|
||||
<HashLink to={`${PubmedPdfUtils.getIndexUrl()}#${initial}`}>{initial}</HashLink>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
{journalInitials.map((initial) => {
|
||||
const journals = PubmedPdfUtils.getJournalsByInitial(initial);
|
||||
return (
|
||||
<div id={initial} className="journal-group" key={initial}>
|
||||
<div className="title">{initial}</div>
|
||||
{journals.map((journal) => {
|
||||
const name = Functions.mlang(journal.name, lang);
|
||||
const nameShort = name.substr(0, 20) + (name.length > 20 ? '...' : '');
|
||||
return <div className="journal" key={journal.id}>
|
||||
<Link to={PubmedPdfUtils.getJournalUrl(journal.id, 0)} title={name}>{nameShort}</Link> ({journal.count})
|
||||
</div>;
|
||||
const nameShort = name.slice(0, 20) + (name.length > 20 ? '...' : '');
|
||||
return (
|
||||
<div className="journal" key={journal.id}>
|
||||
<Link to={PubmedPdfUtils.getJournalUrl(journal.id, 0)} title={name}>
|
||||
{nameShort}
|
||||
</Link>{' '}
|
||||
({journal.count})
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubmedPdfIndex;
|
||||
|
||||
@@ -1,103 +1,59 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Link } from 'react-router';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/pubmedpdf.css';
|
||||
import PubmedPdfBreadcrumbs from './lib/PubmedPdfBreadcrumbs';
|
||||
import PubmedPdfUtils, { PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
import PubmedPdfUtils, { type PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
year: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class PubmedPdfJournalsByYear extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: '',
|
||||
};
|
||||
const PubmedPdfJournalsByYear = (props: Props) => {
|
||||
const { lang, year } = props;
|
||||
const name = PubmedPdfUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const journals = PubmedPdfUtils.getJournalsByYear(year);
|
||||
if (journals.length === 0) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, year } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.year !== year) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await PubmedPdfUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = PubmedPdfUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, year } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if ('' === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const journals = PubmedPdfUtils.getJournalsByYear(year);
|
||||
if (journals.length === 0) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: `y.${year}`, name: `${year}`, link: PubmedPdfUtils.getYearUrl(year, 0) }
|
||||
];
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>{mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
<div className="journals">
|
||||
<div className="journal-group">
|
||||
{journals.map((journal) => {
|
||||
const name = Functions.mlang(journal.name, lang);
|
||||
const nameShort = name.substr(0, 20) + (name.length > 20 ? '...' : '');
|
||||
return <div className="journal" key={journal.id}>
|
||||
<Link to={PubmedPdfUtils.getYearUrl(year, journal.id)} title={name}>{nameShort}</Link> ({journal.count})
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: `y.${year}`, name: `${year}`, link: PubmedPdfUtils.getYearUrl(year, 0) },
|
||||
];
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>
|
||||
{mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
<div className="journals">
|
||||
<div className="journal-group">
|
||||
{journals.map((journal) => {
|
||||
const name = Functions.mlang(journal.name, lang);
|
||||
const nameShort = name.slice(0, 20) + (name.length > 20 ? '...' : '');
|
||||
return (
|
||||
<div className="journal" key={journal.id}>
|
||||
<Link to={PubmedPdfUtils.getYearUrl(year, journal.id)} title={name}>
|
||||
{nameShort}
|
||||
</Link>{' '}
|
||||
({journal.count})
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubmedPdfJournalsByYear;
|
||||
|
||||
@@ -1,123 +1,90 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Fragment } from 'react';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/pubmedpdf.css';
|
||||
import PubmedPdfUtils from './lib/PubmedPdfUtils';
|
||||
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
id: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class PubmedPdfPaperDesc extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: '',
|
||||
};
|
||||
const PubmedPdfPaperDesc = (props: Props) => {
|
||||
const { lang, id } = props;
|
||||
const name = PubmedPdfUtils.getName();
|
||||
const paper = PubmedPdfUtils.getPaper(id);
|
||||
if (paper === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang } = this.props;
|
||||
if (prevProps.lang !== lang) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await PubmedPdfUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = PubmedPdfUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, id } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if ('' === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const paper = PubmedPdfUtils.getPaper(id);
|
||||
if (paper === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const journal = PubmedPdfUtils.getJournal(paper.journal_id);
|
||||
const title = Functions.mlang(paper.title, lang);
|
||||
const journalName = journal !== null ? Functions.mlang(journal.name, lang) : '';
|
||||
const pubmedLink = paper.pmid > 0 ? <a href={`https://www.ncbi.nlm.nih.gov/pubmed/${paper.pmid}`} target="_blank" rel="noopener noreferrer">{paper.pmid}</a> : '';
|
||||
return (
|
||||
<div className="pubmedpdf" >
|
||||
<Helmet>
|
||||
<title>{title} - {mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<div className="paper_desc">
|
||||
<table className="outer">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="head">PMID</td>
|
||||
<td className="odd">{pubmedLink}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">Title</td>
|
||||
<td className="even">{title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">Author</td>
|
||||
<td className="odd">{paper.author_ids.map((authorId, index) => {
|
||||
const author = PubmedPdfUtils.getAuthor(authorId);
|
||||
return <Fragment key={authorId}>
|
||||
{index !== 0 && (paper.author_ids.length - 1 !== index ? <>, </> : <> and </>)}
|
||||
{author !== null && author.name}
|
||||
</Fragment>;
|
||||
})}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">Journal/Book</td>
|
||||
<td className="even">{journalName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">Year</td>
|
||||
<td className="odd">{paper.year}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">vol/pp</td>
|
||||
<td className="even">{paper.volume && `${paper.volume} / `}{paper.page}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const journal = PubmedPdfUtils.getJournal(paper.journal_id);
|
||||
const title = Functions.mlang(paper.title, lang);
|
||||
const journalName = journal !== null ? Functions.mlang(journal.name, lang) : '';
|
||||
const pubmedLink =
|
||||
paper.pmid > 0 ? (
|
||||
<a href={`https://www.ncbi.nlm.nih.gov/pubmed/${paper.pmid}`} target="_blank" rel="noopener noreferrer">
|
||||
{paper.pmid}
|
||||
</a>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>
|
||||
{title} - {mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<div className="paper_desc">
|
||||
<table className="outer">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="head">PMID</td>
|
||||
<td className="odd">{pubmedLink}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">Title</td>
|
||||
<td className="even">{title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">Author</td>
|
||||
<td className="odd">
|
||||
{paper.author_ids.map((authorId, index) => {
|
||||
const author = PubmedPdfUtils.getAuthor(authorId);
|
||||
return (
|
||||
<Fragment key={authorId}>
|
||||
{index !== 0 &&
|
||||
(paper.author_ids.length - 1 !== index ? <>, </> : <> and </>)}
|
||||
{author?.name}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">Journal/Book</td>
|
||||
<td className="even">{journalName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">Year</td>
|
||||
<td className="odd">{paper.year}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="head">vol/pp</td>
|
||||
<td className="even">
|
||||
{paper.volume && `${paper.volume} / `}
|
||||
{paper.page}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubmedPdfPaperDesc;
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Link } from 'react-router';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/pubmedpdf.css';
|
||||
import PubmedPdfBreadcrumbs from './lib/PubmedPdfBreadcrumbs';
|
||||
import PubmedPdfListItemPaper from './lib/PubmedPdfPaper';
|
||||
import PubmedPdfUtils, { PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
import PubmedPdfUtils, { type PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -17,95 +15,57 @@ interface Props {
|
||||
year: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class PubmedPdfPapersByAuthor extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: '',
|
||||
};
|
||||
const PubmedPdfPapersByAuthor = (props: Props) => {
|
||||
const { lang, authorId, year } = props;
|
||||
const name = PubmedPdfUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const author = PubmedPdfUtils.getAuthor(authorId);
|
||||
if (author === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
const initial = PubmedPdfUtils.getInitial(author.name);
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: `a-${initial}`, name: initial, link: PubmedPdfUtils.getAuthorInitialUrl(initial) },
|
||||
{
|
||||
id: `a.${authorId}`,
|
||||
name: Functions.mlang(author.name, lang),
|
||||
link: PubmedPdfUtils.getAuthorUrl(authorId, 0),
|
||||
},
|
||||
];
|
||||
if (year !== 0) {
|
||||
breadcrumbs.push({ id: `y.${year}`, name: `${year}`, link: PubmedPdfUtils.getAuthorUrl(authorId, year) });
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, authorId, year } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.authorId !== authorId || prevProps.year !== year) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await PubmedPdfUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = PubmedPdfUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, authorId, year } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if ('' === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const author = PubmedPdfUtils.getAuthor(authorId);
|
||||
if (author === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const initial = PubmedPdfUtils.getInitial(author.name);
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: `a-${initial}`, name: initial, link: PubmedPdfUtils.getAuthorInitialUrl(initial) },
|
||||
{ id: `a.${authorId}`, name: Functions.mlang(author.name, lang), link: PubmedPdfUtils.getAuthorUrl(authorId, 0) }
|
||||
];
|
||||
if (year !== 0) {
|
||||
breadcrumbs.push({ id: `y.${year}`, name: `${year}`, link: PubmedPdfUtils.getAuthorUrl(authorId, year) });
|
||||
}
|
||||
const papers = PubmedPdfUtils.getPapersByAuthorId(authorId, year);
|
||||
const years = PubmedPdfUtils.getYearsByPapers(papers);
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>{mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
<div className="years">
|
||||
{years.map((year) => {
|
||||
return <div className="year" key={year.year}><Link to={PubmedPdfUtils.getAuthorUrl(authorId, year.year)}>{year.year}</Link> ({year.count})</div>;
|
||||
})}
|
||||
</div>
|
||||
<hr />
|
||||
<div className="papers">
|
||||
{papers.map((paper) => {
|
||||
return <PubmedPdfListItemPaper key={paper.id} lang={lang} paper={paper} />;
|
||||
})}
|
||||
</div>
|
||||
const papers = PubmedPdfUtils.getPapersByAuthorId(authorId, year);
|
||||
const years = PubmedPdfUtils.getYearsByPapers(papers);
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>
|
||||
{mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
<div className="years">
|
||||
{years.map((year) => {
|
||||
return (
|
||||
<div className="year" key={year.year}>
|
||||
<Link to={PubmedPdfUtils.getAuthorUrl(authorId, year.year)}>{year.year}</Link> ({year.count}
|
||||
)
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
<hr />
|
||||
<div className="papers">
|
||||
{papers.map((paper) => {
|
||||
return <PubmedPdfListItemPaper key={paper.id} lang={lang} paper={paper} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubmedPdfPapersByAuthor;
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Link } from 'react-router';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/pubmedpdf.css';
|
||||
import PubmedPdfBreadcrumbs from './lib/PubmedPdfBreadcrumbs';
|
||||
import PubmedPdfListItemPaper from './lib/PubmedPdfPaper';
|
||||
import PubmedPdfUtils, { PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
import PubmedPdfUtils, { type PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -17,95 +15,59 @@ interface Props {
|
||||
year: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class PubmedPdfPapersByJournal extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: '',
|
||||
};
|
||||
const PubmedPdfPapersByJournal = (props: Props) => {
|
||||
const { lang, journalId, year } = props;
|
||||
const name = PubmedPdfUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const journal = PubmedPdfUtils.getJournal(journalId);
|
||||
if (journal === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{
|
||||
id: `j.${journal.id}`,
|
||||
name: Functions.mlang(journal.name, lang),
|
||||
link: PubmedPdfUtils.getJournalUrl(journalId, 0),
|
||||
},
|
||||
];
|
||||
if (year !== 0) {
|
||||
breadcrumbs.push({ id: `y.${year}`, name: `${year}`, link: PubmedPdfUtils.getJournalUrl(journalId, year) });
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, journalId, year } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.journalId !== journalId || prevProps.year !== year) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await PubmedPdfUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = PubmedPdfUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, journalId, year } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if ('' === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const journal = PubmedPdfUtils.getJournal(journalId);
|
||||
if (journal === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: `j.${journal.id}`, name: Functions.mlang(journal.name, lang), link: PubmedPdfUtils.getJournalUrl(journalId, 0) }
|
||||
];
|
||||
if (year !== 0) {
|
||||
breadcrumbs.push({ id: `y.${year}`, name: `${year}`, link: PubmedPdfUtils.getJournalUrl(journalId, year) });
|
||||
}
|
||||
const papers = PubmedPdfUtils.getPapersByJournalId(journalId, year);
|
||||
const years = PubmedPdfUtils.getYearsByPapers(papers);
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>{mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
{year === 0 && <>
|
||||
const papers = PubmedPdfUtils.getPapersByJournalId(journalId, year);
|
||||
const years = PubmedPdfUtils.getYearsByPapers(papers);
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>
|
||||
{mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
{year === 0 && (
|
||||
<>
|
||||
<div className="years">
|
||||
{years.map((year) => {
|
||||
return <div className="year" key={year.year}><Link to={PubmedPdfUtils.getJournalUrl(journalId, year.year)}>{year.year}</Link> ({year.count})</div>;
|
||||
return (
|
||||
<div className="year" key={year.year}>
|
||||
<Link to={PubmedPdfUtils.getJournalUrl(journalId, year.year)}>{year.year}</Link> (
|
||||
{year.count})
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<hr />
|
||||
</>}
|
||||
<div className="papers">
|
||||
{papers.map((paper) => {
|
||||
return <PubmedPdfListItemPaper key={paper.id} lang={lang} paper={paper} />;
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="papers">
|
||||
{papers.map((paper) => {
|
||||
return <PubmedPdfListItemPaper key={paper.id} lang={lang} paper={paper} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubmedPdfPapersByJournal;
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/pubmedpdf.css';
|
||||
import PubmedPdfBreadcrumbs from './lib/PubmedPdfBreadcrumbs';
|
||||
import PubmedPdfListItemPaper from './lib/PubmedPdfPaper';
|
||||
import PubmedPdfUtils, { PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
import PubmedPdfUtils, { type PubmedPdfBreadcrumbsData } from './lib/PubmedPdfUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -16,84 +14,41 @@ interface Props {
|
||||
journalId: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class PubmedPdfPapersByYear extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: '',
|
||||
};
|
||||
const PubmedPdfPapersByYear = (props: Props) => {
|
||||
const { lang, year, journalId } = props;
|
||||
const name = PubmedPdfUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const journal = PubmedPdfUtils.getJournal(journalId);
|
||||
if (journal === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, year, journalId } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.year !== year || prevProps.journalId !== journalId) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await PubmedPdfUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = PubmedPdfUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, year, journalId } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if ('' === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const journal = PubmedPdfUtils.getJournal(journalId);
|
||||
if (journal === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: `y.${year}`, name: `${year}`, link: PubmedPdfUtils.getYearUrl(year, 0) },
|
||||
{ id: `j.${journal.id}`, name: Functions.mlang(journal.name, lang), link: PubmedPdfUtils.getYearUrl(year, journalId) }
|
||||
];
|
||||
const papers = PubmedPdfUtils.getPapersByJournalId(journalId, year);
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>{mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
<div className="papers">
|
||||
{papers.map((paper) => {
|
||||
return <PubmedPdfListItemPaper key={paper.id} lang={lang} paper={paper} />;
|
||||
})}
|
||||
</div>
|
||||
const breadcrumbs: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: `y.${year}`, name: `${year}`, link: PubmedPdfUtils.getYearUrl(year, 0) },
|
||||
{
|
||||
id: `j.${journal.id}`,
|
||||
name: Functions.mlang(journal.name, lang),
|
||||
link: PubmedPdfUtils.getYearUrl(year, journalId),
|
||||
},
|
||||
];
|
||||
const papers = PubmedPdfUtils.getPapersByJournalId(journalId, year);
|
||||
return (
|
||||
<div className="pubmedpdf">
|
||||
<Helmet>
|
||||
<title>
|
||||
{mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<PubmedPdfBreadcrumbs lang={lang} breadcrumbs={breadcrumbs} />
|
||||
<hr />
|
||||
<div className="papers">
|
||||
{papers.map((paper) => {
|
||||
return <PubmedPdfListItemPaper key={paper.id} lang={lang} paper={paper} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubmedPdfPapersByYear;
|
||||
|
||||
@@ -2,54 +2,54 @@
|
||||
.pubmedpdf .years::after,
|
||||
.pubmedpdf .author-group::after,
|
||||
.pubmedpdf .journal-group::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.pubmedpdf nav.header .count {
|
||||
float: left;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.pubmedpdf nav.header .link {
|
||||
float: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.pubmedpdf h4.pt {
|
||||
border-bottom: 1px dashed #555;
|
||||
width: 100%;
|
||||
border-bottom: 1px dashed #555;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pubmedpdf span.initial:not(:first-child) {
|
||||
margin-left: 0.5em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.pubmedpdf .years .year {
|
||||
float: left;
|
||||
width: 12%;
|
||||
float: left;
|
||||
width: 12%;
|
||||
}
|
||||
|
||||
.pubmedpdf .journal-group .title {
|
||||
margin: 0.8em 0 0.3em;
|
||||
padding: 5px;
|
||||
background-color: #eff4f9;
|
||||
margin: 0.8em 0 0.3em;
|
||||
padding: 5px;
|
||||
background-color: #eff4f9;
|
||||
}
|
||||
.pubmedpdf .journal-group .journal {
|
||||
float: left;
|
||||
width: 33%;
|
||||
float: left;
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.pubmedpdf .author-group .author {
|
||||
float: left;
|
||||
width: 25%;
|
||||
float: left;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.pubmedpdf .papers .paper {
|
||||
margin: 0.5em 0;
|
||||
padding: 0.5em;
|
||||
background-color: #eff4f9;
|
||||
margin: 0.5em 0;
|
||||
padding: 0.5em;
|
||||
background-color: #eff4f9;
|
||||
}
|
||||
|
||||
.pubmedpdf .paper_desc table td.head {
|
||||
width: 150px;
|
||||
}
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
@@ -1,36 +1,41 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import PubmedPdfUtils, { PubmedPdfBreadcrumbsData } from './PubmedPdfUtils';
|
||||
import PubmedPdfUtils, { type PubmedPdfBreadcrumbsData } from './PubmedPdfUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
breadcrumbs: PubmedPdfBreadcrumbsData[];
|
||||
}
|
||||
|
||||
const PubmedPdfBreadcrumbs: React.FC<Props> = (props: Props) => {
|
||||
const PubmedPdfBreadcrumbs = (props: Props) => {
|
||||
const { lang, breadcrumbs } = props;
|
||||
const items: PubmedPdfBreadcrumbsData[] = [
|
||||
{ id: 'h', name: '[en]Home[/en][ja]ホーム[/ja]', link: '/' },
|
||||
{ id: 'm', name: PubmedPdfUtils.getName(), link: PubmedPdfUtils.getIndexUrl() }
|
||||
{ id: 'm', name: PubmedPdfUtils.getName(), link: PubmedPdfUtils.getIndexUrl() },
|
||||
];
|
||||
return <div className="breadcrumbs">
|
||||
{items.map((item, index) => {
|
||||
const name = Functions.mlang(item.name, lang);
|
||||
return <span key={item.id}>
|
||||
{index !== 0 && <> » </>}
|
||||
<Link to={item.link}>{name}</Link>
|
||||
</span>;
|
||||
})}
|
||||
{breadcrumbs.map((item, index) => {
|
||||
const name = Functions.mlang(item.name, lang);
|
||||
return <span key={item.id}>
|
||||
»
|
||||
{index === (breadcrumbs.length - 1) ? name : <Link to={item.link}>{name}</Link>}
|
||||
</span>;
|
||||
})}
|
||||
</div>;
|
||||
}
|
||||
return (
|
||||
<div className="breadcrumbs">
|
||||
{items.map((item, index) => {
|
||||
const name = Functions.mlang(item.name, lang);
|
||||
return (
|
||||
<span key={item.id}>
|
||||
{index !== 0 && <> » </>}
|
||||
<Link to={item.link}>{name}</Link>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
{breadcrumbs.map((item, index) => {
|
||||
const name = Functions.mlang(item.name, lang);
|
||||
return (
|
||||
<span key={item.id}>
|
||||
»
|
||||
{index === breadcrumbs.length - 1 ? name : <Link to={item.link}>{name}</Link>}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubmedPdfBreadcrumbs;
|
||||
|
||||
@@ -1,38 +1,55 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Fragment } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import PubmedPdfUtils, { PubmedPdfPaperData } from './PubmedPdfUtils';
|
||||
import PubmedPdfUtils, { type PubmedPdfPaperData } from './PubmedPdfUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
paper: PubmedPdfPaperData;
|
||||
}
|
||||
|
||||
const PubmedPdfListItemPaper: React.FC<Props> = (props: Props) => {
|
||||
const PubmedPdfListItemPaper = (props: Props) => {
|
||||
const { lang, paper } = props;
|
||||
const url = PubmedPdfUtils.getPaperDescUrl(paper.id);
|
||||
const journal = PubmedPdfUtils.getJournal(paper.journal_id);
|
||||
const title = Functions.mlang(paper.title, lang);
|
||||
const journalName = journal !== null ? Functions.mlang(journal.name, lang) : '';
|
||||
const pubmedLink = paper.pmid > 0 ? <> [<a href={`https://www.ncbi.nlm.nih.gov/pubmed/${paper.pmid}`} target="_blank" rel="noopener noreferrer">PubMed: {paper.pmid}</a>]</> : '';
|
||||
return <div className="paper">
|
||||
<strong>
|
||||
{paper.author_ids.map((authorId, index) => {
|
||||
const author = PubmedPdfUtils.getAuthor(authorId);
|
||||
return <Fragment key={authorId}>
|
||||
{index !== 0 && (paper.author_ids.length - 1 !== index ? <>, </> : <> and </>)}
|
||||
{author !== null && author.name}
|
||||
</Fragment>;
|
||||
})}
|
||||
({paper.year}).
|
||||
</strong><br />
|
||||
<Link to={url}>{title}</Link><br />
|
||||
<em>{journalName}</em>
|
||||
{paper.volume !== '' && `, ${paper.volume}`}
|
||||
{paper.page !== '' && `, ${paper.page}`}
|
||||
.{pubmedLink}
|
||||
</div>;
|
||||
}
|
||||
const pubmedLink =
|
||||
paper.pmid > 0 ? (
|
||||
<>
|
||||
{' '}
|
||||
[
|
||||
<a href={`https://www.ncbi.nlm.nih.gov/pubmed/${paper.pmid}`} target="_blank" rel="noopener noreferrer">
|
||||
PubMed: {paper.pmid}
|
||||
</a>
|
||||
]
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
return (
|
||||
<div className="paper">
|
||||
<strong>
|
||||
{paper.author_ids.map((authorId, index) => {
|
||||
const author = PubmedPdfUtils.getAuthor(authorId);
|
||||
return (
|
||||
<Fragment key={authorId}>
|
||||
{index !== 0 && (paper.author_ids.length - 1 !== index ? <>, </> : <> and </>)}
|
||||
{author?.name}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
({paper.year}).
|
||||
</strong>
|
||||
<br />
|
||||
<Link to={url}>{title}</Link>
|
||||
<br />
|
||||
<em>{journalName}</em>
|
||||
{paper.volume !== '' && `, ${paper.volume}`}
|
||||
{paper.page !== '' && `, ${paper.page}`}.{pubmedLink}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubmedPdfListItemPaper;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import ky from 'ky';
|
||||
import loki from 'lokijs';
|
||||
import Config from '../../config';
|
||||
|
||||
export interface PubmedPdfAuthorData {
|
||||
id: number;
|
||||
@@ -39,12 +40,10 @@ export interface PubmedPdfIndexData {
|
||||
export interface PubmedPdfBreadcrumbsData {
|
||||
id: string;
|
||||
name: string;
|
||||
link: string
|
||||
link: string;
|
||||
}
|
||||
|
||||
class PubmedPdfUtils {
|
||||
|
||||
private isInitialized: boolean;
|
||||
private pathname: string;
|
||||
private database: loki;
|
||||
private name: string;
|
||||
@@ -54,42 +53,43 @@ class PubmedPdfUtils {
|
||||
private years: Collection<PubmedPdfYearData>;
|
||||
private authorInitials: string[];
|
||||
private journalInitials: string[];
|
||||
private initializing: Promise<boolean> | null;
|
||||
|
||||
constructor(path: string) {
|
||||
this.isInitialized = false;
|
||||
this.pathname = path;
|
||||
this.database = new loki(path);
|
||||
this.name = '';
|
||||
this.authors = this.database.addCollection('authors');
|
||||
this.journals = this.database.addCollection('journals');
|
||||
this.papers = this.database.addCollection('papers');
|
||||
this.years = this.database.addCollection('years');;
|
||||
this.years = this.database.addCollection('years');
|
||||
this.authorInitials = [];
|
||||
this.journalInitials = [];
|
||||
this.initializing = null;
|
||||
}
|
||||
|
||||
getIndexUrl(): string {
|
||||
return this.pathname + '/index.php';
|
||||
return `${this.pathname}/index.php`;
|
||||
}
|
||||
|
||||
getAuthorUrl(id: number, year: number): string {
|
||||
return this.pathname + `/author.php?author=${id}` + (year !== 0 ? `&year=${year}` : '');
|
||||
return `${this.pathname}/author.php?author=${id}${year !== 0 ? `&year=${year}` : ''}`;
|
||||
}
|
||||
|
||||
getAuthorInitialUrl(initial: string): string {
|
||||
return this.pathname + `/author.php?initial=${initial}`;
|
||||
return `${this.pathname}/author.php?initial=${initial}`;
|
||||
}
|
||||
|
||||
getYearUrl(year: number, journalId: number): string {
|
||||
return this.pathname + `/year.php?year=${year}` + (journalId !== 0 ? `&journal=${journalId}` : '');
|
||||
return `${this.pathname}/year.php?year=${year}${journalId !== 0 ? `&journal=${journalId}` : ''}`;
|
||||
}
|
||||
|
||||
getJournalUrl(journalId: number, year: number): string {
|
||||
return this.pathname + `/journal.php?journal=${journalId}` + (year !== 0 ? `&year=${year}` : '');
|
||||
return `${this.pathname}/journal.php?journal=${journalId}${year !== 0 ? `&year=${year}` : ''}`;
|
||||
}
|
||||
|
||||
getPaperDescUrl(id: number): string {
|
||||
return this.pathname + `/paper_desc.php?id=${id}`;
|
||||
return `${this.pathname}/paper_desc.php?id=${id}`;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
@@ -128,23 +128,23 @@ class PubmedPdfUtils {
|
||||
}
|
||||
count[paper.year]++;
|
||||
});
|
||||
const years = Object.keys(count).map((key) => parseInt(key));
|
||||
years.sort();
|
||||
const years = Object.keys(count).map((key) => parseInt(key, 10));
|
||||
years.sort((a, b) => a - b);
|
||||
return years.map((year) => {
|
||||
return { year: year, count: count[year] };
|
||||
});
|
||||
}
|
||||
|
||||
getAuthors(initial?: string): PubmedPdfAuthorData[] {
|
||||
const filter: any = {};
|
||||
const filter: LokiQuery<PubmedPdfAuthorData & LokiObj> = {};
|
||||
if (typeof initial !== 'undefined') {
|
||||
filter.name = { '$regex': [`^${initial}`, 'i'] };
|
||||
filter.name = { $regex: [`^${initial}`, 'i'] };
|
||||
}
|
||||
return this.authors.chain().find(filter).simplesort('name').data();
|
||||
}
|
||||
|
||||
getJournalsByInitial(initial: string): PubmedPdfJournalData[] {
|
||||
const filter = { name: { '$regex': [`^${initial}`, 'i'] } };
|
||||
const filter = { name: { $regex: [`^${initial}`, 'i'] } };
|
||||
return this.journals.chain().find(filter).simplesort('name').data();
|
||||
}
|
||||
|
||||
@@ -159,18 +159,18 @@ class PubmedPdfUtils {
|
||||
count[paper.journal_id]++;
|
||||
});
|
||||
return Object.keys(count).map((journalIdStr) => {
|
||||
const journalId = parseInt(journalIdStr);
|
||||
const journalId = parseInt(journalIdStr, 10);
|
||||
const journal = this.getJournal(journalId);
|
||||
return {
|
||||
id: journalId,
|
||||
name: journal !== null ? journal.name : '',
|
||||
count: count[journalId]
|
||||
count: count[journalId],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getPapersByAuthorId(authorId: number, year: number): PubmedPdfPaperData[] {
|
||||
const filter: any = { author_ids: { '$contains': authorId } };
|
||||
const filter: LokiQuery<PubmedPdfPaperData & LokiObj> = { author_ids: { $contains: authorId } };
|
||||
if (year !== 0) {
|
||||
filter.year = year;
|
||||
}
|
||||
@@ -178,7 +178,7 @@ class PubmedPdfUtils {
|
||||
}
|
||||
|
||||
getPapersByJournalId(journalId: number, year: number): PubmedPdfPaperData[] {
|
||||
const filter: any = { journal_id: journalId };
|
||||
const filter: LokiQuery<PubmedPdfPaperData & LokiObj> = { journal_id: journalId };
|
||||
if (year !== 0) {
|
||||
filter.year = year;
|
||||
}
|
||||
@@ -189,51 +189,53 @@ class PubmedPdfUtils {
|
||||
return this.papers.count();
|
||||
}
|
||||
|
||||
async initialize(): Promise<boolean> {
|
||||
if (!this.isInitialized) {
|
||||
try {
|
||||
const url = this.pathname + '/index.json';
|
||||
const response = await axios.get(url);
|
||||
const index = response.data as PubmedPdfIndexData;
|
||||
this.name = index.name;
|
||||
index.author.forEach((value) => {
|
||||
const initial = this.getInitial(value.name);
|
||||
if (this.authorInitials.indexOf(initial) === -1) {
|
||||
this.authorInitials.push(initial);
|
||||
}
|
||||
this.authors.insert(value);
|
||||
});
|
||||
this.authorInitials.sort();
|
||||
index.journal.forEach((value) => {
|
||||
const initial = this.getInitial(value.name);
|
||||
if (this.journalInitials.indexOf(initial) === -1) {
|
||||
this.journalInitials.push(initial);
|
||||
}
|
||||
this.journals.insert(value);
|
||||
});
|
||||
this.journalInitials.sort();
|
||||
index.paper.forEach((value) => {
|
||||
const year = this.years.findOne({ year: value.year });
|
||||
if (year !== null) {
|
||||
year.count++;
|
||||
this.years.update(year);
|
||||
} else {
|
||||
this.years.insert({ year: value.year, count: 1 });
|
||||
}
|
||||
this.papers.insert(value);
|
||||
});
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
this.isInitialized = true;
|
||||
/**
|
||||
* Loads the module index once. Concurrent callers share the same request,
|
||||
* so the collections are never filled twice.
|
||||
*/
|
||||
initialize = (): Promise<boolean> => {
|
||||
if (this.initializing === null) {
|
||||
this.initializing = ky
|
||||
.get(new URL(`${Config.BASE_URL}${this.pathname}/index.json`, window.location.href))
|
||||
.json<PubmedPdfIndexData>()
|
||||
.then((index) => {
|
||||
this.name = index.name;
|
||||
index.author.forEach((value) => {
|
||||
const initial = this.getInitial(value.name);
|
||||
if (!this.authorInitials.includes(initial)) {
|
||||
this.authorInitials.push(initial);
|
||||
}
|
||||
this.authors.insert(value);
|
||||
});
|
||||
this.authorInitials.sort();
|
||||
index.journal.forEach((value) => {
|
||||
const initial = this.getInitial(value.name);
|
||||
if (!this.journalInitials.includes(initial)) {
|
||||
this.journalInitials.push(initial);
|
||||
}
|
||||
this.journals.insert(value);
|
||||
});
|
||||
this.journalInitials.sort();
|
||||
index.paper.forEach((value) => {
|
||||
const year = this.years.findOne({ year: value.year });
|
||||
if (year !== null) {
|
||||
year.count++;
|
||||
this.years.update(year);
|
||||
} else {
|
||||
this.years.insert({ year: value.year, count: 1 });
|
||||
}
|
||||
this.papers.insert(value);
|
||||
});
|
||||
return true;
|
||||
})
|
||||
.catch(() => false);
|
||||
}
|
||||
return this.isInitialized;
|
||||
}
|
||||
return this.initializing;
|
||||
};
|
||||
|
||||
getInitial(name: string) {
|
||||
return name.charAt(0).toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new PubmedPdfUtils('/modules/PubMedPDF');
|
||||
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
/// <reference types="react-scripts" />
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* The theme's widths and paddings are content-box measurements; sanitize.css
|
||||
* makes every element border-box.
|
||||
*/
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/* Controls and tables are laid out as border-box, which is what the theme
|
||||
assumes when it sizes them. */
|
||||
button,
|
||||
select,
|
||||
table,
|
||||
input[type="button"],
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
type Config = {
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||
};
|
||||
|
||||
export function register(config?: Config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(
|
||||
(process as { env: { [key: string]: string } }).env.PUBLIC_URL,
|
||||
window.location.href
|
||||
);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl: string, config?: Config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
||||
+76
-57
@@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
import { Navigate, Route, Routes, useLocation } from 'react-router';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import useInitialized from '../common/lib/useInitialized';
|
||||
import type { MultiLang } from '../config';
|
||||
import XwordsUtils from './lib/XwordsUtils';
|
||||
import XwordsCategory from './XwordsCategory';
|
||||
import XwordsEntry from './XwordsEntry';
|
||||
import XwordsIndex from './XwordsIndex';
|
||||
@@ -12,63 +14,80 @@ interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const useParams = () => new URLSearchParams(useLocation().search);
|
||||
|
||||
const intParam = (params: URLSearchParams, key: string, defaultValue: string): number => {
|
||||
const value = params.get(key) || defaultValue;
|
||||
return /^[0-9]+$/.test(value) ? parseInt(value, 10) : 0;
|
||||
};
|
||||
|
||||
const CategoryPage = (props: Props) => {
|
||||
const params = useParams();
|
||||
return (
|
||||
<XwordsCategory
|
||||
lang={props.lang}
|
||||
catId={intParam(params, 'categoryID', '0')}
|
||||
start={intParam(params, 'start', '0')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const LetterPage = (props: Props) => {
|
||||
const params = useParams();
|
||||
return (
|
||||
<XwordsLetter
|
||||
lang={props.lang}
|
||||
catId={intParam(params, 'categoryID', '0')}
|
||||
initial={params.get('init') || ''}
|
||||
start={intParam(params, 'start', '0')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const EntryPage = (props: Props) => {
|
||||
const params = useParams();
|
||||
return <XwordsEntry lang={props.lang} entryId={intParam(params, 'entryID', '0')} />;
|
||||
};
|
||||
|
||||
const SearchPage = (props: Props) => {
|
||||
const params = useParams();
|
||||
return (
|
||||
<XwordsSearch
|
||||
lang={props.lang}
|
||||
type={intParam(params, 'type', '4')}
|
||||
catId={intParam(params, 'catID', '0')}
|
||||
andor={params.get('andor') || 'OR'}
|
||||
term={params.get('term') || ''}
|
||||
start={intParam(params, 'start', '0')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const IndexRedirect = () => {
|
||||
const { search, hash } = useLocation();
|
||||
return <Navigate to={`${XwordsUtils.getIndexUrl()}${search}${hash}`} replace />;
|
||||
};
|
||||
|
||||
const Xwords = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const pathname = '/modules/xwords';
|
||||
const status = useInitialized(XwordsUtils.initialize);
|
||||
if (status === null) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (!status) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Switch>
|
||||
<Route exact path={`${pathname}/index.php`} render={() => {
|
||||
return <XwordsIndex lang={lang} />;
|
||||
}} />
|
||||
<Route exact path={`${pathname}/category.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const catIdStr = params.get('categoryID') || '0';
|
||||
const catId = /^[0-9]+$/.test(catIdStr) ? parseInt(catIdStr) : 0;
|
||||
const startStr = params.get('start') || '0';
|
||||
const start = /^[0-9]+$/.test(startStr) ? parseInt(startStr) : 0;
|
||||
return <XwordsCategory lang={lang} catId={catId} start={start} />;
|
||||
}} />
|
||||
<Route exact path={`${pathname}/letter.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const catIdStr = params.get('categoryID') || '0';
|
||||
const catId = /^[0-9]+$/.test(catIdStr) ? parseInt(catIdStr) : 0;
|
||||
const initial = params.get('init') || '';
|
||||
const startStr = params.get('start') || '0';
|
||||
const start = /^[0-9]+$/.test(startStr) ? parseInt(startStr) : 0;
|
||||
return <XwordsLetter lang={lang} catId={catId} initial={initial} start={start} />;
|
||||
}} />
|
||||
<Route exact path={`${pathname}/entry.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const entryIdStr = params.get('entryID') || '0';
|
||||
const entryId = /^[0-9]+$/.test(entryIdStr) ? parseInt(entryIdStr) : 0;
|
||||
return <XwordsEntry lang={lang} entryId={entryId} />;
|
||||
}} />
|
||||
<Route exact path={`${pathname}/search.php`} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const typeStr = params.get('type') || '4';
|
||||
const type = /^[0-9]+$/.test(typeStr) ? parseInt(typeStr) : 0;
|
||||
const catIdStr = params.get('catID') || '0';
|
||||
const catId = /^[0-9]+$/.test(catIdStr) ? parseInt(catIdStr) : 0;
|
||||
const andor = params.get('andor') || 'OR';
|
||||
const term = params.get('term') || '';
|
||||
const startStr = params.get('start') || '0';
|
||||
const start = /^[0-9]+$/.test(startStr) ? parseInt(startStr) : 0;
|
||||
return <XwordsSearch lang={lang} type={type} catId={catId} andor={andor} term={term} start={start} />
|
||||
}} />
|
||||
<Route exact path={pathname} render={(props: RouteComponentProps) => {
|
||||
const { location } = props;
|
||||
const url = pathname + '/index.php' + location.search + location.hash;
|
||||
return <Redirect to={url} />
|
||||
}} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
</>
|
||||
<Routes>
|
||||
<Route index element={<IndexRedirect />} />
|
||||
<Route path="index.php" element={<XwordsIndex lang={lang} />} />
|
||||
<Route path="category.php" element={<CategoryPage lang={lang} />} />
|
||||
<Route path="letter.php" element={<LetterPage lang={lang} />} />
|
||||
<Route path="entry.php" element={<EntryPage lang={lang} />} />
|
||||
<Route path="search.php" element={<SearchPage lang={lang} />} />
|
||||
<Route path="*" element={<PageNotFound lang={lang} />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Xwords;
|
||||
|
||||
+75
-105
@@ -1,10 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { Link } from 'react-router';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/xwords.css';
|
||||
import XwordsBreadcrumbs from './lib/XwordsBreadcrumbs';
|
||||
@@ -19,20 +17,35 @@ interface CategoryListProps {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const CategoryList: React.FC<CategoryListProps> = (props: CategoryListProps) => {
|
||||
const CategoryList = (props: CategoryListProps) => {
|
||||
const { lang } = props;
|
||||
const categories = XwordsUtils.getCategories();
|
||||
const title = Functions.mlang('[en]List of Categories[/en][ja]分類説明一覧[/ja]', lang);
|
||||
return <div className="categories">
|
||||
<h4 className="title">{title}</h4>
|
||||
{categories.map((category) => {
|
||||
return <div key={category.id} className="category">
|
||||
<Link className="link" to={XwordsUtils.getCategoryUrl(category.id)}>{Functions.mlang(XwordsUtils.getCategoryName(category.id), lang)}</Link>
|
||||
<span className="num">[{Functions.mlang('[en]Number of entries:[/en][ja]登録数[/ja]' + category.total + '[en]entries[/en][ja]語[/ja]', lang)}]</span>
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
return (
|
||||
<div className="categories">
|
||||
<h4 className="title">{title}</h4>
|
||||
{categories.map((category) => {
|
||||
return (
|
||||
<div key={category.id} className="category">
|
||||
<Link className="link" to={XwordsUtils.getCategoryUrl(category.id)}>
|
||||
{Functions.mlang(XwordsUtils.getCategoryName(category.id), lang)}
|
||||
</Link>
|
||||
<span className="num">
|
||||
[
|
||||
{Functions.mlang(
|
||||
'[en]Number of entries:[/en][ja]登録数[/ja]' +
|
||||
category.total +
|
||||
'[en]entries[/en][ja]語[/ja]',
|
||||
lang
|
||||
)}
|
||||
]
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -40,95 +53,52 @@ interface Props {
|
||||
start: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string | null;
|
||||
}
|
||||
|
||||
class XwordsCategory extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: null,
|
||||
};
|
||||
const XwordsCategory = (props: Props) => {
|
||||
const { lang, catId, start } = props;
|
||||
const name = XwordsUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const ctitle = Functions.mlang(XwordsUtils.getCategoryName(catId), lang);
|
||||
if (ctitle === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, catId, start } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.catId !== catId || prevProps.start !== start) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await XwordsUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = XwordsUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, catId, start } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (null === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const ctitle = Functions.mlang(XwordsUtils.getCategoryName(catId), lang);
|
||||
if (ctitle === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const title = catId === 0 ? Functions.mlang('[en]List of Categories[/en][ja]分類説明一覧[/ja]', lang) : Functions.mlang('[en]Category[/en][ja]用途別分類[/ja] : ', lang) + ctitle;
|
||||
const baseUrl = XwordsUtils.getCategoryUrl(catId);
|
||||
const result = {
|
||||
total: XwordsUtils.getCountEntries(catId, ''),
|
||||
entries: XwordsUtils.getEntries(catId, '', start)
|
||||
};
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>{title} - {mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} catId={catId} />
|
||||
<div className="row">
|
||||
<XwordsCategoryFieldset lang={lang} />
|
||||
</div>
|
||||
<div className="row">
|
||||
<XwordsInitialFieldset lang={lang} catId={catId} />
|
||||
</div>
|
||||
{catId === 0 && <CategoryList lang={lang} />}
|
||||
{catId !== 0 &&
|
||||
<>
|
||||
<div className="row two">
|
||||
<XwordsRecentlyFieldset lang={lang} catId={catId} />
|
||||
<XwordsRankingFieldset lang={lang} catId={catId} />
|
||||
</div>
|
||||
<h4 className="title">{Functions.mlang(XwordsUtils.getCategoryName(catId), lang)}</h4>
|
||||
<XwordsEntries lang={lang} baseUrl={baseUrl} result={result} start={start} />
|
||||
</>
|
||||
}
|
||||
const title =
|
||||
catId === 0
|
||||
? Functions.mlang('[en]List of Categories[/en][ja]分類説明一覧[/ja]', lang)
|
||||
: Functions.mlang('[en]Category[/en][ja]用途別分類[/ja] : ', lang) + ctitle;
|
||||
const baseUrl = XwordsUtils.getCategoryUrl(catId);
|
||||
const result = {
|
||||
total: XwordsUtils.getCountEntries(catId, ''),
|
||||
entries: XwordsUtils.getEntries(catId, '', start),
|
||||
};
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>
|
||||
{title} - {mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} catId={catId} />
|
||||
<div className="row">
|
||||
<XwordsCategoryFieldset lang={lang} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
<div className="row">
|
||||
<XwordsInitialFieldset lang={lang} catId={catId} />
|
||||
</div>
|
||||
{catId === 0 && <CategoryList lang={lang} />}
|
||||
{catId !== 0 && (
|
||||
<>
|
||||
<div className="row two">
|
||||
<XwordsRecentlyFieldset lang={lang} catId={catId} />
|
||||
<XwordsRankingFieldset lang={lang} catId={catId} />
|
||||
</div>
|
||||
<h4 className="title">{Functions.mlang(XwordsUtils.getCategoryName(catId), lang)}</h4>
|
||||
<XwordsEntries lang={lang} baseUrl={baseUrl} result={result} start={start} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsCategory;
|
||||
|
||||
+41
-86
@@ -1,9 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import { format } from 'date-fns';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/xwords.css';
|
||||
import XwordsBreadcrumbs from './lib/XwordsBreadcrumbs';
|
||||
@@ -16,92 +15,48 @@ interface Props {
|
||||
entryId: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string | null;
|
||||
}
|
||||
|
||||
class XwordsEntry extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: null,
|
||||
};
|
||||
const XwordsEntry = (props: Props) => {
|
||||
const { lang, entryId } = props;
|
||||
const name = XwordsUtils.getName();
|
||||
const entry = XwordsUtils.getEntry(entryId);
|
||||
if (null == entry) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const title = Functions.mlang(entry.term, lang);
|
||||
const date = format(new Date(entry.timestamp * 1000), lang === 'en' ? 'MMMM do, yyyy' : 'yyyy年M月d日');
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, entryId } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.entryId !== entryId) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await XwordsUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = XwordsUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, entryId } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (null === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const entry = XwordsUtils.getEntry(entryId);
|
||||
if (null == entry) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const title = Functions.mlang(entry.term, lang);
|
||||
const date = lang === 'en' ? Functions.formatDate(entry.timestamp, 'MMMM Do, YYYY') : Functions.formatDate(entry.timestamp, 'YYYY年M月D日');
|
||||
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>{title} - {mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} catId={entry.cat_id} initial={entry.initial} entryId={entry.id} />
|
||||
<div className="row">
|
||||
<XwordsCategoryFieldset lang={lang} />
|
||||
</div>
|
||||
<div className="row">
|
||||
<XwordsInitialFieldset lang={lang} catId={entry.cat_id} />
|
||||
</div>
|
||||
<div className="entry">
|
||||
<h4 className="title">{Functions.mlang(title, lang)}</h4>
|
||||
<div className="description">
|
||||
<div className="yomi">{Functions.mlang('[en]Yomi[/en][ja]読み[/ja] : ' + entry.yomi, lang)}</div>
|
||||
<div className="definition">{Functions.mlang(entry.definition, lang)}</div>
|
||||
<div className="note">
|
||||
<span className="date">{Functions.mlang('[en]Date[/en][ja]投稿日[/ja]', lang)} : {date}</span>
|
||||
</div>
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>
|
||||
{title} - {mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} catId={entry.cat_id} initial={entry.initial} entryId={entry.id} />
|
||||
<div className="row">
|
||||
<XwordsCategoryFieldset lang={lang} />
|
||||
</div>
|
||||
<div className="row">
|
||||
<XwordsInitialFieldset lang={lang} catId={entry.cat_id} />
|
||||
</div>
|
||||
<div className="entry">
|
||||
<h4 className="title">{Functions.mlang(title, lang)}</h4>
|
||||
<div className="description">
|
||||
<div className="yomi">{Functions.mlang(`[en]Yomi[/en][ja]読み[/ja] : ${entry.yomi}`, lang)}</div>
|
||||
<div className="definition">{Functions.mlang(entry.definition, lang)}</div>
|
||||
<div className="note">
|
||||
<span className="date">
|
||||
{Functions.mlang('[en]Date[/en][ja]投稿日[/ja]', lang)} : {date}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsEntry;
|
||||
|
||||
+33
-83
@@ -1,9 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/xwords.css';
|
||||
import XwordsBreadcrumbs from './lib/XwordsBreadcrumbs';
|
||||
@@ -15,87 +12,40 @@ import XwordsRecentlyFieldset from './lib/XwordsRecentlyFieldset';
|
||||
import XwordsSearchFieldset from './lib/XwordsSearchFieldset';
|
||||
import XwordsUtils from './lib/XwordsUtils';
|
||||
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string;
|
||||
}
|
||||
|
||||
class XwordsIndex extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang } = this.props;
|
||||
if (prevProps.lang !== lang) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await XwordsUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = XwordsUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if ('' === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>{mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} />
|
||||
<div className="row">
|
||||
<XwordsCategoryFieldset lang={lang} />
|
||||
</div>
|
||||
<div className="row">
|
||||
<XwordsInitialFieldset lang={lang} catId={0} />
|
||||
</div>
|
||||
<div className="row three">
|
||||
<XwordsRecentlyFieldset lang={lang} catId={0} />
|
||||
<XwordsRankingFieldset lang={lang} catId={0} />
|
||||
<XwordsRandomFieldset lang={lang} />
|
||||
</div>
|
||||
<div className="row">
|
||||
<XwordsSearchFieldset lang={lang} type={4} catId={0} andor="OR" term="" />
|
||||
</div>
|
||||
const XwordsIndex = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const name = XwordsUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>
|
||||
{mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{mtitle}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} />
|
||||
<div className="row">
|
||||
<XwordsCategoryFieldset lang={lang} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
<div className="row">
|
||||
<XwordsInitialFieldset lang={lang} catId={0} />
|
||||
</div>
|
||||
<div className="row three">
|
||||
<XwordsRecentlyFieldset lang={lang} catId={0} />
|
||||
<XwordsRankingFieldset lang={lang} catId={0} />
|
||||
<XwordsRandomFieldset lang={lang} />
|
||||
</div>
|
||||
<div className="row">
|
||||
<XwordsSearchFieldset lang={lang} type={4} catId={0} andor="OR" term="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsIndex;
|
||||
|
||||
+37
-85
@@ -1,9 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/xwords.css';
|
||||
import XwordsBreadcrumbs from './lib/XwordsBreadcrumbs';
|
||||
@@ -19,87 +17,41 @@ interface Props {
|
||||
start: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string | null;
|
||||
}
|
||||
|
||||
class XwordsLetter extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: null,
|
||||
};
|
||||
const XwordsLetter = (props: Props) => {
|
||||
const { lang, catId, initial, start } = props;
|
||||
const name = XwordsUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const ctitle = Functions.mlang(XwordsUtils.getCategoryName(catId), lang);
|
||||
const ititle = Functions.mlang(XwordsUtils.getInitialName(initial), lang);
|
||||
if (ctitle === '' || ititle === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, catId, initial } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.catId !== catId || prevProps.initial !== initial) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await XwordsUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const name = XwordsUtils.getName();
|
||||
this.setState({ loading: false, name });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, catId, initial, start } = this.props;
|
||||
const { loading, name } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (null === name) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const ctitle = Functions.mlang(XwordsUtils.getCategoryName(catId), lang);
|
||||
const ititle = Functions.mlang(XwordsUtils.getInitialName(initial), lang);
|
||||
if (ctitle === '' || ititle === '') {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const title = Functions.mlang('[en]Initial[/en][ja]頭文字[/ja] : ', lang) + ititle;
|
||||
const baseUrl = XwordsUtils.getLetterUrl(initial, catId);
|
||||
const result = {
|
||||
total: XwordsUtils.getCountEntries(catId, initial),
|
||||
entries: XwordsUtils.getEntries(catId, initial, start)
|
||||
};
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>{ititle} - {ctitle} - {mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} catId={catId} initial={initial} />
|
||||
<div className="row">
|
||||
<XwordsCategoryFieldset lang={lang} />
|
||||
</div>
|
||||
<div className="row">
|
||||
<XwordsInitialFieldset lang={lang} catId={catId} />
|
||||
</div>
|
||||
<h4 className="title">{Functions.mlang(XwordsUtils.getInitialName(initial), lang)}</h4>
|
||||
<XwordsEntries lang={lang} baseUrl={baseUrl} result={result} start={start} />
|
||||
const title = Functions.mlang('[en]Initial[/en][ja]頭文字[/ja] : ', lang) + ititle;
|
||||
const baseUrl = XwordsUtils.getLetterUrl(initial, catId);
|
||||
const result = {
|
||||
total: XwordsUtils.getCountEntries(catId, initial),
|
||||
entries: XwordsUtils.getEntries(catId, initial, start),
|
||||
};
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>
|
||||
{ititle} - {ctitle} - {mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} catId={catId} initial={initial} />
|
||||
<div className="row">
|
||||
<XwordsCategoryFieldset lang={lang} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
<div className="row">
|
||||
<XwordsInitialFieldset lang={lang} catId={catId} />
|
||||
</div>
|
||||
<h4 className="title">{Functions.mlang(XwordsUtils.getInitialName(initial), lang)}</h4>
|
||||
<XwordsEntries lang={lang} baseUrl={baseUrl} result={result} start={start} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsLetter;
|
||||
|
||||
+39
-83
@@ -1,15 +1,12 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Loading from '../common/lib/Loading';
|
||||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import NoticeSiteHasBeenArchived from '../common/lib/NoticeSiteHasBeenArchived';
|
||||
import PageNotFound from '../common/lib/PageNotFound';
|
||||
import { MultiLang } from '../config';
|
||||
import type { MultiLang } from '../config';
|
||||
import Functions from '../functions';
|
||||
import './assets/xwords.css';
|
||||
import XwordsBreadcrumbs from './lib/XwordsBreadcrumbs';
|
||||
import XwordsEntries from './lib/XwordsEntries';
|
||||
import XwordsSearchFieldset from './lib/XwordsSearchFieldset';
|
||||
import XwordsUtils, { XwordsSearchResult } from './lib/XwordsUtils';
|
||||
import XwordsUtils from './lib/XwordsUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -20,82 +17,41 @@ interface Props {
|
||||
start: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
name: string | null;
|
||||
result: XwordsSearchResult;
|
||||
}
|
||||
|
||||
class XwordsSearch extends Component<Props, State> {
|
||||
|
||||
private isActive: boolean;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.isActive = false;
|
||||
this.state = {
|
||||
loading: true,
|
||||
name: null,
|
||||
result: { total: 0, entries: [] },
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isActive = true;
|
||||
this.updatePage();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
const { lang, type, catId, andor, term, start } = this.props;
|
||||
if (prevProps.lang !== lang || prevProps.type !== type || prevProps.catId !== catId || prevProps.andor !== andor || prevProps.term !== term || prevProps.start !== start) {
|
||||
this.updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
async updatePage() {
|
||||
await XwordsUtils.initialize();
|
||||
if (this.isActive) {
|
||||
const { type, catId, andor, term, start } = this.props;
|
||||
const name = XwordsUtils.getName();
|
||||
const result = XwordsUtils.search(type, catId, andor, term, start);
|
||||
this.setState({ loading: false, name, result });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lang, type, catId, andor, term, start } = this.props;
|
||||
const { loading, name, result } = this.state;
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
if (name === null) {
|
||||
return <PageNotFound lang={lang} />;
|
||||
}
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const title = Functions.mlang('[en]Search[/en][ja]検索[/ja]', lang);
|
||||
const baseUrl = XwordsUtils.getSearchUrl(type, catId, andor, term);
|
||||
const etitle = result.total === 0 ?
|
||||
Functions.mlang('[en]No entries found.[/en][ja]該当なし。再度お試しください。[/ja]', lang) :
|
||||
Functions.mlang('[en]' + result.total + ' entries found.[/en][ja]上記の条件で ' + result.total + ' 件見つかりました。[/ja]', lang);
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>{title} - {mtitle} - {Functions.siteTitle(lang)}</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} isSearch={true} />
|
||||
<div className="row">
|
||||
<XwordsSearchFieldset lang={lang} type={type} catId={catId} andor={andor} term={term} />
|
||||
</div>
|
||||
<h4 className="title">{etitle}</h4>
|
||||
{result.total > 0 && <XwordsEntries lang={lang} baseUrl={baseUrl} result={result} start={start} />}
|
||||
const XwordsSearch = (props: Props) => {
|
||||
const { lang, type, catId, andor, term, start } = props;
|
||||
const name = XwordsUtils.getName();
|
||||
const mtitle = Functions.mlang(name, lang);
|
||||
const title = Functions.mlang('[en]Search[/en][ja]検索[/ja]', lang);
|
||||
const result = XwordsUtils.search(type, catId, andor, term, start);
|
||||
const baseUrl = XwordsUtils.getSearchUrl(type, catId, andor, term);
|
||||
const etitle =
|
||||
result.total === 0
|
||||
? Functions.mlang('[en]No entries found.[/en][ja]該当なし。再度お試しください。[/ja]', lang)
|
||||
: Functions.mlang(
|
||||
'[en]' +
|
||||
result.total +
|
||||
' entries found.[/en][ja]上記の条件で ' +
|
||||
result.total +
|
||||
' 件見つかりました。[/ja]',
|
||||
lang
|
||||
);
|
||||
return (
|
||||
<div className="xwords">
|
||||
<Helmet>
|
||||
<title>
|
||||
{title} - {mtitle} - {Functions.siteTitle(lang)}
|
||||
</title>
|
||||
</Helmet>
|
||||
<h3>{title}</h3>
|
||||
<NoticeSiteHasBeenArchived lang={lang} />
|
||||
<XwordsBreadcrumbs lang={lang} isSearch={true} />
|
||||
<div className="row">
|
||||
<XwordsSearchFieldset lang={lang} type={type} catId={catId} andor={andor} term={term} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
<h4 className="title">{etitle}</h4>
|
||||
{result.total > 0 && <XwordsEntries lang={lang} baseUrl={baseUrl} result={result} start={start} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsSearch;
|
||||
|
||||
@@ -1,148 +1,148 @@
|
||||
.xwords .row {
|
||||
font-size: 90%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item {
|
||||
border: 1px solid #777788;
|
||||
margin: 1em 0em;
|
||||
text-align: left;
|
||||
background-color: transparent;
|
||||
border: 1px solid #777788;
|
||||
margin: 1em 0em;
|
||||
text-align: left;
|
||||
background-color: transparent;
|
||||
}
|
||||
.xwords .row.two fieldset.item {
|
||||
display: inline-block;
|
||||
width: 49%;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
width: 49%;
|
||||
vertical-align: top;
|
||||
}
|
||||
.xwords .row.two fieldset.item:not(:last-child) {
|
||||
margin-right: 2%;
|
||||
margin-right: 2%;
|
||||
}
|
||||
.xwords .row.three fieldset.item {
|
||||
display: inline-block;
|
||||
width: 32%;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
width: 32%;
|
||||
vertical-align: top;
|
||||
}
|
||||
.xwords .row.three fieldset.item:not(:last-child) {
|
||||
margin-right: 2%;
|
||||
margin-right: 2%;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item legend.title {
|
||||
padding: 2px 5px;
|
||||
background: #2f5376;
|
||||
color: #ffffff;
|
||||
border-bottom: 1px solid #000000;
|
||||
padding: 2px 5px;
|
||||
background: #2f5376;
|
||||
color: #ffffff;
|
||||
border-bottom: 1px solid #000000;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item div.body {
|
||||
margin: 1em;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
margin: 1em;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item.category div.body span {
|
||||
margin: 0 5px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.xwords .row fieldset.item.initial div.body span {
|
||||
margin: 0 8px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item div.body span.info {
|
||||
font-size: 90%;
|
||||
color: #333333;
|
||||
font-size: 90%;
|
||||
color: #333333;
|
||||
}
|
||||
.xwords .row fieldset.item.recently div.body span.info {
|
||||
margin-left: 3px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
.xwords .row fieldset.item.ranking div.body span.info {
|
||||
margin-right: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item.search div.body dl {
|
||||
margin: 1em 0;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.xwords .row fieldset.item.search div.body dl dt {
|
||||
width: 25%;
|
||||
text-align: right;
|
||||
float: left;
|
||||
margin: 0 0.5em 0 0;
|
||||
width: 25%;
|
||||
text-align: right;
|
||||
float: left;
|
||||
margin: 0 0.5em 0 0;
|
||||
}
|
||||
.xwords .row fieldset.item.search div.body dl dd {
|
||||
text-align: left;
|
||||
clear: right;
|
||||
text-align: left;
|
||||
clear: right;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item div.body a {
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item div.body ul {
|
||||
text-align: left;
|
||||
padding-left: 0;
|
||||
text-align: left;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item div.body ul li {
|
||||
list-style-type: none;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.xwords .row fieldset.item div.body .term {
|
||||
text-align: left;
|
||||
background-color: #f5f7f7;
|
||||
text-align: left;
|
||||
background-color: #f5f7f7;
|
||||
}
|
||||
.xwords .row fieldset.item div.body .definition {
|
||||
text-align: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.xwords .breadcrumbs {
|
||||
font-size: 80%;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.xwords h4.title {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.xwords .categories .category {
|
||||
margin: 20px 0;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.xwords .categories .category .link {
|
||||
padding: 3px 1px;
|
||||
background-color: #cee1eb;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
padding: 3px 1px;
|
||||
background-color: #cee1eb;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.xwords .categories .category .num {
|
||||
margin-left: 10px;
|
||||
font-size: 0.6em;
|
||||
color: #cd5c5c;
|
||||
margin-left: 10px;
|
||||
font-size: 0.6em;
|
||||
color: #cd5c5c;
|
||||
}
|
||||
|
||||
.xwords .entries .entry {
|
||||
margin: 20px 0;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.xwords .entries .entry .link {
|
||||
padding: 3px 1px;
|
||||
background-color: #cee1eb;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
padding: 3px 1px;
|
||||
background-color: #cee1eb;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.xwords .entries .entry .description {
|
||||
margin: 0.5em 0 0.5em 1em;
|
||||
font-size: 0.9em;
|
||||
margin: 0.5em 0 0.5em 1em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.xwords .entries .entry .description .definition {
|
||||
margin-left: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.xwords > .entry > .description {
|
||||
margin: 1em;
|
||||
margin: 1em;
|
||||
}
|
||||
.xwords > .entry > .description .yomi {
|
||||
margin: 1em 0;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.xwords > .entry > .description .note {
|
||||
margin: 1em 0;
|
||||
text-align: right;
|
||||
font-size: 0.9em;
|
||||
margin: 1em 0;
|
||||
text-align: right;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.xwords .entries .pagenavi {
|
||||
text-align: right;
|
||||
font-size: 0.9em;
|
||||
text-align: right;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import XwordsUtils from './XwordsUtils';
|
||||
|
||||
@@ -12,33 +11,48 @@ interface Props {
|
||||
isSearch?: boolean;
|
||||
}
|
||||
|
||||
const XwordsBreadcrumbs: React.FC<Props> = (props: Props) => {
|
||||
const XwordsBreadcrumbs = (props: Props) => {
|
||||
const { lang, catId, initial, entryId, isSearch } = props;
|
||||
const items: { id: string, name: string, link: string }[] = [];
|
||||
const items: { id: string; name: string; link: string }[] = [];
|
||||
items.push({ id: 'h', name: '[en]Home[/en][ja]ホーム[/ja]', link: '/' });
|
||||
items.push({ id: 'm', name: XwordsUtils.getName(), link: XwordsUtils.getIndexUrl() });
|
||||
if (typeof isSearch !== undefined && isSearch) {
|
||||
if (isSearch) {
|
||||
items.push({ id: 's', name: '[en]Search[/en][ja]検索[/ja]', link: '/' });
|
||||
} else if (typeof catId !== 'undefined') {
|
||||
const cname = catId === 0 && typeof initial === 'undefined' ? '[en]List of Categories[/en][ja]分類説明一覧[/ja]' : XwordsUtils.getCategoryName(catId);
|
||||
items.push({ id: 'c' + catId, name: cname, link: XwordsUtils.getCategoryUrl(catId) });
|
||||
const cname =
|
||||
catId === 0 && typeof initial === 'undefined'
|
||||
? '[en]List of Categories[/en][ja]分類説明一覧[/ja]'
|
||||
: XwordsUtils.getCategoryName(catId);
|
||||
items.push({ id: `c${catId}`, name: cname, link: XwordsUtils.getCategoryUrl(catId) });
|
||||
if (typeof initial !== 'undefined') {
|
||||
items.push({ id: 'i' + initial, name: XwordsUtils.getInitialName(initial), link: XwordsUtils.getLetterUrl(initial, catId) });
|
||||
items.push({
|
||||
id: `i${initial}`,
|
||||
name: XwordsUtils.getInitialName(initial),
|
||||
link: XwordsUtils.getLetterUrl(initial, catId),
|
||||
});
|
||||
if (typeof entryId !== 'undefined') {
|
||||
items.push({ id: 'e' + entryId, name: XwordsUtils.getEntryName(entryId), link: XwordsUtils.getEntryUrl(entryId) })
|
||||
items.push({
|
||||
id: `e${entryId}`,
|
||||
name: XwordsUtils.getEntryName(entryId),
|
||||
link: XwordsUtils.getEntryUrl(entryId),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return <div className="breadcrumbs">
|
||||
{items.map((item, index) => {
|
||||
const name = Functions.mlang(item.name, lang);
|
||||
return <span key={item.id}>
|
||||
{index !== 0 && <> » </>}
|
||||
{index === (items.length - 1) ? name : <Link to={item.link}>{name}</Link>}
|
||||
</span>;
|
||||
})}
|
||||
</div>;
|
||||
}
|
||||
return (
|
||||
<div className="breadcrumbs">
|
||||
{items.map((item, index) => {
|
||||
const name = Functions.mlang(item.name, lang);
|
||||
return (
|
||||
<span key={item.id}>
|
||||
{index !== 0 && <> » </>}
|
||||
{index === items.length - 1 ? name : <Link to={item.link}>{name}</Link>}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsBreadcrumbs;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import XwordsUtils from './XwordsUtils';
|
||||
|
||||
@@ -8,21 +7,36 @@ interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const XwordsCategoryFieldset: React.FC<Props> = (props: Props) => {
|
||||
const XwordsCategoryFieldset = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const category = XwordsUtils.getCategories();
|
||||
return <fieldset className="item category">
|
||||
<legend className="title" > {Functions.mlang('[en]Categories[/en][ja]用途別分類(カテゴリー)別[/ja]', lang)}</legend>
|
||||
<div className="body">
|
||||
{category.map((category) => {
|
||||
const url = XwordsUtils.getCategoryUrl(category.id);
|
||||
const title = '[' + category.total + ']';
|
||||
const name = Functions.mlang(category.name, lang);
|
||||
return <span key={category.id} ><Link to={url} title={title}>{name}</Link></span>
|
||||
})}
|
||||
<span><Link to={XwordsUtils.getCategoryUrl(0)}>{Functions.mlang('[en]List of Categories[/en][ja]分類説明一覧[/ja]', lang)}</Link></span>
|
||||
</div>
|
||||
</fieldset>;
|
||||
}
|
||||
return (
|
||||
<fieldset className="item category">
|
||||
<legend className="title">
|
||||
{' '}
|
||||
{Functions.mlang('[en]Categories[/en][ja]用途別分類(カテゴリー)別[/ja]', lang)}
|
||||
</legend>
|
||||
<div className="body">
|
||||
{category.map((category) => {
|
||||
const url = XwordsUtils.getCategoryUrl(category.id);
|
||||
const title = `[${category.total}]`;
|
||||
const name = Functions.mlang(category.name, lang);
|
||||
return (
|
||||
<span key={category.id}>
|
||||
<Link to={url} title={title}>
|
||||
{name}
|
||||
</Link>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
<span>
|
||||
<Link to={XwordsUtils.getCategoryUrl(0)}>
|
||||
{Functions.mlang('[en]List of Categories[/en][ja]分類説明一覧[/ja]', lang)}
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsCategoryFieldset;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import XwordsEntriesPageNavi from './XwordsEntriesPageNavi';
|
||||
import XwordsUtils, { XwordsEntryData, XwordsSearchResult } from './XwordsUtils';
|
||||
import XwordsUtils, { type XwordsEntryData, type XwordsSearchResult } from './XwordsUtils';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -12,26 +11,39 @@ interface Props {
|
||||
start: number;
|
||||
}
|
||||
|
||||
const XwordsEntries: React.FC<Props> = (props: Props) => {
|
||||
const XwordsEntries = (props: Props) => {
|
||||
const { lang, baseUrl, result, start } = props;
|
||||
const definition = (entry: XwordsEntryData): string => {
|
||||
const text = Functions.mlang(entry.definition, lang);
|
||||
return text.substr(0, 50) + (text.length > 50 ? '...' : '');
|
||||
return text.slice(0, 50) + (text.length > 50 ? '...' : '');
|
||||
};
|
||||
return <div className="entries">
|
||||
{result.entries.length === 0 && <>{Functions.mlang('[en]No entries found[/en][ja]エントリが見つかりませんでした[/ja]', lang)}</>}
|
||||
<XwordsEntriesPageNavi lang={lang} baseUrl={baseUrl} total={result.total} start={start} />
|
||||
{result.entries.map((entry) => {
|
||||
return <div key={entry.id} className="entry">
|
||||
<div className="title"><Link className="link" to={XwordsUtils.getEntryUrl(entry.id)}>{Functions.mlang(entry.term, lang)}</Link></div>
|
||||
<div className="description">
|
||||
<span className="yomi">{Functions.mlang('[en]Yomi[/en][ja]読み[/ja] : ' + entry.yomi, lang)}</span>
|
||||
<span className="definition">{Functions.mlang('[en]Definition[/en][ja]解説[/ja] : ', lang) + definition(entry)}</span>
|
||||
</div>
|
||||
</div>;
|
||||
})}
|
||||
<XwordsEntriesPageNavi lang={lang} baseUrl={baseUrl} total={result.total} start={start} />
|
||||
</div>
|
||||
}
|
||||
return (
|
||||
<div className="entries">
|
||||
{result.entries.length === 0 &&
|
||||
Functions.mlang('[en]No entries found[/en][ja]エントリが見つかりませんでした[/ja]', lang)}
|
||||
<XwordsEntriesPageNavi lang={lang} baseUrl={baseUrl} total={result.total} start={start} />
|
||||
{result.entries.map((entry) => {
|
||||
return (
|
||||
<div key={entry.id} className="entry">
|
||||
<div className="title">
|
||||
<Link className="link" to={XwordsUtils.getEntryUrl(entry.id)}>
|
||||
{Functions.mlang(entry.term, lang)}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="description">
|
||||
<span className="yomi">
|
||||
{Functions.mlang(`[en]Yomi[/en][ja]読み[/ja] : ${entry.yomi}`, lang)}
|
||||
</span>
|
||||
<span className="definition">
|
||||
{Functions.mlang('[en]Definition[/en][ja]解説[/ja] : ', lang) + definition(entry)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<XwordsEntriesPageNavi lang={lang} baseUrl={baseUrl} total={result.total} start={start} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsEntries;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Fragment, type ReactNode } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
@@ -9,7 +9,7 @@ interface Props {
|
||||
start: number;
|
||||
}
|
||||
|
||||
const XwordsEntriesPageNavi: React.FC<Props> = (props: Props) => {
|
||||
const XwordsEntriesPageNavi = (props: Props) => {
|
||||
const { baseUrl, total, start } = props;
|
||||
if (total <= 20 || start > total) {
|
||||
return null;
|
||||
@@ -27,34 +27,39 @@ const XwordsEntriesPageNavi: React.FC<Props> = (props: Props) => {
|
||||
if (rangeMax > max - 1) {
|
||||
rangeMax = max - 1;
|
||||
}
|
||||
const link = (num: number, el?: JSX.Element) => {
|
||||
const link = (num: number, el?: ReactNode) => {
|
||||
const label = el || num;
|
||||
const url = baseUrl + (num - 1 > 0 ? ('&start=' + ((num - 1) * 20)) : '');
|
||||
return num === now ? <>({label})</> : <Link to={url}>{label}</Link>
|
||||
}
|
||||
const items: JSX.Element[] = [];
|
||||
const url = baseUrl + (num - 1 > 0 ? `&start=${(num - 1) * 20}` : '');
|
||||
return num === now ? <>({label})</> : <Link to={url}>{label}</Link>;
|
||||
};
|
||||
const items: { key: string; node: ReactNode }[] = [];
|
||||
if (prev !== 0) {
|
||||
items.push(link(prev, <>«</>));
|
||||
items.push({ key: 'prev', node: link(prev, <>«</>) });
|
||||
}
|
||||
items.push(link(min));
|
||||
items.push({ key: `${min}`, node: link(min) });
|
||||
if (now - min > 4) {
|
||||
items.push(<>...</>);
|
||||
items.push({ key: 'gap-prev', node: <>...</> });
|
||||
}
|
||||
for (let range = rangeMin; range <= rangeMax; range++) {
|
||||
items.push(link(range));
|
||||
items.push({ key: `${range}`, node: link(range) });
|
||||
}
|
||||
if (max - now > 4) {
|
||||
items.push(<>...</>);
|
||||
items.push({ key: 'gap-next', node: <>...</> });
|
||||
}
|
||||
items.push(link(max));
|
||||
items.push({ key: `${max}`, node: link(max) });
|
||||
if (next !== 0) {
|
||||
items.push(link(next, <>»</>));
|
||||
items.push({ key: 'next', node: link(next, <>»</>) });
|
||||
}
|
||||
return <nav className="pagenavi">
|
||||
{items.map((el, index) => {
|
||||
return <Fragment key={index}>{index !== 0 && <> </>}{el}</Fragment>;
|
||||
})}
|
||||
</nav>;
|
||||
}
|
||||
return (
|
||||
<nav className="pagenavi">
|
||||
{items.map((item, index) => (
|
||||
<Fragment key={item.key}>
|
||||
{index !== 0 && <> </>}
|
||||
{item.node}
|
||||
</Fragment>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsEntriesPageNavi;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import XwordsUtils, { XWORDS_INITIAL_KEYS } from './XwordsUtils';
|
||||
|
||||
@@ -9,30 +8,36 @@ interface Props {
|
||||
catId: number;
|
||||
}
|
||||
|
||||
const XwordsInitialFieldset: React.FC<Props> = (props: Props) => {
|
||||
const XwordsInitialFieldset = (props: Props) => {
|
||||
const { lang, catId } = props;
|
||||
const initial = XwordsUtils.getCountInitials(catId);
|
||||
const initialsAlpha: JSX.Element[] = [];
|
||||
const initialsKana: JSX.Element[] = [];
|
||||
XWORDS_INITIAL_KEYS.forEach((value, idx) => {
|
||||
const initials = XWORDS_INITIAL_KEYS.map((value) => {
|
||||
const num = initial[value.key];
|
||||
const url = XwordsUtils.getLetterUrl(value.key, catId);
|
||||
const label = Functions.mlang(value.label, lang);
|
||||
const name = num === 0 ? label : <Link to={url} title={`[ ${num} ]`}>{label}</Link>;
|
||||
const el = <span key={value.key}>{name}</span>;
|
||||
if (idx < 26) {
|
||||
initialsAlpha.push(el);
|
||||
} else {
|
||||
initialsKana.push(el);
|
||||
}
|
||||
return (
|
||||
<span key={value.key}>
|
||||
{num === 0 ? (
|
||||
label
|
||||
) : (
|
||||
<Link to={url} title={`[ ${num} ]`}>
|
||||
{label}
|
||||
</Link>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
return <fieldset className="item initial">
|
||||
<legend className="title">{Functions.mlang('[en]Initials[/en][ja]頭文字(イニシャル)別[/ja]', lang)}</legend>
|
||||
<div className="body">
|
||||
<div>{initialsAlpha}</div>
|
||||
<div>{initialsKana}</div>
|
||||
</div>
|
||||
</fieldset>;
|
||||
}
|
||||
return (
|
||||
<fieldset className="item initial">
|
||||
<legend className="title">
|
||||
{Functions.mlang('[en]Initials[/en][ja]頭文字(イニシャル)別[/ja]', lang)}
|
||||
</legend>
|
||||
<div className="body">
|
||||
<div>{initials.slice(0, 26)}</div>
|
||||
<div>{initials.slice(26)}</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsInitialFieldset;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import XwordsUtils from './XwordsUtils';
|
||||
|
||||
@@ -8,30 +7,43 @@ interface Props {
|
||||
lang: MultiLang;
|
||||
}
|
||||
|
||||
const XwordsRandomFieldset: React.FC<Props> = (props: Props) => {
|
||||
const XwordsRandomFieldset = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const categories = XwordsUtils.getCategories();
|
||||
const random = XwordsUtils.getRandomEntry();
|
||||
if (random === null) {
|
||||
return null;
|
||||
}
|
||||
const randomCategory = categories.find((category) => { return category.id === random.cat_id; });
|
||||
const randomCategory = categories.find((category) => {
|
||||
return category.id === random.cat_id;
|
||||
});
|
||||
if (typeof randomCategory === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
const randomDefinition = Functions.mlang(random.definition, lang);
|
||||
|
||||
return <fieldset className="item random">
|
||||
<legend className="title">{Functions.mlang('[en]Random[/en][ja]ランダム[/ja]', lang)}</legend>
|
||||
<div className="body">
|
||||
<div className="term">
|
||||
<Link to={XwordsUtils.getEntryUrl(random.id)}>{Functions.mlang(random.term, lang)}</Link> <span className="info">[{Functions.mlang('[en]Category[/en][ja]用途[/ja]', lang)}: <Link to={XwordsUtils.getCategoryUrl(randomCategory.id)}>{Functions.mlang(randomCategory.name, lang)}</Link>]</span>
|
||||
return (
|
||||
<fieldset className="item random">
|
||||
<legend className="title">{Functions.mlang('[en]Random[/en][ja]ランダム[/ja]', lang)}</legend>
|
||||
<div className="body">
|
||||
<div className="term">
|
||||
<Link to={XwordsUtils.getEntryUrl(random.id)}>{Functions.mlang(random.term, lang)}</Link>{' '}
|
||||
<span className="info">
|
||||
[{Functions.mlang('[en]Category[/en][ja]用途[/ja]', lang)}:{' '}
|
||||
<Link to={XwordsUtils.getCategoryUrl(randomCategory.id)}>
|
||||
{Functions.mlang(randomCategory.name, lang)}
|
||||
</Link>
|
||||
]
|
||||
</span>
|
||||
</div>
|
||||
<div className="definition">
|
||||
<span className="info">{Functions.mlang('[en]Definition: [/en][ja]解説: [/ja]', lang)}</span>
|
||||
{randomDefinition.slice(0, 30)}
|
||||
{randomDefinition.length > 30 && <>...</>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="definition">
|
||||
<span className="info">{Functions.mlang('[en]Definition: [/en][ja]解説: [/ja]', lang)}</span>{randomDefinition.substr(0, 30)}{randomDefinition.length > 30 && <>...</>}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>;
|
||||
}
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsRandomFieldset;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import XwordsUtils from './XwordsUtils';
|
||||
|
||||
@@ -9,20 +8,30 @@ interface Props {
|
||||
catId: number;
|
||||
}
|
||||
|
||||
const XwordsRankingFieldset: React.FC<Props> = (props: Props) => {
|
||||
const XwordsRankingFieldset = (props: Props) => {
|
||||
const { lang, catId } = props;
|
||||
const ranking = XwordsUtils.getRankingEntries(catId);
|
||||
const title = catId === 0 ? '[en]Ranking[/en][ja]ランキング[/ja]' : '[en]Ranking in this Category[/en][ja]この分野でのランキング[/ja]';
|
||||
return <fieldset className="item ranking">
|
||||
<legend className="title">{Functions.mlang(title, lang)}</legend>
|
||||
<div className="body">
|
||||
<ul>
|
||||
{ranking.map((entry, idx) => {
|
||||
return <li key={entry.id}><span className="info">{(idx + 1) + Functions.ordinal(idx + 1)}</span> <Link to={XwordsUtils.getEntryUrl(entry.id)}>{Functions.mlang(entry.term, lang)}</Link></li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>;
|
||||
}
|
||||
const title =
|
||||
catId === 0
|
||||
? '[en]Ranking[/en][ja]ランキング[/ja]'
|
||||
: '[en]Ranking in this Category[/en][ja]この分野でのランキング[/ja]';
|
||||
return (
|
||||
<fieldset className="item ranking">
|
||||
<legend className="title">{Functions.mlang(title, lang)}</legend>
|
||||
<div className="body">
|
||||
<ul>
|
||||
{ranking.map((entry, idx) => {
|
||||
return (
|
||||
<li key={entry.id}>
|
||||
<span className="info">{idx + 1 + Functions.ordinal(idx + 1)}</span>{' '}
|
||||
<Link to={XwordsUtils.getEntryUrl(entry.id)}>{Functions.mlang(entry.term, lang)}</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsRankingFieldset;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { format } from 'date-fns';
|
||||
import { Link } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import XwordsUtils from './XwordsUtils';
|
||||
|
||||
@@ -9,21 +9,31 @@ interface Props {
|
||||
catId: number;
|
||||
}
|
||||
|
||||
const XwordsRecentlyFieldset: React.FC<Props> = (props: Props) => {
|
||||
const XwordsRecentlyFieldset = (props: Props) => {
|
||||
const { lang, catId } = props;
|
||||
const recent = XwordsUtils.getRecentlyEntries(catId);
|
||||
const title = catId === 0 ? '[en]New Arrival[/en][ja]新着[/ja]' : '[en]New Arrival in this Category[/en][ja]この分野での新着[/ja]';
|
||||
return <fieldset className="item recently">
|
||||
<legend className="title">{Functions.mlang(title, lang)}</legend>
|
||||
<div className="body">
|
||||
<ul>
|
||||
{recent.map((entry) => {
|
||||
const date = Functions.formatDate(entry.timestamp, 'YYYY/M/D');
|
||||
return <li key={entry.id}><Link to={XwordsUtils.getEntryUrl(entry.id)}>{Functions.mlang(entry.term, lang)}</Link> <span className="info">[{date}]</span></li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>;
|
||||
}
|
||||
const title =
|
||||
catId === 0
|
||||
? '[en]New Arrival[/en][ja]新着[/ja]'
|
||||
: '[en]New Arrival in this Category[/en][ja]この分野での新着[/ja]';
|
||||
return (
|
||||
<fieldset className="item recently">
|
||||
<legend className="title">{Functions.mlang(title, lang)}</legend>
|
||||
<div className="body">
|
||||
<ul>
|
||||
{recent.map((entry) => {
|
||||
const date = format(new Date(entry.timestamp * 1000), 'yyyy/M/d');
|
||||
return (
|
||||
<li key={entry.id}>
|
||||
<Link to={XwordsUtils.getEntryUrl(entry.id)}>{Functions.mlang(entry.term, lang)}</Link>{' '}
|
||||
<span className="info">[{date}]</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
export default XwordsRecentlyFieldset;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { ChangeEvent, Component, FormEvent } from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { MultiLang } from '../../config';
|
||||
import { type FormEvent, useState } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import type { MultiLang } from '../../config';
|
||||
import Functions from '../../functions';
|
||||
import XwordsUtils from './XwordsUtils';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
interface Props {
|
||||
lang: MultiLang;
|
||||
type: number;
|
||||
catId: number;
|
||||
@@ -12,99 +12,92 @@ interface Props extends RouteComponentProps {
|
||||
term: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
type: number;
|
||||
catId: number;
|
||||
andor: string;
|
||||
term: string;
|
||||
}
|
||||
const XwordsSearchFieldset = (props: Props) => {
|
||||
const { lang } = props;
|
||||
const navigate = useNavigate();
|
||||
const [type, setType] = useState(props.type);
|
||||
const [catId, setCatId] = useState(props.catId);
|
||||
const [andor, setAndor] = useState(props.andor);
|
||||
const [term, setTerm] = useState(props.term);
|
||||
|
||||
class XwordsSearchFieldset extends Component<Props, State> {
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const { type, catId, andor, term } = props;
|
||||
this.state = { type, catId, andor, term };
|
||||
this.handleChangeType = this.handleChangeType.bind(this);
|
||||
this.handleChangeCatId = this.handleChangeCatId.bind(this);
|
||||
this.handleChangeAndOr = this.handleChangeAndOr.bind(this);
|
||||
this.handleChangeTerm = this.handleChangeTerm.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
handleChangeType(event: ChangeEvent<HTMLSelectElement>) {
|
||||
const type = parseInt(event.target.value.trim());
|
||||
this.setState({ type });
|
||||
}
|
||||
|
||||
handleChangeCatId(event: ChangeEvent<HTMLSelectElement>) {
|
||||
const catId = parseInt(event.target.value.trim());
|
||||
this.setState({ catId });
|
||||
}
|
||||
|
||||
handleChangeAndOr(event: ChangeEvent<HTMLSelectElement>) {
|
||||
const andor = event.target.value.trim();
|
||||
this.setState({ andor });
|
||||
}
|
||||
|
||||
handleChangeTerm(event: ChangeEvent<HTMLInputElement>) {
|
||||
const term = event.target.value;
|
||||
this.setState({ term });
|
||||
}
|
||||
|
||||
handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||
const { type, catId, andor, term } = this.state;
|
||||
const term_ = term.trim();
|
||||
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const term_ = term.trim();
|
||||
if (term_.length > 0) {
|
||||
const url = XwordsUtils.getSearchUrl(type, catId, andor, term_);
|
||||
this.props.history.push(url);
|
||||
void navigate(XwordsUtils.getSearchUrl(type, catId, andor, term_));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { lang } = this.props;
|
||||
const { type, catId, andor, term } = this.state;
|
||||
const categories = XwordsUtils.getCategories();
|
||||
return <fieldset className="item search">
|
||||
const categories = XwordsUtils.getCategories();
|
||||
return (
|
||||
<fieldset className="item search">
|
||||
<legend className="title">{Functions.mlang('[en]Search[/en][ja]検索[/ja]', lang)}</legend>
|
||||
<div className="body">
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<dl>
|
||||
<dt>{Functions.mlang('[en]Target[/en][ja]検索対象[/ja]', lang)}</dt>
|
||||
<dd>
|
||||
<select onChange={this.handleChangeType} value={type}>
|
||||
<select onChange={(event) => setType(parseInt(event.target.value.trim(), 10))} value={type}>
|
||||
<option value="1">{Functions.mlang('[en]Term[/en][ja]見出し語[/ja]', lang)}</option>
|
||||
<option value="2">{Functions.mlang('[en]Yomi[/en][ja]読み方[/ja]', lang)}</option>
|
||||
<option value="3">{Functions.mlang('[en]Definition[/en][ja]説明文[/ja]', lang)}</option>
|
||||
<option value="4">{Functions.mlang('[en]Term+Yomi+Definition[/en][ja]見出し語+読み方+説明文[/ja]', lang)}</option>
|
||||
<option value="4">
|
||||
{Functions.mlang(
|
||||
'[en]Term+Yomi+Definition[/en][ja]見出し語+読み方+説明文[/ja]',
|
||||
lang
|
||||
)}
|
||||
</option>
|
||||
</select>
|
||||
</dd>
|
||||
<dt>{Functions.mlang('[en]Category[/en][ja]用途別分類[/ja]', lang)}</dt>
|
||||
<dd>
|
||||
<select onChange={this.handleChangeCatId} value={catId}>
|
||||
<option value="0">{Functions.mlang('[en]ANY[/en][ja]全分類(カテゴリー)[/ja]', lang)}</option>
|
||||
{categories.map((value) => <option key={value.id} value={value.id}>{Functions.mlang(value.name, lang)}</option>)}
|
||||
<select
|
||||
onChange={(event) => setCatId(parseInt(event.target.value.trim(), 10))}
|
||||
value={catId}
|
||||
>
|
||||
<option value="0">
|
||||
{Functions.mlang('[en]ANY[/en][ja]全分類(カテゴリー)[/ja]', lang)}
|
||||
</option>
|
||||
{categories.map((value) => (
|
||||
<option key={value.id} value={value.id}>
|
||||
{Functions.mlang(value.name, lang)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</dd>
|
||||
<dt>{Functions.mlang('[en]Condition[/en][ja]検索の種類[/ja]', lang)}</dt>
|
||||
<dd>
|
||||
<select onChange={this.handleChangeAndOr} value={andor}>
|
||||
<option value="AND">{Functions.mlang('[en]AND[/en][ja]すべての語に一致するもの[/ja]', lang)}</option>
|
||||
<option value="OR">{Functions.mlang('[en]OR[/en][ja]いずれかの語に一致するもの[/ja]', lang)}</option>
|
||||
<option value="EXACT">{Functions.mlang('[en]EXACT[/en][ja]スペースも含め1語として[/ja]', lang)}</option>
|
||||
<select onChange={(event) => setAndor(event.target.value.trim())} value={andor}>
|
||||
<option value="AND">
|
||||
{Functions.mlang('[en]AND[/en][ja]すべての語に一致するもの[/ja]', lang)}
|
||||
</option>
|
||||
<option value="OR">
|
||||
{Functions.mlang('[en]OR[/en][ja]いずれかの語に一致するもの[/ja]', lang)}
|
||||
</option>
|
||||
<option value="EXACT">
|
||||
{Functions.mlang('[en]EXACT[/en][ja]スペースも含め1語として[/ja]', lang)}
|
||||
</option>
|
||||
</select>
|
||||
</dd>
|
||||
<dt>{Functions.mlang('[en]Word[/en][ja]検索語[/ja]', lang)}</dt>
|
||||
<dd>
|
||||
<input type="text" name="term" onChange={this.handleChangeTerm} value={term} />
|
||||
<input className="formButton" type="submit" value={Functions.mlang('[en]Search![/en][ja]検索![/ja]', lang)} />
|
||||
<input
|
||||
type="text"
|
||||
name="term"
|
||||
onChange={(event) => setTerm(event.target.value)}
|
||||
value={term}
|
||||
/>
|
||||
<input
|
||||
className="formButton"
|
||||
type="submit"
|
||||
value={Functions.mlang('[en]Search![/en][ja]検索![/ja]', lang)}
|
||||
/>
|
||||
</dd>
|
||||
</dl>
|
||||
</form>
|
||||
</div>
|
||||
</fieldset>;
|
||||
}
|
||||
}
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(XwordsSearchFieldset);
|
||||
export default XwordsSearchFieldset;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user