1. Shlomi Fish
  2. shlomi-fish-homepage

Commits

Shlomi Fish  committed 1672a3d

Implement jqTree for one of the pages.

Added some items for the TODO for what's next.

  • Participants
  • Parent commits e98ef62
  • Branches convert-from-jquery-treeview-to-jqtree-plugin

Comments (0)

Files changed (5)

File TODO

View file
+Pressing:
+---------
+
+* Extract the jQtree functionality from t2/humour/bits/index.html into
+a common place.
+
+* Tweak the CSS to make it nicer.
+
+* Convert the static navigation menu to the reduced one, so it will
+take less place.
+
+Not so pressing:
+----------------
+
 * Extract <bitbucket_cpan_dist_links> in lib/cpan_dists.wml from more places.
 
 * Fortunes Atom Feed:

File common/jqtree.css

View file
+/* From jqTree-0.14. */
+ul.jqtree-tree {
+    margin-left: 12px;
+}
+
+ul.jqtree-tree,
+ul.jqtree-tree ul.jqtree_common {
+    list-style: none outside;
+    margin-bottom: 0;
+    padding: 0;
+}
+
+ul.jqtree-tree ul.jqtree_common {
+    display: block;
+    margin-left: 12px;
+    margin-right: 0;
+}
+ul.jqtree-tree li.jqtree-closed > ul.jqtree_common {
+    display: none;
+}
+
+ul.jqtree-tree li.jqtree_common {
+    clear: both;
+}
+ul.jqtree-tree .jqtree-toggler {
+    display: block;
+    position: absolute;
+    left: -1.5em;
+    top: 30%;
+    *top: 0;  /* fix for ie7 */
+    font-size: 12px;
+    line-height: 12px;
+    font-family: arial;  /* fix for ie9 */
+    border-bottom: none;
+    color: #333;
+}
+
+ul.jqtree-tree .jqtree-toggler:hover {
+    color: #000;
+}
+
+ul.jqtree-tree div {
+    cursor: pointer;
+}
+
+ul.jqtree-tree .jqtree-title {
+    color: #1C4257;
+    vertical-align: middle;
+}
+
+ul.jqtree-tree li.jqtree-folder {
+    margin-bottom: 4px;
+}
+
+ul.jqtree-tree li.jqtree-folder.jqtree-closed {
+    margin-bottom: 1px;
+}
+
+ul.jqtree-tree li.jqtree-folder .jqtree-title {
+    margin-left: 0;
+}
+
+ul.jqtree-tree .jqtree-toggler.jqtree-closed {
+    background-position: 0 0;
+}
+
+span.jqtree-dragging {
+    color: #fff;
+    background: #000;
+    opacity: 0.6;
+    cursor: pointer;
+    padding: 2px 8px;
+}
+
+ul.jqtree-tree li.jqtree-ghost {
+    position: relative;
+    z-index: 10;
+    margin-right: 10px;
+}
+
+ul.jqtree-tree li.jqtree-ghost span {
+    display: block;
+}
+
+ul.jqtree-tree li.jqtree-ghost span.jqtree-circle {
+    background-image: url(jqtree-circle.png);
+    background-repeat: no-repeat;
+    height: 8px;
+    width: 8px;
+    position: absolute;
+    top: -4px;
+    left: 2px;
+}
+
+ul.jqtree-tree li.jqtree-ghost span.jqtree-line {
+    background-color: #0000ff;
+    height: 2px;
+    padding: 0;
+    position: absolute;
+    top: -1px;
+    left: 10px;
+    width: 100%;
+}
+
+ul.jqtree-tree li.jqtree-ghost.jqtree-inside {
+    margin-left: 48px;
+}
+
+ul.jqtree-tree span.jqtree-border {
+    position: absolute;
+    display: block;
+    left: -2px;
+    top: 0;
+    border: solid 2px #0000ff;
+    -webkit-border-radius: 6px;
+    -moz-border-radius: 6px;
+    border-radius: 6px;
+    margin: 0;
+}
+
+ul.jqtree-tree div {
+    width: 100%; /* todo: why is this in here? */
+    *width: auto; /* ie7 fix; issue 41 */
+    position: relative;
+}
+
+ul.jqtree-tree li.jqtree-selected > div,
+ul.jqtree-tree li.jqtree-selected > div:hover {
+    background-color: #97BDD6;
+    background: -webkit-gradient(linear, left top, left bottom, from(#BEE0F5), to(#89AFCA));
+    background: -moz-linear-gradient(top, #BEE0F5, #89AFCA);
+    background: -ms-linear-gradient(top, #BEE0F5, #89AFCA);
+    background: -o-linear-gradient(top, #BEE0F5, #89AFCA);
+    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
+}
+
+ul.jqtree-tree .jqtree-moving > div .jqtree-title {
+    outline: dashed 1px #0000ff;
+}

File common/js/tree.jquery.js

View file
+// Generated by CoffeeScript 1.4.0
+
+// From jqTree-0.14.0
+
+/*
+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, MouseWidget, Node, NodeElement, Position, SaveStateHandler, ScrollHandler, SelectNodeHandler, SimpleWidget, TRIANGLE_DOWN, TRIANGLE_RIGHT, Tree, html_escape, indexOf, json_escapable, json_meta, json_quote, json_str,
+    __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);
+    }
+
+    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, el, widget, _i, _len;
+        data_key = getDataKey();
+        for (_i = 0, _len = $el.length; _i < _len; _i++) {
+          el = $el[_i];
+          widget = new widget_class(el, options);
+          if (!$.data(el, data_key)) {
+            $.data(el, data_key, widget);
+          }
+          widget._init();
+        }
+        return $el;
+      };
+      destroyWidget = function($el) {
+        var data_key, el, widget, _i, _len, _results;
+        data_key = getDataKey();
+        _results = [];
+        for (_i = 0, _len = $el.length; _i < _len; _i++) {
+          el = $el[_i];
+          widget = $.data(el, data_key);
+          if (widget && (widget instanceof SimpleWidget)) {
+            widget.destroy();
+          }
+          _results.push($.removeData(el, data_key));
+        }
+        return _results;
+      };
+      callFunction = function($el, function_name, args) {
+        var el, result, widget, widget_function, _i, _len;
+        result = null;
+        for (_i = 0, _len = $el.length; _i < _len; _i++) {
+          el = $el[_i];
+          widget = $.data(el, getDataKey());
+          if (widget && (widget instanceof SimpleWidget)) {
+            widget_function = widget[function_name];
+            if (widget_function && (typeof widget_function === 'function')) {
+              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));
+      this.is_mouse_started = false;
+      this.mouse_delay = 0;
+      this._mouse_delay_timer = null;
+      return this._is_mouse_delay_met = true;
+    };
+
+    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));
+      if (this.mouse_delay) {
+        this._startMouseDelayTimer();
+      }
+      e.preventDefault();
+      this.is_mouse_handled = true;
+      return true;
+    };
+
+    MouseWidget.prototype._startMouseDelayTimer = function() {
+      var _this = this;
+      if (this._mouse_delay_timer) {
+        clearTimeout(this._mouse_delay_timer);
+      }
+      this._mouse_delay_timer = setTimeout(function() {
+        return _this._is_mouse_delay_met = true;
+      }, this.mouse_delay);
+      return this._is_mouse_delay_met = false;
+    };
+
+    MouseWidget.prototype._mouseMove = function(e) {
+      if (this.is_mouse_started) {
+        this._mouseDrag(e);
+        return e.preventDefault();
+      }
+      if (this.mouse_delay && !this._is_mouse_delay_met) {
+        return true;
+      }
+      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;
+
+  if (!((this.JSON != null) && (this.JSON.stringify != null) && typeof this.JSON.stringify === 'function')) {
+    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(',') + '}');
+      }
+    };
+    if (!(this.JSON != null)) {
+      this.JSON = {};
+    }
+    this.JSON.stringify = function(value) {
+      return json_str('', {
+        '': value
+      });
+    };
+  }
+
+  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) {
+      return Position.strings[position - 1];
+    },
+    nameToIndex: function(name) {
+      var i, _i, _ref;
+      for (i = _i = 1, _ref = Position.strings.length; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) {
+        if (Position.strings[i - 1] === name) {
+          return i;
+        }
+      }
+      return 0;
+    }
+  };
+
+  Position.BEFORE = 1;
+
+  Position.AFTER = 2;
+
+  Position.INSIDE = 3;
+
+  Position.NONE = 4;
+
+  Position.strings = ['before', 'after', 'inside', 'none'];
+
+  this.Tree.Position = Position;
+
+  Node = (function() {
+
+    function Node(o) {
+      this.setData(o);
+      this.children = [];
+      this.parent = null;
+    }
+
+    Node.prototype.setData = function(o) {
+      var key, value, _results;
+      if (typeof o !== 'object') {
+        return this.name = o;
+      } else {
+        _results = [];
+        for (key in o) {
+          value = o[key];
+          if (key === 'label') {
+            _results.push(this.name = value);
+          } else {
+            _results.push(this[key] = value);
+          }
+        }
+        return _results;
+      }
+    };
+
+    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) {
+      var _this = this;
+      node.iterate(function(child) {
+        _this.tree.removeNodeFromIndex(child);
+        return true;
+      });
+      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;
+    };
+
+    Node.prototype.isFolder = function() {
+      return this.hasChildren() || this.load_on_demand;
+    };
+
+    /*
+        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) {
+      if (moved_node.isParentOf(target_node)) {
+        return;
+      }
+      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;
+    };
+
+    Node.prototype.isParentOf = function(node) {
+      var parent;
+      parent = node.parent;
+      while (parent) {
+        if (parent === this) {
+          return true;
+        }
+        parent = parent.parent;
+      }
+      return false;
+    };
+
+    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;
+
+  this.Tree.Node = Node;
+
+  TRIANGLE_RIGHT = '&#x25ba;';
+
+  TRIANGLE_DOWN = '&#x25bc;';
+
+  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,
+      slide: true
+    };
+
+    JqTreeWidget.prototype.toggle = function(node, slide) {
+      if (slide == null) {
+        slide = true;
+      }
+      if (node.is_open) {
+        return this.closeNode(node, slide);
+      } else {
+        return this.openNode(node, slide);
+      }
+    };
+
+    JqTreeWidget.prototype.getTree = function() {
+      return this.tree;
+    };
+
+    JqTreeWidget.prototype.selectNode = function(node) {
+      return this.select_node_handler.selectNode(node);
+    };
+
+    JqTreeWidget.prototype.getSelectedNode = function() {
+      return this.selected_node || false;
+    };
+
+    JqTreeWidget.prototype.toJson = function() {
+      return JSON.stringify(this.tree.getData());
+    };
+
+    JqTreeWidget.prototype.loadData = function(data, parent_node) {
+      return this._loadData(data, parent_node);
+    };
+
+    JqTreeWidget.prototype.loadDataFromUrl = function(url_info, parent_node, on_finished) {
+      var $li, addLoadingClass, parseUrlInfo, removeLoadingClass,
+        _this = this;
+      $li = null;
+      addLoadingClass = function() {
+        var folder_element;
+        if (parent_node) {
+          folder_element = new FolderElement(parent_node, _this);
+          $li = folder_element.getLi();
+          return $li.addClass('jqtree-loading');
+        }
+      };
+      removeLoadingClass = function() {
+        if ($li) {
+          return $li.removeClass('loading');
+        }
+      };
+      parseUrlInfo = function() {
+        if ($.type(url_info) === 'string') {
+          url_info = {
+            url: url_info
+          };
+        }
+        if (!url_info.method) {
+          return url_info.method = 'get';
+        }
+      };
+      addLoadingClass();
+      parseUrlInfo();
+      return $.ajax({
+        url: url_info.url,
+        data: url_info.data,
+        type: url_info.method.toUpperCase(),
+        cache: false,
+        dataType: 'json',
+        success: function(response) {
+          var data;
+          if ($.isArray(response) || typeof response === 'object') {
+            data = response;
+          } else {
+            data = $.parseJSON(response);
+          }
+          removeLoadingClass();
+          _this._loadData(data, parent_node);
+          if (on_finished) {
+            return on_finished();
+          }
+        }
+      });
+    };
+
+    JqTreeWidget.prototype._loadData = function(data, parent_node) {
+      var child, subtree, _i, _len, _ref;
+      this._triggerEvent('tree.load_data', {
+        tree_data: data
+      });
+      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, slide) {
+      if (slide == null) {
+        slide = true;
+      }
+      return this._openNode(node, slide);
+    };
+
+    JqTreeWidget.prototype._openNode = function(node, slide, on_finished) {
+      var doOpenNode, parent,
+        _this = this;
+      if (slide == null) {
+        slide = true;
+      }
+      doOpenNode = function(_node, _slide, _on_finished) {
+        var folder_element;
+        folder_element = new FolderElement(_node, _this);
+        return folder_element.open(_on_finished, _slide);
+      };
+      if (node.isFolder()) {
+        if (node.load_on_demand) {
+          return this._loadFolderOnDemand(node, slide, on_finished);
+        } else {
+          parent = node.parent;
+          while (parent && !parent.is_open) {
+            if (parent.parent) {
+              doOpenNode(parent, false, null);
+            }
+            parent = parent.parent;
+          }
+          doOpenNode(node, slide, on_finished);
+          return this._saveState();
+        }
+      }
+    };
+
+    JqTreeWidget.prototype._loadFolderOnDemand = function(node, slide, on_finished) {
+      var _this = this;
+      if (slide == null) {
+        slide = true;
+      }
+      node.load_on_demand = false;
+      return this.loadDataFromUrl(this._getDataUrlInfo(node), node, function() {
+        return _this._openNode(node, slide, on_finished);
+      });
+    };
+
+    JqTreeWidget.prototype.closeNode = function(node, slide) {
+      if (slide == null) {
+        slide = true;
+      }
+      if (node.isFolder()) {
+        new FolderElement(node, this).close(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 mustUnselectedNode, parent,
+        _this = this;
+      mustUnselectedNode = function() {
+        var result;
+        if (!_this.selected_node) {
+          return false;
+        } else if (_this.selected_node === node) {
+          return true;
+        } else {
+          result = true;
+          iterate(function(child) {
+            if (node === child) {
+              result = true;
+              return false;
+            } else {
+              return true;
+            }
+          });
+          return result;
+        }
+      };
+      parent = node.parent;
+      if (parent) {
+        if (mustUnselectedNode()) {
+          this.selected_node = null;
+        }
+        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.isFolder();
+      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.updateNode = function(node, data) {
+      node.setData(data);
+      this._refreshElements(node.parent);
+      return this.select_node_handler.selectCurrentNode();
+    };
+
+    JqTreeWidget.prototype.moveNode = function(node, target_node, position) {
+      var position_index;
+      position_index = Position.nameToIndex(position);
+      this.tree.moveNode(node, target_node, position_index);
+      return this._refreshElements();
+    };
+
+    JqTreeWidget.prototype.getStateFromStorage = function() {
+      return this.save_state_handler.getStateFromStorage();
+    };
+
+    JqTreeWidget.prototype._init = function() {
+      JqTreeWidget.__super__._init.call(this);
+      this.element = this.$el;
+      this.selected_node = null;
+      this.mouse_delay = 300;
+      this.save_state_handler = new SaveStateHandler(this);
+      this.select_node_handler = new SelectNodeHandler(this);
+      this.dnd_handler = new DragAndDropHandler(this);
+      this.scroll_handler = new ScrollHandler(this);
+      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() {
+      if (this.options.data) {
+        return this.loadData(this.options.data);
+      } else {
+        return this.loadDataFromUrl(this._getDataUrlInfo());
+      }
+    };
+
+    JqTreeWidget.prototype._getDataUrlInfo = function(node) {
+      var data, data_url, url_info;
+      data_url = this.options.dataUrl || this.element.data('url');
+      if ($.isFunction(data_url)) {
+        return data_url(node);
+      } else if ($.type(data_url) === 'string') {
+        url_info = {
+          url: data_url
+        };
+        if (node && node.id) {
+          data = {
+            node: node.id
+          };
+          url_info['data'] = data;
+        }
+        return url_info;
+      } else {
+        return data_url;
+      }
+    };
+
+    JqTreeWidget.prototype._initTree = function(data) {
+      this.tree = new Tree();
+      this.tree.loadFromData(data);
+      this._openNodes();
+      this._refreshElements();
+      this.select_node_handler.selectCurrentNode();
+      return this._triggerEvent('tree.init');
+    };
+
+    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 = 'jqtree-tree';
+        } else {
+          class_string = '';
+        }
+        return $("<ul class=\"jqtree_common " + class_string + "\"></ul>");
+      };
+      createLi = function(node) {
+        var $li;
+        if (node.isFolder()) {
+          $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 class=\"jqtree_common\"><div><span class=\"jqtree-title jqtree_common\">" + escaped_name + "</span></div></li>");
+      };
+      createFolderLi = function(node) {
+        var button_char, button_classes, escaped_name, folder_classes, getButtonClasses, getFolderClasses;
+        getButtonClasses = function() {
+          var classes;
+          classes = ['jqtree-toggler'];
+          if (!node.is_open) {
+            classes.push('jqtree-closed');
+          }
+          return classes.join(' ');
+        };
+        getFolderClasses = function() {
+          var classes;
+          classes = ['jqtree-folder'];
+          if (!node.is_open) {
+            classes.push('jqtree-closed');
+          }
+          return classes.join(' ');
+        };
+        button_classes = getButtonClasses();
+        folder_classes = getFolderClasses();
+        escaped_name = escapeIfNecessary(node.name);
+        if (node.is_open) {
+          button_char = TRIANGLE_DOWN;
+        } else {
+          button_char = TRIANGLE_RIGHT;
+        }
+        return $("<li class=\"jqtree_common " + folder_classes + "\"><div><a class=\"jqtree_common " + button_classes + "\">" + button_char + "</a><span class=\"jqtree_common jqtree-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;
+      }
+      doCreateDomElements($element, from_node.children, is_root_node, is_root_node);
+      return this._triggerEvent('tree.refresh');
+    };
+
+    JqTreeWidget.prototype._click = function(e) {
+      var $button, $target, $title, node;
+      if (e.ctrlKey) {
+        return;
+      }
+      $target = $(e.target);
+      $title = $target.closest('.jqtree-title');
+      if ($title.length) {
+        node = this._getNode($title);
+        if (node) {
+          this._triggerEvent('tree.click', {
+            node: node
+          });
+          return this.selectNode(node);
+        }
+      } else {
+        $button = $target.closest('.jqtree-toggler');
+        if ($button.length) {
+          node = this._getNode($button);
+          if (node) {
+            this.toggle(node, this.options.slide);
+            e.preventDefault();
+            return e.stopPropagation();
+          }
+        }
+      }
+    };
+
+    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.isFolder()) {
+        return new FolderElement(node, this);
+      } else {
+        return new NodeElement(node, this);
+      }
+    };
+
+    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, node;
+      $div = $(e.target).closest('ul.jqtree-tree div');
+      if ($div.length) {
+        node = this._getNode($div);
+        if (node) {
+          e.preventDefault();
+          e.stopPropagation();
+          this._triggerEvent('tree.contextmenu', {
+            node: node,
+            click_event: e
+          });
+          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) {
+      var result;
+      if (this.options.dragAndDrop) {
+        result = this.dnd_handler.mouseDrag(event);
+        this.scroll_handler.checkScrolling();
+        return result;
+      } else {
+        return false;
+      }
+    };
+
+    JqTreeWidget.prototype._mouseStop = function() {
+      if (this.options.dragAndDrop) {
+        return this.dnd_handler.mouseStop();
+      } else {
+        return false;
+      }
+    };
+
+    JqTreeWidget.prototype._triggerEvent = function(event_name, values) {
+      var event;
+      event = $.Event(event_name);
+      $.extend(event, values);
+      this.element.trigger(event);
+      return event;
+    };
+
+    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="jqtree_common jqtree-ghost"><span class="jqtree_common jqtree-circle"></span><span class="jqtree_common jqtree-line"></span></li>');
+      if (position === Position.AFTER) {
+        this.moveAfter();
+      } else if (position === Position.BEFORE) {
+        this.moveBefore();
+      } else if (position === Position.INSIDE) {
+        if (node.isFolder() && 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('jqtree-inside');
+    };
+
+    return GhostDropHint;
+
+  })();
+
+  BorderDropHint = (function() {
+
+    function BorderDropHint($element) {
+      var $div, width;
+      $div = $element.children('div');
+      width = $element.width() - 4;
+      this.$hint = $('<span class="jqtree-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_widget) {
+      this.init(node, tree_widget);
+    }
+
+    NodeElement.prototype.init = function(node, tree_widget) {
+      this.node = node;
+      this.tree_widget = tree_widget;
+      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.jqtree-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('jqtree-selected');
+    };
+
+    NodeElement.prototype.deselect = function() {
+      return this.getLi().removeClass('jqtree-selected');
+    };
+
+    return NodeElement;
+
+  })();
+
+  FolderElement = (function(_super) {
+
+    __extends(FolderElement, _super);
+
+    function FolderElement() {
+      return FolderElement.__super__.constructor.apply(this, arguments);
+    }
+
+    FolderElement.prototype.open = function(on_finished, slide) {
+      var $button, doOpen,
+        _this = this;
+      if (slide == null) {
+        slide = true;
+      }
+      if (!this.node.is_open) {
+        this.node.is_open = true;
+        $button = this.getButton();
+        $button.removeClass('jqtree-closed');
+        $button.html(TRIANGLE_DOWN);
+        doOpen = function() {
+          _this.getLi().removeClass('jqtree-closed');
+          if (on_finished) {
+            on_finished();
+          }
+          return _this.tree_widget._triggerEvent('tree.open', {
+            node: _this.node
+          });
+        };
+        if (slide) {
+          return this.getUl().slideDown('fast', doOpen);
+        } else {
+          this.getUl().show();
+          return doOpen();
+        }
+      }
+    };
+
+    FolderElement.prototype.close = function(slide) {
+      var $button, doClose,
+        _this = this;
+      if (slide == null) {
+        slide = true;
+      }
+      if (this.node.is_open) {
+        this.node.is_open = false;
+        $button = this.getButton();
+        $button.addClass('jqtree-closed');
+        $button.html(TRIANGLE_RIGHT);
+        doClose = function() {
+          _this.getLi().addClass('jqtree-closed');
+          return _this.tree_widget._triggerEvent('tree.close', {
+            node: _this.node
+          });
+        };
+        if (slide) {
+          return this.getUl().slideUp('fast', doClose);
+        } else {
+          this.getUl().hide();
+          return doClose();
+        }
+      }
+    };
+
+    FolderElement.prototype.getButton = function() {
+      return this.$element.children('div').find('a.jqtree-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=\"jqtree-title jqtree-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;
+      state = this.getStateFromStorage();
+      if (state) {
+        this.setState(state);
+        return true;
+      } else {
+        return false;
+      }
+    };
+
+    SaveStateHandler.prototype.getStateFromStorage = function() {
+      if (this.tree_widget.options.onGetStateFromStorage) {
+        return this.tree_widget.options.onGetStateFromStorage();
+      } else if (typeof localStorage !== "undefined" && localStorage !== null) {
+        return localStorage.getItem(this.getCookieName());
+      } else if ($.cookie) {
+        return $.cookie(this.getCookieName(), {
+          path: '/'
+        });
+      } else {
+        return null;
+      }
+    };
+
+    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 JSON.stringify({
+        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) {
+      var canSelect, parent,
+        _this = this;
+      canSelect = function() {
+        if (!_this.tree_widget.options.onCanSelectNode) {
+          return true;
+        }
+        return _this.tree_widget.options.onCanSelectNode(node);
+      };
+      if (canSelect()) {
+        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;
+          this.tree_widget._triggerEvent('tree.select', {
+            node: node
+          });
+          parent = this.tree_widget.selected_node.parent;
+          if (!parent.is_open) {
+            this.tree_widget.openNode(parent, false);
+          }
+        }
+        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('jqtree-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('jqtree-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;
+      }