tree.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /**
  2. * @files js for collapsible tree view with some helper functions for updating tree structure
  3. */
  4. (function ($) {
  5. Drupal.behaviors.TaxonomyManagerTree = {
  6. attach: function(context, settings) {
  7. var treeSettings = settings.taxonomytree || [];
  8. if (treeSettings instanceof Array) {
  9. for (var i=0; i<treeSettings.length; i++) {
  10. if (!$('#'+ treeSettings[i].id +'.tm-processed').length) {
  11. new Drupal.TaxonomyManagerTree(treeSettings[i].id, treeSettings[i].vid, treeSettings[i].parents);
  12. }
  13. }
  14. }
  15. //only add throbber for TM sites
  16. var throbberSettings = settings.TMAjaxThrobber || [];
  17. if (throbberSettings['add']) {
  18. if (!$('#taxonomy-manager-toolbar-throbber.tm-processed').length) {
  19. $('#taxonomy-manager-toolbar-throbber').addClass('tm-processed');
  20. Drupal.attachThrobber();
  21. Drupal.attachResizeableTreeDiv();
  22. Drupal.attachGlobalSelectAll();
  23. }
  24. }
  25. Drupal.attachMsgCloseLink(context);
  26. }
  27. }
  28. Drupal.TaxonomyManagerTree = function(id, vid, parents) {
  29. this.div = $("#"+ id);
  30. this.ul = $(this.div).children();
  31. this.form = $(this.div).parents('form');
  32. this.form_build_id = $(this.form).children().children(':input[name="form_build_id"]').val();
  33. this.form_id = $(this.form).children().children(' :input[name="form_id"]').val();
  34. this.form_token = $(this.form).children().children(' :input[name="form_token"]').val();
  35. this.language = this.getLanguage();
  36. this.treeId = id;
  37. this.vocId = vid;
  38. this.formParents = parents;
  39. this.childFormUrl = Drupal.settings.childForm['url'];
  40. this.siblingsFormUrl = Drupal.settings.siblingsForm['url'];
  41. this.attachTreeview(this.ul);
  42. this.attachSiblingsForm(this.ul);
  43. this.attachSelectAllChildren(this.ul);
  44. this.attachLanguageSelector();
  45. //attach term data js, if enabled
  46. var term_data_settings = Drupal.settings.termData || [];
  47. if (term_data_settings['url']) {
  48. Drupal.attachTermData(this.ul);
  49. }
  50. $(this.div).addClass("tm-processed");
  51. }
  52. /**
  53. * adds collapsible treeview to a given list
  54. */
  55. Drupal.TaxonomyManagerTree.prototype.attachTreeview = function(ul, currentIndex) {
  56. var tree = this;
  57. if (currentIndex) {
  58. ul = $(ul).slice(currentIndex);
  59. }
  60. var expandableParent = $(ul).find("div.hitArea");
  61. $(expandableParent).click(function() {
  62. var li = $(this).parent();
  63. tree.loadChildForm(li);
  64. tree.toggleTree(li);
  65. });
  66. $(expandableParent).parent("li.expandable, li.lastExpandable").children("ul").hide();
  67. }
  68. /**
  69. * toggles a collapsible/expandable tree element by swaping classes
  70. */
  71. Drupal.TaxonomyManagerTree.prototype.toggleTree = function(node) {
  72. $(node).children("ul").toggle();
  73. this.swapClasses(node, "expandable", "collapsable");
  74. this.swapClasses(node, "lastExpandable", "lastCollapsable");
  75. }
  76. /**
  77. * helper function for swapping two classes
  78. */
  79. Drupal.TaxonomyManagerTree.prototype.swapClasses = function(node, c1, c2) {
  80. if ($(node).hasClass(c1)) {
  81. $(node).removeClass(c1).addClass(c2);
  82. }
  83. else if ($(node).hasClass(c2)) {
  84. $(node).removeClass(c2).addClass(c1);
  85. }
  86. }
  87. /**
  88. * loads child terms and appends html to list
  89. * adds treeview, weighting etc. js to inserted child list
  90. */
  91. Drupal.TaxonomyManagerTree.prototype.loadChildForm = function(li, update, callback) {
  92. var tree = this;
  93. if ($(li).is(".has-children") || update == true) {
  94. $(li).removeClass("has-children");
  95. if (update) {
  96. $(li).children("ul").remove();
  97. }
  98. var parentId = Drupal.getTermId(li);
  99. var url = tree.childFormUrl +'/'+ this.treeId +'/'+ this.vocId +'/'+ parentId;
  100. var param = new Object();
  101. param['form_build_id'] = this.form_build_id;
  102. param['form_id'] = this.form_id;
  103. param['tree_id'] = this.treeId;
  104. param['form_parents'] = this.formParents;
  105. param['language'] = this.language;
  106. $.ajax({
  107. data: param,
  108. type: "GET",
  109. url: url,
  110. dataType: 'json',
  111. success: function(response, status) {
  112. $(li).append(response.data);
  113. var ul = $(li).children("ul");
  114. tree.attachTreeview(ul);
  115. tree.attachSiblingsForm(ul);
  116. tree.attachSelectAllChildren(ul);
  117. //only attach other features if enabled!
  118. var weight_settings = Drupal.settings.updateWeight || [];
  119. if (weight_settings['up']) {
  120. Drupal.attachUpdateWeightTerms(li);
  121. }
  122. var term_data_settings = Drupal.settings.termData || [];
  123. if (term_data_settings['url']) {
  124. Drupal.attachTermDataLinks(ul);
  125. }
  126. if (typeof(callback) == "function") {
  127. callback(li, tree);
  128. }
  129. }
  130. });
  131. }
  132. }
  133. /**
  134. * function for reloading root tree elements
  135. */
  136. Drupal.TaxonomyManagerTree.prototype.loadRootForm = function(tids) {
  137. var tree = this;
  138. var url = this.childFormUrl +'/'+ this.treeId +'/'+ this.vocId +'/0/';
  139. var param = new Object();
  140. param['form_build_id'] = this.form_build_id;
  141. param['form_id'] = this.form_id;
  142. param['tree_id'] = this.treeId;
  143. param['form_parents'] = this.formParents;
  144. param['language'] = this.language;
  145. param['terms_to_expand'] = tids; // can either be a single term id or concatinated ids
  146. $.ajax({
  147. data: param,
  148. type: "GET",
  149. url: url,
  150. dataType: 'json',
  151. success: function(response, status) {
  152. $('#'+ tree.treeId).html(response.data);
  153. var ul = $('#'+ tree.treeId).children("ul");
  154. tree.attachTreeview(ul);
  155. tree.attachSiblingsForm(ul);
  156. tree.attachSelectAllChildren(ul);
  157. Drupal.attachUpdateWeightTerms(ul);
  158. Drupal.attachTermDataLinks(ul);
  159. var lang = $('#edit-'+ tree.treeId +'-language').val();
  160. if (lang != "" && lang != tree.langauge) {
  161. $(tree.div).parent().siblings("div.taxonomy-manager-tree-top").find("select.language-selector option[value="+ lang +"]").attr("selected", "selected");
  162. }
  163. }
  164. });
  165. }
  166. /**
  167. * adds link for loading next siblings terms, when click terms get loaded through ahah
  168. * adds all needed js like treeview, weightning, etc.. to new added terms
  169. */
  170. Drupal.TaxonomyManagerTree.prototype.attachSiblingsForm = function(ul) {
  171. var tree = this;
  172. var url = this.siblingsFormUrl;
  173. var list = "li.has-more-siblings div.term-has-more-siblings";
  174. if (ul) {
  175. list = $(ul).find(list);
  176. }
  177. $(list).bind('click', function() {
  178. $(this).unbind("click");
  179. var li = this.parentNode;
  180. var all = $('li', li.parentNode);
  181. var currentIndex = all.index(li);
  182. var page = Drupal.getPage(li);
  183. var prev_id = Drupal.getTermId(li);
  184. var parentId = Drupal.getParentId(li);
  185. url += '/'+ tree.treeId +'/'+ page +'/'+ prev_id +'/'+ parentId;
  186. var param = new Object();
  187. param['form_build_id'] = tree.form_build_id;
  188. param['form_id'] = tree.form_id;
  189. param['tree_id'] = tree.treeId;
  190. param['form_parents'] = tree.formParents;
  191. param['language'] = tree.language;
  192. $.ajax({
  193. data: param,
  194. type: "GET",
  195. url: url,
  196. dataType: 'json',
  197. success: function(response, status) {
  198. $(list).remove();
  199. $(li).after(response.data);
  200. tree.attachTreeview($('li', li.parentNode), currentIndex);
  201. tree.attachSelectAllChildren($('li', li.parentNode), currentIndex);
  202. //only attach other features if enabled!
  203. var weight_settings = Drupal.settings.updateWeight || [];
  204. if (weight_settings['up']) {
  205. Drupal.attachUpdateWeightTerms($('li', li.parentNode), currentIndex);
  206. }
  207. var term_data_settings = Drupal.settings.termData || [];
  208. if (term_data_settings['url']) {
  209. Drupal.attachTermDataToSiblings($('li', li.parentNode), currentIndex);
  210. }
  211. $(li).removeClass("last").removeClass("has-more-siblings");
  212. $(li).children().children('.term-operations').hide();
  213. tree.swapClasses(li, "lastExpandable", "expandable");
  214. tree.attachSiblingsForm($(li).parent());
  215. }
  216. });
  217. });
  218. }
  219. /**
  220. * helper function for getting out the current page
  221. */
  222. Drupal.getPage = function(li) {
  223. return $(li).find("input:hidden[class=page]").attr("value");
  224. }
  225. /**
  226. * returns terms id of a given list element
  227. */
  228. Drupal.getTermId = function(li) {
  229. return $(li).children().children("input:hidden[class=term-id]").attr("value");
  230. }
  231. /**
  232. * return term id of a prent of a given list element
  233. * if no parent exists (root level), returns 0
  234. */
  235. Drupal.getParentId = function(li) {
  236. var parentId;
  237. try {
  238. var parentLi = $(li).parent("ul").parent("li");
  239. parentId = Drupal.getTermId(parentLi);
  240. } catch(e) {
  241. return 0;
  242. }
  243. return parentId;
  244. }
  245. /**
  246. * update classes for tree view, if list elements get swaped
  247. */
  248. Drupal.updateTree = function(upTerm, downTerm) {
  249. if ($(upTerm).is(".last")) {
  250. $(upTerm).removeClass("last");
  251. Drupal.updateTreeDownTerm(downTerm);
  252. }
  253. else if ($(upTerm).is(".lastExpandable")) {
  254. $(upTerm).removeClass("lastExpandable").addClass("expandable");
  255. Drupal.updateTreeDownTerm(downTerm);
  256. }
  257. else if ($(upTerm).is(".lastCollapsable")) {
  258. $(upTerm).removeClass("lastCollapsable").addClass("collapsable");
  259. Drupal.updateTreeDownTerm(downTerm);
  260. }
  261. }
  262. /**
  263. * update classes for tree view for a list element moved downwards
  264. */
  265. Drupal.updateTreeDownTerm = function(downTerm) {
  266. if ($(downTerm).is(".expandable")) {
  267. $(downTerm).removeClass("expandable").addClass("lastExpandable");
  268. }
  269. else if ($(downTerm).is(".collapsable")) {
  270. $(downTerm).removeClass("collapsable").addClass("lastCollapsable");
  271. }
  272. else {
  273. $(downTerm).addClass("last");
  274. }
  275. }
  276. /**
  277. * Adds button next to parent term to select all available child checkboxes
  278. */
  279. Drupal.TaxonomyManagerTree.prototype.attachSelectAllChildren = function(parent, currentIndex) {
  280. var tree = this;
  281. if (currentIndex) {
  282. parent = $(parent).slice(currentIndex);
  283. }
  284. $(parent).find('span.select-all-children').click(function() {
  285. tree.SelectAllChildrenToggle(this);
  286. });
  287. }
  288. /**
  289. * (un-)selects nested checkboxes
  290. */
  291. Drupal.TaxonomyManagerTree.prototype.SelectAllChildrenToggle = function(span) {
  292. var tree = this;
  293. if ($(span).hasClass("select-all-children")) {
  294. var li = $(span).parents("li:first");
  295. if ($(li).hasClass("has-children")) {
  296. this.loadChildForm(li, true, function(li, tree) {
  297. tree.swapClasses(li, "expandable", "collapsable");
  298. tree.swapClasses(li, "lastExpandable", "lastCollapsable");
  299. var this_span = $(li).find('span.select-all-children:first');
  300. tree.SelectAllChildrenToggle(this_span);
  301. return;
  302. });
  303. }
  304. else {
  305. $(span).removeClass("select-all-children").addClass("deselect-all-children");
  306. $(span).attr("title", Drupal.t("Deselect all children"));
  307. $(span).parents("li:first").find('ul:first').each(function() {
  308. var first_element = $(this).find('.term-line:first');
  309. $(first_element).parent().siblings("li").find('div.term-line:first :checkbox').attr('checked', true);
  310. $(first_element).find(' :checkbox').attr('checked', true);
  311. });
  312. }
  313. }
  314. else {
  315. $(span).removeClass("deselect-all-children").addClass("select-all-children");
  316. $(span).parents(".term-line").siblings("ul").find(':checkbox').attr("checked", false);
  317. $(span).attr("title", Drupal.t("Select all children"));
  318. }
  319. }
  320. /**
  321. * language selector
  322. */
  323. Drupal.TaxonomyManagerTree.prototype.attachLanguageSelector = function() {
  324. var tree = this;
  325. var selector = $(tree.div).parent().siblings("div.taxonomy-manager-tree-top").find("select.language-selector");
  326. $(selector).not(".selector-processed").change(function() {
  327. tree.language = $(this).val();
  328. tree.loadRootForm();
  329. });
  330. $(selector).addClass("selector-processed");
  331. }
  332. Drupal.TaxonomyManagerTree.prototype.getLanguage = function() {
  333. var lang = $('#edit-taxonomy-manager-top-language').val();
  334. if (typeof(lang) == "undefined") {
  335. return "";
  336. }
  337. return lang;
  338. }
  339. /**
  340. * return array of selected terms
  341. */
  342. Drupal.TaxonomyManagerTree.prototype.getSelectedTerms = function() {
  343. var terms = new Array();
  344. $(this.div).find("input[type=checkbox][checked]").each(function() {
  345. var term = $(this).parents("li").eq(0);
  346. terms.push(term);
  347. });
  348. return terms;
  349. }
  350. /**
  351. * returns li node for a given term id, if it exists in the tree
  352. */
  353. Drupal.TaxonomyManagerTree.prototype.getLi = function(termId) {
  354. return $(this.div).find("input:hidden[class=term-id][value="+ termId +"]").parent().parent();
  355. }
  356. Drupal.attachMsgCloseLink = function(context) {
  357. $(context).find('div.messages').once(function() {
  358. $('<span class="taxonomy-manager-message-close"><a href="" title="'+ Drupal.t('Close') +'">x</a></span>').appendTo(this).click(function() {
  359. $(this).parent().fadeOut('fast', function() {
  360. $(this).remove();
  361. });
  362. return false;
  363. });
  364. });
  365. }
  366. /**
  367. * attaches a throbber element to the taxonomy manager
  368. */
  369. Drupal.attachThrobber = function() {
  370. var div = $('#taxonomy-manager');
  371. var throbber = $('<img src="'+ Drupal.settings.taxonomy_manager['modulePath'] +'images/ajax-loader.gif" alt="" height="25">');
  372. throbber.appendTo("#taxonomy-manager-toolbar-throbber").hide();
  373. throbber.ajaxStart(function() {
  374. $(this).show();
  375. });
  376. throbber.ajaxStop(function() {
  377. $(this).hide();
  378. });
  379. throbber.ajaxError(function() {
  380. alert("An AJAX error occurred. Reload the page and check your logs.");
  381. $(this).hide();
  382. });
  383. }
  384. /**
  385. * makes the div resizeable
  386. */
  387. Drupal.attachResizeableTreeDiv = function() {
  388. $('img.div-grippie').each(function() {
  389. var staticOffset = null;
  390. var div = $(this).parents("fieldset").parent();
  391. $(this).mousedown(startDrag);
  392. function startDrag(e) {
  393. staticOffset = div.width() - e.pageX;
  394. div.css('opacity', 0.5);
  395. $(document).mousemove(performDrag).mouseup(endDrag);
  396. return false;
  397. }
  398. function performDrag(e) {
  399. div.width(Math.max(200, staticOffset + e.pageX) + 'px');
  400. return false;
  401. }
  402. function endDrag(e) {
  403. $(document).unbind("mousemove", performDrag).unbind("mouseup", endDrag);
  404. div.css('opacity', 1);
  405. }
  406. });
  407. }
  408. /**
  409. * Adds select all / remove selection functionality.
  410. */
  411. Drupal.attachGlobalSelectAll = function() {
  412. $('span.taxonomy-manager-select-helpers').once(function() {
  413. var form = $(this).parents('.form-wrapper:first');
  414. $(this).find('span.select-all-children').click(function() {
  415. // Only select those that are visible to the end user.
  416. $(form).parent().find(' :checkbox:visible').attr('checked', true);
  417. });
  418. $(this).find('span.deselect-all-children').click(function() {
  419. $(form).parent().find(':checkbox').attr("checked", false);
  420. });
  421. });
  422. }
  423. })(jQuery);