diff --git a/src/js/src/bookmark/app.html b/src/js/src/bookmark/app.html index 41606b9..7d524d1 100644 --- a/src/js/src/bookmark/app.html +++ b/src/js/src/bookmark/app.html @@ -2,133 +2,135 @@ - - - Solitude + + + Solitude -
-
- +
+ + +
+
+

グラフ画像部分

+

+ +

+
+ +

メニュー

+ +

+ +

+
    +
  • +
  • +
  • +
  • +
  • +
+
+
+

ヘッダー部分

+
+
+

編集部分

+
+
    +
  • id:
  • +
  • parent:
  • +
  • type:
  • +
  • label:
  • +
  • url:
  • +
+

+

    +
  • +
+

+
+
+
    +
  • id:
  • +
  • parent:
  • +
  • type:
  • +
  • label:
  • +
  • url:
  • +
+

+

    +
  • +
  • +
+

-
-
-

グラフ画像部分

-

- -

-
- -

メニュー

- -

- -

-
    -
  • -
  • -
  • -
  • -
  • -
-
-
-

ヘッダー部分

-
-
-

編集部分

-
-
    -
  • id:
  • -
  • parent:
  • -
  • type:
  • -
  • label:
  • -
  • url:
  • -
-

-

    -
  • -
-

-
-
-
    -
  • id:
  • -
  • parent:
  • -
  • type:
  • -
  • label:
  • -
  • url:
  • -
-

-

    -
  • -
  • -
-

-
- -
-
-
-
-

DOTソースコード部分

- -
-
-

CSV部分

- +
+
+

CSV部分

+ -
-
-
- -
+
- - - + } + */ + } + + \ No newline at end of file diff --git a/src/js/src/bookmark/main.js b/src/js/src/bookmark/main.js index e9e057e..2f7c1bd 100644 --- a/src/js/src/bookmark/main.js +++ b/src/js/src/bookmark/main.js @@ -1,362 +1,410 @@ import { Graphviz } from "/solitude/libs/@hpcc-js/wasm/dist/index.js"; -window.addEventListener("load", (event) => { - let counter = 0; - const lf = "\n"; - (async () => { - const mime = "image/svg+xml;charset=UTF-8"; - try { - const dotViewer = document.querySelector("#dot-src-viewer"); - const dot = dotViewer.value = card2dot(); - const svg = await dot2svg(dot); - const dataUri = text2DataUriScheme(svg, mime); +(function () { + window.addEventListener("load", boot); - document.querySelector("#ref-map").setAttribute("data", dataUri); - } catch (err) { - console.log(err); - } - })(); + function boot(event) { + let counter = 0; + const lf = "\n"; - - const handle = handlers.call({}); - const menu = document.querySelector("#menuView"); - - menu.addEventListener("click", handle.generate) - menu.addEventListener("click", handle.loadCSV); - menu.addEventListener("click", handle.open) - menu.addEventListener("click", handle.save) - menu.addEventListener("click", handle.export) - - const editor = document.querySelector("#editView"); - editor.addEventListener("click", handle.appendCard) - editor.addEventListener("click", handle.deleteCard) - - const fileChooser = document.querySelector("#openReal"); - fileChooser.addEventListener('change', handle.openReal); - - function handlers() { - const nameOf = { - csv: "favorite.csv", - svg: "result.svg" - } - this.appendCard = handleAppendCardButton; - this.deleteCard = handleDeleteCardButton; - this.export = handleExportButton; - this.open = handleOpenButton; - this.generate = handleGenerateButton; - this.loadCSV = handleLoadCsvButton; - this.save = handleSaveButton; - this.openReal = handleOpenReal; - return this; - - function handleGenerateButton(event) { - if (event.target.id != "generate") { - return; - } - - document.querySelector("#dot-src-viewer").value = card2dot(); - dot2svg(document.querySelector("#dot-src-viewer").value) - .then(xml => { - const canvas = document.querySelector("#ref-map"); - const data = text2DataUriScheme(xml, "image/svg+xml"); - canvas.setAttribute("data", data); - }).catch(err => { - console.log(err) - }); - } - - function handleOpenButton(event) { - if (event.target.id != "open") { - return; - } - - document.querySelector("#openReal").click(); - } - - function handleLoadCsvButton(event) { - if (event.target.id != "loadCsv") { - return; - } - - for (const elem of document.querySelectorAll("button.card-delete")) { - - setTimeout(() => { - deleteCard(elem.closest("div.ref-card")); - }, 50); - }; - - csv2card(document.querySelector("#csv-src-viewer").value); - } - - async function handleSaveButton(event) { - if (event.target.id != "save") { - return; - } - + (async () => { + const mime = "image/svg+xml;charset=UTF-8"; try { - const data = await dot2svg(card2dot()); - download(data, "image/svg+xml;charset=UTF-8", nameOf.svg); + const dotViewer = document.querySelector("#dot-src-viewer"); + const dot = dotViewer.value = card2dot(); + const svg = await dot2svg(dot); + const dataUri = text2DataUriScheme(svg, mime); + + document.querySelector("#ref-map").setAttribute("data", dataUri); } catch (err) { console.log(err); } - } + })(); - function handleExportButton(event) { - if (event.target.id != "export") { - return; + + const handle = buttonHandlers.call({}); + + function Control(elemental) { + if(elemental instanceof Element) { + this.element = elemental; + } else { + this.element = document.querySelector(elemental); } - download(card2csv(), "text/csv;charset=UTF-8", nameOf.csv,); + } + Control.prototype = { + set onClick(handler) { + if(Array.isArray(handler)) { + for(const listener of handler ) { + this.element.addEventListener("click", listener); + } + } else { + this.element.addEventListener("click", handler); + } + }, + + set onChange(handler) { + this.element.addEventListener("change", handler); + } } - function handleAppendCardButton(event) { - if (event.target.classList.contains("card-append")) { - const bigBrotherCard = event.target.closest("div.ref-card"); - appendCard(bigBrotherCard, { - id: counter++ + "", - parent: "root", - type: "folder", - label: "nonelabel", - url: "https://www.exmple.com/" + const menu = new Control("#menuView"); + menu.onClick = [ + handle.generate, + handle.loadCSV, + handle.open, + handle.save, + handle.export, + ]; + + const editor = new Control("#editView"); + editor.onClick = [handle.appendCard, handle.deleteCard] + + const fileChooser = new Control("#openReal"); + fileChooser.onChange = handle.openReal; + + const bars = document.querySelectorAll("div.top-bar,div.bottom-bar"); + bars.forEach((elem) => { + const bar = new Control(elem); + bar.onClick = toggle; + }); + + function buttonHandlers() { + const nameOf = { + csv: "favorite.csv", + svg: "result.svg" + } + this.appendCard = handleAppendCardButton; + this.deleteCard = handleDeleteCardButton; + this.export = handleExportButton; + this.open = handleOpenButton; + this.generate = handleGenerateButton; + this.loadCSV = handleLoadCsvButton; + this.save = handleSaveButton; + this.openReal = handleOpenReal; + return this; + + function handleGenerateButton(event) { + if (event.target.id != "generate") { + return; + } + + document.querySelector("#dot-src-viewer").value = card2dot(); + dot2svg(document.querySelector("#dot-src-viewer").value) + .then(xml => { + const canvas = document.querySelector("#ref-map"); + const data = text2DataUriScheme(xml, "image/svg+xml"); + canvas.setAttribute("data", data); + }).catch(err => { + console.log(err) + }); + } + + function handleOpenButton(event) { + if (event.target.id != "open") { + return; + } + + document.querySelector("#openReal").click(); + } + + function handleLoadCsvButton(event) { + if (event.target.id != "loadCsv") { + return; + } + + for (const elem of document.querySelectorAll("button.card-delete")) { + + setTimeout(() => { + deleteCard(elem.closest("div.ref-card")); + }, 50); + }; + + csv2card(document.querySelector("#csv-src-viewer").value); + } + + async function handleSaveButton(event) { + if (event.target.id != "save") { + return; + } + + try { + const data = await dot2svg(card2dot()); + download(data, "image/svg+xml;charset=UTF-8", nameOf.svg); + } catch (err) { + console.log(err); + } + } + + function handleExportButton(event) { + if (event.target.id != "export") { + return; + } + download(card2csv(), "text/csv;charset=UTF-8", nameOf.csv,); + } + + function handleAppendCardButton(event) { + if (event.target.classList.contains("card-append")) { + const bigBrotherCard = event.target.closest("div.ref-card"); + appendCard(bigBrotherCard, { + id: counter++ + "", + parent: "root", + type: "folder", + label: "nonelabel", + url: "https://www.exmple.com/" + }) + } + } + + function handleDeleteCardButton(event) { + if (event.target.classList.contains("card-delete")) { + setTimeout(() => { + deleteCard(event.target.closest("div.ref-card")); + }, 50); + } + } + + function handleOpenReal(event) { + const files = document.querySelector("#openReal").files; + if (files.length == 0) { + return; + } + + const reader = new FileReader(); + const content = reader.readAsText(files[0]); + + reader.addEventListener("load", () => { + document.querySelector("#csv-src-viewer").value = reader.result; }) } + } - function handleDeleteCardButton(event) { - if (event.target.classList.contains("card-delete")) { - setTimeout(() => { - deleteCard(event.target.closest("div.ref-card")); - }, 50); + function toggle(event) { + const editor = document.querySelector("div.editor"); + if (document.querySelectorAll("div.editor.expanded").length > 0) { + editor.classList.remove("expanded"); + editor.classList.add("shrinked"); + + } else if (document.querySelectorAll("div.editor.shrinked").length > 0) { + editor.classList.remove("shrinked"); + editor.classList.add("expanded"); } } - function handleOpenReal(event) { - const files = fileChooser.files; - if (files.length == 0) { - return; - } - - const reader = new FileReader(); - const content = reader.readAsText(files[0]); - - reader.addEventListener("load", () => { - document.querySelector("#csv-src-viewer").value = reader.result; - }) - } - - } - function appendCard(bigbrother, node = {}) { - const newCard = document.querySelector("#tmpl-ref-card").content.cloneNode(true); - bigbrother.parentNode.insertBefore(newCard, bigbrother.nextElementSibling); + function appendCard(bigbrother, node = {}) { + const newCard = document.querySelector("#tmpl-ref-card").content.cloneNode(true); + bigbrother.parentNode.insertBefore(newCard, bigbrother.nextElementSibling); - const addedCard = bigbrother.nextElementSibling; + const addedCard = bigbrother.nextElementSibling; - new nodeValues().write(addedCard, node) - } - - function deleteCard(card) { - card.parentNode.removeChild(card); - } - - function csv2card(csv) { - //改行をLFだけにしたい - - const text = csv.replaceAll("\r", ""); - const textRows = text.split(lf) - const rows = []; - //当該要素はrootの一つしか残っていない想定 - let bigBrother = document.querySelector("div.ref-card"); - for (const text of textRows) { - //ヘッダは読み飛ばす - if ("id,parent,type,label,url" == text || "" == text.trim()) { - continue; - } - - const row = text.split(",") - - if (row[0] == "root") { - continue; - } - - appendCard(bigBrother, { - id: row[0], - parent: row[1], - type: row[2], - label: row[3], - url: row[4] - }) - - let cards = document.querySelectorAll("div.ref-card"); - bigBrother = cards[cards.length - 1] - } - } - - function card2csv() { - const cards = document.querySelectorAll("div.ref-card"); - const header = ["id", "parent", "type", "label", "url"].join(","); - const csvText = [header] - - for (const card of cards) { - const refNode = new nodeValues().read(card); - csvText.push([ - refNode.id, - refNode.parent, - refNode.type, - refNode.label, - refNode.url, - ].join(",")); + new nodeValues().write(addedCard, node) } - return csvText.join(lf) - } + function deleteCard(card) { + card.parentNode.removeChild(card); + } - function card2dot() { - function digraph(nodeList, edgeList) { - const dot = `digraph G { + function csv2card(csv) { + //改行をLFだけにしたい + + const text = csv.replaceAll("\r", ""); + const textRows = text.split(lf) + const rows = []; + //当該要素はrootの一つしか残っていない想定 + let bigBrother = document.querySelector("div.ref-card"); + for (const text of textRows) { + //ヘッダは読み飛ばす + if ("id,parent,type,label,url" == text || "" == text.trim()) { + continue; + } + + const row = text.split(",") + + if (row[0] == "root") { + continue; + } + + appendCard(bigBrother, { + id: row[0], + parent: row[1], + type: row[2], + label: row[3], + url: row[4] + }) + + let cards = document.querySelectorAll("div.ref-card"); + bigBrother = cards[cards.length - 1] + } + } + + function card2csv() { + const cards = document.querySelectorAll("div.ref-card"); + const header = ["id", "parent", "type", "label", "url"].join(","); + const csvText = [header] + + for (const card of cards) { + const refNode = new nodeValues().read(card); + csvText.push([ + refNode.id, + refNode.parent, + refNode.type, + refNode.label, + refNode.url, + ].join(",")); + } + + return csvText.join(lf) + } + + function card2dot() { + function digraph(nodeList, edgeList) { + const dot = `digraph G { graph [overlap="true", bgcolor="#ffffdd", mindist="0.01"]; node [fontname="BIZUDP Gothic" target="_blank", shape="tab", style="filled", color="#cc6666", fontcolor="#333333", fillcolor="#ffcc90"]; edge [fontname="meiryo", color="#cc6666"]; - + //nodeList ${nodeList} - + //edgeList ${edgeList} } `; - return dot; + return dot; + } + + const id = 0, parent = 1, type = 2, label = 3, url = 4; + + const [nodeList, edgeList] = [[], []]; + const cards = document.querySelectorAll("div.ref-card"); + for (const card of cards) { + const refNode = new nodeValues().read(card); + + const id = refNode.id, + parent = refNode.parent, + type = refNode.type, + label = refNode.label, + url = refNode.url; + + let shapeStmt; + if (type == "folder") { + shapeStmt = `, shape="ellipse"`; + } else { + shapeStmt = "" + } + + let labelStmt; + if (label == "") { + labelStmt = `label="${id}"`; + } else { + labelStmt = `label="${label}"`; + } + + let hrefStmt; + if (url == "") { + hrefStmt = ""; + } else { + //label属性が必ずあるため区切りの, を決め打ちで入れて問題なし + hrefStmt = `, href="${url}"`; + } + + const nodeStmt = `${id} [${labelStmt}${hrefStmt}${shapeStmt}];`; + nodeList.push(nodeStmt); + + if (id == "root") { + continue; + } else { + const edgeStmt = `${parent} -> ${id};`; + edgeList.push(edgeStmt); + } + } + + //改行してインデント + const indent = " "; + const delim = lf + indent + return digraph(nodeList.join(delim), edgeList.join(delim)); } - const id = 0, parent = 1, type = 2, label = 3, url = 4; + async function dot2svg(dot = "") { + const graphviz = await Graphviz.load(); + const src = dot == "" ? 'digraph G { Hello -> World }' : dot; + const svg = graphviz.circo(src); + return svg; + } - const [nodeList, edgeList] = [[], []]; - const cards = document.querySelectorAll("div.ref-card"); - for (const card of cards) { - const refNode = new nodeValues().read(card); + function nodeValues() { + this.read = readNodeValues; + this.write = writeNodeValues; + return this; - const id = refNode.id, - parent = refNode.parent, - type = refNode.type, - label = refNode.label, - url = refNode.url; + /** + * ノードを表すカードからノードの情報を取り出す。 + * カード内のinputについて知っている + * @param {*} card + * @returns + */ + function readNodeValues(card) { + const values = card.querySelectorAll("input"); + const refNode = {}; + refNode.id = values[0].value; + refNode.parent = values[1].value; + refNode.tytpe = values[2].value; + refNode.label = values[3].value; + refNode.url = values[4].value; - let shapeStmt; - if (type == "folder") { - shapeStmt = `, shape="ellipse"`; - } else { - shapeStmt = "" + return refNode; } - let labelStmt; - if (label == "") { - labelStmt = `label="${id}"`; - } else { - labelStmt = `label="${label}"`; - } + /** + * ノードを表すカードに書き込む + * カード内のinputについて知っている + */ + function writeNodeValues(card, values) { + const inputs = card.querySelectorAll("input"); + const { id, parent, type, label, url } = values; - let hrefStmt; - if (url == "") { - hrefStmt = ""; - } else { - //label属性が必ずあるため区切りの, を決め打ちで入れて問題なし - hrefStmt = `, href="${url}"`; - } + if (id && id.length > 0) { + inputs[0].value = id + } - const nodeStmt = `${id} [${labelStmt}${hrefStmt}${shapeStmt}];`; - nodeList.push(nodeStmt); + if (parent && parent.length > 0) { + inputs[1].value = parent + } - if (id == "root") { - continue; - } else { - const edgeStmt = `${parent} -> ${id};`; - edgeList.push(edgeStmt); + if (type && type.length > 0) { + inputs[2].value = type + } + + if (label && label.length > 0) { + inputs[3].value = label + } + + if (url && url.length > 0) { + inputs[4].value = url + } + } } - //改行してインデント - const indent = " "; - const delim = lf + indent - return digraph(nodeList.join(delim), edgeList.join(delim)); - } - - async function dot2svg(dot = "") { - const graphviz = await Graphviz.load(); - const src = dot == "" ? 'digraph G { Hello -> World }' : dot; - const svg = graphviz.circo(src); - return svg; - } - - function nodeValues() { - this.read = readNodeValues; - this.write = writeNodeValues; - return this; - - /** - * ノードを表すカードからノードの情報を取り出す。 - * カード内のinputについて知っている - * @param {*} card - * @returns - */ - function readNodeValues(card) { - const values = card.querySelectorAll("input"); - const refNode = {}; - refNode.id = values[0].value; - refNode.parent = values[1].value; - refNode.tytpe = values[2].value; - refNode.label = values[3].value; - refNode.url = values[4].value; - - return refNode; + function text2DataUriScheme(text, mimeType) { + const binary = window.btoa(unescape(encodeURIComponent(text))) + const dataUri = `data:${mimeType};base64,${binary}` + return dataUri; } - /** - * ノードを表すカードに書き込む - * カード内のinputについて知っている - */ - function writeNodeValues(card, values) { - const inputs = card.querySelectorAll("input"); - const { id, parent, type, label, url } = values; + function download(text, mimeType, name,) { + const save = document.querySelector("#savelink"); + const dataUri = text2DataUriScheme(text, mimeType); - if (id && id.length > 0) { - inputs[0].value = id - } - - if (parent && parent.length > 0) { - inputs[1].value = parent - } - - if (type && type.length > 0) { - inputs[2].value = type - } - - if (label && label.length > 0) { - inputs[3].value = label - } - - if (url && url.length > 0) { - inputs[4].value = url - } - + save.setAttribute("href", dataUri); + save.setAttribute("download", name); + setTimeout(() => { + save.click(); + save.setAttribute("href", ""); + save.setAttribute("download", ""); + }, 50); } } - - function text2DataUriScheme(text, mimeType) { - const binary = window.btoa(unescape(encodeURIComponent(text))) - const dataUri = `data:${mimeType};base64,${binary}` - return dataUri; - } - - function download(text, mimeType, name,) { - const save = document.querySelector("#savelink"); - const dataUri = text2DataUriScheme(text, mimeType); - - save.setAttribute("href", dataUri); - save.setAttribute("download", name); - setTimeout(() => { - save.click(); - save.setAttribute("href", ""); - save.setAttribute("download", ""); - }, 50); - } -}); +})()