Comparar commits
88 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| d88b758f43 | |||
| 0e6ae8f9b6 | |||
| 3426a07096 | |||
| e95f611b79 | |||
| 5d0f13c5ec | |||
| 88e1f3828e | |||
| 4de9cf29fe | |||
| 448616be12 | |||
| 93a2905534 | |||
| 0bd0bd70f2 | |||
| 1720fc869a | |||
| 41e7750dd1 | |||
| 81afc81887 | |||
| 3e9931fa3b | |||
| 5135ccef09 | |||
| 6314e00a8d | |||
| 779429e24d | |||
| a7f4eac09d | |||
| 7c645e9b23 | |||
| fc3e14644f | |||
| fd48c1b480 | |||
| 8928d0f105 | |||
| 6857da5ac7 | |||
| fee584cfde | |||
| 45fcd81739 | |||
| d831f5e032 | |||
| 65e938f489 | |||
| b9b5cea5a1 | |||
| 6c639376d1 | |||
| 58ed3048f8 | |||
| f64492061d | |||
| 552e71687d | |||
| 448de23945 | |||
| dbd00e5946 | |||
| 4d0b4ae017 | |||
| d6c201cce6 | |||
| b8bc3af524 | |||
| 72de1d44da | |||
| da6f186b52 | |||
| 5f1c50beb6 | |||
| 3a79afbc41 | |||
| ba210e4b27 | |||
| bdb21a5262 | |||
| 4b6471efbc | |||
| beeb1deb3c | |||
| 0bc6846c32 | |||
| 8a40cb95fb | |||
| 92ea4b2ff5 | |||
| e0a83b4d6c | |||
| 49268b9554 | |||
| 65164bcb76 | |||
| 1afcaf9238 | |||
| 92021a06ba | |||
| be1e4fa388 | |||
| 09505c9989 | |||
| b96f2a845a | |||
| 14c0af86c9 | |||
| 56a0b788ee | |||
| 873cbc2145 | |||
| 3e3326688c | |||
| 6e32394e01 | |||
| afed957af8 | |||
| 4c100ac001 | |||
| 29724551f0 | |||
| 1f5c8d7423 | |||
| a613a1e8f7 | |||
| 7fce6e98dd | |||
| ea572e8f6c | |||
| 36290d8dc7 | |||
| dadc09e0e1 | |||
| be3d278d13 | |||
| 32a96fbf2b | |||
| ebe391c948 | |||
| e30ebf3a45 | |||
| 9e0a780ed2 | |||
| 841637e804 | |||
| 75241262c4 | |||
| b39ac022b6 | |||
| 8486b86a05 | |||
| 322af8a4f5 | |||
| 5e1930f096 | |||
| c0f1348a14 | |||
| 383f5f9fa0 | |||
| 5cf592e103 | |||
| a14e750b6f | |||
| 73e165197f | |||
| 9dd0815625 | |||
| 8e3ac1d203 |
@@ -0,0 +1,36 @@
|
||||
---
|
||||
engines:
|
||||
csslint:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: true
|
||||
exclude_fingerprints:
|
||||
- 1e004cf4e49528a58a0ac3858112601c
|
||||
config:
|
||||
languages:
|
||||
- ruby
|
||||
- javascript
|
||||
- python
|
||||
- php
|
||||
eslint:
|
||||
enabled: true
|
||||
fixme:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.css"
|
||||
- "**.inc"
|
||||
- "**.js"
|
||||
- "**.jsx"
|
||||
- "**.module"
|
||||
- "**.php"
|
||||
- "**.py"
|
||||
- "**.rb"
|
||||
exclude_paths:
|
||||
- config/
|
||||
- test/
|
||||
- src/common/lua.js
|
||||
- src/common/js-yaml.min.js
|
||||
- src/visualizers/widgets/TextEditor/lib/
|
||||
- src/visualizers/widgets/PipelineIndex/styles/PipelineIndex.css
|
||||
- src/visualizers/widgets/LineGraph/lib/
|
||||
@@ -0,0 +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
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
[](https://img.shields.io/badge/state-alpha-orange.svg)
|
||||
[](https://img.shields.io/badge/state-beta-yellow.svg)
|
||||
[](./LICENSE)
|
||||
[](https://travis-ci.org/dfst/deepforge)
|
||||
[](https://gitter.im/dfst/deepforge?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://waffle.io/dfst/deepforge)
|
||||
|
||||
**Notice**: DeepForge is still very much a work in progress and is also lacking significant documentation! That being said, any contributions and/or feedback is greatly appreciated (and feel free to always ask any questions on the gitter)!
|
||||
**Notice**: DeepForge is still a work in progress and is also lacking significant documentation! That being said, any contributions and/or feedback is greatly appreciated (and feel free to always ask any questions on the gitter)!
|
||||
|
||||
# DeepForge
|
||||
DeepForge is an open-source visual development environment for deep learning. Currently, it supports Convolutional Neural Networks but we are planning on supporting additional deep learning classifiers such as RNNs and LSTMs. Additional features include real-time collaborative editing and version control.
|
||||
@@ -20,8 +20,8 @@ Next, follow the postinstall instructions to start MongoDB and DeepForge!
|
||||
|
||||
Finally, navigate to [http://localhost:8888](http://localhost:8888) to start using DeepForge! For more, detailed instructions,check out our [wiki](https://github.com/dfst/deepforge/wiki/Installation-Guide).
|
||||
|
||||
## Caffe Support?
|
||||
DeepForge uses Torch to perform the actual training and testing of the models. If you are interested in DeepForge using Caffe for actual training and testing, check out [DeepForge-Caffe](https://github.com/dfst/deepforge-caffe).
|
||||
|
||||
## Interested in contributing?
|
||||
Contributions are welcome! Either fork the project and submit some PR's or shoot me an email about getting more involved!
|
||||
|
||||
Sponsored by [Digital Reasoning](http://www.digitalreasoning.com/)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
env:
|
||||
browser: true
|
||||
node: true
|
||||
mocha: true
|
||||
es6: true
|
||||
extends: 'eslint:recommended'
|
||||
rules:
|
||||
no-console:
|
||||
- 0
|
||||
indent:
|
||||
- 2
|
||||
- 4
|
||||
linebreak-style:
|
||||
- 2
|
||||
- unix
|
||||
quotes:
|
||||
- 2
|
||||
- single
|
||||
semi:
|
||||
- 2
|
||||
- always
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"torch": {
|
||||
"dir": "~/.deepforge/torch"
|
||||
},
|
||||
"mongo": {
|
||||
"dir": "~/.deepforge/data"
|
||||
}
|
||||
}
|
||||
Arquivo executável
+393
@@ -0,0 +1,393 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var Command = require('commander').Command,
|
||||
program = new Command(),
|
||||
childProcess = require('child_process'),
|
||||
spawn = childProcess.spawn,
|
||||
execSync = childProcess.execSync,
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
version = require('../package.json').version,
|
||||
exists = require('exists-file'),
|
||||
forever = require('forever-monitor'),
|
||||
DEFAULT_CONFIG = require('./config.json'),
|
||||
assign = require('lodash.assign'),
|
||||
config,
|
||||
|
||||
configDir = path.join(process.env.HOME, '.deepforge'),
|
||||
configPath = path.join(configDir, 'config.json'),
|
||||
dataPath = path.join(configDir, 'data'),
|
||||
|
||||
localConfig,
|
||||
p = dir => dir.replace(/^~/, process.env.HOME); // resolve '~' to '$HOME'
|
||||
|
||||
// Check for any commands
|
||||
if (process.argv.length === 2) {
|
||||
process.argv.push('--help');
|
||||
}
|
||||
|
||||
// Create the config if it doesn't exist
|
||||
if (!exists.sync(configDir)) {
|
||||
fs.mkdirSync(configDir);
|
||||
}
|
||||
if (!exists.sync(dataPath)) {
|
||||
fs.mkdirSync(dataPath);
|
||||
}
|
||||
if (!exists.sync(configPath)) {
|
||||
fs.writeFileSync(configPath, '{\n}');
|
||||
}
|
||||
|
||||
localConfig = require(configPath);
|
||||
config = assign(DEFAULT_CONFIG, require(configPath));
|
||||
|
||||
var getConfigValue = function(id) {
|
||||
var keys = id.split('.'),
|
||||
value = config;
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
value = value[keys[i]];
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
var storeConfig = function(id, value) {
|
||||
// load the config
|
||||
var keys = id.split('.').filter(k => k),
|
||||
lastKey = keys.pop(),
|
||||
currentObj = localConfig,
|
||||
current = getConfigValue(id);
|
||||
|
||||
// Check if it is a valid key
|
||||
if (current === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (!currentObj[keys[i]]) {
|
||||
currentObj[keys[i]] = {};
|
||||
}
|
||||
currentObj = currentObj[keys[i]];
|
||||
}
|
||||
|
||||
currentObj[lastKey] = value;
|
||||
fs.writeFileSync(configPath, JSON.stringify(localConfig, null, 2));
|
||||
return true;
|
||||
};
|
||||
|
||||
program
|
||||
.version('v' + version)
|
||||
.description('Command line interface for managing deepforge');
|
||||
|
||||
// start
|
||||
var start = function(main, opts) {
|
||||
var child = new forever.Monitor(main, opts);
|
||||
child.on('exit', function () {
|
||||
console.log('Exited after 3 failed restarts');
|
||||
});
|
||||
|
||||
child.start();
|
||||
};
|
||||
|
||||
var isLocalUri = function(protocol, uri) {
|
||||
return uri.indexOf(protocol + '://localhost') === 0 ||
|
||||
uri.indexOf(protocol + '://127.0.0.1') === 0;
|
||||
};
|
||||
|
||||
var checkMongo = function(args) {
|
||||
// check the webgme config
|
||||
var gmeConfig = require('../config'),
|
||||
mongoUri = gmeConfig.mongo.uri;
|
||||
|
||||
if (isLocalUri('mongodb', mongoUri)) {
|
||||
// Make sure mongo is running locally (using pgrep)
|
||||
try {
|
||||
execSync('pgrep mongod').toString();
|
||||
console.log('MongoDB is already running!');
|
||||
} catch (e) { // no pIds
|
||||
console.log('Starting MongoDB...');
|
||||
startMongo(args, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var startMongo = function(args, silent) {
|
||||
var job = spawn('mongod', ['--dbpath', p(config.mongo.dir)], {
|
||||
cwd: process.env.HOME
|
||||
});
|
||||
if (!silent) {
|
||||
job.stdout.on('data',
|
||||
data => process.stdout.write(data.toString()));
|
||||
}
|
||||
job.on('error', err => {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.log('Could not find MongoDB. Is it installed?');
|
||||
if (!args.mongo) {
|
||||
console.log('Otherwise, set MONGO_URI to the desired mongo uri and try again:');
|
||||
console.log('');
|
||||
console.log(' MONGO_URI=mongodb://some.other.ip:27017' +
|
||||
`/deepforge deepforge ${process.argv.slice(2).join(' ')}`);
|
||||
console.log('');
|
||||
}
|
||||
} else {
|
||||
console.log('Error encountered while starting MongoDB');
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
job.stderr.on('data', data => {
|
||||
var msg = 'mongodb: ' + data;
|
||||
process.stdout.write(msg);
|
||||
});
|
||||
job.on('exit', code => {
|
||||
if (code) {
|
||||
console.log('MongoDB closed w/ error code: ' + code);
|
||||
}
|
||||
});
|
||||
};
|
||||
var checkTorch = function() {
|
||||
return new Promise(_checkTorch)
|
||||
.catch(() => 'Torch installation failed');
|
||||
};
|
||||
|
||||
var _checkTorch = function(resolve, reject) {
|
||||
var result = childProcess.spawnSync('th', ['--help']),
|
||||
tgtDir = p(config.torch.dir),
|
||||
cmds;
|
||||
|
||||
if (result.error) {
|
||||
// Try to install torch
|
||||
console.log(`Torch7 not found. Installing to ${tgtDir}...`);
|
||||
|
||||
cmds = [
|
||||
`git clone https://github.com/torch/distro.git ${tgtDir} --recursive`,
|
||||
`cd ${tgtDir}`,
|
||||
'bash install-deps',
|
||||
'./install.sh'
|
||||
];
|
||||
|
||||
spawnMany(cmds,
|
||||
() => {
|
||||
storeConfig('torch.dir', tgtDir);
|
||||
resolve(true);
|
||||
},
|
||||
reject
|
||||
);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
};
|
||||
|
||||
var spawnMany = function(cmds, succ, err) {
|
||||
var rawCmd,
|
||||
cmd,
|
||||
args,
|
||||
job;
|
||||
|
||||
if (cmds.length === 0) {
|
||||
return succ();
|
||||
}
|
||||
|
||||
rawCmd = cmds.shift();
|
||||
args = rawCmd.split(' ');
|
||||
cmd = args.shift();
|
||||
job = spawn(cmd, args);
|
||||
job.stdout.on('data', data => process.stdout.write(data));
|
||||
job.stderr.on('data', data => process.stderr.write(data));
|
||||
job.on('close', code => {
|
||||
if (code) {
|
||||
console.log(`${rawCmd} failed w/ error code ${code}`);
|
||||
err(code, rawCmd);
|
||||
} else {
|
||||
spawnMany(cmds, succ, err);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
program.command('start')
|
||||
.description('start deepforge locally (default) or specific components')
|
||||
.option('-p, --port <port>', 'specify the port to use')
|
||||
.option('-s, --server', 'start the server')
|
||||
.option('-w, --worker [url]', 'start a worker and connect to given url. Defaults to local deepforge')
|
||||
.option('-m, --mongo', 'start MongoDB')
|
||||
.action(args => {
|
||||
var main = path.join(__dirname, 'start-local.js'),
|
||||
opts;
|
||||
|
||||
opts = {
|
||||
max: 3,
|
||||
args: []
|
||||
};
|
||||
|
||||
if (args.port) {
|
||||
opts.env = {
|
||||
PORT: args.port
|
||||
};
|
||||
}
|
||||
|
||||
if (args.server) {
|
||||
checkMongo(args);
|
||||
main = path.join(__dirname, '..', 'app.js');
|
||||
start(main, opts);
|
||||
}
|
||||
|
||||
if (args.worker) {
|
||||
checkTorch().then(() => {
|
||||
main = path.join(__dirname, 'start-worker.js');
|
||||
if (args.worker !== true) {
|
||||
opts.args.push(args.worker);
|
||||
}
|
||||
start(main, opts);
|
||||
});
|
||||
}
|
||||
|
||||
if (args.mongo) {
|
||||
startMongo(args);
|
||||
}
|
||||
|
||||
if (!args.server && !args.worker && !args.mongo) {
|
||||
// Starting everything
|
||||
checkMongo(args);
|
||||
checkTorch().then(() => start(main, opts));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// update
|
||||
program
|
||||
.command('update')
|
||||
.description('upgrade deepforge to latest version')
|
||||
.option('-g, --git', 'update tracking the git repo')
|
||||
.option('-t, --torch', 'update torch installation')
|
||||
.option('-s, --server', 'update deepforge')
|
||||
.action(args => {
|
||||
var pkg = 'deepforge',
|
||||
job,
|
||||
latestVersion;
|
||||
|
||||
// Install the project
|
||||
if (!args.torch || args.server) {
|
||||
|
||||
if (args.git) {
|
||||
pkg = 'dfst/deepforge';
|
||||
} else {
|
||||
// Check the version
|
||||
try {
|
||||
latestVersion = execSync('npm show deepforge version')
|
||||
.toString().replace(/\s+$/, '');
|
||||
|
||||
if (latestVersion === version) {
|
||||
console.log('Already up-to-date');
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Could not retrieve the latest deepforge version');
|
||||
}
|
||||
}
|
||||
|
||||
job = spawn('npm', ['install', '-g', pkg]);
|
||||
job.stdout.on('data', data => process.stdout.write(data.toString()));
|
||||
job.stderr.on('data', data => process.stderr.write(data.toString()));
|
||||
job.on('close', code => {
|
||||
if (!code) {
|
||||
console.log('Upgrade successful!');
|
||||
} else {
|
||||
console.log('Upgrade failed w/ error code: ' + code);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (args.torch || !args.server) {
|
||||
// Update torch
|
||||
checkTorch().then(justInstalled => {
|
||||
if (!justInstalled) {
|
||||
// Upgrade torch
|
||||
console.log('Upgrading torch...');
|
||||
job = spawn('bash', ['./update.sh'], {
|
||||
cwd: p(config.torch.dir)
|
||||
});
|
||||
job.stdout.on('data', data => process.stdout.write(data.toString()));
|
||||
job.stderr.on('data', data => process.stderr.write(data.toString()));
|
||||
job.on('close', code => {
|
||||
if (!code) {
|
||||
console.log('Upgrade successful!');
|
||||
} else {
|
||||
console.log('Upgrade failed w/ error code: ' + code);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// uninstall command
|
||||
program
|
||||
.command('uninstall')
|
||||
.description('uninstall deepforge from the system')
|
||||
.option('-t, --torch', 'uninstall torch')
|
||||
.option('-c, --clean', 'uninstall deepforge, torch and all associated data/config')
|
||||
.action(opts => {
|
||||
if (opts.torch || opts.clean) {
|
||||
if (opts.torch) {
|
||||
console.log(`uninstalling torch at ${p(config.torch.dir)}`);
|
||||
}
|
||||
fs.unlinkSync(p(config.torch.dir));
|
||||
}
|
||||
|
||||
if (opts.clean) { // remove the .deepforge directory
|
||||
console.log('removing config and data files...');
|
||||
fs.unlinkSync(p(config.mongo.dir));
|
||||
fs.unlinkSync(p(configDir));
|
||||
}
|
||||
|
||||
if (!opts.torch || opts.clean) { // uninstall deepforge
|
||||
spawnMany(
|
||||
['npm uninstall -g deepforge'],
|
||||
() => console.log('deepforge has been uninstalled!'),
|
||||
() => console.log('uninstall failed')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// config
|
||||
program
|
||||
.command('config [key] [value]')
|
||||
.description('read or edit config options (omit "value" to see current value)')
|
||||
.action(key => {
|
||||
var value = program.args[1],
|
||||
success;
|
||||
|
||||
if (value) { // write a value
|
||||
success = storeConfig(key, value);
|
||||
if (success) {
|
||||
console.log('Config has been updated!');
|
||||
}
|
||||
} else if (key) { // read a single value
|
||||
value = getConfigValue(key);
|
||||
if (value === null) {
|
||||
console.log(`Invalid config value: "${key}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
value = JSON.stringify(value, null, 2);
|
||||
}
|
||||
|
||||
console.log(value);
|
||||
} else { // print entire config
|
||||
console.log(`Current config:\n${JSON.stringify(config, null, 2)}`);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = function(cmd) {
|
||||
var cmds = cmd.split(/\s+/).filter(w => !!w);
|
||||
cmds.unshift('node');
|
||||
cmds.unshift('./bin/deepforge');
|
||||
program.parse(cmds);
|
||||
};
|
||||
|
||||
if (require.main === module) {
|
||||
program.parse(process.argv);
|
||||
}
|
||||
+16
-27
@@ -1,8 +1,19 @@
|
||||
{
|
||||
"AutoViz": {
|
||||
"preloadIds": [
|
||||
"ArchEditor",
|
||||
"PipelineEditor",
|
||||
"OperationEditor",
|
||||
"ExecutionView"
|
||||
]
|
||||
},
|
||||
"ArchEditor": {
|
||||
"hotkeys": "none",
|
||||
"LayerColors": {}
|
||||
},
|
||||
"BreadcrumbHeader": {
|
||||
"pathRule": "history"
|
||||
},
|
||||
"FloatingActionButton": {
|
||||
"hideOnEmpty": true
|
||||
},
|
||||
@@ -43,48 +54,26 @@
|
||||
"nodes": [
|
||||
{
|
||||
"nodeName": "MyArchitectures",
|
||||
"title": "Architectures",
|
||||
"icon": "shuffle",
|
||||
"rank": 1,
|
||||
"description": "Neural network architectures are stored here and can be used in pipelines."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyOperations",
|
||||
"icon": "mode_edit",
|
||||
"rank": 2,
|
||||
"color": "blue-grey",
|
||||
"description": "Operations are the building blocks of pipelines. Custom operations can be created and stored here."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyPipelines",
|
||||
"title": "Pipelines",
|
||||
"color": "blue-grey",
|
||||
"icon": "input",
|
||||
"rank": 3,
|
||||
"description": "Pipelines compose operations together to effectively train, test and/or ensemble models."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyLayers",
|
||||
"icon": "clear_all",
|
||||
"rank": 4,
|
||||
"color": "blue-grey",
|
||||
"description": "Custom torch layers can be created and stored here for use in neural network architectures."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyArtifacts",
|
||||
"title": "Artifacts",
|
||||
"icon": "view_quilt",
|
||||
"color": "blue-grey",
|
||||
"rank": 5,
|
||||
"description": "Artifacts from pipeline executions are stored here."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyExecutions",
|
||||
"icon": "list",
|
||||
"rank": 6,
|
||||
"color": "blue-grey",
|
||||
"description": "Executions are read-only snapshots of pipelines that have been executed. Past and current executing pipelines are stored here."
|
||||
},
|
||||
{
|
||||
"nodeName": "MyDataTypes",
|
||||
"icon": "settings",
|
||||
"rank": 7,
|
||||
"description": "Custom defined data types are stored here."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ var config = require('./config.webgme'),
|
||||
require('dotenv').load({silent: true});
|
||||
|
||||
// Add/overwrite any additional settings here
|
||||
config.server.port = process.env.PORT || config.server.port;
|
||||
config.server.port = +process.env.PORT || config.server.port;
|
||||
config.mongo.uri = process.env.MONGO_URI || config.mongo.uri;
|
||||
config.requirejsPaths.deepforge = './src/common';
|
||||
config.seedProjects.defaultProject = 'project';
|
||||
@@ -16,6 +16,7 @@ config.plugin.allowBrowserExecution = true;
|
||||
config.plugin.allowServerExecution = true;
|
||||
|
||||
config.executor.enable = true;
|
||||
config.executor.clearOldDataAtStartUp = true;
|
||||
|
||||
config.visualization.extraCss.push('deepforge/styles/global.css');
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ config.seedProjects.basePaths.push('src/seeds/devTests');
|
||||
config.seedProjects.basePaths.push('src/seeds/devUtilTests');
|
||||
config.seedProjects.basePaths.push('src/seeds/pipeline');
|
||||
config.seedProjects.basePaths.push('src/seeds/devPipelineTests');
|
||||
config.seedProjects.basePaths.push('src/seeds/demo');
|
||||
config.seedProjects.basePaths.push('src/seeds/project');
|
||||
config.seedProjects.basePaths.push('src/seeds/cifar10');
|
||||
|
||||
|
||||
+42
-2
@@ -53,6 +53,7 @@ command -v node >/dev/null 2>&1 || {
|
||||
echo >&2 "NodeJS is not found. Installing (using nvm)...";
|
||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash;
|
||||
source $DETECTED_PROFILE
|
||||
. $NVM_DIR/nvm.sh
|
||||
|
||||
# Install nodejs v6.2.0
|
||||
echo "Installing nodejs v6.2.0"
|
||||
@@ -61,7 +62,6 @@ command -v node >/dev/null 2>&1 || {
|
||||
|
||||
# Install npm@2
|
||||
npm install npm@2 -g
|
||||
|
||||
}
|
||||
|
||||
command -v node >/dev/null 2>&1 || {
|
||||
@@ -69,6 +69,35 @@ command -v node >/dev/null 2>&1 || {
|
||||
echo >&2 "MongoDB is not found. Installing...";
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
brew install mongodb
|
||||
elif [[ "$(uname)" == 'Linux' ]]; then
|
||||
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
# this will get the required information without dirtying any env state
|
||||
DIST_VERS="$( ( . /etc/os-release &>/dev/null
|
||||
echo "$ID $VERSION_ID") )"
|
||||
DISTRO="${DIST_VERS%% *}" # get our distro name
|
||||
VERSION="${DIST_VERS##* }" # get our version number
|
||||
elif [[ -r /etc/lsb-release ]]; then
|
||||
DIST_VERS="$( ( . /etc/lsb-release &>/dev/null
|
||||
echo "${DISTRIB_ID,,} $DISTRIB_RELEASE") )"
|
||||
DISTRO="${DIST_VERS%% *}" # get our distro name
|
||||
VERSION="${DIST_VERS##* }" # get our version number
|
||||
else # well, I'm out of ideas for now
|
||||
echo '==> Failed to determine distro and version.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect archlinux
|
||||
if [[ "$DISTRO" = "arch" ]]; then
|
||||
distribution="archlinux"
|
||||
sudo pacman -S mongodb
|
||||
# Detect Ubuntu
|
||||
elif [[ "$DISTRO" = "ubuntu" ]]; then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get install mongodb
|
||||
else
|
||||
NEEDS_MONGO=true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -80,7 +109,18 @@ cd ~/deepforge
|
||||
npm install
|
||||
|
||||
mkdir ~/deepforge/data 2> /dev/null
|
||||
echo "DeepForge is installed! To run it:"
|
||||
|
||||
echo "Final Installation steps:"
|
||||
echo " 1) Close and re-open your terminal"
|
||||
echo " (or run \"source $DETECTED_PROFILE\")"
|
||||
|
||||
if [[ $NEEDS_MONGO ]]; then
|
||||
echo " 2) Install MongoDB for your OS"
|
||||
echo " (available at https://www.mongodb.com/download-center)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Then run DeepForge!"
|
||||
echo " 1) make sure MongoDB is running locally"
|
||||
echo " (start mongo w/ \"mongod --dbpath ~/deepforge/data\")"
|
||||
echo " 2) Run \"npm run local\" from ~/deepforge"
|
||||
|
||||
+13
-5
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"name": "deepforge",
|
||||
"bin": {
|
||||
"deepforge": "./bin/deepforge"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
"start-dev": "NODE_ENV=dev node app.js",
|
||||
@@ -9,23 +12,28 @@
|
||||
"watch-test": "./node_modules/nodemon/bin/nodemon.js --exec 'node ./node_modules/mocha/bin/mocha --recursive test'",
|
||||
"build-nn": "node ./utils/nn-parser.js"
|
||||
},
|
||||
"version": "0.7.0",
|
||||
"version": "0.10.0",
|
||||
"dependencies": {
|
||||
"commander": "^2.9.0",
|
||||
"dotenv": "^2.0.0",
|
||||
"exists-file": "^2.1.0",
|
||||
"forever-monitor": "^1.7.0",
|
||||
"lodash.assign": "^4.0.9",
|
||||
"lodash.difference": "^4.1.2",
|
||||
"nodemon": "^1.9.2",
|
||||
"webgme": "^2.0.0",
|
||||
"webgme-autoviz": "^2.0.3",
|
||||
"webgme-breadcrumbheader": "^2.0.0",
|
||||
"webgme-autoviz": "dfst/webgme-autoviz",
|
||||
"webgme-breadcrumbheader": "^2.1.0",
|
||||
"webgme-chflayout": "^2.0.0",
|
||||
"webgme-easydag": "dfst/webgme-easydag",
|
||||
"webgme-fab": "dfst/webgme-fab",
|
||||
"webgme-simple-nodes": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^3.0.0",
|
||||
"jszip": "^2.5.0",
|
||||
"mocha": "^2.2.5",
|
||||
"rimraf": "^2.4.0",
|
||||
"chai": "^3.0.0"
|
||||
"mockery": "^1.7.0",
|
||||
"rimraf": "^2.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/* globals define */
|
||||
define({
|
||||
LINE_OFFSET: 'lineOffset',
|
||||
|
||||
// DeepForge metadata creation in dist execution
|
||||
START_CMD: 'deepforge-cmd',
|
||||
GRAPH_CREATE: 'GRAPH',
|
||||
GRAPH_PLOT: 'PLOT',
|
||||
GRAPH_CREATE_LINE: 'LINE'
|
||||
});
|
||||
+97
-5
@@ -1,4 +1,4 @@
|
||||
/* globals WebGMEGlobal, define*/
|
||||
/* globals Materialize, WebGMEGlobal, define*/
|
||||
// This file creates the DeepForge namespace and defines basic actions
|
||||
define([
|
||||
'js/RegistryKeys',
|
||||
@@ -89,13 +89,14 @@ define([
|
||||
return name;
|
||||
};
|
||||
|
||||
/////////// Initializing DeepForge ///////////
|
||||
//////////////////// DeepForge places detection ////////////////////
|
||||
var TYPE_TO_CONTAINER = {
|
||||
|
||||
Architecture: 'MyArchitectures',
|
||||
Pipeline: 'MyPipelines',
|
||||
Execution: 'MyExecutions',
|
||||
Layer: 'MyLayers',
|
||||
Artifact: 'MyArtifacts',
|
||||
Operation: 'MyOperations',
|
||||
Primitive: 'MyDataTypes',
|
||||
Complex: 'MyDataTypes'
|
||||
@@ -141,8 +142,7 @@ define([
|
||||
placesTerritoryId = null;
|
||||
};
|
||||
|
||||
// Add DeepForge action primitives
|
||||
// Creating custom operations
|
||||
//////////////////// DeepForge creation actions ////////////////////
|
||||
var instances = [
|
||||
'Architecture',
|
||||
'Pipeline'
|
||||
@@ -180,6 +180,92 @@ define([
|
||||
return newId;
|
||||
};
|
||||
|
||||
var createCustomLayer = function(typeName) {
|
||||
var metanodes = client.getAllMetaNodes(),
|
||||
msg = `Created new custom ${typeName} layer`,
|
||||
newId,
|
||||
customLayerId,
|
||||
baseId,
|
||||
name,
|
||||
i = metanodes.length;
|
||||
|
||||
while (i-- && !(baseId && customLayerId)) {
|
||||
name = metanodes[i].getAttribute('name');
|
||||
if (name === 'CustomLayer') {
|
||||
customLayerId = metanodes[i].getId();
|
||||
} else if (name === typeName) {
|
||||
baseId = metanodes[i].getId();
|
||||
}
|
||||
}
|
||||
|
||||
client.startTransaction(msg);
|
||||
|
||||
newId = createNamedNode(baseId, DeepForge.places.MyLayers, true);
|
||||
addToMetaSheet(newId, 'CustomLayers');
|
||||
client.addMixin(newId, customLayerId);
|
||||
client.setRegistry(newId, REGISTRY_KEYS.IS_ABSTRACT, false);
|
||||
|
||||
client.completeTransaction();
|
||||
|
||||
WebGMEGlobal.State.registerActiveObject(newId);
|
||||
};
|
||||
|
||||
// Creating Artifacts
|
||||
var UPLOAD_PLUGIN = 'ImportArtifact',
|
||||
DATA_TYPE_CONFIG = {
|
||||
name: 'dataTypeId',
|
||||
displayName: 'Data Type Id',
|
||||
valueType: 'string',
|
||||
valueItems: []
|
||||
};
|
||||
|
||||
var uploadArtifact = function() {
|
||||
// Get the data types
|
||||
var dataBase,
|
||||
dataBaseId,
|
||||
metanodes = client.getAllMetaNodes(),
|
||||
dataTypes = [];
|
||||
|
||||
dataBase = metanodes.find(n => n.getAttribute('name') === 'Data');
|
||||
|
||||
if (!dataBase) {
|
||||
this.logger.error('Could not find the base Data node!');
|
||||
return;
|
||||
}
|
||||
|
||||
dataBaseId = dataBase.getId();
|
||||
dataTypes = metanodes.filter(n => client.isTypeOf(n.getId(), dataBaseId))
|
||||
.filter(n => !n.getRegistry('isAbstract'))
|
||||
.map(node => node.getAttribute('name'));
|
||||
|
||||
//this.logger.info(`Found ${dataTypes.length} data types`);
|
||||
|
||||
// Add the target type to the pluginMetadata... hacky :/
|
||||
var metadata = WebGMEGlobal.allPluginsMetadata[UPLOAD_PLUGIN],
|
||||
config = metadata.configStructure
|
||||
.find(opt => opt.name === DATA_TYPE_CONFIG.name);
|
||||
|
||||
if (!config) {
|
||||
config = DATA_TYPE_CONFIG;
|
||||
WebGMEGlobal.allPluginsMetadata[UPLOAD_PLUGIN].configStructure.push(config);
|
||||
}
|
||||
|
||||
config.valueItems = dataTypes;
|
||||
config.value = dataTypes[0];
|
||||
|
||||
WebGMEGlobal.InterpreterManager.configureAndRun(metadata, (result) => {
|
||||
var msg = 'Artifact upload complete!';
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
if (!result.success) {
|
||||
msg = `Artifact upload failed: ${result.error}`;
|
||||
}
|
||||
Materialize.toast(msg, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
DeepForge.last = {};
|
||||
DeepForge.create = {};
|
||||
instances.forEach(type => {
|
||||
DeepForge.create[type] = function() {
|
||||
@@ -193,10 +279,16 @@ define([
|
||||
};
|
||||
});
|
||||
|
||||
DeepForge.create.Layer = createCustomLayer;
|
||||
DeepForge.create.Artifact = uploadArtifact;
|
||||
|
||||
//////////////////// DeepForge prev locations ////////////////////
|
||||
// Update DeepForge on project changed
|
||||
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_PROJECT_NAME, updateDeepForgeNamespace, null);
|
||||
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_PROJECT_NAME,
|
||||
updateDeepForgeNamespace, null);
|
||||
|
||||
// define DeepForge globally
|
||||
window.DeepForge = DeepForge;
|
||||
|
||||
return DeepForge;
|
||||
});
|
||||
|
||||
@@ -2687,6 +2687,7 @@ function LuaContext(){
|
||||
}
|
||||
exports.stdlib(_G, helpers)();
|
||||
}
|
||||
this.__helpers = helpers;
|
||||
}
|
||||
|
||||
LuaContext.prototype = {}
|
||||
|
||||
@@ -18,3 +18,7 @@
|
||||
.deepforge-logo .item-label {
|
||||
font-family: 'Audiowide', cursive;
|
||||
}
|
||||
|
||||
.create-node text {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
.new-node-decorator rect {
|
||||
fill: #78909C;
|
||||
/*fill: #90caf9;*/
|
||||
}
|
||||
|
||||
.new-node-decorator circle {
|
||||
fill: #81c784;
|
||||
/*fill: #90caf9;*/
|
||||
}
|
||||
|
||||
.new-node-decorator line {
|
||||
stroke: white;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.new-node-decorator .dark line {
|
||||
stroke: black;
|
||||
stroke-width: 2.5px;
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/* globals define */
|
||||
// This contains decorators for actions such as 'New Operation' so
|
||||
// the given action can be used as a node in NodePrompter, etc
|
||||
define([
|
||||
'css!./AddDecorator.css'
|
||||
], function(
|
||||
) {
|
||||
|
||||
var NewDecorator = function (opts) {
|
||||
this.$el = opts.parentEl.append('g')
|
||||
.attr('class', 'centering-offset');
|
||||
|
||||
this.$body = this.$el.append('g')
|
||||
.attr('class', 'new-node-decorator');
|
||||
|
||||
this.radius = opts.radius || 20;
|
||||
this.height = this.radius*2;
|
||||
this.width = opts.width || 90;
|
||||
this.size = this.radius * 1.00;
|
||||
|
||||
if (opts.circle) {
|
||||
this.render = this.renderCircle;
|
||||
} else {
|
||||
this.render = this.renderRect;
|
||||
}
|
||||
};
|
||||
|
||||
NewDecorator.prototype.renderRect = function() {
|
||||
this.$body.remove();
|
||||
this.$body = this.$el.append('g')
|
||||
.attr('class', 'new-node-decorator');
|
||||
|
||||
this.$body.append('rect')
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('width', this.width)
|
||||
.attr('height', this.height);
|
||||
|
||||
this.renderPlus()
|
||||
.attr('class', 'dark')
|
||||
.attr('transform', `translate(${this.width/2-this.size}, 0)`);
|
||||
|
||||
};
|
||||
|
||||
NewDecorator.prototype.renderCircle = function() {
|
||||
this.$body.remove();
|
||||
this.$body = this.$el.append('g')
|
||||
.attr('class', 'new-node-decorator');
|
||||
|
||||
this.$body.append('circle')
|
||||
.attr('cx', this.radius)
|
||||
.attr('cy', this.radius)
|
||||
.attr('r', this.radius);
|
||||
|
||||
this.renderPlus();
|
||||
this.$el.attr('transform', `translate(${this.width/2-this.size}, ${this.height/2-this.size})`);
|
||||
};
|
||||
|
||||
NewDecorator.prototype.renderPlus = function() {
|
||||
// Create a large '+' symbol in a rectangle
|
||||
var start = this.radius-this.size/2,
|
||||
end = start + this.size,
|
||||
middle = (start+end)/2,
|
||||
plus = this.$body.append('g');
|
||||
|
||||
plus.append('line')
|
||||
.attr('x1', start)
|
||||
.attr('x2', end)
|
||||
.attr('y1', middle)
|
||||
.attr('y2', middle)
|
||||
.attr('stroke', 'black');
|
||||
|
||||
plus.append('line')
|
||||
.attr('x1', middle)
|
||||
.attr('x2', middle)
|
||||
.attr('y1', start)
|
||||
.attr('y2', end)
|
||||
.attr('stroke', 'black');
|
||||
|
||||
return plus;
|
||||
};
|
||||
|
||||
return NewDecorator;
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
define([
|
||||
'widgets/EasyDAG/Buttons',
|
||||
'widgets/EasyDAG/Icons'
|
||||
], function(
|
||||
EasyDAGButtons,
|
||||
Icons
|
||||
) {
|
||||
|
||||
// Create a GoToBase button
|
||||
var client = WebGMEGlobal.Client;
|
||||
|
||||
var GoToBase = function(params) {
|
||||
// Check if it should be disabled
|
||||
var baseId = this._getBaseId(params.item),
|
||||
base = baseId && client.getNode(baseId);
|
||||
|
||||
if (!params.disabled) {
|
||||
params.disabled = base ? base.isLibraryElement() : true;
|
||||
}
|
||||
EasyDAGButtons.ButtonBase.call(this, params);
|
||||
};
|
||||
|
||||
GoToBase.SIZE = 10;
|
||||
GoToBase.BORDER = 1;
|
||||
GoToBase.prototype.BTN_CLASS = 'go-to-base';
|
||||
GoToBase.prototype = new EasyDAGButtons.ButtonBase();
|
||||
|
||||
GoToBase.prototype._render = function() {
|
||||
var lineRadius = GoToBase.SIZE - GoToBase.BORDER,
|
||||
btnColor = '#90caf9';
|
||||
|
||||
if (this.disabled) {
|
||||
btnColor = '#e0e0e0';
|
||||
}
|
||||
|
||||
this.$el
|
||||
.append('circle')
|
||||
.attr('r', GoToBase.SIZE)
|
||||
.attr('fill', btnColor);
|
||||
|
||||
// Show the 'code' icon
|
||||
Icons.addIcon('code', this.$el, {
|
||||
radius: lineRadius
|
||||
});
|
||||
};
|
||||
|
||||
GoToBase.prototype._onClick = function(item) {
|
||||
var node = client.getNode(item.id),
|
||||
baseId = node.getBaseId();
|
||||
|
||||
WebGMEGlobal.State.registerActiveObject(baseId);
|
||||
};
|
||||
|
||||
GoToBase.prototype._getBaseId = function(item) {
|
||||
var n = client.getNode(item.id);
|
||||
return n && n.getBaseId();
|
||||
};
|
||||
|
||||
return {
|
||||
DeleteOne: EasyDAGButtons.DeleteOne,
|
||||
GoToBase: GoToBase
|
||||
};
|
||||
});
|
||||
|
||||
@@ -7,9 +7,9 @@ define([
|
||||
var OperationControl = function() {
|
||||
};
|
||||
|
||||
OperationControl.prototype.hasMetaName = function(id, name) {
|
||||
OperationControl.prototype.hasMetaName = function(id, name, inclusive) {
|
||||
var node = this._client.getNode(id),
|
||||
bId = node.getBaseId(),
|
||||
bId = inclusive ? id : node.getBaseId(),
|
||||
baseName;
|
||||
|
||||
while (bId) {
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
define([
|
||||
'panels/EasyDAG/EasyDAGControl',
|
||||
'deepforge/viz/OperationControl',
|
||||
'deepforge/Constants',
|
||||
'widgets/EasyDAG/AddNodeDialog',
|
||||
'underscore'
|
||||
], function(
|
||||
EasyDAGControl,
|
||||
OperationControl,
|
||||
CONSTANTS,
|
||||
AddNodeDialog,
|
||||
_
|
||||
) {
|
||||
@@ -72,6 +74,8 @@ define([
|
||||
var desc = EasyDAGControl.prototype._getObjectDescriptor.call(this, id),
|
||||
node = this._client.getNode(id);
|
||||
|
||||
desc.inputs = [];
|
||||
desc.outputs = [];
|
||||
if (this.hasMetaName(id, 'Operation')) {
|
||||
// Only decorate operations in the currently active node
|
||||
if (this._currentNodeId !== desc.parentId) {
|
||||
@@ -95,6 +99,7 @@ define([
|
||||
|
||||
// Remove the 'code' attribute
|
||||
if (desc.attributes.code) {
|
||||
delete desc.attributes[CONSTANTS.LINE_OFFSET];
|
||||
delete desc.attributes.code;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*globals define*/
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'underscore'
|
||||
], function(
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
_
|
||||
) {
|
||||
|
||||
var RenameablePanel = function() {
|
||||
PanelBaseWithHeader.apply(this, arguments);
|
||||
};
|
||||
|
||||
_.extend(
|
||||
RenameablePanel.prototype,
|
||||
PanelBaseWithHeader.prototype,
|
||||
IActivePanel.prototype
|
||||
);
|
||||
|
||||
RenameablePanel.OPTIONS = PanelBaseWithHeader.OPTIONS;
|
||||
RenameablePanel.prototype.initializeRenameable = function () {
|
||||
this.$panelHeaderTitle.on('dblclick', this.editTitle.bind(this));
|
||||
};
|
||||
|
||||
RenameablePanel.prototype.currentNodeId = function () {
|
||||
return this.control._currentNodeId;
|
||||
};
|
||||
|
||||
RenameablePanel.prototype.currentBaseName = function () {
|
||||
var currentId = this.currentNodeId(),
|
||||
node = this._client.getNode(currentId),
|
||||
baseId = node.getBaseId(),
|
||||
base = this._client.getNode(baseId);
|
||||
|
||||
return base.getAttribute('name');
|
||||
};
|
||||
|
||||
RenameablePanel.prototype.editTitle = function () {
|
||||
this.$panelHeaderTitle.editInPlace({
|
||||
css: {
|
||||
'z-index': 1000
|
||||
},
|
||||
onChange: (oldValue, newValue) => {
|
||||
var nodeId = this.currentNodeId(),
|
||||
type = this.currentBaseName(),
|
||||
msg = `Renamed ${type}: ${oldValue} -> ${newValue}`;
|
||||
|
||||
if (!/^\s*$/.test(newValue)) {
|
||||
this._client.startTransaction(msg);
|
||||
this._client.setAttributes(nodeId, 'name', newValue);
|
||||
this._client.completeTransaction();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return RenameablePanel;
|
||||
});
|
||||
@@ -53,7 +53,17 @@ define([
|
||||
ArtifactOpDecorator.prototype.savePointer = function(name, to) {
|
||||
// When the 'artifact' pointer is changed, we should change the base
|
||||
// of the data output node to the target type
|
||||
if (name === this.castOpts.ptr && (typeof to === 'string')) {
|
||||
if (typeof to !== 'string') {
|
||||
var outputId = this._node.outputs[0] && this._node.outputs[0].id;
|
||||
|
||||
// Clear the data handle of the output
|
||||
this.client.startTransaction(`Removing output of ${this.name}`);
|
||||
this.client.delPointer(this._node.id, name);
|
||||
if (outputId) {
|
||||
this.client.delAttributes(outputId, 'data');
|
||||
}
|
||||
this.client.completeTransaction();
|
||||
} else if (name === this.castOpts.ptr) { // set the casted value
|
||||
this.client.startTransaction(`Setting output of ${this.name} to ${to}`);
|
||||
this.castOutputType(to);
|
||||
this.client.makePointer(this._node.id, name, to);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/* globals define, _*/
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/AttributeField'
|
||||
], function(
|
||||
AttributeFieldBase
|
||||
) {
|
||||
// Attribute field in which the label is clickable and the attribute meta is editable
|
||||
var AttributeField = function() {
|
||||
AttributeFieldBase.apply(this, arguments);
|
||||
this.$label.on('click', () => this.onLabelClick());
|
||||
};
|
||||
|
||||
_.extend(AttributeField.prototype, AttributeFieldBase.prototype);
|
||||
|
||||
return AttributeField;
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
/* globals define */
|
||||
define([
|
||||
], function(
|
||||
) {
|
||||
|
||||
var CreateAttrField = function(logger, pEl, y) {
|
||||
this.$el = pEl.append('text')
|
||||
.attr('y', y)
|
||||
.attr('class', 'create-attr-field')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('font-weight', 'bold')
|
||||
.attr('font-style', 'italic')
|
||||
.text('New Attribute')
|
||||
.on('click', () => this.onClick());
|
||||
};
|
||||
|
||||
CreateAttrField.prototype.render =
|
||||
CreateAttrField.prototype.destroy = function() {};
|
||||
|
||||
CreateAttrField.prototype.width = function() {
|
||||
return this.$el[0][0].getBoundingClientRect().width;
|
||||
};
|
||||
|
||||
return CreateAttrField;
|
||||
});
|
||||
@@ -1,15 +1,17 @@
|
||||
/*globals define, $,_*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
|
||||
'./AttributeField',
|
||||
'./CreateAttributeField',
|
||||
'decorators/MetaDecorator/DiagramDesigner/AttributeDetailsDialog',
|
||||
'css!./OpIntDecorator.EasyDAGWidget.css'
|
||||
], function (
|
||||
DecoratorBase
|
||||
DecoratorBase,
|
||||
AttributeField,
|
||||
CreateAttributeField,
|
||||
AttributeDetailsDialog
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
@@ -17,13 +19,7 @@ define([
|
||||
var OpIntDecorator,
|
||||
DECORATOR_ID = 'OpIntDecorator';
|
||||
|
||||
// OpInt nodes need to be able to...
|
||||
// - show their ports
|
||||
// - highlight ports
|
||||
// - unhighlight ports
|
||||
// - report the location of specific ports
|
||||
OpIntDecorator = function (options) {
|
||||
this.color = this.color || '#78909c';
|
||||
DecoratorBase.call(this, options);
|
||||
};
|
||||
|
||||
@@ -31,9 +27,9 @@ define([
|
||||
|
||||
OpIntDecorator.prototype.DECORATOR_ID = DECORATOR_ID;
|
||||
OpIntDecorator.prototype.initialize = function() {
|
||||
if (this._node.baseName === 'Operation') {
|
||||
if (this.isOperation()) {
|
||||
this.color = '#2196f3';
|
||||
} else {
|
||||
} else if (this._node.baseName) {
|
||||
// On hover, show the type
|
||||
this.enableTooltip(this._node.baseName, 'dark');
|
||||
}
|
||||
@@ -41,6 +37,109 @@ define([
|
||||
this.$name.on('dblclick', this.editName.bind(this));
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.AttributeField = AttributeField;
|
||||
OpIntDecorator.prototype.isOperation = function() {
|
||||
return this._node.baseName === 'Operation';
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.createAttributeFields = function(y, width) {
|
||||
var field,
|
||||
initialY = y;
|
||||
|
||||
if (!this.isOperation()) {
|
||||
return y;
|
||||
}
|
||||
|
||||
y = DecoratorBase.prototype.createAttributeFields.call(this, y, width);
|
||||
// Change attribute field so clicking allows user to edit/delete the field
|
||||
this.fields.forEach(field =>
|
||||
field.onLabelClick = this.editAttributeMeta.bind(this, field.name));
|
||||
|
||||
// Add the 'create new attribute' field
|
||||
y += this.ROW_HEIGHT + (y === initialY ? 0 : 10);
|
||||
field = new CreateAttributeField(this.logger, this.$attributes, y, width);
|
||||
field.onClick = this.newAttribute.bind(this);
|
||||
this.fields.push(field);
|
||||
return y;
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.newAttribute = function() {
|
||||
var defSchema = {
|
||||
type: 'string'
|
||||
};
|
||||
|
||||
this.editAttributeMeta(null, defSchema);
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.expand = function() {
|
||||
DecoratorBase.prototype.expand.call(this, this.isOperation());
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.editAttributeMeta = function(name, defSchema) {
|
||||
var dialog = new AttributeDetailsDialog(),
|
||||
node = this.client.getNode(this._node.id),
|
||||
attrNames = node.getValidAttributeNames(),
|
||||
attrInfo = this._node.attributes[name] || defSchema,
|
||||
schema,
|
||||
i;
|
||||
|
||||
// Open the dialog for editing the attribute
|
||||
schema = _.extend({defaultValue: attrInfo.value}, attrInfo);
|
||||
|
||||
// Remove the current name
|
||||
i = attrNames.indexOf(name);
|
||||
if (i !== -1) {
|
||||
attrNames.splice(i, 1);
|
||||
}
|
||||
|
||||
dialog.show(schema, attrNames,
|
||||
desc => this.setAttributeMeta(name, desc),
|
||||
() => this.deleteAttribute(name));
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.deleteAttribute = function(name) {
|
||||
var opName = this._node.attributes.name.value,
|
||||
msg = `Deleting "${name}" attribute from "${opName}" operation`;
|
||||
|
||||
this.client.startTransaction(msg);
|
||||
this.client.removeAttributeSchema(this._node.id, name);
|
||||
this.client.delAttributes(this._node.id, name);
|
||||
this.client.completeTransaction();
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.setAttributeMeta = function(name, desc) {
|
||||
var schema,
|
||||
opName = this._node.attributes.name.value,
|
||||
msg = `Updating "${name}" attribute in "${opName}" operation`;
|
||||
|
||||
// Create the schema from the desc
|
||||
schema = {
|
||||
type: desc.type,
|
||||
min: desc.min,
|
||||
max: desc.max,
|
||||
regexp: desc.regexp
|
||||
};
|
||||
|
||||
if (desc.isEnum) {
|
||||
schema.enum = desc.enumValues;
|
||||
}
|
||||
|
||||
// Update the operation's attribute
|
||||
this.client.startTransaction(msg);
|
||||
|
||||
if (name !== desc.name) { // Renaming attribute
|
||||
if (name) {
|
||||
this.client.removeAttributeSchema(this._node.id, name);
|
||||
this.client.delAttributes(this._node.id, name);
|
||||
}
|
||||
name = desc.name;
|
||||
}
|
||||
|
||||
this.client.setAttributeSchema(this._node.id, name, schema);
|
||||
this.client.setAttributes(this._node.id, name, desc.defaultValue);
|
||||
this.client.completeTransaction();
|
||||
};
|
||||
|
||||
OpIntDecorator.prototype.editName = function() {
|
||||
var html = this.$name[0][0],
|
||||
position = html.getBoundingClientRect(),
|
||||
@@ -90,8 +189,5 @@ define([
|
||||
return this._node.name;
|
||||
};
|
||||
|
||||
// clicking on the name should allow the user to edit it in place
|
||||
// TODO
|
||||
|
||||
return OpIntDecorator;
|
||||
});
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
/*globals define, _, Opentip*/
|
||||
/*jshint browser: true, camelcase: false*/
|
||||
|
||||
/**
|
||||
* @author brollb / https://github.com/brollb
|
||||
*/
|
||||
|
||||
define([
|
||||
'decorators/EllipseDecorator/EasyDAG/EllipseDecorator.EasyDAGWidget',
|
||||
'css!./OperationDecorator.EasyDAGWidget.css'
|
||||
@@ -47,29 +43,22 @@ define([
|
||||
};
|
||||
|
||||
OperationDecorator.prototype.condense = function() {
|
||||
var path,
|
||||
width,
|
||||
rx;
|
||||
|
||||
width = Math.max(this.nameWidth + 2 * NAME_MARGIN, this.dense.width);
|
||||
rx = width/2;
|
||||
|
||||
path = [
|
||||
`M${-rx},0`,
|
||||
`l ${width} 0`,
|
||||
`l 0 ${this.dense.height}`,
|
||||
`l -${width} 0`,
|
||||
`l 0 -${this.dense.height}`
|
||||
].join(' ');
|
||||
|
||||
var width = Math.max(this.nameWidth + 2 * NAME_MARGIN, this.dense.width);
|
||||
|
||||
this.$body
|
||||
.attr('d', path);
|
||||
.transition()
|
||||
.attr('x', -width/2)
|
||||
.attr('y', 0)
|
||||
.attr('width', width)
|
||||
.attr('height', this.dense.height);
|
||||
|
||||
// Clear the attributes
|
||||
this.$attributes.remove();
|
||||
this.clearFields();
|
||||
this.$attributes = this.$el.append('g')
|
||||
.attr('fill', '#222222');
|
||||
.attr('fill', 'none');
|
||||
|
||||
this.createAttributeFields(0, width);
|
||||
this.createPointerFields(0, width, true);
|
||||
|
||||
this.height = this.dense.height;
|
||||
this.width = width;
|
||||
|
||||
@@ -118,7 +118,7 @@ define([
|
||||
this.core.addMember(this.activeNode, 'executions', tgtNode);
|
||||
|
||||
return this.project.createTag(
|
||||
execName.replace(/ /g, '_'),
|
||||
execName.replace(/[ -]/g, '_'),
|
||||
this.currentHash
|
||||
);
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "CreateExecution",
|
||||
"name": "CreateExecution",
|
||||
"name": "Create Execution",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
|
||||
@@ -86,8 +86,7 @@ define([
|
||||
layers;
|
||||
|
||||
try {
|
||||
layers = JSON.parse(text)
|
||||
.filter(layer => layer.type !== 'Criterion');
|
||||
layers = JSON.parse(text);
|
||||
} catch (e) {
|
||||
return callback('JSON parse error: ' + e, this.result);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,927 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
define([
|
||||
'text!./metadata.json',
|
||||
'executor/ExecutorClient',
|
||||
'plugin/PluginBase',
|
||||
'deepforge/plugin/LocalExecutor',
|
||||
'deepforge/plugin/PtrCodeGen',
|
||||
'deepforge/Constants',
|
||||
'./templates/index',
|
||||
'q',
|
||||
'underscore'
|
||||
], function (
|
||||
pluginMetadata,
|
||||
ExecutorClient,
|
||||
PluginBase,
|
||||
LocalExecutor, // DeepForge operation primitives
|
||||
PtrCodeGen,
|
||||
CONSTANTS,
|
||||
Templates,
|
||||
Q,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
pluginMetadata = JSON.parse(pluginMetadata);
|
||||
|
||||
var OUTPUT_INTERVAL = 1500,
|
||||
STDOUT_FILE = 'job_stdout.txt';
|
||||
|
||||
/**
|
||||
* Initializes a new instance of ExecuteJob.
|
||||
* @class
|
||||
* @augments {PluginBase}
|
||||
* @classdesc This class represents the plugin ExecuteJob.
|
||||
* @constructor
|
||||
*/
|
||||
var ExecuteJob = function () {
|
||||
// Call base class' constructor.
|
||||
PluginBase.call(this);
|
||||
this.pluginMetadata = pluginMetadata;
|
||||
this._metadata = {};
|
||||
|
||||
// Metadata updating
|
||||
this._markForDeletion = {}; // id -> node
|
||||
this._oldMetadataByName = {}; // name -> id
|
||||
this.lastAppliedCmd = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
|
||||
* This is also available at the instance at this.pluginMetadata.
|
||||
* @type {object}
|
||||
*/
|
||||
ExecuteJob.metadata = pluginMetadata;
|
||||
ExecuteJob.UPDATE_INTERVAL = 1500;
|
||||
|
||||
// Prototypical inheritance from PluginBase.
|
||||
ExecuteJob.prototype = Object.create(PluginBase.prototype);
|
||||
ExecuteJob.prototype.constructor = ExecuteJob;
|
||||
|
||||
/**
|
||||
* Main function for the plugin to execute. This will perform the execution.
|
||||
* Notes:
|
||||
* - Always log with the provided logger.[error,warning,info,debug].
|
||||
* - Do NOT put any user interaction logic UI, etc. inside this method.
|
||||
* - callback always has to be called even if error happened.
|
||||
*
|
||||
* @param {function(string, plugin.PluginResult)} callback - the result callback
|
||||
*/
|
||||
ExecuteJob.prototype.main = function (callback) {
|
||||
// Check the activeNode to make sure it is a valid node
|
||||
var type = this.core.getMetaType(this.activeNode),
|
||||
typeName = type && this.core.getAttribute(type, 'name');
|
||||
|
||||
if (typeName !== 'Job') {
|
||||
return callback(`Cannot execute ${typeName} (expected Job)`, this.result);
|
||||
}
|
||||
|
||||
this._callback = callback;
|
||||
this.prepare()
|
||||
.then(() => this.executeJob(this.activeNode));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getConnections = function (nodes) {
|
||||
var conns = [];
|
||||
for (var i = nodes.length; i--;) {
|
||||
if (this.core.getPointerPath(nodes[i], 'src') &&
|
||||
this.core.getPointerPath(nodes[i], 'dst')) {
|
||||
|
||||
conns.push(nodes[i]);
|
||||
}
|
||||
}
|
||||
return conns;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.prepare = function () {
|
||||
var dstPortId,
|
||||
srcPortId,
|
||||
conns,
|
||||
executionNode = this.core.getParent(this.activeNode);
|
||||
|
||||
this.pipelineName = this.core.getAttribute(executionNode, 'name');
|
||||
return this.core.loadSubTree(executionNode)
|
||||
.then(nodes => {
|
||||
this.inputPortsFor = {};
|
||||
this.outputLineCount = {};
|
||||
|
||||
conns = this.getConnections(nodes);
|
||||
|
||||
// Create inputPortsFor for the given input ports
|
||||
for (var i = conns.length; i--;) {
|
||||
dstPortId = this.core.getPointerPath(conns[i], 'dst');
|
||||
srcPortId = this.core.getPointerPath(conns[i], 'src');
|
||||
|
||||
if (!this.inputPortsFor[dstPortId]) {
|
||||
this.inputPortsFor[dstPortId] = [srcPortId];
|
||||
} else {
|
||||
this.inputPortsFor[dstPortId].push(srcPortId);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(() => this.recordOldMetadata(this.activeNode));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.recordOldMetadata = function (job) {
|
||||
var nodeId = this.core.getPath(job),
|
||||
name,
|
||||
id,
|
||||
idsToDelete = [],
|
||||
child;
|
||||
|
||||
this.lastAppliedCmd[nodeId] = 0;
|
||||
this._oldMetadataByName[nodeId] = {};
|
||||
this._markForDeletion[nodeId] = {};
|
||||
return this.core.loadChildren(job)
|
||||
.then(jobChildren => {
|
||||
// Remove any metadata nodes
|
||||
for (var i = jobChildren.length; i--;) {
|
||||
child = jobChildren[i];
|
||||
if (this.isMetaTypeOf(child, this.META.Metadata)) {
|
||||
id = this.core.getPath(child);
|
||||
name = this.core.getAttribute(child, 'name');
|
||||
|
||||
this._markForDeletion[nodeId][id] = child;
|
||||
this._oldMetadataByName[nodeId][name] = id;
|
||||
|
||||
// children of metadata nodes get deleted
|
||||
idsToDelete = idsToDelete
|
||||
.concat(this.core.getChildrenPaths(child));
|
||||
}
|
||||
}
|
||||
|
||||
// make the deletion ids relative to the job node
|
||||
idsToDelete = idsToDelete.map(id => id.replace(nodeId, ''));
|
||||
return Q.all(idsToDelete.map(id => this.core.loadByPath(job, id)));
|
||||
})
|
||||
.then(nodes => nodes.forEach(node => this.core.deleteNode(node)));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.clearOldMetadata = function (job) {
|
||||
var nodeId = this.core.getPath(job),
|
||||
nodeIds = Object.keys(this._markForDeletion[nodeId]);
|
||||
|
||||
for (var i = nodeIds.length; i--;) {
|
||||
this.core.deleteNode(this._markForDeletion[nodeId][nodeIds[i]]);
|
||||
}
|
||||
delete this.lastAppliedCmd[nodeId];
|
||||
delete this._markForDeletion[nodeId];
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.onOperationFail =
|
||||
ExecuteJob.prototype.onOperationComplete =
|
||||
ExecuteJob.prototype.onComplete = function (opNode, err) {
|
||||
var job = this.core.getParent(opNode),
|
||||
exec = this.core.getParent(job),
|
||||
name = this.core.getAttribute(job, 'name'),
|
||||
jobId = this.core.getPath(job),
|
||||
status = err ? 'fail' : 'success',
|
||||
msg = err ? `${name} execution failed: ${err}` :
|
||||
`${name} executed successfully!`,
|
||||
promise = Q();
|
||||
|
||||
this.core.setAttribute(job, 'status', status);
|
||||
this.logger.info(`Setting ${name} (${jobId}) status to ${status}`);
|
||||
this.clearOldMetadata(job);
|
||||
|
||||
if (err) {
|
||||
this.core.setAttribute(exec, 'status', 'failed');
|
||||
} else {
|
||||
// Check if all the other jobs are successful. If so, set the
|
||||
// execution status to 'success'
|
||||
promise = this.core.loadChildren(exec)
|
||||
.then(nodes => {
|
||||
var execSuccess = true,
|
||||
type,
|
||||
typeName;
|
||||
|
||||
for (var i = nodes.length; i--;) {
|
||||
type = this.core.getMetaType(nodes[i]);
|
||||
typeName = this.core.getAttribute(type, 'name');
|
||||
|
||||
if (typeName === 'Job' &&
|
||||
this.core.getAttribute(nodes[i], 'status') !== 'success') {
|
||||
execSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (execSuccess) {
|
||||
this.core.setAttribute(exec, 'status', 'success');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
promise
|
||||
.then(() => this.save(msg))
|
||||
.then(() => {
|
||||
this.result.setSuccess(!err);
|
||||
this._callback(err, this.result);
|
||||
})
|
||||
.catch(err => {
|
||||
// Result success is false at invocation.
|
||||
this._callback(err, this.result);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getOperation = function (job) {
|
||||
return this.core.loadChildren(job).then(children =>
|
||||
children.find(child => this.isMetaTypeOf(child, this.META.Operation)));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.executeJob = function (job) {
|
||||
return this.getOperation(job).then(node => {
|
||||
var jobId = this.core.getPath(job),
|
||||
name = this.core.getAttribute(node, 'name'),
|
||||
localTypeId = this.getLocalOperationType(node),
|
||||
artifact,
|
||||
artifactName,
|
||||
files,
|
||||
data = {},
|
||||
inputs;
|
||||
|
||||
// Execute any special operation types here - not on an executor
|
||||
this.logger.debug(`Executing operation "${name}"`);
|
||||
if (localTypeId !== null) {
|
||||
return this.executeLocalOperation(localTypeId, node);
|
||||
} else {
|
||||
// Generate all execution files
|
||||
return this.createOperationFiles(node).then(results => {
|
||||
this.logger.info('Created operation files!');
|
||||
files = results;
|
||||
artifactName = `${name}_${jobId.replace(/\//g, '_')}-execution-files`;
|
||||
artifact = this.blobClient.createArtifact(artifactName);
|
||||
|
||||
// Add the input assets
|
||||
// - get the metadata (name)
|
||||
// - add the given inputs
|
||||
inputs = Object.keys(files.inputAssets);
|
||||
|
||||
return Q.all(
|
||||
inputs.map(input => { // Get the metadata for each input
|
||||
var hash = files.inputAssets[input];
|
||||
|
||||
// data asset for "input"
|
||||
return this.blobClient.getMetadata(hash);
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(mds => {
|
||||
// get (input, filename) tuples
|
||||
mds.forEach((metadata, i) => {
|
||||
// add the hashes for each input
|
||||
var input = inputs[i],
|
||||
name = metadata.name,
|
||||
hash = files.inputAssets[input];
|
||||
|
||||
data['inputs/' + input + '/' + name] = hash;
|
||||
});
|
||||
|
||||
delete files.inputAssets;
|
||||
|
||||
// Add pointer assets
|
||||
Object.keys(files.ptrAssets)
|
||||
.forEach(path => data[path] = files.ptrAssets[path]);
|
||||
|
||||
delete files.ptrAssets;
|
||||
|
||||
// Add the executor config
|
||||
return this.getOutputs(node);
|
||||
})
|
||||
.then(outputArgs => {
|
||||
var config,
|
||||
outputs,
|
||||
file;
|
||||
|
||||
outputs = outputArgs.map(pair => pair[0])
|
||||
.map(name => {
|
||||
return {
|
||||
name: name,
|
||||
resultPatterns: [`outputs/${name}`]
|
||||
};
|
||||
});
|
||||
|
||||
outputs.push(
|
||||
{
|
||||
name: 'stdout',
|
||||
resultPatterns: [STDOUT_FILE]
|
||||
},
|
||||
{
|
||||
name: name + '-all-files',
|
||||
resultPatterns: []
|
||||
}
|
||||
);
|
||||
|
||||
config = {
|
||||
cmd: 'bash',
|
||||
args: ['run.sh'],
|
||||
outputInterval: OUTPUT_INTERVAL,
|
||||
resultArtifacts: outputs
|
||||
};
|
||||
files['executor_config.json'] = JSON.stringify(config, null, 4);
|
||||
files['run.sh'] = Templates.BASH;
|
||||
|
||||
// Save the artifact
|
||||
// Remove empty hashes
|
||||
for (file in data) {
|
||||
if (!data[file]) {
|
||||
this.logger.warn(`Empty data hash has been found for file "${file}". Removing it...`);
|
||||
delete data[file];
|
||||
}
|
||||
}
|
||||
return artifact.addObjectHashes(data);
|
||||
})
|
||||
.then(() => {
|
||||
this.logger.info(`Added ptr/input data hashes for "${artifactName}"`);
|
||||
return artifact.addFiles(files);
|
||||
})
|
||||
.then(() => {
|
||||
this.logger.info(`Added execution files for "${artifactName}"`);
|
||||
return artifact.save();
|
||||
})
|
||||
.then(hash => {
|
||||
this.logger.info(`Saved execution files "${artifactName}"`);
|
||||
this.result.addArtifact(hash); // Probably only need this for debugging...
|
||||
this.executeDistOperation(job, node, hash);
|
||||
})
|
||||
.fail(e => {
|
||||
this.onOperationFail(node, `Distributed operation "${name}" failed ${e}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.executeDistOperation = function (job, opNode, hash) {
|
||||
var name = this.core.getAttribute(opNode, 'name'),
|
||||
jobId = this.core.getPath(job),
|
||||
isHttps = typeof window === 'undefined' ? false :
|
||||
window.location.protocol !== 'http:',
|
||||
executor = new ExecutorClient({
|
||||
logger: this.logger,
|
||||
serverPort: this.gmeConfig.server.port,
|
||||
httpsecure: isHttps
|
||||
});
|
||||
|
||||
this.logger.info(`Executing operation "${name}"`);
|
||||
|
||||
this.outputLineCount[jobId] = 0;
|
||||
// Set the job status to 'running'
|
||||
this.core.setAttribute(job, 'status', 'queued');
|
||||
this.core.setAttribute(job, 'stdout', '');
|
||||
this.logger.info(`Setting ${jobId} status to "queued" (${this.currentHash})`);
|
||||
this.logger.debug(`Making a commit from ${this.currentHash}`);
|
||||
this.save(`Queued "${name}" operation in ${this.pipelineName}`)
|
||||
.then(() => executor.createJob({hash}))
|
||||
.then(() => this.watchOperation(executor, hash, opNode, job))
|
||||
.catch(err => this.logger.error(`Could not execute "${name}": ${err}`));
|
||||
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createOperationFiles = function (node) {
|
||||
var files = {};
|
||||
// For each operation, generate the output files:
|
||||
// inputs/<arg-name>/init.lua (respective data deserializer)
|
||||
// pointers/<name>/init.lua (result of running the main plugin on pointer target - may need a rename)
|
||||
// outputs/<name>/ (make dirs for each of the outputs)
|
||||
// outputs/init.lua (serializers for data outputs)
|
||||
//
|
||||
// attributes.lua (returns lua table of operation attributes)
|
||||
// init.lua (main file -> calls main and serializes outputs)
|
||||
// <name>.lua (entry point -> calls main operation code)
|
||||
|
||||
// add the given files
|
||||
this.logger.info('About to create dist execution files');
|
||||
return this.createEntryFile(node, files)
|
||||
.then(() => this.createClasses(node, files))
|
||||
.then(() => this.createCustomLayers(node, files))
|
||||
.then(() => this.createInputs(node, files))
|
||||
.then(() => this.createOutputs(node, files))
|
||||
.then(() => this.createMainFile(node, files))
|
||||
.then(() => {
|
||||
this.createAttributeFile(node, files);
|
||||
return Q.ninvoke(this, 'createPointers', node, files);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createEntryFile = function (node, files) {
|
||||
this.logger.info('Creating entry files...');
|
||||
return this.getOutputs(node)
|
||||
.then(outputs => {
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
content = {};
|
||||
|
||||
// inputs and outputs
|
||||
content.name = name;
|
||||
content.outputs = outputs;
|
||||
|
||||
files['init.lua'] = _.template(Templates.ENTRY)(content);
|
||||
|
||||
// Create the deepforge file
|
||||
files['deepforge.lua'] = _.template(Templates.DEEPFORGE)(CONSTANTS);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createClasses = function (node, files) {
|
||||
var metaDict = this.core.getAllMetaNodes(this.rootNode),
|
||||
isClass,
|
||||
metanodes,
|
||||
classNodes,
|
||||
code;
|
||||
|
||||
this.logger.info('Creating custom layer file...');
|
||||
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
|
||||
isClass = this.getTypeDictFor('Complex', metanodes);
|
||||
|
||||
classNodes = metanodes.filter(node => {
|
||||
var base = this.core.getBase(node),
|
||||
baseId = this.core.getPath(base);
|
||||
|
||||
return isClass[baseId];
|
||||
});
|
||||
|
||||
// Get the code definitions for each
|
||||
code = classNodes.map(node =>
|
||||
`require './${this.core.getAttribute(node, 'name')}.lua'`
|
||||
).join('\n');
|
||||
|
||||
// Create the class files
|
||||
classNodes.forEach(node => {
|
||||
var name = this.core.getAttribute(node, 'name');
|
||||
files[`classes/${name}.lua`] = this.core.getAttribute(node, 'code');
|
||||
});
|
||||
|
||||
// Create the custom layers file
|
||||
files['classes/init.lua'] = code;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getTypeDictFor = function (name, metanodes) {
|
||||
var isType = {};
|
||||
// Get all the custom layers
|
||||
for (var i = metanodes.length; i--;) {
|
||||
if (this.core.getAttribute(metanodes[i], 'name') === name) {
|
||||
isType[this.core.getPath(metanodes[i])] = true;
|
||||
}
|
||||
}
|
||||
return isType;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createCustomLayers = function (node, files) {
|
||||
var metaDict = this.core.getAllMetaNodes(this.rootNode),
|
||||
isCustomLayer,
|
||||
metanodes,
|
||||
customLayers,
|
||||
code;
|
||||
|
||||
this.logger.info('Creating custom layer file...');
|
||||
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
|
||||
isCustomLayer = this.getTypeDictFor('CustomLayer', metanodes);
|
||||
|
||||
customLayers = metanodes.filter(node =>
|
||||
this.core.getMixinPaths(node).some(id => isCustomLayer[id]));
|
||||
|
||||
// Get the code definitions for each
|
||||
code = 'require \'nn\'\n\n' + customLayers
|
||||
.map(node => this.core.getAttribute(node, 'code')).join('\n');
|
||||
|
||||
// Create the custom layers file
|
||||
files['custom-layers.lua'] = code;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createInputs = function (node, files) {
|
||||
var tplContents,
|
||||
inputs;
|
||||
|
||||
this.logger.info('Retrieving inputs and deserialize fns...');
|
||||
return this.getInputs(node)
|
||||
.then(allInputs => {
|
||||
// For each input, match the connection with the input name
|
||||
// [ name, type ] => [ name, type, node ]
|
||||
//
|
||||
// For each input,
|
||||
// - create the deserializer
|
||||
// - put it in inputs/<name>/init.lua
|
||||
// - copy the data asset to /inputs/<name>/init.lua
|
||||
inputs = allInputs
|
||||
.filter(pair => !!this.core.getAttribute(pair[2], 'data')); // remove empty inputs
|
||||
|
||||
files.inputAssets = {}; // data assets
|
||||
return Q.all(inputs.map(pair => {
|
||||
var name = pair[0],
|
||||
node = pair[2],
|
||||
nodeId = this.core.getPath(node),
|
||||
fromNodeId;
|
||||
|
||||
// Get the deserialize function. First, try to get it from
|
||||
// the source method (this guarantees that the correct
|
||||
// deserialize method is used despite any auto-upcasting
|
||||
fromNodeId = this.inputPortsFor[nodeId][0] || nodeId;
|
||||
|
||||
return this.core.loadByPath(this.rootNode, fromNodeId)
|
||||
.then(fromNode => {
|
||||
var deserFn,
|
||||
base,
|
||||
className;
|
||||
|
||||
deserFn = this.core.getAttribute(fromNode, 'deserialize');
|
||||
|
||||
if (this.isMetaTypeOf(node, this.META.Complex)) {
|
||||
// Complex objects are expected to define their own
|
||||
// (static) deserialize factory method
|
||||
base = this.core.getMetaType(node);
|
||||
className = this.core.getAttribute(base, 'name');
|
||||
deserFn = `return ${className}.deserialize(path)`;
|
||||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
code: deserFn
|
||||
};
|
||||
});
|
||||
}));
|
||||
})
|
||||
.then(_tplContents => {
|
||||
tplContents = _tplContents;
|
||||
var hashes = inputs
|
||||
// storing the hash for now...
|
||||
.map(pair =>
|
||||
files.inputAssets[pair[0]] = this.core.getAttribute(pair[2], 'data')
|
||||
);
|
||||
return Q.all(hashes.map(h => this.blobClient.getMetadata(h)));
|
||||
})
|
||||
.then(metadatas => {
|
||||
// Create the deserializer
|
||||
tplContents.forEach((ctnt, i) => {
|
||||
// Get the name of the given asset
|
||||
ctnt.filename = metadatas[i].name;
|
||||
files['inputs/' + ctnt.name + '/init.lua'] = _.template(Templates.DESERIALIZE)(ctnt);
|
||||
});
|
||||
return files;
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createOutputs = function (node, files) {
|
||||
// For each of the output types, grab their serialization functions and
|
||||
// create the `outputs/init.lua` file
|
||||
this.logger.info('Creating outputs/init.lua...');
|
||||
return this.getOutputs(node)
|
||||
.then(outputs => {
|
||||
var outputTypes = outputs
|
||||
// Get the serialize functions for each
|
||||
.map(tuple => {
|
||||
var node = tuple[2],
|
||||
serFn = this.core.getAttribute(node, 'serialize');
|
||||
|
||||
if (this.isMetaTypeOf(node, this.META.Complex)) {
|
||||
// Complex objects are expected to define their own
|
||||
// serialize methods
|
||||
serFn = 'if data ~= nil then data:serialize(path) end';
|
||||
}
|
||||
|
||||
return [tuple[1], serFn];
|
||||
});
|
||||
|
||||
files['outputs/init.lua'] = _.template(Templates.SERIALIZE)({types: outputTypes});
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createMainFile = function (node, files) {
|
||||
this.logger.info('Creating main file...');
|
||||
return this.getInputs(node)
|
||||
.then(inputs => {
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
code = this.core.getAttribute(node, 'code'),
|
||||
pointers = this.core.getPointerNames(node).filter(ptr => ptr !== 'base'),
|
||||
content = {
|
||||
name: name
|
||||
};
|
||||
|
||||
// Get input data arguments
|
||||
content.inputs = inputs
|
||||
.map(pair => [pair[0], !this.core.getAttribute(pair[2], 'data')]); // remove empty inputs
|
||||
|
||||
// Defined variables for each pointers
|
||||
content.pointers = pointers
|
||||
.map(id => [id, this.core.getPointerPath(node, id) === null]);
|
||||
|
||||
// Add remaining code
|
||||
content.code = code;
|
||||
|
||||
files['main.lua'] = _.template(Templates.MAIN)(content);
|
||||
|
||||
// Set the line offset
|
||||
var lineOffset = this.getLineOffset(files['main.lua'], code);
|
||||
this.core.setAttribute(node, CONSTANTS.LINE_OFFSET, lineOffset);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getLineOffset = function (main, snippet) {
|
||||
var i = main.indexOf(snippet),
|
||||
lines = main.substring(0, i).match(/\n/g);
|
||||
|
||||
return lines ? lines.length : 0;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createAttributeFile = function (node, files) {
|
||||
var skip = ['code'],
|
||||
numRegex = /^\d+\.?\d*((e|e-)\d+)?$/,
|
||||
table;
|
||||
|
||||
this.logger.info('Creating attributes file...');
|
||||
table = '{\n\t' + this.core.getAttributeNames(node)
|
||||
.filter(attr => skip.indexOf(attr) === -1)
|
||||
.map(name => {
|
||||
var value = this.core.getAttribute(node, name);
|
||||
if (!numRegex.test(value)) {
|
||||
value = `"${value}"`;
|
||||
}
|
||||
return [name, value];
|
||||
})
|
||||
.map(pair => pair.join(' = '))
|
||||
.join(',\n\t') + '\n}';
|
||||
|
||||
files['attributes.lua'] = `-- attributes of ${this.core.getAttribute(node, 'name')}\nreturn ${table}`;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createPointers = function (node, files, cb) {
|
||||
var pointers,
|
||||
nIds;
|
||||
|
||||
this.logger.info('Creating pointers file...');
|
||||
pointers = this.core.getPointerNames(node)
|
||||
.filter(name => name !== 'base')
|
||||
.filter(id => this.core.getPointerPath(node, id) !== null);
|
||||
|
||||
nIds = pointers.map(p => this.core.getPointerPath(node, p));
|
||||
files.ptrAssets = {};
|
||||
Q.all(
|
||||
nIds.map(nId => this.getPtrCodeHash(nId))
|
||||
)
|
||||
.then(resultHashes => {
|
||||
var name = this.core.getAttribute(node, 'name');
|
||||
this.logger.info(`Pointer generation for ${name} FINISHED!`);
|
||||
resultHashes.forEach((hash, index) => {
|
||||
files.ptrAssets[`pointers/${pointers[index]}/init.lua`] = hash;
|
||||
});
|
||||
return cb(null, files);
|
||||
})
|
||||
.fail(e => {
|
||||
this.logger.error(`Could not generate pointer files for ${this.core.getAttribute(node, 'name')}: ${JSON.stringify(e)}`);
|
||||
return cb(e);
|
||||
});
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.watchOperation = function (executor, hash, op, job) {
|
||||
var jobId = this.core.getPath(job),
|
||||
opId = this.core.getPath(op),
|
||||
info,
|
||||
name;
|
||||
|
||||
return executor.getInfo(hash)
|
||||
.then(_info => { // Update the job's stdout
|
||||
var actualLine, // on executing job
|
||||
currentLine = this.outputLineCount[jobId];
|
||||
|
||||
info = _info;
|
||||
actualLine = info.outputNumber;
|
||||
if (actualLine !== null && actualLine >= currentLine) {
|
||||
this.outputLineCount[jobId] = actualLine + 1;
|
||||
return executor.getOutput(hash, currentLine, actualLine+1)
|
||||
.then(outputLines => {
|
||||
var stdout = this.core.getAttribute(job, 'stdout'),
|
||||
output = outputLines.map(o => o.output).join(''),
|
||||
jobName = this.core.getAttribute(job, 'name');
|
||||
|
||||
// parse deepforge commands
|
||||
output = this.parseForMetadataCmds(job, output);
|
||||
|
||||
if (output) {
|
||||
stdout += output;
|
||||
this.core.setAttribute(job, 'stdout', stdout);
|
||||
return this.save(`Received stdout for ${jobName}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (info.status === 'CREATED' || info.status === 'RUNNING') {
|
||||
if (info.status === 'RUNNING' &&
|
||||
this.core.getAttribute(job, 'status') !== 'running') {
|
||||
|
||||
name = this.core.getAttribute(job, 'name');
|
||||
this.core.setAttribute(job, 'status', 'running');
|
||||
this.save(`Started "${name}" operation in ${this.pipelineName}`);
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
this.watchOperation.bind(this, executor, hash, op, job),
|
||||
ExecuteJob.UPDATE_INTERVAL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
name = this.core.getAttribute(job, 'name');
|
||||
this.core.setAttribute(job, 'execFiles', info.resultHashes[name + '-all-files']);
|
||||
return this.blobClient.getArtifact(info.resultHashes.stdout)
|
||||
.then(artifact => {
|
||||
var stdoutHash = artifact.descriptor.content[STDOUT_FILE].content;
|
||||
return this.blobClient.getObjectAsString(stdoutHash);
|
||||
})
|
||||
.then(stdout => {
|
||||
// Parse the remaining code
|
||||
stdout = this.parseForMetadataCmds(job, stdout, true);
|
||||
this.core.setAttribute(job, 'stdout', stdout);
|
||||
if (info.status !== 'SUCCESS') {
|
||||
// Download all files
|
||||
this.result.addArtifact(info.resultHashes[name + '-all-files']);
|
||||
// Set the job to failed! Store the error
|
||||
this.onOperationFail(op, `Operation "${opId}" failed! ${JSON.stringify(info)}`);
|
||||
} else {
|
||||
this.onDistOperationComplete(op, info);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => this.logger.error(`Could not get op info for ${opId}: ${err}`));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.onDistOperationComplete = function (node, result) {
|
||||
var nodeId = this.core.getPath(node),
|
||||
outputMap = {},
|
||||
outputs;
|
||||
|
||||
// Match the output names to the actual nodes
|
||||
// Create an array of [name, node]
|
||||
// For now, just match by type. Later we may use ports for input/outputs
|
||||
// Store the results in the outgoing ports
|
||||
this.getOutputs(node)
|
||||
.then(outputPorts => {
|
||||
outputs = outputPorts.map(tuple => [tuple[0], tuple[2]]);
|
||||
outputs.forEach(output => outputMap[output[0]] = output[1]);
|
||||
|
||||
// this should not be in directories -> flatten the data!
|
||||
return Q.all(outputs.map(tuple => // [ name, node ]
|
||||
this.blobClient.getArtifact(result.resultHashes[tuple[0]])
|
||||
));
|
||||
})
|
||||
.then(artifacts => {
|
||||
this.logger.info(`preparing outputs -> retrieved ${artifacts.length} objects`);
|
||||
// Create new metadata for each
|
||||
artifacts.forEach((artifact, i) => {
|
||||
var name = outputs[i][0],
|
||||
outputData = artifact.descriptor.content[`outputs/${name}`],
|
||||
hash = outputData && outputData.content;
|
||||
|
||||
if (hash) {
|
||||
this.core.setAttribute(outputMap[name], 'data', hash);
|
||||
this.logger.info(`Setting ${nodeId} data to ${hash}`);
|
||||
}
|
||||
});
|
||||
|
||||
return this.onOperationComplete(node);
|
||||
})
|
||||
.fail(e => this.onOperationFail(node, `Operation ${nodeId} failed: ${e}`));
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getOutputs = function (node) {
|
||||
return this.getOperationData(node, this.META.Outputs);
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getInputs = function (node) {
|
||||
return this.getOperationData(node, this.META.Inputs);
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.getOperationData = function (node, metaType) {
|
||||
// Load the children and the output's children
|
||||
return this.core.loadChildren(node)
|
||||
.then(containers => {
|
||||
var outputs = containers.find(c => this.core.isTypeOf(c, metaType));
|
||||
return outputs ? this.core.loadChildren(outputs) : [];
|
||||
})
|
||||
.then(outputs => {
|
||||
var bases = outputs.map(node => this.core.getMetaType(node));
|
||||
// return [[arg1, Type1, node1], [arg2, Type2, node2]]
|
||||
return outputs.map((node, i) => [
|
||||
this.core.getAttribute(node, 'name'),
|
||||
this.core.getAttribute(bases[i], 'name'),
|
||||
node
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
//////////////////////////// Special Operations ////////////////////////////
|
||||
ExecuteJob.prototype.executeLocalOperation = function (type, node) {
|
||||
// Retrieve the given LOCAL_OP type
|
||||
if (!this[type]) {
|
||||
this.logger.error(`No local operation handler for ${type}`);
|
||||
}
|
||||
this.logger.info(`Running local operation ${type}`);
|
||||
|
||||
return this[type](node);
|
||||
};
|
||||
|
||||
_.extend(
|
||||
ExecuteJob.prototype,
|
||||
PtrCodeGen.prototype,
|
||||
LocalExecutor.prototype
|
||||
);
|
||||
|
||||
//////////////////////////// Metadata ////////////////////////////
|
||||
ExecuteJob.prototype.parseForMetadataCmds = function (job, text, skip) {
|
||||
var jobId = this.core.getPath(job),
|
||||
lines = text.split('\n'),
|
||||
args,
|
||||
result = [],
|
||||
cmdCnt = 0,
|
||||
cmd;
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
// Check for a deepforge command
|
||||
if (lines[i].indexOf(CONSTANTS.START_CMD) === 0) {
|
||||
cmdCnt++;
|
||||
args = lines[i].split(/\s+/);
|
||||
args.shift();
|
||||
cmd = args[0];
|
||||
args[0] = job;
|
||||
if (this[cmd] && (!skip || cmdCnt >= this.lastAppliedCmd[jobId])) {
|
||||
this[cmd].apply(this, args);
|
||||
this.lastAppliedCmd[jobId]++;
|
||||
} else if (!this[cmd]) {
|
||||
this.logger.error(`Invoked unimplemented metadata method "${cmd}"`);
|
||||
}
|
||||
} else {
|
||||
result.push(lines[i]);
|
||||
}
|
||||
}
|
||||
return result.join('\n');
|
||||
};
|
||||
|
||||
ExecuteJob.prototype[CONSTANTS.GRAPH_CREATE] = function (job, id) {
|
||||
var graph,
|
||||
name = Array.prototype.slice.call(arguments, 2).join(' '),
|
||||
jobId = this.core.getPath(job),
|
||||
oldMetadata = this._oldMetadataByName[jobId],
|
||||
oldId;
|
||||
|
||||
id = jobId + '/' + id;
|
||||
this.logger.info(`Creating graph ${id} named ${name}`);
|
||||
|
||||
// Check if the graph already exists
|
||||
if (oldMetadata && oldMetadata[name]) {
|
||||
oldId = oldMetadata[name];
|
||||
graph = this._markForDeletion[jobId][oldId];
|
||||
|
||||
// Reset points
|
||||
this.core.setAttribute(graph, 'points', '');
|
||||
|
||||
delete this._markForDeletion[jobId][oldId];
|
||||
} else { // create new graph
|
||||
graph = this.core.createNode({
|
||||
base: this.META.Graph,
|
||||
parent: job
|
||||
});
|
||||
|
||||
if (name) {
|
||||
this.core.setAttribute(graph, 'name', name);
|
||||
}
|
||||
}
|
||||
|
||||
this._metadata[id] = graph;
|
||||
};
|
||||
|
||||
ExecuteJob.prototype[CONSTANTS.GRAPH_PLOT] = function (job, id, x, y) {
|
||||
var jobId = this.core.getPath(job),
|
||||
graph,
|
||||
points;
|
||||
|
||||
|
||||
id = jobId + '/' + id;
|
||||
this.logger.info(`Adding point ${x}, ${y} to ${id}`);
|
||||
graph = this._metadata[id];
|
||||
if (!graph) {
|
||||
this.logger.warn(`Can't add point to non-existent graph: ${id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
points = this.core.getAttribute(graph, 'points');
|
||||
points += `${x},${y};`;
|
||||
this.core.setAttribute(graph, 'points', points);
|
||||
};
|
||||
|
||||
ExecuteJob.prototype[CONSTANTS.GRAPH_CREATE_LINE] = function (job, graphId, id) {
|
||||
var jobId = this.core.getPath(job),
|
||||
graph = this._metadata[jobId + '/' + graphId],
|
||||
name = Array.prototype.slice.call(arguments, 3).join(' '),
|
||||
line;
|
||||
|
||||
// Create a 'line' node in the given Graph metadata node
|
||||
name = name.replace(/\s+$/, '');
|
||||
line = this.core.createNode({
|
||||
base: this.META.Line,
|
||||
parent: graph
|
||||
});
|
||||
this.core.setAttribute(line, 'name', name);
|
||||
this._metadata[jobId + '/' + id] = line;
|
||||
};
|
||||
|
||||
return ExecuteJob;
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "ExecuteJob",
|
||||
"name": "ExecuteJob",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"writeAccessRequired": false,
|
||||
"configStructure": []
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
-- Instantiate the deepforge object
|
||||
deepforge = {}
|
||||
|
||||
function deepforge.id()
|
||||
if __deepforge_id == nil then
|
||||
__deepforge_id = 0
|
||||
end
|
||||
__deepforge_id = __deepforge_id + 1
|
||||
return __deepforge_id
|
||||
end
|
||||
|
||||
function deepforge._cmd(...)
|
||||
local cmd = '<%= START_CMD %>'
|
||||
local arg = {...}
|
||||
local n = #arg
|
||||
for i=1,n do
|
||||
cmd = cmd .. ' ' .. tostring(arg[i])
|
||||
end
|
||||
print(cmd)
|
||||
end
|
||||
|
||||
Graph = torch.class('deepforge.Graph')
|
||||
|
||||
function Graph:__init(name)
|
||||
self.id = deepforge.id()
|
||||
self.name = name
|
||||
deepforge._cmd('<%= GRAPH_CREATE %>', self.id, name)
|
||||
end
|
||||
|
||||
_Line = torch.class('deepforge._Line')
|
||||
|
||||
function _Line:__init(graphId, name, opts)
|
||||
self.id = deepforge.id()
|
||||
self.name = name
|
||||
deepforge._cmd('<%= GRAPH_CREATE_LINE %>', graphId, self.id, name)
|
||||
end
|
||||
|
||||
function _Line:add(x, y)
|
||||
deepforge._cmd('<%= GRAPH_PLOT %>', self.id, x, y)
|
||||
end
|
||||
|
||||
function Graph:line(name, opts)
|
||||
return deepforge._Line(self.id, name, opts)
|
||||
end
|
||||
|
||||
return deepforge
|
||||
+5
-2
@@ -1,7 +1,10 @@
|
||||
-- Instantiate the deepforge object
|
||||
require './deepforge'
|
||||
|
||||
-- run the <%= name %> and serialize the results
|
||||
print('\n############### Running "<%= name %>" Operation ############### ')
|
||||
print('\n############### Running "<%= name.replace(/'/g, '\\\'') %>" Operation ############### ')
|
||||
results = require './main'
|
||||
print('############### "<%= name %>" Operation Complete! ###############')
|
||||
print('############### "<%= name.replace(/'/g, '\\\'') %>" Operation Complete! ###############')
|
||||
|
||||
-- serialize by type
|
||||
outputs = require './outputs'
|
||||
+3
@@ -2,11 +2,13 @@
|
||||
define([
|
||||
'text!./entry.ejs',
|
||||
'text!./main.ejs',
|
||||
'text!./deepforge.ejs',
|
||||
'text!./serialize.ejs',
|
||||
'text!./deserialize.ejs'
|
||||
], function(
|
||||
ENTRY,
|
||||
MAIN,
|
||||
DEEPFORGE,
|
||||
SERIALIZE,
|
||||
DESERIALIZE
|
||||
) {
|
||||
@@ -17,6 +19,7 @@ define([
|
||||
ENTRY,
|
||||
MAIN,
|
||||
SERIALIZE,
|
||||
DEEPFORGE,
|
||||
DESERIALIZE
|
||||
};
|
||||
});
|
||||
@@ -3,23 +3,19 @@
|
||||
|
||||
define([
|
||||
'plugin/CreateExecution/CreateExecution/CreateExecution',
|
||||
'deepforge/plugin/PtrCodeGen',
|
||||
'plugin/ExecuteJob/ExecuteJob/ExecuteJob',
|
||||
'common/storage/constants',
|
||||
'common/core/constants',
|
||||
'q',
|
||||
'text!./metadata.json',
|
||||
'./templates/index',
|
||||
'deepforge/plugin/LocalExecutor',
|
||||
'executor/ExecutorClient',
|
||||
'underscore'
|
||||
], function (
|
||||
CreateExecution,
|
||||
PtrCodeGen,
|
||||
ExecuteJob,
|
||||
STORAGE_CONSTANTS,
|
||||
CONSTANTS,
|
||||
Q,
|
||||
pluginMetadata,
|
||||
Templates,
|
||||
LocalExecutor, // DeepForge operation primitives
|
||||
ExecutorClient,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
@@ -32,8 +28,6 @@ define([
|
||||
* @classdesc This class represents the plugin ExecutePipeline.
|
||||
* @constructor
|
||||
*/
|
||||
var OUTPUT_INTERVAL = 1500,
|
||||
STDOUT_FILE = 'job_stdout.txt';
|
||||
var ExecutePipeline = function () {
|
||||
// Call base class' constructor.
|
||||
CreateExecution.call(this);
|
||||
@@ -49,12 +43,13 @@ define([
|
||||
* @type {object}
|
||||
*/
|
||||
ExecutePipeline.metadata = pluginMetadata;
|
||||
ExecutePipeline.UPDATE_INTERVAL = 1500;
|
||||
|
||||
// Prototypical inheritance from CreateExecution.
|
||||
ExecutePipeline.prototype = Object.create(CreateExecution.prototype);
|
||||
ExecutePipeline.prototype.constructor = ExecutePipeline;
|
||||
|
||||
_.extend(ExecutePipeline.prototype, ExecuteJob.prototype);
|
||||
|
||||
ExecutePipeline.prototype.initRun = function () {
|
||||
// Cache
|
||||
this.nodes = {};
|
||||
@@ -80,6 +75,12 @@ define([
|
||||
// - if so, don't start any more jobs
|
||||
this.pipelineError = null;
|
||||
this.runningJobs = 0;
|
||||
|
||||
// metadata records
|
||||
this._metadata = {};
|
||||
this._markForDeletion = {}; // id -> node
|
||||
this._oldMetadataByName = {}; // name -> id
|
||||
this.lastAppliedCmd = {};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -103,10 +104,9 @@ define([
|
||||
startPromise = this.createExecution(this.activeNode)
|
||||
.then(execNode => {
|
||||
this.activeNode = execNode;
|
||||
return this.core.loadSubTree(this.activeNode);
|
||||
});
|
||||
} else if (this.core.isTypeOf(this.activeNode, this.META.Execution)) {
|
||||
startPromise = this.core.loadSubTree(this.activeNode);
|
||||
startPromise = Q();
|
||||
} else {
|
||||
return callback('Current node is not a Pipeline or Execution!', this.result);
|
||||
}
|
||||
@@ -115,7 +115,9 @@ define([
|
||||
this.debug = true; // this.getCurrentConfig().debug;
|
||||
this._callback = callback;
|
||||
|
||||
startPromise.then(subtree => {
|
||||
startPromise
|
||||
.then(() => this.core.loadSubTree(this.activeNode))
|
||||
.then(subtree => {
|
||||
var children = subtree
|
||||
.filter(n => this.core.getParent(n) === this.activeNode);
|
||||
|
||||
@@ -129,12 +131,36 @@ define([
|
||||
.fail(e => this.logger.error(e));
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.updateForkName = function () {
|
||||
var basename = this.pipelineName + '_fork';
|
||||
return this.project.getBranches().then(branches => {
|
||||
var names = Object.keys(branches),
|
||||
name = basename,
|
||||
i = 2;
|
||||
|
||||
while (names.indexOf(name) !== -1) {
|
||||
name = basename + '_' + i;
|
||||
i++;
|
||||
}
|
||||
|
||||
this.forkName = name;
|
||||
});
|
||||
};
|
||||
|
||||
// Override 'save' to prevent race conditions while saving
|
||||
ExecutePipeline.prototype.save = function (msg) {
|
||||
// When 'save' is called, it should still finish any current save op
|
||||
// before continuing
|
||||
this._currentSave = this._currentSave
|
||||
.then(() => CreateExecution.prototype.save.call(this, msg));
|
||||
.then(() => this.updateForkName())
|
||||
.then(() => CreateExecution.prototype.save.call(this, msg))
|
||||
.then(result => {
|
||||
var msg;
|
||||
if (result.status === STORAGE_CONSTANTS.FORKED) {
|
||||
msg = `"${this.pipelineName}" execution has forked to "${result.forkName}"`;
|
||||
this.sendNotification(msg);
|
||||
}
|
||||
});
|
||||
|
||||
return this._currentSave;
|
||||
};
|
||||
@@ -156,7 +182,10 @@ define([
|
||||
|
||||
// Set the status for each job to 'pending'
|
||||
nodes.filter(node => this.core.isTypeOf(node, this.META.Job))
|
||||
.forEach(node => this.core.setAttribute(node, 'status', 'pending'));
|
||||
.forEach(node => {
|
||||
this.recordOldMetadata(node);
|
||||
this.core.setAttribute(node, 'status', 'pending');
|
||||
});
|
||||
|
||||
// Set the status of the execution to 'running'
|
||||
this.core.setAttribute(this.activeNode, 'status', 'running');
|
||||
@@ -186,9 +215,7 @@ define([
|
||||
this.completedCount = 0;
|
||||
|
||||
// Get all connections
|
||||
conns = nodes.filter(node =>
|
||||
this.core.getPointerPath(node, 'src') && this.core.getPointerPath(node, 'dst')
|
||||
);
|
||||
conns = this.getConnections(nodes);
|
||||
|
||||
// Get all operations
|
||||
nodes
|
||||
@@ -251,6 +278,17 @@ define([
|
||||
this.executeReadyOperations();
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onOperationFail = function(node, err) {
|
||||
var job = this.core.getParent(node),
|
||||
id = this.core.getPath(node),
|
||||
name = this.core.getAttribute(node, 'name');
|
||||
|
||||
this.logger.debug(`Operation ${name} (${id}) failed: ${err}`);
|
||||
this.core.setAttribute(job, 'status', 'fail');
|
||||
this.clearOldMetadata(job);
|
||||
this.onPipelineComplete(err);
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onPipelineComplete = function(err) {
|
||||
var name = this.core.getAttribute(this.activeNode, 'name');
|
||||
|
||||
@@ -298,276 +336,16 @@ define([
|
||||
readyOps.forEach(jobId => {
|
||||
delete this.incomingCounts[jobId];
|
||||
});
|
||||
this.logger.info(`Found ${readyOps.length} ready job(s)`);
|
||||
readyOps.reduce((prev, jobId) => {
|
||||
return prev.then(() => this.executeOperation(jobId));
|
||||
return prev.then(() => this.executeJob(this.nodes[jobId]));
|
||||
}, Q());
|
||||
this.runningJobs += readyOps.length;
|
||||
this.logger.info(`There are now ${this.runningJobs} running jobs`);
|
||||
this.logger.info(`There ${this.runningJobs === 1 ? 'is' : 'are'} now ${this.runningJobs} running job(s)`);
|
||||
|
||||
return readyOps.length;
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.getOperation = function (jobId) {
|
||||
var node = this.nodes[jobId],
|
||||
children = this.core.getChildrenPaths(node).map(id => this.nodes[id]);
|
||||
|
||||
// Currently, jobs
|
||||
return children.find(child => this.isMetaTypeOf(child, this.META.Operation));
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.executeOperation = function (jobId) {
|
||||
var node = this.getOperation(jobId),
|
||||
name = this.core.getAttribute(node, 'name'),
|
||||
localTypeId = this.getLocalOperationType(node),
|
||||
artifact,
|
||||
artifactName,
|
||||
files,
|
||||
data = {},
|
||||
inputs;
|
||||
|
||||
// Execute any special operation types here - not on an executor
|
||||
this.logger.debug(`Executing operation "${name}"`);
|
||||
if (localTypeId !== null) {
|
||||
return this.executeLocalOperation(localTypeId, node);
|
||||
} else {
|
||||
// Generate all execution files
|
||||
return this.createOperationFiles(node).then(results => {
|
||||
this.logger.info('Created operation files!');
|
||||
files = results;
|
||||
artifactName = `${name}_${jobId.replace(/\//g, '_')}-execution-files`;
|
||||
artifact = this.blobClient.createArtifact(artifactName);
|
||||
|
||||
// Add the input assets
|
||||
// - get the metadata (name)
|
||||
// - add the given inputs
|
||||
inputs = Object.keys(files.inputAssets);
|
||||
|
||||
return Q.all(
|
||||
inputs.map(input => { // Get the metadata for each input
|
||||
var hash = files.inputAssets[input];
|
||||
|
||||
// data asset for "input"
|
||||
return this.blobClient.getMetadata(hash);
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(mds => {
|
||||
// get (input, filename) tuples
|
||||
mds.forEach((metadata, i) => {
|
||||
// add the hashes for each input
|
||||
var input = inputs[i],
|
||||
name = metadata.name,
|
||||
hash = files.inputAssets[input];
|
||||
|
||||
data['inputs/' + input + '/' + name] = hash;
|
||||
});
|
||||
|
||||
delete files.inputAssets;
|
||||
|
||||
// Add pointer assets
|
||||
Object.keys(files.ptrAssets)
|
||||
.forEach(path => data[path] = files.ptrAssets[path]);
|
||||
|
||||
delete files.ptrAssets;
|
||||
|
||||
// Add the executor config
|
||||
return this.getOutputs(node);
|
||||
})
|
||||
.then(outputArgs => {
|
||||
var config,
|
||||
outputs,
|
||||
file;
|
||||
|
||||
outputs = outputArgs.map(pair => pair[0])
|
||||
.map(name => {
|
||||
return {
|
||||
name: name,
|
||||
resultPatterns: [`outputs/${name}`]
|
||||
};
|
||||
});
|
||||
|
||||
outputs.push({
|
||||
name: 'stdout',
|
||||
resultPatterns: [STDOUT_FILE]
|
||||
});
|
||||
|
||||
if (this.debug) {
|
||||
outputs.push({
|
||||
name: name + '-all-files',
|
||||
resultPatterns: []
|
||||
});
|
||||
}
|
||||
|
||||
config = {
|
||||
cmd: 'bash',
|
||||
args: ['run.sh'],
|
||||
outputInterval: OUTPUT_INTERVAL,
|
||||
resultArtifacts: outputs
|
||||
};
|
||||
files['executor_config.json'] = JSON.stringify(config, null, 4);
|
||||
files['run.sh'] = Templates.BASH;
|
||||
|
||||
// Save the artifact
|
||||
// Remove empty hashes
|
||||
for (file in data) {
|
||||
if (!data[file]) {
|
||||
this.logger.warn(`Empty data hash has been found for file "${file}". Removing it...`);
|
||||
delete data[file];
|
||||
}
|
||||
}
|
||||
return artifact.addObjectHashes(data);
|
||||
})
|
||||
.then(() => {
|
||||
this.logger.info(`Added ptr/input data hashes for "${artifactName}"`);
|
||||
return artifact.addFiles(files);
|
||||
})
|
||||
.then(() => {
|
||||
this.logger.info(`Added execution files for "${artifactName}"`);
|
||||
return artifact.save();
|
||||
})
|
||||
.then(hash => {
|
||||
this.logger.info(`Saved execution files "${artifactName}"`);
|
||||
this.result.addArtifact(hash); // Probably only need this for debugging...
|
||||
this.executeDistOperation(jobId, node, hash);
|
||||
})
|
||||
.fail(e => {
|
||||
this.core.setAttribute(this.nodes[jobId], 'status', 'fail');
|
||||
this.logger.info(`Setting ${jobId} status to "fail"`);
|
||||
this.onPipelineComplete(`Distributed operation "${name}" failed ${e}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.executeDistOperation = function (jobId, opNode, hash) {
|
||||
var name = this.core.getAttribute(opNode, 'name'),
|
||||
opId = this.core.getPath(opNode),
|
||||
executor = new ExecutorClient({
|
||||
logger: this.logger,
|
||||
serverPort: this.gmeConfig.server.port
|
||||
});
|
||||
|
||||
this.logger.info(`Executing operation "${name}"`);
|
||||
|
||||
this.outputLineCount[jobId] = 0;
|
||||
// Set the job status to 'running'
|
||||
this.core.setAttribute(this.nodes[jobId], 'status', 'queued');
|
||||
this.core.setAttribute(this.nodes[jobId], 'stdout', '');
|
||||
this.logger.info(`Setting ${jobId} status to "queued" (${this.currentHash})`);
|
||||
this.logger.debug(`Making a commit from ${this.currentHash}`);
|
||||
this.save(`Queued "${name}" operation in ${this.pipelineName}`)
|
||||
.then(() => executor.createJob({hash}))
|
||||
.then(() => this.watchOperation(executor, hash, opId, jobId))
|
||||
.catch(err => this.logger.error(`Could not execute "${name}": ${err}`));
|
||||
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.watchOperation = function (executor, hash, opId, jobId) {
|
||||
var job = this.nodes[jobId],
|
||||
info,
|
||||
name;
|
||||
|
||||
return executor.getInfo(hash)
|
||||
.then(_info => { // Update the job's stdout
|
||||
var actualLine, // on executing job
|
||||
currentLine = this.outputLineCount[jobId];
|
||||
|
||||
info = _info;
|
||||
actualLine = info.outputNumber;
|
||||
if (actualLine !== null && actualLine >= currentLine) {
|
||||
this.outputLineCount[jobId] = actualLine + 1;
|
||||
return executor.getOutput(hash, currentLine, actualLine+1)
|
||||
.then(outputLines => {
|
||||
var stdout = this.core.getAttribute(job, 'stdout'),
|
||||
output = outputLines.map(o => o.output).join(''),
|
||||
jobName = this.core.getAttribute(job, 'name');
|
||||
|
||||
// Handle the \b
|
||||
// TODO
|
||||
stdout += output;
|
||||
this.core.setAttribute(job, 'stdout', stdout);
|
||||
return this.save(`Received stdout for ${jobName}`);
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (info.status === 'CREATED' || info.status === 'RUNNING') {
|
||||
if (info.status === 'RUNNING' &&
|
||||
this.core.getAttribute(this.nodes[jobId], 'status') !== 'running') {
|
||||
|
||||
name = this.core.getAttribute(this.nodes[jobId], 'name');
|
||||
this.core.setAttribute(this.nodes[jobId], 'status', 'running');
|
||||
this.save(`Started "${name}" operation in ${this.pipelineName}`);
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
this.watchOperation.bind(this, executor, hash, opId, jobId),
|
||||
ExecutePipeline.UPDATE_INTERVAL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
name = this.core.getAttribute(job, 'name');
|
||||
this.core.setAttribute(job, 'execFiles', info.resultHashes[name + '-all-files']);
|
||||
return this.blobClient.getArtifact(info.resultHashes.stdout)
|
||||
.then(artifact => {
|
||||
var stdoutHash = artifact.descriptor.content[STDOUT_FILE].content;
|
||||
return this.blobClient.getObjectAsString(stdoutHash);
|
||||
})
|
||||
.then(stdout => {
|
||||
this.core.setAttribute(job, 'stdout', stdout);
|
||||
if (info.status !== 'SUCCESS') {
|
||||
// Download all files
|
||||
this.result.addArtifact(info.resultHashes[name + '-all-files']);
|
||||
// Set the job to failed! Store the error
|
||||
this.core.setAttribute(this.nodes[jobId], 'status', 'fail');
|
||||
this.logger.info(`Setting ${jobId} status to "fail"`);
|
||||
this.onPipelineComplete(`Operation "${opId}" failed! ${JSON.stringify(info)}`); // Failed
|
||||
} else {
|
||||
if (this.debug) {
|
||||
this.result.addArtifact(info.resultHashes[name + '-all-files']);
|
||||
}
|
||||
this.onDistOperationComplete(opId, info);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => this.logger.error(`Could not get op info for ${opId}: ${err}`));
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onDistOperationComplete = function (nodeId, result) {
|
||||
var node = this.nodes[nodeId],
|
||||
outputMap = {},
|
||||
outputs;
|
||||
|
||||
// Match the output names to the actual nodes
|
||||
// Create an array of [name, node]
|
||||
// For now, just match by type. Later we may use ports for input/outputs
|
||||
// Store the results in the outgoing ports
|
||||
this.getOutputs(node)
|
||||
.then(outputPorts => {
|
||||
outputs = outputPorts.map(tuple => [tuple[0], tuple[2]]);
|
||||
outputs.forEach(output => outputMap[output[0]] = output[1]);
|
||||
|
||||
// this should not be in directories -> flatten the data!
|
||||
return Q.all(outputs.map(tuple => // [ name, node ]
|
||||
this.blobClient.getArtifact(result.resultHashes[tuple[0]])
|
||||
));
|
||||
})
|
||||
.then(artifacts => {
|
||||
this.logger.info(`preparing outputs -> retrieved ${artifacts.length} objects`);
|
||||
// Create new metadata for each
|
||||
artifacts.forEach((artifact, i) => {
|
||||
var name = outputs[i][0],
|
||||
hash = artifact.descriptor.content[`outputs/${name}`].content;
|
||||
|
||||
this.core.setAttribute(outputMap[name], 'data', hash);
|
||||
this.logger.info(`Setting ${nodeId} data to ${hash}`);
|
||||
});
|
||||
|
||||
return this.onOperationComplete(node);
|
||||
})
|
||||
.fail(e => this.onPipelineComplete(`Operation ${nodeId} failed: ${e}`));
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.onOperationComplete = function (opNode) {
|
||||
var name = this.core.getAttribute(opNode, 'name'),
|
||||
nextPortIds = this.getOperationOutputIds(opNode),
|
||||
@@ -577,6 +355,7 @@ define([
|
||||
hasReadyOps;
|
||||
|
||||
// Set the operation to 'success'!
|
||||
this.clearOldMetadata(jNode);
|
||||
this.runningJobs--;
|
||||
this.core.setAttribute(jNode, 'status', 'success');
|
||||
this.logger.info(`Setting ${jobId} status to "success"`);
|
||||
@@ -634,305 +413,5 @@ define([
|
||||
return this.getOperationOutputIds(node).map(id => this.nodes[id]);
|
||||
};
|
||||
|
||||
//////////////////////////// Operation File/Dir Creators ////////////////////////////
|
||||
ExecutePipeline.prototype.createOperationFiles = function (node) {
|
||||
var files = {};
|
||||
// For each operation, generate the output files:
|
||||
// inputs/<arg-name>/init.lua (respective data deserializer)
|
||||
// pointers/<name>/init.lua (result of running the main plugin on pointer target - may need a rename)
|
||||
// outputs/<name>/ (make dirs for each of the outputs)
|
||||
// outputs/init.lua (serializers for data outputs)
|
||||
//
|
||||
// attributes.lua (returns lua table of operation attributes)
|
||||
// init.lua (main file -> calls main and serializes outputs)
|
||||
// <name>.lua (entry point -> calls main operation code)
|
||||
|
||||
// add the given files
|
||||
this.logger.info('About to create dist execution files');
|
||||
return this.createEntryFile(node, files)
|
||||
.then(() => this.createClasses(node, files))
|
||||
.then(() => this.createCustomLayers(node, files))
|
||||
.then(() => this.createInputs(node, files))
|
||||
.then(() => this.createOutputs(node, files))
|
||||
.then(() => this.createMainFile(node, files))
|
||||
.then(() => {
|
||||
this.createAttributeFile(node, files);
|
||||
return Q.ninvoke(this, 'createPointers', node, files);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createClasses = function (node, files) {
|
||||
var isClass = {},
|
||||
metaDict = this.core.getAllMetaNodes(this.rootNode),
|
||||
metanodes,
|
||||
classNodes,
|
||||
code;
|
||||
|
||||
this.logger.info('Creating custom layer file...');
|
||||
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
|
||||
// Get all the custom layers
|
||||
metanodes.forEach(node => {
|
||||
if (this.core.getAttribute(node, 'name') === 'Complex') {
|
||||
isClass[this.core.getPath(node)] = true;
|
||||
}
|
||||
});
|
||||
classNodes = metanodes.filter(node => {
|
||||
var base = this.core.getBase(node),
|
||||
baseId = this.core.getPath(base);
|
||||
|
||||
return isClass[baseId];
|
||||
});
|
||||
|
||||
// Get the code definitions for each
|
||||
code = classNodes.map(node =>
|
||||
`require './${this.core.getAttribute(node, 'name')}.lua'`
|
||||
).join('\n');
|
||||
|
||||
// Create the class files
|
||||
classNodes.forEach(node => {
|
||||
var name = this.core.getAttribute(node, 'name');
|
||||
files[`classes/${name}.lua`] = this.core.getAttribute(node, 'code');
|
||||
});
|
||||
|
||||
// Create the custom layers file
|
||||
files['classes/init.lua'] = code;
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createCustomLayers = function (node, files) {
|
||||
var isCustomLayer = {},
|
||||
metaDict = this.core.getAllMetaNodes(this.rootNode),
|
||||
metanodes,
|
||||
customLayers,
|
||||
code;
|
||||
|
||||
this.logger.info('Creating custom layer file...');
|
||||
metanodes = Object.keys(metaDict).map(id => metaDict[id]);
|
||||
// Get all the custom layers
|
||||
metanodes.forEach(node => {
|
||||
if (this.core.getAttribute(node, 'name') === 'CustomLayer') {
|
||||
isCustomLayer[this.core.getPath(node)] = true;
|
||||
}
|
||||
});
|
||||
customLayers = metanodes.filter(node =>
|
||||
this.core.getMixinPaths(node).some(id => isCustomLayer[id]));
|
||||
|
||||
// Get the code definitions for each
|
||||
code = 'require \'nn\'\n\n' + customLayers
|
||||
.map(node => this.core.getAttribute(node, 'code')).join('\n');
|
||||
|
||||
// Create the custom layers file
|
||||
files['custom-layers.lua'] = code;
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createInputs = function (node, files) {
|
||||
var tplContents;
|
||||
this.logger.info('Retrieving inputs and deserialize fns...');
|
||||
return this.getInputs(node)
|
||||
.then(inputs => {
|
||||
// For each input, match the connection with the input name
|
||||
// [ name, type ] => [ name, type, node ]
|
||||
//
|
||||
// For each input,
|
||||
// - create the deserializer
|
||||
// - put it in inputs/<name>/init.lua
|
||||
// - copy the data asset to /inputs/<name>/init.lua
|
||||
inputs = inputs
|
||||
.filter(pair => !!this.core.getAttribute(pair[2], 'data')); // remove empty inputs
|
||||
|
||||
files.inputAssets = {}; // data assets
|
||||
tplContents = inputs.map(pair => {
|
||||
var name = pair[0],
|
||||
node = pair[2],
|
||||
deserFn = this.core.getAttribute(node, 'deserialize'),
|
||||
base,
|
||||
className;
|
||||
|
||||
if (this.isMetaTypeOf(node, this.META.Complex)) {
|
||||
// Complex objects are expected to define their own
|
||||
// (static) deserialize factory method
|
||||
base = this.core.getBase(node);
|
||||
className = this.core.getAttribute(base, 'name');
|
||||
deserFn = `return ${className}.deserialize(path)`;
|
||||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
code: deserFn
|
||||
};
|
||||
});
|
||||
var hashes = inputs
|
||||
// storing the hash for now...
|
||||
.map(pair =>
|
||||
files.inputAssets[pair[0]] = this.core.getAttribute(pair[2], 'data')
|
||||
);
|
||||
return Q.all(hashes.map(h => this.blobClient.getMetadata(h)));
|
||||
})
|
||||
.then(metadatas => {
|
||||
// Create the deserializer
|
||||
tplContents.forEach((ctnt, i) => {
|
||||
// Get the name of the given asset
|
||||
ctnt.filename = metadatas[i].name;
|
||||
files['inputs/' + ctnt.name + '/init.lua'] = _.template(Templates.DESERIALIZE)(ctnt);
|
||||
});
|
||||
return files;
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createPointers = function (node, files, cb) {
|
||||
var pointers,
|
||||
nIds;
|
||||
|
||||
this.logger.info('Creating pointers file...');
|
||||
pointers = this.core.getPointerNames(node)
|
||||
.filter(name => name !== 'base')
|
||||
.filter(id => this.core.getPointerPath(node, id) !== null);
|
||||
|
||||
nIds = pointers.map(p => this.core.getPointerPath(node, p));
|
||||
files.ptrAssets = {};
|
||||
Q.all(
|
||||
nIds.map(nId => this.getPtrCodeHash(nId))
|
||||
)
|
||||
.then(resultHashes => {
|
||||
var name = this.core.getAttribute(node, 'name');
|
||||
this.logger.info(`Pointer generation for ${name} FINISHED!`);
|
||||
resultHashes.forEach((hash, index) => {
|
||||
files.ptrAssets[`pointers/${pointers[index]}/init.lua`] = hash;
|
||||
});
|
||||
return cb(null, files);
|
||||
})
|
||||
.fail(e => {
|
||||
this.logger.error(`Could not generate pointer files for ${this.core.getAttribute(node, 'name')}: ${JSON.stringify(e)}`);
|
||||
return cb(e);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createOutputs = function (node, files) {
|
||||
// For each of the output types, grab their serialization functions and
|
||||
// create the `outputs/init.lua` file
|
||||
this.logger.info('Creating outputs/init.lua...');
|
||||
return this.getOutputs(node)
|
||||
.then(outputs => {
|
||||
var outputTypes = outputs
|
||||
// Get the serialize functions for each
|
||||
.map(tuple => {
|
||||
var node = tuple[2],
|
||||
serFn = this.core.getAttribute(node, 'serialize');
|
||||
|
||||
if (this.isMetaTypeOf(node, this.META.Complex)) {
|
||||
// Complex objects are expected to define their own
|
||||
// serialize methods
|
||||
serFn = 'data:serialize(path)';
|
||||
}
|
||||
|
||||
return [tuple[1], serFn];
|
||||
});
|
||||
|
||||
files['outputs/init.lua'] = _.template(Templates.SERIALIZE)({types: outputTypes});
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.getOutputs = function (node) {
|
||||
return this.getOperationData(node, this.META.Outputs);
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.getInputs = function (node) {
|
||||
return this.getOperationData(node, this.META.Inputs);
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.getOperationData = function (node, metaType) {
|
||||
// Load the children and the output's children
|
||||
return this.core.loadChildren(node)
|
||||
.then(containers => {
|
||||
var outputs = containers.find(c => this.core.isTypeOf(c, metaType));
|
||||
return outputs ? this.core.loadChildren(outputs) : [];
|
||||
})
|
||||
.then(outputs => {
|
||||
var bases = outputs.map(node => this.core.getMetaType(node));
|
||||
// return [[arg1, Type1, node1], [arg2, Type2, node2]]
|
||||
return outputs.map((node, i) => [
|
||||
this.core.getAttribute(node, 'name'),
|
||||
this.core.getAttribute(bases[i], 'name'),
|
||||
node
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createEntryFile = function (node, files) {
|
||||
this.logger.info('Creating entry files...');
|
||||
return this.getOutputs(node)
|
||||
.then(outputs => {
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
content = {};
|
||||
|
||||
// sort the outputs by the return values?
|
||||
if (outputs.length > 1) {
|
||||
this.logger.error('Multiple outputs not yet supported!');
|
||||
}
|
||||
|
||||
// inputs and outputs
|
||||
content.name = name;
|
||||
content.outputs = outputs;
|
||||
|
||||
files['init.lua'] = _.template(Templates.ENTRY)(content);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createMainFile = function (node, files) {
|
||||
this.logger.info('Creating main file...');
|
||||
return this.getInputs(node)
|
||||
.then(inputs => {
|
||||
var name = this.core.getAttribute(node, 'name'),
|
||||
code = this.core.getAttribute(node, 'code'),
|
||||
pointers = this.core.getPointerNames(node).filter(ptr => ptr !== 'base'),
|
||||
content = {
|
||||
name: name
|
||||
};
|
||||
|
||||
// Get input data arguments
|
||||
content.inputs = inputs
|
||||
.map(pair => [pair[0], !this.core.getAttribute(pair[2], 'data')]); // remove empty inputs
|
||||
|
||||
// Defined variables for each pointers
|
||||
content.pointers = pointers
|
||||
.map(id => [id, this.core.getPointerPath(node, id) === null]);
|
||||
|
||||
// Add remaining code
|
||||
content.code = code;
|
||||
|
||||
files['main.lua'] = _.template(Templates.MAIN)(content);
|
||||
});
|
||||
};
|
||||
|
||||
ExecutePipeline.prototype.createAttributeFile = function (node, files) {
|
||||
var skip = ['outputs', 'inputs'],
|
||||
table;
|
||||
|
||||
this.logger.info('Creating attributes file...');
|
||||
table = '{\n\t' + this.core.getAttributeNames(node)
|
||||
.filter(attr => skip.indexOf(attr) === -1)
|
||||
.map(name => [name, JSON.stringify(this.core.getAttribute(node, name))])
|
||||
.map(pair => pair.join(' = '))
|
||||
.join(',\n\t') + '\n}';
|
||||
|
||||
files['attributes.lua'] = `-- attributes of ${this.core.getAttribute(node, 'name')}\nreturn ${table}`;
|
||||
};
|
||||
|
||||
//////////////////////////// Special Operations ////////////////////////////
|
||||
ExecutePipeline.prototype.executeLocalOperation = function (type, node) {
|
||||
// Retrieve the given LOCAL_OP type
|
||||
if (!this[type]) {
|
||||
this.logger.error(`No local operation handler for ${type}`);
|
||||
}
|
||||
this.logger.info(`Running local operation ${type}`);
|
||||
|
||||
return this[type](node);
|
||||
};
|
||||
|
||||
_.extend(
|
||||
ExecutePipeline.prototype,
|
||||
LocalExecutor.prototype,
|
||||
PtrCodeGen.prototype
|
||||
);
|
||||
|
||||
return ExecutePipeline;
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"id": "ExecutePipeline",
|
||||
"name": "ExecutePipeline",
|
||||
"name": "Execute Pipeline",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"class": "glyphicon glyphicon-random",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"disableBrowserSideExecution": true,
|
||||
"configStructure": [
|
||||
{
|
||||
"name": "snapshot",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"id": "GenerateArchitecture",
|
||||
"name": "Generate Architecture",
|
||||
"name": "Generate Torch Code",
|
||||
"version": "0.1.0",
|
||||
"description": "Generate torch architecture code",
|
||||
"icon": {
|
||||
"src": "",
|
||||
"class": "glyphicon glyphicon-ok-circle"
|
||||
"class": "glyphicon glyphicon-file"
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*globals define*/
|
||||
/*jshint node:true, browser:true*/
|
||||
|
||||
define([
|
||||
'text!./metadata.json',
|
||||
'plugin/PluginBase'
|
||||
], function (
|
||||
pluginMetadata,
|
||||
PluginBase
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
pluginMetadata = JSON.parse(pluginMetadata);
|
||||
|
||||
/**
|
||||
* Initializes a new instance of GenerateCriterion.
|
||||
* @class
|
||||
* @augments {PluginBase}
|
||||
* @classdesc This class represents the plugin GenerateCriterion.
|
||||
* @constructor
|
||||
*/
|
||||
var GenerateCriterion = function () {
|
||||
// Call base class' constructor.
|
||||
PluginBase.call(this);
|
||||
this.pluginMetadata = pluginMetadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
|
||||
* This is also available at the instance at this.pluginMetadata.
|
||||
* @type {object}
|
||||
*/
|
||||
GenerateCriterion.metadata = pluginMetadata;
|
||||
|
||||
// Prototypical inheritance from PluginBase.
|
||||
GenerateCriterion.prototype = Object.create(PluginBase.prototype);
|
||||
GenerateCriterion.prototype.constructor = GenerateCriterion;
|
||||
|
||||
/**
|
||||
* Main function for the plugin to execute. This will perform the execution.
|
||||
* Notes:
|
||||
* - Always log with the provided logger.[error,warning,info,debug].
|
||||
* - Do NOT put any user interaction logic UI, etc. inside this method.
|
||||
* - callback always has to be called even if error happened.
|
||||
*
|
||||
* @param {function(string, plugin.PluginResult)} callback - the result callback
|
||||
*/
|
||||
GenerateCriterion.prototype.main = function (callback) {
|
||||
// Generate the code for the criterion layer and return a file
|
||||
var name = this.core.getAttribute(this.activeNode, 'name'),
|
||||
code = `require 'nn'\nreturn nn.${name}()`,
|
||||
filename = `${name}.lua`;
|
||||
|
||||
// Using the logger.
|
||||
this.logger.debug(`Generating code for ${name} criterion layer.`);
|
||||
|
||||
// Save the file
|
||||
this.blobClient.putFile(filename, code)
|
||||
.then(hash => {
|
||||
this.result.setSuccess(true);
|
||||
this.result.addArtifact(hash);
|
||||
callback(null, this.result);
|
||||
})
|
||||
.catch(err => callback(err, this.result));
|
||||
|
||||
};
|
||||
|
||||
return GenerateCriterion;
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "GenerateCriterion",
|
||||
"name": "Generate Criterion Code",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
"disableBrowserSideExecution": false,
|
||||
"writeAccessRequired": false,
|
||||
"configStructure": []
|
||||
}
|
||||
@@ -78,29 +78,32 @@ define([
|
||||
}
|
||||
|
||||
// Get the base node
|
||||
dataNode = this.core.createNode({
|
||||
base: baseType,
|
||||
parent: this.activeNode
|
||||
});
|
||||
|
||||
this.core.setAttribute(dataNode, 'data', hash);
|
||||
baseName = this.core.getAttribute(baseType, 'name');
|
||||
|
||||
var getName;
|
||||
if (config.name) {
|
||||
getName = Q().then(() => config.name);
|
||||
} else {
|
||||
getName = this.blobClient.getMetadata(hash)
|
||||
.then(md => {
|
||||
name = baseName[0].toLowerCase() + baseName.substring(1);
|
||||
if (md) {
|
||||
name = md.name.replace(/\.[^\.]*?$/, '');
|
||||
}
|
||||
return name;
|
||||
this.getArtifactsDir()
|
||||
.then(targetDir => {
|
||||
dataNode = this.core.createNode({
|
||||
base: baseType,
|
||||
parent: targetDir
|
||||
});
|
||||
}
|
||||
|
||||
getName.then(name => this.core.setAttribute(dataNode, 'name', name))
|
||||
this.core.setAttribute(dataNode, 'data', hash);
|
||||
baseName = this.core.getAttribute(baseType, 'name');
|
||||
|
||||
var getName;
|
||||
if (config.name) {
|
||||
getName = Q().then(() => config.name);
|
||||
} else {
|
||||
getName = this.blobClient.getMetadata(hash)
|
||||
.then(md => {
|
||||
name = baseName[0].toLowerCase() + baseName.substring(1);
|
||||
if (md) {
|
||||
name = md.name.replace(/\.[^\.]*?$/, '');
|
||||
}
|
||||
return name;
|
||||
});
|
||||
}
|
||||
return getName;
|
||||
})
|
||||
.then(name => this.core.setAttribute(dataNode, 'name', name))
|
||||
.then(() => this.save(`Uploaded "${name}" data`))
|
||||
.then(function () {
|
||||
self.result.setSuccess(true);
|
||||
@@ -112,5 +115,14 @@ define([
|
||||
|
||||
};
|
||||
|
||||
ImportArtifact.prototype.getArtifactsDir = function() {
|
||||
// Find the artifacts dir
|
||||
return this.core.loadChildren(this.rootNode)
|
||||
.then(children => children
|
||||
.find(child => this.core.getAttribute(child, 'name') === 'MyArtifacts') ||
|
||||
this.activeNode
|
||||
);
|
||||
};
|
||||
|
||||
return ImportArtifact;
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"id": "ImportArtifact",
|
||||
"name": "ImportArtifact",
|
||||
"name": "Import Artifact",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"icon": {
|
||||
"class": "glyphicon glyphicon-cog",
|
||||
"class": "glyphicon glyphicon-cloud-upload",
|
||||
"src": ""
|
||||
},
|
||||
"disableServerSideExecution": false,
|
||||
|
||||
@@ -57,12 +57,17 @@ define([
|
||||
|
||||
this.blobClient.getMetadata(srcHash)
|
||||
.then(mdata => { // Create the new model
|
||||
var name = mdata.name.replace('.lua', '');
|
||||
this.tgtNode = this.core.createNode({
|
||||
base: this.META.Architecture,
|
||||
parent: this.activeNode
|
||||
});
|
||||
this.core.setAttribute(this.tgtNode, 'name', name);
|
||||
// If the current node is an architecture, assume we are just extending it
|
||||
this.importedName = mdata.name.replace('.lua', '');
|
||||
if (this.isMetaTypeOf(this.activeNode, this.META.Architecture)) {
|
||||
this.tgtNode = this.activeNode;
|
||||
} else { // Create a new architecture
|
||||
this.tgtNode = this.core.createNode({
|
||||
base: this.META.Architecture,
|
||||
parent: this.activeNode
|
||||
});
|
||||
this.core.setAttribute(this.tgtNode, 'name', this.importedName);
|
||||
}
|
||||
return this.blobClient.getObjectAsString(srcHash);
|
||||
})
|
||||
.then(src => { // Retrieved the source code
|
||||
@@ -81,7 +86,7 @@ define([
|
||||
return this.save('ImportTorch updated model.');
|
||||
})
|
||||
.then(() => { // changes saved successfully
|
||||
var name = this.core.getAttribute(this.tgtNode, 'name');
|
||||
var name = this.importedName;
|
||||
this.result.setSuccess(true);
|
||||
this.createMessage(this.tgtNode,
|
||||
`Successfully imported ${name} architecture`);
|
||||
@@ -96,7 +101,7 @@ define([
|
||||
ImportTorch.prototype.loadNNMock = function () {
|
||||
// This needs a refactor...
|
||||
// createNN(this)
|
||||
var lib = createNNSearcher(this).bind(this.context);
|
||||
var lib = createNNSearcher(this, this.context).bind(this.context);
|
||||
|
||||
// Create a "searcher" to allow this 'nn' to be in the lib path
|
||||
this.context._G.get('package').set('searchers', [function(name) {
|
||||
|
||||
@@ -11,12 +11,23 @@ define([
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var createSearcher = function(plugin) {
|
||||
var createSearcher = function(plugin, context) {
|
||||
var core = plugin.core,
|
||||
META = plugin.META,
|
||||
logger = plugin.logger.fork('nn'),
|
||||
parent = plugin.tgtNode,
|
||||
LayerDict = createLayerDict(core, META);
|
||||
LayerDict = createLayerDict(core, META),
|
||||
helpers = context.__helpers,
|
||||
oldSet = helpers.__set,
|
||||
isSetting = false;
|
||||
|
||||
// Override the helper's '__set' method to detect
|
||||
// if the code is in the middle of a "set".
|
||||
helpers.__set = function() {
|
||||
isSetting = true;
|
||||
oldSet.apply(this, arguments);
|
||||
isSetting = false;
|
||||
};
|
||||
|
||||
var connect = function(src, dst) {
|
||||
var conn = core.createNode({
|
||||
@@ -145,6 +156,7 @@ define([
|
||||
var CreateLayer = function(type) {
|
||||
var res = luajs.newContext()._G,
|
||||
attrs = [].slice.call(arguments, 1),
|
||||
ltGet = luajs.types.LuaTable.prototype.get,
|
||||
node;
|
||||
|
||||
if (LAYERS[type]) {
|
||||
@@ -165,6 +177,16 @@ define([
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override get
|
||||
res.get = function noNilGet(value) {
|
||||
var result = ltGet.call(this, value);
|
||||
if (!result && !isSetting) {
|
||||
throw Error(`"${value}" is not supported for ${type}`);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
@@ -31,26 +31,8 @@
|
||||
},
|
||||
{
|
||||
"id": "RootViz",
|
||||
"title": "RootViz",
|
||||
"panel": "panels/RootViz/RootVizPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "TilingViz",
|
||||
"title": "TilingViz",
|
||||
"panel": "panels/TilingViz/TilingVizPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "OperationCodeEditor",
|
||||
"title": "OperationCodeEditor",
|
||||
"panel": "panels/OperationCodeEditor/OperationCodeEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "OperationInterfaceEditor",
|
||||
"title": "OperationInterfaceEditor",
|
||||
"panel": "panels/OperationInterfaceEditor/OperationInterfaceEditorPanel",
|
||||
"title": "MainView",
|
||||
"panel": "panels/MainView/MainViewPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
@@ -59,24 +41,6 @@
|
||||
"panel": "panels/DataTypeEditor/DataTypeEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "SerializeEditor",
|
||||
"title": "SerializeEditor",
|
||||
"panel": "panels/SerializeEditor/SerializeEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "DeserializeEditor",
|
||||
"title": "DeserializeEditor",
|
||||
"panel": "panels/DeserializeEditor/DeserializeEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "Footer",
|
||||
"title": "Footer",
|
||||
"panel": "panels/Footer/FooterPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "LogViewer",
|
||||
"title": "LogViewer",
|
||||
@@ -100,5 +64,29 @@
|
||||
"title": "ClassEditor",
|
||||
"panel": "panels/ClassEditor/ClassEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "PipelineIndex",
|
||||
"title": "PipelineIndex",
|
||||
"panel": "panels/PipelineIndex/PipelineIndexPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "JobEditor",
|
||||
"title": "JobEditor",
|
||||
"panel": "panels/JobEditor/JobEditorPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "OutputViewer",
|
||||
"title": "OutputViewer",
|
||||
"panel": "panels/OutputViewer/OutputViewerPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "LineGraph",
|
||||
"title": "LineGraph",
|
||||
"panel": "panels/LineGraph/LineGraphPanel",
|
||||
"DEBUG_ONLY": false
|
||||
}
|
||||
]
|
||||
@@ -1,15 +1,14 @@
|
||||
/*globals define */
|
||||
/*jshint browser: true*/
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Tue May 17 2016 11:25:46 GMT-0400 (EDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'deepforge/globals',
|
||||
'panels/EasyDAG/EasyDAGControl',
|
||||
'js/NodePropertyNames',
|
||||
'js/Utils/ComponentSettings',
|
||||
'underscore'
|
||||
], function (
|
||||
DeepForge,
|
||||
EasyDAGControl,
|
||||
nodePropertyNames,
|
||||
ComponentSettings,
|
||||
@@ -43,6 +42,16 @@ define([
|
||||
return 'ArchEditor';
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype.selectedObjectChanged = function(id) {
|
||||
EasyDAGControl.prototype.selectedObjectChanged.call(this, id);
|
||||
|
||||
DeepForge.last.Architecture = id;
|
||||
if (typeof id === 'string') {
|
||||
var name = this._client.getNode(id).getAttribute('name');
|
||||
this._widget.setTitle(name);
|
||||
}
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype._getObjectDescriptor = function(id) {
|
||||
var desc = EasyDAGControl.prototype._getObjectDescriptor.call(this, id);
|
||||
|
||||
@@ -84,6 +93,7 @@ define([
|
||||
return desc;
|
||||
};
|
||||
|
||||
////////////////////////// Layer Selection Logic //////////////////////////
|
||||
ArchEditorControl.prototype._getValidInitialNodes = function() {
|
||||
return this._client.getChildrenMeta(this._currentNodeId).items
|
||||
// For now, anything is possible!
|
||||
@@ -95,7 +105,67 @@ define([
|
||||
return !this._client.getNode(nodeId).isAbstract();
|
||||
})
|
||||
.map(id => this._getObjectDescriptor(id))
|
||||
.filter(obj => !obj.isConnection && obj.name !== 'Connection');
|
||||
.filter(obj => !obj.isConnection && obj.name !== 'Connection')
|
||||
.filter(layer => layer.layerType !== 'Criterion');
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype._getValidSuccessorNodes =
|
||||
ArchEditorControl.prototype._getValidInitialNodes =
|
||||
ArchEditorControl.prototype.getNonCriterionLayers = function() {
|
||||
// Return all (non-criterion) layer types
|
||||
var metanodes = this._client.getAllMetaNodes(),
|
||||
layerId,
|
||||
criterionId,
|
||||
allLayerIds = [],
|
||||
layers = [],
|
||||
i;
|
||||
|
||||
for (i = metanodes.length; i--;) {
|
||||
if (metanodes[i].getAttribute('name') === 'Layer') {
|
||||
layerId = metanodes[i].getId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = metanodes.length; i--;) {
|
||||
if (layerId) {
|
||||
if (!metanodes[i].isAbstract() &&
|
||||
this._client.isTypeOf(metanodes[i].getId(), layerId)) {
|
||||
|
||||
if (metanodes[i].getAttribute('name') === 'Criterion') {
|
||||
criterionId = metanodes[i].getId();
|
||||
} else {
|
||||
allLayerIds.push(metanodes[i].getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all criterion layers and abstract layers
|
||||
for (i = allLayerIds.length; i--;) {
|
||||
if (!this._client.isTypeOf(allLayerIds[i], criterionId)) {
|
||||
layers.push({node: this._getObjectDescriptor(allLayerIds[i])});
|
||||
}
|
||||
}
|
||||
|
||||
return layers;
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype._isValidTerminalNode = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Widget extensions
|
||||
ArchEditorControl.prototype._initWidgetEventHandlers = function() {
|
||||
EasyDAGControl.prototype._initWidgetEventHandlers.call(this);
|
||||
this._widget.getCreateNewDecorator = this.getCreateNewDecorator.bind(this);
|
||||
};
|
||||
|
||||
ArchEditorControl.prototype.getCreateNewDecorator = function() {
|
||||
return this._client.decoratorManager.getDecoratorForWidget(
|
||||
'EllipseDecorator',
|
||||
'EasyDAG'
|
||||
);
|
||||
};
|
||||
|
||||
return ArchEditorControl;
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'deepforge/viz/RenameablePanel',
|
||||
'widgets/ArchEditor/ArchEditorWidget',
|
||||
'./ArchEditorControl'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
RenameablePanel,
|
||||
ArchEditorWidget,
|
||||
ArchEditorControl
|
||||
) {
|
||||
@@ -22,11 +20,11 @@ define([
|
||||
ArchEditorPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'ArchEditorPanel';
|
||||
options[PanelBaseWithHeader.OPTIONS.FLOATING_TITLE] = true;
|
||||
options[RenameablePanel.OPTIONS.LOGGER_INSTANCE_NAME] = 'ArchEditorPanel';
|
||||
options[RenameablePanel.OPTIONS.FLOATING_TITLE] = true;
|
||||
|
||||
//call parent's constructor
|
||||
PanelBaseWithHeader.apply(this, [options, layoutManager]);
|
||||
RenameablePanel.apply(this, [options, layoutManager]);
|
||||
|
||||
this._client = params.client;
|
||||
this._embedded = params.embedded;
|
||||
@@ -37,9 +35,7 @@ define([
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(ArchEditorPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(ArchEditorPanel.prototype, IActivePanel.prototype);
|
||||
_.extend(ArchEditorPanel.prototype, RenameablePanel.prototype);
|
||||
|
||||
ArchEditorPanel.prototype._initialize = function () {
|
||||
var self = this;
|
||||
@@ -60,6 +56,7 @@ define([
|
||||
widget: this.widget
|
||||
});
|
||||
|
||||
this.initializeRenameable();
|
||||
this.onActivate();
|
||||
};
|
||||
|
||||
@@ -67,7 +64,7 @@ define([
|
||||
/* METHOD CALLED WHEN THE WIDGET'S READ-ONLY PROPERTY CHANGES */
|
||||
ArchEditorPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
RenameablePanel.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
@@ -81,7 +78,7 @@ define([
|
||||
this.control.destroy();
|
||||
this.widget.destroy();
|
||||
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
RenameablePanel.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ define([
|
||||
// input/output updates are actually activeNode updates
|
||||
ClassCodeEditorControl.prototype._onUpdate = function (id) {
|
||||
if (id === this._currentNodeId) {
|
||||
TextEditorControl.prototype._onUpdate.call(this, this._currentNodeId);
|
||||
TextEditorControl.prototype._onUpdate.call(this, id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'panels/SerializeEditor/SerializeEditorControl',
|
||||
'panels/TextEditor/TextEditorControl',
|
||||
'underscore'
|
||||
], function (
|
||||
SerializeEditorControl,
|
||||
TextEditorControl,
|
||||
_
|
||||
) {
|
||||
@@ -20,15 +22,8 @@ define([
|
||||
|
||||
_.extend(
|
||||
DeserializeEditorControl.prototype,
|
||||
TextEditorControl.prototype
|
||||
SerializeEditorControl.prototype
|
||||
);
|
||||
|
||||
// input/output updates are actually activeNode updates
|
||||
DeserializeEditorControl.prototype._onUpdate = function (id) {
|
||||
if (id === this._currentNodeId) {
|
||||
TextEditorControl.prototype._onUpdate.call(this, this._currentNodeId);
|
||||
}
|
||||
};
|
||||
|
||||
return DeserializeEditorControl;
|
||||
});
|
||||
|
||||
@@ -68,58 +68,6 @@ define([
|
||||
return null;
|
||||
};
|
||||
|
||||
var UPLOAD_PLUGIN = 'ImportArtifact',
|
||||
DATA_TYPE_CONFIG = {
|
||||
name: 'dataTypeId',
|
||||
displayName: 'Data Type Id',
|
||||
valueType: 'string',
|
||||
valueItems: []
|
||||
};
|
||||
var uploadArtifact = function() {
|
||||
// Get the data types
|
||||
var dataBase,
|
||||
dataBaseId,
|
||||
metanodes = this.client.getAllMetaNodes(),
|
||||
dataTypes = []; // TODO
|
||||
|
||||
dataBase = metanodes.find(n => n.getAttribute('name') === 'Data');
|
||||
|
||||
if (!dataBase) {
|
||||
this.logger.error('Could not find the base Data node!');
|
||||
return;
|
||||
}
|
||||
|
||||
dataBaseId = dataBase.getId();
|
||||
dataTypes = metanodes.filter(n => this.client.isTypeOf(n.getId(), dataBaseId))
|
||||
.map(node => node.getAttribute('name'));
|
||||
|
||||
this.logger.info(`Found ${dataTypes.length} data types`);
|
||||
|
||||
// Add the target type to the pluginMetadata... hacky :/
|
||||
// FIXME: this should create it's own plugin dialog which allows
|
||||
// users to select the data type
|
||||
var metadata = WebGMEGlobal.allPluginsMetadata[UPLOAD_PLUGIN],
|
||||
config = metadata.configStructure
|
||||
.find(opt => opt.name === DATA_TYPE_CONFIG.name);
|
||||
|
||||
if (!config) {
|
||||
config = DATA_TYPE_CONFIG;
|
||||
WebGMEGlobal.allPluginsMetadata[UPLOAD_PLUGIN].configStructure.push(config);
|
||||
}
|
||||
|
||||
config.valueItems = dataTypes;
|
||||
config.value = dataTypes[0];
|
||||
|
||||
WebGMEGlobal.InterpreterManager.configureAndRun(metadata, (result) => {
|
||||
if (!result) {
|
||||
Materialize.toast('Artifact upload failed!', 2000);
|
||||
return;
|
||||
}
|
||||
this.logger.info('Finished uploading ' + UPLOAD_PLUGIN);
|
||||
Materialize.toast('Artifact upload complete!', 2000);
|
||||
});
|
||||
};
|
||||
|
||||
var importTorch = function() {
|
||||
var pluginId = 'ImportTorch',
|
||||
context = this.client.getCurrentPluginContext(pluginId),
|
||||
@@ -144,20 +92,62 @@ define([
|
||||
fileInput.click();
|
||||
};
|
||||
|
||||
var returnToLastPipeline = () => {
|
||||
var returnId = DeepForge.lastPipeline || DeepForge.places.MyPipelines;
|
||||
var returnToLast = (place) => {
|
||||
var returnId = DeepForge.last[place];
|
||||
WebGMEGlobal.State.registerActiveObject(returnId);
|
||||
};
|
||||
|
||||
return {
|
||||
// Meta nodes
|
||||
MyPipelines_META: [
|
||||
var prototypeButtons = function(type, fromType) {
|
||||
return [
|
||||
{
|
||||
name: 'Create new pipeline',
|
||||
icon: 'queue',
|
||||
action: DeepForge.create.Pipeline
|
||||
name: `Return to ${fromType}`,
|
||||
icon: 'input',
|
||||
priority: 2,
|
||||
filter: () => {
|
||||
return DeepForge.last[fromType];
|
||||
},
|
||||
action: returnToLast.bind(null, fromType)
|
||||
},
|
||||
{
|
||||
name: `Delete ${type} Definition`,
|
||||
icon: 'delete',
|
||||
priority: 1,
|
||||
action: function() {
|
||||
// Delete and go to the last pipeline?
|
||||
var node = this.client.getNode(this._currentNodeId),
|
||||
name = node.getAttribute('name'),
|
||||
msg = `Deleted ${type} Definition for "${name}"`;
|
||||
|
||||
this.deleteCurrentNode(msg);
|
||||
setTimeout(() => Materialize.toast(msg, 2000), 10);
|
||||
returnToLast(fromType);
|
||||
}
|
||||
}
|
||||
],
|
||||
];
|
||||
};
|
||||
|
||||
var MyPipelinesButtons = [
|
||||
{
|
||||
name: 'Create new pipeline',
|
||||
icon: 'queue',
|
||||
action: DeepForge.create.Pipeline
|
||||
}
|
||||
];
|
||||
|
||||
var makeRestartButton = function(name, pluginId) {
|
||||
return {
|
||||
name: 'Restart ' + name,
|
||||
icon: 'replay',
|
||||
priority: 1000,
|
||||
action: function(event) {
|
||||
this.runExecutionPlugin(pluginId, event.shiftKey);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
HOME: MyPipelinesButtons,
|
||||
MyPipelines_META: MyPipelinesButtons,
|
||||
MyArchitectures_META: [
|
||||
{
|
||||
name: 'Create new architecture',
|
||||
@@ -200,17 +190,16 @@ define([
|
||||
{
|
||||
name: 'Upload artifact',
|
||||
icon: 'swap_vert',
|
||||
action: uploadArtifact
|
||||
}
|
||||
],
|
||||
Operation_META: [
|
||||
{
|
||||
name: 'Return to Pipeline',
|
||||
icon: 'input',
|
||||
action: returnToLastPipeline
|
||||
action: DeepForge.create.Artifact
|
||||
}
|
||||
],
|
||||
|
||||
// Creating prototypes
|
||||
Operation_META: prototypeButtons('Operation', 'Pipeline'),
|
||||
Layer_META: prototypeButtons('Layer', 'Architecture'),
|
||||
Complex_META: prototypeButtons('Class', 'Operation'),
|
||||
Primitive_META: prototypeButtons('Primitive Type', 'Operation'),
|
||||
|
||||
// Instances
|
||||
Data: [
|
||||
{
|
||||
@@ -220,12 +209,15 @@ define([
|
||||
}
|
||||
],
|
||||
Job: [
|
||||
makeRestartButton('Job', 'ExecuteJob'),
|
||||
{
|
||||
name: 'Download Execution Files',
|
||||
icon: 'play_for_work',
|
||||
priority: 1,
|
||||
href: download.execFiles
|
||||
}
|
||||
],
|
||||
Execution: [makeRestartButton('Execution', 'ExecutePipeline')],
|
||||
Pipeline: [
|
||||
{
|
||||
name: 'Create new node',
|
||||
@@ -234,14 +226,13 @@ define([
|
||||
action: function() {
|
||||
this.addOperation();
|
||||
}
|
||||
},
|
||||
}
|
||||
],
|
||||
Architecture: [
|
||||
{
|
||||
name: 'Create new node',
|
||||
icon: 'queue',
|
||||
priority: 2,
|
||||
action: function() {
|
||||
this.addOperation();
|
||||
}
|
||||
name: 'Import Torch Architecture',
|
||||
icon: 'swap_vert',
|
||||
action: importTorch
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*globals DeepForge, $, define, _ */
|
||||
/*globals DeepForge, $, Materialize, define, _ */
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
@@ -7,7 +7,6 @@ define([
|
||||
'panel/FloatingActionButton/FloatingActionButton',
|
||||
'deepforge/viz/PipelineControl',
|
||||
'deepforge/viz/NodePrompter',
|
||||
'deepforge/viz/AddDecorator',
|
||||
'./Actions',
|
||||
'widgets/EasyDAG/AddNodeDialog',
|
||||
'js/RegistryKeys',
|
||||
@@ -21,7 +20,6 @@ define([
|
||||
PluginButton,
|
||||
PipelineControl,
|
||||
NodePrompter,
|
||||
AddDecorator,
|
||||
ACTIONS,
|
||||
AddNodeDialog,
|
||||
REGISTRY_KEYS,
|
||||
@@ -59,6 +57,13 @@ define([
|
||||
actions,
|
||||
basename;
|
||||
|
||||
if (!base) { // must be ROOT or FCO
|
||||
basename = node.getAttribute('name') || 'ROOT_NODE';
|
||||
actions = (ACTIONS[basename] || [])
|
||||
.filter(action => !action.filter || action.filter());
|
||||
return actions;
|
||||
}
|
||||
|
||||
while (base && !(actions && actions.length)) {
|
||||
basename = base.getAttribute('name') + suffix;
|
||||
base = this.client.getNode(base.getBaseId());
|
||||
@@ -183,8 +188,7 @@ define([
|
||||
|
||||
ForgeActionButton.prototype.promptLayerType = function() {
|
||||
// Prompt for the new custom layer's base type
|
||||
var deferred = Q.defer(),
|
||||
metanodes = this.client.getAllMetaNodes(),
|
||||
var metanodes = this.client.getAllMetaNodes(),
|
||||
baseLayerId = metanodes.find(n => n.getAttribute('name') === 'Layer').getId(),
|
||||
layerType,
|
||||
types;
|
||||
@@ -206,8 +210,7 @@ define([
|
||||
};
|
||||
});
|
||||
|
||||
AddNodeDialog.prompt(types, deferred.resolve);
|
||||
return deferred.promise;
|
||||
return AddNodeDialog.prompt(types);
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.uploadFile = function(event) {
|
||||
@@ -249,10 +252,7 @@ define([
|
||||
/////////////// Expanding containers ///////////////
|
||||
ForgeActionButton.prototype.addOperation = function() {
|
||||
var ops = this.getValidInitialNodes(),
|
||||
newOperation = {
|
||||
id: NEW_OPERATION_ID,
|
||||
Decorator: AddDecorator
|
||||
};
|
||||
newOperation = this.getNewOpNode();
|
||||
|
||||
// Add the 'New op button'
|
||||
ops.push(newOperation);
|
||||
@@ -260,15 +260,26 @@ define([
|
||||
this.promptNode(ops, (selected, prompter) => {
|
||||
if (selected.id === NEW_OPERATION_ID) {
|
||||
prompter.destroy();
|
||||
DeepForge.lastPipeline = this._currentNodeId;
|
||||
DeepForge.create.Operation();
|
||||
//WebGMEGlobal.State.registerActiveObject(newId);
|
||||
} else {
|
||||
this.createNode(selected.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.getNewOpNode = function() {
|
||||
var Decorator = this.client.decoratorManager.getDecoratorForWidget(
|
||||
'OperationDecorator', 'EasyDAG');
|
||||
|
||||
return {
|
||||
id: NEW_OPERATION_ID,
|
||||
class: 'create-node',
|
||||
name: 'New Operation...',
|
||||
Decorator: Decorator,
|
||||
attributes: {}
|
||||
};
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.promptNode = function(nodes, selectFn) {
|
||||
// Get the absolute location of the given button
|
||||
var mainBtn = this.$el[0].children[0],
|
||||
@@ -301,5 +312,30 @@ define([
|
||||
return prompter.prompt(nodes, selectFn);
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.deleteCurrentNode = function(msg) {
|
||||
var nodeId = this._currentNodeId;
|
||||
if (nodeId) {
|
||||
this.client.startTransaction(msg);
|
||||
this.client.delMoreNodes([nodeId]);
|
||||
this.client.completeTransaction(msg);
|
||||
}
|
||||
};
|
||||
|
||||
ForgeActionButton.prototype.runExecutionPlugin = function(pluginId, useSecondary) {
|
||||
var context = this.client.getCurrentPluginContext(pluginId),
|
||||
name = this.client.getNode(this._currentNodeId).getAttribute('name'),
|
||||
method;
|
||||
|
||||
context.managerConfig.namespace = 'pipeline';
|
||||
method = useSecondary ? 'runBrowserPlugin' : 'runServerPlugin';
|
||||
this.client[method](pluginId, context, err => {
|
||||
if (err) {
|
||||
return Materialize.toast(`${name} failed!`, 4000);
|
||||
}
|
||||
|
||||
Materialize.toast(`${name} executed successfully!`, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
return ForgeActionButton;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*globals define, _ */
|
||||
/*jshint browser: true*/
|
||||
|
||||
// This editor will be a split screen view w/ the operation code on the left
|
||||
// and the terminal on the right.
|
||||
//
|
||||
// However, if the job is contained in a "snapshotted" execution, then it will
|
||||
// be considered read only and only show the terminal output
|
||||
define([
|
||||
'panels/TilingViz/TilingVizPanel',
|
||||
'panels/OutputViewer/OutputViewerPanel',
|
||||
'panels/OperationCodeEditor/OperationCodeEditorPanel',
|
||||
'js/Constants'
|
||||
], function (
|
||||
TilingViz,
|
||||
OutputViewer,
|
||||
OperationCodeEditor,
|
||||
CONSTANTS
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var JobEditorPanel;
|
||||
|
||||
JobEditorPanel = function (layoutManager, params) {
|
||||
TilingViz.call(this, layoutManager, params);
|
||||
this.readOnly = false;
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(JobEditorPanel.prototype, TilingViz.prototype);
|
||||
|
||||
JobEditorPanel.prototype.getPanels = function () {
|
||||
if (this.readOnly) {
|
||||
return [OutputViewer];
|
||||
} else {
|
||||
return [OperationCodeEditor, OutputViewer];
|
||||
}
|
||||
};
|
||||
|
||||
JobEditorPanel.prototype.selectedObjectChanged = function (nodeId) {
|
||||
var node = this._client.getNode(nodeId),
|
||||
typeId,
|
||||
type,
|
||||
typeName,
|
||||
executionId,
|
||||
execution;
|
||||
|
||||
if (typeof nodeId === 'string') {
|
||||
typeId = node.getMetaTypeId();
|
||||
type = this._client.getNode(typeId);
|
||||
typeName = type.getAttribute('name');
|
||||
|
||||
if (typeName !== 'Job') {
|
||||
this.logger.error(`Invalid node type for JobEditor: ${typeName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
executionId = node.getParentId();
|
||||
execution = this._client.getNode(executionId);
|
||||
|
||||
// If the current node is in a snapshotted execution, only show the log
|
||||
// viewer
|
||||
if (this.readOnly !== execution.getAttribute('snapshot')) {
|
||||
this.readOnly = execution.getAttribute('snapshot');
|
||||
this.logger.info(`readonly set to ${this.readOnly}`);
|
||||
this.updatePanels();
|
||||
}
|
||||
|
||||
// The OperationCodeEditor should receive the
|
||||
if (!this.readOnly) {
|
||||
// Get the operation base node id and pass it to OpCodeEditor selObjChanged
|
||||
if (this._territoryId) {
|
||||
this._client.removeUI(this._territoryId);
|
||||
}
|
||||
this._territoryId = this._client.addUI(this,
|
||||
this.onOperationEvents.bind(this));
|
||||
|
||||
// Update the territory
|
||||
this._territory = {};
|
||||
this._territory[nodeId] = {children: 1};
|
||||
|
||||
this._client.updateTerritory(this._territoryId, this._territory);
|
||||
}
|
||||
|
||||
// update the OutputViewer controller
|
||||
var i = this._panels.length;
|
||||
this._panels[i-1].control.selectedObjectChanged(nodeId);
|
||||
}
|
||||
};
|
||||
|
||||
JobEditorPanel.prototype.onOperationEvents = function (events) {
|
||||
var event = events.find(event => {
|
||||
if (event.etype === CONSTANTS.TERRITORY_EVENT_LOAD) {
|
||||
// Check if the eid is an Operation
|
||||
var typeId = this._client.getNode(event.eid).getMetaTypeId(),
|
||||
type = this._client.getNode(typeId),
|
||||
metaBaseId = type && type.getBaseId(),
|
||||
typeName;
|
||||
|
||||
if (metaBaseId) {
|
||||
typeName = this._client.getNode(metaBaseId).getAttribute('name');
|
||||
}
|
||||
|
||||
return typeName === 'Operation';
|
||||
}
|
||||
});
|
||||
|
||||
if (event && !this.readOnly) {
|
||||
var opNode = this._client.getNode(event.eid),
|
||||
opDefId = opNode.getMetaTypeId();
|
||||
|
||||
this._panels[0].control.selectedObjectChanged(opDefId);
|
||||
this._panels[0].control.offsetNodeChanged(event.eid);
|
||||
}
|
||||
};
|
||||
|
||||
return JobEditorPanel;
|
||||
});
|
||||
@@ -0,0 +1,184 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'js/Constants',
|
||||
'js/Utils/GMEConcepts',
|
||||
'js/NodePropertyNames'
|
||||
], function (
|
||||
CONSTANTS,
|
||||
GMEConcepts,
|
||||
nodePropertyNames
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var LineGraphControl;
|
||||
|
||||
LineGraphControl = function (options) {
|
||||
|
||||
this._logger = options.logger.fork('Control');
|
||||
|
||||
this._client = options.client;
|
||||
this._embedded = options.embedded;
|
||||
|
||||
// Initialize core collections and variables
|
||||
this._widget = options.widget;
|
||||
|
||||
this._currentNodeId = null;
|
||||
this._currentNodeParentId = undefined;
|
||||
|
||||
this._logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
/* * * * * * * * 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).
|
||||
LineGraphControl.prototype.selectedObjectChanged = function (nodeId) {
|
||||
var desc = this._getObjectDescriptor(nodeId),
|
||||
self = this;
|
||||
|
||||
self._logger.debug('activeObject nodeId \'' + nodeId + '\'');
|
||||
|
||||
// Remove current territory patterns
|
||||
if (self._currentNodeId) {
|
||||
self._client.removeUI(self._territoryId);
|
||||
}
|
||||
|
||||
self._currentNodeId = nodeId;
|
||||
self._currentNodeParentId = undefined;
|
||||
|
||||
if (typeof self._currentNodeId === 'string') {
|
||||
// Put new node's info into territory rules
|
||||
self._selfPatterns = {};
|
||||
self._selfPatterns[nodeId] = {children: 0}; // Territory "rule"
|
||||
|
||||
self._widget.setTitle(desc.name.toUpperCase());
|
||||
|
||||
self._currentNodeParentId = desc.parentId;
|
||||
|
||||
self._territoryId = self._client.addUI(self, function (events) {
|
||||
self._eventCallback(events);
|
||||
});
|
||||
|
||||
// Update the territory
|
||||
self._client.updateTerritory(self._territoryId, self._selfPatterns);
|
||||
|
||||
self._selfPatterns[nodeId] = {children: 1};
|
||||
self._client.updateTerritory(self._territoryId, self._selfPatterns);
|
||||
}
|
||||
};
|
||||
|
||||
// This next function retrieves the relevant node information for the widget
|
||||
LineGraphControl.prototype._getObjectDescriptor = function (nodeId) {
|
||||
var node = this._client.getNode(nodeId),
|
||||
desc;
|
||||
|
||||
if (node) {
|
||||
desc = {
|
||||
id: node.getId(),
|
||||
name: node.getAttribute(nodePropertyNames.Attributes.name)
|
||||
};
|
||||
|
||||
// Check if it is a line
|
||||
if (desc.id !== this._currentNodeId) {
|
||||
var points = (node.getAttribute('points') || '').split(';')
|
||||
.map(pair => {
|
||||
var nums = pair.split(','),
|
||||
x = +nums[0],
|
||||
y = +nums[1];
|
||||
|
||||
return {
|
||||
x: x,
|
||||
y:y
|
||||
};
|
||||
});
|
||||
desc.type = 'line';
|
||||
desc.points = points;
|
||||
}
|
||||
}
|
||||
|
||||
return desc;
|
||||
};
|
||||
|
||||
/* * * * * * * * Node Event Handling * * * * * * * */
|
||||
LineGraphControl.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');
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._onLoad = function (gmeId) {
|
||||
var description = this._getObjectDescriptor(gmeId);
|
||||
this._widget.addNode(description);
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._onUpdate = function (gmeId) {
|
||||
var description = this._getObjectDescriptor(gmeId);
|
||||
this._widget.updateNode(description);
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._onUnload = function (gmeId) {
|
||||
this._widget.removeNode(gmeId);
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._stateActiveObjectChanged = function (model, activeObjectId) {
|
||||
if (this._currentNodeId === activeObjectId) {
|
||||
// The same node selected as before - do not trigger
|
||||
} else {
|
||||
this.selectedObjectChanged(activeObjectId);
|
||||
}
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
LineGraphControl.prototype.destroy = function () {
|
||||
this._detachClientEventListeners();
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._attachClientEventListeners = function () {
|
||||
this._detachClientEventListeners();
|
||||
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged, this);
|
||||
};
|
||||
|
||||
LineGraphControl.prototype._detachClientEventListeners = function () {
|
||||
WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged);
|
||||
};
|
||||
|
||||
LineGraphControl.prototype.onActivate = function () {
|
||||
this._attachClientEventListeners();
|
||||
|
||||
if (typeof this._currentNodeId === 'string') {
|
||||
WebGMEGlobal.State.registerSuppressVisualizerFromNode(true);
|
||||
WebGMEGlobal.State.registerActiveObject(this._currentNodeId);
|
||||
WebGMEGlobal.State.registerSuppressVisualizerFromNode(false);
|
||||
}
|
||||
};
|
||||
|
||||
LineGraphControl.prototype.onDeactivate = function () {
|
||||
this._detachClientEventListeners();
|
||||
};
|
||||
|
||||
return LineGraphControl;
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
/*globals define, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'widgets/LineGraph/LineGraphWidget',
|
||||
'./LineGraphControl'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
LineGraphWidget,
|
||||
LineGraphControl
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var LineGraphPanel;
|
||||
|
||||
LineGraphPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'LineGraphPanel';
|
||||
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(LineGraphPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(LineGraphPanel.prototype, IActivePanel.prototype);
|
||||
|
||||
LineGraphPanel.prototype._initialize = function () {
|
||||
var self = this;
|
||||
|
||||
//set Widget title
|
||||
this.setTitle('');
|
||||
|
||||
this.widget = new LineGraphWidget(this.logger, this.$el);
|
||||
|
||||
this.widget.setTitle = function (title) {
|
||||
self.setTitle(title);
|
||||
};
|
||||
|
||||
this.control = new LineGraphControl({
|
||||
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 */
|
||||
LineGraphPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
LineGraphPanel.prototype.onResize = function (width, height) {
|
||||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
|
||||
this.widget.onWidgetContainerResize(width, height);
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
LineGraphPanel.prototype.destroy = function () {
|
||||
this.control.destroy();
|
||||
this.widget.destroy();
|
||||
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
LineGraphPanel.prototype.onActivate = function () {
|
||||
this.widget.onActivate();
|
||||
this.control.onActivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(this.widget);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
LineGraphPanel.prototype.onDeactivate = function () {
|
||||
this.widget.onDeactivate();
|
||||
this.control.onDeactivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
return LineGraphPanel;
|
||||
});
|
||||
@@ -0,0 +1,225 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'blob/BlobClient',
|
||||
'js/Constants',
|
||||
'js/Utils/GMEConcepts',
|
||||
'js/NodePropertyNames',
|
||||
'deepforge/globals'
|
||||
], function (
|
||||
BlobClient,
|
||||
CONSTANTS,
|
||||
GMEConcepts,
|
||||
nodePropertyNames,
|
||||
DeepForge
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var MainViewControl;
|
||||
|
||||
MainViewControl = 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.territory = {};
|
||||
this.ui = {};
|
||||
this._blobClient = new BlobClient({
|
||||
logger: this._logger.fork('BlobClient')
|
||||
});
|
||||
|
||||
this._initWidgetEventHandlers();
|
||||
this._logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
MainViewControl.prototype._initWidgetEventHandlers = function () {
|
||||
this._widget.deleteNode = id => {
|
||||
var node = this._client.getNode(id),
|
||||
baseId = node.getBaseId(),
|
||||
base = this._client.getNode(baseId),
|
||||
baseName = base.getAttribute('name'),
|
||||
name = node.getAttribute('name'),
|
||||
msg = `Deleting ${baseName} "${name}"`;
|
||||
|
||||
this._client.startTransaction(msg);
|
||||
this._client.delMoreNodes([id]);
|
||||
this._client.completeTransaction();
|
||||
};
|
||||
|
||||
this._widget.dataUrlFor = id => {
|
||||
var node = this._client.getNode(id),
|
||||
hash = node.getAttribute('data');
|
||||
|
||||
if (hash) {
|
||||
return this._blobClient.getDownloadURL(hash);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* * * * * * * * 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).
|
||||
MainViewControl.prototype.selectedObjectChanged = function (nodeId) {
|
||||
this._logger.debug('activeObject nodeId \'' + nodeId + '\'');
|
||||
|
||||
// Remove current territory patterns
|
||||
this.clearTerritoryRules();
|
||||
|
||||
this._currentNodeId = nodeId;
|
||||
|
||||
if (typeof this._currentNodeId === 'string') {
|
||||
var terrTypes = [
|
||||
/* [type, root dir] */
|
||||
['arch', 'MyArchitectures'],
|
||||
['artifact', 'MyArtifacts']
|
||||
];
|
||||
|
||||
terrTypes.forEach(pair => {
|
||||
var type = pair[0],
|
||||
dirname = pair[1];
|
||||
|
||||
// Update the territory
|
||||
this.territory[type] = {};
|
||||
this.territory[type][DeepForge.places[dirname]] = {children: 1};
|
||||
this.ui[type] = this._client.addUI(this, this.handleEvents.bind(this, type));
|
||||
this._client.updateTerritory(this.ui[type], this.territory[type]);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype.handleEvents = function (type, events) {
|
||||
var event;
|
||||
|
||||
// Remove the containing dir
|
||||
events = events.filter(e => !this.territory[type][e.eid]);
|
||||
this._logger.debug('_eventCallback \'' + i + '\' items');
|
||||
|
||||
for (var i = events.length; i--;) {
|
||||
event = events[i];
|
||||
switch (event.etype) {
|
||||
|
||||
case CONSTANTS.TERRITORY_EVENT_LOAD:
|
||||
this.onLoad(type, event.eid);
|
||||
break;
|
||||
case CONSTANTS.TERRITORY_EVENT_UPDATE:
|
||||
this._onUpdate(type, event.eid);
|
||||
break;
|
||||
case CONSTANTS.TERRITORY_EVENT_UNLOAD:
|
||||
this._onUnload(event.eid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._logger.debug('_eventCallback \'' + events.length + '\' items - DONE');
|
||||
};
|
||||
|
||||
MainViewControl.prototype.onLoad = function(type, id) {
|
||||
// Load a node of the given type
|
||||
var desc = this._getObjectDescriptor(type, id);
|
||||
if (type === 'arch') {
|
||||
this._widget.addArch(desc);
|
||||
} else { // artifacts
|
||||
this._widget.addArtifact(desc);
|
||||
}
|
||||
};
|
||||
|
||||
// This next function retrieves the relevant node information for the widget
|
||||
MainViewControl.prototype._getArtifactDesc = function (id) {
|
||||
var node = this._client.getNode(id),
|
||||
data = node.getAttribute('data'),
|
||||
desc = this._getBasicDesc(id);
|
||||
|
||||
desc.data = data;
|
||||
return desc;
|
||||
};
|
||||
|
||||
MainViewControl.prototype._getArchDesc =
|
||||
MainViewControl.prototype._getBasicDesc = function (id) {
|
||||
var node = this._client.getNode(id);
|
||||
|
||||
return {
|
||||
id: id,
|
||||
name: node.getAttribute('name')
|
||||
};
|
||||
};
|
||||
|
||||
MainViewControl.prototype._getObjectDescriptor = function (type, id) {
|
||||
return type === 'arch' ?
|
||||
this._getArchDesc(id) :
|
||||
this._getArtifactDesc(id);
|
||||
};
|
||||
|
||||
/* * * * * * * * Node Event Handling * * * * * * * */
|
||||
MainViewControl.prototype._onUpdate = function (type, gmeId) {
|
||||
var description = this._getObjectDescriptor(type, gmeId);
|
||||
this._widget.updateNode(description);
|
||||
};
|
||||
|
||||
MainViewControl.prototype._onUnload = function (gmeId) {
|
||||
this._widget.removeNode(gmeId);
|
||||
};
|
||||
|
||||
MainViewControl.prototype._stateActiveObjectChanged = function (model, activeObjectId) {
|
||||
if (this._currentNodeId !== activeObjectId) {
|
||||
this.selectedObjectChanged(activeObjectId);
|
||||
}
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
MainViewControl.prototype.destroy = function () {
|
||||
this._detachClientEventListeners();
|
||||
this.clearTerritoryRules();
|
||||
};
|
||||
|
||||
MainViewControl.prototype._attachClientEventListeners = function () {
|
||||
this._detachClientEventListeners();
|
||||
if (!this._embedded) {
|
||||
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT,
|
||||
this._stateActiveObjectChanged, this);
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype._detachClientEventListeners = function () {
|
||||
if (!this._embedded) {
|
||||
WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_OBJECT,
|
||||
this._stateActiveObjectChanged);
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype.onActivate = function () {
|
||||
this._attachClientEventListeners();
|
||||
|
||||
if (typeof this._currentNodeId === 'string') {
|
||||
WebGMEGlobal.State.registerSuppressVisualizerFromNode(true);
|
||||
WebGMEGlobal.State.registerActiveObject(this._currentNodeId);
|
||||
WebGMEGlobal.State.registerSuppressVisualizerFromNode(false);
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype.clearTerritoryRules = function () {
|
||||
if (Object.keys(this.ui).length) {
|
||||
Object.keys(this.ui).forEach(id =>
|
||||
this._client.removeUI(this.ui[id]));
|
||||
}
|
||||
};
|
||||
|
||||
MainViewControl.prototype.onDeactivate = function () {
|
||||
this._detachClientEventListeners();
|
||||
};
|
||||
|
||||
return MainViewControl;
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
/*globals define, $, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
// The main panel shows the PipelineIndex w/ a bar on the left for viewing architectures
|
||||
// and pipelines
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'widgets/MainView/MainViewWidget',
|
||||
'./MainViewControl',
|
||||
'panels/PipelineIndex/PipelineIndexPanel',
|
||||
'deepforge/globals'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
MainViewWidget,
|
||||
MainViewControl,
|
||||
PipelineIndexPanel,
|
||||
DeepForge
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var MainViewPanel;
|
||||
|
||||
MainViewPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'MainViewPanel';
|
||||
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.$nav = $('<div>', {id: 'nav-container'});
|
||||
this.$el.css({padding: 0});
|
||||
|
||||
this.embeddedPanel = new PipelineIndexPanel(layoutManager, params);
|
||||
this.$embedded = this.embeddedPanel.$el;
|
||||
this.$embedded.addClass('embedded');
|
||||
|
||||
this.$el.append(this.$nav, this.$embedded);
|
||||
|
||||
this._initialize();
|
||||
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(MainViewPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(MainViewPanel.prototype, IActivePanel.prototype);
|
||||
|
||||
MainViewPanel.prototype._initialize = function () {
|
||||
//set Widget title
|
||||
this.setTitle('');
|
||||
|
||||
this.widget = new MainViewWidget(this.logger, this.$nav);
|
||||
|
||||
this.control = new MainViewControl({
|
||||
logger: this.logger,
|
||||
client: this._client,
|
||||
embedded: this._embedded,
|
||||
widget: this.widget
|
||||
});
|
||||
|
||||
var controlObjectChanged = this.control.selectedObjectChanged;
|
||||
this.control.selectedObjectChanged = nodeId => {
|
||||
this.embeddedPanel.control.selectedObjectChanged(DeepForge.places.MyPipelines);
|
||||
return controlObjectChanged.call(this.control, nodeId);
|
||||
};
|
||||
|
||||
this.onActivate();
|
||||
};
|
||||
|
||||
/* OVERRIDE FROM WIDGET-WITH-HEADER */
|
||||
/* METHOD CALLED WHEN THE WIDGET'S READ-ONLY PROPERTY CHANGES */
|
||||
MainViewPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
MainViewPanel.prototype.onResize = function (width, height) {
|
||||
var navWidth,
|
||||
embeddedWidth;
|
||||
|
||||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
|
||||
this.widget.onWidgetContainerResize(width, height);
|
||||
navWidth = this.widget.width();
|
||||
embeddedWidth = width-navWidth;
|
||||
this.$embedded.css({
|
||||
width: embeddedWidth,
|
||||
height: height,
|
||||
left: navWidth,
|
||||
margin: 'inherit'
|
||||
});
|
||||
this.embeddedPanel.onResize(embeddedWidth, height);
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
MainViewPanel.prototype.destroy = function () {
|
||||
this.control.destroy();
|
||||
this.widget.destroy();
|
||||
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
MainViewPanel.prototype.onActivate = function () {
|
||||
this.widget.onActivate();
|
||||
this.control.onActivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(this.widget);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
MainViewPanel.prototype.onDeactivate = function () {
|
||||
this.widget.onDeactivate();
|
||||
this.control.onDeactivate();
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
return MainViewPanel;
|
||||
});
|
||||
@@ -1,16 +1,15 @@
|
||||
/*globals define */
|
||||
/*jshint browser: true*/
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Wed May 18 2016 12:00:46 GMT-0500 (CDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'panels/TextEditor/TextEditorControl',
|
||||
'deepforge/viz/OperationControl',
|
||||
'deepforge/Constants',
|
||||
'underscore'
|
||||
], function (
|
||||
TextEditorControl,
|
||||
OperationControl,
|
||||
CONSTANTS,
|
||||
_
|
||||
) {
|
||||
|
||||
@@ -59,5 +58,32 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
// Line offset handling
|
||||
OperationCodeEditorControl.prototype.offsetNodeChanged = function (id) {
|
||||
// Create a territory for this node
|
||||
if (this._offsetUI) {
|
||||
this._client.removeUI(this._offsetUI);
|
||||
}
|
||||
this._offsetNodeId = id;
|
||||
this._offsetUI = this._client.addUI(this, this.onOffsetNodeEvents.bind(this));
|
||||
this._offsetTerritory = {};
|
||||
this._offsetTerritory[id] = {children: 0};
|
||||
this._client.updateTerritory(this._offsetUI, this._offsetTerritory);
|
||||
};
|
||||
|
||||
OperationCodeEditorControl.prototype.onOffsetNodeEvents = function () {
|
||||
var node = this._client.getNode(this._offsetNodeId);
|
||||
if (node) { // wasn't a 'delete' event
|
||||
this._widget.setLineOffset(node.getAttribute(CONSTANTS.LINE_OFFSET) || 0);
|
||||
}
|
||||
};
|
||||
|
||||
OperationCodeEditorControl.prototype.destroy = function () {
|
||||
TextEditorControl.prototype.destroy.call(this);
|
||||
if (this._offsetUI) {
|
||||
this._client.removeUI(this._offsetUI);
|
||||
}
|
||||
};
|
||||
|
||||
return OperationCodeEditorControl;
|
||||
});
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
/*globals define, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Wed May 18 2016 12:00:46 GMT-0500 (CDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
/*globals define, */
|
||||
/*globals define, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'deepforge/globals',
|
||||
'deepforge/viz/RenameablePanel',
|
||||
'panels/TilingViz/TilingVizPanel',
|
||||
'panels/OperationCodeEditor/OperationCodeEditorPanel',
|
||||
'panels/OperationInterfaceEditor/OperationInterfaceEditorPanel',
|
||||
'js/Constants',
|
||||
'underscore'
|
||||
], function (
|
||||
DeepForge,
|
||||
RenameablePanel,
|
||||
TilingViz,
|
||||
CodeEditor,
|
||||
InterfaceEditor,
|
||||
CONSTANTS,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
@@ -18,13 +24,66 @@ define([
|
||||
|
||||
OperationEditorPanel = function (layoutManager, params) {
|
||||
TilingViz.call(this, layoutManager, params);
|
||||
this.initialize();
|
||||
};
|
||||
|
||||
OperationEditorPanel.prototype.initialize = function () {
|
||||
this.territory = {};
|
||||
this.territoryId = null;
|
||||
this._currentNodeId = null;
|
||||
|
||||
this.control = this;
|
||||
|
||||
// Set the editable title on node change
|
||||
this.initializeRenameable();
|
||||
};
|
||||
|
||||
//inherit from TilingViz
|
||||
_.extend(OperationEditorPanel.prototype, TilingViz.prototype);
|
||||
_.extend(
|
||||
OperationEditorPanel.prototype,
|
||||
RenameablePanel.prototype,
|
||||
TilingViz.prototype
|
||||
);
|
||||
|
||||
OperationEditorPanel.prototype.selectedObjectChanged = function (id) {
|
||||
this._currentNodeId = id;
|
||||
DeepForge.last.Operation = id;
|
||||
if (typeof this._currentNodeId === 'string') {
|
||||
// Setup the territory
|
||||
this.territory = {};
|
||||
this.territory[this._currentNodeId] = {children: 0};
|
||||
this.territoryId = this._client.addUI(this, this._eventCallback.bind(this));
|
||||
this._client.updateTerritory(this.territoryId, this.territory);
|
||||
}
|
||||
TilingViz.prototype.selectedObjectChanged.call(this, id);
|
||||
};
|
||||
|
||||
OperationEditorPanel.prototype._eventCallback = function (events) {
|
||||
events = events.find(e => e.eid === this._currentNodeId);
|
||||
this.updateTitle();
|
||||
};
|
||||
|
||||
OperationEditorPanel.prototype.updateTitle = function () {
|
||||
var id = this._currentNodeId,
|
||||
node = this._client.getNode(id),
|
||||
name = node && node.getAttribute('name');
|
||||
|
||||
this.setTitle(name || '');
|
||||
};
|
||||
|
||||
OperationEditorPanel.prototype.getPanels = function () {
|
||||
return [CodeEditor, InterfaceEditor];
|
||||
return [InterfaceEditor, CodeEditor];
|
||||
};
|
||||
|
||||
OperationEditorPanel.prototype.onDeactivate = function () {
|
||||
WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_OBJECT,
|
||||
this._stateActiveObjectChanged);
|
||||
|
||||
if (this.territoryId) {
|
||||
this._client.removeUI(this.territoryId);
|
||||
}
|
||||
|
||||
TilingViz.prototype.onDeactivate.call(this);
|
||||
};
|
||||
|
||||
return OperationEditorPanel;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/*globals define*/
|
||||
define({
|
||||
PRIMITIVE: '#b0bec5',
|
||||
COMPLEX: '#78909c'
|
||||
});
|
||||
+34
-8
@@ -1,6 +1,8 @@
|
||||
/*globals define*/
|
||||
define([
|
||||
'./Colors'
|
||||
], function(
|
||||
COLORS
|
||||
) {
|
||||
'use strict';
|
||||
var OperationInterfaceEditorEvents = function() {
|
||||
@@ -9,6 +11,25 @@ define([
|
||||
this._widget.addRefTo = this.addRefTo.bind(this);
|
||||
this._widget.changePtrName = this.changePtrName.bind(this);
|
||||
this._widget.removePtr = this.removePtr.bind(this);
|
||||
this._widget.getCreationNode = this.getCreationNode.bind(this);
|
||||
};
|
||||
|
||||
OperationInterfaceEditorEvents.prototype.getCreationNode = function(type, id) {
|
||||
var typeName = type === 'Complex' ? 'Class' : 'Primitive',
|
||||
Decorator = this._client.decoratorManager.getDecoratorForWidget(
|
||||
this.DEFAULT_DECORATOR, 'EasyDAG');
|
||||
|
||||
return {
|
||||
node: {
|
||||
id: id,
|
||||
class: 'create-node',
|
||||
name: `New ${typeName}...`,
|
||||
Decorator: Decorator,
|
||||
color: COLORS[type.toUpperCase()],
|
||||
isPrimitive: type === 'Primitive',
|
||||
attributes: {}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
OperationInterfaceEditorEvents.prototype.allValidReferences = function() {
|
||||
@@ -19,7 +40,7 @@ define([
|
||||
var notTypes = ['Data', 'Operation', 'Pipeline'];
|
||||
return this._client.getAllMetaNodes()
|
||||
.filter(node => {
|
||||
var plugins = node.getRegistry('validPlugins');
|
||||
var plugins = node.getOwnRegistry('validPlugins');
|
||||
// Convention is enforced; if the plugin generates lua artifacts,
|
||||
// it should be called `Generate`.. (something)
|
||||
return plugins && plugins.indexOf('Generate') !== -1;
|
||||
@@ -34,23 +55,28 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
OperationInterfaceEditorEvents.prototype.allDataTypeIds = function() {
|
||||
return this.allDataTypes().map(node => node.getId());
|
||||
OperationInterfaceEditorEvents.prototype.allDataTypeIds = function(incAbstract) {
|
||||
return this.allDataTypes(incAbstract).map(node => node.getId());
|
||||
};
|
||||
|
||||
OperationInterfaceEditorEvents.prototype.allDataTypes = function() {
|
||||
OperationInterfaceEditorEvents.prototype.allDataTypes = function(incAbstract) {
|
||||
return this._client.getAllMetaNodes()
|
||||
.filter(node => this.hasMetaName(node.getId(), 'Data'))
|
||||
.filter(node => this.hasMetaName(node.getId(), 'Data', incAbstract))
|
||||
.filter(node => !node.isAbstract());
|
||||
};
|
||||
|
||||
OperationInterfaceEditorEvents.prototype._getValidSuccessorNodes = function(nodeId) {
|
||||
// Return all data types in the meta
|
||||
OperationInterfaceEditorEvents.prototype.getValidSuccessors = function(nodeId, isInput) {
|
||||
var dataTypeIds;
|
||||
|
||||
if (nodeId !== this._currentNodeId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.allDataTypeIds().map(id => {
|
||||
// Return all data types in the meta
|
||||
// If input, include abstract types
|
||||
dataTypeIds = this.allDataTypeIds(isInput);
|
||||
|
||||
return dataTypeIds.map(id => {
|
||||
return {
|
||||
node: this._getObjectDescriptor(id)
|
||||
};
|
||||
|
||||
+158
-6
@@ -9,14 +9,20 @@
|
||||
define([
|
||||
'panels/EasyDAG/EasyDAGControl',
|
||||
'js/Constants',
|
||||
'deepforge/Constants',
|
||||
'deepforge/lua',
|
||||
'deepforge/viz/OperationControl',
|
||||
'./OperationInterfaceEditorControl.EventHandlers',
|
||||
'./Colors',
|
||||
'underscore'
|
||||
], function (
|
||||
EasyDAGControl,
|
||||
GME_CONSTANTS,
|
||||
CONSTANTS,
|
||||
luajs,
|
||||
OperationControl,
|
||||
OperationInterfaceEditorControlEvents,
|
||||
COLORS,
|
||||
_
|
||||
) {
|
||||
|
||||
@@ -30,6 +36,9 @@ define([
|
||||
OperationInterfaceEditorControlEvents.call(this);
|
||||
this._connections = {};
|
||||
this._pointers = {};
|
||||
|
||||
this._usage = {}; // info about input usage
|
||||
this._inputs = {};
|
||||
};
|
||||
|
||||
_.extend(
|
||||
@@ -81,13 +90,13 @@ define([
|
||||
for (var i = 0; i < events.length; i++) {
|
||||
event = events[i];
|
||||
switch (event.etype) {
|
||||
case CONSTANTS.TERRITORY_EVENT_LOAD:
|
||||
case GME_CONSTANTS.TERRITORY_EVENT_LOAD:
|
||||
this._onLoad(event.eid);
|
||||
break;
|
||||
case CONSTANTS.TERRITORY_EVENT_UPDATE:
|
||||
case GME_CONSTANTS.TERRITORY_EVENT_UPDATE:
|
||||
this._onUpdate(event.eid);
|
||||
break;
|
||||
case CONSTANTS.TERRITORY_EVENT_UNLOAD:
|
||||
case GME_CONSTANTS.TERRITORY_EVENT_UNLOAD:
|
||||
this._onUnload(event.eid);
|
||||
break;
|
||||
default:
|
||||
@@ -139,18 +148,48 @@ define([
|
||||
|
||||
desc.container = cntr.toLowerCase();
|
||||
desc.attributes = {};
|
||||
|
||||
} else if (desc.id === this._currentNodeId) {
|
||||
desc.pointers = {};
|
||||
|
||||
// Remove DeepForge hidden attributes
|
||||
delete desc.attributes.code;
|
||||
delete desc.attributes[CONSTANTS.LINE_OFFSET];
|
||||
}
|
||||
|
||||
// Extra decoration for data
|
||||
if (this.hasMetaName(desc.id, 'Data', true)) {
|
||||
desc.color = this.getDescColor(gmeId);
|
||||
desc.isPrimitive = this.hasMetaName(gmeId, 'Primitive');
|
||||
|
||||
var used = desc.container === 'inputs' ?
|
||||
this.isUsedInput(desc.name) : this.isUsedOutput(desc.name);
|
||||
if (used !== null) {
|
||||
desc.used = used;
|
||||
this._usage[desc.id] = desc.used;
|
||||
} else {
|
||||
desc.used = this._usage[desc.id] !== undefined ?
|
||||
this._usage[desc.id] : true;
|
||||
}
|
||||
|
||||
this._inputs[desc.id] = desc.container === 'inputs';
|
||||
}
|
||||
return desc;
|
||||
};
|
||||
|
||||
OperationInterfaceEditorControl.prototype.getDescColor = function(gmeId) {
|
||||
return !this.hasMetaName(gmeId, 'Primitive', true) ? COLORS.COMPLEX :
|
||||
COLORS.PRIMITIVE;
|
||||
};
|
||||
|
||||
OperationInterfaceEditorControl.prototype._onUnload = function(gmeId) {
|
||||
EasyDAGControl.prototype._onUnload.call(this, gmeId);
|
||||
var conn = this._connections[gmeId];
|
||||
if (conn) {
|
||||
this._widget.removeNode(conn.id);
|
||||
}
|
||||
delete this._usage[gmeId];
|
||||
delete this._inputs[gmeId];
|
||||
};
|
||||
|
||||
OperationInterfaceEditorControl.prototype._onLoad = function(gmeId) {
|
||||
@@ -170,12 +209,40 @@ define([
|
||||
};
|
||||
|
||||
OperationInterfaceEditorControl.prototype._onUpdate = function(gmeId) {
|
||||
var variableIds,
|
||||
wasUsed,
|
||||
isUsed,
|
||||
name,
|
||||
ast,
|
||||
code;
|
||||
|
||||
if (gmeId === this._currentNodeId) {
|
||||
EasyDAGControl.prototype._onUpdate.call(this, gmeId);
|
||||
|
||||
// Update the valid pointers
|
||||
this.updatePtrs();
|
||||
|
||||
// Update the remaining usage info
|
||||
variableIds = Object.keys(this._usage);
|
||||
|
||||
code = this._client.getNode(this._currentNodeId).getAttribute('code');
|
||||
try {
|
||||
ast = luajs.parser.parse(code);
|
||||
for (var i = variableIds.length; i--;) {
|
||||
wasUsed = this._usage[variableIds[i]];
|
||||
name = this._client.getNode(variableIds[i]).getAttribute('name');
|
||||
|
||||
isUsed = this._inputs[variableIds[i]] ?
|
||||
this.isUsedInput(name, ast) :
|
||||
this.isUsedOutput(name, ast);
|
||||
if (isUsed !== wasUsed) {
|
||||
this._onUpdate(variableIds[i]);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this._logger.debug(`failed parsing lua: ${e}`);
|
||||
}
|
||||
|
||||
} else if (this.containedInCurrent(gmeId) && this.hasMetaName(gmeId, 'Data')) {
|
||||
EasyDAGControl.prototype._onUpdate.call(this, gmeId);
|
||||
}
|
||||
@@ -192,13 +259,20 @@ define([
|
||||
.items[0].id,
|
||||
target = this._client.getNode(targetId),
|
||||
decManager = this._client.decoratorManager,
|
||||
Decorator = decManager.getDecoratorForWidget('OpIntPtrDecorator', 'EasyDAG');
|
||||
Decorator = decManager.getDecoratorForWidget('OpIntPtrDecorator', 'EasyDAG'),
|
||||
id = 'ptr_'+name,
|
||||
used = this.isUsedInput(name);
|
||||
|
||||
if (used === null) {
|
||||
used = this._usage[id] !== undefined ? this._usage[id] : true;
|
||||
}
|
||||
|
||||
return {
|
||||
id: 'ptr_'+name,
|
||||
id: id,
|
||||
isPointer: true,
|
||||
baseName: target.getAttribute('name'),
|
||||
Decorator: Decorator,
|
||||
used: used,
|
||||
attributes: {},
|
||||
name: name,
|
||||
parentId: this._currentNodeId
|
||||
@@ -217,7 +291,7 @@ define([
|
||||
// Get the pointers that should exist [name, target]
|
||||
this.loadMeta();
|
||||
newPtrs = node.getPointerNames()
|
||||
.filter(name => name !== CONSTANTS.POINTER_BASE)
|
||||
.filter(name => name !== GME_CONSTANTS.POINTER_BASE)
|
||||
.map(name => this.getPtrDescriptor(name));
|
||||
|
||||
// Compare them to the existing...
|
||||
@@ -255,9 +329,13 @@ define([
|
||||
OperationInterfaceEditorControl.prototype.rmPtr = function(id) {
|
||||
// Remove the pointer's node
|
||||
this._widget.removeNode(id);
|
||||
|
||||
// and connection
|
||||
var conn = this._connections[id];
|
||||
this._widget.removeNode(conn.id);
|
||||
|
||||
// and usage info
|
||||
delete this._usage[id];
|
||||
};
|
||||
|
||||
OperationInterfaceEditorControl.prototype.containedInCurrent = function(id) {
|
||||
@@ -282,5 +360,79 @@ define([
|
||||
return conn;
|
||||
};
|
||||
|
||||
////////////////////// Unused input checking //////////////////////
|
||||
OperationInterfaceEditorControl.prototype.isUsedInput = function(name, ast) {
|
||||
return this._isUsed(name, true, ast);
|
||||
};
|
||||
|
||||
OperationInterfaceEditorControl.prototype.isUsedOutput = function(name, ast) {
|
||||
return this._isUsed(name, false, ast);
|
||||
};
|
||||
|
||||
OperationInterfaceEditorControl.prototype._isUsed = function(name, isInput, ast) {
|
||||
var code = this._client.getNode(this._currentNodeId).getAttribute('code'),
|
||||
r = new RegExp('\\b' + name + '\\b'),
|
||||
hasText = code.match(r) !== null;
|
||||
|
||||
// verify that it is not used only in the left side of an assignment
|
||||
if (hasText) {
|
||||
try {
|
||||
ast = ast || luajs.parser.parse(code);
|
||||
return isInput ? this.isUsedVariable(name, ast) : this.isReturnValue(name, ast);
|
||||
} catch(e) {
|
||||
this._logger.debug(`failed parsing lua: ${e}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Check if it is used in the given ast node
|
||||
OperationInterfaceEditorControl.prototype.isUsedVariable = function(name, node) {
|
||||
var isUsed = false,
|
||||
checker;
|
||||
|
||||
checker = luajs.codegen.traverse((curr, parent) => {
|
||||
if (curr.type === 'variable' && curr.val === name) {
|
||||
// Ignore if it is being assigned...
|
||||
if (parent.type === 'stat.assignment') {
|
||||
isUsed = isUsed || parent.right.indexOf(curr) !== -1;
|
||||
} else {
|
||||
isUsed = true;
|
||||
}
|
||||
}
|
||||
return curr;
|
||||
});
|
||||
|
||||
checker(node);
|
||||
return isUsed;
|
||||
};
|
||||
|
||||
OperationInterfaceEditorControl.prototype.isReturnValue = function(name, ast) {
|
||||
var firstReturn,
|
||||
fields,
|
||||
key,
|
||||
node;
|
||||
|
||||
for (var i = ast.block.stats.length; i--;) {
|
||||
node = ast.block.stats[i];
|
||||
if (node.type === 'stat.return') {
|
||||
// Check that it returns an object w/ a key of the given name
|
||||
firstReturn = node.nret[0];
|
||||
if (firstReturn && firstReturn.type === 'expr.constructor') {
|
||||
fields = firstReturn.fields;
|
||||
for (var j = fields.length; j--;) {
|
||||
key = fields[j].key;
|
||||
if (key.type === 'const.string' && key.val === name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return OperationInterfaceEditorControl;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
.output-viewer .output-pager {
|
||||
position: absolute;
|
||||
left: 0.75em;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
.output-viewer .empty {
|
||||
visibility: hidden;
|
||||
}
|
||||
.output-viewer .pagination {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
/*globals define, $, _, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
// The OutputViewer is a viz which shows the LogViewer and, if the job has
|
||||
// metadata, provides a pagination bar in the lower left to page through
|
||||
// the metadata results
|
||||
define([
|
||||
'js/PanelBase/PanelBaseWithHeader',
|
||||
'js/PanelManager/IActivePanel',
|
||||
'panels/LogViewer/LogViewerPanel',
|
||||
'js/Constants',
|
||||
'js/RegistryKeys',
|
||||
'text!api/visualizers',
|
||||
'css!./OutputViewer.css'
|
||||
], function (
|
||||
PanelBaseWithHeader,
|
||||
IActivePanel,
|
||||
LogViewer,
|
||||
CONSTANTS,
|
||||
REGISTRY_KEYS,
|
||||
VisualizersJSON
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var OutputViewerPanel,
|
||||
Visualizers = JSON.parse(VisualizersJSON);
|
||||
|
||||
OutputViewerPanel = function (layoutManager, params) {
|
||||
var options = {};
|
||||
//set properties from options
|
||||
options[PanelBaseWithHeader.OPTIONS.LOGGER_INSTANCE_NAME] = 'OutputViewerPanel';
|
||||
|
||||
//call parent's constructor
|
||||
PanelBaseWithHeader.apply(this, [options, layoutManager]);
|
||||
|
||||
this._currentNodeId = null;
|
||||
this._client = params.client;
|
||||
this._embedded = params.embedded;
|
||||
|
||||
//initialize UI
|
||||
this._layoutManager = layoutManager;
|
||||
this.dimensions = null;
|
||||
this._params = params;
|
||||
this._initialize();
|
||||
|
||||
this.logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(OutputViewerPanel.prototype, PanelBaseWithHeader.prototype);
|
||||
_.extend(OutputViewerPanel.prototype, IActivePanel.prototype);
|
||||
|
||||
OutputViewerPanel.prototype._initialize = function () {
|
||||
this.control = this; // implement selectedObjectChanged here
|
||||
this._panels = {
|
||||
LogViewer: LogViewer
|
||||
};
|
||||
|
||||
// Create the pagination container
|
||||
this._pages = {};
|
||||
this._pageCount = 0;
|
||||
this.$el.addClass('output-viewer');
|
||||
this.$pager = $('<nav>', {class: 'output-pager empty'});
|
||||
this.$pagerList = $('<ul>', {class: 'pagination'});
|
||||
|
||||
// Add the console item
|
||||
var logviewer = $('<li class="active"><a>Console</a></li>');
|
||||
this.$logviewer = logviewer.find('a');
|
||||
this.$selected = this.$logviewer;
|
||||
this.$pagerList.append(logviewer);
|
||||
|
||||
this.$pager.append(this.$pagerList);
|
||||
this.$el.append(this.$pager);
|
||||
this.$el.css({padding: 0});
|
||||
|
||||
// On 'page' clicked, set the activePanel to a new AutoViz and set
|
||||
// the given nodeId as the active node for it
|
||||
this.$pager.on('click', 'li', event => {
|
||||
var element = $(event.target);
|
||||
this.selectOutput(element);
|
||||
});
|
||||
|
||||
// Set the activePanel
|
||||
this.activePanel = new LogViewer(this._layoutManager, this._params);
|
||||
this.$el.append(this.activePanel.$el);
|
||||
|
||||
this.onActivate();
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.selectOutput = function (element) {
|
||||
if (this.$selected !== element) {
|
||||
this.$selected.parent().removeClass('active');
|
||||
element.parent().addClass('active');
|
||||
this.$selected = element;
|
||||
|
||||
// Update the panel
|
||||
var dataId = element.data('id');
|
||||
|
||||
if (dataId) {
|
||||
this.loadOutputFor(dataId);
|
||||
} else { // Set the logviewer
|
||||
this.loadPanel(LogViewer, this._currentNodeId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.loadOutputFor = function (id) {
|
||||
var node = this._client.getNode(id),
|
||||
panelId,
|
||||
panel,
|
||||
panelPath;
|
||||
|
||||
if (!node) {
|
||||
this.logger.error(`could not load node: ${id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the registered visualizer
|
||||
panelId = (node.getRegistry(REGISTRY_KEYS.VALID_VISUALIZERS) || '')
|
||||
.split(' ')
|
||||
.shift();
|
||||
|
||||
// Load it (embedded) and set the selected node to 'id'
|
||||
panel= Visualizers.find(desc => desc.id === panelId);
|
||||
if (panel) {
|
||||
panelPath = panel.panel;
|
||||
if (this._panels[panelPath]) {
|
||||
this.loadPanel(this._panels[panelPath], id);
|
||||
} else {
|
||||
require([panelPath],
|
||||
function(Panel) {
|
||||
this._panels[panelPath] = Panel;
|
||||
this.loadPanel(Panel, id);
|
||||
}.bind(this),
|
||||
err =>
|
||||
this.logger.error(`could not load ${panelPath}: ${err}`)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.logger.warn(`Could not find visualizer: ${panelId}`);
|
||||
}
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.loadPanel = function (Panel, nodeId) {
|
||||
if (this.activePanel) {
|
||||
this.activePanel.destroy();
|
||||
this.activePanel.$el.remove();
|
||||
}
|
||||
|
||||
this.activePanel = new Panel(this._layoutManager, this._params);
|
||||
this.$el.append(this.activePanel.$el);
|
||||
if (nodeId) {
|
||||
this.activePanel.control.selectedObjectChanged(nodeId);
|
||||
}
|
||||
if (this.dimensions) {
|
||||
this.onResize.apply(this, this.dimensions);
|
||||
}
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.clearTerritory = function () {
|
||||
if (this._territoryUI) {
|
||||
this._client.removeUI(this._territoryUI);
|
||||
this._territoryUI = null;
|
||||
}
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.selectedObjectChanged = function (nodeId) {
|
||||
if (typeof nodeId === 'string') {
|
||||
this._currentNodeId = nodeId;
|
||||
this.clearTerritory();
|
||||
this._territoryUI = this._client.addUI(this, this.handleEvents.bind(this));
|
||||
|
||||
// Create the territory (active node and children)
|
||||
this._territory = {};
|
||||
this._territory[nodeId] = {children: 1};
|
||||
this._client.updateTerritory(this._territoryUI, this._territory);
|
||||
|
||||
this.activePanel.control.selectedObjectChanged(nodeId);
|
||||
}
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.getMetadataId = function () {
|
||||
var metanodes = this._client.getAllMetaNodes();
|
||||
for (var i = metanodes.length; i--;) {
|
||||
if (metanodes[i].getAttribute('name') === 'Metadata') {
|
||||
return metanodes[i].getId();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.handleEvents = function (events) {
|
||||
var metadataId = this.getMetadataId(),
|
||||
event;
|
||||
|
||||
if (!metadataId) {
|
||||
this.logger.error(`No metadata id found! "${metadataId}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
events = events.filter(event => event.eid && (this._pages[event.eid] ||
|
||||
this._client.isTypeOf(event.eid, metadataId)));
|
||||
for (var i = events.length; 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.onUpdate = function (nodeId) {
|
||||
var name = this._client.getNode(nodeId).getAttribute('name');
|
||||
|
||||
if (this._pages[nodeId]) {
|
||||
this._pages[nodeId].find('a').text(name);
|
||||
}
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.onLoad = function (nodeId) {
|
||||
var node = this._client.getNode(nodeId),
|
||||
name = node.getAttribute('name');
|
||||
|
||||
this.addToPager(name, nodeId);
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.onUnload = function (nodeId) {
|
||||
// If the current metadata node is deleted, change back to logviewer
|
||||
if (this._pages[nodeId]) {
|
||||
var selectedId = this.$selected.data('id');
|
||||
if (nodeId === selectedId) {
|
||||
this.selectOutput(this.$logviewer);
|
||||
}
|
||||
|
||||
// Update the pager
|
||||
this._pages[nodeId].remove();
|
||||
this._pageCount--;
|
||||
if (this._pageCount === 0) {
|
||||
this.$pager.addClass('empty');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.addToPager = function (name, nodeId) {
|
||||
var $el = $('<li>'),
|
||||
$a = $('<a>');
|
||||
|
||||
$a.text(name);
|
||||
$a.attr('data-id', nodeId);
|
||||
$el.append($a);
|
||||
this._pages[nodeId] = $el;
|
||||
this.$pager.removeClass('empty');
|
||||
this.$pagerList.append($el);
|
||||
|
||||
this._pageCount++;
|
||||
};
|
||||
|
||||
/* OVERRIDE FROM WIDGET-WITH-HEADER */
|
||||
/* METHOD CALLED WHEN THE WIDGET'S READ-ONLY PROPERTY CHANGES */
|
||||
OutputViewerPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
//apply parent's onReadOnlyChanged
|
||||
PanelBaseWithHeader.prototype.onReadOnlyChanged.call(this, isReadOnly);
|
||||
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.onResize = function (width, height) {
|
||||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
|
||||
this.dimensions = arguments;
|
||||
this.$el.css({
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
this.activePanel.onResize(width, height);
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
OutputViewerPanel.prototype.destroy = function () {
|
||||
this.activePanel.destroy();
|
||||
PanelBaseWithHeader.prototype.destroy.call(this);
|
||||
WebGMEGlobal.KeyboardManager.setListener(undefined);
|
||||
WebGMEGlobal.Toolbar.refresh();
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.onActivate = function () {
|
||||
this.activePanel.onActivate();
|
||||
};
|
||||
|
||||
OutputViewerPanel.prototype.onDeactivate = function () {
|
||||
this.activePanel.onDeactivate();
|
||||
};
|
||||
|
||||
return OutputViewerPanel;
|
||||
});
|
||||
@@ -5,6 +5,7 @@ define([
|
||||
'js/Constants',
|
||||
'panels/EasyDAG/EasyDAGControl',
|
||||
'deepforge/viz/PipelineControl',
|
||||
'deepforge/globals',
|
||||
'common/core/coreQ',
|
||||
'common/storage/constants',
|
||||
'q',
|
||||
@@ -13,6 +14,7 @@ define([
|
||||
CONSTANTS,
|
||||
EasyDAGControl,
|
||||
PipelineControl,
|
||||
DeepForge,
|
||||
Core,
|
||||
STORAGE_CONSTANTS,
|
||||
Q,
|
||||
@@ -37,6 +39,7 @@ define([
|
||||
this.addedIds = {};
|
||||
this.executionTerritory = {};
|
||||
this.executionUI = null;
|
||||
this.invalidated = {};
|
||||
this._widget.deleteNode = id => {
|
||||
this._deleteNode(id);
|
||||
};
|
||||
@@ -53,6 +56,7 @@ define([
|
||||
|
||||
PipelineEditorControl.prototype.TERRITORY_RULE = {children: 3};
|
||||
PipelineEditorControl.prototype.selectedObjectChanged = function (nodeId) {
|
||||
DeepForge.last.Pipeline = nodeId;
|
||||
this._logger.debug('activeObject nodeId \'' + nodeId + '\'');
|
||||
|
||||
// Remove current territory patterns
|
||||
@@ -65,7 +69,7 @@ define([
|
||||
|
||||
if (typeof this._currentNodeId === 'string') {
|
||||
var desc = this._getObjectDescriptor(nodeId);
|
||||
this._widget.setTitle(desc.name.toUpperCase());
|
||||
this._widget.setTitle(desc.name);
|
||||
|
||||
if (typeof desc.parentId === 'string') {
|
||||
this.$btnModelHierarchyUp.show();
|
||||
@@ -80,6 +84,19 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
PipelineEditorControl.prototype.hasCurrentNode = function () {
|
||||
return typeof this._currentNodeId === 'string';
|
||||
};
|
||||
|
||||
PipelineEditorControl.prototype.onNodeNameChanged = function (from, to) {
|
||||
var msg = `Renaming pipeline "${from}" -> "${to}"`;
|
||||
if (from !== to && !/^\s*$/.test(to)) {
|
||||
this._client.startTransaction(msg);
|
||||
this._client.setAttributes(this._currentNodeId, 'name', to);
|
||||
this._client.completeTransaction();
|
||||
}
|
||||
};
|
||||
|
||||
PipelineEditorControl.prototype.updateTerritory = function() {
|
||||
var nodeId = this._currentNodeId;
|
||||
|
||||
@@ -119,6 +136,8 @@ define([
|
||||
this._widget.getExistingPortMatches = this.getExistingPortMatches.bind(this);
|
||||
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) {
|
||||
@@ -132,7 +151,10 @@ define([
|
||||
var desc = this._getObjectDescriptor(gmeId);
|
||||
if (desc.parentId === this._currentNodeId) {
|
||||
this.addedIds[desc.id] = true;
|
||||
return EasyDAGControl.prototype._onLoad.call(this, gmeId);
|
||||
// Validate any connections
|
||||
if (this.isValid(desc)) {
|
||||
return EasyDAGControl.prototype._onLoad.call(this, gmeId);
|
||||
}
|
||||
} else if (desc.parentId !== null &&
|
||||
this.isContainedInActive(desc.parentId) && desc.isDataPort) {
|
||||
// port added!
|
||||
@@ -141,8 +163,33 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
PipelineEditorControl.prototype.isValid = function (desc) {
|
||||
// If it is a "dangling connection", remove it!
|
||||
if (desc.isConnection) {
|
||||
if (!(desc.src && desc.dst)) {
|
||||
var node = this._client.getNode(this._currentNodeId),
|
||||
name = node.getAttribute('name'),
|
||||
msg = `Removing invalid connection ${desc.id} in "${name}"`;
|
||||
|
||||
this.invalidated[desc.id] = true;
|
||||
this._client.startTransaction(msg);
|
||||
this._client.delMoreNodes([desc.id]);
|
||||
this._client.completeTransaction();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
PipelineEditorControl.prototype._onUnload = function (gmeId) {
|
||||
// Check if it has been added
|
||||
if (this.invalidated[gmeId]) {
|
||||
// No need to notify the widget; this was filtered bc it was
|
||||
// an invalid connection
|
||||
delete this.invalidated[gmeId];
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.addedIds[gmeId]) {
|
||||
delete this.addedIds[gmeId];
|
||||
return EasyDAGControl.prototype._onUnload.call(this, gmeId);
|
||||
@@ -537,5 +584,65 @@ define([
|
||||
EasyDAGControl.prototype._detachClientEventListeners.call(this);
|
||||
};
|
||||
|
||||
////////////////////// Execution Support END //////////////////////
|
||||
PipelineEditorControl.prototype.getDecorator = function (nodeId) {
|
||||
var node = this._client.getNode(nodeId);
|
||||
if (node) {
|
||||
return this._getNodeDecorator(node);
|
||||
} else {
|
||||
return this._client.decoratorManager.getDecoratorForWidget(
|
||||
this.DEFAULT_DECORATOR, WIDGET_NAME);
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////// Criterion Support //////////////////////
|
||||
PipelineEditorControl.prototype._getValidTargetsFor = function (id, ptr) {
|
||||
// Check if the pointer is a Criterion pointer -> if so, only show the meta types
|
||||
// and the ones in the custom layer dir
|
||||
var typeIds = this._client.getPointerMeta(id, ptr).items.map(item => item.id),
|
||||
types = typeIds.map(id => this._client.getNode(id)),
|
||||
criterion = types.find(node => node.getAttribute('name') === 'Criterion'),
|
||||
items,
|
||||
criterionId;
|
||||
|
||||
if (criterion) {
|
||||
// Get all criterion types
|
||||
criterionId = criterion.getId();
|
||||
items = this._client.getAllMetaNodes().map(node => node.getId())
|
||||
.filter(id => this._client.isTypeOf(id, criterionId));
|
||||
|
||||
return items.map(id => {
|
||||
return {
|
||||
node: this._getObjectDescriptor(id)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
return EasyDAGControl.prototype._getValidTargetsFor.apply(this, arguments);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return PipelineEditorControl;
|
||||
});
|
||||
|
||||
@@ -61,9 +61,25 @@ define(['js/PanelBase/PanelBaseWithHeader',
|
||||
widget: this.widget
|
||||
});
|
||||
|
||||
// Editable pipeline name
|
||||
this.$panelHeaderTitle.on('dblclick', () => this.editTitle());
|
||||
|
||||
this.onActivate();
|
||||
};
|
||||
|
||||
PipelineEditorPanel.prototype.editTitle = function () {
|
||||
if (this.control.hasCurrentNode()) {
|
||||
this.$panelHeaderTitle.editInPlace({
|
||||
css: {
|
||||
'z-index': 1000
|
||||
},
|
||||
onChange: (oldValue, newValue) => {
|
||||
this.control.onNodeNameChanged(oldValue, newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* OVERRIDE FROM WIDGET-WITH-HEADER */
|
||||
/* METHOD CALLED WHEN THE WIDGET'S READ-ONLY PROPERTY CHANGES */
|
||||
PipelineEditorPanel.prototype.onReadOnlyChanged = function (isReadOnly) {
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
@@ -16,6 +16,7 @@ define([
|
||||
SerializeEditorControl = function (options) {
|
||||
options.attributeName = 'serialize';
|
||||
TextEditorControl.call(this, options);
|
||||
this._widget.setName = this.setName.bind(this);
|
||||
};
|
||||
|
||||
_.extend(
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
/*jshint browser: true*/
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Wed May 18 2016 08:58:20 GMT-0500 (CDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'js/Constants',
|
||||
@@ -47,12 +44,23 @@ define([
|
||||
`for ${id} - node doesn't have the given attribute!`);
|
||||
}
|
||||
};
|
||||
this._widget.setName = this.setName.bind(this);
|
||||
};
|
||||
|
||||
TextEditorControl.prototype.saveTextFor = function (id, text) {
|
||||
this._client.setAttributes(id, this.ATTRIBUTE_NAME, text);
|
||||
};
|
||||
|
||||
TextEditorControl.prototype.setName = function (name) {
|
||||
var node = this._client.getNode(this._currentNodeId),
|
||||
oldName = node.getAttribute('name'),
|
||||
msg = `Renaming ${oldName} -> ${name}`;
|
||||
|
||||
this._client.startTransaction(msg);
|
||||
this._client.setAttributes(this._currentNodeId, 'name', name);
|
||||
this._client.completeTransaction();
|
||||
};
|
||||
|
||||
TextEditorControl.prototype.TERRITORY_RULE = {children: 0};
|
||||
TextEditorControl.prototype.selectedObjectChanged = function (nodeId) {
|
||||
var self = this;
|
||||
@@ -178,6 +186,10 @@ define([
|
||||
TextEditorControl.prototype.destroy = function () {
|
||||
this._detachClientEventListeners();
|
||||
this._removeToolbarItems();
|
||||
|
||||
if (this._territoryId) {
|
||||
this._client.removeUI(this._territoryId);
|
||||
}
|
||||
};
|
||||
|
||||
TextEditorControl.prototype._attachClientEventListeners = function () {
|
||||
|
||||
@@ -33,6 +33,7 @@ define([
|
||||
this._params = params;
|
||||
this._client = params.client;
|
||||
this._embedded = params.embedded;
|
||||
this._resizeArgs = null;
|
||||
|
||||
//initialize UI
|
||||
this._initialize();
|
||||
@@ -49,19 +50,29 @@ define([
|
||||
};
|
||||
|
||||
TilingVizPanel.prototype._initialize = function () {
|
||||
var panels = this.getPanels();
|
||||
|
||||
// Trigger active object
|
||||
if (!this._embedded) {
|
||||
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT,
|
||||
(model, nodeId) => this.selectedObjectChanged(nodeId)
|
||||
);
|
||||
}
|
||||
this.$el.css({padding: 0});
|
||||
this.updatePanels();
|
||||
};
|
||||
|
||||
TilingVizPanel.prototype.updatePanels = function () {
|
||||
var panels = this.getPanels();
|
||||
|
||||
this.logger.info(`updating panels (${panels.length})`);
|
||||
if (panels.length > 2) {
|
||||
this.logger.error(`Unsupported number of panels (${panels.length})`);
|
||||
}
|
||||
|
||||
if (this._panels) {
|
||||
this._panels.forEach(panel => panel.destroy());
|
||||
this.$el.empty();
|
||||
}
|
||||
|
||||
// Create the panels and containers
|
||||
this._panels = panels.map(Panel => new Panel(this._layoutManager, this._params));
|
||||
this._containers = this._panels.map((p, i) => $('<div>', {id: `panel ${i}`}));
|
||||
@@ -73,10 +84,11 @@ define([
|
||||
this._containers[i].append(panel.$el) && panel.onDeactivate()
|
||||
);
|
||||
|
||||
this.control = { // For use in dev mode
|
||||
selectedObjectChanged: this.selectedObjectChanged.bind(this)
|
||||
};
|
||||
this.control = this;
|
||||
this.onActivate();
|
||||
if (this._resizeArgs) {
|
||||
this.onResize.apply(this, this._resizeArgs);
|
||||
}
|
||||
};
|
||||
|
||||
TilingVizPanel.prototype.selectedObjectChanged = function (nodeId) {
|
||||
@@ -103,6 +115,7 @@ define([
|
||||
// Call onResize for each of the tiles
|
||||
this.logger.debug('onResize --> width: ' + width + ', height: ' + height);
|
||||
this._panels.forEach(p => p.onResize(pwidth, height));
|
||||
this._resizeArgs = [width, height];
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
|
||||
@@ -1,32 +1,96 @@
|
||||
/*globals define*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Tue May 17 2016 11:25:46 GMT-0400 (EDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'deepforge/globals',
|
||||
'widgets/EasyDAG/EasyDAGWidget',
|
||||
'widgets/EasyDAG/AddNodeDialog',
|
||||
'./Layer',
|
||||
'q',
|
||||
'underscore',
|
||||
'css!./styles/ArchEditorWidget.css'
|
||||
], function (
|
||||
DeepForge,
|
||||
EasyDAGWidget,
|
||||
AddNodeDialog,
|
||||
Layer,
|
||||
Q,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var ArchEditorWidget;
|
||||
// WIDGET_CLASS = 'arch-editor',
|
||||
var CREATE_ID = '__NEW_LAYER__',
|
||||
ArchEditorWidget,
|
||||
WIDGET_CLASS = 'arch-editor';
|
||||
|
||||
ArchEditorWidget = function (logger, container) {
|
||||
EasyDAGWidget.call(this, logger, container);
|
||||
this.$el.addClass(WIDGET_CLASS);
|
||||
};
|
||||
|
||||
_.extend(ArchEditorWidget.prototype, EasyDAGWidget.prototype);
|
||||
|
||||
ArchEditorWidget.prototype.ItemClass = Layer;
|
||||
|
||||
ArchEditorWidget.prototype.onCreateInitialNode = function() {
|
||||
var nodes = this.getValidInitialNodes();
|
||||
return this.promptLayer(nodes)
|
||||
.then(selected => this.createNode(selected.node.id));
|
||||
};
|
||||
|
||||
ArchEditorWidget.prototype.onAddButtonClicked = function(item, reverse) {
|
||||
var nodes = this.getValidSuccessors(item.id);
|
||||
|
||||
return this.promptLayer(nodes)
|
||||
.then(selected => this.onAddItemSelected(item, selected, reverse));
|
||||
};
|
||||
|
||||
ArchEditorWidget.prototype.promptLayer = function(nodes) {
|
||||
var deferred = Q.defer(),
|
||||
types = {},
|
||||
Decorator = this.getCreateNewDecorator(),
|
||||
createNews,
|
||||
opts = {}; // 'create new' nodes
|
||||
|
||||
nodes.map(pair => pair.node)
|
||||
.forEach(node => types[node.layerType] = node.color);
|
||||
|
||||
createNews = Object.keys(types).map(type =>
|
||||
this._creationNode(type, types[type], Decorator));
|
||||
|
||||
nodes = nodes.concat(createNews);
|
||||
|
||||
// Sort by layer type
|
||||
opts.tabs = Object.keys(types);
|
||||
opts.tabFilter = (tab, pair) => {
|
||||
return pair.node.layerType === tab;
|
||||
};
|
||||
|
||||
AddNodeDialog.prompt(nodes, opts)
|
||||
.then(selected => {
|
||||
if (selected.node.id === CREATE_ID) {
|
||||
DeepForge.create.Layer(selected.node.layerType);
|
||||
} else {
|
||||
deferred.resolve(selected);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
ArchEditorWidget.prototype._creationNode = function(type, color, Decorator) {
|
||||
return {
|
||||
node: {
|
||||
id: CREATE_ID,
|
||||
class: 'create-node',
|
||||
attributes: {},
|
||||
name: `New ${type} Layer...`,
|
||||
baseName: `New ${type} Layer...`,
|
||||
layerType: type,
|
||||
color: color,
|
||||
Decorator: Decorator
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return ArchEditorWidget;
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@ define([
|
||||
this.width = this.decorator.width;
|
||||
this.height = this.decorator.height;
|
||||
|
||||
this.initializeTooltips();
|
||||
// Set up decorator callbacks
|
||||
this.setupDecoratorCallbacks();
|
||||
};
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
*/
|
||||
|
||||
define([
|
||||
'widgets/TextEditor/TextEditorWidget',
|
||||
'widgets/SerializeEditor/SerializeEditorWidget',
|
||||
'underscore',
|
||||
'css!./styles/DeserializeEditorWidget.css'
|
||||
], function (
|
||||
TextEditorWidget,
|
||||
SerializeEditorWidget,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
@@ -19,10 +19,10 @@ define([
|
||||
//WIDGET_CLASS = 'deserialize-editor';
|
||||
|
||||
DeserializeEditorWidget = function (logger, container) {
|
||||
TextEditorWidget.call(this, logger, container);
|
||||
SerializeEditorWidget.call(this, logger, container);
|
||||
};
|
||||
|
||||
_.extend(DeserializeEditorWidget.prototype, TextEditorWidget.prototype);
|
||||
_.extend(DeserializeEditorWidget.prototype, SerializeEditorWidget.prototype);
|
||||
|
||||
DeserializeEditorWidget.prototype.getHeader = function(desc) {
|
||||
return [
|
||||
@@ -34,8 +34,8 @@ define([
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
DeserializeEditorWidget.prototype.updateNode = function() {
|
||||
// nop
|
||||
DeserializeEditorWidget.prototype.getNameRegex = function() {
|
||||
return /The deserialization function for (.*)/;
|
||||
};
|
||||
|
||||
return DeserializeEditorWidget;
|
||||
|
||||
@@ -24,6 +24,7 @@ define([
|
||||
context: this._widget,
|
||||
$pEl: this.$selection,
|
||||
item: this.selectedItem,
|
||||
icon: 'monitor',
|
||||
x: width,
|
||||
y: 0
|
||||
});
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*globals define, d3, nv */
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'./lib/nv.d3.min',
|
||||
'css!./lib/nv.d3.min.css'
|
||||
], function (
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var LineGraphWidget,
|
||||
WIDGET_CLASS = 'line-graph';
|
||||
|
||||
LineGraphWidget = function (logger, container) {
|
||||
this._logger = logger.fork('Widget');
|
||||
|
||||
this.$el = container;
|
||||
|
||||
this.lineData = [];
|
||||
this._initialize();
|
||||
|
||||
this._logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
LineGraphWidget.prototype._initialize = function () {
|
||||
// set widget class
|
||||
this.$el.addClass(WIDGET_CLASS);
|
||||
|
||||
// Create the chart
|
||||
this.options = {};
|
||||
this.options.xAxis = null;
|
||||
this.options.yAxis = null;
|
||||
|
||||
this.chart = null;
|
||||
this.$chart = d3.select(this.$el[0]).append('svg');
|
||||
nv.addGraph(() => {
|
||||
var chart = nv.models.lineChart()
|
||||
//.margin({left: 100})
|
||||
.useInteractiveGuideline(true)
|
||||
.showLegend(true)
|
||||
.showYAxis(true)
|
||||
.showXAxis(true);
|
||||
|
||||
chart.xAxis
|
||||
.tickFormat(d3.format(',r'));
|
||||
|
||||
if (this.options.xAxis) {
|
||||
chart.xAxis
|
||||
.axisLabel(this.options.xAxis);
|
||||
}
|
||||
|
||||
if (this.options.yAxis) {
|
||||
chart.yAxis
|
||||
.axisLabel(this.options.yAxis);
|
||||
}
|
||||
|
||||
var myData = this.getData();
|
||||
|
||||
this.$chart
|
||||
.datum(myData)
|
||||
.call(chart);
|
||||
|
||||
//Update the chart when window resizes.
|
||||
nv.utils.windowResize(() => chart.update());
|
||||
this.chart = chart;
|
||||
return chart;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
LineGraphWidget.prototype.getData = function () {
|
||||
return Object.keys(this.lineData).map(id => this.lineData[id]);
|
||||
};
|
||||
|
||||
// Adding/Removing/Updating items
|
||||
LineGraphWidget.prototype.addNode = function (desc) {
|
||||
if (desc) {
|
||||
// Add node to a table of nodes
|
||||
if (desc.type === 'line') {
|
||||
this.lineData[desc.id] = {
|
||||
key: desc.name,
|
||||
values: desc.points
|
||||
};
|
||||
}
|
||||
this.updateChartData();
|
||||
}
|
||||
};
|
||||
|
||||
LineGraphWidget.prototype.removeNode = function (id) {
|
||||
delete this.lineData[id];
|
||||
this.updateChartData();
|
||||
};
|
||||
|
||||
LineGraphWidget.prototype.updateNode = function (desc) {
|
||||
if (desc && this.lineData[desc.id]) {
|
||||
this.lineData[desc.id].values = desc.points;
|
||||
this.lineData[desc.id].key = desc.name;
|
||||
this.updateChartData();
|
||||
}
|
||||
};
|
||||
|
||||
LineGraphWidget.prototype.onWidgetContainerResize = function(width, height) {
|
||||
this.$el.css({
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
this.updateChart();
|
||||
};
|
||||
|
||||
LineGraphWidget.prototype.updateChartData = function () {
|
||||
if (this.$chart && this.chart) {
|
||||
this.$chart
|
||||
.datum(this.getData())
|
||||
.call(this.chart);
|
||||
}
|
||||
};
|
||||
|
||||
LineGraphWidget.prototype.updateChart = function () {
|
||||
if (this.chart) {
|
||||
this.chart.update();
|
||||
}
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
LineGraphWidget.prototype.destroy = function () {
|
||||
};
|
||||
|
||||
LineGraphWidget.prototype.onActivate = function () {
|
||||
this._logger.debug('LineGraphWidget has been activated');
|
||||
};
|
||||
|
||||
LineGraphWidget.prototype.onDeactivate = function () {
|
||||
this._logger.debug('LineGraphWidget has been deactivated');
|
||||
};
|
||||
|
||||
return LineGraphWidget;
|
||||
});
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["build/nv.d3.css"],"names":[],"mappings":"AAqBA,oBAfA,oBAgBI,KAAM,KAmXN,gBAAiB,WAhFrB,kBA+DA,uBAnWA,oBAfA,oBAmYI,gBAAiB,WAmErB,UAAW,UAJX,mBAvcA,eAsbA,uBA8BA,uCACI,eAAgB,KA+DpB,WA7QA,aAoRI,QAAS,MAST,sBAAuB,KAEvB,mBAAoB,KAtiBxB,eAEI,QAAS,EAuCb,2BA4JA,0DACI,QAAS,EAjMb,oBAEI,OAAQ,KACR,eAAgB,IAIpB,2BACI,eAAgB,IAGpB,gCACI,eAAgB,EAGpB,oBAEI,OAAQ,QAIZ,0BACI,0BACA,eAAgB,IAGpB,mCACI,YAAa,IAGjB,sCACA,uCACA,uCACI,YAAa,OAOjB,oBACI,aAAc,IAEd,WAAY,aAAa,MAAM,OAC/B,gBAAiB,aAAa,MAAM,OACpC,mBAAoB,aAAa,MAAM,OAG3C,0BACI,aAAc,EAGlB,2BACI,KAAM,QAGV,oBACI,KAAM,YAGV,2BACI,KAAM,cAKV,sCAFA,mCACA,6CAEI,eAAgB,EAEhB,WAAY,aAAa,MAAM,OAC/B,gBAAiB,aAAa,MAAM,OACpC,mBAAoB,aAAa,MAAM,OAK3C,8CACA,4CAHA,yCACA,mDAGI,aAAc,EAGlB,sCACA,6CACI,YAAa,IACb,KAAM,cACN,OAAQ,YAIZ,yBACE,aAAc,GAGhB,+BAIA,6BAHE,aAAc,EAOhB,6BACE,OAAQ,KAGV,uBACE,aAAc,MAGhB,gBAAkB,KAAM,KAAK,WAC7B,4BAA8B,aAAc,GAC5C,kCAAoC,aAAc,EAClD,2BAA6B,OAAQ,KAAM,aAAc,IACzD,mCAAqC,OAAQ,KAAM,KAAM,KAAM,aAAc,MAC7E,+BAAiC,OAAQ,KAAM,aAAc,MAC7D,8BAAgC,OAAQ,KAAM,aAAc,KAC5D,gCAAkC,KAAM,KACxC,gCAAkC,KAAM,KACxC,gCAAkC,KAAM,KACxC,0BAA4B,UAAW,KAAM,YAAa,IAC1D,6BAA+B,KAAM,KAGrC,0BACI,KAAM,QACN,aAAc,GAElB,gCACI,aAAc,GAGlB,2CACI,aAAc,IAGlB,iDACI,aAAc,IAGlB,yDACI,OAAQ,QACR,KAAM,QAGV,yDACI,OAAQ,QACR,KAAM,QAGV,wDACI,WAAY,aAAa,MAAM,OAAQ,eAAe,MAAM,OAC5D,gBAAiB,aAAa,MAAM,OAAQ,eAAe,MAAM,OACjE,mBAAoB,aAAa,MAAM,OAAQ,eAAe,MAAM,OAIxE,uCACI,OAAQ,KAIZ,eACE,OAAQ,KACR,aAAc,MAEhB,eACE,OAAQ,KACR,eAAgB,GAElB,oBACE,aAAc,EAOhB,4BACI,aAAa,EACb,aAAa,EAGjB,8BACI,aAAa,EACb,aAAa,EAGjB,qDACI,aAAa,EACb,eAAe,EAQnB,kCACI,aAAc,IAGlB,wCACI,aAAc,EAElB,8BACI,KAAM,KAGV,8BACI,OAAQ,KAGZ,oDACI,aAAc,EACd,eAAgB,EAGpB,sDACI,aAAc,aACd,eAAgB,aAIpB,6CACI,WAAY,aAAa,MAAM,OAAQ,eAAe,MAAM,OAC5D,gBAAiB,aAAa,MAAM,OAAQ,eAAe,MAAM,OACjE,mBAAoB,aAAa,MAAM,OAAQ,eAAe,MAAM,OAKxE,iCADA,4CAEI,aAAc,IACd,aAAc,cACd,eAAgB,cAIpB,2BACI,OAAQ,KACR,eAAgB,EAChB,KAAM,KACN,aAAc,EAKlB,oBACI,OAAQ,UAUZ,aAEI,oBAAqB,KAErB,gBAAiB,KACjB,iBAAkB,KAClB,YAAa,KAEb,MAAM,KACN,OAAO,KAMX,0BAA2B,2BACvB,gBAAiB,EAAE,IAAI,KAAK,eAC5B,mBAAoB,EAAE,IAAI,KAAK,eAC/B,WAAY,EAAE,IAAI,KAAK,eAEvB,sBAAuB,IACvB,mBAAoB,IACpB,cAAe,IAInB,WACI,KAAM,IAAO,KAAK,MAGtB,aACI,KAAM,IAAK,KAAK,MAGpB,qBACI,KAAM,KACN,aAAc,EAGlB,gBACI,UAAW,KACX,YAAa,IAQjB,kBACI,aAAc,KAIlB,uBACI,KAAM,KACN,OAAQ,KAQZ,4BACI,OAAQ,QAGZ,qCACI,aAAc,EAIlB,wBACI,aAAc,YAGlB,+BACI,OAAQ,KACR,aAAc,GACd,KAAM,KACN,aAAc,GAOlB,aACE,WACE,aAAc,EACd,aAAc,GAIlB,oCACI,aAAc,IAGlB,0CACI,aAAc,IAGlB,6CACI,OAAQ,QAGZ,6CACI,OAAQ,QAIZ,uBACI,KAAM,KACN,OAAQ,KACR,eAAgB,GAIpB,uBACI,KAAM,KACN,eAAgB,GAGpB,4CAEI,KAAM,KACN,aAAc,GACd,OAAQ,KACR,gBAAiB,WAGrB,qCACI,aAAc,EACjB,aAAc,IAIf,8BACE,KAAM,KACN,OAAQ,KACR,aAAc,EACd,eAAgB,EAChB,iBAAkB,EAAG,EAUvB,2BACI,UAAW,KACX,KAAM,qBAGV,4BACI,OAAQ,KACR,aAAc,EAGlB,kBAhBI,WAAY,aAAa,MAAM,OAAQ,aAAa,MAAM,OAAQ,eAAe,MAAM,OACvF,gBAAiB,aAAa,MAAM,OAAQ,aAAa,MAAM,OAAQ,eAAe,MAAM,OAC5F,mBAAoB,aAAa,MAAM,OAAQ,aAAa,MAAM,OAAQ,eAAe,MAAM,OAe/F,OAAQ,KACR,aAAc,IACd,eAAgB,EAIhB,aAAc,GAElB,yBACI,aAAc,EAKlB,4BACI,aAAc,EACd,eAAgB,EAIpB,iCACI,aAAc,KACd,eAAgB,GAGpB,kCACI,aAAc,EAWlB,wBACI,KAAM,KAOV,2CACI,OAAQ,KACR,aAAc,MAGlB,uBACA,yBACI,eAAgB,IA4LpB,+BAvIA,WAwII,eAAe,KA1LnB,oBACI,aAAc,EACd,eAAgB,EAGpB,kCACA,kCACI,aAAc,EACd,UAAW,KACX,YAAa,IAGjB,kCACI,OAAQ,KAGZ,oCACI,OAAQ,QACR,KAAM,QAGV,oCACI,OAAQ,QACR,KAAM,QAGV,wCACI,YAAa,IACb,UAAW,MAsEf,cAsCA,wBACI,YAAa,IA1GjB,kCACI,aAAc,GACd,eAAgB,EAChB,WAAY,aAAa,MAAM,OAAQ,eAAe,MAAM,OAC5D,gBAAiB,aAAa,MAAM,OAAQ,eAAe,MAAM,OACjE,mBAAoB,aAAa,MAAM,OAAQ,eAAe,MAAM,OAGxE,wCACI,aAAc,GAIlB,0CACI,eAAgB,EAChB,aAAc,EAIlB,WACI,SAAU,SAEV,MAAO,cACP,QAAS,IAET,QAAS,MAGT,YAAa,MACb,UAAW,KACX,WAAY,KAGZ,YAAa,OAGb,oBAAqB,KAErB,iBAAkB,KAClB,gBAAiB,KACjB,YAAa,KAIb,WAAY,qBACZ,OAAQ,IAAI,MAAM,eAClB,cAAe,IAqBnB,cAgBA,aACI,OAAQ,EAER,WAAY,OAlChB,4BAA6B,6BACzB,WAAY,QAAQ,KAAK,OACzB,gBAAiB,QAAQ,KAAK,OAC9B,mBAAoB,QAAQ,KAAK,OAEjC,iBAAkB,MAClB,sBAAuB,MACvB,yBAA0B,MAG9B,uBACA,uBACI,QAAS,IAGb,cAEI,QAAS,IAAI,KACb,YAAa,KAEb,iBAAkB,sBAClB,MAAO,cAGP,cAAe,IAAI,MAAM,QAEzB,sBAAuB,IAAI,IAAI,EAAE,EACjC,mBAAoB,IAAI,IAAI,EAAE,EAC9B,cAAe,IAAI,IAAI,EAAE,EAG7B,aAEI,QAAS,IAAI,KAIjB,gBACI,QAAS,aACT,OAAQ,IAAI,EAGhB,iBACI,OAAQ,IACR,eAAe,EAInB,oBACI,QAAS,IAAI,IAAI,IAAI,EACrB,eAAgB,OAMpB,8BACI,YAAa,IAEjB,0BACI,WAAY,MACZ,YAAa,IAGjB,4BACI,MAAO,QAGX,iCACI,QAAS,IAAI,IAAI,IAAI,EACrB,oBAAqB,MACrB,oBAAqB,IACrB,iBAAkB,MAClB,iBAAkB,IAGtB,2CAGI,eAAgB,OAIhB,MAAO,KACP,OAAQ,KACR,OAAQ,IAAI,MAAM,KAGtB,mBACI,QAAS,IACT,WAAY,OAGhB,2BACI,eAAgB,KAChB,QAAS,KAUb,wBACI,OAAQ"}
|
||||
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -100,6 +100,17 @@ define([
|
||||
};
|
||||
};
|
||||
|
||||
LogViewerWidget.prototype.addNode = function (desc) {
|
||||
var atEOF = this.editor.getLastVisibleRow()+1 ===
|
||||
this.editor.session.getLength();
|
||||
|
||||
TextEditorWidget.prototype.addNode.call(this, desc);
|
||||
|
||||
if (atEOF) { // Scroll to bottom
|
||||
this.editor.gotoLine(Infinity);
|
||||
}
|
||||
};
|
||||
|
||||
LogViewerWidget.prototype.getEditorOptions = function() {
|
||||
return {
|
||||
fontSize: '10pt'
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<li data-id="<%= id %>" class="select-node">
|
||||
<a>
|
||||
<%= name %>
|
||||
<div class="pull-right">
|
||||
<% if (typeof data !== 'undefined') { %>
|
||||
<span class="glyphicon glyphicon-cloud-download dl-node"></span>
|
||||
<% } %>
|
||||
<span class="glyphicon glyphicon-remove del-node"></span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
@@ -0,0 +1,182 @@
|
||||
/*globals $, WebGMEGlobal,define */
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'deepforge/globals',
|
||||
'text!./NavBar.html',
|
||||
'text!./ListItem.ejs',
|
||||
'underscore',
|
||||
'css!./styles/MainViewWidget.css'
|
||||
], function (
|
||||
DeepForge,
|
||||
NavBarHTML,
|
||||
ListItem,
|
||||
_
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var MainViewWidget,
|
||||
WIDGET_CLASS = 'main-view',
|
||||
CreateListItem = _.template(ListItem);
|
||||
|
||||
MainViewWidget = function (logger, container) {
|
||||
this._logger = logger.fork('Widget');
|
||||
this.$el = container;
|
||||
this.$el.addClass(WIDGET_CLASS);
|
||||
this.initialize();
|
||||
this._logger.debug('ctor finished');
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.initialize = function () {
|
||||
// Create the nav bar
|
||||
this.$nav = $(NavBarHTML);
|
||||
this.$el.append(this.$nav);
|
||||
|
||||
this.$archlist = this.$nav.find('#arch-list-content');
|
||||
this.$artifacts = this.$nav.find('#artifact-list-content');
|
||||
|
||||
// opening, closing
|
||||
this._closed = true;
|
||||
this.$nav.find('.side-nav-control').on('click', this.controlClicked.bind(this));
|
||||
this.$nav.on('transitionend', this.onChanged.bind(this));
|
||||
|
||||
// action buttons
|
||||
this.$nav.on('click', '#create-artifact', DeepForge.create.Artifact);
|
||||
this.$nav.on('click', '#create-arch', DeepForge.create.Architecture);
|
||||
this.$nav.on('click', '.select-node', this.onNodeClick.bind(this));
|
||||
this.$nav.on('click', '.del-node', this.onDelNodeClick.bind(this));
|
||||
this.$nav.on('click', '.dl-node', this.onDownloadNodeClick.bind(this));
|
||||
|
||||
this.htmlFor = {};
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.width = function () {
|
||||
return this._closedWidth;
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.onChanged = function () {
|
||||
if (!this._closed) { // add the text back
|
||||
this.$nav.removeClass('hide-list');
|
||||
} else {
|
||||
this._closedWidth = this.$nav.width();
|
||||
}
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.controlClicked = function () {
|
||||
this._closed = !this._closed;
|
||||
if (this._closed) {
|
||||
this.$nav.addClass('hide-list');
|
||||
this.$nav.addClass('closed');
|
||||
} else { // remove the 'closed' class
|
||||
this.$nav.removeClass('closed');
|
||||
}
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.onWidgetContainerResize = function () {
|
||||
var rect = this.$el[0].getBoundingClientRect(),
|
||||
top = rect.top;
|
||||
|
||||
this.$nav.css({
|
||||
top: top + 'px'
|
||||
});
|
||||
|
||||
if (this._closed) {
|
||||
this._closedWidth = this.$nav.width();
|
||||
}
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.createNode = function(desc) {
|
||||
// Create the architecture list item
|
||||
var li;
|
||||
|
||||
desc.download = false;
|
||||
li = $(CreateListItem(desc));
|
||||
this.htmlFor[desc.id] = li;
|
||||
return li;
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.addArch = function(desc) {
|
||||
var html = this.createNode(desc);
|
||||
this.$archlist.append(html);
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.addArtifact = function(desc) {
|
||||
var html = this.createNode(desc);
|
||||
this.$artifacts.append(html);
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.onDelNodeClick = function(event) {
|
||||
var id = this.getElementId(event.target);
|
||||
event.stopPropagation();
|
||||
if (id) {
|
||||
this.deleteNode(id);
|
||||
}
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.onDownloadNodeClick = function(event) {
|
||||
var id = this.getElementId(event.target),
|
||||
url;
|
||||
|
||||
event.stopPropagation();
|
||||
if (id) {
|
||||
url = this.dataUrlFor(id);
|
||||
if (url) {
|
||||
this._download(url);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MainViewWidget.prototype._download = function(url) {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.setAttribute('href', url);
|
||||
anchor.setAttribute('target', '_self');
|
||||
anchor.click();
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.onNodeClick = function(event) {
|
||||
var id = this.getElementId(event.target);
|
||||
event.stopPropagation();
|
||||
if (id) {
|
||||
WebGMEGlobal.State.registerActiveObject(id);
|
||||
}
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.getElementId = function(element) {
|
||||
while(!element.getAttribute('data-id') && element.parentNode) {
|
||||
element = element.parentNode;
|
||||
}
|
||||
return element.getAttribute('data-id');
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.removeNode = function(id) {
|
||||
if (this.htmlFor[id]) {
|
||||
this.htmlFor[id].remove();
|
||||
delete this.htmlFor[id];
|
||||
}
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.updateNode = function (desc) {
|
||||
var oldHtml = this.htmlFor[desc.id],
|
||||
node;
|
||||
|
||||
if (oldHtml) {
|
||||
node = this.createNode(desc);
|
||||
node.insertAfter(oldHtml);
|
||||
oldHtml.remove();
|
||||
}
|
||||
};
|
||||
|
||||
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
|
||||
MainViewWidget.prototype.destroy = function () {
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.onActivate = function () {
|
||||
this._logger.debug('MainViewWidget has been activated');
|
||||
};
|
||||
|
||||
MainViewWidget.prototype.onDeactivate = function () {
|
||||
this._logger.debug('MainViewWidget has been deactivated');
|
||||
};
|
||||
|
||||
return MainViewWidget;
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
<nav class="side-nav fixed closed hide-list">
|
||||
<li class="pull-right side-nav-control">
|
||||
<span class="glyphicon glyphicon-menu-hamburger" aria-hidden="true"></span>
|
||||
</li>
|
||||
<li class="no-padding">
|
||||
<ul class="collapsible" data-collapsible="accordion">
|
||||
<li>
|
||||
<a data-target="#arch-list" data-toggle="collapse">Architectures</a>
|
||||
<div id="arch-list" class="collapse">
|
||||
<ul class="sublist">
|
||||
<div id="arch-list-content"></div>
|
||||
<li><a id="create-arch" class="action" href="#">New...</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="no-padding">
|
||||
<ul class="collapsible collapsible-accordion">
|
||||
<li>
|
||||
<a data-target="#artifact-list" data-toggle="collapse">Artifacts</a>
|
||||
<div id="artifact-list" class="collapse">
|
||||
<ul class="sublist">
|
||||
<div id="artifact-list-content"></div>
|
||||
<li>
|
||||
<a id="create-artifact" class="action" href="#!">Upload...</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</nav>
|
||||
@@ -0,0 +1,276 @@
|
||||
.main-view .side-nav-control {
|
||||
padding-right: 1em;
|
||||
padding-top: 1em;
|
||||
line-height: inherit !important;
|
||||
}
|
||||
|
||||
.main-view .side-nav-control span {
|
||||
font-size: 16px;
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
/* closed */
|
||||
.main-view .side-nav.closed {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.main-view .hide-list ul {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.main-view .side-nav {
|
||||
transform: translateX(0);
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
/* closed END */
|
||||
|
||||
.main-view .side-nav .action {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.main-view .side-nav {
|
||||
background-color: #f5f5f5 !important;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.main-view .side-nav a {
|
||||
color: #444;
|
||||
display: block;
|
||||
font-size: 1.5rem;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
.main-view .side-nav li {
|
||||
text-align: left;
|
||||
color: #f5f5f5 !important;
|
||||
}
|
||||
|
||||
.main-view .side-nav .sublist ul {
|
||||
padding-left: 20px;
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.main-view .side-nav .sublist a {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.main-view ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.main-view .valign-wrapper {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main-view .valign-wrapper .valign {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.main-view ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.main-view ul li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.main-view .clearfix {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.main-view .z-depth-1,
|
||||
.main-view nav,
|
||||
.main-view .card-panel,
|
||||
.main-view .card,
|
||||
.main-view .toast,
|
||||
.main-view .btn,
|
||||
.main-view .btn-large,
|
||||
.main-view .btn-floating,
|
||||
.main-view .dropdown-content,
|
||||
.main-view .collapsible,
|
||||
.main-view .side-nav {
|
||||
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible,
|
||||
.main-view .side-nav.fixed .collapsible {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible li,
|
||||
.main-view .side-nav.fixed .collapsible li {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible-header,
|
||||
.main-view .side-nav.fixed .collapsible-header {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
line-height: inherit;
|
||||
height: inherit;
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible-header:hover,
|
||||
.main-view .side-nav.fixed .collapsible-header:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible-header i,
|
||||
.main-view .side-nav.fixed .collapsible-header i {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible-body,
|
||||
.main-view .side-nav.fixed .collapsible-body {
|
||||
border: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible-body li a,
|
||||
.main-view .side-nav.fixed .collapsible-body li a {
|
||||
padding: 0 37.5px 0 45px;
|
||||
}
|
||||
|
||||
.main-view .side-nav {
|
||||
position: fixed;
|
||||
width: 200px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin: 0;
|
||||
-o-transform: translateX(-100%);
|
||||
-ms-transform: translateX(-100%);
|
||||
-webkit-transform: translateX(-100%);
|
||||
-moz-transform: translateX(-100%);
|
||||
transform: translateX(-100%);
|
||||
height: 100%;
|
||||
height: calc(100% + 60px);
|
||||
height: -moz-calc(100%);
|
||||
padding-bottom: 60px;
|
||||
background-color: #fff;
|
||||
z-index: 999;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.main-view .side-nav li {
|
||||
float: none;
|
||||
line-height: 64px;
|
||||
}
|
||||
|
||||
.main-view .side-nav li.active {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.main-view .side-nav a:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.main-view .side-nav a.btn,
|
||||
.main-view .side-nav a.btn-large,
|
||||
.main-view .side-nav a.btn-large,
|
||||
.main-view .side-nav a.btn-flat,
|
||||
.main-view .side-nav a.btn-floating {
|
||||
margin: 10px 15px;
|
||||
}
|
||||
|
||||
.main-view .side-nav a.btn,
|
||||
.main-view .side-nav a.btn-large,
|
||||
.main-view .side-nav a.btn-large,
|
||||
.main-view .side-nav a.btn-floating {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.main-view .side-nav a.btn-flat {
|
||||
color: #343434;
|
||||
}
|
||||
|
||||
.main-view .side-nav a.btn:hover,
|
||||
.main-view .side-nav a.btn-large:hover,
|
||||
.main-view .side-nav a.btn-large:hover {
|
||||
background-color: #2bbbad;
|
||||
}
|
||||
|
||||
.main-view .side-nav a.btn-floating:hover {
|
||||
background-color: #26a69a;
|
||||
}
|
||||
|
||||
.main-view .drag-target {
|
||||
height: 100%;
|
||||
width: 10px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
.main-view .side-nav.fixed a {
|
||||
display: block;
|
||||
padding: 0 30px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.main-view .side-nav.fixed {
|
||||
left: 0;
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible-body li.active,
|
||||
.main-view .side-nav.fixed .collapsible-body li.active {
|
||||
background-color: #ee6e73;
|
||||
}
|
||||
|
||||
.main-view .side-nav .collapsible-body li.active a,
|
||||
.main-view .side-nav.fixed .collapsible-body li.active a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Links
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background color from active links in IE 10.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Improve readability of focused elements when they are also in an
|
||||
* active/hover state.
|
||||
*/
|
||||
|
||||
.main-view a:active,
|
||||
.main-view a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.main-view a {
|
||||
background-color: transparent;
|
||||
color: #039be5;
|
||||
text-decoration: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.main-view .collapsible.popout {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.main-view .action a {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ define([
|
||||
|
||||
OperationCodeEditorWidget = function (logger, container) {
|
||||
TextEditorWidget.call(this, logger, container);
|
||||
this.lineOffset = 0;
|
||||
};
|
||||
|
||||
_.extend(OperationCodeEditorWidget.prototype, TextEditorWidget.prototype);
|
||||
@@ -30,11 +31,11 @@ define([
|
||||
refs = desc.references.map(name => `-- ${name}`).join('\n'),
|
||||
outputs,
|
||||
header = [
|
||||
`-- Editing "${desc.name}"`,
|
||||
'-- '
|
||||
`-- Editing "${desc.name}" Implementation`
|
||||
];
|
||||
|
||||
if (inputs.length) {
|
||||
header.push('--');
|
||||
header.push('-- Defined variables:');
|
||||
header.push(inputs);
|
||||
}
|
||||
@@ -42,6 +43,7 @@ define([
|
||||
header.push(refs);
|
||||
}
|
||||
header.push('--');
|
||||
header.push('-- The following will be executed when the operation is run:');
|
||||
|
||||
// Add info about outputs
|
||||
outputs = desc.outputs.map(pair => `-- ${pair[0]} = <some ${pair[1]}>`)
|
||||
@@ -57,5 +59,26 @@ define([
|
||||
return header.join('\n');
|
||||
};
|
||||
|
||||
OperationCodeEditorWidget.prototype.addNode = function () {
|
||||
TextEditorWidget.prototype.addNode.apply(this, arguments);
|
||||
this.updateOffset();
|
||||
};
|
||||
|
||||
OperationCodeEditorWidget.prototype.setLineOffset = function (offset) {
|
||||
if (this.lineOffset !== offset) {
|
||||
this.lineOffset = offset;
|
||||
this.updateOffset();
|
||||
}
|
||||
};
|
||||
|
||||
OperationCodeEditorWidget.prototype.updateOffset = function () {
|
||||
var lines,
|
||||
actualOffset;
|
||||
|
||||
lines = this.currentHeader.match(/\n/g);
|
||||
actualOffset = this.lineOffset - (lines ? lines.length : 0);
|
||||
this.editor.setOption('firstLineNumber', actualOffset);
|
||||
};
|
||||
|
||||
return OperationCodeEditorWidget;
|
||||
});
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
/*globals define*/
|
||||
define([
|
||||
'deepforge/viz/Buttons',
|
||||
'widgets/EasyDAG/Buttons',
|
||||
'widgets/EasyDAG/Icons',
|
||||
'underscore'
|
||||
], function(
|
||||
CommonButtons,
|
||||
EasyDAGButtons,
|
||||
Icons,
|
||||
_
|
||||
) {
|
||||
|
||||
@@ -13,14 +17,13 @@ define([
|
||||
|
||||
_.extend(AddOutput.prototype, EasyDAGButtons.Add.prototype);
|
||||
|
||||
AddOutput.BORDER = 2;
|
||||
AddOutput.prototype._render = function() {
|
||||
var lineRadius = EasyDAGButtons.Add.SIZE - EasyDAGButtons.Add.BORDER,
|
||||
btnColor = '#90caf9',
|
||||
lineColor = '#7986cb';
|
||||
var lineRadius = EasyDAGButtons.Add.SIZE - AddOutput.BORDER,
|
||||
btnColor = '#90caf9';
|
||||
|
||||
if (this.disabled) {
|
||||
btnColor = '#e0e0e0';
|
||||
lineColor = '#9e9e9e';
|
||||
}
|
||||
|
||||
this.$el
|
||||
@@ -28,34 +31,7 @@ define([
|
||||
.attr('r', EasyDAGButtons.Add.SIZE)
|
||||
.attr('fill', btnColor);
|
||||
|
||||
this.$el
|
||||
.append('line')
|
||||
.attr('x1', 0)
|
||||
.attr('x2', 0)
|
||||
.attr('y1', -lineRadius)
|
||||
.attr('y2', lineRadius)
|
||||
.attr('stroke-width', 2)
|
||||
.attr('stroke', lineColor);
|
||||
|
||||
// Arrow
|
||||
this.$el
|
||||
.append('line')
|
||||
.attr('y1', lineRadius)
|
||||
.attr('y2', 0)
|
||||
.attr('x1', 0)
|
||||
.attr('x2', -lineRadius)
|
||||
.attr('stroke-width', 2)
|
||||
.attr('stroke', lineColor);
|
||||
|
||||
this.$el
|
||||
.append('line')
|
||||
.attr('y1', lineRadius)
|
||||
.attr('y2', 0)
|
||||
.attr('x1', 0)
|
||||
.attr('x2', lineRadius)
|
||||
.attr('stroke-width', 2)
|
||||
.attr('stroke', lineColor);
|
||||
|
||||
Icons.addIcon('chevron-bottom', this.$el, {radius: lineRadius});
|
||||
};
|
||||
|
||||
var AddInput = function(params) {
|
||||
@@ -80,12 +56,10 @@ define([
|
||||
|
||||
AddRef.prototype._render = function() {
|
||||
var lineRadius = EasyDAGButtons.Add.SIZE - EasyDAGButtons.Add.BORDER,
|
||||
btnColor = '#81c784',
|
||||
lineColor = '#7986cb';
|
||||
btnColor = '#80deea';
|
||||
|
||||
if (this.disabled) {
|
||||
btnColor = '#e0e0e0';
|
||||
lineColor = '#9e9e9e';
|
||||
}
|
||||
|
||||
this.$el
|
||||
@@ -93,24 +67,7 @@ define([
|
||||
.attr('r', EasyDAGButtons.Add.SIZE)
|
||||
.attr('fill', btnColor);
|
||||
|
||||
this.$el
|
||||
.append('line')
|
||||
.attr('x1', 0)
|
||||
.attr('x2', 0)
|
||||
.attr('y1', -lineRadius)
|
||||
.attr('y2', lineRadius)
|
||||
.attr('stroke-width', 2.5)
|
||||
.attr('stroke', lineColor);
|
||||
|
||||
this.$el
|
||||
.append('line')
|
||||
.attr('y1', 0)
|
||||
.attr('y2', 0)
|
||||
.attr('x1', -lineRadius)
|
||||
.attr('x2', lineRadius)
|
||||
.attr('stroke-width', 2.5)
|
||||
.attr('stroke', lineColor);
|
||||
|
||||
Icons.addIcon('plus', this.$el, {radius: lineRadius});
|
||||
};
|
||||
|
||||
var Delete = function(params) {
|
||||
@@ -132,6 +89,7 @@ define([
|
||||
AddOutput: AddOutput,
|
||||
AddInput: AddInput,
|
||||
AddRef: AddRef,
|
||||
GoToBase: CommonButtons.GoToBase,
|
||||
Delete: Delete
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*globals define*/
|
||||
/*globals define */
|
||||
define([
|
||||
'widgets/EasyDAG/DAGItem',
|
||||
'underscore'
|
||||
@@ -9,10 +9,60 @@ define([
|
||||
|
||||
var Item = function(parentEl, desc) {
|
||||
DAGItem.call(this, parentEl, desc);
|
||||
|
||||
// Show the warnings
|
||||
this.$warning = null;
|
||||
this.updateWarnings();
|
||||
};
|
||||
|
||||
_.extend(Item.prototype, DAGItem.prototype);
|
||||
|
||||
Item.prototype.update = function(desc) {
|
||||
DAGItem.prototype.update.call(this, desc);
|
||||
this.updateWarnings();
|
||||
};
|
||||
|
||||
Item.prototype.updateWarnings = function() {
|
||||
var isInput = this.desc.container === 'inputs',
|
||||
msg = 'Unused ' + (isInput ? 'Input' : 'Output') + '!';
|
||||
|
||||
if (this.desc.used === false) {
|
||||
this.warn(msg, isInput ? 'bottom' : 'top');
|
||||
} else {
|
||||
this.clearWarning();
|
||||
}
|
||||
};
|
||||
|
||||
Item.prototype.warn = function(message, tipJoint) {
|
||||
// Create a temporary div over the given svg element
|
||||
if (this.$warning) {
|
||||
this.clearWarning();
|
||||
}
|
||||
|
||||
this.decorator.highlight('#ffeb3b');
|
||||
this.$warning = this.createTooltip(message, {
|
||||
showIf: () => !this.isSelected(),
|
||||
tipJoint: tipJoint,
|
||||
style: 'standard'
|
||||
});
|
||||
};
|
||||
|
||||
Item.prototype.clearWarning = function() {
|
||||
if (this.$warning) {
|
||||
this.destroyTooltip(this.$warning);
|
||||
this.$warning = null;
|
||||
}
|
||||
this.decorator.unHighlight();
|
||||
};
|
||||
|
||||
|
||||
Item.prototype.onSelect = function() {
|
||||
DAGItem.prototype.onSelect.call(this);
|
||||
if (this.$warning) {
|
||||
this.$warning.hide();
|
||||
}
|
||||
};
|
||||
|
||||
Item.prototype.setupDecoratorCallbacks = function() {
|
||||
DAGItem.prototype.setupDecoratorCallbacks.call(this);
|
||||
// Add ptr name change
|
||||
|
||||
+28
-19
@@ -1,11 +1,8 @@
|
||||
/*globals define */
|
||||
/*jshint browser: true*/
|
||||
|
||||
/**
|
||||
* Generated by VisualizerGenerator 1.7.0 from webgme on Tue May 31 2016 09:16:24 GMT-0500 (CDT).
|
||||
*/
|
||||
|
||||
define([
|
||||
'deepforge/globals',
|
||||
'widgets/EasyDAG/EasyDAGWidget',
|
||||
'widgets/EasyDAG/AddNodeDialog',
|
||||
'./SelectionManager',
|
||||
@@ -13,6 +10,7 @@ define([
|
||||
'underscore',
|
||||
'css!./styles/OperationInterfaceEditorWidget.css'
|
||||
], function (
|
||||
DeepForge,
|
||||
EasyDAG,
|
||||
AddNodeDialog,
|
||||
SelectionManager,
|
||||
@@ -22,10 +20,13 @@ define([
|
||||
'use strict';
|
||||
|
||||
var OperationInterfaceEditorWidget,
|
||||
WIDGET_CLASS = 'operation-interface-editor';
|
||||
WIDGET_CLASS = 'operation-interface-editor',
|
||||
NEW_CLASS_ID = '__NEW_CLASS__',
|
||||
NEW_PRIM_ID = '__NEW_PRIM__';
|
||||
|
||||
OperationInterfaceEditorWidget = function (logger, container) {
|
||||
EasyDAG.call(this, logger, container);
|
||||
this.$el.addClass(WIDGET_CLASS);
|
||||
};
|
||||
|
||||
_.extend(OperationInterfaceEditorWidget.prototype, EasyDAG.prototype);
|
||||
@@ -44,23 +45,31 @@ define([
|
||||
};
|
||||
|
||||
OperationInterfaceEditorWidget.prototype.onAddButtonClicked = function(item, isInput) {
|
||||
var successorPairs = this.getValidSuccessorNodes(item.id),
|
||||
successor = successorPairs[0];
|
||||
var successorPairs = this.getValidSuccessors(item.id, isInput),
|
||||
newClass = this.getCreationNode('Complex', NEW_CLASS_ID),
|
||||
newPrim = this.getCreationNode('Primitive', NEW_PRIM_ID),
|
||||
opts = {};
|
||||
|
||||
if (successorPairs.length > 1) {
|
||||
// Create the modal view with all possible subsequent nodes
|
||||
var dialog = new AddNodeDialog(),
|
||||
title = this._getAddSuccessorTitle(item);
|
||||
// Add the 'Create Class' node
|
||||
successorPairs.push(newClass);
|
||||
successorPairs.push(newPrim);
|
||||
|
||||
dialog.show(title, successorPairs);
|
||||
dialog.onSelect = pair => {
|
||||
if (pair) {
|
||||
this.onAddItemSelected(pair, isInput);
|
||||
// Add tabs
|
||||
opts.tabs = ['Primitive', 'Classes'];
|
||||
opts.tabFilter = (tab, pair) => {
|
||||
return pair.node.isPrimitive === (tab === 'Primitive');
|
||||
};
|
||||
|
||||
AddNodeDialog.prompt(successorPairs, opts)
|
||||
.then(selected => {
|
||||
if (selected.node.id === NEW_CLASS_ID) {
|
||||
DeepForge.create.Complex();
|
||||
} else if (selected.node.id === NEW_PRIM_ID) {
|
||||
DeepForge.create.Primitive();
|
||||
} else {
|
||||
this.onAddItemSelected(selected, isInput);
|
||||
}
|
||||
};
|
||||
} else if (successor) {
|
||||
this.onAddItemSelected(successor, isInput);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
OperationInterfaceEditorWidget.prototype.onDeactivate = function() {
|
||||
|
||||
@@ -53,7 +53,7 @@ define([
|
||||
x: 2*width/3,
|
||||
y: 0
|
||||
});
|
||||
} else { // Data...
|
||||
} else { // Data or pointer...
|
||||
new Buttons.Delete({
|
||||
context: this._widget,
|
||||
$pEl: this.$selection,
|
||||
@@ -61,6 +61,16 @@ define([
|
||||
x: cx,
|
||||
y: 0
|
||||
});
|
||||
|
||||
if (!this.selectedItem.desc.isPointer) {
|
||||
new Buttons.GoToBase({
|
||||
context: this._widget,
|
||||
$pEl: this.$selection,
|
||||
item: this.selectedItem,
|
||||
x: width,
|
||||
y: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/*globals define, WebGMEGlobal*/
|
||||
define([
|
||||
'widgets/EasyDAG/Buttons'
|
||||
], function(
|
||||
EasyDAGButtons
|
||||
) {
|
||||
|
||||
// Create a GoToBase button
|
||||
var client = WebGMEGlobal.Client,
|
||||
LocalOps = {
|
||||
ArtifactLoader: true,
|
||||
Save: true
|
||||
};
|
||||
|
||||
var GoToBase = function(params) {
|
||||
// Check if it should be disabled
|
||||
var baseId = this._getBaseId(params.item),
|
||||
base = client.getNode(baseId);
|
||||
|
||||
params.disabled = base ? base.isLibraryElement() : false;
|
||||
EasyDAGButtons.ButtonBase.call(this, params);
|
||||
};
|
||||
|
||||
GoToBase.SIZE = 10;
|
||||
GoToBase.BORDER = 5;
|
||||
GoToBase.prototype.BTN_CLASS = 'add';
|
||||
GoToBase.prototype = new EasyDAGButtons.ButtonBase();
|
||||
|
||||
GoToBase.prototype._render = function() {
|
||||
var lineRadius = GoToBase.SIZE - GoToBase.BORDER,
|
||||
btnColor = '#90caf9',
|
||||
lineColor = '#7986cb';
|
||||
|
||||
if (this.disabled) {
|
||||
btnColor = '#e0e0e0';
|
||||
lineColor = '#9e9e9e';
|
||||
}
|
||||
|
||||
this.$el
|
||||
.append('circle')
|
||||
.attr('r', GoToBase.SIZE)
|
||||
.attr('fill', btnColor);
|
||||
|
||||
this.$el
|
||||
.append('circle')
|
||||
//.attr('cx', -)
|
||||
//.attr('cy', 0)
|
||||
.attr('r', GoToBase.SIZE/3)
|
||||
.attr('stroke-width', 3)
|
||||
.attr('stroke', lineColor);
|
||||
|
||||
this.$el
|
||||
.append('line')
|
||||
.attr('y1', 0)
|
||||
.attr('y2', 0)
|
||||
.attr('x1', -lineRadius)
|
||||
.attr('x2', lineRadius)
|
||||
.attr('stroke-width', 3)
|
||||
.attr('stroke', lineColor);
|
||||
|
||||
};
|
||||
|
||||
GoToBase.prototype._onClick = function(item) {
|
||||
var node = client.getNode(item.id),
|
||||
baseId = node.getBaseId();
|
||||
|
||||
window.DeepForge = window.DeepForge || {};
|
||||
window.DeepForge.lastPipeline = item.desc.parentId;
|
||||
|
||||
WebGMEGlobal.State.registerActiveObject(baseId);
|
||||
};
|
||||
|
||||
GoToBase.prototype._getBaseId = function(item) {
|
||||
var n = client.getNode(item.id);
|
||||
return n.getBaseId();
|
||||
};
|
||||
|
||||
return {
|
||||
DeleteOne: EasyDAGButtons.DeleteOne,
|
||||
GoToBase: GoToBase
|
||||
};
|
||||
});
|
||||
@@ -6,16 +6,20 @@
|
||||
*/
|
||||
|
||||
define([
|
||||
'widgets/EasyDAG/AddNodeDialog',
|
||||
'widgets/EasyDAG/EasyDAGWidget',
|
||||
'deepforge/viz/PipelineControl',
|
||||
'deepforge/globals',
|
||||
'./OperationNode',
|
||||
'./Connection',
|
||||
'./SelectionManager',
|
||||
'underscore',
|
||||
'css!./styles/PipelineEditorWidget.css'
|
||||
], function (
|
||||
AddNodeDialog,
|
||||
EasyDAGWidget,
|
||||
PipelineControl,
|
||||
DeepForge,
|
||||
OperationNode,
|
||||
Connection,
|
||||
SelectionManager,
|
||||
@@ -31,6 +35,7 @@ define([
|
||||
DEFAULT: 'default',
|
||||
CONNECTING: 'connecting'
|
||||
},
|
||||
UPLOAD_ARTIFACT_ID = '__UPLOAD_ARTIFACT__',
|
||||
STATUS_TO_CLASS = {
|
||||
running: 'warning',
|
||||
success: 'success',
|
||||
@@ -85,6 +90,7 @@ define([
|
||||
// Update the given port...
|
||||
dstItem.refreshPorts();
|
||||
}
|
||||
this.refreshThumbnail();
|
||||
};
|
||||
|
||||
PipelineEditorWidget.prototype.addNode = function(desc) {
|
||||
@@ -104,6 +110,7 @@ define([
|
||||
this.PORT_STATE = STATE.DEFAULT;
|
||||
this.connectPort.apply(this, this.srcPortToConnectArgs);
|
||||
}
|
||||
this.refreshThumbnail();
|
||||
};
|
||||
|
||||
PipelineEditorWidget.prototype._removeConnection = function(id) {
|
||||
@@ -118,6 +125,7 @@ define([
|
||||
dst.refreshPorts();
|
||||
}
|
||||
EasyDAGWidget.prototype._removeConnection.call(this, id);
|
||||
this.refreshThumbnail();
|
||||
};
|
||||
|
||||
// May not actually need these port methods
|
||||
@@ -137,6 +145,7 @@ define([
|
||||
this.removePort(gmeId);
|
||||
} else {
|
||||
EasyDAGWidget.prototype.removeNode.call(this, gmeId);
|
||||
this.refreshThumbnail();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -338,5 +347,91 @@ define([
|
||||
this.updateExecutions();
|
||||
};
|
||||
|
||||
////////////////////////// Action Overrides //////////////////////////
|
||||
PipelineEditorWidget.prototype.selectTargetFor = function(itemId) {
|
||||
// If it is an 'ArtifactLoader', then we will need to add 'upload artifact'
|
||||
// options
|
||||
if (this.items[itemId].desc.baseName === 'ArtifactLoader') {
|
||||
this.selectTargetForLoader.apply(this, arguments);
|
||||
} else {
|
||||
return EasyDAGWidget.prototype.selectTargetFor.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
PipelineEditorWidget.prototype.selectTargetForLoader = function(itemId, ptr, filter) {
|
||||
var validTargets = this.getValidTargetsFor(itemId, ptr, filter),
|
||||
nodeId = validTargets.length ? validTargets[0].node.id : null,
|
||||
uploadNode;
|
||||
|
||||
// Add the 'Upload Artifact' option
|
||||
uploadNode = {
|
||||
node: {
|
||||
id: UPLOAD_ARTIFACT_ID,
|
||||
name: 'Upload Artifact',
|
||||
class: 'create-node',
|
||||
attributes: {},
|
||||
Decorator: this.getDecorator(nodeId)
|
||||
}
|
||||
};
|
||||
validTargets.push(uploadNode);
|
||||
|
||||
AddNodeDialog.prompt(validTargets)
|
||||
.then(selected => {
|
||||
if (selected.node.id === UPLOAD_ARTIFACT_ID) {
|
||||
DeepForge.create.Artifact();
|
||||
} else {
|
||||
var item = this.items[itemId];
|
||||
if (item.decorator.savePointer) {
|
||||
return item.decorator.savePointer(ptr, selected.node.id);
|
||||
} else {
|
||||
this.setPointerForNode(itemId, ptr, selected.node.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
////////////////////////// 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;
|
||||
});
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário