http://git-wip-us.apache.org/repos/asf/oodt/blob/50df1e1d/webapp/curator/src/main/webapp/lib/jstree.js
----------------------------------------------------------------------
diff --git a/webapp/curator/src/main/webapp/lib/jstree.js 
b/webapp/curator/src/main/webapp/lib/jstree.js
new file mode 100755
index 0000000..20ef21f
--- /dev/null
+++ b/webapp/curator/src/main/webapp/lib/jstree.js
@@ -0,0 +1,7428 @@
+/*globals jQuery, define, exports, require, window, document, postMessage */
+(function (factory) {
+       "use strict";
+       if (typeof define === 'function' && define.amd) {
+               define(['jquery'], factory);
+       }
+       else if(typeof exports === 'object') {
+               factory(require('jquery'));
+       }
+       else {
+               factory(jQuery);
+       }
+}(function ($, undefined) {
+       "use strict";
+/*!
+ * jsTree 3.1.1
+ * http://jstree.com/
+ *
+ * Copyright (c) 2014 Ivan Bozhanov (http://vakata.com)
+ *
+ * Licensed same as jquery - under the terms of the MIT License
+ *   http://www.opensource.org/licenses/mit-license.php
+ */
+/*!
+ * if using jslint please allow for the jQuery global and use following 
options:
+ * jslint: browser: true, ass: true, bitwise: true, continue: true, nomen: 
true, plusplus: true, regexp: true, unparam: true, todo: true, white: true
+ */
+
+       // prevent another load? maybe there is a better way?
+       if($.jstree) {
+               return;
+       }
+
+       /**
+        * ### jsTree core functionality
+        */
+
+       // internal variables
+       var instance_counter = 0,
+               ccp_node = false,
+               ccp_mode = false,
+               ccp_inst = false,
+               themes_loaded = [],
+               src = $('script:last').attr('src'),
+               document = window.document, // local variable is always faster 
to access then a global
+               _node = document.createElement('LI'), _temp1, _temp2;
+
+       _node.setAttribute('role', 'treeitem');
+       _temp1 = document.createElement('I');
+       _temp1.className = 'jstree-icon jstree-ocl';
+       _temp1.setAttribute('role', 'presentation');
+       _node.appendChild(_temp1);
+       _temp1 = document.createElement('A');
+       _temp1.className = 'jstree-anchor';
+       _temp1.setAttribute('href','#');
+       _temp1.setAttribute('tabindex','-1');
+       _temp2 = document.createElement('I');
+       _temp2.className = 'jstree-icon jstree-themeicon';
+       _temp2.setAttribute('role', 'presentation');
+       _temp1.appendChild(_temp2);
+       _node.appendChild(_temp1);
+       _temp1 = _temp2 = null;
+
+
+       /**
+        * holds all jstree related functions and variables, including the 
actual class and methods to create, access and manipulate instances.
+        * @name $.jstree
+        */
+       $.jstree = {
+               /**
+                * specifies the jstree version in use
+                * @name $.jstree.version
+                */
+               version : '3.1.1',
+               /**
+                * holds all the default options used when creating new 
instances
+                * @name $.jstree.defaults
+                */
+               defaults : {
+                       /**
+                        * configure which plugins will be active on an 
instance. Should be an array of strings, where each element is a plugin name. 
The default is `[]`
+                        * @name $.jstree.defaults.plugins
+                        */
+                       plugins : []
+               },
+               /**
+                * stores all loaded jstree plugins (used internally)
+                * @name $.jstree.plugins
+                */
+               plugins : {},
+               path : src && src.indexOf('/') !== -1 ? 
src.replace(/\/[^\/]+$/,'') : '',
+               idregex : /[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g
+       };
+       /**
+        * creates a jstree instance
+        * @name $.jstree.create(el [, options])
+        * @param {DOMElement|jQuery|String} el the element to create the 
instance on, can be jQuery extended or a selector
+        * @param {Object} options options for this instance (extends 
`$.jstree.defaults`)
+        * @return {jsTree} the new instance
+        */
+       $.jstree.create = function (el, options) {
+               var tmp = new $.jstree.core(++instance_counter),
+                       opt = options;
+               options = $.extend(true, {}, $.jstree.defaults, options);
+               if(opt && opt.plugins) {
+                       options.plugins = opt.plugins;
+               }
+               $.each(options.plugins, function (i, k) {
+                       if(i !== 'core') {
+                               tmp = tmp.plugin(k, options[k]);
+                       }
+               });
+               $(el).data('jstree', tmp);
+               tmp.init(el, options);
+               return tmp;
+       };
+       /**
+        * remove all traces of jstree from the DOM and destroy all instances
+        * @name $.jstree.destroy()
+        */
+       $.jstree.destroy = function () {
+               $('.jstree:jstree').jstree('destroy');
+               $(document).off('.jstree');
+       };
+       /**
+        * the jstree class constructor, used only internally
+        * @private
+        * @name $.jstree.core(id)
+        * @param {Number} id this instance's index
+        */
+       $.jstree.core = function (id) {
+               this._id = id;
+               this._cnt = 0;
+               this._wrk = null;
+               this._data = {
+                       core : {
+                               themes : {
+                                       name : false,
+                                       dots : false,
+                                       icons : false
+                               },
+                               selected : [],
+                               last_error : {},
+                               working : false,
+                               worker_queue : [],
+                               focused : null
+                       }
+               };
+       };
+       /**
+        * get a reference to an existing instance
+        *
+        * __Examples__
+        *
+        *      // provided a container with an ID of "tree", and a nested node 
with an ID of "branch"
+        *      // all of there will return the same instance
+        *      $.jstree.reference('tree');
+        *      $.jstree.reference('#tree');
+        *      $.jstree.reference($('#tree'));
+        *      $.jstree.reference(document.getElementByID('tree'));
+        *      $.jstree.reference('branch');
+        *      $.jstree.reference('#branch');
+        *      $.jstree.reference($('#branch'));
+        *      $.jstree.reference(document.getElementByID('branch'));
+        *
+        * @name $.jstree.reference(needle)
+        * @param {DOMElement|jQuery|String} needle
+        * @return {jsTree|null} the instance or `null` if not found
+        */
+       $.jstree.reference = function (needle) {
+               var tmp = null,
+                       obj = null;
+               if(needle && needle.id && (!needle.tagName || 
!needle.nodeType)) { needle = needle.id; }
+
+               if(!obj || !obj.length) {
+                       try { obj = $(needle); } catch (ignore) { }
+               }
+               if(!obj || !obj.length) {
+                       try { obj = $('#' + 
needle.replace($.jstree.idregex,'\\$&')); } catch (ignore) { }
+               }
+               if(obj && obj.length && (obj = obj.closest('.jstree')).length 
&& (obj = obj.data('jstree'))) {
+                       tmp = obj;
+               }
+               else {
+                       $('.jstree').each(function () {
+                               var inst = $(this).data('jstree');
+                               if(inst && inst._model.data[needle]) {
+                                       tmp = inst;
+                                       return false;
+                               }
+                       });
+               }
+               return tmp;
+       };
+       /**
+        * Create an instance, get an instance or invoke a command on a 
instance.
+        *
+        * If there is no instance associated with the current node a new one 
is created and `arg` is used to extend `$.jstree.defaults` for this new 
instance. There would be no return value (chaining is not broken).
+        *
+        * If there is an existing instance and `arg` is a string the command 
specified by `arg` is executed on the instance, with any additional arguments 
passed to the function. If the function returns a value it will be returned 
(chaining could break depending on function).
+        *
+        * If there is an existing instance and `arg` is not a string the 
instance itself is returned (similar to `$.jstree.reference`).
+        *
+        * In any other case - nothing is returned and chaining is not broken.
+        *
+        * __Examples__
+        *
+        *      $('#tree1').jstree(); // creates an instance
+        *      $('#tree2').jstree({ plugins : [] }); // create an instance 
with some options
+        *      $('#tree1').jstree('open_node', '#branch_1'); // call a method 
on an existing instance, passing additional arguments
+        *      $('#tree2').jstree(); // get an existing instance (or create an 
instance)
+        *      $('#tree2').jstree(true); // get an existing instance (will not 
create new instance)
+        *      $('#branch_1').jstree().select_node('#branch_1'); // get an 
instance (using a nested element and call a method)
+        *
+        * @name $().jstree([arg])
+        * @param {String|Object} arg
+        * @return {Mixed}
+        */
+       $.fn.jstree = function (arg) {
+               // check for string argument
+               var is_method   = (typeof arg === 'string'),
+                       args            = Array.prototype.slice.call(arguments, 
1),
+                       result          = null;
+               if(arg === true && !this.length) { return false; }
+               this.each(function () {
+                       // get the instance (if there is one) and method (if it 
exists)
+                       var instance = $.jstree.reference(this),
+                               method = is_method && instance ? instance[arg] 
: null;
+                       // if calling a method, and method is available - 
execute on the instance
+                       result = is_method && method ?
+                               method.apply(instance, args) :
+                               null;
+                       // if there is no instance and no method is being 
called - create one
+                       if(!instance && !is_method && (arg === undefined || 
$.isPlainObject(arg))) {
+                               $.jstree.create(this, arg);
+                       }
+                       // if there is an instance and no method is called - 
return the instance
+                       if( (instance && !is_method) || arg === true ) {
+                               result = instance || false;
+                       }
+                       // if there was a method call which returned a result - 
break and return the value
+                       if(result !== null && result !== undefined) {
+                               return false;
+                       }
+               });
+               // if there was a method call with a valid return value - 
return that, otherwise continue the chain
+               return result !== null && result !== undefined ?
+                       result : this;
+       };
+       /**
+        * used to find elements containing an instance
+        *
+        * __Examples__
+        *
+        *      $('div:jstree').each(function () {
+        *              $(this).jstree('destroy');
+        *      });
+        *
+        * @name $(':jstree')
+        * @return {jQuery}
+        */
+       $.expr[':'].jstree = $.expr.createPseudo(function(search) {
+               return function(a) {
+                       return $(a).hasClass('jstree') &&
+                               $(a).data('jstree') !== undefined;
+               };
+       });
+
+       /**
+        * stores all defaults for the core
+        * @name $.jstree.defaults.core
+        */
+       $.jstree.defaults.core = {
+               /**
+                * data configuration
+                *
+                * If left as `false` the HTML inside the jstree container 
element is used to populate the tree (that should be an unordered list with 
list items).
+                *
+                * You can also pass in a HTML string or a JSON array here.
+                *
+                * It is possible to pass in a standard jQuery-like AJAX config 
and jstree will automatically determine if the response is JSON or HTML and use 
that to populate the tree.
+                * In addition to the standard jQuery ajax options here you can 
suppy functions for `data` and `url`, the functions will be run in the current 
instance's scope and a param will be passed indicating which node is being 
loaded, the return value of those functions will be used.
+                *
+                * The last option is to specify a function, that function will 
receive the node being loaded as argument and a second param which is a 
function which should be called with the result.
+                *
+                * __Examples__
+                *
+                *      // AJAX
+                *      $('#tree').jstree({
+                *              'core' : {
+                *                      'data' : {
+                *                              'url' : '/get/children/',
+                *                              'data' : function (node) {
+                *                                      return { 'id' : node.id 
};
+                *                              }
+                *                      }
+                *              });
+                *
+                *      // direct data
+                *      $('#tree').jstree({
+                *              'core' : {
+                *                      'data' : [
+                *                              'Simple root node',
+                *                              {
+                *                                      'id' : 'node_2',
+                *                                      'text' : 'Root node 
with options',
+                *                                      'state' : { 'opened' : 
true, 'selected' : true },
+                *                                      'children' : [ { 'text' 
: 'Child 1' }, 'Child 2']
+                *                              }
+                *                      ]
+                *              });
+                *
+                *      // function
+                *      $('#tree').jstree({
+                *              'core' : {
+                *                      'data' : function (obj, callback) {
+                *                              callback.call(this, ['Root 1', 
'Root 2']);
+                *                      }
+                *              });
+                *
+                * @name $.jstree.defaults.core.data
+                */
+               data                    : false,
+               /**
+                * configure the various strings used throughout the tree
+                *
+                * You can use an object where the key is the string you need 
to replace and the value is your replacement.
+                * Another option is to specify a function which will be called 
with an argument of the needed string and should return the replacement.
+                * If left as `false` no replacement is made.
+                *
+                * __Examples__
+                *
+                *      $('#tree').jstree({
+                *              'core' : {
+                *                      'strings' : {
+                *                              'Loading ...' : 'Please wait 
...'
+                *                      }
+                *              }
+                *      });
+                *
+                * @name $.jstree.defaults.core.strings
+                */
+               strings                 : false,
+               /**
+                * determines what happens when a user tries to modify the 
structure of the tree
+                * If left as `false` all operations like create, rename, 
delete, move or copy are prevented.
+                * You can set this to `true` to allow all interactions or use 
a function to have better control.
+                *
+                * __Examples__
+                *
+                *      $('#tree').jstree({
+                *              'core' : {
+                *                      'check_callback' : function (operation, 
node, node_parent, node_position, more) {
+                *                              // operation can be 
'create_node', 'rename_node', 'delete_node', 'move_node' or 'copy_node'
+                *                              // in case of 'rename_node' 
node_position is filled with the new node name
+                *                              return operation === 
'rename_node' ? true : false;
+                *                      }
+                *              }
+                *      });
+                *
+                * @name $.jstree.defaults.core.check_callback
+                */
+               check_callback  : false,
+               /**
+                * a callback called with a single object parameter in the 
instance's scope when something goes wrong (operation prevented, ajax failed, 
etc)
+                * @name $.jstree.defaults.core.error
+                */
+               error                   : $.noop,
+               /**
+                * the open / close animation duration in milliseconds - set 
this to `false` to disable the animation (default is `200`)
+                * @name $.jstree.defaults.core.animation
+                */
+               animation               : 200,
+               /**
+                * a boolean indicating if multiple nodes can be selected
+                * @name $.jstree.defaults.core.multiple
+                */
+               multiple                : true,
+               /**
+                * theme configuration object
+                * @name $.jstree.defaults.core.themes
+                */
+               themes                  : {
+                       /**
+                        * the name of the theme to use (if left as `false` the 
default theme is used)
+                        * @name $.jstree.defaults.core.themes.name
+                        */
+                       name                    : false,
+                       /**
+                        * the URL of the theme's CSS file, leave this as 
`false` if you have manually included the theme CSS (recommended). You can set 
this to `true` too which will try to autoload the theme.
+                        * @name $.jstree.defaults.core.themes.url
+                        */
+                       url                             : false,
+                       /**
+                        * the location of all jstree themes - only used if 
`url` is set to `true`
+                        * @name $.jstree.defaults.core.themes.dir
+                        */
+                       dir                             : false,
+                       /**
+                        * a boolean indicating if connecting dots are shown
+                        * @name $.jstree.defaults.core.themes.dots
+                        */
+                       dots                    : true,
+                       /**
+                        * a boolean indicating if node icons are shown
+                        * @name $.jstree.defaults.core.themes.icons
+                        */
+                       icons                   : true,
+                       /**
+                        * a boolean indicating if the tree background is 
striped
+                        * @name $.jstree.defaults.core.themes.stripes
+                        */
+                       stripes                 : false,
+                       /**
+                        * a string (or boolean `false`) specifying the theme 
variant to use (if the theme supports variants)
+                        * @name $.jstree.defaults.core.themes.variant
+                        */
+                       variant                 : false,
+                       /**
+                        * a boolean specifying if a reponsive version of the 
theme should kick in on smaller screens (if the theme supports it). Defaults to 
`false`.
+                        * @name $.jstree.defaults.core.themes.responsive
+                        */
+                       responsive              : false
+               },
+               /**
+                * if left as `true` all parents of all selected nodes will be 
opened once the tree loads (so that all selected nodes are visible to the user)
+                * @name $.jstree.defaults.core.expand_selected_onload
+                */
+               expand_selected_onload : true,
+               /**
+                * if left as `true` web workers will be used to parse incoming 
JSON data where possible, so that the UI will not be blocked by large requests. 
Workers are however about 30% slower. Defaults to `true`
+                * @name $.jstree.defaults.core.worker
+                */
+               worker : true,
+               /**
+                * Force node text to plain text (and escape HTML). Defaults to 
`false`
+                * @name $.jstree.defaults.core.force_text
+                */
+               force_text : false,
+               /**
+                * Should the node should be toggled if the text is double 
clicked . Defaults to `true`
+                * @name $.jstree.defaults.core.dblclick_toggle
+                */
+               dblclick_toggle : true
+       };
+       $.jstree.core.prototype = {
+               /**
+                * used to decorate an instance with a plugin. Used internally.
+                * @private
+                * @name plugin(deco [, opts])
+                * @param  {String} deco the plugin to decorate with
+                * @param  {Object} opts options for the plugin
+                * @return {jsTree}
+                */
+               plugin : function (deco, opts) {
+                       var Child = $.jstree.plugins[deco];
+                       if(Child) {
+                               this._data[deco] = {};
+                               Child.prototype = this;
+                               return new Child(opts, this);
+                       }
+                       return this;
+               },
+               /**
+                * initialize the instance. Used internally.
+                * @private
+                * @name init(el, optons)
+                * @param {DOMElement|jQuery|String} el the element we are 
transforming
+                * @param {Object} options options for this instance
+                * @trigger init.jstree, loading.jstree, loaded.jstree, 
ready.jstree, changed.jstree
+                */
+               init : function (el, options) {
+                       this._model = {
+                               data : {
+                                       '#' : {
+                                               id : '#',
+                                               parent : null,
+                                               parents : [],
+                                               children : [],
+                                               children_d : [],
+                                               state : { loaded : false }
+                                       }
+                               },
+                               changed : [],
+                               force_full_redraw : false,
+                               redraw_timeout : false,
+                               default_state : {
+                                       loaded : true,
+                                       opened : false,
+                                       selected : false,
+                                       disabled : false
+                               }
+                       };
+
+                       this.element = $(el).addClass('jstree jstree-' + 
this._id);
+                       this.settings = options;
+
+                       this._data.core.ready = false;
+                       this._data.core.loaded = false;
+                       this._data.core.rtl = (this.element.css("direction") 
=== "rtl");
+                       this.element[this._data.core.rtl ? 'addClass' : 
'removeClass']("jstree-rtl");
+                       this.element.attr('role','tree');
+                       if(this.settings.core.multiple) {
+                               this.element.attr('aria-multiselectable', true);
+                       }
+                       if(!this.element.attr('tabindex')) {
+                               this.element.attr('tabindex','0');
+                       }
+
+                       this.bind();
+                       /**
+                        * triggered after all events are bound
+                        * @event
+                        * @name init.jstree
+                        */
+                       this.trigger("init");
+
+                       this._data.core.original_container_html = 
this.element.find(" > ul > li").clone(true);
+                       this._data.core.original_container_html
+                               .find("li").addBack()
+                               .contents().filter(function() {
+                                       return this.nodeType === 3 && 
(!this.nodeValue || /^\s+$/.test(this.nodeValue));
+                               })
+                               .remove();
+                       this.element.html("<"+"ul class='jstree-container-ul 
jstree-children' role='group'><"+"li id='j"+this._id+"_loading' 
class='jstree-initial-node jstree-loading jstree-leaf jstree-last' 
role='tree-item'><i class='jstree-icon jstree-ocl'></i><"+"a 
class='jstree-anchor' href='#'><i class='jstree-icon 
jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + 
"</a></li></ul>");
+                       this.element.attr('aria-activedescendant','j' + 
this._id + '_loading');
+                       this._data.core.li_height = 
this.get_container_ul().children("li").first().height() || 24;
+                       /**
+                        * triggered after the loading text is shown and before 
loading starts
+                        * @event
+                        * @name loading.jstree
+                        */
+                       this.trigger("loading");
+                       this.load_node('#');
+               },
+               /**
+                * destroy an instance
+                * @name destroy()
+                * @param  {Boolean} keep_html if not set to `true` the 
container will be emptied, otherwise the current DOM elements will be kept 
intact
+                */
+               destroy : function (keep_html) {
+                       if(this._wrk) {
+                               try {
+                                       window.URL.revokeObjectURL(this._wrk);
+                                       this._wrk = null;
+                               }
+                               catch (ignore) { }
+                       }
+                       if(!keep_html) { this.element.empty(); }
+                       this.teardown();
+               },
+               /**
+                * part of the destroying of an instance. Used internally.
+                * @private
+                * @name teardown()
+                */
+               teardown : function () {
+                       this.unbind();
+                       this.element
+                               .removeClass('jstree')
+                               .removeData('jstree')
+                               .find("[class^='jstree']")
+                                       .addBack()
+                                       .attr("class", function () { return 
this.className.replace(/jstree[^ ]*|$/ig,''); });
+                       this.element = null;
+               },
+               /**
+                * bind all events. Used internally.
+                * @private
+                * @name bind()
+                */
+               bind : function () {
+                       var word = '',
+                               tout = null,
+                               was_click = 0;
+                       this.element
+                               .on("dblclick.jstree", function () {
+                                               if(document.selection && 
document.selection.empty) {
+                                                       
document.selection.empty();
+                                               }
+                                               else {
+                                                       if(window.getSelection) 
{
+                                                               var sel = 
window.getSelection();
+                                                               try {
+                                                                       
sel.removeAllRanges();
+                                                                       
sel.collapse();
+                                                               } catch 
(ignore) { }
+                                                       }
+                                               }
+                                       })
+                               .on("mousedown.jstree", $.proxy(function (e) {
+                                               if(e.target === 
this.element[0]) {
+                                                       e.preventDefault(); // 
prevent losing focus when clicking scroll arrows (FF, Chrome)
+                                                       was_click = +(new 
Date()); // ie does not allow to prevent losing focus
+                                               }
+                                       }, this))
+                               .on("mousedown.jstree", ".jstree-ocl", function 
(e) {
+                                               e.preventDefault(); // prevent 
any node inside from losing focus when clicking the open/close icon
+                                       })
+                               .on("click.jstree", ".jstree-ocl", 
$.proxy(function (e) {
+                                               this.toggle_node(e.target);
+                                       }, this))
+                               .on("dblclick.jstree", ".jstree-anchor", 
$.proxy(function (e) {
+                                               
if(this.settings.core.dblclick_toggle) {
+                                                       
this.toggle_node(e.target);
+                                               }
+                                       }, this))
+                               .on("click.jstree", ".jstree-anchor", 
$.proxy(function (e) {
+                                               e.preventDefault();
+                                               if(e.currentTarget !== 
document.activeElement) { $(e.currentTarget).focus(); }
+                                               
this.activate_node(e.currentTarget, e);
+                                       }, this))
+                               .on('keydown.jstree', '.jstree-anchor', 
$.proxy(function (e) {
+                                               if(e.target.tagName === 
"INPUT") { return true; }
+                                               if(e.which !== 32 && e.which 
!== 13 && (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey)) { return true; }
+                                               var o = null;
+                                               if(this._data.core.rtl) {
+                                                       if(e.which === 37) { 
e.which = 39; }
+                                                       else if(e.which === 39) 
{ e.which = 37; }
+                                               }
+                                               switch(e.which) {
+                                                       case 32: // aria 
defines space only with Ctrl
+                                                               if(e.ctrlKey) {
+                                                                       e.type 
= "click";
+                                                                       
$(e.currentTarget).trigger(e);
+                                                               }
+                                                               break;
+                                                       case 13: // enter
+                                                               e.type = 
"click";
+                                                               
$(e.currentTarget).trigger(e);
+                                                               break;
+                                                       case 37: // right
+                                                               
e.preventDefault();
+                                                               
if(this.is_open(e.currentTarget)) {
+                                                                       
this.close_node(e.currentTarget);
+                                                               }
+                                                               else {
+                                                                       o = 
this.get_parent(e.currentTarget);
+                                                                       if(o && 
o.id !== '#') { this.get_node(o, true).children('.jstree-anchor').focus(); }
+                                                               }
+                                                               break;
+                                                       case 38: // up
+                                                               
e.preventDefault();
+                                                               o = 
this.get_prev_dom(e.currentTarget);
+                                                               if(o && 
o.length) { o.children('.jstree-anchor').focus(); }
+                                                               break;
+                                                       case 39: // left
+                                                               
e.preventDefault();
+                                                               
if(this.is_closed(e.currentTarget)) {
+                                                                       
this.open_node(e.currentTarget, function (o) { this.get_node(o, 
true).children('.jstree-anchor').focus(); });
+                                                               }
+                                                               else if 
(this.is_open(e.currentTarget)) {
+                                                                       o = 
this.get_node(e.currentTarget, true).children('.jstree-children')[0];
+                                                                       if(o) { 
$(this._firstChild(o)).children('.jstree-anchor').focus(); }
+                                                               }
+                                                               break;
+                                                       case 40: // down
+                                                               
e.preventDefault();
+                                                               o = 
this.get_next_dom(e.currentTarget);
+                                                               if(o && 
o.length) { o.children('.jstree-anchor').focus(); }
+                                                               break;
+                                                       case 106: // aria 
defines * on numpad as open_all - not very common
+                                                               this.open_all();
+                                                               break;
+                                                       case 36: // home
+                                                               
e.preventDefault();
+                                                               o = 
this._firstChild(this.get_container_ul()[0]);
+                                                               if(o) { 
$(o).children('.jstree-anchor').filter(':visible').focus(); }
+                                                               break;
+                                                       case 35: // end
+                                                               
e.preventDefault();
+                                                               
this.element.find('.jstree-anchor').filter(':visible').last().focus();
+                                                               break;
+                                                       /*
+                                                       // delete
+                                                       case 46:
+                                                               
e.preventDefault();
+                                                               o = 
this.get_node(e.currentTarget);
+                                                               if(o && o.id && 
o.id !== '#') {
+                                                                       o = 
this.is_selected(o) ? this.get_selected() : o;
+                                                                       
this.delete_node(o);
+                                                               }
+                                                               break;
+                                                       // f2
+                                                       case 113:
+                                                               
e.preventDefault();
+                                                               o = 
this.get_node(e.currentTarget);
+                                                               if(o && o.id && 
o.id !== '#') {
+                                                                       // 
this.edit(o);
+                                                               }
+                                                               break;
+                                                       default:
+                                                               // 
console.log(e.which);
+                                                               break;
+                                                       */
+                                               }
+                                       }, this))
+                               .on("load_node.jstree", $.proxy(function (e, 
data) {
+                                               if(data.status) {
+                                                       if(data.node.id === '#' 
&& !this._data.core.loaded) {
+                                                               
this._data.core.loaded = true;
+                                                               
if(this._firstChild(this.get_container_ul()[0])) {
+                                                                       
this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id);
+                                                               }
+                                                               /**
+                                                                * triggered 
after the root node is loaded for the first time
+                                                                * @event
+                                                                * @name 
loaded.jstree
+                                                                */
+                                                               
this.trigger("loaded");
+                                                       }
+                                                       
if(!this._data.core.ready) {
+                                                               
setTimeout($.proxy(function() {
+                                                                       
if(this.element && !this.get_container_ul().find('.jstree-loading').length) {
+                                                                               
this._data.core.ready = true;
+                                                                               
if(this._data.core.selected.length) {
+                                                                               
        if(this.settings.core.expand_selected_onload) {
+                                                                               
                var tmp = [], i, j;
+                                                                               
                for(i = 0, j = this._data.core.selected.length; i < j; i++) {
+                                                                               
                        tmp = 
tmp.concat(this._model.data[this._data.core.selected[i]].parents);
+                                                                               
                }
+                                                                               
                tmp = $.vakata.array_unique(tmp);
+                                                                               
                for(i = 0, j = tmp.length; i < j; i++) {
+                                                                               
                        this.open_node(tmp[i], false, 0);
+                                                                               
                }
+                                                                               
        }
+                                                                               
        this.trigger('changed', { 'action' : 'ready', 'selected' : 
this._data.core.selected });
+                                                                               
}
+                                                                               
/**
+                                                                               
 * triggered after all nodes are finished loading
+                                                                               
 * @event
+                                                                               
 * @name ready.jstree
+                                                                               
 */
+                                                                               
this.trigger("ready");
+                                                                       }
+                                                               }, this), 0);
+                                                       }
+                                               }
+                                       }, this))
+                               // quick searching when the tree is focused
+                               .on('keypress.jstree', $.proxy(function (e) {
+                                               if(e.target.tagName === 
"INPUT") { return true; }
+                                               if(tout) { clearTimeout(tout); }
+                                               tout = setTimeout(function () {
+                                                       word = '';
+                                               }, 500);
+
+                                               var chr = 
String.fromCharCode(e.which).toLowerCase(),
+                                                       col = 
this.element.find('.jstree-anchor').filter(':visible'),
+                                                       ind = 
col.index(document.activeElement) || 0,
+                                                       end = false;
+                                               word += chr;
+
+                                               // match for whole word from 
current node down (including the current node)
+                                               if(word.length > 1) {
+                                                       
col.slice(ind).each($.proxy(function (i, v) {
+                                                               
if($(v).text().toLowerCase().indexOf(word) === 0) {
+                                                                       
$(v).focus();
+                                                                       end = 
true;
+                                                                       return 
false;
+                                                               }
+                                                       }, this));
+                                                       if(end) { return; }
+
+                                                       // match for whole word 
from the beginning of the tree
+                                                       col.slice(0, 
ind).each($.proxy(function (i, v) {
+                                                               
if($(v).text().toLowerCase().indexOf(word) === 0) {
+                                                                       
$(v).focus();
+                                                                       end = 
true;
+                                                                       return 
false;
+                                                               }
+                                                       }, this));
+                                                       if(end) { return; }
+                                               }
+                                               // list nodes that start with 
that letter (only if word consists of a single char)
+                                               if(new RegExp('^' + chr + 
'+$').test(word)) {
+                                                       // search for the next 
node starting with that letter
+                                                       col.slice(ind + 
1).each($.proxy(function (i, v) {
+                                                               
if($(v).text().toLowerCase().charAt(0) === chr) {
+                                                                       
$(v).focus();
+                                                                       end = 
true;
+                                                                       return 
false;
+                                                               }
+                                                       }, this));
+                                                       if(end) { return; }
+
+                                                       // search from the 
beginning
+                                                       col.slice(0, ind + 
1).each($.proxy(function (i, v) {
+                                                               
if($(v).text().toLowerCase().charAt(0) === chr) {
+                                                                       
$(v).focus();
+                                                                       end = 
true;
+                                                                       return 
false;
+                                                               }
+                                                       }, this));
+                                                       if(end) { return; }
+                                               }
+                                       }, this))
+                               // THEME RELATED
+                               .on("init.jstree", $.proxy(function () {
+                                               var s = 
this.settings.core.themes;
+                                               this._data.core.themes.dots     
                = s.dots;
+                                               this._data.core.themes.stripes  
        = s.stripes;
+                                               this._data.core.themes.icons    
        = s.icons;
+                                               this.set_theme(s.name || 
"default", s.url);
+                                               
this.set_theme_variant(s.variant);
+                                       }, this))
+                               .on("loading.jstree", $.proxy(function () {
+                                               this[ 
this._data.core.themes.dots ? "show_dots" : "hide_dots" ]();
+                                               this[ 
this._data.core.themes.icons ? "show_icons" : "hide_icons" ]();
+                                               this[ 
this._data.core.themes.stripes ? "show_stripes" : "hide_stripes" ]();
+                                       }, this))
+                               .on('blur.jstree', '.jstree-anchor', 
$.proxy(function (e) {
+                                               this._data.core.focused = null;
+                                               
$(e.currentTarget).filter('.jstree-hovered').mouseleave();
+                                               this.element.attr('tabindex', 
'0');
+                                       }, this))
+                               .on('focus.jstree', '.jstree-anchor', 
$.proxy(function (e) {
+                                               var tmp = 
this.get_node(e.currentTarget);
+                                               if(tmp && tmp.id) {
+                                                       this._data.core.focused 
= tmp.id;
+                                               }
+                                               
this.element.find('.jstree-hovered').not(e.currentTarget).mouseleave();
+                                               $(e.currentTarget).mouseenter();
+                                               this.element.attr('tabindex', 
'-1');
+                                       }, this))
+                               .on('focus.jstree', $.proxy(function () {
+                                               if(+(new Date()) - was_click > 
500 && !this._data.core.focused) {
+                                                       was_click = 0;
+                                                       var act = 
this.get_node(this.element.attr('aria-activedescendant'), true);
+                                                       if(act) {
+                                                               act.find('> 
.jstree-anchor').focus();
+                                                       }
+                                               }
+                                       }, this))
+                               .on('mouseenter.jstree', '.jstree-anchor', 
$.proxy(function (e) {
+                                               
this.hover_node(e.currentTarget);
+                                       }, this))
+                               .on('mouseleave.jstree', '.jstree-anchor', 
$.proxy(function (e) {
+                                               
this.dehover_node(e.currentTarget);
+                                       }, this));
+               },
+               /**
+                * part of the destroying of an instance. Used internally.
+                * @private
+                * @name unbind()
+                */
+               unbind : function () {
+                       this.element.off('.jstree');
+                       $(document).off('.jstree-' + this._id);
+               },
+               /**
+                * trigger an event. Used internally.
+                * @private
+                * @name trigger(ev [, data])
+                * @param  {String} ev the name of the event to trigger
+                * @param  {Object} data additional data to pass with the event
+                */
+               trigger : function (ev, data) {
+                       if(!data) {
+                               data = {};
+                       }
+                       data.instance = this;
+                       this.element.triggerHandler(ev.replace('.jstree','') + 
'.jstree', data);
+               },
+               /**
+                * returns the jQuery extended instance container
+                * @name get_container()
+                * @return {jQuery}
+                */
+               get_container : function () {
+                       return this.element;
+               },
+               /**
+                * returns the jQuery extended main UL node inside the instance 
container. Used internally.
+                * @private
+                * @name get_container_ul()
+                * @return {jQuery}
+                */
+               get_container_ul : function () {
+                       return 
this.element.children(".jstree-children").first();
+               },
+               /**
+                * gets string replacements (localization). Used internally.
+                * @private
+                * @name get_string(key)
+                * @param  {String} key
+                * @return {String}
+                */
+               get_string : function (key) {
+                       var a = this.settings.core.strings;
+                       if($.isFunction(a)) { return a.call(this, key); }
+                       if(a && a[key]) { return a[key]; }
+                       return key;
+               },
+               /**
+                * gets the first child of a DOM node. Used internally.
+                * @private
+                * @name _firstChild(dom)
+                * @param  {DOMElement} dom
+                * @return {DOMElement}
+                */
+               _firstChild : function (dom) {
+                       dom = dom ? dom.firstChild : null;
+                       while(dom !== null && dom.nodeType !== 1) {
+                               dom = dom.nextSibling;
+                       }
+                       return dom;
+               },
+               /**
+                * gets the next sibling of a DOM node. Used internally.
+                * @private
+                * @name _nextSibling(dom)
+                * @param  {DOMElement} dom
+                * @return {DOMElement}
+                */
+               _nextSibling : function (dom) {
+                       dom = dom ? dom.nextSibling : null;
+                       while(dom !== null && dom.nodeType !== 1) {
+                               dom = dom.nextSibling;
+                       }
+                       return dom;
+               },
+               /**
+                * gets the previous sibling of a DOM node. Used internally.
+                * @private
+                * @name _previousSibling(dom)
+                * @param  {DOMElement} dom
+                * @return {DOMElement}
+                */
+               _previousSibling : function (dom) {
+                       dom = dom ? dom.previousSibling : null;
+                       while(dom !== null && dom.nodeType !== 1) {
+                               dom = dom.previousSibling;
+                       }
+                       return dom;
+               },
+               /**
+                * get the JSON representation of a node (or the actual jQuery 
extended DOM node) by using any input (child DOM element, ID string, selector, 
etc)
+                * @name get_node(obj [, as_dom])
+                * @param  {mixed} obj
+                * @param  {Boolean} as_dom
+                * @return {Object|jQuery}
+                */
+               get_node : function (obj, as_dom) {
+                       if(obj && obj.id) {
+                               obj = obj.id;
+                       }
+                       var dom;
+                       try {
+                               if(this._model.data[obj]) {
+                                       obj = this._model.data[obj];
+                               }
+                               else if(typeof obj === "string" && 
this._model.data[obj.replace(/^#/, '')]) {
+                                       obj = 
this._model.data[obj.replace(/^#/, '')];
+                               }
+                               else if(typeof obj === "string" && (dom = $('#' 
+ obj.replace($.jstree.idregex,'\\$&'), this.element)).length && 
this._model.data[dom.closest('.jstree-node').attr('id')]) {
+                                       obj = 
this._model.data[dom.closest('.jstree-node').attr('id')];
+                               }
+                               else if((dom = $(obj, this.element)).length && 
this._model.data[dom.closest('.jstree-node').attr('id')]) {
+                                       obj = 
this._model.data[dom.closest('.jstree-node').attr('id')];
+                               }
+                               else if((dom = $(obj, this.element)).length && 
dom.hasClass('jstree')) {
+                                       obj = this._model.data['#'];
+                               }
+                               else {
+                                       return false;
+                               }
+
+                               if(as_dom) {
+                                       obj = obj.id === '#' ? this.element : 
$('#' + obj.id.replace($.jstree.idregex,'\\$&'), this.element);
+                               }
+                               return obj;
+                       } catch (ex) { return false; }
+               },
+               /**
+                * get the path to a node, either consisting of node texts, or 
of node IDs, optionally glued together (otherwise an array)
+                * @name get_path(obj [, glue, ids])
+                * @param  {mixed} obj the node
+                * @param  {String} glue if you want the path as a string - 
pass the glue here (for example '/'), if a falsy value is supplied here, an 
array is returned
+                * @param  {Boolean} ids if set to true build the path using 
ID, otherwise node text is used
+                * @return {mixed}
+                */
+               get_path : function (obj, glue, ids) {
+                       obj = obj.parents ? obj : this.get_node(obj);
+                       if(!obj || obj.id === '#' || !obj.parents) {
+                               return false;
+                       }
+                       var i, j, p = [];
+                       p.push(ids ? obj.id : obj.text);
+                       for(i = 0, j = obj.parents.length; i < j; i++) {
+                               p.push(ids ? obj.parents[i] : 
this.get_text(obj.parents[i]));
+                       }
+                       p = p.reverse().slice(1);
+                       return glue ? p.join(glue) : p;
+               },
+               /**
+                * get the next visible node that is below the `obj` node. If 
`strict` is set to `true` only sibling nodes are returned.
+                * @name get_next_dom(obj [, strict])
+                * @param  {mixed} obj
+                * @param  {Boolean} strict
+                * @return {jQuery}
+                */
+               get_next_dom : function (obj, strict) {
+                       var tmp;
+                       obj = this.get_node(obj, true);
+                       if(obj[0] === this.element[0]) {
+                               tmp = 
this._firstChild(this.get_container_ul()[0]);
+                               while (tmp && tmp.offsetHeight === 0) {
+                                       tmp = this._nextSibling(tmp);
+                               }
+                               return tmp ? $(tmp) : false;
+                       }
+                       if(!obj || !obj.length) {
+                               return false;
+                       }
+                       if(strict) {
+                               tmp = obj[0];
+                               do {
+                                       tmp = this._nextSibling(tmp);
+                               } while (tmp && tmp.offsetHeight === 0);
+                               return tmp ? $(tmp) : false;
+                       }
+                       if(obj.hasClass("jstree-open")) {
+                               tmp = 
this._firstChild(obj.children('.jstree-children')[0]);
+                               while (tmp && tmp.offsetHeight === 0) {
+                                       tmp = this._nextSibling(tmp);
+                               }
+                               if(tmp !== null) {
+                                       return $(tmp);
+                               }
+                       }
+                       tmp = obj[0];
+                       do {
+                               tmp = this._nextSibling(tmp);
+                       } while (tmp && tmp.offsetHeight === 0);
+                       if(tmp !== null) {
+                               return $(tmp);
+                       }
+                       return 
obj.parentsUntil(".jstree",".jstree-node").nextAll(".jstree-node:visible").first();
+               },
+               /**
+                * get the previous visible node that is above the `obj` node. 
If `strict` is set to `true` only sibling nodes are returned.
+                * @name get_prev_dom(obj [, strict])
+                * @param  {mixed} obj
+                * @param  {Boolean} strict
+                * @return {jQuery}
+                */
+               get_prev_dom : function (obj, strict) {
+                       var tmp;
+                       obj = this.get_node(obj, true);
+                       if(obj[0] === this.element[0]) {
+                               tmp = this.get_container_ul()[0].lastChild;
+                               while (tmp && tmp.offsetHeight === 0) {
+                                       tmp = this._previousSibling(tmp);
+                               }
+                               return tmp ? $(tmp) : false;
+                       }
+                       if(!obj || !obj.length) {
+                               return false;
+                       }
+                       if(strict) {
+                               tmp = obj[0];
+                               do {
+                                       tmp = this._previousSibling(tmp);
+                               } while (tmp && tmp.offsetHeight === 0);
+                               return tmp ? $(tmp) : false;
+                       }
+                       tmp = obj[0];
+                       do {
+                               tmp = this._previousSibling(tmp);
+                       } while (tmp && tmp.offsetHeight === 0);
+                       if(tmp !== null) {
+                               obj = $(tmp);
+                               while(obj.hasClass("jstree-open")) {
+                                       obj = 
obj.children(".jstree-children").first().children(".jstree-node:visible:last");
+                               }
+                               return obj;
+                       }
+                       tmp = obj[0].parentNode.parentNode;
+                       return tmp && tmp.className && 
tmp.className.indexOf('jstree-node') !== -1 ? $(tmp) : false;
+               },
+               /**
+                * get the parent ID of a node
+                * @name get_parent(obj)
+                * @param  {mixed} obj
+                * @return {String}
+                */
+               get_parent : function (obj) {
+                       obj = this.get_node(obj);
+                       if(!obj || obj.id === '#') {
+                               return false;
+                       }
+                       return obj.parent;
+               },
+               /**
+                * get a jQuery collection of all the children of a node (node 
must be rendered)
+                * @name get_children_dom(obj)
+                * @param  {mixed} obj
+                * @return {jQuery}
+                */
+               get_children_dom : function (obj) {
+                       obj = this.get_node(obj, true);
+                       if(obj[0] === this.element[0]) {
+                               return 
this.get_container_ul().children(".jstree-node");
+                       }
+                       if(!obj || !obj.length) {
+                               return false;
+                       }
+                       return 
obj.children(".jstree-children").children(".jstree-node");
+               },
+               /**
+                * checks if a node has children
+                * @name is_parent(obj)
+                * @param  {mixed} obj
+                * @return {Boolean}
+                */
+               is_parent : function (obj) {
+                       obj = this.get_node(obj);
+                       return obj && (obj.state.loaded === false || 
obj.children.length > 0);
+               },
+               /**
+                * checks if a node is loaded (its children are available)
+                * @name is_loaded(obj)
+                * @param  {mixed} obj
+                * @return {Boolean}
+                */
+               is_loaded : function (obj) {
+                       obj = this.get_node(obj);
+                       return obj && obj.state.loaded;
+               },
+               /**
+                * check if a node is currently loading (fetching children)
+                * @name is_loading(obj)
+                * @param  {mixed} obj
+                * @return {Boolean}
+                */
+               is_loading : function (obj) {
+                       obj = this.get_node(obj);
+                       return obj && obj.state && obj.state.loading;
+               },
+               /**
+                * check if a node is opened
+                * @name is_open(obj)
+                * @param  {mixed} obj
+                * @return {Boolean}
+                */
+               is_open : function (obj) {
+                       obj = this.get_node(obj);
+                       return obj && obj.state.opened;
+               },
+               /**
+                * check if a node is in a closed state
+                * @name is_closed(obj)
+                * @param  {mixed} obj
+                * @return {Boolean}
+                */
+               is_closed : function (obj) {
+                       obj = this.get_node(obj);
+                       return obj && this.is_parent(obj) && !obj.state.opened;
+               },
+               /**
+                * check if a node has no children
+                * @name is_leaf(obj)
+                * @param  {mixed} obj
+                * @return {Boolean}
+                */
+               is_leaf : function (obj) {
+                       return !this.is_parent(obj);
+               },
+               /**
+                * loads a node (fetches its children using the `core.data` 
setting). Multiple nodes can be passed to by using an array.
+                * @name load_node(obj [, callback])
+                * @param  {mixed} obj
+                * @param  {function} callback a function to be executed once 
loading is complete, the function is executed in the instance's scope and 
receives two arguments - the node and a boolean status
+                * @return {Boolean}
+                * @trigger load_node.jstree
+                */
+               load_node : function (obj, callback) {
+                       var k, l, i, j, c;
+                       if($.isArray(obj)) {
+                               this._load_nodes(obj.slice(), callback);
+                               return true;
+                       }
+                       obj = this.get_node(obj);
+                       if(!obj) {
+                               if(callback) { callback.call(this, obj, false); 
}
+                               return false;
+                       }
+                       // if(obj.state.loading) { } // the node is already 
loading - just wait for it to load and invoke callback? but if called 
implicitly it should be loaded again?
+                       if(obj.state.loaded) {
+                               obj.state.loaded = false;
+                               for(k = 0, l = obj.children_d.length; k < l; 
k++) {
+                                       for(i = 0, j = obj.parents.length; i < 
j; i++) {
+                                               
this._model.data[obj.parents[i]].children_d = 
$.vakata.array_remove_item(this._model.data[obj.parents[i]].children_d, 
obj.children_d[k]);
+                                       }
+                                       
if(this._model.data[obj.children_d[k]].state.selected) {
+                                               c = true;
+                                               this._data.core.selected = 
$.vakata.array_remove_item(this._data.core.selected, obj.children_d[k]);
+                                       }
+                                       delete 
this._model.data[obj.children_d[k]];
+                               }
+                               obj.children = [];
+                               obj.children_d = [];
+                               if(c) {
+                                       this.trigger('changed', { 'action' : 
'load_node', 'node' : obj, 'selected' : this._data.core.selected });
+                               }
+                       }
+                       obj.state.failed = false;
+                       obj.state.loading = true;
+                       this.get_node(obj, 
true).addClass("jstree-loading").attr('aria-busy',true);
+                       this._load_node(obj, $.proxy(function (status) {
+                               obj = this._model.data[obj.id];
+                               obj.state.loading = false;
+                               obj.state.loaded = status;
+                               obj.state.failed = !obj.state.loaded;
+                               var dom = this.get_node(obj, true);
+                               if(obj.state.loaded && !obj.children.length && 
dom && dom.length && !dom.hasClass('jstree-leaf')) {
+                                       dom.removeClass('jstree-closed 
jstree-open').addClass('jstree-leaf');
+                               }
+                               
dom.removeClass("jstree-loading").attr('aria-busy',false);
+                               /**
+                                * triggered after a node is loaded
+                                * @event
+                                * @name load_node.jstree
+                                * @param {Object} node the node that was 
loading
+                                * @param {Boolean} status was the node loaded 
successfully
+                                */
+                               this.trigger('load_node', { "node" : obj, 
"status" : status });
+                               if(callback) {
+                                       callback.call(this, obj, status);
+                               }
+                       }, this));
+                       return true;
+               },
+               /**
+                * load an array of nodes (will also load unavailable nodes as 
soon as the appear in the structure). Used internally.
+                * @private
+                * @name _load_nodes(nodes [, callback])
+                * @param  {array} nodes
+                * @param  {function} callback a function to be executed once 
loading is complete, the function is executed in the instance's scope and 
receives one argument - the array passed to _load_nodes
+                */
+               _load_nodes : function (nodes, callback, is_callback) {
+                       var r = true,
+                               c = function () { this._load_nodes(nodes, 
callback, true); },
+                               m = this._model.data, i, j, tmp = [];
+                       for(i = 0, j = nodes.length; i < j; i++) {
+                               if(m[nodes[i]] && ( (!m[nodes[i]].state.loaded 
&& !m[nodes[i]].state.failed) || !is_callback)) {
+                                       if(!this.is_loading(nodes[i])) {
+                                               this.load_node(nodes[i], c);
+                                       }
+                                       r = false;
+                               }
+                       }
+                       if(r) {
+                               for(i = 0, j = nodes.length; i < j; i++) {
+                                       if(m[nodes[i]] && 
m[nodes[i]].state.loaded) {
+                                               tmp.push(nodes[i]);
+                                       }
+                               }
+                               if(callback && !callback.done) {
+                                       callback.call(this, tmp);
+                                       callback.done = true;
+                               }
+                       }
+               },
+               /**
+                * loads all unloaded nodes
+                * @name load_all([obj, callback])
+                * @param {mixed} obj the node to load recursively, omit to 
load all nodes in the tree
+                * @param {function} callback a function to be executed once 
loading all the nodes is complete,
+                * @trigger load_all.jstree
+                */
+               load_all : function (obj, callback) {
+                       if(!obj) { obj = '#'; }
+                       obj = this.get_node(obj);
+                       if(!obj) { return false; }
+                       var to_load = [],
+                               m = this._model.data,
+                               c = m[obj.id].children_d,
+                               i, j;
+                       if(obj.state && !obj.state.loaded) {
+                               to_load.push(obj.id);
+                       }
+                       for(i = 0, j = c.length; i < j; i++) {
+                               if(m[c[i]] && m[c[i]].state && 
!m[c[i]].state.loaded) {
+                                       to_load.push(c[i]);
+                               }
+                       }
+                       if(to_load.length) {
+                               this._load_nodes(to_load, function () {
+                                       this.load_all(obj, callback);
+                               });
+                       }
+                       else {
+                               /**
+                                * triggered after a load_all call completes
+                                * @event
+                                * @name load_all.jstree
+                                * @param {Object} node the recursively loaded 
node
+                                */
+                               if(callback) { callback.call(this, obj); }
+                               this.trigger('load_all', { "node" : obj });
+                       }
+               },
+               /**
+                * handles the actual loading of a node. Used only internally.
+                * @private
+                * @name _load_node(obj [, callback])
+                * @param  {mixed} obj
+                * @param  {function} callback a function to be executed once 
loading is complete, the function is executed in the instance's scope and 
receives one argument - a boolean status
+                * @return {Boolean}
+                */
+               _load_node : function (obj, callback) {
+                       var s = this.settings.core.data, t;
+                       // use original HTML
+                       if(!s) {
+                               if(obj.id === '#') {
+                                       return this._append_html_data(obj, 
this._data.core.original_container_html.clone(true), function (status) {
+                                               callback.call(this, status);
+                                       });
+                               }
+                               else {
+                                       return callback.call(this, false);
+                               }
+                               // return callback.call(this, obj.id === '#' ? 
this._append_html_data(obj, 
this._data.core.original_container_html.clone(true)) : false);
+                       }
+                       if($.isFunction(s)) {
+                               return s.call(this, obj, $.proxy(function (d) {
+                                       if(d === false) {
+                                               callback.call(this, false);
+                                       }
+                                       this[typeof d === 'string' ? 
'_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? 
$($.parseHTML(d)).filter(function () { return this.nodeType !== 3; }) : d, 
function (status) {
+                                               callback.call(this, status);
+                                       });
+                                       // return d === false ? 
callback.call(this, false) : callback.call(this, this[typeof d === 'string' ? 
'_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $(d) : 
d));
+                               }, this));
+                       }
+                       if(typeof s === 'object') {
+                               if(s.url) {
+                                       s = $.extend(true, {}, s);
+                                       if($.isFunction(s.url)) {
+                                               s.url = s.url.call(this, obj);
+                                       }
+                                       if($.isFunction(s.data)) {
+                                               s.data = s.data.call(this, obj);
+                                       }
+                                       return $.ajax(s)
+                                               .done($.proxy(function (d,t,x) {
+                                                               var type = 
x.getResponseHeader('Content-Type');
+                                                               if((type && 
type.indexOf('json') !== -1) || typeof d === "object") {
+                                                                       return 
this._append_json_data(obj, d, function (status) { callback.call(this, status); 
});
+                                                                       
//return callback.call(this, this._append_json_data(obj, d));
+                                                               }
+                                                               if((type && 
type.indexOf('html') !== -1) || typeof d === "string") {
+                                                                       return 
this._append_html_data(obj, $($.parseHTML(d)).filter(function () { return 
this.nodeType !== 3; }), function (status) { callback.call(this, status); });
+                                                                       // 
return callback.call(this, this._append_html_data(obj, $(d)));
+                                                               }
+                                                               
this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 
'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : 
obj.id, 'xhr' : x }) };
+                                                               
this.settings.core.error.call(this, this._data.core.last_error);
+                                                               return 
callback.call(this, false);
+                                                       }, this))
+                                               .fail($.proxy(function (f) {
+                                                               
callback.call(this, false);
+                                                               
this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 
'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : 
obj.id, 'xhr' : f }) };
+                                                               
this.settings.core.error.call(this, this._data.core.last_error);
+                                                       }, this));
+                               }
+                               t = ($.isArray(s) || $.isPlainObject(s)) ? 
JSON.parse(JSON.stringify(s)) : s;
+                               if(obj.id === '#') {
+                                       return this._append_json_data(obj, t, 
function (status) {
+                                               callback.call(this, status);
+                                       });
+                               }
+                               else {
+                                       this._data.core.last_error = { 'error' 
: 'nodata', 'plugin' : 'core', 'id' : 'core_05', 'reason' : 'Could not load 
node', 'data' : JSON.stringify({ 'id' : obj.id }) };
+                                       this.settings.core.error.call(this, 
this._data.core.last_error);
+                                       return callback.call(this, false);
+                               }
+                               //return callback.call(this, (obj.id === "#" ? 
this._append_json_data(obj, t) : false) );
+                       }
+                       if(typeof s === 'string') {
+                               if(obj.id === '#') {
+                                       return this._append_html_data(obj, 
$($.parseHTML(s)).filter(function () { return this.nodeType !== 3; }), function 
(status) {
+                                               callback.call(this, status);
+                                       });
+                               }
+                               else {
+                                       this._data.core.last_error = { 'error' 
: 'nodata', 'plugin' : 'core', 'id' : 'core_06', 'reason' : 'Could not load 
node', 'data' : JSON.stringify({ 'id' : obj.id }) };
+                                       this.settings.core.error.call(this, 
this._data.core.last_error);
+                                       return callback.call(this, false);
+                               }
+                               //return callback.call(this, (obj.id === "#" ? 
this._append_html_data(obj, $(s)) : false) );
+                       }
+                       return callback.call(this, false);
+               },
+               /**
+                * adds a node to the list of nodes to redraw. Used only 
internally.
+                * @private
+                * @name _node_changed(obj [, callback])
+                * @param  {mixed} obj
+                */
+               _node_changed : function (obj) {
+                       obj = this.get_node(obj);
+                       if(obj) {
+                               this._model.changed.push(obj.id);
+                       }
+               },
+               /**
+                * appends HTML content to the tree. Used internally.
+                * @private
+                * @name _append_html_data(obj, data)
+                * @param  {mixed} obj the node to append to
+                * @param  {String} data the HTML string to parse and append
+                * @trigger model.jstree, changed.jstree
+                */
+               _append_html_data : function (dom, data, cb) {
+                       dom = this.get_node(dom);
+                       dom.children = [];
+                       dom.children_d = [];
+                       var dat = data.is('ul') ? data.children() : data,
+                               par = dom.id,
+                               chd = [],
+                               dpc = [],
+                               m = this._model.data,
+                               p = m[par],
+                               s = this._data.core.selected.length,
+                               tmp, i, j;
+                       dat.each($.proxy(function (i, v) {
+                               tmp = this._parse_model_from_html($(v), par, 
p.parents.concat());
+                               if(tmp) {
+                                       chd.push(tmp);
+                                       dpc.push(tmp);
+                                       if(m[tmp].children_d.length) {
+                                               dpc = 
dpc.concat(m[tmp].children_d);
+                                       }
+                               }
+                       }, this));
+                       p.children = chd;
+                       p.children_d = dpc;
+                       for(i = 0, j = p.parents.length; i < j; i++) {
+                               m[p.parents[i]].children_d = 
m[p.parents[i]].children_d.concat(dpc);
+                       }
+                       /**
+                        * triggered when new data is inserted to the tree model
+                        * @event
+                        * @name model.jstree
+                        * @param {Array} nodes an array of node IDs
+                        * @param {String} parent the parent ID of the nodes
+                        */
+                       this.trigger('model', { "nodes" : dpc, 'parent' : par 
});
+                       if(par !== '#') {
+                               this._node_changed(par);
+                               this.redraw();
+                       }
+                       else {
+                               
this.get_container_ul().children('.jstree-initial-node').remove();
+                               this.redraw(true);
+                       }
+                       if(this._data.core.selected.length !== s) {
+                               this.trigger('changed', { 'action' : 'model', 
'selected' : this._data.core.selected });
+                       }
+                       cb.call(this, true);
+               },
+               /**
+                * appends JSON content to the tree. Used internally.
+                * @private
+                * @name _append_json_data(obj, data)
+                * @param  {mixed} obj the node to append to
+                * @param  {String} data the JSON object to parse and append
+                * @param  {Boolean} force_processing internal param - do not 
set
+                * @trigger model.jstree, changed.jstree
+                */
+               _append_json_data : function (dom, data, cb, force_processing) {
+                       if(this.element === null) { return; }
+                       dom = this.get_node(dom);
+                       dom.children = [];
+                       dom.children_d = [];
+                       // *%$@!!!
+                       if(data.d) {
+                               data = data.d;
+                               if(typeof data === "string") {
+                                       data = JSON.parse(data);
+                               }
+                       }
+                       if(!$.isArray(data)) { data = [data]; }
+                       var w = null,
+                               args = {
+                                       'df'    : this._model.default_state,
+                                       'dat'   : data,
+                                       'par'   : dom.id,
+                                       'm'             : this._model.data,
+                                       't_id'  : this._id,
+                                       't_cnt' : this._cnt,
+                                       'sel'   : this._data.core.selected
+                               },
+                               func = function (data, undefined) {
+                                       if(data.data) { data = data.data; }
+                                       var dat = data.dat,
+                                               par = data.par,
+                                               chd = [],
+                                               dpc = [],
+                                               add = [],
+                                               df = data.df,
+                                               t_id = data.t_id,
+                                               t_cnt = data.t_cnt,
+                                               m = data.m,
+                                               p = m[par],
+                                               sel = data.sel,
+                                               tmp, i, j, rslt,
+                                               parse_flat = function (d, p, 
ps) {
+                                                       if(!ps) { ps = []; }
+                                                       else { ps = 
ps.concat(); }
+                                                       if(p) { ps.unshift(p); }
+                                                       var tid = 
d.id.toString(),
+                                                               i, j, c, e,
+                                                               tmp = {
+                                                                       id      
                : tid,
+                                                                       text    
        : d.text || '',
+                                                                       icon    
        : d.icon !== undefined ? d.icon : true,
+                                                                       parent  
        : p,
+                                                                       parents 
        : ps,
+                                                                       
children        : d.children || [],
+                                                                       
children_d      : d.children_d || [],
+                                                                       data    
        : d.data,
+                                                                       state   
        : { },
+                                                                       li_attr 
        : { id : false },
+                                                                       a_attr  
        : { href : '#' },
+                                                                       
original        : false
+                                                               };
+                                                       for(i in df) {
+                                                               
if(df.hasOwnProperty(i)) {
+                                                                       
tmp.state[i] = df[i];
+                                                               }
+                                                       }
+                                                       if(d && d.data && 
d.data.jstree && d.data.jstree.icon) {
+                                                               tmp.icon = 
d.data.jstree.icon;
+                                                       }
+                                                       if(tmp.icon === 
undefined || tmp.icon === null || tmp.icon === "") {
+                                                               tmp.icon = true;
+                                                       }
+                                                       if(d && d.data) {
+                                                               tmp.data = 
d.data;
+                                                               
if(d.data.jstree) {
+                                                                       for(i 
in d.data.jstree) {
+                                                                               
if(d.data.jstree.hasOwnProperty(i)) {
+                                                                               
        tmp.state[i] = d.data.jstree[i];
+                                                                               
}
+                                                                       }
+                                                               }
+                                                       }
+                                                       if(d && typeof d.state 
=== 'object') {
+                                                               for (i in 
d.state) {
+                                                                       
if(d.state.hasOwnProperty(i)) {
+                                                                               
tmp.state[i] = d.state[i];
+                                                                       }
+                                                               }
+                                                       }
+                                                       if(d && typeof 
d.li_attr === 'object') {
+                                                               for (i in 
d.li_attr) {
+                                                                       
if(d.li_attr.hasOwnProperty(i)) {
+                                                                               
tmp.li_attr[i] = d.li_attr[i];
+                                                                       }
+                                                               }
+                                                       }
+                                                       if(!tmp.li_attr.id) {
+                                                               tmp.li_attr.id 
= tid;
+                                                       }
+                                                       if(d && typeof d.a_attr 
=== 'object') {
+                                                               for (i in 
d.a_attr) {
+                                                                       
if(d.a_attr.hasOwnProperty(i)) {
+                                                                               
tmp.a_attr[i] = d.a_attr[i];
+                                                                       }
+                                                               }
+                                                       }
+                                                       if(d && d.children && 
d.children === true) {
+                                                               
tmp.state.loaded = false;
+                                                               tmp.children = 
[];
+                                                               tmp.children_d 
= [];
+                                                       }
+                                                       m[tmp.id] = tmp;
+                                                       for(i = 0, j = 
tmp.children.length; i < j; i++) {
+                                                               c = 
parse_flat(m[tmp.children[i]], tmp.id, ps);
+                                                               e = m[c];
+                                                               
tmp.children_d.push(c);
+                                                               
if(e.children_d.length) {
+                                                                       
tmp.children_d = tmp.children_d.concat(e.children_d);
+                                                               }
+                                                       }
+                                                       delete d.data;
+                                                       delete d.children;
+                                                       m[tmp.id].original = d;
+                                                       if(tmp.state.selected) {
+                                                               
add.push(tmp.id);
+                                                       }
+                                                       return tmp.id;
+                                               },
+                                               parse_nest = function (d, p, 
ps) {
+                                                       if(!ps) { ps = []; }
+                                                       else { ps = 
ps.concat(); }
+                                                       if(p) { ps.unshift(p); }
+                                                       var tid = false, i, j, 
c, e, tmp;
+                                                       do {
+                                                               tid = 'j' + 
t_id + '_' + (++t_cnt);
+                                                       } while(m[tid]);
+
+                                                       tmp = {
+                                                               id              
        : false,
+                                                               text            
: typeof d === 'string' ? d : '',
+                                                               icon            
: typeof d === 'object' && d.icon !== undefined ? d.icon : true,
+                                                               parent          
: p,
+                                                               parents         
: ps,
+                                                               children        
: [],
+                                                               children_d      
: [],
+                                                               data            
: null,
+                                                               state           
: { },
+                                                               li_attr         
: { id : false },
+                                                               a_attr          
: { href : '#' },
+                                                               original        
: false
+                                                       };
+                                                       for(i in df) {
+                                                               
if(df.hasOwnProperty(i)) {
+                                                                       
tmp.state[i] = df[i];
+                                                               }
+                                                       }
+                                                       if(d && d.id) { tmp.id 
= d.id.toString(); }
+                                                       if(d && d.text) { 
tmp.text = d.text; }
+                                                       if(d && d.data && 
d.data.jstree && d.data.jstree.icon) {
+                                                               tmp.icon = 
d.data.jstree.icon;
+                                                       }
+                                                       if(tmp.icon === 
undefined || tmp.icon === null || tmp.icon === "") {
+                                                               tmp.icon = true;
+                                                       }
+                                                       if(d && d.data) {
+                                                               tmp.data = 
d.data;
+                                                               
if(d.data.jstree) {
+                                                                       for(i 
in d.data.jstree) {
+                                                                               
if(d.data.jstree.hasOwnProperty(i)) {
+                                                                               
        tmp.state[i] = d.data.jstree[i];
+                                                                               
}
+                                                                       }
+                                                               }
+                                                       }
+                                                       if(d && typeof d.state 
=== 'object') {
+                                                               for (i in 
d.state) {
+                                                                       
if(d.state.hasOwnProperty(i)) {
+                                                                               
tmp.state[i] = d.state[i];
+                                                                       }
+                                                               }
+                                                       }
+                                                       if(d && typeof 
d.li_attr === 'object') {
+                                                               for (i in 
d.li_attr) {
+                                                                       
if(d.li_attr.hasOwnProperty(i)) {
+                                                                               
tmp.li_attr[i] = d.li_attr[i];
+                                                                       }
+                                                               }
+                                                       }
+                                                       if(tmp.li_attr.id && 
!tmp.id) {
+                                                               tmp.id = 
tmp.li_attr.id.toString();
+                                                       }
+                                                       if(!tmp.id) {
+                                                               tmp.id = tid;
+                                                       }
+                                                       if(!tmp.li_attr.id) {
+                                                               tmp.li_attr.id 
= tmp.id;
+                                                       }
+                                                       if(d && typeof d.a_attr 
=== 'object') {
+                                                               for (i in 
d.a_attr) {
+                                                                       
if(d.a_attr.hasOwnProperty(i)) {
+                                                                               
tmp.a_attr[i] = d.a_attr[i];
+                                                                       }
+                                                               }
+                                                       }
+                                                       if(d && d.children && 
d.children.length) {
+                                                               for(i = 0, j = 
d.children.length; i < j; i++) {
+                                                                       c = 
parse_nest(d.children[i], tmp.id, ps);
+                                                                       e = 
m[c];
+                                                                       
tmp.children.push(c);
+                                                                       
if(e.children_d.length) {
+                                                                               
tmp.children_d = tmp.children_d.concat(e.children_d);
+                                                                       }
+                                                               }
+                                                               tmp.children_d 
= tmp.children_d.concat(tmp.children);
+                                                       }
+                                                       if(d && d.children && 
d.children === true) {
+                                                               
tmp.state.loaded = false;
+                                                               tmp.children = 
[];
+                                                               tmp.children_d 
= [];
+                                                       }
+                                                       delete d.data;
+                                                       delete d.children;
+                                                       tmp.original = d;
+                                                       m[tmp.id] = tmp;
+                                                       if(tmp.state.selected) {
+                                                               
add.push(tmp.id);
+                                                       }
+                                                       return tmp.id;
+                                               };
+
+                                       if(dat.length && dat[0].id !== 
undefined && dat[0].parent !== undefined) {
+                                               // Flat JSON support (for easy 
import from DB):
+                                               // 1) convert to object 
(foreach)
+                                               for(i = 0, j = dat.length; i < 
j; i++) {
+                                                       if(!dat[i].children) {
+                                                               dat[i].children 
= [];
+                                                       }
+                                                       m[dat[i].id.toString()] 
= dat[i];
+                                               }
+                                               // 2) populate children 
(foreach)
+                                               for(i = 0, j = dat.length; i < 
j; i++) {
+                                                       
m[dat[i].parent.toString()].children.push(dat[i].id.toString());
+                                                       // populate 
parent.children_d
+                                                       
p.children_d.push(dat[i].id.toString());
+                                               }
+                                               // 3) normalize && populate 
parents and children_d with recursion
+                                               for(i = 0, j = 
p.children.length; i < j; i++) {
+                                                       tmp = 
parse_flat(m[p.children[i]], par, p.parents.concat());
+                                                       dpc.push(tmp);
+                                                       
if(m[tmp].children_d.length) {
+                                                               dpc = 
dpc.concat(m[tmp].children_d);
+                                                       }
+                                               }
+                                               for(i = 0, j = 
p.parents.length; i < j; i++) {
+                                                       
m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
+                                               }
+                                               // ?) three_state selection - 
p.state.selected && t - (if three_state foreach(dat => ch) -> foreach(parents) 
if(parent.selected) child.selected = true;
+                                               rslt = {
+                                                       'cnt' : t_cnt,
+                                                       'mod' : m,
+                                                       'sel' : sel,
+                                                       'par' : par,
+                                                       'dpc' : dpc,
+                                                       'add' : add
+                                               };
+                                       }
+                                       else {
+                                               for(i = 0, j = dat.length; i < 
j; i++) {
+                                                       tmp = 
parse_nest(dat[i], par, p.parents.concat());
+                                                       if(tmp) {
+                                                               chd.push(tmp);
+                                                               dpc.push(tmp);
+                                                               
if(m[tmp].children_d.length) {
+                                                                       dpc = 
dpc.concat(m[tmp].children_d);
+                                                               }
+                                                       }
+                                               }
+                                               p.children = chd;
+                                               p.children_d = dpc;
+                                               for(i = 0, j = 
p.parents.length; i < j; i++) {
+                                                       
m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
+                                               }
+                                               rslt = {
+                                                       'cnt' : t_cnt,
+                                                       'mod' : m,
+                                                       'sel' : sel,
+                                                       'par' : par,
+                                                       'dpc' : dpc,
+                                                       'add' : add
+                                               };
+                                       }
+                                       if(typeof window === 'undefined' || 
typeof window.document === 'undefined') {
+                                               postMessage(rslt);
+                                       }
+                                       else {
+                                               return rslt;
+                                       }
+                               },
+                               rslt = function (rslt, worker) {
+                                       if(this.element === null) { return; }
+                                       this._cnt = rslt.cnt;
+                                       this._model.data = rslt.mod; // breaks 
the reference in load_node - careful
+
+                                       if(worker) {
+                                               var i, j, a = rslt.add, r = 
rslt.sel, s = this._data.core.selected.slice(), m = this._model.data;
+                                               // if selection was changed 
while calculating in worker
+                                               if(r.length !== s.length || 
$.vakata.array_unique(r.concat(s)).length !== r.length) {
+                                                       // deselect nodes that 
are no longer selected
+                                                       for(i = 0, j = 
r.length; i < j; i++) {
+                                                               
if($.inArray(r[i], a) === -1 && $.inArray(r[i], s) === -1) {
+                                                                       
m[r[i]].state.selected = false;
+                                                               }
+                                                       }
+                                                       // select nodes that 
were selected in the mean time
+                                                       for(i = 0, j = 
s.length; i < j; i++) {
+                                                               
if($.inArray(s[i], r) === -1) {
+                                                                       
m[s[i]].state.selected = true;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       if(rslt.add.length) {
+                                               this._data.core.selected = 
this._data.core.selected.concat(rslt.add);
+                                       }
+
+                                       this.trigger('model', { "nodes" : 
rslt.dpc, 'parent' : rslt.par });
+
+                                       if(rslt.par !== '#') {
+                                               this._node_changed(rslt.par);
+                                               this.redraw();
+                                       }
+                                       else {
+                                               // 
this.get_container_ul().children('.jstree-initial-node').remove();
+                                               this.redraw(true);
+                                       }
+                                       if(rslt.add.length) {
+                                               this.trigger('changed', { 
'action' : 'model', 'selected' : this._data.core.selected });
+                                       }
+                                       cb.call(this, true);
+                               };
+                       if(this.settings.core.worker && window.Blob && 
window.URL && window.Worker) {
+                               try {
+                                       if(this._wrk === null) {
+                                               this._wrk = 
window.URL.createObjectURL(
+                                                       new window.Blob(
+                                                               
['self.onmessage = ' + func.toString()],
+                                                               
{type:"text/javascript"}
+                                                       )
+                                               );
+                                       }
+                                       if(!this._data.core.working || 
force_processing) {
+                                               this._data.core.working = true;
+                                               w = new 
window.Worker(this._wrk);
+                                               w.onmessage = $.proxy(function 
(e) {
+                                                       rslt.call(this, e.data, 
true);
+                                                       try { w.terminate(); w 
= null; } catch(ignore) { }
+                                                       
if(this._data.core.worker_queue.length) {
+                                                               
this._append_json_data.apply(this, this._data.core.worker_queue.shift());
+                                                       }
+                                                       else {
+                                                               
this._data.core.working = false;
+                                                       }
+                                               }, this);
+                                               if(!args.par) {
+                                                       
if(this._data.core.worker_queue.length) {
+                                                               
this._append_json_data.apply(this, this._data.core.worker_queue.shift());
+                                                       }
+                                                       else {
+                                                               
this._data.core.working = false;
+                                                       }
+                                               }
+                                               else {
+                                                       w.postMessage(args);
+                                               }
+                                       }
+                                       else {
+                                               
this._data.core.worker_queue.push([dom, data, cb, true]);
+                                       }
+                               }
+                               catch(e) {
+                                       rslt.call(this, func(args), false);
+                                       if(this._data.core.worker_queue.length) 
{
+                                               
this._append_json_data.apply(this, this._data.core.worker_queue.shift());
+                                       }
+                                       else {
+                                               this._data.core.working = false;
+                                       }
+                               }
+                       }
+                       else {
+                               rslt.call(this, func(args), false);
+                       }
+               },
+               /**
+                * parses a node from a jQuery object and appends them to the 
in memory tree model. Used internally.
+                * @private
+                * @name _parse_model_from_html(d [, p, ps])
+                * @param  {jQuery} d the jQuery object to parse
+                * @param  {String} p the parent ID
+                * @param  {Array} ps list of all parents
+                * @return {String} the ID of the object added to the model
+                */
+               _parse_model_from_html : function (d, p, ps) {
+                       if(!ps) { ps = []; }
+                       else { ps = [].concat(ps); }
+                       if(p) { ps.unshift(p); }
+                       var c, e, m = this._model.data,
+                               data = {
+                                       id                      : false,
+                                       text            : false,
+                                       icon            : true,
+                                       parent          : p,
+                                       parents         : ps,
+                                       children        : [],
+                                       children_d      : [],
+                                       data            : null,
+                                       state           : { },
+                                       li_attr         : { id : false },
+                                       a_attr          : { href : '#' },
+                                       original        : false
+                               }, i, tmp, tid;
+                       for(i in this._model.default_state) {
+                               if(this._model.default_state.hasOwnProperty(i)) 
{
+                                       data.state[i] = 
this._model.default_state[i];
+                               }
+                       }
+                       tmp = $.vakata.attributes(d, true);
+                       $.each(tmp, function (i, v) {
+                               v = $.trim(v);
+                               if(!v.length) { return true; }
+                               data.li_attr[i] = v;
+                               if(i === 'id') {
+                                       data.id = v.toString();
+                               }
+                       });
+                       tmp = d.children('a').first();
+                       if(tmp.length) {
+                               tmp = $.vakata.attributes(tmp, true);
+                               $.each(tmp, function (i, v) {
+                                       v = $.trim(v);
+                                       if(v.length) {
+                                               data.a_attr[i] = v;
+                                       }
+                               });
+                       }
+                       tmp = d.children("a").first().length ? 
d.children("a").first().clone() : d.clone();
+                       tmp.children("ins, i, ul").remove();
+                       tmp = tmp.html();
+                       tmp = $('<div />').html(tmp);
+                       data.text = this.settings.core.force_text ? tmp.text() 
: tmp.html();
+                       tmp = d.data();
+                       data.data = tmp ? $.extend(true, {}, tmp) : null;
+                       data.state.opened = d.hasClass('jstree-open');
+                       data.state.selected = 
d.children('a').hasClass('jstree-clicked');
+                       data.state.disabled = 
d.children('a').hasClass('jstree-disabled');
+                       if(data.data && data.data.jstree) {
+                               for(i in data.data.jstree) {
+                                       if(data.data.jstree.hasOwnProperty(i)) {
+                                               data.state[i] = 
data.data.jstree[i];
+                                       }
+                               }
+                       }
+                       tmp = d.children("a").children(".jstree-themeicon");
+                       if(tmp.length) {
+                               data.icon = 
tmp.hasClass('jstree-themeicon-hidden') ? false : tmp.attr('rel');
+                       }
+                       if(data.state.icon !== undefined) {
+                               data.icon = data.state.icon;
+                       }
+                       if(data.icon === undefined || data.icon === null || 
data.icon === "") {
+                               data.icon = true;
+                       }
+                       tmp = d.children("ul").children("li");
+                       do {
+                               tid = 'j' + this._id + '_' + (++this._cnt);
+                       } while(m[tid]);
+                       data.id = data.li_attr.id ? data.li_attr.id.toString() 
: tid;
+                       if(tmp.length) {
+                               tmp.each($.proxy(function (i, v) {
+                                       c = this._parse_model_from_html($(v), 
data.id, ps);
+                                       e = this._model.data[c];
+                                       data.children.push(c);
+                                       if(e.children_d.length) {
+                                               data.children_d = 
data.children_d.concat(e.children_d);
+                                       }
+                               }, this));
+                               data.children_d = 
data.children_d.concat(data.children);
+                       }
+                       else {
+                               if(d.hasClass('jstree-closed')) {
+                                       data.state.loaded = false;
+                               }
+                       }
+                       if(data.li_attr['class']) {
+                               data.li_attr['class'] = 
data.li_attr['class'].replace('jstree-closed','').replace('jstree-open','');
+                       }
+                       if(data.a_attr['class']) {
+                               data.a_attr['class'] = 
data.a_attr['class'].replace('jstree-clicked','').replace('jstree-disabled','');
+                       }
+                       m[data.id] = data;
+                       if(data.state.selected) {
+                               this._data.core.selected.push(data.id);
+                       }
+                       return data.id;
+               },
+               /**
+                * parses a node from a JSON object (used when dealing with 
flat data, which has no nesting of children, but has id and parent properties) 
and appends it to the in memory tree model. Used internally.
+                * @private
+                * @name _parse_model_from_flat_json(d [, p, ps])
+                * @param  {Object} d the JSON object to parse
+                * @param  {String} p the parent ID
+                * @param  {Array} ps list of all parents
+                * @return {String} the ID of the object added to the model
+                */
+               _parse_model_from_flat_json : function (d, p, ps) {
+                       if(!ps) { ps = []; }
+                       else { ps = ps.concat(); }
+                       if(p) { ps.unshift(p); }
+                       var tid = d.id.toString(),
+                               m = this._model.data,
+                               df = this._model.default_state,
+                               i, j, c, e,
+                               tmp = {
+                                       id                      : tid,
+                                       text            : d.text || '',
+                                       icon            : d.icon !== undefined 
? d.icon : true,
+                                       parent          : p,
+                                       parents         : ps,
+                                       children        : d.children || [],
+                                       children_d      : d.children_d || [],
+                                       data            : d.data,
+                                       state           : { },
+                                       li_attr         : { id : false },
+                                       a_attr          : { href : '#' },
+                                       original        : false
+                               };
+                       for(i in df) {
+                               if(df.hasOwnProperty(i)) {
+                                       tmp.state[i] = df[i];
+                               }
+                       }
+                       if(d && d.data && d.data.jstree && d.data.jstree.icon) {
+                               tmp.icon = d.data.jstree.icon;
+                       }
+                       if(tmp.icon === undefined || tmp.icon === null || 
tmp.icon === "") {
+                               tmp.icon = true;
+                       }
+                       if(d && d.data) {
+                               tmp.data = d.data;
+                               if(d.data.jstree) {
+                                       for(i in d.data.jstree) {
+                                               
if(d.data.jstree.hasOwnProperty(i)) {
+                                                       tmp.state[i] = 
d.data.jstree[i];
+                                               }
+                                       }
+                               }
+                       }
+                       if(d && typeof d.state === 'object') {
+                               for (i in d.state) {
+                                       if(d.state.hasOwnProperty(i)) {
+                                               tmp.state[i] = d.state[i];
+                                       }
+                               }
+                       }
+                       if(d && typeof d.li_attr === 'object') {
+                               for (i in d.li_attr) {
+                                       if(d.li_attr.hasOwnProperty(i)) {
+                                               tmp.li_attr[i] = d.li_attr[i];
+                                       }
+                               }
+                       }
+                       if(!tmp.li_attr.id) {
+                               tmp.li_attr.id = tid;
+                       }
+                       if(d && typeof d.a_attr === 'object') {
+                               for (i in d.a_attr) {
+                                       if(d.a_attr.hasOwnProperty(i)) {
+                                               tmp.a_attr[i] = d.a_attr[i];
+                                       }
+                               }
+                       }
+                       if(d && d.children && d.children === true) {
+                               tmp.state.loaded = false;
+                               tmp.children = [];
+                               tmp.children_d = [];
+                       }
+                       m[tmp.id] = tmp;
+                       for(i = 0, j = tmp.children.length; i < j; i++) {
+                               c = 
this._parse_model_from_flat_json(m[tmp.children[i]], tmp.id, ps);
+                               e = m[c];
+                               tmp.children_d.push(c);
+                               if(e.children_d.length) {
+                                       tmp.children_d = 
tmp.children_d.concat(e.children_d);
+                               }
+                       }
+                       delete d.data;
+                       delete d.children;
+                       m[tmp.id].original = d;
+                       if(tmp.state.selected) {
+                               this._data.core.selected.push(tmp.id);
+                       }
+                       return tmp.id;
+               },
+               /**
+                * parses a node from a JSON object and appends it to the in 
memory tree model. Used internally.
+                * @private
+                * @name _parse_model_from_json(d [, p, ps])
+                * @param  {Object} d the JSON object to parse
+                * @param  {String} p the parent ID
+                * @param  {Array} ps list of all parents
+                * @return {String} the ID of the object added to the model
+                */
+               _parse_model_from_json : function (d, p, ps) {
+                       if(!ps) { ps = []; }
+                       else { ps = ps.concat(); }
+                       if(p) { ps.unshift(p); }
+                       var tid = false, i, j, c, e, m = this._model.data, df = 
this._model.default_state, tmp;
+                       do {
+                               tid = 'j' + this._id + '_' + (++this._cnt);
+                       } while(m[tid]);
+
+                       tmp = {
+                               id                      : false,
+                               text            : typeof d === 'string' ? d : 
'',
+                               icon            : typeof d === 'object' && 
d.icon !== undefined ? d.icon : true,
+                               parent          : p,
+                               parents         : ps,
+                               children        : [],
+                               children_d      : [],
+                               data            : null,
+                               state           : { },
+                               li_attr         : { id : false },
+                               a_attr          : { href : '#' },
+                               original        : false
+                       };
+                       for(i in df) {
+                               if(df.hasOwnProperty(i)) {
+                                       tmp.state[i] = df[i];
+                               }
+                       }
+                       if(d && d.id) { tmp.id = d.id.toString(); }
+                       if(d && d.text) { tmp.text = d.text; }
+                       if(d && d.data && d.data.jstree && d.data.jstree.icon) {
+                               tmp.icon = d.data.jstree.icon;
+                       }
+                       if(tmp.icon === undefined || tmp.icon === null || 
tmp.icon === "") {
+                               tmp.icon = true;
+                       }
+                       if(d && d.data) {
+                               tmp.data = d.data;
+                               if(d.data.jstree) {
+                                       for(i in d.data.jstree) {
+                                               
if(d.data.jstree.hasOwnProperty(i)) {
+                                                       tmp.state[i] = 
d.data.jstree[i];
+                                               }
+                                       }
+                               }
+                       }
+                       if(d && typeof d.state === 'object') {
+                               for (i in d.state) {
+                                       if(d.state.hasOwnProperty(i)) {
+                                               tmp.state[i] = d.state[i];
+                                       }
+                               }
+                       }
+                       if(d && typeof d.li_attr === 'object') {
+                               for (i in d.li_attr) {
+                                       if(d.li_attr.hasOwnProperty(i)) {
+                                               tmp.li_attr[i] = d.li_attr[i];
+                                       }
+                               }
+                       }
+                       if(tmp.li_attr.id && !tmp.id) {
+                               tmp.id = tmp.li_attr.id.toString();
+                       }
+                       if(!tmp.id) {
+                               tmp.id = tid;
+                       }
+                       if(!tmp.li_attr.id) {
+                               tmp.li_attr.id = tmp.id;
+                       }
+                       if(d && typeof d.a_attr === 'object') {
+                               for (i in d.a_attr) {
+                                       if(d.a_attr.hasOwnProperty(i)) {
+                                               tmp.a_attr[i] = d.a_attr[i];
+                                       }
+                               }
+                       }
+                       if(d && d.children && d.children.length) {
+                               for(i = 0, j = d.children.length; i < j; i++) {
+                                       c = 
this._parse_model_from_json(d.children[i], tmp.id, ps);
+                                       e = m[c];
+                                       tmp.children.push(c);
+                                       if(e.children_d.length) {
+                                               tmp.children_d = 
tmp.children_d.concat(e.children_d);
+                                       }
+                               }
+                               tmp.children_d = 
tmp.children_d.concat(tmp.children);
+                       }
+                       if(d && d.children && d.children === true) {
+                               tmp.state.loaded = false;
+                               tmp.children = [];
+                               tmp.children_d = [];
+                       }
+                       delete d.data;
+                       delete d.children;
+                       tmp.original = d;
+                       m[tmp.id] = tmp;
+                       if(tmp.state.selected) {
+                               this._data.core.selected.push(tmp.id);
+                       }
+                       return tmp.id;
+               },
+               /**
+                * redraws all nodes that need to be redrawn. Used internally.
+                * @private
+                * @name _redraw()
+                * @trigger redraw.jstree
+                */
+               _redraw : function () {
+                       var nodes = this._model.force_full_redraw ? 
this._model.data['#'].children.concat([]) : this._model.changed.concat([]),
+                               f = document.createElement('UL'), tmp, i, j, fe 
= this._data.core.focused;
+                       for(i = 0, j = nodes.length; i < j; i++) {
+                               tmp = this.redraw_node(nodes[i], true, 
this._model.force_full_redraw);
+                               if(tmp && this._model.force_full_redraw) {
+                                       f.appendChild(tmp);
+                               }
+                       }
+                       if(this._model.force_full_redraw) {
+                               f.className = 
this.get_container_ul()[0].className;
+                               f.setAttribute('role','group');
+                               this.element.empty().append(f);
+                               //this.get_container_ul()[0].appendChild(f);
+                       }
+                       if(fe !== null) {
+                               tmp = this.get_node(fe, true);
+                               if(tmp && tmp.length && 
tmp.children('.jstree-anchor')[0] !== document.activeElement) {
+                                       tmp.children('.jstree-anchor').focus();
+                               }
+                               else {
+                                       this._data.core.focused = null;
+                               }
+                       }
+                       this._model.force_full_redraw = false;
+                       this._model.changed = [];
+                       /**
+                        * triggered after nodes are redrawn
+                        * @event
+                        * @name redraw.jstree
+                        * @param {array} nodes the redrawn nodes
+                        */
+                       this.trigger('redraw', { "nodes" : nodes });
+               },
+               /**
+                * redraws all nodes that need to be redrawn or optionally - 
the whole tree
+                * @name redraw([full])
+                * @param {Boolean} full if set to `true` all nodes are redrawn.
+                */
+               redraw : function (full) {
+                       if(full) {
+                               this._model.force_full_redraw = true;
+                       }
+                       //if(this._model.redraw_timeout) {
+                       //      clearTimeout(this._model.redraw_timeout);
+                       //}
+                       //this._model.redraw_timeout = 
setTimeout($.proxy(this._redraw, this),0);
+                       this._redraw();
+               },
+               /**
+                * redraws a single node's children. Used internally.
+                * @private
+                * @name draw_children(node)
+                * @param {mixed} node the node whose children will be redrawn
+                */
+               draw_children : function (node) {
+                       var obj = this.get_node(node),
+                               i = false,
+                               j = false,
+                               k = false,
+                               d = document;
+                       if(!obj) { return false; }
+                       if(obj.id === '#') { return this.redraw(true); }
+                       node = this.get_node(node, true);
+                       if(!node || !node.length) { return false; } // TODO: 
quick toggle
+
+                       node.children('.jstree-children').remove();
+                       node = node[0];
+                       if(obj.children.length && obj.state.loaded) {
+                               k = d.createElement('UL');
+                               k.setAttribute('role', 'group');
+                               k.className = 'jstree-children';
+                               for(i = 0, j = obj.children.length; i < j; i++) 
{
+                                       
k.appendChild(this.redraw_node(obj.children[i], true, true));
+                               }
+                               node.appendChild(k);
+                       }
+               },
+               /**
+                * redraws a single node. Used internally.
+                * @private
+                * @name redraw_node(node, deep, is_callback, force_render)
+                * @param {mixed} node the node to redraw
+                * @param {Boolean} deep should child nodes be redrawn too
+                * @param {Boolean} is_callback is this a recursion call
+                * @param {Boolean} force_render should children of closed 
parents be drawn anyway
+                */
+               redraw_node : function (node, deep, is_callback, force_render) {
+                       var obj = this.get_node(node),
+                               par = false,
+                               ind = false,
+                               old = false,
+                               i = false,
+                               j = false,
+                               k = false,
+                               c = '',
+                               d = document,
+                               m = this._model.data,
+                               f = false,
+                               s = false,
+                               tmp = null,
+                               t = 0,
+                               l = 0;
+                       if(!obj) { return false; }
+                       if(obj.id === '#') {  return this.redraw(true); }
+                       deep = deep || obj.children.length === 0;
+                       node = !document.querySelector ? 
document.getElementById(obj.id) : this.element[0].querySelector('#' + 
("0123456789".indexOf(obj.id[0]) !== -1 ? '\\3' + obj.id[0] + ' ' + 
obj.id.substr(1).replace($.jstree.idregex,'\\$&') : 
obj.id.replace($.jstree.idregex,'\\$&')) ); //, this.element);
+                       if(!node) {
+                               deep = true;
+                               //node = d.createElement('LI');
+                               if(!is_callback) {
+                                       par = obj.parent !== '#' ? $('#' + 
obj.parent.replace($.jstree.idregex,'\\$&'), this.element)[0] : null;
+                                       if(par !== null && (!par || 
!m[obj.parent].state.opened)) {
+                                               return false;
+                                       }
+                                       ind = $.inArray(obj.id, par === null ? 
m['#'].children : m[obj.parent].children);
+                               }
+                       }
+                       else {
+                               node = $(node);
+                               if(!is_callback) {
+                                       par = node.parent().parent()[0];
+                                       if(par === this.element[0]) {
+                                               par = null;
+                                       }
+                                       ind = node.index();
+                               }
+                               // m[obj.id].data = node.data(); // use only 
node's data, no need to touch jquery storage
+                               if(!deep && obj.children.length && 
!node.children('.jstree-children').length) {
+                                       deep = true;
+                               }
+                               if(!deep) {
+                                       old = 
node.children('.jstree-children')[0];
+                               }
+                               f = node.children('.jstree-anchor')[0] === 
document.activeElement;
+                               node.remove();
+                               //node = d.createElement('LI');
+                               //node = node[0];
+                       }
+                       node = _node.cloneNode(true);
+                       // node is DOM, deep is boolean
+
+                       c = 'jstree-node ';
+                       for(i in obj.li_attr) {
+                               if(obj.li_attr.hasOwnProperty(i)) {
+                                       if(i === 'id') { continue; }
+                                       if(i !== 'class') {
+                                               node.setAttribute(i, 
obj.li_attr[i]);
+                                       }
+                                       else {
+                                               c += obj.li_attr[i];
+                                       }
+                               }
+                       }
+                       if(!obj.a_attr.id) {
+                               obj.a_attr.id = obj.id + '_anchor';
+                       }
+                       node.setAttribute('aria-selected', 
!!obj.state.selected);
+                       node.setAttribute('aria-level', obj.parents.length);
+                       node.setAttribute('aria-labelledby', obj.a_attr.id);
+                       if(obj.state.disabled) {
+                               node.setAttribute('aria-disabled', true);
+                       }
+
+                       if(obj.state.loaded && !obj.children.length) {
+                               c += ' jstree-leaf';
+                       }
+                       else {
+                               c += obj.state.opened && obj.state.loaded ? ' 
jstree-open' : ' jstree-closed';
+                               node.setAttribute('aria-expanded', 
(obj.state.opened && obj.state.loaded) );
+                       }
+                       if(obj.parent !== null && 
m[obj.parent].children[m[obj.parent].children.length - 1] === obj.id) {
+                               c += ' jstree-last';
+                       }
+                       node.id = obj.id;
+                       node.className = c;
+                       c = ( obj.state.selected ? ' jstree-clicked' : '') + ( 
obj.state.disabled ? ' jstree-disabled' : '');
+                       for(j in obj.a_attr) {
+                               if(obj.a_attr.hasOwnProperty(j)) {
+                                       if(j === 'href' && obj.a_attr[j] === 
'#') { continue; }
+                                       if(j !== 'class') {
+                                               
node.childNodes[1].setAttribute(j, obj.a_attr[j]);
+                                       }
+                                       else {
+                                               c += ' ' + obj.a_attr[j];
+                                       }
+                               }
+                       }
+                       if(c.length) {
+                               node.childNodes[1].className = 'jstree-anchor ' 
+ c;
+                       }
+                       if((obj.icon && obj.icon !== true) || obj.icon === 
false) {
+                               if(obj.icon === false) {
+                                       
node.childNodes[1].childNodes[0].className += ' jstree-themeicon-hidden';
+                               }
+                               else if(obj.icon.indexOf('/') === -1 && 
obj.icon.indexOf('.') === -1) {
+                                       
node.childNodes[1].childNodes[0].className += ' ' + obj.icon + ' 
jstree-themeicon-custom';
+                               }
+                               else {
+                                       
node.childNodes[1].childNodes[0].style.backgroundImage = 'url('+obj.icon+')';
+                                       
node.childNodes[1].childNodes[0].style.backgroundPosition = 'center center';
+                                       
node.childNodes[1].childNodes[0].style.backgroundSize = 'auto';
+                                       
node.childNodes[1].childNodes[0].className += ' jstree-themeicon-custom';
+                               }
+                       }
+
+                       if(this.settings.core.force_text) {
+                               
node.childNodes[1].appendChild(d.createTextNode(obj.text));
+                       }
+                       else {
+                               node.childNodes[1].innerHTML += obj.text;
+                       }
+
+
+                       if(deep && obj.children.length && (obj.state.opened || 
force_render) && obj.state.loaded) {
+                               k = d.createElement('UL');
+                               k.setAttribute('role', 'group');
+                               k.className = 'jstree-children';
+                               for(i = 0, j = obj.children.length; i < j; i++) 
{
+                                       
k.appendChild(this.redraw_node(obj.children[i], deep, true));
+                               }
+                               node.appendChild(k);
+                       }
+                       if(old) {
+                               node.appendChild(old);
+                       }
+                       if(!is_callback) {
+                               // append back using par / ind
+                               if(!par) {
+                                       par = this.element[0];
+                               }
+                               for(i = 0, j = par.childNodes.length; i < j; 
i++) {
+                                       if(par.childNodes[i] && 
par.childNodes[i].className && 
par.childNodes[i].className.indexOf('jstree-children') !== -1) {
+                                               tmp = par.childNodes[i];
+                                               break;
+                                       }
+                               }
+                               if(!tmp) {
+                                       tmp = d.createElement('UL');
+                                       tmp.setAttribute('role', 'group');
+                                       tmp.className = 'jstree-children';
+                                       par.appendChild(tmp);
+                               }
+                               par = tmp;
+
+                               if(ind < par.childNodes.length) {
+                                       par.insertBefore(node, 
par.childNodes[ind]);
+                               }
+                               else {
+                                       par.appendChild(node);
+                               }
+                               if(f) {
+                                       t = this.element[0].scrollTop;
+                                       l = this.element[0].scrollLeft;
+                                       node.childNodes[1].focus();
+                                       this.element[0].scrollTop = t;
+                                       this.element[0].scrollLeft = l;
+                               }
+                       }
+                       if(obj.state.opened && !obj.state.loaded) {
+                               obj.state.opened = false;
+                               setTimeout($.proxy(function () {
+                                       this.open_node(obj.id, false, 0);
+                               }, this), 0);
+                       }
+                       return node;
+               },
+               /**
+                * opens a node, revaling its children. If the node is not 
loaded it will be loaded and opened once ready.
+                * @name open_node(obj [, callback, animation])
+                * @param {mixed} obj the node to open
+                * @param {Function} callback a function to execute once the 
node is opened
+                * @param {Number} animation the animation duration in 
milliseconds when opening the node (overrides the `core.animation` setting). 
Use `false` for no animation.
+                * @trigger open_node.jstree, after_open.jstree, 
before_open.jstree
+                */
+               open_node : function (obj, callback, animation) {
+                       var t1, t2, d, t;
+                       if($.isArray(obj)) {
+                               obj = obj.slice();
+                               for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
+                                       this.open_node(obj[t1], callback, 
animation);
+                               }
+                               return true;
+                       }
+                       obj = this.get_node(obj);
+                       if(!obj || obj.id === '#') {
+                               return false;
+                       }
+                       animation = animation === undefined ? 
this.settings.core.animation : animation;
+                       if(!this.is_closed(obj)) {
+                               if(callback) {
+                                       callback.call(this, obj, false);
+                               }
+                               return false;
+                       }
+                       if(!this.is_loaded(obj)) {
+                               if(this.is_loading(obj)) {
+                                       return setTimeout($.proxy(function () {
+                                               this.open_node(obj, callback, 
animation);
+                                       }, this), 500);
+                               }
+                               this.load_node(obj, function (o, ok) {
+                                       return ok ? this.open_node(o, callback, 
animation) : (callback ? callback.call(this, o, false) : false);
+                               });
+                       }
+                       else {
+                               d = this.get_node(obj, true);
+                               t = this;
+                               if(d.length) {
+                                       if(animation && 
d.children(".jstree-children").length) {
+                                               
d.children(".jstree-children").stop(true, true);
+                                       }
+                                       if(obj.children.length && 
!this._firstChild(d.children('.jstree-children')[0])) {
+                                               this.draw_children(obj);
+                                               //d = this.get_node(obj, true);
+                                       }
+                                       if(!animation) {
+                                               this.trigger('before_open', { 
"node" : obj });
+                                               d[0].className = 
d[0].className.replace('jstree-closed', 'jstree-open');
+                                               
d[0].setAttribute("aria-expanded", true);
+                                       }
+                                       else {
+                                               this.trigger('before_open', { 
"node" : obj });
+                                               d
+                                                       
.children(".jstree-children").css("display","none").end()
+                                                       
.removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded", 
true)
+                                                       
.children(".jstree-children").stop(true, true)
+                                                               
.slideDown(animation, function () {
+                                                                       
this.style.display = "";
+                                                                       
t.trigger("after_open", { "node" : obj });
+                                                               });
+                                       }
+                               }
+                               obj.state.opened = true;
+                               if(callback) {
+                                       callback.call(this, obj, true);
+                               }
+                               if(!d.length) {
+                                       /**
+                                        * triggered when a node is about to be 
opened (if the node is supposed to be in the DOM, it will be, but it won't be 
visible yet)
+                                        * @event
+                                        * @name before_open.jstree
+                                        * @param {Object} node the opened node
+                                        */
+                                       this.trigger('before_open', { "node" : 
obj });
+                               }
+                               /**
+                                * triggered when a node is opened (if there is 
an animation it will not be completed yet)
+                                * @event
+                                * @name open_node.jstree
+                                * @param {Object} node the opened node
+                                */
+                               this.trigger('open_node', { "node" : obj });
+                               if(!animation || !d.length) {
+                                       /**
+                                        * triggered when a node is opened and 
the animation is complete
+                                        * @event
+                                        * @name after_open.jstree
+                                        * @param {Object} node the opened node
+                                        */
+                                       this.trigger("after_open", { "node" : 
obj });
+                               }
+                       }
+               },
+               /**
+                * opens every parent of a node (node should be loaded)
+                * @name _open_to(obj)
+                * @param {mixed} obj the node to reveal
+                * @private
+                */
+               _open_to : function (obj) {
+                       obj = this.get_node(obj);
+                       if(!obj || obj.id === '#') {
+                               return false;
+                       }
+                       var i, j, p = obj.parents;
+                       for(i = 0, j = p.length; i < j; i+=1) {
+                               if(i !== '#') {
+                                       this.open_node(p[i], false, 0);
+                               }
+                       }
+                       return $('#' + obj.id.replace($.jstree.idregex,'\\$&'), 
this.element);
+               },
+               /**
+                * closes a node, hiding its children
+                * @name close_node(obj [, animation])
+                * @param {mixed} obj the node to close
+                * @param {Number} animation the animation duration in 
milliseconds when closing the node (overrides the `core.animation` setting). 
Use `false` for no animation.
+                * @trigger close_node.jstree, after_close.jstree
+                */
+               close_node : function (obj, animation) {
+                       var t1, t2, t, d;
+                       if($.isArray(obj)) {
+                               obj = obj.slice();
+                               for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
+                                       this.close_node(obj[t1], animation);
+                               }
+                               return true;
+                       }
+                       obj = this.get_node(obj);
+                       if(!obj || obj.id === '#') {
+                               return false;
+                       }
+                       if(this.is_closed(obj)) {
+                               return false;
+                       }
+                       animation = animation === undefined ? 
this.settings.core.animation : animation;
+                       t = this;
+                       d = this.get_node(obj, true);
+                       if(d.length) {
+                               if(!animation) {
+                                       d[0].className = 
d[0].className.replace('jstree-open', 'jstree-closed');
+                                       d.attr("aria-expanded", 
false).children('.jstree-children').remove();
+                               }
+                               else {
+                                       d
+                                               
.children(".jstree-children").attr("style","display:block !important").end()
+                                               
.removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded", 
false)
+                                               
.children(".jstree-children").stop(true, true).slideUp(animation, function () {
+                                                       this.style.display = "";
+                                                       
d.children('.jstree-children').remove();
+                                                       
t.trigger("after_close", { "node" : obj });
+                                               });
+                               }
+                       }
+                       obj.state.opened = false;
+                       /**
+                        * triggered when a node is closed (if there is an 
animation it will not be complete yet)
+                        * @event
+                        * @name close_node.jstree
+                        * @param {Object} node the closed node
+                        */
+                       this.trigger('close_node',{ "node" : obj });
+                       if(!animation || !d.length) {
+                               /**
+                                * triggered when a node is closed and the 
animation is complete
+                                * @event
+                                * @name after_close.jstree
+                                * @param {Object} node the closed node
+                                */
+                               this.trigger("after_close", { "node" : obj });
+                       }
+               },
+               /**
+                * toggles a node - closing it if it is open, opening it if it 
is closed
+                * @name toggle_node(obj)
+                * @param {mixed} obj the node to toggle
+                */
+               toggle_node : function (obj) {
+                       var t1, t2;
+                       if($.isArray(obj)) {
+                               obj = obj.slice();
+                               for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
+                                       this.toggle_node(obj[t1]);
+                               }
+                               return true;
+                       }
+                       if(this.is_closed(obj)) {
+                               return this.open_node(obj);
+                       }
+                       if(this.is_open(obj)) {
+                               return this.close_node(obj);
+                       }
+               },
+               /**
+                * opens all nodes within a node (or the tree), revaling their 
children. If the node is not loaded it will be loaded and opened once ready.
+                * @name open_all([obj, animation, original_obj])
+                * @param {mixed} obj the node to open recursively, omit to 
open all nodes in the tree
+                * @param {Number} animation the animation duration in 
milliseconds when opening the nodes, the default is no animation
+                * @param {jQuery} reference to the node that started the 
process (internal use)
+                * @trigger open_all.jstree
+                */
+               open_all : function (obj, animation, original_obj) {
+                       if(!obj) { obj = '#'; }
+                       obj = this.get_node(obj);
+                       if(!obj) { return false; }
+                       var dom = obj.id === '#' ? this.get_container_ul() : 
this.get_node(obj, true), i, j, _this;
+                       if(!dom.length) {
+                               for(i = 0, j = obj.children_d.length; i < j; 
i++) {
+                                       
if(this.is_closed(this._model.data[obj.children_d[i]])) {
+                                               
this._model.data[obj.children_d[i]].state.opened = true;
+                                       }
+                               }
+                               return this.trigger('open_all', { "node" : obj 
});
+                       }
+                       original_obj = original_obj || dom;
+                       _this = this;
+                       dom = this.is_closed(obj) ? 
dom.find('.jstree-closed').addBack() : dom.find('.jstree-closed');
+                       dom.each(function () {
+                               _this.open_node(
+                                       this,
+                                       function(node, status) { if(status && 
this.is_parent(node)) { this.open_all(node, animation, original_obj); } },
+                                       animation || 0
+                               );
+                       });
+                       if(original_obj.find('.jstree-closed').length === 0) {
+                               /**
+                                * triggered when an `open_all` call completes
+                                * @event
+                                * @name open_all.jstree
+                                * @param {Object} node the opened node
+                                */
+                               this.trigger('open_all', { "node" : 
this.get_node(original_obj) });
+                       }
+               },
+               /**
+                * closes all nodes within a node (or the tree), revaling their 
children
+                * @name close_all([obj, animation])
+                * @param {mixed} obj the node to close recursively, omit to 
close all nodes in the tree
+                * @param {Number} animat

<TRUNCATED>

Reply via email to