async getModel() { const model = {}; model.nodepath = ko.pureComputed(() => ko.unwrap(this.props.nodepath)); model.person = ko.observable(false); model.height = ko.observable(300); model.zoom = ko.observable(0.8); let dragging = false; let canvasElement = false; let markDragging = false; let markElement = false; let meOffset = { top: 0, left: 0 }; let canvasSize = { width: 0, height: 0 }; model.startDragHandler = (data, event) => { dragging = { top: event.clientY, left: event.clientX }; canvasElement = $("#relation-canvas"); }; model.dragHandler = (data, event) => { event.preventDefault(); event.stopPropagation(); if (dragging) { let diffTop = event.clientY - dragging.top; let diffLeft = event.clientX - dragging.left; let position = canvasElement.position(); canvasElement.css("top", position.top + diffTop); canvasElement.css("left", position.left + diffLeft); dragging = { top: event.clientY, left: event.clientX }; model._storePosition(); } else if (markDragging) { let top = event.clientY - markElement.offset().top; model.setZoomByPosition(top, true); } }; model.stopDragHandler = (data, event) => { event.preventDefault(); event.stopPropagation(); if (dragging) { dragging = false; canvasElement = false; } if (markDragging) { let top = event.clientY - markElement.offset().top; model.setZoomByPosition(top); markDragging = false; } }; model.round = (value, precision) => { return Math.round(value * Math.pow(10, precision)) / Math.pow(10, precision); }; model.scrollHandler = (/*data, event*/) => { // let wheelData = event.detail ? event.detail * -1 : event.wheelDelta / 40; // // wheelData /= 50; // // event.preventDefault(); // event.stopPropagation(); // // model.zoomSet(model.round(model.zoom() + wheelData, 1)); }; model.zoomSet = (value, noanimation) => { value = value < 0.2 ? 0.2 : value; value = value > 1.6 ? 1.6 : value; model.zoom(value); model._adjustCanvasPosition(); let markElement = $(".mark-container .mark"); let containerHeight = $(".mark-container").innerHeight(); let markHeight = markElement.outerHeight(); containerHeight -= markHeight; value -= 0.1; let step = containerHeight / 14; let top = (containerHeight - step * ((value - 0.1) * 10)); markElement.stop(); if (noanimation) { markElement.css("top", top); } else { markElement.animate({ top: top }, 50); } }; model.zoomInc = () => { model.zoomSet(model.zoom() + 0.1); }; model.zoomDec = () => { model.zoomSet(model.zoom() - 0.1); }; model.markContainerClickHandler = (data, event) => { event.preventDefault(); event.stopPropagation(); if (event.srcElement !== $(".mark-container")[0]) { return; } model.setZoomByPosition(event.offsetY); }; model.setZoomByPosition = (top, noanimation) => { let containerHeight = $(".mark-container").innerHeight(); let markHeight = $(".mark-container .mark").outerHeight(); top -= markHeight; containerHeight -= markHeight; let step = containerHeight / 14; model.zoomSet(model.round((containerHeight - top) / (step * 10), 1) + 0.1, noanimation); }; model.startDragMarkHandler = (data, event) => { event.preventDefault(); event.stopPropagation(); markElement = $(".mark-container"); markDragging = true; }; model._storePosition = () => { let canvasElement = $("#relation-canvas"); let meElement = $(".relation-me"); meOffset = meElement.offset(); canvasSize = { width: canvasElement.width(), height: canvasElement.height() }; }; model._adjustCanvasPosition = () => { let canvasElement = $("#relation-canvas"); if (canvasElement.is(":visible")) { let size = { width: canvasElement.width(), height: canvasElement.height() }; let diffHeight = (canvasSize.height - size.height) / 2; let diffWidth = (canvasSize.width - size.width) / 2; let position = canvasElement.position(); canvasElement.css("top", position.top + diffHeight); canvasElement.css("left", position.left + diffWidth); model._storePosition(); } }; model._adjustPosition = () => { let canvasElement = $("#relation-canvas"); if (canvasElement.is(":visible")) { let meElement = $(".relation-me"); let offset = meElement.offset(); let diffTop = meOffset.top - offset.top; let diffLeft = meOffset.left - offset.left; let position = canvasElement.position(); canvasElement.css("top", position.top + diffTop); canvasElement.css("left", position.left + diffLeft); model._storePosition(); } }; model._center = () => { let meElement = $(".relation-me"); if (meElement.length === 0 || meElement.width() === 0) { setTimeout(() => { model._center(); }, 100); return; } let canvasElement = $("#relation-canvas"); let element = $("#relation-element"); let position = meElement.position(); position.left -= (element.width() - meElement.width()) / 2; position.top -= (model.height() - meElement.height()) / 2; canvasElement.css("top", -position.top); canvasElement.css("left", -position.left); model._storePosition(); }; model.createPerson = (parentNodeData, nodeData, nodePath, metrics, type, index, count) => { console.log("createPerson", nodeData, nodePath); nodeData.tree = {}; nodeData.tree.parentsVisible = ko.observable(false); nodeData.tree.childrenVisible = ko.observable(false); nodeData.tree.first = index === 0; nodeData.tree.last = index === count - 1; nodeData.tree.type = type; nodeData.tree.parentsLoading = ko.observable(false); nodeData.tree.childrenLoading = ko.observable(false); nodeData.tree.parentsLoaded = false; nodeData.tree.childrenLoaded = false; nodeData.tree.parents = ko.observableArray(); nodeData.tree.children = ko.observableArray(); nodeData.tree.nodepath = ko.observable({ node: nodeData, path: nodePath }); nodeData.tree.metrics = ko.observable(metrics); nodeData.tree.expandParents = () => { nodeData.tree.parentsVisible(!nodeData.tree.parentsVisible()); model._adjustPosition(); }; nodeData.tree.expandChildren = () => { nodeData.tree.childrenVisible(!nodeData.tree.childrenVisible()); model._adjustPosition(); }; nodeData.tree.loadParents = async () => { if (!nodeData.tree.parents.loaded) { nodeData.tree.parents.loaded = true; nodeData.tree.parentsLoading(true); let list = await api.vfs.list(nodePath + "/parents"); nodeData.tree.parentsLoading(false); list.sort(function(a, b) { if (a.node.attributes.gender === b.node.attributes.gender) { return 0; } else if (a.node.attributes.gender === "m") { return 1; } else if (a.node.attributes.gender === "f") { return -1; } return 0; }); for (let n = 0; n < list.length; n++) { let metrics = await api.people.getMetrics(list[n].path); nodeData.tree.parents.push(model.createPerson(nodeData, list[n].node, list[n].path, metrics, "parent", n, list.length)); } model._adjustPosition(); } }; nodeData.tree.loadChildren = async () => { if (!nodeData.tree.children.loaded) { nodeData.tree.children.loaded = true; nodeData.tree.childrenLoading(true); let list = await api.vfs.list(nodePath + "/children"); nodeData.tree.childrenLoading(false); for (let child of list) { child.metrics = await api.people.getMetrics(child.path); } list.sort((a, b) => { console.log(a, b); if (a.metrics.birthdate === b.metrics.birthdate) { return 0; } else if (!a.metrics.birthdate) { return -1; } else if (!b.metrics.birthdate) { return 1; } return b.metrics.age - a.metrics.age; }); for (let n = 0; n < list.length; n++) { nodeData.tree.children.push(model.createPerson(nodeData, list[n].node, list[n].path, list[n].metrics, "child", n, list.length)); } model._adjustPosition(); } }; if (!parentNodeData) { nodeData.tree.loadParents().catch(stat.printError); nodeData.tree.loadChildren().catch(stat.printError); } else { parentNodeData.tree.parentsVisible.subscribe((value) => { if (value) { nodeData.tree.loadParents(); } }); parentNodeData.tree.childrenVisible.subscribe((value) => { if (value) { nodeData.tree.loadChildren(); } }); } return nodeData; }; model.afterRender = () => { model.height($(window).height() - 51); model._center(); model.zoomSet(0.8); }; model.zoomSet(0.8); model.person = ko.asyncComputed(false, async (setter) => { if (!model.nodepath()) { return false; } setter(false); let metrics = await api.people.getMetrics(model.nodepath().path); return model.createPerson(null, ko.unwrap(model.nodepath().node), model.nodepath().path, metrics, "me", 0, 1); }, (error) => { stat.printError(error); return false; }); return model; }
async getModel() { const model = {}; model.nodepath = ko.pureComputed(() => ko.unwrap(this.props.nodepath)); model.loading = stat.create(); model.reload = ko.observable(false); model.createTitle = ko.observable(""); model.createType = ko.observable("generic"); model.createText = ko.observable(""); model.createTime = ko.observable(false); model.createPersonPath = ko.observable(false); model.texts = ko.asyncComputed([], async (setter) => { if (!model.nodepath() || model.nodepath() === "") { return []; } model.reload(); setter([]); model.loading(true); let texts = await api.vfs.list(model.nodepath().path + "/texts", { checkwritable: true }); utils.sortNodeList(texts); console.log("texts", texts); let list = []; for (let text of texts) { let paths = await api.vfs.lookup(text.node._id); let withPaths = paths.filter((path) => path !== text.path).map((path) => path.split("/", 3).join("/")); list.push(ko.observable({ path: text.path, node: ko.observable(text.node), editable: text.editable, withPaths: withPaths })); } model.loading(false); return list; }, (error) => { model.loading(false); stat.printError(error); return []; }); model.events = ko.asyncComputed([], async (setter) => { if (!model.nodepath() || model.nodepath() === "") { return []; } model.reload(); setter([]); model.loading(true); let texts = await api.vfs.list(model.nodepath().path + "/texts", { checkwritable: true }); utils.sortNodeList(texts); console.log("texts", texts); let days = {}; for (let text of texts) { let day = moment.utc(text.node.attributes.time.timestamp * 1000).format("YYYY-MM-DD"); let paths = await api.vfs.lookup(text.node._id); let withPaths = paths.filter((path) => path !== text.path).map((path) => path.split("/", 3).join("/")); days[day] = days[day] || { texts: [], day: text.node.attributes.time.timestamp }; days[day].texts.push(ko.observable({ path: text.path, node: ko.observable(text.node), editable: text.editable, withPaths: withPaths })); } days = Object.keys(days).map((key) => days[key]); days.sort((a, b) => { return a.day - b.day; }); console.log("days", days); model.loading(false); return days; }, (error) => { model.loading(false); stat.printError(error); return []; }); model.createShow = () => { $("#createPeopleEventModal").modal("show"); }; model.createEvent = () => { console.log("type", model.createType()); console.log("title", model.createTitle()); console.log("time", model.createTime()); console.log("person", model.createPersonPath()); console.log("text", model.createText()); let basepath = model.nodepath().path + "/texts"; let abspath = ""; let attributes = { type: model.createType(), name: model.createTitle().trim(), text: model.createText().trim(), when: { manual: model.createTime() } }; if (attributes.name === "") { stat.printError("Name can not be empty"); return; } if (!attributes.when.manual) { throw new Error("An event must must have date/time set"); } api.vfs.uniqueName(basepath, attributes.name) .then((name) => { abspath = basepath + "/" + name; return api.text.mktext(abspath, attributes); }) .then(() => { if (model.createPersonPath()) { return api.vfs.link(abspath, model.createPersonPath() + "/texts"); } }) .then(() => { model.createType("generic"); model.createTitle(""); model.createTime(false); model.createPersonPath(false); model.createText(""); $("#createPeopleEventModal").modal("hide"); model.reload(!model.reload()); stat.printSuccess(attributes.name + " successfully created!"); }) .catch((error) => { stat.printError(error); }); }; model.dispose = () => { stat.destroy(model.loading); }; return model; }