[Rt-commit] rt branch, 4.6/lifecycle-ui-dev, repushed
Craig Kaiser
craig at bestpractical.com
Tue Nov 19 16:49:51 EST 2019
The branch 4.6/lifecycle-ui-dev was deleted and repushed:
was db4a3b570bb07b709ae6b9ac0afe01c79a2cca35
now f45bc0d57a62f0da5b43836f73d5dbe86a9eefed
1: 86f8aa51f6 ! 1: f45bc0d57a Core Lifecycle-UI
@@ -1,6 +1,119 @@
Author: Craig Kaiser <craig at bestpractical.com>
Core Lifecycle-UI
+
+diff --git a/etc/acl.Pg b/etc/acl.Pg
+--- a/etc/acl.Pg
++++ b/etc/acl.Pg
+@@
+ CustomRoles
+ objectcustomroles_id_seq
+ ObjectCustomRoles
++ databasesettings_id_seq
++ DatabaseSettings
+ );
+
+ my $db_user = RT->Config->Get('DatabaseUser');
+
+diff --git a/etc/schema.Oracle b/etc/schema.Oracle
+--- a/etc/schema.Oracle
++++ b/etc/schema.Oracle
+@@
+ LastUpdated DATE
+ );
+ CREATE UNIQUE INDEX ObjectCustomRoles1 ON ObjectCustomRoles (ObjectId, CustomRole);
++
++CREATE SEQUENCE DatabaseSettings_seq;
++CREATE TABLE DatabaseSettings (
++ id NUMBER(11,0) CONSTRAINT DatabaseSettings_key PRIMARY KEY,
++ Name VARCHAR2(255) CONSTRAINT DatabaseSettings_Name_Unique unique NOT NULL,
++ Content CLOB,
++ ContentType VARCHAR2(80),
++ Disabled NUMBER(11,0) DEFAULT 0 NOT NULL,
++ Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
++ Created DATE,
++ LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
++ LastUpdated DATE
++);
++
++CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (LOWER(Name));
++CREATE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
++
+
+diff --git a/etc/schema.Pg b/etc/schema.Pg
+--- a/etc/schema.Pg
++++ b/etc/schema.Pg
+@@
+ );
+
+ CREATE UNIQUE INDEX ObjectCustomRoles1 ON ObjectCustomRoles (ObjectId, CustomRole);
++
++CREATE SEQUENCE databasesettings_id_seq;
++CREATE TABLE DatabaseSettings (
++ id integer DEFAULT nextval('databasesettings_id_seq'),
++ Name varchar(255) NOT NULL,
++ Content text NULL,
++ ContentType varchar(80) NULL,
++ Disabled integer NOT NULL DEFAULT 0 ,
++ Creator integer NOT NULL DEFAULT 0,
++ Created timestamp DEFAULT NULL,
++ LastUpdatedBy integer NOT NULL DEFAULT 0,
++ LastUpdated timestamp DEFAULT NULL,
++ PRIMARY KEY (id)
++);
++
++CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (LOWER(Name));
++CREATE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
++
+
+diff --git a/etc/schema.SQLite b/etc/schema.SQLite
+--- a/etc/schema.SQLite
++++ b/etc/schema.SQLite
+@@
+ PRIMARY KEY (id)
+ );
+ CREATE UNIQUE INDEX ObjectCustomRoles1 ON ObjectCustomRoles (ObjectId, CustomRole);
++
++CREATE TABLE DatabaseSettings (
++ id INTEGER PRIMARY KEY,
++ Name varchar(255) collate NOCASE NOT NULL,
++ Content longtext collate NOCASE NULL,
++ ContentType varchar(80) collate NOCASE NULL,
++ Disabled int2 NOT NULL DEFAULT 0,
++ Creator int(11) NOT NULL DEFAULT 0,
++ Created timestamp DEFAULT NULL,
++ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
++ LastUpdated timestamp DEFAULT NULL
++);
++
++CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (Name);
++CREATE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
++
+
+diff --git a/etc/schema.mysql b/etc/schema.mysql
+--- a/etc/schema.mysql
++++ b/etc/schema.mysql
+@@
+ ) ENGINE=InnoDB CHARACTER SET utf8;
+
+ CREATE UNIQUE INDEX ObjectCustomRoles1 ON ObjectCustomRoles (ObjectId, CustomRole);
++
++CREATE TABLE DatabaseSettings (
++ id int(11) NOT NULL AUTO_INCREMENT,
++ Name varchar(255) NOT NULL,
++ Content longblob NULL,
++ ContentType varchar(80) CHARACTER SET ascii NULL,
++ Disabled int2 NOT NULL DEFAULT 0,
++ Creator int(11) NOT NULL DEFAULT 0,
++ Created datetime DEFAULT NULL,
++ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
++ LastUpdated datetime DEFAULT NULL,
++ PRIMARY KEY (id)
++) ENGINE=InnoDB DEFAULT CHARSET=utf8;
++
++CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (Name);
++CREATE UNIQUE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
++
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
--- a/lib/RT/Interface/Web.pm
@@ -925,20 +1038,54 @@
+%#
+%# END BPS TAGGED BLOCK }}}
+<div class="lifecycle-ui" id="lifecycle-<% $id %>">
-+ <div class="lifecycle-ui">
-+ <svg>
-+ </svg>
++ <div class="row">
++ <div class="col-md-7"></div>
++ <div class="col-md-5">
++ <div id="lifeycycle-ui-edit-node" class="lifeycycle-ui-edit-node collapse card card-body">
++ <input type="hidden" name="id" />
++ <div class="row">
++ <div class="col-md-3 label">
++ <label for="name">Name:</label>
++ </div>
++ <div class="col-md-9 value">
++ <input class="form-control" type="text" id="name" name="name" value="" />
++ </div>
++ </div>
++
++ <div class="row">
++ <div class="col-md-3 label">
++ <label for="type">Type:</label>
++ </div>
++ <div class="col-md-9 value">
++ <select class="selectpicker form-control" id="type" name="type">
++ <option value="initial">initial</option>
++ <option value="active">active</option>
++ <option value="inactive">inactive</option>
++ </select>
++ </div>
++ </div>
++ <div class="row float-right">
++ <div class="col-md-12">
++ <button class="button btn btn-primary form-control" id="SaveNode">Save</button>
++ </div>
++ </div>
++ </div>
+ </div>
-+
-+ <script type="text/javascript">
-+ jQuery(function () {
-+ var container = document.getElementById('lifecycle-<% $id %>'),
-+ config = <% JSON($config) |n %>,
-+ name = <% $Lifecycle | j%>;
-+
-+ var editor = new RT.NewEditor( container, config );
-+ });
-+ </script>
++ </div>
++
++ <svg>
++ </svg>
++ </div>
++
++ <script type="text/javascript">
++ jQuery(function () {
++ var container = document.getElementById('lifecycle-<% $id %>'),
++ config = <% JSON($config) |n %>,
++ name = <% $Lifecycle | j%>;
++
++ var editor = new RT.NewEditor( container, config );
++ });
++ </script>
+</div>
+
+<%INIT>
@@ -1334,6 +1481,10 @@
+ stroke-width: 2px;
+ cursor: pointer;
+}
++
++.lifeycycle-ui-edit-node {
++ position: absolute;
++}
diff --git a/share/static/css/elevator-light/main.css b/share/static/css/elevator-light/main.css
--- a/share/static/css/elevator-light/main.css
@@ -1367,6 +1518,11 @@
+ self.width = 960;
+ self.height = 350;
+ self.node_radius = 35;
++
++ jQuery("#SaveNode").click(function( event ) {
++ event.preventDefault();
++ self.UpdateNode();
++ });
+
+ self.svg = d3.select(container).select('svg')
+ .attr("preserveAspectRatio", "xMinYMin meet")
@@ -1378,6 +1534,7 @@
+ self.animationFactor = 1;
+ // mouse event vars
+ self.selected_node = null;
++ self.editing_node = null;
+ self.selected_link = null;
+ self.mousedown_link = null;
+ self.mousedown_node = null;
@@ -1503,11 +1660,12 @@
+ d3.event.preventDefault();
+ d3.event.stopPropagation();
+
-+ if ( self.selected_node ) {
++ if ( self.selected_node || self.editing_node ) {
+ self.Deselect();
+ }
+ else {
+ self.simulation.stop();
++ self.Deselect();
+ self.AddNode(d3.mouse(this));
+
+ self.ExportAsConfiguration();
@@ -1521,7 +1679,7 @@
+ .on('mousedown', function() { self.Mousedown(this); });
+
+ d3.select("body").on("keydown", function (d) {
-+ if ( self.selected_node && ( d3.event.keyCode == 68 || d3.event.keyCode == 46 ) ) {
++ if ( !self.editing_node && self.selected_node && ( d3.event.keyCode == 68 || d3.event.keyCode == 46 ) ) {
+ d3.event.preventDefault();
+ d3.event.stopPropagation();
+
@@ -1624,76 +1782,38 @@
+ .on("click", function(d) {
+ d3.event.stopPropagation();
+ d3.event.preventDefault();
-+ self.RenderNodeInput(this, d)
++ self.UpdateNode(d);
+ });
+
+ self.node.select("title")
+ .text(function(d) { return d.type; });
+ }
+
-+ RenderNodeInput(elem, d) {
++ UpdateNode(element) {
+ var self = this;
+
-+ var p_el = d3.select(elem.parentNode);
-+ var el = d3.select(elem);
-+
-+ // Clear the text value while we edit
-+ var original_value = el.text();
-+ el.text('');
-+
-+ var index = self.nodes.findIndex(x => x.id == d.id);
-+
-+ var frm = p_el.append("foreignObject");
-+ var input = frm
-+ .attr("x", -22.5)
-+ .attr("y", -15)
-+ .attr("width", 450)
-+ .attr("height", 250)
-+ .style("font-size", "10px")
-+ .append("xhtml:form")
-+ .append("input")
-+ .on("click", d => {d3.event.stopPropagation(); d3.event.preventDefault();})
-+ .attr("value", d => original_value)
-+ .attr("style", "width: 35px; background: transparent; border: none;")
-+ .on("blur", function(d) {
-+ var text = input.node().value;
-+ if ( text && text != original_value && !self.nodes.filter(n => n.name === text)[0] ) {
-+ self.UpdateNodeStatus(self.nodes[index], text);
-+ el.text(text);
++ const nodeInput = jQuery("#lifeycycle-ui-edit-node");
++ var list = document.getElementById('lifeycycle-ui-edit-node').querySelectorAll('input, select');
++
++ if ( element ) {
++ for (let item of list) {
++ jQuery(item).val(element[item.name]);
++ }
++ self.editing_node = element;
++ }
++ else {
++ var values = {};
++ for (let item of list) {
++ if ( item.name === 'id' ) {
++ values.index = self.nodes.findIndex(x => x.id == item.value);
+ }
-+ self.TruncateLabel(elem);
-+ p_el.select("foreignObject").remove();
-+ self.Deselect();
-+ self.Refresh();
-+ })
-+ .on("keypress", function(d) {
-+ // IE fix
-+ if (!d3.event)
-+ d3.event = window.event;
-+
-+ var e = d3.event;
-+ if (e.keyCode == 13)
-+ {
-+ if (typeof(e.cancelBubble) !== 'undefined') // IE
-+ e.cancelBubble = true;
-+ if (e.stopPropagation)
-+ e.stopPropagation();
-+ e.preventDefault();
-+
-+ var text = input.node().value;
-+ if ( text && text != original_value ) {
-+ self.UpdateNodeStatus(self.nodes[index], text);
-+ el.text(text);
-+ }
-+ else {
-+ el.text(original_value);
-+ }
-+ self.TruncateLabel(elem);
-+ p_el.select("foreignObject").remove();
-+ self.Deselect();
-+ self.Refresh();
-+ }
-+ });
++ values[item.name] = item.value;
++ }
++ self.UpdateNodeModel(self.nodes[values.index], values);
++ self.ExportAsConfiguration();
++ self.Refresh();
++ }
++ nodeInput.toggle();
+ }
+
+ RenderLink() {
@@ -1761,6 +1881,12 @@
+ }
+
+ Deselect() {
++ if ( jQuery("#lifeycycle-ui-edit-node").is(':visible') ) {
++ jQuery("#lifeycycle-ui-edit-node").toggle();
++ }
++
++ this.editing_node = null;
++
+ if (!this.selected_node) return;
+ this.selected_node = null;
+
@@ -1814,7 +1940,6 @@
+ }
+ }
+});
-\ No newline at end of file
diff --git a/share/static/js/lifecycleui-model.js b/share/static/js/lifecycleui-model.js
new file mode 100644
@@ -1936,14 +2061,14 @@
+ });
+ }
+
-+ UpdateNodeStatus(node, text) {
++ UpdateNodeModel(node, args) {
+ var nodeIndex = this.nodes.findIndex(x => x.id == node.id);
+
-+ this.nodes[nodeIndex] = {...this.nodes[nodeIndex], name: text};
++ this.nodes[nodeIndex] = {...this.nodes[nodeIndex], ...args};
+ var nodeUpdated = this.nodes[nodeIndex];
+
+ // Update any links with node being changed as source
-+ var links = this.links.filter(function(l) { return (
++ var links = this.links.filter(function(l) { return (
+ ( l.source.id === node.id ) )
+ });
+ links.forEach(link => {
@@ -1952,7 +2077,7 @@
+ });
+
+ // Update any links with node being changed as target
-+ var links = this.links.filter(function(l) { return (
++ var links = this.links.filter(function(l) { return (
+ ( l.target.id === node.id ) )
+ });
+ links.forEach(link => {
@@ -1981,6 +2106,7 @@
+ // Grab our links
+ config.transitions[""] = self.config.transitions ? self.config.transitions[""]: [];
+
++ var seen = {};
+ self.nodes.forEach(source => {
+ var links = self.LinksForNode(source);
+ var targets = links.map(link => {
@@ -1992,596 +2118,17 @@
+ }
+ });
+ config.transitions[source.name] = targets;
++ seen[source.name] = 1;
+ });
++
++ for (let transition in config.transitions) {
++ if( !seen[transition] ) {
++ delete config.transitions[transition];
++ }
++ }
+ self.config = config;
+
+ var field = jQuery('input[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
-new file mode 100644
---- /dev/null
-+++ b/share/static/js/lifecycleui-viewer-interactive.js
-@@
-+jQuery(function () {
-+ var Super = RT.LifecycleViewer;
-+
-+ function Interactive (container) {
-+ Super.call(this);
-+ };
-+ Interactive.prototype = Object.create(Super.prototype);
-+
-+ Interactive.prototype.deselectStatus = function () {
-+ delete this.selectedStatus;
-+ delete this.selectedMenu;
-+
-+ this.statusContainer.selectAll('.selected').classed('selected', false);
-+ this.menuContainer.find('.status-menu.selected').removeClass('selected');
-+ };
-+
-+ Interactive.prototype._setMenuPosition = function () {
-+ if (!this.selectedStatus) {
-+ return;
-+ }
-+
-+ var d = this.selectedStatus;
-+ var statusNode = this.statusContainer.select('g[data-key="'+ d._key + '"]');
-+ var bbox = statusNode.node().getBoundingClientRect();
-+ var x = bbox.right + window.scrollX;
-+ var y = bbox.top + window.scrollY;
-+
-+ this.selectedMenu.css({top: y, left: x});
-+ };
-+
-+ Interactive.prototype.clickedStatus = function (d) {
-+ var statusName = d.name;
-+ this.selectedMenu = this.menuContainer.find('.status-menu[data-status="'+statusName+'"]');
-+ this.selectedStatus = d;
-+ var statusNode = this.statusContainer.select('g[data-key="'+ d._key + '"]');
-+
-+ this.statusContainer.selectAll('.selected').classed('selected', false);
-+ statusNode.classed('selected', true);
-+
-+ this.menuContainer.find('.status-menu.selected').removeClass('selected');
-+ this.selectedMenu.addClass('selected');
-+
-+ this.selectedMenu.find(".toplevel").addClass('sf-menu sf-vertical sf-js-enabled sf-shadow').supersubs().superfish({ speed: 'fast' });
-+
-+ this._setMenuPosition();
-+ };
-+
-+ Interactive.prototype.didZoom = function () {
-+ Super.prototype.didZoom.call(this);
-+ if (this.selectedMenu) {
-+ this._setMenuPosition();
-+ var svgBox = this.svg.node().getBoundingClientRect();
-+ var menuBox = this.selectedMenu[0].getBoundingClientRect();
-+
-+ var overlap = !(svgBox.right < menuBox.left ||
-+ svgBox.left > menuBox.right ||
-+ svgBox.bottom < menuBox.top ||
-+ svgBox.top > menuBox.bottom);
-+ if (!overlap) {
-+ this.deselectStatus();
-+ }
-+ }
-+ };
-+
-+ Interactive.prototype.initializeViewer = function (node, name, config, focusStatus) {
-+ var self = this;
-+ Super.prototype.initializeViewer.call(self, node, name, config, focusStatus);
-+ self.menuContainer = jQuery(node).find('.status-menus');
-+ self.svg.on('click', function () { self.deselectStatus() });
-+
-+ // copy classes from <a> to <li> for improved styling
-+ self.menuContainer.find('.status-menu li a').each(function () {
-+ var link = jQuery(this);
-+ var item = link.closest('li');
-+ item.addClass(link.attr("class"));
-+ item.removeClass('menu-item');
-+ });
-+ };
-+
-+ RT.LifecycleViewerInteractive = Interactive;
-+});
-+
-
-diff --git a/share/static/js/lifecycleui-viewer.js b/share/static/js/lifecycleui-viewer.js
-new file mode 100644
---- /dev/null
-+++ b/share/static/js/lifecycleui-viewer.js
-@@
-+jQuery(function () {
-+ class Viewer {
-+ constructor(container) {
-+ this.width = 809;
-+ this.height = 500;
-+ this.statusCircleRadius = 35;
-+ this.statusCircleRadiusFudge = 4; // required to give room for the arrowhead
-+ this.gridSize = 10;
-+ this.padding = this.statusCircleRadius * 2;
-+ this.animationFactor = 1; // bump this to 10 debug JS animations
-+ }
-+ createScale(size, padding) {
-+ return d3.scaleLinear()
-+ .domain([0, 10000])
-+ .range([padding, size - padding]);
-+ }
-+ gridScale(v) { return Math.round(v / this.gridSize) * this.gridSize; }
-+ xScale(x) { return this.gridScale(this._xScale(x)); }
-+ yScale(y) { return this.gridScale(this._yScale(y)); }
-+ xScaleZero(x) { return this.gridScale(this._xScaleZero(x)); }
-+ yScaleZero(y) { return this.gridScale(this._yScaleZero(y)); }
-+ xScaleInvert(x) { return Math.floor(this._xScale.invert(x)); }
-+ yScaleInvert(y) { return Math.floor(this._yScale.invert(y)); }
-+ xScaleZeroInvert(x) { return Math.floor(this._xScaleZero.invert(x)); }
-+ yScaleZeroInvert(y) { return Math.floor(this._yScaleZero.invert(y)); }
-+ addZoomBehavior() {
-+ var self = this;
-+ self._zoom = d3.zoom()
-+ .scaleExtent([.3, 2])
-+ .on("zoom", function () {
-+ if (self.zoomControl) {
-+ self.didZoom();
-+ }
-+ });
-+ self.svg.call(self._zoom);
-+ }
-+ didZoom() {
-+ this._currentZoom = d3.event.transform;
-+ this.transformContainer.attr("transform", d3.event.transform);
-+ }
-+ zoomScale(scaleBy, animated) {
-+ if (animated) {
-+ this.svg.transition()
-+ .duration(350 * this.animationFactor)
-+ .call(this._zoom.scaleBy, scaleBy);
-+ }
-+ else {
-+ this.svg.call(this._zoom.scaleBy, scaleBy);
-+ }
-+ }
-+ _setZoom(zoom, animated) {
-+ if (animated) {
-+ this.svg.transition()
-+ .duration(750 * this.animationFactor)
-+ .call(this._zoom.transform, zoom);
-+ }
-+ else {
-+ this.svg.call(this._zoom.transform, zoom);
-+ }
-+ }
-+ resetZoom(animated) {
-+ this._setZoom(this._zoomIdentity, animated);
-+ }
-+ zoomToFit(animated) {
-+ var bounds = this.transformContainer.node().getBBox();
-+ var parent = this.transformContainer.node().parentElement;
-+ var fullWidth = parent.clientWidth || parent.parentNode.clientWidth, fullHeight = parent.clientHeight || parent.parentNode.clientHeight;
-+ var width = bounds.width, height = bounds.height;
-+ var midX = bounds.x + width / 2, midY = bounds.y + height / 2;
-+ var scale = .9 / Math.max(width / fullWidth, height / fullHeight);
-+ var tx = fullWidth / 2 - scale * midX;
-+ var ty = fullHeight / 2 - scale * midY;
-+ this._setZoom(d3.zoomIdentity.translate(tx, ty).scale(scale), animated);
-+ }
-+ didEnterStatusNodes(statuses) { }
-+ didEnterTransitions(paths) { }
-+ didEnterTextDecorations(labels) { }
-+ didEnterPolygonDecorations(polygons) { }
-+ didEnterCircleDecorations(circles) { }
-+ didEnterLineDecorations(lines) { }
-+ renderStatusNodes(initial) {
-+ var self = this;
-+ var statuses = self.statusContainer.selectAll("g")
-+ .data(self.lifecycle.statusObjects(), function (d) { return d._key; });
-+ var exitStatuses = statuses.exit()
-+ .classed("removing", true)
-+ .transition().duration(200 * self.animationFactor)
-+ .remove();
-+ exitStatuses.select('circle')
-+ .attr("r", self.statusCircleRadius * .8);
-+ var newStatuses = statuses.enter().append("g")
-+ .attr("data-key", function (d) { return d._key; })
-+ .attr("id", function (d) { return 'key-'+d._key; })
-+ .call(function (statuses) { self.didEnterStatusNodes(statuses); });
-+ newStatuses.append("circle")
-+ .attr("r", initial ? self.statusCircleRadius : self.statusCircleRadius * .8)
-+ .on("click", function (d) {
-+ d3.event.stopPropagation();
-+
-+ self.focusItem(d)
-+ })
-+ newStatuses.append("text")
-+ .attr("r", initial ? self.statusCircleRadius : self.statusCircleRadius * .8)
-+ .on("click", function (d) {
-+ d3.event.stopPropagation();
-+ self.clickedStatus(d);
-+ })
-+ if (!initial) {
-+ newStatuses.transition().duration(200 * self.animationFactor)
-+ .select("circle")
-+ .attr("r", self.statusCircleRadius);
-+ }
-+ var allStatuses = newStatuses.merge(statuses)
-+ .classed("focus", function (d) { return self.isFocused(d); })
-+ .classed("focus-from", function (d) { return self.isFocusedTransition(d, true); })
-+ .classed("focus-to", function (d) { return self.isFocusedTransition(d, false); });
-+ allStatuses.select("circle")
-+ .attr("cx", function (d) { return self.xScale(d.x); })
-+ .attr("cy", function (d) { return self.yScale(d.y); })
-+ .attr("fill", function (d) { return d.color; });
-+ allStatuses.select("text")
-+ .attr("x", function (d) { return self.xScale(d.x); })
-+ .attr("y", function (d) { return self.yScale(d.y); })
-+ .attr("fill", function (d) { return d3.hsl(d.color).l > 0.35 ? '#000' : '#fff'; })
-+ .text(function (d) { return d.name; }).each(function () { self.truncateLabel(this); });
-+ }
-+ clickedStatus(d) { }
-+ clickedTransition(d) { }
-+ clickedDecoration(d) { }
-+ truncateLabel(element) {
-+ var node = d3.select(element), textLength = node.node().getComputedTextLength(), text = node.text();
-+ while (textLength > this.statusCircleRadius * 1.8 && text.length > 0) {
-+ text = text.slice(0, -1);
-+ node.text(text + '…');
-+ textLength = node.node().getComputedTextLength();
-+ }
-+ }
-+ transitionArc(d) {
-+ // c* variables are circle centers
-+ // a* variables are for the arc path which is from circle edge to circle edge
-+ var from = this.lifecycle.statusObjectForName(d.from), to = this.lifecycle.statusObjectForName(d.to), cx0 = this.xScale(from.x), cx1 = this.xScale(to.x), cy0 = this.yScale(from.y), cy1 = this.yScale(to.y), cdx = cx1 - cx0, cdy = cy1 - cy0;
-+ // the circles on top of each other would calculate atan2(0,0) which is
-+ // undefined and a little nonsensical
-+ if (cdx == 0 && cdy == 0) {
-+ return null;
-+ }
-+ var theta = Math.atan2(cdy, cdx), r = this.statusCircleRadius, ax0 = cx0 + r * Math.cos(theta), ay0 = cy0 + r * Math.sin(theta), ax1 = cx1 - (r + this.statusCircleRadiusFudge) * Math.cos(theta), ay1 = cy1 - (r + this.statusCircleRadiusFudge) * Math.sin(theta), dr = Math.abs((ax1 - ax0) * 4) + Math.abs((ay1 - ay0) * 4);
-+ return "M" + ax0 + "," + ay0 + " A" + dr + "," + dr + " 0 0,1 " + ax1 + "," + ay1;
-+ }
-+ renderTransitions(initial) {
-+ var self = this;
-+ var paths = self.transitionContainer.selectAll("path")
-+ .data(self.lifecycle.transitions, function (d) { return d._key; });
-+ paths.exit().classed("removing", true)
-+ .each(function (d) {
-+ var length = this.getTotalLength();
-+ var path = d3.select(this);
-+ path.attr("stroke-dasharray", length + " " + length)
-+ .attr("stroke-dashoffset", 0)
-+ .style("marker-end", "none")
-+ .transition().duration(200 * self.animationFactor).ease(d3.easeLinear)
-+ .attr("stroke-dashoffset", length)
-+ .remove();
-+ });
-+ var newPaths = paths.enter().append("path")
-+ .attr("data-key", function (d) { return d._key; })
-+ .on("click", function (d) {
-+ d3.event.stopPropagation();
-+ self.clickedTransition(d);
-+ })
-+ .call(function (paths) { self.didEnterTransitions(paths); });
-+ newPaths.merge(paths)
-+ .attr("d", function (d) { return self.transitionArc(d); })
-+ .classed("dashed", function (d) { return d.style == 'dashed'; })
-+ .classed("dotted", function (d) { return d.style == 'dotted'; })
-+ .classed("focus", function (d) { return self.isFocused(d); })
-+ .classed("focus-from", function (d) { return self.isFocusedTransition(d, true); })
-+ .classed("focus-to", function (d) { return self.isFocusedTransition(d, false); });
-+ if (!initial) {
-+ newPaths.each(function (d) {
-+ var length = this.getTotalLength();
-+ var path = d3.select(this);
-+ path.attr("stroke-dasharray", length + " " + length)
-+ .attr("stroke-dashoffset", length)
-+ .style("marker-end", "none")
-+ .transition().duration(200 * self.animationFactor).ease(d3.easeLinear)
-+ .attr("stroke-dashoffset", 0)
-+ .on("end", function () {
-+ d3.select(this)
-+ .attr("stroke-dasharray", undefined)
-+ .attr("stroke-offset", undefined)
-+ .style("marker-end", undefined);
-+ });
-+ });
-+ }
-+ }
-+ _wrapTextDecoration(node, text) {
-+ if (node.attr('data-text') == text) {
-+ return;
-+ }
-+ var lines = text.split(/\n/), lineHeight = 1.1;
-+ if (node.attr('data-text')) {
-+ node.selectAll("*").remove();
-+ }
-+ node.attr('data-text', text);
-+ for (var i = 0; i < lines.length; ++i) {
-+ node.append("tspan").attr("dy", (i + 1) * lineHeight + "em").text(lines[i]);
-+ }
-+ }
-+ renderTextDecorations(initial) {
-+ var self = this;
-+ var labels = self.decorationContainer.selectAll("text")
-+ .data(self.lifecycle.decorations.text, function (d) { return d._key; });
-+ labels.exit()
-+ .classed("removing", true)
-+ .transition().duration(200 * self.animationFactor)
-+ .remove();
-+ var newLabels = labels.enter().append("text")
-+ .attr("data-key", function (d) { return d._key; })
-+ .on("click", function (d) {
-+ d3.event.stopPropagation();
-+ self.clickedDecoration(d);
-+ })
-+ .call(function (labels) { self.didEnterTextDecorations(labels); });
-+ if (!initial) {
-+ newLabels.style("opacity", 0.15)
-+ .transition().duration(200 * self.animationFactor)
-+ .style("opacity", 1)
-+ .on("end", function () { d3.select(this).style("opacity", undefined); });
-+ }
-+ newLabels.merge(labels)
-+ .attr("x", function (d) { return self.xScale(d.x); })
-+ .attr("y", function (d) { return self.yScale(d.y); })
-+ .classed("bold", function (d) { return d.bold; })
-+ .classed("italic", function (d) { return d.italic; })
-+ .classed("focus", function (d) { return self.isFocused(d); })
-+ .each(function (d) { self._wrapTextDecoration(d3.select(this), d.text); })
-+ .selectAll("tspan")
-+ .attr("x", function (d) { return self.xScale(d.x); })
-+ .attr("y", function (d) { return self.yScale(d.y); });
-+ }
-+ renderPolygonDecorations(initial) {
-+ var self = this;
-+ var polygons = self.decorationContainer.selectAll("polygon")
-+ .data(self.lifecycle.decorations.polygon, function (d) { return d._key; });
-+ polygons.exit()
-+ .classed("removing", true)
-+ .transition().duration(200 * self.animationFactor)
-+ .remove();
-+ var newPolygons = polygons.enter().append("polygon")
-+ .attr("data-key", function (d) { return d._key; })
-+ .on("click", function (d) {
-+ d3.event.stopPropagation();
-+ self.clickedDecoration(d);
-+ })
-+ .call(function (polygons) { self.didEnterPolygonDecorations(polygons); });
-+ if (!initial) {
-+ newPolygons.style("opacity", 0.15)
-+ .transition().duration(200 * self.animationFactor)
-+ .style("opacity", 1)
-+ .on("end", function () { d3.select(this).style("opacity", undefined); });
-+ }
-+ newPolygons.merge(polygons)
-+ .attr("stroke", function (d) { return d.renderStroke ? d.stroke : 'none'; })
-+ .classed("dashed", function (d) { return d.strokeStyle == 'dashed'; })
-+ .classed("dotted", function (d) { return d.strokeStyle == 'dotted'; })
-+ .attr("fill", function (d) { return d.renderFill ? d.fill : 'none'; })
-+ .attr("transform", function (d) { return "translate(" + self.xScale(d.x) + ", " + self.yScale(d.y) + ")"; })
-+ .attr("points", function (d) {
-+ return jQuery.map(d.points, function (p) {
-+ return [self.xScaleZero(p.x), self.yScaleZero(p.y)].join(",");
-+ }).join(" ");
-+ })
-+ .classed("focus", function (d) { return self.isFocused(d); });
-+ }
-+ renderCircleDecorations(initial) {
-+ var self = this;
-+ var circles = self.decorationContainer.selectAll("circle.decoration")
-+ .data(self.lifecycle.decorations.circle, function (d) { return d._key; });
-+ circles.exit()
-+ .classed("removing", true)
-+ .transition().duration(200 * self.animationFactor)
-+ .remove();
-+ var newCircles = circles.enter().append("circle")
-+ .classed("decoration", true)
-+ .attr("data-key", function (d) { return d._key; })
-+ .on("click", function (d) {
-+ d3.event.stopPropagation();
-+ self.clickedDecoration(d);
-+ })
-+ .call(function (circles) { self.didEnterCircleDecorations(circles); });
-+ if (!initial) {
-+ newCircles.style("opacity", 0.15)
-+ .transition().duration(200 * self.animationFactor)
-+ .style("opacity", 1)
-+ .on("end", function () { d3.select(this).style("opacity", undefined); });
-+ }
-+ newCircles.merge(circles)
-+ .attr("stroke", function (d) { return d.renderStroke ? d.stroke : 'none'; })
-+ .classed("dashed", function (d) { return d.strokeStyle == 'dashed'; })
-+ .classed("dotted", function (d) { return d.strokeStyle == 'dotted'; })
-+ .attr("fill", function (d) { return d.renderFill ? d.fill : 'none'; })
-+ .attr("cx", function (d) { return self.xScale(d.x); })
-+ .attr("cy", function (d) { return self.yScale(d.y); })
-+ .attr("r", function (d) { return d.r; })
-+ .classed("focus", function (d) { return self.isFocused(d); });
-+ }
-+ renderLineDecorations(initial) {
-+ var self = this;
-+ var lines = self.decorationContainer.selectAll("line")
-+ .data(self.lifecycle.decorations.line, function (d) { return d._key; });
-+ lines.exit()
-+ .classed("removing", true)
-+ .transition().duration(200 * self.animationFactor)
-+ .remove();
-+ var newLines = lines.enter().append("line")
-+ .attr("data-key", function (d) { return d._key; })
-+ .on("click", function (d) {
-+ d3.event.stopPropagation();
-+ self.clickedDecoration(d);
-+ })
-+ .call(function (lines) { self.didEnterLineDecorations(lines); });
-+ if (!initial) {
-+ newLines.each(function (d) {
-+ var length = Math.sqrt((d.points[1].x - d.points[0].x) ** 2 + (d.points[1].y - d.points[0].y) ** 2);
-+ var path = d3.select(this);
-+ path.attr("stroke-dasharray", length + " " + length)
-+ .attr("stroke-dashoffset", length)
-+ .style("marker-start", "none")
-+ .style("marker-end", "none")
-+ .transition().duration(200 * self.animationFactor).ease(d3.easeLinear)
-+ .attr("stroke-dashoffset", 0)
-+ .on("end", function () {
-+ d3.select(this)
-+ .attr("stroke-dasharray", undefined)
-+ .attr("stroke-offset", undefined)
-+ .style("marker-start", undefined)
-+ .style("marker-end", undefined);
-+ });
-+ });
-+ }
-+ newLines.merge(lines)
-+ .classed("dashed", function (d) { return d.style == 'dashed'; })
-+ .classed("dotted", function (d) { return d.style == 'dotted'; })
-+ .attr("transform", function (d) { return "translate(" + self.xScale(d.x) + ", " + self.yScale(d.y) + ")"; })
-+ .attr("x1", function (d) { return self.xScaleZero(d.points[0].x); })
-+ .attr("y1", function (d) { return self.yScaleZero(d.points[0].y); })
-+ .attr("x2", function (d) { return self.xScaleZero(d.points[1].x); })
-+ .attr("y2", function (d) { return self.yScaleZero(d.points[1].y); })
-+ .classed("focus", function (d) { return self.isFocused(d); })
-+ .attr("marker-start", function (d) { return d.startMarker == 'none' ? undefined : "url(#line_marker_" + d.startMarker + ")"; })
-+ .attr("marker-end", function (d) { return d.endMarker == 'none' ? undefined : "url(#line_marker_" + d.endMarker + ")"; });
-+ }
-+ renderDecorations(initial) {
-+ this.renderPolygonDecorations(initial);
-+ this.renderCircleDecorations(initial);
-+ this.renderLineDecorations(initial);
-+ this.renderTextDecorations(initial);
-+ }
-+ renderDisplay(initial) {
-+ this.renderTransitions(initial);
-+ this.renderStatusNodes(initial);
-+ this.renderDecorations(initial);
-+ }
-+ centerOnItem(item, animated) {
-+ var rect = this.svg.node().getBoundingClientRect();
-+ var scale = this._zoomIdentityScale;
-+ var x = rect.width / 2 - this.xScale(item.x) * scale;
-+ var y = rect.height / 2 - this.yScale(item.y) * scale;
-+ this._zoomIdentity = d3.zoomIdentity.translate(x, y).scale(this._zoomIdentityScale);
-+ this.resetZoom(animated);
-+ }
-+ defocus() {
-+ this._focusItem = null;
-+ this.svg.classed("has-focus", false)
-+ .attr('data-focus-type', undefined);
-+ }
-+ focusItem(d) {
-+ this.defocus();
-+ this._focusItem = d;
-+ this.svg.classed("has-focus", true)
-+ .attr('data-focus-type', d._type);
-+ }
-+ focusOnStatus(statusName, center, animated) {
-+ if (!statusName) {
-+ return;
-+ }
-+ var meta = this.lifecycle.statusObjectForName(statusName);
-+ this.focusItem(meta);
-+ if (center) {
-+ this.centerOnItem(meta, animated);
-+ }
-+ }
-+ isFocused(d) {
-+ if (!this._focusItem) {
-+ return false;
-+ }
-+ return this._focusItem._key == d._key;
-+ }
-+ isFocusedTransition(d, isFrom) {
-+ if (!this._focusItem) {
-+ return false;
-+ }
-+ if (d._type == 'status') {
-+ if (this._focusItem._type == 'status') {
-+ if (isFrom) {
-+ return this.lifecycle.hasTransition(d.name, this._focusItem.name);
-+ }
-+ else {
-+ return this.lifecycle.hasTransition(this._focusItem.name, d.name);
-+ }
-+ }
-+ else if (this._focusItem._type == 'transition') {
-+ if (isFrom) {
-+ return this._focusItem.from == d.name;
-+ }
-+ else {
-+ return this._focusItem.to == d.name;
-+ }
-+ }
-+ }
-+ else if (d._type == 'transition') {
-+ if (this._focusItem._type == 'status') {
-+ if (isFrom) {
-+ return d.to == this._focusItem.name;
-+ }
-+ else {
-+ return d.from == this._focusItem.name;
-+ }
-+ }
-+ }
-+ return false;
-+ }
-+
-+ initializeViewer(node, name, config, focusStatus) {
-+ var self = this;
-+ self.container = jQuery(node);
-+ self.svg = d3.select(node).select('svg');
-+ self.transformContainer = self.svg.select('g.transform');
-+ self.transitionContainer = self.svg.select('g.transitions');
-+ self.statusContainer = self.svg.select('g.statuses');
-+ self.decorationContainer = self.svg.select('g.decorations');
-+ self._xScale = self.createScale(self.width, self.padding);
-+ self._yScale = self.createScale(self.height, self.padding);
-+ self._xScaleZero = self.createScale(self.width, 0);
-+ self._yScaleZero = self.createScale(self.height, 0);
-+ // zoom in a bit, but not too much
-+ var scale = self.svg.node().getBoundingClientRect().width / self.width;
-+ scale = scale ** .6;
-+ self._zoomIdentityScale = scale;
-+ self._zoomIdentity = self._currentZoom = d3.zoomIdentity.scale(self._zoomIdentityScale);
-+ RT.Lifecycle.name = name;
-+ self.lifecycle = RT.Lifecycle;
-+ self.lifecycle.initializeFromConfig(config);
-+ // need to start with zoom control on to set the initial zoom
-+ this.zoomControl = true;
-+ self.addZoomBehavior();
-+ if (self.container.hasClass('center-status')) {
-+ self.focusOnStatus(focusStatus, true, false);
-+ self.renderDisplay(true);
-+ }
-+ else {
-+ self.focusOnStatus(focusStatus, false, false);
-+ self.renderDisplay(true);
-+ if (self.container.hasClass('center-fit')) {
-+ self.zoomToFit(false);
-+ }
-+ else if (self.container.hasClass('center-origin')) {
-+ self.resetZoom(false);
-+ }
-+ }
-+ self._zoomIdentity = self._currentZoom;
-+ self.zoomControl = self.container.hasClass('zoomable');
-+ self.container.on('click', 'button.zoom-in', function (e) {
-+ e.preventDefault();
-+ self.zoomScale(1.25, true);
-+ });
-+ self.container.on('click', 'button.zoom-out', function (e) {
-+ e.preventDefault();
-+ self.zoomScale(.75, true);
-+ });
-+ self.container.on('click', 'button.zoom-reset', function (e) {
-+ e.preventDefault();
-+ self.resetZoom(true);
-+ });
-+ }
-+ };
-+
-+ RT.LifecycleViewer = Viewer;
-+});
-+
-
2: db4a3b570b < -: ------- Temp
More information about the rt-commit
mailing list