first commit.

This commit is contained in:
2022-06-08 13:15:52 +09:00
commit 3ed26dd828
238 changed files with 16732 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
.database {
margin: 0;
}
.database :global(.list) {
width: 100%;
}
.database :global(.listTable) {
width: 100%;
border-collapse: separate;
border-spacing: 5px;
}
.database :global(.listTable .listIcon),
.database :global(.listTable .listExtra) {
vertical-align: middle;
text-align: center;
width: 65px;
line-height: 0;
}
.database :global(.itemDetail) {
border-collapse: separate;
border-spacing: 1px;
}
.database :global(.itemDetail .head) {
width: 30%;
}
.database :global(.itemDetail .readme),
.database :global(.itemDetail .rights) {
background-color: #ffffff;
width: 100%;
max-height: 200px;
overflow: auto;
}
.database :global(.advancedSearch .head) {
width: 30%;
}
.database :global(.advancedSearch .search) {
text-align: center;
margin: 10px;
}
.database :global(.advancedSearch .itemtype) {
margin-bottom: 5px;
}
.database :global(.advancedSearch .itemtype .itemtypeName),
.database :global(.advancedSearch .itemtype .itemtypeFields) {
border-collapse: separate;
border-spacing: 1px;
}
.database :global(.advancedSearch .itemtype .itemtypeName th) {
padding: 5px;
}
.database :global(.advancedSearch .fieldDateLabel) {
display: inline-block;
width: 50px;
}
.database :global(.advancedSearch .fieldDate select),
.database :global(.advancedSearch .fieldDate input) {
margin: 0 5px 0 0;
}

92
src/database/Database.tsx Normal file
View File

@@ -0,0 +1,92 @@
import React from "react";
import { Helmet } from "react-helmet-async";
import { Route, Routes, useLocation } from "react-router-dom";
import PageNotFound from "../common/lib/PageNotFound";
import { BrainAtlasType, MultiLang } from "../config";
import Functions from "../functions";
import styles from "./Database.module.css";
import DatabaseAdvancedSearch from "./DatabaseAdvancedSearch";
import DatabaseDetailItem from "./DatabaseDetailItem";
import DatabaseSearchByAdvancedKeyword from "./DatabaseSearchByAdvancedKeyword";
import DatabaseSearchByIndexId from "./DatabaseSearchByIndexId";
import DatabaseSearchByItemType from "./DatabaseSearchByItemType";
import DatabaseSearchByKeyword from "./DatabaseSearchByKeyword";
import DatabaseTop from "./DatabaseTop";
import { INDEX_ID_PUBLIC } from "./lib/IndexUtil";
interface Props {
lang: MultiLang;
type: BrainAtlasType;
}
const ItemDetail: React.FC<Props> = (props: Props) => {
const { lang, type } = props;
const location = useLocation();
const params = new URLSearchParams(location.search);
const itemId_ = params.get("item_id") || "";
const itemId = /^\d+$/.test(itemId_) ? parseInt(itemId_, 10) : 0;
const doi = params.get("id") || "";
return <DatabaseDetailItem lang={lang} id={itemId} doi={doi} type={type} />;
};
const ItemList: React.FC<Props> = (props: Props) => {
const { lang, type } = props;
const location = useLocation();
const params = new URLSearchParams(location.search);
const id = params.get("index_id") || "";
const indexId = /^\d+$/.test(id) ? parseInt(id, 10) : INDEX_ID_PUBLIC;
return <DatabaseSearchByIndexId lang={lang} type={type} indexId={indexId} />;
};
const ItemSelect: React.FC<Props> = (props: Props) => {
const { lang, type } = props;
const location = useLocation();
const params = new URLSearchParams(location.search);
const op = params.get("op") || "";
switch (op) {
case "itemtypesearch": {
const searchItemtype = params.get("search_itemtype") || "";
const match = searchItemtype.match(/^xnp([a-z]+)$/);
const itemType = match !== null ? match[1] : "";
return <DatabaseSearchByItemType lang={lang} itemType={itemType} subItemType="" type={type} />;
}
case "itemsubtypesearch": {
const searchItemtype = params.get("search_itemtype") || "";
const match = searchItemtype.match(/^xnp([a-z]+)$/);
const itemType = match !== null ? match[1] : "";
const subItemtype = params.get("search_subitemtype") || "";
return <DatabaseSearchByItemType lang={lang} itemType={itemType} subItemType={subItemtype} type={type} />;
}
case "quicksearch": {
return <DatabaseSearchByKeyword lang={lang} type={type} />;
}
case "advanced": {
return <DatabaseSearchByAdvancedKeyword lang={lang} type={type} />;
}
}
return <PageNotFound lang={lang} />;
};
const Database: React.FC<Props> = (props: Props) => {
const { lang, type } = props;
return (
<div className={styles.database}>
<Helmet>
<title>
{Functions.mlang("[en]Database[/en][ja]データベース[/ja]", lang)} - {Functions.siteTitle(lang)}
</title>
</Helmet>
<Routes>
<Route index element={<DatabaseTop lang={lang} type={type} />} />
<Route path="index.php" element={<DatabaseTop lang={lang} type={type} />} />
<Route path="detail.php" element={<ItemDetail lang={lang} type={type} />} />
<Route path="listitem.php" element={<ItemList lang={lang} type={type} />} />
<Route path="itemselect.php" element={<ItemSelect lang={lang} type={type} />} />
<Route path="advanced_search.php" element={<DatabaseAdvancedSearch lang={lang} type={type} />} />
<Route path="*" element={<PageNotFound lang={lang} />} />
</Routes>
</div>
);
};
export default Database;

View File

@@ -0,0 +1,63 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import Config, { BrainAtlasType, MultiLang } from "../config";
import Functions from "../functions";
import ItemType from "./item-type";
import AdvancedSearchQuery from "./lib/AdvancedSearchQuery";
import ItemUtil from "./lib/ItemUtil";
interface PropsFC {
lang: MultiLang;
type: BrainAtlasType;
}
interface Props extends PropsFC {
navigate: any;
}
class DatabaseAdvancedSearch extends React.Component<Props> {
private query: AdvancedSearchQuery = new AdvancedSearchQuery();
constructor(props: Props) {
super(props);
this.handleClickSearchButton = this.handleClickSearchButton.bind(this);
}
handleClickSearchButton() {
const { type, navigate } = this.props;
if (!this.query.empty()) {
const url = ItemUtil.getSearchByAdvancedKeywordsUrl(type, this.query);
navigate(url);
}
}
render() {
const { lang } = this.props;
return (
<div className="advancedSearch">
<h3>{Functions.mlang("[en]Search Items[/en][ja]アイテム検索[/ja]", lang)}</h3>
<div className="search">
<button className="formButton" onClick={this.handleClickSearchButton}>
Search
</button>
</div>
{Config.XOONIPS_ITEMTYPES.map((type) => {
return <ItemType.AdvancedSearch key={type} type={"xnp" + type} lang={lang} query={this.query} />;
})}
<div className="search">
<button className="formButton" onClick={this.handleClickSearchButton}>
Search
</button>
</div>
</div>
);
}
}
const DatabaseAdvancedSearchFC: React.FC<PropsFC> = (props: PropsFC) => {
const { lang, type } = props;
return <DatabaseAdvancedSearch lang={lang} type={type} navigate={useNavigate()} />;
};
export default DatabaseAdvancedSearchFC;

View File

@@ -0,0 +1,78 @@
import React from "react";
import { Helmet } from "react-helmet-async";
import Loading from "../common/lib/Loading";
import PageNotFound from "../common/lib/PageNotFound";
import { BrainAtlasType, MultiLang } from "../config";
import Functions from "../functions";
import ItemType from "./item-type";
import ItemUtil, { Item } from "./lib/ItemUtil";
interface Props {
lang: MultiLang;
type: BrainAtlasType;
id: number;
doi: string;
}
interface State {
loading: boolean;
item: Item | null;
}
class DatabaseDetailItem extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
loading: true,
item: null,
};
}
componentDidMount() {
this.updateItem();
}
componentDidUpdate(prevProps: Props, prevState: State) {
const { id, doi } = this.props;
if (id !== prevProps.id || doi !== prevProps.doi) {
this.updateItem();
}
}
updateItem() {
const { id, doi, type } = this.props;
if (doi !== "") {
ItemUtil.getByDoi(type, doi, (item) => {
this.setState({ loading: false, item });
});
} else if (id !== 0) {
ItemUtil.get(type, id, (item) => {
this.setState({ loading: false, item });
});
}
}
render() {
const { lang, type } = this.props;
if (this.state.loading) {
return <Loading />;
}
if (this.state.item === null) {
return <PageNotFound lang={lang} />;
}
return (
<>
<Helmet>
<title>
{Functions.mlang(this.state.item.title, lang)} - {Functions.mlang("[en]Database[/en][ja]データベース[/ja]", lang)} - {Functions.siteTitle(lang)}
</title>
</Helmet>
<h3>{Functions.mlang("[en]Detail[/en][ja]詳細[/ja]", lang)}</h3>
<br />
<ItemType.Detail lang={lang} item={this.state.item} type={type} />
</>
);
}
}
export default DatabaseDetailItem;

View File

@@ -0,0 +1,67 @@
import React from "react";
import { useLocation } from "react-router-dom";
import { BrainAtlasType, MultiLang } from "../config";
import AdvancedSearchQuery from "./lib/AdvancedSearchQuery";
import DatabaseListItem from "./lib/DatabaseListItem";
import ItemUtil, { SearchCallbackFunc, SortCondition } from "./lib/ItemUtil";
interface PropsFC {
lang: MultiLang;
type: BrainAtlasType;
}
interface Props extends PropsFC {
location: any;
}
interface State {
search: string;
query: AdvancedSearchQuery;
}
class DatabaseSearchByAdvancedKeyword extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
const search = props.location.search;
const query = ItemUtil.getAdvancedSearchQueryByQuery(search);
this.state = { search, query };
this.searchFunc = this.searchFunc.bind(this);
}
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
const search = nextProps.location.search;
if (prevState.search !== search) {
const query = ItemUtil.getAdvancedSearchQueryByQuery(search);
return { search, query };
}
return null;
}
getUrl() {
const { type } = this.props;
return ItemUtil.getSearchByAdvancedKeywordsUrl(type, this.state.query);
}
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
const { type } = this.props;
ItemUtil.getListByAdvancedSearchQuery(type, this.state.query, condition, func);
}
render() {
const { lang, type } = this.props;
const baseUrl = this.getUrl();
return (
<div className="list">
<h3>Listing item</h3>
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} type={type} />
</div>
);
}
}
const DatabaseSearchByAdvancedKeywordFC: React.FC<PropsFC> = (props: PropsFC) => {
const { lang, type } = props;
return <DatabaseSearchByAdvancedKeyword lang={lang} type={type} location={useLocation()} />;
};
export default DatabaseSearchByAdvancedKeywordFC;

View File

@@ -0,0 +1,80 @@
import React, { Fragment } from "react";
import { Helmet } from "react-helmet-async";
import { Link } from "react-router-dom";
import PageNotFound from "../common/lib/PageNotFound";
import { BrainAtlasType, MultiLang } from "../config";
import Functions from "../functions";
import DatabaseListIndex from "./lib/DatabaseListIndex";
import DatabaseListItem from "./lib/DatabaseListItem";
import IndexUtil, { Index } from "./lib/IndexUtil";
import ItemUtil, { SearchCallbackFunc, SortCondition } from "./lib/ItemUtil";
export interface Props {
lang: MultiLang;
type: BrainAtlasType;
indexId: number;
}
class DatabaseSearchByIndexId extends React.Component<Props> {
constructor(props: Props) {
super(props);
this.searchFunc = this.searchFunc.bind(this);
}
getUrl() {
const { type, indexId } = this.props;
if (indexId === 0) {
return "/";
}
return IndexUtil.getUrl(type, indexId);
}
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
const { type, indexId } = this.props;
if (indexId === 0) {
const res = { total: 0, data: [] };
func(res);
} else {
ItemUtil.getListByIndexId(type, indexId, condition, func);
}
}
render() {
const { lang, type, indexId } = this.props;
const index = IndexUtil.get(type, indexId);
if (index === null) {
return <PageNotFound lang={lang} />;
}
const baseUrl = this.getUrl();
const pIndexes = IndexUtil.getParents(type, indexId);
const parents = pIndexes.map((value: Index) => {
const url: string = IndexUtil.getUrl(type, value.id);
const title = Functions.mlang(value.title, lang);
return (
<Fragment key={value.id}>
/ <Link to={url}>{title}</Link>{" "}
</Fragment>
);
});
const title = pIndexes
.map((value) => {
return "/" + Functions.mlang(value.title, lang);
})
.join("");
return (
<div className="list">
<Helmet>
<title>
{Functions.mlang(title, lang)} - {Functions.mlang("[en]Database[/en][ja]データベース[/ja]", lang)} - {Functions.siteTitle(lang)}
</title>
</Helmet>
<h3>{Functions.mlang("[en]Listing item[/en][ja]アイテム一覧[/ja]", lang)}</h3>
<div>{parents}</div>
<DatabaseListIndex lang={lang} index={index} type={type} />
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} type={type} />
</div>
);
}
}
export default DatabaseSearchByIndexId;

View File

@@ -0,0 +1,48 @@
import React from "react";
import { BrainAtlasType, MultiLang } from "../config";
import Functions from "../functions";
import DatabaseListItem from "./lib/DatabaseListItem";
import ItemUtil, { SearchCallbackFunc, SortCondition } from "./lib/ItemUtil";
interface Props {
lang: MultiLang;
itemType: string;
subItemType: string;
type: BrainAtlasType;
}
class DatabaseSearchByItemType extends React.Component<Props> {
constructor(props: Props) {
super(props);
this.searchFunc = this.searchFunc.bind(this);
}
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
const { itemType, subItemType, type } = this.props;
if (itemType === "") {
const res = { total: 0, data: [] };
func(res);
} else {
ItemUtil.getListByItemType(type, itemType, subItemType, condition, func);
}
}
getUrl() {
const { itemType, subItemType, type } = this.props;
let url = ItemUtil.getItemTypeSearchUrl(type, itemType, subItemType);
return url;
}
render() {
const { lang, type } = this.props;
const baseUrl = this.getUrl();
return (
<div className="list">
<h3>{Functions.mlang("[en]Listing item[/en][ja]アイテム一覧[/ja]", lang)}</h3>
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} type={type} />
</div>
);
}
}
export default DatabaseSearchByItemType;

View File

@@ -0,0 +1,74 @@
import React from "react";
import { useLocation } from "react-router-dom";
import { BrainAtlasType, MultiLang } from "../config";
import Functions from "../functions";
import DatabaseListItem from "./lib/DatabaseListItem";
import ItemUtil, { KeywordSearchType, SearchCallbackFunc, SortCondition } from "./lib/ItemUtil";
interface PropsFC {
lang: MultiLang;
type: BrainAtlasType;
}
interface Props extends PropsFC {
location: any;
}
interface State {
type: KeywordSearchType;
keyword: string;
}
class DatabaseSearchByKeyword extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
const { location } = props;
const { type, keyword } = ItemUtil.getSearchKeywordByQuery(location.search);
this.state = { type, keyword };
this.searchFunc = this.searchFunc.bind(this);
}
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
const { type, keyword } = ItemUtil.getSearchKeywordByQuery(nextProps.location.search);
if (prevState.type !== type || prevState.keyword !== keyword) {
return { type, keyword };
}
return null;
}
getUrl() {
const { type } = this.props;
return ItemUtil.getSearchByKeywordUrl(type, this.state.type, this.state.keyword);
}
searchFunc(condition: SortCondition, func: SearchCallbackFunc) {
const { type } = this.props;
if (this.state.keyword === "") {
const res = { total: 0, data: [] };
func(res);
} else {
ItemUtil.getListByKeyword(type, this.state.type, this.state.keyword, condition, func);
}
}
render() {
const { lang, type } = this.props;
const baseUrl = this.getUrl();
return (
<div className="list">
<h3>{Functions.mlang("[en]Listing item[/en][ja]アイテム一覧[/ja]", lang)}</h3>
<p>
{Functions.mlang("[en]Search Keyword[/en][ja]検索キーワード[/ja]", lang)} : {this.state.keyword}
</p>
<DatabaseListItem lang={lang} url={baseUrl} search={this.searchFunc} type={type} />
</div>
);
}
}
const DatabaseSearchByKeywordFC: React.FC<PropsFC> = (props: PropsFC) => {
const { lang, type } = props;
return <DatabaseSearchByKeyword lang={lang} type={type} location={useLocation()} />;
};
export default DatabaseSearchByKeywordFC;

View File

@@ -0,0 +1,13 @@
.itemTypes .itemType {
width: 50%;
padding: 5px;
}
.itemTypes .itemType :global(table) {
width: auto;
}
.itemTypes .itemType :global(table .itemTypeName) {
vertical-align: middle;
font-size: large;
}

View File

@@ -0,0 +1,41 @@
import React from "react";
import Config, { BrainAtlasType, MultiLang } from "../config";
import styles from "./DatabaseTop.module.css";
import ItemType from "./item-type";
interface Props {
lang: MultiLang;
type: BrainAtlasType;
}
const DatabaseTop: React.FC<Props> = (props: Props) => {
const { lang, type } = props;
const types: string[][] = [];
const len = Config.XOONIPS_ITEMTYPES.length;
for (let i = 0; i < Math.ceil(len / 2); i++) {
const j = i * 2;
const p = Config.XOONIPS_ITEMTYPES.slice(j, j + 2);
types.push(p);
}
return (
<table className={styles.itemTypes}>
<tbody>
{types.map((value, idx) => {
return (
<tr key={idx}>
{value.map((itemType, idx) => {
return (
<td key={idx} className={styles.itemType}>
{itemType !== "" && <ItemType.Top lang={lang} itemType={"xnp" + itemType} type={type} />}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
);
};
export default DatabaseTop;

View File

@@ -0,0 +1,148 @@
import React from "react";
import { Navigate, useLocation } from "react-router-dom";
import PageNotFound from "../common/lib/PageNotFound";
import { MultiLang } from "../config";
import Functions from "../functions";
interface Props {
lang: MultiLang;
}
const DatabaseXoopsPathRedirect: React.FC<Props> = (props: Props) => {
const { lang } = props;
const getRedirectUrl = (): string => {
const location = useLocation();
const pathname = location.pathname || "";
const query = new URLSearchParams(location.search);
const search = new RegExp("^/modules/xoonips(?:/+(.*))?$");
const matches = pathname.match(search);
if (matches === null) {
return "";
}
const path = matches[1] || "";
switch (path) {
case "":
case "index.php": {
return "/database";
}
case "detail.php": {
const id = query.get("id");
if (id !== null) {
return "/database/item/id/" + Functions.escape(id);
}
const itemId = query.get("item_id");
if (itemId !== null && itemId.match(/^\d+$/) !== null) {
return "/database/item/" + Functions.escape(itemId);
}
return "";
}
case "listitem.php": {
const indexId = query.get("index_id");
if (indexId !== null && indexId.match(/^\d+$/) !== null) {
const params = new URLSearchParams();
const map: any = {
orderby: { key: "orderby", isNumber: false },
order_dir: { key: "order_dir", isNumber: true },
itemcount: { key: "itemcount", isNumber: true },
page: { key: "page", isNumber: true },
};
for (let k in map) {
const v = query.get(k);
if (v === null || v.length === 0) {
continue;
}
if (map[k].isNumber && v.match(/^\d+$/) !== null) {
continue;
}
params.set(map[k].key, v);
}
const paramStr = params.toString();
return "/database/list/" + Functions.escape(indexId) + (paramStr.length > 0 ? "?" + paramStr : "");
}
break;
}
case "itemselect.php": {
const op = query.get("op");
if (op === null) {
break;
}
switch (op) {
case "quicksearch": {
const keyword = query.get("keyword");
const itemType = query.get("search_itemtype");
if (keyword === null || itemType === null || keyword === "") {
return "";
}
const type = itemType.replace("xnp", "");
if (itemType !== "basic" && itemType !== "all" && itemType.match(/^xnp.+/) === null) {
return "";
}
const params = new URLSearchParams({ type, keyword });
const map: any = {
orderby: { key: "orderby", isNumber: false },
orderdir: { key: "order_dir", isNumber: true },
item_per_page: { key: "itemcount", isNumber: true },
page: { key: "page", isNumber: true },
};
for (let k in map) {
const v = query.get(k);
if (v === null || v.length === 0) {
continue;
}
if (map[k].isNumber && v.match(/^\d+$/) !== null) {
continue;
}
params.set(map[k].key, v);
}
return "/database/search?" + params.toString();
}
case "itemtypesearch": {
const itemType = query.get("search_itemtype");
if (itemType === null || itemType.match(/^xnp.+/) === null) {
return "";
}
const type = itemType.replace("xnp", "");
return "/database/search/itemtype/" + Functions.escape(type);
}
case "itemsubtypesearch": {
let type = "";
let subtype = "";
query.forEach((v, k) => {
if (k.match(/^xnp[a-z]+$/) !== null && !!v) {
type = k.replace("xnp", "");
return;
}
});
if (type === "") {
return "";
}
query.forEach((v, k) => {
if (k.match(`^xnp${type}_.+$`) !== null && !!v) {
subtype = v;
return;
}
});
if (subtype === "") {
return "";
}
return "/database/search/itemtype/" + Functions.escape(type) + "/" + Functions.escape(subtype);
}
}
return "";
}
case "advanced_search.php": {
return "/database/advanced";
}
}
return "";
};
const url = getRedirectUrl();
if (url === "") {
return <PageNotFound lang={lang} />;
}
return <Navigate to={url} />;
};
export default DatabaseXoopsPathRedirect;

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1 @@
[{"type":"degu","tree":[{"id":3,"title":"Public","num_of_items":0,"children":[{"id":16,"title":"Degu (Octodon degu)","num_of_items":0,"children":[{"id":24,"title":"The Degu 3D Brain Atlas","num_of_items":4,"children":[{"id":18,"title":"References","num_of_items":0,"children":[]}]}]}]}]},{"type":"jm","tree":[{"id":3,"title":"Public","num_of_items":0,"children":[{"id":7,"title":"Japanese Macaque Monkey","num_of_items":0,"children":[{"id":9,"title":"The MRI Standard Brain of Japanese Macaque Monkey","num_of_items":1,"children":[{"id":44,"title":"References","num_of_items":0,"children":[]}]}]}]}]},{"type":"marmoset","tree":[{"id":3,"title":"Public","num_of_items":0,"children":[{"id":66,"title":"The Marmoset 3D Brain Atlas","num_of_items":4,"children":[{"id":69,"title":"References","num_of_items":0,"children":[]}]},{"id":71,"title":"The Marmoset MRI Standard Brain","num_of_items":1,"children":[{"id":73,"title":"References","num_of_items":0,"children":[]}]}]}]}]

View File

@@ -0,0 +1,91 @@
.indexTree {
border-top: 1px solid #999999;
border-left: 1px solid #999999;
border-bottom: 1px solid #404040;
border-right: 1px solid #404040;
background-color: white;
height: 400px;
width: calc(100% - 6px);
overflow: auto;
margin: 0 auto;
padding: 3px;
line-height: 100%;
}
.formButton {
margin: 3px 3px 10px;
}
.indexTree div span {
color: #333;
font-size: 12px;
font-weight: bold;
line-height: 19px;
vertical-align: bottom;
}
.indexTree div span:hover {
color: #f60;
}
.indexTree:global(.rc-tree .rc-tree-treenode) {
line-height: 20px;
white-space: nowrap;
}
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-indent) {
display: inline-block;
}
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-indent .rc-tree-indent-unit) {
display: inline-block;
height: 20px;
width: 9px;
background: url(../assets/images/tree_line.png);
}
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-indent .rc-tree-indent-unit:not(:last-child)) {
background-position: -9px 0;
}
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-indent .rc-tree-indent-unit:not(:last-child).rc-tree-indent-unit-end) {
background-position: -18px 0;
}
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-indent .rc-tree-indent-unit:last-child.rc-tree-indent-unit-end) {
background-position: -27px 0;
}
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-switcher) {
display: inline-block;
height: 20px;
width: 16px;
margin-right: 2px;
background: url(../assets/images/tree_node.png);
cursor: pointer;
}
.indexTree:global(.rc-tree .rc-tree-treenode:first-child .rc-tree-switcher.rc-tree-switcher-noop) {
background-position: 0 0;
cursor: auto;
}
.indexTree:global(.rc-tree .rc-tree-treenode:first-child .rc-tree-switcher.rc-tree-switcher_open) {
background-position: -16px 0;
}
.indexTree:global(.rc-tree .rc-tree-treenode:first-child .rc-tree-switcher.rc-tree-switcher_close) {
background-position: -32px 0;
}
.indexTree:global(.rc-tree .rc-tree-treenode:not(:first-child) .rc-tree-switcher.rc-tree-switcher-noop) {
background-position: 0 -20px;
cursor: auto;
}
.indexTree:global(.rc-tree .rc-tree-treenode:not(:first-child) .rc-tree-switcher.rc-tree-switcher_open) {
background-position: -16px -20px;
}
.indexTree:global(.rc-tree .rc-tree-treenode:not(:first-child) .rc-tree-switcher.rc-tree-switcher_close) {
background-position: -32px -20px;
}
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-node-content-wrapper) {
display: inline-block;
height: 20px;
cursor: pointer;
}
.indexTree:global(.rc-tree .rc-tree-treenode .rc-tree-node-content-wrapper .rc-tree-title) {
vertical-align: middle;
line-height: 20px;
}

View File

@@ -0,0 +1,136 @@
import Tree from "rc-tree";
import { DataNode, EventDataNode } from "rc-tree/lib/interface";
import React, { Key } from "react";
import { useNavigate } from "react-router";
import { BrainAtlasType, MultiLang } from "../../config";
import Functions from "../../functions";
import IndexUtil, { Index, INDEX_ID_PUBLIC } from "../lib/IndexUtil";
import styles from "./IndexTree.module.css";
interface Props {
lang: MultiLang;
type: BrainAtlasType;
}
interface State {
tree: DataNode[];
expandableKeys: string[];
expandedKeys: string[];
selectedKeys: number[];
}
class IndexTree extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.handleClickOpenAll = this.handleClickOpenAll.bind(this);
this.handleClickCloseAll = this.handleClickCloseAll.bind(this);
this.handleExpand = this.handleExpand.bind(this);
this.handleSelect = this.handleSelect.bind(this);
const res = this.load();
this.state = {
tree: res.elements,
expandableKeys: res.keys,
expandedKeys: res.expandedKeys,
selectedKeys: [],
};
}
load() {
const { lang, type } = this.props;
let keys: string[] = [];
let eKeys: string[] = [];
const makeTreeNode = (index: Index, depth: number): DataNode => {
const title = Functions.mlang(index.title, lang) + (index.numOfItems > 0 ? " (" + index.numOfItems + ")" : "");
const children = IndexUtil.getChildren(type, index.id);
if (children.length === 0) {
return { key: String(index.id), title: title };
}
const childTreeNodes = children.map((value: Index) => {
return makeTreeNode(value, depth + 1);
});
if (depth < 1) {
eKeys.push(String(index.id));
}
keys.push(String(index.id));
return { key: String(index.id), title: title, children: childTreeNodes };
};
let elements: DataNode[] = [];
const index = IndexUtil.get(type, INDEX_ID_PUBLIC);
if (index !== null) {
elements.push(makeTreeNode(index, 0));
}
return {
elements: elements,
keys: keys,
expandedKeys: eKeys,
};
}
componentDidUpdate(prevProps: Props, prevState: State) {
const { lang, type } = this.props;
if (prevProps.lang !== lang || prevProps.type !== type) {
const res = this.load();
this.setState({
tree: res.elements,
selectedKeys: [],
});
}
}
handleClickOpenAll() {
this.setState({ expandedKeys: this.state.expandableKeys });
}
handleClickCloseAll() {
this.setState({ expandedKeys: [] });
}
handleExpand(
expandedKeys: Key[],
info: {
node: EventDataNode<DataNode>;
expanded: boolean;
nativeEvent: MouseEvent;
}
): void {
const keys: string[] = expandedKeys.map((key) => {
return typeof key === "string" ? key : String(key);
});
this.setState({ expandedKeys: keys });
}
handleSelect(
selectedKeys: Key[],
info: {
event: "select";
selected: boolean;
node: EventDataNode<DataNode>;
selectedNodes: DataNode[];
nativeEvent: MouseEvent;
}
): void {
const navigate = useNavigate();
const { type } = this.props;
const selectedKey = selectedKeys.shift() || 0;
const key = typeof selectedKey === "string" ? parseInt(selectedKey, 10) : selectedKey;
const url = IndexUtil.getUrl(type, key);
navigate(url);
this.setState({ selectedKeys: [] });
}
render() {
return (
<div className={styles.container}>
<button className={styles.formButton} onClick={this.handleClickOpenAll}>
open all
</button>
<button className={styles.formButton} onClick={this.handleClickCloseAll}>
close all
</button>
<Tree className={styles.indexTree} expandedKeys={this.state.expandedKeys} selectedKeys={this.state.selectedKeys} onExpand={this.handleExpand} onSelect={this.handleSelect} showIcon={false} treeData={this.state.tree} />
</div>
);
}
}
export default IndexTree;

View File

@@ -0,0 +1,25 @@
import AdvancedSearchBase, { AdvancedSearchBaseProps } from "../lib/AdvancedSearchBase";
class BinderAdvancedSearch extends AdvancedSearchBase {
constructor(props: AdvancedSearchBaseProps) {
super(props);
this.type = "binder";
this.title = "Binder";
this.state.values["title"] = "";
this.state.values["keyword"] = "";
this.state.values["description"] = "";
this.state.values["doi"] = "";
}
getRows() {
const rows = [
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: this.renderFieldInputText("title", 50) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: this.renderFieldInputText("keyword", 50) },
{ label: "[en]Description[/en][ja]概要[/ja]", value: this.renderFieldInputText("description", 50) },
{ label: "ID", value: this.renderFieldInputText("doi", 50) },
];
return rows;
}
}
export default BinderAdvancedSearch;

View File

@@ -0,0 +1,101 @@
import React from "react";
import ItemType from "..";
import { BrainAtlasType, MultiLang } from "../../../config";
import Functions from "../../../functions";
import ItemUtil, { Item, ItemBinder } from "../../lib/ItemUtil";
import DetailBase from "../lib/DetailBase";
import ItemTypeField from "../lib/field";
interface Props {
lang: MultiLang;
item: ItemBinder;
type: BrainAtlasType;
}
interface State {
items: Item[];
}
class BinderLinkItems extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
items: [],
};
}
componentDidMount() {
this.updateItems();
}
componentDidUpdate(prevProps: Props, prevState: State) {
const prevItemIds = prevProps.item.item_link;
const nextItemIds = this.props.item.item_link;
if (prevItemIds.toString() !== nextItemIds.toString()) {
this.updateItems();
}
}
updateItems() {
const { item, type } = this.props;
const itemIds = item.item_link;
ItemUtil.getList(type, itemIds, (results) => {
const items = results.data;
this.setState({ items });
});
}
render() {
const { lang, type } = this.props;
return (
<table className="listTable">
<tbody>
{this.state.items.map((item, idx) => {
const evenodd = idx % 2 ? "even" : "odd";
return (
<tr key={item.item_id}>
<td className={evenodd}>
<ItemType.List lang={lang} item={item} type={type} />
</td>
</tr>
);
})}
</tbody>
</table>
);
}
}
class BinderDetail extends DetailBase {
getFields() {
const { lang, type } = this.props;
const item = this.props.item as ItemBinder;
return [
{ label: "ID", value: item.doi },
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: Functions.mlang(item.title, lang) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} /> },
{ label: "[en]Description[/en][ja]概要[/ja]", value: <ItemTypeField.Description lang={lang} description={item.description} /> },
{ label: "[en]Last Modified Date[/en][ja]最終更新日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
{ label: "[en]Created Date[/en][ja]作成日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
{ label: "[en]Contributor[/en][ja]登録者[/ja]", value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
{ label: "[en]Item Type[/en][ja]アイテムタイプ[/ja]", value: item.item_type_display_name },
{ label: "[en]Change Log(History)[/en][ja]変更履歴[/ja]", value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
{ label: "Index", value: <ItemTypeField.ItemIndex lang={lang} index={item.index} type={type} /> },
];
}
render() {
const { lang, type } = this.props;
const item = this.props.item as ItemBinder;
const detail = super.render.call(this);
return (
<>
{detail}
<h4>Registered Items</h4>
<BinderLinkItems lang={lang} item={item} type={type} />
</>
);
}
}
export default BinderDetail;

View File

@@ -0,0 +1,28 @@
import React from "react";
import { Link } from "react-router-dom";
import Functions from "../../../functions";
import iconFile from "../../assets/images/icon_binder.gif";
import { ItemBinder } from "../../lib/ItemUtil";
import ListBase, { ListBaseProps } from "../lib/ListBase";
class BinderList extends ListBase {
constructor(props: ListBaseProps) {
super(props);
this.label = "Binder";
this.icon = iconFile;
}
renderBody() {
const { lang } = this.props;
const item = this.props.item as ItemBinder;
return (
<>
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
<br />
{Functions.mlang(item.description, lang)}
</>
);
}
}
export default BinderList;

View File

@@ -0,0 +1,14 @@
import iconFile from "../../assets/images/icon_binder.gif";
import TopBase, { TopBaseProps } from "../lib/TopBase";
class BinderTop extends TopBase {
constructor(props: TopBaseProps) {
super(props);
this.type = "binder";
this.label = "Binder";
this.icon = iconFile;
this.description = "[en]Binder collection.[/en][ja]バインダー[/ja]";
}
}
export default BinderTop;

View File

@@ -0,0 +1,13 @@
import BinderAdvancedSearch from "./BinderAdvancedSearch";
import BinderDetail from "./BinderDetail";
import BinderList from "./BinderList";
import BinderTop from "./BinderTop";
const ItemTypeBinder = {
Top: BinderTop,
List: BinderList,
Detail: BinderDetail,
AdvancedSearch: BinderAdvancedSearch,
};
export default ItemTypeBinder;

View File

@@ -0,0 +1,37 @@
import AdvancedSearchBase, { AdvancedSearchBaseProps } from "../lib/AdvancedSearchBase";
class BookAdvancedSearch extends AdvancedSearchBase {
constructor(props: AdvancedSearchBaseProps) {
super(props);
this.type = "book";
this.title = "Book";
this.state.values["title"] = "";
this.state.values["keyword"] = "";
this.state.values["description"] = "";
this.state.values["doi"] = "";
this.state.values["author"] = "";
this.state.values["editor"] = "";
this.state.values["publisher"] = "";
this.state.values["publication_year"] = "";
this.state.values["isbn"] = "";
this.state.values["file.book_pdf.original_file_name"] = "";
}
getRows() {
const rows = [
{ label: "[en]Book Title[/en][ja]著書名[/ja]", value: this.renderFieldInputText("title", 50) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: this.renderFieldInputText("keyword", 50) },
{ label: "[en]Description[/en][ja]概要[/ja]", value: this.renderFieldInputText("description", 50) },
{ label: "ID", value: this.renderFieldInputText("doi", 50) },
{ label: "[en]Author[/en][ja]著者[/ja]", value: this.renderFieldInputText("author", 50) },
{ label: "[en]Editor[/en][ja]編集者[/ja]", value: this.renderFieldInputText("editor", 50) },
{ label: "[en]Publisher[/en][ja]出版社[/ja]", value: this.renderFieldInputText("publisher", 50) },
{ label: "[en]Publication Year[/en][ja]出版年[/ja]", value: this.renderFieldInputText("publication_year", 10) },
{ label: "ISBN", value: this.renderFieldInputText("isbn", 50) },
{ label: "[en]PDF File[/en][ja]PDF ファイル[/ja]", value: this.renderFieldInputText("file.book_pdf.original_file_name", 50) },
];
return rows;
}
}
export default BookAdvancedSearch;

View File

@@ -0,0 +1,41 @@
import React from "react";
import Functions from "../../../functions";
import { ItemBook } from "../../lib/ItemUtil";
import DetailBase from "../lib/DetailBase";
import ItemTypeField from "../lib/field";
class BookDetail extends DetailBase {
getFields() {
const { lang, type } = this.props;
const item = this.props.item as ItemBook;
return [
{ label: "ID", value: item.doi },
{ label: "[en]Language[/en][ja]言語[/ja]", value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
{ label: "[en]Book Title[/en][ja]著書名[/ja]", value: Functions.mlang(item.title, lang) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} /> },
{ label: "[en]Description[/en][ja]概要[/ja]", value: <ItemTypeField.Description lang={lang} description={item.description} /> },
{ label: "[en]Last Modified Date[/en][ja]最終更新日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
{ label: "[en]Created Date[/en][ja]作成日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
{ label: "[en]Contributor[/en][ja]登録者[/ja]", value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
{ label: "[en]Item Type[/en][ja]アイテムタイプ[/ja]", value: item.item_type_display_name },
{ label: "[en]Change Log(History)[/en][ja]変更履歴[/ja]", value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
{ label: "[en]Author[/en][ja]著者[/ja]", value: <ItemTypeField.Author lang={lang} author={item.author} /> },
{ label: "[en]Editor[/en][ja]編集者[/ja]", value: Functions.mlang(item.editor, lang) },
{ label: "[en]Publisher[/en][ja]出版社[/ja]", value: Functions.mlang(item.publisher, lang) },
{ label: "[en]Publication Year[/en][ja]出版年[/ja]", value: item.publication_year },
{
label: "URL",
value: (
<a href={item.url} target="_blank" rel="noopener noreferrer">
{item.url}
</a>
),
},
{ label: "[en]PDF File[/en][ja]PDF ファイル[/ja]", value: <ItemTypeField.ItemFile lang={lang} file={item.file} ftype="book_pdf" downloadLimit={item.attachment_dl_limit} type={type} /> },
{ label: "Index", value: <ItemTypeField.ItemIndex lang={lang} index={item.index} type={type} /> },
{ label: "[en]Related to[/en][ja]関連アイテム[/ja]", value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} type={type} /> },
];
}
}
export default BookDetail;

View File

@@ -0,0 +1,36 @@
import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import Functions from "../../../functions";
import iconFile from "../../assets/images/icon_book.gif";
import { ItemBook } from "../../lib/ItemUtil";
import ListBase, { ListBaseProps } from "../lib/ListBase";
class BookList extends ListBase {
constructor(props: ListBaseProps) {
super(props);
this.label = "Book";
this.icon = iconFile;
}
renderBody() {
const { lang } = this.props;
const item = this.props.item as ItemBook;
const authors = item.author.map((author, i) => {
return (
<Fragment key={i}>
{i > 0 && ", "}
{Functions.mlang(author, lang)}
</Fragment>
);
});
return (
<>
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
<br />
{authors}
</>
);
}
}
export default BookList;

View File

@@ -0,0 +1,14 @@
import iconFile from "../../assets/images/icon_book.gif";
import TopBase, { TopBaseProps } from "../lib/TopBase";
class BookTop extends TopBase {
constructor(props: TopBaseProps) {
super(props);
this.type = "book";
this.label = "Book";
this.icon = iconFile;
this.description = "[en]Related book collection.[/en][ja]関連書籍[/ja]";
}
}
export default BookTop;

View File

@@ -0,0 +1,13 @@
import BookAdvancedSearch from "./BookAdvancedSearch";
import BookDetail from "./BookDetail";
import BookList from "./BookList";
import BookTop from "./BookTop";
const ItemTypeBook = {
Top: BookTop,
List: BookList,
Detail: BookDetail,
AdvancedSearch: BookAdvancedSearch,
};
export default ItemTypeBook;

View File

@@ -0,0 +1,51 @@
import React from "react";
import { ItemConferenceSubTypes } from "../../lib/ItemUtil";
import AdvancedSearchBase, { AdvancedSearchBaseProps } from "../lib/AdvancedSearchBase";
class ConferenceAdvancedSearch extends AdvancedSearchBase {
constructor(props: AdvancedSearchBaseProps) {
super(props);
this.type = "conference";
this.title = "Conference";
const now = new Date();
const year = String(now.getFullYear());
const month = String(now.getMonth() + 1);
const mday = String(now.getDate());
this.state.values["title"] = "";
this.state.values["presentation_type"] = "";
this.state.values["author"] = "";
this.state.values["conference_from_year"] = year;
this.state.values["conference_from_month"] = month;
this.state.values["conference_from_mday"] = mday;
this.state.values["conference_to_year"] = year;
this.state.values["conference_to_month"] = month;
this.state.values["conference_to_mday"] = mday;
this.setIgnoreKey("conference_from_year");
this.setIgnoreKey("conference_from_month");
this.setIgnoreKey("conference_from_mday");
this.setIgnoreKey("conference_to_year");
this.setIgnoreKey("conference_to_month");
this.setIgnoreKey("conference_to_mday");
}
renderDate() {
return (
<>
<div>{this.renderFieldDate("From", "conference_from_year", "conference_from_month", "conference_from_mday")}</div>
<div>{this.renderFieldDate("To", "conference_to_year", "conference_to_month", "conference_to_mday")}</div>
</>
);
}
getRows() {
const rows = [
{ label: "[en]Presentation Title[/en][ja]発表議題[/ja]", value: this.renderFieldInputText("title", 50) },
{ label: "[en]Presentation Type[/en][ja]発表資料ファイル形式[/ja]", value: this.renderFieldSelect("presentation_type", ItemConferenceSubTypes) },
{ label: "[en]Author[/en][ja]発表者[/ja]", value: this.renderFieldInputText("author", 50) },
{ label: "[en]Date[/en][ja]日付[/ja]", value: this.renderDate() },
];
return rows;
}
}
export default ConferenceAdvancedSearch;

View File

@@ -0,0 +1,35 @@
import React from "react";
import Functions from "../../../functions";
import { ItemConference } from "../../lib/ItemUtil";
import DetailBase from "../lib/DetailBase";
import ItemTypeField from "../lib/field";
import ConferenceUtil from "./ConferenceUtil";
class ConferenceDetail extends DetailBase {
getFields() {
const { lang, type } = this.props;
const item = this.props.item as ItemConference;
return [
{ label: "ID", value: item.doi },
{ label: "[en]Language[/en][ja]言語[/ja]", value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
{ label: "[en]Conference Title[/en][ja]学会名[/ja]", value: Functions.mlang(item.conference_title, lang) },
{ label: "[en]Place[/en][ja]開催地[/ja]", value: item.place },
{ label: "[en]Date[/en][ja]日付[/ja]", value: <ConferenceUtil.ConferenceDate lang={lang} item={item} /> },
{ label: "[en]Last Modified Date[/en][ja]最終更新日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
{ label: "[en]Created Date[/en][ja]作成日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
{ label: "[en]Contributor[/en][ja]登録者[/ja]", value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
{ label: "[en]Item Type[/en][ja]アイテムタイプ[/ja]", value: item.item_type_display_name },
{ label: "[en]Change Log(History)[/en][ja]変更履歴[/ja]", value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
{ label: "[en]Presentation Title[/en][ja]発表議題[/ja]", value: Functions.mlang(item.title, lang) },
{ label: "[en]Author[/en][ja]発表者[/ja]", value: <ItemTypeField.Author lang={lang} author={item.author} /> },
{ label: "[en]Abstract[/en][ja]要約[/ja]", value: <ItemTypeField.Description lang={lang} description={item.abstract} /> },
{ label: "[en]Presentation File[/en][ja]発表資料[/ja]", value: <ItemTypeField.ItemFile lang={lang} file={item.file} ftype="conference_file" type={type} /> },
{ label: "[en]Presentation Type[/en][ja]発表資料ファイル形式[/ja]", value: <ConferenceUtil.PresentationType lang={lang} type={item.presentation_type} /> },
{ label: "[en]Conference Paper[/en][ja]学会資料[/ja]", value: <ItemTypeField.ItemFile lang={lang} file={item.file} ftype="conference_paper" type={type} /> },
{ label: "Index", value: <ItemTypeField.ItemIndex lang={lang} index={item.index} type={type} /> },
{ label: "[en]Related to[/en][ja]関連アイテム[/ja]", value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} type={type} /> },
];
}
}
export default ConferenceDetail;

View File

@@ -0,0 +1,38 @@
import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import Functions from "../../../functions";
import iconFile from "../../assets/images/icon_conference.gif";
import { ItemConference } from "../../lib/ItemUtil";
import ListBase, { ListBaseProps } from "../lib/ListBase";
import ConferenceUtil from "./ConferenceUtil";
class ConferenceList extends ListBase {
constructor(props: ListBaseProps) {
super(props);
this.label = "Conference";
this.icon = iconFile;
}
renderBody() {
const { lang } = this.props;
const item = this.props.item as ItemConference;
const authors = item.author.map((author, i) => {
return (
<Fragment key={i}>
{i > 0 && ", "}
{Functions.mlang(author, lang)}
</Fragment>
);
});
return (
<>
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
<br />
{Functions.mlang(item.conference_title, lang)} (<ConferenceUtil.PresentationType lang={lang} type={item.presentation_type} />)<br />
{authors}
</>
);
}
}
export default ConferenceList;

View File

@@ -0,0 +1,16 @@
import iconFile from "../../assets/images/icon_conference.gif";
import { ItemConferenceSubTypes } from "../../lib/ItemUtil";
import TopBase, { TopBaseProps } from "../lib/TopBase";
class ConferenceTop extends TopBase {
constructor(props: TopBaseProps) {
super(props);
this.type = "conference";
this.label = "Conference";
this.icon = iconFile;
this.description = "[en]Electrical presentation files for conference.[/en][ja]学会発表[/ja]";
this.subTypes = ItemConferenceSubTypes;
}
}
export default ConferenceTop;

View File

@@ -0,0 +1,43 @@
import React from "react";
import { MultiLang } from "../../../config";
import { ItemConference, ItemConferenceSubType, ItemConferenceSubTypes } from "../../lib/ItemUtil";
interface PresentationTypeProps {
lang: MultiLang;
type: ItemConferenceSubType;
}
const PresentationType: React.FC<PresentationTypeProps> = (props: PresentationTypeProps) => {
const { type } = props;
const subtype = ItemConferenceSubTypes.find((value) => {
return value.type === type;
});
if (typeof subtype === "undefined") {
return null;
}
return <span>{subtype.label}</span>;
};
interface ConferenceDateProps {
lang: MultiLang;
item: ItemConference;
}
const ConferenceDate: React.FC<ConferenceDateProps> = (props: ConferenceDateProps) => {
const { item } = props;
const monthStr = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const from = "From: " + monthStr[item.conference_from_month - 1] + " " + item.conference_from_mday + ", " + item.conference_from_year;
const to = "To: " + monthStr[item.conference_to_month - 1] + " " + item.conference_to_mday + ", " + item.conference_to_year;
return (
<span>
{from} {to}
</span>
);
};
const ConferenceUtil = {
PresentationType,
ConferenceDate,
};
export default ConferenceUtil;

View File

@@ -0,0 +1,13 @@
import ConferenceAdvancedSearch from "./ConferenceAdvancedSearch";
import ConferenceDetail from "./ConferenceDetail";
import ConferenceList from "./ConferenceList";
import ConferenceTop from "./ConferenceTop";
const ItemTypeConference = {
Top: ConferenceTop,
List: ConferenceList,
Detail: ConferenceDetail,
AdvancedSearch: ConferenceAdvancedSearch,
};
export default ItemTypeConference;

View File

@@ -0,0 +1,45 @@
import { ItemDataSubTypes } from "../../lib/ItemUtil";
import AdvancedSearchBase, { AdvancedSearchBaseProps } from "../lib/AdvancedSearchBase";
class DataAdvancedSearch extends AdvancedSearchBase {
constructor(props: AdvancedSearchBaseProps) {
super(props);
this.type = "data";
this.title = "Data";
const now = new Date();
const year = String(now.getFullYear());
const month = String(now.getMonth() + 1);
const mday = String(now.getDate());
this.state.values["title"] = "";
this.state.values["keyword"] = "";
this.state.values["description"] = "";
this.state.values["doi"] = "";
this.state.values["data_type"] = "";
this.state.values["experimenter"] = "";
this.state.values["publication_year"] = year;
this.state.values["publication_month"] = month;
this.state.values["publication_mday"] = mday;
this.state.values["file.preview.caption"] = "";
this.state.values["file.data_file.original_file_name"] = "";
this.setIgnoreKey("publication_year");
this.setIgnoreKey("publication_month");
this.setIgnoreKey("publication_mday");
}
getRows() {
const rows = [
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: this.renderFieldInputText("title", 50) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: this.renderFieldInputText("keyword", 50) },
{ label: "[en]Description[/en][ja]概要[/ja]", value: this.renderFieldInputText("description", 50) },
{ label: "ID", value: this.renderFieldInputText("doi", 50) },
{ label: "[en]Data Type[/en][ja]データタイプ[/ja]", value: this.renderFieldSelect("data_type", ItemDataSubTypes) },
{ label: "[en]Experimenter[/en][ja]実験者[/ja]", value: this.renderFieldInputText("experimenter", 50) },
{ label: "[en]Date[/en][ja]日付[/ja]", value: this.renderFieldDate("", "publication_year", "publication_month", "publication_mday") },
{ label: "[en]Caption[/en][ja]キャプション[/ja]", value: this.renderFieldInputText("file.preview.caption", 50) },
{ label: "[en]Data File[/en][ja]データファイル[/ja]", value: this.renderFieldInputText("file.data_file.original_file_name", 50) },
];
return rows;
}
}
export default DataAdvancedSearch;

View File

@@ -0,0 +1,43 @@
import React from "react";
import Functions from "../../../functions";
import ItemUtil, { ItemData } from "../../lib/ItemUtil";
import DetailBase from "../lib/DetailBase";
import ItemTypeField from "../lib/field";
import SimPFLinkIcon from "../lib/field/SimPFLinkIcon";
import DataUtil from "./DataUtil";
class DataDetail extends DetailBase {
getFields() {
const { lang, type } = this.props;
const item = this.props.item as ItemData;
const fields = [
{ label: "ID", value: item.doi },
{ label: "[en]Language[/en][ja]言語[/ja]", value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: Functions.mlang(item.title, lang) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} /> },
{ label: "[en]Description[/en][ja]概要[/ja]", value: <ItemTypeField.Description lang={lang} description={item.description} /> },
{ label: "[en]Date[/en][ja]日付[/ja]", value: <ItemTypeField.PublicationDate lang={lang} year={item.publication_year} month={item.publication_month} mday={item.publication_mday} /> },
{ label: "[en]Last Modified Date[/en][ja]最終更新日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
{ label: "[en]Created Date[/en][ja]作成日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
{ label: "[en]Contributor[/en][ja]登録者[/ja]", value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
{ label: "[en]Item Type[/en][ja]アイテムタイプ[/ja]", value: item.item_type_display_name },
{ label: "[en]Change Log(History)[/en][ja]変更履歴[/ja]", value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
{ label: "[en]Data Type[/en][ja]データタイプ[/ja]", value: <DataUtil.DataType lang={lang} type={item.data_type} /> },
{ label: "[en]Experimenter[/en][ja]実験者[/ja]", value: <ItemTypeField.Author lang={lang} author={item.experimenter} /> },
{ label: "[en]Preview[/en][ja]プレビュー[/ja]", value: <ItemTypeField.Preview lang={lang} file={item.file} type={type} /> },
{ label: "[en]Data File[/en][ja]データファイル[/ja]", value: <ItemTypeField.ItemFile lang={lang} file={item.file} ftype="data_file" rights={item.rights} useCc={item.use_cc} ccCommercialUse={item.cc_commercial_use} ccModification={item.cc_modification} downloadLimit={item.attachment_dl_limit} type={type} /> },
{ label: "Readme", value: <ItemTypeField.Readme lang={lang} readme={item.readme} /> },
{ label: "Rights", value: <ItemTypeField.Rights lang={lang} rights={item.rights} useCc={item.use_cc} ccCommercialUse={item.cc_commercial_use} ccModification={item.cc_modification} /> },
{ label: "Index", value: <ItemTypeField.ItemIndex lang={lang} index={item.index} type={type} /> },
{ label: "[en]Related to[/en][ja]関連アイテム[/ja]", value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} type={type} /> },
];
const simpfLinkUrl = ItemUtil.getSimPFLinkUrl(item.item_id);
if (simpfLinkUrl !== "") {
const field = { label: "Online Simulation", value: <SimPFLinkIcon lang={lang} url={simpfLinkUrl} isDetail={true} /> };
fields.splice(14, 0, field);
}
return fields;
}
}
export default DataDetail;

View File

@@ -0,0 +1,36 @@
import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import Functions from "../../../functions";
import iconFile from "../../assets/images/icon_data.gif";
import { ItemData } from "../../lib/ItemUtil";
import ListBase, { ListBaseProps } from "../lib/ListBase";
class DataList extends ListBase {
constructor(props: ListBaseProps) {
super(props);
this.label = "Data";
this.icon = iconFile;
}
renderBody() {
const { lang } = this.props;
const item = this.props.item as ItemData;
const authors = item.experimenter.map((author, i) => {
return (
<Fragment key={i}>
{i > 0 && ", "}
{Functions.mlang(author, lang)}
</Fragment>
);
});
return (
<>
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
<br />
{authors}
</>
);
}
}
export default DataList;

View File

@@ -0,0 +1,16 @@
import iconFile from "../../assets/images/icon_data.gif";
import { ItemDataSubTypes } from "../../lib/ItemUtil";
import TopBase, { TopBaseProps } from "../lib/TopBase";
class DataTop extends TopBase {
constructor(props: TopBaseProps) {
super(props);
this.type = "data";
this.label = "Data";
this.icon = iconFile;
this.description = "[en]Result data in numerical text/image/movie formats.[/en][ja]実験結果の数値データ/画像/動画など[/ja]";
this.subTypes = ItemDataSubTypes;
}
}
export default DataTop;

View File

@@ -0,0 +1,25 @@
import React from "react";
import { MultiLang } from "../../../config";
import { ItemDataSubType, ItemDataSubTypes } from "../../lib/ItemUtil";
interface DataTypeProps {
lang: MultiLang;
type: ItemDataSubType;
}
const DataType: React.FC<DataTypeProps> = (props: DataTypeProps) => {
const { type } = props;
const subtype = ItemDataSubTypes.find((value) => {
return value.type === type;
});
if (typeof subtype === "undefined") {
return null;
}
return <span>{subtype.label}</span>;
};
const DataUtil = {
DataType,
};
export default DataUtil;

View File

@@ -0,0 +1,13 @@
import DataAdvancedSearch from "./DataAdvancedSearch";
import DataDetail from "./DataDetail";
import DataList from "./DataList";
import DataTop from "./DataTop";
const ItemTypeData = {
Top: DataTop,
List: DataList,
Detail: DataDetail,
AdvancedSearch: DataAdvancedSearch,
};
export default ItemTypeData;

View File

@@ -0,0 +1,29 @@
import AdvancedSearchBase, { AdvancedSearchBaseProps } from "../lib/AdvancedSearchBase";
class FilesAdvancedSearch extends AdvancedSearchBase {
constructor(props: AdvancedSearchBaseProps) {
super(props);
this.type = "files";
this.title = "Files";
this.state.values["title"] = "";
this.state.values["data_file_name"] = "";
this.state.values["data_file_mimetype"] = "";
this.state.values["data_file_filetype"] = "";
this.state.values["keyword"] = "";
this.state.values["description"] = "";
}
getRows() {
const rows = [
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: this.renderFieldInputText("title", 50) },
{ label: "- [en]File Name[/en][ja]ファイル名[/ja]", value: this.renderFieldInputText("data_file_name", 50) },
{ label: "- [en]MIME Type[/en][ja]MIMEタイプ[/ja]", value: this.renderFieldInputText("data_file_mimetype", 50) },
{ label: "- [en]File Type[/en][ja]ファイルタイプ[/ja]", value: this.renderFieldInputText("data_file_filetype", 20) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: this.renderFieldInputText("keyword", 50) },
{ label: "[en]Description[/en][ja]概要[/ja]", value: this.renderFieldInputText("description", 50) },
];
return rows;
}
}
export default FilesAdvancedSearch;

View File

@@ -0,0 +1,32 @@
import React from "react";
import Functions from "../../../functions";
import { ItemFiles } from "../../lib/ItemUtil";
import DetailBase from "../lib/DetailBase";
import ItemTypeField from "../lib/field";
class FilesDetail extends DetailBase {
getFields() {
const { lang, type } = this.props;
const item = this.props.item as ItemFiles;
return [
{ label: "ID", value: item.doi },
{ label: "[en]Language[/en][ja]言語[/ja]", value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: Functions.mlang(item.title, lang) },
{ label: "[en]Last Modified Date[/en][ja]最終更新日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
{ label: "[en]Created Date[/en][ja]作成日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
{ label: "[en]Contributor[/en][ja]登録者[/ja]", value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
{ label: "[en]Item Type[/en][ja]アイテムタイプ[/ja]", value: item.item_type_display_name },
{ label: "[en]Change Log(History)[/en][ja]変更履歴[/ja]", value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
{ label: "[en]Data File[/en][ja]データファイル[/ja]", value: <ItemTypeField.ItemFile lang={lang} file={item.file} ftype="files_file" type={type} /> },
{ label: "- [en]File Name[/en][ja]ファイル名[/ja]", value: item.data_file_name },
{ label: "- [en]MIME Type[/en][ja]MIMEタイプ[/ja]", value: item.data_file_mimetype },
{ label: "- [en]File Type[/en][ja]ファイルタイプ[/ja]", value: item.data_file_filetype },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} /> },
{ label: "[en]Description[/en][ja]概要[/ja]", value: <ItemTypeField.Description lang={lang} description={item.description} /> },
{ label: "Index", value: <ItemTypeField.ItemIndex lang={lang} index={item.index} type={type} /> },
{ label: "[en]Related to[/en][ja]関連アイテム[/ja]", value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} type={type} /> },
];
}
}
export default FilesDetail;

View File

@@ -0,0 +1,31 @@
import React from "react";
import { Link } from "react-router-dom";
import Functions from "../../../functions";
import iconFile from "../../assets/images/icon_files.gif";
import { ItemFiles } from "../../lib/ItemUtil";
import Contributer from "../lib/field/Contributer";
import ListBase, { ListBaseProps } from "../lib/ListBase";
class FilesList extends ListBase {
constructor(props: ListBaseProps) {
super(props);
this.label = "Files";
this.icon = iconFile;
}
renderBody() {
const { lang } = this.props;
const item = this.props.item as ItemFiles;
return (
<>
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
<br />
<Contributer lang={lang} uname={item.uname} name={item.name} />
<br />
{item.data_file_mimetype}
</>
);
}
}
export default FilesList;

View File

@@ -0,0 +1,16 @@
import iconFile from "../../assets/images/icon_files.gif";
import { ItemFilesSubTypes } from "../../lib/ItemUtil";
import TopBase, { TopBaseProps } from "../lib/TopBase";
class FilesTop extends TopBase {
constructor(props: TopBaseProps) {
super(props);
this.type = "files";
this.label = "Files";
this.icon = iconFile;
this.description = "[en]Various type of File.[/en][ja]ファイル[/ja]";
this.subTypes = ItemFilesSubTypes;
}
}
export default FilesTop;

View File

@@ -0,0 +1,3 @@
const FilesUtil = {};
export default FilesUtil;

View File

@@ -0,0 +1,13 @@
import FilesAdvancedSearch from "./FilesAdvancedSearch";
import FilesDetail from "./FilesDetail";
import FilesList from "./FilesList";
import FilesTop from "./FilesTop";
const ItemTypeFiles = {
Top: FilesTop,
List: FilesList,
Detail: FilesDetail,
AdvancedSearch: FilesAdvancedSearch,
};
export default ItemTypeFiles;

View File

@@ -0,0 +1,182 @@
import React from 'react';
import { MultiLang, BrainAtlasType } from '../../config';
import AdvancedSearchQuery from '../lib/AdvancedSearchQuery';
import { Item, ItemBinder, ItemBook, ItemConference, ItemData, ItemFiles, ItemModel, ItemPaper, ItemPresentation, ItemSimulator, ItemStimulus, ItemTool, ItemUrl, ItemMemo } from '../lib/ItemUtil';
import ItemTypeBinder from './binder';
import ItemTypeBook from './book';
import ItemTypeConference from './conference';
import ItemTypeData from './data';
import ItemTypeFiles from './files';
import ItemTypeMemo from './memo';
import ItemTypeModel from './model';
import ItemTypePaper from './paper';
import ItemTypePresentation from './presentation';
import ItemTypeSimulator from './simulator';
import ItemTypeStimulus from './stimulus';
import ItemTypeTool from './tool';
import ItemTypeUrl from './url';
interface TopProps {
lang: MultiLang;
itemType: string;
type: BrainAtlasType;
}
const Top = (props: TopProps) => {
const { lang, itemType, type } = props;
switch (itemType) {
case 'xnpbinder':
return <ItemTypeBinder.Top lang={lang} type={type} />;
case 'xnpbook':
return <ItemTypeBook.Top lang={lang} type={type} />;
case 'xnpconference':
return <ItemTypeConference.Top lang={lang} type={type} />;
case 'xnpdata':
return <ItemTypeData.Top lang={lang} type={type} />;
case 'xnpfiles':
return <ItemTypeFiles.Top lang={lang} type={type} />;
case 'xnpmemo':
return <ItemTypeMemo.Top lang={lang} type={type} />;
case 'xnpmodel':
return <ItemTypeModel.Top lang={lang} type={type} />;
case 'xnppaper':
return <ItemTypePaper.Top lang={lang} type={type} />;
case 'xnppresentation':
return <ItemTypePresentation.Top lang={lang} type={type} />;
case 'xnpsimulator':
return <ItemTypeSimulator.Top lang={lang} type={type} />;
case 'xnpstimulus':
return <ItemTypeStimulus.Top lang={lang} type={type} />;
case 'xnptool':
return <ItemTypeTool.Top lang={lang} type={type} />;
case 'xnpurl':
return <ItemTypeUrl.Top lang={lang} type={type} />;
default:
return null;
}
}
interface ListProps {
lang: MultiLang;
item: Item;
type: BrainAtlasType;
}
const List = (props: ListProps) => {
const { lang, item, type } = props;
switch (item.item_type_name) {
case 'xnpbinder':
return <ItemTypeBinder.List lang={lang} item={item as ItemBinder} type={type} />;
case 'xnpbook':
return <ItemTypeBook.List lang={lang} item={item as ItemBook} type={type} />;
case 'xnpconference':
return <ItemTypeConference.List lang={lang} item={item as ItemConference} type={type} />;
case 'xnpdata':
return <ItemTypeData.List lang={lang} item={item as ItemData} type={type} />;
case 'xnpfiles':
return <ItemTypeFiles.List lang={lang} item={item as ItemFiles} type={type} />;
case 'xnpmemo':
return <ItemTypeMemo.List lang={lang} item={item as ItemMemo} type={type} />;
case 'xnpmodel':
return <ItemTypeModel.List lang={lang} item={item as ItemModel} type={type} />;
case 'xnppaper':
return <ItemTypePaper.List lang={lang} item={item as ItemPaper} type={type} />;
case 'xnppresentation':
return <ItemTypePresentation.List lang={lang} item={item as ItemPresentation} type={type} />;
case 'xnpsimulator':
return <ItemTypeSimulator.List lang={lang} item={item as ItemSimulator} type={type} />;
case 'xnpstimulus':
return <ItemTypeStimulus.List lang={lang} item={item as ItemStimulus} type={type} />;
case 'xnptool':
return <ItemTypeTool.List lang={lang} item={item as ItemTool} type={type} />;
case 'xnpurl':
return <ItemTypeUrl.List lang={lang} item={item as ItemUrl} type={type} />;
default:
return null;
}
}
interface DetailProps {
lang: MultiLang;
item: Item;
type: BrainAtlasType;
}
const Detail = (props: DetailProps) => {
const { lang, item, type } = props;
switch (item.item_type_name) {
case 'xnpbinder':
return <ItemTypeBinder.Detail lang={lang} item={item as ItemBinder} type={type} />;
case 'xnpbook':
return <ItemTypeBook.Detail lang={lang} item={item as ItemBook} type={type} />;
case 'xnpconference':
return <ItemTypeConference.Detail lang={lang} item={item as ItemConference} type={type} />;
case 'xnpdata':
return <ItemTypeData.Detail lang={lang} item={item as ItemData} type={type} />;
case 'xnpfiles':
return <ItemTypeFiles.Detail lang={lang} item={item as ItemFiles} type={type} />;
case 'xnpmemo':
return <ItemTypeMemo.Detail lang={lang} item={item as ItemMemo} type={type} />;
case 'xnpmodel':
return <ItemTypeModel.Detail lang={lang} item={item as ItemModel} type={type} />;
case 'xnppaper':
return <ItemTypePaper.Detail lang={lang} item={item as ItemPaper} type={type} />;
case 'xnppresentation':
return <ItemTypePresentation.Detail lang={lang} item={item as ItemPresentation} type={type} />;
case 'xnpsimulator':
return <ItemTypeSimulator.Detail lang={lang} item={item as ItemSimulator} type={type} />;
case 'xnpstimulus':
return <ItemTypeStimulus.Detail lang={lang} item={item as ItemStimulus} type={type} />;
case 'xnptool':
return <ItemTypeTool.Detail lang={lang} item={item as ItemTool} type={type} />;
case 'xnpurl':
return <ItemTypeUrl.Detail lang={lang} item={item as ItemUrl} type={type} />;
default:
return null;
}
}
interface AdvancedSearchProps {
lang: MultiLang;
type: string;
query: AdvancedSearchQuery;
}
const AdvancedSearch = (props: AdvancedSearchProps) => {
const { lang, type, query } = props;
switch (type) {
case 'xnpbinder':
return <ItemTypeBinder.AdvancedSearch lang={lang} query={query} />;
case 'xnpbook':
return <ItemTypeBook.AdvancedSearch lang={lang} query={query} />;
case 'xnpconference':
return <ItemTypeConference.AdvancedSearch lang={lang} query={query} />;
case 'xnpdata':
return <ItemTypeData.AdvancedSearch lang={lang} query={query} />;
case 'xnpfiles':
return <ItemTypeFiles.AdvancedSearch lang={lang} query={query} />;
case 'xnpmemo':
return <ItemTypeMemo.AdvancedSearch lang={lang} query={query} />;
case 'xnpmodel':
return <ItemTypeModel.AdvancedSearch lang={lang} query={query} />;
case 'xnppaper':
return <ItemTypePaper.AdvancedSearch lang={lang} query={query} />;
case 'xnppresentation':
return <ItemTypePresentation.AdvancedSearch lang={lang} query={query} />;
case 'xnpsimulator':
return <ItemTypeSimulator.AdvancedSearch lang={lang} query={query} />;
case 'xnpstimulus':
return <ItemTypeStimulus.AdvancedSearch lang={lang} query={query} />;
case 'xnptool':
return <ItemTypeTool.AdvancedSearch lang={lang} query={query} />;
case 'xnpurl':
return <ItemTypeUrl.AdvancedSearch lang={lang} query={query} />;
default:
return null;
}
}
const ItemType = {
Top,
List,
Detail,
AdvancedSearch
}
export default ItemType;

View File

@@ -0,0 +1,195 @@
import React, { ChangeEvent } from "react";
import { MultiLang } from "../../../config";
import Functions from "../../../functions";
import AdvancedSearchQuery from "../../lib/AdvancedSearchQuery";
import { ItemSubTypes } from "../../lib/ItemUtil";
export interface AdvancedSearchBaseProps {
lang: MultiLang;
query: AdvancedSearchQuery;
}
interface State {
show: boolean;
values: any;
}
class AdvancedSearchBase extends React.Component<AdvancedSearchBaseProps, State> {
protected type: string = "base";
protected title: string = "Base";
protected query: AdvancedSearchQuery;
protected ignoreKeys: string[] = [];
constructor(props: AdvancedSearchBaseProps) {
super(props);
this.state = {
show: false,
values: {},
};
this.query = props.query;
this.handleChangeTitleCheck = this.handleChangeTitleCheck.bind(this);
}
updateQuery(key: string, value: string) {
if (this.ignoreKeys.includes(key)) {
this.query.delete(this.type, key);
} else {
this.query.set(this.type, key, value);
}
}
setIgnoreKey(key: string) {
if (!this.ignoreKeys.includes(key)) {
this.ignoreKeys = this.ignoreKeys.concat(key);
}
this.query.delete(this.type, key);
}
deleteIgnoreKey(key: string) {
if (this.ignoreKeys.includes(key)) {
this.ignoreKeys = this.ignoreKeys.filter((v) => {
return v !== key;
});
}
this.query.set(this.type, key, this.state.values[key]);
}
updateField(key: string, value: string) {
let values = Object.assign({}, this.state.values);
values[key] = value;
this.updateQuery(key, value);
this.setState({ values });
}
handleChangeTitleCheck(e: ChangeEvent<HTMLInputElement>) {
const show = e.target.checked;
if (show) {
Object.keys(this.state.values).forEach((key) => {
const value = this.state.values[key];
this.updateQuery(key, value);
});
} else {
this.query.deleteType(this.type);
}
this.setState({ show });
}
getRows(): { label: string; value: JSX.Element }[] {
return [];
}
renderFieldInputText(key: string, size: number) {
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
this.updateField(key, e.target.value);
};
return <input className="fieldInputText" type="text" value={this.state.values[key]} size={size} onChange={onChange} />;
}
renderFieldSelect(key: string, values: ItemSubTypes<any>) {
const onChange = (e: ChangeEvent<HTMLSelectElement>) => {
this.updateField(key, e.target.value);
};
const options = values.map(({ type, label }, i) => {
return (
<option key={i} value={type}>
{label}
</option>
);
});
return (
<select className="fieldSelect" value={this.state.values[key]} onChange={onChange}>
<option value="">Any</option>
{options}
</select>
);
}
renderFieldDate(label: string, keyYear: string, keyMonth: string, keyMday: string) {
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.checked) {
this.deleteIgnoreKey(keyYear);
keyMonth !== "" && this.deleteIgnoreKey(keyMonth);
keyMday !== "" && this.deleteIgnoreKey(keyMday);
} else {
this.setIgnoreKey(keyYear);
keyMonth !== "" && this.setIgnoreKey(keyMonth);
keyMday !== "" && this.setIgnoreKey(keyMday);
}
};
const month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const monthOptions = month.map((value, i) => {
return (
<option key={i} value={i + 1}>
{value}
</option>
);
});
let mdayOptions: JSX.Element[] = [];
for (let i = 1; i <= 31; i++) {
mdayOptions.push(
<option key={i} value={i}>
{i}
</option>
);
}
return (
<div className="fieldDate">
<input type="checkbox" onChange={onChange} />
{label.length !== 0 && <label className="fieldDateLabel">{label}</label>}
{keyMonth !== "" && (
<select value={this.state.values[keyMonth]} onChange={(e) => this.updateField(keyMonth, e.target.value)}>
{monthOptions}
</select>
)}
{keyMday !== "" && (
<select value={this.state.values[keyMday]} onChange={(e) => this.updateField(keyMday, e.target.value)}>
{mdayOptions}
</select>
)}
<input type="text" value={this.state.values[keyYear]} size={5} onChange={(e) => this.updateField(keyYear, e.target.value)} />
</div>
);
}
renderBody() {
const { lang } = this.props;
if (this.state.show === false) {
return null;
}
const rows = this.getRows();
const fields = rows.map((value, idx) => {
const evenodd = idx % 2 === 0 ? "even" : "odd";
return (
<tr key={idx}>
<td className="head">{Functions.mlang(value.label, lang)}</td>
<td className={evenodd}>{value.value}</td>
</tr>
);
});
return (
<table className="itemtypeFields outer">
<tbody>{fields}</tbody>
</table>
);
}
render() {
return (
<div className="itemtype">
<table className="itemtypeName outer">
<tbody>
<tr>
<th align="left">
<input type="checkbox" checked={this.state.show} onChange={this.handleChangeTitleCheck} />
{this.title}
</th>
</tr>
</tbody>
</table>
{this.renderBody()}
</div>
);
}
}
export default AdvancedSearchBase;

View File

@@ -0,0 +1,41 @@
import React, { ReactNode } from "react";
import { BrainAtlasType, MultiLang } from "../../../config";
import Functions from "../../../functions";
import { Item } from "../../lib/ItemUtil";
export interface DetailBaseField {
label: string;
value: ReactNode;
}
export interface DetailBaseProps {
lang: MultiLang;
item: Item;
type: BrainAtlasType;
}
class DetailBase extends React.Component<DetailBaseProps> {
getFields(): DetailBaseField[] {
return [];
}
render() {
const { lang } = this.props;
const elements = this.getFields().map((value, idx) => {
const evenodd = idx % 2 === 0 ? "even" : "odd";
return (
<tr key={idx}>
<td className="head">{Functions.mlang(value.label, lang)}</td>
<td className={evenodd}>{value.value}</td>
</tr>
);
});
return (
<table className="outer itemDetail">
<tbody>{elements}</tbody>
</table>
);
}
}
export default DetailBase;

View File

@@ -0,0 +1,48 @@
import React from "react";
import { BrainAtlasType, MultiLang } from "../../../config";
import ItemUtil, { Item } from "../../lib/ItemUtil";
import SimPFLinkIcon from "./field/SimPFLinkIcon";
export interface ListBaseProps {
lang: MultiLang;
item: Item;
type: BrainAtlasType;
}
class ListBase extends React.Component<ListBaseProps> {
protected label = "";
protected icon = "";
protected url: string;
protected simpfLinkUrl: string;
constructor(props: ListBaseProps) {
super(props);
this.url = ItemUtil.getUrl(props.type, props.item);
this.simpfLinkUrl = ItemUtil.getSimPFLinkUrl(props.item.item_id);
}
renderBody() {
return <></>;
}
render() {
const { lang } = this.props;
return (
<table>
<tbody>
<tr>
<td className="listIcon">
<img src={this.icon} alt={this.label} />
</td>
<td>{this.renderBody()}</td>
<td className="listExtra">
<SimPFLinkIcon lang={lang} url={this.simpfLinkUrl} isDetail={false} />
</td>
</tr>
</tbody>
</table>
);
}
}
export default ListBase;

View File

@@ -0,0 +1,53 @@
import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import { BrainAtlasType, MultiLang } from "../../../config";
import Functions from "../../../functions";
import ItemUtil, { ItemSubTypes } from "../../lib/ItemUtil";
export interface TopBaseProps {
lang: MultiLang;
type: BrainAtlasType;
}
class TopBase extends React.Component<TopBaseProps> {
protected type: string = "";
protected label: string = "";
protected icon: string = "";
protected description: string = "";
protected subTypes: ItemSubTypes<any> = [];
render() {
const { lang, type } = this.props;
const url = ItemUtil.getItemTypeSearchUrl(type, this.type, "");
const links = this.subTypes.map((subtype, i) => {
const url = ItemUtil.getItemTypeSearchUrl(type, this.type, subtype.type);
return (
<Fragment key={i}>
{i > 0 && " / "}
<Link to={url}>{subtype.label}</Link>
</Fragment>
);
});
return (
<div>
<table>
<tbody>
<tr>
<td>
<img src={this.icon} alt={this.label} />
</td>
<td className="itemTypeName">
<Link to={url}>{this.label}</Link>
</td>
</tr>
</tbody>
</table>
<hr />
<div>{Functions.mlang(this.description, lang)}</div>
{links}
</div>
);
}
}
export default TopBase;

View File

@@ -0,0 +1,29 @@
import React from "react";
import { MultiLang } from "../../../../config";
interface Props {
lang: MultiLang;
author: string[];
}
const Author: React.FC<Props> = (props: Props) => {
const { author } = props;
if (author.length === 0) {
return null;
}
const elements = author.map((value, idx) => {
const evenodd = idx % 2 === 0 ? "even" : "odd";
return (
<tr key={idx}>
<td className={evenodd}>{value}</td>
</tr>
);
});
return (
<table>
<tbody>{elements}</tbody>
</table>
);
};
export default Author;

View File

@@ -0,0 +1,34 @@
import React from "react";
import { MultiLang } from "../../../../config";
import Functions from "../../../../functions";
import { ItemBasicChangeLog } from "../../../lib/ItemUtil";
import DateTime from "./DateTime";
interface Props {
lang: MultiLang;
changelog: ItemBasicChangeLog[];
}
const ChangeLog: React.FC<Props> = (props: Props) => {
const { lang, changelog } = props;
if (changelog.length === 0) {
return null;
}
const elements = changelog.map((value, i) => {
return (
<tr key={i}>
<td>
<DateTime lang={lang} date={value.log_date} onlyDate={true} />
</td>
<td>{Functions.mlang(value.log, lang)}</td>
</tr>
);
});
return (
<table>
<tbody>{elements}</tbody>
</table>
);
};
export default ChangeLog;

View File

@@ -0,0 +1,18 @@
import React from "react";
import { MultiLang } from "../../../../config";
import Functions from "../../../../functions";
interface Props {
lang: MultiLang;
uname: string;
name: string;
}
const Contributer: React.FC<Props> = (props: Props) => {
const { lang, name, uname } = props;
const unsubscribed = "([en]Unsubscribed User[/en][ja]退会済みユーザ[/ja])";
const label = uname === "" ? unsubscribed : name === "" ? uname : name + " (" + uname + ")";
return <span>{Functions.mlang(label, lang)}</span>;
};
export default Contributer;

View File

@@ -0,0 +1,69 @@
import React from "react";
import { MultiLang } from "../../../../config";
export type CreativeCommonsType = "by" | "by-nc" | "by-nc-nd" | "by-nc-sa" | "by-nd" | "by-sa";
export const getCreativeCommonsType = (ccCommercialUse: number, ccModification: number): CreativeCommonsType => {
const cc = ccCommercialUse * 10 + ccModification;
switch (cc) {
case 0:
return "by-nc-nd";
case 1:
return "by-nc-sa";
case 2:
return "by-nc";
case 10:
return "by-nd";
case 11:
return "by-sa";
case 12:
default:
return "by";
}
};
interface Props {
lang: MultiLang;
type: CreativeCommonsType;
}
const CreativeCommons: React.FC<Props> = (props: Props) => {
const { type } = props;
const url = "http://creativecommons.org/licenses/" + type + "/4.0/";
const logoUrl = "https://i.creativecommons.org/l/" + type + "/4.0/88x31.png";
const labels = {
by: "Attribution",
nc: "NonCommercial",
nd: "NoDerivatives",
sa: "ShareAlike",
};
const label = type
.split("-")
.map((value) => {
const prop = value as "by" | "nc" | "nd" | "sa";
return labels[prop];
})
.join("-");
return (
<table style={{ borderCollapse: "separate", borderSpacing: "5px" }}>
<tbody>
<tr>
<td>
<a href={url} target="_blank" rel="license noopener noreferrer">
<img alt="Creative Commons License" src={logoUrl} />
</a>
</td>
<td>
This work is licensed under a{" "}
<a href={url} target="_blank" rel="license noopener noreferrer">
Criative Commons {label} 4.0 International License
</a>
.
</td>
</tr>
</tbody>
</table>
);
};
export default CreativeCommons;

View File

@@ -0,0 +1,21 @@
import moment from "moment";
import React from "react";
import { MultiLang } from "../../../../config";
interface Props {
lang: MultiLang;
date: number;
onlyDate?: boolean;
}
const DateTime: React.FC<Props> = (props: Props) => {
const { date, onlyDate } = props;
const d = moment(new Date(date * 1000));
let format = "MMM D, Y";
if (typeof onlyDate === "undefined" || !onlyDate) {
format += " HH:mm:ss";
}
return <span>{d.format(format)}</span>;
};
export default DateTime;

View File

@@ -0,0 +1,18 @@
import React from "react";
import XoopsCode from "../../../../common/lib/XoopsCode";
import { MultiLang } from "../../../../config";
interface Props {
lang: MultiLang;
description: string;
className?: string;
}
const Description: React.FC<Props> = (props: Props) => {
const { lang, description, className } = props;
const textarea = <XoopsCode lang={lang} text={description} dobr={true} />;
const name = typeof className === "undefined" ? "description" : className;
return <div className={name}>{textarea}</div>;
};
export default Description;

View File

@@ -0,0 +1,21 @@
.downloadButton {
display: inline-block;
padding: 7px 20px;
text-decoration: none !important;
font-weight: normal !important;
background: #f0f0f0;
color: #000 !important;
border: solid 1px #e0e0e0;
box-shadow: 2px 2px #bbbbbb;
border-radius: 5px;
}
.downloadButton:hover {
background: #e8e8e8 !important;
border: solid 1px #cccccc;
}
.downloadButton:active {
transform: translate(2px, 2px);
box-shadow: none;
}

View File

@@ -0,0 +1,57 @@
import React from "react";
import { BrainAtlasType, MultiLang } from "../../../../config";
import ItemUtil, { ItemBasicFile } from "../../../lib/ItemUtil";
import styles from "./FileDownloadButton.module.css";
import LicenseAgreementDialog from "./LicenseAgreementDialog";
interface Props {
lang: MultiLang;
file: ItemBasicFile;
rights: string;
useCc: number;
ccCommercialUse: number;
ccModification: number;
type: BrainAtlasType;
}
interface State {
show: boolean;
}
class FileDownloadButton extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
show: false,
};
this.handleClickDownload = this.handleClickDownload.bind(this);
this.unsetShow = this.unsetShow.bind(this);
}
handleClickDownload(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
if (this.props.rights !== "") {
e.stopPropagation();
e.preventDefault();
this.setState({ show: true });
}
}
unsetShow() {
this.setState({ show: false });
}
render() {
const { lang, file, rights, useCc, ccCommercialUse, ccModification, type } = this.props;
const url = ItemUtil.getFileUrl(type, this.props.file);
return (
<>
<a className={styles.downloadButton} href={url} download={file.original_file_name} target="_blank" rel="noopener noreferrer" onClick={this.handleClickDownload}>
Download
</a>
<LicenseAgreementDialog lang={lang} file={file} rights={rights} useCc={useCc} ccCommercialUse={ccCommercialUse} ccModification={ccModification} show={this.state.show} unsetShow={this.unsetShow} type={type} />
</>
);
}
}
export default FileDownloadButton;

View File

@@ -0,0 +1,17 @@
import React from "react";
import { MultiLang } from "../../../../config";
interface Props {
lang: MultiLang;
size: number;
}
const FileSize: React.FC<Props> = (props: Props) => {
const { size } = props;
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const power = size > 0 ? Math.floor(Math.log(size) / Math.log(1024)) : 0;
const label = Math.round((size / Math.pow(1024, power)) * 10) / 10 + " " + units[power];
return <span>{label}</span>;
};
export default FileSize;

View File

@@ -0,0 +1,18 @@
import React from "react";
import { MultiLang } from "../../../../config";
interface Props {
lang: MultiLang;
keyword: string[];
}
const FreeKeyword: React.FC<Props> = (props: Props) => {
const { keyword } = props;
if (keyword.length === 0) {
return null;
}
const label = keyword.join(", ");
return <span>{label}</span>;
};
export default FreeKeyword;

View File

@@ -0,0 +1,70 @@
import React from "react";
import { BrainAtlasType, MultiLang } from "../../../../config";
import Functions from "../../../../functions";
import { ItemBasicFile } from "../../../lib/ItemUtil";
import DateTime from "./DateTime";
import FileDownloadButton from "./FileDownloadButton";
import FileSize from "./FileSize";
interface Props {
lang: MultiLang;
file: ItemBasicFile[];
ftype: string;
rights?: string;
useCc?: number;
ccCommercialUse?: number;
ccModification?: number;
downloadLimit?: number;
type: BrainAtlasType;
}
const ItemFile: React.FC<Props> = (props: Props) => {
const { lang, file, ftype, type } = props;
const rights = typeof props.rights === "undefined" ? "" : props.rights;
const useCc = typeof props.useCc === "undefined" ? 0 : props.useCc;
const ccCommercialUse = typeof props.ccCommercialUse === "undefined" ? 0 : props.ccCommercialUse;
const ccModification = typeof props.ccModification === "undefined" ? 0 : props.ccModification;
const downloadLimit = typeof props.downloadLimit === "undefined" ? 0 : props.downloadLimit;
const data = file.find((value) => {
return value.file_type_name === ftype;
});
if (typeof data === "undefined") {
return null;
}
const date = new Date(data.timestamp);
const timestamp = Math.floor(date.valueOf() / 1000);
return (
<div>
{data.original_file_name}
<br />
<table>
<tbody>
<tr>
<td>Type</td>
<td>: {data.mime_type}</td>
<td rowSpan={4}>{downloadLimit === 0 && <FileDownloadButton lang={lang} file={data} rights={rights} useCc={useCc} ccCommercialUse={ccCommercialUse} ccModification={ccModification} type={type} />}</td>
</tr>
<tr>
<td>Size</td>
<td>
: <FileSize lang={lang} size={data.file_size} />
</td>
</tr>
<tr>
<td>Last updated</td>
<td>
: <DateTime lang={lang} date={timestamp} onlyDate={true} />
</td>
</tr>
</tbody>
</table>
{downloadLimit === 1 && (
<>
<br />({Functions.mlang("[en]File has been removed[/en][ja]ファイルは削除されました[/ja]", lang)})
</>
)}
</div>
);
};
export default ItemFile;

View File

@@ -0,0 +1,37 @@
import React from "react";
import { Link } from "react-router-dom";
import { BrainAtlasType, MultiLang } from "../../../../config";
import Functions from "../../../../functions";
import IndexUtil from "../../../lib/IndexUtil";
import { ItemBasicIndex } from "../../../lib/ItemUtil";
interface Props {
lang: MultiLang;
index: ItemBasicIndex[];
type: BrainAtlasType;
}
const ItemIndex: React.FC<Props> = (props: Props) => {
const { lang, index, type } = props;
if (index.length === 0) {
return null;
}
const elements = index.map((value, idx) => {
const evenodd = idx % 2 === 0 ? "even" : "odd";
const url = IndexUtil.getUrl(type, value.index_id);
return (
<tr key={value.index_id}>
<td className={evenodd}>
<Link to={url}>{Functions.mlang(value.title, lang)}</Link>
</td>
</tr>
);
});
return (
<table>
<tbody>{elements}</tbody>
</table>
);
};
export default ItemIndex;

View File

@@ -0,0 +1,35 @@
import React from "react";
import { MultiLang } from "../../../../config";
import Functions from "../../../../functions";
import { ItemBasicLang } from "../../../lib/ItemUtil";
interface Props {
lang: MultiLang;
itemLang: ItemBasicLang;
}
const Language: React.FC<Props> = (props: Props) => {
const { lang, itemLang } = props;
const langStr = {
eng: "[en]English[/en][ja]英語[/ja]",
jpn: "[en]Japanese[/en][ja]日本語[/ja]",
fra: "[en]French[/en][ja]フランス語[/ja]",
deu: "[en]German[/en][ja]ドイツ語[/ja]",
esl: "[en]Spanish[/en][ja]スペイン語[/ja]",
ita: "[en]Italian[/en][ja]イタリア語[/ja]",
dut: "[en]Dutch[/en][ja]オランダ語[/ja]",
sve: "[en]Swedish[/en][ja]スウェーデン語[/ja]",
nor: "[en]Norwegian[/en][ja]ノルウェー語[/ja]",
dan: "[en]Danish[/en][ja]デンマーク語[/ja]",
fin: "[en]Finnish[/en][ja]フィンランド語[/ja]",
por: "[en]Portuguese[/en][ja]ポルトガル語[/ja]",
chi: "[en]Chinese[/en][ja]中国語[/ja]",
kor: "[en]Korean[/en][ja]韓国語[/ja]",
};
if (!(itemLang in langStr)) {
return null;
}
return <span>{Functions.mlang(langStr[itemLang], lang)}</span>;
};
export default Language;

View File

@@ -0,0 +1,38 @@
.overlay {
position: fixed;
z-index: 90;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: #000000;
opacity: 0.8;
}
.dialog {
position: fixed;
background-color: #d8d8d8;
width: 570px;
z-index: 100;
top: 50%;
left: 50%;
right: auto;
bottom: auto;
margin: 0 auto;
padding: 20px;
transform: translate(-50%, -50%);
border-radius: 5px;
}
.box {
background-color: #fff;
padding: 10px;
}
.download {
text-align: center;
}
.download button {
margin: 5px 5px 0;
}

View File

@@ -0,0 +1,136 @@
import React, { ChangeEvent, MouseEvent } from "react";
import { Modal } from "react-overlays";
import { BrainAtlasType, MultiLang } from "../../../../config";
import Functions from "../../../../functions";
import ItemUtil, { ItemBasicFile } from "../../../lib/ItemUtil";
import DateTime from "./DateTime";
import FileSize from "./FileSize";
import styles from "./LicenseAgreementDialog.module.css";
import Rights from "./Rights";
interface Props {
lang: MultiLang;
file: ItemBasicFile;
rights: string;
useCc: number;
ccCommercialUse: number;
ccModification: number;
show: boolean;
unsetShow: () => void;
type: BrainAtlasType;
}
interface State {
show: boolean;
disabled: boolean;
}
class LicenseAgreementDialog extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
show: props.show,
disabled: false,
};
this.handleChangeCheckbox = this.handleChangeCheckbox.bind(this);
this.handleClickDownload = this.handleClickDownload.bind(this);
this.handleClickCancel = this.handleClickCancel.bind(this);
}
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
if (nextProps.show && !prevState.show) {
return { disabled: true, show: nextProps.show };
}
return null;
}
handleChangeCheckbox(e: ChangeEvent<HTMLInputElement>) {
const disabled = e.target.value === "0";
this.setState({ disabled });
}
handleClickDownload(e: MouseEvent<HTMLButtonElement>) {
this.props.unsetShow();
this.setState({ show: false });
}
handleClickCancel() {
this.props.unsetShow();
this.setState({ show: false });
}
renderBackdrop(props: any) {
return <div className={styles.overlay} {...props} />;
}
render() {
const { lang, type } = this.props;
const date = new Date(this.props.file.timestamp);
const timestamp = Math.floor(date.valueOf() / 1000);
const url = ItemUtil.getFileUrl(type, this.props.file);
return (
<Modal className={styles.dialog} show={this.state.show} onHide={this.handleClickCancel} renderBackdrop={this.renderBackdrop}>
<div>
<div>
{Functions.mlang("[en]Download file information[/en][ja]ダウンロードするファイルの情報[/ja]", lang)}
<div className={styles.box}>
{this.props.file.original_file_name}
<br />
<table>
<tbody>
<tr>
<td>Type</td>
<td>: {this.props.file.mime_type}</td>
</tr>
<tr>
<td>Size</td>
<td>
: <FileSize lang={lang} size={this.props.file.file_size} />
</td>
</tr>
<tr>
<td>Last updated</td>
<td>
: <DateTime lang={lang} date={timestamp} onlyDate={true} />
</td>
</tr>
</tbody>
</table>
</div>
</div>
<br />
<div>
{Functions.mlang("[en]License agreement[/en][ja]ファイルのライセンス[/ja]", lang)}
<div className={styles.box}>
{Functions.mlang("[en]Please read the following license agreement carefully.[/en][ja]このファイルには下記のライセンスが設定されています。[/ja]", lang)}
<div>
<Rights lang={lang} rights={this.props.rights} useCc={this.props.useCc} ccCommercialUse={this.props.ccCommercialUse} ccModification={this.props.ccModification} />
<input type="radio" name="radio_license" value="1" onChange={this.handleChangeCheckbox} checked={!this.state.disabled} />
{Functions.mlang("[en]I accept the terms in the license agreement.[/en][ja]ライセンスに同意します。[/ja]", lang)}
<br />
<input type="radio" name="radio_license" value="0" onChange={this.handleChangeCheckbox} checked={this.state.disabled} />
{Functions.mlang("[en]I do not accept the terms in the license agreement.[/en][ja]ライセンスに同意しません。[/ja]", lang)}
<br />
</div>
</div>
</div>
<br />
<div className={styles.download}>
Acceptance is needed to download this file.
<br />
<a href={url} download={this.props.file.original_file_name}>
<button className="formButton" onClick={this.handleClickDownload} disabled={this.state.disabled}>
Download
</button>
</a>
<button className="formButton" onClick={this.handleClickCancel}>
Cancel
</button>
</div>
</div>
</Modal>
);
}
}
export default LicenseAgreementDialog;

View File

@@ -0,0 +1,23 @@
.previewBox {
text-align: center;
margin: 10px;
}
.previewBox::after {
content: "";
display: block;
clear: both;
}
.preview {
width: 200px;
margin: 0 auto;
float: left;
text-align: center;
}
.preview caption {
width: 200px;
margin: 5px;
font-size: 80%;
}

View File

@@ -0,0 +1,86 @@
import React from "react";
import Lightbox from "react-image-lightbox";
import "react-image-lightbox/style.css";
import { BrainAtlasType, MultiLang } from "../../../../config";
import Functions from "../../../../functions";
import ItemUtil, { ItemBasicFile } from "../../../lib/ItemUtil";
import styles from "./Preview.module.css";
interface Props {
lang: MultiLang;
file: ItemBasicFile[];
type: BrainAtlasType;
}
interface State {
isOpen: boolean;
imageIndex: number;
}
class Preview extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
isOpen: false,
imageIndex: 0,
};
}
render() {
const { lang, file, type } = this.props;
const data = file.filter((value) => {
return value.file_type_name === "preview";
});
if (data.length === 0) {
return null;
}
let imageUrls: string[] = [];
const previews = data.map((value, idx) => {
const fileUrl = ItemUtil.getFileUrl(type, value);
const previewUrl = ItemUtil.getPreviewFileUrl(type, value);
const caption = Functions.mlang(value.caption, lang);
imageUrls.push(fileUrl);
return (
<figure key={value.file_id} className={styles.preview}>
<a
href={fileUrl}
download={value.original_file_name}
onClick={(e) => {
e.preventDefault();
this.setState({ isOpen: true, imageIndex: idx });
}}
>
<img src={previewUrl} alt={caption} />
</a>
<figcaption>{caption}</figcaption>
</figure>
);
});
const { isOpen, imageIndex } = this.state;
return (
<>
<div className={styles.previewBox}>{previews}</div>
{isOpen && (
<Lightbox
mainSrc={imageUrls[imageIndex]}
nextSrc={imageUrls[(imageIndex + 1) % data.length]}
prevSrc={imageUrls[(imageIndex + data.length - 1) % data.length]}
onCloseRequest={() => this.setState({ isOpen: false })}
onMovePrevRequest={() =>
this.setState({
imageIndex: (imageIndex + data.length - 1) % data.length,
})
}
onMoveNextRequest={() =>
this.setState({
imageIndex: (imageIndex + 1) % data.length,
})
}
/>
)}
</>
);
}
}
export default Preview;

View File

@@ -0,0 +1,19 @@
import React from "react";
import { MultiLang } from "../../../../config";
import DateTime from "./DateTime";
interface Props {
lang: MultiLang;
year: number;
month: number;
mday: number;
}
const PublicationDate: React.FC<Props> = (props: Props) => {
const { lang, year, month, mday } = props;
const d = new Date(year + "-" + month + "-" + mday);
const timestamp = Math.floor(d.valueOf() / 1000);
return <DateTime lang={lang} date={timestamp} onlyDate={true} />;
};
export default PublicationDate;

View File

@@ -0,0 +1,15 @@
import React from "react";
import { MultiLang } from "../../../../config";
import Description from "./Description";
interface Props {
lang: MultiLang;
readme: string;
}
const Readme: React.FC<Props> = (props: Props) => {
const { lang, readme } = props;
return <Description lang={lang} description={readme} className="readme" />;
};
export default Readme;

View File

@@ -0,0 +1,82 @@
import React from "react";
import { BrainAtlasType, MultiLang } from "../../../../config";
import ItemType from "../../../item-type";
import ItemUtil from "../../../lib/ItemUtil";
interface Props {
lang: MultiLang;
relatedTo: number[];
type: BrainAtlasType;
}
interface State {
elements: JSX.Element[];
}
class RelatedTo extends React.Component<Props, State> {
private isActive: boolean;
constructor(props: Props) {
super(props);
this.state = {
elements: [],
};
this.isActive = false;
}
componentDidMount() {
this.isActive = true;
this.updateElements(this.props.relatedTo);
}
componentDidUpdate(prevProps: Props) {
if (JSON.stringify(this.props.relatedTo) !== JSON.stringify(prevProps.relatedTo)) {
this.updateElements(this.props.relatedTo);
}
}
componentWillUnmount() {
this.isActive = false;
}
updateElements(relatedTo: number[]) {
const { lang, type } = this.props;
if (relatedTo.length === 0) {
this.setState({ elements: [] });
} else {
ItemUtil.getList(type, relatedTo, (results) => {
const elements = results.data.map((item, idx) => {
const evenodd = idx % 0 ? "even" : "odd";
return (
<tr key={item.item_id}>
<td className={evenodd}>
<ItemType.List lang={lang} item={item} type={type} />
</td>
</tr>
);
});
if (this.isActive) {
this.setState({ elements });
}
});
}
}
render() {
if (this.state.elements.length === 0) {
return null;
}
return (
<table className="listTable">
<tbody>
<tr>
<th>Item summary</th>
</tr>
{this.state.elements}
</tbody>
</table>
);
}
}
export default RelatedTo;

View File

@@ -0,0 +1,23 @@
import React from "react";
import { MultiLang } from "../../../../config";
import CreativeCommons, { getCreativeCommonsType } from "./CreativeCommons";
import Description from "./Description";
interface Props {
lang: MultiLang;
rights: string;
useCc: number;
ccCommercialUse: number;
ccModification: number;
}
const Rights: React.FC<Props> = (props: Props) => {
const { lang, rights, useCc, ccCommercialUse, ccModification } = props;
if (useCc === 0) {
return <Description lang={lang} description={rights} className="rights" />;
}
const ccType = getCreativeCommonsType(ccCommercialUse, ccModification);
return <CreativeCommons lang={lang} type={ccType} />;
};
export default Rights;

View File

@@ -0,0 +1,25 @@
import React from "react";
import { MultiLang } from "../../../../config";
import imageButton from "../../../assets/images/simpf_button.png";
interface Props {
lang: MultiLang;
url: string;
isDetail: boolean;
}
const SimPFLinkIcon: React.FC<Props> = (props: Props) => {
const { url, isDetail } = props;
const title = "Online Simulation";
const size = isDetail ? 64 : 35;
if (url === "") {
return null;
}
return (
<a href={url} target="_blank" rel="noopener noreferrer" title={title}>
<img src={imageButton} alt={title} width={size} height={size} />
</a>
);
};
export default SimPFLinkIcon;

View File

@@ -0,0 +1,39 @@
import Author from "./Author";
import ChangeLog from "./ChangeLog";
import Contributer from "./Contributer";
import CreativeCommons from "./CreativeCommons";
import DateTime from "./DateTime";
import Description from "./Description";
import FileDownloadButton from "./FileDownloadButton";
import FileSize from "./FileSize";
import FreeKeyword from "./FreeKeyword";
import ItemFile from "./ItemFile";
import ItemIndex from "./ItemIndex";
import Language from "./Language";
import Preview from "./Preview";
import PublicationDate from "./PublicationDate";
import Readme from "./Readme";
import RelatedTo from "./RelatedTo";
import Rights from "./Rights";
const ItemTypeField = {
Author,
ChangeLog,
Contributer,
CreativeCommons,
DateTime,
Description,
FileDownloadButton,
FileSize,
FreeKeyword,
ItemFile,
ItemIndex,
Language,
Preview,
PublicationDate,
Readme,
RelatedTo,
Rights,
};
export default ItemTypeField;

View File

@@ -0,0 +1,27 @@
import AdvancedSearchBase, { AdvancedSearchBaseProps } from "../lib/AdvancedSearchBase";
class MemoAdvancedSearch extends AdvancedSearchBase {
constructor(props: AdvancedSearchBaseProps) {
super(props);
this.type = "memo";
this.title = "Memo";
this.state.values["title"] = "";
this.state.values["keyword"] = "";
this.state.values["description"] = "";
this.state.values["doi"] = "";
this.state.values["item_link"] = "";
}
getRows() {
const rows = [
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: this.renderFieldInputText("title", 50) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: this.renderFieldInputText("keyword", 50) },
{ label: "[en]Description[/en][ja]概要[/ja]", value: this.renderFieldInputText("description", 50) },
{ label: "ID", value: this.renderFieldInputText("doi", 50) },
{ label: "[en]Item Link[/en][ja]リンク[/ja]", value: this.renderFieldInputText("item_link", 50) },
];
return rows;
}
}
export default MemoAdvancedSearch;

View File

@@ -0,0 +1,32 @@
import React from "react";
import XoopsCode from "../../../common/lib/XoopsCode";
import Functions from "../../../functions";
import { ItemMemo } from "../../lib/ItemUtil";
import DetailBase from "../lib/DetailBase";
import ItemTypeField from "../lib/field";
class MemoDetail extends DetailBase {
getFields() {
const { lang, type } = this.props;
const item = this.props.item as ItemMemo;
const fields = [
{ label: "ID", value: item.doi },
{ label: "[en]Language[/en][ja]言語[/ja]", value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: Functions.mlang(item.title, lang) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} /> },
{ label: "[en]Description[/en][ja]概要[/ja]", value: <ItemTypeField.Description lang={lang} description={item.description} /> },
{ label: "[en]Last Modified Date[/en][ja]最終更新日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
{ label: "[en]Created Date[/en][ja]作成日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
{ label: "[en]Contributor[/en][ja]登録者[/ja]", value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
{ label: "[en]Item Type[/en][ja]アイテムタイプ[/ja]", value: item.item_type_display_name },
{ label: "[en]Change Log(History)[/en][ja]変更履歴[/ja]", value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
{ label: "[en]Item Link[/en][ja]リンク[/ja]", value: <XoopsCode lang={lang} text={item.item_link} /> },
{ label: "[en]Memo File[/en][ja]メモファイル[/ja]", value: <ItemTypeField.ItemFile lang={lang} file={item.file} ftype="memo_file" type={type} /> },
{ label: "Index", value: <ItemTypeField.ItemIndex lang={lang} index={item.index} type={type} /> },
{ label: "[en]Related to[/en][ja]関連アイテム[/ja]", value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} type={type} /> },
];
return fields;
}
}
export default MemoDetail;

View File

@@ -0,0 +1,30 @@
import React from "react";
import { Link } from "react-router-dom";
import XoopsCode from "../../../common/lib/XoopsCode";
import Functions from "../../../functions";
import iconFile from "../../assets/images/icon_memo.gif";
import { ItemMemo } from "../../lib/ItemUtil";
import ListBase, { ListBaseProps } from "../lib/ListBase";
class MemoList extends ListBase {
constructor(props: ListBaseProps) {
super(props);
this.label = "Memo";
this.icon = iconFile;
}
renderBody() {
const { lang } = this.props;
const item = this.props.item as ItemMemo;
const link = item.item_link !== "" ? <XoopsCode lang={lang} text={item.item_link} /> : null;
return (
<>
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
<br />
{link}
</>
);
}
}
export default MemoList;

View File

@@ -0,0 +1,14 @@
import iconFile from "../../assets/images/icon_memo.gif";
import TopBase, { TopBaseProps } from "../lib/TopBase";
class MemoTop extends TopBase {
constructor(props: TopBaseProps) {
super(props);
this.type = "memo";
this.label = "Memo";
this.icon = iconFile;
this.description = "[en]Personal Memo Pad.[/en][ja]汎用メモパッド[/ja]";
}
}
export default MemoTop;

View File

@@ -0,0 +1,13 @@
import MemoAdvancedSearch from "./MemoAdvancedSearch";
import MemoDetail from "./MemoDetail";
import MemoList from "./MemoList";
import MemoTop from "./MemoTop";
const ItemTypeMemo = {
Top: MemoTop,
List: MemoList,
Detail: MemoDetail,
AdvancedSearch: MemoAdvancedSearch,
};
export default ItemTypeMemo;

View File

@@ -0,0 +1,34 @@
import { ItemModelSubTypes } from "../../lib/ItemUtil";
import AdvancedSearchBase, { AdvancedSearchBaseProps } from "../lib/AdvancedSearchBase";
class ModelAdvancedSearch extends AdvancedSearchBase {
constructor(props: AdvancedSearchBaseProps) {
super(props);
this.type = "model";
this.title = "Model";
this.state.values["title"] = "";
this.state.values["keyword"] = "";
this.state.values["description"] = "";
this.state.values["doi"] = "";
this.state.values["model_type"] = "";
this.state.values["creator"] = "";
this.state.values["file.preview.caption"] = "";
this.state.values["file.model_data.original_file_name"] = "";
}
getRows() {
const rows = [
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: this.renderFieldInputText("title", 50) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: this.renderFieldInputText("keyword", 50) },
{ label: "[en]Description[/en][ja]概要[/ja]", value: this.renderFieldInputText("description", 50) },
{ label: "ID", value: this.renderFieldInputText("doi", 50) },
{ label: "[en]Model Type[/en][ja]モデルタイプ[/ja]", value: this.renderFieldSelect("model_type", ItemModelSubTypes) },
{ label: "[en]Creator[/en][ja]作成者[/ja]", value: this.renderFieldInputText("creator", 50) },
{ label: "[en]Caption[/en][ja]キャプション[/ja]", value: this.renderFieldInputText("file.preview.caption", 50) },
{ label: "[en]Model File[/en][ja]モデルファイル[/ja]", value: this.renderFieldInputText("file.model_data.original_file_name", 50) },
];
return rows;
}
}
export default ModelAdvancedSearch;

View File

@@ -0,0 +1,42 @@
import React from "react";
import Functions from "../../../functions";
import ItemUtil, { ItemModel } from "../../lib/ItemUtil";
import DetailBase from "../lib/DetailBase";
import ItemTypeField from "../lib/field";
import SimPFLinkIcon from "../lib/field/SimPFLinkIcon";
import ModelUtil from "./ModelUtil";
class ModelDetail extends DetailBase {
getFields() {
const { lang, type } = this.props;
const item = this.props.item as ItemModel;
const fields = [
{ label: "ID", value: item.doi },
{ label: "[en]Language[/en][ja]言語[/ja]", value: <ItemTypeField.Language lang={lang} itemLang={item.lang} /> },
{ label: "[en]Title[/en][ja]タイトル[/ja]", value: Functions.mlang(item.title, lang) },
{ label: "[en]Free Keywords[/en][ja]フリーキーワード[/ja]", value: <ItemTypeField.FreeKeyword lang={lang} keyword={item.keyword} /> },
{ label: "[en]Description[/en][ja]概要[/ja]", value: <ItemTypeField.Description lang={lang} description={item.description} /> },
{ label: "[en]Last Modified Date[/en][ja]最終更新日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.last_update_date} /> },
{ label: "[en]Created Date[/en][ja]作成日[/ja]", value: <ItemTypeField.DateTime lang={lang} date={item.creation_date} /> },
{ label: "[en]Contributor[/en][ja]登録者[/ja]", value: <ItemTypeField.Contributer lang={lang} uname={item.uname} name={item.name} /> },
{ label: "[en]Item Type[/en][ja]アイテムタイプ[/ja]", value: item.item_type_display_name },
{ label: "[en]Change Log(History)[/en][ja]変更履歴[/ja]", value: <ItemTypeField.ChangeLog lang={lang} changelog={item.changelog} /> },
{ label: "[en]Model Type[/en][ja]モデルタイプ[/ja]", value: <ModelUtil.ModelType lang={lang} type={item.model_type} /> },
{ label: "[en]Creator[/en][ja]作成者[/ja]", value: <ItemTypeField.Author lang={lang} author={item.creator} /> },
{ label: "[en]Preview[/en][ja]プレビュー[/ja]", value: <ItemTypeField.Preview lang={lang} file={item.file} type={type} /> },
{ label: "[en]Model File[/en][ja]モデルファイル[/ja]", value: <ItemTypeField.ItemFile lang={lang} file={item.file} ftype="model_data" rights={item.rights} useCc={item.use_cc} ccCommercialUse={item.cc_commercial_use} ccModification={item.cc_modification} downloadLimit={item.attachment_dl_limit} type={type} /> },
{ label: "Readme", value: <ItemTypeField.Readme lang={lang} readme={item.readme} /> },
{ label: "Rights", value: <ItemTypeField.Rights lang={lang} rights={item.rights} useCc={item.use_cc} ccCommercialUse={item.cc_commercial_use} ccModification={item.cc_modification} /> },
{ label: "Index", value: <ItemTypeField.ItemIndex lang={lang} index={item.index} type={type} /> },
{ label: "[en]Related to[/en][ja]関連アイテム[/ja]", value: <ItemTypeField.RelatedTo lang={lang} relatedTo={item.related_to} type={type} /> },
];
const simpfLinkUrl = ItemUtil.getSimPFLinkUrl(item.item_id);
if (simpfLinkUrl !== "") {
const field = { label: "Online Simulation", value: <SimPFLinkIcon lang={lang} url={simpfLinkUrl} isDetail={true} /> };
fields.splice(13, 0, field);
}
return fields;
}
}
export default ModelDetail;

View File

@@ -0,0 +1,36 @@
import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import Functions from "../../../functions";
import iconFile from "../../assets/images/icon_model.gif";
import { ItemModel } from "../../lib/ItemUtil";
import ListBase, { ListBaseProps } from "../lib/ListBase";
class ModelList extends ListBase {
constructor(props: ListBaseProps) {
super(props);
this.label = "Model";
this.icon = iconFile;
}
renderBody() {
const { lang } = this.props;
const item = this.props.item as ItemModel;
const authors = item.creator.map((author, i) => {
return (
<Fragment key={i}>
{i > 0 && ", "}
{Functions.mlang(author, lang)}
</Fragment>
);
});
return (
<>
<Link to={this.url}>{Functions.mlang(item.title, lang)}</Link>
<br />
{authors}
</>
);
}
}
export default ModelList;

View File

@@ -0,0 +1,16 @@
import iconFile from "../../assets/images/icon_model.gif";
import { ItemModelSubTypes } from "../../lib/ItemUtil";
import TopBase, { TopBaseProps } from "../lib/TopBase";
class ModelTop extends TopBase {
constructor(props: TopBaseProps) {
super(props);
this.type = "model";
this.label = "Model";
this.icon = iconFile;
this.description = "[en]Model programs/scripts.[/en][ja]モデル プログラム/スクリプト[/ja]";
this.subTypes = ItemModelSubTypes;
}
}
export default ModelTop;

View File

@@ -0,0 +1,25 @@
import React from "react";
import { MultiLang } from "../../../config";
import { ItemModelSubType, ItemModelSubTypes } from "../../lib/ItemUtil";
interface ModelTypeProps {
lang: MultiLang;
type: ItemModelSubType;
}
const ModelType: React.FC<ModelTypeProps> = (props: ModelTypeProps) => {
const { type } = props;
const subtype = ItemModelSubTypes.find((value) => {
return value.type === type;
});
if (typeof subtype === "undefined") {
return null;
}
return <span>{subtype.label}</span>;
};
const ModelUtil = {
ModelType,
};
export default ModelUtil;

View File

@@ -0,0 +1,13 @@
import ModelAdvancedSearch from "./ModelAdvancedSearch";
import ModelDetail from "./ModelDetail";
import ModelList from "./ModelList";
import ModelTop from "./ModelTop";
const ItemTypeModel = {
Top: ModelTop,
List: ModelList,
Detail: ModelDetail,
AdvancedSearch: ModelAdvancedSearch,
};
export default ItemTypeModel;

Some files were not shown because too many files have changed in this diff Show More