1 var $, DragAndDropHandler, DragElement, HitAreasGenerator, Position, VisibleNodeIterator, node_module, util,
2 extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
3 hasProp = {}.hasOwnProperty;
5 node_module = require('./node');
7 util = require('./util');
9 Position = node_module.Position;
13 DragAndDropHandler = (function() {
14 function DragAndDropHandler(tree_widget) {
15 this.tree_widget = tree_widget;
16 this.hovered_area = null;
19 this.is_dragging = false;
20 this.current_item = null;
23 DragAndDropHandler.prototype.mouseCapture = function(position_info) {
24 var $element, node_element;
25 $element = $(position_info.target);
26 if (!this.mustCaptureElement($element)) {
29 if (this.tree_widget.options.onIsMoveHandle && !this.tree_widget.options.onIsMoveHandle($element)) {
32 node_element = this.tree_widget._getNodeElement($element);
33 if (node_element && this.tree_widget.options.onCanMove) {
34 if (!this.tree_widget.options.onCanMove(node_element.node)) {
38 this.current_item = node_element;
39 return this.current_item !== null;
42 DragAndDropHandler.prototype.mouseStart = function(position_info) {
43 var node, node_name, offset;
45 offset = $(position_info.target).offset();
46 node = this.current_item.node;
47 if (this.tree_widget.options.autoEscape) {
48 node_name = util.html_escape(node.name);
50 node_name = node.name;
52 this.drag_element = new DragElement(node_name, position_info.page_x - offset.left, position_info.page_y - offset.top, this.tree_widget.element);
53 this.is_dragging = true;
54 this.current_item.$element.addClass('jqtree-moving');
58 DragAndDropHandler.prototype.mouseDrag = function(position_info) {
59 var area, can_move_to;
60 this.drag_element.move(position_info.page_x, position_info.page_y);
61 area = this.findHoveredArea(position_info.page_x, position_info.page_y);
62 can_move_to = this.canMoveToArea(area);
63 if (can_move_to && area) {
64 if (!area.node.isFolder()) {
65 this.stopOpenFolderTimer();
67 if (this.hovered_area !== area) {
68 this.hovered_area = area;
69 if (this.mustOpenFolderTimer(area)) {
70 this.startOpenFolderTimer(area.node);
72 this.stopOpenFolderTimer();
74 this.updateDropHint();
78 this.removeDropHint();
79 this.stopOpenFolderTimer();
82 if (this.tree_widget.options.onDragMove != null) {
83 this.tree_widget.options.onDragMove(this.current_item.node, position_info.original_event);
89 DragAndDropHandler.prototype.mustCaptureElement = function($element) {
90 return !$element.is('input,select,textarea');
93 DragAndDropHandler.prototype.canMoveToArea = function(area) {
97 } else if (this.tree_widget.options.onCanMoveTo) {
98 position_name = Position.getName(area.position);
99 return this.tree_widget.options.onCanMoveTo(this.current_item.node, area.node, position_name);
105 DragAndDropHandler.prototype.mouseStop = function(position_info) {
107 this.moveItem(position_info);
110 this.removeDropHint();
111 this.removeHitAreas();
112 current_item = this.current_item;
113 if (this.current_item) {
114 this.current_item.$element.removeClass('jqtree-moving');
115 this.current_item = null;
117 this.is_dragging = false;
118 if (!this.hovered_area && current_item) {
119 if (this.tree_widget.options.onDragStop != null) {
120 this.tree_widget.options.onDragStop(current_item.node, position_info.original_event);
126 DragAndDropHandler.prototype.refresh = function() {
127 this.removeHitAreas();
128 if (this.current_item) {
129 this.generateHitAreas();
130 this.current_item = this.tree_widget._getNodeElementForNode(this.current_item.node);
131 if (this.is_dragging) {
132 return this.current_item.$element.addClass('jqtree-moving');
137 DragAndDropHandler.prototype.removeHitAreas = function() {
138 return this.hit_areas = [];
141 DragAndDropHandler.prototype.clear = function() {
142 this.drag_element.remove();
143 return this.drag_element = null;
146 DragAndDropHandler.prototype.removeDropHint = function() {
147 if (this.previous_ghost) {
148 return this.previous_ghost.remove();
152 DragAndDropHandler.prototype.removeHover = function() {
153 return this.hovered_area = null;
156 DragAndDropHandler.prototype.generateHitAreas = function() {
157 var hit_areas_generator;
158 hit_areas_generator = new HitAreasGenerator(this.tree_widget.tree, this.current_item.node, this.getTreeDimensions().bottom);
159 return this.hit_areas = hit_areas_generator.generate();
162 DragAndDropHandler.prototype.findHoveredArea = function(x, y) {
163 var area, dimensions, high, low, mid;
164 dimensions = this.getTreeDimensions();
165 if (x < dimensions.left || y < dimensions.top || x > dimensions.right || y > dimensions.bottom) {
169 high = this.hit_areas.length;
171 mid = (low + high) >> 1;
172 area = this.hit_areas[mid];
175 } else if (y > area.bottom) {
184 DragAndDropHandler.prototype.mustOpenFolderTimer = function(area) {
187 return node.isFolder() && !node.is_open && area.position === Position.INSIDE;
190 DragAndDropHandler.prototype.updateDropHint = function() {
192 if (!this.hovered_area) {
195 this.removeDropHint();
196 node_element = this.tree_widget._getNodeElementForNode(this.hovered_area.node);
197 return this.previous_ghost = node_element.addDropHint(this.hovered_area.position);
200 DragAndDropHandler.prototype.startOpenFolderTimer = function(folder) {
202 openFolder = (function(_this) {
204 return _this.tree_widget._openNode(folder, _this.tree_widget.options.slide, function() {
206 return _this.updateDropHint();
210 this.stopOpenFolderTimer();
211 return this.open_folder_timer = setTimeout(openFolder, this.tree_widget.options.openFolderDelay);
214 DragAndDropHandler.prototype.stopOpenFolderTimer = function() {
215 if (this.open_folder_timer) {
216 clearTimeout(this.open_folder_timer);
217 return this.open_folder_timer = null;
221 DragAndDropHandler.prototype.moveItem = function(position_info) {
222 var doMove, event, moved_node, position, previous_parent, target_node;
223 if (this.hovered_area && this.hovered_area.position !== Position.NONE && this.canMoveToArea(this.hovered_area)) {
224 moved_node = this.current_item.node;
225 target_node = this.hovered_area.node;
226 position = this.hovered_area.position;
227 previous_parent = moved_node.parent;
228 if (position === Position.INSIDE) {
229 this.hovered_area.node.is_open = true;
231 doMove = (function(_this) {
233 _this.tree_widget.tree.moveNode(moved_node, target_node, position);
234 _this.tree_widget.element.empty();
235 return _this.tree_widget._refreshElements();
238 event = this.tree_widget._triggerEvent('tree.move', {
240 moved_node: moved_node,
241 target_node: target_node,
242 position: Position.getName(position),
243 previous_parent: previous_parent,
245 original_event: position_info.original_event
248 if (!event.isDefaultPrevented()) {
254 DragAndDropHandler.prototype.getTreeDimensions = function() {
256 offset = this.tree_widget.element.offset();
260 right: offset.left + this.tree_widget.element.width(),
261 bottom: offset.top + this.tree_widget.element.height() + 16
265 return DragAndDropHandler;
269 VisibleNodeIterator = (function() {
270 function VisibleNodeIterator(tree) {
274 VisibleNodeIterator.prototype.iterate = function() {
275 var _iterateNode, is_first_node;
276 is_first_node = true;
277 _iterateNode = (function(_this) {
278 return function(node, next_node) {
279 var $element, child, children_length, i, j, len, must_iterate_inside, ref;
280 must_iterate_inside = (node.is_open || !node.element) && node.hasChildren();
282 $element = $(node.element);
283 if (!$element.is(':visible')) {
287 _this.handleFirstNode(node, $element);
288 is_first_node = false;
290 if (!node.hasChildren()) {
291 _this.handleNode(node, next_node, $element);
292 } else if (node.is_open) {
293 if (!_this.handleOpenFolder(node, $element)) {
294 must_iterate_inside = false;
297 _this.handleClosedFolder(node, next_node, $element);
300 if (must_iterate_inside) {
301 children_length = node.children.length;
303 for (i = j = 0, len = ref.length; j < len; i = ++j) {
305 if (i === (children_length - 1)) {
306 _iterateNode(node.children[i], null);
308 _iterateNode(node.children[i], node.children[i + 1]);
312 return _this.handleAfterOpenFolder(node, next_node, $element);
317 return _iterateNode(this.tree, null);
320 VisibleNodeIterator.prototype.handleNode = function(node, next_node, $element) {};
322 VisibleNodeIterator.prototype.handleOpenFolder = function(node, $element) {};
324 VisibleNodeIterator.prototype.handleClosedFolder = function(node, next_node, $element) {};
326 VisibleNodeIterator.prototype.handleAfterOpenFolder = function(node, next_node, $element) {};
328 VisibleNodeIterator.prototype.handleFirstNode = function(node, $element) {};
330 return VisibleNodeIterator;
334 HitAreasGenerator = (function(superClass) {
335 extend(HitAreasGenerator, superClass);
337 function HitAreasGenerator(tree, current_node, tree_bottom) {
338 HitAreasGenerator.__super__.constructor.call(this, tree);
339 this.current_node = current_node;
340 this.tree_bottom = tree_bottom;
343 HitAreasGenerator.prototype.generate = function() {
347 return this.generateHitAreas(this.positions);
350 HitAreasGenerator.prototype.getTop = function($element) {
351 return $element.offset().top;
354 HitAreasGenerator.prototype.addPosition = function(node, position, top) {
361 this.positions.push(area);
362 return this.last_top = top;
365 HitAreasGenerator.prototype.handleNode = function(node, next_node, $element) {
367 top = this.getTop($element);
368 if (node === this.current_node) {
369 this.addPosition(node, Position.NONE, top);
371 this.addPosition(node, Position.INSIDE, top);
373 if (next_node === this.current_node || node === this.current_node) {
374 return this.addPosition(node, Position.NONE, top);
376 return this.addPosition(node, Position.AFTER, top);
380 HitAreasGenerator.prototype.handleOpenFolder = function(node, $element) {
381 if (node === this.current_node) {
384 if (node.children[0] !== this.current_node) {
385 this.addPosition(node, Position.INSIDE, this.getTop($element));
390 HitAreasGenerator.prototype.handleClosedFolder = function(node, next_node, $element) {
392 top = this.getTop($element);
393 if (node === this.current_node) {
394 return this.addPosition(node, Position.NONE, top);
396 this.addPosition(node, Position.INSIDE, top);
397 if (next_node !== this.current_node) {
398 return this.addPosition(node, Position.AFTER, top);
403 HitAreasGenerator.prototype.handleFirstNode = function(node, $element) {
404 if (node !== this.current_node) {
405 return this.addPosition(node, Position.BEFORE, this.getTop($(node.element)));
409 HitAreasGenerator.prototype.handleAfterOpenFolder = function(node, next_node, $element) {
410 if (node === this.current_node.node || next_node === this.current_node.node) {
411 return this.addPosition(node, Position.NONE, this.last_top);
413 return this.addPosition(node, Position.AFTER, this.last_top);
417 HitAreasGenerator.prototype.generateHitAreas = function(positions) {
418 var group, hit_areas, j, len, position, previous_top;
422 for (j = 0, len = positions.length; j < len; j++) {
423 position = positions[j];
424 if (position.top !== previous_top && group.length) {
426 this.generateHitAreasForGroup(hit_areas, group, previous_top, position.top);
428 previous_top = position.top;
431 group.push(position);
433 this.generateHitAreasForGroup(hit_areas, group, previous_top, this.tree_bottom);
437 HitAreasGenerator.prototype.generateHitAreasForGroup = function(hit_areas, positions_in_group, top, bottom) {
438 var area_height, area_top, i, position, position_count;
439 position_count = Math.min(positions_in_group.length, 4);
440 area_height = Math.round((bottom - top) / position_count);
443 while (i < position_count) {
444 position = positions_in_group[i];
447 bottom: area_top + area_height,
449 position: position.position
451 area_top += area_height;
457 return HitAreasGenerator;
459 })(VisibleNodeIterator);
461 DragElement = (function() {
462 function DragElement(node_name, offset_x, offset_y, $tree) {
463 this.offset_x = offset_x;
464 this.offset_y = offset_y;
465 this.$element = $("<span class=\"jqtree-title jqtree-dragging\">" + node_name + "</span>");
466 this.$element.css("position", "absolute");
467 $tree.append(this.$element);
470 DragElement.prototype.move = function(page_x, page_y) {
471 return this.$element.offset({
472 left: page_x - this.offset_x,
473 top: page_y - this.offset_y
477 DragElement.prototype.remove = function() {
478 return this.$element.remove();
486 DragAndDropHandler: DragAndDropHandler,
487 DragElement: DragElement,
488 HitAreasGenerator: HitAreasGenerator