/*
 * Activiti Modeler component part of the Activiti project
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * 这个文件和activiti的源码已经变化比较多了，by dengyg
 */
'use strict';

angular.module('activitiModeler')
    .controller('StencilController', ['$rootScope', '$scope', '$http', '$modal', '$timeout', '$controller', '$compile', 'templateService', function ($rootScope, $scope, $http, $modal, $timeout, $controller, $compile, templateService) {
        $controller('HtStencilController', { $scope: $scope });

        $scope.expandStatus = {
            palette: "lock",
            property: "lock"
        };

        $scope.togglePaletteExpand = function (enter) {
            if ($scope.expandStatus.palette != 'lock') {
                $scope.expandStatus.palette = enter ? 'expand' : 'collapse';
            }
        }

        $scope.togglePaletteLock = function () {
            if ($scope.expandStatus.palette == 'lock') {
                $scope.expandStatus.palette = 'expand';
            }
            else {
                $scope.expandStatus.palette = 'lock';
            }
        }

        $scope.togglePropertyExpand = function (enter) {
            if ($scope.expandStatus.property != 'lock') {
                $scope.expandStatus.property = enter ? 'expand' : 'collapse';
            }
        }

        $scope.togglePropertyLock = function () {
            if ($scope.expandStatus.property == 'lock') {
                $scope.expandStatus.property = 'expand';
            }
            else {
                $scope.expandStatus.property = 'lock';
            }
        }

        $scope.showHelpDialog = function () {
            layer.ready(function () {
                layer.open({
                    type: 1,
                    title: "使用说明",
                    area: ['300px', '340px'],
                    btn: ['确定'],
                    yes: function (index) {
                        layer.close(index);
                    },
                    fixed: false, //不固定
                    maxmin: false,
                    content: '<div class="help-container"><p>1.在画布区域按住<b>Alt键</b>，鼠标<b>滚轴前后滚动</b>可以放大/缩小画布；</p>'
                        + '<p>2.画布较大时，按住<b>Alt键</b>，鼠标<b>左键拖拽</b>画布可以在画布中漫游；</p>'
                        + '<p>3.按住<b>Alt键</b>在线条上<b>左键单击</b>可以添加拐点，<b>左键双击</b>生成的拐点可以删除它；</p>'
                        + '<p>4.工具栏最左侧的<b>本地保存/本地打开</b>可以暂存流程图在本地，即使系统不可访问也可保存流程图。</p>'
                        + '</div>'
                });
            });
        }

        // Property window toggle state
        $scope.propertyWindowState = { 'collapsed': false };
        //记录保存按钮状态，防止重复提交
        $scope.btnDisable = {};

        // Add reference to global header-config
        $scope.headerConfig = KISBPM.HEADER_CONFIG;

        $scope.propertyWindowState.toggle = function () {
            $scope.propertyWindowState.collapsed = !$scope.propertyWindowState.collapsed;
            $timeout(function () {
                jQuery(window).trigger('resize');
            });
        };

        //返回后台的context path
        var ctx = ACTIVITI.CONFIG.getContext();

        //输入流程名称后，根据流程名称获取首字母拼音作为流程Key
        $scope.pinyin = function () {
            if ($scope.modelData) {
                if ($scope.modelData.defKey) return;
                var value = $scope.modelData.name;
                if (!value) return;
                $http({
                    method: 'Get',
                    headers: { 'Authorization': KISBPM.URL.getToken },
                    url: ctx['uc'] + "/base/tools/v1/getPinyin?chinese=" + value + "&type=0"
                }).success(function (data) {
                    $scope.modelData.defKey = data.value;
                });
            }
        }

        // Code that is dependent on an initialised Editor is wrapped in a promise for the editor
        $scope.editorFactory.promise.then(function () {

            /* Build stencil item list */

            // Build simple json representation of stencil set
            var stencilItemGroups = [];

            // Helper method: find a group in an array
            var findGroup = function (name, groupArray) {
                for (var index = 0; index < groupArray.length; index++) {
                    if (groupArray[index].name === name) {
                        return groupArray[index];
                    }
                }
                return null;
            };

            // Helper method: add a new group to an array of groups
            var addGroup = function (groupName, groupArray) {
                var group = { name: groupName, items: [], paletteItems: [], groups: [], visible: true };
                groupArray.push(group);
                return group;
            };

            if ($scope.modelData && !$scope.modelData.defId) {
                var url = "editor-app/editor/defaultDefsign.json";
                $http({ method: 'GET', url: url}).success(function (data, status, headers, config) {
                    if (data  && data.childShapes) {
                        $scope.modelData.model = data;
                        $scope.editor.importJSON(data, false);
                    }
                })
            }
        
            /*
             StencilSet items
             */

            $http({ method: 'GET', url: "editor-app/editor/stencilset.json" }).success(function (data, status, headers, config) {

                var quickMenuDefinition = ['UserTask', 'EndEvent', 'XORGateway', 'ANDGateway', 'ORGateway', 'SequenceFlow'];
                var ignoreForPaletteDefinition = ['SequenceFlow', 'MessageFlow', 'Association', 'DataAssociation', 'DataStore', 'SendTask'];
                var quickMenuItems = [];

                var morphRoles = [];
                for (var i = 0; i < data.rules.morphingRules.length; i++) {
                    var role = data.rules.morphingRules[i].role;
                    var roleItem = { 'role': role, 'morphOptions': [] };
                    morphRoles.push(roleItem);
                }

                // Check all received items
                for (var stencilIndex = 0; stencilIndex < data.stencils.length; stencilIndex++) {
                    // Check if the root group is the 'diagram' group. If so, this item should not be shown.
                    var currentGroupName = data.stencils[stencilIndex].groups[0];
                    if (currentGroupName === 'Diagram' || currentGroupName === 'Form') {
                        continue;  // go to next item
                    }

                    var removed = false;
                    if (data.stencils[stencilIndex].removed) {
                        removed = true;
                    }

                    var currentGroup = undefined;
                    if (!removed) {
                        // Check if this group already exists. If not, we create a new one

                        if (currentGroupName !== null && currentGroupName !== undefined && currentGroupName.length > 0) {

                            currentGroup = findGroup(currentGroupName, stencilItemGroups); // Find group in root groups array
                            if (currentGroup === null) {
                                currentGroup = addGroup(currentGroupName, stencilItemGroups);
                            }

                            // Add all child groups (if any)
                            for (var groupIndex = 1; groupIndex < data.stencils[stencilIndex].groups.length; groupIndex++) {
                                var childGroupName = data.stencils[stencilIndex].groups[groupIndex];
                                var childGroup = findGroup(childGroupName, currentGroup.groups);
                                if (childGroup === null) {
                                    childGroup = addGroup(childGroupName, currentGroup.groups);
                                }

                                // The current group variable holds the parent of the next group (if any),
                                // and is basically the last element in the array of groups defined in the stencil item
                                currentGroup = childGroup;

                            }

                        }
                    }

                    // Construct the stencil item
                    var stencilItem = {
                        'id': data.stencils[stencilIndex].id,
                        'name': data.stencils[stencilIndex].title,
                        'description': data.stencils[stencilIndex].description,
                        'icon': data.stencils[stencilIndex].icon,
                        'type': data.stencils[stencilIndex].type,
                        'roles': data.stencils[stencilIndex].roles,
                        'removed': removed,
                        'customIcon': false,
                        'canConnect': false,
                        'canConnectTo': false,
                        'canConnectAssociation': false
                    };

                    if (data.stencils[stencilIndex].customIconId && data.stencils[stencilIndex].customIconId > 0) {
                        stencilItem.customIcon = true;
                        stencilItem.icon = data.stencils[stencilIndex].customIconId;
                    }

                    if (!removed) {
                        if (quickMenuDefinition.indexOf(stencilItem.id) >= 0) {
                            quickMenuItems[quickMenuDefinition.indexOf(stencilItem.id)] = stencilItem;
                        }
                    }

                    if (stencilItem.id === 'TextAnnotation' || stencilItem.id === 'BoundaryCompensationEvent') {
                        stencilItem.canConnectAssociation = true;
                    }

                    for (var i = 0; i < data.stencils[stencilIndex].roles.length; i++) {
                        var stencilRole = data.stencils[stencilIndex].roles[i];
                        if (stencilRole === 'sequence_start') {
                            stencilItem.canConnect = true;
                        } else if (stencilRole === 'sequence_end') {
                            stencilItem.canConnectTo = true;
                        }

                        for (var j = 0; j < morphRoles.length; j++) {
                            if (stencilRole === morphRoles[j].role) {
                                if (!removed) {
                                    morphRoles[j].morphOptions.push(stencilItem);
                                }
                                stencilItem.morphRole = morphRoles[j].role;
                                break;
                            }
                        }
                    }

                    if (currentGroup) {
                        // Add the stencil item to the correct group
                        currentGroup.items.push(stencilItem);
                        if (ignoreForPaletteDefinition.indexOf(stencilItem.id) < 0) {
                            currentGroup.paletteItems.push(stencilItem);
                        }

                    } else {
                        // It's a root stencil element
                        if (!removed) {
                            stencilItemGroups.push(stencilItem);
                        }
                    }
                }

                for (var i = 0; i < stencilItemGroups.length; i++) {
                    if (stencilItemGroups[i].paletteItems && stencilItemGroups[i].paletteItems.length == 0) {
                        stencilItemGroups[i].visible = false;
                    }
                }

                $scope.stencilItemGroups = stencilItemGroups;

                var containmentRules = [];
                for (var i = 0; i < data.rules.containmentRules.length; i++) {
                    var rule = data.rules.containmentRules[i];
                    containmentRules.push(rule);
                }
                $scope.containmentRules = containmentRules;

                // remove quick menu items which are not available anymore due to custom pallette
                var availableQuickMenuItems = [];
                for (var i = 0; i < quickMenuItems.length; i++) {
                    if (quickMenuItems[i]) {
                        availableQuickMenuItems[availableQuickMenuItems.length] = quickMenuItems[i];
                    }
                }

                $scope.quickMenuItems = availableQuickMenuItems;
                $scope.morphRoles = morphRoles;
            }).

                error(function (data, status, headers, config) {
                    console.log('Something went wrong when fetching stencil items:' + JSON.stringify(data));
                });

            /*
             * Listen to selection change events: show properties
             */
            $scope.editor.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, function (event) {
                var shapes = event.elements;
                var canvasSelected = false;
                if (shapes && shapes.length == 0) {
                    //return;
                    shapes = [$scope.editor.getCanvas()];
                    canvasSelected = true;
                }
                if (shapes && shapes.length > 0) {

                    var selectedShape = shapes.first();
                    var stencil = selectedShape.getStencil();
                    if (stencil.id().indexOf('BPMNDiagram') !== -1) {
                        return;
                    }

                    if ($rootScope.selectedElementBeforeScrolling && $rootScope.selectedElementBeforeScrolling.getId() === selectedShape.getId()) {
                        $rootScope.selectedElementBeforeScrolling = null;
                        return;
                    }

                    // Store previous selection
                    $scope.previousSelectedShape = $scope.selectedShape;

                    // Only do something if another element is selected (Oryx fires this event multiple times)
                    if ($scope.selectedShape !== undefined && $scope.selectedShape.getId() === selectedShape.getId()) {
                        if ($rootScope.forceSelectionRefresh) {
                            // Switch the flag again, this run will force refresh
                            $rootScope.forceSelectionRefresh = false;
                        }
                        // 通过raiseEvent触发的事件除外
                        else if (!event.triggerByRaise) {
                            // Selected the same element again, no need to update anything
                            return;
                        }
                    }

                    var selectedItem = { 'title': '流程信息', 'nodeTitle': '节点信息', 'properties': [] };

                    /*                    if (canvasSelected) {
                                            selectedItem.auditData = {
                                                'author': $scope.modelData.createdByUser,
                                                'createDate': $scope.modelData.createDate
                                            };
                                        }
                    */
                    // Gather properties of selected item
                    var properties = stencil.properties();

                    $scope.isAddNewNode = false;
                    // 连线对象不需要默认的id和name
                    if (!selectedShape.properties['oryx-overrideid'] && stencil.idWithoutNs() != "SequenceFlow") {
                        $scope.isAddNewNode = true;
                        $scope.addNewNodeNum(stencil.idWithoutNs());
                    }

                    var ismultiinstance = true;
                    //外部和内部子流程是否是多实例判断
                    if (stencil.idWithoutNs() == "StartSubFlowTask") {
                        if (selectedShape.properties['oryx-ismultiinstance']) {
                            if (selectedShape.properties['oryx-ismultiinstance'] == '单实例') {
                                ismultiinstance = false;
                                selectedShape.properties['oryx-multiinstance_collection'] = "";
                                selectedShape.properties['oryx-multiinstance_type'] = "";
                                selectedShape.properties['oryx-multiinstance_variable'] = "";
                            } else {
                                selectedShape.properties['oryx-multiinstance_collection'] = "${actUserService.getExtSubProcessMultipleUser(execution)}";
                                selectedShape.properties['oryx-multiinstance_variable'] = "assignee";
                                selectedShape.hiddenProperties['oryx-multiinstance_collection'] = "${actUserService.getExtSubProcessMultipleUser(execution)}";
                                selectedShape.hiddenProperties['oryx-multiinstance_variable'] = "assignee";
                                if (!selectedShape.properties['oryx-multiinstance_type']) {
                                    selectedShape.properties['oryx-multiinstance_type'] = "Sequential";
                                }
                            }
                        }
                    }
                    if (stencil.idWithoutNs() == "SubProcess") {
                        if (selectedShape.properties['oryx-ismultiinstance']) {
                            if (selectedShape.properties['oryx-ismultiinstance'] == '单实例') {
                                ismultiinstance = false;
                                selectedShape.properties['oryx-multiinstance_collection'] = "";
                                selectedShape.properties['oryx-multiinstance_type'] = "";
                                selectedShape.properties['oryx-multiinstance_variable'] = "";
                            } else {
                                selectedShape.properties['oryx-multiinstance_collection'] = "${actUserService.getSubProcessUser(execution)}";
                                selectedShape.properties['oryx-multiinstance_variable'] = "assignee";
                                selectedShape.hiddenProperties['oryx-multiinstance_collection'] = "${actUserService.getSubProcessUser(execution)}";
                                selectedShape.hiddenProperties['oryx-multiinstance_variable'] = "assignee";
                                if (!selectedShape.properties['oryx-multiinstance_type']) {
                                    selectedShape.properties['oryx-multiinstance_type'] = "Sequential";
                                }
                            }
                        }
                    }

                    for (var i = 0; i < properties.length; i++) {
                        var property = properties[i];
                        if (property.popular() == false) continue;
                        if (property.id() == "multiinstance_type" && !ismultiinstance) continue;
                        var key = property.prefix() + "-" + property.id();

                        if (key === 'oryx-name') {
                            selectedItem.title = selectedShape.properties[key];
                        }

                        // First we check if there is a config for 'key-type' and then for 'type' alone
                        var propertyConfig = KISBPM.PROPERTY_CONFIG[key + '-' + property.type()];
                        if (propertyConfig === undefined || propertyConfig === null) {
                            propertyConfig = KISBPM.PROPERTY_CONFIG[property.type()];
                        }

                        if (propertyConfig === undefined || propertyConfig === null) {
                            console.log('WARNING: no property configuration defined for ' + key + ' of type ' + property.type());
                        } else {

                            if (selectedShape.properties[key] === 'true') {
                                selectedShape.properties[key] = true;
                            }

                            if (KISBPM.CONFIG.showRemovedProperties == false && property.isHidden()) {
                                continue;
                            }

                            var currentProperty = {
                                'key': key,
                                'title': property.title(),
                                'type': property.type(),
                                'mode': 'read',
                                'hidden': property.isHidden(),
                                'value': selectedShape.properties[key],
                                'readonly': selectedShape.isFromServer
                            };

                            if ((currentProperty.type === 'complex' || currentProperty.type === 'multiplecomplex') && currentProperty.value && currentProperty.value.length > 0) {
                                try {
                                    currentProperty.value = JSON.parse(currentProperty.value);
                                } catch (err) {
                                    // ignore
                                }
                            }

                            if (propertyConfig.readModeTemplateUrl !== undefined && propertyConfig.readModeTemplateUrl !== null) {
                                currentProperty.readModeTemplateUrl = propertyConfig.readModeTemplateUrl + '?version=' + $rootScope.staticIncludeVersion;
                            }
                            if (propertyConfig.writeModeTemplateUrl !== null && propertyConfig.writeModeTemplateUrl !== null) {
                                currentProperty.writeModeTemplateUrl = propertyConfig.writeModeTemplateUrl + '?version=' + $rootScope.staticIncludeVersion;
                            }

                            if (propertyConfig.templateUrl !== undefined && propertyConfig.templateUrl !== null) {
                                currentProperty.templateUrl = propertyConfig.templateUrl + '?version=' + $rootScope.staticIncludeVersion;
                                currentProperty.hasReadWriteMode = false;
                            }
                            else {
                                currentProperty.hasReadWriteMode = true;
                            }

                            if (currentProperty.value === undefined
                                || currentProperty.value === null
                                || currentProperty.value.length == 0) {
                                currentProperty.noValue = true;
                            }
                            if (canvasSelected == false) {

                                if (property.id() == "overrideid" && $scope.isAddNewNode) {
                                    currentProperty.value = stencil.idWithoutNs() + $scope.stencilIdNumber[stencil.idWithoutNs()];
                                    $scope.idProperty = currentProperty;
                                }
                                if (property.id() == "name" && $scope.isAddNewNode) {
                                    currentProperty.value = stencil.title() + $scope.stencilIdNumber[stencil.idWithoutNs()];
                                    $scope.nameProperty = currentProperty;
                                }
                                selectedItem.properties.push(currentProperty);
                            }
                        }
                    }
                    // Need to wrap this in an $apply block, see http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
                    $scope.safeApply(function () {
                        $scope.selectedItem = selectedItem;
                        $scope.selectedShape = selectedShape;
                        if ($scope.isAddNewNode && $scope.idProperty && stencil.idWithoutNs() != "SequenceFlow") {
                            $scope.updatePropertyInModel($scope.nameProperty);
                            $scope.updatePropertyInModel($scope.idProperty);
                        }
                    });

                } else {
                    $scope.safeApply(function () {
                        $scope.selectedItem = {};
                        $scope.selectedShape = null;
                        $scope.nameProperty = {};
                        $scope.idProperty = {};
                    });
                }
            });

            $scope.editor.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, function (event) {

                KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS);
                var shapes = event.elements;

                if (shapes && shapes.length == 1) {

                    var selectedShape = shapes.first();

                    var a = $scope.editor.getCanvas().node.getScreenCTM();

                    var absoluteXY = selectedShape.absoluteXY();

                    absoluteXY.x *= a.a;
                    absoluteXY.y *= a.d;

                    var additionalIEZoom = 1;
                    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                        var ua = navigator.userAgent;
                        if (ua.indexOf('MSIE') >= 0) {
                            //IE 10 and below
                            var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
                            if (zoom !== 100) {
                                additionalIEZoom = zoom / 100
                            }
                        }
                    }

                    if (additionalIEZoom === 1) {
                        absoluteXY.y = absoluteXY.y - jQuery("#canvasSection").offset().top + 5;
                        absoluteXY.x = absoluteXY.x - jQuery("#canvasSection").offset().left;

                    } else {
                        var canvasOffsetLeft = jQuery("#canvasSection").offset().left;
                        var canvasScrollLeft = jQuery("#canvasSection").scrollLeft();
                        var canvasScrollTop = jQuery("#canvasSection").scrollTop();

                        var offset = a.e - (canvasOffsetLeft * additionalIEZoom);
                        var additionaloffset = 0;
                        if (offset > 10) {
                            additionaloffset = (offset / additionalIEZoom) - offset;
                        }
                        absoluteXY.y = absoluteXY.y - (jQuery("#canvasSection").offset().top * additionalIEZoom) + 5 + ((canvasScrollTop * additionalIEZoom) - canvasScrollTop);
                        absoluteXY.x = absoluteXY.x - (canvasOffsetLeft * additionalIEZoom) + additionaloffset + ((canvasScrollLeft * additionalIEZoom) - canvasScrollLeft);
                    }

                    var bounds = new ORYX.Core.Bounds(a.e + absoluteXY.x, a.f + absoluteXY.y, a.e + absoluteXY.x + a.a * selectedShape.bounds.width(), a.f + absoluteXY.y + a.d * selectedShape.bounds.height());
                    var shapeXY = bounds.upperLeft();

                    var stencilItem = $scope.getStencilItemById(selectedShape.getStencil().idWithoutNs());
                    var morphShapes = [];
                    if (stencilItem && stencilItem.morphRole) {
                        for (var i = 0; i < $scope.morphRoles.length; i++) {
                            if ($scope.morphRoles[i].role === stencilItem.morphRole) {
                                morphShapes = $scope.morphRoles[i].morphOptions;
                            }
                        }
                    }

                    var x = shapeXY.x;
                    if (bounds.width() < 48) {
                        x -= 24;
                    }

                    if (morphShapes && morphShapes.length > 0) {
                        // In case the element is not wide enough, start the 2 bottom-buttons more to the left
                        // to prevent overflow in the right-menu
                        var morphButton = document.getElementById('morph-button');
                        morphButton.style.display = "block";
                        morphButton.style.left = x + 24 + 'px';
                        morphButton.style.top = (shapeXY.y + bounds.height() + 2) + 'px';
                    }

                    var deleteButton = document.getElementById('delete-button');
                    deleteButton.style.display = "block";
                    deleteButton.style.left = x + 'px';
                    deleteButton.style.top = (shapeXY.y + bounds.height() + 2) + 'px';

                    if (stencilItem && (stencilItem.canConnect || stencilItem.canConnectAssociation)) {
                        var quickButtonCounter = 0;
                        var quickButtonX = shapeXY.x + bounds.width() + 5;
                        var quickButtonY = shapeXY.y;
                        jQuery('.Oryx_button').each(function (i, obj) {
                            if (obj.id !== 'morph-button' && obj.id != 'delete-button') {
                                quickButtonCounter++;
                                if (quickButtonCounter > 3) {
                                    quickButtonX = shapeXY.x + bounds.width() + 5;
                                    quickButtonY += 24;
                                    quickButtonCounter = 1;

                                } else if (quickButtonCounter > 1) {
                                    quickButtonX += 24;
                                }
                                obj.style.display = "block";
                                obj.style.left = quickButtonX + 'px';
                                obj.style.top = quickButtonY + 'px';
                            }
                        });
                    }
                }
            });

            if (!$rootScope.stencilInitialized) {
                KISBPM.eventBus.addListener(KISBPM.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS, function (event) {
                    jQuery('.Oryx_button').each(function (i, obj) {
                        obj.style.display = "none";
                    });
                });

	            /*
	             * Listen to property updates and act upon them
	             */
                KISBPM.eventBus.addListener(KISBPM.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED, function (event) {
                    if (event.property && event.property.key) {
                        // If the name property is been updated, we also need to change the title of the currently selected item
                        if (event.property.key === 'oryx-name' && $scope.selectedItem !== undefined && $scope.selectedItem !== null) {
                            $scope.selectedItem.title = event.newValue;
                        }

                        // Update "no value" flag
                        event.property.noValue = (event.property.value === undefined
                            || event.property.value === null
                            || event.property.value.length == 0);
                    }
                });

                $rootScope.stencilInitialized = true;
            }

            $scope.morphShape = function () {
                $scope.safeApply(function () {

                    var shapes = $rootScope.editor.getSelection();
                    if (shapes && shapes.length == 1) {
                        $rootScope.currentSelectedShape = shapes.first();
                        var stencilItem = $scope.getStencilItemById($rootScope.currentSelectedShape.getStencil().idWithoutNs());
                        var morphShapes = [];
                        for (var i = 0; i < $scope.morphRoles.length; i++) {
                            if ($scope.morphRoles[i].role === stencilItem.morphRole) {
                                morphShapes = $scope.morphRoles[i].morphOptions.slice();
                            }
                        }

                        layer.ready(function () {
                            $rootScope.morphShapes = morphShapes;
                            templateService.get('editor-app/popups/select-shape.html?version=' + Date.now(), $scope).then(function (target) {
                                layer.open({
                                    type: 1,
                                    title: "选择任务类型",
                                    area: ['300px', '340px'],
                                    btn: ['确定', '取消'],
                                    yes: function (index, layerno) {
                                        layerno.find("[ng-controller='KisBpmShapeSelectionCtrl']").scope().select();
                                        layer.close(index);
                                    },
                                    fixed: false, //不固定
                                    maxmin: false,
                                    content: target
                                });
                            });
                        });
                    }
                });
            };

            $scope.deleteShape = function () {
                KISBPM.TOOLBAR.ACTIONS.deleteItem({ '$scope': $scope });
            };

            $scope.quickAddItem = function (newItemId) {
                $scope.safeApply(function () {

                    var shapes = $rootScope.editor.getSelection();
                    if (shapes && shapes.length == 1) {
                        $rootScope.currentSelectedShape = shapes.first();

                        var containedStencil = undefined;
                        var stencilSets = $scope.editor.getStencilSets().values();
                        for (var i = 0; i < stencilSets.length; i++) {
                            var stencilSet = stencilSets[i];
                            var nodes = stencilSet.nodes();
                            for (var j = 0; j < nodes.length; j++) {
                                if (nodes[j].idWithoutNs() === newItemId) {
                                    containedStencil = nodes[j];
                                    break;
                                }
                            }
                        }

                        if (!containedStencil) return;

                        var option = { type: $scope.currentSelectedShape.getStencil().namespace() + newItemId, namespace: $scope.currentSelectedShape.getStencil().namespace() };
                        option['connectedShape'] = $rootScope.currentSelectedShape;
                        option['parent'] = $rootScope.currentSelectedShape.parent;
                        option['containedStencil'] = containedStencil;

                        var args = { sourceShape: $rootScope.currentSelectedShape, targetStencil: containedStencil };
                        var targetStencil = $scope.editor.getRules().connectMorph(args);
                        if (!targetStencil) { return; }// Check if there can be a target shape
                        option['connectingType'] = targetStencil.id();

                        var command = new KISBPM.CreateCommand(option, undefined, undefined, $scope.editor);
                        $scope.editor.executeCommands([command]);
                    }
                });
            };

        }); // end of $scope.editorFactory.promise block

        /* Click handler for clicking a property */
        $scope.propertyClicked = function (index, $event) {
            if (!$scope.selectedItem.properties[index].hidden) {
                $scope.selectedItem.properties[index].mode = "write";
            }
            $event.stopPropagation();
        };

        $scope.propertyBlur = function () {
            angular.forEach($scope.selectedItem.properties, function (property) {
                property.mode = "read";
            });
        }

        /* Helper method to retrieve the template url for a property */
        $scope.getPropertyTemplateUrl = function (index) {
            return $scope.selectedItem.properties[index].templateUrl;
        };
        $scope.getPropertyReadModeTemplateUrl = function (index) {
            return $scope.selectedItem.properties[index].readModeTemplateUrl;
        };
        $scope.getPropertyWriteModeTemplateUrl = function (index) {
            return $scope.selectedItem.properties[index].writeModeTemplateUrl;
        };

        /* Method available to all sub controllers (for property controllers) to update the internal Oryx model */
        $scope.updatePropertyInModel = function (property, shapeId) {

            var shape = $scope.selectedShape;
            // Some updates may happen when selected shape is already changed, so when an additional
            // shapeId is supplied, we need to make sure the correct shape is updated (current or previous)
            if (shapeId) {
                if (shape.id != shapeId && $scope.previousSelectedShape && $scope.previousSelectedShape.id == shapeId) {
                    shape = $scope.previousSelectedShape;
                } else {
                    shape = null;
                }
            }

            if (!shape) {
                // When no shape is selected, or no shape is found for the alternative
                // shape ID, do nothing
                return;
            }
            var key = property.key;
            var newValue = property.value;
            var oldValue = shape.properties[key];

            if (newValue != oldValue) {
                var commandClass = ORYX.Core.Command.extend({
                    construct: function () {
                        this.key = key;
                        this.oldValue = oldValue;
                        this.newValue = newValue;
                        this.shape = shape;
                        this.facade = $scope.editor;
                    },
                    execute: function () {
                        this.shape.setProperty(this.key, this.newValue);
                        this.facade.getCanvas().update();
                        this.facade.updateSelection();
                    },
                    rollback: function () {
                        this.shape.setProperty(this.key, this.oldValue);
                        this.facade.getCanvas().update();
                        this.facade.updateSelection();
                    }
                });
                // Instantiate the class
                var command = new commandClass();

                // Execute the command
                $scope.editor.executeCommands([command]);
                $scope.editor.handleEvents({
                    type: ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED,
                    elements: [shape],
                    key: key
                });

                // Switch the property back to read mode, now the update is done
                property.mode = 'read';

                // Fire event to all who is interested
                // Fire event to all who want to know about this
                var event = {
                    type: KISBPM.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED,
                    property: property,
                    oldValue: oldValue,
                    newValue: newValue
                };
                KISBPM.eventBus.dispatch(event.type, event);
            } else {
                // Switch the property back to read mode, no update was needed
                property.mode = 'read';
            }

        };

        /**
         * Helper method that searches a group for an item with the given id.
         * If not found, will return undefined.
         */
        $scope.findStencilItemInGroup = function (stencilItemId, group) {

            var item;

            // Check all items directly in this group
            for (var j = 0; j < group.items.length; j++) {
                item = group.items[j];
                if (item.id === stencilItemId) {
                    return item;
                }
            }

            // Check the child groups
            if (group.groups && group.groups.length > 0) {
                for (var k = 0; k < group.groups.length; k++) {
                    item = $scope.findStencilItemInGroup(stencilItemId, group.groups[k]);
                    if (item) {
                        return item;
                    }
                }
            }

            return undefined;
        };

        /**
         * Helper method to find a stencil item.
         */
        $scope.getStencilItemById = function (stencilItemId) {
            for (var i = 0; i < $scope.stencilItemGroups.length; i++) {
                var element = $scope.stencilItemGroups[i];

                // Real group
                if (element.items !== null && element.items !== undefined) {
                    var item = $scope.findStencilItemInGroup(stencilItemId, element);
                    if (item) {
                        return item;
                    }
                } else { // Root stencil item
                    if (element.id === stencilItemId) {
                        return element;
                    }
                }
            }
            return undefined;
        };

        /*
         * DRAG AND DROP FUNCTIONALITY
         */

        $scope.dropCallback = function (event, ui) {

            $scope.editor.handleEvents({
                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                highlightId: "shapeRepo.attached"
            });
            $scope.editor.handleEvents({
                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                highlightId: "shapeRepo.added"
            });

            $scope.editor.handleEvents({
                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                highlightId: "shapeMenu"
            });

            KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS);

            if ($scope.dragCanContain) {

                var item = $scope.getStencilItemById(ui.draggable[0].id);

                var pos = { x: event.pageX, y: event.pageY };

                var additionalIEZoom = 1;
                if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                    var ua = navigator.userAgent;
                    if (ua.indexOf('MSIE') >= 0) {
                        //IE 10 and below
                        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
                        if (zoom !== 100) {
                            additionalIEZoom = zoom / 100;
                        }
                    }
                }

                var screenCTM = $scope.editor.getCanvas().node.getScreenCTM();

                // Correcting the UpperLeft-Offset
                pos.x -= (screenCTM.e / additionalIEZoom);
                pos.y -= (screenCTM.f / additionalIEZoom);
                // Correcting the Zoom-Factor
                pos.x /= screenCTM.a;
                pos.y /= screenCTM.d;

                // Correcting the ScrollOffset
                pos.x -= document.documentElement.scrollLeft;
                pos.y -= document.documentElement.scrollTop;

                var parentAbs = $scope.dragCurrentParent.absoluteXY();
                pos.x -= parentAbs.x;
                pos.y -= parentAbs.y;

                var containedStencil = undefined;
                var stencilSets = $scope.editor.getStencilSets().values();
                for (var i = 0; i < stencilSets.length; i++) {
                    var stencilSet = stencilSets[i];
                    var nodes = stencilSet.nodes();
                    for (var j = 0; j < nodes.length; j++) {
                        if (nodes[j].idWithoutNs() === ui.draggable[0].id) {
                            containedStencil = nodes[j];
                            break;
                        }
                    }

                    if (!containedStencil) {
                        var edges = stencilSet.edges();
                        for (var j = 0; j < edges.length; j++) {
                            if (edges[j].idWithoutNs() === ui.draggable[0].id) {
                                containedStencil = edges[j];
                                break;
                            }
                        }
                    }
                }

                if (!containedStencil) return;

                if ($scope.quickMenu) {
                    var shapes = $scope.editor.getSelection();
                    if (shapes && shapes.length == 1) {
                        var currentSelectedShape = shapes.first();

                        var option = {};
                        option.type = currentSelectedShape.getStencil().namespace() + ui.draggable[0].id;
                        option.namespace = currentSelectedShape.getStencil().namespace();
                        option.connectedShape = currentSelectedShape;
                        option.parent = $scope.dragCurrentParent;
                        option.containedStencil = containedStencil;

                        // If the ctrl key is not pressed, 
                        // snapp the new shape to the center 
                        // if it is near to the center of the other shape
                        if (!event.ctrlKey) {
                            // Get the center of the shape
                            var cShape = currentSelectedShape.bounds.center();
                            // Snapp +-20 Pixel horizontal to the center 
                            if (20 > Math.abs(cShape.x - pos.x)) {
                                pos.x = cShape.x;
                            }
                            // Snapp +-20 Pixel vertical to the center 
                            if (20 > Math.abs(cShape.y - pos.y)) {
                                pos.y = cShape.y;
                            }
                        }

                        option.position = pos;

                        if (containedStencil.idWithoutNs() !== 'SequenceFlow' && containedStencil.idWithoutNs() !== 'Association' &&
                            containedStencil.idWithoutNs() !== 'MessageFlow' && containedStencil.idWithoutNs() !== 'DataAssociation') {
                            var args = { sourceShape: currentSelectedShape, targetStencil: containedStencil };
                            var targetStencil = $scope.editor.getRules().connectMorph(args);
                            if (!targetStencil) { return; }// Check if there can be a target shape
                            option.connectingType = targetStencil.id();
                        }

                        var command = new KISBPM.CreateCommand(option, $scope.dropTargetElement, pos, $scope.editor);

                        $scope.editor.executeCommands([command]);
                    }
                }
                else {
                    var canAttach = false;
                    if (containedStencil.idWithoutNs() === 'BoundaryErrorEvent' || containedStencil.idWithoutNs() === 'BoundaryTimerEvent' ||
                        containedStencil.idWithoutNs() === 'BoundarySignalEvent' || containedStencil.idWithoutNs() === 'BoundaryMessageEvent' ||
                        containedStencil.idWithoutNs() === 'BoundaryCancelEvent' || containedStencil.idWithoutNs() === 'BoundaryCompensationEvent') {
                        // Modify position, otherwise boundary event will get position related to left corner of the canvas instead of the container
                        pos = $scope.editor.eventCoordinates(event);
                        canAttach = true;
                    }

                    var option = {};
                    option['type'] = $scope.modelData.model.stencilset.namespace + item.id;
                    option['namespace'] = $scope.modelData.model.stencilset.namespace;
                    option['position'] = pos;
                    option['parent'] = $scope.dragCurrentParent;

                    var commandClass = ORYX.Core.Command.extend({
                        construct: function (option, dockedShape, canAttach, position, facade) {
                            this.option = option;
                            this.docker = null;
                            this.dockedShape = dockedShape;
                            this.dockedShapeParent = dockedShape.parent || facade.getCanvas();
                            this.position = position;
                            this.facade = facade;
                            this.selection = this.facade.getSelection();
                            this.shape = null;
                            this.parent = null;
                            this.canAttach = canAttach;
                        },
                        execute: function () {
                            if (!this.shape) {
                                this.shape = this.facade.createShape(option);
                                this.parent = this.shape.parent;
                            } else if (this.parent) {
                                this.parent.add(this.shape);
                            }

                            if (this.canAttach && this.shape.dockers && this.shape.dockers.length) {
                                this.docker = this.shape.dockers[0];

                                this.dockedShapeParent.add(this.docker.parent);

                                // Set the Docker to the new Shape
                                this.docker.setDockedShape(undefined);
                                this.docker.bounds.centerMoveTo(this.position);
                                if (this.dockedShape !== this.facade.getCanvas()) {
                                    this.docker.setDockedShape(this.dockedShape);
                                }
                                this.facade.setSelection([this.docker.parent]);
                            }

                            this.facade.getCanvas().update();
                            this.facade.updateSelection();

                        },
                        rollback: function () {
                            if (this.shape) {
                                this.facade.setSelection(this.selection.without(this.shape));
                                this.facade.deleteShape(this.shape);
                            }
                            if (this.canAttach && this.docker) {
                                this.docker.setDockedShape(undefined);
                            }
                            this.facade.getCanvas().update();
                            this.facade.updateSelection();

                        }
                    });

                    // Update canvas
                    var command = new commandClass(option, $scope.dragCurrentParent, canAttach, pos, $scope.editor);
                    $scope.editor.executeCommands([command]);

                    // Fire event to all who want to know about this
                    var dropEvent = {
                        type: KISBPM.eventBus.EVENT_TYPE_ITEM_DROPPED,
                        droppedItem: item,
                        position: pos
                    };
                    KISBPM.eventBus.dispatch(dropEvent.type, dropEvent);
                }
            }

            $scope.dragCurrentParent = undefined;
            $scope.dragCurrentParentId = undefined;
            $scope.dragCurrentParentStencil = undefined;
            $scope.dragCanContain = undefined;
            $scope.quickMenu = undefined;
            $scope.dropTargetElement = undefined;
        };


        $scope.overCallback = function (event, ui) {
            $scope.dragModeOver = true;
        };

        $scope.outCallback = function (event, ui) {
            $scope.dragModeOver = false;
        };

        $scope.startDragCallback = function (event, ui) {
            $scope.dragModeOver = false;
            $scope.quickMenu = false;
            if (!ui.helper.hasClass('stencil-item-dragged')) {
                ui.helper.addClass('stencil-item-dragged');
            }
        };

        $scope.startDragCallbackQuickMenu = function (event, ui) {
            $scope.dragModeOver = false;
            $scope.quickMenu = true;
        };

        $scope.dragCallback = function (event, ui) {

            if ($scope.dragModeOver != false) {

                var coord = $scope.editor.eventCoordinatesXY(event.pageX, event.pageY);

                var additionalIEZoom = 1;
                if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                    var ua = navigator.userAgent;
                    if (ua.indexOf('MSIE') >= 0) {
                        //IE 10 and below
                        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
                        if (zoom !== 100) {
                            additionalIEZoom = zoom / 100
                        }
                    }
                }

                if (additionalIEZoom !== 1) {
                    coord.x = coord.x / additionalIEZoom;
                    coord.y = coord.y / additionalIEZoom;
                }

                var aShapes = $scope.editor.getCanvas().getAbstractShapesAtPosition(coord);

                if (aShapes.length <= 0) {
                    if (event.helper) {
                        $scope.dragCanContain = false;
                        return false;
                    }
                }

                if (aShapes[0] instanceof ORYX.Core.Canvas) {
                    $scope.editor.getCanvas().setHightlightStateBasedOnX(coord.x);
                }

                var item = $scope.getStencilItemById(event.target.id);

                if (aShapes.length == 1 && aShapes[0] instanceof ORYX.Core.Canvas) {
                    if (item.id === 'Lane') {
                        return false;
                    }
                    var parentCandidate = aShapes[0];

                    $scope.dragCanContain = true;
                    $scope.dragCurrentParent = parentCandidate;
                    $scope.dragCurrentParentId = parentCandidate.id;

                    $scope.editor.handleEvents({
                        type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                        highlightId: "shapeRepo.attached"
                    });
                    $scope.editor.handleEvents({
                        type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                        highlightId: "shapeRepo.added"
                    });
                    return false;
                }
                else {
                    var parentCandidate = aShapes.reverse().find(function (candidate) {
                        return (candidate instanceof ORYX.Core.Canvas
                            || candidate instanceof ORYX.Core.Node
                            || candidate instanceof ORYX.Core.Edge);
                    });

                    if (!parentCandidate) {
                        $scope.dragCanContain = false;
                        return false;
                    }

                    if (item.type === "node") {

                        // check if the draggable is a boundary event and the parent an Activity
                        var _canContain = false;
                        var parentStencilId = parentCandidate.getStencil().id();

                        if ($scope.dragCurrentParentId && $scope.dragCurrentParentId === parentCandidate.id) {
                            return false;
                        }

                        var parentItem = $scope.getStencilItemById(parentCandidate.getStencil().idWithoutNs());
                        if (parentItem.roles.indexOf("Activity") > -1) {
                            if (item.roles.indexOf("IntermediateEventOnActivityBoundary") > -1) {
                                _canContain = true;
                            }
                        }
                        else if (parentCandidate.getStencil().idWithoutNs() === 'Pool') {
                            if (item.id === 'Lane') {
                                _canContain = true;
                            }
                        }

                        if (_canContain) {
                            $scope.editor.handleEvents({
                                type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
                                highlightId: "shapeRepo.attached",
                                elements: [parentCandidate],
                                style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
                                color: ORYX.CONFIG.SELECTION_VALID_COLOR
                            });

                            $scope.editor.handleEvents({
                                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                                highlightId: "shapeRepo.added"
                            });
                        }
                        else {
                            for (var i = 0; i < $scope.containmentRules.length; i++) {
                                var rule = $scope.containmentRules[i];
                                if (rule.role === parentItem.id) {
                                    for (var j = 0; j < rule.contains.length; j++) {
                                        if (item.roles.indexOf(rule.contains[j]) > -1) {
                                            _canContain = true;
                                            break;
                                        }
                                    }

                                    if (_canContain) {
                                        break;
                                    }
                                }
                            }

                            // Show Highlight
                            $scope.editor.handleEvents({
                                type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
                                highlightId: 'shapeRepo.added',
                                elements: [parentCandidate],
                                color: _canContain ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
                            });

                            $scope.editor.handleEvents({
                                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                                highlightId: "shapeRepo.attached"
                            });
                        }

                        $scope.dragCurrentParent = parentCandidate;
                        $scope.dragCurrentParentId = parentCandidate.id;
                        $scope.dragCurrentParentStencil = parentStencilId;
                        $scope.dragCanContain = _canContain;

                    } else {
                        var canvasCandidate = $scope.editor.getCanvas();
                        var canConnect = false;

                        var targetStencil = $scope.getStencilItemById(parentCandidate.getStencil().idWithoutNs());
                        if (targetStencil) {
                            var associationConnect = false;
                            if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent')) {
                                associationConnect = true;
                            } else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') {
                                associationConnect = true;
                            }

                            if (targetStencil.canConnectTo || associationConnect) {
                                canConnect = true;
                            }
                        }

                        //Edge
                        $scope.dragCurrentParent = canvasCandidate;
                        $scope.dragCurrentParentId = canvasCandidate.id;
                        $scope.dragCurrentParentStencil = canvasCandidate.getStencil().id();
                        $scope.dragCanContain = canConnect;

                        // Show Highlight
                        $scope.editor.handleEvents({
                            type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
                            highlightId: 'shapeRepo.added',
                            elements: [canvasCandidate],
                            color: ORYX.CONFIG.SELECTION_VALID_COLOR
                        });

                        $scope.editor.handleEvents({
                            type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                            highlightId: "shapeRepo.attached"
                        });
                    }
                }
            }
        };

        $scope.dragCallbackQuickMenu = function (event, ui) {

            if ($scope.dragModeOver != false) {
                var coord = $scope.editor.eventCoordinatesXY(event.pageX, event.pageY);

                var additionalIEZoom = 1;
                if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                    var ua = navigator.userAgent;
                    if (ua.indexOf('MSIE') >= 0) {
                        //IE 10 and below
                        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
                        if (zoom !== 100) {
                            additionalIEZoom = zoom / 100
                        }
                    }
                }

                if (additionalIEZoom !== 1) {
                    coord.x = coord.x / additionalIEZoom;
                    coord.y = coord.y / additionalIEZoom;
                }

                var aShapes = $scope.editor.getCanvas().getAbstractShapesAtPosition(coord);

                if (aShapes.length <= 0) {
                    if (event.helper) {
                        $scope.dragCanContain = false;
                        return false;
                    }
                }

                if (aShapes[0] instanceof ORYX.Core.Canvas) {
                    $scope.editor.getCanvas().setHightlightStateBasedOnX(coord.x);
                }

                var stencil = undefined;
                var stencilSets = $scope.editor.getStencilSets().values();
                for (var i = 0; i < stencilSets.length; i++) {
                    var stencilSet = stencilSets[i];
                    var nodes = stencilSet.nodes();
                    for (var j = 0; j < nodes.length; j++) {
                        if (nodes[j].idWithoutNs() === event.target.id) {
                            stencil = nodes[j];
                            break;
                        }
                    }

                    if (!stencil) {
                        var edges = stencilSet.edges();
                        for (var j = 0; j < edges.length; j++) {
                            if (edges[j].idWithoutNs() === event.target.id) {
                                stencil = edges[j];
                                break;
                            }
                        }
                    }
                }

                var candidate = aShapes.last();

                var isValid = false;
                if (stencil.type() === "node") {
                    //check containment rules
                    var canContain = $scope.editor.getRules().canContain({ containingShape: candidate, containedStencil: stencil });

                    var parentCandidate = aShapes.reverse().find(function (candidate) {
                        return (candidate instanceof ORYX.Core.Canvas
                            || candidate instanceof ORYX.Core.Node
                            || candidate instanceof ORYX.Core.Edge);
                    });

                    if (!parentCandidate) {
                        $scope.dragCanContain = false;
                        return false;
                    }

                    $scope.dragCurrentParent = parentCandidate;
                    $scope.dragCurrentParentId = parentCandidate.id;
                    $scope.dragCurrentParentStencil = parentCandidate.getStencil().id();
                    $scope.dragCanContain = canContain;
                    $scope.dropTargetElement = parentCandidate;
                    isValid = canContain;

                } else { //Edge

                    var shapes = $scope.editor.getSelection();
                    if (shapes && shapes.length == 1) {
                        var currentSelectedShape = shapes.first();
                        var curCan = candidate;
                        var canConnect = false;

                        var targetStencil = $scope.getStencilItemById(curCan.getStencil().idWithoutNs());
                        if (targetStencil) {
                            var associationConnect = false;
                            if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent')) {
                                associationConnect = true;
                            }
                            else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') {
                                associationConnect = true;
                            }

                            if (targetStencil.canConnectTo || associationConnect) {
                                while (!canConnect && curCan && !(curCan instanceof ORYX.Core.Canvas)) {
                                    candidate = curCan;
                                    //check connection rules
                                    canConnect = $scope.editor.getRules().canConnect({
                                        sourceShape: currentSelectedShape,
                                        edgeStencil: stencil,
                                        targetShape: curCan
                                    });
                                    curCan = curCan.parent;
                                }
                            }
                        }
                        var parentCandidate = $scope.editor.getCanvas();

                        isValid = canConnect;
                        $scope.dragCurrentParent = parentCandidate;
                        $scope.dragCurrentParentId = parentCandidate.id;
                        $scope.dragCurrentParentStencil = parentCandidate.getStencil().id();
                        $scope.dragCanContain = canConnect;
                        $scope.dropTargetElement = candidate;
                    }

                }

                $scope.editor.handleEvents({
                    type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
                    highlightId: 'shapeMenu',
                    elements: [candidate],
                    color: isValid ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
                });
            }
        };

    }]);

var KISBPM = KISBPM || {};
//create command for undo/redo
KISBPM.CreateCommand = ORYX.Core.Command.extend({
    construct: function (option, currentReference, position, facade) {
        this.option = option;
        this.currentReference = currentReference;
        this.position = position;
        this.facade = facade;
        this.shape;
        this.edge;
        this.targetRefPos;
        this.sourceRefPos;
		/*
		 * clone options parameters
		 */
        this.connectedShape = option.connectedShape;
        this.connectingType = option.connectingType;
        this.namespace = option.namespace;
        this.type = option.type;
        this.containedStencil = option.containedStencil;
        this.parent = option.parent;
        this.currentReference = currentReference;
        this.shapeOptions = option.shapeOptions;
    },
    execute: function () {

        if (this.shape) {
            if (this.shape instanceof ORYX.Core.Node) {
                this.parent.add(this.shape);
                if (this.edge) {
                    this.facade.getCanvas().add(this.edge);
                    this.edge.dockers.first().setDockedShape(this.connectedShape);
                    this.edge.dockers.first().setReferencePoint(this.sourceRefPos);
                    this.edge.dockers.last().setDockedShape(this.shape);
                    this.edge.dockers.last().setReferencePoint(this.targetRefPos);
                }

                this.facade.setSelection([this.shape]);

            } else if (this.shape instanceof ORYX.Core.Edge) {
                this.facade.getCanvas().add(this.shape);
                this.shape.dockers.first().setDockedShape(this.connectedShape);
                this.shape.dockers.first().setReferencePoint(this.sourceRefPos);
            }
        }
        else {
            this.shape = this.facade.createShape(this.option);
            this.edge = (!(this.shape instanceof ORYX.Core.Edge)) ? this.shape.getIncomingShapes().first() : undefined;
        }

        if (this.currentReference && this.position) {

            if (this.shape instanceof ORYX.Core.Edge) {

                if (!(this.currentReference instanceof ORYX.Core.Canvas)) {
                    this.shape.dockers.last().setDockedShape(this.currentReference);

                    if (this.currentReference.getStencil().idWithoutNs() === 'TextAnnotation') {
                        var midpoint = {};
                        midpoint.x = 0;
                        midpoint.y = this.currentReference.bounds.height() / 2;
                        this.shape.dockers.last().setReferencePoint(midpoint);
                    }
                    else {
                        this.shape.dockers.last().setReferencePoint(this.currentReference.bounds.midPoint());
                    }
                }
                else {
                    this.shape.dockers.last().bounds.centerMoveTo(this.position);
                }
                this.sourceRefPos = this.shape.dockers.first().referencePoint;
                this.targetRefPos = this.shape.dockers.last().referencePoint;

            } else if (this.edge) {
                this.sourceRefPos = this.edge.dockers.first().referencePoint;
                this.targetRefPos = this.edge.dockers.last().referencePoint;
            }
        } else {
            var containedStencil = this.containedStencil;
            var connectedShape = this.connectedShape;
            var bc = connectedShape.bounds;
            var bs = this.shape.bounds;

            var pos = bc.center();
            if (containedStencil.defaultAlign() === "north") {
                pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height() / 2);
            } else if (containedStencil.defaultAlign() === "northeast") {
                pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width() / 2);
                pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height() / 2);
            } else if (containedStencil.defaultAlign() === "southeast") {
                pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width() / 2);
                pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height() / 2);
            } else if (containedStencil.defaultAlign() === "south") {
                pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height() / 2);
            } else if (containedStencil.defaultAlign() === "southwest") {
                pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width() / 2);
                pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height() / 2);
            } else if (containedStencil.defaultAlign() === "west") {
                pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width() / 2);
            } else if (containedStencil.defaultAlign() === "northwest") {
                pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width() / 2);
                pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height() / 2);
            } else {
                pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width() / 2);
            }

            // Move shape to the new position
            this.shape.bounds.centerMoveTo(pos);

            // Move all dockers of a node to the position
            if (this.shape instanceof ORYX.Core.Node) {
                (this.shape.dockers || []).each(function (docker) {
                    docker.bounds.centerMoveTo(pos);
                });
            }

            //this.shape.update();
            this.position = pos;

            if (this.edge) {
                this.sourceRefPos = this.edge.dockers.first().referencePoint;
                this.targetRefPos = this.edge.dockers.last().referencePoint;
            }
        }

        this.facade.getCanvas().update();
        this.facade.updateSelection();

    },
    rollback: function () {
        this.facade.deleteShape(this.shape);
        if (this.edge) {
            this.facade.deleteShape(this.edge);
        }
        //this.currentParent.update();
        this.facade.setSelection(this.facade.getSelection().without(this.shape, this.edge));
    }
});
