Added pipeline index viz. Fixes #429 (#438)

WIP #429 Added basic boilerplate for the pipeline index viz

WIP #429 Removed the title

WIP #429 Fixed columns (and text)

WIP #429 Added open, delete actions

WIP #429 Added double click to edit pipeline name

WIP #429 Added check for empty, same name

WIP #429 Added comment for thing to add next

WIP #429 Added some thumbnail support and embedded support

WIP #429 Fixed the thumbnail

WIP #429 thumbnail updates

WIP #429 Set background color and fixed updateNode bug

WIP #429 Fixed update thumbnail deletion

WIP #429 Added preview click-to-open

WIP #429 Added empty message

WIP #429 slight refactor

WIP #429 Added executions text (better phrasing)

WIP #429 Removed toolbar items

WIP #429 Fixed remove undefined bug

WIP #429 Updated seeds

WIP #429 Added pipeline index css to ignore

WIP #429 Fixed code climate issues

WIP #429 Added csslint ignore file

WIP #429 Fixed code climate issues

WIP #429 Added csslint ignore block for materialize

WIP #429 Added thumbnails to cifar10 example
Esse commit está contido em:
Brian Broll
2016-06-30 15:33:05 -05:00
commit de GitHub
commit 6e32394e01
15 arquivos alterados com 6043 adições e 0 exclusões
+1
Ver Arquivo
@@ -32,3 +32,4 @@ exclude_paths:
- src/common/lua.js
- src/common/js-yaml.min.js
- src/visualizers/widgets/TextEditor/lib/
- src/visualizers/widgets/PipelineIndex/styles/PipelineIndex.css
+1
Ver Arquivo
@@ -1,2 +1,3 @@
--exclude-exts=.min.css
--exclude-list=src/visualizers/widgets/PipelineIndex/styles/PipelineIndex.css
--ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
+6
Ver Arquivo
@@ -100,5 +100,11 @@
"title": "ClassEditor",
"panel": "panels/ClassEditor/ClassEditorPanel",
"DEBUG_ONLY": false
},
{
"id": "PipelineIndex",
"title": "PipelineIndex",
"panel": "panels/PipelineIndex/PipelineIndexPanel",
"DEBUG_ONLY": false
}
]
@@ -137,6 +137,7 @@ define([
this._widget.createConnection = this.createConnection.bind(this);
this._widget.removeConnection = this.removeConnection.bind(this);
this._widget.getDecorator = this.getDecorator.bind(this);
this._widget.updateThumbnail = this.updateThumbnail.bind(this);
};
PipelineEditorControl.prototype.isContainedInActive = function (gmeId) {
@@ -594,5 +595,27 @@ define([
}
};
PipelineEditorControl.prototype.updateThumbnail = function (svg) {
var node = this._client.getNode(this._currentNodeId),
name,
attrs,
currentThumbnail,
attrName = 'thumbnail',
msg;
if (node) { // may have been deleted
name = node.getAttribute('name');
attrs = node.getValidAttributeNames();
currentThumbnail = node.getAttribute(attrName);
msg = `Updating pipeline thumbnail for "${name}"`;
if (attrs.indexOf(attrName) > -1 && currentThumbnail !== svg) {
this._client.startTransaction(msg);
this._client.setAttributes(this._currentNodeId, attrName, svg);
this._client.completeTransaction();
}
}
};
return PipelineEditorControl;
});
@@ -0,0 +1,225 @@
/*globals define, WebGMEGlobal*/
/*jshint browser: true*/
define([
'js/Constants',
'js/NodePropertyNames'
], function (
CONSTANTS,
nodePropertyNames
) {
'use strict';
var PipelineIndexControl;
PipelineIndexControl = function (options) {
this._logger = options.logger.fork('Control');
this._client = options.client;
// Initialize core collections and variables
this._widget = options.widget;
this._currentNodeId = null;
this._embedded = options.embedded;
this._initWidgetEventHandlers();
this._logger.debug('ctor finished');
};
PipelineIndexControl.prototype._initWidgetEventHandlers = function () {
this._widget.deletePipeline = id => {
var node = this._client.getNode(id),
name = node.getAttribute('name'),
msg = `Deleting pipeline "${name}"`;
// Change the current active object
this._client.startTransaction(msg);
this._client.delMoreNodes([id]);
this._client.completeTransaction();
};
this._widget.setName = (id, name) => {
var oldName = this._client.getNode(id).getAttribute('name'),
msg = `Renaming pipeline: "${oldName}" -> "${name}"`;
if (oldName !== name && !/^\s*$/.test(name)) {
this._client.startTransaction(msg);
this._client.setAttributes(id, 'name', name);
this._client.completeTransaction();
}
};
};
/* * * * * * * * Visualizer content update callbacks * * * * * * * */
// One major concept here is with managing the territory. The territory
// defines the parts of the project that the visualizer is interested in
// (this allows the browser to then only load those relevant parts).
PipelineIndexControl.prototype.selectedObjectChanged = function (nodeId) {
this._logger.debug('activeObject nodeId \'' + nodeId + '\'');
// Remove current territory patterns
if (this._currentNodeId) {
this._client.removeUI(this._territoryId);
}
this._currentNodeId = nodeId;
if (typeof this._currentNodeId === 'string') {
// Put new node's info into territory rules
this._selfPatterns = {};
this._selfPatterns[nodeId] = {children: 1};
this._territoryId = this._client.addUI(this, this._eventCallback.bind(this));
// Update the territory
this._client.updateTerritory(this._territoryId, this._selfPatterns);
}
};
// This next function retrieves the relevant node information for the widget
PipelineIndexControl.prototype._getObjectDescriptor = function (nodeId) {
var node = this._client.getNode(nodeId),
objDescriptor;
if (node) {
objDescriptor = {
id: undefined,
name: undefined,
parentId: undefined,
thumbnail: node.getAttribute('thumbnail'),
executionCount: node.getMemberIds('executions').length
};
objDescriptor.id = node.getId();
objDescriptor.name = node.getAttribute(nodePropertyNames.Attributes.name);
objDescriptor.parentId = node.getParentId();
}
return objDescriptor;
};
/* * * * * * * * Node Event Handling * * * * * * * */
PipelineIndexControl.prototype._eventCallback = function (events) {
var i = events ? events.length : 0,
event;
this._logger.debug('_eventCallback \'' + i + '\' items');
while (i--) {
event = events[i];
switch (event.etype) {
case CONSTANTS.TERRITORY_EVENT_LOAD:
this._onLoad(event.eid);
break;
case CONSTANTS.TERRITORY_EVENT_UPDATE:
this._onUpdate(event.eid);
break;
case CONSTANTS.TERRITORY_EVENT_UNLOAD:
this._onUnload(event.eid);
break;
default:
break;
}
}
this._logger.debug('_eventCallback \'' + events.length + '\' items - DONE');
};
PipelineIndexControl.prototype._onLoad = function (gmeId) {
if (gmeId !== this._currentNodeId) {
var description = this._getObjectDescriptor(gmeId);
this._widget.addNode(description);
}
};
PipelineIndexControl.prototype._onUpdate = function (gmeId) {
if (gmeId !== this._currentNodeId) {
var description = this._getObjectDescriptor(gmeId);
this._widget.updateNode(description);
}
};
PipelineIndexControl.prototype._onUnload = function (gmeId) {
this._widget.removeNode(gmeId);
};
PipelineIndexControl.prototype._stateActiveObjectChanged = function (model, activeObjectId) {
if (this._currentNodeId !== activeObjectId) {
this.selectedObjectChanged(activeObjectId);
}
};
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
PipelineIndexControl.prototype.destroy = function () {
this._detachClientEventListeners();
this._removeToolbarItems();
};
PipelineIndexControl.prototype._attachClientEventListeners = function () {
this._detachClientEventListeners();
if (!this._embedded) {
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged, this);
}
};
PipelineIndexControl.prototype._detachClientEventListeners = function () {
if (!this._embedded) {
WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged);
}
};
PipelineIndexControl.prototype.onActivate = function () {
this._attachClientEventListeners();
this._displayToolbarItems();
if (typeof this._currentNodeId === 'string') {
WebGMEGlobal.State.registerSuppressVisualizerFromNode(true);
WebGMEGlobal.State.registerActiveObject(this._currentNodeId);
WebGMEGlobal.State.registerSuppressVisualizerFromNode(false);
}
};
PipelineIndexControl.prototype.onDeactivate = function () {
this._detachClientEventListeners();
this._hideToolbarItems();
};
/* * * * * * * * * * Updating the toolbar * * * * * * * * * */
PipelineIndexControl.prototype._displayToolbarItems = function () {
if (this._toolbarInitialized === true) {
for (var i = this._toolbarItems.length; i--;) {
this._toolbarItems[i].show();
}
} else {
this._initializeToolbar();
}
};
PipelineIndexControl.prototype._hideToolbarItems = function () {
if (this._toolbarInitialized === true) {
for (var i = this._toolbarItems.length; i--;) {
this._toolbarItems[i].hide();
}
}
};
PipelineIndexControl.prototype._removeToolbarItems = function () {
if (this._toolbarInitialized === true) {
for (var i = this._toolbarItems.length; i--;) {
this._toolbarItems[i].destroy();
}
}
};
PipelineIndexControl.prototype._initializeToolbar = function () {
this._toolbarItems = [];
this._toolbarInitialized = true;
};
return PipelineIndexControl;
});
@@ -0,0 +1,94 @@
/*globals define, _, WebGMEGlobal*/
/*jshint browser: true*/
/**
* Generated by VisualizerGenerator 1.7.0 from webgme on Wed Jun 29 2016 16:10:46 GMT-0500 (CDT).
*/
define(['js/PanelBase/PanelBaseWithHeader',
'js/PanelManager/IActivePanel',
'widgets/PipelineIndex/PipelineIndexWidget',
'./PipelineIndexControl'
], function (PanelBaseWithHeader,
IActivePanel,
PipelineIndexWidget,
PipelineIndexControl) {
'use strict';
var PipelineIndexPanel;
PipelineIndexPanel = function (layoutManager, params) {
var options = {};
//set properties from options
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'PipelineIndexPanel';
options[PanelBaseWithHeader.OPTIONS.FLOATING_TITLE] = true;
//call parent's constructor
PanelBaseWithHeader.apply(this, [options, layoutManager]);
this._client = params.client;
this._embedded = params.embedded;
//initialize UI
this._initialize();
this.logger.debug('ctor finished');
};
//inherit from PanelBaseWithHeader
_.extend(PipelineIndexPanel.prototype, PanelBaseWithHeader.prototype);
_.extend(PipelineIndexPanel.prototype, IActivePanel.prototype);
PipelineIndexPanel.prototype._initialize = function () {
//set Widget title
this.setTitle('');
this.widget = new PipelineIndexWidget(this.logger, this.$el);
this.control = new PipelineIndexControl({
logger: this.logger,
client: this._client,
embedded: this._embedded,
widget: this.widget
});
this.onActivate();
};
/* OVERRIDE FROM WIDGET-WITH-HEADER */
/* METHOD CALLED WHEN THE WIDGET'S READ-ONLY PROPERTY CHANGES */
PipelineIndexPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
//apply parent's onReadOnlyChanged
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
};
PipelineIndexPanel.prototype.onResize = function (width, height) {
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
};
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
PipelineIndexPanel.prototype.destroy = function () {
this.control.destroy();
this.widget.destroy();
PanelBaseWithHeader.prototype.destroy.call(this);
WebGMEGlobal.KeyboardManager.setListener(undefined);
WebGMEGlobal.Toolbar.refresh();
};
PipelineIndexPanel.prototype.onActivate = function () {
this.widget.onActivate();
this.control.onActivate();
WebGMEGlobal.KeyboardManager.setListener(this.widget);
WebGMEGlobal.Toolbar.refresh();
};
PipelineIndexPanel.prototype.onDeactivate = function () {
this.widget.onDeactivate();
this.control.onDeactivate();
WebGMEGlobal.KeyboardManager.setListener(undefined);
WebGMEGlobal.Toolbar.refresh();
};
return PipelineIndexPanel;
});
@@ -90,6 +90,7 @@ define([
// Update the given port...
dstItem.refreshPorts();
}
this.refreshThumbnail();
};
PipelineEditorWidget.prototype.addNode = function(desc) {
@@ -109,6 +110,7 @@ define([
this.PORT_STATE = STATE.DEFAULT;
this.connectPort.apply(this, this.srcPortToConnectArgs);
}
this.refreshThumbnail();
};
PipelineEditorWidget.prototype._removeConnection = function(id) {
@@ -123,6 +125,7 @@ define([
dst.refreshPorts();
}
EasyDAGWidget.prototype._removeConnection.call(this, id);
this.refreshThumbnail();
};
// May not actually need these port methods
@@ -142,6 +145,7 @@ define([
this.removePort(gmeId);
} else {
EasyDAGWidget.prototype.removeNode.call(this, gmeId);
this.refreshThumbnail();
}
};
@@ -386,5 +390,48 @@ define([
});
};
////////////////////////// Action Overrides END //////////////////////////
////////////////////////// Thumbnail updates //////////////////////////
PipelineEditorWidget.prototype.getSvgDistanceDim = function(dim) {
var maxValue = this._getMaxAlongAxis(dim),
nodes,
minValue;
nodes = this.graph.nodes().map(id => this.graph.node(id));
minValue = Math.min.apply(null, nodes.map(node => node[dim]));
return maxValue-minValue;
};
PipelineEditorWidget.prototype.getSvgWidth = function() {
return this.getSvgDistanceDim('x');
};
PipelineEditorWidget.prototype.getSvgHeight = function() {
return this.getSvgDistanceDim('y');
};
PipelineEditorWidget.prototype.getViewBox = function() {
var maxX = this.getSvgWidth('x'),
maxY = this.getSvgHeight('y');
return `0 0 ${maxX} ${maxY}`;
};
PipelineEditorWidget.prototype.refreshThumbnail = _.debounce(function() {
// Get the svg...
var svg = document.createElement('svg'),
group = this.$svg.node(),
child;
svg.setAttribute('viewBox', this.getViewBox());
for (var i = 0; i < group.children.length; i++) {
child = $(group.children[i]);
svg.appendChild(child.clone()[0]);
}
this.updateThumbnail(svg.outerHTML);
}, 1000);
return PipelineEditorWidget;
});
@@ -0,0 +1,28 @@
<div class="col s6 m3 l2">
<div class="pipeline card" data-id="<%= id %>" style="overflow: hidden;">
<div class="center preview card-image waves-effect waves-block waves-light">
</div>
<div class="card-content">
<span data-id="<%= id %>" class="card-title activator grey-text text-darken-4 pipeline-name"><%= name %></span>
<p>
<% if (executionCount === 0) { %>
No executions
<% } else if (executionCount === 1){ %>
Executed once
<% } else if (executionCount === 2){ %>
Executed twice
<% } else { %>
Executed <%= executionCount %> times
<% } %>
</p>
</div>
<div class="card-reveal" style="display: none; transform: translateY(0px);">
<span class="card-title grey-text text-darken-4">"<%= name %>" Executions<i class="material-icons right">close</i></span>
<p></p>
</div>
<div class="card-action">
<a class="open-pipeline" href="#">Open</a>
<a class="delete-pipeline" data-id="<%= id %>" href="#">Delete</a>
</div>
</div>
</div>
@@ -0,0 +1,154 @@
/*globals define, $, WebGMEGlobal */
/*jshint browser: true*/
define([
'text!./Pipeline.ejs',
'underscore',
'css!./styles/PipelineIndexWidget.css'
], function (
PipelineHtml,
_
) {
'use strict';
var PipelineIndexWidget,
PipelineTemplate = _.template(PipelineHtml),
EMPTY_MSG = 'No Existing Pipelines... yet!',
WIDGET_CLASS = 'pipeline-index';
PipelineIndexWidget = function (logger, container) {
this._logger = logger.fork('Widget');
this.$el = $('<div>', {
class: 'row'
});
container.append(this.$el);
container.addClass(`${WIDGET_CLASS} container`);
this.cards = {};
this.nodes = {};
this.$backgroundText = null;
this.updateBackgroundText();
this._initializeEventHandlers();
this._logger.debug('ctor finished');
};
PipelineIndexWidget.prototype._initializeEventHandlers = function () {
this.$el.on('click', '.open-pipeline', this.openPipeline);
this.$el.on('click', '.preview.card-image', this.openPipeline);
this.$el.on('click', '.delete-pipeline', event => {
var id = event.target.getAttribute('data-id');
this.deletePipeline(id);
});
this.$el.on('dblclick', '.pipeline-name', event => {
var html = $(event.target),
id = html.data('id');
html.editInPlace({
css: {
'z-index': 1000
},
onChange: (oldVal, newVal) => {
this.setName(id, newVal);
}
});
});
};
PipelineIndexWidget.prototype.updateBackgroundText = function() {
if (this.$backgroundText) {
this.$backgroundText.remove();
}
// Add background text if empty
if (Object.keys(this.cards).length === 0) {
this.$backgroundText = $('<div>', {class: 'background-text'});
this.$backgroundText.text(EMPTY_MSG);
this.$el.append(this.$backgroundText);
}
};
PipelineIndexWidget.prototype.openPipeline = function (event) {
var target = event.target,
indexOf = Array.prototype.indexOf,
id;
while (!target.classList || indexOf.call(target.classList, 'pipeline') === -1) {
target = target.parentNode;
}
id = target.getAttribute('data-id');
WebGMEGlobal.State.registerActiveObject(id);
};
// Adding/Removing/Updating items
PipelineIndexWidget.prototype.addNode = function (desc) {
var node;
if (desc) {
// Add node to a table of cards
this.nodes[desc.id] = desc;
node = $(PipelineTemplate(desc));
this.cards[desc.id] = node;
// Add click listeners
this.$el.append(node);
// Add the thumbnail
if (desc.thumbnail) {
this.addThumbnail(desc.thumbnail, node);
}
this.updateBackgroundText();
}
};
PipelineIndexWidget.prototype.addThumbnail = function (thumbnail, node) {
var container = node.find('.preview'),
svg = $(thumbnail);
// scale and shift the thumbnail
svg.attr('width', 150)
.attr('height', 150);
container.empty();
container.append(svg);
};
PipelineIndexWidget.prototype.removeNode = function (gmeId) {
var html = this.cards[gmeId];
if (html) {
html.remove();
delete this.cards[gmeId];
this.updateBackgroundText();
}
};
PipelineIndexWidget.prototype.updateNode = function (desc) {
if (desc && this.cards[desc.id]) {
this.cards[desc.id].outerHTML = PipelineTemplate(desc);
// Check if the preview changed
if (desc.thumbnail !== this.nodes[desc.id].thumbnail) {
this.addThumbnail(desc.thumbnail, this.cards[desc.id]);
}
this.nodes[desc.id] = desc;
}
};
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
PipelineIndexWidget.prototype.destroy = function () {
};
PipelineIndexWidget.prototype.onActivate = function () {
this._logger.debug('PipelineIndexWidget has been activated');
};
PipelineIndexWidget.prototype.onDeactivate = function () {
this._logger.debug('PipelineIndexWidget has been deactivated');
};
return PipelineIndexWidget;
});
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
@@ -0,0 +1,7 @@
/**
* This file is for any scss that you may want for this visualizer.
*/
.pipeline-index {
outline: none;
}
+7
Ver Arquivo
@@ -165,6 +165,13 @@
"panel": "src/visualizers/panels/ClassEditor",
"secondary": false,
"widget": "src/visualizers/widgets/ClassEditor"
},
"PipelineIndex": {
"src": "panels/PipelineIndex/PipelineIndexPanel",
"title": "PipelineIndex",
"panel": "src/visualizers/panels/PipelineIndex",
"secondary": false,
"widget": "src/visualizers/widgets/PipelineIndex"
}
},
"addons": {},