diff options
Diffstat (limited to 'dox-sequence-diagram-ui')
32 files changed, 5426 insertions, 5080 deletions
diff --git a/dox-sequence-diagram-ui/.prettierrc b/dox-sequence-diagram-ui/.prettierrc new file mode 100644 index 0000000000..40321d4ab2 --- /dev/null +++ b/dox-sequence-diagram-ui/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "jsxBracketSameLine": true, + "tabWidth": 4 +}
\ No newline at end of file diff --git a/dox-sequence-diagram-ui/eslintrc.json b/dox-sequence-diagram-ui/eslintrc.json index b2a3f24aaa..01b65f3845 100644 --- a/dox-sequence-diagram-ui/eslintrc.json +++ b/dox-sequence-diagram-ui/eslintrc.json @@ -1,32 +1,110 @@ { - "parserOptions": { - "ecmaVersion": 6, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true + "parser": "babel-eslint", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true, + "classes": true, + "modules": true, + "experimentalDecorators": true + } + }, + "env": { + "es6": true, + "jquery": true, + "node": true, + "jest": true + }, + "plugins": ["react", "import", "prettier"], + "extends": ["prettier"], + "globals": { + "Event": true, + "window": true, + "navigator": true, + "System": true, + "document": true, + "localStorage": true, + "sessionStorage": true, + "Image": true, + "requestAnimationFrame": true, + "cancelAnimationFrame": true, + "DEBUG": true, + "SVGElement": true, + "FormData": true, + "DEV": true, + "Blob": true, + "XMLHttpRequest": true, + "WebSocket": true, + "URL": true, + "PunchOutRegistry": true, + "it": true, + "describe": true + }, + "rules": { + "prettier/prettier": [ + "error", + { + "singleQuote": true, + "jsxBracketSameLine": true, + "tabWidth": 4 + } + ], + "linebreak-style": 0, + "no-unused-vars": 2, + "no-bitwise": 0, + "no-eq-null": 2, + "eqeqeq": 2, + "no-unused-expressions": 2, + "no-use-before-define": 2, + "new-cap": [ + 2, + { + "capIsNewExceptions": ["DataTable", "V"] + } + ], + "no-caller": 2, + "no-empty": 2, + "no-undef": 2, + "quotes": [2, "single", "avoid-escape"], + "no-plusplus": 0, + "no-cond-assign": [2, "except-parens"], + "no-invalid-this": 0, + "dot-notation": 0, + "camelcase": [ + 2, + { + "properties": "never" + } + ], + "curly": 2, + "semi": [2, "always"], + "import/default": 0, + "import/no-unresolved": 0, + "import/no-named-as-default": 2, + "import/no-duplicates": 0, + "import/imports-first": 2, + "import/export": 2, + "react/display-name": 0, + "react/forbid-prop-types": 0, + "react/jsx-boolean-value": 0, + "react/jsx-no-duplicate-props": 1, + "react/jsx-no-literals": 0, + "react/jsx-no-undef": 1, + "react/jsx-sort-prop-types": 0, + "react/jsx-sort-props": 0, + "react/jsx-uses-react": 1, + "react/jsx-uses-vars": 1, + "react/no-danger": 1, + "react/no-did-mount-set-state": 2, + "react/no-did-update-set-state": 2, + "react/no-direct-mutation-state": 1, + "react/no-multi-comp": 0, + "react/no-set-state": 0, + "react/no-unknown-property": 1, + "react/prop-types": 0, + "react/react-in-jsx-scope": 1, + "react/self-closing-comp": 1, + "react/sort-comp": 0 } - }, - "env": { - "browser": true - }, - "plugins": [ - "react" - ], - "extends": [ - "airbnb" - ], - "rules": { - "padded-blocks": 0, - "max-len": ["error", 160, 4], - "no-underscore-dangle": 0, - "global-require": 0, - "react/sort-comp": 0, - "new-cap": 0 - }, - "settings": { - "react": { - "pragma": "React", - "version": "0.14.8" - } - } } diff --git a/dox-sequence-diagram-ui/package.json b/dox-sequence-diagram-ui/package.json index aa9976e31e..dbbfd8465f 100644 --- a/dox-sequence-diagram-ui/package.json +++ b/dox-sequence-diagram-ui/package.json @@ -1,57 +1,59 @@ { - "name": "dox-sequence-diagram-ui", - "version": "1.0.0", - "description": "", - "main": "dist/index.js", - "scripts": { - "build": "webpack", - "start": "webpack-dev-server", - "lint": "eslint . --ext .js --ext .jsx --cache" - }, - "author": "ONAP", - "license": "SEE LICENSE IN LICENSE", - "dependencies": { - "d3": "^4.10.0", - "lodash": "^4.12.0", - "prop-types": "^15.6.0", - "react": "^15.6.2", - "react-dnd": "^2.5.4", - "react-dnd-html5-backend": "^2.5.4", - "react-dom": "^15.6.2", - "react-redux": "^5.0.6", - "react-select": "1.2.1", - "redux": "^3.7.2" - }, - "devDependencies": { - "babel-core": "^6.26.0", - "babel-eslint": "^8.2.1", - "babel-loader": "^7.1.2", - "babel-plugin-lodash": "^3.3.2", - "babel-preset-env": "^1.6.1", - "babel-preset-react": "^6.5.0", - "copy": "^0.2.0", - "css-loader": "^0.28.7", - "eslint": "^2.10.2", - "eslint-config-airbnb": "^9.0.1", - "eslint-loader": "^1.3.0", - "eslint-plugin-import": "^1.8.0", - "eslint-plugin-jsx-a11y": "^1.2.2", - "eslint-plugin-react": "^5.1.1", - "file-loader": "^1.1.6", - "node-sass": "^4.7.2", - "path": "^0.12.7", - "raw-loader": "^0.5.1", - "redux-devtools": "^3.3.1", - "sass-loader": "^6.0.6", - "style-loader": "^0.19.1", - "svg-sprite-loader": "0.0.19", - "url-loader": "^0.6.2", - "webpack": "^2.2.1", - "webpack-dev-server": "^2.4.2" - }, - "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0", - "yarn": "^1.3.2" - } -}
\ No newline at end of file + "name": "dox-sequence-diagram-ui", + "version": "1.0.0", + "description": "", + "main": "dist/index.js", + "scripts": { + "build": "webpack", + "start": "webpack-dev-server", + "lint": "eslint . --ext .js --ext .jsx --cache -c eslintrc.json", + "lint-fix": "eslint --fix -c eslintrc.json --ext .js --ext .jsx src" + }, + "author": "ONAP", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "d3": "^4.10.0", + "lodash": "^4.12.0", + "prop-types": "^15.6.0", + "react": "^15.6.2", + "react-dnd": "^2.5.4", + "react-dnd-html5-backend": "^2.5.4", + "react-dom": "^15.6.2", + "react-redux": "^5.0.6", + "react-select": "1.2.1", + "redux": "^3.7.2" + }, + "devDependencies": { + "babel-core": "^6.26.0", + "babel-eslint": "^8.2.1", + "babel-loader": "^7.1.2", + "babel-plugin-lodash": "^3.3.2", + "babel-preset-env": "^1.6.1", + "babel-preset-react": "^6.5.0", + "copy": "^0.2.0", + "css-loader": "^0.28.7", + "eslint": "^4.18.1", + "eslint-config-prettier": "^2.9.0", + "eslint-loader": "^1.9.0", + "eslint-plugin-import": "^2.9.0", + "eslint-plugin-prettier": "^2.6.0", + "eslint-plugin-react": "^7.7.0", + "file-loader": "^1.1.6", + "node-sass": "^4.7.2", + "path": "^0.12.7", + "prettier": "^1.10.2", + "raw-loader": "^0.5.1", + "redux-devtools": "^3.3.1", + "sass-loader": "^6.0.6", + "style-loader": "^0.19.1", + "svg-sprite-loader": "0.0.19", + "url-loader": "^0.6.2", + "webpack": "^2.2.1", + "webpack-dev-server": "^2.4.2" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0", + "yarn": "^1.3.2" + } +} diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/Sequencer.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/Sequencer.jsx index fc2bfbc0e3..c0e9483e92 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/Sequencer.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/Sequencer.jsx @@ -28,176 +28,177 @@ import '../../../../res/sdc-sequencer.scss'; * ASDC Sequencer entry point. */ class Sequencer extends React.Component { + // ////////////////////////////////////////////////////////////////////////////////////////////// - // ////////////////////////////////////////////////////////////////////////////////////////////// - - constructor(props, context) { - super(props, context); - - - this.setMetamodel.bind(this); - this.setModel.bind(this); - this.getModel.bind(this); - this.getMetamodel.bind(this); - this.getSVG.bind(this); - this.getDemoScenarios.bind(this); - this.newModel.bind(this); - - // Parse options. - - this.options = new Options(props.options); - - // Default scenarios. - - const scenarios = this.getDemoScenarios(); - this.setMetamodel(scenarios.getMetamodels()); - - // this.setModel(scenarios.getBlank()); - this.setModel(scenarios.getDimensions()); - // this.setModel(scenarios.getECOMP()); - - } - - // ////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Optionally save known metamodels so that subsequent loading and unloading - * of models needn't include the corresponding metamodel. - * @param metamodels array of conformant metamodel JSON definitions. - * @return this. - */ - setMetamodel(metamodels) { - Common.assertType(metamodels, 'Array'); - this.metamodels = new Metamodels(metamodels); - return this; - } - - // ////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Set current diagram. - * @param modelJSON JSON diagram spec. - * @param metamodelIdOrDefinition optional metamodel definition or reference. Defaults to - * the model's metadata @ref, or the default (permissive) metamodel. - * @return this. - */ - setModel(modelJSON, metamodelIdOrDefinition) { - Common.assertType(modelJSON, 'Object'); - const ref = (modelJSON.metadata) ? modelJSON.metadata.ref : undefined; - const metamodel = this.getMetamodel(metamodelIdOrDefinition || ref); - Common.assertInstanceOf(metamodel, Metamodel); - this.model = new Model(modelJSON, metamodel); - if (this.application) { - this.application.setModel(this.model); + constructor(props, context) { + super(props, context); + + this.setMetamodel.bind(this); + this.setModel.bind(this); + this.getModel.bind(this); + this.getMetamodel.bind(this); + this.getSVG.bind(this); + this.getDemoScenarios.bind(this); + this.newModel.bind(this); + + // Parse options. + + this.options = new Options(props.options); + + // Default scenarios. + + const scenarios = this.getDemoScenarios(); + this.setMetamodel(scenarios.getMetamodels()); + + // this.setModel(scenarios.getBlank()); + this.setModel(scenarios.getDimensions()); + // this.setModel(scenarios.getECOMP()); } - return this; - } - - // ////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get current diagram state. At any given instant the diagram might not make *sense* - * but it should always be syntactically valid. - * @return current Model. - */ - getModel() { - - if (this.application) { - const model = this.application.getModel(); - if (model) { - return model.unwrap(); - } + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Optionally save known metamodels so that subsequent loading and unloading + * of models needn't include the corresponding metamodel. + * @param metamodels array of conformant metamodel JSON definitions. + * @return this. + */ + setMetamodel(metamodels) { + Common.assertType(metamodels, 'Array'); + this.metamodels = new Metamodels(metamodels); + return this; } - return this.model; - } - - // ////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Extract SVG element. - * @return stringified SVG element. - */ - getSVG() { - return this.application.getSVG(); - } - - // ////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get demo scenarios, allowing initialization in demo mode from the outside. - * @returns {Scenarios} - */ - getDemoScenarios() { - return new Scenarios(); - } - - // ////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Create new model. - * @param metamodelIdOrDefinition - * @return newly-created model. - */ - newModel(metamodelIdOrDefinition) { - const metamodel = this.getMetamodel(metamodelIdOrDefinition); - Common.assertInstanceOf(metamodel, Metamodel); - const model = new Model({}, metamodel); - if (this.application) { - this.application.setModel(model); + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set current diagram. + * @param modelJSON JSON diagram spec. + * @param metamodelIdOrDefinition optional metamodel definition or reference. Defaults to + * the model's metadata @ref, or the default (permissive) metamodel. + * @return this. + */ + setModel(modelJSON, metamodelIdOrDefinition) { + Common.assertType(modelJSON, 'Object'); + const ref = modelJSON.metadata ? modelJSON.metadata.ref : undefined; + const metamodel = this.getMetamodel(metamodelIdOrDefinition || ref); + Common.assertInstanceOf(metamodel, Metamodel); + this.model = new Model(modelJSON, metamodel); + if (this.application) { + this.application.setModel(this.model); + } + return this; } - return model; - } - - // ////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get Metamodel instance corresponding to an ID or JSON definition. - * @param metamodelIdOrDefinition String ID or JSON definition. - * @returns Metamodel instance. - * @private - */ - getMetamodel(metamodelIdOrDefinition) { - const metamodelType = Common.getType(metamodelIdOrDefinition); - if (metamodelType === 'Object') { - return new Metamodel(metamodelIdOrDefinition); + + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get current diagram state. At any given instant the diagram might not make *sense* + * but it should always be syntactically valid. + * @return current Model. + */ + getModel() { + if (this.application) { + const model = this.application.getModel(); + if (model) { + return model.unwrap(); + } + } + + return this.model; } - return this.metamodels.getMetamodelOrDefault(metamodelIdOrDefinition); - } - // ////////////////////////////////////////////////////////////////////////////////////////////// + // ////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Render current diagram state. - */ - render() { + /** + * Extract SVG element. + * @return stringified SVG element. + */ + getSVG() { + return this.application.getSVG(); + } - if (this.props.model) { + // ////////////////////////////////////////////////////////////////////////////////////////////// - // If a model was specified as a property, apply it. Otherwise - // fall back to the demo model. + /** + * Get demo scenarios, allowing initialization in demo mode from the outside. + * @returns {Scenarios} + */ + getDemoScenarios() { + return new Scenarios(); + } - const scenarios = this.getDemoScenarios(); - const metamodel = [scenarios.getBlankMetamodel(), scenarios.getECOMPMetamodel()]; - if (this.props.metamodel) { - metamodel.push(this.props.metamodel); - } - this.setMetamodel(metamodel); - this.setModel(this.props.model); + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Create new model. + * @param metamodelIdOrDefinition + * @return newly-created model. + */ + newModel(metamodelIdOrDefinition) { + const metamodel = this.getMetamodel(metamodelIdOrDefinition); + Common.assertInstanceOf(metamodel, Metamodel); + const model = new Model({}, metamodel); + if (this.application) { + this.application.setModel(model); + } + return model; } - return ( - <Application options={this.options} sequencer={this} ref={(a) => { this.application = a; }} /> - ); - } + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get Metamodel instance corresponding to an ID or JSON definition. + * @param metamodelIdOrDefinition String ID or JSON definition. + * @returns Metamodel instance. + * @private + */ + getMetamodel(metamodelIdOrDefinition) { + const metamodelType = Common.getType(metamodelIdOrDefinition); + if (metamodelType === 'Object') { + return new Metamodel(metamodelIdOrDefinition); + } + return this.metamodels.getMetamodelOrDefault(metamodelIdOrDefinition); + } + // ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render current diagram state. + */ + render() { + if (this.props.model) { + // If a model was specified as a property, apply it. Otherwise + // fall back to the demo model. + + const scenarios = this.getDemoScenarios(); + const metamodel = [ + scenarios.getBlankMetamodel(), + scenarios.getECOMPMetamodel() + ]; + if (this.props.metamodel) { + metamodel.push(this.props.metamodel); + } + this.setMetamodel(metamodel); + this.setModel(this.props.model); + } + + return ( + <Application + options={this.options} + sequencer={this} + ref={a => { + this.application = a; + }} + /> + ); + } } Sequencer.propTypes = { - options: PropTypes.object.isRequired, - model: PropTypes.object, - metamodel: PropTypes.object, + options: PropTypes.object.isRequired, + model: PropTypes.object, + metamodel: PropTypes.object }; - export default Sequencer; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Common.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Common.js index 7337367dca..1c6bd69dc2 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Common.js +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Common.js @@ -18,339 +18,347 @@ * Common operations. */ export default class Common { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Retrieve and start a simple timer. Retrieve elapsed time by calling #ms(). + * @returns {*} + */ + static timer() { + const start = new Date().getTime(); + return { + ms() { + return new Date().getTime() - start; + } + }; + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Retrieve and start a simple timer. Retrieve elapsed time by calling #ms(). - * @returns {*} - */ - static timer() { - const start = new Date().getTime(); - return { - ms() { - return (new Date().getTime() - start); - }, - }; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get datatype, stripping '[object Boolean]' to just 'Boolean'. - * @param o JS object. - * @return String like String, Number, Date, Null, Undefined, stuff like that. - */ - static getType(o) { - const str = Object.prototype.toString.call(o); - const prefix = '[object '; - if (str.substr(str, prefix.length) === prefix) { - return str.substr(prefix.length, str.length - (prefix.length + 1)); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get datatype, stripping '[object Boolean]' to just 'Boolean'. + * @param o JS object. + * @return String like String, Number, Date, Null, Undefined, stuff like that. + */ + static getType(o) { + const str = Object.prototype.toString.call(o); + const prefix = '[object '; + if (str.substr(str, prefix.length) === prefix) { + return str.substr(prefix.length, str.length - (prefix.length + 1)); + } + return str; } - return str; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Assert that an argument was provided. - * @param value to be checked. - * @param message message on assertion failure. - * @return value. - */ - static assertNotNull(value, message = 'Unexpected null value') { - if (!value) { - throw new Error(message); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert that an argument was provided. + * @param value to be checked. + * @param message message on assertion failure. + * @return value. + */ + static assertNotNull(value, message = 'Unexpected null value') { + if (!value) { + throw new Error(message); + } + return value; } - return value; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Assert argument type. - * @param value to be checked. - * @param expected expected type string, e,g. Number from [object Number]. - * @return value. - */ - static assertType(value, expected) { - const type = this.getType(value); - if (type !== expected) { - throw new Error(`Expected type ${expected}, got ${type}`); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert argument type. + * @param value to be checked. + * @param expected expected type string, e,g. Number from [object Number]. + * @return value. + */ + static assertType(value, expected) { + const type = this.getType(value); + if (type !== expected) { + throw new Error(`Expected type ${expected}, got ${type}`); + } + return value; } - return value; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Assert argument type. - * @param value to be checked. - * @param unexpected unexpected type string, e,g. Number from [object Number]. - * @return value. - */ - static assertNotType(value, unexpected) { - const type = this.getType(value); - if (type === unexpected) { - throw new Error(`Forbidden type "${unexpected}"`); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert argument type. + * @param value to be checked. + * @param unexpected unexpected type string, e,g. Number from [object Number]. + * @return value. + */ + static assertNotType(value, unexpected) { + const type = this.getType(value); + if (type === unexpected) { + throw new Error(`Forbidden type "${unexpected}"`); + } + return value; } - return value; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Assert argument is a simple JSON object, and specifically not (something like an) ES6 class. - * @param value to be checked. - * @return value. - */ - static assertPlainObject(value) { - Common.assertType(value, 'Object'); - // TODO - /* + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert argument is a simple JSON object, and specifically not (something like an) ES6 class. + * @param value to be checked. + * @return value. + */ + static assertPlainObject(value) { + Common.assertType(value, 'Object'); + // TODO + /* if (!($.isPlainObject(value))) { throw new Error(`Expected plain object: ${value}`); } */ - return value; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Assert argument type. - * @param value to be checked. - * @param c expected class. - * @return value. - */ - static assertInstanceOf(value, c) { - Common.assertNotNull(value); - if (!(value instanceof c)) { - throw new Error(`Expected instanceof ${c}: ${value}`); - } - return value; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Assert that a string matches a regex. - * @param value value to be tested. - * @param re pattern to be applied. - * @return value. - */ - static assertMatches(value, re) { - this.assertType(value, 'String'); - this.assertType(re, 'RegExp'); - if (!re.test(value)) { - throw new Error(`Value ${value} doesn't match pattern ${re}`); + return value; } - return value; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Assert the value of a boolean. - * - * @param bool to be checked. - * @param message optional message on assertion failure. - * @return value. - */ - static assertThat(bool, message) { - if (!bool) { - throw new Error(message || `Unexpected: ${bool}`); - } - return bool; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Verify that a value, generally a function arg, is a DOM element. - * @param value to be checked. - * @return value. - */ - static assertHTMLElement(value) { - if (!Common.isHTMLElement(value)) { - throw new Error(`Expected HTMLElement: ${value}`); - } - return value; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Check whether a value, generally a function arg, is an HTML DOM element. - * @param o to be checked. - * @return true if DOM element. - */ - static isHTMLElement(o) { - if (typeof HTMLElement === 'object') { - return o instanceof HTMLElement; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert argument type. + * @param value to be checked. + * @param c expected class. + * @return value. + */ + static assertInstanceOf(value, c) { + Common.assertNotNull(value); + if (!(value instanceof c)) { + throw new Error(`Expected instanceof ${c}: ${value}`); + } + return value; } - return o && typeof o === 'object' && o !== null - && o.nodeType === 1 && typeof o.nodeName === 'string'; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Check if a string is non-empty. - * @param s string to be checked. - * @returns false if non-blank string, true otherwise. - */ - static isBlank(s) { - if (Common.getType(s) === 'String') { - return (s.trim().length === 0); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert that a string matches a regex. + * @param value value to be tested. + * @param re pattern to be applied. + * @return value. + */ + static assertMatches(value, re) { + this.assertType(value, 'String'); + this.assertType(re, 'RegExp'); + if (!re.test(value)) { + throw new Error(`Value ${value} doesn't match pattern ${re}`); + } + return value; } - return true; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Detect dates that are numbers, milli/seconds since epoch.. - * - * @param n candidate number. - * @returns {boolean} - */ - static isNumber(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Parse the text output from a template to a DOM element. - * @param txt input text. - * @returns {Element} - */ - static txt2dom(txt) { - return new DOMParser().parseFromString(txt, 'image/svg+xml').documentElement; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Recursively convert a DOM element to an SVG (namespaced) element. Otherwise - * you get HTML elements that *happen* to have SVG names, but which aren't actually SVG. - * - * @param node DOM node to be converted. - * @param svg to be updated. - * @returns {*} for chaining. - */ - static dom2svg(node, svg) { - - Common.assertNotType(node, 'String'); - - if (node.childNodes && node.childNodes.length > 0) { - - for (const c of node.childNodes) { - switch (c.nodeType) { - case document.TEXT_NODE: - svg.text(c.nodeValue); - break; - default: - break; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Assert the value of a boolean. + * + * @param bool to be checked. + * @param message optional message on assertion failure. + * @return value. + */ + static assertThat(bool, message) { + if (!bool) { + throw new Error(message || `Unexpected: ${bool}`); } - } - - for (const c of node.childNodes) { - switch (c.nodeType) { - case document.ELEMENT_NODE: - Common.dom2svg(c, svg.append(`svg:${c.nodeName.toLowerCase()}`)); - break; - default: - break; + return bool; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Verify that a value, generally a function arg, is a DOM element. + * @param value to be checked. + * @return value. + */ + static assertHTMLElement(value) { + if (!Common.isHTMLElement(value)) { + throw new Error(`Expected HTMLElement: ${value}`); } - } + return value; } - if (node.hasAttributes()) { - for (let i = 0; i < node.attributes.length; i++) { - const a = node.attributes.item(i); - svg.attr(a.name, a.value); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Check whether a value, generally a function arg, is an HTML DOM element. + * @param o to be checked. + * @return true if DOM element. + */ + static isHTMLElement(o) { + /* eslint-disable no-undef */ + if (typeof HTMLElement === 'object') { + return o instanceof HTMLElement; + } + /* eslint-enable no-undef */ + return ( + o && + typeof o === 'object' && + o !== null && + o.nodeType === 1 && + typeof o.nodeName === 'string' + ); } - return svg; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get the lines to be shown in the label. - * - * @param labelText original label text. - * @param wordWrapAt chars at which to break words. - * @param lineWrapAt chars at which to wrap. - * @param maximumLines lines at which to truncate. - * @returns {Array} - */ - static tokenize(labelText = '', wordWrapAt, lineWrapAt, maximumLines) { - - let l = labelText; - - // Hyphenate and break long words. - - const regex = new RegExp(`(\\w{${wordWrapAt - 1}})(?=\\w)`, 'g'); - l = l.replace(regex, '$1- '); - - const labelTokens = l.split(/\s+/); - const lines = []; - let label = ''; - for (const labelToken of labelTokens) { - if (label.length > 0) { - const length = label.length + labelToken.length + 1; - if (length > lineWrapAt) { - lines.push(label.trim()); - label = labelToken; - continue; + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Check if a string is non-empty. + * @param s string to be checked. + * @returns false if non-blank string, true otherwise. + */ + static isBlank(s) { + if (Common.getType(s) === 'String') { + return s.trim().length === 0; } - } - label = `${label} ${labelToken}`; + return true; } - if (label) { - lines.push(label.trim()); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Detect dates that are numbers, milli/seconds since epoch.. + * + * @param n candidate number. + * @returns {boolean} + */ + static isNumber(n) { + return !isNaN(parseFloat(n)) && isFinite(n); } - const truncated = lines.slice(0, maximumLines); - if (truncated.length < lines.length) { - let finalLine = truncated[maximumLines - 1]; - if (finalLine.length > (lineWrapAt - 4)) { - finalLine = finalLine.substring(0, lineWrapAt - 4); - } - finalLine = `${finalLine} ...`; - truncated[maximumLines - 1] = finalLine; + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Parse the text output from a template to a DOM element. + * @param txt input text. + * @returns {Element} + */ + static txt2dom(txt) { + /* eslint-disable no-undef */ + return new DOMParser().parseFromString(txt, 'image/svg+xml') + .documentElement; + /* eslint-enable no-undef */ } - return truncated; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Brutally sanitize an input string. We have no syntax rules, and hence no specific - * rules to apply, but we have very few unconstrained fields, so we can implement a - * crude default and devolve the rest to options. - * @param value value to be sanitized. - * @param options control options including validation rules. - * @param type validation type. - * @returns {*} sanitized string. - * @private - */ - static sanitizeText(value, options, type) { - const rules = Common.assertNotNull(options.validation[type]); - let v = value || rules.defaultValue || ''; - if (rules.replace) { - v = v.replace(rules.replace, ''); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Recursively convert a DOM element to an SVG (namespaced) element. Otherwise + * you get HTML elements that *happen* to have SVG names, but which aren't actually SVG. + * + * @param node DOM node to be converted. + * @param svg to be updated. + * @returns {*} for chaining. + */ + static dom2svg(node, svg) { + Common.assertNotType(node, 'String'); + + if (node.childNodes && node.childNodes.length > 0) { + for (const c of node.childNodes) { + switch (c.nodeType) { + case document.TEXT_NODE: + svg.text(c.nodeValue); + break; + default: + break; + } + } + + for (const c of node.childNodes) { + switch (c.nodeType) { + case document.ELEMENT_NODE: + Common.dom2svg( + c, + svg.append(`svg:${c.nodeName.toLowerCase()}`) + ); + break; + default: + break; + } + } + } + + if (node.hasAttributes()) { + for (let i = 0; i < node.attributes.length; i++) { + const a = node.attributes.item(i); + svg.attr(a.name, a.value); + } + } + + return svg; } - if (v.length > rules.maxLength) { - v = `${v.substring(0, rules.maxLength)}...`; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get the lines to be shown in the label. + * + * @param labelText original label text. + * @param wordWrapAt chars at which to break words. + * @param lineWrapAt chars at which to wrap. + * @param maximumLines lines at which to truncate. + * @returns {Array} + */ + static tokenize(labelText = '', wordWrapAt, lineWrapAt, maximumLines) { + let l = labelText; + + // Hyphenate and break long words. + + const regex = new RegExp(`(\\w{${wordWrapAt - 1}})(?=\\w)`, 'g'); + l = l.replace(regex, '$1- '); + + const labelTokens = l.split(/\s+/); + const lines = []; + let label = ''; + for (const labelToken of labelTokens) { + if (label.length > 0) { + const length = label.length + labelToken.length + 1; + if (length > lineWrapAt) { + lines.push(label.trim()); + label = labelToken; + continue; + } + } + label = `${label} ${labelToken}`; + } + + if (label) { + lines.push(label.trim()); + } + + const truncated = lines.slice(0, maximumLines); + if (truncated.length < lines.length) { + let finalLine = truncated[maximumLines - 1]; + if (finalLine.length > lineWrapAt - 4) { + finalLine = finalLine.substring(0, lineWrapAt - 4); + } + finalLine = `${finalLine} ...`; + truncated[maximumLines - 1] = finalLine; + } + + return truncated; } - return v; - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Brutally sanitize an input string. We have no syntax rules, and hence no specific + * rules to apply, but we have very few unconstrained fields, so we can implement a + * crude default and devolve the rest to options. + * @param value value to be sanitized. + * @param options control options including validation rules. + * @param type validation type. + * @returns {*} sanitized string. + * @private + */ + static sanitizeText(value, options, type) { + const rules = Common.assertNotNull(options.validation[type]); + let v = value || rules.defaultValue || ''; + if (rules.replace) { + v = v.replace(rules.replace, ''); + } + if (v.length > rules.maxLength) { + v = `${v.substring(0, rules.maxLength)}...`; + } + return v; + } } diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Logger.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Logger.js index 187f49bb08..8b6ae67ca7 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Logger.js +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Logger.js @@ -23,108 +23,107 @@ import Common from './Common'; * disable them for production. */ export default class Logger { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * No-op call so that we can leave imports in place, + * even when there's no debugging. + */ + static noop() { + // Nothing. + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set debug level. + * @param level threshold. + */ + static setLevel(level) { + this.level = Logger.OFF; + if (Common.getType(level) === 'Number') { + this.level = level; + } else { + this.level = Logger[level]; + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get debug level. + * @returns {number|*} + */ + static getLevel() { + return this.level; + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * No-op call so that we can leave imports in place, - * even when there's no debugging. - */ - static noop() { - // Nothing. - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Set debug level. - * @param level threshold. - */ - static setLevel(level) { - this.level = Logger.OFF; - if (Common.getType(level) === 'Number') { - this.level = level; - } else { - this.level = Logger[level]; + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Write DEBUG-level log. + * @param msg message or tokens. + */ + static debug(...msg) { + if (this.level >= Logger.DEBUG) { + const out = this.serialize(msg); + console.info(`ASDCS [DEBUG] ${out}`); + } } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get debug level. - * @returns {number|*} - */ - static getLevel() { - return this.level; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Write DEBUG-level log. - * @param msg message or tokens. - */ - static debug(...msg) { - if (this.level >= Logger.DEBUG) { - const out = this.serialize(msg); - console.info(`ASDCS [DEBUG] ${out}`); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Write INFO-level log. + * @param msg message or tokens. + */ + static info(...msg) { + if (this.level >= Logger.INFO) { + const out = this.serialize(msg); + console.info(`ASDCS [INFO] ${out}`); + } } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Write INFO-level log. - * @param msg message or tokens. - */ - static info(...msg) { - if (this.level >= Logger.INFO) { - const out = this.serialize(msg); - console.info(`ASDCS [INFO] ${out}`); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Write debug. + * @param msg message or tokens. + */ + static warn(msg) { + if (this.level >= Logger.WARN) { + const out = this.serialize(msg); + console.warn(`ASDCS [WARN] ${out}`); + } } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Write debug. - * @param msg message or tokens. - */ - static warn(msg) { - if (this.level >= Logger.WARN) { - const out = this.serialize(msg); - console.warn(`ASDCS [WARN] ${out}`); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Write error. + * @param msg message or tokens. + */ + static error(...msg) { + if (this.level >= Logger.ERROR) { + const out = this.serialize(msg); + console.error(`ASDCS [ERROR] ${out}`); + } } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Write error. - * @param msg message or tokens. - */ - static error(...msg) { - if (this.level >= Logger.ERROR) { - const out = this.serialize(msg); - console.error(`ASDCS [ERROR] ${out}`); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Serialize msg. + * @param msg message or tokens. + * @returns {string} + */ + static serialize(...msg) { + let out = ''; + msg.forEach(token => { + out = `${out}${token}`; + }); + return out; } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Serialize msg. - * @param msg message or tokens. - * @returns {string} - */ - static serialize(...msg) { - let out = ''; - msg.forEach((token) => { - out = `${out}${token}`; - }); - return out; - } } // ///////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Options.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Options.js index 15897d7ee3..b054cb26e7 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Options.js +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/common/Options.js @@ -23,26 +23,25 @@ import Logger from './Logger'; * and the result -- runtime options -- are available by calling #getOptions(). */ export default class Options { + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Construct options, applying defaults. + * @param options optional override options. + */ + constructor(options = {}) { + this.options = _merge({}, Options.DEFAULTS, options); + } - /** - * Construct options, applying defaults. - * @param options optional override options. - */ - constructor(options = {}) { - this.options = _merge({}, Options.DEFAULTS, options); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Unwrap options. - * @returns {*} - */ - unwrap() { - return this.options; - } + /** + * Unwrap options. + * @returns {*} + */ + unwrap() { + return this.options; + } } // ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -51,86 +50,86 @@ export default class Options { * Default options, overridden by anything of the same name. */ Options.DEFAULTS = { - log: { - level: Logger.WARN, - }, - demo: false, - useHtmlSelect: true, - diagram: { - svg: { - x: 0, - y: 0, - width: 1600, - height: 1200, - margin: 50, - floodColor: '#009fdb', - scale: { - height: true, - width: true, - minimum: 0.25, - }, - }, - title: { - height: 0, - }, - metadata: false, - lifelines: { - header: { - height: 225, - width: 350, - wrapWords: 14, - wrapLines: 18, - maxLines: 5, - }, - occurrences: { - marginTop: 50, - marginBottom: 75, - foreshortening: 5, - width: 50, - }, - spacing: { - horizontal: 400, - vertical: 400, - }, - }, - messages: { - label: { - wrapWords: 14, - wrapLines: 18, - maxLines: 4, - }, - }, - fragments: { - leftMargin: 150, - topMargin: 200, - widthMargin: 300, - heightMargin: 350, - label: { - wrapWords: 50, - wrapLines: 50, - maxLines: 2, - }, - }, - }, - validation: { - lifeline: { - maxLength: 100, - defaultValue: '', - replace: /[^\-\.\+ &%#@\?\(\)\[\]<>\w\d]/g, - }, - message: { - maxLength: 100, - defaultValue: '', - replace: /[^\-\.\+ &%#@\?\(\)\[\]<>\w\d]/g, - }, - notes: { - maxLength: 255, - defaultValue: '', + log: { + level: Logger.WARN }, - guard: { - maxLength: 80, - defaultValue: '', - replace: /[^\-\.\+ &%#@\?\(\)\[\]<>\w\d]/g, + demo: false, + useHtmlSelect: true, + diagram: { + svg: { + x: 0, + y: 0, + width: 1600, + height: 1200, + margin: 50, + floodColor: '#009fdb', + scale: { + height: true, + width: true, + minimum: 0.25 + } + }, + title: { + height: 0 + }, + metadata: false, + lifelines: { + header: { + height: 225, + width: 350, + wrapWords: 14, + wrapLines: 18, + maxLines: 5 + }, + occurrences: { + marginTop: 50, + marginBottom: 75, + foreshortening: 5, + width: 50 + }, + spacing: { + horizontal: 400, + vertical: 400 + } + }, + messages: { + label: { + wrapWords: 14, + wrapLines: 18, + maxLines: 4 + } + }, + fragments: { + leftMargin: 150, + topMargin: 200, + widthMargin: 300, + heightMargin: 350, + label: { + wrapWords: 50, + wrapLines: 50, + maxLines: 2 + } + } }, - }, + validation: { + lifeline: { + maxLength: 100, + defaultValue: '', + replace: /[^\-\.\+ &%#@\?\(\)\[\]<>\w\d]/g + }, + message: { + maxLength: 100, + defaultValue: '', + replace: /[^\-\.\+ &%#@\?\(\)\[\]<>\w\d]/g + }, + notes: { + maxLength: 255, + defaultValue: '' + }, + guard: { + maxLength: 80, + defaultValue: '', + replace: /[^\-\.\+ &%#@\?\(\)\[\]<>\w\d]/g + } + } }; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/application/Application.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/application/Application.jsx index 6889e0ab9f..b63e69a4ed 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/application/Application.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/application/Application.jsx @@ -28,258 +28,274 @@ import Overlay from '../overlay/Overlay'; * Application controller, also a view. */ class Application extends React.Component { + /** + * Construct application view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + + this.sequencer = Common.assertNotNull(props.sequencer); + this.model = this.sequencer.getModel(); + this.metamodel = this.sequencer.getMetamodel(); + this.options = props.options; + Logger.setLevel(this.options.unwrap().log.level); + + // Bindings. + + this.showInfoDialog = this.showInfoDialog.bind(this); + this.showEditDialog = this.showEditDialog.bind(this); + this.showConfirmDialog = this.showConfirmDialog.bind(this); + this.hideOverlay = this.hideOverlay.bind(this); + this.onMouseMove = this.onMouseMove.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Construct application view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); - - this.sequencer = Common.assertNotNull(props.sequencer); - this.model = this.sequencer.getModel(); - this.metamodel = this.sequencer.getMetamodel(); - this.options = props.options; - Logger.setLevel(this.options.unwrap().log.level); - - // Bindings. - - this.showInfoDialog = this.showInfoDialog.bind(this); - this.showEditDialog = this.showEditDialog.bind(this); - this.showConfirmDialog = this.showConfirmDialog.bind(this); - this.hideOverlay = this.hideOverlay.bind(this); - this.onMouseMove = this.onMouseMove.bind(this); - this.onMouseUp = this.onMouseUp.bind(this); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get application options. - * @returns JSON options, see Options.js. - */ - getOptions() { - return this.options.unwrap(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Set diagram name. - * @param n diagram (human-readable) name. - */ - setName(n) { - this.diagram.setName(n); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Set diagram model. - * @param model diagram instance. - */ - setModel(model) { - - Common.assertNotNull(model); - - this.model = model; - - if (this.editor) { - this.editor.render(); + /** + * Get application options. + * @returns JSON options, see Options.js. + */ + getOptions() { + return this.options.unwrap(); } - if (this.diagram) { - this.diagram.render(); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set diagram name. + * @param n diagram (human-readable) name. + */ + setName(n) { + this.diagram.setName(n); } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get Model wrapper. - * @returns Model. - */ - getModel() { - return this.model; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get SVG element. - * @returns {*} - */ - getSVG() { - return this.diagram.getSVG(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get top-level widget. Provides the demo toolbar with access to the public API. - * @returns {*} - */ - getSequencer() { - return this.sequencer; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Present info dialog. - * @param msg info message. - */ - showInfoDialog(msg) { - this.dialog.showInfoDialog(msg); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Present error dialog. - * @param msg error message. - */ - showErrorDialog(msg) { - this.dialog.showErrorDialog(msg); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Present confirmation dialog. - * @param msg info message. - * @param cb callback function to be invoked on OK. - */ - showConfirmDialog(msg, cb) { - Common.assertType(cb, 'Function'); - this.dialog.showConfirmDialog(msg, cb); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Present edit (textarea) dialog. - * @param msg prompt. - * @param text current edit text. - * @param cb callback function to be invoked on OK, taking the updated text - * as an argument. - */ - showEditDialog(msg, text, cb) { - this.dialog.showEditDialog(msg, text, cb); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Select lifeline by ID. - * @param id lifeline ID. - */ - selectLifeline(id) { - if (this.editor) { - this.editor.selectLifeline(id); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Set diagram model. + * @param model diagram instance. + */ + setModel(model) { + Common.assertNotNull(model); + + this.model = model; + + if (this.editor) { + this.editor.render(); + } + + if (this.diagram) { + this.diagram.render(); + } } - if (this.diagram) { - this.diagram.selectLifeline(id); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get Model wrapper. + * @returns Model. + */ + getModel() { + return this.model; } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Select message by ID. - * @param id message ID. - */ - selectMessage(id) { - if (this.editor) { - this.editor.selectMessage(id); + /** + * Get SVG element. + * @returns {*} + */ + getSVG() { + return this.diagram.getSVG(); } - if (this.diagram) { - this.diagram.selectMessage(id); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get top-level widget. Provides the demo toolbar with access to the public API. + * @returns {*} + */ + getSequencer() { + return this.sequencer; } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * (Re)render just the diagram. - */ - renderDiagram() { - this.diagram.redraw(); - } + /** + * Present info dialog. + * @param msg info message. + */ + showInfoDialog(msg) { + this.dialog.showInfoDialog(msg); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Show overlay between application and modal dialog. - */ - showOverlay() { - if (this.overlay) { - this.overlay.setVisible(true); + /** + * Present error dialog. + * @param msg error message. + */ + showErrorDialog(msg) { + this.dialog.showErrorDialog(msg); } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Present confirmation dialog. + * @param msg info message. + * @param cb callback function to be invoked on OK. + */ + showConfirmDialog(msg, cb) { + Common.assertType(cb, 'Function'); + this.dialog.showConfirmDialog(msg, cb); + } - /** - * Hide overlay between application and modal dialog. - */ - hideOverlay() { - if (this.overlay) { - this.overlay.setVisible(false); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Present edit (textarea) dialog. + * @param msg prompt. + * @param text current edit text. + * @param cb callback function to be invoked on OK, taking the updated text + * as an argument. + */ + showEditDialog(msg, text, cb) { + this.dialog.showEditDialog(msg, text, cb); } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Select lifeline by ID. + * @param id lifeline ID. + */ + selectLifeline(id) { + if (this.editor) { + this.editor.selectLifeline(id); + } + if (this.diagram) { + this.diagram.selectLifeline(id); + } + } - /** - * Capture mouse move events, for resize. - * @param event move event. - */ - onMouseMove(event) { - if (this.editor) { - this.editor.onMouseMove(event); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Select message by ID. + * @param id message ID. + */ + selectMessage(id) { + if (this.editor) { + this.editor.selectMessage(id); + } + if (this.diagram) { + this.diagram.selectMessage(id); + } } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Propagate mouse event to the editor that manages the resize. - */ - onMouseUp() { - if (this.editor) { - this.editor.onMouseUp(); + /** + * (Re)render just the diagram. + */ + renderDiagram() { + this.diagram.redraw(); } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Render current model state. - */ - render() { + /** + * Show overlay between application and modal dialog. + */ + showOverlay() { + if (this.overlay) { + this.overlay.setVisible(true); + } + } - return ( + // /////////////////////////////////////////////////////////////////////////////////////////////// - <div className="asdcs-control" onMouseMove={this.onMouseMove} onMouseUp={this.onMouseUp}> + /** + * Hide overlay between application and modal dialog. + */ + hideOverlay() { + if (this.overlay) { + this.overlay.setVisible(false); + } + } - <Editor application={this} ref={(r) => { this.editor = r; }} /> - <Diagram application={this} ref={(r) => { this.diagram = r; }} /> - <Dialog application={this} ref={(r) => { this.dialog = r; }} /> - <Export /> - <Overlay application={this} ref={(r) => { this.overlay = r; }} /> + // /////////////////////////////////////////////////////////////////////////////////////////////// - </div> - ); - } + /** + * Capture mouse move events, for resize. + * @param event move event. + */ + onMouseMove(event) { + if (this.editor) { + this.editor.onMouseMove(event); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Propagate mouse event to the editor that manages the resize. + */ + onMouseUp() { + if (this.editor) { + this.editor.onMouseUp(); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render current model state. + */ + render() { + return ( + <div + className="asdcs-control" + onMouseMove={this.onMouseMove} + onMouseUp={this.onMouseUp}> + <Editor + application={this} + ref={r => { + this.editor = r; + }} + /> + <Diagram + application={this} + ref={r => { + this.diagram = r; + }} + /> + <Dialog + application={this} + ref={r => { + this.dialog = r; + }} + /> + <Export /> + <Overlay + application={this} + ref={r => { + this.overlay = r; + }} + /> + </div> + ); + } } /** React properties. */ Application.propTypes = { - options: PropTypes.object.isRequired, - sequencer: PropTypes.object.isRequired, + options: PropTypes.object.isRequired, + sequencer: PropTypes.object.isRequired }; export default Application; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/Diagram.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/Diagram.jsx index b3544d7066..129a1afc2b 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/Diagram.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/Diagram.jsx @@ -28,898 +28,1013 @@ import Popup from './components/popup/Popup'; * SVG diagram view. */ class Diagram extends React.Component { - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct React view. - * @param props properties. - * @param context context. - */ - constructor(props, context) { - super(props, context); - - this.application = Common.assertNotNull(props.application); - this.options = this.application.getOptions().diagram; - - this.events = {}; - this.state = { - height: 0, - width: 0, - }; - - this.templates = { - diagram: _template(require('./templates/diagram.html')), - lifeline: _template(require('./templates/lifeline.html')), - message: _template(require('./templates/message.html')), - occurrence: _template(require('./templates/occurrence.html')), - fragment: _template(require('./templates/fragment.html')), - title: _template(require('./templates/title.html')), - }; - - this.handleResize = this.handleResize.bind(this); - this.initialTransformX = 0; - this.initialTransformY = 0; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Set diagram name. - * @param n name. - */ - setName(n) { - this.svg.select('').text(n); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get SVG from diagram. - * @returns {*|string} - */ - getSVG() { - const svg = this.svg.node().outerHTML; - return svg.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" '); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Select message by ID. - * @param id message ID. - */ - selectMessage(id) { - const sel = this.svg.selectAll('g.asdcs-diagram-message-container'); - sel.classed('asdcs-active', false); - sel.selectAll('rect.asdcs-diagram-message-bg').attr('filter', null); - if (id) { - const parent = this.svg.select(`g.asdcs-diagram-message-container[data-id="${id}"]`); - parent.classed('asdcs-active', true); - parent.selectAll('rect.asdcs-diagram-message-bg').attr('filter', 'url(#asdcsSvgHighlight)'); - } - this._showNotesPopup(id); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Select lifeline by ID. - * @param id lifeline ID. - */ - selectLifeline(id) { - const sel = this.svg.selectAll('g.asdcs-diagram-lifeline-container'); - sel.classed('asdcs-active', false); - sel.selectAll('rect').attr('filter', null); - if (id) { - const parent = this.svg.select(`g.asdcs-diagram-lifeline-container[data-id="${id}"]`); - parent.selectAll('rect').attr('filter', 'url(#asdcsSvgHighlight)'); - parent.classed('asdcs-active', true); - } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle resize, including initial sizing. - */ - handleResize() { - if (this.wrapper) { - const height = this.wrapper.offsetHeight; - const width = this.wrapper.offsetWidth; - if (this.state.height !== height || this.state.width !== width) { - this.setState({ height, width }); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct React view. + * @param props properties. + * @param context context. + */ + constructor(props, context) { + super(props, context); + + this.application = Common.assertNotNull(props.application); + this.options = this.application.getOptions().diagram; + + this.events = {}; + this.state = { + height: 0, + width: 0 + }; + + this.templates = { + diagram: _template(require('./templates/diagram.html')), + lifeline: _template(require('./templates/lifeline.html')), + message: _template(require('./templates/message.html')), + occurrence: _template(require('./templates/occurrence.html')), + fragment: _template(require('./templates/fragment.html')), + title: _template(require('./templates/title.html')) + }; + + this.handleResize = this.handleResize.bind(this); + this.initialTransformX = 0; + this.initialTransformY = 0; } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * (Re)render diagram. - */ - render() { - - const model = this.application.getModel(); - const modelJSON = model.unwrap(); - const name = modelJSON.diagram.metadata.name; - const options = this.application.getOptions(); - const titleHeight = options.diagram.title.height; - const titleClass = (titleHeight && titleHeight > 0) ? `height:${titleHeight}` : 'asdcs-hidden'; - - return ( - <div className="asdcs-diagram"> - <div className={`asdcs-diagram-name ${titleClass}`}>{name}</div> - <div className="asdcs-diagram-svg" ref={(r) => { this.wrapper = r; }}></div> - <Popup visible={false} ref={(r) => { this.popup = r; }} /> - </div> - ); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - redraw() { - this.updateSVG(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Initial render. - */ - componentDidMount() { - window.addEventListener('resize', this.handleResize); - this.updateSVG(); - - // Insurance: - - setTimeout(() => { - this.handleResize(); - }, 500); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - componentWillUnmount() { - window.removeEventListener('resize', this.handleResize); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render on update. - */ - componentDidUpdate() { - this.updateSVG(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Redraw SVG diagram. So far it's fast enough that it doesn't seem to matter whether - * it's completely redrawn. - */ - updateSVG() { - - if (!this.svg) { - const svgparams = _merge({}, this.options.svg); - this.wrapper.innerHTML = this.templates.diagram(svgparams); - this.svg = d3.select(this.wrapper).select('svg'); - } - - if (this.state.height === 0) { - - // We'll get a resize event, and the height will be non-zero when it's actually time. - return; - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - if (this.state.height && this.state.width) { - const margin = this.options.svg.margin; - const x = -margin; - const y = -margin; - const height = this.state.height + (margin * 2); - const width = this.state.width + (margin * 2); - const viewBox = `${x} ${y} ${width} ${height}`; - this.svg.attr('viewBox', viewBox); + /** + * Set diagram name. + * @param n name. + */ + setName(n) { + this.svg.select('').text(n); } + // /////////////////////////////////////////////////////////////////////////////////////////////// - // If we've already rendered, then save the current scale/translate so that we - // can reapply it after rendering. - - const gContentSelection = this.svg.selectAll('g.asdcs-diagram-content'); - if (gContentSelection.size() === 1) { - const transform = gContentSelection.attr('transform'); - if (transform) { - this.savedTransform = transform; - } + /** + * Get SVG from diagram. + * @returns {*|string} + */ + getSVG() { + const svg = this.svg.node().outerHTML; + return svg.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" '); } - // Empty the document. We're starting again. - - this.svg.selectAll('.asdcs-diagram-content').remove(); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Select message by ID. + * @param id message ID. + */ + selectMessage(id) { + const sel = this.svg.selectAll('g.asdcs-diagram-message-container'); + sel.classed('asdcs-active', false); + sel.selectAll('rect.asdcs-diagram-message-bg').attr('filter', null); + if (id) { + const parent = this.svg.select( + `g.asdcs-diagram-message-container[data-id="${id}"]` + ); + parent.classed('asdcs-active', true); + parent + .selectAll('rect.asdcs-diagram-message-bg') + .attr('filter', 'url(#asdcsSvgHighlight)'); + } + this._showNotesPopup(id); + } - // Extract the model. + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Select lifeline by ID. + * @param id lifeline ID. + */ + selectLifeline(id) { + const sel = this.svg.selectAll('g.asdcs-diagram-lifeline-container'); + sel.classed('asdcs-active', false); + sel.selectAll('rect').attr('filter', null); + if (id) { + const parent = this.svg.select( + `g.asdcs-diagram-lifeline-container[data-id="${id}"]` + ); + parent.selectAll('rect').attr('filter', 'url(#asdcsSvgHighlight)'); + parent.classed('asdcs-active', true); + } + } - const model = this.application.getModel(); - if (!model) { - return; + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle resize, including initial sizing. + */ + handleResize() { + if (this.wrapper) { + const height = this.wrapper.offsetHeight; + const width = this.wrapper.offsetWidth; + if (this.state.height !== height || this.state.width !== width) { + this.setState({ height, width }); + } + } } - const modelJSON = model.unwrap(); - // Extract dimension options. + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * (Re)render diagram. + */ + render() { + const model = this.application.getModel(); + const modelJSON = model.unwrap(); + const name = modelJSON.diagram.metadata.name; + const options = this.application.getOptions(); + const titleHeight = options.diagram.title.height; + const titleClass = + titleHeight && titleHeight > 0 + ? `height:${titleHeight}` + : 'asdcs-hidden'; + + return ( + <div className="asdcs-diagram"> + <div className={`asdcs-diagram-name ${titleClass}`}>{name}</div> + <div + className="asdcs-diagram-svg" + ref={r => { + this.wrapper = r; + }} + /> + <Popup + visible={false} + ref={r => { + this.popup = r; + }} + /> + </div> + ); + } - const header = this.options.lifelines.header; - const spacing = this.options.lifelines.spacing; + // /////////////////////////////////////////////////////////////////////////////////////////////// - // Make separate container elements so that we can control Z order. + redraw() { + this.updateSVG(); + } - const gContent = this.svg.append('g').attr('class', 'asdcs-diagram-content'); - const gLifelines = gContent.append('g').attr('class', 'asdcs-diagram-lifelines'); - const gCanvas = gContent.append('g').attr('class', 'asdcs-diagram-canvas'); - gCanvas.append('g').attr('class', 'asdcs-diagram-occurrences'); - gCanvas.append('g').attr('class', 'asdcs-diagram-fragments'); - gCanvas.append('g').attr('class', 'asdcs-diagram-messages'); + // /////////////////////////////////////////////////////////////////////////////////////////////// - // Lifelines ----------------------------------------------------------------------------------- + /** + * Initial render. + */ + componentDidMount() { + window.addEventListener('resize', this.handleResize); + this.updateSVG(); - const actorsById = {}; - const positionsByMessageId = {}; - const lifelines = []; - for (const actor of modelJSON.diagram.lifelines) { - const x = (header.width / 2) + (lifelines.length * spacing.horizontal); - Diagram._processLifeline(actor, x); - lifelines.push({ x, actor }); - actorsById[actor.id] = actor; - } + // Insurance: - // Messages ------------------------------------------------------------------------------------ - - // Analyze occurrence information. - - const occurrences = model.analyzeOccurrences(); - const fragments = model.analyzeFragments(); - let y = this.options.lifelines.header.height + spacing.vertical; - let messageIndex = 0; - for (const step of modelJSON.diagram.steps) { - if (step.message) { - positionsByMessageId[step.message.id] = positionsByMessageId[step.message.id] || {}; - positionsByMessageId[step.message.id].y = y; - this._drawMessage(gCanvas, step.message, y, actorsById, - positionsByMessageId, ++messageIndex, occurrences, fragments); - } - y += spacing.vertical; + setTimeout(() => { + this.handleResize(); + }, 500); } - // --------------------------------------------------------------------------------------------- - - // Draw the actual (dashed) lifelines in a background <g>. - - this._drawLifelines(gLifelines, lifelines, y); - - // Initialize mouse event handlers. - - this._initMouseEvents(gLifelines, gCanvas); - - // Scale to fit. - - const bb = gContent.node().getBBox(); - this._initZoom(gContent, bb.width, bb.height); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Draw message into SVG canvas. - * @param gCanvas container. - * @param message message to be rendered. - * @param y current y position. - * @param actorsById actor lookup. - * @param positionsByMessageId x- and y-position of each message. - * @param messageIndex where we are in the set of messages to be rendered. - * @param oData occurrences info. - * @param fData fragments info. - * @private - */ - _drawMessage(gCanvas, message, y, actorsById, positionsByMessageId, - messageIndex, oData, fData) { - - Common.assertNotNull(oData); - - const request = message.type === 'request'; - const fromActor = request ? actorsById[message.from] : actorsById[message.to]; - const toActor = request ? actorsById[message.to] : actorsById[message.from]; - - if (!fromActor) { - Logger.warn(`Cannot draw message ${JSON.stringify(message)}: 'from' not found.`); - return; - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - if (!toActor) { - Logger.warn(`Cannot draw message ${JSON.stringify(message)}: 'to' not found.`); - return; + componentWillUnmount() { + window.removeEventListener('resize', this.handleResize); } - // Occurrences. -------------------------------------------------------------------------------- + // /////////////////////////////////////////////////////////////////////////////////////////////// - if (message.occurrence) { - Logger.debug(`Found occurrence for ${message.name}: ${JSON.stringify(message.occurrence)}`); + /** + * Render on update. + */ + componentDidUpdate() { + this.updateSVG(); } - const activeTo = Diagram._calcActive(oData, toActor.id); - this._drawOccurrence(gCanvas, oData, positionsByMessageId, fromActor, message.id); - this._drawOccurrence(gCanvas, oData, positionsByMessageId, toActor, message.id); - const activeFrom = Diagram._calcActive(oData, fromActor.id); - - // Messages. ----------------------------------------------------------------------------------- - - const gMessages = gCanvas.select('g.asdcs-diagram-messages'); - - // Save positions for later. - - const positions = positionsByMessageId[message.id]; - positions.x0 = fromActor.x; - positions.x1 = toActor.x; - - // Calculate. - const leftToRight = fromActor.x < toActor.x; - const loopback = (message.to === message.from); - const x1 = this._calcMessageX(activeTo, toActor.x, true, leftToRight); - const x0 = loopback ? x1 : this._calcMessageX(activeFrom, fromActor.x, false, leftToRight); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Redraw SVG diagram. So far it's fast enough that it doesn't seem to matter whether + * it's completely redrawn. + */ + updateSVG() { + if (!this.svg) { + const svgparams = _merge({}, this.options.svg); + this.wrapper.innerHTML = this.templates.diagram(svgparams); + this.svg = d3.select(this.wrapper).select('svg'); + } - let messagePath; - if (loopback) { + if (this.state.height === 0) { + // We'll get a resize event, and the height will be non-zero when it's actually time. - // To self. + return; + } - messagePath = `M${x1},${y}`; - messagePath = `${messagePath} L${x1 + 200},${y}`; - messagePath = `${messagePath} L${x1 + 200},${y + 50}`; - messagePath = `${messagePath} L${x1},${y + 50}`; - } else { + if (this.state.height && this.state.width) { + const margin = this.options.svg.margin; + const x = -margin; + const y = -margin; + const height = this.state.height + margin * 2; + const width = this.state.width + margin * 2; + const viewBox = `${x} ${y} ${width} ${height}`; + this.svg.attr('viewBox', viewBox); + } - // Between lifelines. + // If we've already rendered, then save the current scale/translate so that we + // can reapply it after rendering. - messagePath = `M${x0},${y}`; - messagePath = `${messagePath} L${x1},${y}`; - } + const gContentSelection = this.svg.selectAll('g.asdcs-diagram-content'); + if (gContentSelection.size() === 1) { + const transform = gContentSelection.attr('transform'); + if (transform) { + this.savedTransform = transform; + } + } - const styles = Diagram._getMessageStyles(message); + // Empty the document. We're starting again. - // Split message over lines. + this.svg.selectAll('.asdcs-diagram-content').remove(); - const messageWithPrefix = `${messageIndex}. ${message.name}`; - const maxLines = this.options.messages.label.maxLines; - const wrapWords = this.options.messages.label.wrapWords; - const wrapLines = this.options.messages.label.wrapLines; - const messageLines = Common.tokenize(messageWithPrefix, wrapWords, wrapLines, maxLines); + // Extract the model. - const messageTxt = this.templates.message({ - id: message.id, - classes: styles.css, - marker: styles.marker, - dasharray: styles.dasharray, - labels: messageLines, - lines: maxLines, - path: messagePath, - index: messageIndex, - x0, x1, y, - }); + const model = this.application.getModel(); + if (!model) { + return; + } + const modelJSON = model.unwrap(); + + // Extract dimension options. + + const header = this.options.lifelines.header; + const spacing = this.options.lifelines.spacing; + + // Make separate container elements so that we can control Z order. + + const gContent = this.svg + .append('g') + .attr('class', 'asdcs-diagram-content'); + const gLifelines = gContent + .append('g') + .attr('class', 'asdcs-diagram-lifelines'); + const gCanvas = gContent + .append('g') + .attr('class', 'asdcs-diagram-canvas'); + gCanvas.append('g').attr('class', 'asdcs-diagram-occurrences'); + gCanvas.append('g').attr('class', 'asdcs-diagram-fragments'); + gCanvas.append('g').attr('class', 'asdcs-diagram-messages'); + + // Lifelines ----------------------------------------------------------------------------------- + + const actorsById = {}; + const positionsByMessageId = {}; + const lifelines = []; + for (const actor of modelJSON.diagram.lifelines) { + const x = header.width / 2 + lifelines.length * spacing.horizontal; + Diagram._processLifeline(actor, x); + lifelines.push({ x, actor }); + actorsById[actor.id] = actor; + } - const messageEl = Common.txt2dom(messageTxt); - const gMessage = gMessages.append('g'); - Common.dom2svg(messageEl, gMessage); + // Messages ------------------------------------------------------------------------------------ + + // Analyze occurrence information. + + const occurrences = model.analyzeOccurrences(); + const fragments = model.analyzeFragments(); + let y = this.options.lifelines.header.height + spacing.vertical; + let messageIndex = 0; + for (const step of modelJSON.diagram.steps) { + if (step.message) { + positionsByMessageId[step.message.id] = + positionsByMessageId[step.message.id] || {}; + positionsByMessageId[step.message.id].y = y; + this._drawMessage( + gCanvas, + step.message, + y, + actorsById, + positionsByMessageId, + ++messageIndex, + occurrences, + fragments + ); + } + y += spacing.vertical; + } - // Set the background's bounding box to that of the text, - // so that they fit snugly. + // --------------------------------------------------------------------------------------------- - const labelBB = gMessage.select('.asdcs-diagram-message-label').node().getBBox(); - gMessage.select('.asdcs-diagram-message-label-bg') - .attr('x', labelBB.x) - .attr('y', labelBB.y) - .attr('height', labelBB.height) - .attr('width', labelBB.width); + // Draw the actual (dashed) lifelines in a background <g>. - // Fragments. ---------------------------------------------------------------------------------- + this._drawLifelines(gLifelines, lifelines, y); - const fragment = fData[message.id]; - if (fragment) { + // Initialize mouse event handlers. - // It ends on this message. + this._initMouseEvents(gLifelines, gCanvas); - this._drawFragment(gCanvas, fragment, positionsByMessageId); + // Scale to fit. + const bb = gContent.node().getBBox(); + this._initZoom(gContent, bb.width, bb.height); } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw message into SVG canvas. + * @param gCanvas container. + * @param message message to be rendered. + * @param y current y position. + * @param actorsById actor lookup. + * @param positionsByMessageId x- and y-position of each message. + * @param messageIndex where we are in the set of messages to be rendered. + * @param oData occurrences info. + * @param fData fragments info. + * @private + */ + _drawMessage( + gCanvas, + message, + y, + actorsById, + positionsByMessageId, + messageIndex, + oData, + fData + ) { + Common.assertNotNull(oData); + + const request = message.type === 'request'; + const fromActor = request + ? actorsById[message.from] + : actorsById[message.to]; + const toActor = request + ? actorsById[message.to] + : actorsById[message.from]; + + if (!fromActor) { + Logger.warn( + `Cannot draw message ${JSON.stringify( + message + )}: 'from' not found.` + ); + return; + } - /** - * Draw a single occurrence. - * @param gCanvas container. - * @param oData occurrence data. - * @param positionsByMessageId map of y positions by message ID. - * @param actor wrapper containing lifeline ID (.id), position (.x) and name (.name). - * @param messageId message identifier. - * @private - */ - _drawOccurrence(gCanvas, oData, positionsByMessageId, actor, messageId) { + if (!toActor) { + Logger.warn( + `Cannot draw message ${JSON.stringify( + message + )}: 'to' not found.` + ); + return; + } - Common.assertType(oData, 'Object'); - Common.assertType(positionsByMessageId, 'Object'); - Common.assertType(actor, 'Object'); - Common.assertType(messageId, 'String'); + // Occurrences. -------------------------------------------------------------------------------- - const gOccurrences = gCanvas.select('g.asdcs-diagram-occurrences'); + if (message.occurrence) { + Logger.debug( + `Found occurrence for ${message.name}: ${JSON.stringify( + message.occurrence + )}` + ); + } + const activeTo = Diagram._calcActive(oData, toActor.id); + this._drawOccurrence( + gCanvas, + oData, + positionsByMessageId, + fromActor, + message.id + ); + this._drawOccurrence( + gCanvas, + oData, + positionsByMessageId, + toActor, + message.id + ); + const activeFrom = Diagram._calcActive(oData, fromActor.id); + + // Messages. ----------------------------------------------------------------------------------- + + const gMessages = gCanvas.select('g.asdcs-diagram-messages'); + + // Save positions for later. + + const positions = positionsByMessageId[message.id]; + positions.x0 = fromActor.x; + positions.x1 = toActor.x; + + // Calculate. + + const leftToRight = fromActor.x < toActor.x; + const loopback = message.to === message.from; + const x1 = this._calcMessageX(activeTo, toActor.x, true, leftToRight); + const x0 = loopback + ? x1 + : this._calcMessageX(activeFrom, fromActor.x, false, leftToRight); + + let messagePath; + if (loopback) { + // To self. + + messagePath = `M${x1},${y}`; + messagePath = `${messagePath} L${x1 + 200},${y}`; + messagePath = `${messagePath} L${x1 + 200},${y + 50}`; + messagePath = `${messagePath} L${x1},${y + 50}`; + } else { + // Between lifelines. - const oOptions = this.options.lifelines.occurrences; - const oWidth = oOptions.width; - const oHalfWidth = oWidth / 2; - const oForeshortening = oOptions.foreshortening; - const oMarginTop = oOptions.marginTop; - const oMarginBottom = oOptions.marginBottom; - const o = oData[actor.id]; + messagePath = `M${x0},${y}`; + messagePath = `${messagePath} L${x1},${y}`; + } - const active = Diagram._calcActive(oData, actor.id); + const styles = Diagram._getMessageStyles(message); + + // Split message over lines. + + const messageWithPrefix = `${messageIndex}. ${message.name}`; + const maxLines = this.options.messages.label.maxLines; + const wrapWords = this.options.messages.label.wrapWords; + const wrapLines = this.options.messages.label.wrapLines; + const messageLines = Common.tokenize( + messageWithPrefix, + wrapWords, + wrapLines, + maxLines + ); + + const messageTxt = this.templates.message({ + id: message.id, + classes: styles.css, + marker: styles.marker, + dasharray: styles.dasharray, + labels: messageLines, + lines: maxLines, + path: messagePath, + index: messageIndex, + x0, + x1, + y + }); + + const messageEl = Common.txt2dom(messageTxt); + const gMessage = gMessages.append('g'); + Common.dom2svg(messageEl, gMessage); + + // Set the background's bounding box to that of the text, + // so that they fit snugly. + + const labelBB = gMessage + .select('.asdcs-diagram-message-label') + .node() + .getBBox(); + gMessage + .select('.asdcs-diagram-message-label-bg') + .attr('x', labelBB.x) + .attr('y', labelBB.y) + .attr('height', labelBB.height) + .attr('width', labelBB.width); - const x = (actor.x - oHalfWidth) + (active * oWidth); - const positions = positionsByMessageId[messageId]; - const y = positions.y; + // Fragments. ---------------------------------------------------------------------------------- - let draw = true; - if (o) { + const fragment = fData[message.id]; + if (fragment) { + // It ends on this message. - if (o.start[messageId]) { + this._drawFragment(gCanvas, fragment, positionsByMessageId); + } + } - // Starting, but drawing nothing until we find the end. + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw a single occurrence. + * @param gCanvas container. + * @param oData occurrence data. + * @param positionsByMessageId map of y positions by message ID. + * @param actor wrapper containing lifeline ID (.id), position (.x) and name (.name). + * @param messageId message identifier. + * @private + */ + _drawOccurrence(gCanvas, oData, positionsByMessageId, actor, messageId) { + Common.assertType(oData, 'Object'); + Common.assertType(positionsByMessageId, 'Object'); + Common.assertType(actor, 'Object'); + Common.assertType(messageId, 'String'); + + const gOccurrences = gCanvas.select('g.asdcs-diagram-occurrences'); + + const oOptions = this.options.lifelines.occurrences; + const oWidth = oOptions.width; + const oHalfWidth = oWidth / 2; + const oForeshortening = oOptions.foreshortening; + const oMarginTop = oOptions.marginTop; + const oMarginBottom = oOptions.marginBottom; + const o = oData[actor.id]; + + const active = Diagram._calcActive(oData, actor.id); + + const x = actor.x - oHalfWidth + active * oWidth; + const positions = positionsByMessageId[messageId]; + const y = positions.y; + + let draw = true; + if (o) { + if (o.start[messageId]) { + // Starting, but drawing nothing until we find the end. + + o.active.push(messageId); + draw = false; + } else if (active > 0) { + const startMessageId = o.stop[messageId]; + if (startMessageId) { + // OK, it ends here. Draw the occurrence box. + + o.active.pop(); + const foreshorteningY = active * oForeshortening; + const startY = positionsByMessageId[startMessageId].y; + const height = + oMarginTop + + oMarginBottom + + (y - startY) - + foreshorteningY * 2; + const oProps = { + x: actor.x - oHalfWidth + (active - 1) * oWidth, + y: startY - oMarginTop + foreshorteningY, + height, + width: oWidth + }; + + const occurrenceTxt = this.templates.occurrence(oProps); + const occurrenceEl = Common.txt2dom(occurrenceTxt); + Common.dom2svg(occurrenceEl, gOccurrences.append('g')); + } + draw = false; + } + } - o.active.push(messageId); - draw = false; + if (draw) { + // Seems this is a singleton occurrence. We just draw a wee box around it. + + const height = oMarginTop + oMarginBottom; + const occurrenceProperties = { + x, + y: y - oMarginTop, + height, + width: oWidth + }; + const defaultTxt = this.templates.occurrence(occurrenceProperties); + const defaultEl = Common.txt2dom(defaultTxt); + Common.dom2svg(defaultEl, gOccurrences.append('g')); + } + } - } else if (active > 0) { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw box(es) around fragment(s). + * @param gCanvas container. + * @param fragment fragment definition, corresponding to its final (stop) message. + * @param positionsByMessageId message dimensions. + * @private + */ + _drawFragment(gCanvas, fragment, positionsByMessageId) { + const optFragments = this.options.fragments; + const gFragments = gCanvas.select('g.asdcs-diagram-fragments'); + const p1 = positionsByMessageId[fragment.stop]; + if (p1 && fragment.start && fragment.start.length > 0) { + for (const start of fragment.start) { + const message = this.application + .getModel() + .getMessageById(start); + const bounds = this._calcFragmentBounds( + message, + fragment, + positionsByMessageId + ); + if (bounds) { + const maxLines = this.options.fragments.label.maxLines; + const wrapWords = this.options.fragments.label.wrapWords; + const wrapLines = this.options.fragments.label.wrapLines; + const lines = Common.tokenize( + message.fragment.guard, + wrapWords, + wrapLines, + maxLines + ); + + const params = { + id: start, + x: bounds.x0 - optFragments.leftMargin, + y: bounds.y0 - optFragments.topMargin, + height: + bounds.y1 - bounds.y0 + optFragments.heightMargin, + width: bounds.x1 - bounds.x0 + optFragments.widthMargin, + operator: message.fragment.operator || 'alt', + lines + }; + + const fragmentTxt = this.templates.fragment(params); + const fragmentEl = Common.txt2dom(fragmentTxt); + const gFragment = gFragments.append('g'); + Common.dom2svg(fragmentEl, gFragment); + + const labelBB = gFragment + .select('.asdcs-diagram-fragment-guard') + .node() + .getBBox(); + gFragment + .select('.asdcs-diagram-fragment-guard-bg') + .attr('x', labelBB.x) + .attr('y', labelBB.y) + .attr('height', labelBB.height) + .attr('width', labelBB.width); + } else { + Logger.warn(`Bad fragment: ${JSON.stringify(fragment)}`); + } + } + } + } - const startMessageId = o.stop[messageId]; - if (startMessageId) { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + _calcFragmentBounds(startMessage, fragment, positionsByMessageId) { + if (startMessage) { + const steps = this.application.getModel().unwrap().diagram.steps; + const bounds = { x0: 99999, x1: 0, y0: 99999, y1: 0 }; + let foundStart = false; + let foundStop = false; + for (const step of steps) { + const message = step.message; + if (message) { + if (message.id === startMessage.id) { + foundStart = true; + } + if (foundStart && !foundStop) { + const positions = positionsByMessageId[message.id]; + if (positions) { + bounds.x0 = Math.min( + bounds.x0, + Math.min(positions.x0, positions.x1) + ); + bounds.y0 = Math.min(bounds.y0, positions.y); + bounds.x1 = Math.max( + bounds.x1, + Math.max(positions.x0, positions.x1) + ); + bounds.y1 = Math.max(bounds.y1, positions.y); + } else { + // This probably means it hasn't been recorded yet, which is fine, because + // we draw fragments from where they END. + foundStop = true; + } + } + + if (message.id === fragment.stop) { + foundStop = true; + } + } + } + return bounds; + } + return undefined; + } - // OK, it ends here. Draw the occurrence box. + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw all lifelines. + * @param gLifelines lifelines container. + * @param lifelines lifelines definitions. + * @param y height. + * @private + */ + _drawLifelines(gLifelines, lifelines, y) { + const maxLines = this.options.lifelines.header.maxLines; + const wrapWords = this.options.lifelines.header.wrapWords; + const wrapLines = this.options.lifelines.header.wrapLines; + + for (const lifeline of lifelines) { + const lines = Common.tokenize( + lifeline.actor.name, + wrapWords, + wrapLines, + maxLines + ); + const lifelineTxt = this.templates.lifeline({ + x: lifeline.x, + y0: 0, + y1: y, + lines, + rows: maxLines, + headerHeight: this.options.lifelines.header.height, + headerWidth: this.options.lifelines.header.width, + id: lifeline.actor.id + }); + + const lifelineEl = Common.txt2dom(lifelineTxt); + Common.dom2svg(lifelineEl, gLifelines.append('g')); + } + } - o.active.pop(); - const foreshorteningY = active * oForeshortening; - const startY = positionsByMessageId[startMessageId].y; - const height = ((oMarginTop + oMarginBottom) + (y - startY)) - (foreshorteningY * 2); - const oProps = { - x: (actor.x - oHalfWidth) + ((active - 1) * oWidth), - y: ((startY - oMarginTop) + foreshorteningY), - height, - width: oWidth, - }; + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Initialize all mouse events. + * @param gLifelines lifelines container. + * @param gCanvas top-level canvas container. + * @private + */ + _initMouseEvents(gLifelines, gCanvas) { + const self = this; + const source = 'asdcs'; + const origin = `${window.location.protocol}//${window.location.host}`; + + let timer; + gLifelines + .selectAll('.asdcs-diagram-lifeline-selectable') + .on('mouseenter', function f() { + timer = setTimeout(() => { + self.application.selectLifeline( + d3.select(this.parentNode).attr('data-id') + ); + }, 150); + }) + .on('mouseleave', () => { + clearTimeout(timer); + self.application.selectLifeline(); + }) + .on('click', function f() { + const id = d3.select(this.parentNode).attr('data-id'); + window.postMessage({ source, id, type: 'lifeline' }, origin); + }); + + gLifelines + .selectAll('.asdcs-diagram-lifeline-heading-box') + .on('mouseenter', function f() { + timer = setTimeout(() => { + self.application.selectLifeline( + d3.select(this.parentNode).attr('data-id') + ); + }, 150); + }) + .on('mouseleave', () => { + clearTimeout(timer); + self.application.selectLifeline(); + }) + .on('click', function f() { + const id = d3.select(this.parentNode).attr('data-id'); + window.postMessage( + { source, id, type: 'lifelineHeader' }, + origin + ); + }); + + gCanvas + .selectAll('.asdcs-diagram-message-selectable') + .on('mouseenter', function f() { + self.events.message = { x: d3.event.pageX, y: d3.event.pageY }; + timer = setTimeout(() => { + self.application.selectMessage( + d3.select(this.parentNode).attr('data-id') + ); + }, 200); + }) + .on('mouseleave', () => { + delete self.events.message; + clearTimeout(timer); + self.application.selectMessage(); + }) + .on('click', function f() { + const id = d3.select(this.parentNode).attr('data-id'); + window.postMessage({ source, id, type: 'message' }, origin); + }); + } - const occurrenceTxt = this.templates.occurrence(oProps); - const occurrenceEl = Common.txt2dom(occurrenceTxt); - Common.dom2svg(occurrenceEl, gOccurrences.append('g')); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get CSS classes to be applied to a message, according to whether request/response + * or synchronous/asynchronous. + * @param message message being rendered. + * @returns CSS class name(s). + * @private + */ + static _getMessageStyles(message) { + let marker = 'asdcsDiagramArrowSolid'; + let dasharray = ''; + let css = 'asdcs-diagram-message'; + if (message.type === 'request') { + css = `${css} asdcs-diagram-message-request`; + } else { + css = `${css} asdcs-diagram-message-response`; + marker = 'asdcsDiagramArrowOpen'; + dasharray = '30, 10'; + } + if (message.asynchronous) { + css = `${css} asdcs-diagram-message-asynchronous`; + marker = 'asdcsDiagramArrowOpen'; + } else { + css = `${css} asdcs-diagram-message-synchronous`; } - draw = false; - } + + return { css, marker, dasharray }; } - if (draw) { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Initialize or reinitialize zoom. This sets the initial zoom in the case of + * a re-rendering, and initializes the eventhandling in all cases. + * + * It does some fairly risky parsing of the 'transform' attribute, assuming that it + * can contain scale() and translate(). But only the zoom handler and us are writing + * the transform values, so that's probably OK. + * + * @param gContent container. + * @param width diagram width. + * @param height diagram height. + * @private + */ + _initZoom(gContent, width, height) { + const zoomed = function zoomed() { + if (!this.initialTransformX && !this.initialTransformY) { + this.initialTransformX = d3.event.transform.x; + this.initialTransformY = d3.event.transform.y; + } - // Seems this is a singleton occurrence. We just draw a wee box around it. + gContent.attr( + 'transform', + `translate(${d3.event.transform.x - + this.initialTransformX}, ${d3.event.transform.y - + this.initialTransformY})scale(${d3.event.transform.k}, ${ + d3.event.transform.k + })` + ); + }; + + const viewWidth = this.state.width || this.options.svg.width; + const viewHeight = this.state.height || this.options.svg.height; + const scaleMinimum = this.options.svg.scale.minimum; + const scaleWidth = viewWidth / width; + const scaleHeight = viewHeight / height; + + let scale = scaleMinimum; + if (this.options.svg.scale.width) { + scale = Math.max(scale, scaleWidth); + } + if (this.options.svg.scale.height) { + scale = Math.min(scale, scaleHeight); + } - const height = (oMarginTop + oMarginBottom); - const occurrenceProperties = { x, y: y - oMarginTop, height, width: oWidth }; - const defaultTxt = this.templates.occurrence(occurrenceProperties); - const defaultEl = Common.txt2dom(defaultTxt); - Common.dom2svg(defaultEl, gOccurrences.append('g')); - } + scale = Math.max(scale, scaleMinimum); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Draw box(es) around fragment(s). - * @param gCanvas container. - * @param fragment fragment definition, corresponding to its final (stop) message. - * @param positionsByMessageId message dimensions. - * @private - */ - _drawFragment(gCanvas, fragment, positionsByMessageId) { - - const optFragments = this.options.fragments; - const gFragments = gCanvas.select('g.asdcs-diagram-fragments'); - const p1 = positionsByMessageId[fragment.stop]; - if (p1 && fragment.start && fragment.start.length > 0) { - - for (const start of fragment.start) { - - const message = this.application.getModel().getMessageById(start); - const bounds = this._calcFragmentBounds(message, fragment, positionsByMessageId); - if (bounds) { - - const maxLines = this.options.fragments.label.maxLines; - const wrapWords = this.options.fragments.label.wrapWords; - const wrapLines = this.options.fragments.label.wrapLines; - const lines = Common.tokenize(message.fragment.guard, wrapWords, wrapLines, maxLines); - - const params = { - id: start, - x: bounds.x0 - optFragments.leftMargin, - y: bounds.y0 - optFragments.topMargin, - height: (bounds.y1 - bounds.y0) + optFragments.heightMargin, - width: (bounds.x1 - bounds.x0) + optFragments.widthMargin, - operator: (message.fragment.operator || 'alt'), - lines, - }; - - const fragmentTxt = this.templates.fragment(params); - const fragmentEl = Common.txt2dom(fragmentTxt); - const gFragment = gFragments.append('g'); - Common.dom2svg(fragmentEl, gFragment); - - const labelBB = gFragment.select('.asdcs-diagram-fragment-guard').node().getBBox(); - gFragment.select('.asdcs-diagram-fragment-guard-bg') - .attr('x', labelBB.x) - .attr('y', labelBB.y) - .attr('height', labelBB.height) - .attr('width', labelBB.width); + let translate = [0, 0]; + if (this.savedTransform) { + const s = this.savedTransform; + const scaleStart = s.indexOf('scale('); + if (scaleStart !== -1) { + scale = parseFloat(s.substring(scaleStart + 6, s.length - 1)); + } + const translateStart = s.indexOf('translate('); + if (translateStart !== -1) { + const spec = s.substring( + translateStart + 10, + s.indexOf(')', translateStart) + ); + const tokens = spec.split(','); + translate = [parseFloat(tokens[0]), parseFloat(tokens[1])]; + } + gContent.attr('transform', this.savedTransform); } else { - Logger.warn(`Bad fragment: ${JSON.stringify(fragment)}`); + gContent.attr('transform', `scale(${scale})`); } - } - } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - _calcFragmentBounds(startMessage, fragment, positionsByMessageId) { - if (startMessage) { - const steps = this.application.getModel().unwrap().diagram.steps; - const bounds = { x0: 99999, x1: 0, y0: 99999, y1: 0 }; - let foundStart = false; - let foundStop = false; - for (const step of steps) { - const message = step.message; - if (message) { - if (message.id === startMessage.id) { - foundStart = true; - } - if (foundStart && !foundStop) { - const positions = positionsByMessageId[message.id]; - if (positions) { - bounds.x0 = Math.min(bounds.x0, Math.min(positions.x0, positions.x1)); - bounds.y0 = Math.min(bounds.y0, positions.y); - bounds.x1 = Math.max(bounds.x1, Math.max(positions.x0, positions.x1)); - bounds.y1 = Math.max(bounds.y1, positions.y); - } else { - // This probably means it hasn't been recorded yet, which is fine, because - // we draw fragments from where they END. - foundStop = true; - } - } - if (message.id === fragment.stop) { - foundStop = true; - } - } - } - return bounds; - } - return undefined; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Draw all lifelines. - * @param gLifelines lifelines container. - * @param lifelines lifelines definitions. - * @param y height. - * @private - */ - _drawLifelines(gLifelines, lifelines, y) { - - const maxLines = this.options.lifelines.header.maxLines; - const wrapWords = this.options.lifelines.header.wrapWords; - const wrapLines = this.options.lifelines.header.wrapLines; - - for (const lifeline of lifelines) { - const lines = Common.tokenize(lifeline.actor.name, wrapWords, wrapLines, maxLines); - const lifelineTxt = this.templates.lifeline({ - x: lifeline.x, - y0: 0, - y1: y, - lines, - rows: maxLines, - headerHeight: this.options.lifelines.header.height, - headerWidth: this.options.lifelines.header.width, - id: lifeline.actor.id, - }); - - const lifelineEl = Common.txt2dom(lifelineTxt); - Common.dom2svg(lifelineEl, gLifelines.append('g')); - } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Initialize all mouse events. - * @param gLifelines lifelines container. - * @param gCanvas top-level canvas container. - * @private - */ - _initMouseEvents(gLifelines, gCanvas) { - - const self = this; - const source = 'asdcs'; - const origin = `${window.location.protocol}//${window.location.host}`; - - let timer; - gLifelines.selectAll('.asdcs-diagram-lifeline-selectable') - .on('mouseenter', function f() { - timer = setTimeout(() => { - self.application.selectLifeline(d3.select(this.parentNode).attr('data-id')); - }, 150); - }) - .on('mouseleave', () => { - clearTimeout(timer); - self.application.selectLifeline(); - }) - .on('click', function f() { - const id = d3.select(this.parentNode).attr('data-id'); - window.postMessage({ source, id, type: 'lifeline' }, origin); - }); - - gLifelines.selectAll('.asdcs-diagram-lifeline-heading-box') - .on('mouseenter', function f() { - timer = setTimeout(() => { - self.application.selectLifeline(d3.select(this.parentNode).attr('data-id')); - }, 150); - }) - .on('mouseleave', () => { - clearTimeout(timer); - self.application.selectLifeline(); - }) - .on('click', function f() { - const id = d3.select(this.parentNode).attr('data-id'); - window.postMessage({ source, id, type: 'lifelineHeader' }, origin); - }); - - gCanvas.selectAll('.asdcs-diagram-message-selectable') - .on('mouseenter', function f() { - self.events.message = { x: d3.event.pageX, y: d3.event.pageY }; - timer = setTimeout(() => { - self.application.selectMessage(d3.select(this.parentNode).attr('data-id')); - }, 200); - }) - .on('mouseleave', () => { - delete self.events.message; - clearTimeout(timer); - self.application.selectMessage(); - }) - .on('click', function f() { - const id = d3.select(this.parentNode).attr('data-id'); - window.postMessage({ source, id, type: 'message' }, origin); - }); - - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get CSS classes to be applied to a message, according to whether request/response - * or synchronous/asynchronous. - * @param message message being rendered. - * @returns CSS class name(s). - * @private - */ - static _getMessageStyles(message) { - - let marker = 'asdcsDiagramArrowSolid'; - let dasharray = ''; - let css = 'asdcs-diagram-message'; - if (message.type === 'request') { - css = `${css} asdcs-diagram-message-request`; - } else { - css = `${css} asdcs-diagram-message-response`; - marker = 'asdcsDiagramArrowOpen'; - dasharray = '30, 10'; - } + const zoom = d3.zoom().on('zoom', zoomed); - if (message.asynchronous) { - css = `${css} asdcs-diagram-message-asynchronous`; - marker = 'asdcsDiagramArrowOpen'; - } else { - css = `${css} asdcs-diagram-message-synchronous`; - } + this.svg.call(zoom); + this.svg.call(zoom.scaleBy, scale); - return { css, marker, dasharray }; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Initialize or reinitialize zoom. This sets the initial zoom in the case of - * a re-rendering, and initializes the eventhandling in all cases. - * - * It does some fairly risky parsing of the 'transform' attribute, assuming that it - * can contain scale() and translate(). But only the zoom handler and us are writing - * the transform values, so that's probably OK. - * - * @param gContent container. - * @param width diagram width. - * @param height diagram height. - * @private - */ - _initZoom(gContent, width, height) { - - const zoomed = function zoomed() { - if (!this.initialTransformX && !this.initialTransformY) { - this.initialTransformX = d3.event.transform.x; - this.initialTransformY = d3.event.transform.y; - } - - gContent.attr('transform', - `translate(${d3.event.transform.x - this.initialTransformX}, ${d3.event.transform.y - - this.initialTransformY})scale(${d3.event.transform.k}, ${d3.event.transform.k})`); - }; - - const viewWidth = this.state.width || this.options.svg.width; - const viewHeight = this.state.height || this.options.svg.height; - const scaleMinimum = this.options.svg.scale.minimum; - const scaleWidth = viewWidth / width; - const scaleHeight = viewHeight / height; - - let scale = scaleMinimum; - if (this.options.svg.scale.width) { - scale = Math.max(scale, scaleWidth); - } - if (this.options.svg.scale.height) { - scale = Math.min(scale, scaleHeight); + gContent.attr( + 'transform', + `translate(${translate[0]}, ${translate[1]})` + ); + gContent.attr('transform', `scale(${scale})`); } - scale = Math.max(scale, scaleMinimum); - - let translate = [0, 0]; - if (this.savedTransform) { - const s = this.savedTransform; - const scaleStart = s.indexOf('scale('); - if (scaleStart !== -1) { - scale = parseFloat(s.substring(scaleStart + 6, s.length - 1)); - } - const translateStart = s.indexOf('translate('); - if (translateStart !== -1) { - const spec = s.substring(translateStart + 10, s.indexOf(')', translateStart)); - const tokens = spec.split(','); - translate = [parseFloat(tokens[0]), parseFloat(tokens[1])]; - } - - gContent.attr('transform', this.savedTransform); - } else { - gContent.attr('transform', `scale(${scale})`); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Hide from the linter the fact that we're modifying the lifeline. + * @param lifeline to be updated with X position. + * @param x X position. + * @private + */ + static _processLifeline(lifeline, x) { + const actor = lifeline; + actor.id = actor.id || actor.name; + actor.x = x; } - const zoom = d3.zoom() - .on('zoom', zoomed); - - this.svg.call(zoom); - this.svg.call(zoom.scaleBy, scale); - - gContent.attr('transform', `translate(${translate[0]}, ${translate[1]})`); - gContent.attr('transform', `scale(${scale})`); - - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Hide from the linter the fact that we're modifying the lifeline. - * @param lifeline to be updated with X position. - * @param x X position. - * @private - */ - static _processLifeline(lifeline, x) { - const actor = lifeline; - actor.id = actor.id || actor.name; - actor.x = x; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Derive active occurrences for lifeline. - * @param oData occurrences data. - * @param lifelineId lifeline to be analyzed. - * @returns {number} - * @private - */ - static _calcActive(oData, lifelineId) { - const o = oData[lifelineId]; - let active = 0; - if (o && o.active) { - active = o.active.length; + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Derive active occurrences for lifeline. + * @param oData occurrences data. + * @param lifelineId lifeline to be analyzed. + * @returns {number} + * @private + */ + static _calcActive(oData, lifelineId) { + const o = oData[lifelineId]; + let active = 0; + if (o && o.active) { + active = o.active.length; + } + return active; } - return active; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Derive the X position of an occurrence on a lifeline, taking into account how - * many occurrences are active. - * @param active active count. - * @param x lifeline X position; basis for offset. - * @param arrow whether this is the arrow (to) end. - * @param leftToRight whether this message goes left-to-right. - * @returns {*} calculated X position for occurrence left-hand side. - * @private - */ - _calcMessageX(active, x, arrow, leftToRight) { - const width = this.options.lifelines.occurrences.width; - const halfWidth = width / 2; - const active0 = Math.max(0, active - 1); - let calculated = x + (active0 * width); - if (arrow) { - // End (ARROW). - if (leftToRight) { - calculated -= halfWidth; - } else { - calculated += halfWidth; - } - } else { - // Start (NOT ARROW). - if (leftToRight) { - calculated += halfWidth; - } else { - calculated -= halfWidth; - } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Derive the X position of an occurrence on a lifeline, taking into account how + * many occurrences are active. + * @param active active count. + * @param x lifeline X position; basis for offset. + * @param arrow whether this is the arrow (to) end. + * @param leftToRight whether this message goes left-to-right. + * @returns {*} calculated X position for occurrence left-hand side. + * @private + */ + _calcMessageX(active, x, arrow, leftToRight) { + const width = this.options.lifelines.occurrences.width; + const halfWidth = width / 2; + const active0 = Math.max(0, active - 1); + let calculated = x + active0 * width; + if (arrow) { + // End (ARROW). + if (leftToRight) { + calculated -= halfWidth; + } else { + calculated += halfWidth; + } + } else { + // Start (NOT ARROW). + if (leftToRight) { + calculated += halfWidth; + } else { + calculated -= halfWidth; + } + } + + return calculated; } - return calculated; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show popup upon hovering over a messages that has associated notes. - * @param id - * @private - */ - _showNotesPopup(id) { - if (this.popup) { - if (id) { - const message = this.application.getModel().getMessageById(id); - if (message && message.notes && message.notes.length > 0 && this.events.message) { - this.popup.setState({ - visible: true, - left: this.events.message.x - 50, - top: this.events.message.y + 20, - notes: message.notes[0], - }); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show popup upon hovering over a messages that has associated notes. + * @param id + * @private + */ + _showNotesPopup(id) { + if (this.popup) { + if (id) { + const message = this.application.getModel().getMessageById(id); + if ( + message && + message.notes && + message.notes.length > 0 && + this.events.message + ) { + this.popup.setState({ + visible: true, + left: this.events.message.x - 50, + top: this.events.message.y + 20, + notes: message.notes[0] + }); + } + } else { + this.popup.setState({ visible: false, notes: '' }); + } } - } else { - this.popup.setState({ visible: false, notes: '' }); - } } - } } - Diagram.propTypes = { - application: PropTypes.object.isRequired, + application: PropTypes.object.isRequired }; export default Diagram; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/components/popup/Popup.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/components/popup/Popup.jsx index 8f8f859aad..a2a6582ab0 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/components/popup/Popup.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/diagram/components/popup/Popup.jsx @@ -25,85 +25,81 @@ import iconEdit from '../../../../../../../../res/ecomp/asdc/sequencer/sprites/i * @constructor */ class Popup extends React.Component { - - // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct react view. - * @param props element properties (of which there are none). - * @param context react context. - */ - constructor(props, context) { - super(props, context); - this.state = { - top: 0, - left: 0, - visible: false, - notes: '', - }; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render view. - * @returns {XML} - */ - render() { - - // Build CSS + styles to position and configure popup. - - let top = this.state.top; - let left = this.state.left; - - const popupHeight = 200; - const popupWidth = 320; - - let auxCssVertical = 'top'; - let auxCssHorizontal = 'left'; - - if (this.state.top > (window.innerHeight - popupHeight)) { - top -= (popupHeight + 50); - auxCssVertical = 'bottom'; - } - - if (this.state.left > (window.innerWidth - popupWidth)) { - left -= (popupWidth - 80); - auxCssHorizontal = 'right'; - } - - const auxCss = `asdcs-diagram-popup-${auxCssVertical}${auxCssHorizontal}`; - const styles = { - top, - left, - display: (this.state.visible ? 'block' : 'none'), - }; - - // Render element. - - let notes = this.state.notes || ''; - if (notes.length > 255) { - notes = notes.substring(0, 255); - notes = `${notes} ...`; + // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct react view. + * @param props element properties (of which there are none). + * @param context react context. + */ + constructor(props, context) { + super(props, context); + this.state = { + top: 0, + left: 0, + visible: false, + notes: '' + }; } - return ( - <div className={`asdcs-diagram-popup ${auxCss}`} style={styles}> - <div className="asdcs-diagram-popup-header">Notes</div> - <div className="asdcs-diagram-popup-body"> - <div className="asdcs-icon-popup"> - <Icon glyph={iconEdit} /> - </div> - <div className="asdcs-diagram-notes"> - <div className="asdcs-diagram-note"> - {notes} + // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render view. + * @returns {XML} + */ + render() { + // Build CSS + styles to position and configure popup. + + let top = this.state.top; + let left = this.state.left; + + const popupHeight = 200; + const popupWidth = 320; + + let auxCssVertical = 'top'; + let auxCssHorizontal = 'left'; + + if (this.state.top > window.innerHeight - popupHeight) { + top -= popupHeight + 50; + auxCssVertical = 'bottom'; + } + + if (this.state.left > window.innerWidth - popupWidth) { + left -= popupWidth - 80; + auxCssHorizontal = 'right'; + } + + const auxCss = `asdcs-diagram-popup-${auxCssVertical}${auxCssHorizontal}`; + const styles = { + top, + left, + display: this.state.visible ? 'block' : 'none' + }; + + // Render element. + + let notes = this.state.notes || ''; + if (notes.length > 255) { + notes = notes.substring(0, 255); + notes = `${notes} ...`; + } + + return ( + <div className={`asdcs-diagram-popup ${auxCss}`} style={styles}> + <div className="asdcs-diagram-popup-header">Notes</div> + <div className="asdcs-diagram-popup-body"> + <div className="asdcs-icon-popup"> + <Icon glyph={iconEdit} /> + </div> + <div className="asdcs-diagram-notes"> + <div className="asdcs-diagram-note">{notes}</div> + </div> + </div> + <div className="asdcs-diagram-popup-footer" /> </div> - </div> - </div> - <div className="asdcs-diagram-popup-footer"></div> - </div> - ); - } + ); + } } export default Popup; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/dialog/Dialog.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/dialog/Dialog.jsx index d48ef3bd88..96a709948c 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/dialog/Dialog.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/dialog/Dialog.jsx @@ -28,211 +28,216 @@ import iconClose from '../../../../../../res/ecomp/asdc/sequencer/sprites/icons/ * configured, shown and hidden as required. */ class Dialog extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct view. + */ + constructor(props, context) { + super(props, context); + + this.MODE = { + INFO: { + icon: 'asdcs-icon-info', + heading: 'Information' + }, + ERROR: { + icon: 'asdcs-icon-exclaim', + heading: 'Error' + }, + EDIT: { + icon: 'asdcs-icon-edit', + heading: 'Edit', + edit: true, + confirm: true + }, + CONFIRM: { + icon: 'asdcs-icon-question', + heading: 'Confirm', + confirm: true + } + }; + + this.state = { + mode: this.MODE.INFO, + message: '', + text: '', + visible: false + }; + + // Bindings. + + this.onClickOK = this.onClickOK.bind(this); + this.onClickCancel = this.onClickCancel.bind(this); + this.onChangeText = this.onChangeText.bind(this); + this.showConfirmDialog = this.showConfirmDialog.bind(this); + this.showInfoDialog = this.showInfoDialog.bind(this); + this.showEditDialog = this.showEditDialog.bind(this); + this.showErrorDialog = this.showErrorDialog.bind(this); + this.showDialog = this.showDialog.bind(this); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show info dialog. + * @param message info message. + */ + showInfoDialog(message) { + this.showDialog(this.MODE.INFO, { message }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show error dialog. + * @param message error message. + */ + showErrorDialog(message) { + this.showDialog(this.MODE.ERROR, { message }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show edit dialog. + * @param message dialog message. + * @param text current edit text. + * @param callback callback function to be invoked on OK. + */ + showEditDialog(message, text, callback) { + this.showDialog(this.MODE.EDIT, { message, text, callback }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show confirmation dialog. + * @param message dialog message. + * @param callback callback function to be invoked on OK. + */ + showConfirmDialog(message, callback) { + this.showDialog(this.MODE.CONFIRM, { message, callback }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle buttonclick. + */ + onClickOK() { + this.props.application.hideOverlay(); + this.setState({ visible: false }); + if (this.callback) { + // So far the only thing we can return is edit text, but send it back + // as properties to allow for future return values. + + this.callback({ text: this.state.text }); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle buttonclick. + */ + onClickCancel() { + this.props.application.hideOverlay(); + this.setState({ visible: false }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle text changes. + * @param event update event. + */ + onChangeText(event) { + this.setState({ text: event.target.value }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show dialog in specified configuration. + * @param mode dialog mode. + * @param args dialog parameters, varying slightly by dialog type. + * @private + */ + showDialog(mode, args) { + this.props.application.showOverlay(); + this.callback = args.callback; + this.setState({ + mode, + visible: true, + message: args.message || '', + text: args.text || '' + }); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct view. - */ - constructor(props, context) { - - super(props, context); - - this.MODE = { - INFO: { - icon: 'asdcs-icon-info', - heading: 'Information', - }, - ERROR: { - icon: 'asdcs-icon-exclaim', - heading: 'Error', - }, - EDIT: { - icon: 'asdcs-icon-edit', - heading: 'Edit', - edit: true, - confirm: true, - }, - CONFIRM: { - icon: 'asdcs-icon-question', - heading: 'Confirm', - confirm: true, - }, - }; - - this.state = { - mode: this.MODE.INFO, - message: '', - text: '', - visible: false, - }; - - // Bindings. - - this.onClickOK = this.onClickOK.bind(this); - this.onClickCancel = this.onClickCancel.bind(this); - this.onChangeText = this.onChangeText.bind(this); - this.showConfirmDialog = this.showConfirmDialog.bind(this); - this.showInfoDialog = this.showInfoDialog.bind(this); - this.showEditDialog = this.showEditDialog.bind(this); - this.showErrorDialog = this.showErrorDialog.bind(this); - this.showDialog = this.showDialog.bind(this); - - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show info dialog. - * @param message info message. - */ - showInfoDialog(message) { - this.showDialog(this.MODE.INFO, { message }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show error dialog. - * @param message error message. - */ - showErrorDialog(message) { - this.showDialog(this.MODE.ERROR, { message }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show edit dialog. - * @param message dialog message. - * @param text current edit text. - * @param callback callback function to be invoked on OK. - */ - showEditDialog(message, text, callback) { - this.showDialog(this.MODE.EDIT, { message, text, callback }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show confirmation dialog. - * @param message dialog message. - * @param callback callback function to be invoked on OK. - */ - showConfirmDialog(message, callback) { - this.showDialog(this.MODE.CONFIRM, { message, callback }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle buttonclick. - */ - onClickOK() { - this.props.application.hideOverlay(); - this.setState({ visible: false }); - if (this.callback) { - - // So far the only thing we can return is edit text, but send it back - // as properties to allow for future return values. - - this.callback({ text: this.state.text }); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render dialog into the page, initially hidden. + */ + render() { + const dialogClass = this.state.visible ? '' : 'asdcs-hidden'; + const cancelClass = this.callback ? '' : 'asdcs-hidden'; + const textClass = + this.state.mode === this.MODE.EDIT ? '' : 'asdcs-hidden'; + + return ( + <div className={`asdcs-dialog ${dialogClass}`}> + <div className="asdcs-dialog-header"> + {this.state.mode.heading} + </div> + <div + className="asdcs-dialog-close" + onClick={this.onClickCancel}> + <Icon + glyph={iconClose} + className={this.MODE.CONFIRM.icon} + /> + </div> + <div className={`asdcs-dialog-icon ${this.state.mode.icon}`}> + <Icon + glyph={iconQuestion} + className={this.MODE.CONFIRM.icon} + /> + <Icon + glyph={iconExclaim} + className={this.MODE.ERROR.icon} + /> + <Icon glyph={iconInfo} className={this.MODE.INFO.icon} /> + <Icon glyph={iconEdit} className={this.MODE.EDIT.icon} /> + </div> + <div className="asdcs-dialog-message">{this.state.message}</div> + <div className={`asdcs-dialog-text ${textClass}`}> + <textarea + maxLength="255" + value={this.state.text} + onChange={this.onChangeText} + /> + </div> + <div className="asdcs-dialog-buttonbar"> + <button + className={`asdcs-dialog-button-cancel ${cancelClass}`} + onClick={this.onClickCancel}> + Cancel + </button> + <button + className="asdcs-dialog-button-ok" + onClick={this.onClickOK}> + OK + </button> + </div> + </div> + ); } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle buttonclick. - */ - onClickCancel() { - this.props.application.hideOverlay(); - this.setState({ visible: false }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle text changes. - * @param event update event. - */ - onChangeText(event) { - this.setState({ text: event.target.value }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show dialog in specified configuration. - * @param mode dialog mode. - * @param args dialog parameters, varying slightly by dialog type. - * @private - */ - showDialog(mode, args) { - this.props.application.showOverlay(); - this.callback = args.callback; - this.setState({ - mode, - visible: true, - message: args.message || '', - text: args.text || '', - }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render dialog into the page, initially hidden. - */ - render() { - - const dialogClass = (this.state.visible) ? '' : 'asdcs-hidden'; - const cancelClass = (this.callback) ? '' : 'asdcs-hidden'; - const textClass = (this.state.mode === this.MODE.EDIT) ? '' : 'asdcs-hidden'; - - return ( - <div className={`asdcs-dialog ${dialogClass}`}> - <div className="asdcs-dialog-header">{this.state.mode.heading}</div> - <div className="asdcs-dialog-close" onClick={this.onClickCancel} > - <Icon glyph={iconClose} className={this.MODE.CONFIRM.icon} /> - </div> - <div className={`asdcs-dialog-icon ${this.state.mode.icon}`}> - <Icon glyph={iconQuestion} className={this.MODE.CONFIRM.icon} /> - <Icon glyph={iconExclaim} className={this.MODE.ERROR.icon} /> - <Icon glyph={iconInfo} className={this.MODE.INFO.icon} /> - <Icon glyph={iconEdit} className={this.MODE.EDIT.icon} /> - </div> - <div className="asdcs-dialog-message"> - {this.state.message} - </div> - <div className={`asdcs-dialog-text ${textClass}`}> - <textarea - maxLength="255" - value={this.state.text} - onChange={this.onChangeText} - /> - </div> - <div className="asdcs-dialog-buttonbar"> - <button - className={`asdcs-dialog-button-cancel ${cancelClass}`} - onClick={this.onClickCancel} - > - Cancel - </button> - <button - className="asdcs-dialog-button-ok" - onClick={this.onClickOK} - > - OK - </button> - </div> - </div> - ); - } } Dialog.propTypes = { - application: PropTypes.object.isRequired, + application: PropTypes.object.isRequired }; export default Dialog; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/Editor.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/Editor.jsx index 00c8f7c47c..087bc8029f 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/Editor.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/Editor.jsx @@ -26,149 +26,147 @@ import Source from './components/source/Source'; * Editor view, aggregating the designer, the code editor, the toolbar. */ class Editor extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Construct React view. + * @param props properties. + * @param context context. + */ + constructor(props, context) { + super(props, context); - /** - * Construct React view. - * @param props properties. - * @param context context. - */ - constructor(props, context) { - super(props, context); + this.application = Common.assertNotNull(props.application); + this.demo = this.application.getOptions().demo; - this.application = Common.assertNotNull(props.application); - this.demo = this.application.getOptions().demo; + // Bindings. - // Bindings. + this.selectMessage = this.selectMessage.bind(this); + this.selectLifeline = this.selectLifeline.bind(this); - this.selectMessage = this.selectMessage.bind(this); - this.selectLifeline = this.selectLifeline.bind(this); - - this.onMouseDown = this.onMouseDown.bind(this); - this.onMouseUp = this.onMouseUp.bind(this); - this.onMouseMove = this.onMouseMove.bind(this); - } + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + this.onMouseMove = this.onMouseMove.bind(this); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Select message by ID. - * @param id message ID. - */ - selectMessage(id) { - if (this.designer) { - this.designer.selectMessage(id); + /** + * Select message by ID. + * @param id message ID. + */ + selectMessage(id) { + if (this.designer) { + this.designer.selectMessage(id); + } } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Select lifeline by ID. - * @param id lifeline ID. - */ - selectLifeline(id) { - if (this.designer) { - this.designer.selectLifeline(id); + /** + * Select lifeline by ID. + * @param id lifeline ID. + */ + selectLifeline(id) { + if (this.designer) { + this.designer.selectLifeline(id); + } } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Record that we're dragging. - */ - onMouseDown() { - if (this.editor) { - this.resize = { - initialWidth: this.editor.offsetWidth, - initialPageX: undefined, - }; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Record that we're dragging. + */ + onMouseDown() { + if (this.editor) { + this.resize = { + initialWidth: this.editor.offsetWidth, + initialPageX: undefined + }; + } } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Record that we're not dragging. - */ - onMouseUp() { - this.resize = undefined; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Record mouse movement. - */ - onMouseMove(event) { - if (this.resize) { - if (this.editor) { - if (this.resize.initialPageX) { - const deltaX = event.pageX - this.resize.initialPageX; - const newWidth = this.resize.initialWidth + deltaX; - const newWidthBounded = Math.min(800, Math.max(400, newWidth)); - this.editor.style.width = `${newWidthBounded}px`; - } else { - this.resize.initialPageX = event.pageX; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Record that we're not dragging. + */ + onMouseUp() { + this.resize = undefined; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Record mouse movement. + */ + onMouseMove(event) { + if (this.resize) { + if (this.editor) { + if (this.resize.initialPageX) { + const deltaX = event.pageX - this.resize.initialPageX; + const newWidth = this.resize.initialWidth + deltaX; + const newWidthBounded = Math.min( + 800, + Math.max(400, newWidth) + ); + this.editor.style.width = `${newWidthBounded}px`; + } else { + this.resize.initialPageX = event.pageX; + } + } } - } } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render editor. - */ - render() { - - Logger.info('Editor.jsx - render()'); - - return ( - - <div - className="asdcs-editor" - ref={(r) => { this.editor = r; }} - > - - <Toolbar application={this.props.application} editor={this} /> - - <div className="asdcs-editor-content"> - <Source application={this.props.application} /> - <Designer - application={this.props.application} - ref={(r) => { - if (r) { - this.designer = r.getDecoratedComponentInstance(); - } else { - this.designer = null; - } - }} - /> - </div> - - <div className="asdcs-editor-statusbar"> - <div className="asdcs-editor-status"></div> - <div className="asdcs-editor-validation"></div> - </div> - - <div - className="asdcs-editor-resize-handle" - onMouseDown={this.onMouseDown} - onMouseUp={this.onMouseUp} - > - </div> - </div> - ); - } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render editor. + */ + render() { + Logger.info('Editor.jsx - render()'); + + return ( + <div + className="asdcs-editor" + ref={r => { + this.editor = r; + }}> + <Toolbar application={this.props.application} editor={this} /> + + <div className="asdcs-editor-content"> + <Source application={this.props.application} /> + <Designer + application={this.props.application} + ref={r => { + if (r) { + this.designer = r.getDecoratedComponentInstance(); + } else { + this.designer = null; + } + }} + /> + </div> + + <div className="asdcs-editor-statusbar"> + <div className="asdcs-editor-status" /> + <div className="asdcs-editor-validation" /> + </div> + + <div + className="asdcs-editor-resize-handle" + onMouseDown={this.onMouseDown} + onMouseUp={this.onMouseUp} + /> + </div> + ); + } } /** Element properties. */ Editor.propTypes = { - application: PropTypes.object.isRequired, + application: PropTypes.object.isRequired }; export default Editor; - diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/Designer.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/Designer.jsx index c67c68e4e2..09e321dd54 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/Designer.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/Designer.jsx @@ -35,369 +35,383 @@ import iconCollapsed from '../../../../../../../../res/ecomp/asdc/sequencer/spri * LHS design wid` view. */ class Designer extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + + Logger.noop(); + + this.application = Common.assertNotNull(props.application); + + this.state = { + lifelinesExpanded: false, + messagesExpanded: true, + activeLifelineId: undefined, + activeMessageId: undefined + }; + + // Bind this. + + this.onToggle = this.onToggle.bind(this); + this.onMouseEnterLifeline = this.onMouseEnterLifeline.bind(this); + this.onMouseLeaveLifeline = this.onMouseLeaveLifeline.bind(this); + this.onMouseEnterMessage = this.onMouseEnterMessage.bind(this); + this.onMouseLeaveMessage = this.onMouseLeaveMessage.bind(this); + + this.addMessage = this.addMessage.bind(this); + this.updateMessage = this.updateMessage.bind(this); + this.deleteMessage = this.deleteMessage.bind(this); + this.addLifeline = this.addLifeline.bind(this); + this.updateLifeline = this.updateLifeline.bind(this); + this.deleteLifeline = this.deleteLifeline.bind(this); + + this.selectMessage = this.selectMessage.bind(this); + this.selectLifeline = this.selectLifeline.bind(this); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Select message by ID. + * @param id message ID. + */ + selectMessage(id) { + // TODO: scroll into view. + + this.setState({ activeMessageId: id }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Select lifeline by ID. + * @param id lifeline ID. + */ + selectLifeline(id) { + // TODO: scroll into view. + + this.setState({ activeLifelineId: id }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show/hide lifelines section. + */ + onToggle() { + const lifelinesExpanded = !this.state.lifelinesExpanded; + const messagesExpanded = !lifelinesExpanded; + this.setState({ lifelinesExpanded, messagesExpanded }); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle mouse event. + * @param id lifeline identifier. + */ + onMouseEnterLifeline(id) { + this.application.selectLifeline(id); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle mouse event. + */ + onMouseLeaveLifeline() { + this.application.selectLifeline(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle mouse event. + * @param id message identifier. + */ + onMouseEnterMessage(id) { + this.application.selectMessage(id); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Construct view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); + /** + * Handle mouse event. + */ + onMouseLeaveMessage() { + // Only on next selection. + // this.application.selectMessage(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Add new message. + */ + addMessage() { + if (this.application.getModel().unwrap().diagram.lifelines.length < 2) { + this.application.showErrorDialog( + 'You need at least two lifelines.' + ); + return; + } - Logger.noop(); - - this.application = Common.assertNotNull(props.application); - - this.state = { - lifelinesExpanded: false, - messagesExpanded: true, - activeLifelineId: undefined, - activeMessageId: undefined, - }; - - // Bind this. - - this.onToggle = this.onToggle.bind(this); - this.onMouseEnterLifeline = this.onMouseEnterLifeline.bind(this); - this.onMouseLeaveLifeline = this.onMouseLeaveLifeline.bind(this); - this.onMouseEnterMessage = this.onMouseEnterMessage.bind(this); - this.onMouseLeaveMessage = this.onMouseLeaveMessage.bind(this); - - this.addMessage = this.addMessage.bind(this); - this.updateMessage = this.updateMessage.bind(this); - this.deleteMessage = this.deleteMessage.bind(this); - this.addLifeline = this.addLifeline.bind(this); - this.updateLifeline = this.updateLifeline.bind(this); - this.deleteLifeline = this.deleteLifeline.bind(this); - - this.selectMessage = this.selectMessage.bind(this); - this.selectLifeline = this.selectLifeline.bind(this); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Select message by ID. - * @param id message ID. - */ - selectMessage(id) { - - // TODO: scroll into view. - - this.setState({ activeMessageId: id }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Select lifeline by ID. - * @param id lifeline ID. - */ - selectLifeline(id) { - - // TODO: scroll into view. - - this.setState({ activeLifelineId: id }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show/hide lifelines section. - */ - onToggle() { - const lifelinesExpanded = !this.state.lifelinesExpanded; - const messagesExpanded = !lifelinesExpanded; - this.setState({ lifelinesExpanded, messagesExpanded }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle mouse event. - * @param id lifeline identifier. - */ - onMouseEnterLifeline(id) { - this.application.selectLifeline(id); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle mouse event. - */ - onMouseLeaveLifeline() { - this.application.selectLifeline(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle mouse event. - * @param id message identifier. - */ - onMouseEnterMessage(id) { - this.application.selectMessage(id); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle mouse event. - */ - onMouseLeaveMessage() { - // Only on next selection. - // this.application.selectMessage(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Add new message. - */ - addMessage() { - - if (this.application.getModel().unwrap().diagram.lifelines.length < 2) { - this.application.showErrorDialog('You need at least two lifelines.'); - return; + this.application.getModel().addMessage(); + this.forceUpdate(); + this.application.renderDiagram(); } - this.application.getModel().addMessage(); - this.forceUpdate(); - this.application.renderDiagram(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Apply property changes to the message identified by props.id. - * @param props properties to be updated (excluding 'id'). - */ - updateMessage(props) { - Common.assertPlainObject(props); - const model = this.application.getModel(); - const message = model.getMessageById(props.id); - if (message) { - for (const k of Object.keys(props)) { - if (k !== 'id') { - message[k] = props[k]; + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Apply property changes to the message identified by props.id. + * @param props properties to be updated (excluding 'id'). + */ + updateMessage(props) { + Common.assertPlainObject(props); + const model = this.application.getModel(); + const message = model.getMessageById(props.id); + if (message) { + for (const k of Object.keys(props)) { + if (k !== 'id') { + message[k] = props[k]; + } + } } - } + this.forceUpdate(); + this.application.renderDiagram(); } - this.forceUpdate(); - this.application.renderDiagram(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Delete message after confirmation. - * @param id ID of message to be deleted. - */ - deleteMessage(id) { - - const self = this; - const model = this.application.getModel(); - - const confirmComplete = function f() { - model.deleteMessageById(id); - self.render(); - self.application.renderDiagram(); - }; - - this.application.showConfirmDialog('Delete this message?', - confirmComplete); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Add new lifeline. - */ - addLifeline() { - this.application.getModel().addLifeline(); - this.forceUpdate(); - this.application.renderDiagram(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Apply property changes to the lifeline identified by props.id. - * @param props properties to be updated (excluding 'id'). - */ - updateLifeline(props) { - Common.assertPlainObject(props); - const model = this.application.getModel(); - const lifeline = model.getLifelineById(props.id); - if (lifeline) { - for (const k of Object.keys(props)) { - if (k !== 'id') { - lifeline[k] = props[k]; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Delete message after confirmation. + * @param id ID of message to be deleted. + */ + deleteMessage(id) { + const self = this; + const model = this.application.getModel(); + + const confirmComplete = function f() { + model.deleteMessageById(id); + self.render(); + self.application.renderDiagram(); + }; + + this.application.showConfirmDialog( + 'Delete this message?', + confirmComplete + ); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Add new lifeline. + */ + addLifeline() { + this.application.getModel().addLifeline(); + this.forceUpdate(); + this.application.renderDiagram(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Apply property changes to the lifeline identified by props.id. + * @param props properties to be updated (excluding 'id'). + */ + updateLifeline(props) { + Common.assertPlainObject(props); + const model = this.application.getModel(); + const lifeline = model.getLifelineById(props.id); + if (lifeline) { + for (const k of Object.keys(props)) { + if (k !== 'id') { + lifeline[k] = props[k]; + } + } } - } + this.forceUpdate(); + this.application.renderDiagram(); } - this.forceUpdate(); - this.application.renderDiagram(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Delete lifeline after confirmation. - * @param id candidate for deletion. - */ - deleteLifeline(id) { - - const self = this; - const model = this.application.getModel(); - - const confirmComplete = function f() { - model.deleteLifelineById(id); - self.forceUpdate(); - self.application.renderDiagram(); - }; - this.application.showConfirmDialog('Delete this lifeline and all its steps?', - confirmComplete); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render designer. - */ - render() { - - const application = this.props.application; - const model = application.getModel(); - const diagram = model.unwrap().diagram; - const metadata = diagram.metadata; - - const lifelinesIcon = this.state.lifelinesExpanded ? iconExpanded : iconCollapsed; - const lifelinesClass = this.state.lifelinesExpanded ? '' : 'asdcs-hidden'; - const messagesIcon = this.state.messagesExpanded ? iconExpanded : iconCollapsed; - const messagesClass = this.state.messagesExpanded ? '' : 'asdcs-hidden'; - - return ( - - <div className="asdcs-editor-designer"> - <div className="asdcs-designer-accordion"> - - <div className="asdcs-designer-metadata-container"> - <Metadata metadata={metadata} /> - </div> - - <h3 onClick={this.onToggle}>Lifelines - <div className="asdcs-designer-icon" onClick={this.onToggle}> - <Icon glyph={lifelinesIcon} /> - </div> - </h3> - - <div className={`asdcs-designer-lifelines-container ${lifelinesClass}`}> - <Lifelines - application={this.application} - designer={this} - activeLifelineId={this.state.activeLifelineId} - /> - </div> - - <h3 onClick={this.onToggle}>Steps - <div className="asdcs-designer-icon" onClick={this.onToggle}> - <Icon glyph={messagesIcon} /> + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Delete lifeline after confirmation. + * @param id candidate for deletion. + */ + deleteLifeline(id) { + const self = this; + const model = this.application.getModel(); + + const confirmComplete = function f() { + model.deleteLifelineById(id); + self.forceUpdate(); + self.application.renderDiagram(); + }; + this.application.showConfirmDialog( + 'Delete this lifeline and all its steps?', + confirmComplete + ); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render designer. + */ + render() { + const application = this.props.application; + const model = application.getModel(); + const diagram = model.unwrap().diagram; + const metadata = diagram.metadata; + + const lifelinesIcon = this.state.lifelinesExpanded + ? iconExpanded + : iconCollapsed; + const lifelinesClass = this.state.lifelinesExpanded + ? '' + : 'asdcs-hidden'; + const messagesIcon = this.state.messagesExpanded + ? iconExpanded + : iconCollapsed; + const messagesClass = this.state.messagesExpanded ? '' : 'asdcs-hidden'; + + return ( + <div className="asdcs-editor-designer"> + <div className="asdcs-designer-accordion"> + <div className="asdcs-designer-metadata-container"> + <Metadata metadata={metadata} /> + </div> + + <h3 onClick={this.onToggle}> + Lifelines + <div + className="asdcs-designer-icon" + onClick={this.onToggle}> + <Icon glyph={lifelinesIcon} /> + </div> + </h3> + + <div + className={`asdcs-designer-lifelines-container ${lifelinesClass}`}> + <Lifelines + application={this.application} + designer={this} + activeLifelineId={this.state.activeLifelineId} + /> + </div> + + <h3 onClick={this.onToggle}> + Steps + <div + className="asdcs-designer-icon" + onClick={this.onToggle}> + <Icon glyph={messagesIcon} /> + </div> + </h3> + + <div + className={`asdcs-designer-steps-container ${messagesClass}`}> + <Messages + application={this.application} + designer={this} + activeMessageId={this.state.activeMessageId} + /> + </div> + </div> + + <Actions + application={this.props.application} + model={model} + ref={r => { + this.actions = r; + }} + /> </div> - </h3> - - <div className={`asdcs-designer-steps-container ${messagesClass}`} > - <Messages - application={this.application} - designer={this} - activeMessageId={this.state.activeMessageId} - /> - </div> - - </div> - - <Actions - application={this.props.application} - model={model} - ref={(r) => { this.actions = r; }} - /> - - </div> - ); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Scroll accordion pane to make - * @param $element focused element. - * @private - */ - static _scrollIntoView($element) { - const $pane = $element.closest('.ui-accordion-content'); - const paneScrollTop = $pane.scrollTop(); - const paneHeight = $pane.height(); - const paneBottom = paneScrollTop + paneHeight; - const elementTop = $element[0].offsetTop - $pane[0].offsetTop; - const elementHeight = $element.height(); - const elementBottom = elementTop + elementHeight; - if (elementBottom > paneBottom) { - $pane.scrollTop(elementTop); - } else if (elementTop < paneScrollTop) { - $pane.scrollTop(elementTop); + ); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Scroll accordion pane to make + * @param $element focused element. + * @private + */ + static _scrollIntoView($element) { + const $pane = $element.closest('.ui-accordion-content'); + const paneScrollTop = $pane.scrollTop(); + const paneHeight = $pane.height(); + const paneBottom = paneScrollTop + paneHeight; + const elementTop = $element[0].offsetTop - $pane[0].offsetTop; + const elementHeight = $element.height(); + const elementBottom = elementTop + elementHeight; + if (elementBottom > paneBottom) { + $pane.scrollTop(elementTop); + } else if (elementTop < paneScrollTop) { + $pane.scrollTop(elementTop); + } } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show actions menu. - * @param id selected message ID. - * @param position page coordinates. - */ - showActions(id, position) { - if (this.actions) { - this.actions.show(id, position); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show actions menu. + * @param id selected message ID. + * @param position page coordinates. + */ + showActions(id, position) { + if (this.actions) { + this.actions.show(id, position); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show notes popup. + * @param id selected message identifier. + */ + showNotes(id) { + const model = this.application.getModel(); + const options = this.application.getOptions(); + const message = model.getMessageById(id); + const notes = + message.notes && message.notes.length > 0 ? message.notes[0] : ''; + const editComplete = function f(p) { + message.notes = []; + if (p && p.text) { + const sanitized = Common.sanitizeText(p.text, options, 'notes'); + message.notes.push(sanitized); + } + }; + this.application.showEditDialog('Notes:', notes, editComplete); } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show notes popup. - * @param id selected message identifier. - */ - showNotes(id) { - const model = this.application.getModel(); - const options = this.application.getOptions(); - const message = model.getMessageById(id); - const notes = (message.notes && (message.notes.length > 0)) ? message.notes[0] : ''; - const editComplete = function f(p) { - message.notes = []; - if (p && p.text) { - const sanitized = Common.sanitizeText(p.text, options, 'notes'); - message.notes.push(sanitized); - } - }; - this.application.showEditDialog('Notes:', notes, editComplete); - } } /** Element properties. */ Designer.propTypes = { - application: PropTypes.object.isRequired, + application: PropTypes.object.isRequired }; +/* eslint-disable new-cap */ export default DragDropContext(HTML5Backend)(Designer); +/* eslint-enable new-cap */ diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/actions/Actions.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/actions/Actions.jsx index 5a8343cccf..a558a14f1d 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/actions/Actions.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/actions/Actions.jsx @@ -36,439 +36,499 @@ import iconFragmentStop from '../../../../../../../../../../res/ecomp/asdc/seque * Action menu view. */ class Actions extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Construct view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); - /** - * Construct view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); + Logger.noop(); - Logger.noop(); + this.state = { + id: undefined, + visible: false + }; - this.state = { - id: undefined, - visible: false, - }; + // Bindings. - // Bindings. + this.show = this.show.bind(this); - this.show = this.show.bind(this); + this.onClickOccurrenceToggle = this.onClickOccurrenceToggle.bind(this); + this.onClickOccurrenceFrom = this.onClickOccurrenceFrom.bind(this); + this.onClickOccurrenceTo = this.onClickOccurrenceTo.bind(this); - this.onClickOccurrenceToggle = this.onClickOccurrenceToggle.bind(this); - this.onClickOccurrenceFrom = this.onClickOccurrenceFrom.bind(this); - this.onClickOccurrenceTo = this.onClickOccurrenceTo.bind(this); + this.onClickFragmentToggle = this.onClickFragmentToggle.bind(this); + this.onChangeFragmentGuard = this.onChangeFragmentGuard.bind(this); + this.onChangeFragmentOperator = this.onChangeFragmentOperator.bind( + this + ); - this.onClickFragmentToggle = this.onClickFragmentToggle.bind(this); - this.onChangeFragmentGuard = this.onChangeFragmentGuard.bind(this); - this.onChangeFragmentOperator = this.onChangeFragmentOperator.bind(this); - - this.onMouseOut = this.onMouseOut.bind(this); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Show for message. - * @param id message ID. - * @param position xy coordinates. - */ - show(id, position) { - const message = this.props.model.getMessageById(id); - - let occurrencesToggle = false; - let fragmentToggle = false; - if (message) { - - message.occurrences = message.occurrences || { start: [], stop: [] }; - message.occurrences.start = message.occurrences.start || []; - message.occurrences.stop = message.occurrences.stop || []; - message.fragment = message.fragment || {}; - message.fragment.start = message.fragment.start || false; - message.fragment.stop = message.fragment.stop || false; - message.fragment.guard = message.fragment.guard || ''; - message.fragment.operator = message.fragment.operator || ''; - - const mo = message.occurrences; - occurrencesToggle = (mo.start.length > 0 || mo.stop.length > 0); - - const mf = message.fragment; - fragmentToggle = (mf.start || mf.stop); + this.onMouseOut = this.onMouseOut.bind(this); } - this.setState({ - id, - message, - occurrencesToggle, - fragmentToggle, - visible: true, - x: position.x, - y: position.y, - }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Toggle occurrence state. - */ - onClickOccurrenceToggle() { - const message = this.state.message; - if (message) { - const oFromState = Actions.getOccurrenceState(message.occurrences, message.from); - const oToState = Actions.getOccurrenceState(message.occurrences, message.to); - const oExpanded = oFromState > 0 || oToState > 0; - if (oExpanded) { - this.setState({ occurrencesExpanded: true }); - } else { - const occurrencesExpanded = !this.state.occurrencesExpanded; - this.setState({ occurrencesExpanded }); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Show for message. + * @param id message ID. + * @param position xy coordinates. + */ + show(id, position) { + const message = this.props.model.getMessageById(id); + + let occurrencesToggle = false; + let fragmentToggle = false; + if (message) { + message.occurrences = message.occurrences || { + start: [], + stop: [] + }; + message.occurrences.start = message.occurrences.start || []; + message.occurrences.stop = message.occurrences.stop || []; + message.fragment = message.fragment || {}; + message.fragment.start = message.fragment.start || false; + message.fragment.stop = message.fragment.stop || false; + message.fragment.guard = message.fragment.guard || ''; + message.fragment.operator = message.fragment.operator || ''; + + const mo = message.occurrences; + occurrencesToggle = mo.start.length > 0 || mo.stop.length > 0; + + const mf = message.fragment; + fragmentToggle = mf.start || mf.stop; + } + + this.setState({ + id, + message, + occurrencesToggle, + fragmentToggle, + visible: true, + x: position.x, + y: position.y + }); } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Handle menu click. - */ - onClickOccurrenceFrom() { - const message = this.state.message; - if (message) { - Actions._toggleOccurrence(message.occurrences, message.from); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Toggle occurrence state. + */ + onClickOccurrenceToggle() { + const message = this.state.message; + if (message) { + const oFromState = Actions.getOccurrenceState( + message.occurrences, + message.from + ); + const oToState = Actions.getOccurrenceState( + message.occurrences, + message.to + ); + const oExpanded = oFromState > 0 || oToState > 0; + if (oExpanded) { + this.setState({ occurrencesExpanded: true }); + } else { + const occurrencesExpanded = !this.state.occurrencesExpanded; + this.setState({ occurrencesExpanded }); + } + } } - this.setState({ message }); - this.props.application.renderDiagram(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle menu click. - */ - onClickOccurrenceTo() { - const message = this.state.message; - if (message) { - Actions._toggleOccurrence(message.occurrences, message.to); - } - this.setState({ message }); - this.props.application.renderDiagram(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Toggle fragment. - */ - onClickFragmentToggle() { - const message = this.state.message; - if (message) { - Actions._toggleFragment(message.fragment); - } - this.setState({ message }); - this.props.application.renderDiagram(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle menu click. - * @param event update event. - */ - onChangeFragmentGuard(event) { - const message = this.state.message; - if (message) { - const options = this.props.application.getOptions(); - message.fragment.guard = Common.sanitizeText(event.target.value, options, 'guard'); - } - this.setState({ message }); - this.props.application.renderDiagram(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle menu click. - * @param value updated value. - */ - onChangeFragmentOperator(value) { - const message = this.state.message; - if (message) { - message.fragment.operator = value.value; - } - this.setState({ message }); - this.props.application.renderDiagram(); - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle menu click. + */ + onClickOccurrenceFrom() { + const message = this.state.message; + if (message) { + Actions._toggleOccurrence(message.occurrences, message.from); + } + this.setState({ message }); + this.props.application.renderDiagram(); + } - /** - * Handle mouse movement. - */ - onMouseOut() { - this.setState({ id: -1, visible: false, x: 0, y: 0 }); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle menu click. + */ + onClickOccurrenceTo() { + const message = this.state.message; + if (message) { + Actions._toggleOccurrence(message.occurrences, message.to); + } + this.setState({ message }); + this.props.application.renderDiagram(); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Toggle fragment. + */ + onClickFragmentToggle() { + const message = this.state.message; + if (message) { + Actions._toggleFragment(message.fragment); + } + this.setState({ message }); + this.props.application.renderDiagram(); + } - /** - * Render view. - * @returns {XML} - */ - render() { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle menu click. + * @param event update event. + */ + onChangeFragmentGuard(event) { + const message = this.state.message; + if (message) { + const options = this.props.application.getOptions(); + message.fragment.guard = Common.sanitizeText( + event.target.value, + options, + 'guard' + ); + } + this.setState({ message }); + this.props.application.renderDiagram(); + } - const actionsStyles = { }; - const message = this.state.message; - if (!message || !this.state.visible) { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle menu click. + * @param value updated value. + */ + onChangeFragmentOperator(value) { + const message = this.state.message; + if (message) { + message.fragment.operator = value.value; + } + this.setState({ message }); + this.props.application.renderDiagram(); + } - // Invisible. + // /////////////////////////////////////////////////////////////////////////////////////////////// - return (<div className="asdcs-actions" ></div>); + /** + * Handle mouse movement. + */ + onMouseOut() { + this.setState({ id: -1, visible: false, x: 0, y: 0 }); } - // Position and display. - - actionsStyles.display = 'block'; - actionsStyles.left = this.state.x - 10; - actionsStyles.top = this.state.y - 10; - - const oFromState = Actions.getOccurrenceState(message.occurrences, message.from); - const oToState = Actions.getOccurrenceState(message.occurrences, message.to); - const fState = Actions.getFragmentState(message.fragment); - - const oExpanded = this.state.occurrencesExpanded || (oFromState > 0) || (oToState > 0); - const oAuxClassName = oExpanded ? '' : 'asdcs-hidden'; - - const fExpanded = fState !== 0; - const fAuxClassName = fExpanded ? '' : 'asdcs-hidden'; - - const fragmentOperatorOptions = [{ - value: 'alt', - label: 'Alternate', - }, { - value: 'opt', - label: 'Optional', - }, { - value: 'loop', - label: 'Loop', - }]; - - const operator = message.fragment.operator || 'alt'; - - return ( - <div - className="asdcs-actions" - style={actionsStyles} - onMouseLeave={this.onMouseOut} - > - <div className="asdcs-actions-header"> - <div className="asdcs-actions-icon"> - <Icon glyph={iconSettings} /> - </div> - </div> - - <div className="asdcs-actions-options"> - - <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-occurrence"> - <div - className="asdcs-actions-option asdcs-actions-option-occurrence-toggle" - onClick={this.onClickOccurrenceToggle} - > - <span className="asdcs-label">Occurrence</span> - <div className="asdcs-actions-state"> - <Icon glyph={iconCollapsed} className={oExpanded ? 'asdcs-hidden' : ''} /> - <Icon glyph={iconExpanded} className={oExpanded ? '' : 'asdcs-hidden'} /> - </div> - </div> - </div> - - <div - className={`asdcs-actions-option asdcs-actions-option-occurrence-from ${oAuxClassName}`} - onClick={this.onClickOccurrenceFrom} - > - <span className="asdcs-label">From</span> - <div className="asdcs-actions-state"> - <span className="asdcs-annotation"></span> - <Icon glyph={iconOccurrenceDefault} className={oFromState === 0 ? '' : 'asdcs-hidden'} /> - <Icon glyph={iconOccurrenceStart} className={oFromState === 1 ? '' : 'asdcs-hidden'} /> - <Icon glyph={iconOccurrenceStop} className={oFromState === 2 ? '' : 'asdcs-hidden'} /> - </div> - </div> - - <div - className={`asdcs-actions-option asdcs-actions-option-occurrence-to ${oAuxClassName}`} - onClick={this.onClickOccurrenceTo} - > - <span className="asdcs-label">To</span> - <div className="asdcs-actions-state"> - <span className="asdcs-annotation"></span> - <Icon glyph={iconOccurrenceDefault} className={oToState === 0 ? '' : 'asdcs-hidden'} /> - <Icon glyph={iconOccurrenceStart} className={oToState === 1 ? '' : 'asdcs-hidden'} /> - <Icon glyph={iconOccurrenceStop} className={oToState === 2 ? '' : 'asdcs-hidden'} /> - </div> - </div> - - <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-fragment"> + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render view. + * @returns {XML} + */ + render() { + const actionsStyles = {}; + const message = this.state.message; + if (!message || !this.state.visible) { + // Invisible. + + return <div className="asdcs-actions" />; + } + + // Position and display. + + actionsStyles.display = 'block'; + actionsStyles.left = this.state.x - 10; + actionsStyles.top = this.state.y - 10; + + const oFromState = Actions.getOccurrenceState( + message.occurrences, + message.from + ); + const oToState = Actions.getOccurrenceState( + message.occurrences, + message.to + ); + const fState = Actions.getFragmentState(message.fragment); + + const oExpanded = + this.state.occurrencesExpanded || oFromState > 0 || oToState > 0; + const oAuxClassName = oExpanded ? '' : 'asdcs-hidden'; + + const fExpanded = fState !== 0; + const fAuxClassName = fExpanded ? '' : 'asdcs-hidden'; + + const fragmentOperatorOptions = [ + { + value: 'alt', + label: 'Alternate' + }, + { + value: 'opt', + label: 'Optional' + }, + { + value: 'loop', + label: 'Loop' + } + ]; + + const operator = message.fragment.operator || 'alt'; + + return ( <div - className="asdcs-actions-option asdcs-actions-fragment-toggle" - onClick={this.onClickFragmentToggle} - > - <span className="asdcs-label">Fragment</span> - <div className="asdcs-actions-state"> - <span className="asdcs-annotation"></span> - <Icon glyph={iconFragmentDefault} className={fState === 0 ? '' : 'asdcs-hidden'} /> - <Icon glyph={iconFragmentStart} className={fState === 1 ? '' : 'asdcs-hidden'} /> - <Icon glyph={iconFragmentStop} className={fState === 2 ? '' : 'asdcs-hidden'} /> - </div> + className="asdcs-actions" + style={actionsStyles} + onMouseLeave={this.onMouseOut}> + <div className="asdcs-actions-header"> + <div className="asdcs-actions-icon"> + <Icon glyph={iconSettings} /> + </div> + </div> + + <div className="asdcs-actions-options"> + <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-occurrence"> + <div + className="asdcs-actions-option asdcs-actions-option-occurrence-toggle" + onClick={this.onClickOccurrenceToggle}> + <span className="asdcs-label">Occurrence</span> + <div className="asdcs-actions-state"> + <Icon + glyph={iconCollapsed} + className={oExpanded ? 'asdcs-hidden' : ''} + /> + <Icon + glyph={iconExpanded} + className={oExpanded ? '' : 'asdcs-hidden'} + /> + </div> + </div> + </div> + + <div + className={`asdcs-actions-option asdcs-actions-option-occurrence-from ${oAuxClassName}`} + onClick={this.onClickOccurrenceFrom}> + <span className="asdcs-label">From</span> + <div className="asdcs-actions-state"> + <span className="asdcs-annotation" /> + <Icon + glyph={iconOccurrenceDefault} + className={ + oFromState === 0 ? '' : 'asdcs-hidden' + } + /> + <Icon + glyph={iconOccurrenceStart} + className={ + oFromState === 1 ? '' : 'asdcs-hidden' + } + /> + <Icon + glyph={iconOccurrenceStop} + className={ + oFromState === 2 ? '' : 'asdcs-hidden' + } + /> + </div> + </div> + + <div + className={`asdcs-actions-option asdcs-actions-option-occurrence-to ${oAuxClassName}`} + onClick={this.onClickOccurrenceTo}> + <span className="asdcs-label">To</span> + <div className="asdcs-actions-state"> + <span className="asdcs-annotation" /> + <Icon + glyph={iconOccurrenceDefault} + className={oToState === 0 ? '' : 'asdcs-hidden'} + /> + <Icon + glyph={iconOccurrenceStart} + className={oToState === 1 ? '' : 'asdcs-hidden'} + /> + <Icon + glyph={iconOccurrenceStop} + className={oToState === 2 ? '' : 'asdcs-hidden'} + /> + </div> + </div> + + <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-fragment"> + <div + className="asdcs-actions-option asdcs-actions-fragment-toggle" + onClick={this.onClickFragmentToggle}> + <span className="asdcs-label">Fragment</span> + <div className="asdcs-actions-state"> + <span className="asdcs-annotation" /> + <Icon + glyph={iconFragmentDefault} + className={ + fState === 0 ? '' : 'asdcs-hidden' + } + /> + <Icon + glyph={iconFragmentStart} + className={ + fState === 1 ? '' : 'asdcs-hidden' + } + /> + <Icon + glyph={iconFragmentStop} + className={ + fState === 2 ? '' : 'asdcs-hidden' + } + /> + </div> + </div> + </div> + + <div + className={`asdcs-actions-option asdcs-actions-fragment-operator ${fAuxClassName}`}> + <div className="asdcs-label">Operator</div> + <div className="asdcs-value"> + <Select + className="asdcs-editable-select" + openOnFocus + clearable={false} + searchable={false} + value={operator} + onChange={this.onChangeFragmentOperator} + options={fragmentOperatorOptions} + /> + </div> + </div> + + <div + className={`asdcs-actions-option asdcs-actions-fragment-guard ${fAuxClassName}`}> + <div className="asdcs-label">Guard</div> + <div className="asdcs-value"> + <input + className="asdcs-editable" + type="text" + size="20" + maxLength="80" + value={message.fragment.guard} + placeholder="Condition" + onChange={this.onChangeFragmentGuard} + /> + </div> + </div> + </div> + + <div className="asdcs-actions-footer" /> </div> - </div> - - <div className={`asdcs-actions-option asdcs-actions-fragment-operator ${fAuxClassName}`}> - <div className="asdcs-label">Operator</div> - <div className="asdcs-value"> - <Select - className="asdcs-editable-select" - openOnFocus - clearable={false} - searchable={false} - value={operator} - onChange={this.onChangeFragmentOperator} - options={fragmentOperatorOptions} - /> - </div> - </div> - - <div className={`asdcs-actions-option asdcs-actions-fragment-guard ${fAuxClassName}`}> - <div className="asdcs-label">Guard</div> - <div className="asdcs-value"> - <input - className="asdcs-editable" - type="text" - size="20" - maxLength="80" - value={message.fragment.guard} - placeholder="Condition" - onChange={this.onChangeFragmentGuard} - /> - </div> - </div> - - </div> - - <div className="asdcs-actions-footer"></div> - - </div> - - ); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Toggle through three occurrence states on click. - * @param occurrence occurrences state, updated as side-effect. - * @param lifelineId message end that's being toggled. - * @private - */ - static _toggleOccurrence(occurrence, lifelineId) { - const o = occurrence; - - const rm = function rm(array, value) { - const index = array.indexOf(value); - if (index !== -1) { - array.splice(index, 1); - } - }; - - const add = function add(array, value) { - if (array.indexOf(value) === -1) { - array.push(value); - } - }; - - if (o.start && o.start.indexOf(lifelineId) !== -1) { - // Start -> stop. - rm(o.start, lifelineId); - add(o.stop, lifelineId); - } else if (o.stop && o.stop.indexOf(lifelineId) !== -1) { - // Stop -> default. - rm(o.start, lifelineId); - rm(o.stop, lifelineId); - } else { - // Default -> start. - add(o.start, lifelineId); - rm(o.stop, lifelineId); + ); } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Toggle fragment setting on click. - * @param fragment - * @private - **/ - static _toggleFragment(fragment) { - const f = fragment; - if (f.start === true) { - f.start = false; - f.stop = true; - } else if (f.stop === true) { - f.stop = false; - f.start = false; - } else { - f.start = true; - f.stop = false; - } - f.guard = ''; - f.operator = ''; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get ternary occurrences state. - * @param o occurrences. - * @param lifelineId from/to lifeline ID. - * @returns {number} - * @private - */ - static getOccurrenceState(o, lifelineId) { - if (o.start.indexOf(lifelineId) !== -1) { - return 1; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Toggle through three occurrence states on click. + * @param occurrence occurrences state, updated as side-effect. + * @param lifelineId message end that's being toggled. + * @private + */ + static _toggleOccurrence(occurrence, lifelineId) { + const o = occurrence; + + const rm = function rm(array, value) { + const index = array.indexOf(value); + if (index !== -1) { + array.splice(index, 1); + } + }; + + const add = function add(array, value) { + if (array.indexOf(value) === -1) { + array.push(value); + } + }; + + if (o.start && o.start.indexOf(lifelineId) !== -1) { + // Start -> stop. + rm(o.start, lifelineId); + add(o.stop, lifelineId); + } else if (o.stop && o.stop.indexOf(lifelineId) !== -1) { + // Stop -> default. + rm(o.start, lifelineId); + rm(o.stop, lifelineId); + } else { + // Default -> start. + add(o.start, lifelineId); + rm(o.stop, lifelineId); + } } - if (o.stop.indexOf(lifelineId) !== -1) { - return 2; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Toggle fragment setting on click. + * @param fragment + * @private + **/ + static _toggleFragment(fragment) { + const f = fragment; + if (f.start === true) { + f.start = false; + f.stop = true; + } else if (f.stop === true) { + f.stop = false; + f.start = false; + } else { + f.start = true; + f.stop = false; + } + f.guard = ''; + f.operator = ''; } - return 0; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get ternary fragment state. - * @param f fragment. - * @returns {number} - * @private - */ - static getFragmentState(f) { - if (f.start) { - return 1; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get ternary occurrences state. + * @param o occurrences. + * @param lifelineId from/to lifeline ID. + * @returns {number} + * @private + */ + static getOccurrenceState(o, lifelineId) { + if (o.start.indexOf(lifelineId) !== -1) { + return 1; + } + if (o.stop.indexOf(lifelineId) !== -1) { + return 2; + } + return 0; } - if (f.stop) { - return 2; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get ternary fragment state. + * @param f fragment. + * @returns {number} + * @private + */ + static getFragmentState(f) { + if (f.start) { + return 1; + } + if (f.stop) { + return 2; + } + return 0; } - return 0; - } } /** Element properties. */ Actions.propTypes = { - application: PropTypes.object.isRequired, - model: PropTypes.object.isRequired, + application: PropTypes.object.isRequired, + model: PropTypes.object.isRequired }; export default Actions; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/Lifeline.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/Lifeline.jsx index 86ee80f72e..09c8d92c9b 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/Lifeline.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/Lifeline.jsx @@ -28,238 +28,247 @@ import iconDelete from '../../../../../../../../../../res/ecomp/asdc/sequencer/s * LHS lifeline row view. */ class Lifeline extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct editor view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + + this.state = { + active: false, + name: props.lifeline.name + }; + + const metamodel = Common.assertNotNull(this.props.metamodel).unwrap(); + this.canReorder = metamodel.diagram.lifelines.constraints.reorder; + this.canDelete = metamodel.diagram.lifelines.constraints.delete; + + // Bindings. + + this.onChangeName = this.onChangeName.bind(this); + this.onBlurName = this.onBlurName.bind(this); + this.onClickDelete = this.onClickDelete.bind(this); + this.onMouseEnter = this.onMouseEnter.bind(this); + this.onMouseLeave = this.onMouseLeave.bind(this); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct editor view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); - - this.state = { - active: false, - name: props.lifeline.name, - }; - - const metamodel = Common.assertNotNull(this.props.metamodel).unwrap(); - this.canReorder = metamodel.diagram.lifelines.constraints.reorder; - this.canDelete = metamodel.diagram.lifelines.constraints.delete; - - // Bindings. - - this.onChangeName = this.onChangeName.bind(this); - this.onBlurName = this.onBlurName.bind(this); - this.onClickDelete = this.onClickDelete.bind(this); - this.onMouseEnter = this.onMouseEnter.bind(this); - this.onMouseLeave = this.onMouseLeave.bind(this); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle name change. - * @param event change event. - */ - onChangeName(event) { - this.setState({ name: event.target.value }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle name change. - * @param event change event. - */ - onBlurName(event) { - const options = this.props.application.getOptions(); - const sanitized = Common.sanitizeText(event.target.value, options, 'lifeline'); - const props = { - id: this.props.lifeline.id, - name: sanitized, - }; - this.props.designer.updateLifeline(props); - this.setState({ name: sanitized }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Handle lifeline delete. - */ - onClickDelete() { - this.props.designer.deleteLifeline(this.props.lifeline.id); - } + /** + * Handle name change. + * @param event change event. + */ + onChangeName(event) { + this.setState({ name: event.target.value }); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle name change. + * @param event change event. + */ + onBlurName(event) { + const options = this.props.application.getOptions(); + const sanitized = Common.sanitizeText( + event.target.value, + options, + 'lifeline' + ); + const props = { + id: this.props.lifeline.id, + name: sanitized + }; + this.props.designer.updateLifeline(props); + this.setState({ name: sanitized }); + } - /** - * Handle mouseover event. - */ - onMouseEnter() { - this.setState({ active: true }); - this.props.designer.onMouseEnterLifeline(this.props.lifeline.id); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Handle lifeline delete. + */ + onClickDelete() { + this.props.designer.deleteLifeline(this.props.lifeline.id); + } - /** - * Handle mouseleave event. - */ - onMouseLeave() { - this.setState({ active: false }); - this.props.designer.onMouseLeaveLifeline(this.props.lifeline.id); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Handle mouseover event. + */ + onMouseEnter() { + this.setState({ active: true }); + this.props.designer.onMouseEnterLifeline(this.props.lifeline.id); + } - /** - * Get whether metadata permits reorder. - * @returns true if reorderable. - */ - isCanReorder() { - return this.canReorder; - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Handle mouseleave event. + */ + onMouseLeave() { + this.setState({ active: false }); + this.props.designer.onMouseLeaveLifeline(this.props.lifeline.id); + } - /** - * Get whether metadata permits delete. - * @returns true if lifeline can be deleted. - */ - isCanDelete() { - return this.canDelete; - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Get whether metadata permits reorder. + * @returns true if reorderable. + */ + isCanReorder() { + return this.canReorder; + } - /** - * React render. - * @returns {*} - */ - render() { + // /////////////////////////////////////////////////////////////////////////////////////////////// - const id = this.props.lifeline.id; - const activeClass = (this.props.active === true) ? 'asdcs-active' : ''; - const { connectDragSource, connectDropTarget } = this.props; - return connectDragSource(connectDropTarget( + /** + * Get whether metadata permits delete. + * @returns true if lifeline can be deleted. + */ + isCanDelete() { + return this.canDelete; + } - <div - className={`asdcs-designer-lifeline ${activeClass}`} - data-id={id} - onMouseEnter={this.onMouseEnter} - onMouseLeave={this.onMouseLeave} - > - <table className="asdcs-designer-layout asdcs-designer-lifeline-row1"> - <tbody> - <tr> - <td> - <div className="asdcs-designer-sort asdcs-designer-icon"> - <Icon glyph={iconHandle} /> - </div> - </td> - <td> - <div className="asdcs-designer-lifeline-index">{this.props.lifeline.index}.</div> - </td> - <td> - <div className="asdcs-designer-lifeline-name"> - <input - type="text" - className="asdcs-editable" - placeholder="Unnamed" - value={this.state.name} - onChange={this.onChangeName} - onBlur={this.onBlurName} - /> + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * React render. + * @returns {*} + */ + render() { + const id = this.props.lifeline.id; + const activeClass = this.props.active === true ? 'asdcs-active' : ''; + const { connectDragSource, connectDropTarget } = this.props; + return connectDragSource( + connectDropTarget( + <div + className={`asdcs-designer-lifeline ${activeClass}`} + data-id={id} + onMouseEnter={this.onMouseEnter} + onMouseLeave={this.onMouseLeave}> + <table className="asdcs-designer-layout asdcs-designer-lifeline-row1"> + <tbody> + <tr> + <td> + <div className="asdcs-designer-sort asdcs-designer-icon"> + <Icon glyph={iconHandle} /> + </div> + </td> + <td> + <div className="asdcs-designer-lifeline-index"> + {this.props.lifeline.index}. + </div> + </td> + <td> + <div className="asdcs-designer-lifeline-name"> + <input + type="text" + className="asdcs-editable" + placeholder="Unnamed" + value={this.state.name} + onChange={this.onChangeName} + onBlur={this.onBlurName} + /> + </div> + </td> + <td> + <div + className="asdcs-designer-delete asdcs-designer-icon" + onClick={this.onClickDelete}> + <Icon glyph={iconDelete} /> + </div> + </td> + </tr> + </tbody> + </table> </div> - </td> - <td> - <div className="asdcs-designer-delete asdcs-designer-icon" onClick={this.onClickDelete}> - <Icon glyph={iconDelete} /> - </div> - </td> - </tr> - </tbody> - </table> - </div> - )); - } + ) + ); + } } /** * Declare properties. */ Lifeline.propTypes = { - application: PropTypes.object.isRequired, - designer: PropTypes.object.isRequired, - lifeline: PropTypes.object.isRequired, - active: PropTypes.bool.isRequired, - metamodel: PropTypes.object.isRequired, - id: PropTypes.any.isRequired, - index: PropTypes.number.isRequired, - lifelines: PropTypes.object.isRequired, - isDragging: PropTypes.bool.isRequired, - connectDragSource: PropTypes.func.isRequired, - connectDropTarget: PropTypes.func.isRequired, + application: PropTypes.object.isRequired, + designer: PropTypes.object.isRequired, + lifeline: PropTypes.object.isRequired, + active: PropTypes.bool.isRequired, + metamodel: PropTypes.object.isRequired, + id: PropTypes.any.isRequired, + index: PropTypes.number.isRequired, + lifelines: PropTypes.object.isRequired, + isDragging: PropTypes.bool.isRequired, + connectDragSource: PropTypes.func.isRequired, + connectDropTarget: PropTypes.func.isRequired }; /** DND. */ const source = { - beginDrag(props) { - return { - id: props.id, - index: props.index, - }; - }, + beginDrag(props) { + return { + id: props.id, + index: props.index + }; + } }; /** DND. */ const sourceCollect = function collection(connect, monitor) { - return { - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging(), - }; + return { + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging() + }; }; /** DND. */ const target = { - drop(props, monitor, component) { - Common.assertNotNull(props); - Common.assertNotNull(monitor); - const decorated = component.getDecoratedComponentInstance(); - if (decorated) { - const lifelines = decorated.props.lifelines; - if (lifelines) { - const dragIndex = monitor.getItem().index; - const hoverIndex = lifelines.getHoverIndex(); - lifelines.onDrop(dragIndex, hoverIndex); - } - } - }, - hover(props, monitor, component) { - Common.assertNotNull(props); - Common.assertNotNull(monitor); - if (component) { - const decorated = component.getDecoratedComponentInstance(); - if (decorated) { - const lifelines = decorated.props.lifelines; - if (lifelines) { - lifelines.setHoverIndex(decorated.props.index); + drop(props, monitor, component) { + Common.assertNotNull(props); + Common.assertNotNull(monitor); + const decorated = component.getDecoratedComponentInstance(); + if (decorated) { + const lifelines = decorated.props.lifelines; + if (lifelines) { + const dragIndex = monitor.getItem().index; + const hoverIndex = lifelines.getHoverIndex(); + lifelines.onDrop(dragIndex, hoverIndex); + } + } + }, + hover(props, monitor, component) { + Common.assertNotNull(props); + Common.assertNotNull(monitor); + if (component) { + const decorated = component.getDecoratedComponentInstance(); + if (decorated) { + const lifelines = decorated.props.lifelines; + if (lifelines) { + lifelines.setHoverIndex(decorated.props.index); + } + } } - } } - }, }; /** DND. */ function targetCollect(connect, monitor) { - return { - connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver(), - }; + return { + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver() + }; } - +/* eslint-disable new-cap */ const wrapper1 = DragSource('lifeline', source, sourceCollect)(Lifeline); -export default DropTarget(['lifeline', 'lifeline-new'], target, targetCollect)(wrapper1); +export default DropTarget(['lifeline', 'lifeline-new'], target, targetCollect)( + wrapper1 +); +/* eslint-enable new-cap */ diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/LifelineNew.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/LifelineNew.jsx index a8147be370..beb6364670 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/LifelineNew.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/LifelineNew.jsx @@ -26,88 +26,92 @@ import iconHandle from '../../../../../../../../../../res/ecomp/asdc/sequencer/s * LHS lifeline row view. */ class LifelineNew extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Construct view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); - /** - * Construct view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); + // Bindings. - // Bindings. + this.onClickAdd = this.onClickAdd.bind(this); + } - this.onClickAdd = this.onClickAdd.bind(this); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Handle click event. + */ + onClickAdd() { + this.props.designer.addLifeline(); + } - /** - * Handle click event. - */ - onClickAdd() { - this.props.designer.addLifeline(); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render view. - * @returns {*} - */ - render() { - const { connectDragSource } = this.props; - return connectDragSource( - <div className="asdcs-designer-lifeline asdcs-designer-lifeline-new"> - <table className="asdcs-designer-layout asdcs-designer-lifeline-new"> - <tbody> - <tr> - <td> - <div className="asdcs-designer-sort asdcs-designer-icon"> - <Icon glyph={iconHandle} /> - </div> - </td> - <td> - <div className="asdcs-designer-label" onClick={this.onClickAdd}> - Add Lifeline - </div> - </td> - <td> - <div className="asdcs-designer-icon" onClick={this.onClickAdd}> - <Icon glyph={iconPlus} /> - </div> - </td> - <td> </td> - </tr> - </tbody> - </table> - </div> - ); - } + /** + * Render view. + * @returns {*} + */ + render() { + const { connectDragSource } = this.props; + return connectDragSource( + <div className="asdcs-designer-lifeline asdcs-designer-lifeline-new"> + <table className="asdcs-designer-layout asdcs-designer-lifeline-new"> + <tbody> + <tr> + <td> + <div className="asdcs-designer-sort asdcs-designer-icon"> + <Icon glyph={iconHandle} /> + </div> + </td> + <td> + <div + className="asdcs-designer-label" + onClick={this.onClickAdd}> + Add Lifeline + </div> + </td> + <td> + <div + className="asdcs-designer-icon" + onClick={this.onClickAdd}> + <Icon glyph={iconPlus} /> + </div> + </td> + <td> </td> + </tr> + </tbody> + </table> + </div> + ); + } } /** Element properties. */ LifelineNew.propTypes = { - designer: PropTypes.object.isRequired, - lifelines: PropTypes.object.isRequired, - connectDragSource: PropTypes.func.isRequired, + designer: PropTypes.object.isRequired, + lifelines: PropTypes.object.isRequired, + connectDragSource: PropTypes.func.isRequired }; /** DND. */ const source = { - beginDrag(props) { - return { id: props.id }; - }, + beginDrag(props) { + return { id: props.id }; + } }; /** DND. */ const collect = function collection(connect, monitor) { - return { - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging(), - }; + return { + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging() + }; }; - +/* eslint-disable new-cap */ export default DragSource('lifeline-new', source, collect)(LifelineNew); +/* eslint-enable new-cap */ diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/Lifelines.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/Lifelines.jsx index 2f82fec7a0..248264b6df 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/Lifelines.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/lifeline/Lifelines.jsx @@ -28,111 +28,110 @@ import LifelineNew from './LifelineNew'; * @constructor */ class Lifelines extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + this.setHoverIndex = this.setHoverIndex.bind(this); + this.getHoverIndex = this.getHoverIndex.bind(this); + this.onDrop = this.onDrop.bind(this); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); - this.setHoverIndex = this.setHoverIndex.bind(this); - this.getHoverIndex = this.getHoverIndex.bind(this); - this.onDrop = this.onDrop.bind(this); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Record last hover index as non-state. - * @param index index. - */ - setHoverIndex(index) { - this.hoverIndex = index; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get last recorded hover index. - * @returns {*} - */ - getHoverIndex() { - return this.hoverIndex; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle drop. - * @param dragIndex dragged item index; undefined if new. - * @param hoverIndex drop index. - */ - onDrop(dragIndex, hoverIndex) { - if (hoverIndex >= 0) { - const application = this.props.application; - const model = application.getModel(); - if (Common.isNumber(dragIndex)) { - if (dragIndex !== hoverIndex) { - model.reorderLifelines(dragIndex, hoverIndex); - } - } else { - model.addLifeline(hoverIndex); - } - this.forceUpdate(); - application.renderDiagram(); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Record last hover index as non-state. + * @param index index. + */ + setHoverIndex(index) { + this.hoverIndex = index; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get last recorded hover index. + * @returns {*} + */ + getHoverIndex() { + return this.hoverIndex; } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render view. - * @returns {XML} - */ - render() { - const model = this.props.application.getModel(); - const metamodel = model.getMetamodel(); - const diagram = model.unwrap().diagram; - - const lifelines = []; - for (const lifeline of diagram.lifelines) { - lifelines.push(<Lifeline - key={`l${lifeline.id}`} - application={this.props.application} - designer={this.props.designer} - lifeline={lifeline} - active={this.props.activeLifelineId === lifeline.id} - id={lifeline.id} - metamodel={metamodel} - lifelines={this} - index={lifelines.length} - />); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle drop. + * @param dragIndex dragged item index; undefined if new. + * @param hoverIndex drop index. + */ + onDrop(dragIndex, hoverIndex) { + if (hoverIndex >= 0) { + const application = this.props.application; + const model = application.getModel(); + if (Common.isNumber(dragIndex)) { + if (dragIndex !== hoverIndex) { + model.reorderLifelines(dragIndex, hoverIndex); + } + } else { + model.addLifeline(hoverIndex); + } + this.forceUpdate(); + application.renderDiagram(); + } } - lifelines.push(<LifelineNew - key="_l" - designer={this.props.designer} - lifelines={this} - />); - - return ( - <div className="asdcs-designer-lifelines"> - {lifelines} - </div> - ); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render view. + * @returns {XML} + */ + render() { + const model = this.props.application.getModel(); + const metamodel = model.getMetamodel(); + const diagram = model.unwrap().diagram; + + const lifelines = []; + for (const lifeline of diagram.lifelines) { + lifelines.push( + <Lifeline + key={`l${lifeline.id}`} + application={this.props.application} + designer={this.props.designer} + lifeline={lifeline} + active={this.props.activeLifelineId === lifeline.id} + id={lifeline.id} + metamodel={metamodel} + lifelines={this} + index={lifelines.length} + /> + ); + } + + lifelines.push( + <LifelineNew + key="_l" + designer={this.props.designer} + lifelines={this} + /> + ); + + return <div className="asdcs-designer-lifelines">{lifelines}</div>; + } } /** * Declare properties. */ Lifelines.propTypes = { - application: PropTypes.object.isRequired, - designer: PropTypes.object.isRequired, - activeLifelineId: PropTypes.string, + application: PropTypes.object.isRequired, + designer: PropTypes.object.isRequired, + activeLifelineId: PropTypes.string }; export default Lifelines; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/Message.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/Message.jsx index a2c7f5122a..ba77e8658f 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/Message.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/Message.jsx @@ -34,482 +34,480 @@ import iconResponse from '../../../../../../../../../../res/ecomp/asdc/sequencer * LHS message row view. */ class Message extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + + this.state = { + active: false, + name: props.message.name || '' + }; + + this.combinedOptions = [ + { + value: 'REQUEST_SYNC' + }, + { + value: 'REQUEST_ASYNC' + }, + { + value: 'RESPONSE' + } + ]; + + // Bindings. + + this.onChangeName = this.onChangeName.bind(this); + this.onBlurName = this.onBlurName.bind(this); + this.onChangeType = this.onChangeType.bind(this); + this.onChangeFrom = this.onChangeFrom.bind(this); + this.onChangeTo = this.onChangeTo.bind(this); + this.onClickDelete = this.onClickDelete.bind(this); + this.onClickActions = this.onClickActions.bind(this); + this.onClickNotes = this.onClickNotes.bind(this); + this.onMouseEnter = this.onMouseEnter.bind(this); + this.onMouseLeave = this.onMouseLeave.bind(this); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Construct view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); + /** + * Handle name change. + * @param event change event. + */ + onChangeName(event) { + this.setState({ name: event.target.value }); + } - this.state = { - active: false, - name: props.message.name || '', - }; + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle name change. + * @param event change event. + */ + onBlurName(event) { + const options = this.props.application.getOptions(); + const sanitized = Common.sanitizeText( + event.target.value, + options, + 'message' + ); + const props = { + id: this.props.message.id, + name: sanitized + }; + this.props.designer.updateMessage(props); + this.setState({ name: sanitized }); + } - this.combinedOptions = [{ - value: 'REQUEST_SYNC', - }, { - value: 'REQUEST_ASYNC', - }, { - value: 'RESPONSE', - }]; - - // Bindings. - - this.onChangeName = this.onChangeName.bind(this); - this.onBlurName = this.onBlurName.bind(this); - this.onChangeType = this.onChangeType.bind(this); - this.onChangeFrom = this.onChangeFrom.bind(this); - this.onChangeTo = this.onChangeTo.bind(this); - this.onClickDelete = this.onClickDelete.bind(this); - this.onClickActions = this.onClickActions.bind(this); - this.onClickNotes = this.onClickNotes.bind(this); - this.onMouseEnter = this.onMouseEnter.bind(this); - this.onMouseLeave = this.onMouseLeave.bind(this); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle name change. - * @param event change event. - */ - onChangeName(event) { - this.setState({ name: event.target.value }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle name change. - * @param event change event. - */ - onBlurName(event) { - const options = this.props.application.getOptions(); - const sanitized = Common.sanitizeText(event.target.value, options, 'message'); - const props = { - id: this.props.message.id, - name: sanitized, - }; - this.props.designer.updateMessage(props); - this.setState({ name: sanitized }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle delete. - */ - onClickDelete() { - this.props.designer.deleteMessage(this.props.message.id); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle menu click. - */ - onClickActions(event) { - this.props.designer.showActions(this.props.message.id, { x: event.pageX, y: event.pageY }); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle menu click. - */ - onClickNotes() { - this.props.designer.showNotes(this.props.message.id); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle selection. - * @param value selection. - */ - onChangeFrom(value) { - if (value.target) { - this.updateMessage({ from: value.target.value }); - } else { - this.updateMessage({ from: value.value }); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle delete. + */ + onClickDelete() { + this.props.designer.deleteMessage(this.props.message.id); } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle selection. - * @param value selection. - */ - onChangeTo(value) { - if (value.target) { - this.updateMessage({ to: value.target.value }); - } else { - this.updateMessage({ to: value.value }); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle menu click. + */ + onClickActions(event) { + this.props.designer.showActions(this.props.message.id, { + x: event.pageX, + y: event.pageY + }); } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle selection. - * @param selected selection. - */ - onChangeType(selected) { - - const value = selected.target ? selected.target.value : selected.value; - const props = {}; - if (value.indexOf('RESPONSE') !== -1) { - props.type = 'response'; - props.asynchronous = false; - } else { - props.type = 'request'; - props.asynchronous = (value.indexOf('ASYNC') !== -1); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle menu click. + */ + onClickNotes() { + this.props.designer.showNotes(this.props.message.id); } - this.updateMessage(props); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle mouse event. - */ - onMouseEnter() { - this.setState({ active: true }); - this.props.designer.onMouseEnterMessage(this.props.message.id); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle mouse event. - */ - onMouseLeave() { - this.setState({ active: false }); - this.props.designer.onMouseLeaveMessage(this.props.message.id); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Update message properties. - * @param props properties updates. - */ - updateMessage(props) { - const update = { - id: this.props.message.id, - }; - for (const k of Object.keys(props)) { - update[k] = props[k]; + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle selection. + * @param value selection. + */ + onChangeFrom(value) { + if (value.target) { + this.updateMessage({ from: value.target.value }); + } else { + this.updateMessage({ from: value.value }); + } } - this.props.designer.updateMessage(update); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render icon. - * @param option selection. - * @returns {XML} - */ - renderOption(option) { - if (option.value === 'RESPONSE') { - return <Icon glyph={iconResponse} />; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle selection. + * @param value selection. + */ + onChangeTo(value) { + if (value.target) { + this.updateMessage({ to: value.target.value }); + } else { + this.updateMessage({ to: value.value }); + } } - if (option.value === 'REQUEST_ASYNC') { - return <Icon glyph={iconRequestAsync} />; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle selection. + * @param selected selection. + */ + onChangeType(selected) { + const value = selected.target ? selected.target.value : selected.value; + const props = {}; + if (value.indexOf('RESPONSE') !== -1) { + props.type = 'response'; + props.asynchronous = false; + } else { + props.type = 'request'; + props.asynchronous = value.indexOf('ASYNC') !== -1; + } + + this.updateMessage(props); } - return <Icon glyph={iconRequestSync} />; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get request/response and asynchronous combined constant. - * @param message message whose properties define spec. - * @returns {*} - */ - getMessageSpec(message) { - if (message.type === 'response') { - return 'RESPONSE'; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle mouse event. + */ + onMouseEnter() { + this.setState({ active: true }); + this.props.designer.onMouseEnterMessage(this.props.message.id); } - if (message.asynchronous) { - return 'REQUEST_ASYNC'; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle mouse event. + */ + onMouseLeave() { + this.setState({ active: false }); + this.props.designer.onMouseLeaveMessage(this.props.message.id); } - return 'REQUEST_SYNC'; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * @returns {*} - * @private - */ - renderHTMLSelect() { - - const message = this.props.message; - const from = this.props.from; - const to = Common.assertNotNull(this.props.to); - const messageNotesActiveClass = message.notes && message.notes.length > 0 ? 'asdcs-active' : ''; - const combinedValue = this.getMessageSpec(message); - - const lifelineOptions = []; - for (const lifeline of this.props.model.unwrap().diagram.lifelines) { - lifelineOptions.push(<option - key={lifeline.id} - value={lifeline.id} - > - {lifeline.name} - </option>); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Update message properties. + * @param props properties updates. + */ + updateMessage(props) { + const update = { + id: this.props.message.id + }; + for (const k of Object.keys(props)) { + update[k] = props[k]; + } + this.props.designer.updateMessage(update); } - const activeClass = (this.state.active || this.props.active) ? 'asdcs-active' : ''; - const { connectDragSource, connectDropTarget } = this.props; - return connectDragSource(connectDropTarget( - <div - className={`asdcs-designer-message ${activeClass}`} - data-id={message.id} - onMouseEnter={this.onMouseEnter} - onMouseLeave={this.onMouseLeave} - > - - <table className="asdcs-designer-layout asdcs-designer-message-row1"> - <tbody> - <tr> - <td> - <div className="asdcs-designer-sort asdcs-designer-icon"> - <Icon glyph={iconHandle} /> - </div> - </td> - <td> - <div className="asdcs-designer-message-index">{message.index}.</div> - </td> - <td> - <div className="asdcs-designer-message-name"> - <input - type="text" - className="asdcs-editable" - value={this.state.name} - placeholder="Unnamed" - onBlur={this.onBlurName} - onChange={this.onChangeName} - /> - </div> - </td> - <td> - <div className="asdcs-designer-actions"> - <div - className="asdcs-designer-settings asdcs-designer-icon" - onClick={this.onClickActions} - > - <Icon glyph={iconSettings} /> - </div> - <div - className={`asdcs-designer-notes asdcs-designer-icon ${messageNotesActiveClass}`} - onClick={this.onClickNotes} - > - <Icon glyph={iconNotes} /> - </div> - <div - className="asdcs-designer-delete asdcs-designer-icon" - onClick={this.onClickDelete} - > - <Icon glyph={iconDelete} /> - </div> - </div> - </td> - </tr> - </tbody> - </table> - - <table className="asdcs-designer-layout asdcs-designer-message-row2"> - <tbody> - <tr> - <td> - <select - onChange={this.onChangeFrom} - className="asdcs-designer-select-message-from" - value={from.id} - onChange={this.onChangeFrom} - > - options={lifelineOptions} - </select> - </td> - <td> - <select - onChange={this.onChangeFrom} - className="asdcs-designer-select-message-type" - value={combinedValue} - onChange={this.onChangeType} - > - <option value="REQUEST_SYNC">⇾</option> - <option value="REQUEST_ASYNC">→</option> - <option value="RESPONSE">⇠</option> - </select> - </td> - <td> - <select - onChange={this.onChangeFrom} - className="asdcs-designer-select-message-to" - value={to.id} - onChange={this.onChangeTo} - > - options={lifelineOptions} - </select> - </td> - </tr> - </tbody> - </table> - - </div> - )); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render view. - * @returns {*} - * @private - */ - renderReactSelect() { - - const message = this.props.message; - const from = this.props.from; - const to = Common.assertNotNull(this.props.to); - const messageNotesActiveClass = message.notes && message.notes.length > 0 ? 'asdcs-active' : ''; - const combinedValue = this.getMessageSpec(message); - - const lifelineOptions = []; - for (const lifeline of this.props.model.unwrap().diagram.lifelines) { - lifelineOptions.push({ - value: lifeline.id, - label: lifeline.name, - }); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render icon. + * @param option selection. + * @returns {XML} + */ + renderOption(option) { + if (option.value === 'RESPONSE') { + return <Icon glyph={iconResponse} />; + } + if (option.value === 'REQUEST_ASYNC') { + return <Icon glyph={iconRequestAsync} />; + } + return <Icon glyph={iconRequestSync} />; } - const activeClass = (this.state.active || this.props.active) ? 'asdcs-active' : ''; - const { connectDragSource, connectDropTarget } = this.props; - return connectDragSource(connectDropTarget( - - <div - className={`asdcs-designer-message ${activeClass}`} - data-id={message.id} - onMouseEnter={this.onMouseEnter} - onMouseLeave={this.onMouseLeave} - > - - <table className="asdcs-designer-layout asdcs-designer-message-row1"> - <tbody> - <tr> - <td> - <div className="asdcs-designer-sort asdcs-designer-icon"> - <Icon glyph={iconHandle} /> - </div> - </td> - <td> - <div className="asdcs-designer-message-index">{message.index}.</div> - </td> - <td> - <div className="asdcs-designer-message-name"> - <input - type="text" - className="asdcs-editable" - value={this.state.name} - placeholder="Unnamed" - onBlur={this.onBlurName} - onChange={this.onChangeName} - /> + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get request/response and asynchronous combined constant. + * @param message message whose properties define spec. + * @returns {*} + */ + getMessageSpec(message) { + if (message.type === 'response') { + return 'RESPONSE'; + } + if (message.asynchronous) { + return 'REQUEST_ASYNC'; + } + return 'REQUEST_SYNC'; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * @returns {*} + * @private + */ + renderHTMLSelect() { + const message = this.props.message; + const from = this.props.from; + const to = Common.assertNotNull(this.props.to); + const messageNotesActiveClass = + message.notes && message.notes.length > 0 ? 'asdcs-active' : ''; + const combinedValue = this.getMessageSpec(message); + + const lifelineOptions = []; + for (const lifeline of this.props.model.unwrap().diagram.lifelines) { + lifelineOptions.push( + <option key={lifeline.id} value={lifeline.id}> + {lifeline.name} + </option> + ); + } + + const activeClass = + this.state.active || this.props.active ? 'asdcs-active' : ''; + const { connectDragSource, connectDropTarget } = this.props; + return connectDragSource( + connectDropTarget( + <div + className={`asdcs-designer-message ${activeClass}`} + data-id={message.id} + onMouseEnter={this.onMouseEnter} + onMouseLeave={this.onMouseLeave}> + <table className="asdcs-designer-layout asdcs-designer-message-row1"> + <tbody> + <tr> + <td> + <div className="asdcs-designer-sort asdcs-designer-icon"> + <Icon glyph={iconHandle} /> + </div> + </td> + <td> + <div className="asdcs-designer-message-index"> + {message.index}. + </div> + </td> + <td> + <div className="asdcs-designer-message-name"> + <input + type="text" + className="asdcs-editable" + value={this.state.name} + placeholder="Unnamed" + onBlur={this.onBlurName} + onChange={this.onChangeName} + /> + </div> + </td> + <td> + <div className="asdcs-designer-actions"> + <div + className="asdcs-designer-settings asdcs-designer-icon" + onClick={this.onClickActions}> + <Icon glyph={iconSettings} /> + </div> + <div + className={`asdcs-designer-notes asdcs-designer-icon ${messageNotesActiveClass}`} + onClick={this.onClickNotes}> + <Icon glyph={iconNotes} /> + </div> + <div + className="asdcs-designer-delete asdcs-designer-icon" + onClick={this.onClickDelete}> + <Icon glyph={iconDelete} /> + </div> + </div> + </td> + </tr> + </tbody> + </table> + + <table className="asdcs-designer-layout asdcs-designer-message-row2"> + <tbody> + <tr> + <td> + <select + className="asdcs-designer-select-message-from" + value={from.id} + onChange={this.onChangeFrom}> + options={lifelineOptions} + </select> + </td> + <td> + <select + className="asdcs-designer-select-message-type" + value={combinedValue} + onChange={this.onChangeType}> + <option value="REQUEST_SYNC">⇾</option> + <option value="REQUEST_ASYNC">→</option> + <option value="RESPONSE">⇠</option> + </select> + </td> + <td> + <select + className="asdcs-designer-select-message-to" + value={to.id} + onChange={this.onChangeTo}> + options={lifelineOptions} + </select> + </td> + </tr> + </tbody> + </table> </div> - </td> - <td> - <div className="asdcs-designer-actions"> - <div - className="asdcs-designer-settings asdcs-designer-icon" - onClick={this.onClickActions} - > - <Icon glyph={iconSettings} /> - </div> - <div - className={`asdcs-designer-notes asdcs-designer-icon ${messageNotesActiveClass}`} - onClick={this.onClickNotes} - > - <Icon glyph={iconNotes} /> - </div> - <div - className="asdcs-designer-delete asdcs-designer-icon" - onClick={this.onClickDelete} - > - <Icon glyph={iconDelete} /> - </div> + ) + ); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render view. + * @returns {*} + * @private + */ + renderReactSelect() { + const message = this.props.message; + const from = this.props.from; + const to = Common.assertNotNull(this.props.to); + const messageNotesActiveClass = + message.notes && message.notes.length > 0 ? 'asdcs-active' : ''; + const combinedValue = this.getMessageSpec(message); + + const lifelineOptions = []; + for (const lifeline of this.props.model.unwrap().diagram.lifelines) { + lifelineOptions.push({ + value: lifeline.id, + label: lifeline.name + }); + } + + const activeClass = + this.state.active || this.props.active ? 'asdcs-active' : ''; + const { connectDragSource, connectDropTarget } = this.props; + return connectDragSource( + connectDropTarget( + <div + className={`asdcs-designer-message ${activeClass}`} + data-id={message.id} + onMouseEnter={this.onMouseEnter} + onMouseLeave={this.onMouseLeave}> + <table className="asdcs-designer-layout asdcs-designer-message-row1"> + <tbody> + <tr> + <td> + <div className="asdcs-designer-sort asdcs-designer-icon"> + <Icon glyph={iconHandle} /> + </div> + </td> + <td> + <div className="asdcs-designer-message-index"> + {message.index}. + </div> + </td> + <td> + <div className="asdcs-designer-message-name"> + <input + type="text" + className="asdcs-editable" + value={this.state.name} + placeholder="Unnamed" + onBlur={this.onBlurName} + onChange={this.onChangeName} + /> + </div> + </td> + <td> + <div className="asdcs-designer-actions"> + <div + className="asdcs-designer-settings asdcs-designer-icon" + onClick={this.onClickActions}> + <Icon glyph={iconSettings} /> + </div> + <div + className={`asdcs-designer-notes asdcs-designer-icon ${messageNotesActiveClass}`} + onClick={this.onClickNotes}> + <Icon glyph={iconNotes} /> + </div> + <div + className="asdcs-designer-delete asdcs-designer-icon" + onClick={this.onClickDelete}> + <Icon glyph={iconDelete} /> + </div> + </div> + </td> + </tr> + </tbody> + </table> + + <table className="asdcs-designer-layout asdcs-designer-message-row2"> + <tbody> + <tr> + <td> + <Select + className="asdcs-editable-select asdcs-designer-editable-message-from" + openOnFocus + clearable={false} + searchable={false} + value={from.id} + onChange={this.onChangeFrom} + options={lifelineOptions} + /> + </td> + <td> + <Select + className="asdcs-editable-select asdcs-designer-editable-message-type" + openOnFocus + clearable={false} + searchable={false} + value={combinedValue} + onChange={this.onChangeType} + options={this.combinedOptions} + optionRenderer={this.renderOption} + valueRenderer={this.renderOption} + /> + </td> + <td> + <Select + className="asdcs-editable-select asdcs-designer-editable-message-to" + openOnFocus + clearable={false} + searchable={false} + value={to.id} + onChange={this.onChangeTo} + options={lifelineOptions} + /> + </td> + </tr> + </tbody> + </table> </div> - </td> - </tr> - </tbody> - </table> - - <table className="asdcs-designer-layout asdcs-designer-message-row2"> - <tbody> - <tr> - <td> - <Select - className="asdcs-editable-select asdcs-designer-editable-message-from" - openOnFocus - clearable={false} - searchable={false} - value={from.id} - onChange={this.onChangeFrom} - options={lifelineOptions} - /> - </td> - <td> - <Select - className="asdcs-editable-select asdcs-designer-editable-message-type" - openOnFocus - clearable={false} - searchable={false} - value={combinedValue} - onChange={this.onChangeType} - options={this.combinedOptions} - optionRenderer={this.renderOption} - valueRenderer={this.renderOption} - /> - </td> - <td> - <Select - className="asdcs-editable-select asdcs-designer-editable-message-to" - openOnFocus - clearable={false} - searchable={false} - value={to.id} - onChange={this.onChangeTo} - options={lifelineOptions} - /> - </td> - - </tr> - </tbody> - </table> - - </div> - )); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - render() { - const options = this.props.application.getOptions(); - if (options.useHtmlSelect) { - return this.renderHTMLSelect(); + ) + ); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + render() { + const options = this.props.application.getOptions(); + if (options.useHtmlSelect) { + return this.renderHTMLSelect(); + } + return this.renderReactSelect(); } - return this.renderReactSelect(); - } } /** @@ -517,72 +515,74 @@ class Message extends React.Component { * @type {{designer: *, message: *, from: *, to: *, model: *, connectDragSource: *}} */ Message.propTypes = { - application: PropTypes.object.isRequired, - designer: PropTypes.object.isRequired, - message: PropTypes.object.isRequired, - active: PropTypes.bool.isRequired, - from: PropTypes.object.isRequired, - to: PropTypes.object.isRequired, - model: PropTypes.object.isRequired, - index: PropTypes.number.isRequired, - messages: PropTypes.object.isRequired, - connectDragSource: PropTypes.func.isRequired, - connectDropTarget: PropTypes.func.isRequired, + application: PropTypes.object.isRequired, + designer: PropTypes.object.isRequired, + message: PropTypes.object.isRequired, + active: PropTypes.bool.isRequired, + from: PropTypes.object.isRequired, + to: PropTypes.object.isRequired, + model: PropTypes.object.isRequired, + index: PropTypes.number.isRequired, + messages: PropTypes.object.isRequired, + connectDragSource: PropTypes.func.isRequired, + connectDropTarget: PropTypes.func.isRequired }; /** DND. */ const source = { - beginDrag(props) { - return { - id: props.id, - index: props.index, - }; - }, + beginDrag(props) { + return { + id: props.id, + index: props.index + }; + } }; /** DND. */ const sourceCollect = function collection(connect, monitor) { - return { - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging(), - }; + return { + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging() + }; }; - /** DND. */ const target = { - drop(props, monitor, component) { - Common.assertNotNull(props); - Common.assertNotNull(monitor); - const decorated = component.getDecoratedComponentInstance(); - if (decorated) { - const messages = decorated.props.messages; - if (messages) { - const dragIndex = monitor.getItem().index; - const hoverIndex = messages.getHoverIndex(); - messages.onDrop(dragIndex, hoverIndex); - } + drop(props, monitor, component) { + Common.assertNotNull(props); + Common.assertNotNull(monitor); + const decorated = component.getDecoratedComponentInstance(); + if (decorated) { + const messages = decorated.props.messages; + if (messages) { + const dragIndex = monitor.getItem().index; + const hoverIndex = messages.getHoverIndex(); + messages.onDrop(dragIndex, hoverIndex); + } + } + }, + hover(props, monitor, component) { + Common.assertNotNull(props); + Common.assertNotNull(monitor); + if (component) { + const decorated = component.getDecoratedComponentInstance(); + if (decorated) { + decorated.props.messages.setHoverIndex(decorated.props.index); + } + } } - }, - hover(props, monitor, component) { - Common.assertNotNull(props); - Common.assertNotNull(monitor); - if (component) { - const decorated = component.getDecoratedComponentInstance(); - if (decorated) { - decorated.props.messages.setHoverIndex(decorated.props.index); - } - } - }, }; /** DND. */ function targetCollect(connect, monitor) { - return { - connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver(), - }; + return { + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver() + }; } - +/* eslint-disable new-cap */ const wrapper = DragSource('message', source, sourceCollect)(Message); -export default DropTarget(['message', 'message-new'], target, targetCollect)(wrapper); +export default DropTarget(['message', 'message-new'], target, targetCollect)( + wrapper +); +/* eslint-enable new-cap */ diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/MessageNew.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/MessageNew.jsx index c47cf28a64..78e892baf8 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/MessageNew.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/MessageNew.jsx @@ -26,82 +26,84 @@ import iconHandle from '../../../../../../../../../../res/ecomp/asdc/sequencer/s * LHS lifeline row view. */ class MessageNew extends React.Component { + /** + * Construct view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + this.onClickAdd = this.onClickAdd.bind(this); + } - /** - * Construct view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); - this.onClickAdd = this.onClickAdd.bind(this); - } + /** + * Handle add. + */ + onClickAdd() { + this.props.designer.addMessage(); + } - /** - * Handle add. - */ - onClickAdd() { - this.props.designer.addMessage(); - } - - /** - * Render view. - * @returns {*} - */ - render() { - const { connectDragSource } = this.props; - return connectDragSource( - <div className="asdcs-designer-message asdcs-designer-message-new"> - <table className="asdcs-designer-layout asdcs-designer-message-new"> - <tbody> - <tr> - <td> - <div className="asdcs-designer-sort asdcs-designer-icon"> - <Icon glyph={iconHandle} /> - </div> - </td> - <td> - <div className="asdcs-designer-label" onClick={this.onClickAdd}> - Add Message - </div> - </td> - <td> - <div className="asdcs-designer-icon" onClick={this.onClickAdd}> - <Icon glyph={iconPlus} /> - </div> - </td> - <td> - - </td> - </tr> - </tbody> - </table> - </div> - ); - } + /** + * Render view. + * @returns {*} + */ + render() { + const { connectDragSource } = this.props; + return connectDragSource( + <div className="asdcs-designer-message asdcs-designer-message-new"> + <table className="asdcs-designer-layout asdcs-designer-message-new"> + <tbody> + <tr> + <td> + <div className="asdcs-designer-sort asdcs-designer-icon"> + <Icon glyph={iconHandle} /> + </div> + </td> + <td> + <div + className="asdcs-designer-label" + onClick={this.onClickAdd}> + Add Message + </div> + </td> + <td> + <div + className="asdcs-designer-icon" + onClick={this.onClickAdd}> + <Icon glyph={iconPlus} /> + </div> + </td> + <td> </td> + </tr> + </tbody> + </table> + </div> + ); + } } /** Element properties. */ MessageNew.propTypes = { - designer: PropTypes.object.isRequired, - messages: PropTypes.object.isRequired, - connectDragSource: PropTypes.func.isRequired, + designer: PropTypes.object.isRequired, + messages: PropTypes.object.isRequired, + connectDragSource: PropTypes.func.isRequired }; /** DND. */ const source = { - beginDrag(props) { - return { id: props.id }; - }, + beginDrag(props) { + return { id: props.id }; + } }; /** DND. */ const collect = function collection(connect, monitor) { - return { - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging(), - }; + return { + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging() + }; }; +/* eslint-disable new-cap */ export default DragSource('message-new', source, collect)(MessageNew); - +/* eslint-enable new-cap */ diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/Messages.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/Messages.jsx index 417155498e..6d2b35e41d 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/Messages.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/message/Messages.jsx @@ -28,116 +28,113 @@ import MessageNew from './MessageNew'; * @constructor */ export default class Messages extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + this.state = {}; + this.setHoverIndex = this.setHoverIndex.bind(this); + this.getHoverIndex = this.getHoverIndex.bind(this); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); - this.state = { - }; - this.setHoverIndex = this.setHoverIndex.bind(this); - this.getHoverIndex = this.getHoverIndex.bind(this); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Record last hover index as non-state. - * @param index index. - */ - setHoverIndex(index) { - this.hoverIndex = index; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get last recorded hover index. - * @returns {*} - */ - getHoverIndex() { - return this.hoverIndex; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Handle drop. - * @param dragIndex dragged item index; undefined if new. - * @param hoverIndex drop index. - */ - onDrop(dragIndex, hoverIndex) { - if (hoverIndex >= 0) { - const application = this.props.application; - const model = application.getModel(); - if (Common.isNumber(dragIndex)) { - if (dragIndex !== hoverIndex) { - model.reorderMessages(dragIndex, hoverIndex); - } - } else { - model.addMessage(hoverIndex); - } - this.forceUpdate(); - application.renderDiagram(); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Record last hover index as non-state. + * @param index index. + */ + setHoverIndex(index) { + this.hoverIndex = index; } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render view. - * @returns {*} - */ - render() { - - const model = this.props.application.getModel(); - const diagram = model.unwrap().diagram; - - // Render existing messages. - - const messages = []; - for (const step of diagram.steps) { - const message = step.message; - const from = model.getLifelineById(message.from); - const to = model.getLifelineById(message.to); - messages.push(<Message - key={`m${message.id}`} - application={this.props.application} - designer={this.props.designer} - message={message} - active={this.props.activeMessageId === message.id} - from={from} - to={to} - model={model} - index={messages.length} - messages={this} - />); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get last recorded hover index. + * @returns {*} + */ + getHoverIndex() { + return this.hoverIndex; } - // Render add. + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handle drop. + * @param dragIndex dragged item index; undefined if new. + * @param hoverIndex drop index. + */ + onDrop(dragIndex, hoverIndex) { + if (hoverIndex >= 0) { + const application = this.props.application; + const model = application.getModel(); + if (Common.isNumber(dragIndex)) { + if (dragIndex !== hoverIndex) { + model.reorderMessages(dragIndex, hoverIndex); + } + } else { + model.addMessage(hoverIndex); + } + this.forceUpdate(); + application.renderDiagram(); + } + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render view. + * @returns {*} + */ + render() { + const model = this.props.application.getModel(); + const diagram = model.unwrap().diagram; + + // Render existing messages. + + const messages = []; + for (const step of diagram.steps) { + const message = step.message; + const from = model.getLifelineById(message.from); + const to = model.getLifelineById(message.to); + messages.push( + <Message + key={`m${message.id}`} + application={this.props.application} + designer={this.props.designer} + message={message} + active={this.props.activeMessageId === message.id} + from={from} + to={to} + model={model} + index={messages.length} + messages={this} + /> + ); + } - messages.push(<MessageNew - key="_m" - designer={this.props.designer} - messages={this} - />); + // Render add. - return ( - <div className="asdcs-designer-steps"> - {messages} - </div> - ); - } + messages.push( + <MessageNew + key="_m" + designer={this.props.designer} + messages={this} + /> + ); + + return <div className="asdcs-designer-steps">{messages}</div>; + } } /** Element properties. */ Messages.propTypes = { - application: PropTypes.object.isRequired, - designer: PropTypes.object.isRequired, - activeMessageId: PropTypes.string, + application: PropTypes.object.isRequired, + designer: PropTypes.object.isRequired, + activeMessageId: PropTypes.string }; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/metadata/Metadata.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/metadata/Metadata.jsx index cc1faddcba..419c728671 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/metadata/Metadata.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/designer/components/metadata/Metadata.jsx @@ -20,15 +20,11 @@ import PropTypes from 'prop-types'; * Metadata view. */ const Metadata = function Metadata(props) { - return ( - <div className="asdcs-designer-metadata"> - {props.metadata.name} - </div> - ); + return <div className="asdcs-designer-metadata">{props.metadata.name}</div>; }; Metadata.propTypes = { - metadata: PropTypes.object.isRequired, + metadata: PropTypes.object.isRequired }; export default Metadata; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/source/Source.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/source/Source.jsx index 04ea5280ba..1ba7e9f324 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/source/Source.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/source/Source.jsx @@ -20,67 +20,69 @@ import PropTypes from 'prop-types'; * Editor view, aggregating the designer, the code editor, the toolbar. */ export default class Source extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct view. - */ - constructor(props, context) { - super(props, context); - this.demo = this.props.application.getOptions().demo; - } + /** + * Construct view. + */ + constructor(props, context) { + super(props, context); + this.demo = this.props.application.getOptions().demo; + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Set JSON mode. - * @param json JSON (stringified) code. - */ - setJSON(json = '') { - if (this.textarea) { - this.textarea.value = json; + /** + * Set JSON mode. + * @param json JSON (stringified) code. + */ + setJSON(json = '') { + if (this.textarea) { + this.textarea.value = json; + } } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Set YAML mode. - * @param yaml YAML code. - */ - setYAML(yaml = '') { - if (this.textarea) { - this.textarea.value = yaml; + /** + * Set YAML mode. + * @param yaml YAML code. + */ + setYAML(yaml = '') { + if (this.textarea) { + this.textarea.value = yaml; + } } - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - componentDidMount() { - /* + componentDidMount() { + /* this.cm = CodeMirror.fromTextArea(this.textarea, { lineNumbers: true, readOnly: true, }); */ - } + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Render to DOM. - */ - render() { - return ( - <div className="asdcs-editor-code"> - <textarea ref={(r) => { this.textarea = r; }}></textarea> - </div> - ); - } + /** + * Render to DOM. + */ + render() { + return ( + <div className="asdcs-editor-code"> + <textarea + ref={r => { + this.textarea = r; + }} + /> + </div> + ); + } } Source.propTypes = { - application: PropTypes.object.isRequired, + application: PropTypes.object.isRequired }; - diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/toolbar/Toolbar.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/toolbar/Toolbar.jsx index 4ac9c3dfad..c32172571d 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/toolbar/Toolbar.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/editor/components/toolbar/Toolbar.jsx @@ -26,100 +26,120 @@ import iconOpen from '../../../../../../../../res/ecomp/asdc/sequencer/sprites/i * all you get are the buttons for toggling between JSON/YAML/Designer. */ export default class Toolbar extends React.Component { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct view. + */ + constructor(props, context) { + super(props, context); + this.application = Common.assertType(this.props.application, 'Object'); + this.editor = Common.assertType(this.props.editor, 'Object'); + this.mode = 'design'; + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct view. - */ - constructor(props, context) { - super(props, context); - this.application = Common.assertType(this.props.application, 'Object'); - this.editor = Common.assertType(this.props.editor, 'Object'); - this.mode = 'design'; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Set editor mode, one of {design, json, yaml}. - * @param mode - */ - setMode(mode = 'design') { - this.mode = mode; - } + /** + * Set editor mode, one of {design, json, yaml}. + * @param mode + */ + setMode(mode = 'design') { + this.mode = mode; + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Render into the DOM. - */ - render() { - - const demo = this.application.getOptions().demo; - const demoCss = demo ? '' : 'asdc-hide'; - - return ( - <div className={`asdcs-editor-toolbar ${demoCss}`}> - <div className="asdcs-editor-toolbar-demo"> - <button className="asdcs-button-new" data-title="New sequence"> - <svg> - <use xlinkHref={iconPlus} className="asdcs-icon" /> - </svg> - </button> - <button className="asdcs-button-open" data-title="Open sequence"> - <svg> - <use xlinkHref={iconOpen} className="asdcs-icon" /> - </svg> - </button> - <button className="asdcs-button-save" data-title="Save checkpoint"> - <svg> - <use xlinkHref="#icon--save" className="asdcs-icon" /> - </svg> - </button> - <button className="asdcs-button-validate" data-title="Validate"> - <svg> - <use xlinkHref="#icon--validate" className="asdcs-icon" /> - </svg> - </button> - <button className="asdcs-button-download" data-title="Download"> - <svg> - <use xlinkHref="#icon--download" className="asdcs-icon" /> - </svg> - </button> - <button className="asdcs-button-upload" data-title="Upload"> - <svg> - <use xlinkHref="#icon--upload" className="asdcs-icon" /> - </svg> - </button> - </div> - <div className="asdcs-editor-toolbar-toggle"> - <button className="asdcs-button-design asdcs-button-mode asdcs-button-toggle-left"> - Design - </button> - <button className="asdcs-button-json asdcs-button-mode asdcs-button-toggle-center"> - JSON - </button> - <button className="asdcs-button-yaml asdcs-button-mode asdcs-button-toggle-right"> - YAML - </button> - </div> - </div> - ); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Render into the DOM. + */ + render() { + const demo = this.application.getOptions().demo; + const demoCss = demo ? '' : 'asdc-hide'; + + return ( + <div className={`asdcs-editor-toolbar ${demoCss}`}> + <div className="asdcs-editor-toolbar-demo"> + <button + className="asdcs-button-new" + data-title="New sequence"> + <svg> + <use xlinkHref={iconPlus} className="asdcs-icon" /> + </svg> + </button> + <button + className="asdcs-button-open" + data-title="Open sequence"> + <svg> + <use xlinkHref={iconOpen} className="asdcs-icon" /> + </svg> + </button> + <button + className="asdcs-button-save" + data-title="Save checkpoint"> + <svg> + <use + xlinkHref="#icon--save" + className="asdcs-icon" + /> + </svg> + </button> + <button + className="asdcs-button-validate" + data-title="Validate"> + <svg> + <use + xlinkHref="#icon--validate" + className="asdcs-icon" + /> + </svg> + </button> + <button + className="asdcs-button-download" + data-title="Download"> + <svg> + <use + xlinkHref="#icon--download" + className="asdcs-icon" + /> + </svg> + </button> + <button className="asdcs-button-upload" data-title="Upload"> + <svg> + <use + xlinkHref="#icon--upload" + className="asdcs-icon" + /> + </svg> + </button> + </div> + <div className="asdcs-editor-toolbar-toggle"> + <button className="asdcs-button-design asdcs-button-mode asdcs-button-toggle-left"> + Design + </button> + <button className="asdcs-button-json asdcs-button-mode asdcs-button-toggle-center"> + JSON + </button> + <button className="asdcs-button-yaml asdcs-button-mode asdcs-button-toggle-right"> + YAML + </button> + </div> + </div> + ); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** * Initialize eventhandlers. * @private * @@ -172,9 +192,9 @@ export default class Toolbar extends React.Component { } */ - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** + /** * Demo action. * _doDemoOpen() { @@ -189,9 +209,9 @@ export default class Toolbar extends React.Component { } */ - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** + /** * Demo action. * _doDemoNew() { @@ -204,9 +224,9 @@ export default class Toolbar extends React.Component { } */ - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** + /** * Demo action. * _doDemoSave() { @@ -217,9 +237,9 @@ export default class Toolbar extends React.Component { } */ - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** + /** * Demo action. * _doDemoUpload() { @@ -241,9 +261,9 @@ export default class Toolbar extends React.Component { } */ - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** + /** * Demo action. * _doDemoDownload() { @@ -256,9 +276,9 @@ export default class Toolbar extends React.Component { } */ - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** + /** * Demo action. * _doDemoValidate() { @@ -270,6 +290,6 @@ export default class Toolbar extends React.Component { } Toolbar.propTypes = { - application: PropTypes.object.isRequired, - editor: PropTypes.object.isRequired, + application: PropTypes.object.isRequired, + editor: PropTypes.object.isRequired }; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/export/Export.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/export/Export.jsx index 529ae92ded..a6cb4abb49 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/export/Export.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/export/Export.jsx @@ -17,15 +17,18 @@ import React from 'react'; const Export = function Export() { - return ( - <form className="asdcs-export" action="/ossui-svg/services/ossui/svg/export" method="post"> - <input name="svg" type="hidden" value="" /> - <input name="css" type="hidden" value="sdc/sequencer/default" /> - <input name="type" type="hidden" value="PDF" /> - <input name="height" type="hidden" value="1920" /> - <input name="width" type="hidden" value="1080" /> - </form> - ); + return ( + <form + className="asdcs-export" + action="/ossui-svg/services/ossui/svg/export" + method="post"> + <input name="svg" type="hidden" value="" /> + <input name="css" type="hidden" value="sdc/sequencer/default" /> + <input name="type" type="hidden" value="PDF" /> + <input name="height" type="hidden" value="1920" /> + <input name="width" type="hidden" value="1080" /> + </form> + ); }; export default Export; diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/icons/Icon.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/icons/Icon.jsx index 6ed2d451d9..51849676e4 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/icons/Icon.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/icons/Icon.jsx @@ -24,18 +24,17 @@ import PropTypes from 'prop-types'; * @constructor */ const Icon = function Icon({ glyph, className }) { - return ( - <svg viewBox="0 0 1000 1000" className={className} > - <use xlinkHref={glyph} className="asdcs-icon" /> - </svg> - ); + return ( + <svg viewBox="0 0 1000 1000" className={className}> + <use xlinkHref={glyph} className="asdcs-icon" /> + </svg> + ); }; /** Declare properties. */ Icon.propTypes = { - className: PropTypes.string, - glyph: PropTypes.string.isRequired, + className: PropTypes.string, + glyph: PropTypes.string.isRequired }; export default Icon; - diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/overlay/Overlay.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/overlay/Overlay.jsx index 817f4f1697..7d03b468aa 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/overlay/Overlay.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/components/overlay/Overlay.jsx @@ -20,42 +20,35 @@ import React from 'react'; * Overlay view. */ export default class Overlay extends React.Component { + /** + * Construct view. + * @param props element properties. + * @param context react context. + */ + constructor(props, context) { + super(props, context); + this.state = { + visible: false + }; + this.setVisible = this.setVisible.bind(this); + } - /** - * Construct view. - * @param props element properties. - * @param context react context. - */ - constructor(props, context) { - super(props, context); - this.state = { - visible: false, - }; - this.setVisible = this.setVisible.bind(this); - } + /** + * Set visibility. + * @param visible true if visible. + */ + setVisible(visible) { + this.setState({ + visible + }); + } - /** - * Set visibility. - * @param visible true if visible. - */ - setVisible(visible) { - this.setState({ - visible, - }); - } - - /** - * Render view. - * @returns {XML} - */ - render() { - const display = this.state.visible ? 'block' : 'none'; - return ( - <div - className="asdcs-overlay" - style={{ display }} - > - </div> - ); - } + /** + * Render view. + * @returns {XML} + */ + render() { + const display = this.state.visible ? 'block' : 'none'; + return <div className="asdcs-overlay" style={{ display }} />; + } } diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodel.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodel.js index 82e8ada588..140b7ef547 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodel.js +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodel.js @@ -22,73 +22,71 @@ import Common from '../common/Common'; * Rules governing what a definition can contain. */ export default class Metamodel { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct from JSON definition. + * @param json schema definition. + */ + constructor(json) { + Common.assertType(json, 'Object'); + const dfault = require('./templates/default.metamodel.json'); + this.json = _merge({}, dfault, json); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct from JSON definition. - * @param json schema definition. - */ - constructor(json) { - Common.assertType(json, 'Object'); - const dfault = require('./templates/default.metamodel.json'); - this.json = _merge({}, dfault, json); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get schema identifier. - * @returns ID. - */ - getId() { - return this.json.diagram.metadata.id; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Get lifeline constraints. - * @returns {*} - */ - getConstraints() { - return this.json.diagram.lifelines.constraints; - } + /** + * Get schema identifier. + * @returns ID. + */ + getId() { + return this.json.diagram.metadata.id; + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Get lifeline metadata by lifeline ID. - * @param id sought lifeline. - * @returns lifeline if found. - */ - getLifelineById(id) { - for (const lifeline of this.json.diagram.lifelines.lifelines) { - if (lifeline.id === id) { - return lifeline; - } + /** + * Get lifeline constraints. + * @returns {*} + */ + getConstraints() { + return this.json.diagram.lifelines.constraints; } - return undefined; - } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get lifeline metadata by lifeline ID. + * @param id sought lifeline. + * @returns lifeline if found. + */ + getLifelineById(id) { + for (const lifeline of this.json.diagram.lifelines.lifelines) { + if (lifeline.id === id) { + return lifeline; + } + } + return undefined; + } - /** - * Get original JSON. - * @returns JSON. - */ - unwrap() { - return this.json; - } + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Get original JSON. + * @returns JSON. + */ + unwrap() { + return this.json; + } - /** - * Get default schema. - * @returns Metamodel default (permissive) Metamodel. - */ - static getDefault() { - return new Metamodel({}); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Get default schema. + * @returns Metamodel default (permissive) Metamodel. + */ + static getDefault() { + return new Metamodel({}); + } } diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodels.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodels.js index 4ecfc0b5f7..40756a8e09 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodels.js +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Metamodels.js @@ -21,67 +21,64 @@ import Metamodel from './Metamodel'; * A simple lookup for schemas by ID. */ export default class Metamodels { + // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Construct metamodels from provided JSON definitions. + * @param metamodels JSON metamodel definitions. + */ + constructor(metamodels) { + Common.assertType(metamodels, 'Array'); - /** - * Construct metamodels from provided JSON definitions. - * @param metamodels JSON metamodel definitions. - */ - constructor(metamodels) { + this.lookup = {}; - Common.assertType(metamodels, 'Array'); + // Save each metamodel. It's up to the Metamodel class to make sense of + // potentially nonsense metamodel definitions. - this.lookup = {}; + for (const json of metamodels) { + const metamodel = new Metamodel(json); + this.lookup[metamodel.getId()] = metamodel; + } - // Save each metamodel. It's up to the Metamodel class to make sense of - // potentially nonsense metamodel definitions. + // Set (or override) the default metamodel with the inlined one. - for (const json of metamodels) { - const metamodel = new Metamodel(json); - this.lookup[metamodel.getId()] = metamodel; + this.lookup.$ = Metamodel.getDefault(); + Common.assertInstanceOf(this.lookup.$, Metamodel); } - // Set (or override) the default metamodel with the inlined one. + // /////////////////////////////////////////////////////////////////////////////////////////////// - this.lookup.$ = Metamodel.getDefault(); - Common.assertInstanceOf(this.lookup.$, Metamodel); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get Metamodel by its @id. - * @param id identifier. - * @returns Metamodel, or undefined if no matching metamodel found. - */ - getMetamodel(id) { - return this.lookup[id]; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get the default (permissive) metamodel. - * @returns default Metamodel. - */ - getDefault() { - return this.lookup.$; - } + /** + * Get Metamodel by its @id. + * @param id identifier. + * @returns Metamodel, or undefined if no matching metamodel found. + */ + getMetamodel(id) { + return this.lookup[id]; + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// - /** - * Get metamodel by its @id, falling back to the default. - * @param id identifier. - * @returns matching metamodel, or default. - */ - getMetamodelOrDefault(id) { - const metamodel = this.getMetamodel(id); - if (metamodel) { - return metamodel; + /** + * Get the default (permissive) metamodel. + * @returns default Metamodel. + */ + getDefault() { + return this.lookup.$; } - return this.getDefault(); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get metamodel by its @id, falling back to the default. + * @param id identifier. + * @returns matching metamodel, or default. + */ + getMetamodelOrDefault(id) { + const metamodel = this.getMetamodel(id); + if (metamodel) { + return metamodel; + } + return this.getDefault(); + } } diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Model.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Model.js index 1e68cd6034..3145506268 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Model.js +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/Model.js @@ -24,489 +24,481 @@ import Metamodel from './Metamodel'; * A wrapper for a model instance. */ export default class Model { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct model from model JSON. JSON is assumed to be in more or less + * the correct structure, but it's OK if it's missing IDs. + * + * @param json initial JSON; will be updated in situ. + * @param metamodel Metaobject definition. + */ + constructor(json, metamodel) { + if (metamodel) { + Common.assertInstanceOf(metamodel, Metamodel); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + this.metamodel = metamodel || Metamodel.getDefault(); + Common.assertInstanceOf(this.metamodel, Metamodel); - /** - * Construct model from model JSON. JSON is assumed to be in more or less - * the correct structure, but it's OK if it's missing IDs. - * - * @param json initial JSON; will be updated in situ. - * @param metamodel Metaobject definition. - */ - constructor(json, metamodel) { + this.jsonschema = require('./schema/asdc_sequencer_schema.json'); + this.templates = { + defaultModel: require('./templates/default.model.json'), + defaultMetamodel: require('./templates/default.metamodel.json') + }; - if (metamodel) { - Common.assertInstanceOf(metamodel, Metamodel); - } + this.model = this._preprocess(Common.assertType(json, 'Object')); + Common.assertPlainObject(this.model); - this.metamodel = metamodel || Metamodel.getDefault(); - Common.assertInstanceOf(this.metamodel, Metamodel); - - this.jsonschema = require('./schema/asdc_sequencer_schema.json'); - this.templates = { - defaultModel: require('./templates/default.model.json'), - defaultMetamodel: require('./templates/default.metamodel.json'), - }; - - this.model = this._preprocess(Common.assertType(json, 'Object')); - Common.assertPlainObject(this.model); - - this.renumber(); - - this.addLifeline = this.addLifeline.bind(this); - this.addMessage = this.addMessage.bind(this); - this.renumber = this.renumber.bind(this); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Unwrap to get model object. - * @returns {*} - */ - unwrap() { - return Common.assertPlainObject(this.model); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get the metamodel which defines valid states for this model. - * @returns Metamodel definition. - */ - getMetamodel() { - return Common.assertInstanceOf(this.metamodel, Metamodel); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Find lifeline by its ID. - * @param id lifeline ID. - * @returns lifeline object, if found. - */ - getLifelineById(id) { - for (const lifeline of this.model.diagram.lifelines) { - if (lifeline.id === id) { - return lifeline; - } - } - return undefined; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get message by ID. - * @param id message ID. - * @returns message if matched. - */ - getMessageById(id) { - Common.assertNotNull(id); - const step = this.getStepByMessageId(id); - if (step) { - return step.message; + this.renumber(); + + this.addLifeline = this.addLifeline.bind(this); + this.addMessage = this.addMessage.bind(this); + this.renumber = this.renumber.bind(this); } - return undefined; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get step by message ID. - * @param id step ID. - * @returns step if matched. - */ - getStepByMessageId(id) { - Common.assertNotNull(id); - for (const step of this.model.diagram.steps) { - if (step.message && step.message.id === id) { - return step; - } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Unwrap to get model object. + * @returns {*} + */ + unwrap() { + return Common.assertPlainObject(this.model); } - return undefined; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Add message to steps. - * @returns {{}} - */ - addMessage(index) { - const d = this.model.diagram; - const step = {}; - step.message = {}; - step.message.id = Model._guid(); - step.message.name = '[Unnamed Message]'; - step.message.type = 'request'; - step.message.from = d.lifelines.length > 0 ? d.lifelines[0].id : -1; - step.message.to = d.lifelines.length > 1 ? d.lifelines[1].id : -1; - if (index >= 0) { - d.steps.splice(index, 0, step); - } else { - d.steps.push(step); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get the metamodel which defines valid states for this model. + * @returns Metamodel definition. + */ + getMetamodel() { + return Common.assertInstanceOf(this.metamodel, Metamodel); } - this.renumber(); - return step; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Delete message with ID. - * @param id to be deleted. - */ - deleteMessageById(id) { - Common.assertNotNull(id); - const step = this.getStepByMessageId(id); - if (step) { - const index = this.model.diagram.steps.indexOf(step); - if (index !== -1) { - this.model.diagram.steps.splice(index, 1); - } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Find lifeline by its ID. + * @param id lifeline ID. + * @returns lifeline object, if found. + */ + getLifelineById(id) { + for (const lifeline of this.model.diagram.lifelines) { + if (lifeline.id === id) { + return lifeline; + } + } + return undefined; } - this.renumber(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Add lifeline to lifelines. - * @param index optional index. - * @returns {{}} - */ - addLifeline(index) { - const lifeline = {}; - lifeline.id = Model._guid(); - lifeline.name = '[Unnamed Lifeline]'; - if (index >= 0) { - this.model.diagram.lifelines.splice(index, 0, lifeline); - } else { - this.model.diagram.lifelines.push(lifeline); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get message by ID. + * @param id message ID. + * @returns message if matched. + */ + getMessageById(id) { + Common.assertNotNull(id); + const step = this.getStepByMessageId(id); + if (step) { + return step.message; + } + return undefined; } - this.renumber(); - return lifeline; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Delete lifeline with ID. - * @param id to be deleted. - */ - deleteLifelineById(id) { - Common.assertNotNull(id); - this.deleteStepsByLifelineId(id); - const lifeline = this.getLifelineById(id); - if (lifeline) { - const index = this.model.diagram.lifelines.indexOf(lifeline); - if (index !== -1) { - this.model.diagram.lifelines.splice(index, 1); - } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get step by message ID. + * @param id step ID. + * @returns step if matched. + */ + getStepByMessageId(id) { + Common.assertNotNull(id); + for (const step of this.model.diagram.steps) { + if (step.message && step.message.id === id) { + return step; + } + } + return undefined; } - this.renumber(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Delete all steps corresponding to lifeline. - * @param id lifeline ID. - */ - deleteStepsByLifelineId(id) { - Common.assertNotNull(id); - const steps = this.getStepsByLifelineId(id); - for (const step of steps) { - this.deleteMessageById(step.message.id); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Add message to steps. + * @returns {{}} + */ + addMessage(index) { + const d = this.model.diagram; + const step = {}; + step.message = {}; + step.message.id = Model._guid(); + step.message.name = '[Unnamed Message]'; + step.message.type = 'request'; + step.message.from = d.lifelines.length > 0 ? d.lifelines[0].id : -1; + step.message.to = d.lifelines.length > 1 ? d.lifelines[1].id : -1; + if (index >= 0) { + d.steps.splice(index, 0, step); + } else { + d.steps.push(step); + } + this.renumber(); + return step; } - this.renumber(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get all steps corresponding to lifeline. - * @param id lifeline ID. - * @return steps from/to lifeline. - */ - getStepsByLifelineId(id) { - Common.assertNotNull(id); - const steps = []; - for (const step of this.model.diagram.steps) { - if (step.message) { - if (step.message.from === id || step.message.to === id) { - steps.push(step); + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Delete message with ID. + * @param id to be deleted. + */ + deleteMessageById(id) { + Common.assertNotNull(id); + const step = this.getStepByMessageId(id); + if (step) { + const index = this.model.diagram.steps.indexOf(step); + if (index !== -1) { + this.model.diagram.steps.splice(index, 1); + } } - } + this.renumber(); } - return steps; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Validate model. Disabled, because we removed the jsonschema dependency. - * @returns {Array} of validation errors, if any. - */ - validate() { - const errors = []; - return errors; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Reorder messages. - * @param index message index. - * @param afterIndex new (after) index. - */ - reorderMessages(index, afterIndex) { - Common.assertType(index, 'Number'); - Common.assertType(afterIndex, 'Number'); - const steps = this.model.diagram.steps; - const element = steps[index]; - steps.splice(index, 1); - steps.splice(afterIndex, 0, element); - this.renumber(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Reorder lifelines. - * @param index lifeline index. - * @param afterIndex new (after) index. - */ - reorderLifelines(index, afterIndex) { - Common.assertType(index, 'Number'); - Common.assertType(afterIndex, 'Number'); - const lifelines = this.model.diagram.lifelines; - const element = lifelines[index]; - lifelines.splice(index, 1); - lifelines.splice(afterIndex, 0, element); - this.renumber(); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Renumber lifelines and messages. - */ - renumber() { - const modelJSON = this.unwrap(); - let stepIndex = 1; - let lifelineIndex = 1; - for (const step of modelJSON.diagram.steps) { - if (step.message) { - step.message.index = stepIndex++; - } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Add lifeline to lifelines. + * @param index optional index. + * @returns {{}} + */ + addLifeline(index) { + const lifeline = {}; + lifeline.id = Model._guid(); + lifeline.name = '[Unnamed Lifeline]'; + if (index >= 0) { + this.model.diagram.lifelines.splice(index, 0, lifeline); + } else { + this.model.diagram.lifelines.push(lifeline); + } + this.renumber(); + return lifeline; } - for (const lifeline of modelJSON.diagram.lifelines) { - lifeline.index = lifelineIndex++; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Delete lifeline with ID. + * @param id to be deleted. + */ + deleteLifelineById(id) { + Common.assertNotNull(id); + this.deleteStepsByLifelineId(id); + const lifeline = this.getLifelineById(id); + if (lifeline) { + const index = this.model.diagram.lifelines.indexOf(lifeline); + if (index !== -1) { + this.model.diagram.lifelines.splice(index, 1); + } + } + this.renumber(); } - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Build a simple, navigable dataset describing fragments. - * @returns {{}}, indexed by (stop) message ID, describing fragments. - */ - analyzeFragments() { - - const fData = {}; - - let depth = 0; - const modelJSON = this.unwrap(); - const open = []; - - const getData = function g(stop, fragment) { - let data = fData[stop]; - if (!data) { - data = { stop, start: [], fragment }; - fData[stop] = data; - } - return data; - }; - - const fragmentsByStart = {}; - for (const step of modelJSON.diagram.steps) { - if (step.message && step.message.fragment) { - const message = step.message; - const fragment = message.fragment; - if (fragment.start) { - fragmentsByStart[fragment.start] = fragment; - open.push(message.id); - depth++; + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Delete all steps corresponding to lifeline. + * @param id lifeline ID. + */ + deleteStepsByLifelineId(id) { + Common.assertNotNull(id); + const steps = this.getStepsByLifelineId(id); + for (const step of steps) { + this.deleteMessageById(step.message.id); } - if (fragment.stop) { - if (open.length > 0) { - getData(message.id).start.push(open.pop()); - } - depth = Math.max(depth - 1, 0); + this.renumber(); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get all steps corresponding to lifeline. + * @param id lifeline ID. + * @return steps from/to lifeline. + */ + getStepsByLifelineId(id) { + Common.assertNotNull(id); + const steps = []; + for (const step of this.model.diagram.steps) { + if (step.message) { + if (step.message.from === id || step.message.to === id) { + steps.push(step); + } + } } - } + return steps; } - if (open.length > 0) { - for (const o of open) { - getData(o, fragmentsByStart[o]).start.push(o); - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Validate model. Disabled, because we removed the jsonschema dependency. + * @returns {Array} of validation errors, if any. + */ + validate() { + const errors = []; + return errors; } - return fData; - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Reorder messages. + * @param index message index. + * @param afterIndex new (after) index. + */ + reorderMessages(index, afterIndex) { + Common.assertType(index, 'Number'); + Common.assertType(afterIndex, 'Number'); + const steps = this.model.diagram.steps; + const element = steps[index]; + steps.splice(index, 1); + steps.splice(afterIndex, 0, element); + this.renumber(); + } - // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Reorder lifelines. + * @param index lifeline index. + * @param afterIndex new (after) index. + */ + reorderLifelines(index, afterIndex) { + Common.assertType(index, 'Number'); + Common.assertType(afterIndex, 'Number'); + const lifelines = this.model.diagram.lifelines; + const element = lifelines[index]; + lifelines.splice(index, 1); + lifelines.splice(afterIndex, 0, element); + this.renumber(); + } - /** - * Build a simple, navigable dataset describing occurrences. - * @returns a map, indexed by lifeline ID, of objects containing {start:[],stop:[],active[]}. - * @private - */ - analyzeOccurrences() { + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Renumber lifelines and messages. + */ + renumber() { + const modelJSON = this.unwrap(); + let stepIndex = 1; + let lifelineIndex = 1; + for (const step of modelJSON.diagram.steps) { + if (step.message) { + step.message.index = stepIndex++; + } + } + for (const lifeline of modelJSON.diagram.lifelines) { + lifeline.index = lifelineIndex++; + } + } - const oData = {}; + // /////////////////////////////////////////////////////////////////////////////////////////////// - // A few inline functions. They make this method kinda lengthy, but they - // reduce clutter in the class and keep it coherent, so it's OK. + /** + * Build a simple, navigable dataset describing fragments. + * @returns {{}}, indexed by (stop) message ID, describing fragments. + */ + analyzeFragments() { + const fData = {}; - const getDataByLifelineId = function get(lifelineId) { - if (!oData[lifelineId]) { - oData[lifelineId] = { active: [], start: {}, stop: {} }; - } - return oData[lifelineId]; - }; + let depth = 0; + const modelJSON = this.unwrap(); + const open = []; - const contains = function contains(array, value) { - return (array && (array.indexOf(value) !== -1)); - }; + const getData = function g(stop, fragment) { + let data = fData[stop]; + if (!data) { + data = { stop, start: [], fragment }; + fData[stop] = data; + } + return data; + }; + + const fragmentsByStart = {}; + for (const step of modelJSON.diagram.steps) { + if (step.message && step.message.fragment) { + const message = step.message; + const fragment = message.fragment; + if (fragment.start) { + fragmentsByStart[fragment.start] = fragment; + open.push(message.id); + depth++; + } + if (fragment.stop) { + if (open.length > 0) { + getData(message.id).start.push(open.pop()); + } + depth = Math.max(depth - 1, 0); + } + } + } - const process = function process(message, lifelineId) { - const oRule = message.occurrences; - if (oRule) { + if (open.length > 0) { + for (const o of open) { + getData(o, fragmentsByStart[o]).start.push(o); + } + } - const oDataLifeline = getDataByLifelineId(lifelineId); - if (oDataLifeline) { + return fData; + } - // Record all starts. + // /////////////////////////////////////////////////////////////////////////////////////////////// - if (contains(oRule.start, lifelineId)) { - oDataLifeline.active.push(message.id); - oDataLifeline.start[message.id] = undefined; - } + /** + * Build a simple, navigable dataset describing occurrences. + * @returns a map, indexed by lifeline ID, of objects containing {start:[],stop:[],active[]}. + * @private + */ + analyzeOccurrences() { + const oData = {}; - // Reconcile with stops. + // A few inline functions. They make this method kinda lengthy, but they + // reduce clutter in the class and keep it coherent, so it's OK. - if (contains(oRule.stop, lifelineId)) { - const startMessageId = oDataLifeline.active.pop(); - oDataLifeline.stop[message.id] = startMessageId; - if (startMessageId) { - oDataLifeline.start[startMessageId] = message.id; + const getDataByLifelineId = function get(lifelineId) { + if (!oData[lifelineId]) { + oData[lifelineId] = { active: [], start: {}, stop: {} }; + } + return oData[lifelineId]; + }; + + const contains = function contains(array, value) { + return array && array.indexOf(value) !== -1; + }; + + const process = function process(message, lifelineId) { + const oRule = message.occurrences; + if (oRule) { + const oDataLifeline = getDataByLifelineId(lifelineId); + if (oDataLifeline) { + // Record all starts. + + if (contains(oRule.start, lifelineId)) { + oDataLifeline.active.push(message.id); + oDataLifeline.start[message.id] = undefined; + } + + // Reconcile with stops. + + if (contains(oRule.stop, lifelineId)) { + const startMessageId = oDataLifeline.active.pop(); + oDataLifeline.stop[message.id] = startMessageId; + if (startMessageId) { + oDataLifeline.start[startMessageId] = message.id; + } + } + } + } + }; + + // Analyze start and end. + + const modelJSON = this.unwrap(); + for (const step of modelJSON.diagram.steps) { + if (step.message) { + const message = step.message; + if (message.occurrences) { + process(message, message.from); + process(message, message.to); + } } - } } - } - }; - - // Analyze start and end. - - const modelJSON = this.unwrap(); - for (const step of modelJSON.diagram.steps) { - if (step.message) { - const message = step.message; - if (message.occurrences) { - process(message, message.from); - process(message, message.to); + + // Reset active. (We used it, but it's not actually for us; it's for keeping + // track of active occurrences when rendering the diagram.) + + for (const lifelineId of Object.keys(oData)) { + oData[lifelineId].active = []; } - } - } - // Reset active. (We used it, but it's not actually for us; it's for keeping - // track of active occurrences when rendering the diagram.) + // Reconcile the start and end (message ID) maps for each lifeline, + // finding a "stop" for every start. Default to starting and stopping + // on the same message, which is the same as no occurrence. + + for (const lifelineId of Object.keys(oData)) { + const lifelineData = oData[lifelineId]; + for (const startId of Object.keys(lifelineData.start)) { + const stopId = lifelineData.start[startId]; + if (!stopId) { + lifelineData.start[startId] = startId; + lifelineData.stop[startId] = startId; + } + } + } - for (const lifelineId of Object.keys(oData)) { - oData[lifelineId].active = []; + return oData; } - // Reconcile the start and end (message ID) maps for each lifeline, - // finding a "stop" for every start. Default to starting and stopping - // on the same message, which is the same as no occurrence. - - for (const lifelineId of Object.keys(oData)) { - const lifelineData = oData[lifelineId]; - for (const startId of Object.keys(lifelineData.start)) { - const stopId = lifelineData.start[startId]; - if (!stopId) { - lifelineData.start[startId] = startId; - lifelineData.stop[startId] = startId; + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Preprocess model, adding IDs and whatnot. + * @param original to be preprocessed. + * @returns preprocessed JSON. + * @private + */ + _preprocess(original) { + const json = _merge({}, this.templates.defaultModel, original); + const metamodel = this.metamodel.unwrap(); + if (!json.diagram.metadata.ref) { + if (metamodel.diagram.metadata.id) { + json.diagram.metadata.ref = metamodel.diagram.metadata.id; + } else { + json.diagram.metadata.ref = '$'; + } } - } - } - return oData; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Preprocess model, adding IDs and whatnot. - * @param original to be preprocessed. - * @returns preprocessed JSON. - * @private - */ - _preprocess(original) { - - const json = _merge({}, this.templates.defaultModel, original); - const metamodel = this.metamodel.unwrap(); - if (!json.diagram.metadata.ref) { - if (metamodel.diagram.metadata.id) { - json.diagram.metadata.ref = metamodel.diagram.metadata.id; - } else { - json.diagram.metadata.ref = '$'; - } - } + for (const lifeline of json.diagram.lifelines) { + lifeline.id = lifeline.id || lifeline.name; + } - for (const lifeline of json.diagram.lifelines) { - lifeline.id = lifeline.id || lifeline.name; - } + for (const step of json.diagram.steps) { + if (step.message) { + step.message.id = step.message.id || Model._guid(); + const occurrences = step.message.occurrences; + if (occurrences) { + occurrences.start = occurrences.start || []; + occurrences.stop = occurrences.stop || []; + } + } + } - for (const step of json.diagram.steps) { - if (step.message) { - step.message.id = step.message.id || Model._guid(); - const occurrences = step.message.occurrences; - if (occurrences) { - occurrences.start = occurrences.start || []; - occurrences.stop = occurrences.stop || []; + if (!json.diagram.metadata.id || json.diagram.metadata.id === '$') { + json.diagram.metadata.id = Model._guid(); } - } - } - if (!json.diagram.metadata.id || json.diagram.metadata.id === '$') { - json.diagram.metadata.id = Model._guid(); + return json; } - return json; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Create pseudo-UUID. - * @returns {string} - * @private - */ - static _guid() { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Create pseudo-UUID. + * @returns {string} + * @private + */ + static _guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return `${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}`; } - return `${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}-${s4()}`; - } - } diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/Scenarios.js b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/Scenarios.js index 4130ec7ec3..74707d472c 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/Scenarios.js +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/ecomp/asdc/sequencer/model/demo/scenarios/Scenarios.js @@ -18,93 +18,96 @@ * Example scenarios, for development, testing and demos. */ export default class Scenarios { - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Construct scenarios; read model and metamodel templates. - */ - constructor() { - this.templates = { - model: { - ecomp: require('./model/ECOMP.json'), - blank: require('./model/BLANK.json'), - dimensions: require('./model/DIMENSIONS.json'), - }, - metamodel: { - ecomp: require('./metamodel/ECOMP.json'), - blank: require('./metamodel/BLANK.json'), - }, - }; - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get ECOMP scenario. - * @return ECOMP scenario JSON. - */ - getECOMP() { - return JSON.parse(JSON.stringify(this.templates.model.ecomp)); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get ECOMP scenario metamodel. - * @return scenario metamodel JSON. - */ - getECOMPMetamodel() { - return JSON.parse(JSON.stringify(this.templates.metamodel.ecomp)); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get blank scenario. - * @return blank scenario JSON. - */ - getBlank() { - return JSON.parse(JSON.stringify(this.templates.model.blank)); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get empty scenario metamodel. - * @return empty metamodel JSON. - */ - getBlankMetamodel() { - return JSON.parse(JSON.stringify(this.templates.metamodel.blank)); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get scenario. - * @return scenario JSON. - */ - getDimensions() { - return JSON.parse(JSON.stringify(this.templates.model.dimensions)); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get scenario metamodel. - * @return metamodel JSON. - */ - getDimensionsMetamodel() { - return JSON.parse(JSON.stringify(this.templates.metamodel.blank)); - } - - // /////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Get demo metamodels. - * @returns {*[]} - */ - getMetamodels() { - return [this.getBlankMetamodel(), this.getDimensionsMetamodel(), this.getECOMPMetamodel()]; - } + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Construct scenarios; read model and metamodel templates. + */ + constructor() { + this.templates = { + model: { + ecomp: require('./model/ECOMP.json'), + blank: require('./model/BLANK.json'), + dimensions: require('./model/DIMENSIONS.json') + }, + metamodel: { + ecomp: require('./metamodel/ECOMP.json'), + blank: require('./metamodel/BLANK.json') + } + }; + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get ECOMP scenario. + * @return ECOMP scenario JSON. + */ + getECOMP() { + return JSON.parse(JSON.stringify(this.templates.model.ecomp)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get ECOMP scenario metamodel. + * @return scenario metamodel JSON. + */ + getECOMPMetamodel() { + return JSON.parse(JSON.stringify(this.templates.metamodel.ecomp)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get blank scenario. + * @return blank scenario JSON. + */ + getBlank() { + return JSON.parse(JSON.stringify(this.templates.model.blank)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get empty scenario metamodel. + * @return empty metamodel JSON. + */ + getBlankMetamodel() { + return JSON.parse(JSON.stringify(this.templates.metamodel.blank)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get scenario. + * @return scenario JSON. + */ + getDimensions() { + return JSON.parse(JSON.stringify(this.templates.model.dimensions)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get scenario metamodel. + * @return metamodel JSON. + */ + getDimensionsMetamodel() { + return JSON.parse(JSON.stringify(this.templates.metamodel.blank)); + } + + // /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get demo metamodels. + * @returns {*[]} + */ + getMetamodels() { + return [ + this.getBlankMetamodel(), + this.getDimensionsMetamodel(), + this.getECOMPMetamodel() + ]; + } } diff --git a/dox-sequence-diagram-ui/src/main/webapp/lib/main.jsx b/dox-sequence-diagram-ui/src/main/webapp/lib/main.jsx index 4b0f7d2388..1c783cb890 100644 --- a/dox-sequence-diagram-ui/src/main/webapp/lib/main.jsx +++ b/dox-sequence-diagram-ui/src/main/webapp/lib/main.jsx @@ -21,15 +21,15 @@ import '../res/ecomp/asdc/sequencer/sequencer-development.scss'; import '../res/thirdparty/react-select/react-select.min.css'; function renderApplication() { - const shell = document.createElement('div'); - shell.setAttribute('style', 'height:100%;width:100%;margin:0;padding:0'); - document.body.appendChild(shell); - const options = { demo: true }; - render(<Sequencer options={options} />, shell); + const shell = document.createElement('div'); + shell.setAttribute('style', 'height:100%;width:100%;margin:0;padding:0'); + document.body.appendChild(shell); + const options = { demo: true }; + render(<Sequencer options={options} />, shell); } if (window.addEventListener) { - window.addEventListener('DOMContentLoaded', renderApplication); + window.addEventListener('DOMContentLoaded', renderApplication); } else { - window.attachEvent('onload', renderApplication); + window.attachEvent('onload', renderApplication); } diff --git a/dox-sequence-diagram-ui/yarn.lock b/dox-sequence-diagram-ui/yarn.lock index 4f3cc76a00..043f65b66b 100644 --- a/dox-sequence-diagram-ui/yarn.lock +++ b/dox-sequence-diagram-ui/yarn.lock @@ -85,14 +85,22 @@ acorn@^4.0.3: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" -acorn@^5.0.0, acorn@^5.2.1: +acorn@^5.0.0: version "5.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822" -ajv-keywords@^1.0.0, ajv-keywords@^1.1.1: +acorn@^5.4.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.0.tgz#1abb587fbf051f94e3de20e6b26ef910b1828298" + +ajv-keywords@^1.1.1: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" +ajv-keywords@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be" + ajv@^4.7.0, ajv@^4.9.1: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" @@ -100,7 +108,7 @@ ajv@^4.7.0, ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.0.0, ajv@^5.1.0: +ajv@^5.0.0, ajv@^5.1.0, ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: @@ -109,6 +117,14 @@ ajv@^5.0.0, ajv@^5.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ajv@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.2.0.tgz#afac295bbaa0152449e522742e4547c1ae9328d2" + dependencies: + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -125,9 +141,9 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" -ansi-escapes@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" +ansi-escapes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" ansi-green@^0.1.1: version "0.1.1" @@ -151,7 +167,7 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.1.0: +ansi-styles@^3.1.0, ansi-styles@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" dependencies: @@ -338,7 +354,7 @@ aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -babel-code-frame@^6.26.0: +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: @@ -1220,7 +1236,7 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -1238,6 +1254,18 @@ chalk@^2.0.0, chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chalk@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796" + dependencies: + ansi-styles "^3.2.0" + escape-string-regexp "^1.0.5" + supports-color "^5.2.0" + +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + cheerio@^0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.19.0.tgz#772e7015f2ee29965096d71ea4175b75ab354925" @@ -1310,11 +1338,11 @@ classnames@^2.2.4: version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" -cli-cursor@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" dependencies: - restore-cursor "^1.0.1" + restore-cursor "^2.0.0" cli-width@^2.0.0: version "2.2.0" @@ -1450,7 +1478,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.6: +concat-stream@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: @@ -1576,6 +1604,14 @@ cross-spawn@^3.0.0: lru-cache "^4.0.1" which "^1.2.9" +cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -1916,16 +1952,6 @@ d3@^4.10.0: d3-voronoi "1.1.2" d3-zoom "1.7.1" -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - -damerau-levenshtein@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1936,7 +1962,7 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -2093,19 +2119,18 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" -doctrine@1.3.x: - version "1.3.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.3.0.tgz#13e75682b55518424276f7c173783456ef913d26" +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" dependencies: esutils "^2.0.2" isarray "^1.0.0" -doctrine@^1.2.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" +doctrine@^2.0.2, doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" dependencies: esutils "^2.0.2" - isarray "^1.0.0" dom-serializer@0, dom-serializer@~0.1.0: version "0.1.0" @@ -2232,58 +2257,6 @@ es-to-primitive@^1.1.1: is-date-object "^1.0.1" is-symbol "^1.0.1" -es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.38" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.38.tgz#fa7d40d65bbc9bb8a67e1d3f9cc656a00530eed3" - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.1" - -es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-set@^0.1.4, es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-symbol "3.1.1" - event-emitter "~0.3.5" - -es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-weak-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -2292,34 +2265,20 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-config-airbnb-base@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-3.0.1.tgz#b777e01f65e946933442b499fc8518aa251a6530" - -eslint-config-airbnb@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-9.0.1.tgz#6708170d5034b579d52913fe49dee2f7fec7d894" +eslint-config-prettier@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz#5ecd65174d486c22dff389fe036febf502d468a3" dependencies: - eslint-config-airbnb-base "^3.0.0" + get-stdin "^5.0.1" -eslint-import-resolver-node@^0.2.0: - version "0.2.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz#5add8106e8c928db2cba232bcd9efa846e3da16c" +eslint-import-resolver-node@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" dependencies: - debug "^2.2.0" - object-assign "^4.0.1" - resolve "^1.1.6" + debug "^2.6.9" + resolve "^1.5.0" -eslint-loader@^1.3.0: +eslint-loader@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-1.9.0.tgz#7e1be9feddca328d3dcfaef1ad49d5beffe83a13" dependencies: @@ -2329,43 +2288,45 @@ eslint-loader@^1.3.0: object-hash "^1.1.4" rimraf "^2.6.1" -eslint-plugin-import@^1.8.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-1.16.0.tgz#b2fa07ebcc53504d0f2a4477582ec8bff1871b9f" +eslint-module-utils@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449" + dependencies: + debug "^2.6.8" + pkg-dir "^1.0.0" + +eslint-plugin-import@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.9.0.tgz#26002efbfca5989b7288ac047508bd24f217b169" dependencies: builtin-modules "^1.1.1" contains-path "^0.1.0" - debug "^2.2.0" - doctrine "1.3.x" - es6-map "^0.1.3" - es6-set "^0.1.4" - eslint-import-resolver-node "^0.2.0" + debug "^2.6.8" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.1" + eslint-module-utils "^2.1.1" has "^1.0.1" - lodash.cond "^4.3.0" - lodash.endswith "^4.0.1" - lodash.find "^4.3.0" - lodash.findindex "^4.3.0" + lodash "^4.17.4" minimatch "^3.0.3" - object-assign "^4.0.1" - pkg-dir "^1.0.0" - pkg-up "^1.0.0" + read-pkg-up "^2.0.0" -eslint-plugin-jsx-a11y@^1.2.2: - version "1.5.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-1.5.5.tgz#da284a016c1889e73698180217e2eb988a98bab5" +eslint-plugin-prettier@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz#33e4e228bdb06142d03c560ce04ec23f6c767dd7" dependencies: - damerau-levenshtein "^1.0.0" - jsx-ast-utils "^1.0.0" - object-assign "^4.0.1" + fast-diff "^1.1.1" + jest-docblock "^21.0.0" -eslint-plugin-react@^5.1.1: - version "5.2.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-5.2.2.tgz#7db068e1f5487f6871e4deef36a381c303eac161" +eslint-plugin-react@^7.7.0: + version "7.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz#f606c719dbd8a1a2b3d25c16299813878cca0160" dependencies: - doctrine "^1.2.2" - jsx-ast-utils "^1.2.1" + doctrine "^2.0.2" + has "^1.0.1" + jsx-ast-utils "^2.0.1" + prop-types "^15.6.0" -eslint-scope@~3.7.1: +eslint-scope@^3.7.1, eslint-scope@~3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" dependencies: @@ -2376,49 +2337,53 @@ eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" -eslint@^2.10.2: - version "2.13.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-2.13.1.tgz#e4cc8fa0f009fb829aaae23855a29360be1f6c11" +eslint@^4.18.1: + version "4.18.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.18.1.tgz#b9138440cb1e98b2f44a0d578c6ecf8eae6150b0" dependencies: - chalk "^1.1.3" - concat-stream "^1.4.6" - debug "^2.1.1" - doctrine "^1.2.2" - es6-map "^0.1.3" - escope "^3.6.0" - espree "^3.1.6" - estraverse "^4.2.0" + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.2" + esquery "^1.0.0" esutils "^2.0.2" - file-entry-cache "^1.1.1" - glob "^7.0.3" - globals "^9.2.0" - ignore "^3.1.2" + file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" + inquirer "^3.0.6" is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" - optionator "^0.8.1" - path-is-absolute "^1.0.0" - path-is-inside "^1.0.1" - pluralize "^1.2.1" - progress "^1.1.8" - require-uncached "^1.0.2" - shelljs "^0.6.0" - strip-json-comments "~1.0.1" - table "^3.7.8" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" + strip-json-comments "~2.0.1" + table "^4.0.1" text-table "~0.2.0" - user-home "^2.0.0" -espree@^3.1.6: - version "3.5.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.2.tgz#756ada8b979e9dcfcdb30aad8d1a9304a905e1ca" +espree@^3.5.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.3.tgz#931e0af64e7fbbed26b050a29daad1fc64799fa6" dependencies: - acorn "^5.2.1" + acorn "^5.4.0" acorn-jsx "^3.0.0" esprima@^2.6.0: @@ -2429,6 +2394,12 @@ esprima@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" +esquery@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" + dependencies: + estraverse "^4.0.0" + esrecurse@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" @@ -2436,7 +2407,7 @@ esrecurse@^4.1.0: estraverse "^4.1.0" object-assign "^4.0.1" -estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" @@ -2448,13 +2419,6 @@ etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" -event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - dependencies: - d "1" - es5-ext "~0.10.14" - eventemitter3@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" @@ -2476,10 +2440,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -2562,6 +2522,14 @@ extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" +external-editor@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48" + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" @@ -2593,6 +2561,10 @@ fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" +fast-diff@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -2629,12 +2601,11 @@ fbjs@^0.8.16, fbjs@^0.8.9: setimmediate "^1.0.5" ua-parser-js "^0.7.9" -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" dependencies: escape-string-regexp "^1.0.5" - object-assign "^4.1.0" file-contents@^0.2.4: version "0.2.4" @@ -2665,9 +2636,9 @@ file-contents@^0.3.1: through2 "^2.0.1" vinyl "^1.1.1" -file-entry-cache@^1.1.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-1.3.1.tgz#44c61ea607ae4be9c1402f41f44270cbfe334ff8" +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" dependencies: flat-cache "^1.2.1" object-assign "^4.0.1" @@ -2754,7 +2725,7 @@ find-up@^1.0.0: path-exists "^2.0.0" pinkie-promise "^2.0.0" -find-up@^2.1.0: +find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" dependencies: @@ -2867,6 +2838,10 @@ function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -2904,6 +2879,10 @@ get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -2944,7 +2923,7 @@ glob@^6.0.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@~7.1.1: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -2971,11 +2950,15 @@ global-prefix@^0.1.4: is-windows "^0.2.0" which "^1.2.12" +globals@^11.0.1: + version "11.3.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.3.0.tgz#e04fdb7b9796d8adac9c8f64c14837b2313378b0" + globals@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.1.0.tgz#632644457f5f0e3ae711807183700ebf2e4633e4" -globals@^9.18.0, globals@^9.2.0: +globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -3061,6 +3044,10 @@ has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + has-glob@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/has-glob/-/has-glob-0.1.1.tgz#a261c4c2a6c667e0c77b700a7f297c39ef3aa589" @@ -3259,7 +3246,7 @@ https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" -iconv-lite@0.4, iconv-lite@0.4.19, iconv-lite@~0.4.13: +iconv-lite@0.4, iconv-lite@0.4.19, iconv-lite@^0.4.17, iconv-lite@~0.4.13: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -3277,7 +3264,7 @@ ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" -ignore@^3.1.2: +ignore@^3.3.3: version "3.3.7" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" @@ -3333,22 +3320,23 @@ ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" +inquirer@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" cli-width "^2.0.0" - figures "^1.3.5" + external-editor "^2.0.4" + figures "^2.0.0" lodash "^4.3.0" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" - string-width "^1.0.1" - strip-ansi "^3.0.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" through "^2.3.6" internal-ip@1.2.0: @@ -3513,7 +3501,7 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" -is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: +is-my-json-valid@^2.12.4: version "2.17.1" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz#3da98914a70a22f0a8563ef1511a246c6fc55471" dependencies: @@ -3574,6 +3562,10 @@ is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" @@ -3655,6 +3647,10 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" +jest-docblock@^21.0.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" + js-base64@^2.1.8, js-base64@^2.1.9: version "2.4.2" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.2.tgz#1896da010ef8862f385d8887648e9b6dc4a7a2e9" @@ -3663,7 +3659,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-yaml@^3.5.1: +js-yaml@^3.9.1: version "3.10.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" dependencies: @@ -3701,7 +3697,11 @@ json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" -json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + +json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" dependencies: @@ -3736,9 +3736,11 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.2.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" +jsx-ast-utils@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f" + dependencies: + array-includes "^3.0.3" killable@^1.0.0: version "1.0.0" @@ -3807,6 +3809,15 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + loader-fs-cache@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz#56e0bf08bd9708b26a765b68509840c8dec9fdbc" @@ -3862,22 +3873,6 @@ lodash.clonedeep@^4.3.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" -lodash.cond@^4.3.0: - version "4.5.2" - resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" - -lodash.endswith@^4.0.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09" - -lodash.find@^4.3.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" - -lodash.findindex@^4.3.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.findindex/-/lodash.findindex-4.6.0.tgz#a3245dee61fb9b6e0624b535125624bb69c11106" - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -4085,6 +4080,10 @@ mime@^1.4.1, mime@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" @@ -4142,9 +4141,9 @@ multicast-dns@^6.0.1: dns-packet "^1.0.1" thunky "^0.1.0" -mute-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" nan@^2.3.0, nan@^2.3.2: version "2.8.0" @@ -4166,6 +4165,10 @@ nanomatch@^1.2.5: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" @@ -4394,9 +4397,11 @@ once@^1.3.0, once@^1.3.3: dependencies: wrappy "1" -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" opn@^5.1.0: version "5.2.0" @@ -4404,7 +4409,7 @@ opn@^5.1.0: dependencies: is-wsl "^1.1.0" -optionator@^0.8.1: +optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" dependencies: @@ -4435,7 +4440,7 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -4529,7 +4534,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -4549,6 +4554,12 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + dependencies: + pify "^2.0.0" + path@^0.12.7: version "0.12.7" resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" @@ -4604,15 +4615,9 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" -pkg-up@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-1.0.0.tgz#3e08fb461525c4421624a33b9f7e6d0af5b05a26" - dependencies: - find-up "^1.0.0" - -pluralize@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" portfinder@^1.0.9: version "1.0.13" @@ -4884,6 +4889,10 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier@^1.10.2: + version "1.11.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.0.tgz#c024f70cab158c993f50fc0c25ffe738cb8b0f85" + private@^0.1.6, private@^0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -4896,9 +4905,9 @@ process@^0.11.1, process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" -progress@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" promise@^7.1.1: version "7.3.1" @@ -5100,6 +5109,13 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -5108,6 +5124,14 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + readable-stream@1.1: version "1.1.13" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" @@ -5138,14 +5162,6 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" -readline2@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -5358,7 +5374,7 @@ require-package-name@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9" -require-uncached@^1.0.2: +require-uncached@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" dependencies: @@ -5394,18 +5410,18 @@ resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" -resolve@^1.1.6: +resolve@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" dependencies: path-parse "^1.0.5" -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" + onetime "^2.0.0" + signal-exit "^3.0.2" right-align@^0.1.1: version "0.1.3" @@ -5426,19 +5442,25 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" -run-async@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" dependencies: - once "^1.3.0" + is-promise "^2.1.0" rw@1: version "1.3.3" resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" @@ -5597,11 +5619,17 @@ shallow-clone@^0.1.2: lazy-cache "^0.2.3" mixin-object "^2.0.1" -shelljs@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" -signal-exit@^3.0.0: +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -5609,9 +5637,11 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" +slice-ansi@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" + dependencies: + is-fullwidth-code-point "^2.0.0" snapdragon-node@^2.0.1: version "2.1.1" @@ -5829,7 +5859,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0: +string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: @@ -5879,16 +5909,16 @@ strip-bom@^2.0.0: dependencies: is-utf8 "^0.2.0" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" dependencies: get-stdin "^4.0.1" -strip-json-comments@~1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" - strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -5926,6 +5956,12 @@ supports-color@^5.1.0: dependencies: has-flag "^2.0.0" +supports-color@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.2.0.tgz#b0d5333b1184dd3666cbe5aa0b45c5ac7ac17a4a" + dependencies: + has-flag "^3.0.0" + svg-sprite-loader@0.0.19: version "0.0.19" resolved "https://registry.yarnpkg.com/svg-sprite-loader/-/svg-sprite-loader-0.0.19.tgz#769a83009e34a1111b08efc84ae6df3038564542" @@ -5952,16 +5988,16 @@ symbol-observable@^1.0.2, symbol-observable@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32" -table@^3.7.8: - version "3.8.3" - resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" +table@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc" dependencies: - ajv "^4.7.0" - ajv-keywords "^1.0.0" - chalk "^1.1.1" - lodash "^4.0.0" - slice-ansi "0.0.4" - string-width "^2.0.0" + ajv "^6.0.1" + ajv-keywords "^3.0.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" tapable@^0.2.7, tapable@~0.2.5: version "0.2.8" @@ -6017,6 +6053,12 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -6214,12 +6256,6 @@ use@^2.0.0: isobject "^3.0.0" lazy-cache "^2.0.2" -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" - dependencies: - os-homedir "^1.0.0" - util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" |