2  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
 
   4  * Licensed under the Apache License, Version 2.0 (the "License");
 
   5  * you may not use this file except in compliance with the License.
 
   6  * You may obtain a copy of the License at
 
   8  * http://www.apache.org/licenses/LICENSE-2.0
 
  10  * Unless required by applicable law or agreed to in writing, software
 
  11  * distributed under the License is distributed on an "AS IS" BASIS,
 
  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 
  13  * or implied. See the License for the specific language governing
 
  14  * permissions and limitations under the License.
 
  17 import React from 'react';
 
  18 import Select from 'react-select';
 
  20 import Common from '../../../../../../common/Common';
 
  21 import Logger from '../../../../../../common/Logger';
 
  23 import Icon from '../../../../../icons/Icon';
 
  24 import iconSettings from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/settings.svg';
 
  25 import iconExpanded from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/expanded.svg';
 
  26 import iconCollapsed from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/collapsed.svg';
 
  27 import iconOccurrenceDefault from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/occurrence-default.svg';
 
  28 import iconOccurrenceStart from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/occurrence-start.svg';
 
  29 import iconOccurrenceStop from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/occurrence-stop.svg';
 
  30 import iconFragmentDefault from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/fragment-default.svg';
 
  31 import iconFragmentStart from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/fragment-start.svg';
 
  32 import iconFragmentStop from '../../../../../../../../../../res/ecomp/asdc/sequencer/sprites/icons/fragment-stop.svg';
 
  37 export default class Actions extends React.Component {
 
  39   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
  43    * @param props element properties.
 
  44    * @param context react context.
 
  46   constructor(props, context) {
 
  47     super(props, context);
 
  58     this.show = this.show.bind(this);
 
  60     this.onClickOccurrenceToggle = this.onClickOccurrenceToggle.bind(this);
 
  61     this.onClickOccurrenceFrom = this.onClickOccurrenceFrom.bind(this);
 
  62     this.onClickOccurrenceTo = this.onClickOccurrenceTo.bind(this);
 
  64     this.onClickFragmentToggle = this.onClickFragmentToggle.bind(this);
 
  65     this.onChangeFragmentGuard = this.onChangeFragmentGuard.bind(this);
 
  66     this.onChangeFragmentOperator = this.onChangeFragmentOperator.bind(this);
 
  68     this.onMouseOut = this.onMouseOut.bind(this);
 
  71   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
  75    * @param id message ID.
 
  76    * @param position xy coordinates.
 
  79     const message = this.props.model.getMessageById(id);
 
  81     let occurrencesToggle = false;
 
  82     let fragmentToggle = false;
 
  85       message.occurrences = message.occurrences || { start: [], stop: [] };
 
  86       message.occurrences.start = message.occurrences.start || [];
 
  87       message.occurrences.stop = message.occurrences.stop || [];
 
  88       message.fragment = message.fragment || {};
 
  89       message.fragment.start = message.fragment.start || false;
 
  90       message.fragment.stop = message.fragment.stop || false;
 
  91       message.fragment.guard = message.fragment.guard || '';
 
  92       message.fragment.operator = message.fragment.operator || '';
 
  94       const mo = message.occurrences;
 
  95       occurrencesToggle = (mo.start.length > 0 || mo.stop.length > 0);
 
  97       const mf = message.fragment;
 
  98       fragmentToggle = (mf.start || mf.stop);
 
 112   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 115    * Toggle occurrence state.
 
 117   onClickOccurrenceToggle() {
 
 118     const message = this.state.message;
 
 120       const oFromState = Actions.getOccurrenceState(message.occurrences, message.from);
 
 121       const oToState = Actions.getOccurrenceState(message.occurrences, message.to);
 
 122       const oExpanded = oFromState > 0 || oToState > 0;
 
 124         this.setState({ occurrencesExpanded: true });
 
 126         const occurrencesExpanded = !this.state.occurrencesExpanded;
 
 127         this.setState({ occurrencesExpanded });
 
 132   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 137   onClickOccurrenceFrom() {
 
 138     const message = this.state.message;
 
 140       Actions._toggleOccurrence(message.occurrences, message.from);
 
 142     this.setState({ message });
 
 143     this.props.application.renderDiagram();
 
 146   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 151   onClickOccurrenceTo() {
 
 152     const message = this.state.message;
 
 154       Actions._toggleOccurrence(message.occurrences, message.to);
 
 156     this.setState({ message });
 
 157     this.props.application.renderDiagram();
 
 160   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 165   onClickFragmentToggle() {
 
 166     const message = this.state.message;
 
 168       Actions._toggleFragment(message.fragment);
 
 170     this.setState({ message });
 
 171     this.props.application.renderDiagram();
 
 174   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 178    * @param event update event.
 
 180   onChangeFragmentGuard(event) {
 
 181     const message = this.state.message;
 
 183       const options = this.props.application.getOptions();
 
 184       message.fragment.guard = Common.sanitizeText(event.target.value, options, 'guard');
 
 186     this.setState({ message });
 
 187     this.props.application.renderDiagram();
 
 190   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 194    * @param value updated value.
 
 196   onChangeFragmentOperator(value) {
 
 197     const message = this.state.message;
 
 199       message.fragment.operator = value.value;
 
 201     this.setState({ message });
 
 202     this.props.application.renderDiagram();
 
 205   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 208    * Handle mouse movement.
 
 211     this.setState({ id: -1, visible: false, x: 0, y: 0 });
 
 214   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 222     const actionsStyles = { };
 
 223     const message = this.state.message;
 
 224     if (!message || !this.state.visible) {
 
 228       return (<div className="asdcs-actions" ></div>);
 
 231     // Position and display.
 
 233     actionsStyles.display = 'block';
 
 234     actionsStyles.left = this.state.x - 10;
 
 235     actionsStyles.top = this.state.y - 10;
 
 237     const oFromState = Actions.getOccurrenceState(message.occurrences, message.from);
 
 238     const oToState = Actions.getOccurrenceState(message.occurrences, message.to);
 
 239     const fState = Actions.getFragmentState(message.fragment);
 
 241     const oExpanded = this.state.occurrencesExpanded || (oFromState > 0) || (oToState > 0);
 
 242     const oAuxClassName = oExpanded ? '' : 'asdcs-hidden';
 
 244     const fExpanded = fState !== 0;
 
 245     const fAuxClassName = fExpanded ? '' : 'asdcs-hidden';
 
 247     const fragmentOperatorOptions = [{
 
 258     const operator = message.fragment.operator || 'alt';
 
 262         className="asdcs-actions"
 
 263         style={actionsStyles}
 
 264         onMouseLeave={this.onMouseOut}
 
 266         <div className="asdcs-actions-header">
 
 267           <div className="asdcs-actions-icon">
 
 268             <Icon glyph={iconSettings} />
 
 272         <div className="asdcs-actions-options">
 
 274           <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-occurrence">
 
 276               className="asdcs-actions-option asdcs-actions-option-occurrence-toggle"
 
 277               onClick={this.onClickOccurrenceToggle}
 
 279               <span className="asdcs-label">Occurrence</span>
 
 280               <div className="asdcs-actions-state">
 
 281                 <Icon glyph={iconCollapsed} className={oExpanded ? 'asdcs-hidden' : ''} />
 
 282                 <Icon glyph={iconExpanded} className={oExpanded ? '' : 'asdcs-hidden'} />
 
 288             className={`asdcs-actions-option asdcs-actions-option-occurrence-from ${oAuxClassName}`}
 
 289             onClick={this.onClickOccurrenceFrom}
 
 291             <span className="asdcs-label">From</span>
 
 292             <div className="asdcs-actions-state">
 
 293               <span className="asdcs-annotation"></span>
 
 294               <Icon glyph={iconOccurrenceDefault} className={oFromState === 0 ? '' : 'asdcs-hidden'} />
 
 295               <Icon glyph={iconOccurrenceStart} className={oFromState === 1 ? '' : 'asdcs-hidden'} />
 
 296               <Icon glyph={iconOccurrenceStop} className={oFromState === 2 ? '' : 'asdcs-hidden'} />
 
 301             className={`asdcs-actions-option asdcs-actions-option-occurrence-to ${oAuxClassName}`}
 
 302             onClick={this.onClickOccurrenceTo}
 
 304             <span className="asdcs-label">To</span>
 
 305             <div className="asdcs-actions-state">
 
 306               <span className="asdcs-annotation"></span>
 
 307               <Icon glyph={iconOccurrenceDefault} className={oToState === 0 ? '' : 'asdcs-hidden'} />
 
 308               <Icon glyph={iconOccurrenceStart} className={oToState === 1 ? '' : 'asdcs-hidden'} />
 
 309               <Icon glyph={iconOccurrenceStop} className={oToState === 2 ? '' : 'asdcs-hidden'} />
 
 313           <div className="asdcs-actions-optiongroup asdcs-actions-optiongroup-fragment">
 
 315               className="asdcs-actions-option asdcs-actions-fragment-toggle"
 
 316               onClick={this.onClickFragmentToggle}
 
 318               <span className="asdcs-label">Fragment</span>
 
 319               <div className="asdcs-actions-state">
 
 320                 <span className="asdcs-annotation"></span>
 
 321                 <Icon glyph={iconFragmentDefault} className={fState === 0 ? '' : 'asdcs-hidden'} />
 
 322                 <Icon glyph={iconFragmentStart} className={fState === 1 ? '' : 'asdcs-hidden'} />
 
 323                 <Icon glyph={iconFragmentStop} className={fState === 2 ? '' : 'asdcs-hidden'} />
 
 328           <div className={`asdcs-actions-option asdcs-actions-fragment-operator ${fAuxClassName}`}>
 
 329             <div className="asdcs-label">Operator</div>
 
 330             <div className="asdcs-value">
 
 332                 className="asdcs-editable-select"
 
 337                 onChange={this.onChangeFragmentOperator}
 
 338                 options={fragmentOperatorOptions}
 
 343           <div className={`asdcs-actions-option asdcs-actions-fragment-guard ${fAuxClassName}`}>
 
 344             <div className="asdcs-label">Guard</div>
 
 345             <div className="asdcs-value">
 
 347                 className="asdcs-editable"
 
 351                 value={message.fragment.guard}
 
 352                 placeholder="Condition"
 
 353                 onChange={this.onChangeFragmentGuard}
 
 360         <div className="asdcs-actions-footer"></div>
 
 367   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 370    * Toggle through three occurrence states on click.
 
 371    * @param occurrence occurrences state, updated as side-effect.
 
 372    * @param lifelineId message end that's being toggled.
 
 375   static _toggleOccurrence(occurrence, lifelineId) {
 
 376     const o = occurrence;
 
 378     const rm = function rm(array, value) {
 
 379       const index = array.indexOf(value);
 
 381         array.splice(index, 1);
 
 385     const add = function add(array, value) {
 
 386       if (array.indexOf(value) === -1) {
 
 391     if (o.start && o.start.indexOf(lifelineId) !== -1) {
 
 393       rm(o.start, lifelineId);
 
 394       add(o.stop, lifelineId);
 
 395     } else if (o.stop && o.stop.indexOf(lifelineId) !== -1) {
 
 397       rm(o.start, lifelineId);
 
 398       rm(o.stop, lifelineId);
 
 401       add(o.start, lifelineId);
 
 402       rm(o.stop, lifelineId);
 
 406   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 409    * Toggle fragment setting on click.
 
 413   static _toggleFragment(fragment) {
 
 415     if (f.start === true) {
 
 418     } else if (f.stop === true) {
 
 429   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 432    * Get ternary occurrences state.
 
 433    * @param o occurrences.
 
 434    * @param lifelineId from/to lifeline ID.
 
 438   static getOccurrenceState(o, lifelineId) {
 
 439     if (o.start.indexOf(lifelineId) !== -1) {
 
 442     if (o.stop.indexOf(lifelineId) !== -1) {
 
 448   // ///////////////////////////////////////////////////////////////////////////////////////////////
 
 451    * Get ternary fragment state.
 
 456   static getFragmentState(f) {
 
 467 /** Element properties. */
 
 468 Actions.propTypes = {
 
 469   application: React.PropTypes.object.isRequired,
 
 470   model: React.PropTypes.object.isRequired,