Updating the amenity select to multiselect
authorSteve Sutton <steve@gaslightmedia.com>
Fri, 17 Jun 2016 20:20:12 +0000 (16:20 -0400)
committerLaury GvR <laury@gaslightmedia.com>
Tue, 21 Jun 2016 21:37:53 +0000 (17:37 -0400)
Also Adding asmselect jquery plugin to display the multiselect list.
Updated the member filter form and the search parts to use an array of
amenities instead of a single amenity. Added a filter for the request
part to make sure that the amenity array used in the query is an array
of integers.

css/jquery.asmselect.css [new file with mode: 0644]
js/jquery.asmselect.js [new file with mode: 0644]
models/front/members/list.php
views/front/members/list.html

diff --git a/css/jquery.asmselect.css b/css/jquery.asmselect.css
new file mode 100644 (file)
index 0000000..10330b2
--- /dev/null
@@ -0,0 +1,63 @@
+.asmContainer {
+       /* container that surrounds entire asmSelect widget */
+}
+
+.asmSelect {
+       /* the newly created regular 'select' */
+       display: inline; 
+}
+
+.asmOptionDisabled {
+       /* disabled options in new select */
+       color: #999; 
+}
+
+.asmHighlight {
+       /* the highlight span */
+       padding: 0;
+       margin: 0 0 0 1em;
+}
+
+.asmList {
+       /* html list that contains selected items */
+       margin: 0.25em 0 1em 0; 
+       position: relative;
+       display: block;
+       padding-left: 0; 
+       list-style: none; 
+}
+
+.asmListItem {
+       /* li item from the html list above */
+       position: relative; 
+       margin-left: 0;
+       padding-left: 0;
+       list-style: none;
+       background: #ddd;
+       border: 1px solid #bbb; 
+       width: 100%; 
+       margin: 0 0 -1px 0; 
+       line-height: 1em;
+}
+
+.asmListItem:hover {
+       background-color: #e5e5e5;
+}
+
+.asmListItemLabel {
+       /* this is a span that surrounds the text in the item, except for the remove link */
+       padding: 5px; 
+       display: block;
+}
+
+.asmListSortable .asmListItemLabel {
+       cursor: move; 
+}
+
+.asmListItemRemove {
+       /* the remove link in each list item */
+       position: absolute;
+       right: 0; 
+       top: 0;
+       padding: 5px; 
+}
diff --git a/js/jquery.asmselect.js b/js/jquery.asmselect.js
new file mode 100644 (file)
index 0000000..792c62b
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * Alternate Select Multiple (asmSelect) 1.0.4a beta - jQuery Plugin
+ * http://www.ryancramer.com/projects/asmselect/
+ * 
+ * Copyright (c) 2009 by Ryan Cramer - http://www.ryancramer.com
+ * 
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ */
+
+(function($) {
+
+       $.fn.asmSelect = function(customOptions) {
+
+               var options = {
+
+                       listType: 'ol',                                         // Ordered list 'ol', or unordered list 'ul'
+                       sortable: false,                                        // Should the list be sortable?
+                       highlight: false,                                       // Use the highlight feature? 
+                       animate: false,                                         // Animate the the adding/removing of items in the list?
+                       addItemTarget: 'bottom',                                // Where to place new selected items in list: top or bottom
+                       hideWhenAdded: false,                                   // Hide the option when added to the list? works only in FF
+                       debugMode: false,                                       // Debug mode keeps original select visible 
+
+                       removeLabel: 'remove',                                  // Text used in the "remove" link
+                       highlightAddedLabel: 'Added: ',                         // Text that precedes highlight of added item
+                       highlightRemovedLabel: 'Removed: ',                     // Text that precedes highlight of removed item
+
+                       containerClass: 'asmContainer',                         // Class for container that wraps this widget
+                       selectClass: 'asmSelect',                               // Class for the newly created <select>
+                       optionDisabledClass: 'asmOptionDisabled',               // Class for items that are already selected / disabled
+                       listClass: 'asmList',                                   // Class for the list ($ol)
+                       listSortableClass: 'asmListSortable',                   // Another class given to the list when it is sortable
+                       listItemClass: 'asmListItem',                           // Class for the <li> list items
+                       listItemLabelClass: 'asmListItemLabel',                 // Class for the label text that appears in list items
+                       removeClass: 'asmListItemRemove',                       // Class given to the "remove" link
+                       highlightClass: 'asmHighlight'                          // Class given to the highlight <span>
+
+                       };
+
+               $.extend(options, customOptions); 
+
+               return this.each(function(index) {
+
+                       var $original = $(this);                                // the original select multiple
+                       var $container;                                         // a container that is wrapped around our widget
+                       var $select;                                            // the new select we have created
+                       var $ol;                                                // the list that we are manipulating
+                       var buildingSelect = false;                             // is the new select being constructed right now?
+                       var ieClick = false;                                    // in IE, has a click event occurred? ignore if not
+                       var ignoreOriginalChangeEvent = false;                  // originalChangeEvent bypassed when this is true
+
+                       function init() {
+
+                               // initialize the alternate select multiple
+
+                               // this loop ensures uniqueness, in case of existing asmSelects placed by ajax (1.0.3)
+                               while($("#" + options.containerClass + index).size() > 0) index++; 
+
+                               $select = $("<select></select>")
+                                       .addClass(options.selectClass)
+                                       .attr('name', options.selectClass + index)
+                                       .attr('id', options.selectClass + index); 
+
+                               $selectRemoved = $("<select></select>"); 
+
+                               $ol = $("<" + options.listType + "></" + options.listType + ">")
+                                       .addClass(options.listClass)
+                                       .attr('id', options.listClass + index); 
+
+                               $container = $("<div></div>")
+                                       .addClass(options.containerClass) 
+                                       .attr('id', options.containerClass + index); 
+
+                               buildSelect();
+
+                               $select.change(selectChangeEvent)
+                                       .click(selectClickEvent); 
+
+                               $original.change(originalChangeEvent)
+                                       .wrap($container).before($select).before($ol);
+
+                               if(options.sortable) makeSortable();
+
+                               if($.browser.msie && $.browser.version < 8) $ol.css('display', 'inline-block'); // Thanks Matthew Hutton
+                       }
+
+                       function makeSortable() {
+
+                               // make any items in the selected list sortable
+                               // requires jQuery UI sortables, draggables, droppables
+
+                               $ol.sortable({
+                                       items: 'li.' + options.listItemClass,
+                                       handle: '.' + options.listItemLabelClass,
+                                       axis: 'y',
+                                       update: function(e, data) {
+
+                                               var updatedOptionId;
+
+                                               $(this).children("li").each(function(n) {
+
+                                                       $option = $('#' + $(this).attr('rel')); 
+
+                                                       if($(this).is(".ui-sortable-helper")) {
+                                                               updatedOptionId = $option.attr('id'); 
+                                                               return;
+                                                       }
+
+                                                       $original.append($option); 
+                                               }); 
+
+                                               if(updatedOptionId) triggerOriginalChange(updatedOptionId, 'sort'); 
+                                       }
+
+                               }).addClass(options.listSortableClass); 
+                       }
+
+                       function selectChangeEvent(e) {
+                               
+                               // an item has been selected on the regular select we created
+                               // check to make sure it's not an IE screwup, and add it to the list
+
+                               if($.browser.msie && $.browser.version < 7 && !ieClick) return;
+                               var id = $(this).children("option:selected").slice(0,1).attr('rel'); 
+                               addListItem(id);        
+                               ieClick = false; 
+                               triggerOriginalChange(id, 'add'); // for use by user-defined callbacks
+                       }
+
+                       function selectClickEvent() {
+
+                               // IE6 lets you scroll around in a select without it being pulled down
+                               // making sure a click preceded the change() event reduces the chance
+                               // if unintended items being added. there may be a better solution?
+
+                               ieClick = true; 
+                       }
+
+                       function originalChangeEvent(e) {
+
+                               // select or option change event manually triggered
+                               // on the original <select multiple>, so rebuild ours
+
+                               if(ignoreOriginalChangeEvent) {
+                                       ignoreOriginalChangeEvent = false; 
+                                       return; 
+                               }
+
+                               $select.empty();
+                               $ol.empty();
+                               buildSelect();
+
+                               // opera has an issue where it needs a force redraw, otherwise
+                               // the items won't appear until something else forces a redraw
+                               if($.browser.opera) $ol.hide().fadeIn("fast");
+                       }
+
+                       function buildSelect() {
+
+                               // build or rebuild the new select that the user
+                               // will select items from
+
+                               buildingSelect = true; 
+
+                               // add a first option to be the home option / default selectLabel
+                               $select.prepend("<option>" + $original.attr('title') + "</option>"); 
+
+                               $original.children("option").each(function(n) {
+
+                                       var $t = $(this); 
+                                       var id; 
+
+                                       if(!$t.attr('id')) $t.attr('id', 'asm' + index + 'option' + n); 
+                                       id = $t.attr('id'); 
+                                       origClass = $t.attr('class');
+
+                                       if($t.is(":selected")) {
+                                               addListItem(id); 
+                                               addSelectOption(id, origClass, true);                                           
+                                       } else {
+                                               addSelectOption(id, origClass); 
+                                       }
+                               });
+
+                               if(!options.debugMode) $original.hide(); // IE6 requires this on every buildSelect()
+                               selectFirstItem();
+                               buildingSelect = false; 
+                       }
+
+                       function addSelectOption(optionId, origClass, disabled) {
+
+                               // add an <option> to the <select>
+                               // used only by buildSelect()
+
+                               if(disabled == undefined) var disabled = false; 
+
+                               if(origClass == undefined) origClass = '';
+
+                               var $O = $('#' + optionId); 
+                               var $option = $("<option class=\""+origClass+"\">" + $O.text() + "</option>")
+                                       .val($O.val())
+                                       .attr('rel', optionId);
+
+                               if(disabled) disableSelectOption($option); 
+
+                               $select.append($option); 
+                       }
+
+                       function selectFirstItem() {
+
+                               // select the firm item from the regular select that we created
+
+                               $select.children(":eq(0)").attr("selected", true); 
+                       }
+
+                       function disableSelectOption($option) {
+
+                               // make an option disabled, indicating that it's already been selected
+                               // because safari is the only browser that makes disabled items look 'disabled'
+                               // we apply a class that reproduces the disabled look in other browsers
+
+                               $option.addClass(options.optionDisabledClass)
+                                       .attr("selected", false)
+                                       .attr("disabled", true);
+
+                               if(options.hideWhenAdded) $option.hide();
+                               if($.browser.msie) $select.hide().show(); // this forces IE to update display
+                       }
+
+                       function enableSelectOption($option) {
+
+                               // given an already disabled select option, enable it
+
+                               $option.removeClass(options.optionDisabledClass)
+                                       .attr("disabled", false);
+
+                               if(options.hideWhenAdded) $option.show();
+                               if($.browser.msie) $select.hide().show(); // this forces IE to update display
+                       }
+
+                       function addListItem(optionId) {
+
+                               // add a new item to the html list
+
+                               var $O = $('#' + optionId); 
+
+                               if(!$O) return; // this is the first item, selectLabel
+
+                               var $removeLink = $("<a></a>")
+                                       .attr("href", "#")
+                                       .addClass(options.removeClass)
+                                       .prepend(options.removeLabel)
+                                       .click(function() { 
+                                               dropListItem($(this).parent('li').attr('rel')); 
+                                               return false; 
+                                       }); 
+
+                               var $itemLabel = $("<span></span>")
+                                       .addClass(options.listItemLabelClass)
+                                       .html($O.html()); 
+
+                               var $item = $("<li></li>")
+                                       .attr('rel', optionId)
+                                       .addClass(options.listItemClass)
+                                       .append($itemLabel)
+                                       .append($removeLink)
+                                       .hide();
+
+                               if(!buildingSelect) {
+                                       if($O.is(":selected")) return; // already have it
+                                       $O.attr('selected', true); 
+                               }
+
+                               if(options.addItemTarget == 'top' && !buildingSelect) {
+                                       $ol.prepend($item); 
+                                       if(options.sortable) $original.prepend($O); 
+                               } else {
+                                       $ol.append($item); 
+                                       if(options.sortable) $original.append($O); 
+                               }
+
+                               addListItemShow($item); 
+
+                               disableSelectOption($("[rel=" + optionId + "]", $select));
+
+                               if(!buildingSelect) {
+                                       setHighlight($item, options.highlightAddedLabel); 
+                                       selectFirstItem();
+                                       if(options.sortable) $ol.sortable("refresh");   
+                               }
+
+                       }
+
+                       function addListItemShow($item) {
+
+                               // reveal the currently hidden item with optional animation
+                               // used only by addListItem()
+
+                               if(options.animate && !buildingSelect) {
+                                       $item.animate({
+                                               opacity: "show",
+                                               height: "show"
+                                       }, 100, "swing", function() { 
+                                               $item.animate({
+                                                       height: "+=2px"
+                                               }, 50, "swing", function() {
+                                                       $item.animate({
+                                                               height: "-=2px"
+                                                       }, 25, "swing"); 
+                                               }); 
+                                       }); 
+                               } else {
+                                       $item.show();
+                               }
+                       }
+
+                       function dropListItem(optionId, highlightItem) {
+
+                               // remove an item from the html list
+
+                               if(highlightItem == undefined) var highlightItem = true; 
+                               var $O = $('#' + optionId); 
+
+                               $O.attr('selected', false); 
+                               $item = $ol.children("li[rel=" + optionId + "]");
+
+                               dropListItemHide($item); 
+                               enableSelectOption($("[rel=" + optionId + "]", options.removeWhenAdded ? $selectRemoved : $select));
+
+                               if(highlightItem) setHighlight($item, options.highlightRemovedLabel); 
+
+                               triggerOriginalChange(optionId, 'drop'); 
+                               
+                       }
+
+                       function dropListItemHide($item) {
+
+                               // remove the currently visible item with optional animation
+                               // used only by dropListItem()
+
+                               if(options.animate && !buildingSelect) {
+
+                                       $prevItem = $item.prev("li");
+
+                                       $item.animate({
+                                               opacity: "hide",
+                                               height: "hide"
+                                       }, 100, "linear", function() {
+                                               $prevItem.animate({
+                                                       height: "-=2px"
+                                               }, 50, "swing", function() {
+                                                       $prevItem.animate({
+                                                               height: "+=2px"
+                                                       }, 100, "swing"); 
+                                               }); 
+                                               $item.remove(); 
+                                       }); 
+                                       
+                               } else {
+                                       $item.remove(); 
+                               }
+                       }
+
+                       function setHighlight($item, label) {
+
+                               // set the contents of the highlight area that appears
+                               // directly after the <select> single
+                               // fade it in quickly, then fade it out
+
+                               if(!options.highlight) return; 
+
+                               $select.next("#" + options.highlightClass + index).remove();
+
+                               var $highlight = $("<span></span>")
+                                       .hide()
+                                       .addClass(options.highlightClass)
+                                       .attr('id', options.highlightClass + index)
+                                       .html(label + $item.children("." + options.listItemLabelClass).slice(0,1).text()); 
+                                       
+                               $select.after($highlight); 
+
+                               $highlight.fadeIn("fast", function() {
+                                       setTimeout(function() { $highlight.fadeOut("slow"); }, 50); 
+                               }); 
+                       }
+
+                       function triggerOriginalChange(optionId, type) {
+
+                               // trigger a change event on the original select multiple
+                               // so that other scripts can pick them up
+
+                               ignoreOriginalChangeEvent = true; 
+                               $option = $("#" + optionId); 
+
+                               $original.trigger('change', [{
+                                       'option': $option,
+                                       'value': $option.val(),
+                                       'id': optionId,
+                                       'item': $ol.children("[rel=" + optionId + "]"),
+                                       'type': type
+                               }]); 
+                       }
+
+                       init();
+               });
+       };
+
+})(jQuery); 
index 80caa86..408a5a7 100644 (file)
@@ -63,7 +63,7 @@ $GLOBALS['showOpts'] = array(
 );
 
 // Load Members data abstract
-require_once(GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataMemberInfo.php');
+require_once GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataMemberInfo.php';
 
 /*
  * This class performs the work for the default action of the "Members" menu
@@ -219,6 +219,21 @@ class GlmMembersFront_members_list extends GlmDataMemberInfo
             }
         }
 
+        // enque the script and css for asmselect
+        wp_enqueue_script(
+            'asmselect',
+            GLM_MEMBERS_PLUGIN_BASE_URL . '/js/jquery.asmselect.js',
+            'jquery',
+            '1.0.4a',
+            true
+        );
+        wp_enqueue_style(
+            'asmselect',
+            GLM_MEMBERS_PLUGIN_BASE_URL . '/css/jquery.asmselect.css',
+            null,
+            '1.0.4a'
+        );
+
         // Apply any provided text search to name, description, short description, and street address
         if (isset($_REQUEST['textSearch']) && $_REQUEST['textSearch'] != '') {
             $textSearch = addslashes(filter_input(INPUT_POST, 'textSearch', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES));
@@ -239,7 +254,7 @@ class GlmMembersFront_members_list extends GlmDataMemberInfo
         }
 
         // Get category data for search pick list and shortcode selection
-        require_once(GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataCategories.php');
+        require_once GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataCategories.php';
         $Categories = new GlmDataCategories($this->wpdb, $this->config);
         $categoryData = $Categories->getListSortedParentChild(false);
 
@@ -402,22 +417,29 @@ class GlmMembersFront_members_list extends GlmDataMemberInfo
 
             // Check if an amenity has been submitted
             if (isset($_REQUEST['amenitySearch'])) {
-                $amenSelected = $_REQUEST['amenitySearch'] - 0;
+                $amenSelected = $_REQUEST['amenitySearch'];
+                $amenSelected = array_filter( $amenSelected, function($var){
+                    return is_numeric( $var ) && $var > 0;
+                } );
 
                 // If we have an amenity ID
-                if ($amenSelected > 0) {
+                if ($amenSelected ) {
 
                     // Build query to get the member ID's that have the selected amenity
                     $where .= $whereSep." T.id in (
                        SELECT DISTINCT(ref_dest)
                          FROM ".GLM_MEMBERS_PLUGIN_DB_PREFIX."amenity_ref
-                                      WHERE amenity = $amenSelected
+                                      WHERE amenity IN ( " . implode( ',', $amenSelected ) . " )
                                     AND ref_type = ".$this->config['ref_type_numb']['MemberInfo']."
                         )";
                     $whereSep = ' AND ';
 
                     // Set default to true for the selected category
-                    $amenityData[$amenSelected]['default'] = true;
+                    foreach ( $amenityData as $amenElement ) {
+                        if ( in_array( $amenElement['id'], $amenSelected ) ) {
+                            $amenityData[$amenElement['id']]['default'] = true;
+                        }
+                    }
 
                 } // If we have a sane amenity ID
             } // If an amenity search has been selected
index 1aeb04b..ac7929f 100644 (file)
@@ -39,8 +39,7 @@
                         {if $settings.list_show_search_amenities}
                             <div>
                                 {$terms.term_member_cap} Amenity:
-                                <select name="amenitySearch" id="amenitySelect">
-                                    <option value=""></option>https://www.youtube.com/watch?v=18tGkzfPbmA
+                                <select name="amenitySearch[]" id="amenitySelect" title="Click to Select Amenities" multiple="multiple">
                                     {foreach from=$amenities item=v}
                                         <option value="{$v.id}" {if $v.default} selected="selected"{/if}>
                                             {$v.name}
     {/if} {*list_show_list*}
     <script type="text/javascript">
         jQuery(document).ready(function($) {
-
+            jQuery('select[multiple').asmSelect();
             // Show search filters box
             {if $settings.list_show_search}