text: /**
   * ### Drag'n'drop plugin
   *
   * Enables dragging and dropping of nodes in the tree, resulting in a move or copy operations.
   */
  /*globals jQuery, define, exports, require, document */
  (function (factory) {
  	"use strict";
  	if (typeof define === 'function' && define.amd) {
  		define('jstree.dnd', ['jquery','jstree'], factory);
  	}
  	else if(typeof exports === 'object') {
  		factory(require('jquery'), require('jstree'));
  	}
  	else {
  		factory(jQuery, jQuery.jstree);
  	}
  }(function ($, jstree, undefined) {
  	"use strict";
  
  	if($.jstree.plugins.dnd) { return; }
  
  	/**
  	 * stores all defaults for the drag'n'drop plugin
  	 * @name $.jstree.defaults.dnd
  	 * @plugin dnd
  	 */
  	$.jstree.defaults.dnd = {
  		/**
  		 * a boolean indicating if a copy should be possible while dragging (by pressint the meta key or Ctrl). Defaults to `true`.
  		 * @name $.jstree.defaults.dnd.copy
  		 * @plugin dnd
  		 */
  		copy : true,
  		/**
  		 * a number indicating how long a node should remain hovered while dragging to be opened. Defaults to `500`.
  		 * @name $.jstree.defaults.dnd.open_timeout
  		 * @plugin dnd
  		 */
  		open_timeout : 500,
  		/**
  		 * a function invoked each time a node is about to be dragged, invoked in the tree's scope and receives the nodes about to be dragged as an argument (array) - return `false` to prevent dragging
  		 * @name $.jstree.defaults.dnd.is_draggable
  		 * @plugin dnd
  		 */
  		is_draggable : true,
  		/**
  		 * a boolean indicating if checks should constantly be made while the user is dragging the node (as opposed to checking only on drop), default is `true`
  		 * @name $.jstree.defaults.dnd.check_while_dragging
  		 * @plugin dnd
  		 */
  		check_while_dragging : true,
  		/**
  		 * a boolean indicating if nodes from this tree should only be copied with dnd (as opposed to moved), default is `false`
  		 * @name $.jstree.defaults.dnd.always_copy
  		 * @plugin dnd
  		 */
  		always_copy : false,
  		/**
  		 * when dropping a node "inside", this setting indicates the position the node should go to - it can be an integer or a string: "first" (same as 0) or "last", default is `0`
  		 * @name $.jstree.defaults.dnd.inside_pos
  		 * @plugin dnd
  		 */
  		inside_pos : 0
  	};
  	// TODO: now check works by checking for each node individually, how about max_children, unique, etc?
  	$.jstree.plugins.dnd = function (options, parent) {
  		this.bind = function () {
  			parent.bind.call(this);
  
  			this.element
  				.on('mousedown.jstree touchstart.jstree', '.jstree-anchor', $.proxy(function (e) {
  					var obj = this.get_node(e.target),
  						mlt = this.is_selected(obj) ? this.get_selected().length : 1;
  					if(obj && obj.id && obj.id !== "#" && (e.which === 1 || e.type === "touchstart") &&
  						(this.settings.dnd.is_draggable === true || ($.isFunction(this.settings.dnd.is_draggable) && this.settings.dnd.is_draggable.call(this, (mlt > 1 ? this.get_selected(true) : [obj]))))
  					) {
  						this.element.trigger('mousedown.jstree');
  						return $.vakata.dnd.start(e, { 'jstree' : true, 'origin' : this, 'obj' : this.get_node(obj,true), 'nodes' : mlt > 1 ? this.get_selected() : [obj.id] }, '<div id="jstree-dnd" class="jstree-' + this.get_theme() + ' jstree-' + this.get_theme() + '-' + this.get_theme_variant() + ' ' + ( this.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ) + '"><i class="jstree-icon jstree-er"></i>' + (mlt > 1 ? mlt + ' ' + this.get_string('nodes') : this.get_text(e.currentTarget, true)) + '<ins class="jstree-copy" style="display:none;">+</ins></div>');
  					}
  				}, this));
  		};
  	};
  
  	$(function() {
  		// bind only once for all instances
  		var lastmv = false,
  			laster = false,
  			opento = false,
  			marker = $('<div id="jstree-marker">&#160;</div>').hide(); //.appendTo('body');
  
  		$(document)
  			.on('dnd_start.vakata.jstree', function (e, data) {
  				lastmv = false;
  				if(!data || !data.data || !data.data.jstree) { return; }
  				marker.appendTo('body'); //.show();
  			})
  			.on('dnd_move.vakata.jstree', function (e, data) {
  				if(opento) { clearTimeout(opento); }
  				if(!data || !data.data || !data.data.jstree) { return; }
  
  				// if we are hovering the marker image do nothing (can happen on "inside" drags)
  				if(data.event.target.id && data.event.target.id === 'jstree-marker') {
  					return;
  				}
  
  				var ins = $.jstree.reference(data.event.target),
  					ref = false,
  					off = false,
  					rel = false,
  					l, t, h, p, i, o, ok, t1, t2, op, ps, pr, ip, tm;
  				// if we are over an instance
  				if(ins && ins._data && ins._data.dnd) {
  					marker.attr('class', 'jstree-' + ins.get_theme() + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ));
  					data.helper
  						.children().attr('class', 'jstree-' + ins.get_theme() + ' jstree-' + ins.get_theme() + '-' + ins.get_theme_variant() + ' ' + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ))
  						.find('.jstree-copy').first()[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? 'show' : 'hide' ]();
  
  
  					// if are hovering the container itself add a new root node
  					if( (data.event.target === ins.element[0] || data.event.target === ins.get_container_ul()[0]) && ins.get_container_ul().children().length === 0) {
  						ok = true;
  						for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
  							ok = ok && ins.check( (data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)) ) ? "copy_node" : "move_node"), (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), '#', 'last', { 'dnd' : true, 'ref' : ins.get_node('#'), 'pos' : 'i', 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) });
  							if(!ok) { break; }
  						}
  						if(ok) {
  							lastmv = { 'ins' : ins, 'par' : '#', 'pos' : 'last' };
  							marker.hide();
  							data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
  							return;
  						}
  					}
  					else {
  						// if we are hovering a tree node
  						ref = $(data.event.target).closest('.jstree-anchor');
  						if(ref && ref.length && ref.parent().is('.jstree-closed, .jstree-open, .jstree-leaf')) {
  							off = ref.offset();
  							rel = data.event.pageY - off.top;
  							h = ref.height();
  							if(rel < h / 3) {
  								o = ['b', 'i', 'a'];
  							}
  							else if(rel > h - h / 3) {
  								o = ['a', 'i', 'b'];
  							}
  							else {
  								o = rel > h / 2 ? ['i', 'a', 'b'] : ['i', 'b', 'a'];
  							}
  							$.each(o, function (j, v) {
  								switch(v) {
  									case 'b':
  										l = off.left - 6;
  										t = off.top;
  										p = ins.get_parent(ref);
  										i = ref.parent().index();
  										break;
  									case 'i':
  										ip = ins.settings.dnd.inside_pos;
  										tm = ins.get_node(ref.parent());
  										l = off.left - 2;
  										t = off.top + h / 2 + 1;
  										p = tm.id;
  										i = ip === 'first' ? 0 : (ip === 'last' ? tm.children.length : Math.min(ip, tm.children.length));
  										break;
  									case 'a':
  										l = off.left - 6;
  										t = off.top + h;
  										p = ins.get_parent(ref);
  										i = ref.parent().index() + 1;
  										break;
  								}
  								ok = true;
  								for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
  									op = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? "copy_node" : "move_node";
  									ps = i;
  									if(op === "move_node" && v === 'a' && (data.data.origin && data.data.origin === ins) && p === ins.get_parent(data.data.nodes[t1])) {
  										pr = ins.get_node(p);
  										if(ps > $.inArray(data.data.nodes[t1], pr.children)) {
  											ps -= 1;
  										}
  									}
  									ok = ok && ( (ins && ins.settings && ins.settings.dnd && ins.settings.dnd.check_while_dragging === false) || ins.check(op, (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), p, ps, { 'dnd' : true, 'ref' : ins.get_node(ref.parent()), 'pos' : v, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) }) );
  									if(!ok) {
  										if(ins && ins.last_error) { laster = ins.last_error(); }
  										break;
  									}
  								}
  								if(v === 'i' && ref.parent().is('.jstree-closed') && ins.settings.dnd.open_timeout) {
  									opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout);
  								}
  								if(ok) {
  									lastmv = { 'ins' : ins, 'par' : p, 'pos' : v === 'i' && ip === 'last' && i === 0 && !ins.is_loaded(tm) ? 'last' : i };
  									marker.css({ 'left' : l + 'px', 'top' : t + 'px' }).show();
  									data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
  									laster = {};
  									o = true;
  									return false;
  								}
  							});
  							if(o === true) { return; }
  						}
  					}
  				}
  				lastmv = false;
  				data.helper.find('.jstree-icon').removeClass('jstree-ok').addClass('jstree-er');
  				marker.hide();
  			})
  			.on('dnd_scroll.vakata.jstree', function (e, data) {
  				if(!data || !data.data || !data.data.jstree) { return; }
  				marker.hide();
  				lastmv = false;
  				data.helper.find('.jstree-icon').first().removeClass('jstree-ok').addClass('jstree-er');
  			})
  			.on('dnd_stop.vakata.jstree', function (e, data) {
  				if(opento) { clearTimeout(opento); }
  				if(!data || !data.data || !data.data.jstree) { return; }
  				marker.hide().detach();
  				var i, j, nodes = [];
  				if(lastmv) {
  					for(i = 0, j = data.data.nodes.length; i < j; i++) {
  						nodes[i] = data.data.origin ? data.data.origin.get_node(data.data.nodes[i]) : data.data.nodes[i];
  						if(data.data.origin) {
  							nodes[i].instance = data.data.origin;
  						}
  					}
  					lastmv.ins[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? 'copy_node' : 'move_node' ](nodes, lastmv.par, lastmv.pos);
  					for(i = 0, j = nodes.length; i < j; i++) {
  						if(nodes[i].instance) {
  							nodes[i].instance = null;
  						}
  					}
  				}
  				else {
  					i = $(data.event.target).closest('.jstree');
  					if(i.length && laster && laster.error && laster.error === 'check') {
  						i = i.jstree(true);
  						if(i) {
  							i.settings.core.error.call(this, laster);
  						}
  					}
  				}
  			})
  			.on('keyup.jstree keydown.jstree', function (e, data) {
  				data = $.vakata.dnd._get();
  				if(data && data.data && data.data.jstree) {
  					data.helper.find('.jstree-copy').first()[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (e.metaKey || e.ctrlKey))) ? 'show' : 'hide' ]();
  				}
  			});
  	});
  
  	// helpers
  	(function ($) {
  		// private variable
  		var vakata_dnd = {
  			element	: false,
  			target	: false,
  			is_down	: false,
  			is_drag	: false,
  			helper	: false,
  			helper_w: 0,
  			data	: false,
  			init_x	: 0,
  			init_y	: 0,
  			scroll_l: 0,
  			scroll_t: 0,
  			scroll_e: false,
  			scroll_i: false,
  			is_touch: false
  		};
  		$.vakata.dnd = {
  			settings : {
  				scroll_speed		: 10,
  				scroll_proximity	: 20,
  				helper_left			: 5,
  				helper_top			: 10,
  				threshold			: 5,
  				threshold_touch		: 50
  			},
  			_trigger : function (event_name, e) {
  				var data = $.vakata.dnd._get();
  				data.event = e;
  				$(document).triggerHandler("dnd_" + event_name + ".vakata", data);
  			},
  			_get : function () {
  				return {
  					"data"		: vakata_dnd.data,
  					"element"	: vakata_dnd.element,
  					"helper"	: vakata_dnd.helper
  				};
  			},
  			_clean : function () {
  				if(vakata_dnd.helper) { vakata_dnd.helper.remove(); }
  				if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
  				vakata_dnd = {
  					element	: false,
  					target	: false,
  					is_down	: false,
  					is_drag	: false,
  					helper	: false,
  					helper_w: 0,
  					data	: false,
  					init_x	: 0,
  					init_y	: 0,
  					scroll_l: 0,
  					scroll_t: 0,
  					scroll_e: false,
  					scroll_i: false,
  					is_touch: false
  				};
  				$(document).off("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
  				$(document).off("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
  			},
  			_scroll : function (init_only) {
  				if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) {
  					if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
  					return false;
  				}
  				if(!vakata_dnd.scroll_i) {
  					vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100);
  					return false;
  				}
  				if(init_only === true) { return false; }
  
  				var i = vakata_dnd.scroll_e.scrollTop(),
  					j = vakata_dnd.scroll_e.scrollLeft();
  				vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed);
  				vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed);
  				if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) {
  					/**
  					 * triggered on the document when a drag causes an element to scroll
  					 * @event
  					 * @plugin dnd
  					 * @name dnd_scroll.vakata
  					 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  					 * @param {DOM} element the DOM element being dragged
  					 * @param {jQuery} helper the helper shown next to the mouse
  					 * @param {jQuery} event the element that is scrolling
  					 */
  					$.vakata.dnd._trigger("scroll", vakata_dnd.scroll_e);
  				}
  			},
  			start : function (e, data, html) {
  				if(e.type === "touchstart" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
  					e.pageX = e.originalEvent.changedTouches[0].pageX;
  					e.pageY = e.originalEvent.changedTouches[0].pageY;
  					e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
  				}
  				if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); }
  				try {
  					e.currentTarget.unselectable = "on";
  					e.currentTarget.onselectstart = function() { return false; };
  					if(e.currentTarget.style) { e.currentTarget.style.MozUserSelect = "none"; }
  				} catch(ignore) { }
  				vakata_dnd.init_x	= e.pageX;
  				vakata_dnd.init_y	= e.pageY;
  				vakata_dnd.data		= data;
  				vakata_dnd.is_down	= true;
  				vakata_dnd.element	= e.currentTarget;
  				vakata_dnd.target	= e.target;
  				vakata_dnd.is_touch	= e.type === "touchstart";
  				if(html !== false) {
  					vakata_dnd.helper = $("<div id='vakata-dnd'></div>").html(html).css({
  						"display"		: "block",
  						"margin"		: "0",
  						"padding"		: "0",
  						"position"		: "absolute",
  						"top"			: "-2000px",
  						"lineHeight"	: "16px",
  						"zIndex"		: "10000"
  					});
  				}
  				$(document).on("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
  				$(document).on("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
  				return false;
  			},
  			drag : function (e) {
  				if(e.type === "touchmove" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
  					e.pageX = e.originalEvent.changedTouches[0].pageX;
  					e.pageY = e.originalEvent.changedTouches[0].pageY;
  					e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
  				}
  				if(!vakata_dnd.is_down) { return; }
  				if(!vakata_dnd.is_drag) {
  					if(
  						Math.abs(e.pageX - vakata_dnd.init_x) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) ||
  						Math.abs(e.pageY - vakata_dnd.init_y) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold)
  					) {
  						if(vakata_dnd.helper) {
  							vakata_dnd.helper.appendTo("body");
  							vakata_dnd.helper_w = vakata_dnd.helper.outerWidth();
  						}
  						vakata_dnd.is_drag = true;
  						/**
  						 * triggered on the document when a drag starts
  						 * @event
  						 * @plugin dnd
  						 * @name dnd_start.vakata
  						 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  						 * @param {DOM} element the DOM element being dragged
  						 * @param {jQuery} helper the helper shown next to the mouse
  						 * @param {Object} event the event that caused the start (probably mousemove)
  						 */
  						$.vakata.dnd._trigger("start", e);
  					}
  					else { return; }
  				}
  
  				var d  = false, w  = false,
  					dh = false, wh = false,
  					dw = false, ww = false,
  					dt = false, dl = false,
  					ht = false, hl = false;
  
  				vakata_dnd.scroll_t = 0;
  				vakata_dnd.scroll_l = 0;
  				vakata_dnd.scroll_e = false;
  				$($(e.target).parentsUntil("body").addBack().get().reverse())
  					.filter(function () {
  						return	(/^auto|scroll$/).test($(this).css("overflow")) &&
  								(this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth);
  					})
  					.each(function () {
  						var t = $(this), o = t.offset();
  						if(this.scrollHeight > this.offsetHeight) {
  							if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity)	{ vakata_dnd.scroll_t = 1; }
  							if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity)				{ vakata_dnd.scroll_t = -1; }
  						}
  						if(this.scrollWidth > this.offsetWidth) {
  							if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity)	{ vakata_dnd.scroll_l = 1; }
  							if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity)				{ vakata_dnd.scroll_l = -1; }
  						}
  						if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
  							vakata_dnd.scroll_e = $(this);
  							return false;
  						}
  					});
  
  				if(!vakata_dnd.scroll_e) {
  					d  = $(document); w = $(window);
  					dh = d.height(); wh = w.height();
  					dw = d.width(); ww = w.width();
  					dt = d.scrollTop(); dl = d.scrollLeft();
  					if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity)		{ vakata_dnd.scroll_t = -1;  }
  					if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity)	{ vakata_dnd.scroll_t = 1; }
  					if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity)		{ vakata_dnd.scroll_l = -1; }
  					if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity)	{ vakata_dnd.scroll_l = 1; }
  					if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
  						vakata_dnd.scroll_e = d;
  					}
  				}
  				if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); }
  
  				if(vakata_dnd.helper) {
  					ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10);
  					hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10);
  					if(dh && ht + 25 > dh) { ht = dh - 50; }
  					if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); }
  					vakata_dnd.helper.css({
  						left	: hl + "px",
  						top		: ht + "px"
  					});
  				}
  				/**
  				 * triggered on the document when a drag is in progress
  				 * @event
  				 * @plugin dnd
  				 * @name dnd_move.vakata
  				 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  				 * @param {DOM} element the DOM element being dragged
  				 * @param {jQuery} helper the helper shown next to the mouse
  				 * @param {Object} event the event that caused this to trigger (most likely mousemove)
  				 */
  				$.vakata.dnd._trigger("move", e);
  				return false;
  			},
  			stop : function (e) {
  				if(e.type === "touchend" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
  					e.pageX = e.originalEvent.changedTouches[0].pageX;
  					e.pageY = e.originalEvent.changedTouches[0].pageY;
  					e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
  				}
  				if(vakata_dnd.is_drag) {
  					/**
  					 * triggered on the document when a drag stops (the dragged element is dropped)
  					 * @event
  					 * @plugin dnd
  					 * @name dnd_stop.vakata
  					 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  					 * @param {DOM} element the DOM element being dragged
  					 * @param {jQuery} helper the helper shown next to the mouse
  					 * @param {Object} event the event that caused the stop
  					 */
  					$.vakata.dnd._trigger("stop", e);
  				}
  				else {
  					if(e.type === "touchend" && e.target === vakata_dnd.target) {
  						var to = setTimeout(function () { $(e.target).click(); }, 100);
  						$(e.target).one('click', function() { if(to) { clearTimeout(to); } });
  					}
  				}
  				$.vakata.dnd._clean();
  				return false;
  			}
  		};
  	}($));
  
  	// include the dnd plugin by default
  	// $.jstree.defaults.plugins.push("dnd");
  }));
  