[Rt-commit] rt branch, 4.6/lifecycle-ui-dev, repushed

Craig Kaiser craig at bestpractical.com
Thu Nov 7 16:51:48 EST 2019


The branch 4.6/lifecycle-ui-dev was deleted and repushed:
       was c3ffb47e27da103975523ed3e045af7c49ce2bc1
       now 94f88d59dbc3362c030dc6aba95db60378bc97d6

1: c3ffb47e27 ! 1: 94f88d59db Core Lifecycle-UI
    @@ -925,7 +925,7 @@
     +%#
     +%# END BPS TAGGED BLOCK }}}
     +<div class="lifecycle-ui" id="lifecycle-<% $id %>">
    -+    <div class="lifecycle-view">
    ++    <div class="lifecycle-ui">
     +      <svg>
     +      </svg>
     +    </div>
    @@ -937,7 +937,7 @@
     +                var config = <% JSON($config) |n %>;
     +                var name = <% $Lifecycle | j%>;
     +
    -+                var editor = new RT.NewEditor( container, name, config );
    ++                var editor = new RT.NewEditor( container, config );
     +            });
     +        });
     +    </script>
    @@ -1310,386 +1310,31 @@
     +                     || $Ticket->CurrentUserHasRight('WatchAsAdminCc');
     +</%INIT>
     
    -diff --git a/share/static/css/elevator-light/lifecycleui-editor.css b/share/static/css/elevator-light/lifecycleui-editor.css
    -new file mode 100644
    ---- /dev/null
    -+++ b/share/static/css/elevator-light/lifecycleui-editor.css
    -@@
    -+.lifecycle-ui {
    -+}
    -+
    -+.lifecycle-ui.editing svg {
    -+    width: 100%;
    -+    height: 500px;
    -+}
    -+
    -+.lifecycle-ui.editing .overlay-buttons {
    -+    left: 85%;
    -+}
    -+
    -+.lifecycle-ui .inspector {
    -+    display: inline-block;
    -+    min-height: 500px;
    -+    border: 1px solid black;
    -+}
    -+
    -+.lifecycle-ui .inspector .content {
    -+    padding: 1em;
    -+    border-top: 1px solid black;
    -+}
    -+
    -+.lifecycle-ui .inspector .header .toplevel {
    -+    padding-top: 1em;
    -+}
    -+
    -+.lifecycle-ui .inspector .header .controls {
    -+    padding: 0 0 1em 1em;
    -+}
    -+
    -+.lifecycle-ui .inspector input[type=text] {
    -+    width: 10em;
    -+}
    -+
    -+.lifecycle-ui .inspector .color-control span.current-color {
    -+    display: inline;
    -+    padding-left: 1em;
    -+    border: 1px solid black;
    -+}
    -+
    -+.lifecycle-ui .removing {
    -+    opacity: 0;
    -+}
    -+
    -+.lifecycle-ui .has-focus .point-handle {
    -+    stroke: black;
    -+    fill: steelblue;
    -+}
    -+
    -+.lifecycle-ui .inspector .actions {
    -+    list-style-type: none;
    -+    padding: 0;
    -+}
    -+
    -+.lifecycle-ui .inspector .actions .action {
    -+    border: 1px solid black;
    -+    margin-bottom: .5em;
    -+    padding: .5em;
    -+}
    -+
    -+.lifecycle-ui.editing svg.has-focus .decorations > * {
    -+    opacity: .15;
    -+}
    -+
    -+.lifecycle-ui.editing svg.has-focus .decorations .focus,
    -+.lifecycle-ui.editing svg.has-focus .transitions .focus {
    -+    opacity: 1;
    -+}
    -+
    -+.lifecycle-ui.editing svg.has-focus[data-focus-type=transition] .statuses .focus-from {
    -+    opacity: 1;
    -+}
    -+
    -+.lifecycle-ui.editing .decorations .text-background {
    -+    stroke: none;
    -+    fill: none;
    -+}
    -+
    -+.lifecycle-ui.editing svg.has-focus .decorations .focus.text-background {
    -+    fill: white;
    -+    filter: url(#focus);
    -+}
    -+
    -+.lifecycle-ui .inspector button.delete {
    -+    margin-top: 2em;
    -+}
    -+
    -+.lifecycle-ui .inspector .actions button.delete {
    -+    margin-top: 0;
    -+}
    -+
    -+.lifecycle-ui .inspector .hint {
    -+    vertical-align: super;
    -+    font-size: .8em;
    -+    color: gray;
    -+}
    -+
    -+.lifecycle-ui .invisible {
    -+    visibility: hidden;
    -+}
    -+
    -+.lifecycle-ui .inspector .sf-menu a:visited,
    -+.lifecycle-ui .inspector .sf-menu a {
    -+    border: none;
    -+    color: #000;
    -+}
    -+
    -+.lifecycle-ui .inspector .sf-menu.sf-vertical {
    -+    margin-left: -1em;
    -+}
    -+
    -+.lifecycle-ui .inspector .sf-menu.sf-vertical,
    -+.lifecycle-ui .inspector .sf-menu.sf-vertical > li {
    -+    width: 175px;
    -+}
    -+
    -+.lifecycle-ui .inspector .sf-vertical li:hover ul,
    -+.lifecycle-ui .inspector .sf-vertical li.sfHover ul {
    -+    left: 175px;
    -+}
    -+
    -+.lifecycle-ui .statuses .hover {
    -+    opacity: 1;
    -+}
    -+
    -+.lifecycle-ui .statuses .hover circle,
    -+.lifecycle-ui .transitions .hover,
    -+.lifecycle-ui .decorations :not(text).hover {
    -+    opacity: 1;
    -+    filter: url(#hover);
    -+}
    -+
    -+.lifecycle-ui.editing .decorations .hover.text-background {
    -+    fill: white;
    -+}
    -+
    -+.lifecycle-ui .inspector button:disabled {
    -+    background: hsl(222, 20%, 40%);
    -+    cursor: not-allowed;
    -+}
    -+
    -+.lifecycle-ui .inspector tr.section td {
    -+    padding-top: 1em;
    -+}
    -+
    -+
    -+
    -+
    -+
    -+/* NEW */
    -+g text {
    -+    cursor: text;
    -+}
    -+
    -+g circle.node-selected {
    -+    fill: #98b9eb !important;
    -+}
    -
    -diff --git a/share/static/css/elevator-light/lifecycleui-viewer-interactive.css b/share/static/css/elevator-light/lifecycleui-viewer-interactive.css
    -new file mode 100644
    ---- /dev/null
    -+++ b/share/static/css/elevator-light/lifecycleui-viewer-interactive.css
    -@@
    -+.lifecycle-ui .status-menu {
    -+    position: absolute;
    -+    top: 0;
    -+    left: 0;
    -+    display: none;
    -+}
    -+
    -+.lifecycle-ui .status-menu.selected {
    -+    display: block;
    -+}
    -+
    -+.lifecycle-ui .status-menu .sf-menu a:visited,
    -+.lifecycle-ui .status-menu .sf-menu a {
    -+    border: none;
    -+    color: #000;
    -+}
    -+
    -+.lifecycle-ui .status-menu a.not-current,
    -+.lifecycle-ui .status-menu a.no-transition,
    -+.lifecycle-ui .status-menu a.no-permission,
    -+.lifecycle-ui .status-menu a.hide-resolve-with-deps {
    -+    color: #AAAAAA;
    -+    cursor: default;
    -+    text-decoration: none;
    -+}
    -+
    -+.lifecycle-ui .status-menu .not-current:hover,
    -+.lifecycle-ui .status-menu .no-transition:hover,
    -+.lifecycle-ui .status-menu .no-permission:hover,
    -+.lifecycle-ui .status-menu .hide-resolve-with-deps:hover {
    -+    background-color: #fff;
    -+}
    -+
    -+.lifecycle-ui .status-menu .sf-menu.sf-vertical {
    -+    width: 10em;
    -+}
    -+
    -+.lifecycle-ui .status-menu .sf-menu.sf-vertical li {
    -+    width: 100%;
    -+}
    -+
    -+.lifecycle-ui .status-menu .sf-menu.sf-shadow {
    -+    -moz-border-radius: 0;
    -+    -webkit-border-radius: 0;
    -+    border-radius: 0;
    -+    -moz-box-shadow: 2px 2px 8px -2px #999;
    -+    -webkit-box-shadow: 2px 2px 8px -2px #999;
    -+    box-shadow: 2px 2px 8px -2px #999;
    -+}
    -+
    -+.lifecycle-ui .statuses .selected circle {
    -+    filter: url(#hover);
    -+}
    -+
    -
    -diff --git a/share/static/css/elevator-light/lifecycleui-viewer.css b/share/static/css/elevator-light/lifecycleui-viewer.css
    -new file mode 100644
    ---- /dev/null
    -+++ b/share/static/css/elevator-light/lifecycleui-viewer.css
    -@@
    -+.lifecycle-ui svg {
    -+    border: 1px solid black;
    -+    width: 100%;
    -+    height: 100%;
    -+}
    -+
    -+.lifecycle-ui.center-fit:not(.zoomable) svg {
    -+    border: none;
    -+}
    -+
    -+.lifecycle-ui .overlay-buttons {
    -+    position: absolute;
    -+    top: 1em;
    -+    left: 391px;
    -+}
    -+
    -+.lifecycle-ui .overlay-buttons .zoom {
    -+    display: none;
    -+}
    -+
    -+.lifecycle-ui.zoomable .overlay-buttons .zoom {
    -+    display: inline-block;
    -+}
    -+
    -+.lifecycle-ui .statuses circle {
    -+    stroke: black;
    -+    stroke-width: 2px;
    -+}
    -+
    -+.lifecycle-ui .transitions path {
    -+    stroke: black;
    -+    stroke-width: 3px;
    -+    fill: none;
    -+}
    -+
    -+.lifecycle-ui .dotted {
    -+    stroke-dasharray: 2;
    -+}
    -+
    -+.lifecycle-ui .dashed {
    -+    stroke-dasharray: 5;
    -+}
    -+
    -+.lifecycle-ui .statuses text {
    -+    text-anchor: middle;
    -+    alignment-baseline: middle;
    -+}
    -+
    -+.lifecycle-ui .decorations line {
    -+    stroke: #000000;
    -+}
    -+
    -+.lifecycle-ui .decorations polygon,
    -+.lifecycle-ui .decorations circle,
    -+.lifecycle-ui .decorations line {
    -+    stroke-width: 2px;
    -+}
    -+
    -+.lifecycle-ui text {
    -+    cursor: default;
    -+    -webkit-user-select: none;
    -+       -moz-user-select: none;
    -+        -ms-user-select: none;
    -+            user-select: none;
    -+}
    -+.lifecycle-ui text::selection {
    -+    background: none;
    -+}
    -+
    -+.lifecycle-ui .statuses > *,
    -+.lifecycle-ui .transitions > *,
    -+.lifecycle-ui .decorations > * {
    -+    transition: opacity .2s;
    -+}
    -+
    -+.lifecycle-ui .has-focus .statuses > * {
    -+    opacity: .15;
    -+}
    -+
    -+.lifecycle-ui .has-focus .transitions > * {
    -+    opacity: 0;
    -+}
    -+
    -+.lifecycle-ui .has-focus .statuses .focus {
    -+    opacity: 1;
    -+}
    -+
    -+.lifecycle-ui .has-focus .statuses .focus circle {
    -+    stroke-width: 6px;
    -+}
    -+
    -+.lifecycle-ui .has-focus .statuses .focus circle,
    -+.lifecycle-ui .has-focus .transitions .focus,
    -+.lifecycle-ui .has-focus .decorations :not(text).focus {
    -+    filter: url(#focus);
    -+}
    -+
    -+.lifecycle-ui .has-focus .statuses .focus-to,
    -+.lifecycle-ui .has-focus .transitions .focus-to {
    -+    opacity: 1;
    -+}
    -+
    -+.lifecycle-ui .decorations text.bold {
    -+    font-weight: bold;
    -+}
    -+
    -+.lifecycle-ui .decorations text.italic {
    -+    font-style: italic;
    -+}
    -+
    -+.lifecycle-ui .lifecycle-view {
    -+    position: relative;
    -+    display: inline-block;
    -+}
    -+
    -+.lifecycle-ui .overlay-buttons button {
    -+    border: none;
    -+}
    -
     diff --git a/share/static/css/elevator-light/lifecycleui.css b/share/static/css/elevator-light/lifecycleui.css
     new file mode 100644
     --- /dev/null
     +++ b/share/static/css/elevator-light/lifecycleui.css
     @@
    -+body#comp-Admin-Lifecycles ul + h2 {
    -+    margin-top: 2em;
    ++path.link {
    ++    fill: none;
    ++    stroke: #000;
    ++    stroke-width: 3px;
    ++    cursor: pointer;
     +}
     +
    -+.ticket-info-lifecycle .titlebox-title .widget a {
    -+    background-position: center -7px
    ++g text {
    ++    cursor: text;
     +}
     +
    -+.ticket-info-lifecycle .titlebox-title {
    -+    margin-left: 1em
    ++g circle.node-selected {
    ++    fill: #98b9eb !important;
     +}
     +
    -+.ticket-info-lifecycle .titlebox-title .left {
    -+    padding-left: 2.25em;
    -+    margin-left: 0;
    -+    padding-bottom: 4px;
    -+    margin-bottom: 8px;
    -+    -webkit-border-top-left-radius: 0.3em;
    -+    -webkit-border-top-right-radius: 0.3em;
    -+    -moz-border-radius-topleft: 0.3em;
    -+    -moz-border-radius-topright: 0.3em;
    -+    border-radius: 0.3em 0.3em 0 0;
    -+}
    -+
    -+.ticket-info-lifecycle .titlebox .titlebox-title .left {
    -+    background-color: #b32;
    -+    color: #fff;
    ++.dragline {
    ++    fill: none;
    ++    stroke: #000;
    ++    stroke-width: 3px;
    ++    cursor: pointer;
     +}
     
     diff --git a/share/static/css/elevator-light/main.css b/share/static/css/elevator-light/main.css
    @@ -1700,9 +1345,6 @@
      @import "print.css";
      @import "ckeditor5.css";
     +
    -+ at import "lifecycleui-editor.css";
    -+ at import "lifecycleui-viewer-interactive.css";
    -+ at import "lifecycleui-viewer.css";
     + at import "lifecycleui.css";
     
     diff --git a/share/static/js/d3.min.js b/share/static/js/d3.min.js
    @@ -1720,12 +1362,12 @@
     @@
     +jQuery( document ).ready(function () {
     +    RT.NewEditor = class LifecycleEditorNew extends LifecycleModel {
    -+        constructor(container, name, config) {
    ++        constructor(container, config) {
     +            super("LifecycleModel");
     +
     +            var self         = this;
     +            self.width       = 960;
    -+            self.height      = 500;
    ++            self.height      = 350;
     +            self.node_radius = 35;
     +
     +            self.svg      = d3.select(container).select('svg')
    @@ -1801,8 +1443,8 @@
     +                        dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
     +                        normX = deltaX / dist,
     +                        normY = deltaY / dist,
    -+                        sourcePadding = 40,
    -+                        targetPadding = 40,
    ++                        sourcePadding = 45,
    ++                        targetPadding = 45,
     +                        sourceX = sx + (sourcePadding * normX),
     +                        sourceY = sy + (sourcePadding * normY),
     +                        targetX = tx - (targetPadding * normX),
    @@ -1814,6 +1456,12 @@
     +            .force("link")
     +            .links(self.links)
     +            .id(d => d.id);
    ++
    ++            // Add our current config to the DOM
    ++            var form  = jQuery('form[name="ModifyLifecycle"]');
    ++            var field = jQuery('<input type="hidden" name="Config">');
    ++            field.val(JSON.stringify(self.config));
    ++            form.append(field);
     +        }
     +
     +        SetUp() {
    @@ -1824,8 +1472,8 @@
     +                .attr('id', 'end-arrow')
     +                .attr('viewBox', '0 -5 10 10')
     +                .attr('refX', 6)
    -+                .attr('markerWidth', 8)
    -+                .attr('markerHeight', 8)
    ++                .attr('markerWidth', 6)
    ++                .attr('markerHeight', 6)
     +                .attr('orient', 'auto')
     +                .append('svg:path')
     +                .attr('d', 'M0,-5L10,0L0,5')
    @@ -1835,8 +1483,8 @@
     +                .attr('id', 'start-arrow')
     +                .attr('viewBox', '0 -5 10 10')
     +                .attr('refX', 4)
    -+                .attr('markerWidth', 8)
    -+                .attr('markerHeight', 8)
    ++                .attr('markerWidth', 6)
    ++                .attr('markerHeight', 6)
     +                .attr('orient', 'auto')
     +                .append('svg:path')
     +                .attr('d', 'M10,-5L0,0L10,5')
    @@ -1845,27 +1493,45 @@
     +            // line displayed when dragging new nodes
     +            self.drag_line = self.svg.append('svg:path')
     +                .attr('class', 'dragline hidden')
    -+                .attr('d', 'M0,0L0,0');
    -+
    -+            self.svg.on('contextmenu', function () {
    -+                d3.event.preventDefault();
    -+
    -+                var newNodes = self.AddNode(d3.mouse(this));
    -+                self.Refresh(newNodes);
    -+            });
    -+
    -+            self.svg.on("click", function () {
    -+                d3.event.stopPropagation();
    -+                self.Deselect();
    -+            })
    ++                .attr('d', 'M0,0L0,0')
    ++                .attr('fill', '#000')
    ++                .attr('markerWidth', 8)
    ++                .attr('markerHeight', 8)
    ++                .attr("stroke-width", 1)
    ++                .attr("style", "stroke: black; stroke-opacity: 0.6;");
    ++
    ++            self.svg
    ++                .on('click', function () {
    ++                    d3.event.preventDefault();
    ++                    d3.event.stopPropagation();
    ++
    ++                    if ( self.selected_node ) {
    ++                        self.Deselect();
    ++                    }
    ++                    else {
    ++                        self.simulation.stop();
    ++                        self.AddNode(d3.mouse(this));
    ++
    ++                        self.ExportAsConfiguration();
    ++
    ++                        self.Refresh();
    ++                    }
    ++                })
    ++                .on('contextmenu', function() { d3.event.preventDefault(); })
    ++                .on('mousemove', function() { self.Mousemove(this); })
    ++                .on('mouseup', function() { self.Mouseup(this); })
    ++                .on('mousedown', function() { self.Mousedown(this); });
     +
     +            d3.select("body").on("keydown", function (d) {
     +                if ( d3.event.keyCode == 68 || d3.event.keyCode == 46 && d ) {
     +                    d3.event.preventDefault();
     +                    d3.event.stopPropagation();
     +
    ++                    self.simulation.stop();
     +                    self.svg.selectAll('.node-selected').each(function(d) {
     +                        self.DeleteNode(d);
    ++
    ++                        self.ExportAsConfiguration();
     +
     +                        self.Deselect();
     +                        self.Refresh();
    @@ -1881,11 +1547,6 @@
     +                .data(self.nodes.filter(d => d.id >= 0));
     +
     +            self.node.exit()
    -+                .classed("removing", true)
    -+                .append('circle')
    -+                .append('text')
    -+                .append('title')
    -+                .transition().duration(200 * self.animationFactor).ease(d3.easeLinear)
     +                .remove();
     +
     +            // Add new nodes and draw them
    @@ -1920,6 +1581,39 @@
     +                    d3.event.stopPropagation();
     +                    self.SelectNode(this);
     +                })
    ++                .on('mousedown', function(d) {
    ++                    if(!d3.event.ctrlKey || self.mousedown_node || self.mousedown_link) return;
    ++                    d3.event.preventDefault();
    ++                    d3.event.stopPropagation();
    ++
    ++                    // select node
    ++                    self.mousedown_node = d;
    ++                    if ( !self.mousedown_node ) return;
    ++
    ++                    // reposition drag line
    ++                    self.drag_line
    ++                      .style('marker-end', 'url(#end-arrow)')
    ++                      .classed('hidden', false)
    ++                      .attr('d', 'M' + self.mousedown_node.x + ',' + self.mousedown_node.y + 'L' + self.mousedown_node.x + ',' + self.mousedown_node.y);
    ++
    ++                    self.Refresh();
    ++                  })
    ++                  .on('mouseup', function(d) {
    ++                    self.mouseup_node = d;
    ++                    // needed by FF
    ++                    self.drag_line
    ++                      .classed('hidden', true)
    ++                      .style('marker-end', '');
    ++
    ++                    self.simulation.stop();
    ++                    // add link to model
    ++                    self.AddLink(self.mousedown_node, self.mouseup_node);
    ++
    ++                    self.ExportAsConfiguration();
    ++
    ++                    self.ResetMouseVars();
    ++                    self.Refresh();
    ++                });
     +
     +            self.node.select("text")
     +                .text(function(d) { return d.name; })
    @@ -2020,26 +1714,36 @@
     +            var linkEnter = self.link.enter().append("g")
     +                .append("path")
     +                .attr("class", 'link')
    -+                .attr("stroke-width", 1)
    -+                .attr("style", "stroke: black; stroke-opacity: 0.6;")
     +                .style("marker-start", (d => d.right ? 'url(#start-arrow)' : '' ))
    -+                .style("marker-end", (d => d.left ? 'url(#end-arrow)' : '' ))
    -+                .attr("transform", "translate(0,0)");
    ++                .style("marker-end", (d   => d.left ? 'url(#end-arrow)' : '' ))
    ++                .attr("transform", "translate(0,0)")
    ++                .on("click", d => {
    ++                    d3.event.stopPropagation();
    ++                    self.simulation.stop();
    ++                    self.ToggleLink(d);
    ++
    ++                    self.ExportAsConfiguration();
    ++
    ++                    self.Refresh();
    ++                });
     +            self.link = linkEnter.merge(self.link);
    ++            self.link
    ++                .style("marker-start", (d => d.right ? 'url(#start-arrow)' : '' ))
    ++                .style("marker-end", (d   => d.left ? 'url(#end-arrow)' : '' ));
     +        }
     +
     +        Refresh() {
     +            var self = this;
     +
    -+            self.simulation.stop();
    -+            self.simulation.nodes(self.nodes);
    -+            self.simulation.force("link").links(self.links);
    -+
    ++            self.simulation
    ++                .nodes(self.nodes)
    ++                .force("link")
    ++                    .links(self.links)
    ++                    .id(d => d.id);
    ++
    ++            self.RenderLink();
     +            self.RenderNode();
    -+            self.RenderLink();
    -+
    -+            self.simulation.alpha(1);
    -+            self.simulation.restart();
    ++            self.simulation.alpha(0.5).restart();
     +        }
     +
     +        SelectNode(node) {
    @@ -2053,7 +1757,7 @@
     +        Deselect() {
     +            this.selected_node = null;
     +
    -+            var node = this.svg.selectAll('.node-selected')
    ++            this.svg.selectAll('.node-selected')
     +                .classed('node-selected', false);
     +        }
     +
    @@ -2065,6 +1769,39 @@
     +                node.text(text + '…');
     +                textLength = node.node().getComputedTextLength();
     +            }
    ++        }
    ++
    ++        Mousemove(d) {
    ++            var self = this;
    ++            if (!self.mousedown_node) return;
    ++
    ++            this.drag_line.attr('d', 'M' + self.mousedown_node.x + ',' + self.mousedown_node.y + 'L' + d3.mouse(d)[0] + ',' + d3.mouse(d)[1]);
    ++
    ++            this.Refresh();
    ++        }
    ++
    ++        Mouseup() {
    ++            var self = this;
    ++
    ++            if(self.mousedown_node) {
    ++              // hide drag line
    ++              self.drag_line
    ++                .classed('hidden', true)
    ++                .style('marker-end', '');
    ++            }
    ++        }
    ++
    ++        Mousedown(d) {
    ++            d3.event.preventDefault();
    ++            d3.event.stopPropagation();
    ++        }
    ++
    ++        ResetMouseVars(){
    ++            var self = this;
    ++
    ++            self.mousedown_link  = null;
    ++            self.mousedown_node  = null;
    ++            self.mouseup_node    = null;
     +        }
     +    }
     +});
    @@ -2120,6 +1857,32 @@
     +        this.nodes.push({id: ++this.nodes_seq, name: name, type: 'active', x: point[0], y: point[1]});
     +    }
     +
    ++    AddLink(source, target) {
    ++        self = this;
    ++        if (!source || !target) return;
    ++
    ++        var link = self.links.filter(function(l) { return (l.source === target && l.target === source); })[0];
    ++        if(link) link.right = true;
    ++        else self.links.push({id: ++self.links_seq, source: source, target: target, right: false, left: true});
    ++    }
    ++
    ++    ToggleLink(d) {
    ++        var self = this;
    ++        var index = self.links.findIndex(x => x.id == d.id);
    ++
    ++        var link = self.links[index];
    ++        // delete link if we have both transitions already
    ++        if ( link.right && link.left ) {
    ++            self.links.splice(index, 1);
    ++        }
    ++        else if( link.right ) {
    ++            link.left = true;
    ++        }
    ++        else {
    ++            link.right = true;
    ++        }
    ++    }
    ++
     +    NodeById(id) {
     +        var nodeMap = d3.map(this.nodes, function(d) { return d.id; });
     +        return nodeMap.get( id );
    @@ -2135,6 +1898,20 @@
     +        this.DeleteLinksForNode(this.nodes[index]);
     +
     +        this.nodes.splice(index, 1);
    ++    }
    ++
    ++    LinksForNode (node) {
    ++        return this.links.filter(link => {
    ++            if ( link.source.id === node.id ) {
    ++                return true;
    ++            }
    ++            else if ( link.target.id === node.id && link.left ) {
    ++                return true;
    ++            }
    ++            else {
    ++                return false;
    ++            }
    ++        });
     +    }
     +
     +    DeleteLinksForNode(node) {
    @@ -2170,6 +1947,35 @@
     +            this.links[index] = {...link, target: nodeUpdated}
     +        });
     +    }
    ++
    ++    ExportAsConfiguration () {
    ++        var self = this;
    ++        var config = {
    ++            type: self.type,
    ++            initial:  [],
    ++            active:   [],
    ++            inactive: [],
    ++            actions:  [],
    ++            rights:   {},
    ++            transitions: [],
    ++        };
    ++
    ++        // Grab our status nodes
    ++        ['initial', 'active', 'inactive'].forEach(type => {
    ++            config[type] = self.nodes.filter(n => n.type == type);
    ++        });
    ++
    ++        // Grab our links
    ++        config.transitions[""] = self.config.transitions[""];
    ++
    ++        self.nodes.forEach(source => {
    ++            config.transitions.push({ [source.name] : self.LinksForNode(source).forEach(l => {return l.target.name;}) });
    ++        });
    ++        self.config = config;
    ++
    ++        var field = jQuery('<input type="hidden" name="Config">');
    ++        field.val(JSON.stringify(self.config));
    ++    };
     +}
     
     diff --git a/share/static/js/lifecycleui-viewer-interactive.js b/share/static/js/lifecycleui-viewer-interactive.js



More information about the rt-commit mailing list