diff --git a/images/bullet_move.png b/images/bullet_move.png new file mode 100644 index 0000000000000000000000000000000000000000..45f869428fd24f50acd6a16be70bf04a9f014e08 Binary files /dev/null and b/images/bullet_move.png differ diff --git a/term_reference_tree.css b/term_reference_tree.css index 40ba2cfbe530fa873c2fd7809636b9201eeb2623..5b72ebbfe96c05ff9ea680bf4105ec880c3d7370 100644 --- a/term_reference_tree.css +++ b/term_reference_tree.css @@ -94,6 +94,37 @@ background-position: middle left; } +.term-reference-tree-track-list.order-list li.track-item { + padding-left:0; +} + +.term-reference-tree-track-list.order-list li.track-item:hover { + color:#000; + background-image: none; +} + +.term-reference-tree-track-list.order-list .term-reference-tree-button-move, .term-reference-tree-track-list.order-list .term-reference-tree-button-delete{ + display:inline-block; vertical-align:middle; zoom:1; + width:16px; height:16px; + background-repeat: no-repeat; + background-position: bottom center; + margin:0 5px; +} + + +.term-reference-tree-track-list.order-list li.track-item .term-reference-tree-button-move{ + background-image: url("images/bullet_move.png"); +} +.term-reference-tree-track-list.order-list li.track-item .term-reference-tree-button-move:hover{ + cursor:move; +} +.term-reference-tree-track-list.order-list li.track-item .term-reference-tree-button-delete{ + background-image: url("images/bullet_delete.png"); float:right; +} +.term-reference-tree-track-list.order-list li.track-item .term-reference-tree-button-:hover{ + cursor:pointer; +} + .term-reference-tree-track-list li.term_ref_tree_nothing_message { list-style-type: none; list-style-image: none; diff --git a/term_reference_tree.js b/term_reference_tree.js index 319312f2dd0626ef4545bc66e3310f0467a8fb9e..23e742b3ed68653a749ebbabd0b7e7bbb71af887 100644 --- a/term_reference_tree.js +++ b/term_reference_tree.js @@ -28,7 +28,26 @@ //currently selected items to it. if($(this).hasClass('term-reference-tree-track-list-shown')) { var track_list_container = $(this).find('.term-reference-tree-track-list'); - + var tracklist_is_orderable = track_list_container.is('.order-list'); + if(tracklist_is_orderable){ + track_list_container.sortable({ + update: function(event, ui) { + console.log('sort update : event', event); + console.log('sort update : ui', ui); + + $.each(event.target.children, function(index, val) { + var $item = $(val), + // event.target = ul.list + // ui.item = li.track-item + control_id = $item.data('control_id'), + $hiddenInput = $('#'+control_id).parent('.form-item').next('input[type=hidden]'); + // $hiddenInput.attr('value', $item.index()); + $hiddenInput.val($item.index()); + }); + }, + }); + } + //Var to track whether using checkboxes or radio buttons. var input_type = ( $(this).has('input[type=checkbox]').size() > 0 ) ? 'checkbox' : 'radio'; @@ -40,14 +59,22 @@ var labels = checked_controls.next(); var label_element; + //get delta + if(tracklist_is_orderable){ + var weights = checked_controls.parent('.form-item').next('input[type=hidden]'); + } + //For each label of the checked boxes, add item to the track list. labels.each(function(index) { label_element = $(labels[index]); + delta = tracklist_is_orderable ? $(weights[index]).val() : -1; + addItemToTrackList( track_list_container, //Where to add new item. label_element.html(), //Text of new item. $(label_element).attr('for'), //Id of control new item is for. - input_type //checkbox or radio + input_type, //checkbox or radio + delta //delta ); }); //End labels.each @@ -62,10 +89,14 @@ //Remove the "nothing selected" message if showing - add it later if needed. //removeNothingSelectedMessage(track_list_container); var event_target = $(event.target); - var control_id = event_target.data('control_id'); - - if(control_id) { - event_target.remove(); + var event_parent_list = event_target.parent('li'); + var control_id = event_parent_list.data('control_id'); + // console.log('event', event); + // console.log('event_target.parent("li")', event_target.parent('li')); + // console.log('control_id', control_id); + // console.log('event_target.is(term-reference-tree-delete)', event_target.is('term-reference-tree-delete')); + if(event_target.is('.term-reference-tree-button-delete') && control_id) { + event_parent_list.remove(); var checkbox = $('#' + control_id); checkbox.removeAttr('checked'); @@ -89,7 +120,8 @@ track_list_container, //Where to add new item. label_element.html(), //Text of new item. $(label_element).attr('for'), //Id of control new item is for. - input_type //checkbox or radio + input_type, //checkbox or radio + -1 // delta ); } else { @@ -144,8 +176,9 @@ * * @param control_type Control type - 'checkbox' or 'radio'. */ - function addItemToTrackList(track_list_container, item_text, control_id, control_type) { - var new_item = $('
  • ' + item_text + '
  • '); + function addItemToTrackList(track_list_container, item_text, control_id, control_type, delta) { + console.log('addItemToTrackList'); + var new_item = $('
  • ' + item_text + '
  • '); new_item.data('control_id', control_id); //Add an id for easy finding of the item. @@ -173,35 +206,66 @@ } return; } - - //Using checkboxes, so there can be more than one selected item. - //Find the right place to put the new item, to match the order of the - //checkboxes. + + //Using checkboxes, so there can be more than one selected item. + //Find the right place to put the new item, + // to match the order of the checkboxes. + // OR order of delta var list_items = track_list_container.find('li'); var item_comparing_to; - + //Flag to tell whether the item was inserted. var inserted_flag = false; - list_items.each(function(index){ - item_comparing_to = $(list_items[index]); + + if(!track_list_container.is('.order-list')){ + + list_items.each(function(index){ + item_comparing_to = $(list_items[index]); - //If item is already on the track list, do nothing. - if ( control_id == item_comparing_to.data('control_id') ) { - inserted_flag = true; - return false; //Returning false stops the loop. - } - else if ( control_id < item_comparing_to.data('control_id') ) { - //Add it here. - item_comparing_to.before(new_item); - inserted_flag = true; - return false; //Returning false stops the loop. - } - }); + //If item is already on the track list, do nothing. + if ( control_id == item_comparing_to.data('control_id') ) { + inserted_flag = true; + return false; //Returning false stops the loop. + } + else if ( control_id < item_comparing_to.data('control_id') ) { + //Add it here. + item_comparing_to.before(new_item); + inserted_flag = true; + return false; //Returning false stops the loop. + } + }); - //If not inserted yet, add new item at the end of the track list. - if ( ! inserted_flag ) { - track_list_container.append(new_item); - } + //If not inserted yet, add new item at the end of the track list. + if ( ! inserted_flag ) { + track_list_container.append(new_item); + } + + }else{ + if( ! track_list_container.find('#'+new_item.attr('id')).size() ){ + + if(delta == -1){ + track_list_container.append(new_item); + inserted_flag = true; + }else{ + list_items.each(function(index){ + item_comparing_to = $(this); + if ( delta < item_comparing_to.attr('delta') ) { + //Add it here. + item_comparing_to.before(new_item); + inserted_flag = true; + return false; //Returning false stops the loop. + } + }); + //If not inserted yet, add new item at the end of the track list. + if ( ! inserted_flag ) + track_list_container.append(new_item); + + } + + track_list_container.sortable('refresh'); + } + } + } /** @@ -246,7 +310,7 @@ // This helper function checks if the maximum number of choices is already selected. // If so, it disables all the other options. If not, it enables them. - function checkMaxChoices(item, checkbox) { + function checkMaxChoices(item, checkbox, order_list) { var maxChoices = -1; try { maxChoices = parseInt(Drupal.settings.term_reference_tree.trees[item.attr('id')]['max_choices']); diff --git a/term_reference_tree.module b/term_reference_tree.module index 1aea8b531d81c88ea58f3a1bcd05443483ab19a2..4346ed47a589bd32c68d2982bcd3ff80f82160c6 100644 --- a/term_reference_tree.module +++ b/term_reference_tree.module @@ -33,7 +33,7 @@ function term_reference_tree_element_info() { '#input' => false, '#theme' => array('checkbox_tree_track_list'), '#pre_render' => array('form_pre_render_conditional_form_element'), - ), + ) ); return $types; @@ -183,6 +183,7 @@ function _term_reference_tree_flatten($element, &$form_state) { $children = element_children($element); foreach($children as $c) { $child = $element[$c]; + // dsm($child, '$child'); if (array_key_exists('#type', $child) && ($child['#type'] == 'radio' || $child['#type'] == 'checkbox')) { $output[] = $child; } diff --git a/term_reference_tree.widget.inc b/term_reference_tree.widget.inc index 8bae7a1fb46632d924d2625b9897235506b7da0f..1d4f6b758387564850b55ed9f9d56a185fdacf1c 100644 --- a/term_reference_tree.widget.inc +++ b/term_reference_tree.widget.inc @@ -19,6 +19,7 @@ function term_reference_tree_field_widget_info() { 'select_parents' => 0, 'cascading_selection' => 0, 'track_list' => 0, + 'track_list_order' => 0, 'token_display' => '', 'parent_term_id' => '', 'max_depth' => '', @@ -171,7 +172,18 @@ function term_reference_tree_field_widget_settings_form($field, $instance) { '#default_value' => $settings['track_list'], '#return_value' => 1, ); + + $form['track_list_order'] = array( + '#type' => 'checkbox', + '#title' => t('Track list drag and drop order'), + '#description' => t( + 'Allow drag and drop selected terms ordering on tracklist.'), + '#default_value' => $settings['track_list_order'], + '#return_value' => 1, + '#element_validate' => array('_term_reference_tree_track_list_order_validate'), + ); + $form['max_depth'] = array( '#type' => 'textfield', '#title' => t('Maximum Depth'), @@ -251,6 +263,18 @@ function _term_reference_tree_cascading_selection_validate($element, &$form_stat } } +function _term_reference_tree_track_list_order_validate($element, &$form_state){ + if ($form_state['values']['instance']['widget']['settings']['track_list'] == 0 && $form_state['values']['instance']['widget']['settings']['track_list_order'] == 1) { + // This is pretty wonky syntax for the field name in form_set_error, but it's + // correct. + form_set_error('field][track_list_order', t('You must enable Track List if Track List Order is enabled.')); + } + /* + TODO check if number of values is diffrent from 1 + */ +} + + /** * Process the checkbox_tree widget. * @@ -310,6 +334,7 @@ function term_reference_tree_process_checkbox_tree($element, $form_state) { $element[] = array( '#type' => 'checkbox_tree_track_list', '#max_choices' => $max_choices, + '#track_list_order' => $element['#track_list_order'], ); } } @@ -443,7 +468,8 @@ function theme_checkbox_tree_label($variables) { function theme_checkbox_tree_track_list($variables) { //Should the label be singular or plural? Depends on cardinality of term field. static $nothingselected; - + // dsm($variables, 'theme_checkbox_tree_track_list : $variables'); + if(!$nothingselected) { $nothingselected = t('[Nothing selected]'); //Add the "Nothing selected" text. To style it, replace it with whatever you want. @@ -459,17 +485,17 @@ function theme_checkbox_tree_track_list($variables) { 'Selected item (click the item to uncheck it)', 'Selected items (click an item to uncheck it)' ); + $order = $variables['element']['#track_list_order'] ? 'order-list' : ''; $output = '
    ' . $label . '
    - +
    '; return $output; } - /** * Implements hook_widget_field_form(). */ @@ -512,6 +538,7 @@ function term_reference_tree_field_widget_form(&$form, &$form_state, $field, $in $element['#select_parents'] = $settings['select_parents']; $element['#cascading_selection'] = $settings['cascading_selection']; $element['#track_list'] = $settings['track_list']; + $element['#track_list_order'] = $settings['track_list_order']; $element['#parent_tid'] = $settings['parent_term_id'] || $field['settings']['allowed_values'][0]['parent']; $element['#vocabulary'] = $voc->vid; $element['#token_display'] = module_exists('token') ? $settings['token_display'] : ''; @@ -523,7 +550,11 @@ function term_reference_tree_field_widget_form(&$form, &$form_state, $field, $in '#element_validate' => array('_term_reference_tree_widget_validate'), '#properties' => $properties, ); - + + if ($settings['track_list_order']) { + drupal_add_library('system', 'ui.sortable'); + } + return $element; } @@ -544,25 +575,58 @@ function term_reference_tree_field_widget_form(&$form, &$form_state, $field, $in * The validated element. */ function _term_reference_tree_widget_validate(&$element, &$form_state) { + dsm($element, '_term_reference_tree_widget_validate | $element'); $items = _term_reference_tree_flatten($element, $form_state); + dsm($items, '$items'); $value = array(); if ($element['#max_choices'] != 1) { - foreach($items as $child) { - if (array_key_exists('#value', $child) && $child['#value'] !== 0) { - array_push($value, array($element['#value_key'] => $child['#value'])); - - // If the element is leaves only and select parents is on, then automatically - // add all the parents of each selected value. - if ($element['#select_parents'] && $element['#leaves_only']) { - foreach($child['#parent_values'] as $parent_tid) { - if (!in_array(array($element['#value_key'] => $parent_tid), $value)) { - array_push($value, array($element['#value_key'] => $parent_tid)); - } - } - } - } - } + if(!$element['#track_list_order']){ + foreach($items as $child) { + if (array_key_exists('#value', $child) && $child['#value'] !== 0) { + array_push($value, array( $element['#value_key'] => $child['#value'])); + + // If the element is leaves only and select parents is on, then automatically + // add all the parents of each selected value. + if ($element['#select_parents'] && $element['#leaves_only']) { + foreach($child['#parent_values'] as $parent_tid) { + if (!in_array(array($element['#value_key'] => $parent_tid), $value)) { + array_push($value, array($element['#value_key'] => $parent_tid)); + } + } + } + + } + } + + }else{ + $selected_terms = array(); + foreach($items as $child) { + if (array_key_exists('#value', $child) && $child['#value'] !== 0) { + $delta = $form_state['input'][$child['#value'].'-weight']; + $selected_terms[$delta] = array($element['#value_key'] => $child['#value']); + + // If the element is leaves only and select parents is on, then automatically + // add all the parents of each selected value. + if ($element['#select_parents'] && $element['#leaves_only']) { + foreach($child['#parent_values'] as $parent_tid) { + if (!in_array(array($element['#value_key'] => $parent_tid), $selected_terms)) { + $delta = $form_state['input'][$parent_tid.'-weight']; + $selected_terms[$delta] = array($element['#value_key'] => $parent_tid); + } + } + } + + } + } + // reorder items + ksort($selected_terms); + // record in value + foreach ($selected_terms as $delta => $term) { + $value[] = $term; + } + + } } else { // If it's a tree of radio buttons, they all have the same value, so we can just @@ -575,6 +639,8 @@ function _term_reference_tree_widget_validate(&$element, &$form_state) { } } + dsm($value, '$value'); + if ($element['#required'] && empty($value)) { // The title is already check_plained so it's appropriate to use !. form_error($element, t('!name field is required.', array('!name' => $element['#title']))); @@ -701,7 +767,23 @@ function _term_reference_tree_build_item(&$element, &$term, &$form_state, &$valu $parents_for_id = array_merge($element['#parents'], array($term->tid)); $e['#id'] = drupal_html_id('edit-' . implode('-', $parents_for_id)); $e['#parents'] = $element['#parents']; - } + }else if($element['#track_list_order']){ + $delta = 0; + $i = -1; + if(isset($value[$term->tid])){ + foreach ($value as $tid) { + $i++; + if($term->tid == $tid) + break; + } + } + + $e_weight = array( + '#type' => 'hidden', + '#value' => $i, + '#name' => $term->tid.'-weight', + ); + } } else { $e = array( @@ -710,9 +792,12 @@ function _term_reference_tree_build_item(&$element, &$term, &$form_state, &$valu ); } - $container[$term->tid] = $e; + if(isset($e_weight)){ + $container[$term->tid.'-weight'] = $e_weight; + } + if (($depth + 1 <= $element['#max_depth'] || !$element['#max_depth']) && property_exists($term, 'children') && count($term->children) > 0) { $parents = $parent_tids; $parents[] = $term->tid;