term_reference_tree.js 14 KB

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