Sync Integ to Master
[sdc.git] / catalog-ui / src / app / directives / graphs-v2 / palette / palette.directive.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 import * as _ from "lodash";
21 import {Component, IAppMenu, LeftPanelModel, NodesFactory, LeftPaletteComponent, CompositionCiNodeBase, ComponentInstance, Point} from "app/models";
22 import {CompositionGraphGeneralUtils} from "../composition-graph/utils/composition-graph-general-utils";
23 import {EventListenerService} from "app/services";
24 import {ResourceType, GRAPH_EVENTS, EVENTS, ComponentInstanceFactory, ModalsHandler} from "app/utils";
25 import 'sdc-angular-dragdrop';
26 import {LeftPaletteLoaderService} from "../../../services/components/utils/composition-left-palette-service";
27 import {Resource} from "app/models/components/resource";
28 import {ComponentType} from "app/utils/constants";
29 import {LeftPaletteMetadataTypes} from "../../../models/components/displayComponent";
30
31
32 interface IPaletteScope {
33     components:Array<LeftPaletteComponent>;
34     currentComponent:Component;
35     model:any;
36     displaySortedCategories:any;
37     expandedSection:string;
38     dragElement:JQuery;
39     dragbleNode:{
40         event:JQueryEventObject,
41         components:LeftPaletteComponent,
42         ui:any
43     }
44
45     addInstanceClick: ()=>void; // added code
46     onPopupMouseOver: ()=>void  // added code
47     onPopupMouseOut: ()=>void   // added code
48
49     sectionClick:(section:string)=>void;
50     searchComponents:(searchText:string)=>void;
51     onMouseOver:(displayComponent:LeftPaletteComponent, elem: HTMLElement)=>void;
52     onMouseOut:(displayComponent:LeftPaletteComponent)=>void;
53
54     dragStartCallback:(event:JQueryEventObject, ui, displayComponent:LeftPaletteComponent)=>void;
55     dragStopCallback:()=>void;
56     onDragCallback:(event:JQueryEventObject) => void;
57
58     setElementTemplate:(e:JQueryEventObject)=>void;
59
60     isOnDrag:boolean;
61     isDragable:boolean;
62     isLoading:boolean;
63     isViewOnly:boolean;
64 }
65
66 export class Palette implements ng.IDirective {
67     constructor(private $log:ng.ILogService,
68                 private LeftPaletteLoaderService: LeftPaletteLoaderService,
69                 private sdcConfig,
70                 private ComponentFactory,
71                 private ComponentInstanceFactory:ComponentInstanceFactory,
72                 private NodesFactory:NodesFactory,
73                 private CompositionGraphGeneralUtils:CompositionGraphGeneralUtils,
74                 private EventListenerService:EventListenerService,
75                 private sdcMenu:IAppMenu,
76                 private ModalsHandler:ModalsHandler
77             ) {
78     }
79
80     private fetchingComponentFromServer:boolean = false;
81     private nodeHtmlSubstitute:JQuery;
82
83     scope = {
84         currentComponent: '=',
85         isViewOnly: '=',
86         isLoading: '='
87     };
88     restrict = 'E';
89     template = require('./palette.html');
90
91     link = (scope:IPaletteScope, el:JQuery) => {
92         this.nodeHtmlSubstitute = $('<div class="node-substitute"><span></span><img /></div>');
93         el.append(this.nodeHtmlSubstitute);
94         this.registerEventListenerForLeftPalette(scope);
95
96         this.initComponents(scope);
97         this.initEvents(scope);
98         this.initDragEvents(scope);
99         this._initExpandedSection(scope, '');
100         el.on('$destroy', ()=> {
101             //remove listener of download event
102             this.unRegisterEventListenerForLeftPalette(scope);
103         });
104     };
105
106
107     private registerEventListenerForLeftPalette = (scope:IPaletteScope):void => {
108         this.EventListenerService.registerObserverCallback(EVENTS.LEFT_PALETTE_UPDATE_EVENT, () => {
109             this.updateLeftPanelDisplay(scope);
110         });
111     };
112
113     private unRegisterEventListenerForLeftPalette = (scope:IPaletteScope):void => {
114         this.EventListenerService.unRegisterObserver(EVENTS.LEFT_PALETTE_UPDATE_EVENT);
115     };
116
117     private leftPanelResourceFilter(resourcesNotAbstract:Array<LeftPaletteComponent>, resourceFilterTypes:Array<string>):Array<LeftPaletteComponent> {
118         let filterResources = _.filter(resourcesNotAbstract, (component) => {
119             return resourceFilterTypes.indexOf(component.getComponentSubType()) > -1;
120         });
121         return filterResources;
122     }
123
124     private initLeftPanel(leftPanelComponents:Array<LeftPaletteComponent>, resourceFilterTypes:Array<string>):LeftPanelModel {
125         let leftPanelModel = new LeftPanelModel();
126
127         if (resourceFilterTypes && resourceFilterTypes.length) {
128             leftPanelComponents = this.leftPanelResourceFilter(leftPanelComponents, resourceFilterTypes);
129         }
130         leftPanelModel.numberOfElements = leftPanelComponents && leftPanelComponents.length || 0;
131
132         if (leftPanelComponents && leftPanelComponents.length) {
133
134             let categories:any = _.groupBy(leftPanelComponents, 'mainCategory');
135             for (let category in categories)
136                 categories[category] = _.groupBy(categories[category], 'subCategory');
137
138             leftPanelModel.sortedCategories = categories;
139         }
140         return leftPanelModel;
141     }
142
143
144     private initEvents(scope:IPaletteScope) {
145
146         scope.sectionClick = (section:string) => {
147             if (section === scope.expandedSection) {
148                 scope.expandedSection = '';
149                 return;
150             }
151             scope.expandedSection = section;
152         };
153
154         scope.onMouseOver = (displayComponent:LeftPaletteComponent, sectionElem: HTMLElement) => {
155             if (this.isGroupOrPolicy(displayComponent)) {
156                 this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_SHOW_POPUP_PANEL, scope.currentComponent, displayComponent, sectionElem);
157             } else {
158                 if (scope.isOnDrag) {
159                     return;
160                 }
161                 scope.isOnDrag = true;
162                 this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, displayComponent);
163                 this.$log.debug('palette::onMouseOver:: fired');
164             }
165
166         };
167
168         scope.onMouseOut = (displayComponent:LeftPaletteComponent) => {
169             if(this.isGroupOrPolicy(displayComponent)) {
170                 this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HIDE_POPUP_PANEL);
171             } else {
172                 scope.isOnDrag = false;
173                 this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT);
174             }
175         };
176     }
177
178     private isGroupOrPolicy(component:LeftPaletteComponent): boolean {
179         if(component &&
180             (component.categoryType === LeftPaletteMetadataTypes.Group ||
181                 component.categoryType === LeftPaletteMetadataTypes.Policy)) {
182             return true;
183         }
184         return false;
185     }
186
187     private initComponents(scope:IPaletteScope) {
188         scope.searchComponents = (searchText:any):void => {
189             scope.displaySortedCategories = this._searchComponents(searchText, scope.model.sortedCategories);
190             this._initExpandedSection(scope, searchText);
191         };
192
193         scope.isDragable = scope.currentComponent.isComplex();
194         this.updateLeftPanelDisplay(scope);
195     }
196
197     private updateLeftPanelDisplay(scope:IPaletteScope) {
198         let entityType:string = scope.currentComponent.componentType.toLowerCase();
199         let resourceFilterTypes:Array<string> = this.sdcConfig.resourceTypesFilter[entityType];
200          scope.components = this.LeftPaletteLoaderService.getLeftPanelComponentsForDisplay(scope.currentComponent);
201         //remove the container component  from the list
202         let componentTempToDisplay = angular.copy(scope.components);
203         componentTempToDisplay = _.remove(componentTempToDisplay, function (leftPalettecomponent) {
204             return leftPalettecomponent.invariantUUID !== scope.currentComponent.invariantUUID;
205         });
206         scope.model = this.initLeftPanel(componentTempToDisplay, resourceFilterTypes);
207         scope.displaySortedCategories = angular.copy(scope.model.sortedCategories);
208     };
209
210     private _initExpandedSection(scope:IPaletteScope, searchText:string):void {
211         if (searchText == '') {
212             let isContainingCategory:boolean = false;
213             let categoryToExpand:string;
214             if (scope.currentComponent && scope.currentComponent.categories && scope.currentComponent.categories[0]) {
215                 categoryToExpand = this.sdcMenu.categoriesDictionary[scope.currentComponent.categories[0].name];
216                 for (let category in scope.model.sortedCategories) {
217                     if (categoryToExpand == category) {
218                         isContainingCategory = true;
219                         break;
220                     }
221                 }
222             }
223             isContainingCategory ? scope.expandedSection = categoryToExpand : scope.expandedSection = 'Generic';
224         }
225         else {
226             scope.expandedSection = Object.keys(scope.displaySortedCategories).sort()[0];
227         }
228     };
229
230     private initDragEvents(scope:IPaletteScope) {
231         scope.dragStartCallback = (event:IDragDropEvent, ui, displayComponent:LeftPaletteComponent):void => {
232             if (scope.isLoading || !scope.isDragable || scope.isViewOnly || this.isGroupOrPolicy(displayComponent)) {
233                 return;
234             }
235
236             let component = _.find(this.LeftPaletteLoaderService.getLeftPanelComponentsForDisplay(scope.currentComponent), (componentFullData:LeftPaletteComponent) => {
237                 return displayComponent.uniqueId === componentFullData.uniqueId;
238             });
239             this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, scope.dragElement, component);
240
241             scope.isOnDrag = true;
242
243             // this.graphUtils.showMatchingNodes(component, myDiagram, scope.sdcConfig.imagesPath);
244             // document.addEventListener('mousemove', moveOnDocument);
245             event.dataTransfer.component = component;
246         };
247
248         scope.dragStopCallback = () => {
249             scope.isOnDrag = false;
250         };
251
252         scope.onDragCallback = (event:IDragDropEvent):void => {
253             this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, event);
254         };
255         scope.setElementTemplate = (e) => {
256             let dragComponent:LeftPaletteComponent = _.find(this.LeftPaletteLoaderService.getLeftPanelComponentsForDisplay(scope.currentComponent),
257                 (fullComponent:LeftPaletteComponent) => {
258                     return (<any>angular.element(e.currentTarget).scope()).component.uniqueId === fullComponent.uniqueId;
259                 });
260             let componentInstance:ComponentInstance = this.ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent);
261             let node:CompositionCiNodeBase = this.NodesFactory.createNode(componentInstance);
262
263             // myDiagram.dragFromPalette = node;
264             this.nodeHtmlSubstitute.find("img").attr('src', node.img);
265             scope.dragElement = this.nodeHtmlSubstitute.clone().show();
266
267             return scope.dragElement;
268         };
269     }
270
271     private _searchComponents = (searchText:string, categories:any):void => {
272         let displaySortedCategories = angular.copy(categories);
273         if (searchText != '') {
274             angular.forEach(categories, function (category:any, categoryKey) {
275
276                 angular.forEach(category, function (subcategory:Array<LeftPaletteComponent>, subcategoryKey) {
277                     let filteredResources = [];
278                     angular.forEach(subcategory, function (component:LeftPaletteComponent) {
279
280                         let resourceFilterTerm:string = component.searchFilterTerms;
281                         if (resourceFilterTerm.indexOf(searchText.toLowerCase()) >= 0) {
282                             filteredResources.push(component);
283                         }
284                     });
285                     if (filteredResources.length > 0) {
286                         displaySortedCategories[categoryKey][subcategoryKey] = filteredResources;
287                     }
288                     else {
289                         delete  displaySortedCategories[categoryKey][subcategoryKey];
290                     }
291                 });
292                 if (!(Object.keys(displaySortedCategories[categoryKey]).length > 0)) {
293                     delete  displaySortedCategories[categoryKey];
294                 }
295
296             });
297         }
298         return displaySortedCategories;
299     };
300
301     public static factory = ($log,
302                              LeftPaletteLoaderService,
303                              sdcConfig,
304                              ComponentFactory,
305                              ComponentInstanceFactory,
306                              NodesFactory,
307                              CompositionGraphGeneralUtils,
308                              EventListenerService,
309                              sdcMenu,
310                              ModalsHandler
311                             ) => {
312         return new Palette($log,
313             LeftPaletteLoaderService,
314             sdcConfig,
315             ComponentFactory,
316             ComponentInstanceFactory,
317             NodesFactory,
318             CompositionGraphGeneralUtils,
319             EventListenerService,
320             sdcMenu,
321             ModalsHandler
322         );
323     };
324 }
325
326 Palette.factory.$inject = [
327     '$log',
328     'LeftPaletteLoaderService',
329     'sdcConfig',
330     'ComponentFactory',
331     'ComponentInstanceFactory',
332     'NodesFactory',
333     'CompositionGraphGeneralUtils',
334     'EventListenerService',
335     'sdcMenu',
336     'ModalsHandler'
337 ];