jquery.simple.tree.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. // TODO: check if it is really need, and update to the latest version at least (also it is used in Ads only)
  2. /*
  3. * jQuery SimpleTree Drag&Drop plugin
  4. * Update on 22th May 2008
  5. * Version 0.3
  6. *
  7. * Licensed under BSD <http://en.wikipedia.org/wiki/BSD_License>
  8. * Copyright (c) 2008, Peter Panov <panov@elcat.kg>, IKEEN Group http://www.ikeen.com
  9. * All rights reserved.
  10. *
  11. * Redistribution and use in source and binary forms, with or without
  12. * modification, are permitted provided that the following conditions are met:
  13. * * Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * * Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. * * Neither the name of the Peter Panov, IKEEN Group nor the
  19. * names of its contributors may be used to endorse or promote products
  20. * derived from this software without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY Peter Panov, IKEEN Group ``AS IS'' AND ANY
  23. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  24. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  25. * DISCLAIMED. IN NO EVENT SHALL Peter Panov, IKEEN Group BE LIABLE FOR ANY
  26. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  27. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  28. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  29. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. */
  33. $.fn.simpleTree = function(opt){
  34. return this.each(function(){
  35. var TREE = this;
  36. var ROOT = $('.root',this);
  37. var mousePressed = false;
  38. var mouseMoved = false;
  39. var dragMoveType = false;
  40. var dragNode_destination = false;
  41. var dragNode_source = false;
  42. var dragDropTimer = false;
  43. var ajaxCache = Array();
  44. TREE.option = {
  45. drag: true,
  46. animate: false,
  47. autoclose: false,
  48. speed: 'fast',
  49. afterAjax: false,
  50. afterMove: false,
  51. afterClick: false,
  52. afterDblClick: false,
  53. // added by Erik Dohmen (2BinBusiness.nl) to make context menu cliks available
  54. afterContextMenu: false,
  55. docToFolderConvert:false
  56. };
  57. TREE.option = $.extend(TREE.option,opt);
  58. $.extend(this, {getSelected: function(){
  59. return $('span.active', this).parent();
  60. }});
  61. TREE.closeNearby = function(obj)
  62. {
  63. $(obj).siblings().filter('.folder-open, .folder-open-last').each(function(){
  64. var childUl = $('>ul',this);
  65. var className = this.className;
  66. this.className = className.replace('open','close');
  67. if(TREE.option.animate)
  68. {
  69. childUl.animate({height:"toggle"},TREE.option.speed);
  70. }else{
  71. childUl.hide();
  72. }
  73. });
  74. };
  75. TREE.nodeToggle = function(obj)
  76. {
  77. var childUl = $('>ul',obj);
  78. if(childUl.is(':visible')){
  79. obj.className = obj.className.replace('open','close');
  80. if(TREE.option.animate)
  81. {
  82. childUl.animate({height:"toggle"},TREE.option.speed);
  83. }else{
  84. childUl.hide();
  85. }
  86. }else{
  87. obj.className = obj.className.replace('close','open');
  88. if(TREE.option.animate)
  89. {
  90. childUl.animate({height:"toggle"},TREE.option.speed, function(){
  91. if(TREE.option.autoclose)TREE.closeNearby(obj);
  92. if(childUl.is('.ajax'))TREE.setAjaxNodes(childUl, obj.id);
  93. });
  94. }else{
  95. childUl.show();
  96. if(TREE.option.autoclose)TREE.closeNearby(obj);
  97. if(childUl.is('.ajax'))TREE.setAjaxNodes(childUl, obj.id);
  98. }
  99. }
  100. };
  101. TREE.setAjaxNodes = function(node, parentId, callback)
  102. {
  103. if($.inArray(parentId,ajaxCache) == -1){
  104. ajaxCache[ajaxCache.length]=parentId;
  105. var url = $.trim($('>li', node).text());
  106. if(url && url.indexOf('url:'))
  107. {
  108. url=$.trim(url.replace(/.*\{url:(.*)\}/i ,'$1'));
  109. $.ajax({
  110. type: "GET",
  111. url: url,
  112. contentType:'html',
  113. cache:false,
  114. success: function(responce){
  115. node.removeAttr('class');
  116. node.html(responce);
  117. $.extend(node,{url:url});
  118. TREE.setTreeNodes(node, true);
  119. if(typeof TREE.option.afterAjax == 'function')
  120. {
  121. TREE.option.afterAjax(node);
  122. }
  123. if(typeof callback == 'function')
  124. {
  125. callback(node);
  126. }
  127. }
  128. });
  129. }
  130. }
  131. };
  132. TREE.setTreeNodes = function(obj, useParent){
  133. obj = useParent? obj.parent():obj;
  134. $('li>span', obj).addClass('text')
  135. .bind('selectstart', function() {
  136. return false;
  137. }).click(function(){
  138. $('.active',TREE).attr('class','text');
  139. if(this.className=='text')
  140. {
  141. this.className='active';
  142. }
  143. if(typeof TREE.option.afterClick == 'function')
  144. {
  145. TREE.option.afterClick($(this).parent());
  146. }
  147. return false;
  148. }).dblclick(function(){
  149. mousePressed = false;
  150. TREE.nodeToggle($(this).parent().get(0));
  151. if(typeof TREE.option.afterDblClick == 'function')
  152. {
  153. TREE.option.afterDblClick($(this).parent());
  154. }
  155. return false;
  156. // added by Erik Dohmen (2BinBusiness.nl) to make context menu actions
  157. // available
  158. }).bind("contextmenu",function(){
  159. $('.active',TREE).attr('class','text');
  160. if(this.className=='text')
  161. {
  162. this.className='active';
  163. }
  164. if(typeof TREE.option.afterContextMenu == 'function')
  165. {
  166. TREE.option.afterContextMenu($(this).parent());
  167. }
  168. return false;
  169. }).mousedown(function(event){
  170. mousePressed = true;
  171. cloneNode = $(this).parent().clone();
  172. var LI = $(this).parent();
  173. if(TREE.option.drag)
  174. {
  175. $('>ul', cloneNode).hide();
  176. $('body').append('<div id="drag_container"><ul></ul></div>');
  177. $('#drag_container').hide().css({opacity:'0.8'});
  178. $('#drag_container >ul').append(cloneNode);
  179. $("<img>").attr({id : "tree_plus",src : "images/plus.gif"}).css({width: "7px",display: "block",position: "absolute",left : "5px",top: "5px", display:'none'}).appendTo("body");
  180. $(document).bind("mousemove", {LI:LI}, TREE.dragStart).bind("mouseup",TREE.dragEnd);
  181. }
  182. return false;
  183. }).mouseup(function(){
  184. if(mousePressed && mouseMoved && dragNode_source)
  185. {
  186. TREE.moveNodeToFolder($(this).parent());
  187. }
  188. TREE.eventDestroy();
  189. });
  190. $('li', obj).each(function(i){
  191. var className = this.className;
  192. var open = false;
  193. var cloneNode=false;
  194. var LI = this;
  195. var childNode = $('>ul',this);
  196. if(childNode.size()>0){
  197. var setClassName = 'folder-';
  198. if(className && className.indexOf('open')>=0){
  199. setClassName=setClassName+'open';
  200. open=true;
  201. }else{
  202. setClassName=setClassName+'close';
  203. }
  204. this.className = setClassName + ($(this).is(':last-child')? '-last':'');
  205. if(!open || className.indexOf('ajax')>=0)childNode.hide();
  206. TREE.setTrigger(this);
  207. }else{
  208. var setClassName = 'doc';
  209. this.className = setClassName + ($(this).is(':last-child')? '-last':'');
  210. }
  211. }).before('<li class="line">&nbsp;</li>')
  212. .filter(':last-child').after('<li class="line-last"></li>');
  213. TREE.setEventLine($('.line, .line-last', obj));
  214. };
  215. TREE.setTrigger = function(node){
  216. $('>span',node).before('<img class="trigger" src="images/spacer.gif" border=0>');
  217. var trigger = $('>.trigger', node);
  218. trigger.click(function(event){
  219. TREE.nodeToggle(node);
  220. });
  221. if(!$.browser.msie)
  222. {
  223. trigger.css('float','left');
  224. }
  225. };
  226. TREE.dragStart = function(event){
  227. var LI = $(event.data.LI);
  228. if(mousePressed)
  229. {
  230. mouseMoved = true;
  231. if(dragDropTimer) clearTimeout(dragDropTimer);
  232. if($('#drag_container:not(:visible)')){
  233. $('#drag_container').show();
  234. LI.prev('.line').hide();
  235. dragNode_source = LI;
  236. }
  237. $('#drag_container').css({position:'absolute', "left" : (event.pageX + 5), "top": (event.pageY + 15) });
  238. if(LI.is(':visible'))LI.hide();
  239. var temp_move = false;
  240. if(event.target.tagName.toLowerCase()=='span' && $.inArray(event.target.className, Array('text','active','trigger'))!= -1)
  241. {
  242. var parent = event.target.parentNode;
  243. var offs = $(parent).offset({scroll:false});
  244. var screenScroll = {x : (offs.left - 3),y : event.pageY - offs.top};
  245. var isrc = $("#tree_plus").attr('src');
  246. var ajaxChildSize = $('>ul.ajax',parent).size();
  247. var ajaxChild = $('>ul.ajax',parent);
  248. screenScroll.x += 19;
  249. screenScroll.y = event.pageY - screenScroll.y + 5;
  250. if(parent.className.indexOf('folder-close')>=0 && ajaxChildSize==0)
  251. {
  252. if(isrc.indexOf('minus')!=-1)$("#tree_plus").attr('src','images/plus.gif');
  253. $("#tree_plus").css({"left": screenScroll.x, "top": screenScroll.y}).show();
  254. dragDropTimer = setTimeout(function(){
  255. parent.className = parent.className.replace('close','open');
  256. $('>ul',parent).show();
  257. }, 700);
  258. }else if(parent.className.indexOf('folder')>=0 && ajaxChildSize==0){
  259. if(isrc.indexOf('minus')!=-1)$("#tree_plus").attr('src','images/plus.gif');
  260. $("#tree_plus").css({"left": screenScroll.x, "top": screenScroll.y}).show();
  261. }else if(parent.className.indexOf('folder-close')>=0 && ajaxChildSize>0)
  262. {
  263. mouseMoved = false;
  264. $("#tree_plus").attr('src','images/minus.gif');
  265. $("#tree_plus").css({"left": screenScroll.x, "top": screenScroll.y}).show();
  266. $('>ul',parent).show();
  267. /*
  268. Thanks for the idea of Erik Dohmen
  269. */
  270. TREE.setAjaxNodes(ajaxChild,parent.id, function(){
  271. parent.className = parent.className.replace('close','open');
  272. mouseMoved = true;
  273. $("#tree_plus").attr('src','images/plus.gif');
  274. $("#tree_plus").css({"left": screenScroll.x, "top": screenScroll.y}).show();
  275. });
  276. }else{
  277. if(TREE.option.docToFolderConvert)
  278. {
  279. $("#tree_plus").css({"left": screenScroll.x, "top": screenScroll.y}).show();
  280. }else{
  281. $("#tree_plus").hide();
  282. }
  283. }
  284. }else{
  285. $("#tree_plus").hide();
  286. }
  287. return false;
  288. }
  289. return true;
  290. };
  291. TREE.dragEnd = function(){
  292. if(dragDropTimer) clearTimeout(dragDropTimer);
  293. TREE.eventDestroy();
  294. };
  295. TREE.setEventLine = function(obj){
  296. obj.mouseover(function(){
  297. if(this.className.indexOf('over')<0 && mousePressed && mouseMoved)
  298. {
  299. this.className = this.className.replace('line','line-over');
  300. }
  301. }).mouseout(function(){
  302. if(this.className.indexOf('over')>=0)
  303. {
  304. this.className = this.className.replace('-over','');
  305. }
  306. }).mouseup(function(){
  307. if(mousePressed && dragNode_source && mouseMoved)
  308. {
  309. dragNode_destination = $(this).parents('li:first');
  310. TREE.moveNodeToLine(this);
  311. TREE.eventDestroy();
  312. }
  313. });
  314. };
  315. TREE.checkNodeIsLast = function(node)
  316. {
  317. if(node.className.indexOf('last')>=0)
  318. {
  319. var prev_source = dragNode_source.prev().prev();
  320. if(prev_source.size()>0)
  321. {
  322. prev_source[0].className+='-last';
  323. }
  324. node.className = node.className.replace('-last','');
  325. }
  326. };
  327. TREE.checkLineIsLast = function(line)
  328. {
  329. if(line.className.indexOf('last')>=0)
  330. {
  331. var prev = $(line).prev();
  332. if(prev.size()>0)
  333. {
  334. prev[0].className = prev[0].className.replace('-last','');
  335. }
  336. dragNode_source[0].className+='-last';
  337. }
  338. };
  339. TREE.eventDestroy = function()
  340. {
  341. // added by Erik Dohmen (2BinBusiness.nl), the unbind mousemove TREE.dragStart action
  342. // like this other mousemove actions binded through other actions ain't removed (use it myself
  343. // to determine location for context menu)
  344. $(document).unbind('mousemove',TREE.dragStart).unbind('mouseup').unbind('mousedown');
  345. $('#drag_container, #tree_plus').remove();
  346. if(dragNode_source)
  347. {
  348. $(dragNode_source).show().prev('.line').show();
  349. }
  350. dragNode_destination = dragNode_source = mousePressed = mouseMoved = false;
  351. //ajaxCache = Array();
  352. };
  353. TREE.convertToFolder = function(node){
  354. node[0].className = node[0].className.replace('doc','folder-open');
  355. node.append('<ul><li class="line-last"></li></ul>');
  356. TREE.setTrigger(node[0]);
  357. TREE.setEventLine($('.line, .line-last', node));
  358. };
  359. TREE.convertToDoc = function(node){
  360. $('>ul', node).remove();
  361. $('img', node).remove();
  362. node[0].className = node[0].className.replace(/folder-(open|close)/gi , 'doc');
  363. };
  364. TREE.moveNodeToFolder = function(node)
  365. {
  366. if(!TREE.option.docToFolderConvert && node[0].className.indexOf('doc')!=-1)
  367. {
  368. return true;
  369. }else if(TREE.option.docToFolderConvert && node[0].className.indexOf('doc')!=-1){
  370. TREE.convertToFolder(node);
  371. }
  372. TREE.checkNodeIsLast(dragNode_source[0]);
  373. var lastLine = $('>ul >.line-last', node);
  374. if(lastLine.size()>0)
  375. {
  376. TREE.moveNodeToLine(lastLine[0]);
  377. }
  378. };
  379. TREE.moveNodeToLine = function(node){
  380. TREE.checkNodeIsLast(dragNode_source[0]);
  381. TREE.checkLineIsLast(node);
  382. var parent = $(dragNode_source).parents('li:first');
  383. var line = $(dragNode_source).prev('.line');
  384. $(node).before(dragNode_source);
  385. $(dragNode_source).before(line);
  386. node.className = node.className.replace('-over','');
  387. var nodeSize = $('>ul >li', parent).not('.line, .line-last').filter(':visible').size();
  388. if(TREE.option.docToFolderConvert && nodeSize==0)
  389. {
  390. TREE.convertToDoc(parent);
  391. }else if(nodeSize==0)
  392. {
  393. parent[0].className=parent[0].className.replace('open','close');
  394. $('>ul',parent).hide();
  395. }
  396. // added by Erik Dohmen (2BinBusiness.nl) select node
  397. if($('span:first',dragNode_source).attr('class')=='text')
  398. {
  399. $('.active',TREE).attr('class','text');
  400. $('span:first',dragNode_source).attr('class','active');
  401. }
  402. if(typeof(TREE.option.afterMove) == 'function')
  403. {
  404. var pos = $(dragNode_source).prevAll(':not(.line)').size();
  405. TREE.option.afterMove($(node).parents('li:first'), $(dragNode_source), pos);
  406. }
  407. };
  408. TREE.addNode = function(id, text, callback)
  409. {
  410. var temp_node = $('<li><ul><li id="'+id+'"><span>'+text+'</span></li></ul></li>');
  411. TREE.setTreeNodes(temp_node);
  412. dragNode_destination = TREE.getSelected();
  413. dragNode_source = $('.doc-last',temp_node);
  414. TREE.moveNodeToFolder(dragNode_destination);
  415. temp_node.remove();
  416. if(typeof(callback) == 'function')
  417. {
  418. callback(dragNode_destination, dragNode_source);
  419. }
  420. };
  421. TREE.delNode = function(callback)
  422. {
  423. dragNode_source = TREE.getSelected();
  424. TREE.checkNodeIsLast(dragNode_source[0]);
  425. dragNode_source.prev().remove();
  426. dragNode_source.remove();
  427. if(typeof(callback) == 'function')
  428. {
  429. callback(dragNode_destination);
  430. }
  431. };
  432. TREE.init = function(obj)
  433. {
  434. TREE.setTreeNodes(obj, false);
  435. };
  436. TREE.init(ROOT);
  437. });
  438. }