diff --git a/etc/public b/etc/public
new file mode 120000
index 0000000..9621348
--- /dev/null
+++ b/etc/public
@@ -0,0 +1 @@
+/home/k.sakai/public/solitude/resources/
\ No newline at end of file
diff --git a/src/js/.babelrc b/src/js/.babelrc
deleted file mode 100644
index 0545379..0000000
--- a/src/js/.babelrc
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "presets": [
- [
- "@babel/preset-env",
- {
- "useBuiltIns": "usage",
- "corejs": "3.0.0"
- }
- ]
- ],
- "plugins": [
- "@babel/plugin-syntax-dynamic-import",
- "@babel/plugin-proposal-class-properties"
- ]
-}
diff --git a/src/js/.eslintrc b/src/js/.eslintrc
deleted file mode 100644
index 0fe0ce6..0000000
--- a/src/js/.eslintrc
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "parser": "@babel/eslint-parser",
- "extends": [
- "eslint:recommended"
- ],
- "env": {
- "browser": true,
- "node": true
- },
- "parserOptions": {
- "ecmaVersion": 8,
- "sourceType": "module",
- "requireConfigFile": false
- },
- "rules": {
- "semi": 2
- }
-}
diff --git a/src/js/.gitignore b/src/js/.gitignore
index f2261c1..e4dbfac 100644
--- a/src/js/.gitignore
+++ b/src/js/.gitignore
@@ -5,6 +5,13 @@
build
dist
+#symlink
+etc/public/*
+
+#local environment
+local/*
+local/doc/*
+
# misc
.DS_Store
diff --git a/src/js/.stylelintrc.json b/src/js/.stylelintrc.json
deleted file mode 100644
index 40db42c..0000000
--- a/src/js/.stylelintrc.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "extends": "stylelint-config-standard"
-}
diff --git a/src/js/.travis.yml b/src/js/.travis.yml
deleted file mode 100644
index a1be7d1..0000000
--- a/src/js/.travis.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-language: node_js
-node_js:
- - "lts/*"
- - "node"
-script: npm run build
diff --git a/src/js/package.json b/src/js/package.json
index b6702f6..3df7063 100644
--- a/src/js/package.json
+++ b/src/js/package.json
@@ -2,11 +2,12 @@
"name": "solitude",
"version": "1.0.0",
"description": "",
- "main": "src/index.js",
+ "main": "main/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"portable": "electron-builder build --win --dir",
- "build-ready": "rm -f solitude.zip && zip solitude.zip package.json $(find ./src/) && mv solitude.zip ~/public/solitude/ ;"
+ "build-ready": "rm -f solitude.zip && zip solitude.zip package.json $(find ./src/) && mv solitude.zip ~/public/solitude/ ;",
+ "build-preready": "rsync -av --delete src/ ../../etc/public/"
},
"build": {
"appId": "work.sylow-castle.slitude.app",
diff --git a/src/js/src/bookmark/app.html b/src/js/src/bookmark/app.html
new file mode 100644
index 0000000..dd5409c
--- /dev/null
+++ b/src/js/src/bookmark/app.html
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+ Solitude
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/js/src/bookmark/main.css b/src/js/src/bookmark/main.css
new file mode 100644
index 0000000..583a15b
--- /dev/null
+++ b/src/js/src/bookmark/main.css
@@ -0,0 +1,143 @@
+:root {
+ --main-border-color: brown;
+ font-size: 16px;
+ }
+
+
+
+body, p {
+ margin: 0px;
+}
+
+ul {
+ padding-left: 1em;
+ padding-right: 1em;
+ margin: 5px;
+}
+
+div.app-body {
+ /*
+ display: grid;
+ grid-template-columns: 1fr;
+ */
+ display: block flex;
+ min-width: 640px;
+ min-height: 480px;
+}
+
+div.app-body div.left-pane {
+ min-width: 200px;
+ max-width: 250px;
+ overflow-x: scroll;
+ width: 30%;
+
+ height: 98vh;
+ border-style: solid;
+ border-color: orange;
+ border-width: 1px;
+
+ position: sticky;
+ top: 0px;
+ margin: 5px
+}
+
+div.app-body div.left-pane ul{
+ margin: 0px;
+}
+
+div.app-body div.right-pane {
+ min-width: 400px;
+ min-height: 100px;
+ overflow-x: scroll;
+ overflow-y: scroll;
+}
+
+div.top-bar, div.bottom-bar {
+ height: 1em; ;
+ border: 0.1em solid var(--main-border-color);
+}
+
+#mainView {
+ height: 60vh;
+ overflow-x: scroll;
+ overflow-y: scroll;
+ min-height: 480px;
+ min-width: 640px;
+}
+
+#mainView p {
+ width: fit-content;
+}
+
+#ref-map {
+ width: 70vw;
+ overflow: scroll;
+ object-fit: scale-down;
+}
+
+#editMenu {
+ display: flex;
+ list-style: none;
+}
+
+div.editor.shrinked {
+ /* expanded -> shrinked*/
+ animation: 1s linear 0.2s reverse editor-expanding ;
+ max-height: 0vh;
+ overflow: hidden;
+}
+
+div.editor.expanded {
+ /* shrinked -> expanded */
+ animation: 1s linear 0.2s normal editor-expanding ;
+ max-height: 60vh;
+ overflow: scroll;
+
+}
+
+div.editor input[readonly=""] {
+ background-color: lightsteelblue;
+}
+
+div.editor div.ref-card {
+ border-style: solid;
+ border-color: orange;
+ border-width: 0.1em;
+}
+
+
+div.editor div.ref-card li{
+ display: flex;
+}
+
+div.editor div.ref-card li span{
+ width:20%;
+}
+
+div.editor div.ref-card input{
+ width:80%;
+}
+
+div.editor div.ref-card ul.card-menu {
+ display:flex;
+}
+
+#dot-src-viewer, #csv-src-viewer {
+ display: block;
+ min-width: 400px;
+ min-height: 20vh;
+ width: 70vw;
+}
+
+/*
+@keyframes editor-expanding {
+ 0% {
+ max-height: 0vh;
+ overflow: hidden;
+ }
+ 100% {
+ max-height: 100vh;
+ overflow: hidden;
+ }
+}
+*/
\ No newline at end of file
diff --git a/src/js/src/bookmark/main.js b/src/js/src/bookmark/main.js
new file mode 100644
index 0000000..777df6c
--- /dev/null
+++ b/src/js/src/bookmark/main.js
@@ -0,0 +1,452 @@
+import { Graphviz } from "/solitude/libs/@hpcc-js/wasm/dist/index.js";
+
+(function () {
+ window.addEventListener("load", boot);
+
+ function boot(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 parser = new DOMParser();
+ const doc = parser.parseFromString(svg, "application/xml");
+ document.querySelector("#ref-map").innerHTML = doc.documentElement.innerHTML
+
+ } catch (err) {
+ console.log(err);
+ }
+ })();
+
+
+ const handle = buttonHandlers({});
+
+ function Control(elemental) {
+ if (elemental instanceof Element) {
+ this.element = elemental;
+ } else {
+ this.element = document.querySelector(elemental);
+ }
+ }
+ 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);
+ }
+ }
+
+ 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 saveSvgLink = new Control("#saveSvgLink");
+ saveSvgLink.onClick = (event) => {
+ event.preventDefault();
+ alert("クリックは無効です");
+ };
+
+ const saveCsvLink = new Control("#saveCsvLink");
+ saveCsvLink.onClick = (event) => {
+ event.preventDefault();
+ alert("クリックは無効です");
+ };
+
+
+ const bars = document.querySelectorAll("div.top-bar,div.bottom-bar");
+ bars.forEach((elem) => {
+ const bar = new Control(elem);
+ bar.onClick = toggle;
+ });
+
+ function buttonHandlers(result) {
+ const nameOf = {
+ csv: "favorite.csv",
+ svg: "result.svg"
+ }
+ result.appendCard = handleAppendCardButton;
+ result.deleteCard = handleDeleteCardButton;
+ result.export = handleExportButton;
+ result.open = handleOpenButton;
+ result.generate = handleGenerateButton;
+ result.loadCSV = handleLoadCsvButton;
+ result.save = handleSaveButton;
+ result.openReal = handleOpenReal;
+ return result;
+
+ 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 parser = new DOMParser();
+ const doc = parser.parseFromString(xml, "application/xml");
+
+ const svgAttr = doc.documentElement.attributes;
+ for (const attr of svgAttr) {
+ canvas.setAttribute(attr.name, attr.value);
+ }
+ canvas.setAttribute("overflow", "scroll")
+
+ canvas.innerHTML = doc.documentElement.innerHTML
+
+ const serializer = new XMLSerializer();
+ const serializedSvg = serializer.serializeToString(doc);
+
+ setDownloadLink(document.querySelector("#saveSvgLink"), serializedSvg, "image/svg+xml;charset=utf-8", nameOf.svg);
+ }).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 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 appendCard(bigbrother, node = {}) {
+ const newCard = document.querySelector("#tmpl-ref-card").content.cloneNode(true);
+ bigbrother.parentNode.insertBefore(newCard, bigbrother.nextElementSibling);
+
+ const addedCard = bigbrother.nextElementSibling;
+
+ new nodeValues().write(addedCard, node);
+ setSaveCsvLink();
+ }
+
+ function deleteCard(card) {
+ card.parentNode.removeChild(card);
+ setSaveCsvLink();
+ }
+
+ function setSaveCsvLink() {
+ const csv = card2csv();
+ const saveCsvLink = document.querySelector("#saveCsvLink");
+ setDownloadLink(saveCsvLink, csv, "text/csv;charset=UTF-8", "favorite.csv");
+ }
+
+ 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;
+ }
+
+ 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));
+ }
+
+ 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;
+ }
+
+ /**
+ * ノードを表すカードに書き込む
+ * カード内のinputについて知っている
+ */
+ function writeNodeValues(card, values) {
+ const inputs = card.querySelectorAll("input");
+ const { id, parent, type, label, url } = values;
+
+ 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
+ }
+
+ }
+ }
+
+ function text2DataUriScheme(text, mimeType) {
+ const binary = window.btoa(unescape(encodeURIComponent(text)))
+ const dataUri = `data:${mimeType};base64,${binary}`
+ return dataUri;
+ }
+
+ function setDownloadLink(anchor, text, mimeType, name) {
+ const link = anchor;
+ const dataUri = text2DataUriScheme(text, mimeType);
+
+ link.setAttribute("href", dataUri);
+ link.setAttribute("download", name);
+ }
+
+ function download(text, mimeType, name,) {
+
+ const save = document.querySelector("#savelink");
+ setDownloadLink(save, text, mimeType, name);
+
+ save.click();
+ save.setAttribute("href", "");
+ save.setAttribute("download", "");
+ }
+ }
+})()
+
diff --git a/src/js/src/example/app.html b/src/js/src/example/app.html
new file mode 100644
index 0000000..796ec15
--- /dev/null
+++ b/src/js/src/example/app.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
this is abn "StaticPage" app example.
+
+
+
\ No newline at end of file
diff --git a/src/js/src/index.html b/src/js/src/index.html
deleted file mode 100644
index 00f6d28..0000000
--- a/src/js/src/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
- Solitude
-
-
- Hello World
- This is first Solitude
-
-
\ No newline at end of file
diff --git a/src/js/src/index.js b/src/js/src/index.js
deleted file mode 100644
index 091791f..0000000
--- a/src/js/src/index.js
+++ /dev/null
@@ -1,14 +0,0 @@
-const { app, BrowserWindow} = require('electron');
-
-function createWindow() {
- let win = new BrowserWindow({
- width:400,
- height: 200,
- webPreference: {
- nodeIntegration:true
- }
- });
- win.loadFile('index.html');
-}
-
-app.whenReady().then(createWindow)
\ No newline at end of file
diff --git a/src/js/src/js/File.js b/src/js/src/js/File.js
new file mode 100644
index 0000000..929caa8
--- /dev/null
+++ b/src/js/src/js/File.js
@@ -0,0 +1,25 @@
+export const File = {
+ get load() { return loadFileContent }
+}
+
+/**
+ * 引数Pathで渡されたファイルの中身を読み込む
+ */
+async function loadFileContent(path) {
+ try {
+ const response = await fetch(path);
+ if (response.ok) {
+ return response.text();
+ } else {
+ throw new Error(`HTTP error, status = ${response.status}`);
+ }
+ } catch (ex) {
+ const error = new Error('ファイルのオープンに失敗しました。causeプロパティで原因となったオブジェクトを返します。');
+ error.cause = ex;
+ throw error;
+ }
+}
+
+async function writeFilecontent() {
+
+}
\ No newline at end of file
diff --git a/src/js/src/js/Que.js b/src/js/src/js/Que.js
new file mode 100644
index 0000000..ca936e5
--- /dev/null
+++ b/src/js/src/js/Que.js
@@ -0,0 +1,39 @@
+ export const Que = newQue;
+ Query.prototype = {
+ get list() {return asList},
+ get get() {return asNode},
+ get que() {return reQue},
+ }
+
+ function Query(selector, base) {
+ if (base instanceof Query) {
+ this.origin = base.get();
+ } else {
+ this.origin = base || document;
+ }
+ this.raw = this.origin.querySelectorAll(selector);
+ /*
+ this.list = asList;
+ this.get = asNode;
+ this.que = reQue;
+ */
+ }
+
+ function asList() {
+ return this.raw;
+ }
+
+ function asNode() {
+ if (this.raw.length > 0) {
+ return this.raw[0];
+ }
+ }
+
+ function newQue(selector, base) {
+ return new Query(selector, base);
+ }
+
+ function reQue(selector) {
+ return new Query(selector, this.get())
+ }
+
diff --git a/src/js/src/js/solitude.js b/src/js/src/js/solitude.js
new file mode 100644
index 0000000..e35dfb6
--- /dev/null
+++ b/src/js/src/js/solitude.js
@@ -0,0 +1,156 @@
+import { File } from "/solitude/resources/js/File.js"
+
+export function graftSolitude(global) {
+ var solitude;
+ (function dismantleGlobal() {
+ global.solitude = solitude = global.solitude || {
+ "variant": "web",
+ get global() { return global; },
+ //ネイティブ関数は別名つけられないらしいのでラップする
+ get postMessage() { return message => global.postMessage(message)},
+ get createURL() { return global.URL.createObjectURL },
+ get main() { return global.parent },
+ get origin() { return global.parent.location.origin },
+ get myOrigin() { return global.location.origin },
+ get console() { return global.parent.console },
+ get message() { return message },
+ get persistence() { return persistenceFunc },
+ get log() { return log },
+ };
+
+ const message = {
+ get fire() { return fireMessage},
+ get addListener() { return addMessageListener },
+ get removeListener() { return removeMessageListener }
+ };
+
+ const log = {
+ get debug() {return msg => logging("debug", msg)},
+ get info() {return msg => logging("info", msg)},
+ }
+ })();
+
+
+ io(solitude);
+ return solitude;
+
+ //declaretions
+ function persistenceFunc() {
+ const strategies = {
+ get local() { return writeLocal },
+ get download() { return download },
+ get console() { return solitude.console }
+ };
+ return strategies;
+ }
+
+ //file output strategies
+ function writeLocal(path, content) {
+ const message = {
+ "path": path,
+ "content": content,
+ }
+ solitude.message.post(JSON.stringify(message), solitude.origin)
+ }
+
+ function readLocal(path) {
+ // stringで解決するPromiseを返したい。
+ return fetch(path).then(
+ (response) => {
+ if (response.ok) {
+ return response.text();
+ } else {
+ throw new Error(`HTTP error, status = ${response.status}`);
+ }
+ }, (cause) => {
+ const error = new Error('ファイルのオープンに失敗しました。causeプロパティで原因となったオブジェクトを返します。');
+ error.cause = cause;
+ throw error;
+ }
+ );
+ }
+
+ function download(filename, content, mimeType) {
+ const blob = new Blob([content], { "type": mimeType });
+ const link = document.createElement('a');
+
+ link.href = solitude.createURL(blob);
+ link.download = filename;
+ link.click()
+ }
+
+ function io(solitude) {
+ //std can customize
+ const initialStd = {
+ get out() { return solitude.global.console.log },
+ get in() { return noop },
+ }
+ var std = initialStd;
+
+ solitude.io = {
+ get std() { return std; },
+ set std(value) { std = value; },
+ get console() { return solitude.console; },
+ get fs() { return fs }
+ }
+
+ const fs = {
+ get write() { return writeLocal },
+ get download() { return download },
+ get read() { return File.load },
+ get out() { return this.write },
+ get in() { return this.read },
+ }
+ }
+
+ /*
+ * solitude.message
+ */
+ function fireMessage(message) {
+ const enveloped = {
+ h: {
+ origin: "solitude"
+ },
+ b: message
+ };
+
+ solitude.postMessage(enveloped);
+ }
+
+ function addMessageListener(listener) {
+ const handler = event => {
+ if (event.data.h.origin != "solitude") {
+ return;
+ }
+
+ listener(event.data.b);
+ }
+ //JSONシリアライズ可能なオブジェクトが送信できれば十分
+ global.addEventListener("message", handler, false);
+ return handler;
+ }
+
+ function removeMessageListener(listener) {
+ global.removeEventListener("message", listener)
+ }
+
+ /*
+ * solitude.log
+ */
+ function logging(level, message) {
+ solitude.message.fire({
+ type: "log",
+ level: level,
+ msg: message
+ })
+ }
+
+
+ /*
+ * その他
+ */
+ function noop() {
+ //do nothing
+ }
+}
+
diff --git a/src/js/src/main/app.html b/src/js/src/main/app.html
new file mode 100644
index 0000000..1e89cc8
--- /dev/null
+++ b/src/js/src/main/app.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+ Solitude
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Hello Solitude World
+
This is Solitude
+
+
+
+
+
+
+
+
+
+
+
+ 表示名
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/js/src/main/app.js b/src/js/src/main/app.js
new file mode 100644
index 0000000..94fb8a7
--- /dev/null
+++ b/src/js/src/main/app.js
@@ -0,0 +1,261 @@
+import { Que } from "/solitude/resources/js/Que.js";
+import { graftSolitude } from "/solitude/resources/js/solitude.js"
+
+window.addEventListener('load', main(document));
+
+function main(document) {
+ graftSolitude(window);
+ webconfig(window);
+ var mysolitude;
+ if (true || (typeof solitude.style) === 'undefined') {
+ mysolitude = readyWeb();
+ }
+
+ //アプリケーション毎に初期化操作
+ initializeSolitudeApps(mysolitude.apps)
+
+ Que("li.tab").list().forEach(function (val, index, _list) {
+ val.addEventListener("click", function () {
+ selectTab(index);
+ });
+ })
+
+ const leftMenu = Que("#left-menu").get();
+ const menuToggle = Que("#menu-toggle").get();
+ menuToggle.addEventListener("click", function () {
+ leftMenu.classList.remove("initial")
+ toggleMenuItem(leftMenu);
+ Que("#left-menu li span").list().forEach(function (val) {
+ toggleMenuItem(val);
+ })
+ toggleMenuItem(Que("#main").get());
+
+ })
+
+ // 以下、宣言
+ function toggleMenuItem(element) {
+ if (element.classList.contains("expanded")) {
+ element.classList.add("shrinked");
+ element.classList.remove("expanded");
+ } else {
+ element.classList.add("expanded");
+ element.classList.remove("shrinked");
+ }
+ }
+
+ function selectTab(index) {
+ let tab, content;
+ Que("#main-pane li.tab").list().forEach(function (val) {
+ val.classList.remove("selected");
+ if (val.dataset.soloAppId == index) {
+ tab = val;
+ }
+ });
+
+ Que("#main-pane div.main-article").list().forEach(function (val) {
+ val.classList.remove("selected");
+ if (val.dataset.soloAppId == index) {
+ content = val;
+ }
+ });
+
+ tab.classList.add("selected");
+ content.classList.add("selected");
+ }
+
+ /**
+ * solitude web版でのsolitudeオブジェクトの組み立てを行う
+ */
+ function readyWeb() {
+ //先にwebconfigを起動している前提
+ const solitude = window.solitude;
+ solitude.apps = {};
+ Object.assign(solitude.apps, window.tmp.solitudeapps);
+ return solitude;
+ }
+
+ function initializeSolitudeApps(apps) {
+ const appsProto = {
+ useMenu: true,
+ useTab: true,
+ autorun: false,
+ };
+
+ const appFuncs = [IframeApp, StaticPageApp, BatchApp];
+ appFuncs.forEach(appFunc => {
+ appFunc.prototype = appsProto;
+ });
+
+ let app;
+ let appInstanceID = 0;
+ for (const [name, conf] of Object.entries(apps)) {
+ const appType = conf.type || "undef";
+ switch (appType) {
+ case "main":
+ case "undef":
+ app = null;
+ //do nothing
+ break;
+ case "staticpage":
+ app = new StaticPageApp(name, conf);
+ break;
+ case "iframe":
+ app = new IframeApp(name, conf);
+ break;
+ case "batch":
+ app = new BatchApp(name, conf);
+ break;
+ default:
+ //something wrong;
+ break;
+ }
+ if(app) {
+ app.instanceID = ++appInstanceID;
+ app.load();
+ solitude.log.info("loaded " + name +", instanceID " + app.instanceID )
+ }
+
+ }
+
+
+ //巻き上げを期待しているためclass構文ではない。
+ function IframeApp(name, conf) {
+ this.name = name;
+ this.conf = conf;
+ this.load = loadApp;
+ this.template = "#app-type-iframe";
+ this.callback = iframeCallback;
+ }
+
+ function StaticPageApp(name, conf) {
+ this.name = name;
+ this.conf = conf;
+ this.load = loadApp;
+ this.template = "#app-type-staticpage";
+ this.callback = staticpageCallback;
+ }
+
+ function BatchApp(name, conf) {
+ this.name = name;
+ this.conf = conf;
+ this.load = loadBatchApp;
+ this.template = "#app-type-batch";
+ this.callback = batchCallback;
+ }
+
+ function loadApp() {
+ addNewMenu(this.name);
+ const mount = Que("#left-menu ol");
+ const app = this;
+ mount.que("li.init").get().addEventListener("click", function () {
+
+ if(isInitialized(app.instanceID)) {
+ return;
+ }
+
+ addNewTab(app);
+ addNewContent(app);
+ solitude.log.info(app.name + " is initialized");
+ });
+ mount.que("li.init").get().classList.remove("init");
+ }
+
+ function isInitialized(appInstanceID) {
+ //アプリのタブが出ているかでロード済みかを判断
+ let result = false;
+ Que("#main-tabs li").list().forEach(elem => {
+ if(appInstanceID == elem.dataset.soloAppId) {
+ result = true;
+ }
+ });
+ return result;
+
+ }
+
+ function loadBatchApp() {
+ addNewContent(this);
+ }
+
+ function addNewMenu(name) {
+ const mount = Que("#left-menu ol").get();
+ let menu = Que("#app-menu").get();
+ menu = menu.content.cloneNode(true);
+
+ const span = Que("span", menu).get();
+ // template では初期状態はexpanded
+ if (Que("#left-menu.shrinked").list().length > 0) {
+ const cls = span.classList;
+ cls.remove("expanded");
+ cls.add("shrinked");
+ }
+ span.textContent = name;
+ mount.appendChild(menu);
+ }
+
+ function addNewTab(app) {
+ const mount = Que("#main-tabs").get();
+ let tab = Que("#app-tab").get()
+ tab = tab.content.cloneNode(true);
+ const li = Que("li", tab).get();
+ li.textContent = app.name;
+ li.dataset.soloAppId = app.instanceID;
+ const index = app.instanceID;
+ mount.appendChild(tab);
+
+ li.addEventListener("click", () => {
+ selectTab(index);
+ });
+ }
+
+ function addNewContent(app) {
+ const appNode = (Que(app.template).get()).content.cloneNode(true);
+ const base = Que("div.main-article", appNode);
+ base.get().classList.remove("undefined");
+ base.get().dataset.soloAppId = app.instanceID;
+ base.que("div.app-container").get().classList.add(app.name);
+ app.callback(app, base);
+
+ const mount = Que("#app-pane").get();
+ mount.appendChild(base.get());
+
+ }
+
+ function iframeCallback(app, base) {
+ const iframe = Que("div.app-container > iframe", base).get();
+ iframe.setAttribute("id", app.name);
+ iframe.setAttribute("title", app.name);
+ iframe.setAttribute("src", app.conf.src);
+ }
+
+ async function staticpageCallback(app, base) {
+ const text = await mysolitude.io.fs.read(app.conf.src);
+ const parser = new DOMParser();
+ const doc = parser.parseFromString(text, "text/html");
+ const content = Que("div.app-body", doc).get().cloneNode(true);
+ Que("div.app-container", base).get().appendChild(content);
+
+ //文書内のJavascriptコードを実行する。簡単な実装。
+ //これだとon.loadイベントとかがキックされなくなってしまう気がする。
+ const removes = [];
+ Que("script", content).list().forEach(element => {
+ const script = document.createElement("script")
+ script.innerHTML = element.innerHTML;
+ for (const attr of element.attributes) {
+ script.setAttribute(attr.name, attr.value);
+ }
+ element.before(script)
+ removes.push(element);
+ });
+
+ for (const script of removes) {
+ script.remove();
+ }
+ }
+
+ function batchCallback(app, base) {
+ const content = document.createElement("script")
+ content.setAttribute("src", app.conf.src);
+ Que("div.app-container", base).get().appendChild(content);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/js/src/main/color-sample.html b/src/js/src/main/color-sample.html
new file mode 100644
index 0000000..45f9ff5
--- /dev/null
+++ b/src/js/src/main/color-sample.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+ Solitude
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/js/src/main/css/color-sample.css b/src/js/src/main/css/color-sample.css
new file mode 100644
index 0000000..2921260
--- /dev/null
+++ b/src/js/src/main/css/color-sample.css
@@ -0,0 +1,30 @@
+body {
+ background-color:#28385E
+}
+
+#main > div {
+ display: inline-block;
+ width: 100px;
+ height:50px;
+ vertical-align: top;
+}
+
+#main > div:nth-child(1) {
+ background-color: #516C8D;
+}
+#main > div:nth-child(2) {
+ background-color: #6A91C1;
+}
+#main > div:nth-child(3) {
+ background-color: #aac0db;
+}
+#main > div:nth-child(4) {
+ background-color: #CCCCCC;
+}
+#main > div:nth-child(5) {
+ background-color: #ffffff;
+}
+
+#plugins {
+ display: none;
+}
\ No newline at end of file
diff --git a/src/js/src/main/css/index.css b/src/js/src/main/css/index.css
new file mode 100644
index 0000000..d567feb
--- /dev/null
+++ b/src/js/src/main/css/index.css
@@ -0,0 +1,247 @@
+html, body {
+ margin: 0em;
+ height: 100%;
+}
+
+body {
+ color: #ffffff;
+ background-color: #6A91C1;
+ font-size: 16px;
+ margin: 0em;
+}
+
+#header {
+ color:#ffffff;
+ font-size: 1.25em;
+ background-color: #6A91C1;
+ margin-bottom: 0em;
+ margin-left: 0.5em;
+ height: 4vh;
+ vertical-align: middle;
+}
+
+#header > p {
+ margin: 0em;
+ padding-left: 0.5em;
+ padding-bottom: 0px;;
+}
+
+#main {
+ grid-template-columns: 3fr 7fr;
+ white-space:nowrap;
+ overflow-x: scroll;
+ perspective: 3000px;
+ box-sizing: border-box;
+ height: 85vh;
+}
+
+#main.expanded {
+ display: grid;
+ grid-template-columns: 250px 1fr;
+}
+
+#main.shrinked {
+ display: grid;
+ grid-template-columns: 50px 1fr;
+}
+
+#main > div {
+ vertical-align: top;
+ margin-right: 0.25em;
+}
+
+
+#left-menu {
+ font-size: 1.25em;
+ height: 100%;
+ min-height: 640px;
+ max-height: 100%;
+ margin-left: 0.5em;
+ color:#ffffff;
+ background-color: #6A91C1;
+}
+
+/*
+#left-menu.expanded.initial li {
+ animation: left-menu-expanding forwards 0s ease 0s normal;
+}
+
+#left-menu.expanded li {
+ animation: left-menu-expanding forwards 0.2s ease 0s normal;
+}
+
+#left-menu.shrinked li {
+ animation: left-menu-shrinking forwards 0.2s ease 0s normal;
+}
+
+@keyframes left-menu-expanding {
+ 0% {
+ min-width: 1.5em;
+ max-width: 1.5em;
+ }
+ 100% {
+ min-width: 300px;
+ max-width: inherit;
+ }
+}
+
+@keyframes left-menu-shrinking {
+ 0% {
+ min-width: 300px;
+ max-width: inherit;
+ }
+ 100% {
+ min-width: 1.5em;
+ max-width: 1.5em;
+ }
+}
+*/
+#left-menu ol {
+ padding-left: 0em;
+ margin: 0px;
+}
+
+#left-menu li:nth-child(1) {
+ border-top-style:solid;
+ border-top-width:0.1em;
+ border-top-color:#ffffff;
+}
+#left-menu li {
+ padding-top: 0.25em;
+ padding-bottom:0.25em;
+ padding-left:0.25em;
+ border-bottom-style:solid;
+ border-bottom-width:0.1em;
+ border-bottom-color: #ffffff;
+ list-style:none;
+ height: 1.25em;
+ overflow:hidden;
+ text-overflow: clip;
+
+}
+
+#left-menu img {
+ padding-right: 0.25em;
+ width:auto;
+ height:auto;
+ max-width:100%;
+ max-height:100%;
+}
+
+
+#left-menu li,#left-menu img {
+ vertical-align:middle;
+}
+
+#left-menu li:hover {
+ background-color: #516C8D;
+}
+
+#main-pane {
+ width:100%;
+ height: inherit;
+ background-color:#6A91C1;
+ display: block;
+}
+
+#main-tabs {
+ margin:0em;
+ padding: 0em;
+}
+
+#main-tabs li.tab {
+ display: inline-block;
+ color: #516C8D;
+ background-color: #ffffff;
+
+
+ border-width: 0em;
+ border-style:solid;
+ border-color:#516C8D;
+ padding:0.4em;
+ min-width:5em;
+}
+
+
+#main-tabs li.tab:hover {
+ background-color: #cccccc;
+}
+#main-tabs li.tab.selected {
+ border-bottom-color:#ffffff;
+ border-bottom-width: 0.25em;
+}
+
+#main-tabs li.tab:hover.selected {
+ background-color: #ffffff;
+ border-bottom-color:#ffffff;
+}
+
+
+div.main-article {
+ display: none;
+ overflow-y: scroll;
+}
+
+div.main-article.selected {
+ display: inline-block;
+ font-size: 1em;
+ min-width: 300px;
+ min-height: 480px;
+ width:100%;
+ height:calc(80vh - 1em);
+ padding :0em;
+ padding-left : 0.5em;
+ color: #000000;
+ background-color: #ffffff;
+}
+
+#main-pane div.type-iframe iframe,
+#main-pane #petari {
+ width:100%;
+ height:100%;
+ overflow: auto;
+}
+
+#main-pane div.app-container {
+ border: 0.1em solid #516C8D;
+ height: 100%;
+ visibility: initial;
+ resize: both;
+}
+
+#footer {
+ bottom: 0px;
+ position: sticky;
+ height: 10vh;
+ width: 100%;
+ padding-left: 50px;
+ padding-right: 50px;
+ box-sizing: border-box;
+}
+
+#cli-pane {
+ border-style: solid;
+ border-width: 2px;
+ width: 95%;
+ height: 100%;
+ font-size: 16px;
+
+}
+
+#cli-pane div.control {
+ margin-left: 1em;
+ margin-right: 1em;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ width: 90%;
+}
+
+#commandline {
+ width: 100%;
+ font-size: 16px;
+}
+
+
+#logarea {
+ display:none;
+}
diff --git a/src/js/src/main/img/menu-empty.jpeg b/src/js/src/main/img/menu-empty.jpeg
new file mode 100644
index 0000000..a450308
--- /dev/null
+++ b/src/js/src/main/img/menu-empty.jpeg
Binary files differ
diff --git a/src/js/src/main/img/menu-empty.svg b/src/js/src/main/img/menu-empty.svg
new file mode 100644
index 0000000..bda0108
--- /dev/null
+++ b/src/js/src/main/img/menu-empty.svg
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/src/js/src/main/img/menu-home.png b/src/js/src/main/img/menu-home.png
new file mode 100644
index 0000000..0575b0d
--- /dev/null
+++ b/src/js/src/main/img/menu-home.png
Binary files differ
diff --git a/src/js/src/main/img/menu-home.svg b/src/js/src/main/img/menu-home.svg
new file mode 100644
index 0000000..aaf5daa
--- /dev/null
+++ b/src/js/src/main/img/menu-home.svg
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/src/js/src/main/img/menu-toggle.png b/src/js/src/main/img/menu-toggle.png
new file mode 100644
index 0000000..f0990d9
--- /dev/null
+++ b/src/js/src/main/img/menu-toggle.png
Binary files differ
diff --git a/src/js/src/main/img/menu-toggle.svg b/src/js/src/main/img/menu-toggle.svg
new file mode 100644
index 0000000..f5d6818
--- /dev/null
+++ b/src/js/src/main/img/menu-toggle.svg
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/src/js/src/main/index.js b/src/js/src/main/index.js
new file mode 100644
index 0000000..bc35c34
--- /dev/null
+++ b/src/js/src/main/index.js
@@ -0,0 +1,35 @@
+const { app, BrowserWindow, ipcMain} = require('electron');
+const path = require('path');
+const fs = require('fs');
+
+ipcMain.on("write.local", (ipcEvent, savePath, content ) => {
+ console.log("listen wirte.local message");
+ let dest = path.join(__dirname, "..", "var","local", "petari", "app.html");
+ fs.writeFile(dest, content , (err) => {
+ if(err) {
+ console.log("dest: " + dest);
+ console.log("content: " + content);
+ console.log(err);
+ }
+ });
+});
+
+
+function createWindow() {
+ const preloadPath = path.join(__dirname, 'preload.js')
+ console.log(preloadPath);
+
+ let win = new BrowserWindow({
+ width:640,
+ height: 480,
+ webPreferences: {
+ enableRemoteModule: true,
+ preload: preloadPath,
+ }
+ });
+ win.loadFile('app.html');
+}
+
+
+
+app.whenReady().then(createWindow)
\ No newline at end of file
diff --git a/src/js/src/main/preload.js b/src/js/src/main/preload.js
new file mode 100644
index 0000000..9149c24
--- /dev/null
+++ b/src/js/src/main/preload.js
@@ -0,0 +1,16 @@
+const {contextBridge, ipcRenderer} = require('electron');
+const { remote } = require('electron');
+console.log("preload");
+window.remote = remote;
+
+contextBridge.exposeInMainWorld('solitude', {
+ "write": {
+ "local" : (path,contents) => ipcRenderer.send("write.local", path,contents)
+ },
+
+ "env": {
+ "dir" : __dirname
+ },
+
+ get style() { return "electron" },
+})
diff --git a/src/js/src/main/web-conf.js b/src/js/src/main/web-conf.js
new file mode 100644
index 0000000..b99530c
--- /dev/null
+++ b/src/js/src/main/web-conf.js
@@ -0,0 +1,65 @@
+function webconfig(global) {
+ global.tmp = global.tmp || {};
+ global.tmp.solitudeapps = {};
+ const configurator = createAppConfigurator(global.tmp.solitudeapps);
+ loadTo(configurator);
+}
+
+function createAppConfigurator(target) {
+ const app = ((writeback)=> {
+ const apps = writeback;
+ let currentApp = {};
+
+ const result = {
+ set new(val) {
+ apps[val] = {};
+ currentApp = val;
+ },
+
+ set type(val) {
+ apps[currentApp].type = val;
+ },
+
+ set src(val) {
+ apps[currentApp].src = val;
+ },
+
+ get prop() {
+ return apps[currentApp];
+ }
+
+ }
+
+ return result;
+ })(target);
+
+ return app
+}
+
+
+function loadTo(app) {
+ app.new = "main";
+ app.type = "mainType";
+
+ app.new = "logger";
+ app.type = "staticpage";
+ app.src = "../simplelog/app.html"
+
+ app.new = "mdeditor";
+ app.type = "iframe";
+ app.src = "../mdeditor/app.html"
+
+ app.new = "example"
+ app.type = "staticpage"
+ app.src = "../example/app.html"
+ app.prop.loader = fetch;
+
+ app.new = "bookmark";
+ app.type = "iframe";
+ app.src = "../bookmark/app.html"
+
+ app.new = "logData";
+ app.type = "batch";
+ app.src = "../logData/app.js"
+
+}
\ No newline at end of file
diff --git a/src/js/src/mdeditor/app.html b/src/js/src/mdeditor/app.html
new file mode 100644
index 0000000..688b0fe
--- /dev/null
+++ b/src/js/src/mdeditor/app.html
@@ -0,0 +1,309 @@
+
+
+
+
+
+
+ Marked in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Marked in the browser
+
Rendered by marked.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/js/src/mdeditor/articles/example/example.md b/src/js/src/mdeditor/articles/example/example.md
new file mode 100644
index 0000000..f4e61ee
--- /dev/null
+++ b/src/js/src/mdeditor/articles/example/example.md
@@ -0,0 +1,78 @@
+# 段落1
+## 段落2
+### 段落3
+#### 段落4
+##### 段落5
+###### 段落6
+
+てきとうな
+本文を
+ここに書く
+
+
+-------
+
+## 引用ブロック
+
+> 引用ブロックです。
+>
+> 我思う 故に我あり
+>
+> > ## 引用ブロック内の段落2
+> > そして入れ子の引用ブロック
+>
+> 元のブロックに戻る
+
+こうやって*強調*するらしい。
+アスタリスク二つでより**強い強調**、うーん頭痛が痛い。
+アンダースコア2つでも__強い強調__になる。馬から落馬した。
+ならないじゃないか。
+
+## リスト
+
+- おにぎり
+- 味噌汁
+- お漬物
+ - きゅうり
+ - なす
+ - たくあん
+- 焼き魚
+
+CSSと合わせてタグのリストっぽくする作りにしてみました。
+
+
+
+* foo
+* bar
+* fiz
+* baz
+* 料理
+* おにぎり
+
+
+
+やり方はtag_listクラスを持つdivタグで囲ってあげるだけ。
+日本語と英語で高さが変わって1時間悩んだ。デザイナさんって大変。
+
+```
+
+
+* foo
+* bar
+* fizz
+* bazz
+* 料理
+* おにぎり
+
+
+
+```
+
+## 大きな画像
+
+大きな画像は縮小する。何故って?A4用紙にちゃんと印刷をしたいからです。
+
+とあるプログラマの作業風景(スクリーンショット)
+
+
+
diff --git a/src/js/src/mdeditor/articles/example/large_image_sample.png b/src/js/src/mdeditor/articles/example/large_image_sample.png
new file mode 100644
index 0000000..1898475
--- /dev/null
+++ b/src/js/src/mdeditor/articles/example/large_image_sample.png
Binary files differ
diff --git a/src/js/src/mdeditor/lib/jquery-3.5.1.js b/src/js/src/mdeditor/lib/jquery-3.5.1.js
new file mode 100644
index 0000000..5093733
--- /dev/null
+++ b/src/js/src/mdeditor/lib/jquery-3.5.1.js
@@ -0,0 +1,10872 @@
+/*!
+ * jQuery JavaScript Library v3.5.1
+ * https://jquery.com/
+ *
+ * Includes Sizzle.js
+ * https://sizzlejs.com/
+ *
+ * Copyright JS Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2020-05-04T22:49Z
+ */
+( function( global, factory ) {
+
+ "use strict";
+
+ if ( typeof module === "object" && typeof module.exports === "object" ) {
+
+ // For CommonJS and CommonJS-like environments where a proper `window`
+ // is present, execute the factory and get jQuery.
+ // For environments that do not have a `window` with a `document`
+ // (such as Node.js), expose a factory as module.exports.
+ // This accentuates the need for the creation of a real `window`.
+ // e.g. var jQuery = require("jquery")(window);
+ // See ticket #14549 for more info.
+ module.exports = global.document ?
+ factory( global, true ) :
+ function( w ) {
+ if ( !w.document ) {
+ throw new Error( "jQuery requires a window with a document" );
+ }
+ return factory( w );
+ };
+ } else {
+ factory( global );
+ }
+
+// Pass this if window is not defined yet
+} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
+// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
+// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
+// enough that all such attempts are guarded in a try block.
+"use strict";
+
+var arr = [];
+
+var getProto = Object.getPrototypeOf;
+
+var slice = arr.slice;
+
+var flat = arr.flat ? function( array ) {
+ return arr.flat.call( array );
+} : function( array ) {
+ return arr.concat.apply( [], array );
+};
+
+
+var push = arr.push;
+
+var indexOf = arr.indexOf;
+
+var class2type = {};
+
+var toString = class2type.toString;
+
+var hasOwn = class2type.hasOwnProperty;
+
+var fnToString = hasOwn.toString;
+
+var ObjectFunctionString = fnToString.call( Object );
+
+var support = {};
+
+var isFunction = function isFunction( obj ) {
+
+ // Support: Chrome <=57, Firefox <=52
+ // In some browsers, typeof returns "function" for HTML