GraphEditor now support mermaid

This commit is contained in:
Xuan Sang LE
2018-03-16 19:53:32 +01:00
parent 2f57c05ef3
commit 0ff98fee98
15 changed files with 59525 additions and 252 deletions

View File

@ -0,0 +1,10 @@
coffee_files = main.coffee
jsfiles = libs/viz-lite.js libs/svg-pan-zoom.js libs/mermaidAPI.js
cssfiles = main.css
copyfiles = scheme.html package.json
PKG_NAME=DotEditor
include ../pkg.mk

View File

@ -0,0 +1,273 @@
/* Flowchart variables */
/* Sequence Diagram variables */
/* Gantt chart variables */
afx-app-window[data-id="graph_editor_win"] .mermaid .label {
color: #333;
}
afx-app-window[data-id="graph_editor_win"] .node rect,
afx-app-window[data-id="graph_editor_win"] .node circle,
afx-app-window[data-id="graph_editor_win"] .node ellipse,
afx-app-window[data-id="graph_editor_win"] .node polygon {
fill: #ECECFF;
stroke: #CCCCFF;
stroke-width: 1px;
}
afx-app-window[data-id="graph_editor_win"] .edgePath .path {
stroke: #333333;
}
afx-app-window[data-id="graph_editor_win"] .edgeLabel {
background-color: #e8e8e8;
}
afx-app-window[data-id="graph_editor_win"] .cluster rect {
fill: #ffffde !important;
rx: 4 !important;
stroke: #aaaa33 !important;
stroke-width: 1px !important;
}
afx-app-window[data-id="graph_editor_win"] .cluster text {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .actor {
stroke: #CCCCFF;
fill: #ECECFF;
}
afx-app-window[data-id="graph_editor_win"] text.actor {
fill: black;
stroke: none;
}
afx-app-window[data-id="graph_editor_win"] .actor-line {
stroke: grey;
}
afx-app-window[data-id="graph_editor_win"] .messageLine0 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #333;
}
afx-app-window[data-id="graph_editor_win"] .messageLine1 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
stroke: #333;
}
afx-app-window[data-id="graph_editor_win"] #arrowhead {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] #crosshead path {
fill: #333 !important;
stroke: #333 !important;
}
afx-app-window[data-id="graph_editor_win"] .messageText {
fill: #333;
stroke: none;
}
afx-app-window[data-id="graph_editor_win"] .labelBox {
stroke: #CCCCFF;
fill: #ECECFF;
}
afx-app-window[data-id="graph_editor_win"] .labelText {
fill: black;
stroke: none;
}
afx-app-window[data-id="graph_editor_win"] .loopText {
fill: black;
stroke: none;
}
afx-app-window[data-id="graph_editor_win"] .loopLine {
stroke-width: 2;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #CCCCFF;
}
afx-app-window[data-id="graph_editor_win"] .note {
stroke: #aaaa33;
fill: #fff5ad;
}
afx-app-window[data-id="graph_editor_win"] .noteText {
fill: black;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
/** Section styling */
afx-app-window[data-id="graph_editor_win"] .section {
stroke: none;
opacity: 0.2;
}
afx-app-window[data-id="graph_editor_win"] .section0 {
fill: rgba(102, 102, 255, 0.49);
}
afx-app-window[data-id="graph_editor_win"] .section2 {
fill: #fff400;
}
afx-app-window[data-id="graph_editor_win"] .section1,
afx-app-window[data-id="graph_editor_win"] .section3 {
fill: white;
opacity: 0.2;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle0 {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle1 {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle2 {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle3 {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle {
text-anchor: start;
font-size: 11px;
text-height: 14px;
}
/* Grid and axis */
afx-app-window[data-id="graph_editor_win"] .grid .tick {
stroke: lightgrey;
opacity: 0.3;
shape-rendering: crispEdges;
}
afx-app-window[data-id="graph_editor_win"] .grid path {
stroke-width: 0;
}
/* Today line */
afx-app-window[data-id="graph_editor_win"] .today {
fill: none;
stroke: red;
stroke-width: 2px;
}
/* Task styling */
/* Default task */
afx-app-window[data-id="graph_editor_win"] .task {
stroke-width: 2;
}
afx-app-window[data-id="graph_editor_win"] .taskText {
text-anchor: middle;
font-size: 11px;
}
afx-app-window[data-id="graph_editor_win"] .taskTextOutsideRight {
fill: black;
text-anchor: start;
font-size: 11px;
}
afx-app-window[data-id="graph_editor_win"] .taskTextOutsideLeft {
fill: black;
text-anchor: end;
font-size: 11px;
}
/* Specific task settings for the sections*/
afx-app-window[data-id="graph_editor_win"] .taskText0,
afx-app-window[data-id="graph_editor_win"] .taskText1,
afx-app-window[data-id="graph_editor_win"] .taskText2,
afx-app-window[data-id="graph_editor_win"] .taskText3 {
fill: white;
}
afx-app-window[data-id="graph_editor_win"] .task0,
afx-app-window[data-id="graph_editor_win"] .task1,
afx-app-window[data-id="graph_editor_win"] .task2,
afx-app-window[data-id="graph_editor_win"] .task3 {
fill: #8a90dd;
stroke: #534fbc;
}
afx-app-window[data-id="graph_editor_win"] .taskTextOutside0,
afx-app-window[data-id="graph_editor_win"] .taskTextOutside2 {
fill: black;
}
afx-app-window[data-id="graph_editor_win"] .taskTextOutside1,
afx-app-window[data-id="graph_editor_win"] .taskTextOutside3 {
fill: black;
}
/* Active task */
afx-app-window[data-id="graph_editor_win"] .active0,
afx-app-window[data-id="graph_editor_win"] .active1,
afx-app-window[data-id="graph_editor_win"] .active2,
afx-app-window[data-id="graph_editor_win"] .active3 {
fill: #bfc7ff;
stroke: #534fbc;
}
afx-app-window[data-id="graph_editor_win"] .activeText0,
afx-app-window[data-id="graph_editor_win"] .activeText1,
afx-app-window[data-id="graph_editor_win"] .activeText2,
afx-app-window[data-id="graph_editor_win"] .activeText3 {
fill: black !important;
}
/* Completed task */
afx-app-window[data-id="graph_editor_win"] .done0,
afx-app-window[data-id="graph_editor_win"] .done1,
afx-app-window[data-id="graph_editor_win"] .done2,
afx-app-window[data-id="graph_editor_win"] .done3 {
stroke: grey;
fill: lightgrey;
stroke-width: 2;
}
afx-app-window[data-id="graph_editor_win"] .doneText0,
afx-app-window[data-id="graph_editor_win"] .doneText1,
afx-app-window[data-id="graph_editor_win"] .doneText2,
afx-app-window[data-id="graph_editor_win"] .doneText3 {
fill: black !important;
}
/* Tasks on the critical line */
afx-app-window[data-id="graph_editor_win"] .crit0,
afx-app-window[data-id="graph_editor_win"] .crit1,
afx-app-window[data-id="graph_editor_win"] .crit2,
afx-app-window[data-id="graph_editor_win"] .crit3 {
stroke: #ff8888;
fill: red;
stroke-width: 2;
}
afx-app-window[data-id="graph_editor_win"] .activeCrit0,
afx-app-window[data-id="graph_editor_win"] .activeCrit1,
afx-app-window[data-id="graph_editor_win"] .activeCrit2,
afx-app-window[data-id="graph_editor_win"] .activeCrit3 {
stroke: #ff8888;
fill: #bfc7ff;
stroke-width: 2;
}
afx-app-window[data-id="graph_editor_win"] .doneCrit0,
afx-app-window[data-id="graph_editor_win"] .doneCrit1,
afx-app-window[data-id="graph_editor_win"] .doneCrit2,
afx-app-window[data-id="graph_editor_win"] .doneCrit3 {
stroke: #ff8888;
fill: lightgrey;
stroke-width: 2;
cursor: pointer;
shape-rendering: crispEdges;
}
afx-app-window[data-id="graph_editor_win"] .doneCritText0,
afx-app-window[data-id="graph_editor_win"] .doneCritText1,
afx-app-window[data-id="graph_editor_win"] .doneCritText2,
afx-app-window[data-id="graph_editor_win"] .doneCritText3 {
fill: black !important;
}
afx-app-window[data-id="graph_editor_win"] .activeCritText0,
afx-app-window[data-id="graph_editor_win"] .activeCritText1,
afx-app-window[data-id="graph_editor_win"] .activeCritText2,
afx-app-window[data-id="graph_editor_win"] .activeCritText3 {
fill: black !important;
}
afx-app-window[data-id="graph_editor_win"] .titleText {
text-anchor: middle;
font-size: 18px;
fill: black;
}
/*
*/
afx-app-window[data-id="graph_editor_win"] .node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
afx-app-window[data-id="graph_editor_win"] div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: 'trebuchet ms', verdana, arial;
font-size: 12px;
background: #ffffde;
border: 1px solid #aaaa33;
border-radius: 2px;
pointer-events: none;
z-index: 100;
}

View File

@ -0,0 +1,353 @@
/* Flowchart variables */
/* Sequence Diagram variables */
/* Gantt chart variables */
afx-app-window[data-id="graph_editor_win"] .mermaid .label {
font-family: 'trebuchet ms', verdana, arial;
color: #333;
}
afx-app-window[data-id="graph_editor_win"] .node rect,
afx-app-window[data-id="graph_editor_win"] .node circle,
afx-app-window[data-id="graph_editor_win"] .node ellipse,
afx-app-window[data-id="graph_editor_win"] .node polygon {
fill: #cde498;
stroke: #13540c;
stroke-width: 1px;
}
afx-app-window[data-id="graph_editor_win"] .edgePath .path {
stroke: green;
stroke-width: 1.5px;
}
afx-app-window[data-id="graph_editor_win"] .edgeLabel {
background-color: #e8e8e8;
}
afx-app-window[data-id="graph_editor_win"] .cluster rect {
fill: #cdffb2 !important;
rx: 4 !important;
stroke: #6eaa49 !important;
stroke-width: 1px !important;
}
afx-app-window[data-id="graph_editor_win"] .cluster text {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .actor {
stroke: #13540c;
fill: #cde498;
}
afx-app-window[data-id="graph_editor_win"] text.actor {
fill: black;
stroke: none;
}
afx-app-window[data-id="graph_editor_win"] .actor-line {
stroke: grey;
}
afx-app-window[data-id="graph_editor_win"] .messageLine0 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #333;
}
afx-app-window[data-id="graph_editor_win"] .messageLine1 {
stroke-width: 1.5;
stroke-dasharray: "2 2";
stroke: #333;
}
afx-app-window[data-id="graph_editor_win"] #arrowhead {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] #crosshead path {
fill: #333 !important;
stroke: #333 !important;
}
afx-app-window[data-id="graph_editor_win"] .messageText {
fill: #333;
stroke: none;
}
afx-app-window[data-id="graph_editor_win"] .labelBox {
stroke: #326932;
fill: #cde498;
}
afx-app-window[data-id="graph_editor_win"] .labelText {
fill: black;
stroke: none;
}
afx-app-window[data-id="graph_editor_win"] .loopText {
fill: black;
stroke: none;
}
afx-app-window[data-id="graph_editor_win"] .loopLine {
stroke-width: 2;
stroke-dasharray: "2 2";
marker-end: "url(#arrowhead)";
stroke: #326932;
}
afx-app-window[data-id="graph_editor_win"] .note {
stroke: #6eaa49;
fill: #fff5ad;
}
afx-app-window[data-id="graph_editor_win"] .noteText {
fill: black;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
/** Section styling */
afx-app-window[data-id="graph_editor_win"] .section {
stroke: none;
opacity: 0.2;
}
afx-app-window[data-id="graph_editor_win"] .section0 {
fill: #6eaa49;
}
afx-app-window[data-id="graph_editor_win"] .section2 {
fill: #6eaa49;
}
afx-app-window[data-id="graph_editor_win"] .section1,
afx-app-window[data-id="graph_editor_win"] .section3 {
fill: white;
opacity: 0.2;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle0 {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle1 {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle2 {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle3 {
fill: #333;
}
afx-app-window[data-id="graph_editor_win"] .sectionTitle {
text-anchor: start;
font-size: 11px;
text-height: 14px;
}
/* Grid and axis */
afx-app-window[data-id="graph_editor_win"] .grid .tick {
stroke: lightgrey;
opacity: 0.3;
shape-rendering: crispEdges;
}
afx-app-window[data-id="graph_editor_win"] .grid path {
stroke-width: 0;
}
/* Today line */
afx-app-window[data-id="graph_editor_win"] .today {
fill: none;
stroke: red;
stroke-width: 2px;
}
/* Task styling */
/* Default task */
afx-app-window[data-id="graph_editor_win"] .task {
stroke-width: 2;
}
afx-app-window[data-id="graph_editor_win"] .taskText {
text-anchor: middle;
font-size: 11px;
}
afx-app-window[data-id="graph_editor_win"] .taskTextOutsideRight {
fill: black;
text-anchor: start;
font-size: 11px;
}
afx-app-window[data-id="graph_editor_win"] .taskTextOutsideLeft {
fill: black;
text-anchor: end;
font-size: 11px;
}
/* Specific task settings for the sections*/
afx-app-window[data-id="graph_editor_win"] .taskText0,
afx-app-window[data-id="graph_editor_win"] .taskText1,
afx-app-window[data-id="graph_editor_win"] .taskText2,
afx-app-window[data-id="graph_editor_win"] .taskText3 {
fill: white;
}
afx-app-window[data-id="graph_editor_win"] .task0,
afx-app-window[data-id="graph_editor_win"] .task1,
afx-app-window[data-id="graph_editor_win"] .task2,
afx-app-window[data-id="graph_editor_win"] .task3 {
fill: #487e3a;
stroke: #13540c;
}
afx-app-window[data-id="graph_editor_win"] .taskTextOutside0,
afx-app-window[data-id="graph_editor_win"] .taskTextOutside2 {
fill: black;
}
afx-app-window[data-id="graph_editor_win"] .taskTextOutside1,
afx-app-window[data-id="graph_editor_win"] .taskTextOutside3 {
fill: black;
}
/* Active task */
afx-app-window[data-id="graph_editor_win"] .active0,
afx-app-window[data-id="graph_editor_win"] .active1,
afx-app-window[data-id="graph_editor_win"] .active2,
afx-app-window[data-id="graph_editor_win"] .active3 {
fill: #cde498;
stroke: #13540c;
}
afx-app-window[data-id="graph_editor_win"] .activeText0,
afx-app-window[data-id="graph_editor_win"] .activeText1,
afx-app-window[data-id="graph_editor_win"] .activeText2,
afx-app-window[data-id="graph_editor_win"] .activeText3 {
fill: black !important;
}
/* Completed task */
afx-app-window[data-id="graph_editor_win"] .done0,
afx-app-window[data-id="graph_editor_win"] .done1,
afx-app-window[data-id="graph_editor_win"] .done2,
afx-app-window[data-id="graph_editor_win"] .done3 {
stroke: grey;
fill: lightgrey;
stroke-width: 2;
}
afx-app-window[data-id="graph_editor_win"] .doneText0,
afx-app-window[data-id="graph_editor_win"] .doneText1,
afx-app-window[data-id="graph_editor_win"] .doneText2,
afx-app-window[data-id="graph_editor_win"] .doneText3 {
fill: black !important;
}
/* Tasks on the critical line */
afx-app-window[data-id="graph_editor_win"] .crit0,
afx-app-window[data-id="graph_editor_win"] .crit1,
afx-app-window[data-id="graph_editor_win"] .crit2,
afx-app-window[data-id="graph_editor_win"] .crit3 {
stroke: #ff8888;
fill: red;
stroke-width: 2;
}
afx-app-window[data-id="graph_editor_win"] .activeCrit0,
afx-app-window[data-id="graph_editor_win"] .activeCrit1,
afx-app-window[data-id="graph_editor_win"] .activeCrit2,
afx-app-window[data-id="graph_editor_win"] .activeCrit3 {
stroke: #ff8888;
fill: #cde498;
stroke-width: 2;
}
afx-app-window[data-id="graph_editor_win"] .doneCrit0,
afx-app-window[data-id="graph_editor_win"] .doneCrit1,
afx-app-window[data-id="graph_editor_win"] .doneCrit2,
afx-app-window[data-id="graph_editor_win"] .doneCrit3 {
stroke: #ff8888;
fill: lightgrey;
stroke-width: 2;
cursor: pointer;
shape-rendering: crispEdges;
}
afx-app-window[data-id="graph_editor_win"] .doneCritText0,
afx-app-window[data-id="graph_editor_win"] .doneCritText1,
afx-app-window[data-id="graph_editor_win"] .doneCritText2,
afx-app-window[data-id="graph_editor_win"] .doneCritText3 {
fill: black !important;
}
afx-app-window[data-id="graph_editor_win"] .activeCritText0,
afx-app-window[data-id="graph_editor_win"] .activeCritText1,
afx-app-window[data-id="graph_editor_win"] .activeCritText2,
afx-app-window[data-id="graph_editor_win"] .activeCritText3 {
fill: black !important;
}
afx-app-window[data-id="graph_editor_win"] .titleText {
text-anchor: middle;
font-size: 18px;
fill: black;
}
/*
*/
afx-app-window[data-id="graph_editor_win"] g.classGroup text {
fill: #13540c;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
afx-app-window[data-id="graph_editor_win"] g.classGroup rect {
fill: #cde498;
stroke: #13540c;
}
afx-app-window[data-id="graph_editor_win"] g.classGroup line {
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] svg .classLabel .box {
stroke: none;
stroke-width: 0;
fill: #cde498;
opacity: 0.5;
}
afx-app-window[data-id="graph_editor_win"] svg .classLabel .label {
fill: #13540c;
}
afx-app-window[data-id="graph_editor_win"] .relation {
stroke: #13540c;
stroke-width: 1;
fill: none;
}
afx-app-window[data-id="graph_editor_win"] .composition {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] #compositionStart {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] #compositionEnd {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] .aggregation {
fill: #cde498;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] #aggregationStart {
fill: #cde498;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] #aggregationEnd {
fill: #cde498;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] #dependencyStart {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] #dependencyEnd {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] #extensionStart {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] #extensionEnd {
fill: #13540c;
stroke: #13540c;
stroke-width: 1;
}
afx-app-window[data-id="graph_editor_win"] .node text {
font-family: 'trebuchet ms', verdana, arial;
font-size: 14px;
}
afx-app-window[data-id="graph_editor_win"] div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: 'trebuchet ms', verdana, arial;
font-size: 12px;
background: #cdffb2;
border: 1px solid #6eaa49;
border-radius: 2px;
pointer-events: none;
z-index: 100;
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,280 @@
# Copyright 2017-2018 Xuan Sang LE <xsang.le AT gmail DOT com>
# AnTOS Web desktop is is licensed under the GNU General Public
# License v3.0, see the LICENCE file for more information
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
# You should have received a copy of the GNU General Public License
#along with this program. If not, see https://www.gnu.org/licenses/.
class GraphEditor extends this.OS.GUI.BaseApplication
constructor: ( args ) ->
super "GraphEditor", args
main: () ->
me = @
mermaidAPI.initialize { startOnLoad: false }
@currfile = if @args and @args.length > 0 then @args[0].asFileHandler() else "Untitled".asFileHandler()
@currfile.dirty = false
@datarea = @find "datarea"
@preview = @find "preview"
@renderlist = @find "render-list"
@btctn = @find "btn-container"
@.editor = ace.edit @datarea
@.editor.setOptions {
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
fontSize: "9pt"
}
#@.editor.completers.push { getCompletions: ( editor, session, pos, prefix, callback ) -> }
@editor.getSession().setUseWrapMode true
@editor.session.setMode "ace/mode/dot"
@editor.setTheme "ace/theme/monokai"
@editor.on "input", () ->
if me.editormux
me.editormux = false
return false
if not me.currfile.dirty
me.currfile.dirty = true
@renderlist.set "onlistselect", (e) ->
text = ""
if me.engine() is "Dot"
text = GraphEditor.dummydot
else
text = GraphEditor.dummymermaid
if not me.currfile.basename
me.editormux = true
me.editor.setValue text
me.renderSVG false
@editor.container.addEventListener "keydown", (e) ->
me.renderSVG true if e.keyCode is 13
, true
@bindKey "CTRL-R", () -> me.renderSVG false
@bindKey "ALT-G", () -> me.export "SVG"
@bindKey "ALT-P", () -> me.export "PNG"
@bindKey "ALT-N", () -> me.actionFile "#{me.name}-New"
@bindKey "ALT-O", () -> me.actionFile "#{me.name}-Open"
@bindKey "CTRL-S", () -> me.actionFile "#{me.name}-Save"
@bindKey "ALT-W", () -> me.actionFile "#{me.name}-Saveas"
#@bindKey "CTRL-M", () -> me.svgToCanvas(()->)
@on "hboxchange", () ->
me.editor.resize()
me.calibrate()
@on "focus", () -> me.editor.focus()
(@find "btn-zoomin").set "onbtclick", (e) ->
me.pan.zoomIn() if me.pan
(@find "btn-zoomout").set "onbtclick", (e) ->
me.pan.zoomOut() if me.pan
(@find "btn-reset").set "onbtclick", (e) ->
me.pan.resetZoom() if me.pan
@renderlist.set "items", [{ text: "Dot", selected: true }, { text: "Mermaid" } ]
@open @currfile
engine: () ->
sel = @renderlist.get "selected"
sel.text
menu: () ->
me = @
menu = [{
text: "__(File)",
child: [
{ text: "__(New)", dataid: "#{@name}-New", shortcut: "A-N" },
{ text: "__(Open)", dataid: "#{@name}-Open", shortcut: "A-O" },
{ text: "__(Save)", dataid: "#{@name}-Save", shortcut: "C-S" },
{ text: "__(Save as)",dataid: "#{@name}-Saveas" , shortcut: "A-W" },
{ text: "__(Render)", dataid: "#{@name}-Render", shortcut: "C-R" },
{
text: "__(Export as)",
child: [
{ text: "SVG", shortcut: "A-G" },
{ text: "PNG", shortcut: "A-P" }
],
onmenuselect: (e) -> me.export e.item.data.text
},
],
onmenuselect: (e) -> me.actionFile e.item.data.dataid
}]
menu
open: (file) ->
return if file.path is "Untitled"
me = @
file.dirty = false
file.read (d) ->
me.currfile = file
me.editormux = true
me.currfile.dirty = false
me.editor.setValue d
me.scheme.set "apptitle", "#{me.currfile.basename}"
me.renderlist.set "selected", if file.info.mime.match /.*graphviz/ then 0 else 1
#me.renderSVG false
save: (file) ->
me = @
file.write "text/plain", (d) ->
return me.error __("Error saving file {0}", file.basename) if d.error
file.dirty = false
file.text = file.basename
me.scheme.set "apptitle", "#{me.currfile.basename}"
actionFile: (e) ->
me = @
saveas = () ->
me.openDialog "FileDiaLog", (d, n) ->
me.currfile.setPath "#{d}/#{n}"
me.save me.currfile
, __("Save as"), { file: me.currfile }
switch e
when "#{@name}-Open"
@openDialog "FileDiaLog", ( d, f ) ->
me.open "#{d}/#{f}".asFileHandler()
, __("Open file")
when "#{@name}-Save"
@currfile.cache = @editor.getValue()
return @save @currfile if @currfile.basename
saveas()
when "#{@name}-Saveas"
@currfile.cache = @editor.getValue()
saveas()
when "#{@name}-Render"
me.renderSVG false
when "#{@name}-New"
@currfile = "Untitled".asFileHandler()
@currfile.cache = ""
@currfile.dirty = false
@editormux = true
@editor.setValue("")
export: (t) ->
me = @
me.openDialog "FileDiaLog", (d, n) ->
fp = "#{d}/#{n}".asFileHandler()
try
switch t
when "SVG"
fp.cache = me.svgtext()
fp.write "text/plain", (r) ->
return me.error __("Cannot export to {0}: {1}", t, r.error) if r.error
me.notify __("File exported")
when "PNG"
# toDataURL("image/png")
me.svgToCanvas (canvas) ->
fp.cache = canvas.toDataURL "image/png"
console.log fp.cache
fp.write "base64", (r) ->
return me.error __("Cannot export to {0}: {1}", t, r.error) if r.error
me.notify __("File exported")
catch e
me.error __("Cannot export: {0}", e.message)
, __("Export as"), { file: me.currfile }
renderSVG: (silent) ->
svg = undefined
me = @
try
rd = (obj) ->
$(me.preview).children("svg").remove()
$(me.preview).append obj
svg = $(me.preview).children("svg")[0]
$(svg).prepend($ "<defs>")
$($(svg).children("defs")[0]).html "<style type='text/css'>#{GraphEditor.css}</style>"
me.calibrate()
me.pan = svgPanZoom svg, {
zoomEnabled: true,
controlIconsEnabled: false,
fit: true,
center: true,
minZoom: 0.1
}
if @engine() is "Dot"
result = Viz @editor.getValue(), { format: "svg", scale: 1 }
rd($.parseHTML result)
else
id = Math.floor(Math.random() * 100000) + 1
if silent
mermaidAPI.parseError = (e, h) ->
else
mermaidAPI.parseError = (e, h) ->
me.error e
mermaidAPI.render "c#{id}", @editor.getValue(), (text, f) ->
$(me.preview).append me.btctn
rd $($.parseHTML text).attr("style", "")
, me.preview
catch e
@error e.message unless silent
svgtext: () ->
svg = $(@preview).children("svg")[0]
serializer = new XMLSerializer()
return serializer.serializeToString(svg)
svgToCanvas: (f) ->
me = @
img = new Image()
svgStr = @svgtext()
img.onload = () ->
canvas = me.find "offscreen"
canvas.width = img.width
canvas.height = img.height
canvas.getContext("2d").drawImage img, 0, 0, img.width, img.height
f(canvas)
img.src = 'data:image/svg+xml;base64,' + window.btoa(svgStr)
calibrate: () ->
svg = ($ @preview).children("svg")[0]
if svg
prs = [$(@preview).width(), $(@preview).height()]
$(svg).attr "width", prs[0] + "px"
$(svg).attr "height", prs[1] + "px"
cleanup: (evt) ->
return unless @currfile.dirty
me = @
evt.preventDefault()
@.openDialog "YesNoDialog", (d) ->
if d
me.currfile.dirty = false
me.quit()
, __("Quit"), { text: __("Quit without saving ?") }
GraphEditor.dummydot = """
graph graphname {
// This attribute applies to the graph itself
size="2";
// The label attribute can be used to change the label of a node
a [label="Foo"];
// Here, the node shape is changed.
b [shape=box];
// These edges both have different line properties
a -- b -- c [color=blue];
b -- d [style=dotted];
// [style=invis] hides a node.
}
"""
GraphEditor.dummymermaid = """
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
"""
GraphEditor.css = """
<defs><style type="text/css">
/* <![CDATA[ */.mermaid .label{font-family:'trebuchet ms',verdana,arial;color:#333}.node rect,.node circle,.node ellipse,.node polygon{fill:#cde498;stroke:#13540c;stroke-width:1px}.edgePath .path{stroke:green;stroke-width:1.5px}.edgeLabel{background-color:#e8e8e8}.cluster rect{fill:#cdffb2 !important;rx:4 !important;stroke:#6eaa49 !important;stroke-width:1px !important}.cluster text{fill:#333}.actor{stroke:#13540c;fill:#cde498}text.actor{fill:black;stroke:none}.actor-line{stroke:grey}.messageLine0{stroke-width:1.5;stroke-dasharray:"2 2";marker-end:"url(#arrowhead)";stroke:#333}.messageLine1{stroke-width:1.5;stroke-dasharray:"2 2";stroke:#333}#arrowhead{fill:#333}#crosshead path{fill:#333 !important;stroke:#333 !important}.messageText{fill:#333;stroke:none}.labelBox{stroke:#326932;fill:#cde498}.labelText{fill:black;stroke:none}.loopText{fill:black;stroke:none}.loopLine{stroke-width:2;stroke-dasharray:"2 2";marker-end:"url(#arrowhead)";stroke:#326932}.note{stroke:#6eaa49;fill:#fff5ad}.noteText{fill:black;stroke:none;font-family:'trebuchet ms',verdana,arial;font-size:14px}.section{stroke:none;opacity:.2}.section0{fill:#6eaa49}.section2{fill:#6eaa49}.section1,.section3{fill:white;opacity:.2}.sectionTitle0{fill:#333}.sectionTitle1{fill:#333}.sectionTitle2{fill:#333}.sectionTitle3{fill:#333}.sectionTitle{text-anchor:start;font-size:11px;text-height:14px}.grid .tick{stroke:lightgrey;opacity:.3;shape-rendering:crispEdges}.grid path{stroke-width:0}.today{fill:none;stroke:red;stroke-width:2px}.task{stroke-width:2}.taskText{text-anchor:middle;font-size:11px}.taskTextOutsideRight{fill:black;text-anchor:start;font-size:11px}.taskTextOutsideLeft{fill:black;text-anchor:end;font-size:11px}.taskText0,.taskText1,.taskText2,.taskText3{fill:white}.task0,.task1,.task2,.task3{fill:#487e3a;stroke:#13540c}.taskTextOutside0,.taskTextOutside2{fill:black}.taskTextOutside1,.taskTextOutside3{fill:black}.active0,.active1,.active2,.active3{fill:#cde498;stroke:#13540c}.activeText0,.activeText1,.activeText2,.activeText3{fill:black !important}.done0,.done1,.done2,.done3{stroke:grey;fill:lightgrey;stroke-width:2}.doneText0,.doneText1,.doneText2,.doneText3{fill:black !important}.crit0,.crit1,.crit2,.crit3{stroke:#f88;fill:red;stroke-width:2}.activeCrit0,.activeCrit1,.activeCrit2,.activeCrit3{stroke:#f88;fill:#cde498;stroke-width:2}.doneCrit0,.doneCrit1,.doneCrit2,.doneCrit3{stroke:#f88;fill:lightgrey;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}.doneCritText0,.doneCritText1,.doneCritText2,.doneCritText3{fill:black !important}.activeCritText0,.activeCritText1,.activeCritText2,.activeCritText3{fill:black !important}.titleText{text-anchor:middle;font-size:18px;fill:black}g.classGroup text{fill:#13540c;stroke:none;font-family:'trebuchet ms',verdana,arial;font-size:14px}g.classGroup rect{fill:#cde498;stroke:#13540c}g.classGroup line{stroke:#13540c;stroke-width:1}svg .classLabel .box{stroke:none;stroke-width:0;fill:#cde498;opacity:.5}svg .classLabel .label{fill:#13540c}.relation{stroke:#13540c;stroke-width:1;fill:none}.composition{fill:#13540c;stroke:#13540c;stroke-width:1}#compositionStart{fill:#13540c;stroke:#13540c;stroke-width:1}#compositionEnd{fill:#13540c;stroke:#13540c;stroke-width:1}.aggregation{fill:#cde498;stroke:#13540c;stroke-width:1}#aggregationStart{fill:#cde498;stroke:#13540c;stroke-width:1}#aggregationEnd{fill:#cde498;stroke:#13540c;stroke-width:1}#dependencyStart{fill:#13540c;stroke:#13540c;stroke-width:1}#dependencyEnd{fill:#13540c;stroke:#13540c;stroke-width:1}#extensionStart{fill:#13540c;stroke:#13540c;stroke-width:1}#extensionEnd{fill:#13540c;stroke:#13540c;stroke-width:1}.node text{font-family:'trebuchet ms',verdana,arial;font-size:14px}div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms',verdana,arial;font-size:12px;background:#cdffb2;border:1px solid #6eaa49;border-radius:2px;pointer-events:none;z-index:100} /* ]]> */
</style></defs>
"""
GraphEditor.dependencies = [
"ace/ace"
]
this.OS.register "GraphEditor", GraphEditor

View File

@ -0,0 +1,22 @@
afx-app-window[data-id="graph_editor_win"] div[data-id="preview"]
{
display: flex;
align-items: center;
justify-content: center;
}
afx-app-window[data-id="graph_editor_win"] afx-button button
{
border-radius: 0;
padding-top:2px;
padding-bottom: 2px;
}
afx-app-window[data-id="graph_editor_win"] afx-resizer{
background-color: transparent;
}
afx-app-window[data-id="graph_editor_win"] div[data-id="btn-container"]{
background-color: transparent;
position: absolute;
bottom:10px;
right: 10px;
display: inline;
}

View File

@ -0,0 +1,14 @@
{
"app":"GraphEditor",
"name":"Graph Editor",
"description":"Create graph with dot language or mermaid",
"info":{
"author": "Xuan Sang LE",
"email": "xsang.le@gmail.com",
"licences": "GPLv3"
},
"version":"0.1a",
"category":"Office",
"iconclass": "fa fa-sitemap",
"mimes":["text/.*graphviz"]
}

View File

@ -0,0 +1,18 @@
<afx-app-window data-id="graph_editor_win" apptitle="__(Graph editor)" width="650" height="500">
<afx-hbox>
<afx-vbox clas="section0">
<afx-list-view data-height="25" dropdown="true" data-id="render-list"></afx-list-view>
<div data-id="datarea"></div>
</afx-vbox>
<afx-resizer data-width="5" ></afx-resizer>
<div data-id="preview">
<div data-height="25" data-id="btn-container">
<afx-button data-id="btn-zoomin" iconclass="fa fa-search-plus"></afx-button>
<afx-button data-id="btn-zoomout" iconclass="fa fa-search-minus"></afx-button>
<afx-button data-id="btn-reset" iconclass="fa fa-square-o"></afx-button>
</div>
</div>
<canvas data-id="offscreen" data-width='0' style="display:none;"></canvas>
</afx-hbox>
</afx-app-window>