view OrthancServer/OrthancExplorer/libs/tree.jquery.js @ 5541:5a34160b7627

todo
author Alain Mazy <am@osimis.io>
date Wed, 20 Mar 2024 10:08:42 +0100
parents d25f4c0fa160
children
line wrap: on
line source

// Generated by CoffeeScript 1.3.3

/*
Copyright 2012 Marco Braak

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/


(function() {
  var $, BorderDropHint, DragAndDropHandler, DragElement, FolderElement, GhostDropHint, JqTreeWidget, Json, MouseWidget, Node, NodeElement, Position, SaveStateHandler, SelectNodeHandler, SimpleWidget, Tree, html_escape, indexOf, toJson,
    __slice = [].slice,
    __hasProp = {}.hasOwnProperty,
    __extends = 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; };

  $ = this.jQuery;

  SimpleWidget = (function() {

    SimpleWidget.prototype.defaults = {};

    function SimpleWidget(el, options) {
      this.$el = $(el);
      this.options = $.extend({}, this.defaults, options);
      this._init();
    }

    SimpleWidget.prototype.destroy = function() {
      return this._deinit();
    };

    SimpleWidget.prototype._init = function() {
      return null;
    };

    SimpleWidget.prototype._deinit = function() {
      return null;
    };

    SimpleWidget.register = function(widget_class, widget_name) {
      var callFunction, createWidget, destroyWidget, getDataKey;
      getDataKey = function() {
        return "simple_widget_" + widget_name;
      };
      createWidget = function($el, options) {
        var data_key;
        data_key = getDataKey();
        $el.each(function() {
          var widget;
          widget = new widget_class(this, options);
          if (!$.data(this, data_key)) {
            return $.data(this, data_key, widget);
          }
        });
        return $el;
      };
      destroyWidget = function($el) {
        var data_key;
        data_key = getDataKey();
        return $el.each(function() {
          var widget;
          widget = $.data(this, data_key);
          if (widget && (widget instanceof SimpleWidget)) {
            widget.destroy();
          }
          return $.removeData(this, data_key);
        });
      };
      callFunction = function($el, function_name, args) {
        var result;
        result = null;
        $el.each(function() {
          var widget, widget_function;
          widget = $.data(this, getDataKey());
          if (widget && (widget instanceof SimpleWidget)) {
            widget_function = widget[function_name];
            if (widget_function && (typeof widget_function === 'function')) {
              return result = widget_function.apply(widget, args);
            }
          }
        });
        return result;
      };
      return $.fn[widget_name] = function() {
        var $el, args, argument1, function_name, options;
        argument1 = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
        $el = this;
        if (argument1 === void 0 || typeof argument1 === 'object') {
          options = argument1;
          return createWidget($el, options);
        } else if (typeof argument1 === 'string' && argument1[0] !== '_') {
          function_name = argument1;
          if (function_name === 'destroy') {
            return destroyWidget($el);
          } else {
            return callFunction($el, function_name, args);
          }
        }
      };
    };

    return SimpleWidget;

  })();

  this.SimpleWidget = SimpleWidget;

  /*
  This widget does the same a the mouse widget in jqueryui.
  */


  MouseWidget = (function(_super) {

    __extends(MouseWidget, _super);

    function MouseWidget() {
      return MouseWidget.__super__.constructor.apply(this, arguments);
    }

    MouseWidget.is_mouse_handled = false;

    MouseWidget.prototype._init = function() {
      this.$el.bind('mousedown.mousewidget', $.proxy(this._mouseDown, this));
      return this.is_mouse_started = false;
    };

    MouseWidget.prototype._deinit = function() {
      var $document;
      this.$el.unbind('mousedown.mousewidget');
      $document = $(document);
      $document.unbind('mousemove.mousewidget');
      return $document.unbind('mouseup.mousewidget');
    };

    MouseWidget.prototype._mouseDown = function(e) {
      var $document;
      if (MouseWidget.is_mouse_handled) {
        return;
      }
      if (!this.is_mouse_started) {
        this._mouseUp(e);
      }
      if (e.which !== 1) {
        return;
      }
      if (!this._mouseCapture(e)) {
        return;
      }
      this.mouse_down_event = e;
      $document = $(document);
      $document.bind('mousemove.mousewidget', $.proxy(this._mouseMove, this));
      $document.bind('mouseup.mousewidget', $.proxy(this._mouseUp, this));
      e.preventDefault();
      this.is_mouse_handled = true;
      return true;
    };

    MouseWidget.prototype._mouseMove = function(e) {
      if (this.is_mouse_started) {
        this._mouseDrag(e);
        return e.preventDefault();
      }
      this.is_mouse_started = this._mouseStart(this.mouse_down_event) !== false;
      if (this.is_mouse_started) {
        this._mouseDrag(e);
      } else {
        this._mouseUp(e);
      }
      return !this.is_mouse_started;
    };

    MouseWidget.prototype._mouseUp = function(e) {
      var $document;
      $document = $(document);
      $document.unbind('mousemove.mousewidget');
      $document.unbind('mouseup.mousewidget');
      if (this.is_mouse_started) {
        this.is_mouse_started = false;
        this._mouseStop(e);
      }
      return false;
    };

    MouseWidget.prototype._mouseCapture = function(e) {
      return true;
    };

    MouseWidget.prototype._mouseStart = function(e) {
      return null;
    };

    MouseWidget.prototype._mouseDrag = function(e) {
      return null;
    };

    MouseWidget.prototype._mouseStop = function(e) {
      return null;
    };

    return MouseWidget;

  })(SimpleWidget);

  /*
  Copyright 2012 Marco Braak
  
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  
      http://www.apache.org/licenses/LICENSE-2.0
  
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */


  this.Tree = {};

  $ = this.jQuery;

  indexOf = function(array, item) {
    var i, value, _i, _len;
    if (array.indexOf) {
      return array.indexOf(item);
    } else {
      for (i = _i = 0, _len = array.length; _i < _len; i = ++_i) {
        value = array[i];
        if (value === item) {
          return i;
        }
      }
      return -1;
    }
  };

  this.Tree.indexOf = indexOf;

  Json = {};

  Json.escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

  Json.meta = {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '"': '\\"',
    '\\': '\\\\'
  };

  Json.quote = function(string) {
    Json.escapable.lastIndex = 0;
    if (Json.escapable.test(string)) {
      return '"' + string.replace(Json.escapable, function(a) {
        var c;
        c = Json.meta[a];
        return (typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4));
      }) + '"';
    } else {
      return '"' + string + '"';
    }
  };

  Json.str = function(key, holder) {
    var i, k, partial, v, value, _i, _len;
    value = holder[key];
    switch (typeof value) {
      case 'string':
        return Json.quote(value);
      case 'number':
        if (isFinite(value)) {
          return String(value);
        } else {
          return 'null';
        }
      case 'boolean':
      case 'null':
        return String(value);
      case 'object':
        if (!value) {
          return 'null';
        }
        partial = [];
        if (Object.prototype.toString.apply(value) === '[object Array]') {
          for (i = _i = 0, _len = value.length; _i < _len; i = ++_i) {
            v = value[i];
            partial[i] = Json.str(i, value) || 'null';
          }
          return (partial.length === 0 ? '[]' : '[' + partial.join(',') + ']');
        }
        for (k in value) {
          if (Object.prototype.hasOwnProperty.call(value, k)) {
            v = Json.str(k, value);
            if (v) {
              partial.push(Json.quote(k) + ':' + v);
            }
          }
        }
        return (partial.length === 0 ? '{}' : '{' + partial.join(',') + '}');
    }
  };

  toJson = function(value) {
    return Json.str('', {
      '': value
    });
  };

  this.Tree.toJson = toJson;

  html_escape = function(string) {
    return ('' + string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g, '&#x2F;');
  };

  Position = {
    getName: function(position) {
      if (position === Position.BEFORE) {
        return 'before';
      } else if (position === Position.AFTER) {
        return 'after';
      } else if (position === Position.INSIDE) {
        return 'inside';
      } else {
        return 'none';
      }
    }
  };

  Position.BEFORE = 1;

  Position.AFTER = 2;

  Position.INSIDE = 3;

  Position.NONE = 4;

  this.Tree.Position = Position;

  Node = (function() {

    function Node(o) {
      this.setData(o);
    }

    Node.prototype.setData = function(o) {
      var key, value;
      if (typeof o !== 'object') {
        this.name = o;
      } else {
        for (key in o) {
          value = o[key];
          if (key === 'label') {
            this.name = value;
          } else {
            this[key] = value;
          }
        }
      }
      this.children = [];
      return this.parent = null;
    };

    Node.prototype.initFromData = function(data) {
      var addChildren, addNode,
        _this = this;
      addNode = function(node_data) {
        _this.setData(node_data);
        if (node_data.children) {
          return addChildren(node_data.children);
        }
      };
      addChildren = function(children_data) {
        var child, node, _i, _len;
        for (_i = 0, _len = children_data.length; _i < _len; _i++) {
          child = children_data[_i];
          node = new Node('');
          node.initFromData(child);
          _this.addChild(node);
        }
        return null;
      };
      addNode(data);
      return null;
    };

    /*
        Create tree from data.
    
        Structure of data is:
        [
            {
                label: 'node1',
                children: [
                    { label: 'child1' },
                    { label: 'child2' }
                ]
            },
            {
                label: 'node2'
            }
        ]
    */


    Node.prototype.loadFromData = function(data) {
      var node, o, _i, _len;
      this.children = [];
      for (_i = 0, _len = data.length; _i < _len; _i++) {
        o = data[_i];
        node = new Node(o);
        this.addChild(node);
        if (typeof o === 'object' && o.children) {
          node.loadFromData(o.children);
        }
      }
      return null;
    };

    /*
        Add child.
    
        tree.addChild(
            new Node('child1')
        );
    */


    Node.prototype.addChild = function(node) {
      this.children.push(node);
      return node._setParent(this);
    };

    /*
        Add child at position. Index starts at 0.
    
        tree.addChildAtPosition(
            new Node('abc'),
            1
        );
    */


    Node.prototype.addChildAtPosition = function(node, index) {
      this.children.splice(index, 0, node);
      return node._setParent(this);
    };

    Node.prototype._setParent = function(parent) {
      this.parent = parent;
      this.tree = parent.tree;
      return this.tree.addNodeToIndex(this);
    };

    /*
        Remove child.
    
        tree.removeChild(tree.children[0]);
    */


    Node.prototype.removeChild = function(node) {
      this.children.splice(this.getChildIndex(node), 1);
      return this.tree.removeNodeFromIndex(node);
    };

    /*
        Get child index.
    
        var index = getChildIndex(node);
    */


    Node.prototype.getChildIndex = function(node) {
      return $.inArray(node, this.children);
    };

    /*
        Does the tree have children?
    
        if (tree.hasChildren()) {
            //
        }
    */


    Node.prototype.hasChildren = function() {
      return this.children.length !== 0;
    };

    /*
        Iterate over all the nodes in the tree.
    
        Calls callback with (node, level).
    
        The callback must return true to continue the iteration on current node.
    
        tree.iterate(
            function(node, level) {
               console.log(node.name);
    
               // stop iteration after level 2
               return (level <= 2);
            }
        );
    */


    Node.prototype.iterate = function(callback) {
      var _iterate,
        _this = this;
      _iterate = function(node, level) {
        var child, result, _i, _len, _ref;
        if (node.children) {
          _ref = node.children;
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            child = _ref[_i];
            result = callback(child, level);
            if (_this.hasChildren() && result) {
              _iterate(child, level + 1);
            }
          }
          return null;
        }
      };
      _iterate(this, 0);
      return null;
    };

    /*
        Move node relative to another node.
    
        Argument position: Position.BEFORE, Position.AFTER or Position.Inside
    
        // move node1 after node2
        tree.moveNode(node1, node2, Position.AFTER);
    */


    Node.prototype.moveNode = function(moved_node, target_node, position) {
      moved_node.parent.removeChild(moved_node);
      if (position === Position.AFTER) {
        return target_node.parent.addChildAtPosition(moved_node, target_node.parent.getChildIndex(target_node) + 1);
      } else if (position === Position.BEFORE) {
        return target_node.parent.addChildAtPosition(moved_node, target_node.parent.getChildIndex(target_node));
      } else if (position === Position.INSIDE) {
        return target_node.addChildAtPosition(moved_node, 0);
      }
    };

    /*
        Get the tree as data.
    */


    Node.prototype.getData = function() {
      var getDataFromNodes,
        _this = this;
      getDataFromNodes = function(nodes) {
        var data, k, node, tmp_node, v, _i, _len;
        data = [];
        for (_i = 0, _len = nodes.length; _i < _len; _i++) {
          node = nodes[_i];
          tmp_node = {};
          for (k in node) {
            v = node[k];
            if ((k !== 'parent' && k !== 'children' && k !== 'element' && k !== 'tree') && Object.prototype.hasOwnProperty.call(node, k)) {
              tmp_node[k] = v;
            }
          }
          if (node.hasChildren()) {
            tmp_node.children = getDataFromNodes(node.children);
          }
          data.push(tmp_node);
        }
        return data;
      };
      return getDataFromNodes(this.children);
    };

    Node.prototype.getNodeByName = function(name) {
      var result;
      result = null;
      this.iterate(function(node) {
        if (node.name === name) {
          result = node;
          return false;
        } else {
          return true;
        }
      });
      return result;
    };

    Node.prototype.addAfter = function(node_info) {
      var child_index, node;
      if (!this.parent) {
        return null;
      } else {
        node = new Node(node_info);
        child_index = this.parent.getChildIndex(this);
        this.parent.addChildAtPosition(node, child_index + 1);
        return node;
      }
    };

    Node.prototype.addBefore = function(node_info) {
      var child_index, node;
      if (!this.parent) {
        return null;
      } else {
        node = new Node(node_info);
        child_index = this.parent.getChildIndex(this);
        return this.parent.addChildAtPosition(node, child_index);
      }
    };

    Node.prototype.addParent = function(node_info) {
      var child, new_parent, original_parent, _i, _len, _ref;
      if (!this.parent) {
        return null;
      } else {
        new_parent = new Node(node_info);
        new_parent._setParent(this.tree);
        original_parent = this.parent;
        _ref = original_parent.children;
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          child = _ref[_i];
          new_parent.addChild(child);
        }
        original_parent.children = [];
        original_parent.addChild(new_parent);
        return new_parent;
      }
    };

    Node.prototype.remove = function() {
      if (this.parent) {
        this.parent.removeChild(this);
        return this.parent = null;
      }
    };

    Node.prototype.append = function(node_info) {
      var node;
      node = new Node(node_info);
      this.addChild(node);
      return node;
    };

    Node.prototype.prepend = function(node_info) {
      var node;
      node = new Node(node_info);
      this.addChildAtPosition(node, 0);
      return node;
    };

    return Node;

  })();

  Tree = (function(_super) {

    __extends(Tree, _super);

    function Tree(o) {
      Tree.__super__.constructor.call(this, o, null, true);
      this.id_mapping = {};
      this.tree = this;
    }

    Tree.prototype.getNodeById = function(node_id) {
      return this.id_mapping[node_id];
    };

    Tree.prototype.addNodeToIndex = function(node) {
      if (node.id) {
        return this.id_mapping[node.id] = node;
      }
    };

    Tree.prototype.removeNodeFromIndex = function(node) {
      if (node.id) {
        return delete this.id_mapping[node.id];
      }
    };

    return Tree;

  })(Node);

  this.Tree.Tree = Tree;

  JqTreeWidget = (function(_super) {

    __extends(JqTreeWidget, _super);

    function JqTreeWidget() {
      return JqTreeWidget.__super__.constructor.apply(this, arguments);
    }

    JqTreeWidget.prototype.defaults = {
      autoOpen: false,
      saveState: false,
      dragAndDrop: false,
      selectable: false,
      onCanSelectNode: null,
      onSetStateFromStorage: null,
      onGetStateFromStorage: null,
      onCreateLi: null,
      onIsMoveHandle: null,
      onCanMove: null,
      onCanMoveTo: null,
      autoEscape: true,
      dataUrl: null
    };

    JqTreeWidget.prototype.toggle = function(node) {
      if (node.hasChildren()) {
        new FolderElement(node, this.element).toggle();
      }
      return this._saveState();
    };

    JqTreeWidget.prototype.getTree = function() {
      return this.tree;
    };

    JqTreeWidget.prototype.selectNode = function(node, must_open_parents) {
      return this.select_node_handler.selectNode(node, must_open_parents);
    };

    JqTreeWidget.prototype.getSelectedNode = function() {
      return this.selected_node || false;
    };

    JqTreeWidget.prototype.toJson = function() {
      return toJson(this.tree.getData());
    };

    JqTreeWidget.prototype.loadData = function(data, parent_node) {
      var child, subtree, _i, _len, _ref;
      if (!parent_node) {
        this._initTree(data);
      } else {
        subtree = new Node('');
        subtree._setParent(parent_node.tree);
        subtree.loadFromData(data);
        _ref = subtree.children;
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          child = _ref[_i];
          parent_node.addChild(child);
        }
        this._refreshElements(parent_node.parent);
      }
      if (this.is_dragging) {
        return this.dnd_handler.refreshHitAreas();
      }
    };

    JqTreeWidget.prototype.getNodeById = function(node_id) {
      return this.tree.getNodeById(node_id);
    };

    JqTreeWidget.prototype.getNodeByName = function(name) {
      return this.tree.getNodeByName(name);
    };

    JqTreeWidget.prototype.openNode = function(node, skip_slide) {
      if (node.hasChildren()) {
        new FolderElement(node, this.element).open(null, skip_slide);
        return this._saveState();
      }
    };

    JqTreeWidget.prototype.closeNode = function(node, skip_slide) {
      if (node.hasChildren()) {
        new FolderElement(node, this.element).close(skip_slide);
        return this._saveState();
      }
    };

    JqTreeWidget.prototype.isDragging = function() {
      return this.is_dragging;
    };

    JqTreeWidget.prototype.refreshHitAreas = function() {
      return this.dnd_handler.refreshHitAreas();
    };

    JqTreeWidget.prototype.addNodeAfter = function(new_node_info, existing_node) {
      var new_node;
      new_node = existing_node.addAfter(new_node_info);
      this._refreshElements(existing_node.parent);
      return new_node;
    };

    JqTreeWidget.prototype.addNodeBefore = function(new_node_info, existing_node) {
      var new_node;
      new_node = existing_node.addBefore(new_node_info);
      this._refreshElements(existing_node.parent);
      return new_node;
    };

    JqTreeWidget.prototype.addParentNode = function(new_node_info, existing_node) {
      var new_node;
      new_node = existing_node.addParent(new_node_info);
      this._refreshElements(new_node.parent);
      return new_node;
    };

    JqTreeWidget.prototype.removeNode = function(node) {
      var parent;
      parent = node.parent;
      if (parent) {
        node.remove();
        return this._refreshElements(parent.parent);
      }
    };

    JqTreeWidget.prototype.appendNode = function(new_node_info, parent_node) {
      var is_already_root_node, node;
      if (!parent_node) {
        parent_node = this.tree;
      }
      is_already_root_node = parent_node.hasChildren();
      node = parent_node.append(new_node_info);
      if (is_already_root_node) {
        this._refreshElements(parent_node);
      } else {
        this._refreshElements(parent_node.parent);
      }
      return node;
    };

    JqTreeWidget.prototype.prependNode = function(new_node_info, parent_node) {
      var node;
      if (!parent_node) {
        parent_node = this.tree;
      }
      node = parent_node.prepend(new_node_info);
      this._refreshElements(parent_node);
      return node;
    };

    JqTreeWidget.prototype._init = function() {
      JqTreeWidget.__super__._init.call(this);
      this.element = this.$el;
      this._initData();
      this.element.click($.proxy(this._click, this));
      return this.element.bind('contextmenu', $.proxy(this._contextmenu, this));
    };

    JqTreeWidget.prototype._deinit = function() {
      this.element.empty();
      this.element.unbind();
      this.tree = null;
      return JqTreeWidget.__super__._deinit.call(this);
    };

    JqTreeWidget.prototype._initData = function() {
      var data_url,
        _this = this;
      if (this.options.data) {
        return this._initTree(this.options.data);
      } else {
        data_url = this.options.dataUrl || this.element.data('url');
        if (data_url) {
          return $.ajax({
            url: data_url,
            cache: false,
            success: function(response) {
              var data;
              if ($.isArray(response) || typeof response === 'object') {
                data = response;
              } else {
                data = $.parseJSON(response);
              }
              return _this._initTree(data);
            }
          });
        }
      }
    };

    JqTreeWidget.prototype._initTree = function(data) {
      var event;
      this.tree = new Tree();
      this.tree.loadFromData(data);
      this.selected_node = null;
      this.save_state_handler = new SaveStateHandler(this);
      this.select_node_handler = new SelectNodeHandler(this);
      this.dnd_handler = new DragAndDropHandler(this);
      this._openNodes();
      this._refreshElements();
      this.select_node_handler.selectCurrentNode();
      event = $.Event('tree.init');
      return this.element.trigger(event);
    };

    JqTreeWidget.prototype._openNodes = function() {
      var max_level;
      if (this.options.saveState) {
        if (this.save_state_handler.restoreState()) {
          return;
        }
      }
      if (this.options.autoOpen === false) {
        return;
      } else if (this.options.autoOpen === true) {
        max_level = -1;
      } else {
        max_level = parseInt(this.options.autoOpen);
      }
      return this.tree.iterate(function(node, level) {
        node.is_open = true;
        return level !== max_level;
      });
    };

    JqTreeWidget.prototype._refreshElements = function(from_node) {
      var $element, createFolderLi, createLi, createNodeLi, createUl, doCreateDomElements, escapeIfNecessary, is_root_node, node_element,
        _this = this;
      if (from_node == null) {
        from_node = null;
      }
      escapeIfNecessary = function(value) {
        if (_this.options.autoEscape) {
          return html_escape(value);
        } else {
          return value;
        }
      };
      createUl = function(is_root_node) {
        var class_string;
        if (is_root_node) {
          class_string = ' class="tree"';
        } else {
          class_string = '';
        }
        return $("<ul" + class_string + "></ul>");
      };
      createLi = function(node) {
        var $li;
        if (node.hasChildren()) {
          $li = createFolderLi(node);
        } else {
          $li = createNodeLi(node);
        }
        if (_this.options.onCreateLi) {
          _this.options.onCreateLi(node, $li);
        }
        return $li;
      };
      createNodeLi = function(node) {
        var escaped_name;
        escaped_name = escapeIfNecessary(node.name);
        return $("<li><div><span class=\"title\">" + escaped_name + "</span></div></li>");
      };
      createFolderLi = function(node) {
        var button_class, escaped_name, folder_class, getButtonClass, getFolderClass;
        getButtonClass = function() {
          var classes;
          classes = ['toggler'];
          if (!node.is_open) {
            classes.push('closed');
          }
          return classes.join(' ');
        };
        getFolderClass = function() {
          var classes;
          classes = ['folder'];
          if (!node.is_open) {
            classes.push('closed');
          }
          return classes.join(' ');
        };
        button_class = getButtonClass();
        folder_class = getFolderClass();
        escaped_name = escapeIfNecessary(node.name);
        return $("<li class=\"" + folder_class + "\"><div><a class=\"" + button_class + "\">&raquo;</a><span class=\"title\">" + escaped_name + "</span></div></li>");
      };
      doCreateDomElements = function($element, children, is_root_node, is_open) {
        var $li, $ul, child, _i, _len;
        $ul = createUl(is_root_node);
        $element.append($ul);
        for (_i = 0, _len = children.length; _i < _len; _i++) {
          child = children[_i];
          $li = createLi(child);
          $ul.append($li);
          child.element = $li[0];
          $li.data('node', child);
          if (child.hasChildren()) {
            doCreateDomElements($li, child.children, false, child.is_open);
          }
        }
        return null;
      };
      if (from_node && from_node.parent) {
        is_root_node = false;
        node_element = this._getNodeElementForNode(from_node);
        node_element.getUl().remove();
        $element = node_element.$element;
      } else {
        from_node = this.tree;
        $element = this.element;
        $element.empty();
        is_root_node = true;
      }
      return doCreateDomElements($element, from_node.children, is_root_node, is_root_node);
    };

    JqTreeWidget.prototype._click = function(e) {
      var $target, event, node, node_element;
      if (e.ctrlKey) {
        return;
      }
      $target = $(e.target);
      if ($target.is('.toggler')) {
        node_element = this._getNodeElement($target);
        if (node_element && node_element.node.hasChildren()) {
          node_element.toggle();
          this._saveState();
          e.preventDefault();
          return e.stopPropagation();
        }
      } else if ($target.is('div') || $target.is('span')) {
        node = this._getNode($target);
        if (node) {
          if ((!this.options.onCanSelectNode) || this.options.onCanSelectNode(node)) {
            this.selectNode(node);
            event = $.Event('tree.click');
            event.node = node;
            return this.element.trigger(event);
          }
        }
      }
    };

    JqTreeWidget.prototype._getNode = function($element) {
      var $li;
      $li = $element.closest('li');
      if ($li.length === 0) {
        return null;
      } else {
        return $li.data('node');
      }
    };

    JqTreeWidget.prototype._getNodeElementForNode = function(node) {
      if (node.hasChildren()) {
        return new FolderElement(node, this.element);
      } else {
        return new NodeElement(node, this.element);
      }
    };

    JqTreeWidget.prototype._getNodeElement = function($element) {
      var node;
      node = this._getNode($element);
      if (node) {
        return this._getNodeElementForNode(node);
      } else {
        return null;
      }
    };

    JqTreeWidget.prototype._contextmenu = function(e) {
      var $div, event, node;
      $div = $(e.target).closest('ul.tree div');
      if ($div.length) {
        node = this._getNode($div);
        if (node) {
          e.preventDefault();
          e.stopPropagation();
          event = $.Event('tree.contextmenu');
          event.node = node;
          event.click_event = e;
          this.element.trigger(event);
          return false;
        }
      }
    };

    JqTreeWidget.prototype._saveState = function() {
      if (this.options.saveState) {
        return this.save_state_handler.saveState();
      }
    };

    JqTreeWidget.prototype._mouseCapture = function(event) {
      if (this.options.dragAndDrop) {
        return this.dnd_handler.mouseCapture(event);
      } else {
        return false;
      }
    };

    JqTreeWidget.prototype._mouseStart = function(event) {
      if (this.options.dragAndDrop) {
        return this.dnd_handler.mouseStart(event);
      } else {
        return false;
      }
    };

    JqTreeWidget.prototype._mouseDrag = function(event) {
      if (this.options.dragAndDrop) {
        return this.dnd_handler.mouseDrag(event);
      } else {
        return false;
      }
    };

    JqTreeWidget.prototype._mouseStop = function() {
      if (this.options.dragAndDrop) {
        return this.dnd_handler.mouseStop();
      } else {
        return false;
      }
    };

    JqTreeWidget.prototype.testGenerateHitAreas = function(moving_node) {
      this.dnd_handler.current_item = this._getNodeElementForNode(moving_node);
      this.dnd_handler.generateHitAreas();
      return this.dnd_handler.hit_areas;
    };

    return JqTreeWidget;

  })(MouseWidget);

  SimpleWidget.register(JqTreeWidget, 'tree');

  GhostDropHint = (function() {

    function GhostDropHint(node, $element, position) {
      this.$element = $element;
      this.node = node;
      this.$ghost = $('<li class="ghost"><span class="circle"></span><span class="line"></span></li>');
      if (position === Position.AFTER) {
        this.moveAfter();
      } else if (position === Position.BEFORE) {
        this.moveBefore();
      } else if (position === Position.INSIDE) {
        if (node.hasChildren() && node.is_open) {
          this.moveInsideOpenFolder();
        } else {
          this.moveInside();
        }
      }
    }

    GhostDropHint.prototype.remove = function() {
      return this.$ghost.remove();
    };

    GhostDropHint.prototype.moveAfter = function() {
      return this.$element.after(this.$ghost);
    };

    GhostDropHint.prototype.moveBefore = function() {
      return this.$element.before(this.$ghost);
    };

    GhostDropHint.prototype.moveInsideOpenFolder = function() {
      return $(this.node.children[0].element).before(this.$ghost);
    };

    GhostDropHint.prototype.moveInside = function() {
      this.$element.after(this.$ghost);
      return this.$ghost.addClass('inside');
    };

    return GhostDropHint;

  })();

  BorderDropHint = (function() {

    function BorderDropHint($element) {
      var $div, width;
      $div = $element.children('div');
      width = $element.width() - 4;
      this.$hint = $('<span class="border"></span>');
      $div.append(this.$hint);
      this.$hint.css({
        width: width,
        height: $div.height() - 4
      });
    }

    BorderDropHint.prototype.remove = function() {
      return this.$hint.remove();
    };

    return BorderDropHint;

  })();

  NodeElement = (function() {

    function NodeElement(node, tree_element) {
      this.init(node, tree_element);
    }

    NodeElement.prototype.init = function(node, tree_element) {
      this.node = node;
      this.tree_element = tree_element;
      return this.$element = $(node.element);
    };

    NodeElement.prototype.getUl = function() {
      return this.$element.children('ul:first');
    };

    NodeElement.prototype.getSpan = function() {
      return this.$element.children('div').find('span.title');
    };

    NodeElement.prototype.getLi = function() {
      return this.$element;
    };

    NodeElement.prototype.addDropHint = function(position) {
      if (position === Position.INSIDE) {
        return new BorderDropHint(this.$element);
      } else {
        return new GhostDropHint(this.node, this.$element, position);
      }
    };

    NodeElement.prototype.select = function() {
      return this.getLi().addClass('selected');
    };

    NodeElement.prototype.deselect = function() {
      return this.getLi().removeClass('selected');
    };

    return NodeElement;

  })();

  FolderElement = (function(_super) {

    __extends(FolderElement, _super);

    function FolderElement() {
      return FolderElement.__super__.constructor.apply(this, arguments);
    }

    FolderElement.prototype.toggle = function() {
      if (this.node.is_open) {
        return this.close();
      } else {
        return this.open();
      }
    };

    FolderElement.prototype.open = function(on_finished, skip_slide) {
      var doOpen,
        _this = this;
      if (!this.node.is_open) {
        this.node.is_open = true;
        this.getButton().removeClass('closed');
        doOpen = function() {
          var event;
          _this.getLi().removeClass('closed');
          if (on_finished) {
            on_finished();
          }
          event = $.Event('tree.open');
          event.node = _this.node;
          return _this.tree_element.trigger(event);
        };
        if (skip_slide) {
          return doOpen();
        } else {
          return this.getUl().slideDown('fast', doOpen);
        }
      }
    };

    FolderElement.prototype.close = function(skip_slide) {
      var doClose,
        _this = this;
      if (this.node.is_open) {
        this.node.is_open = false;
        this.getButton().addClass('closed');
        doClose = function() {
          var event;
          _this.getLi().addClass('closed');
          event = $.Event('tree.close');
          event.node = _this.node;
          return _this.tree_element.trigger(event);
        };
        if (skip_slide) {
          return doClose();
        } else {
          return this.getUl().slideUp('fast', doClose);
        }
      }
    };

    FolderElement.prototype.getButton = function() {
      return this.$element.children('div').find('a.toggler');
    };

    FolderElement.prototype.addDropHint = function(position) {
      if (!this.node.is_open && position === Position.INSIDE) {
        return new BorderDropHint(this.$element);
      } else {
        return new GhostDropHint(this.node, this.$element, position);
      }
    };

    return FolderElement;

  })(NodeElement);

  DragElement = (function() {

    function DragElement(node, offset_x, offset_y, $tree) {
      this.offset_x = offset_x;
      this.offset_y = offset_y;
      this.$element = $("<span class=\"title tree-dragging\">" + node.name + "</span>");
      this.$element.css("position", "absolute");
      $tree.append(this.$element);
    }

    DragElement.prototype.move = function(page_x, page_y) {
      return this.$element.offset({
        left: page_x - this.offset_x,
        top: page_y - this.offset_y
      });
    };

    DragElement.prototype.remove = function() {
      return this.$element.remove();
    };

    return DragElement;

  })();

  SaveStateHandler = (function() {

    function SaveStateHandler(tree_widget) {
      this.tree_widget = tree_widget;
    }

    SaveStateHandler.prototype.saveState = function() {
      if (this.tree_widget.options.onSetStateFromStorage) {
        return this.tree_widget.options.onSetStateFromStorage(this.getState());
      } else if (typeof localStorage !== "undefined" && localStorage !== null) {
        return localStorage.setItem(this.getCookieName(), this.getState());
      } else if ($.cookie) {
        return $.cookie(this.getCookieName(), this.getState(), {
          path: '/'
        });
      }
    };

    SaveStateHandler.prototype.restoreState = function() {
      var state;
      if (this.tree_widget.options.onGetStateFromStorage) {
        state = this.tree_widget.options.onGetStateFromStorage();
      } else if (typeof localStorage !== "undefined" && localStorage !== null) {
        state = localStorage.getItem(this.getCookieName());
      } else if ($.cookie) {
        state = $.cookie(this.getCookieName(), {
          path: '/'
        });
      } else {
        state = null;
      }
      if (!state) {
        return false;
      } else {
        this.setState(state);
        return true;
      }
    };

    SaveStateHandler.prototype.getState = function() {
      var open_nodes, selected_node,
        _this = this;
      open_nodes = [];
      this.tree_widget.tree.iterate(function(node) {
        if (node.is_open && node.id && node.hasChildren()) {
          open_nodes.push(node.id);
        }
        return true;
      });
      selected_node = '';
      if (this.tree_widget.selected_node) {
        selected_node = this.tree_widget.selected_node.id;
      }
      return toJson({
        open_nodes: open_nodes,
        selected_node: selected_node
      });
    };

    SaveStateHandler.prototype.setState = function(state) {
      var data, open_nodes, selected_node_id,
        _this = this;
      data = $.parseJSON(state);
      if (data) {
        open_nodes = data.open_nodes;
        selected_node_id = data.selected_node;
        return this.tree_widget.tree.iterate(function(node) {
          if (node.id && node.hasChildren() && (indexOf(open_nodes, node.id) >= 0)) {
            node.is_open = true;
          }
          if (selected_node_id && (node.id === selected_node_id)) {
            _this.tree_widget.selected_node = node;
          }
          return true;
        });
      }
    };

    SaveStateHandler.prototype.getCookieName = function() {
      if (typeof this.tree_widget.options.saveState === 'string') {
        return this.tree_widget.options.saveState;
      } else {
        return 'tree';
      }
    };

    return SaveStateHandler;

  })();

  SelectNodeHandler = (function() {

    function SelectNodeHandler(tree_widget) {
      this.tree_widget = tree_widget;
    }

    SelectNodeHandler.prototype.selectNode = function(node, must_open_parents) {
      var parent;
      if (this.tree_widget.options.selectable) {
        if (this.tree_widget.selected_node) {
          this.tree_widget._getNodeElementForNode(this.tree_widget.selected_node).deselect();
          this.tree_widget.selected_node = null;
        }
        if (node) {
          this.tree_widget._getNodeElementForNode(node).select();
          this.tree_widget.selected_node = node;
          if (must_open_parents) {
            parent = this.tree_widget.selected_node.parent;
            while (parent) {
              if (!parent.is_open) {
                this.tree_widget.openNode(parent, true);
              }
              parent = parent.parent;
            }
          }
        }
        if (this.tree_widget.options.saveState) {
          return this.tree_widget.save_state_handler.saveState();
        }
      }
    };

    SelectNodeHandler.prototype.selectCurrentNode = function() {
      var node_element;
      if (this.tree_widget.selected_node) {
        node_element = this.tree_widget._getNodeElementForNode(this.tree_widget.selected_node);
        if (node_element) {
          return node_element.select();
        }
      }
    };

    return SelectNodeHandler;

  })();

  DragAndDropHandler = (function() {

    function DragAndDropHandler(tree_widget) {
      this.tree_widget = tree_widget;
      this.hovered_area = null;
      this.$ghost = null;
      this.hit_areas = [];
      this.is_dragging = false;
    }

    DragAndDropHandler.prototype.mouseCapture = function(event) {
      var $element, node_element;
      $element = $(event.target);
      if (this.tree_widget.options.onIsMoveHandle && !this.tree_widget.options.onIsMoveHandle($element)) {
        return null;
      }
      node_element = this.tree_widget._getNodeElement($element);
      if (node_element && this.tree_widget.options.onCanMove) {
        if (!this.tree_widget.options.onCanMove(node_element.node)) {
          node_element = null;
        }
      }
      this.current_item = node_element;
      return this.current_item !== null;
    };

    DragAndDropHandler.prototype.mouseStart = function(event) {
      var offsetX, offsetY, _ref;
      this.refreshHitAreas();
      _ref = this.getOffsetFromEvent(event), offsetX = _ref[0], offsetY = _ref[1];
      this.drag_element = new DragElement(this.current_item.node, offsetX, offsetY, this.tree_widget.element);
      this.is_dragging = true;
      this.current_item.$element.addClass('moving');
      return true;
    };

    DragAndDropHandler.prototype.mouseDrag = function(event) {
      var area, position_name;
      this.drag_element.move(event.pageX, event.pageY);
      area = this.findHoveredArea(event.pageX, event.pageY);
      if (area && this.tree_widget.options.onCanMoveTo) {
        position_name = Position.getName(area.position);
        if (!this.tree_widget.options.onCanMoveTo(this.current_item.node, area.node, position_name)) {
          area = null;
        }
      }
      if (!area) {
        this.removeDropHint();
        this.removeHover();
        this.stopOpenFolderTimer();
      } else {
        if (this.hovered_area !== area) {
          this.hovered_area = area;
          this.updateDropHint();
        }
      }
      return true;
    };

    DragAndDropHandler.prototype.mouseStop = function() {
      this.moveItem();
      this.clear();
      this.removeHover();
      this.removeDropHint();
      this.removeHitAreas();
      this.current_item.$element.removeClass('moving');
      this.is_dragging = false;
      return false;
    };

    DragAndDropHandler.prototype.getOffsetFromEvent = function(event) {
      var element_offset;
      element_offset = $(event.target).offset();
      return [event.pageX - element_offset.left, event.pageY - element_offset.top];
    };

    DragAndDropHandler.prototype.refreshHitAreas = function() {
      this.removeHitAreas();
      return this.generateHitAreas();
    };

    DragAndDropHandler.prototype.removeHitAreas = function() {
      return this.hit_areas = [];
    };

    DragAndDropHandler.prototype.clear = function() {
      this.drag_element.remove();
      return this.drag_element = null;
    };

    DragAndDropHandler.prototype.removeDropHint = function() {
      if (this.previous_ghost) {
        return this.previous_ghost.remove();
      }
    };

    DragAndDropHandler.prototype.removeHover = function() {
      return this.hovered_area = null;
    };

    DragAndDropHandler.prototype.generateHitAreas = function() {
      var addPosition, getTop, groupPositions, handleAfterOpenFolder, handleClosedFolder, handleFirstNode, handleNode, handleOpenFolder, hit_areas, last_top, positions,
        _this = this;
      positions = [];
      last_top = 0;
      getTop = function($element) {
        return $element.offset().top;
      };
      addPosition = function(node, position, top) {
        positions.push({
          top: top,
          node: node,
          position: position
        });
        return last_top = top;
      };
      groupPositions = function(handle_group) {
        var group, position, previous_top, _i, _len;
        previous_top = -1;
        group = [];
        for (_i = 0, _len = positions.length; _i < _len; _i++) {
          position = positions[_i];
          if (position.top !== previous_top) {
            if (group.length) {
              handle_group(group, previous_top, position.top);
            }
            previous_top = position.top;
            group = [];
          }
          group.push(position);
        }
        return handle_group(group, previous_top, _this.tree_widget.element.offset().top + _this.tree_widget.element.height());
      };
      handleNode = function(node, next_node, $element) {
        var top;
        top = getTop($element);
        if (node === _this.current_item.node) {
          addPosition(node, Position.NONE, top);
        } else {
          addPosition(node, Position.INSIDE, top);
        }
        if (next_node === _this.current_item.node || node === _this.current_item.node) {
          return addPosition(node, Position.NONE, top);
        } else {
          return addPosition(node, Position.AFTER, top);
        }
      };
      handleOpenFolder = function(node, $element) {
        if (node === _this.current_item.node) {
          return false;
        }
        if (node.children[0] !== _this.current_item.node) {
          addPosition(node, Position.INSIDE, getTop($element));
        }
        return true;
      };
      handleAfterOpenFolder = function(node, next_node, $element) {
        if (node === _this.current_item.node || next_node === _this.current_item.node) {
          return addPosition(node, Position.NONE, last_top);
        } else {
          return addPosition(node, Position.AFTER, last_top);
        }
      };
      handleClosedFolder = function(node, next_node, $element) {
        var top;
        top = getTop($element);
        if (node === _this.current_item.node) {
          return addPosition(node, Position.NONE, top);
        } else {
          addPosition(node, Position.INSIDE, top);
          if (next_node !== _this.current_item.node) {
            return addPosition(node, Position.AFTER, top);
          }
        }
      };
      handleFirstNode = function(node, $element) {
        if (node !== _this.current_item.node) {
          return addPosition(node, Position.BEFORE, getTop($(node.element)));
        }
      };
      this.iterateVisibleNodes(handleNode, handleOpenFolder, handleClosedFolder, handleAfterOpenFolder, handleFirstNode);
      hit_areas = [];
      groupPositions(function(positions_in_group, top, bottom) {
        var area_height, area_top, position, _i, _len;
        area_height = (bottom - top) / positions_in_group.length;
        area_top = top;
        for (_i = 0, _len = positions_in_group.length; _i < _len; _i++) {
          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;
        }
        return null;
      });
      return this.hit_areas = hit_areas;
    };

    DragAndDropHandler.prototype.iterateVisibleNodes = function(handle_node, handle_open_folder, handle_closed_folder, handle_after_open_folder, handle_first_node) {
      var is_first_node, iterate,
        _this = this;
      is_first_node = true;
      iterate = function(node, next_node) {
        var $element, child, children_length, i, must_iterate_inside, _i, _len, _ref;
        must_iterate_inside = (node.is_open || !node.element) && node.hasChildren();
        if (node.element) {
          $element = $(node.element);
          if (!$element.is(':visible')) {
            return;
          }
          if (is_first_node) {
            handle_first_node(node, $element);
            is_first_node = false;
          }
          if (!node.hasChildren()) {
            handle_node(node, next_node, $element);
          } else if (node.is_open) {
            if (!handle_open_folder(node, $element)) {
              must_iterate_inside = false;
            }
          } else {
            handle_closed_folder(node, next_node, $element);
          }
        }
        if (must_iterate_inside) {
          children_length = node.children.length;
          _ref = node.children;
          for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
            child = _ref[i];
            if (i === (children_length - 1)) {
              iterate(node.children[i], null);
            } else {
              iterate(node.children[i], node.children[i + 1]);
            }
          }
          if (node.is_open) {
            return handle_after_open_folder(node, next_node, $element);
          }
        }
      };
      return iterate(this.tree_widget.tree);
    };

    DragAndDropHandler.prototype.findHoveredArea = function(x, y) {
      var area, high, low, mid, tree_offset;
      tree_offset = this.tree_widget.element.offset();
      if (x < tree_offset.left || y < tree_offset.top || x > (tree_offset.left + this.tree_widget.element.width()) || y > (tree_offset.top + this.tree_widget.element.height())) {
        return null;
      }
      low = 0;
      high = this.hit_areas.length;
      while (low < high) {
        mid = (low + high) >> 1;
        area = this.hit_areas[mid];
        if (y < area.top) {
          high = mid;
        } else if (y > area.bottom) {
          low = mid + 1;
        } else {
          return area;
        }
      }
      return null;
    };

    DragAndDropHandler.prototype.updateDropHint = function() {
      var node, node_element;
      this.stopOpenFolderTimer();
      if (!this.hovered_area) {
        return;
      }
      node = this.hovered_area.node;
      if (node.hasChildren() && !node.is_open && this.hovered_area.position === Position.INSIDE) {
        this.startOpenFolderTimer(node);
      }
      this.removeDropHint();
      node_element = this.tree_widget._getNodeElementForNode(this.hovered_area.node);
      return this.previous_ghost = node_element.addDropHint(this.hovered_area.position);
    };

    DragAndDropHandler.prototype.startOpenFolderTimer = function(folder) {
      var openFolder,
        _this = this;
      openFolder = function() {
        return _this.tree_widget._getNodeElementForNode(folder).open(function() {
          _this.refreshHitAreas();
          return _this.updateDropHint();
        });
      };
      return this.open_folder_timer = setTimeout(openFolder, 500);
    };

    DragAndDropHandler.prototype.stopOpenFolderTimer = function() {
      if (this.open_folder_timer) {
        clearTimeout(this.open_folder_timer);
        return this.open_folder_timer = null;
      }
    };

    DragAndDropHandler.prototype.moveItem = function() {
      var doMove, event, moved_node, position, previous_parent, target_node,
        _this = this;
      if (this.hovered_area && this.hovered_area.position !== Position.NONE) {
        moved_node = this.current_item.node;
        target_node = this.hovered_area.node;
        position = this.hovered_area.position;
        previous_parent = moved_node.parent;
        if (position === Position.INSIDE) {
          this.hovered_area.node.is_open = true;
        }
        doMove = function() {
          _this.tree_widget.tree.moveNode(moved_node, target_node, position);
          _this.tree_widget.element.empty();
          return _this.tree_widget._refreshElements();
        };
        event = $.Event('tree.move');
        event.move_info = {
          moved_node: moved_node,
          target_node: target_node,
          position: Position.getName(position),
          previous_parent: previous_parent,
          do_move: doMove
        };
        this.tree_widget.element.trigger(event);
        if (!event.isDefaultPrevented()) {
          return doMove();
        }
      }
    };

    return DragAndDropHandler;

  })();

  this.Tree.Node = Node;

}).call(this);