diff options
Diffstat (limited to 'ecomp-portal-FE/client/bower_components/jqTree/src/drag_and_drop_handler.coffee')
-rw-r--r-- | ecomp-portal-FE/client/bower_components/jqTree/src/drag_and_drop_handler.coffee | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/ecomp-portal-FE/client/bower_components/jqTree/src/drag_and_drop_handler.coffee b/ecomp-portal-FE/client/bower_components/jqTree/src/drag_and_drop_handler.coffee new file mode 100644 index 00000000..38b1d521 --- /dev/null +++ b/ecomp-portal-FE/client/bower_components/jqTree/src/drag_and_drop_handler.coffee @@ -0,0 +1,491 @@ +node_module = require './node' +util = require './util' + +Position = node_module.Position + +$ = jQuery + + +class DragAndDropHandler + constructor: (tree_widget) -> + @tree_widget = tree_widget + + @hovered_area = null + @$ghost = null + @hit_areas = [] + @is_dragging = false + @current_item = null + + mouseCapture: (position_info) -> + $element = $(position_info.target) + + if not @mustCaptureElement($element) + return null + + if @tree_widget.options.onIsMoveHandle and not @tree_widget.options.onIsMoveHandle($element) + return null + + node_element = @tree_widget._getNodeElement($element) + + if node_element and @tree_widget.options.onCanMove + if not @tree_widget.options.onCanMove(node_element.node) + node_element = null + + @current_item = node_element + return (@current_item != null) + + mouseStart: (position_info) -> + @refresh() + + offset = $(position_info.target).offset() + + node = @current_item.node + + if @tree_widget.options.autoEscape + node_name = util.html_escape(node.name) + else + node_name = node.name + + @drag_element = new DragElement( + node_name, + position_info.page_x - offset.left, + position_info.page_y - offset.top, + @tree_widget.element + ) + + @is_dragging = true + @current_item.$element.addClass('jqtree-moving') + return true + + mouseDrag: (position_info) -> + @drag_element.move(position_info.page_x, position_info.page_y) + + area = @findHoveredArea(position_info.page_x, position_info.page_y) + can_move_to = @canMoveToArea(area) + + if can_move_to and area + if !area.node.isFolder() + @stopOpenFolderTimer() + + if @hovered_area != area + @hovered_area = area + + # If this is a closed folder, start timer to open it + if @mustOpenFolderTimer(area) + @startOpenFolderTimer(area.node) + else + @stopOpenFolderTimer() + + @updateDropHint() + else + @removeHover() + @removeDropHint() + @stopOpenFolderTimer() + + if not area + if @tree_widget.options.onDragMove? + @tree_widget.options.onDragMove(@current_item.node, position_info.original_event) + + return true + + mustCaptureElement: ($element) -> + return not $element.is('input,select,textarea') + + canMoveToArea: (area) -> + if not area + return false + else if @tree_widget.options.onCanMoveTo + position_name = Position.getName(area.position) + + return @tree_widget.options.onCanMoveTo(@current_item.node, area.node, position_name) + else + return true + + mouseStop: (position_info) -> + @moveItem(position_info) + @clear() + @removeHover() + @removeDropHint() + @removeHitAreas() + + current_item = @current_item + + if @current_item + @current_item.$element.removeClass('jqtree-moving') + @current_item = null + + @is_dragging = false + + if not @hovered_area and current_item + if @tree_widget.options.onDragStop? + @tree_widget.options.onDragStop(current_item.node, position_info.original_event) + + return false + + refresh: -> + @removeHitAreas() + + if @current_item + @generateHitAreas() + + @current_item = @tree_widget._getNodeElementForNode(@current_item.node) + + if @is_dragging + @current_item.$element.addClass('jqtree-moving') + + removeHitAreas: -> + @hit_areas = [] + + clear: -> + @drag_element.remove() + @drag_element = null + + removeDropHint: -> + if @previous_ghost + @previous_ghost.remove() + + removeHover: -> + @hovered_area = null + + generateHitAreas: -> + hit_areas_generator = new HitAreasGenerator( + @tree_widget.tree, + @current_item.node, + @getTreeDimensions().bottom + ) + @hit_areas = hit_areas_generator.generate() + + findHoveredArea: (x, y) -> + dimensions = @getTreeDimensions() + + if ( + x < dimensions.left or + y < dimensions.top or + x > dimensions.right or + y > dimensions.bottom + ) + return null + + low = 0 + high = @hit_areas.length + while (low < high) + mid = (low + high) >> 1 + area = @hit_areas[mid] + + if y < area.top + high = mid + else if y > area.bottom + low = mid + 1 + else + return area + + return null + + mustOpenFolderTimer: (area) -> + node = area.node + + return ( + node.isFolder() and + not node.is_open and + area.position == Position.INSIDE + ) + + updateDropHint: -> + if not @hovered_area + return + + # remove previous drop hint + @removeDropHint() + + # add new drop hint + node_element = @tree_widget._getNodeElementForNode(@hovered_area.node) + @previous_ghost = node_element.addDropHint(@hovered_area.position) + + startOpenFolderTimer: (folder) -> + openFolder = => + @tree_widget._openNode( + folder, + @tree_widget.options.slide, + => + @refresh() + @updateDropHint() + ) + + @stopOpenFolderTimer() + + @open_folder_timer = setTimeout(openFolder, @tree_widget.options.openFolderDelay) + + stopOpenFolderTimer: -> + if @open_folder_timer + clearTimeout(@open_folder_timer) + @open_folder_timer = null + + moveItem: (position_info) -> + if ( + @hovered_area and + @hovered_area.position != Position.NONE and + @canMoveToArea(@hovered_area) + ) + moved_node = @current_item.node + target_node = @hovered_area.node + position = @hovered_area.position + previous_parent = moved_node.parent + + if position == Position.INSIDE + @hovered_area.node.is_open = true + + doMove = => + @tree_widget.tree.moveNode(moved_node, target_node, position) + @tree_widget.element.empty() + @tree_widget._refreshElements() + + event = @tree_widget._triggerEvent( + 'tree.move', + move_info: + moved_node: moved_node + target_node: target_node + position: Position.getName(position) + previous_parent: previous_parent + do_move: doMove + original_event: position_info.original_event + ) + + doMove() unless event.isDefaultPrevented() + + getTreeDimensions: -> + # Return the dimensions of the tree. Add a margin to the bottom to allow + # for some to drag-and-drop the last element. + offset = @tree_widget.element.offset() + + return { + left: offset.left, + top: offset.top, + right: offset.left + @tree_widget.element.width(), + bottom: offset.top + @tree_widget.element.height() + 16 + } + + +class VisibleNodeIterator + constructor: (tree) -> + @tree = tree + + iterate: -> + is_first_node = true + + _iterateNode = (node, next_node) => + must_iterate_inside = ( + (node.is_open or not node.element) and node.hasChildren() + ) + + if node.element + $element = $(node.element) + + if not $element.is(':visible') + return + + if is_first_node + @handleFirstNode(node, $element) + is_first_node = false + + if not node.hasChildren() + @handleNode(node, next_node, $element) + else if node.is_open + if not @handleOpenFolder(node, $element) + must_iterate_inside = false + else + @handleClosedFolder(node, next_node, $element) + + if must_iterate_inside + children_length = node.children.length + for child, i in node.children + if i == (children_length - 1) + _iterateNode(node.children[i], null) + else + _iterateNode(node.children[i], node.children[i+1]) + + if node.is_open + @handleAfterOpenFolder(node, next_node, $element) + + _iterateNode(@tree, null) + + handleNode: (node, next_node, $element) -> + # override + + handleOpenFolder: (node, $element) -> + # override + # return + # - true: continue iterating + # - false: stop iterating + + handleClosedFolder: (node, next_node, $element) -> + # override + + handleAfterOpenFolder: (node, next_node, $element) -> + # override + + handleFirstNode: (node, $element) -> + # override + + +class HitAreasGenerator extends VisibleNodeIterator + constructor: (tree, current_node, tree_bottom) -> + super(tree) + + @current_node = current_node + @tree_bottom = tree_bottom + + generate: -> + @positions = [] + @last_top = 0 + + @iterate() + + return @generateHitAreas(@positions) + + getTop: ($element) -> + return $element.offset().top + + addPosition: (node, position, top) -> + area = { + top: top + node: node + position: position + } + + @positions.push(area) + @last_top = top + + handleNode: (node, next_node, $element) -> + top = @getTop($element) + + if node == @current_node + # Cannot move inside current item + @addPosition(node, Position.NONE, top) + else + @addPosition(node, Position.INSIDE, top) + + if ( + next_node == @current_node or + node == @current_node + ) + # Cannot move before or after current item + @addPosition(node, Position.NONE, top) + else + @addPosition(node, Position.AFTER, top) + + handleOpenFolder: (node, $element) -> + if node == @current_node + # Cannot move inside current item + # Stop iterating + return false + + # Cannot move before current item + if node.children[0] != @current_node + @addPosition(node, Position.INSIDE, @getTop($element)) + + # Continue iterating + return true + + handleClosedFolder: (node, next_node, $element) -> + top = @getTop($element) + + if node == @current_node + # Cannot move after current item + @addPosition(node, Position.NONE, top) + else + @addPosition(node, Position.INSIDE, top) + + # Cannot move before current item + if next_node != @current_node + @addPosition(node, Position.AFTER, top) + + handleFirstNode: (node, $element) -> + if node != @current_node + @addPosition(node, Position.BEFORE, @getTop($(node.element))) + + handleAfterOpenFolder: (node, next_node, $element) -> + if ( + node == @current_node.node or + next_node == @current_node.node + ) + # Cannot move before or after current item + @addPosition(node, Position.NONE, @last_top) + else + @addPosition(node, Position.AFTER, @last_top) + + generateHitAreas: (positions) -> + previous_top = -1 + group = [] + hit_areas = [] + + for position in positions + if position.top != previous_top and group.length + if group.length + @generateHitAreasForGroup( + hit_areas, + group, + previous_top, + position.top + ) + + previous_top = position.top + group = [] + + group.push(position) + + @generateHitAreasForGroup( + hit_areas, + group, + previous_top, + @tree_bottom + ) + + return hit_areas + + generateHitAreasForGroup: (hit_areas, positions_in_group, top, bottom) -> + # limit positions in group + position_count = Math.min(positions_in_group.length, 4) + + area_height = Math.round((bottom - top) / position_count) + area_top = top + + i = 0 + while (i < position_count) + position = positions_in_group[i] + + hit_areas.push( + top: area_top, + bottom: area_top + area_height, + node: position.node, + position: position.position + ) + + area_top += area_height + i += 1 + + return null + + +class DragElement + constructor: (node_name, offset_x, offset_y, $tree) -> + @offset_x = offset_x + @offset_y = offset_y + + @$element = $("<span class=\"jqtree-title jqtree-dragging\">#{ node_name }</span>") + @$element.css("position", "absolute") + $tree.append(@$element) + + move: (page_x, page_y) -> + @$element.offset( + left: page_x - @offset_x, + top: page_y - @offset_y + ) + + remove: -> + @$element.remove() + + +module.exports = + DragAndDropHandler: DragAndDropHandler + DragElement: DragElement + HitAreasGenerator: HitAreasGenerator |