diff --git a/build.js b/build.js index 168c6b1..bd2760f 100644 --- a/build.js +++ b/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); - } -} diff --git a/inline-resources.js b/inline-resources.js deleted file mode 100644 index d44f46b..0000000 --- a/inline-resources.js +++ /dev/null @@ -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]); -} diff --git a/package.json b/package.json index c060483..328b6df 100644 --- a/package.json +++ b/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" ] } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..601eba1 --- /dev/null +++ b/rollup.config.js @@ -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' } + } +} diff --git a/rollup.es.config.js b/rollup.es.config.js new file mode 100644 index 0000000..0fae96a --- /dev/null +++ b/rollup.es.config.js @@ -0,0 +1,12 @@ +import sourcemaps from 'rollup-plugin-sourcemaps'; + +export default { + output: { + format: 'es', + sourcemap: true + }, + plugins: [ + sourcemaps() + ], + onwarn: () => { return } +} diff --git a/src/lib/src/code-editor/code-editor.component.html b/src/lib/code-editor/code-editor.component.html similarity index 100% rename from src/lib/src/code-editor/code-editor.component.html rename to src/lib/code-editor/code-editor.component.html diff --git a/src/lib/src/code-editor/code-editor.component.scss b/src/lib/code-editor/code-editor.component.scss similarity index 100% rename from src/lib/src/code-editor/code-editor.component.scss rename to src/lib/code-editor/code-editor.component.scss diff --git a/src/lib/src/code-editor/code-editor.component.ts b/src/lib/code-editor/code-editor.component.ts similarity index 90% rename from src/lib/src/code-editor/code-editor.component.ts rename to src/lib/code-editor/code-editor.component.ts index d1a2192..32aad76 100644 --- a/src/lib/src/code-editor/code-editor.component.ts +++ b/src/lib/code-editor/code-editor.component.ts @@ -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 = (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'}, diff --git a/src/lib/src/directives/resizer.ts b/src/lib/directives/resizer.ts similarity index 87% rename from src/lib/src/directives/resizer.ts rename to src/lib/directives/resizer.ts index df61a6c..035a761 100644 --- a/src/lib/src/directives/resizer.ts +++ b/src/lib/directives/resizer.ts @@ -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(); + 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(); - @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; } diff --git a/src/lib/src/dsl-editor/dsl-editor.component.css b/src/lib/dsl-editor/dsl-editor.component.css similarity index 100% rename from src/lib/src/dsl-editor/dsl-editor.component.css rename to src/lib/dsl-editor/dsl-editor.component.css diff --git a/src/lib/src/dsl-editor/dsl-editor.component.html b/src/lib/dsl-editor/dsl-editor.component.html similarity index 100% rename from src/lib/src/dsl-editor/dsl-editor.component.html rename to src/lib/dsl-editor/dsl-editor.component.html diff --git a/src/lib/src/dsl-editor/dsl-editor.component.ts b/src/lib/dsl-editor/dsl-editor.component.ts similarity index 79% rename from src/lib/src/dsl-editor/dsl-editor.component.ts rename to src/lib/dsl-editor/dsl-editor.component.ts index bfe29ac..c869695 100644 --- a/src/lib/src/dsl-editor/dsl-editor.component.ts +++ b/src/lib/dsl-editor/dsl-editor.component.ts @@ -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(); @@ -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 = (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'}, diff --git a/src/lib/src/editor/editor-utils.ts b/src/lib/editor/editor-utils.ts similarity index 89% rename from src/lib/src/editor/editor-utils.ts rename to src/lib/editor/editor-utils.ts index 1342c4e..d865c5d 100644 --- a/src/lib/src/editor/editor-utils.ts +++ b/src/lib/editor/editor-utils.ts @@ -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) && diff --git a/src/lib/src/editor/editor.component.html b/src/lib/editor/editor.component.html similarity index 100% rename from src/lib/src/editor/editor.component.html rename to src/lib/editor/editor.component.html diff --git a/src/lib/src/editor/editor.component.ts b/src/lib/editor/editor.component.ts similarity index 85% rename from src/lib/src/editor/editor.component.ts rename to src/lib/editor/editor.component.ts index a1c778e..267d7de 100644 --- a/src/lib/src/editor/editor.component.ts +++ b/src/lib/editor/editor.component.ts @@ -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; + visibility: string; + children: Array; } @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(); + + private graphToTextEventEmitter = new EventEmitter(); + + private _graphToTextSyncEnabled = true; + + private validationEventEmitter = new EventEmitter(); + + private _disposables = new CompositeDisposable(); + + private _dslText = ''; + + private textToGraphConversionCompleted = new Subject(); + + private graphToTextConversionCompleted = new Subject(); + + private paletteReady = new BehaviorSubject(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(); @@ -79,114 +130,60 @@ export class EditorComponent implements OnInit, OnDestroy { @Output() contentValidated = new EventEmitter(); - /** - * 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(); - - private graphToTextEventEmitter = new EventEmitter(); - - private _graphToTextSyncEnabled = true; - - private validationEventEmitter = new EventEmitter(); - - private _disposables = new CompositeDisposable(); - - /* DSL Fields */ - - private _dslText : string = ''; - @Output() private dslChange = new EventEmitter(); - private textToGraphConversionCompleted = new Subject(); - - private graphToTextConversionCompleted = new Subject(); - - private paletteReady = new BehaviorSubject(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 { + updateGraph(): Promise { return self.updateGraphRepresentation(); } - updateText() : Promise { + updateText(): Promise { return self.updateTextRepresentation(); } - performLayout() : Promise { + performLayout(): Promise { 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, position? : dia.Point) : dia.Element { + createNode(metadata: Flo.ElementMetadata, props?: Map, position?: dia.Point): dia.Element { return self.createNode(metadata, props, position); } - createLink(source : Flo.LinkEnd, target : Flo.LinkEnd, metadata? : Flo.ElementMetadata, props? : Map) : dia.Link { + createLink(source: Flo.LinkEnd, target: Flo.LinkEnd, metadata?: Flo.ElementMetadata, props?: Map): 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( 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( 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 { + getTargetViewFromEvent(event: MouseEvent, x: number, y: number, excludeViews: Array = []): 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, position : dia.Point) : dia.Element { + createNode(metadata: Flo.ElementMetadata, props: Map, 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) : dia.Link { + createLink(source: Flo.LinkEnd, target: Flo.LinkEnd, metadata: Flo.ElementMetadata, props: Map): 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 { + validateContent(): Promise { 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) { 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 = ( cell).getBBox().topRight().offset(-error.get('size').width, 0); @@ -818,21 +815,21 @@ export class EditorComponent implements OnInit, OnDestroy { } } - doLayout() : Promise { + doLayout(): Promise { 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 { - console.debug(`Updating graph to represent '${this._dslText}'`); + updateGraphRepresentation(): Promise { + 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 { + updateTextRepresentation(): Promise { 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( 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', 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 diff --git a/src/lib/index.ts b/src/lib/index.ts index b56a828..922a6d4 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -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'; diff --git a/src/lib/src/module.ts b/src/lib/module.ts similarity index 100% rename from src/lib/src/module.ts rename to src/lib/module.ts diff --git a/src/lib/src/palette/palette.component.html b/src/lib/palette/palette.component.html similarity index 100% rename from src/lib/src/palette/palette.component.html rename to src/lib/palette/palette.component.html diff --git a/src/lib/src/palette/palette.component.ts b/src/lib/palette/palette.component.ts similarity index 69% rename from src/lib/src/palette/palette.component.ts rename to src/lib/palette/palette.component.ts index 2018947..9b2e05d 100644 --- a/src/lib/src/palette/palette.component.ts +++ b/src/lib/palette/palette.component.ts @@ -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: '', 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; + + /** + * 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(); @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(); @@ -70,42 +93,19 @@ export class Palette implements OnInit, OnDestroy, OnChanges { @Output() paletteFocus = new EventEmitter(); - 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(); - - 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; - - /** - * 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>) { - let startTime : number = new Date().getTime(); + private buildPalette(metamodel: Map>) { + 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 = []; - let groupAdded : Set = new Set(); + let paletteNodes: Array = []; + let groupAdded: Set = new Set(); - 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. $('
', { 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 = (this.floaterpaper.findViewByModel(floaternode)).getBBox(); - let size : dia.Size = floaternode.get('size'); + let box: dia.BBox = (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)); } diff --git a/src/lib/src/properties/df.property.component.html b/src/lib/properties/df.property.component.html similarity index 100% rename from src/lib/src/properties/df.property.component.html rename to src/lib/properties/df.property.component.html diff --git a/src/lib/src/properties/df.property.component.ts b/src/lib/properties/df.property.component.ts similarity index 90% rename from src/lib/src/properties/df.property.component.ts rename to src/lib/properties/df.property.component.ts index 42eea03..0b58755 100644 --- a/src/lib/src/properties/df.property.component.ts +++ b/src/lib/properties/df.property.component.ts @@ -10,7 +10,7 @@ import { Properties } from '../shared/flo-properties'; export class DynamicFormPropertyComponent { @Input() - model : Properties.ControlModel; + model: Properties.ControlModel; @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]; } diff --git a/src/lib/src/properties/properties.group.component.html b/src/lib/properties/properties.group.component.html similarity index 100% rename from src/lib/src/properties/properties.group.component.html rename to src/lib/properties/properties.group.component.html diff --git a/src/lib/src/properties/properties.group.component.ts b/src/lib/properties/properties.group.component.ts similarity index 93% rename from src/lib/src/properties/properties.group.component.ts rename to src/lib/properties/properties.group.component.ts index 2a01bb6..28b7a4a 100644 --- a/src/lib/src/properties/properties.group.component.ts +++ b/src/lib/properties/properties.group.component.ts @@ -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) { diff --git a/src/lib/shared/flo-common.ts b/src/lib/shared/flo-common.ts new file mode 100644 index 0000000..3d45b31 --- /dev/null +++ b/src/lib/shared/flo-common.ts @@ -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; + get(property: String): Promise; + properties(): Promise>; + } + + 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; + graphToText(flo: EditorContext): Promise; + load(): Promise>>; + groups(): Array; + + refresh?(): Promise>>; + subscribe?(listener: MetamodelListener): void; + unsubscribe?(listener: MetamodelListener): void; + isValidPropertyValue?(element: dia.Element, property: string, value: any): boolean; + } + + export interface CreationParams { + metadata?: ElementMetadata; + props?: Map; + } + + 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; + } + + export interface HandleCreationParams extends EmbeddedChildCreationParams { + kind: string; + } + + export interface Renderer { + createNode?(metadata: ElementMetadata, props?: Map): dia.Element; + createLink?(source: LinkEnd, target: LinkEnd, metadata?: ElementMetadata, props?: Map): 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; + 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; + readonly graphToTextConversionObservable: Observable; + readonly paletteReady: Observable; + zoomPercent: number; + gridSize: number; + readOnlyCanvas: boolean; + selection: dia.CellView; + graphToTextSync: boolean; + noPalette: boolean; + setDsl(dsl: string): void; + updateGraph(): Promise; + updateText(): Promise; + performLayout(): Promise; + clearGraph(): void; + getGraph(): dia.Graph; + getPaper(): dia.Paper; + getMinZoom(): number; + getMaxZoom(): number; + getZoomStep(): number; + fitToPage(): void; + createNode(metadata: ElementMetadata, props?: Map, position?: dia.Point): dia.Element; + createLink(source: LinkEnd, target: LinkEnd, metadata?: ElementMetadata, props?: Map): 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>>; + preDelete?(context: EditorContext, deletedElement: dia.Cell): void; + setDefaultContent?(editorContext: EditorContext, data: Map>): 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>, 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()) + }; + } + } + +} + + + + diff --git a/src/lib/src/shared/flo-properties.ts b/src/lib/shared/flo-properties.ts similarity index 74% rename from src/lib/src/shared/flo-properties.ts rename to src/lib/shared/flo-properties.ts index 4cf8390..c2cafaf 100644 --- a/src/lib/src/shared/flo-properties.ts +++ b/src/lib/shared/flo-properties.ts @@ -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?: Array; } export interface ControlModel { - 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 extends ControlModel { @@ -61,7 +61,7 @@ export namespace Properties { export class GenericControlModel implements ControlModel { - 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 { - 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 implements CodeControlModel { - 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 { if (!this._langControlModel) { - this._langControlModel = this._groupModel.getControlsModels().find(c => c.id === this._languagePropertyName); + // Cast to Properties.ControlModel from Properties.ControlModel | undefined + // Should not be undefined! + this._langControlModel = > this._groupModel.getControlsModels().find(c => c.id === this._languagePropertyName); } return this._langControlModel; } @@ -179,22 +181,22 @@ export namespace Properties { export class GenericListControlModel extends GenericControlModel { - 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 { - constructor(_property : Property, type : InputType, public options : Array) { + constructor(_property: Property, type: InputType, public options: Array) { 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> { - let metadata : Flo.ElementMetadata = this.cell.attr('metadata'); + getProperties(): Promise> { + 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>; + protected controlModels: Array>; - protected loading : boolean = true; + protected loading = true; - protected _loadedSubject : Subject; + protected _loadedSubject: Subject; - 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 { + protected createControlModel(property: Property): ControlModel { 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, debounce : number): AsyncValidatorFn { + export function uniqueResource(service: (value: any) => Observable, debounce: number): AsyncValidatorFn { return (control: AbstractControl): Observable => { 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) : ValidatorFn { - return (control: AbstractControl) : {[key: string]: any} => { - return excluded.find(e => e === control.value) ? {'noneOf': {value: control.value}} : null; + export function noneOf(excluded: Array): ValidatorFn { + return (control: AbstractControl): {[key: string]: any} => { + return excluded.find(e => e === control.value) ? {'noneOf': {value: control.value}} : {}; }; } diff --git a/src/lib/src/shared/flo.css b/src/lib/shared/flo.css similarity index 100% rename from src/lib/src/shared/flo.css rename to src/lib/shared/flo.css diff --git a/src/lib/src/shared/shapes.ts b/src/lib/shared/shapes.ts similarity index 78% rename from src/lib/src/shared/shapes.ts rename to src/lib/shared/shapes.ts index 22ae5f0..807f927 100644 --- a/src/lib/src/shared/shapes.ts +++ b/src/lib/shared/shapes.ts @@ -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= new Map(); +const HANDLE_ICON_MAP: Map = new Map(); const REMOVE = 'remove'; HANDLE_ICON_MAP.set(REMOVE, 'icons/delete.svg'); -const DECORATION_ICON_MAP : Map= new Map(); +const DECORATION_ICON_MAP: Map = new Map(); 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('', { - 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( + '', + { + 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('', { - 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( + '', + { + 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: - ''+ + '' + '' + '' + - ''+ - ''+ - ''+ - ''+ - ''+ + '' + + '' + + '' + + '' + + '' + '', 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; + kind: string; + messages: Array; } 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 = 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 = 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 = 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 }; diff --git a/src/lib/src/shared/flo-common.ts b/src/lib/src/shared/flo-common.ts deleted file mode 100644 index 05632c0..0000000 --- a/src/lib/src/shared/flo-common.ts +++ /dev/null @@ -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; - get(property : String) : Promise; - properties() : Promise>; - 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; - graphToText(flo : EditorContext) : Promise; - load() : Promise>>; - groups() : Array; - - refresh?() : Promise>>; - subscribe?(listener : MetamodelListener) : void; - unsubscribe?(listener : MetamodelListener) : void; - isValidPropertyValue?(element : dia.Element, property : string, value : any) : boolean; - } - - export interface CreationParams { - metadata? : ElementMetadata; - props? : Map; - } - - 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; - } - - export interface HandleCreationParams extends EmbeddedChildCreationParams { - kind : string; - } - - export interface Renderer { - createNode?(metadata : ElementMetadata, props? : Map) : dia.Element; - createLink?(source : LinkEnd, target : LinkEnd, metadata? : ElementMetadata, props? : Map) : 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; - 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; - updateText() : Promise; - performLayout() : Promise; - clearGraph() : void; - getGraph() : dia.Graph; - getPaper() : dia.Paper; - getMinZoom() : number; - getMaxZoom() : number; - getZoomStep() : number; - fitToPage() : void; - createNode(metadata : ElementMetadata, props? : Map, position? : dia.Point) : dia.Element; - createLink(source : LinkEnd, target : LinkEnd, metadata? : ElementMetadata, props? : Map) : dia.Link; - deleteSelectedNode() : void; - readonly textToGraphConversionObservable: Observable; - readonly graphToTextConversionObservable: Observable; - readonly paletteReady: Observable; - [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>>; - preDelete?(context : EditorContext, deletedElement : dia.Cell) : void; - setDefaultContent?(editorContext : EditorContext, data : Map>) : 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>, 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()) - }; - } - } - -} - - - - diff --git a/src/lib/tsconfig.es5.json b/src/lib/tsconfig.es5.json deleted file mode 100644 index 4e364a9..0000000 --- a/src/lib/tsconfig.es5.json +++ /dev/null @@ -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/" - } -} diff --git a/src/lib/tsconfig.lib.json b/src/lib/tsconfig.lib.json deleted file mode 100644 index 6a901d8..0000000 --- a/src/lib/tsconfig.lib.json +++ /dev/null @@ -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/" - } -} diff --git a/src/lib/tsconfig.spec.json b/src/lib/tsconfig.spec.json deleted file mode 100644 index f1bcaf3..0000000 --- a/src/lib/tsconfig.spec.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "baseUrl": "", - "module": "commonjs", - "declaration": false, - "emitDecoratorMetadata": true - } -} diff --git a/src/lib/typings.d.ts b/src/lib/typings.d.ts deleted file mode 100644 index 3481089..0000000 --- a/src/lib/typings.d.ts +++ /dev/null @@ -1 +0,0 @@ -// You can add project typings here. diff --git a/tsconfig-build.json b/tsconfig-build.json new file mode 100644 index 0000000..87dcb44 --- /dev/null +++ b/tsconfig-build.json @@ -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" + } +} diff --git a/tsconfig.json b/tsconfig.json index dd3d06d..9798eba 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,6 +14,7 @@ "lib": [ "es2015", "dom" - ] + ], + "strictNullChecks":false } } diff --git a/tslint.json b/tslint.json index 9e0ee5a..1462c69 100644 --- a/tslint.json +++ b/tslint.json @@ -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,