jstree.dnd.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  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('dnd_start.vakata.jstree', function (e, data) {
  180. lastmv = false;
  181. lastev = false;
  182. if(!data || !data.data || !data.data.jstree) { return; }
  183. marker.appendTo('body'); //.show();
  184. })
  185. .on('dnd_move.vakata.jstree', function (e, data) {
  186. var isDifferentNode = data.event.target !== lastev.target;
  187. if(opento) {
  188. if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
  189. clearTimeout(opento);
  190. }
  191. }
  192. if(!data || !data.data || !data.data.jstree) { return; }
  193. // if we are hovering the marker image do nothing (can happen on "inside" drags)
  194. if(data.event.target.id && data.event.target.id === 'jstree-marker') {
  195. return;
  196. }
  197. lastev = data.event;
  198. var ins = $.jstree.reference(data.event.target),
  199. ref = false,
  200. off = false,
  201. rel = false,
  202. tmp, l, t, h, p, i, o, ok, t1, t2, op, ps, pr, ip, tm, is_copy, pn;
  203. // if we are over an instance
  204. if(ins && ins._data && ins._data.dnd) {
  205. marker.attr('class', 'jstree-' + ins.get_theme() + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ));
  206. is_copy = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)));
  207. data.helper
  208. .children().attr('class', 'jstree-' + ins.get_theme() + ' jstree-' + ins.get_theme() + '-' + ins.get_theme_variant() + ' ' + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ))
  209. .find('.jstree-copy').first()[ is_copy ? 'show' : 'hide' ]();
  210. // if are hovering the container itself add a new root node
  211. //console.log(data.event);
  212. if( (data.event.target === ins.element[0] || data.event.target === ins.get_container_ul()[0]) && ins.get_container_ul().children().length === 0) {
  213. ok = true;
  214. for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
  215. 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) });
  216. if(!ok) { break; }
  217. }
  218. if(ok) {
  219. lastmv = { 'ins' : ins, 'par' : $.jstree.root, 'pos' : 'last' };
  220. marker.hide();
  221. data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
  222. if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
  223. data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';
  224. }
  225. return;
  226. }
  227. }
  228. else {
  229. // if we are hovering a tree node
  230. ref = ins.settings.dnd.large_drop_target ? $(data.event.target).closest('.jstree-node').children('.jstree-anchor') : $(data.event.target).closest('.jstree-anchor');
  231. if(ref && ref.length && ref.parent().is('.jstree-closed, .jstree-open, .jstree-leaf')) {
  232. off = ref.offset();
  233. rel = (data.event.pageY !== undefined ? data.event.pageY : data.event.originalEvent.pageY) - off.top;
  234. h = ref.outerHeight();
  235. if(rel < h / 3) {
  236. o = ['b', 'i', 'a'];
  237. }
  238. else if(rel > h - h / 3) {
  239. o = ['a', 'i', 'b'];
  240. }
  241. else {
  242. o = rel > h / 2 ? ['i', 'a', 'b'] : ['i', 'b', 'a'];
  243. }
  244. $.each(o, function (j, v) {
  245. switch(v) {
  246. case 'b':
  247. l = off.left - 6;
  248. t = off.top;
  249. p = ins.get_parent(ref);
  250. i = ref.parent().index();
  251. break;
  252. case 'i':
  253. ip = ins.settings.dnd.inside_pos;
  254. tm = ins.get_node(ref.parent());
  255. l = off.left - 2;
  256. t = off.top + h / 2 + 1;
  257. p = tm.id;
  258. i = ip === 'first' ? 0 : (ip === 'last' ? tm.children.length : Math.min(ip, tm.children.length));
  259. break;
  260. case 'a':
  261. l = off.left - 6;
  262. t = off.top + h;
  263. p = ins.get_parent(ref);
  264. i = ref.parent().index() + 1;
  265. break;
  266. }
  267. ok = true;
  268. for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
  269. 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";
  270. ps = i;
  271. if(op === "move_node" && v === 'a' && (data.data.origin && data.data.origin === ins) && p === ins.get_parent(data.data.nodes[t1])) {
  272. pr = ins.get_node(p);
  273. if(ps > $.inArray(data.data.nodes[t1], pr.children)) {
  274. ps -= 1;
  275. }
  276. }
  277. 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) }) );
  278. if(!ok) {
  279. if(ins && ins.last_error) { laster = ins.last_error(); }
  280. break;
  281. }
  282. }
  283. if(v === 'i' && ref.parent().is('.jstree-closed') && ins.settings.dnd.open_timeout) {
  284. if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
  285. if (opento) { clearTimeout(opento); }
  286. opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout);
  287. }
  288. }
  289. if(ok) {
  290. pn = ins.get_node(p, true);
  291. if (!pn.hasClass('.jstree-dnd-parent')) {
  292. $('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
  293. pn.addClass('jstree-dnd-parent');
  294. }
  295. lastmv = { 'ins' : ins, 'par' : p, 'pos' : v === 'i' && ip === 'last' && i === 0 && !ins.is_loaded(tm) ? 'last' : i };
  296. marker.css({ 'left' : l + 'px', 'top' : t + 'px' }).show();
  297. data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
  298. if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
  299. data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';
  300. }
  301. laster = {};
  302. o = true;
  303. return false;
  304. }
  305. });
  306. if(o === true) { return; }
  307. }
  308. }
  309. }
  310. $('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
  311. lastmv = false;
  312. data.helper.find('.jstree-icon').removeClass('jstree-ok').addClass('jstree-er');
  313. if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
  314. data.event.originalEvent.dataTransfer.dropEffect = 'none';
  315. }
  316. marker.hide();
  317. })
  318. .on('dnd_scroll.vakata.jstree', function (e, data) {
  319. if(!data || !data.data || !data.data.jstree) { return; }
  320. marker.hide();
  321. lastmv = false;
  322. lastev = false;
  323. data.helper.find('.jstree-icon').first().removeClass('jstree-ok').addClass('jstree-er');
  324. })
  325. .on('dnd_stop.vakata.jstree', function (e, data) {
  326. $('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
  327. if(opento) { clearTimeout(opento); }
  328. if(!data || !data.data || !data.data.jstree) { return; }
  329. marker.hide().detach();
  330. var i, j, nodes = [];
  331. if(lastmv) {
  332. for(i = 0, j = data.data.nodes.length; i < j; i++) {
  333. nodes[i] = data.data.origin ? data.data.origin.get_node(data.data.nodes[i]) : data.data.nodes[i];
  334. }
  335. 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);
  336. }
  337. else {
  338. i = $(data.event.target).closest('.jstree');
  339. if(i.length && laster && laster.error && laster.error === 'check') {
  340. i = i.jstree(true);
  341. if(i) {
  342. i.settings.core.error.call(this, laster);
  343. }
  344. }
  345. }
  346. lastev = false;
  347. lastmv = false;
  348. })
  349. .on('keyup.jstree keydown.jstree', function (e, data) {
  350. data = $.vakata.dnd._get();
  351. if(data && data.data && data.data.jstree) {
  352. if (e.type === "keyup" && e.which === 27) {
  353. if (opento) { clearTimeout(opento); }
  354. lastmv = false;
  355. laster = false;
  356. lastev = false;
  357. opento = false;
  358. marker.hide().detach();
  359. $.vakata.dnd._clean();
  360. } else {
  361. 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' ]();
  362. if(lastev) {
  363. lastev.metaKey = e.metaKey;
  364. lastev.ctrlKey = e.ctrlKey;
  365. $.vakata.dnd._trigger('move', lastev);
  366. }
  367. }
  368. }
  369. });
  370. });
  371. // helpers
  372. (function ($) {
  373. $.vakata.html = {
  374. div : $('<div />'),
  375. escape : function (str) {
  376. return $.vakata.html.div.text(str).html();
  377. },
  378. strip : function (str) {
  379. return $.vakata.html.div.empty().append($.parseHTML(str)).text();
  380. }
  381. };
  382. // private variable
  383. var vakata_dnd = {
  384. element : false,
  385. target : false,
  386. is_down : false,
  387. is_drag : false,
  388. helper : false,
  389. helper_w: 0,
  390. data : false,
  391. init_x : 0,
  392. init_y : 0,
  393. scroll_l: 0,
  394. scroll_t: 0,
  395. scroll_e: false,
  396. scroll_i: false,
  397. is_touch: false
  398. };
  399. $.vakata.dnd = {
  400. settings : {
  401. scroll_speed : 10,
  402. scroll_proximity : 20,
  403. helper_left : 5,
  404. helper_top : 10,
  405. threshold : 5,
  406. threshold_touch : 10
  407. },
  408. _trigger : function (event_name, e, data) {
  409. if (data === undefined) {
  410. data = $.vakata.dnd._get();
  411. }
  412. data.event = e;
  413. $(document).triggerHandler("dnd_" + event_name + ".vakata", data);
  414. },
  415. _get : function () {
  416. return {
  417. "data" : vakata_dnd.data,
  418. "element" : vakata_dnd.element,
  419. "helper" : vakata_dnd.helper
  420. };
  421. },
  422. _clean : function () {
  423. if(vakata_dnd.helper) { vakata_dnd.helper.remove(); }
  424. if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
  425. vakata_dnd = {
  426. element : false,
  427. target : false,
  428. is_down : false,
  429. is_drag : false,
  430. helper : false,
  431. helper_w: 0,
  432. data : false,
  433. init_x : 0,
  434. init_y : 0,
  435. scroll_l: 0,
  436. scroll_t: 0,
  437. scroll_e: false,
  438. scroll_i: false,
  439. is_touch: false
  440. };
  441. $(document).off("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
  442. $(document).off("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
  443. },
  444. _scroll : function (init_only) {
  445. if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) {
  446. if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
  447. return false;
  448. }
  449. if(!vakata_dnd.scroll_i) {
  450. vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100);
  451. return false;
  452. }
  453. if(init_only === true) { return false; }
  454. var i = vakata_dnd.scroll_e.scrollTop(),
  455. j = vakata_dnd.scroll_e.scrollLeft();
  456. vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed);
  457. vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed);
  458. if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) {
  459. /**
  460. * triggered on the document when a drag causes an element to scroll
  461. * @event
  462. * @plugin dnd
  463. * @name dnd_scroll.vakata
  464. * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  465. * @param {DOM} element the DOM element being dragged
  466. * @param {jQuery} helper the helper shown next to the mouse
  467. * @param {jQuery} event the element that is scrolling
  468. */
  469. $.vakata.dnd._trigger("scroll", vakata_dnd.scroll_e);
  470. }
  471. },
  472. start : function (e, data, html) {
  473. if(e.type === "touchstart" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
  474. e.pageX = e.originalEvent.changedTouches[0].pageX;
  475. e.pageY = e.originalEvent.changedTouches[0].pageY;
  476. e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
  477. }
  478. if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); }
  479. try {
  480. e.currentTarget.unselectable = "on";
  481. e.currentTarget.onselectstart = function() { return false; };
  482. if(e.currentTarget.style) {
  483. e.currentTarget.style.touchAction = "none";
  484. e.currentTarget.style.msTouchAction = "none";
  485. e.currentTarget.style.MozUserSelect = "none";
  486. }
  487. } catch(ignore) { }
  488. vakata_dnd.init_x = e.pageX;
  489. vakata_dnd.init_y = e.pageY;
  490. vakata_dnd.data = data;
  491. vakata_dnd.is_down = true;
  492. vakata_dnd.element = e.currentTarget;
  493. vakata_dnd.target = e.target;
  494. vakata_dnd.is_touch = e.type === "touchstart";
  495. if(html !== false) {
  496. vakata_dnd.helper = $("<div id='vakata-dnd'></div>").html(html).css({
  497. "display" : "block",
  498. "margin" : "0",
  499. "padding" : "0",
  500. "position" : "absolute",
  501. "top" : "-2000px",
  502. "lineHeight" : "16px",
  503. "zIndex" : "10000"
  504. });
  505. }
  506. $(document).on("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
  507. $(document).on("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
  508. return false;
  509. },
  510. drag : function (e) {
  511. if(e.type === "touchmove" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
  512. e.pageX = e.originalEvent.changedTouches[0].pageX;
  513. e.pageY = e.originalEvent.changedTouches[0].pageY;
  514. e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
  515. }
  516. if(!vakata_dnd.is_down) { return; }
  517. if(!vakata_dnd.is_drag) {
  518. if(
  519. Math.abs(e.pageX - vakata_dnd.init_x) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) ||
  520. Math.abs(e.pageY - vakata_dnd.init_y) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold)
  521. ) {
  522. if(vakata_dnd.helper) {
  523. vakata_dnd.helper.appendTo("body");
  524. vakata_dnd.helper_w = vakata_dnd.helper.outerWidth();
  525. }
  526. vakata_dnd.is_drag = true;
  527. $(vakata_dnd.target).one('click.vakata', false);
  528. /**
  529. * triggered on the document when a drag starts
  530. * @event
  531. * @plugin dnd
  532. * @name dnd_start.vakata
  533. * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  534. * @param {DOM} element the DOM element being dragged
  535. * @param {jQuery} helper the helper shown next to the mouse
  536. * @param {Object} event the event that caused the start (probably mousemove)
  537. */
  538. $.vakata.dnd._trigger("start", e);
  539. }
  540. else { return; }
  541. }
  542. var d = false, w = false,
  543. dh = false, wh = false,
  544. dw = false, ww = false,
  545. dt = false, dl = false,
  546. ht = false, hl = false;
  547. vakata_dnd.scroll_t = 0;
  548. vakata_dnd.scroll_l = 0;
  549. vakata_dnd.scroll_e = false;
  550. $($(e.target).parentsUntil("body").addBack().get().reverse())
  551. .filter(function () {
  552. return (/^auto|scroll$/).test($(this).css("overflow")) &&
  553. (this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth);
  554. })
  555. .each(function () {
  556. var t = $(this), o = t.offset();
  557. if(this.scrollHeight > this.offsetHeight) {
  558. if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; }
  559. if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; }
  560. }
  561. if(this.scrollWidth > this.offsetWidth) {
  562. if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; }
  563. if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; }
  564. }
  565. if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
  566. vakata_dnd.scroll_e = $(this);
  567. return false;
  568. }
  569. });
  570. if(!vakata_dnd.scroll_e) {
  571. d = $(document); w = $(window);
  572. dh = d.height(); wh = w.height();
  573. dw = d.width(); ww = w.width();
  574. dt = d.scrollTop(); dl = d.scrollLeft();
  575. if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; }
  576. if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; }
  577. if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; }
  578. if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; }
  579. if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
  580. vakata_dnd.scroll_e = d;
  581. }
  582. }
  583. if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); }
  584. if(vakata_dnd.helper) {
  585. ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10);
  586. hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10);
  587. if(dh && ht + 25 > dh) { ht = dh - 50; }
  588. if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); }
  589. vakata_dnd.helper.css({
  590. left : hl + "px",
  591. top : ht + "px"
  592. });
  593. }
  594. /**
  595. * triggered on the document when a drag is in progress
  596. * @event
  597. * @plugin dnd
  598. * @name dnd_move.vakata
  599. * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  600. * @param {DOM} element the DOM element being dragged
  601. * @param {jQuery} helper the helper shown next to the mouse
  602. * @param {Object} event the event that caused this to trigger (most likely mousemove)
  603. */
  604. $.vakata.dnd._trigger("move", e);
  605. return false;
  606. },
  607. stop : function (e) {
  608. if(e.type === "touchend" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
  609. e.pageX = e.originalEvent.changedTouches[0].pageX;
  610. e.pageY = e.originalEvent.changedTouches[0].pageY;
  611. e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
  612. }
  613. if(vakata_dnd.is_drag) {
  614. /**
  615. * triggered on the document when a drag stops (the dragged element is dropped)
  616. * @event
  617. * @plugin dnd
  618. * @name dnd_stop.vakata
  619. * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
  620. * @param {DOM} element the DOM element being dragged
  621. * @param {jQuery} helper the helper shown next to the mouse
  622. * @param {Object} event the event that caused the stop
  623. */
  624. if (e.target !== vakata_dnd.target) {
  625. $(vakata_dnd.target).off('click.vakata');
  626. }
  627. $.vakata.dnd._trigger("stop", e);
  628. }
  629. else {
  630. if(e.type === "touchend" && e.target === vakata_dnd.target) {
  631. var to = setTimeout(function () { $(e.target).click(); }, 100);
  632. $(e.target).one('click', function() { if(to) { clearTimeout(to); } });
  633. }
  634. }
  635. $.vakata.dnd._clean();
  636. return false;
  637. }
  638. };
  639. }($));
  640. // include the dnd plugin by default
  641. // $.jstree.defaults.plugins.push("dnd");
  642. }));