UNPKG

webgme

Version:

Web-based Generic Modeling Environment

863 lines (695 loc) 32.1 kB
/*globals define, WebGMEGlobal, $, document*/ /*jshint browser: true*/ /** * @author rkereskenyi / https://github.com/rkereskenyi */ define([ 'js/logger', 'js/util', './DiagramDesignerWidget.Constants' ], function (Logger, clientUtil, DiagramDesignerWidgetConstants) { 'use strict'; var SelectionManager, SELECTION_OVERLAP_RATIO = 0.5, SELECTION_OUTLINE_MARGIN = 15, SELECTION_OUTLINE_MIN_WIDTH = 100, MOUSE_EVENT_POSTFIX = 'SelectionManager'; SelectionManager = function (options) { var loggerName = (options && options.loggerName) || 'gme:Widgets:DiagramDesigner:SelectionManager'; this.logger = (options && options.logger) || Logger.create(loggerName, WebGMEGlobal.gmeConfig.client.log); this._diagramDesigner = options ? options.diagramDesigner : null; if (this._diagramDesigner === undefined || this._diagramDesigner === null) { this.logger.error('Trying to initialize a SelectionManager without a diagramDesigner...'); throw ('SelectionManager can not be created'); } this._selectedElements = []; this._rotationEnabled = true; this._alignEnabled = true; this._openEnabled = true; this.logger.debug('SelectionManager ctor finished'); }; SelectionManager.prototype.activate = function () { this._activateMouseListeners(); }; SelectionManager.prototype.deactivate = function () { this._deactivateMouseListeners(); this._setSelection([]); }; SelectionManager.prototype._activateMouseListeners = function () { //enable SelectionManager specific DOM event listeners var self = this; this._diagramDesigner.onItemMouseDown = function (itemId, eventDetails, mouseMoved) { if (self._diagramDesigner.mode === self._diagramDesigner.OPERATING_MODES.READ_ONLY || self._diagramDesigner.mode === self._diagramDesigner.OPERATING_MODES.DESIGN) { self._setSelection([itemId], self._isMultiSelectionModifierKeyPressed(eventDetails), mouseMoved); } }; this._diagramDesigner.onConnectionMouseDown = function (connId, eventDetails) { if (self._diagramDesigner.mode === self._diagramDesigner.OPERATING_MODES.READ_ONLY || self._diagramDesigner.mode === self._diagramDesigner.OPERATING_MODES.DESIGN) { self._setSelection([connId], self._isMultiSelectionModifierKeyPressed(eventDetails)); } }; this._diagramDesigner.onBackgroundMouseDown = function (eventDetails) { if (self._diagramDesigner.mode === self._diagramDesigner.OPERATING_MODES.READ_ONLY || self._diagramDesigner.mode === self._diagramDesigner.OPERATING_MODES.DESIGN) { self._onBackgroundMouseDown(eventDetails); } }; }; SelectionManager.prototype._deactivateMouseListeners = function () { this._diagramDesigner.onItemMouseDown = undefined; this._diagramDesigner.onConnectionMouseDown = undefined; this._diagramDesigner.onBackgroundMouseDown = undefined; }; SelectionManager.prototype.initialize = function (el) { var self = this; this.$el = el; this._diagramDesigner.addEventListener(this._diagramDesigner.events.ON_COMPONENT_DELETE, function (__diagramDesigner, componentId) { self._onComponentDelete(componentId); } ); }; SelectionManager.prototype.getSelectedElements = function () { return this._selectedElements.slice(0); }; SelectionManager.prototype.clear = function () { this._setSelection([]); }; SelectionManager.prototype.setSelection = function (idList, addToExistingSelection) { this._setSelection(idList, addToExistingSelection); }; SelectionManager.prototype.onSelectionCommandClicked = function (command, selectedIds) { this.logger.warn('SelectionManager.prototype.onSelectionCommandClicked IS NOT OVERRIDDEN IN HOST COMPONENT. ' + ' command: "' + command + '", selectedIds: ' + selectedIds); }; SelectionManager.prototype.onSelectionChanged = function (selectedIDs) { this.logger.warn('SelectionManager.prototype.onSelectionChanged IS NOT OVERRIDDEN IN HOST COMPONENT.' + ' selectedIDs: ' + selectedIDs); }; /********************** MULTIPLE SELECTION MODIFIER KEY CHECK ********************/ SelectionManager.prototype._isMultiSelectionModifierKeyPressed = function (event) { return event.ctrlKey || event.metaKey; }; /***************END OF --- MULTIPLE SELECTION MODIFIER KEY CHECK *****************/ /*********************** RUBBERBAND SELECTION *************************************/ SelectionManager.prototype._onBackgroundMouseDown = function (event) { var mousePos = {mX: event.mouseX, mY: event.mouseY}, self = this, leftButton = event.rightClick !== true; this.logger.debug('SelectionManager._onBackgroundMouseDown at: ' + JSON.stringify(mousePos)); if (leftButton === true) { //start drawing selection rubber-band this._rubberbandSelection = { x: mousePos.mX, y: mousePos.mY, x2: mousePos.mX, y2: mousePos.mY, addToExistingSelection: this._isMultiSelectionModifierKeyPressed(event) }; if (this._rubberbandSelection.addToExistingSelection !== true) { this._setSelection([]); } this.$rubberBand = this._createRubberBand(); this.$el.append(this.$rubberBand); //hook up MouseMove and MouseUp this._diagramDesigner.trackMouseMoveMouseUp( function (event) { self._onBackgroundMouseMove(event); }, function (event) { self._onBackgroundMouseUp(event); } ); } }; var RUBBERBAND_BASE = $('<div/>', {class: 'rubberband'}); SelectionManager.prototype._createRubberBand = function () { //create rubberband DOM element var rubberBand = RUBBERBAND_BASE.clone(); rubberBand.css({ display: 'none', position: 'absolute' }); return rubberBand; }; SelectionManager.prototype._onBackgroundMouseMove = function (event) { var mousePos = {mX: event.mouseX, mY: event.mouseY}; if (this._rubberbandSelection) { this._rubberbandSelection.x2 = mousePos.mX; this._rubberbandSelection.y2 = mousePos.mY; this._drawSelectionRubberBand(); } }; SelectionManager.prototype._onBackgroundMouseUp = function (event) { var mousePos = {mX: event.mouseX, mY: event.mouseY}, params; if (this._rubberbandSelection) { this._rubberbandSelection.x2 = mousePos.mX; this._rubberbandSelection.y2 = mousePos.mY; this._drawSelectionRubberBand(); params = { addToExistingSelection: this._rubberbandSelection.addToExistingSelection, x: Math.min(this._rubberbandSelection.x, this._rubberbandSelection.x2), x2: Math.max(this._rubberbandSelection.x, this._rubberbandSelection.x2), y: Math.min(this._rubberbandSelection.y, this._rubberbandSelection.y2), y2: Math.max(this._rubberbandSelection.y, this._rubberbandSelection.y2) }; this._selectItemsByRubberBand(params); //remove rubber-band DOM this.$rubberBand.remove(); this.$rubberBand = null; delete this._rubberbandSelection; } }; SelectionManager.prototype._drawSelectionRubberBand = function () { var minEdgeLength = 2, x = Math.min(this._rubberbandSelection.x, this._rubberbandSelection.x2), x2 = Math.max(this._rubberbandSelection.x, this._rubberbandSelection.x2), y = Math.min(this._rubberbandSelection.y, this._rubberbandSelection.y2), y2 = Math.max(this._rubberbandSelection.y, this._rubberbandSelection.y2); if (x2 - x < minEdgeLength || y2 - y < minEdgeLength) { this.$rubberBand.hide(); } else { this.$rubberBand.show(); } this.$rubberBand.css({ left: x, top: y, width: x2 - x, height: y2 - y }); }; SelectionManager.prototype._selectItemsByRubberBand = function (params) { var i, rbBBox = { x: params.x, y: params.y, x2: params.x2, y2: params.y2 }, itemsInSelection = [], selectionContainsBBox, items = this._diagramDesigner.items, minRubberBandSize = 10; if (rbBBox.x2 - rbBBox.x < minRubberBandSize || rbBBox.y2 - rbBBox.y < minRubberBandSize) { //selection area is too small, don't bother with it return; } this.logger.debug('Select children by rubber band: [' + rbBBox.x + ',' + rbBBox.y + '], [' + rbBBox.x2 + ',' + rbBBox.y2 + ']'); selectionContainsBBox = function (itemBBox) { var interSectionRect, interSectionRatio; if (itemBBox) { if (clientUtil.overlap(rbBBox, itemBBox)) { interSectionRect = { x: Math.max(itemBBox.x, rbBBox.x), y: Math.max(itemBBox.y, rbBBox.y), x2: Math.min(itemBBox.x2, rbBBox.x2), y2: Math.min(itemBBox.y2, rbBBox.y2) }; interSectionRatio = (interSectionRect.x2 - interSectionRect.x) * (interSectionRect.y2 - interSectionRect.y) / ((itemBBox.x2 - itemBBox.x) * (itemBBox.y2 - itemBBox.y)); if (interSectionRatio > SELECTION_OVERLAP_RATIO) { return true; } } } return false; }; for (i in items) { if (items.hasOwnProperty(i)) { if (selectionContainsBBox(items[i].getBoundingBox())) { itemsInSelection.push(i); } } } this._setSelection(itemsInSelection, params.addToExistingSelection); }; /*********************** END OF - RUBBERBAND SELECTION *************************************/ /*********************** CLEAR SELECTION *********************************/ SelectionManager.prototype._clearSelection = function () { var i = this._selectedElements.length, itemId, items = this._diagramDesigner.items, item; while (i--) { itemId = this._selectedElements[i]; item = items[itemId]; if (item) { if ($.isFunction(item.onDeselect)) { item.onDeselect(); } } } this._selectedElements = []; this.hideSelectionOutline(); }; /*********************** END OF --- CLEAR SELECTION ****************************/ /*********************** SET SELECTION *********************************/ SelectionManager.prototype._setSelection = function (idList, addToExistingSelection, mouseMoved) { var self = this, i, len, item, items = this._diagramDesigner.items, itemId, changed = false, selectionCount = idList.length, onceRendered = function(item) { if (idList.indexOf(item.id) > -1) { if (--selectionCount === 0) { // Update the selection box self.showSelectionOutline(); } } }; this.logger.debug('setSelection: ' + idList + ', addToExistingSelection: ' + addToExistingSelection); if (!mouseMoved) { // Check if the new selection has to be added to the existing selection. if (addToExistingSelection === true) { // If not in the selection yet, add IDs to the selection. // First let the already selected items know that they are participating in a // multiple selection from now on. i = this._selectedElements.length; while (i--) { item = items[this._selectedElements[i]]; if ($.isFunction(item.onDeselect)) { item.onDeselect(); } if ($.isFunction(item.onSelect)) { item.onSelect(true, onceRendered); } else { onceRendered(item); } } i = idList.length; len = idList.length + this._selectedElements.length; selectionCount = idList.length; while (i--) { itemId = idList[i]; if (this._selectedElements.indexOf(itemId) === -1) { this._selectedElements.push(itemId); item = items[itemId]; if ($.isFunction(item.onSelect)) { item.onSelect(len > 1, onceRendered); } else { onceRendered(item); } changed = true; } else { var idx = this._selectedElements.indexOf(itemId); this._selectedElements.splice(idx, 1); item = items[itemId]; if ($.isFunction(item.onDeselect)) { item.onDeselect(onceRendered); } else { onceRendered(item); } changed = true; } } if (this._selectedElements.length === 1) { item = items[this._selectedElements[0]]; if ($.isFunction(item.onSelect)) { item.onSelect(false, onceRendered); } else { onceRendered(item); } } } else { //the existing selection (if any) has to be cleared out first if (idList.length > 0) { this._clearSelection(); changed = true; i = idList.length; while (i--) { itemId = idList[i]; item = items[itemId]; this._selectedElements.push(itemId); if ($.isFunction(item.onSelect)) { item.onSelect(idList.length > 1, onceRendered); } else { onceRendered(item); } } } else { // Empty idList given.. if (this._selectedElements.length !== 0) { this._clearSelection(); changed = true; } } } } this.logger.debug('selected elements: ' + this._selectedElements); if (changed) { this.onSelectionChanged(this._selectedElements); } }; /*********************** END OF --- SET SELECTION *********************************/ /*********************** COMPONENT DELETE HANDLER *******************/ SelectionManager.prototype._onComponentDelete = function (componentId) { var idx, changed = false; //items are already deleted, we just need to remove them from the selectedIdList (if there) idx = this._selectedElements.indexOf(componentId); if (idx !== -1) { this._selectedElements.splice(idx, 1); changed = true; } if (changed) { this.onSelectionChanged(this._selectedElements); } }; /*********************** COMPONENT DELETE HANDLER *******************/ /*********************** SHOW SELECTION OUTLINE *********************************/ var SELECTION_OUTLINE_BASE = $('<div/>', { class: 'selection-outline' }); SelectionManager.prototype.showSelectionOutline = function () { var bBox = this._getSelectionBoundingBox(), cW = this._diagramDesigner._actualSize.w, cH = this._diagramDesigner._actualSize.h; if (bBox && bBox.hasOwnProperty('x')) { bBox.x -= SELECTION_OUTLINE_MARGIN; bBox.y -= SELECTION_OUTLINE_MARGIN; bBox.x2 += SELECTION_OUTLINE_MARGIN; bBox.y2 += SELECTION_OUTLINE_MARGIN; if (bBox.x < 0) { bBox.x = 0; } if (bBox.y < 0) { bBox.y = 0; } if (bBox.x2 > cW) { bBox.x2 = cW; } if (bBox.y2 > cH) { bBox.y2 = cH; } bBox.w = bBox.x2 - bBox.x; bBox.h = bBox.y2 - bBox.y; if (bBox.w < SELECTION_OUTLINE_MIN_WIDTH) { bBox.x -= (SELECTION_OUTLINE_MIN_WIDTH - bBox.w) / 2; bBox.x2 += (SELECTION_OUTLINE_MIN_WIDTH - bBox.w) / 2; bBox.w = bBox.x2 - bBox.x; } if (this._diagramDesigner.skinParts.$selectionOutline) { this._diagramDesigner.skinParts.$selectionOutline.empty(); } else { this._diagramDesigner.skinParts.$selectionOutline = SELECTION_OUTLINE_BASE.clone(); this._diagramDesigner.skinParts.$itemsContainer.prepend( this._diagramDesigner.skinParts.$selectionOutline); } this._diagramDesigner.skinParts.$selectionOutline.css({ left: bBox.x, top: bBox.y, width: bBox.w, height: bBox.h }); this._renderSelectionActions(); } else { this.hideSelectionOutline(); } }; /*********************** END OF --- SHOW SELECTION OUTLINE *********************************/ /*********************** HIDE SELECTION OUTLINE *********************************/ SelectionManager.prototype.hideSelectionOutline = function () { if (this._diagramDesigner.skinParts.$selectionOutline) { this._diagramDesigner.skinParts.$selectionOutline.empty(); this._diagramDesigner.skinParts.$selectionOutline.remove(); this._diagramDesigner.skinParts.$selectionOutline = null; } }; /*********************** END OF --- HIDE SELECTION OUTLINE *********************************/ /************* GET SELECTION OUTLINE COORDINATES AND DIMENSIONS ************************/ SelectionManager.prototype._getSelectionBoundingBox = function () { var i = this._selectedElements.length, bBox, id, childBBox, items = this._diagramDesigner.items; while (i--) { id = this._selectedElements[i]; if (items[id]) { if (!bBox) { bBox = { x: this._diagramDesigner._actualSize.w, y: this._diagramDesigner._actualSize.h, x2: 0, y2: 0 }; } childBBox = items[id].getBoundingBox(); if (childBBox.x < bBox.x) { bBox.x = childBBox.x; } if (childBBox.y < bBox.y) { bBox.y = childBBox.y; } if (childBBox.x2 > bBox.x2) { bBox.x2 = childBBox.x2; } if (childBBox.y2 > bBox.y2) { bBox.y2 = childBBox.y2; } } } return bBox; }; /************* END OF --- GET SELECTION OUTLINE COORDINATES AND DIMENSIONS ************************/ /************* RENDER COMMAND BUTTONS ON SELECTION OUTLINE ************************/ var DELETE_BUTTON_BASE = $('<div/>', { class: 's-btn delete', command: 'delete' }); DELETE_BUTTON_BASE.html('<i class="glyphicon glyphicon-remove" title="Delete"></i>'); var CONTEXT_MENU_BUTTON_BASE = $('<div/>', { class: 's-btn contextmenu', command: 'contextmenu' }); CONTEXT_MENU_BUTTON_BASE.html('<i class="glyphicon glyphicon-list" title="Action Menu"></i>'); var MOVE_BUTTON_BASE = $('<div/>', { class: 's-btn move', command: 'move' }); MOVE_BUTTON_BASE.html('<i class="glyphicon glyphicon-move"></i>'); var OPEN_BUTTON_BASE = $('<div/>', { class: 's-btn open', command: 'open' }); OPEN_BUTTON_BASE.html('<i class="glyphicon glyphicon-circle-arrow-down" title="Open"></i>'); var ALIGN_BUTTON_BASE = $('<div/>', { class: 's-btn align', command: 'align' }); ALIGN_BUTTON_BASE.html('<i class="glyphicon glyphicon-th" title="Align Menu"></i>'); SelectionManager.prototype._renderSelectionActions = function () { var self = this, deleteBtn, openBtn, contextMenuBtn, alignBtn; if (this._diagramDesigner.getIsReadOnlyMode() !== true) { deleteBtn = DELETE_BUTTON_BASE.clone(); this._diagramDesigner.skinParts.$selectionOutline.append(deleteBtn); // FIXME: There is an issue with the x and y offset, try it with zoom and scroll, too. // moveBtn = MOVE_BUTTON_BASE.clone(); // this._diagramDesigner.skinParts.$selectionOutline.append(moveBtn); // this._diagramDesigner._makeDraggable({$el: moveBtn}); if (this._alignEnabled === true) { alignBtn = ALIGN_BUTTON_BASE.clone(); this._diagramDesigner.skinParts.$selectionOutline.append(alignBtn); } } if (this._openBtnEnabled === true && this._selectedElements.length === 1) { openBtn = OPEN_BUTTON_BASE.clone(); this._diagramDesigner.skinParts.$selectionOutline.append(openBtn); } //context menu contextMenuBtn = CONTEXT_MENU_BUTTON_BASE.clone(); this._diagramDesigner.skinParts.$selectionOutline.append(contextMenuBtn); //detach mousedown handler on selection outline this._diagramDesigner.skinParts.$selectionOutline.off('mousedown').off('click', '.s-btn'); this._diagramDesigner.skinParts.$selectionOutline.on('mousedown', function (event) { event.stopPropagation(); }).on('click', '.s-btn', function (event) { var command = $(this).attr('command'); self.logger.debug('Selection button clicked with command: "' + command + '"'); self.onSelectionCommandClicked(command, self._selectedElements, event); event.stopPropagation(); event.preventDefault(); }); this._renderRotateHandlers(); }; /************* END OF --- RENDER COMMAND BUTTONS ON SELECTION OUTLINE ************************/ var ROTATION_BUTTON_BASE = $( '<div/>', { class: 's-btn rotate bottom' }); ROTATION_BUTTON_BASE.html('' + '<i class="glyphicon glyphicon-repeat">' + '<div class="popover right nowrap rotate-options">' + '<div class="arrow"></div><div class="popover-content narrow"><div class="btn-group">' + '<a class="btn btn-small" id="rotate-left" title="Rotate left by 90°">' + '<i class="glyphicon glyphicon-repeat flip-vertical" id="icon-rotate-left"></i></a>' + '<a class="btn btn-small" id="rotate-right" title="Rotate right by 90°">' + '<i class="glyphicon glyphicon-repeat" id="icon-rotate-right"></i></a>' + '<a class="btn btn-small" id="rotate-toleft" title="Rotate left to the closest multiple of 90°">' + '<i class="glyphicon glyphicon-retweet flip-vertical" id="icon-rotate-toleft"></i></a>' + '<a class="btn btn-small" id="rotate-toright" title="Rotate right to the closest multiple of 90°">' + '<i class="glyphicon glyphicon-retweet" id="icon-rotate-toright"></i></a>' + '<a class="btn btn-small" id="rotate-reset" title="Set rotation to 0°">' + '<i class="glyphicon glyphicon-minus" id="icon-rotate-reset"></i></a>' + '<a class="btn btn-small" id="rotate-clear" title="Clear rotation information - reset to inherited">' + '<i class="glyphicon glyphicon-remove" id="icon-rotate-clear"></i></a></div></div></div></i>'); var ROTATION_DEGREE_BASE = $('<div/>', { class: 'rotation-deg' }); SelectionManager.prototype._renderRotateHandlers = function () { var rotateBtnBottom, self = this, rotateEnabled = !this._diagramDesigner.getIsReadOnlyMode() && this._rotationEnabled; if (rotateEnabled) { rotateBtnBottom = ROTATION_BUTTON_BASE.clone(); this._rotationDegree = ROTATION_DEGREE_BASE.clone(); this._diagramDesigner.skinParts.$selectionOutline.append(rotateBtnBottom); this._diagramDesigner.skinParts.$selectionOutline.append(this._rotationDegree); this._diagramDesigner.skinParts.$selectionOutline.off('mousedown.' + MOUSE_EVENT_POSTFIX, '.rotate'); this._diagramDesigner.skinParts.$selectionOutline.on('mousedown.' + MOUSE_EVENT_POSTFIX, '.rotate', function (event) { var rotateBtn = $(this); if ($(event.target).attr('id') === undefined) { self.logger.debug('selection rotate button mousedown'); self._startSelectionRotate(rotateBtn, event); event.stopPropagation(); event.preventDefault(); } } ); rotateBtnBottom.hover( function () { rotateBtnBottom.find('.popover').show(); }, function () { rotateBtnBottom.find('.popover').hide(); } ); rotateBtnBottom.on('click', '.btn', function (event) { var btnId = $(this).attr('id'), deg = 0; switch (btnId.replace('rotate-', '')) { case 'left': deg = -90; break; case 'right': deg = 90; break; case DiagramDesignerWidgetConstants.ROTATION_RESET: deg = DiagramDesignerWidgetConstants.ROTATION_RESET; break; case DiagramDesignerWidgetConstants.ROTATION_TOLEFT: deg = DiagramDesignerWidgetConstants.ROTATION_TOLEFT; break; case DiagramDesignerWidgetConstants.ROTATION_TORIGHT: deg = DiagramDesignerWidgetConstants.ROTATION_TORIGHT; break; case DiagramDesignerWidgetConstants.ROTATION_CLEAR: deg = DiagramDesignerWidgetConstants.ROTATION_CLEAR; break; } self._rotateSelectionBy(deg); event.stopPropagation(); event.preventDefault(); }); } }; SelectionManager.prototype._startSelectionRotate = function (rotateBtn, event) { var mousePos = this._diagramDesigner.getAdjustedMousePos(event), self = this, leftButton = event.which === 1; if (leftButton) { this._rotateDesc = { startX: mousePos.mX, startY: mousePos.mY, oX: parseInt(this._diagramDesigner.skinParts.$selectionOutline.css('left'), 10) + parseInt(this._diagramDesigner.skinParts.$selectionOutline.css('width'), 10) / 2, oY: parseInt(this._diagramDesigner.skinParts.$selectionOutline.css('top'), 10) + parseInt(this._diagramDesigner.skinParts.$selectionOutline.css('height'), 10) / 2, horizontal: rotateBtn.hasClass('top') }; this._rotateAngle = 0; $(document).on('mousemove.rotate.' + MOUSE_EVENT_POSTFIX, function (event) { self._onSelectionRotate(event); }); $(document).on('mouseup.rotate.' + MOUSE_EVENT_POSTFIX, function (event) { //unbind mousemove and mouseup handlers $(document).off('mousemove.rotate.' + MOUSE_EVENT_POSTFIX); $(document).off('mouseup.rotate.' + MOUSE_EVENT_POSTFIX); self._endSelectionRotate(event); }); } }; SelectionManager.prototype._onSelectionRotate = function (event) { var mousePos = this._diagramDesigner.getAdjustedMousePos(event), dx = mousePos.mX - this._rotateDesc.startX, dy = mousePos.mY - this._rotateDesc.startY, roundTo = event.shiftKey ? 10 : (event.ctrlKey || event.metaKey ? 45 : 0), deg = this._getRotationDegree(this._rotateDesc.horizontal ? dx : dy, roundTo); this._rotateAngle = deg; this._rotationDegree.html((deg >= 0 ? '+' : '') + deg + '&deg;'); this._diagramDesigner.skinParts.$selectionOutline.css({ 'transform-origin': '50% 50%', transform: 'rotate(' + deg + 'deg)' }); }; SelectionManager.prototype._endSelectionRotate = function (/*event*/) { this._rotationDegree.html(''); this._diagramDesigner.skinParts.$selectionOutline.css({ 'transform-origin': '50% 50%', transform: 'rotate(0deg)' }); this._rotateSelectionBy(this._rotateAngle); }; SelectionManager.prototype._rotateSelectionBy = function (deg) { var selectedItems = [], i = this._selectedElements.length; while (i--) { if (this._diagramDesigner.itemIds.indexOf(this._selectedElements[i]) !== -1) { selectedItems.push(this._selectedElements[i]); } } if (selectedItems.length > 0) { this.onSelectionRotated(deg, selectedItems); } }; SelectionManager.prototype._getRotationDegree = function (value, roundTo) { var val = roundTo ? Math.round(value / roundTo) * roundTo : value; return Math.floor(val % 360); }; SelectionManager.prototype.onSelectionRotated = function (deg, selectedIds) { this.logger.warn('SelectionManager.prototype.onSelectionRotated IS NOT OVERRIDDEN IN HOST COMPONENT. deg: "' + deg + '"deg, selectedIds: ' + selectedIds); }; SelectionManager.prototype.enableRotation = function (enabled) { if (this._rotationEnabled !== enabled) { this._rotationEnabled = enabled; } }; SelectionManager.prototype.enableAlign = function (enabled) { if (this._alignEnabled !== enabled) { this._alignEnabled = enabled; } }; SelectionManager.prototype.enableOpenButton = function (enabled) { if (this._openBtnEnabled !== enabled) { this._openBtnEnabled = enabled; } }; return SelectionManager; });