jstree.dnd.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. /**
  2. * ### Drag'n'drop plugin
  3. *
  4. * Enables dragging and dropping of nodes in the tree, resulting in a move or copy operations.
  5. */
  6. /*globals jQuery, define, exports, require, document */
  7. (function (factory) {
  8. "use strict";
  9. if (typeof define === 'function' && define.amd) {
  10. define('jstree.dnd', ['jquery','jstree'], factory);
  11. }
  12. else if(typeof exports === 'object') {
  13. factory(require('jquery'), require('jstree'));
  14. }
  15. else {
  16. factory(jQuery, jQuery.jstree);
  17. }
  18. }(function ($, jstree, undefined) {
  19. "use strict";
  20. if($.jstree.plugins.dnd) { return; }
  21. /**
  22. * stores all defaults for the drag'n'drop plugin
  23. * @name $.jstree.defaults.dnd
  24. * @plugin dnd
  25. */
  26. $.jstree.defaults.dnd = {
  27. /**
  28. * a boolean indicating if a copy should be possible while dragging (by pressint the meta key or Ctrl). Defaults to `true`.
  29. * @name $.jstree.defaults.dnd.copy
  30. * @plugin dnd
  31. */
  32. copy : true,
  33. /**
  34. * a number indicating how long a node should remain hovered while dragging to be opened. Defaults to `500`.
  35. * @name $.jstree.defaults.dnd.open_timeout
  36. * @plugin dnd
  37. */
  38. open_timeout : 500,
  39. /**
  40. * 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) and the event that started the drag - return `false` to prevent dragging
  41. * @name $.jstree.defaults.dnd.is_draggable
  42. * @plugin dnd
  43. */
  44. is_draggable : true,
  45. /**
  46. * 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`
  47. * @name $.jstree.defaults.dnd.check_while_dragging
  48. * @plugin dnd
  49. */
  50. check_while_dragging : true,
  51. /**
  52. * a boolean indicating if nodes from this tree should only be copied with dnd (as opposed to moved), default is `false`
  53. * @name $.jstree.defaults.dnd.always_copy
  54. * @plugin dnd
  55. */
  56. always_copy : false,
  57. /**
  58. * 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`
  59. * @name $.jstree.defaults.dnd.inside_pos
  60. * @plugin dnd
  61. */
  62. inside_pos : 0,
  63. /**
  64. * when starting the drag on a node that is selected this setting controls if all selected nodes are dragged or only the single node, default is `true`, which means all selected nodes are dragged when the drag is started on a selected node
  65. * @name $.jstree.defaults.dnd.drag_selection
  66. * @plugin dnd
  67. */
  68. drag_selection : true,
  69. /**
  70. * controls whether dnd works on touch devices. If left as boolean true dnd will work the same as in desktop browsers, which in some cases may impair scrolling. If set to boolean false dnd will not work on touch devices. There is a special third option - string "selected" which means only selected nodes can be dragged on touch devices.
  71. * @name $.jstree.defaults.dnd.touch
  72. * @plugin dnd
  73. */
  74. touch : true,
  75. /**
  76. * controls whether items can be dropped anywhere on the node, not just on the anchor, by default only the node anchor is a valid drop target. Works best with the wholerow plugin. If enabled on mobile depending on the interface it might be hard for the user to cancel the drop, since the whole tree container will be a valid drop target.
  77. * @name $.jstree.defaults.dnd.large_drop_target
  78. * @plugin dnd
  79. */
  80. large_drop_target : false,
  81. /**
  82. * controls whether a drag can be initiated from any part of the node and not just the text/icon part, works best with the wholerow plugin. Keep in mind it can cause problems with tree scrolling on mobile depending on the interface - in that case set the touch option to "selected".
  83. * @name $.jstree.defaults.dnd.large_drag_target
  84. * @plugin dnd
  85. */
  86. large_drag_target : false,
  87. /**
  88. * controls whether use HTML5 dnd api instead of classical. That will allow better integration of dnd events with other HTML5 controls.
  89. * @reference http://caniuse.com/#feat=dragndrop
  90. * @name $.jstree.defaults.dnd.use_html5
  91. * @plugin dnd
  92. */
  93. use_html5: false
  94. };
  95. var drg, elm;
  96. // TODO: now check works by checking for each node individually, how about max_children, unique, etc?
  97. $.jstree.plugins.dnd = function (options, parent) {
  98. this.init = function (el, options) {
  99. parent.init.call(this, el, options);
  100. this.settings.dnd.use_html5 = this.settings.dnd.use_html5 && ('draggable' in document.createElement('span'));
  101. };
  102. this.bind = function () {
  103. parent.bind.call(this);
  104. this.element
  105. .on(this.settings.dnd.use_html5 ? 'dragstart.jstree' : 'mousedown.jstree touchstart.jstree', this.settings.dnd.large_drag_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) {
  106. if(this.settings.dnd.large_drag_target && $(e.target).closest('.jstree-node')[0] !== e.currentTarget) {
  107. return true;
  108. }
  109. if(e.type === "touchstart" && (!this.settings.dnd.touch || (this.settings.dnd.touch === 'selected' && !$(e.currentTarget).closest('.jstree-node').children('.jstree-anchor').hasClass('jstree-clicked')))) {
  110. return true;
  111. }
  112. var obj = this.get_node(e.target),
  113. mlt = this.is_selected(obj) && this.settings.dnd.drag_selection ? this.get_top_selected().length : 1,
  114. txt = (mlt > 1 ? mlt + ' ' + this.get_string('nodes') : this.get_text(e.currentTarget));
  115. if(this.settings.core.force_text) {
  116. txt = $.vakata.html.escape(txt);
  117. }
  118. if(obj && obj.id && obj.id !== $.jstree.root && (e.which === 1 || e.type === "touchstart" || e.type === "dragstart") &&
  119. (this.settings.dnd.is_draggable === true || ($.isFunction(this.settings.dnd.is_draggable) && this.settings.dnd.is_draggable.call(this, (mlt > 1 ? this.get_top_selected(true) : [obj]), e)))
  120. ) {
  121. drg = { 'jstree' : true, 'origin' : this, 'obj' : this.get_node(obj,true), 'nodes' : mlt > 1 ? this.get_top_selected() : [obj.id] };
  122. elm = e.currentTarget;
  123. if (this.settings.dnd.use_html5) {
  124. $.vakata.dnd._trigger('start', e, { 'helper': $(), 'element': elm, 'data': drg });
  125. } else {
  126. this.element.trigger('mousedown.jstree');
  127. return $.vakata.dnd.start(e, drg, '<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>' + txt + '<ins class="jstree-copy" style="display:none;">+</ins></div>');
  128. }
  129. }
  130. }, this));
  131. if (this.settings.dnd.use_html5) {
  132. this.element
  133. .on('dragover.jstree', function (e) {
  134. e.preventDefault();
  135. $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });
  136. return false;
  137. })
  138. //.on('dragenter.jstree', this.settings.dnd.large_drop_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) {
  139. // e.preventDefault();
  140. // $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });
  141. // return false;
  142. // }, this))
  143. .on('drop.jstree', $.proxy(function (e) {
  144. e.preventDefault();
  145. $.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg });
  146. return false;
  147. }, this));
  148. }
  149. };
  150. this.redraw_node = function(obj, deep, callback, force_render) {
  151. obj = parent.redraw_node.apply(this, arguments);
  152. if (obj && this.settings.dnd.use_html5) {
  153. if (this.settings.dnd.large_drag_target) {
  154. obj.setAttribute('draggable', true);
  155. } else {
  156. var i, j, tmp = null;
  157. for(i = 0, j = obj.childNodes.length; i < j; i++) {
  158. if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
  159. tmp = obj.childNodes[i];
  160. break;
  161. }
  162. }
  163. if(tmp) {
  164. tmp.setAttribute('draggable', true);
  165. }
  166. }
  167. }
  168. return obj;
  169. };
  170. };
  171. $(function() {
  172. // bind only once for all instances
  173. var lastmv = false,
  174. laster = false,
  175. lastev = false,
  176. opento = false,
  177. marker = $('<div id="jstree-marker">&#160;</div>').hide(); //.appendTo('body');
  178. $(document)
  179. .on('dragover.vakata.jstree', function (e) {
  180. if (elm) {
  181. $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });
  182. }
  183. })
  184. .on('drop.vakata.jstree', function (e) {
  185. if (elm) {
  186. $.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg });
  187. elm = null;
  188. drg = null;
  189. }
  190. })
  191. .on('dnd_start.vakata.jstree', function (e, data) {
  192. lastmv = false;
  193. lastev = false;
  194. if(!data || !data.data || !data.data.jstree) { return; }
  195. marker.appendTo(document.body); //.show();
  196. })
  197. .on('dnd_move.vakata.jstree', function (e, data) {
  198. var isDifferentNode = data.event.target !== lastev.target;
  199. if(opento) {
  200. if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
  201. clearTimeout(opento);
  202. }
  203. }
  204. if(!data || !data.data || !data.data.jstree) { return; }
  205. // if we are hovering the marker image do nothing (can happen on "inside" drags)
  206. if(data.event.target.id && data.event.target.id === 'jstree-marker') {
  207. return;
  208. }
  209. lastev = data.event;
  210. var ins = $.jstree.reference(data.event.target),
  211. ref = false,
  212. off = false,
  213. rel = false,
  214. tmp, l, t, h, p, i, o, ok, t1, t2, op, ps, pr, ip, tm, is_copy, pn;
  215. // if we are over an instance
  216. if(ins && ins._data && ins._data.dnd) {
  217. marker.attr('class', 'jstree-' + ins.get_theme() + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ));
  218. is_copy = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)));
  219. data.helper
  220. .children().attr('class', 'jstree-' + ins.get_theme() + ' jstree-' + ins.get_theme() + '-' + ins.get_theme_variant() + ' ' + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ))
  221. .find('.jstree-copy').first()[ is_copy ? 'show' : 'hide' ]();
  222. // if are hovering the container itself add a new root node
  223. //console.log(data.event);
  224. if( (data.event.target === ins.element[0] || data.event.target === ins.get_container_ul()[0]) && ins.get_container_ul().children().length === 0) {
  225. ok = true;
  226. for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
  227. 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]), $.jstree.root, 'last', { 'dnd' : true, 'ref' : ins.get_node($.jstree.root), 'pos' : 'i', 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) });
  228. if(!ok) { break; }
  229. }
  230. if(ok) {
  231. lastmv = { 'ins' : ins, 'par' : $.jstree.root, 'pos' : 'last' };
  232. marker.hide();
  233. data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
  234. if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
  235. data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';
  236. }
  237. return;
  238. }
  239. }
  240. else {
  241. // if we are hovering a tree node
  242. ref = ins.settings.dnd.large_drop_target ? $(data.event.target).closest('.jstree-node').children('.jstree-anchor') : $(data.event.target).closest('.jstree-anchor');
  243. if(ref && ref.length && ref.parent().is('.jstree-closed, .jstree-open, .jstree-leaf')) {
  244. off = ref.offset();
  245. rel = (data.event.pageY !== undefined ? data.event.pageY : data.event.originalEvent.pageY) - off.top;
  246. h = ref.outerHeight();
  247. if(rel < h / 3) {
  248. o = ['b', 'i', 'a'];
  249. }
  250. else if(rel > h - h / 3) {
  251. o = ['a', 'i', 'b'];
  252. }
  253. else {
  254. o = rel > h / 2 ? ['i', 'a', 'b'] : ['i', 'b', 'a'];
  255. }
  256. $.each(o, function (j, v) {
  257. switch(v) {
  258. case 'b':
  259. l = off.left - 6;
  260. t = off.top;
  261. p = ins.get_parent(ref);
  262. i = ref.parent().index();
  263. break;
  264. case 'i':
  265. ip = ins.settings.dnd.inside_pos;
  266. tm = ins.get_node(ref.parent());
  267. l = off.left - 2;
  268. t = off.top + h / 2 + 1;
  269. p = tm.id;
  270. i = ip === 'first' ? 0 : (ip === 'last' ? tm.children.length : Math.min(ip, tm.children.length));
  271. break;
  272. case 'a':
  273. l = off.left - 6;
  274. t = off.top + h;
  275. p = ins.get_parent(ref);
  276. i = ref.parent().index() + 1;
  277. break;
  278. }
  279. ok = true;
  280. for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
  281. 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";
  282. ps = i;
  283. if(op === "move_node" && v === 'a' && (data.data.origin && data.data.origin === ins) && p === ins.get_parent(data.data.nodes[t1])) {
  284. pr = ins.get_node(p);
  285. if(ps > $.inArray(data.data.nodes[t1], pr.children)) {
  286. ps -= 1;
  287. }
  288. }
  289. 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, 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) }) );
  290. if(!ok) {
  291. if(ins && ins.last_error) { laster = ins.last_error(); }
  292. break;
  293. }
  294. }
  295. if(v === 'i' && ref.parent().is('.jstree-closed') && ins.settings.dnd.open_timeout) {
  296. if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
  297. if (opento) { clearTimeout(opento); }
  298. opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout);
  299. }
  300. }
  301. if(ok) {
  302. pn = ins.get_node(p, true);
  303. if (!pn.hasClass('.jstree-dnd-parent')) {
  304. $('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
  305. pn.addClass('jstree-dnd-parent');
  306. }
  307. lastmv = { 'ins' : ins, 'par' : p, 'pos' : v === 'i' && ip === 'last' && i === 0 && !ins.is_loaded(tm) ? 'last' : i };
  308. marker.css({ 'left' : l + 'px', 'top' : t + 'px' }).show();
  309. data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
  310. if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
  311. data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';
  312. }
  313. laster = {};
  314. o = true;
  315. return false;
  316. }
  317. });
  318. if(o === true) { return; }
  319. }
  320. }
  321. }
  322. $('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
  323. lastmv = false;
  324. data.helper.find('.jstree-icon').removeClass('jstree-ok').addClass('jstree-er');
  325. if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
  326. //data.event.originalEvent.dataTransfer.dropEffect = 'none';
  327. }
  328. marker.hide();
  329. })
  330. .on('dnd_scroll.vakata.jstree', function (e, data) {
  331. if(!data || !data.data || !data.data.jstree) { return; }
  332. marker.hide();
  333. lastmv = false;
  334. lastev = false;
  335. data.helper.find('.jstree-icon').first().removeClass('jstree-ok').addClass('jstree-er');
  336. })
  337. .on('dnd_stop.vakata.jstree', function (e, data) {
  338. $('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
  339. if(opento) { clearTimeout(opento); }
  340. if(!data || !data.data || !data.data.jstree) { return; }
  341. marker.hide().detach();
  342. var i, j, nodes = [];
  343. if(lastmv) {
  344. for(i = 0, j = data.data.nodes.length; i < j; i++) {
  345. nodes[i] = data.data.origin ? data.data.origin.get_node(data.data.nodes[i]) : data.data.nodes[i];
  346. }
  347. 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, false, false, false, data.data.origin);
  348. }
  349. else {
  350. i = $(data.event.target).closest('.jstree');
  351. if(i.length && laster && laster.error && laster.error === 'check') {
  352. i = i.jstree(true);
  353. if(i) {
  354. i.settings.core.error.call(this, laster);
  355. }
  356. }
  357. }
  358. lastev = false;
  359. lastmv = false;
  360. })
  361. .on('keyup.jstree keydown.jstree', function (e, data) {
  362. data = $.vakata.dnd._get();
  363. if(data && data.data && data.data.jstree) {
  364. if (e.type === "keyup" && e.which === 27) {
  365. if (opento) { clearTimeout(opento); }
  366. lastmv = false;
  367. laster = false;
  368. lastev = false;
  369. opento = false;
  370. marker.hide().detach();
  371. $.vakata.dnd._clean();
  372. } else {
  373. 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' ]();
  374. if(lastev) {
  375. lastev.metaKey = e.metaKey;
  376. lastev.ctrlKey = e.ctrlKey;
  377. $.vakata.dnd._trigger('move', lastev);
  378. }
  379. }
  380. }
  381. });
  382. });
  383. // helpers
  384. (function ($) {
  385. $.vakata.html = {
  386. div : $('<div />'),
  387. escape : function (str) {
  388. return $.vakata.html.div.text(str).html();
  389. },
  390. strip : function (str) {
  391. return $.vakata.html.div.empty().append($.parseHTML(str)).text();
  392. }
  393. };
  394. // private variable
  395. var vakata_dnd = {
  396. element : false,
  397. target : false,
  398. is_down : false,
  399. is_drag : false,
  400. helper : false,
  401. helper_w: 0,
  402. data : false,
  403. init_x : 0,
  404. init_y : 0,
  405. scroll_l: 0,
  406. scroll_t: 0,
  407. scroll_e: false,
  408. scroll_i: false,
  409. is_touch: false
  410. };
  411. $.vakata.dnd = {
  412. settings : {
  413. scroll_speed : 10,
  414. scroll_proximity : 20,
  415. helper_left : 5,
  416. helper_top : 10,
  417. threshold : 5,
  418. threshold_touch : 10
  419. },
  420. _trigger : function (event_name, e, data) {
  421. if (data === undefined) {
  422. data = $.vakata.dnd._get();
  423. }
  424. data.event = e;
  425. $(document).triggerHandler("dnd_" + event_name + ".vakata", data);
  426. },
  427. _get : function () {
  428. return {
  429. "data" : vakata_dnd.data,
  430. "element" : vakata_dnd.element,
  431. "helper" : vakata_dnd.helper
  432. };
  433. },
  434. _clean : function () {
  435. if(vakata_dnd.helper) { vakata_dnd.helper.remove(); }
  436. if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
  437. vakata_dnd = {
  438. element : false,
  439. target : false,
  440. is_down : false,
  441. is_drag : false,
  442. helper : false,
  443. helper_w: 0,
  444. data : false,
  445. init_x : 0,
  446. init_y : 0,
  447. scroll_l: 0,
  448. scroll_t: 0,
  449. scroll_e: false,
  450. scroll_i: false,
  451. is_touch: false
  452. };
  453. $(document).off("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
  454. $(document).off("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
  455. },
  456. _scroll : function (init_only) {
  457. if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) {
  458. if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
  459. return false;
  460. }
  461. if(!vakata_dnd.scroll_i) {
  462. vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100);
  463. return false;
  464. }
  465. if(init_only === true) { return false; }
  466. var i = vakata_dnd.scroll_e.scrollTop(),
  467. j = vakata_dnd.scroll_e.scrollLeft();
  468. vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed);
  469. vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed);
  470. if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) {
  471. /**
  472. * triggered on the document when a drag causes an element to scroll
  473. * @event
  474. * @plugin dnd
  475. * @name dnd_scroll.vakata
  476. * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  477. * @param {DOM} element the DOM element being dragged
  478. * @param {jQuery} helper the helper shown next to the mouse
  479. * @param {jQuery} event the element that is scrolling
  480. */
  481. $.vakata.dnd._trigger("scroll", vakata_dnd.scroll_e);
  482. }
  483. },
  484. start : function (e, data, html) {
  485. if(e.type === "touchstart" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
  486. e.pageX = e.originalEvent.changedTouches[0].pageX;
  487. e.pageY = e.originalEvent.changedTouches[0].pageY;
  488. e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
  489. }
  490. if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); }
  491. try {
  492. e.currentTarget.unselectable = "on";
  493. e.currentTarget.onselectstart = function() { return false; };
  494. if(e.currentTarget.style) {
  495. e.currentTarget.style.touchAction = "none";
  496. e.currentTarget.style.msTouchAction = "none";
  497. e.currentTarget.style.MozUserSelect = "none";
  498. }
  499. } catch(ignore) { }
  500. vakata_dnd.init_x = e.pageX;
  501. vakata_dnd.init_y = e.pageY;
  502. vakata_dnd.data = data;
  503. vakata_dnd.is_down = true;
  504. vakata_dnd.element = e.currentTarget;
  505. vakata_dnd.target = e.target;
  506. vakata_dnd.is_touch = e.type === "touchstart";
  507. if(html !== false) {
  508. vakata_dnd.helper = $("<div id='vakata-dnd'></div>").html(html).css({
  509. "display" : "block",
  510. "margin" : "0",
  511. "padding" : "0",
  512. "position" : "absolute",
  513. "top" : "-2000px",
  514. "lineHeight" : "16px",
  515. "zIndex" : "10000"
  516. });
  517. }
  518. $(document).on("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
  519. $(document).on("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
  520. return false;
  521. },
  522. drag : function (e) {
  523. if(e.type === "touchmove" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
  524. e.pageX = e.originalEvent.changedTouches[0].pageX;
  525. e.pageY = e.originalEvent.changedTouches[0].pageY;
  526. e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
  527. }
  528. if(!vakata_dnd.is_down) { return; }
  529. if(!vakata_dnd.is_drag) {
  530. if(
  531. Math.abs(e.pageX - vakata_dnd.init_x) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) ||
  532. Math.abs(e.pageY - vakata_dnd.init_y) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold)
  533. ) {
  534. if(vakata_dnd.helper) {
  535. vakata_dnd.helper.appendTo(document.body);
  536. vakata_dnd.helper_w = vakata_dnd.helper.outerWidth();
  537. }
  538. vakata_dnd.is_drag = true;
  539. $(vakata_dnd.target).one('click.vakata', false);
  540. /**
  541. * triggered on the document when a drag starts
  542. * @event
  543. * @plugin dnd
  544. * @name dnd_start.vakata
  545. * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  546. * @param {DOM} element the DOM element being dragged
  547. * @param {jQuery} helper the helper shown next to the mouse
  548. * @param {Object} event the event that caused the start (probably mousemove)
  549. */
  550. $.vakata.dnd._trigger("start", e);
  551. }
  552. else { return; }
  553. }
  554. var d = false, w = false,
  555. dh = false, wh = false,
  556. dw = false, ww = false,
  557. dt = false, dl = false,
  558. ht = false, hl = false;
  559. vakata_dnd.scroll_t = 0;
  560. vakata_dnd.scroll_l = 0;
  561. vakata_dnd.scroll_e = false;
  562. $($(e.target).parentsUntil("body").addBack().get().reverse())
  563. .filter(function () {
  564. return (/^auto|scroll$/).test($(this).css("overflow")) &&
  565. (this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth);
  566. })
  567. .each(function () {
  568. var t = $(this), o = t.offset();
  569. if(this.scrollHeight > this.offsetHeight) {
  570. if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; }
  571. if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; }
  572. }
  573. if(this.scrollWidth > this.offsetWidth) {
  574. if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; }
  575. if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; }
  576. }
  577. if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
  578. vakata_dnd.scroll_e = $(this);
  579. return false;
  580. }
  581. });
  582. if(!vakata_dnd.scroll_e) {
  583. d = $(document); w = $(window);
  584. dh = d.height(); wh = w.height();
  585. dw = d.width(); ww = w.width();
  586. dt = d.scrollTop(); dl = d.scrollLeft();
  587. if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; }
  588. if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; }
  589. if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; }
  590. if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; }
  591. if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
  592. vakata_dnd.scroll_e = d;
  593. }
  594. }
  595. if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); }
  596. if(vakata_dnd.helper) {
  597. ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10);
  598. hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10);
  599. if(dh && ht + 25 > dh) { ht = dh - 50; }
  600. if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); }
  601. vakata_dnd.helper.css({
  602. left : hl + "px",
  603. top : ht + "px"
  604. });
  605. }
  606. /**
  607. * triggered on the document when a drag is in progress
  608. * @event
  609. * @plugin dnd
  610. * @name dnd_move.vakata
  611. * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  612. * @param {DOM} element the DOM element being dragged
  613. * @param {jQuery} helper the helper shown next to the mouse
  614. * @param {Object} event the event that caused this to trigger (most likely mousemove)
  615. */
  616. $.vakata.dnd._trigger("move", e);
  617. return false;
  618. },
  619. stop : function (e) {
  620. if(e.type === "touchend" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
  621. e.pageX = e.originalEvent.changedTouches[0].pageX;
  622. e.pageY = e.originalEvent.changedTouches[0].pageY;
  623. e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
  624. }
  625. if(vakata_dnd.is_drag) {
  626. /**
  627. * triggered on the document when a drag stops (the dragged element is dropped)
  628. * @event
  629. * @plugin dnd
  630. * @name dnd_stop.vakata
  631. * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  632. * @param {DOM} element the DOM element being dragged
  633. * @param {jQuery} helper the helper shown next to the mouse
  634. * @param {Object} event the event that caused the stop
  635. */
  636. if (e.target !== vakata_dnd.target) {
  637. $(vakata_dnd.target).off('click.vakata');
  638. }
  639. $.vakata.dnd._trigger("stop", e);
  640. }
  641. else {
  642. if(e.type === "touchend" && e.target === vakata_dnd.target) {
  643. var to = setTimeout(function () { $(e.target).click(); }, 100);
  644. $(e.target).one('click', function() { if(to) { clearTimeout(to); } });
  645. }
  646. }
  647. $.vakata.dnd._clean();
  648. return false;
  649. }
  650. };
  651. }($));
  652. // include the dnd plugin by default
  653. // $.jstree.defaults.plugins.push("dnd");
  654. }));