term_reference_tree.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. (function($) {
  2. /**
  3. * Attaches the tree behavior to the term widget form.
  4. */
  5. Drupal.behaviors.termReferenceTree = {
  6. attach: function(context, settings) {
  7. // Bind the term expand/contract button to slide toggle the list underneath.
  8. $('.term-reference-tree-button', context).once('term-reference-tree-button').click(function() {
  9. $(this).toggleClass('term-reference-tree-collapsed');
  10. $(this).siblings('ul').slideToggle('fast');
  11. });
  12. // An expand all button (unimplemented)
  13. /*
  14. $('.expandbutton').click(function() {
  15. $(this).siblings('.term-reference-tree-button').trigger('click');
  16. });
  17. */
  18. $('.term-reference-tree', context).once('term-reference-tree', function() {
  19. // On page load, check whether the maximum number of choices is already selected.
  20. // If so, disable the other options.
  21. var tree = $(this);
  22. checkMaxChoices(tree, false);
  23. $(this).find('input[type=checkbox]').change(function() {
  24. checkMaxChoices(tree, $(this));
  25. });
  26. //On page load, check if the user wants a track list. If so, add the
  27. //currently selected items to it.
  28. if($(this).hasClass('term-reference-tree-track-list-shown')) {
  29. var track_list_container = $(this).find('.term-reference-tree-track-list');
  30. //Var to track whether using checkboxes or radio buttons.
  31. var input_type =
  32. ( $(this).has('input[type=checkbox]').size() > 0 ) ? 'checkbox' : 'radio';
  33. //Find all the checked controls.
  34. var checked_controls = $(this).find('input[type=' + input_type + ']:checked');
  35. //Get their labels.
  36. var labels = checked_controls.next();
  37. var label_element;
  38. //For each label of the checked boxes, add item to the track list.
  39. labels.each(function(index) {
  40. label_element = $(labels[index]);
  41. addItemToTrackList(
  42. track_list_container, //Where to add new item.
  43. label_element.html(), //Text of new item.
  44. $(label_element).attr('for'), //Id of control new item is for.
  45. input_type //checkbox or radio
  46. );
  47. }); //End labels.each
  48. //Show "nothing selected" message, if needed.
  49. showNothingSelectedMessage(track_list_container);
  50. //Event - when an element on the track list is clicked on:
  51. // 1. Delete it.
  52. // 2. Uncheck the associated checkbox.
  53. //The event is bound to the track list container, not each element.
  54. $(track_list_container).click(function(event){
  55. //Remove the "nothing selected" message if showing - add it later if needed.
  56. //removeNothingSelectedMessage(track_list_container);
  57. var event_target = $(event.target);
  58. var control_id = event_target.data('control_id');
  59. if(control_id) {
  60. event_target.remove();
  61. var checkbox = $('#' + control_id);
  62. checkbox.removeAttr('checked');
  63. checkMaxChoices(tree, checkbox);
  64. //Show "nothing selected" message, if needed.
  65. showNothingSelectedMessage(track_list_container);
  66. }
  67. });
  68. //Change track list when controls are clicked.
  69. $(this).find('.form-' + input_type).change(function(event){
  70. //Remove the "nothing selected" message if showing - add it later if needed.
  71. removeNothingSelectedMessage(track_list_container);
  72. var event_target = $(event.target);
  73. var control_id = event_target.attr('id');
  74. if ( event_target.attr('checked') ) {
  75. //Control checked - add item to the track list.
  76. label_element = event_target.next();
  77. addItemToTrackList(
  78. track_list_container, //Where to add new item.
  79. label_element.html(), //Text of new item.
  80. $(label_element).attr('for'), //Id of control new item is for.
  81. input_type //checkbox or radio
  82. );
  83. }
  84. else {
  85. //Checkbox unchecked. Remove from the track list.
  86. $('#' + control_id + '_list').remove();
  87. }
  88. //Show "nothing selected" message, if needed.
  89. showNothingSelectedMessage(track_list_container);
  90. }); //End process checkbox changes.
  91. } //End Want a track list.
  92. //On page load, check if the user wants a cascading selection.
  93. if($(this).hasClass('term-reference-tree-cascading-selection')) {
  94. //Check children when checkboxes are clicked.
  95. $(this).find('.form-checkbox').change(function(event) {
  96. var event_target = $(event.target);
  97. var control_id = event_target.attr('id');
  98. var children = event_target.parent().next().children().children('div.form-type-checkbox').children('input[id^="' + control_id + '-children"]');
  99. if(event_target.attr('checked')) {
  100. //Checkbox checked - check children if none were checked.
  101. if(!$(children).filter(':checked').length) {
  102. $(children).click().trigger('change');
  103. }
  104. }
  105. else {
  106. //Checkbox unchecked. Uncheck children if all were checked.
  107. if(!$(children).not(':checked').length) {
  108. $(children).click().trigger('change');
  109. }
  110. }
  111. });
  112. //End process checkbox changes.
  113. } //End Want a cascading checking.
  114. });
  115. }
  116. };
  117. /**
  118. * Add a new item to the track list.
  119. * If more than one item can be selected, the new item is positioned to
  120. * match the order of the terms in the checkbox tree.
  121. *
  122. * @param track_list_container Container where the new item will be added.
  123. *
  124. * @param item_text Text of the item to add.
  125. *
  126. * @param control_id Id of the checkbox/radio control the item matches.
  127. *
  128. * @param control_type Control type - 'checkbox' or 'radio'.
  129. */
  130. function addItemToTrackList(track_list_container, item_text, control_id, control_type) {
  131. var new_item = $('<li class="track-item">' + item_text + '</li>');
  132. new_item.data('control_id', control_id);
  133. //Add an id for easy finding of the item.
  134. new_item.attr('id', control_id + '_list');
  135. //Process radio controls - only one item can be selected.
  136. if ( control_type == 'radio') {
  137. //Find the existing element on the track list, if there is one.
  138. var current_items = track_list_container.find('li');
  139. //If there are no items on the track list, add the new item.
  140. if ( current_items.size() == 0 ) {
  141. track_list_container.append(new_item);
  142. }
  143. else {
  144. //There is an item on the list.
  145. var current_item = $(current_items.get(0));
  146. //Is the item we want to add different from what is there?
  147. if ( current_item.data('control_id') != control_id ) {
  148. //Remove exiting element from track list, and add the new one.
  149. current_item.remove();
  150. track_list_container.append(new_item);
  151. }
  152. }
  153. return;
  154. }
  155. //Using checkboxes, so there can be more than one selected item.
  156. //Find the right place to put the new item, to match the order of the
  157. //checkboxes.
  158. var list_items = track_list_container.find('li');
  159. var item_comparing_to;
  160. //Flag to tell whether the item was inserted.
  161. var inserted_flag = false;
  162. list_items.each(function(index){
  163. item_comparing_to = $(list_items[index]);
  164. //If item is already on the track list, do nothing.
  165. if ( control_id == item_comparing_to.data('control_id') ) {
  166. inserted_flag = true;
  167. return false; //Returning false stops the loop.
  168. }
  169. else if ( control_id < item_comparing_to.data('control_id') ) {
  170. //Add it here.
  171. item_comparing_to.before(new_item);
  172. inserted_flag = true;
  173. return false; //Returning false stops the loop.
  174. }
  175. });
  176. //If not inserted yet, add new item at the end of the track list.
  177. if ( ! inserted_flag ) {
  178. track_list_container.append(new_item);
  179. }
  180. }
  181. /**
  182. * Show the 'nothing selected' message if it applies.
  183. *
  184. * @param track_list_container Where the message is to be shown.
  185. */
  186. function showNothingSelectedMessage(track_list_container) {
  187. //Is the message there already?
  188. var message_showing =
  189. (track_list_container.find('.term_ref_tree_nothing_message').size() != 0);
  190. //Number of real items showing.
  191. var num_real_items_showing =
  192. message_showing
  193. ? track_list_container.find('li').size() - 1
  194. : track_list_container.find('li').size();
  195. if ( num_real_items_showing == 0 ) {
  196. //No items showing, so show the message.
  197. if ( ! message_showing ) {
  198. track_list_container.append(
  199. '<li class="term_ref_tree_nothing_message">' + termReferenceTreeNothingSelectedText + '</li>'
  200. );
  201. }
  202. }
  203. else { // !(num_real_items_showing == 0)
  204. //There are real items.
  205. if ( message_showing ) {
  206. track_list_container.find('.term_ref_tree_nothing_message').remove();
  207. }
  208. }
  209. }
  210. /**
  211. * Remove the 'nothing selected' message. Makes processing easier.
  212. *
  213. * @param track_list_container Where the message is shown.
  214. */
  215. function removeNothingSelectedMessage(track_list_container) {
  216. track_list_container.find('.term_ref_tree_nothing_message').remove();
  217. }
  218. // This helper function checks if the maximum number of choices is already selected.
  219. // If so, it disables all the other options. If not, it enables them.
  220. function checkMaxChoices(item, checkbox) {
  221. var maxChoices = -1;
  222. try {
  223. maxChoices = parseInt(Drupal.settings.term_reference_tree.trees[item.attr('id')]['max_choices']);
  224. }
  225. catch (e){}
  226. var count = item.find(':checked').length;
  227. if(maxChoices > 0 && count >= maxChoices) {
  228. item.find('input[type=checkbox]:not(:checked)').attr('disabled', 'disabled').parent().addClass('disabled');
  229. } else {
  230. item.find('input[type=checkbox]').removeAttr('disabled').parent().removeClass('disabled');
  231. }
  232. if(checkbox) {
  233. if(item.hasClass('select-parents')) {
  234. var track_list_container = item.find('.term-reference-tree-track-list');
  235. var input_type =
  236. ( item.has('input[type=checkbox]').size() > 0 ) ? 'checkbox' : 'radio';
  237. if(checkbox.attr('checked')) {
  238. checkbox.parents('ul.term-reference-tree-level li').children('div.form-item').children('input[type=checkbox]').each(function() {
  239. $(this).attr('checked', checkbox.attr('checked'));
  240. if(track_list_container) {
  241. label_element = $(this).next();
  242. addItemToTrackList(
  243. track_list_container, //Where to add new item.
  244. label_element.html(), //Text of new item.
  245. $(label_element).attr('for'), //Id of control new item is for.
  246. input_type //checkbox or radio
  247. );
  248. }
  249. });
  250. }
  251. }
  252. }
  253. }
  254. })(jQuery);