Angular 6 compiler: complete inline/compile/bundle/minify rework
This commit is contained in:
309
build.js
309
build.js
@@ -1,174 +1,149 @@
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
const fs = require('fs');
|
||||
const shell = require('shelljs');
|
||||
const chalk = require('chalk');
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
const camelCase = require('camelcase');
|
||||
const ngc = require('@angular/compiler-cli/src/main').main;
|
||||
const rollup = require('rollup');
|
||||
const uglify = require('rollup-plugin-uglify');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
const fs = require('fs');
|
||||
|
||||
const inlineResources = require('./inline-resources');
|
||||
const PACKAGE = `spring-flo`;
|
||||
const NPM_DIR = `dist`;
|
||||
const TS_COMPILED_DIR = `${NPM_DIR}/out-tsc`;
|
||||
const ESM2015_DIR = `${NPM_DIR}/esm2015`;
|
||||
const ESM5_DIR = `${NPM_DIR}/esm5`;
|
||||
const FESM2015_DIR = `${NPM_DIR}/fesm2015`;
|
||||
const FESM5_DIR = `${NPM_DIR}/fesm5`;
|
||||
const BUNDLES_DIR = `${NPM_DIR}/bundles`;
|
||||
const OUT_DIR = `${NPM_DIR}/package`;
|
||||
const OUT_DIR_ESM5 = `${NPM_DIR}/package/esm5`;
|
||||
|
||||
const gulp = require('gulp');
|
||||
const inlineTemplates = require('gulp-inline-ng2-template');
|
||||
|
||||
/**
|
||||
* Inline templates configuration.
|
||||
* @see https://github.com/ludohenin/gulp-inline-ng2-template
|
||||
*/
|
||||
const INLINE_TEMPLATES = {
|
||||
SRC: './src/lib/**/*.ts',
|
||||
DIST: './dist/package/lib',
|
||||
CONFIG: {
|
||||
base: './src/lib',
|
||||
target: 'es6',
|
||||
useRelativePaths: true
|
||||
}
|
||||
};
|
||||
|
||||
shell.echo(`Start building...`);
|
||||
|
||||
shell.rm(`-Rf`, `${NPM_DIR}/*`);
|
||||
shell.mkdir(`-p`, `./${ESM2015_DIR}`);
|
||||
shell.mkdir(`-p`, `./${ESM5_DIR}`);
|
||||
shell.mkdir(`-p`, `./${FESM2015_DIR}`);
|
||||
shell.mkdir(`-p`, `./${FESM5_DIR}`);
|
||||
shell.mkdir(`-p`, `./${BUNDLES_DIR}`);
|
||||
shell.mkdir(`-p`, `./${OUT_DIR}`);
|
||||
|
||||
/* TSLint with Codelyzer */
|
||||
// https://github.com/palantir/tslint/blob/master/src/configs/recommended.ts
|
||||
// https://github.com/mgechev/codelyzer
|
||||
shell.echo(`Start TSLint`);
|
||||
shell.exec(`tslint -p tsconfig.json -t stylish src/lib/**/*.ts`);
|
||||
shell.echo(chalk.green(`TSLint completed`));
|
||||
|
||||
|
||||
const DIST_FOLDER_NAME = 'dist';
|
||||
|
||||
const libName = require('./package.json').name;
|
||||
const rootFolder = path.join(__dirname);
|
||||
const compilationFolder = path.join(rootFolder, 'out-tsc');
|
||||
const srcFolder = path.join(rootFolder, 'src/lib');
|
||||
const distFolder = path.join(rootFolder, DIST_FOLDER_NAME);
|
||||
const tempLibFolder = path.join(compilationFolder, 'lib');
|
||||
const es5OutputFolder = path.join(compilationFolder, 'lib-es5');
|
||||
const es2015OutputFolder = path.join(compilationFolder, 'lib-es2015');
|
||||
|
||||
return Promise.resolve()
|
||||
// Copy library to temporary folder and inline html/css.
|
||||
.then(() => _relativeCopy(`**/*`, srcFolder, tempLibFolder)
|
||||
.then(() => inlineResources(tempLibFolder))
|
||||
.then(() => console.log('Inlining succeeded.'))
|
||||
)
|
||||
// Compile to ES2015.
|
||||
.then(() => ngc({ project: `${tempLibFolder}/tsconfig.lib.json` })
|
||||
.then(exitCode => exitCode === 0 ? Promise.resolve() : Promise.reject())
|
||||
.then(() => console.log('ES2015 compilation succeeded.'))
|
||||
)
|
||||
// Compile to ES5.
|
||||
.then(() => ngc({ project: `${tempLibFolder}/tsconfig.es5.json` })
|
||||
.then(exitCode => exitCode === 0 ? Promise.resolve() : Promise.reject())
|
||||
.then(() => console.log('ES5 compilation succeeded.'))
|
||||
)
|
||||
// Copy typings and metadata to `dist/` folder.
|
||||
.then(() => Promise.resolve()
|
||||
.then(() => _relativeCopy('**/*.d.ts', es2015OutputFolder, distFolder))
|
||||
.then(() => _relativeCopy('**/*.metadata.json', es2015OutputFolder, distFolder))
|
||||
.then(() => console.log('Typings and metadata copy succeeded.'))
|
||||
)
|
||||
// Bundle lib.
|
||||
.then(() => {
|
||||
// Base configuration.
|
||||
const es5Entry = path.join(es5OutputFolder, `${libName}.js`);
|
||||
const es2015Entry = path.join(es2015OutputFolder, `${libName}.js`);
|
||||
const rollupBaseConfig = {
|
||||
name: camelCase(libName),
|
||||
sourcemap: true,
|
||||
// ATTENTION:
|
||||
// Add any dependency or peer dependency your library to `globals` and `external`.
|
||||
// This is required for UMD bundle users.
|
||||
globals: {
|
||||
// The key here is library name, and the value is the the name of the global variable name
|
||||
// the window object.
|
||||
// See https://github.com/rollup/rollup/wiki/JavaScript-API#globals for more.
|
||||
'@angular/core': 'ng.core',
|
||||
'jquery': '$',
|
||||
'lodash': '_'
|
||||
},
|
||||
external: [
|
||||
// List of dependencies
|
||||
// See https://github.com/rollup/rollup/wiki/JavaScript-API#external for more.
|
||||
'@angular/core',
|
||||
'@angular/forms',
|
||||
'@angular/platform-browser',
|
||||
'codemirror',
|
||||
'jointjs',
|
||||
'lodash',
|
||||
'ts-disposables'
|
||||
],
|
||||
plugins: [
|
||||
sourcemaps()
|
||||
]
|
||||
};
|
||||
|
||||
// UMD bundle.
|
||||
const umdConfig = Object.assign({}, rollupBaseConfig, {
|
||||
input: es5Entry,
|
||||
file: path.join(distFolder, `bundles`, `${libName}.umd.js`),
|
||||
format: 'umd',
|
||||
});
|
||||
|
||||
// Minified UMD bundle.
|
||||
const minifiedUmdConfig = Object.assign({}, rollupBaseConfig, {
|
||||
input: es5Entry,
|
||||
file: path.join(distFolder, `bundles`, `${libName}.umd.min.js`),
|
||||
format: 'umd',
|
||||
plugins: rollupBaseConfig.plugins.concat([uglify({})])
|
||||
});
|
||||
|
||||
// ESM+ES5 flat module bundle.
|
||||
const fesm5config = Object.assign({}, rollupBaseConfig, {
|
||||
input: es5Entry,
|
||||
file: path.join(distFolder, `${libName}.es5.js`),
|
||||
format: 'es'
|
||||
});
|
||||
|
||||
// ESM+ES2015 flat module bundle.
|
||||
const fesm2015config = Object.assign({}, rollupBaseConfig, {
|
||||
input: es2015Entry,
|
||||
file: path.join(distFolder, `${libName}.js`),
|
||||
format: 'es'
|
||||
});
|
||||
|
||||
const allBundles = [
|
||||
umdConfig,
|
||||
minifiedUmdConfig,
|
||||
fesm5config,
|
||||
fesm2015config
|
||||
].map(cfg => rollup.rollup(cfg).then(bundle => bundle.write(cfg)));
|
||||
|
||||
return Promise.all(allBundles)
|
||||
.then(() => console.log('All bundles generated successfully.'))
|
||||
})
|
||||
// Copy package files
|
||||
.then(() => Promise.resolve()
|
||||
.then(() => _relativeCopy('LICENSE', rootFolder, distFolder))
|
||||
.then(() => _relativeCopy('README.md', rootFolder, distFolder))
|
||||
.then(() => _generatePackageJson(path.join(rootFolder, 'package.json'), distFolder))
|
||||
.then(() => console.log('Package files copy succeeded.'))
|
||||
)
|
||||
.catch(e => {
|
||||
console.error('\Build failed. See below for errors.\n');
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
/**
|
||||
* Inline external HTML and SCSS templates into Angular component files.
|
||||
* @see: https://github.com/ludohenin/gulp-inline-ng2-template
|
||||
*/
|
||||
gulp.src(INLINE_TEMPLATES.SRC)
|
||||
.pipe(inlineTemplates(INLINE_TEMPLATES.CONFIG))
|
||||
.pipe(gulp.dest(INLINE_TEMPLATES.DIST)).on('end', compile);
|
||||
|
||||
|
||||
// Copy files maintaining relative paths.
|
||||
function _relativeCopy(fileGlob, from, to) {
|
||||
return new Promise((resolve, reject) => {
|
||||
glob(fileGlob, { cwd: from, nodir: true }, (err, files) => {
|
||||
if (err) reject(err);
|
||||
files.forEach(file => {
|
||||
const origin = path.join(from, file);
|
||||
const dest = path.join(to, file);
|
||||
const data = fs.readFileSync(origin, 'utf-8');
|
||||
_recursiveMkDir(path.dirname(dest));
|
||||
fs.writeFileSync(dest, data);
|
||||
resolve();
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
function compile() {
|
||||
/* Try to process scss files */
|
||||
// shell.echo(`Try to process scss files`);
|
||||
// if (shell.exec(`node-sass -r ${OUT_DIR} -o ${OUT_DIR}`).code === 0) {
|
||||
// shell.rm(`-Rf`, `${OUT_DIR}/**/*.scss`);
|
||||
// shell.ls(`${OUT_DIR}/**/*.css`).forEach(function (file) {
|
||||
// shell.mv(file, file.replace('.css', '.scss'));
|
||||
// });
|
||||
// }
|
||||
|
||||
shell.cp('-Rf', ['*.json'], OUT_DIR);
|
||||
|
||||
/* AoT compilation */
|
||||
shell.echo(`Start AoT compilation`);
|
||||
if (shell.exec(`ngc -p ${OUT_DIR}/tsconfig-build.json`).code !== 0) {
|
||||
shell.echo(chalk.red(`Error: AoT compilation failed`));
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.echo(chalk.green(`AoT compilation completed`));
|
||||
|
||||
shell.echo(`Copy ES2015 for package`);
|
||||
shell.cp(`-Rf`, [ `${TS_COMPILED_DIR}/*` ], `${ESM2015_DIR}`);
|
||||
|
||||
/* BUNDLING PACKAGE */
|
||||
shell.echo(`Start bundling`);
|
||||
shell.echo(`Rollup package`);
|
||||
if (shell.exec(`rollup -c rollup.es.config.js -i ${TS_COMPILED_DIR}/${PACKAGE}.js -o ${FESM2015_DIR}/${PACKAGE}.js`).code !== 0) {
|
||||
shell.echo(chalk.red(`Error: Rollup package failed`));
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
shell.echo(`Produce ESM5/FESM5 versions`);
|
||||
shell.exec(`ngc -p ${OUT_DIR}/tsconfig-build.json --target es5 -d false --outDir ${OUT_DIR_ESM5} --sourceMap`);
|
||||
shell.cp(`-Rf`, [ `${OUT_DIR_ESM5}/*` ], `${ESM5_DIR}`);
|
||||
if (shell.exec(`rollup -c rollup.es.config.js -i ${OUT_DIR_ESM5}/${PACKAGE}.js -o ${FESM5_DIR}/${PACKAGE}.js`).code !== 0) {
|
||||
shell.echo(chalk.red(`Error: FESM5 version failed`));
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
shell.echo(`Run Rollup conversion on package`);
|
||||
if (shell.exec(`rollup -c rollup.config.js -i ${FESM5_DIR}/${PACKAGE}.js -o ${BUNDLES_DIR}/${PACKAGE}.umd.js`).code !== 0) {
|
||||
shell.echo(chalk.red(`Error: Rollup conversion failed`));
|
||||
shell.exit(1);
|
||||
}
|
||||
|
||||
shell.echo(`Minifying`);
|
||||
shell.cd(`${BUNDLES_DIR}`);
|
||||
if (shell.exec(`uglifyjs ${PACKAGE}.umd.js -c --comments -o ${PACKAGE}.umd.min.js --source-map "includeSources=true,filename='${PACKAGE}.umd.min.js.map'"`).code !== 0) {
|
||||
shell.echo(chalk.red(`Error: Minifying failed`));
|
||||
shell.exit(1);
|
||||
}
|
||||
shell.cd(`..`);
|
||||
shell.cd(`..`);
|
||||
|
||||
shell.echo(chalk.green(`Bundling completed`));
|
||||
|
||||
shell.rm(`-Rf`, `${NPM_DIR}/package`);
|
||||
shell.rm(`-Rf`, `${TS_COMPILED_DIR}/**/*.js`);
|
||||
shell.rm(`-Rf`, `${TS_COMPILED_DIR}/**/*.js.map`);
|
||||
shell.rm(`-Rf`, `${ESM2015_DIR}/**/*.d.ts`);
|
||||
|
||||
shell.cp(`-Rf`, [`package.json`, `LICENSE`, `README.md`], `${NPM_DIR}`);
|
||||
|
||||
shell.sed('-i', `"private": true,`, `"private": false,`, `./${NPM_DIR}/package.json`);
|
||||
|
||||
shell.echo(chalk.green(`End building`));
|
||||
|
||||
const packagejson = path.join(NPM_DIR, 'package.json');
|
||||
const json = JSON.parse(fs.readFileSync(packagejson));
|
||||
if (json['scripts']) {
|
||||
delete json['scripts']['postinstall'];
|
||||
}
|
||||
const searchValue = './' + NPM_DIR + '/';
|
||||
const replaceValue = './';
|
||||
replacePropertyValue(json, 'main', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'module', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'es2015', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'typings', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'esm5', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'esm2015', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'fesm5', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'fesm2015', searchValue, replaceValue);
|
||||
fs.writeFileSync(packagejson, JSON.stringify(json, null, 2));
|
||||
|
||||
function _generatePackageJson(basePackageJson, distFolder) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const json = JSON.parse(fs.readFileSync(basePackageJson));
|
||||
if (json['scripts']) {
|
||||
delete json['scripts']['postinstall'];
|
||||
}
|
||||
const searchValue = './' + DIST_FOLDER_NAME + '/';
|
||||
const replaceValue = './';
|
||||
replacePropertyValue(json, 'main', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'module', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'es2015', searchValue, replaceValue);
|
||||
replacePropertyValue(json, 'typings', searchValue, replaceValue);
|
||||
fs.writeFileSync(path.join(distFolder, 'package.json'), JSON.stringify(json, null, 2));
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function replacePropertyValue(json, property, searchValue, newValue) {
|
||||
@@ -176,11 +151,3 @@ function replacePropertyValue(json, property, searchValue, newValue) {
|
||||
json[property] = json[property].replace(searchValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively create a dir.
|
||||
function _recursiveMkDir(dir) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
_recursiveMkDir(path.dirname(dir));
|
||||
fs.mkdirSync(dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
|
||||
|
||||
/**
|
||||
* Simple Promiseify function that takes a Node API and return a version that supports promises.
|
||||
* We use promises instead of synchronized functions to make the process less I/O bound and
|
||||
* faster. It also simplifies the code.
|
||||
*/
|
||||
function promiseify(fn) {
|
||||
return function () {
|
||||
const args = [].slice.call(arguments, 0);
|
||||
return new Promise((resolve, reject) => {
|
||||
fn.apply(this, args.concat([function (err, value) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
}]));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const readFile = promiseify(fs.readFile);
|
||||
const writeFile = promiseify(fs.writeFile);
|
||||
|
||||
/**
|
||||
* Inline resources in a tsc/ngc compilation.
|
||||
* @param projectPath {string} Path to the project.
|
||||
*/
|
||||
function inlineResources(projectPath) {
|
||||
|
||||
// Match only TypeScript files in projectPath.
|
||||
const files = glob.sync('**/*.ts', {cwd: projectPath});
|
||||
|
||||
// For each file, inline the templates and styles under it and write the new file.
|
||||
return Promise.all(files.map(filePath => {
|
||||
const fullFilePath = path.join(projectPath, filePath);
|
||||
return readFile(fullFilePath, 'utf-8')
|
||||
.then(content => inlineResourcesFromString(content, url => {
|
||||
// Resolve the template url.
|
||||
return path.join(path.dirname(fullFilePath), url);
|
||||
}))
|
||||
.then(content => writeFile(fullFilePath, content))
|
||||
.catch(err => {
|
||||
console.error('An error occured: ', err);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline resources from a string content.
|
||||
* @param content {string} The source file's content.
|
||||
* @param urlResolver {Function} A resolver that takes a URL and return a path.
|
||||
* @returns {string} The content with resources inlined.
|
||||
*/
|
||||
function inlineResourcesFromString(content, urlResolver) {
|
||||
// Curry through the inlining functions.
|
||||
return [
|
||||
inlineTemplate,
|
||||
inlineStyle
|
||||
].reduce((content, fn) => fn(content, urlResolver), content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline the templates for a source file. Simply search for instances of `templateUrl: ...` and
|
||||
* replace with `template: ...` (with the content of the file included).
|
||||
* @param content {string} The source file's content.
|
||||
* @param urlResolver {Function} A resolver that takes a URL and return a path.
|
||||
* @return {string} The content with all templates inlined.
|
||||
*/
|
||||
function inlineTemplate(content, urlResolver) {
|
||||
return content.replace(/templateUrl:\s*'([^']+?\.html)'/g, function (m, templateUrl) {
|
||||
const templateFile = urlResolver(templateUrl);
|
||||
const templateContent = fs.readFileSync(templateFile, 'utf-8');
|
||||
const shortenedTemplate = templateContent
|
||||
.replace(/([\n\r]\s*)+/gm, ' ')
|
||||
.replace(/"/g, '\\"');
|
||||
return `template: "${shortenedTemplate}"`;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inline the styles for a source file. Simply search for instances of `styleUrls: [...]` and
|
||||
* replace with `styles: [...]` (with the content of the file included).
|
||||
* @param urlResolver {Function} A resolver that takes a URL and return a path.
|
||||
* @param content {string} The source file's content.
|
||||
* @return {string} The content with all styles inlined.
|
||||
*/
|
||||
function inlineStyle(content, urlResolver) {
|
||||
return content.replace(/styleUrls:\s*(\[[\s\S]*?\])/gm, function (m, styleUrls) {
|
||||
const urls = eval(styleUrls);
|
||||
return 'styles: ['
|
||||
+ urls.map(styleUrl => {
|
||||
const styleFile = urlResolver(styleUrl);
|
||||
const styleContent = fs.readFileSync(styleFile, 'utf-8');
|
||||
const shortenedStyle = styleContent
|
||||
.replace(/([\n\r]\s*)+/gm, ' ')
|
||||
.replace(/"/g, '\\"');
|
||||
return `"${shortenedStyle}"`;
|
||||
})
|
||||
.join(',\n')
|
||||
+ ']';
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = inlineResources;
|
||||
module.exports.inlineResourcesFromString = inlineResourcesFromString;
|
||||
|
||||
// Run inlineResources if module is being called directly from the CLI with arguments.
|
||||
if (require.main === module && process.argv.length > 2) {
|
||||
console.log('Inlining resources from project:', process.argv[2]);
|
||||
return inlineResources(process.argv[2]);
|
||||
}
|
||||
71
package.json
71
package.json
@@ -3,9 +3,13 @@
|
||||
"version": "0.7.5",
|
||||
"description": "Library for quickly building text DSL visualization diagram editor",
|
||||
"main": "./dist/bundles/spring-flo.umd.js",
|
||||
"module": "./dist/spring-flo.es5.js",
|
||||
"es2015": "./dist/spring-flo.js",
|
||||
"typings": "./dist/spring-flo.d.ts",
|
||||
"module": "./dist/fesm5/spring-flo.js",
|
||||
"es2015": "./dist/fesm2015/spring-flo.js",
|
||||
"esm5": "./dist/esm5/spring-flo.js",
|
||||
"esm2015": "./dist/esm2015/spring-flo.js",
|
||||
"fesm5": "./dist/fesm5/spring-flo.js",
|
||||
"fesm2015": "./dist/fesm2015/spring-flo.js",
|
||||
"typings": "./dist/out-tsc/spring-flo.d.ts",
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -43,6 +47,7 @@
|
||||
"@types/codemirror": "0.0.45",
|
||||
"@types/lodash": "4.14.110",
|
||||
"@types/backbone": "1.3.42",
|
||||
"@types/jquery": "3.3.4",
|
||||
"codemirror": "5.28.0",
|
||||
"jointjs": "2.1.3",
|
||||
"lodash": "3.10.1",
|
||||
@@ -59,17 +64,22 @@
|
||||
"devDependencies": {
|
||||
"@angular/common": "6.0.7",
|
||||
"@angular/compiler": "6.0.7",
|
||||
"@angular/compiler-cli": "4.4.6",
|
||||
"@angular/compiler-cli": "6.0.7",
|
||||
"@angular/core": "6.0.7",
|
||||
"@angular/forms": "6.0.7",
|
||||
"@angular/platform-browser": "6.0.7",
|
||||
"@angular/platform-browser-dynamic": "6.0.7",
|
||||
"@angular/platform-server": "6.0.7",
|
||||
"@angular/animations": "6.0.7",
|
||||
"@types/jasmine": "2.5.36",
|
||||
"@types/node": "6.0.46",
|
||||
"@types/codemirror": "0.0.45",
|
||||
"@types/lodash": "4.14.110",
|
||||
"@types/backbone": "1.3.42",
|
||||
"@types/jquery": "3.3.4",
|
||||
"camelcase": "4.0.0",
|
||||
"concurrently": "3.4.0",
|
||||
"core-js": "2.4.1",
|
||||
"core-js": "2.5.7",
|
||||
"glob": "7.1.1",
|
||||
"jasmine-core": "2.5.2",
|
||||
"karma": "1.5.0",
|
||||
@@ -81,47 +91,52 @@
|
||||
"lite-server": "2.2.2",
|
||||
"ngx-bootstrap": "3.0.1",
|
||||
"rimraf": "2.6.1",
|
||||
"rollup": "0.51.6",
|
||||
"rollup": "0.62.0",
|
||||
"rollup-plugin-sourcemaps": "0.4.2",
|
||||
"rollup-plugin-uglify": "2.0.1",
|
||||
"rxjs": "^6.2.1",
|
||||
"rollup-plugin-node-resolve": "3.3.0",
|
||||
"rollup-plugin-commonjs": "9.1.3",
|
||||
"uglify-js": "3.3.23",
|
||||
"shelljs": "0.8.1",
|
||||
"rxjs": "6.2.1",
|
||||
"standard-version": "4.0.0",
|
||||
"systemjs": "0.21.4",
|
||||
"tslint": "5.9.1",
|
||||
"typescript": "2.5.3",
|
||||
"zone.js": "0.8.18",
|
||||
"@types/codemirror": "0.0.45",
|
||||
"@types/lodash": "4.14.73",
|
||||
"@types/backbone": "1.3.42",
|
||||
"tslint": "5.10.0",
|
||||
"typescript": "2.7.2",
|
||||
"zone.js": "0.8.26",
|
||||
"codemirror": "5.28.0",
|
||||
"jointjs": "2.1.3",
|
||||
"lodash": "3.10.1",
|
||||
"jquery": "3.1.1",
|
||||
"ts-disposables": "2.2.3",
|
||||
"jshint": "2.6.3",
|
||||
"systemjs-plugin-babel": "0.0.25"
|
||||
"systemjs-plugin-babel": "0.0.25",
|
||||
"chalk": "2.4.1",
|
||||
"node-sass": "4.9.0",
|
||||
"gulp": "3.9.1",
|
||||
"gulp-inline-ng2-template": "5.0.1"
|
||||
},
|
||||
"buildDependencies": [
|
||||
"@angular/core",
|
||||
"@angular/forms",
|
||||
"@angular/platform-browser",
|
||||
"@types/codemirror",
|
||||
"@types/jointjs",
|
||||
"@types/lodash",
|
||||
"codemirror",
|
||||
"jointjs",
|
||||
"jquery",
|
||||
"lodash",
|
||||
"ts-disposables",
|
||||
"@angular/common",
|
||||
"@angular/compiler",
|
||||
"@angular/compiler-cli",
|
||||
"@angular/platform-browser",
|
||||
"@angular/platform-browser-dynamic",
|
||||
"@angular/platform-server",
|
||||
"@types/codemirror",
|
||||
"@types/lodash",
|
||||
"@types/backbone",
|
||||
"@types/jasmine",
|
||||
"@types/node",
|
||||
"codemirror",
|
||||
"jointjs",
|
||||
"jquery",
|
||||
"lodash",
|
||||
"ts-disposables",
|
||||
"camelcase",
|
||||
"chalk",
|
||||
"concurrently",
|
||||
"core-js",
|
||||
"glob",
|
||||
@@ -134,15 +149,21 @@
|
||||
"karma-jasmine-html-reporter",
|
||||
"lite-server",
|
||||
"ngx-bootstrap",
|
||||
"node-sass",
|
||||
"rimraf",
|
||||
"rollup",
|
||||
"rollup-plugin-sourcemaps",
|
||||
"rollup-plugin-uglify",
|
||||
"rollup-plugin-node-resolve",
|
||||
"rollup-plugin-commonjs",
|
||||
"rxjs",
|
||||
"standard-version",
|
||||
"systemjs",
|
||||
"shelljs",
|
||||
"tslint",
|
||||
"typescript",
|
||||
"zone.js"
|
||||
"uglify-js",
|
||||
"zone.js",
|
||||
"gulp",
|
||||
"gulp-inline-ng2-template"
|
||||
]
|
||||
}
|
||||
|
||||
90
rollup.config.js
Normal file
90
rollup.config.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
|
||||
/**
|
||||
* Add here external dependencies that actually you use.
|
||||
*
|
||||
* Angular dependencies
|
||||
* - '@angular/animations' => 'ng.animations'
|
||||
* - '@angular/animations/browser': 'ng.animations.browser'
|
||||
* - '@angular/common' => 'ng.common'
|
||||
* - '@angular/compiler' => 'ng.compiler'
|
||||
* - '@angular/core' => 'ng.core'
|
||||
* - '@angular/forms' => 'ng.forms'
|
||||
* - '@angular/common/http' => 'ng.common.http'
|
||||
* - '@angular/platform-browser-dynamic' => 'ng.platformBrowserDynamic'
|
||||
* - '@angular/platform-browser' => 'ng.platformBrowser'
|
||||
* - '@angular/platform-browser/animations' => 'ng.platformBrowser.animations'
|
||||
* - '@angular/platform-server' => 'ng.platformServer'
|
||||
* - '@angular/router' => 'ng.router'
|
||||
*
|
||||
* RxJS dependencies
|
||||
* From RxJS v6 you need only 'rxjs' and 'rxjs.operators'.
|
||||
*
|
||||
* Other dependencies
|
||||
* - Angular libraries: refer to their global namespace
|
||||
* - TypeScript/JavaScript libraries:
|
||||
* e.g. lodash: 'lodash' => 'lodash'
|
||||
*
|
||||
* Also, if the dependency uses CommonJS modules, such as lodash,
|
||||
* you should also use a plugin like rollup-plugin-commonjs,
|
||||
* to explicitly specify unresolvable "named exports".
|
||||
*
|
||||
*/
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
'@angular/common': 'ng.common',
|
||||
'@angular/forms': 'ng.forms',
|
||||
'@angular/platform-browser': 'ng.browser',
|
||||
'rxjs': 'rxjs',
|
||||
'rxjs/operators': 'rxjs.operators',
|
||||
'lodash': 'lodash',
|
||||
'ts-disposables': 'ts-disposables',
|
||||
'codemirror': 'codemirror',
|
||||
'jointjs': 'jointjs',
|
||||
'jquery': 'jquery',
|
||||
|
||||
// CodeMirror extensions
|
||||
'codemirror/mode/meta': 'codemirror/mode/meta',
|
||||
'codemirror/addon/lint/lint': 'codemirror/addon/lint/lint',
|
||||
'codemirror/addon/hint/show-hint': 'codemirror/addon/hint/show-hint',
|
||||
'codemirror/addon/mode/loadmode': 'codemirror/addon/mode/loadmode',
|
||||
'codemirror/addon/edit/matchbrackets': 'codemirror/addon/edit/matchbrackets',
|
||||
'codemirror/addon/edit/closebrackets': 'codemirror/addon/edit/closebrackets',
|
||||
'codemirror/addon/display/placeholder': 'codemirror/addon/edit/closebrackets',
|
||||
'codemirror/addon/scroll/annotatescrollbar': 'codemirror/addon/scroll/annotatescrollbar',
|
||||
'codemirror/addon/scroll/simplescrollbars': 'codemirror/addon/scroll/simplescrollbars',
|
||||
|
||||
// Lint support
|
||||
// Unclear how to import this dynamically...
|
||||
'codemirror/addon/lint/javascript-lint': 'codemirror/addon/lint/javascript-lint',
|
||||
'codemirror/addon/lint/coffeescript-lint': 'codemirror/addon/lint/coffeescript-lint',
|
||||
'codemirror/addon/lint/json-lint': 'codemirror/addon/lint/json-lint',
|
||||
'codemirror/addon/lint/yaml-lint': 'codemirror/addon/lint/yaml-lint',
|
||||
|
||||
// TODO: use dynamic import with JS7 in the future. CM autoLoad cannot load it properly - thinks its AMD
|
||||
// Supported languages until dynamic loading
|
||||
'codemirror/mode/groovy/groovy': 'codemirror/mode/groovy/groovy',
|
||||
'codemirror/mode/javascript/javascript': 'codemirror/mode/javascript/javascript',
|
||||
'codemirror/mode/python/python': 'codemirror/mode/python/python',
|
||||
'codemirror/mode/ruby/ruby': 'codemirror/mode/ruby/ruby',
|
||||
'codemirror/mode/clike/clike': 'codemirror/mode/clike/clike',
|
||||
'codemirror/mode/yaml/yaml': 'codemirror/mode/yaml/yaml',
|
||||
'codemirror/mode/coffeescript/coffeescript': 'codemirror/mode/coffeescript/coffeescript'
|
||||
|
||||
};
|
||||
|
||||
export default {
|
||||
external: Object.keys(globals),
|
||||
plugins: [resolve(), sourcemaps(), commonjs()],
|
||||
onwarn: () => { return },
|
||||
output: {
|
||||
format: 'umd',
|
||||
name: 'ng.spring-flo',
|
||||
globals: globals,
|
||||
sourcemap: true,
|
||||
exports: 'named',
|
||||
amd: { id: 'spring-flo' }
|
||||
}
|
||||
}
|
||||
12
rollup.es.config.js
Normal file
12
rollup.es.config.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
|
||||
export default {
|
||||
output: {
|
||||
format: 'es',
|
||||
sourcemap: true
|
||||
},
|
||||
plugins: [
|
||||
sourcemaps()
|
||||
],
|
||||
onwarn: () => { return }
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import * as CodeMirror from 'codemirror';
|
||||
|
||||
import * as _$ from 'jquery';
|
||||
const $ : any = _$;
|
||||
const $: any = _$;
|
||||
|
||||
// CodeMirror extensions
|
||||
import 'codemirror/mode/meta';
|
||||
@@ -37,10 +37,10 @@ import 'codemirror/mode/coffeescript/coffeescript';
|
||||
selector: 'code-editor',
|
||||
templateUrl: './code-editor.component.html',
|
||||
styleUrls: [
|
||||
'./../../../../node_modules/codemirror/lib/codemirror.css',
|
||||
'./../../../../node_modules/codemirror/addon/hint/show-hint.css',
|
||||
'./../../../../node_modules/codemirror/addon/lint/lint.css',
|
||||
'./../../../../node_modules/codemirror/addon/scroll/simplescrollbars.css',
|
||||
'./../../../node_modules/codemirror/lib/codemirror.css',
|
||||
'./../../../node_modules/codemirror/addon/hint/show-hint.css',
|
||||
'./../../../node_modules/codemirror/addon/lint/lint.css',
|
||||
'./../../../node_modules/codemirror/addon/scroll/simplescrollbars.css',
|
||||
'./code-editor.component.scss', ],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
providers: [
|
||||
@@ -53,11 +53,11 @@ import 'codemirror/mode/coffeescript/coffeescript';
|
||||
})
|
||||
export class CodeEditorComponent implements OnInit, OnDestroy, ControlValueAccessor {
|
||||
|
||||
private doc : CodeMirror.EditorFromTextArea;
|
||||
private doc: CodeMirror.EditorFromTextArea;
|
||||
|
||||
private _dsl = '';
|
||||
|
||||
private _lint : boolean | CodeMirror.LintOptions = false;
|
||||
private _lint: boolean | CodeMirror.LintOptions = false;
|
||||
|
||||
private _language: string;
|
||||
|
||||
@@ -70,16 +70,16 @@ export class CodeEditorComponent implements OnInit, OnDestroy, ControlValueAcces
|
||||
private _onTouchHandler: () => void;
|
||||
|
||||
@Input('line-numbers')
|
||||
private lineNumbers : boolean = false;
|
||||
private lineNumbers = false;
|
||||
|
||||
@Input('line-wrapping')
|
||||
private lineWrapping : boolean = false;
|
||||
private lineWrapping = false;
|
||||
|
||||
@Input('scrollbar-style')
|
||||
private scrollbarStyle : string;
|
||||
private scrollbarStyle: string;
|
||||
|
||||
@Input()
|
||||
private placeholder : string;
|
||||
private placeholder: string;
|
||||
|
||||
@Input('overview-ruler')
|
||||
private overviewRuler: boolean;
|
||||
@@ -107,7 +107,7 @@ export class CodeEditorComponent implements OnInit, OnDestroy, ControlValueAcces
|
||||
constructor(private element: ElementRef) {}
|
||||
|
||||
@Input()
|
||||
set dsl(dsl : string) {
|
||||
set dsl(dsl: string) {
|
||||
this._dsl = dsl;
|
||||
if (this.doc && this._dsl !== this.doc.getValue()) {
|
||||
let cursorPosition = (<any>this.doc).getCursor();
|
||||
@@ -126,7 +126,7 @@ export class CodeEditorComponent implements OnInit, OnDestroy, ControlValueAcces
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
let options : any = {
|
||||
let options: any = {
|
||||
value: this._dsl || '',
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
extraKeys: {'Ctrl-Space': 'autocomplete'},
|
||||
@@ -1,34 +1,38 @@
|
||||
import {Directive, Input, Output, EventEmitter, Inject, ElementRef, OnInit, OnDestroy,} from '@angular/core';
|
||||
import {Directive, Input, Output, EventEmitter, Inject, ElementRef, OnInit, OnDestroy} from '@angular/core';
|
||||
import {DOCUMENT} from '@angular/platform-browser'
|
||||
import { fromEvent } from "rxjs";
|
||||
import { fromEvent } from 'rxjs';
|
||||
import { sampleTime } from 'rxjs/operators';
|
||||
|
||||
import { CompositeDisposable, Disposable } from 'ts-disposables';
|
||||
import * as _$ from 'jquery';
|
||||
const $ : any = _$;
|
||||
const $: any = _$;
|
||||
|
||||
@Directive({
|
||||
selector: '[resizer]',
|
||||
host: {'(mousedown)': 'startDrag()'}
|
||||
})
|
||||
export class ResizerDirective implements OnInit, OnDestroy {
|
||||
private dragInProgress: boolean = false;
|
||||
private vertical: boolean = true;
|
||||
private dragInProgress = false;
|
||||
private vertical = true;
|
||||
private first: string;
|
||||
private second: string;
|
||||
private _size: number;
|
||||
private _splitSize: number;
|
||||
private _subscriptions = new CompositeDisposable();
|
||||
@Input()
|
||||
maxSplitSize: number;
|
||||
|
||||
@Output()
|
||||
sizeChange = new EventEmitter<number>();
|
||||
|
||||
private mouseMoveHandler = (e: any) => {
|
||||
if (this.dragInProgress) {
|
||||
this.mousemove(e);
|
||||
}
|
||||
};
|
||||
@Input()
|
||||
maxSplitSize: number;
|
||||
|
||||
@Input()
|
||||
set splitSize(splitSize : number) {
|
||||
set splitSize(splitSize: number) {
|
||||
|
||||
if (this.maxSplitSize && splitSize > this.maxSplitSize) {
|
||||
splitSize = this.maxSplitSize;
|
||||
@@ -66,38 +70,35 @@ export class ResizerDirective implements OnInit, OnDestroy {
|
||||
this.sizeChange.emit(splitSize);
|
||||
}
|
||||
|
||||
@Output()
|
||||
sizeChange = new EventEmitter<number>();
|
||||
|
||||
@Input()
|
||||
set resizerWidth(width : number) {
|
||||
set resizerWidth(width: number) {
|
||||
this._size = width;
|
||||
this.vertical = true;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set resizerHeight(height : number) {
|
||||
set resizerHeight(height: number) {
|
||||
this._size = height;
|
||||
this.vertical = false;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set resizerLeft(first : string) {
|
||||
set resizerLeft(first: string) {
|
||||
this.first = first;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set resizerTop(first : string) {
|
||||
set resizerTop(first: string) {
|
||||
this.first = first;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set resizerRight(second : string) {
|
||||
set resizerRight(second: string) {
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set resizerBottom(second : string) {
|
||||
set resizerBottom(second: string) {
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Component, Input, Output, ElementRef, EventEmitter, OnInit, OnDestroy,
|
||||
import * as _ from 'lodash';
|
||||
import * as CodeMirror from 'codemirror';
|
||||
import * as _$ from 'jquery';
|
||||
const $ : any = _$;
|
||||
const $: any = _$;
|
||||
|
||||
import 'codemirror/addon/lint/lint';
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
@@ -15,36 +15,36 @@ import 'codemirror/addon/scroll/simplescrollbars';
|
||||
selector: 'dsl-editor',
|
||||
templateUrl: './dsl-editor.component.html',
|
||||
styleUrls: [
|
||||
'./../../../../node_modules/codemirror/lib/codemirror.css',
|
||||
'./../../../../node_modules/codemirror/addon/hint/show-hint.css',
|
||||
'./../../../../node_modules/codemirror/addon/lint/lint.css',
|
||||
'./../../../node_modules/codemirror/lib/codemirror.css',
|
||||
'./../../../node_modules/codemirror/addon/hint/show-hint.css',
|
||||
'./../../../node_modules/codemirror/addon/lint/lint.css',
|
||||
'./dsl-editor.component.css', ],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class DslEditorComponent implements OnInit, OnDestroy {
|
||||
|
||||
private doc : CodeMirror.EditorFromTextArea;
|
||||
private doc: CodeMirror.EditorFromTextArea;
|
||||
|
||||
private _dsl = '';
|
||||
|
||||
private _lint : boolean | CodeMirror.LintOptions = false;
|
||||
private _lint: boolean | CodeMirror.LintOptions = false;
|
||||
|
||||
private _hint : any;
|
||||
private _hint: any;
|
||||
|
||||
@Input('line-numbers')
|
||||
private lineNumbers : boolean = false;
|
||||
private lineNumbers = false;
|
||||
|
||||
@Input('line-wrapping')
|
||||
private lineWrapping : boolean = false;
|
||||
private lineWrapping = false;
|
||||
|
||||
@Input('scrollbar-style')
|
||||
private scrollbarStyle : string;
|
||||
private scrollbarStyle: string;
|
||||
|
||||
@Input()
|
||||
private placeholder : string;
|
||||
private placeholder: string;
|
||||
|
||||
@Input()
|
||||
private debounce : number = 0;
|
||||
private debounce = 0;
|
||||
|
||||
@Output()
|
||||
private dslChange = new EventEmitter<string>();
|
||||
@@ -66,7 +66,7 @@ export class DslEditorComponent implements OnInit, OnDestroy {
|
||||
constructor(private element: ElementRef) {}
|
||||
|
||||
@Input()
|
||||
set dsl(dsl : string) {
|
||||
set dsl(dsl: string) {
|
||||
this._dsl = dsl;
|
||||
if (this.doc && this._dsl !== this.doc.getValue()) {
|
||||
let cursorPosition = (<any>this.doc).getCursor();
|
||||
@@ -76,7 +76,7 @@ export class DslEditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
@Input()
|
||||
set lintOptions(lintOptions : boolean | CodeMirror.LintOptions) {
|
||||
set lintOptions(lintOptions: boolean | CodeMirror.LintOptions) {
|
||||
this._lint = lintOptions;
|
||||
if (this.doc) {
|
||||
this.doc.setOption('lint', this._lint);
|
||||
@@ -84,7 +84,7 @@ export class DslEditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
@Input()
|
||||
set hintOptions(hintOptions : any) {
|
||||
set hintOptions(hintOptions: any) {
|
||||
this._hint = hintOptions;
|
||||
if (this.doc) {
|
||||
this.doc.setOption('hintOptions', this._hint);
|
||||
@@ -93,7 +93,7 @@ export class DslEditorComponent implements OnInit, OnDestroy {
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
let options : CodeMirror.EditorConfiguration = {
|
||||
let options: CodeMirror.EditorConfiguration = {
|
||||
value: this._dsl || '',
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
extraKeys: {'Ctrl-Space': 'autocomplete'},
|
||||
@@ -1,32 +1,32 @@
|
||||
import { dia } from 'jointjs';
|
||||
import { Flo } from '../shared/flo-common';
|
||||
import * as _ from 'lodash';
|
||||
const joint : any = Flo.joint;
|
||||
const joint: any = Flo.joint;
|
||||
import * as _$ from 'jquery';
|
||||
const $ : any = _$;
|
||||
const $: any = _$;
|
||||
|
||||
|
||||
export class Utils {
|
||||
|
||||
static fanRoute(graph : dia.Graph, cell : dia.Cell) {
|
||||
static fanRoute(graph: dia.Graph, cell: dia.Cell) {
|
||||
if (cell instanceof joint.dia.Element) {
|
||||
|
||||
_.chain(graph.getConnectedLinks(cell)).groupBy((link : dia.Link) => {
|
||||
_.chain(graph.getConnectedLinks(cell)).groupBy((link: dia.Link) => {
|
||||
// the key of the group is the model id of the link's source or target, but not our cell id.
|
||||
return _.omit([link.get('source').id, link.get('target').id], cell.id)[0];
|
||||
}).each((group : any, key : string) => {
|
||||
}).each((group: any, key: string) => {
|
||||
// If the member of the group has both source and target model adjust vertices.
|
||||
let toRoute : any = {};
|
||||
let toRoute: any = {};
|
||||
if (key !== undefined) {
|
||||
group.forEach((link : dia.Link) => {
|
||||
group.forEach((link: dia.Link) => {
|
||||
if (link.get('source').id === cell.get('id') && link.get('target').id) {
|
||||
toRoute[link.get('target').id] = link;
|
||||
} else if (link.get('target').id === cell.get('id') && link.get('source').id) {
|
||||
toRoute[link.get('source').id] = link;
|
||||
}
|
||||
});
|
||||
Object.keys(toRoute).forEach(key => {
|
||||
Utils.fanRoute(graph, toRoute[key]);
|
||||
Object.keys(toRoute).forEach(k => {
|
||||
Utils.fanRoute(graph, toRoute[k]);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -115,7 +115,7 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
static isCustomPaperEvent(args : any) : boolean {
|
||||
static isCustomPaperEvent(args: any): boolean {
|
||||
return args.length === 5 &&
|
||||
_.isString(args[0]) &&
|
||||
(args[0].indexOf('link:') === 0 || args[0].indexOf('element:') === 0) &&
|
||||
@@ -8,23 +8,74 @@ import { CompositeDisposable, Disposable } from 'ts-disposables';
|
||||
import * as _$ from 'jquery';
|
||||
import * as _ from 'lodash';
|
||||
import { Observable, Subject, BehaviorSubject } from 'rxjs';
|
||||
const joint : any = Flo.joint;
|
||||
const $ : any = _$;
|
||||
|
||||
const joint: any = Flo.joint;
|
||||
const $: any = _$;
|
||||
|
||||
export interface VisibilityState {
|
||||
visibility : string;
|
||||
children : Array<VisibilityState>;
|
||||
visibility: string;
|
||||
children: Array<VisibilityState>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'flo-editor',
|
||||
templateUrl: './editor.component.html',
|
||||
styleUrls: ['./../../../../node_modules/jointjs/dist/joint.css', './../shared/flo.css'],
|
||||
styleUrls: ['./../../../node_modules/jointjs/dist/joint.css', './../shared/flo.css'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class EditorComponent implements OnInit, OnDestroy {
|
||||
|
||||
/**
|
||||
* Joint JS Graph object representing the Graph model
|
||||
*/
|
||||
private graph: dia.Graph;
|
||||
|
||||
/**
|
||||
* Joint JS Paper object representing the canvas control containing the graph view
|
||||
*/
|
||||
private paper: dia.Paper;
|
||||
|
||||
/**
|
||||
* Currently selected element
|
||||
*/
|
||||
private _selection: dia.CellView;
|
||||
|
||||
/**
|
||||
* Current DnD descriptor for frag in progress
|
||||
*/
|
||||
private highlighted: Flo.DnDDescriptor;
|
||||
|
||||
/**
|
||||
* Flag specifying whether the Flo-Editor is in read-only mode.
|
||||
*/
|
||||
private _readOnlyCanvas = false;
|
||||
|
||||
/**
|
||||
* Grid size
|
||||
*/
|
||||
private _gridSize = 1;
|
||||
|
||||
private _hiddenPalette = false;
|
||||
|
||||
private editorContext: Flo.EditorContext;
|
||||
|
||||
private textToGraphEventEmitter = new EventEmitter<void>();
|
||||
|
||||
private graphToTextEventEmitter = new EventEmitter<void>();
|
||||
|
||||
private _graphToTextSyncEnabled = true;
|
||||
|
||||
private validationEventEmitter = new EventEmitter<void>();
|
||||
|
||||
private _disposables = new CompositeDisposable();
|
||||
|
||||
private _dslText = '';
|
||||
|
||||
private textToGraphConversionCompleted = new Subject<void>();
|
||||
|
||||
private graphToTextConversionCompleted = new Subject<void>();
|
||||
|
||||
private paletteReady = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* Metamodel. Retrieves metadata about elements that can be shown in Flo
|
||||
*/
|
||||
@@ -53,22 +104,22 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* Min zoom percent value
|
||||
*/
|
||||
@Input()
|
||||
minZoom: number = 5;
|
||||
minZoom = 5;
|
||||
|
||||
/**
|
||||
* Max zoom percent value
|
||||
*/
|
||||
@Input()
|
||||
maxZoom: number = 400;
|
||||
maxZoom = 400;
|
||||
|
||||
/**
|
||||
* Zoom percent increment/decrement step
|
||||
*/
|
||||
@Input()
|
||||
zoomStep: number = 5;
|
||||
zoomStep = 5;
|
||||
|
||||
@Input()
|
||||
paperPadding : number = 0;
|
||||
paperPadding = 0;
|
||||
|
||||
@Output()
|
||||
floApi = new EventEmitter<Flo.EditorContext>();
|
||||
@@ -79,114 +130,60 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
contentValidated = new EventEmitter<boolean>();
|
||||
|
||||
/**
|
||||
* Joint JS Graph object representing the Graph model
|
||||
*/
|
||||
private graph: dia.Graph;
|
||||
|
||||
/**
|
||||
* Joint JS Paper object representing the canvas control containing the graph view
|
||||
*/
|
||||
private paper: dia.Paper;
|
||||
|
||||
/**
|
||||
* Currently selected element
|
||||
*/
|
||||
private _selection: dia.CellView;
|
||||
|
||||
/**
|
||||
* Current DnD descriptor for frag in progress
|
||||
*/
|
||||
private highlighted: Flo.DnDDescriptor;
|
||||
|
||||
/**
|
||||
* Flag specifying whether the Flo-Editor is in read-only mode.
|
||||
*/
|
||||
private _readOnlyCanvas: boolean = false;
|
||||
|
||||
/**
|
||||
* Grid size
|
||||
*/
|
||||
private _gridSize: number = 1;
|
||||
|
||||
private _hiddenPalette : boolean = false;
|
||||
|
||||
private editorContext : Flo.EditorContext;
|
||||
|
||||
private _resizeHandler = () => this.autosizePaper();
|
||||
|
||||
private textToGraphEventEmitter = new EventEmitter<void>();
|
||||
|
||||
private graphToTextEventEmitter = new EventEmitter<void>();
|
||||
|
||||
private _graphToTextSyncEnabled = true;
|
||||
|
||||
private validationEventEmitter = new EventEmitter<void>();
|
||||
|
||||
private _disposables = new CompositeDisposable();
|
||||
|
||||
/* DSL Fields */
|
||||
|
||||
private _dslText : string = '';
|
||||
|
||||
@Output()
|
||||
private dslChange = new EventEmitter<string>();
|
||||
|
||||
private textToGraphConversionCompleted = new Subject<void>();
|
||||
|
||||
private graphToTextConversionCompleted = new Subject<void>();
|
||||
|
||||
private paletteReady = new BehaviorSubject<boolean>(false);
|
||||
private _resizeHandler = () => this.autosizePaper();
|
||||
|
||||
constructor(private element: ElementRef) {
|
||||
let self = this;
|
||||
this.editorContext = new (class DefaultRunnableContext implements Flo.EditorContext {
|
||||
|
||||
set zoomPercent(percent : number) {
|
||||
set zoomPercent(percent: number) {
|
||||
self.zoomPercent = percent;
|
||||
}
|
||||
|
||||
get zoomPercent() : number {
|
||||
get zoomPercent(): number {
|
||||
return self.zoomPercent;
|
||||
}
|
||||
|
||||
set noPalette(noPalette : boolean) {
|
||||
set noPalette(noPalette: boolean) {
|
||||
self.noPalette = noPalette;
|
||||
}
|
||||
|
||||
get noPalette() : boolean {
|
||||
get noPalette(): boolean {
|
||||
return self.noPalette;
|
||||
}
|
||||
|
||||
set gridSize(gridSize : number) {
|
||||
set gridSize(gridSize: number) {
|
||||
self.gridSize = gridSize;
|
||||
}
|
||||
|
||||
get gridSize() : number {
|
||||
get gridSize(): number {
|
||||
return self.gridSize;
|
||||
}
|
||||
|
||||
set readOnlyCanvas(readOnly : boolean) {
|
||||
set readOnlyCanvas(readOnly: boolean) {
|
||||
self.readOnlyCanvas = readOnly;
|
||||
}
|
||||
|
||||
get readOnlyCanvas() : boolean {
|
||||
get readOnlyCanvas(): boolean {
|
||||
return self.readOnlyCanvas;
|
||||
}
|
||||
|
||||
setDsl(dsl : string) {
|
||||
setDsl(dsl: string) {
|
||||
self.dsl = dsl;
|
||||
}
|
||||
|
||||
updateGraph() : Promise<any> {
|
||||
updateGraph(): Promise<any> {
|
||||
return self.updateGraphRepresentation();
|
||||
}
|
||||
|
||||
updateText() : Promise<any> {
|
||||
updateText(): Promise<any> {
|
||||
return self.updateTextRepresentation();
|
||||
}
|
||||
|
||||
performLayout() : Promise<void> {
|
||||
performLayout(): Promise<void> {
|
||||
return self.doLayout();
|
||||
}
|
||||
|
||||
@@ -215,11 +212,11 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
return self.paper;
|
||||
}
|
||||
|
||||
get graphToTextSync() : boolean {
|
||||
get graphToTextSync(): boolean {
|
||||
return self.graphToTextSync;
|
||||
}
|
||||
|
||||
set graphToTextSync(sync : boolean) {
|
||||
set graphToTextSync(sync: boolean) {
|
||||
self.graphToTextSync = sync;
|
||||
}
|
||||
|
||||
@@ -239,29 +236,29 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
self.fitToPage();
|
||||
}
|
||||
|
||||
createNode(metadata : Flo.ElementMetadata, props? : Map<string, any>, position? : dia.Point) : dia.Element {
|
||||
createNode(metadata: Flo.ElementMetadata, props?: Map<string, any>, position?: dia.Point): dia.Element {
|
||||
return self.createNode(metadata, props, position);
|
||||
}
|
||||
|
||||
createLink(source : Flo.LinkEnd, target : Flo.LinkEnd, metadata? : Flo.ElementMetadata, props? : Map<string, any>) : dia.Link {
|
||||
createLink(source: Flo.LinkEnd, target: Flo.LinkEnd, metadata?: Flo.ElementMetadata, props?: Map<string, any>): dia.Link {
|
||||
return self.createLink(source, target, metadata, props);
|
||||
}
|
||||
|
||||
get selection() : dia.CellView {
|
||||
get selection(): dia.CellView {
|
||||
return self.selection;
|
||||
}
|
||||
|
||||
set selection(newSelection : dia.CellView) {
|
||||
set selection(newSelection: dia.CellView) {
|
||||
self.selection = newSelection;
|
||||
}
|
||||
|
||||
deleteSelectedNode() : void {
|
||||
deleteSelectedNode(): void {
|
||||
if (self.selection) {
|
||||
if (self.editor && self.editor.preDelete) {
|
||||
self.editor.preDelete(self.editorContext, self.selection.model);
|
||||
} else {
|
||||
if (self.selection.model instanceof joint.dia.Element) {
|
||||
self.graph.getConnectedLinks(self.selection.model).forEach((l : dia.Link) => l.remove());
|
||||
self.graph.getConnectedLinks(self.selection.model).forEach((l: dia.Link) => l.remove());
|
||||
}
|
||||
}
|
||||
self.selection.model.remove();
|
||||
@@ -314,11 +311,11 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
get noPalette() : boolean {
|
||||
get noPalette(): boolean {
|
||||
return this._hiddenPalette;
|
||||
}
|
||||
|
||||
set noPalette(hidden : boolean) {
|
||||
set noPalette(hidden: boolean) {
|
||||
this._hiddenPalette = hidden;
|
||||
// If palette is not shown ensure that canvas starts from the left==0!
|
||||
if (hidden) {
|
||||
@@ -326,11 +323,11 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
get graphToTextSync() : boolean {
|
||||
get graphToTextSync(): boolean {
|
||||
return this._graphToTextSyncEnabled;
|
||||
}
|
||||
|
||||
set graphToTextSync(sync : boolean) {
|
||||
set graphToTextSync(sync: boolean) {
|
||||
this._graphToTextSyncEnabled = sync;
|
||||
// Try commenting the sync out. Just set the flag but don't kick off graph->text conversion
|
||||
// this.performGraphToTextSyncing();
|
||||
@@ -384,7 +381,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
get selection() : dia.CellView {
|
||||
get selection(): dia.CellView {
|
||||
return this._selection;
|
||||
}
|
||||
|
||||
@@ -397,7 +394,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
if (newSelection !== this._selection) {
|
||||
if (this._selection) {
|
||||
var elementview = this.paper.findViewByModel(this._selection.model);
|
||||
const elementview = this.paper.findViewByModel(this._selection.model);
|
||||
if (elementview) { // May have been removed from the graph
|
||||
this.removeEmbeddedChildrenOfType(elementview.model, joint.shapes.flo.HANDLE_TYPE);
|
||||
elementview.unhighlight();
|
||||
@@ -413,11 +410,11 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
get readOnlyCanvas() : boolean {
|
||||
get readOnlyCanvas(): boolean {
|
||||
return this._readOnlyCanvas;
|
||||
}
|
||||
|
||||
set readOnlyCanvas(value : boolean) {
|
||||
set readOnlyCanvas(value: boolean) {
|
||||
if (this._readOnlyCanvas === value) {
|
||||
// Nothing to do
|
||||
return
|
||||
@@ -427,7 +424,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
this.selection = undefined;
|
||||
}
|
||||
if (this.graph) {
|
||||
this.graph.getLinks().forEach((link : dia.Link) => {
|
||||
this.graph.getLinks().forEach((link: dia.Link) => {
|
||||
if (value) {
|
||||
link.attr('.link-tools/display', 'none');
|
||||
link.attr('.marker-vertices/display', 'none');
|
||||
@@ -450,11 +447,11 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* @param dragDescriptor DnD info object. Has on info on graph node being dragged (drag source) and what it is
|
||||
* being dragged over at the moment (drop target)
|
||||
*/
|
||||
showDragFeedback(dragDescriptor : Flo.DnDDescriptor) : void {
|
||||
showDragFeedback(dragDescriptor: Flo.DnDDescriptor): void {
|
||||
if (this.editor && this.editor.showDragFeedback) {
|
||||
this.editor.showDragFeedback(this.editorContext, dragDescriptor);
|
||||
} else {
|
||||
let magnet : SVGElement;
|
||||
let magnet: SVGElement;
|
||||
if (dragDescriptor.source && dragDescriptor.source.view) {
|
||||
joint.V(dragDescriptor.source.view.el).addClass('dnd-source-feedback');
|
||||
if (dragDescriptor.source.cssClassSelector) {
|
||||
@@ -482,11 +479,11 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* @param dragDescriptor DnD info object. Has on info on graph node being dragged (drag source) and what it is
|
||||
* being dragged over at the moment (drop target)
|
||||
*/
|
||||
hideDragFeedback(dragDescriptor : Flo.DnDDescriptor) : void {
|
||||
hideDragFeedback(dragDescriptor: Flo.DnDDescriptor): void {
|
||||
if (this.editor && this.editor.hideDragFeedback) {
|
||||
this.editor.hideDragFeedback(this.editorContext, dragDescriptor);
|
||||
} else {
|
||||
let magnet : SVGElement;
|
||||
let magnet: SVGElement;
|
||||
if (dragDescriptor.source && dragDescriptor.source.view) {
|
||||
joint.V(dragDescriptor.source.view.el).removeClass('dnd-source-feedback');
|
||||
if (dragDescriptor.source.cssClassSelector) {
|
||||
@@ -514,7 +511,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* @param dragDescriptor DnD info object. Has on info on graph node being dragged (drag source) and what it is
|
||||
* being dragged over at the moment (drop target)
|
||||
*/
|
||||
setDragDescriptor(dragDescriptor? : Flo.DnDDescriptor) : void {
|
||||
setDragDescriptor(dragDescriptor?: Flo.DnDDescriptor): void {
|
||||
if (this.highlighted === dragDescriptor) {
|
||||
return;
|
||||
}
|
||||
@@ -551,7 +548,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* @param y Y coordinate of the mosue on the canvas
|
||||
* @param context DnD context (palette or canvas)
|
||||
*/
|
||||
handleNodeDragging(draggedView : dia.CellView, targetUnderMouse : dia.CellView, x : number, y : number, sourceComponent : string) {
|
||||
handleNodeDragging(draggedView: dia.CellView, targetUnderMouse: dia.CellView, x: number, y: number, sourceComponent: string) {
|
||||
if (this.editor && this.editor.calculateDragDescriptor) {
|
||||
this.setDragDescriptor(this.editor.calculateDragDescriptor(this.editorContext, draggedView, targetUnderMouse, joint.g.point(x, y), sourceComponent));
|
||||
}
|
||||
@@ -572,12 +569,12 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* @param domNode DOM node to hide
|
||||
* @returns
|
||||
*/
|
||||
private _hideNode(domNode : HTMLElement) : VisibilityState {
|
||||
let oldVisibility : VisibilityState = {
|
||||
private _hideNode(domNode: HTMLElement): VisibilityState {
|
||||
let oldVisibility: VisibilityState = {
|
||||
visibility: domNode.style ? domNode.style.display : undefined,
|
||||
children: []
|
||||
};
|
||||
for (var i = 0; i < domNode.children.length; i++) {
|
||||
for (let i = 0; i < domNode.children.length; i++) {
|
||||
let node = domNode.children.item(i);
|
||||
if (node instanceof HTMLElement) {
|
||||
oldVisibility.children.push(this._hideNode(<HTMLElement> node));
|
||||
@@ -592,14 +589,14 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* @param domNode DOM node to restore visibility of
|
||||
* @param oldVisibility original visibility parameter
|
||||
*/
|
||||
_restoreNodeVisibility(domNode : HTMLElement, oldVisibility : VisibilityState) {
|
||||
_restoreNodeVisibility(domNode: HTMLElement, oldVisibility: VisibilityState) {
|
||||
if (domNode.style) {
|
||||
domNode.style.display = oldVisibility.visibility;
|
||||
}
|
||||
let j = 0;
|
||||
for (var i = 0; i < domNode.childNodes.length; i++) {
|
||||
for (let i = 0; i < domNode.childNodes.length; i++) {
|
||||
if (j < oldVisibility.children.length) {
|
||||
let node= domNode.children.item(i);
|
||||
let node = domNode.children.item(i);
|
||||
if (node instanceof HTMLElement) {
|
||||
this._restoreNodeVisibility(<HTMLElement> node, oldVisibility.children[j++]);
|
||||
}
|
||||
@@ -617,7 +614,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* Excluded views enables you to choose to filter some possible answers (useful in the case where elements are stacked
|
||||
* - e.g. Drag-n-Drop)
|
||||
*/
|
||||
getTargetViewFromEvent(event : MouseEvent, x : number, y : number, excludeViews : Array<dia.CellView> = []) : dia.CellView {
|
||||
getTargetViewFromEvent(event: MouseEvent, x: number, y: number, excludeViews: Array<dia.CellView> = []): dia.CellView {
|
||||
if (!x && !y) {
|
||||
let l = this.paper.snapToGrid({x: event.clientX, y: event.clientY});
|
||||
x = l.x;
|
||||
@@ -631,7 +628,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
// return underMouse;
|
||||
// }
|
||||
|
||||
let oldVisibility = excludeViews.map(x => this._hideNode(x.el));
|
||||
let oldVisibility = excludeViews.map(_x => this._hideNode(_x.el));
|
||||
let targetElement = document.elementFromPoint(event.clientX, event.clientY);
|
||||
excludeViews.forEach((excluded, i) => {
|
||||
this._restoreNodeVisibility(excluded.el, oldVisibility[i]);
|
||||
@@ -639,7 +636,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
return this.paper.findView($(targetElement));
|
||||
}
|
||||
|
||||
handleDnDFromPalette(dndEvent : Flo.DnDEvent) {
|
||||
handleDnDFromPalette(dndEvent: Flo.DnDEvent) {
|
||||
switch (dndEvent.type) {
|
||||
case Flo.DnDEventType.DRAG:
|
||||
this.handleDragFromPalette(dndEvent);
|
||||
@@ -652,7 +649,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
handleDragFromPalette(dnDEvent : Flo.DnDEvent) {
|
||||
handleDragFromPalette(dnDEvent: Flo.DnDEvent) {
|
||||
console.log('Dragging from palette');
|
||||
if (dnDEvent.view && !this.readOnlyCanvas) {
|
||||
let location = this.paper.snapToGrid({x: dnDEvent.event.clientX, y: dnDEvent.event.clientY});
|
||||
@@ -660,7 +657,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
createNode(metadata : Flo.ElementMetadata, props : Map<string, any>, position : dia.Point) : dia.Element {
|
||||
createNode(metadata: Flo.ElementMetadata, props: Map<string, any>, position: dia.Point): dia.Element {
|
||||
return Shapes.Factory.createNode({
|
||||
renderer: this.renderer,
|
||||
paper: this.paper,
|
||||
@@ -670,7 +667,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
createLink(source : Flo.LinkEnd, target : Flo.LinkEnd, metadata : Flo.ElementMetadata, props : Map<string, any>) : dia.Link {
|
||||
createLink(source: Flo.LinkEnd, target: Flo.LinkEnd, metadata: Flo.ElementMetadata, props: Map<string, any>): dia.Link {
|
||||
return Shapes.Factory.createLink({
|
||||
renderer: this.renderer,
|
||||
paper: this.paper,
|
||||
@@ -681,7 +678,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
handleDropFromPalette(event : Flo.DnDEvent) {
|
||||
handleDropFromPalette(event: Flo.DnDEvent) {
|
||||
let cellview = event.view;
|
||||
let evt = event.event;
|
||||
if (this.paper.el === evt.target || $.contains(this.paper.el, evt.target)) {
|
||||
@@ -707,7 +704,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
autosizePaper() : void {
|
||||
autosizePaper(): void {
|
||||
let parent = $('#paper-container', this.element.nativeElement);
|
||||
this.paper.fitToContent({
|
||||
padding: this.paperPadding,
|
||||
@@ -716,7 +713,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
fitToPage() : void {
|
||||
fitToPage(): void {
|
||||
let parent = $('#paper-container', this.element.nativeElement);
|
||||
let minScale = this.minZoom / 100;
|
||||
let maxScale = 2;
|
||||
@@ -736,11 +733,11 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
this.autosizePaper();
|
||||
}
|
||||
|
||||
get zoomPercent() : number {
|
||||
get zoomPercent(): number {
|
||||
return Math.round(joint.V(this.paper.viewport).scale().sx * 100);
|
||||
}
|
||||
|
||||
set zoomPercent(percent : number) {
|
||||
set zoomPercent(percent: number) {
|
||||
if (!isNaN(percent)) {
|
||||
if (percent < this.minZoom) {
|
||||
percent = this.minZoom;
|
||||
@@ -751,15 +748,15 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
percent = 0.00001;
|
||||
}
|
||||
}
|
||||
this.paper.scale(percent/100, percent/100);
|
||||
this.paper.scale(percent / 100, percent / 100);
|
||||
}
|
||||
}
|
||||
|
||||
get gridSize() : number {
|
||||
get gridSize(): number {
|
||||
return this._gridSize;
|
||||
}
|
||||
|
||||
set gridSize(size : number) {
|
||||
set gridSize(size: number) {
|
||||
if (!isNaN(size) && size >= 1) {
|
||||
this._gridSize = size;
|
||||
if (this.paper) {
|
||||
@@ -768,7 +765,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
validateContent() : Promise<any> {
|
||||
validateContent(): Promise<any> {
|
||||
return new Promise(resolve => {
|
||||
if (this.editor && this.editor.validate) {
|
||||
return this.editor
|
||||
@@ -789,7 +786,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
markElement(cell: dia.Cell, markers: Array<Flo.Marker>) {
|
||||
let errorMessages = markers.map(m => m.message);
|
||||
|
||||
let errorCell = cell.getEmbeddedCells().find((e : dia.Cell) => e.attr('./kind') === Constants.ERROR_DECORATION_KIND);
|
||||
let errorCell = cell.getEmbeddedCells().find((e: dia.Cell) => e.attr('./kind') === Constants.ERROR_DECORATION_KIND);
|
||||
if (errorCell) {
|
||||
if (errorMessages.length === 0) {
|
||||
errorCell.remove();
|
||||
@@ -805,7 +802,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
kind: Constants.ERROR_DECORATION_KIND,
|
||||
messages: errorMessages
|
||||
});
|
||||
let pt : dia.Point;
|
||||
let pt: dia.Point;
|
||||
const view = this.paper.findViewByModel(error);
|
||||
if (cell instanceof joint.dia.Element) {
|
||||
pt = (<dia.Element> cell).getBBox().topRight().offset(-error.get('size').width, 0);
|
||||
@@ -818,21 +815,21 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
doLayout() : Promise<void> {
|
||||
doLayout(): Promise<void> {
|
||||
if (this.renderer && this.renderer.layout) {
|
||||
return this.renderer.layout(this.paper);
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
set dsl(dslText : string) {
|
||||
set dsl(dslText: string) {
|
||||
if (this._dslText !== dslText) {
|
||||
this._dslText = dslText;
|
||||
this.textToGraphEventEmitter.emit();
|
||||
}
|
||||
}
|
||||
|
||||
get dsl() : string {
|
||||
get dsl(): string {
|
||||
return this._dslText;
|
||||
}
|
||||
|
||||
@@ -840,8 +837,8 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* Ask the server to parse the supplied text into a JSON graph of nodes and links,
|
||||
* then update the view based on that new information.
|
||||
*/
|
||||
updateGraphRepresentation() : Promise<any> {
|
||||
console.debug(`Updating graph to represent '${this._dslText}'`);
|
||||
updateGraphRepresentation(): Promise<any> {
|
||||
console.log(`Updating graph to represent '${this._dslText}'`);
|
||||
if (this.metamodel && this.metamodel.textToGraph) {
|
||||
return this.metamodel.textToGraph(this.editorContext, this._dslText).then(() => {
|
||||
this.textToGraphConversionCompleted.next();
|
||||
@@ -853,10 +850,10 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
updateTextRepresentation() : Promise<any> {
|
||||
updateTextRepresentation(): Promise<any> {
|
||||
if (this.metamodel && this.metamodel.graphToText) {
|
||||
return this.metamodel.graphToText(this.editorContext).then(text => {
|
||||
if (this._dslText != text) {
|
||||
if (this._dslText !== text) {
|
||||
this._dslText = text;
|
||||
this.dslChange.emit(text);
|
||||
}
|
||||
@@ -911,12 +908,12 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
this.graph.set('type', Constants.CANVAS_CONTEXT);
|
||||
}
|
||||
|
||||
handleNodeCreation(node : dia.Element) {
|
||||
handleNodeCreation(node: dia.Element) {
|
||||
node.on('change:size', this._resizeHandler);
|
||||
node.on('change:position', this._resizeHandler);
|
||||
if (node.attr('metadata')) {
|
||||
|
||||
node.on('change:attrs', (cell : dia.Element, attrs : any, changeData : any) => {
|
||||
node.on('change:attrs', (cell: dia.Element, attrs: any, changeData: any) => {
|
||||
let propertyPath = changeData ? changeData.propertyPath : undefined;
|
||||
if (propertyPath) {
|
||||
let propAttr = propertyPath.substr(propertyPath.indexOf('/') + 1);
|
||||
@@ -938,38 +935,38 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
* Forwards a link event occurrence to any handlers in the editor service, if they are defined. Event examples
|
||||
* are 'change:source', 'change:target'.
|
||||
*/
|
||||
handleLinkEvent(event : string, link : dia.Link) {
|
||||
handleLinkEvent(event: string, link: dia.Link) {
|
||||
if (this.renderer && this.renderer.handleLinkEvent) {
|
||||
this.renderer.handleLinkEvent(this.editorContext, event, link);
|
||||
}
|
||||
}
|
||||
|
||||
handleLinkCreation(link : dia.Link) {
|
||||
handleLinkCreation(link: dia.Link) {
|
||||
this.handleLinkEvent('add', link);
|
||||
|
||||
link.on('change:source', (link : dia.Link) => {
|
||||
link.on('change:source', (l: dia.Link) => {
|
||||
this.autosizePaper();
|
||||
let newSourceId = link.get('source').id;
|
||||
let oldSourceId = link.previous('source').id;
|
||||
let newSourceId = l.get('source').id;
|
||||
let oldSourceId = l.previous('source').id;
|
||||
if (newSourceId !== oldSourceId) {
|
||||
this.performGraphToTextSyncing();
|
||||
}
|
||||
this.handleLinkEvent('change:source', link);
|
||||
this.handleLinkEvent('change:source', l);
|
||||
});
|
||||
|
||||
link.on('change:target', (link : dia.Link) => {
|
||||
link.on('change:target', (l: dia.Link) => {
|
||||
this.autosizePaper();
|
||||
let newTargetId = link.get('target').id;
|
||||
let oldTargetId = link.previous('target').id;
|
||||
let newTargetId = l.get('target').id;
|
||||
let oldTargetId = l.previous('target').id;
|
||||
if (newTargetId !== oldTargetId) {
|
||||
this.performGraphToTextSyncing();
|
||||
}
|
||||
this.handleLinkEvent('change:target', link);
|
||||
this.handleLinkEvent('change:target', l);
|
||||
});
|
||||
|
||||
link.on('change:vertices', this._resizeHandler);
|
||||
|
||||
link.on('change:attrs', (cell : dia.Link, attrs : any, changeData : any) => {
|
||||
link.on('change:attrs', (cell: dia.Link, attrs: any, changeData: any) => {
|
||||
let propertyPath = changeData ? changeData.propertyPath : undefined;
|
||||
if (propertyPath) {
|
||||
let propAttr = propertyPath.substr(propertyPath.indexOf('/') + 1);
|
||||
@@ -994,7 +991,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
initGraphListeners() {
|
||||
this.graph.on('add', (element : dia.Cell) => {
|
||||
this.graph.on('add', (element: dia.Cell) => {
|
||||
if (element instanceof joint.dia.Link) {
|
||||
this.handleLinkCreation(<dia.Link> element);
|
||||
} else if (element instanceof joint.dia.Element) {
|
||||
@@ -1006,7 +1003,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
this.autosizePaper();
|
||||
});
|
||||
|
||||
this.graph.on('remove', (element : dia.Cell) => {
|
||||
this.graph.on('remove', (element: dia.Cell) => {
|
||||
if (element instanceof joint.dia.Link) {
|
||||
this.handleLinkEvent('remove', <dia.Link> element);
|
||||
}
|
||||
@@ -1022,7 +1019,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
// Set if link is fan-routed. Should be called before routing call
|
||||
this.graph.on('change:vertices', (link : dia.Link, changed : any, opt : any) => {
|
||||
this.graph.on('change:vertices', (link: dia.Link, changed: any, opt: any) => {
|
||||
if (opt.fanRouted) {
|
||||
link.set('fanRouted', true);
|
||||
} else {
|
||||
@@ -1035,7 +1032,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
|
||||
initPaperListeners() {
|
||||
// http://stackoverflow.com/questions/20463533/how-to-add-an-onclick-event-to-a-joint-js-element
|
||||
this.paper.on('cell:pointerclick', (cellView : dia.CellView) => {
|
||||
this.paper.on('cell:pointerclick', (cellView: dia.CellView) => {
|
||||
if (!this.readOnlyCanvas) {
|
||||
this.selection = cellView;
|
||||
}
|
||||
@@ -1054,7 +1051,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
});
|
||||
|
||||
this.paper.on('dragging-node-over-canvas', (dndEvent : Flo.DnDEvent) => {
|
||||
this.paper.on('dragging-node-over-canvas', (dndEvent: Flo.DnDEvent) => {
|
||||
console.log(`Canvas DnD type = ${dndEvent.type}`);
|
||||
let location = this.paper.snapToGrid({x: dndEvent.event.clientX, y: dndEvent.event.clientY});
|
||||
switch (dndEvent.type) {
|
||||
@@ -1075,9 +1072,9 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
initPaper() : void {
|
||||
initPaper(): void {
|
||||
|
||||
let options : dia.Paper.Options = {
|
||||
let options: dia.Paper.Options = {
|
||||
el: $('#paper', this.element.nativeElement),
|
||||
gridSize: this._gridSize,
|
||||
drawGrid: true,
|
||||
@@ -1086,10 +1083,10 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
linkView: this.renderer && this.renderer.getLinkView ? this.renderer.getLinkView() : joint.shapes.flo.LinkView,
|
||||
// Enable link snapping within 25px lookup radius
|
||||
snapLinks: { radius: 25 }, // http://www.jointjs.com/tutorial/ports
|
||||
defaultLink: /*this.renderer && this.renderer.createDefaultLink ? this.renderer.createDefaultLink : new joint.shapes.flo.Link*/
|
||||
(cellView: dia.ElementView, magnet: SVGElement) => {
|
||||
defaultLink: /*this.renderer && this.renderer.createDefaultLink ? this.renderer.createDefaultLink: new joint.shapes.flo.Link*/
|
||||
(cellView: dia.CellView, magnet: SVGElement) => {
|
||||
if (this.renderer && this.renderer.createLink) {
|
||||
let linkEnd : Flo.LinkEnd = {
|
||||
let linkEnd: Flo.LinkEnd = {
|
||||
id: cellView.model.id
|
||||
};
|
||||
if (magnet) {
|
||||
@@ -1109,7 +1106,7 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
},
|
||||
|
||||
// decide whether to create a link if the user clicks a magnet
|
||||
validateMagnet: (cellView : dia.ElementView, magnet : SVGElement) => {
|
||||
validateMagnet: (cellView: dia.CellView, magnet: SVGElement) => {
|
||||
if (this.readOnlyCanvas) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -1154,8 +1151,9 @@ export class EditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
if (this.editor && this.editor.validateLink) {
|
||||
options.validateConnection = (cellViewS : dia.ElementView, magnetS : SVGElement, cellViewT : dia.ElementView, magnetT : SVGElement, end: 'source' | 'target', linkView : dia.LinkView) =>
|
||||
this.editor.validateLink(this.editorContext, cellViewS, magnetS, cellViewT, magnetT, end === 'source', linkView);
|
||||
const self = this;
|
||||
options.validateConnection = (cellViewS: dia.CellView, magnetS: SVGElement, cellViewT: dia.CellView, magnetT: SVGElement, end: 'source' | 'target', linkView: dia.LinkView) =>
|
||||
self!.editor!.validateLink(this.editorContext, cellViewS, magnetS, cellViewT, magnetT, end === 'source', linkView);
|
||||
}
|
||||
|
||||
// The paper is what will represent the graph on the screen
|
||||
@@ -1,12 +1,12 @@
|
||||
export { FloModule } from './src/module';
|
||||
export { Palette } from './src/palette/palette.component';
|
||||
export { EditorComponent } from './src/editor/editor.component';
|
||||
export { DslEditorComponent } from './src/dsl-editor/dsl-editor.component';
|
||||
export { CodeEditorComponent } from './src/code-editor/code-editor.component';
|
||||
export { PropertiesGroupComponent } from './src/properties/properties.group.component';
|
||||
export { DynamicFormPropertyComponent } from './src/properties/df.property.component';
|
||||
export { ResizerDirective } from './src/directives/resizer';
|
||||
export { FloModule } from './module';
|
||||
export { Palette } from './palette/palette.component';
|
||||
export { EditorComponent } from './editor/editor.component';
|
||||
export { DslEditorComponent } from './dsl-editor/dsl-editor.component';
|
||||
export { CodeEditorComponent } from './code-editor/code-editor.component';
|
||||
export { PropertiesGroupComponent } from './properties/properties.group.component';
|
||||
export { DynamicFormPropertyComponent } from './properties/df.property.component';
|
||||
export { ResizerDirective } from './directives/resizer';
|
||||
|
||||
export * from './src/shared/flo-common';
|
||||
export * from './src/shared/flo-properties';
|
||||
export * from './src/shared/shapes';
|
||||
export * from './shared/flo-common';
|
||||
export * from './shared/flo-properties';
|
||||
export * from './shared/shapes';
|
||||
|
||||
@@ -3,35 +3,35 @@ import { Subject } from 'rxjs';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { dia } from 'jointjs';
|
||||
import { Flo } from '../shared/flo-common';
|
||||
import { Shapes, Constants } from './../shared/shapes';
|
||||
import { Shapes, Constants } from '../shared/shapes';
|
||||
import { DOCUMENT } from '@angular/platform-browser'
|
||||
import * as _$ from 'jquery';
|
||||
const joint : any = Flo.joint;
|
||||
const $ : any = _$;
|
||||
const joint: any = Flo.joint;
|
||||
const $: any = _$;
|
||||
|
||||
const DEBOUNCE_TIME : number = 300;
|
||||
const DEBOUNCE_TIME = 300;
|
||||
|
||||
joint.shapes.flo.PaletteGroupHeader = joint.shapes.basic.Generic.extend({
|
||||
// The path is the open/close arrow, defaults to vertical (open)
|
||||
markup: '<g class="scalable"><rect/></g><text/><g class="rotatable"><path d="m 10 10 l 5 8.7 l 5 -8.7 z"/></g>',
|
||||
defaults: joint.util.deepSupplement({
|
||||
type: 'palette.groupheader',
|
||||
size:{width:170,height:30},
|
||||
position:{x:0,y:0},
|
||||
size: {width: 170, height: 30},
|
||||
position: {x: 0, y: 0},
|
||||
attrs: {
|
||||
'rect': { fill: '#34302d', 'stroke-width': 1, stroke: '#6db33f', 'follow-scale':true, width:80, height:40 },
|
||||
'rect': { fill: '#34302d', 'stroke-width': 1, stroke: '#6db33f', 'follow-scale': true, width: 80, height: 40 },
|
||||
'text': {
|
||||
text:'',
|
||||
text: '',
|
||||
fill: '#eeeeee',
|
||||
'ref-x': 0.5,
|
||||
'ref-y': 7,
|
||||
'x-alignment':'middle',
|
||||
'x-alignment': 'middle',
|
||||
'font-size': 18/*, 'font-weight': 'bold', 'font-variant': 'small-caps', 'text-transform': 'capitalize'*/
|
||||
},
|
||||
'path': { fill: 'white', 'stroke-width': 2, stroke: 'white'/*,transform:'rotate(90,15,15)'*/}
|
||||
},
|
||||
// custom properties
|
||||
isOpen:true
|
||||
isOpen: true
|
||||
}, joint.shapes.basic.Generic.prototype.defaults)
|
||||
});
|
||||
|
||||
@@ -43,23 +43,46 @@ joint.shapes.flo.PaletteGroupHeader = joint.shapes.basic.Generic.extend({
|
||||
})
|
||||
export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
|
||||
@Input()
|
||||
metamodel : Flo.Metamodel;
|
||||
private _metamodelListener: Flo.MetamodelListener = {
|
||||
metadataError: (data) => {},
|
||||
metadataAboutToChange: () => {},
|
||||
metadataChanged: () => this.rebuildPalette()
|
||||
};
|
||||
|
||||
/**
|
||||
* The names of any groups in the palette that have been deliberately closed (the arrow clicked on)
|
||||
*/
|
||||
private closedGroups: Set<string>;
|
||||
|
||||
/**
|
||||
* Model of the clicked element
|
||||
*/
|
||||
private clickedElement: dia.Cell | undefined;
|
||||
|
||||
private viewBeingDragged: dia.CellView | undefined;
|
||||
|
||||
private initialized = false;
|
||||
|
||||
private _paletteSize: number;
|
||||
|
||||
private _filterText = '';
|
||||
|
||||
private paletteGraph: dia.Graph;
|
||||
|
||||
private palette: dia.Paper;
|
||||
|
||||
private floaterpaper: dia.Paper;
|
||||
|
||||
private filterTextModel = new Subject<string>();
|
||||
|
||||
@Input()
|
||||
renderer : Flo.Renderer;
|
||||
metamodel: Flo.Metamodel;
|
||||
|
||||
@Input()
|
||||
paletteEntryPadding : dia.Size = {width:12, height:12};
|
||||
renderer: Flo.Renderer;
|
||||
|
||||
@Input()
|
||||
set paletteSize(size : number) {
|
||||
console.log('Palette Size : ' + size);
|
||||
if (this._paletteSize != size) {
|
||||
this._paletteSize = size;
|
||||
this.rebuildPalette();
|
||||
}
|
||||
}
|
||||
paletteEntryPadding: dia.Size = {width: 12, height: 12};
|
||||
|
||||
@Output()
|
||||
onPaletteEntryDrop = new EventEmitter<Flo.DnDEvent>();
|
||||
@@ -70,42 +93,19 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
@Output()
|
||||
paletteFocus = new EventEmitter<void>();
|
||||
|
||||
private _paletteSize : number;
|
||||
private mouseMoveHanlder = (e: any) => this.handleDrag(e);
|
||||
private mouseUpHanlder = (e: any) => this.handleMouseUp(e);
|
||||
|
||||
private _filterText : string = '';
|
||||
@Input()
|
||||
set paletteSize(size: number) {
|
||||
console.log('Palette Size: ' + size);
|
||||
if (this._paletteSize !== size) {
|
||||
this._paletteSize = size;
|
||||
this.rebuildPalette();
|
||||
}
|
||||
}
|
||||
|
||||
private paletteGraph : dia.Graph;
|
||||
|
||||
private palette : dia.Paper;
|
||||
|
||||
private floaterpaper : dia.Paper;
|
||||
|
||||
private filterTextModel = new Subject<string>();
|
||||
|
||||
private mouseMoveHanlder = (e : any) => this.handleDrag(e);
|
||||
private mouseUpHanlder = (e : any) => this.handleMouseUp(e);
|
||||
|
||||
private _metamodelListener : Flo.MetamodelListener = {
|
||||
metadataError: (data) => {},
|
||||
metadataAboutToChange: () => {},
|
||||
metadataChanged: () => this.rebuildPalette()
|
||||
};
|
||||
|
||||
/**
|
||||
* The names of any groups in the palette that have been deliberately closed (the arrow clicked on)
|
||||
*/
|
||||
private closedGroups : Set<string>;
|
||||
|
||||
/**
|
||||
* Model of the clicked element
|
||||
*/
|
||||
private clickedElement : dia.Cell;
|
||||
|
||||
private viewBeingDragged : dia.CellView;
|
||||
|
||||
private initialized = false;
|
||||
|
||||
constructor(private element: ElementRef, @Inject(DOCUMENT) private document : any) {
|
||||
constructor(private element: ElementRef, @Inject(DOCUMENT) private document: any) {
|
||||
this.paletteGraph = new joint.dia.Graph();
|
||||
this.paletteGraph.set('type', Constants.PALETTE_CONTEXT);
|
||||
this._filterText = '';
|
||||
@@ -122,7 +122,7 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
// Create the paper for the palette using the specified element view
|
||||
this.palette = new joint.dia.Paper({
|
||||
el: element,
|
||||
gridSize:1,
|
||||
gridSize: 1,
|
||||
model: this.paletteGraph,
|
||||
height: $(this.element.nativeElement.parentNode).height(),
|
||||
width: $(this.element.nativeElement.parentNode).width(),
|
||||
@@ -130,13 +130,12 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
interactive: false
|
||||
});
|
||||
|
||||
this.palette.on('cell:pointerup', (cellview : dia.CellView, evt : any) => {
|
||||
console.debug('pointerup');
|
||||
this.palette.on('cell:pointerup', (cellview: dia.CellView, evt: any) => {
|
||||
if (this.viewBeingDragged) {
|
||||
this.trigger({
|
||||
type: Flo.DnDEventType.DROP,
|
||||
view: this.viewBeingDragged,
|
||||
event : evt
|
||||
event: evt
|
||||
});
|
||||
this.viewBeingDragged = undefined;
|
||||
}
|
||||
@@ -146,16 +145,16 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
});
|
||||
|
||||
// Toggle the header open/closed on a click
|
||||
this.palette.on('cell:pointerclick', (cellview : dia.CellView, event : any) => {
|
||||
this.palette.on('cell:pointerclick', (cellview: dia.CellView, event: any) => {
|
||||
// TODO [design][palette] should the user need to click on the arrow rather than anywhere on the header?
|
||||
// Click position within the element would be: evt.offsetX, evt.offsetY
|
||||
let element : dia.Cell = cellview.model;
|
||||
if (cellview.model.attributes.header) {
|
||||
const cell: dia.Cell = cellview.model;
|
||||
if (cell.attributes.header) {
|
||||
// Toggle the header open/closed
|
||||
if (element.get('isOpen')) {
|
||||
this.rotateClosed(element);
|
||||
if (cell.get('isOpen')) {
|
||||
this.rotateClosed(cell);
|
||||
} else {
|
||||
this.rotateOpen(element);
|
||||
this.rotateOpen(cell);
|
||||
}
|
||||
}
|
||||
// TODO [palette] ensure other mouse handling events do nothing for headers
|
||||
@@ -196,25 +195,24 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
this.palette.remove();
|
||||
}
|
||||
|
||||
ngOnChanges(changes : SimpleChanges) {
|
||||
console.log('Changed!!!');
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
// if (changes.hasOwnProperty('paletteSize') || changes.hasOwnProperty('filterText')) {
|
||||
// this.metamodel.load().then(metamodel => this.buildPalette(metamodel));
|
||||
// }
|
||||
}
|
||||
|
||||
private createPaletteGroup(title : string, isOpen : boolean) : dia.Element {
|
||||
let newGroupHeader = new joint.shapes.flo.PaletteGroupHeader({attrs:{text:{text:title}}});
|
||||
newGroupHeader.set('header',title);
|
||||
private createPaletteGroup(title: string, isOpen: boolean): dia.Element {
|
||||
let newGroupHeader = new joint.shapes.flo.PaletteGroupHeader({attrs: {text: {text: title}}});
|
||||
newGroupHeader.set('header', title);
|
||||
if (!isOpen) {
|
||||
newGroupHeader.attr({'path':{'transform':'rotate(-90,15,13)'}});
|
||||
newGroupHeader.set('isOpen',false);
|
||||
newGroupHeader.attr({'path': {'transform': 'rotate(-90,15,13)'}});
|
||||
newGroupHeader.set('isOpen', false);
|
||||
}
|
||||
this.paletteGraph.addCell(newGroupHeader);
|
||||
return newGroupHeader;
|
||||
}
|
||||
|
||||
private createPaletteEntry(title : string, metadata : Flo.ElementMetadata) {
|
||||
private createPaletteEntry(title: string, metadata: Flo.ElementMetadata) {
|
||||
return Shapes.Factory.createNode({
|
||||
renderer: this.renderer,
|
||||
paper: this.palette,
|
||||
@@ -222,8 +220,8 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
});
|
||||
}
|
||||
|
||||
private buildPalette(metamodel : Map<string, Map<string, Flo.ElementMetadata>>) {
|
||||
let startTime : number = new Date().getTime();
|
||||
private buildPalette(metamodel: Map<string, Map<string, Flo.ElementMetadata>>) {
|
||||
let startTime: number = new Date().getTime();
|
||||
|
||||
this.paletteReady.emit(false);
|
||||
this.paletteGraph.clear();
|
||||
@@ -233,60 +231,61 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
filterText = filterText.toLowerCase();
|
||||
}
|
||||
|
||||
let paletteNodes : Array<dia.Element> = [];
|
||||
let groupAdded : Set<string> = new Set<string>();
|
||||
let paletteNodes: Array<dia.Element> = [];
|
||||
let groupAdded: Set<string> = new Set<string>();
|
||||
|
||||
let parentWidth : number = this._paletteSize;
|
||||
console.log(`Parent Width : ${parentWidth}`);
|
||||
let parentWidth: number = this._paletteSize;
|
||||
console.log(`Parent Width: ${parentWidth}`);
|
||||
|
||||
// The field closedGroups tells us which should not be shown
|
||||
// Work out the list of active groups/nodes based on the filter text
|
||||
this.metamodel.groups().forEach(group => {
|
||||
if (metamodel.has(group)) {
|
||||
Array.from(metamodel.get(group).keys()).sort().forEach(name => {
|
||||
let node : Flo.ElementMetadata = metamodel.get(group).get(name);
|
||||
let nodeActive : boolean = !(node.metadata && node.metadata.noPaletteEntry);
|
||||
if (nodeActive && filterText) {
|
||||
nodeActive = false;
|
||||
if (name.toLowerCase().indexOf(filterText) !== -1) {
|
||||
nodeActive = true;
|
||||
if (metamodel && metamodel.has(group)) {
|
||||
Array.from(metamodel.get(group)!.keys()).sort().forEach(name => {
|
||||
let node = metamodel.get(group)!.get(name);
|
||||
if (node) {
|
||||
let nodeActive: boolean = !(node.metadata && node.metadata.noPaletteEntry);
|
||||
if (nodeActive && filterText) {
|
||||
nodeActive = false;
|
||||
if (name.toLowerCase().indexOf(filterText) !== -1) {
|
||||
nodeActive = true;
|
||||
} else if (group.toLowerCase().indexOf(filterText) !== -1) {
|
||||
nodeActive = true;
|
||||
}
|
||||
// else if (node.description && node.description.toLowerCase().indexOf(filterText) !== -1) {
|
||||
// nodeActive = true;
|
||||
// }
|
||||
// else if (node.properties) {
|
||||
// Object.keys(node.properties).sort().forEach(function(propertyName) {
|
||||
// if (propertyName.toLowerCase().indexOf(filterText) !== -1 ||
|
||||
// (node.properties[propertyName].description &&
|
||||
// node.properties[propertyName].description.toLowerCase().indexOf(filterText) !== -1)) {
|
||||
// nodeActive=true;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
else if (group.toLowerCase().indexOf(filterText) !== -1) {
|
||||
nodeActive = true;
|
||||
}
|
||||
// else if (node.description && node.description.toLowerCase().indexOf(filterText) !== -1) {
|
||||
// nodeActive = true;
|
||||
// }
|
||||
// else if (node.properties) {
|
||||
// Object.keys(node.properties).sort().forEach(function(propertyName) {
|
||||
// if (propertyName.toLowerCase().indexOf(filterText) !== -1 ||
|
||||
// (node.properties[propertyName].description &&
|
||||
// node.properties[propertyName].description.toLowerCase().indexOf(filterText) !== -1)) {
|
||||
// nodeActive=true;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
if (nodeActive) {
|
||||
if (!groupAdded.has(group)) {
|
||||
let header : dia.Element = this.createPaletteGroup(group, !this.closedGroups.has(group));
|
||||
header.set('size', {width: parentWidth, height: 30});
|
||||
paletteNodes.push(header);
|
||||
groupAdded.add(group);
|
||||
}
|
||||
if (!this.closedGroups.has(group)) {
|
||||
paletteNodes.push(this.createPaletteEntry(name, node));
|
||||
if (nodeActive) {
|
||||
if (!groupAdded.has(group)) {
|
||||
let header: dia.Element = this.createPaletteGroup(group, !this.closedGroups.has(group));
|
||||
header.set('size', {width: parentWidth, height: 30});
|
||||
paletteNodes.push(header);
|
||||
groupAdded.add(group);
|
||||
}
|
||||
if (!this.closedGroups.has(group)) {
|
||||
paletteNodes.push(this.createPaletteEntry(name, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let cellWidth : number = 0, cellHeight : number = 0;
|
||||
let cellWidth = 0, cellHeight = 0;
|
||||
// Determine the size of the palette entry cell (width and height)
|
||||
paletteNodes.forEach(pnode => {
|
||||
if (pnode.attr('metadata/name')) {
|
||||
let dimension : dia.Size = {
|
||||
let dimension: dia.Size = {
|
||||
width: pnode.get('size').width,
|
||||
height: pnode.get('size').height
|
||||
};
|
||||
@@ -304,21 +303,21 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
cellHeight += 2 * this.paletteEntryPadding.height;
|
||||
|
||||
// Align palette entries row to be at the center
|
||||
let startX : number = parentWidth >= cellWidth ? (parentWidth - Math.floor(parentWidth / cellWidth) * cellWidth) / 2 : 0;
|
||||
let xpos : number = startX;
|
||||
let ypos : number = 0;
|
||||
let prevNode : dia.Element;
|
||||
let startX: number = parentWidth >= cellWidth ? (parentWidth - Math.floor(parentWidth / cellWidth) * cellWidth) / 2 : 0;
|
||||
let xpos = startX;
|
||||
let ypos = 0;
|
||||
let prevNode: dia.Element;
|
||||
|
||||
// Layout palette entry nodes
|
||||
paletteNodes.forEach(pnode => {
|
||||
let dimension : dia.Size = {
|
||||
let dimension: dia.Size = {
|
||||
width: pnode.get('size').width,
|
||||
height: pnode.get('size').height
|
||||
};
|
||||
if (pnode.get('header')) { //attributes.attrs.header) {
|
||||
// Palette entry header
|
||||
xpos = startX;
|
||||
pnode.set('position',{x:0, y:ypos});
|
||||
pnode.set('position', {x: 0, y: ypos});
|
||||
ypos += dimension.height + 5;
|
||||
} else {
|
||||
// Palette entry element
|
||||
@@ -341,7 +340,7 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
});
|
||||
this.palette.setDimensions(parentWidth, ypos);
|
||||
this.paletteReady.emit(true);
|
||||
console.info('buildPalette took '+(new Date().getTime()-startTime)+'ms');
|
||||
console.log('buildPalette took ' + (new Date().getTime() - startTime) + 'ms');
|
||||
}
|
||||
|
||||
rebuildPalette() {
|
||||
@@ -350,26 +349,26 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
set filterText(text : string) {
|
||||
set filterText(text: string) {
|
||||
if (this._filterText !== text) {
|
||||
this._filterText = text;
|
||||
this.filterTextModel.next(text);
|
||||
}
|
||||
}
|
||||
|
||||
get filterText() : string {
|
||||
get filterText(): string {
|
||||
return this._filterText;
|
||||
}
|
||||
|
||||
private getPaletteView(view : any) : dia.Element {
|
||||
let self : Palette = this;
|
||||
private getPaletteView(view: any): dia.Element {
|
||||
let self: Palette = this;
|
||||
return view.extend({
|
||||
pointerdown: function(/*evt, x, y*/) {
|
||||
// Remove the tooltip
|
||||
// $('.node-tooltip').remove();
|
||||
// TODO move metadata to the right place (not inside attrs I think)
|
||||
self.clickedElement = this.model;
|
||||
if (self.clickedElement.attr('metadata')) {
|
||||
if (self.clickedElement && self.clickedElement.attr('metadata')) {
|
||||
$(self.document).on('mousemove', self.mouseMoveHanlder);
|
||||
}
|
||||
},
|
||||
@@ -414,7 +413,7 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
// $(nodeTooltip).append(nodeDescription);
|
||||
//
|
||||
// metadata.get('description').then(function(description) {
|
||||
// $(nodeDescription).text(description ? description : model.attr('metadata/name'));
|
||||
// $(nodeDescription).text(description ? description: model.attr('metadata/name'));
|
||||
// }, function() {
|
||||
// $(nodeDescription).text(model.attr('metadata/name'));
|
||||
// });
|
||||
@@ -460,29 +459,28 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
});
|
||||
}
|
||||
|
||||
private handleMouseUp(event : any) {
|
||||
private handleMouseUp(event: any) {
|
||||
$(this.document).off('mousemove', this.mouseMoveHanlder);
|
||||
}
|
||||
|
||||
private trigger(event : Flo.DnDEvent) {
|
||||
console.debug('EVENT: type=' + event.type + ' element=' + event.view.model.attr('metadata/name') + ' x=' + event.event.pageX + ' y=' + event.event.pageY);
|
||||
private trigger(event: Flo.DnDEvent) {
|
||||
this.onPaletteEntryDrop.emit(event);
|
||||
}
|
||||
|
||||
private handleDrag(event : any) {
|
||||
private handleDrag(event: any) {
|
||||
// TODO offsetX/Y not on firefox
|
||||
// console.debug("tracking move: x="+event.pageX+",y="+event.pageY);
|
||||
// console.log('Element = ' + (this.clickedElement ? this.clickedElement.attr('metadata/name') : 'null'));
|
||||
// console.log('Element = ' + (this.clickedElement ? this.clickedElement.attr('metadata/name'): 'null'));
|
||||
if (this.clickedElement && this.clickedElement.attr('metadata')) {
|
||||
if (!this.viewBeingDragged) {
|
||||
|
||||
let dataOfClickedElement : Flo.ElementMetadata = this.clickedElement.attr('metadata');
|
||||
let dataOfClickedElement: Flo.ElementMetadata = this.clickedElement.attr('metadata');
|
||||
// custom div if not already built.
|
||||
$('<div>', {
|
||||
id: 'palette-floater'
|
||||
}).appendTo($('body'));
|
||||
|
||||
let floatergraph : dia.Graph = new joint.dia.Graph();
|
||||
let floatergraph: dia.Graph = new joint.dia.Graph();
|
||||
floatergraph.set('type', Constants.FEEDBACK_CONTEXT);
|
||||
|
||||
const parent = $('#palette-floater');
|
||||
@@ -500,22 +498,22 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
|
||||
// TODO float thing needs to be bigger otherwise icon label is missing
|
||||
// Initiative drag and drop - create draggable element
|
||||
let floaternode : dia.Element = Shapes.Factory.createNode({
|
||||
"renderer": this.renderer,
|
||||
let floaternode: dia.Element = Shapes.Factory.createNode({
|
||||
'renderer': this.renderer,
|
||||
'paper': this.floaterpaper,
|
||||
'graph': floatergraph,
|
||||
'metadata': dataOfClickedElement
|
||||
});
|
||||
|
||||
// Only node view expected
|
||||
let box : dia.BBox = (<dia.ElementView>this.floaterpaper.findViewByModel(floaternode)).getBBox();
|
||||
let size : dia.Size = floaternode.get('size');
|
||||
let box: dia.BBox = (<dia.ElementView>this.floaterpaper.findViewByModel(floaternode)).getBBox();
|
||||
let size: dia.Size = floaternode.get('size');
|
||||
// Account for node real size including ports
|
||||
floaternode.translate(box.width - size.width, box.height - size.height);
|
||||
this.viewBeingDragged = this.floaterpaper.findViewByModel(floaternode);
|
||||
$('#palette-floater').offset({left:event.pageX+5,top:event.pageY+5});
|
||||
$('#palette-floater').offset({left: event.pageX + 5, top: event.pageY + 5});
|
||||
} else {
|
||||
$('#palette-floater').offset({left:event.pageX+5,top:event.pageY+5});
|
||||
$('#palette-floater').offset({left: event.pageX + 5, top: event.pageY + 5});
|
||||
this.trigger({
|
||||
type: Flo.DnDEventType.DRAG,
|
||||
view: this.viewBeingDragged,
|
||||
@@ -528,31 +526,31 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
/*
|
||||
* Modify the rotation of the arrow in the header from horizontal(closed) to vertical(open)
|
||||
*/
|
||||
private rotateOpen(element : dia.Cell) {
|
||||
private rotateOpen(element: dia.Cell) {
|
||||
setTimeout(() => this.doRotateOpen(element, 90));
|
||||
}
|
||||
|
||||
private doRotateOpen(element : dia.Cell, angle : number) {
|
||||
private doRotateOpen(element: dia.Cell, angle: number) {
|
||||
angle -= 10;
|
||||
element.attr({'path':{'transform':'rotate(-'+angle+',15,13)'}});
|
||||
element.attr({'path': {'transform': 'rotate(-' + angle + ',15,13)'}});
|
||||
if (angle <= 0) {
|
||||
element.set('isOpen',true);
|
||||
element.set('isOpen', true);
|
||||
this.closedGroups.delete(element.get('header'));
|
||||
this.rebuildPalette();
|
||||
} else {
|
||||
setTimeout(() => this.doRotateOpen(element, angle),10);
|
||||
setTimeout(() => this.doRotateOpen(element, angle), 10);
|
||||
}
|
||||
}
|
||||
|
||||
private doRotateClose(element : dia.Cell, angle : number) {
|
||||
angle +=10;
|
||||
element.attr({'path':{'transform':'rotate(-'+angle+',15,13)'}});
|
||||
private doRotateClose(element: dia.Cell, angle: number) {
|
||||
angle += 10;
|
||||
element.attr({'path': {'transform': 'rotate(-' + angle + ',15,13)'}});
|
||||
if (angle >= 90) {
|
||||
element.set('isOpen',false);
|
||||
element.set('isOpen', false);
|
||||
this.closedGroups.add(element.get('header'));
|
||||
this.rebuildPalette();
|
||||
} else {
|
||||
setTimeout(() => this.doRotateClose(element, angle),10);
|
||||
setTimeout(() => this.doRotateClose(element, angle), 10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,7 +559,7 @@ export class Palette implements OnInit, OnDestroy, OnChanges {
|
||||
/*
|
||||
* Modify the rotation of the arrow in the header from vertical(open) to horizontal(closed)
|
||||
*/
|
||||
private rotateClosed(element : dia.Cell) {
|
||||
private rotateClosed(element: dia.Cell) {
|
||||
setTimeout(() => this.doRotateClose(element, 0));
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Properties } from '../shared/flo-properties';
|
||||
export class DynamicFormPropertyComponent {
|
||||
|
||||
@Input()
|
||||
model : Properties.ControlModel<any>;
|
||||
model: Properties.ControlModel<any>;
|
||||
|
||||
@Input() form: FormGroup;
|
||||
|
||||
@@ -20,7 +20,7 @@ export class DynamicFormPropertyComponent {
|
||||
return Properties.InputType;
|
||||
}
|
||||
|
||||
get control() : AbstractControl {
|
||||
get control(): AbstractControl {
|
||||
return this.form.controls[this.model.id];
|
||||
}
|
||||
|
||||
@@ -10,10 +10,10 @@ import { Properties } from '../shared/flo-properties';
|
||||
export class PropertiesGroupComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
propertiesGroupModel : Properties.PropertiesGroupModel;
|
||||
propertiesGroupModel: Properties.PropertiesGroupModel;
|
||||
|
||||
@Input()
|
||||
form : FormGroup;
|
||||
form: FormGroup;
|
||||
|
||||
ngOnInit() {
|
||||
if (this.propertiesGroupModel.isLoading) {
|
||||
245
src/lib/shared/flo-common.ts
Normal file
245
src/lib/shared/flo-common.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
import { dia, g } from 'jointjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import * as _joint from 'jointjs';
|
||||
import * as _$ from 'jquery';
|
||||
|
||||
const $ = _$;
|
||||
|
||||
export namespace Flo {
|
||||
|
||||
export const joint: any = _joint;
|
||||
|
||||
export enum DnDEventType {
|
||||
DRAG,
|
||||
DROP
|
||||
}
|
||||
|
||||
export interface DnDEvent {
|
||||
type: DnDEventType;
|
||||
view: dia.CellView;
|
||||
event: MouseEvent;
|
||||
}
|
||||
|
||||
export interface PropertyMetadata {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly description?: string;
|
||||
readonly defaultValue?: any;
|
||||
readonly type?: string;
|
||||
readonly [propName: string]: any;
|
||||
}
|
||||
|
||||
export interface ExtraMetadata {
|
||||
readonly titleProperty?: string;
|
||||
readonly noEditableProps?: boolean;
|
||||
readonly noPaletteEntry?: boolean;
|
||||
readonly unselectable?: boolean;
|
||||
readonly [propName: string]: any;
|
||||
|
||||
readonly allowAdditionalProperties?: boolean; //TODO: Verify it is still needed
|
||||
}
|
||||
|
||||
export interface ElementMetadata {
|
||||
readonly name: string;
|
||||
readonly group: string;
|
||||
readonly metadata?: ExtraMetadata;
|
||||
readonly [propName: string]: any;
|
||||
description?(): Promise<string>;
|
||||
get(property: String): Promise<PropertyMetadata>;
|
||||
properties(): Promise<Map<string, PropertyMetadata>>;
|
||||
}
|
||||
|
||||
export interface ViewerDescriptor {
|
||||
readonly graph?: dia.Graph;
|
||||
readonly paper?: dia.Paper;
|
||||
}
|
||||
|
||||
export interface MetamodelListener {
|
||||
metadataError(data: any): void;
|
||||
metadataAboutToChange(): void;
|
||||
metadataChanged(): void;
|
||||
}
|
||||
|
||||
export interface Metamodel {
|
||||
textToGraph(flo: EditorContext, dsl: string): Promise<any>;
|
||||
graphToText(flo: EditorContext): Promise<string>;
|
||||
load(): Promise<Map<string, Map<string, ElementMetadata>>>;
|
||||
groups(): Array<string>;
|
||||
|
||||
refresh?(): Promise<Map<string, Map<string, ElementMetadata>>>;
|
||||
subscribe?(listener: MetamodelListener): void;
|
||||
unsubscribe?(listener: MetamodelListener): void;
|
||||
isValidPropertyValue?(element: dia.Element, property: string, value: any): boolean;
|
||||
}
|
||||
|
||||
export interface CreationParams {
|
||||
metadata?: ElementMetadata;
|
||||
props?: Map<string, any>;
|
||||
}
|
||||
|
||||
export interface ElementCreationParams extends CreationParams {
|
||||
position?: dia.Point;
|
||||
}
|
||||
|
||||
export interface LinkCreationParams extends CreationParams {
|
||||
source: string;
|
||||
target: string;
|
||||
}
|
||||
|
||||
export interface EmbeddedChildCreationParams extends CreationParams {
|
||||
parent: dia.Cell;
|
||||
position?: dia.Point;
|
||||
}
|
||||
|
||||
export interface DecorationCreationParams extends EmbeddedChildCreationParams {
|
||||
kind: string;
|
||||
messages: Array<string>;
|
||||
}
|
||||
|
||||
export interface HandleCreationParams extends EmbeddedChildCreationParams {
|
||||
kind: string;
|
||||
}
|
||||
|
||||
export interface Renderer {
|
||||
createNode?(metadata: ElementMetadata, props?: Map<string, any>): dia.Element;
|
||||
createLink?(source: LinkEnd, target: LinkEnd, metadata?: ElementMetadata, props?: Map<string, any>): dia.Link;
|
||||
createHandle?(kind: string, parent: dia.Cell): dia.Element;
|
||||
createDecoration?(kind: string, parent: dia.Cell): dia.Element;
|
||||
initializeNewNode?(node: dia.Element, viewerDescriptor: ViewerDescriptor): void;
|
||||
initializeNewLink?(link: dia.Link, viewerDescriptor: ViewerDescriptor): void;
|
||||
initializeNewHandle?(handle: dia.Element, viewerDescriptor: ViewerDescriptor): void;
|
||||
initializeNewDecoration?(decoration: dia.Element, viewerDescriptor: ViewerDescriptor): void;
|
||||
getNodeView?(): dia.ElementView;
|
||||
getLinkView?(): dia.LinkView;
|
||||
layout?(paper: dia.Paper): Promise<any>;
|
||||
handleLinkEvent?(context: EditorContext, event: string, link: dia.Link): void;
|
||||
isSemanticProperty?(propertyPath: string, element: dia.Cell): boolean;
|
||||
refreshVisuals?(cell: dia.Cell, propertyPath: string, paper: dia.Paper): void;
|
||||
getLinkAnchorPoint?(linkView: dia.LinkView, view: dia.ElementView, port: SVGElement, reference: dia.Point): dia.Point;
|
||||
}
|
||||
|
||||
export interface EditorContext {
|
||||
readonly textToGraphConversionObservable: Observable<void>;
|
||||
readonly graphToTextConversionObservable: Observable<void>;
|
||||
readonly paletteReady: Observable<boolean>;
|
||||
zoomPercent: number;
|
||||
gridSize: number;
|
||||
readOnlyCanvas: boolean;
|
||||
selection: dia.CellView;
|
||||
graphToTextSync: boolean;
|
||||
noPalette: boolean;
|
||||
setDsl(dsl: string): void;
|
||||
updateGraph(): Promise<any>;
|
||||
updateText(): Promise<any>;
|
||||
performLayout(): Promise<void>;
|
||||
clearGraph(): void;
|
||||
getGraph(): dia.Graph;
|
||||
getPaper(): dia.Paper;
|
||||
getMinZoom(): number;
|
||||
getMaxZoom(): number;
|
||||
getZoomStep(): number;
|
||||
fitToPage(): void;
|
||||
createNode(metadata: ElementMetadata, props?: Map<string, any>, position?: dia.Point): dia.Element;
|
||||
createLink(source: LinkEnd, target: LinkEnd, metadata?: ElementMetadata, props?: Map<string, any>): dia.Link;
|
||||
deleteSelectedNode(): void;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
export interface LinkEndDescriptor {
|
||||
view: dia.CellView;
|
||||
cssClassSelector?: string;
|
||||
}
|
||||
|
||||
export interface DnDDescriptor {
|
||||
sourceComponent?: string;
|
||||
range?: number;
|
||||
source?: LinkEndDescriptor;
|
||||
target?: LinkEndDescriptor;
|
||||
}
|
||||
|
||||
export interface LinkEnd {
|
||||
id: string | number;
|
||||
selector?: string;
|
||||
port?: string;
|
||||
}
|
||||
|
||||
export enum Severity {
|
||||
Error,
|
||||
Warning
|
||||
}
|
||||
|
||||
export interface Marker {
|
||||
severity: Severity;
|
||||
message: string;
|
||||
range?: Range;
|
||||
}
|
||||
|
||||
export interface Position {
|
||||
ch: number;
|
||||
line: number;
|
||||
}
|
||||
|
||||
export interface Range {
|
||||
start: Position;
|
||||
end: Position;
|
||||
}
|
||||
|
||||
export interface Editor {
|
||||
interactive?: ((cellView: dia.CellView, event: string) => boolean) | boolean | dia.CellView.InteractivityOptions;
|
||||
allowLinkVertexEdit?: boolean;
|
||||
highlighting?: any;
|
||||
createHandles?(context: EditorContext, createHandle: (owner: dia.CellView, kind: string, action: () => void, location: dia.Point) => void, owner: dia.CellView): void;
|
||||
validatePort?(context: EditorContext, view: dia.CellView, magnet: SVGElement): boolean;
|
||||
validateLink?(context: EditorContext, cellViewS: dia.CellView, portS: SVGElement, cellViewT: dia.CellView, portT: SVGElement, isSource: boolean, linkView: dia.LinkView): boolean;
|
||||
calculateDragDescriptor?(context: EditorContext, draggedView: dia.CellView, targetUnderMouse: dia.CellView, coordinate: g.Point, sourceComponent: string): DnDDescriptor;
|
||||
handleNodeDropping?(context: EditorContext, dragDescriptor: DnDDescriptor): void;
|
||||
showDragFeedback?(context: EditorContext, dragDescriptor: DnDDescriptor): void;
|
||||
hideDragFeedback?(context: EditorContext, dragDescriptor: DnDDescriptor): void;
|
||||
validate?(graph: dia.Graph, dsl: string, flo: EditorContext): Promise<Map<string | number, Array<Marker>>>;
|
||||
preDelete?(context: EditorContext, deletedElement: dia.Cell): void;
|
||||
setDefaultContent?(editorContext: EditorContext, data: Map<string, Map<string, ElementMetadata>>): void;
|
||||
}
|
||||
|
||||
export function findMagnetByClass(view: dia.CellView, className: string): SVGElement | undefined {
|
||||
if (className && className.startsWith('.')) {
|
||||
className = className.substr(1);
|
||||
}
|
||||
const element = view.$('[magnet]').toArray().find((magnet: any) => magnet.getAttribute('class').split(/\s+/).indexOf(className) >= 0);
|
||||
if (element) {
|
||||
return view.findMagnet($(element));
|
||||
}
|
||||
}
|
||||
|
||||
export function findMagnetByPort(view: dia.CellView, port: string): SVGElement | undefined {
|
||||
const element = view.$('[magnet]').toArray().find((magnet: HTMLElement) => magnet.getAttribute('port') === port);
|
||||
if (element) {
|
||||
return view.findMagnet($(element));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the metadata for a particular palette entry in a particular group.
|
||||
* @param name - name of the palette entry
|
||||
* @param group - group in which the palette entry should exist (e.g. sinks)
|
||||
* @return
|
||||
*/
|
||||
export function getMetadata(metamodel: Map<string, Map<string, ElementMetadata>>, name: string, group: string): ElementMetadata | undefined {
|
||||
const groupObj = metamodel && group ? metamodel.get(group) : undefined;
|
||||
if (name && groupObj && groupObj.get(name)) {
|
||||
return metamodel!.get(group)!.get(name);
|
||||
} else {
|
||||
return {
|
||||
name: name,
|
||||
group: group,
|
||||
unresolved: true,
|
||||
get: (property: string) => new Promise(resolve => resolve()),
|
||||
properties: () => Promise.resolve(new Map<string, PropertyMetadata>())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@ export namespace Properties {
|
||||
}
|
||||
|
||||
export interface Property {
|
||||
readonly id : string;
|
||||
readonly name : string;
|
||||
readonly type : string;
|
||||
readonly description? : string;
|
||||
readonly defaultValue? : any;
|
||||
value? : any;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly type?: string;
|
||||
readonly description?: string;
|
||||
readonly defaultValue?: any;
|
||||
value?: any;
|
||||
readonly valueOptions?: any[]
|
||||
readonly [propName : string] : any;
|
||||
readonly [propName: string]: any;
|
||||
}
|
||||
|
||||
export interface SelectOption {
|
||||
@@ -34,25 +34,25 @@ export namespace Properties {
|
||||
}
|
||||
|
||||
export interface ErrorData {
|
||||
id : string;
|
||||
message : string;
|
||||
id: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface Validation {
|
||||
validator?: ValidatorFn|ValidatorFn[]|null,
|
||||
asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null,
|
||||
errorData? : Array<ErrorData>;
|
||||
errorData?: Array<ErrorData>;
|
||||
}
|
||||
|
||||
export interface ControlModel<T> {
|
||||
readonly type : InputType;
|
||||
readonly id : string;
|
||||
value : T;
|
||||
readonly defaultValue : T;
|
||||
readonly name? : string;
|
||||
readonly description? : string;
|
||||
readonly property : Property
|
||||
readonly validation? : Validation;
|
||||
readonly type: InputType;
|
||||
readonly id: string;
|
||||
value: T;
|
||||
readonly defaultValue: T;
|
||||
readonly name?: string;
|
||||
readonly description?: string;
|
||||
readonly property: Property
|
||||
readonly validation?: Validation;
|
||||
}
|
||||
|
||||
export interface CodeControlModel<T> extends ControlModel<T> {
|
||||
@@ -61,7 +61,7 @@ export namespace Properties {
|
||||
|
||||
export class GenericControlModel<T> implements ControlModel<T> {
|
||||
|
||||
constructor(private _property : Property, public type : InputType, public validation? : Validation) {}
|
||||
constructor(private _property: Property, public type: InputType, public validation?: Validation) {}
|
||||
|
||||
get id() {
|
||||
return this.property.id;
|
||||
@@ -79,15 +79,15 @@ export namespace Properties {
|
||||
return this.property.defaultValue;
|
||||
}
|
||||
|
||||
get value() : T {
|
||||
get value(): T {
|
||||
return this.getValue();
|
||||
}
|
||||
|
||||
set value(value : T) {
|
||||
set value(value: T) {
|
||||
this.setValue(value);
|
||||
}
|
||||
|
||||
get property() : Property {
|
||||
get property(): Property {
|
||||
return this._property;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ export namespace Properties {
|
||||
|
||||
export class CheckBoxControlModel extends GenericControlModel<boolean> {
|
||||
|
||||
constructor(_property : Property, validation? : Validation) {
|
||||
constructor(_property: Property, validation?: Validation) {
|
||||
super(_property, InputType.CHECKBOX, validation);
|
||||
}
|
||||
|
||||
@@ -119,7 +119,9 @@ export namespace Properties {
|
||||
|
||||
export abstract class AbstractCodeControlModel extends GenericControlModel<string> implements CodeControlModel<string> {
|
||||
|
||||
constructor(_property : Property, private encode?: (s: string) => string, private decode?: (s: string) => string, validation? : Validation) {
|
||||
abstract language: string;
|
||||
|
||||
constructor(_property: Property, private encode?: (s: string) => string, private decode?: (s: string) => string, validation?: Validation) {
|
||||
super(_property, InputType.CODE, validation);
|
||||
}
|
||||
|
||||
@@ -140,14 +142,12 @@ export namespace Properties {
|
||||
}
|
||||
}
|
||||
|
||||
abstract language: string;
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class GenericCodeControlModel extends AbstractCodeControlModel {
|
||||
|
||||
constructor(_property : Property, public language: string, encode?: (s: string) => string, decode?: (s: string) => string, validation? : Validation) {
|
||||
constructor(_property: Property, public language: string, encode?: (s: string) => string, decode?: (s: string) => string, validation?: Validation) {
|
||||
super(_property, encode, decode, validation);
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ export namespace Properties {
|
||||
|
||||
constructor(_property: Properties.Property, private _languagePropertyName: string,
|
||||
private _groupModel: Properties.PropertiesGroupModel,
|
||||
encode?: (s: string) => string, decode?: (s: string) => string, validation? : Validation) {
|
||||
encode?: (s: string) => string, decode?: (s: string) => string, validation?: Validation) {
|
||||
super(_property, encode, decode, validation);
|
||||
}
|
||||
|
||||
@@ -170,7 +170,9 @@ export namespace Properties {
|
||||
|
||||
get languageControlModel(): Properties.ControlModel<any> {
|
||||
if (!this._langControlModel) {
|
||||
this._langControlModel = this._groupModel.getControlsModels().find(c => c.id === this._languagePropertyName);
|
||||
// Cast to Properties.ControlModel<any> from Properties.ControlModel<any> | undefined
|
||||
// Should not be undefined!
|
||||
this._langControlModel = <Properties.ControlModel<any>> this._groupModel.getControlsModels().find(c => c.id === this._languagePropertyName);
|
||||
}
|
||||
return this._langControlModel;
|
||||
}
|
||||
@@ -179,22 +181,22 @@ export namespace Properties {
|
||||
|
||||
export class GenericListControlModel extends GenericControlModel<string> {
|
||||
|
||||
constructor(property : Property, validation? : Validation) {
|
||||
constructor(property: Property, validation?: Validation) {
|
||||
super(property, InputType.TEXT, validation);
|
||||
}
|
||||
|
||||
get value() : string {
|
||||
get value(): string {
|
||||
return this.property.value ? this.property.value.join(', ') : '';
|
||||
}
|
||||
|
||||
set value(value : string) {
|
||||
set value(value: string) {
|
||||
this.property.value = value && value.trim() ? value.split(/\s*,\s*/) : undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class SelectControlModel extends GenericControlModel<any> {
|
||||
constructor(_property : Property, type : InputType, public options : Array<SelectOption>) {
|
||||
constructor(_property: Property, type: InputType, public options: Array<SelectOption>) {
|
||||
super(_property, type);
|
||||
if (_property.defaultValue === undefined) {
|
||||
options.unshift({
|
||||
@@ -212,18 +214,18 @@ export namespace Properties {
|
||||
|
||||
export class DefaultCellPropertiesSource implements PropertiesSource {
|
||||
|
||||
protected cell : dia.Cell;
|
||||
protected cell: dia.Cell;
|
||||
|
||||
constructor(cell : dia.Cell) {
|
||||
constructor(cell: dia.Cell) {
|
||||
this.cell = cell;
|
||||
}
|
||||
|
||||
getProperties() : Promise<Array<Property>> {
|
||||
let metadata : Flo.ElementMetadata = this.cell.attr('metadata');
|
||||
getProperties(): Promise<Array<Property>> {
|
||||
let metadata: Flo.ElementMetadata = this.cell.attr('metadata');
|
||||
return Promise.resolve(metadata.properties().then(propsMetadata => Array.from(propsMetadata.values()).map(m => this.createProperty(m))));
|
||||
}
|
||||
|
||||
protected createProperty(metadata : Flo.PropertyMetadata) : Property {
|
||||
protected createProperty(metadata: Flo.PropertyMetadata): Property {
|
||||
return {
|
||||
id: metadata.id,
|
||||
name: metadata.name,
|
||||
@@ -262,13 +264,13 @@ export namespace Properties {
|
||||
|
||||
protected propertiesSource: PropertiesSource;
|
||||
|
||||
protected controlModels : Array<ControlModel<any>>;
|
||||
protected controlModels: Array<ControlModel<any>>;
|
||||
|
||||
protected loading : boolean = true;
|
||||
protected loading = true;
|
||||
|
||||
protected _loadedSubject : Subject<boolean>;
|
||||
protected _loadedSubject: Subject<boolean>;
|
||||
|
||||
constructor(propertiesSource : PropertiesSource) {
|
||||
constructor(propertiesSource: PropertiesSource) {
|
||||
this.propertiesSource = propertiesSource;
|
||||
}
|
||||
|
||||
@@ -283,7 +285,7 @@ export namespace Properties {
|
||||
});
|
||||
}
|
||||
|
||||
get isLoading() : boolean {
|
||||
get isLoading(): boolean {
|
||||
return this.loading;
|
||||
}
|
||||
|
||||
@@ -295,11 +297,11 @@ export namespace Properties {
|
||||
return this.controlModels;
|
||||
}
|
||||
|
||||
protected createControlModel(property : Property) : ControlModel<any> {
|
||||
protected createControlModel(property: Property): ControlModel<any> {
|
||||
return new GenericControlModel(property, InputType.TEXT);
|
||||
}
|
||||
|
||||
public applyChanges() : void {
|
||||
public applyChanges(): void {
|
||||
if (this.loading) {
|
||||
return;
|
||||
}
|
||||
@@ -313,7 +315,7 @@ export namespace Properties {
|
||||
|
||||
export namespace Validators {
|
||||
|
||||
export function uniqueResource(service : (value : any) => Observable<any>, debounce : number): AsyncValidatorFn {
|
||||
export function uniqueResource(service: (value: any) => Observable<any>, debounce: number): AsyncValidatorFn {
|
||||
return (control: AbstractControl): Observable<ValidationErrors> => {
|
||||
return new Observable(obs => {
|
||||
if (control.valueChanges && control.value) {
|
||||
@@ -323,20 +325,20 @@ export namespace Properties {
|
||||
obs.next({uniqueResource: true});
|
||||
obs.complete();
|
||||
}, () => {
|
||||
obs.next(null);
|
||||
obs.next(undefined);
|
||||
obs.complete();
|
||||
})
|
||||
} else {
|
||||
obs.next(null);
|
||||
obs.next(undefined);
|
||||
obs.complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function noneOf(excluded : Array<any>) : ValidatorFn {
|
||||
return (control: AbstractControl) : {[key: string]: any} => {
|
||||
return excluded.find(e => e === control.value) ? {'noneOf': {value: control.value}} : null;
|
||||
export function noneOf(excluded: Array<any>): ValidatorFn {
|
||||
return (control: AbstractControl): {[key: string]: any} => {
|
||||
return excluded.find(e => e === control.value) ? {'noneOf': {value: control.value}} : {};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,18 +3,18 @@ import { Flo } from './flo-common';
|
||||
import EditorDescriptor = Flo.ViewerDescriptor;
|
||||
import * as _ from 'lodash';
|
||||
import * as _$ from 'jquery';
|
||||
const joint : any = Flo.joint;
|
||||
const $ : any = _$;
|
||||
const joint: any = Flo.joint;
|
||||
const $: any = _$;
|
||||
|
||||
const isChrome : boolean = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
|
||||
const isFF : boolean = navigator.userAgent.indexOf("Firefox") > 0;
|
||||
const isChrome: boolean = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
|
||||
const isFF: boolean = navigator.userAgent.indexOf('Firefox') > 0;
|
||||
|
||||
const IMAGE_W : number = 120;
|
||||
const IMAGE_H : number = 35;
|
||||
const IMAGE_W = 120;
|
||||
const IMAGE_H = 35;
|
||||
|
||||
const ERROR_MARKER_SIZE : dia.Size = {width: 16, height: 16};
|
||||
const ERROR_MARKER_SIZE: dia.Size = {width: 16, height: 16};
|
||||
|
||||
const HANDLE_SIZE : dia.Size = {width: 10, height: 10};
|
||||
const HANDLE_SIZE: dia.Size = {width: 10, height: 10};
|
||||
|
||||
joint.shapes.flo = {};
|
||||
|
||||
@@ -23,61 +23,67 @@ joint.shapes.flo.LINK_TYPE = 'sinspctr.Link';
|
||||
joint.shapes.flo.DECORATION_TYPE = 'decoration';
|
||||
joint.shapes.flo.HANDLE_TYPE = 'handle';
|
||||
|
||||
const HANDLE_ICON_MAP : Map<string, string>= new Map<string, string>();
|
||||
const HANDLE_ICON_MAP: Map<string, string> = new Map<string, string>();
|
||||
const REMOVE = 'remove';
|
||||
HANDLE_ICON_MAP.set(REMOVE, 'icons/delete.svg');
|
||||
|
||||
const DECORATION_ICON_MAP : Map<string, string>= new Map<string, string>();
|
||||
const DECORATION_ICON_MAP: Map<string, string> = new Map<string, string>();
|
||||
const ERROR = 'error';
|
||||
DECORATION_ICON_MAP.set(ERROR, 'icons/error.svg');
|
||||
|
||||
joint.util.filter.redscale = (args : Shapes.FilterOptions) => {
|
||||
joint.util.filter.redscale = (args: Shapes.FilterOptions) => {
|
||||
|
||||
let amount = _.isFinite(args.amount) ? args.amount : 1;
|
||||
let amount = Number.isFinite(args.amount) ? args.amount : 1;
|
||||
|
||||
return _.template('<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 ${d} ${e} ${f} ${g} 0 0 ${h} ${i} ${k} 0 0 0 0 0 1 0"/></filter>', <any>{
|
||||
a: 1 - 0.96 * amount,
|
||||
b: 0.95 * amount,
|
||||
c: 0.01 * amount,
|
||||
d: 0.3 * amount,
|
||||
e: 0.2 * amount,
|
||||
f: 1 - 0.9 * amount,
|
||||
g: 0.7 * amount,
|
||||
h: 0.05 * amount,
|
||||
i: 0.05 * amount,
|
||||
k: 1 - 0.1 * amount
|
||||
});
|
||||
return _.template(
|
||||
'<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 ${d} ${e} ${f} ${g} 0 0 ${h} ${i} ${k} 0 0 0 0 0 1 0"/></filter>',
|
||||
<any>{
|
||||
a: 1 - 0.96 * amount,
|
||||
b: 0.95 * amount,
|
||||
c: 0.01 * amount,
|
||||
d: 0.3 * amount,
|
||||
e: 0.2 * amount,
|
||||
f: 1 - 0.9 * amount,
|
||||
g: 0.7 * amount,
|
||||
h: 0.05 * amount,
|
||||
i: 0.05 * amount,
|
||||
k: 1 - 0.1 * amount
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
joint.util.filter.orangescale = (args : Shapes.FilterOptions) => {
|
||||
joint.util.filter.orangescale = (args: Shapes.FilterOptions) => {
|
||||
|
||||
let amount = _.isFinite(args.amount) ? args.amount : 1;
|
||||
let amount = Number.isFinite(args.amount) ? args.amount : 1;
|
||||
|
||||
return _.template('<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 ${d} ${e} ${f} ${g} 0 ${h} ${i} ${k} ${l} 0 0 0 0 0 1 0"/></filter>', <any>{
|
||||
a: 1.0 + 0.5 * amount,
|
||||
b: 1.4 * amount,
|
||||
c: 0.2 * amount,
|
||||
d: 0.3 * amount,
|
||||
e: 0.3 * amount,
|
||||
f: 1 + 0.05 * amount,
|
||||
g: 0.2 * amount,
|
||||
h: 0.15 * amount,
|
||||
i: 0.3 * amount,
|
||||
k: 0.3 * amount,
|
||||
l: 1 - 0.6 * amount
|
||||
});
|
||||
return _.template(
|
||||
'<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 ${d} ${e} ${f} ${g} 0 ${h} ${i} ${k} ${l} 0 0 0 0 0 1 0"/></filter>',
|
||||
<any>{
|
||||
a: 1.0 + 0.5 * amount,
|
||||
b: 1.4 * amount,
|
||||
c: 0.2 * amount,
|
||||
d: 0.3 * amount,
|
||||
e: 0.3 * amount,
|
||||
f: 1 + 0.05 * amount,
|
||||
g: 0.2 * amount,
|
||||
h: 0.15 * amount,
|
||||
i: 0.3 * amount,
|
||||
k: 0.3 * amount,
|
||||
l: 1 - 0.6 * amount
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
joint.shapes.flo.Node = joint.shapes.basic.Generic.extend({
|
||||
markup:
|
||||
'<g class="shape"><image class="image" /></g>'+
|
||||
'<g class="shape"><image class="image" /></g>' +
|
||||
'<rect class="border-white"/>' +
|
||||
'<rect class="border"/>' +
|
||||
'<rect class="box"/>'+
|
||||
'<text class="label"/>'+
|
||||
'<text class="label2"></text>'+
|
||||
'<rect class="input-port" />'+
|
||||
'<rect class="output-port"/>'+
|
||||
'<rect class="box"/>' +
|
||||
'<text class="label"/>' +
|
||||
'<text class="label2"></text>' +
|
||||
'<rect class="input-port" />' +
|
||||
'<rect class="output-port"/>' +
|
||||
'<rect class="output-port-cover"/>',
|
||||
|
||||
defaults: joint.util.deepSupplement({
|
||||
@@ -93,7 +99,7 @@ joint.shapes.flo.Node = joint.shapes.basic.Generic.extend({
|
||||
height: IMAGE_H,
|
||||
rx: 3,
|
||||
ry: 3,
|
||||
'fill-opacity':0, // see through
|
||||
'fill-opacity': 0, // see through
|
||||
stroke: '#eeeeee',
|
||||
'stroke-width': 0
|
||||
},
|
||||
@@ -103,7 +109,7 @@ joint.shapes.flo.Node = joint.shapes.basic.Generic.extend({
|
||||
height: IMAGE_H,
|
||||
rx: 3,
|
||||
ry: 3,
|
||||
//'fill-opacity':0, // see through
|
||||
//'fill-opacity': 0, // see through
|
||||
stroke: '#6db33f',
|
||||
fill: '#eeeeee',
|
||||
'stroke-width': 1
|
||||
@@ -113,7 +119,7 @@ joint.shapes.flo.Node = joint.shapes.basic.Generic.extend({
|
||||
height: 8, width: 8,
|
||||
magnet: true,
|
||||
fill: '#eeeeee',
|
||||
transform: 'translate(' + -4 + ',' + ((IMAGE_H/2)-4) + ')',
|
||||
transform: 'translate(' + -4 + ',' + ((IMAGE_H / 2 ) - 4) + ')',
|
||||
stroke: '#34302d',
|
||||
'stroke-width': 1
|
||||
},
|
||||
@@ -122,7 +128,7 @@ joint.shapes.flo.Node = joint.shapes.basic.Generic.extend({
|
||||
height: 8, width: 8,
|
||||
magnet: true,
|
||||
fill: '#eeeeee',
|
||||
transform: 'translate(' + (IMAGE_W-4) + ',' + ((IMAGE_H/2)-4) + ')',
|
||||
transform: 'translate(' + (IMAGE_W - 4) + ',' + ((IMAGE_H / 2) - 4) + ')',
|
||||
stroke: '#34302d',
|
||||
'stroke-width': 1
|
||||
},
|
||||
@@ -169,9 +175,9 @@ joint.shapes.flo.Link = joint.dia.Link.extend({
|
||||
// '.': { filter: { name: 'dropShadow', args: { dx: 1, dy: 1, blur: 2 } } },
|
||||
// '.connection': { 'stroke-width': 10, 'stroke-linecap': 'round' },
|
||||
// This means: moveto 10 0, lineto 0 5, lineto, 10 10 closepath(z)
|
||||
// '.marker-target': { d: 'M 5 0 L 0 7 L 5 14 z', stroke: '#34302d','stroke-width' : 1},
|
||||
// '.marker-target': { d: 'M 14 2 L 9,2 L9,0 L 0,7 L 9,14 L 9,12 L 14,12 z', 'stroke-width' : 1, fill: '#34302d', stroke: '#34302d'},
|
||||
// '.marker-source': {d: 'M 5 0 L 5,10 L 0,10 L 0,0 z', 'stroke-width' : 0, fill: '#34302d', stroke: '#34302d'},
|
||||
// '.marker-target': { d: 'M 5 0 L 0 7 L 5 14 z', stroke: '#34302d','stroke-width': 1},
|
||||
// '.marker-target': { d: 'M 14 2 L 9,2 L9,0 L 0,7 L 9,14 L 9,12 L 14,12 z', 'stroke-width': 1, fill: '#34302d', stroke: '#34302d'},
|
||||
// '.marker-source': {d: 'M 5 0 L 5,10 L 0,10 L 0,0 z', 'stroke-width': 0, fill: '#34302d', stroke: '#34302d'},
|
||||
// '.marker-target': { stroke: '#E74C3C', fill: '#E74C3C', d: 'M 10 0 L 0 5 L 10 10 z' },
|
||||
'.marker-arrowheads': { display: 'none' },
|
||||
'.tool-options': { display: 'none' }
|
||||
@@ -227,7 +233,9 @@ joint.shapes.flo.ElementView = joint.dia.ElementView.extend({
|
||||
|
||||
dragMagnetStart: function(evt: any, x: number, y: number) {
|
||||
|
||||
if (!this.can('addLinkFromMagnet')) return;
|
||||
if (!this.can('addLinkFromMagnet')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.model.startBatch('add-link');
|
||||
|
||||
@@ -271,7 +279,7 @@ joint.shapes.flo.ElementView = joint.dia.ElementView.extend({
|
||||
this.paper.delegateDragEvents(this, evt.data);
|
||||
},
|
||||
|
||||
// pointerdown: function(evt : any, x : number, y : number) {
|
||||
// pointerdown: function(evt: any, x: number, y: number) {
|
||||
// // this.canShowTooltip = false;
|
||||
// // this.hideTooltip();
|
||||
// this.beingDragged = false;
|
||||
@@ -317,7 +325,7 @@ joint.shapes.flo.ElementView = joint.dia.ElementView.extend({
|
||||
// joint.dia.CellView.prototype.pointerdown.apply(this, arguments);
|
||||
// }
|
||||
// },
|
||||
drag: function(evt : MouseEvent, x : number, y : number) {
|
||||
drag: function(evt: MouseEvent, x: number, y: number) {
|
||||
let interactive = _.isFunction(this.options.interactive) ? this.options.interactive(this, 'pointermove') :
|
||||
this.options.interactive;
|
||||
if (interactive !== false) {
|
||||
@@ -325,13 +333,13 @@ joint.shapes.flo.ElementView = joint.dia.ElementView.extend({
|
||||
}
|
||||
joint.dia.ElementView.prototype.drag.apply(this, arguments);
|
||||
},
|
||||
dragEnd: function(evt : MouseEvent, x : number, y : number) { // jshint ignore:line
|
||||
dragEnd: function(evt: MouseEvent, x: number, y: number) { // jshint ignore:line
|
||||
this.paper.trigger('dragging-node-over-canvas', {type: Flo.DnDEventType.DROP, view: this, event: evt});
|
||||
joint.dia.ElementView.prototype.dragEnd.apply(this, arguments);
|
||||
},
|
||||
// events: {
|
||||
// // Tooltips on the elements in the graph
|
||||
// 'mouseenter': function(evt : MouseEvent) {
|
||||
// 'mouseenter': function(evt: MouseEvent) {
|
||||
// if (this.canShowTooltip) {
|
||||
// this.showTooltip(evt.pageX, evt.pageY);
|
||||
// }
|
||||
@@ -350,21 +358,21 @@ joint.shapes.flo.ElementView = joint.dia.ElementView.extend({
|
||||
// if (isChrome || isFF) {
|
||||
// this.model.set('z', this._tempZorder);
|
||||
// var z = this._tempZorder;
|
||||
// this.model.getEmbeddedCells({breadthFirst: true}).forEach(function(cell : dia.Cell) {
|
||||
// this.model.getEmbeddedCells({breadthFirst: true}).forEach(function(cell: dia.Cell) {
|
||||
// cell.set('z', ++z);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// 'mousemove': function(evt : MouseEvent) {
|
||||
// 'mousemove': function(evt: MouseEvent) {
|
||||
// this.moveTooltip(evt.pageX, evt.pageY);
|
||||
// }
|
||||
// },
|
||||
// showTooltip: function(x : number, y : number) {
|
||||
// showTooltip: function(x: number, y: number) {
|
||||
// var mousex = x + 10;
|
||||
// var mousey = y + 10;
|
||||
//
|
||||
// var nodeTooltip : HTMLElement;
|
||||
// var nodeTooltip: HTMLElement;
|
||||
// if (this.model instanceof joint.dia.Element && this.model.attr('metadata')) {
|
||||
// nodeTooltip = document.createElement('div');
|
||||
// $(nodeTooltip).addClass('node-tooltip');
|
||||
@@ -391,9 +399,9 @@ joint.shapes.flo.ElementView = joint.dia.ElementView.extend({
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// model.attr('metadata').get('description').then(function(description : string) {
|
||||
// model.attr('metadata').get('description').then(function(description: string) {
|
||||
// $(nodeDescription).text(description);
|
||||
// }, function(error : any) {
|
||||
// }, function(error: any) {
|
||||
// if (error) {
|
||||
// console.error(error);
|
||||
// }
|
||||
@@ -401,7 +409,7 @@ joint.shapes.flo.ElementView = joint.dia.ElementView.extend({
|
||||
//
|
||||
// // defaultValue
|
||||
// if (!model.attr('metadata/metadata/hide-tooltip-options')) {
|
||||
// model.attr('metadata').get('properties').then(function(metaProps : any) {
|
||||
// model.attr('metadata').get('properties').then(function(metaProps: any) {
|
||||
// var props = model.attr('props'); // array of {'name':,'value':}
|
||||
// if (metaProps && props) {
|
||||
// Object.keys(props).sort().forEach(function(propertyName) {
|
||||
@@ -427,7 +435,7 @@ joint.shapes.flo.ElementView = joint.dia.ElementView.extend({
|
||||
// // $(nodeTooltip).append(optionRow);
|
||||
// });
|
||||
// }
|
||||
// }, function(error : any) {
|
||||
// }, function(error: any) {
|
||||
// if (error) {
|
||||
// console.error(error);
|
||||
// }
|
||||
@@ -458,7 +466,7 @@ joint.shapes.flo.ElementView = joint.dia.ElementView.extend({
|
||||
// $('.node-tooltip').remove();
|
||||
// $('.error-tooltip').remove();
|
||||
// },
|
||||
// moveTooltip: function(x : number, y : number) {
|
||||
// moveTooltip: function(x: number, y: number) {
|
||||
// $('.node-tooltip')
|
||||
// .css({ top: y + 10, left: x + 10 });
|
||||
// $('.error-tooltip')
|
||||
@@ -500,37 +508,37 @@ export namespace Constants {
|
||||
export namespace Shapes {
|
||||
|
||||
export interface CreationParams extends Flo.CreationParams {
|
||||
renderer? : Flo.Renderer;
|
||||
paper? : dia.Paper;
|
||||
graph? : dia.Graph;
|
||||
renderer?: Flo.Renderer;
|
||||
paper?: dia.Paper;
|
||||
graph?: dia.Graph;
|
||||
}
|
||||
|
||||
export interface ElementCreationParams extends CreationParams {
|
||||
position? : dia.Point;
|
||||
position?: dia.Point;
|
||||
}
|
||||
|
||||
export interface LinkCreationParams extends CreationParams {
|
||||
source : Flo.LinkEnd;
|
||||
target : Flo.LinkEnd;
|
||||
source: Flo.LinkEnd;
|
||||
target: Flo.LinkEnd;
|
||||
}
|
||||
|
||||
export interface EmbeddedChildCreationParams extends CreationParams {
|
||||
parent : dia.Cell;
|
||||
position? : dia.Point;
|
||||
parent: dia.Cell;
|
||||
position?: dia.Point;
|
||||
}
|
||||
|
||||
export interface DecorationCreationParams extends EmbeddedChildCreationParams {
|
||||
kind : string;
|
||||
messages : Array<string>;
|
||||
kind: string;
|
||||
messages: Array<string>;
|
||||
}
|
||||
|
||||
export interface HandleCreationParams extends EmbeddedChildCreationParams {
|
||||
kind : string;
|
||||
kind: string;
|
||||
}
|
||||
|
||||
export interface FilterOptions {
|
||||
amount : number;
|
||||
[propName : string] : any;
|
||||
amount: number;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
|
||||
@@ -539,15 +547,15 @@ export namespace Shapes {
|
||||
/**
|
||||
* Create a JointJS node that embeds extra metadata (properties).
|
||||
*/
|
||||
static createNode(params : ElementCreationParams) : dia.Element {
|
||||
let renderer : Flo.Renderer = params.renderer;
|
||||
let paper : dia.Paper = params.paper;
|
||||
let metadata : Flo.ElementMetadata = params.metadata;
|
||||
let position : dia.Point = params.position;
|
||||
let props : Map<string, any> = params.props;
|
||||
let graph : dia.Graph = params.graph || (params.paper ? params.paper.model : undefined);
|
||||
static createNode(params: ElementCreationParams): dia.Element {
|
||||
let renderer = params.renderer;
|
||||
let paper = params.paper;
|
||||
let metadata = params.metadata;
|
||||
let position = params.position;
|
||||
let props = params.props;
|
||||
let graph = params.graph || (params.paper ? params.paper.model : undefined);
|
||||
|
||||
let node : dia.Element;
|
||||
let node: dia.Element;
|
||||
if (!position) {
|
||||
position = {x: 0, y: 0};
|
||||
}
|
||||
@@ -556,21 +564,23 @@ export namespace Shapes {
|
||||
node = renderer.createNode(metadata, props);
|
||||
} else {
|
||||
node = new joint.shapes.flo.Node();
|
||||
node.attr('.label/text', metadata.name);
|
||||
if (metadata) {
|
||||
node.attr('.label/text', metadata.name);
|
||||
}
|
||||
}
|
||||
node.set('type', joint.shapes.flo.NODE_TYPE);
|
||||
if (position) {
|
||||
node.set('position', position);
|
||||
}
|
||||
if (props) {
|
||||
Array.from(props.keys()).forEach(key => node.attr(`props/${key}`, props.get(key)));
|
||||
Array.from(props.keys()).forEach(key => node.attr(`props/${key}`, props!.get(key)));
|
||||
}
|
||||
node.attr('metadata', metadata);
|
||||
if (graph) {
|
||||
graph.addCell(node);
|
||||
}
|
||||
if (renderer && _.isFunction(renderer.initializeNewNode)) {
|
||||
let descriptor : Flo.ViewerDescriptor = {
|
||||
let descriptor: Flo.ViewerDescriptor = {
|
||||
paper: paper,
|
||||
graph: graph
|
||||
};
|
||||
@@ -579,16 +589,16 @@ export namespace Shapes {
|
||||
return node;
|
||||
}
|
||||
|
||||
static createLink(params : LinkCreationParams) : dia.Link {
|
||||
let renderer : Flo.Renderer = params.renderer;
|
||||
let paper : dia.Paper = params.paper;
|
||||
let metadata : Flo.ElementMetadata = params.metadata;
|
||||
static createLink(params: LinkCreationParams): dia.Link {
|
||||
let renderer = params.renderer;
|
||||
let paper = params.paper;
|
||||
let metadata = params.metadata;
|
||||
let source = params.source;
|
||||
let target = params.target;
|
||||
let props : Map<string, any> = params.props;
|
||||
let graph : dia.Graph= params.graph || (params.paper ? params.paper.model : undefined);
|
||||
let props = params.props;
|
||||
let graph = params.graph || (params.paper ? params.paper.model : undefined);
|
||||
|
||||
let link : dia.Link;
|
||||
let link: dia.Link;
|
||||
if (renderer && _.isFunction(renderer.createLink)) {
|
||||
link = renderer.createLink(source, target, metadata, props);
|
||||
} else {
|
||||
@@ -605,13 +615,13 @@ export namespace Shapes {
|
||||
link.attr('metadata', metadata);
|
||||
}
|
||||
if (props) {
|
||||
Array.from(props.keys()).forEach(key => link.attr(`props/${key}`, props.get(key)));
|
||||
Array.from(props.keys()).forEach(key => link.attr(`props/${key}`, props!.get(key)));
|
||||
}
|
||||
if (graph) {
|
||||
graph.addCell(link);
|
||||
}
|
||||
if (renderer && _.isFunction(renderer.initializeNewLink)) {
|
||||
let descriptor : Flo.ViewerDescriptor = {
|
||||
let descriptor: Flo.ViewerDescriptor = {
|
||||
paper: paper,
|
||||
graph: graph
|
||||
};
|
||||
@@ -622,25 +632,25 @@ export namespace Shapes {
|
||||
return link;
|
||||
}
|
||||
|
||||
static createDecoration(params : DecorationCreationParams) : dia.Element {
|
||||
let renderer : Flo.Renderer = params.renderer;
|
||||
let paper : dia.Paper = params.paper;
|
||||
let parent : dia.Cell = params.parent;
|
||||
let kind : string = params.kind;
|
||||
let messages : Array<string> = params.messages;
|
||||
let location : dia.Point = params.position;
|
||||
let graph : dia.Graph = params.graph || (params.paper ? params.paper.model : undefined);
|
||||
static createDecoration(params: DecorationCreationParams): dia.Element {
|
||||
let renderer = params.renderer;
|
||||
let paper = params.paper;
|
||||
let parent = params.parent;
|
||||
let kind = params.kind;
|
||||
let messages = params.messages;
|
||||
let location = params.position;
|
||||
let graph = params.graph || (params.paper ? params.paper.model : undefined);
|
||||
|
||||
if (!location) {
|
||||
location = {x: 0, y: 0};
|
||||
}
|
||||
let decoration : dia.Element;
|
||||
let decoration: dia.Element;
|
||||
if (renderer && _.isFunction(renderer.createDecoration)) {
|
||||
decoration = renderer.createDecoration(kind, parent);
|
||||
} else {
|
||||
decoration = new joint.shapes.flo.ErrorDecoration({
|
||||
attrs: {
|
||||
image: { 'xlink:href': DECORATION_ICON_MAP[kind] },
|
||||
image: { 'xlink:href': DECORATION_ICON_MAP.get(kind) },
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -656,7 +666,7 @@ export namespace Shapes {
|
||||
}
|
||||
parent.embed(decoration);
|
||||
if (renderer && _.isFunction(renderer.initializeNewDecoration)) {
|
||||
let descriptor : Flo.ViewerDescriptor = {
|
||||
let descriptor: Flo.ViewerDescriptor = {
|
||||
paper: paper,
|
||||
graph: graph
|
||||
};
|
||||
@@ -665,15 +675,15 @@ export namespace Shapes {
|
||||
return decoration;
|
||||
}
|
||||
|
||||
static createHandle(params : HandleCreationParams) : dia.Element {
|
||||
let renderer : Flo.Renderer = params.renderer;
|
||||
let paper : dia.Paper = params.paper;
|
||||
let parent : dia.Cell = params.parent;
|
||||
let kind : string = params.kind;
|
||||
let location : dia.Point = params.position;
|
||||
let graph : dia.Graph = params.graph || (params.paper ? params.paper.model : undefined);
|
||||
static createHandle(params: HandleCreationParams): dia.Element {
|
||||
let renderer = params.renderer;
|
||||
let paper = params.paper;
|
||||
let parent = params.parent;
|
||||
let kind = params.kind;
|
||||
let location = params.position;
|
||||
let graph = params.graph || (params.paper ? params.paper.model : undefined);
|
||||
|
||||
let handle : dia.Element;
|
||||
let handle: dia.Element;
|
||||
if (!location) {
|
||||
location = {x: 0, y: 0};
|
||||
}
|
||||
@@ -684,7 +694,7 @@ export namespace Shapes {
|
||||
size: HANDLE_SIZE,
|
||||
attrs: {
|
||||
'image': {
|
||||
'xlink:href': HANDLE_ICON_MAP[kind]
|
||||
'xlink:href': HANDLE_ICON_MAP.get(kind)
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -700,7 +710,7 @@ export namespace Shapes {
|
||||
}
|
||||
parent.embed(handle);
|
||||
if (renderer && _.isFunction(renderer.initializeNewHandle)) {
|
||||
let descriptor : Flo.ViewerDescriptor = {
|
||||
let descriptor: Flo.ViewerDescriptor = {
|
||||
paper: paper,
|
||||
graph: graph
|
||||
};
|
||||
@@ -1,244 +0,0 @@
|
||||
import { dia, g } from 'jointjs';
|
||||
import { Observable } from "rxjs";
|
||||
import * as _joint from 'jointjs';
|
||||
import * as _$ from 'jquery';
|
||||
|
||||
const $ = _$;
|
||||
|
||||
export namespace Flo {
|
||||
|
||||
export const joint : any = _joint;
|
||||
|
||||
export enum DnDEventType {
|
||||
DRAG,
|
||||
DROP
|
||||
}
|
||||
|
||||
export interface DnDEvent {
|
||||
type : DnDEventType;
|
||||
view : dia.CellView;
|
||||
event : MouseEvent;
|
||||
}
|
||||
|
||||
export interface PropertyMetadata {
|
||||
readonly id : string;
|
||||
readonly name : string;
|
||||
readonly description? : string;
|
||||
readonly defaultValue? : any;
|
||||
readonly type? : string;
|
||||
readonly [propName : string] : any;
|
||||
}
|
||||
|
||||
export interface ExtraMetadata {
|
||||
readonly titleProperty? : string;
|
||||
readonly noEditableProps? : boolean;
|
||||
readonly noPaletteEntry? : boolean;
|
||||
readonly unselectable? : boolean;
|
||||
readonly [propName : string] : any;
|
||||
|
||||
readonly allowAdditionalProperties? : boolean; //TODO: Verify it is still needed
|
||||
}
|
||||
|
||||
export interface ElementMetadata {
|
||||
readonly name : string;
|
||||
readonly group : string;
|
||||
description?() : Promise<string>;
|
||||
get(property : String) : Promise<PropertyMetadata>;
|
||||
properties() : Promise<Map<string, PropertyMetadata>>;
|
||||
readonly metadata? : ExtraMetadata;
|
||||
readonly [propName : string] : any;
|
||||
}
|
||||
|
||||
export interface ViewerDescriptor {
|
||||
readonly graph : dia.Graph;
|
||||
readonly paper? : dia.Paper;
|
||||
}
|
||||
|
||||
export interface MetamodelListener {
|
||||
metadataError(data : any) : void;
|
||||
metadataAboutToChange() : void;
|
||||
metadataChanged() : void;
|
||||
}
|
||||
|
||||
export interface Metamodel {
|
||||
textToGraph(flo : EditorContext, dsl : string) : Promise<any>;
|
||||
graphToText(flo : EditorContext) : Promise<string>;
|
||||
load() : Promise<Map<string, Map<string, ElementMetadata>>>;
|
||||
groups() : Array<string>;
|
||||
|
||||
refresh?() : Promise<Map<string, Map<string, ElementMetadata>>>;
|
||||
subscribe?(listener : MetamodelListener) : void;
|
||||
unsubscribe?(listener : MetamodelListener) : void;
|
||||
isValidPropertyValue?(element : dia.Element, property : string, value : any) : boolean;
|
||||
}
|
||||
|
||||
export interface CreationParams {
|
||||
metadata? : ElementMetadata;
|
||||
props? : Map<string, any>;
|
||||
}
|
||||
|
||||
export interface ElementCreationParams extends CreationParams {
|
||||
position? : dia.Point;
|
||||
}
|
||||
|
||||
export interface LinkCreationParams extends CreationParams {
|
||||
source : string;
|
||||
target : string;
|
||||
}
|
||||
|
||||
export interface EmbeddedChildCreationParams extends CreationParams {
|
||||
parent : dia.Cell;
|
||||
position? : dia.Point;
|
||||
}
|
||||
|
||||
export interface DecorationCreationParams extends EmbeddedChildCreationParams {
|
||||
kind : string;
|
||||
messages : Array<string>;
|
||||
}
|
||||
|
||||
export interface HandleCreationParams extends EmbeddedChildCreationParams {
|
||||
kind : string;
|
||||
}
|
||||
|
||||
export interface Renderer {
|
||||
createNode?(metadata : ElementMetadata, props? : Map<string, any>) : dia.Element;
|
||||
createLink?(source : LinkEnd, target : LinkEnd, metadata? : ElementMetadata, props? : Map<string, any>) : dia.Link;
|
||||
createHandle?(kind : string, parent : dia.Cell) : dia.Element;
|
||||
createDecoration?(kind : string, parent : dia.Cell) : dia.Element;
|
||||
initializeNewNode?(node : dia.Element, viewerDescriptor : ViewerDescriptor) : void;
|
||||
initializeNewLink?(link : dia.Link, viewerDescriptor : ViewerDescriptor) : void;
|
||||
initializeNewHandle?(handle : dia.Element, viewerDescriptor : ViewerDescriptor) : void;
|
||||
initializeNewDecoration?(decoration : dia.Element, viewerDescriptor : ViewerDescriptor) : void;
|
||||
getNodeView?() : dia.ElementView;
|
||||
getLinkView?() : dia.LinkView;
|
||||
layout?(paper : dia.Paper) : Promise<any>;
|
||||
handleLinkEvent?(context : EditorContext, event : string, link : dia.Link) : void;
|
||||
isSemanticProperty?(propertyPath : string, element : dia.Cell) : boolean;
|
||||
refreshVisuals?(cell : dia.Cell, propertyPath : string, paper : dia.Paper) : void;
|
||||
getLinkAnchorPoint?(linkView : dia.LinkView, view : dia.ElementView, port : SVGElement, reference : dia.Point) : dia.Point;
|
||||
}
|
||||
|
||||
export interface EditorContext {
|
||||
zoomPercent : number;
|
||||
gridSize : number;
|
||||
readOnlyCanvas : boolean;
|
||||
selection : dia.CellView;
|
||||
graphToTextSync : boolean;
|
||||
noPalette : boolean;
|
||||
setDsl(dsl : string) : void;
|
||||
updateGraph() : Promise<any>;
|
||||
updateText() : Promise<any>;
|
||||
performLayout() : Promise<void>;
|
||||
clearGraph() : void;
|
||||
getGraph() : dia.Graph;
|
||||
getPaper() : dia.Paper;
|
||||
getMinZoom() : number;
|
||||
getMaxZoom() : number;
|
||||
getZoomStep() : number;
|
||||
fitToPage() : void;
|
||||
createNode(metadata : ElementMetadata, props? : Map<string, any>, position? : dia.Point) : dia.Element;
|
||||
createLink(source : LinkEnd, target : LinkEnd, metadata? : ElementMetadata, props? : Map<string, any>) : dia.Link;
|
||||
deleteSelectedNode() : void;
|
||||
readonly textToGraphConversionObservable: Observable<void>;
|
||||
readonly graphToTextConversionObservable: Observable<void>;
|
||||
readonly paletteReady: Observable<boolean>;
|
||||
[propName : string] : any;
|
||||
}
|
||||
|
||||
export interface LinkEndDescriptor {
|
||||
view : dia.CellView;
|
||||
cssClassSelector? : string;
|
||||
}
|
||||
|
||||
export interface DnDDescriptor {
|
||||
sourceComponent? : string;
|
||||
range?: number;
|
||||
source? : LinkEndDescriptor;
|
||||
target? : LinkEndDescriptor;
|
||||
}
|
||||
|
||||
export interface LinkEnd {
|
||||
id : string | number;
|
||||
selector? : string;
|
||||
port? : string;
|
||||
}
|
||||
|
||||
export enum Severity {
|
||||
Error,
|
||||
Warning
|
||||
}
|
||||
|
||||
export interface Marker {
|
||||
severity : Severity;
|
||||
message : string;
|
||||
range? : Range;
|
||||
}
|
||||
|
||||
export interface Position {
|
||||
ch: number;
|
||||
line: number;
|
||||
}
|
||||
|
||||
export interface Range {
|
||||
start: Position;
|
||||
end: Position;
|
||||
}
|
||||
|
||||
export interface Editor {
|
||||
interactive? : ((cellView: dia.CellView, event: string) => boolean) | boolean | dia.CellView.InteractivityOptions;
|
||||
allowLinkVertexEdit? : boolean;
|
||||
highlighting? : any;
|
||||
createHandles?(context : EditorContext, createHandle : (owner : dia.CellView, kind : string, action : () => void, location : dia.Point) => void, owner : dia.CellView) : void;
|
||||
validatePort?(context : EditorContext, view : dia.ElementView, magnet : SVGElement) : boolean;
|
||||
validateLink?(context : EditorContext, cellViewS : dia.ElementView, portS : SVGElement, cellViewT : dia.ElementView, portT : SVGElement, isSource : boolean, linkView : dia.LinkView) : boolean;
|
||||
calculateDragDescriptor?(context : EditorContext, draggedView : dia.CellView, targetUnderMouse : dia.CellView, coordinate : g.Point, sourceComponent : string) : DnDDescriptor;
|
||||
handleNodeDropping?(context : EditorContext, dragDescriptor : DnDDescriptor) : void;
|
||||
showDragFeedback?(context : EditorContext, dragDescriptor : DnDDescriptor) : void;
|
||||
hideDragFeedback?(context : EditorContext, dragDescriptor : DnDDescriptor) : void;
|
||||
validate?(graph : dia.Graph, dsl: string, flo: EditorContext) : Promise<Map<string | number, Array<Marker>>>;
|
||||
preDelete?(context : EditorContext, deletedElement : dia.Cell) : void;
|
||||
setDefaultContent?(editorContext : EditorContext, data : Map<string, Map<string, ElementMetadata>>) : void;
|
||||
}
|
||||
|
||||
export function findMagnetByClass(view : dia.CellView, className : string) : SVGElement {
|
||||
if (className && className.startsWith('.')) {
|
||||
className = className.substr(1);
|
||||
}
|
||||
const element = view.$('[magnet]').toArray().find((magnet : any) => magnet.getAttribute('class').split(/\s+/).indexOf(className) >= 0);
|
||||
if (element) {
|
||||
return view.findMagnet($(element));
|
||||
}
|
||||
}
|
||||
|
||||
export function findMagnetByPort(view : dia.CellView, port : string) : SVGElement {
|
||||
const element = view.$('[magnet]').toArray().find((magnet : HTMLElement) => magnet.getAttribute('port') === port);
|
||||
if (element) {
|
||||
return view.findMagnet($(element));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the metadata for a particular palette entry in a particular group.
|
||||
* @param name - name of the palette entry
|
||||
* @param group - group in which the palette entry should exist (e.g. sinks)
|
||||
* @return
|
||||
*/
|
||||
export function getMetadata(metamodel : Map<string, Map<string, ElementMetadata>>, name : string, group : string) : ElementMetadata {
|
||||
if (name && group && metamodel.get(group) && metamodel.get(group).get(name)) {
|
||||
return metamodel.get(group).get(name);
|
||||
} else {
|
||||
return {
|
||||
name: name,
|
||||
group: group,
|
||||
unresolved: true,
|
||||
get: (property : string) => new Promise(resolve => resolve()),
|
||||
properties: () => Promise.resolve(new Map<string, PropertyMetadata>())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"outDir": "../../out-tsc/lib-es5/",
|
||||
"baseUrl": "",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"./index.ts",
|
||||
"./typings.d.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": true,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "spring-flo.js",
|
||||
"flatModuleId": "spring-flo",
|
||||
"genDir": "../../out-tsc/lib-gen-dir/"
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/lib-es2015/",
|
||||
"target": "es2015",
|
||||
"rootDir": "./",
|
||||
"baseUrl": "",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"./index.ts",
|
||||
"./typings.d.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": true,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "spring-flo.js",
|
||||
"flatModuleId": "spring-flo",
|
||||
"genDir": "../../out-tsc/lib-gen-dir/"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "",
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"emitDecoratorMetadata": true
|
||||
}
|
||||
}
|
||||
1
src/lib/typings.d.ts
vendored
1
src/lib/typings.d.ts
vendored
@@ -1 +0,0 @@
|
||||
// You can add project typings here.
|
||||
45
tsconfig-build.json
Normal file
45
tsconfig-build.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": "./lib",
|
||||
"baseUrl": "./lib",
|
||||
"paths": {
|
||||
"@angular/*": [
|
||||
"../../node_modules/@angular/*"
|
||||
],
|
||||
"@rxjs/*": [
|
||||
"../../node_modules/rxjs/*"
|
||||
]
|
||||
},
|
||||
"outDir": "../out-tsc",
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"moduleResolution": "node",
|
||||
"module": "es2015",
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
"types": [],
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"importHelpers": true,
|
||||
"strictNullChecks": false
|
||||
},
|
||||
"include": [
|
||||
"./lib/**/*",
|
||||
"../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"enableResourceInlining": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": true,
|
||||
"flatModuleOutFile": "./spring-flo.js",
|
||||
"flatModuleId": "spring-flo"
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
]
|
||||
],
|
||||
"strictNullChecks":false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"rules": {
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
false,
|
||||
"check-space"
|
||||
],
|
||||
"curly": true,
|
||||
@@ -14,7 +14,7 @@
|
||||
],
|
||||
"label-position": true,
|
||||
"max-line-length": [
|
||||
true,
|
||||
false,
|
||||
140
|
||||
],
|
||||
"member-access": false,
|
||||
|
||||
Reference in New Issue
Block a user