Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
jsarnowski/oxygen / angular / controllers / controller.drag-n-drop.js
Size: Mime:
CTFrontendBuilderUI.controller("ControllerDragnDrop", function($scope, $timeout, dragulaService) {

	var dragBottomBubble = null,
        expandTimeout = null,
        setExpandTrigger = 0;
    
    /**
     * Set options
     */

    var navHash;

    var hashCode = function(value) {
        var hash = 0;
        if (value.length == 0) return hash;
        for (i = 0; i < value.length; i++) {
            char = value.charCodeAt(i);
            hash = ((hash<<5)-hash)+char;
            hash = hash & hash; // Convert to 32bit integer
        }
        return hash;
    } 

    $scope.$parent.$on('iframe-init', function(e, iframeScope) {

        if (CtBuilderAjax.userCanFullAccess!="true"&&CtBuilderAjax.userCanDragNDrop!="true") {
            return;
        }

        dragulaService.options($scope.iframeScope, 'ct-dom-tree', {
            
            isContainer: function (el) {
                
                //console.log('isContainer', el, dragBottomBubble);
                if ( el.classList.contains('ct-accept-drops') && dragBottomBubble ) {
                    return true;
                }
                else if ( el.classList.contains('ct-draggable') ) {
                    dragBottomBubble = true;
                    return false;
                }
                else {
                    return false;
                }

            },

            moves: function(el, ctn, target) {

                //console.log(el, ctn, target);
                
                dragBottomBubble = null;

                // don't let the div inside a repeater be moved around
                if(ctn.classList.contains('ct-sortable-dynamic-list') && el.classList.contains('ct-sortable-div-block')) {
                    return false;
                }

                // don't let drag out the last header row from header
                if (ctn.classList.contains('ct-sortable-header')&&jQuery(ctn).children().length < 5)
                    return false;

                // jQuery closest is used to make sure that clicking on any child element of the .ct-handle such as text, icons initiates the drag as well
                return target.classList.contains('ct-draggable') || jQuery(target).closest('.ct-handle').length > 0; 

            },

            accepts: function (el, target, source, sibling) {

                //console.log(el);
                
                var elId = parseInt(el.getAttribute('ng-attr-tree-id'));

                // collapse the child elements hierarchy
                // if($scope.iframeScope.toggledNodes[elId])
                //     $scope.iframeScope.toggledNodes[elId] = false;


                // if the target is no more an expandable one, clear the expand timeout
                if(expandTimeout && parseInt(target.getAttribute('ng-attr-tree-id')) > 0 && parseInt(target.getAttribute('ng-attr-tree-id')) !== setExpandTrigger) {
                    clearTimeout(expandTimeout);
                    expandTimeout = false;
                    setExpandTrigger = 0;

                }

                // Here decision is made to accept the drop inside a nestable container as a child or a sibling
                if(target.classList.contains('ct-accept-drops') && parseInt(target.getAttribute('ng-attr-tree-id')) > 0) {
                    
                    var targetId = parseInt(target.getAttribute('ng-attr-tree-id'));
                    var targetAnchor = jQuery('#ct-dom-tree-node-'+targetId+' >.ct-dom-tree-node-anchor');
                    
                    // if the target node is not expanded, expand it.
                    if(setExpandTrigger === 0 && !$scope.iframeScope.toggledNodes[targetId] && targetId !== parseInt(el.getAttribute('ng-attr-tree-id'))) {
                        
                        setExpandTrigger = targetId;

                        expandTimeout = setTimeout(function() {
                                targetAnchor.find('.ct-expand-butt').trigger('click');
                                setExpandTrigger = 0;
                            }, 1000);

                    }

                    // test if the drop is intended to be a child
                    var targetX = jQuery(target).offset().left;
                    var ghostX = jQuery('.gu-mirror').offset().left;
                    if(ghostX - targetX < 16)
                        return false;
                }

                //console.log(el, target, source, sibling);
                //console.log(angular.element(target).closest('.ct-sortable-section').length);
                
                // don't allow to insert before anchor
                if( sibling && sibling.classList.contains('ct-dom-tree-node-anchor') ) {
                    return false;
                }

                // don't allow to insert 'li' to any components but 'ul'
                if( el.classList.contains('ct-sortable-li') && !target.classList.contains('ct-sortable-ul') ) {
                    return false;
                }

                // do not allow to insert anything under a repeater directly unless its a div 
                if( target.classList.contains('ct-sortable-dynamic-list') && !el.classList.contains('ct-sortable-div-block')) {
                    return false;
                }

                // do not allow to insert a second element directly under the repeater
                if( target.classList.contains('ct-sortable-dynamic-list') && el.classList.contains('ct-sortable-div-block')) {
                    var targetId = parseInt(target.getAttribute('ng-attr-tree-id'));
                    var element = iframeScope.getComponentById(targetId);
                    if(element.length > 0 && element.children().length > 0) {
                        return false;
                    }
                }

                // don't allow to insert any components to 'ul' but 'li'
                if( !el.classList.contains('ct-sortable-li') && target.classList.contains('ct-sortable-ul') ) {
                    return false;
                }

                // don't allow to insert 'slide' to any components but 'slider'
                if( el.classList.contains('ct-sortable-slide') && !target.classList.contains('ct-sortable-slider') ) {
                    return false;
                }

                // don't allow to insert any components to 'slider' but 'slide'
                if( !el.classList.contains('ct-sortable-slide') && target.classList.contains('ct-sortable-slider') ) {
                    return false;
                }
                
                // don't allow to insert 'column' to any components but 'columns'
                if( el.classList.contains('ct-sortable-column') && !target.classList.contains('ct-sortable-columns') ) {
                    return false;
                }

                // don't allow to insert any components to 'columns' but 'column'
                if( !el.classList.contains('ct-sortable-column') && target.classList.contains('ct-sortable-columns') ) {
                    return false;
                }

                // don't allow to insert any components to 'new columns' but 'div'
                if( !el.classList.contains('ct-sortable-div-block') && target.classList.contains('ct-sortable-new-columns') ) {
                    return false;
                }

                // don't allow to insert any components to 'tabs' but 'tab'
                if( !el.classList.contains('ct-sortable-tab') && target.classList.contains('ct-sortable-tabs') ) {
                    return false;
                }

                // don't allow to insert any components to and 'tabs_contents' but 'tab_content'
                if( !el.classList.contains('ct-sortable-tab-content') && target.classList.contains('ct-sortable-tabs-contents') ) {
                    return false;
                }

                // don't allow to insert 'tabs' components to 'tabs'
                if( (el.classList.contains('ct-sortable-tabs')||el.classList.contains('ct-sortable-tabs-contents')) && 
                    (jQuery(target).parents('.ct-sortable-tabs').length||jQuery(target).parents('.ct-sortable-tabs-contents').length) ) {
                    return false;
                }

                // don't allow to insert 'tab' component to anything but 'tabs'
                if( el.classList.contains('ct-sortable-tab') && !target.classList.contains('ct-sortable-tabs') ) {
                    return false;
                }

                // don't allow to insert 'tab-content' component to anything but 'tabs-contents'
                if( el.classList.contains('ct-sortable-tab-content') && !target.classList.contains('ct-sortable-tabs-contents') ) {
                    return false;
                }

                // don't allow to insert 'grid-cell' to any components but 'grid'
                if( el.classList.contains('ct-sortable-grid-cell') && !target.classList.contains('ct-sortable-grid') ) {
                    return false;
                }

                // don't allow to insert any components to 'grid' but 'grid-cell'
                if( !el.classList.contains('ct-sortable-grid-cell') && target.classList.contains('ct-sortable-grid') ) {
                    return false;
                }

                // don't allow to insert 'section' inside any other 'section'
                if( el.classList.contains('ct-sortable-section') && angular.element(target).closest('.ct-sortable-section').length > 0 ) {
                    return false;
                }

                // don't allow to insert 'section' inside 'header builder'
                if( el.classList.contains('ct-sortable-section') && angular.element(target).closest('.ct-sortable-header').length > 0 ) {
                    return false;
                }

                // don't allow to insert 'section' inside 'slider'
                if( el.classList.contains('ct-sortable-section') && angular.element(target).closest('.ct-sortable-slider').length > 0 ) {
                    return false;
                }

                // don't allow to insert 'header builder' inside any other 'section'
                if( el.classList.contains('ct-sortable-header') && angular.element(target).closest('.ct-sortable-section').length > 0 ) {
                    return false;
                }

                // don't allow to insert 'header builder' inside 'header builder'
                if( el.classList.contains('ct-sortable-header') && angular.element(target).closest('.ct-sortable-header').length > 0 ) {
                    return false;
                }

                // don't allow to insert 'link wrapper' inside any other 'link wrapper'
                if( el.classList.contains('ct-sortable-link') && angular.element(target).closest('.ct-sortable-link').length > 0 ) {
                    return false;
                }

                // don't allow to insert 'text link' inside any other 'link wrapper'
                if( el.classList.contains('ct-sortable-link-text') && angular.element(target).closest('.ct-sortable-link').length > 0 ) {
                    return false;
                }

                if( target.classList.contains('gu-transit') || !target.classList.contains('ct-accept-drops')) {
                    return false;
                }

                // don't allow to insert any components to 'header' but 'header row'
                if( !el.classList.contains('ct-sortable-header-row') && target.classList.contains('ct-sortable-header') ) {
                    return false;
                }

                // don't allow to insert 'header_row' to any component but 'header'
                if( el.classList.contains('ct-sortable-header-row') && !target.classList.contains('ct-sortable-header') ) {
                    return false;
                }

                // check API components for any parent/child restrictions
                var elName = jQuery(el).attr('data-component-name'),
                    targetName = jQuery(target).attr('data-component-name');
                if (!$scope.iframeScope.canBeChild(targetName, elName)) {
                    return false;
                }

                // don't allow to insert any component inside itself at any deep
                if( angular.element(target).closest('[data-component-name="'+elName+'"]').length > 0 ) {
                    // any other components that can be inserted to itself?
                    // allow to extend this list with API components?
                    var exceptions = ['ct_div_block'];
                    if (exceptions.indexOf(elName) < 0 ) {
                        return false;
                    }
                }

                var componentId         = jQuery(el).attr("ng-attr-tree-id"),
                    component           = $scope.iframeScope.getComponentById(componentId),
                    targetComponentId   = jQuery(target).attr("ng-attr-tree-id"),
                    targetComponent     = $scope.iframeScope.getComponentById(targetComponentId);

                // don't allow to insert any component with 'section' inside any other component inside 'section' except the Inner Content
                if( !jQuery(component).is(".ct-inner-content") && jQuery(component).find(".ct-section").length > 0 && jQuery(targetComponent).closest(".ct-section").length > 0 ) {
                   return false;
                }

                // don't allow to insert any component with 'link wraper/text link' inside any other component inside 'link wrapper'
                if( jQuery(component).find(".ct-link").length > 0 && jQuery(targetComponent).closest(".ct-link").length > 0 ) {
                   return false;
                }
                if( jQuery(component).find(".ct-link-text").length > 0 && jQuery(targetComponent).closest(".ct-link").length > 0 ) {
                   return false;
                }

                // don't allow to insert modals inside any other modal
                if( el.classList.contains('ct-sortable-modal') && angular.element(target).closest('.ct-sortable-modal').length > 0 ) {
                    return false;
                }
                
                return true;
            },
            revertOnSpill: true,
            mirrorContainer: document.body

        });


        /**
         * Drop Event
         */
        
        $scope.iframeScope.$on('ct-dom-tree.drop', function (e, el, endParent, startParent) {

            // fix offset
        	var newKey          = el.index() - 3,
                startParentId   = startParent[0].attributes['ng-attr-tree-id'].value,
                endParentId     = endParent[0].attributes['ng-attr-tree-id'].value;

            // save to prevent toggle DOM node on drag&drop
            $scope.iframeScope.latestDroppedDOMParent = endParentId;

            // make changes to Components Tree
            $scope.iframeScope.componentsReorder(el, newKey, startParentId, endParentId, startParent, endParent);

            
            // if the receiving item is an oxy list or has an ancestor that is an oxy list, rebuild the whole oxy list
            var component = $scope.iframeScope.getComponentById(endParentId);
            var oxyList = component.closest('.oxy-dynamic-list');
            if(oxyList.length > 0) {
                $scope.iframeScope.updateRepeaterQuery(parseInt(oxyList.attr('ng-attr-component-id')))
            }
            
            // expand new parent after drop
            jQuery(endParent).removeClass("ct-dom-tree-no-children");

            // collapse old parent if no children left
            if ( jQuery(startParent).children().length <= 3 ) { // there is always 3 children: anchor, dashed vertical and horizontla
                jQuery(startParent).addClass("ct-dom-tree-no-children");
            }

            $scope.iframeScope.updateDOMTreeNavigator(el[0].attributes['ng-attr-tree-id'].value);

            $scope.iframeScope.adjustResizeBox();

        });

        $scope.iframeScope.$on('ct-dom-tree.drag', function(e, item, source) {
            var rootID = 0;
            // set innner content id as root
            if ($scope.iframeScope.innerContentRoot&&$scope.iframeScope.innerContentRoot.id) {
                rootID = $scope.iframeScope.innerContentRoot.id;
            }
            navHash = hashCode(angular.element('div#ct-dom-tree-node-'+rootID).html());
        });

        $scope.iframeScope.$on('ct-dom-tree.cancel', function(e, item, source) {
            var id = item[0].attributes['ng-attr-tree-id'].value;
            var parentId = angular.element(item).parent().closest('.ct-dom-tree-node').attr('ng-attr-tree-id');

            var rootID = 0;
            // set innner content id as root
            if ($scope.iframeScope.innerContentRoot&&$scope.iframeScope.innerContentRoot.id) {
                rootID = $scope.iframeScope.innerContentRoot.id;
            }

            if(navHash !== hashCode(jQuery('div#ct-dom-tree-node-'+rootID).html())) {
                if(parentId > 0)
                    $scope.iframeScope.updateDOMTreeNavigator(parentId);
                else
                    $scope.iframeScope.updateDOMTreeNavigator();
            }

            // var item = $scope.iframeScope.findComponentItem($scope.iframeScope.componentsTree.children, id, $scope.iframeScope.getComponentItem);

            // if(parentId !== item.options['ct_parent']) {

            //     if(item.options['ct_parent'] > 0)
            //         $scope.iframeScope.updateDOMTreeNavigator(item.options['ct_parent']);
            //     else
            //         $scope.iframeScope.updateDOMTreeNavigator();

            // }
        });


        /**
         * Drag end event
         */

        $scope.iframeScope.$on('ct-dom-tree.dragend', function (e, el) {
            
            dragBottomBubble = null;
        });

    });

})