diff options
-rw-r--r-- | engine/lib/elgglib.php | 1 | ||||
-rw-r--r-- | engine/lib/input.php | 72 | ||||
-rw-r--r-- | js/lib/autocomplete.js | 31 | ||||
-rw-r--r-- | js/lib/userpicker.js | 123 | ||||
-rw-r--r-- | mod/groups/views/default/group/default.php | 2 | ||||
-rw-r--r-- | vendors/jquery/jquery.ui.autocomplete.html.js | 40 | ||||
-rw-r--r-- | views/default/css/elements/forms.php | 50 | ||||
-rw-r--r-- | views/default/input/autocomplete.php | 20 | ||||
-rw-r--r-- | views/default/input/userpicker.php | 32 | ||||
-rw-r--r-- | views/default/object/default.php | 2 | ||||
-rw-r--r-- | views/default/user/default.php | 4 |
11 files changed, 259 insertions, 118 deletions
diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index 1469067ca..53a15ba7a 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -2042,6 +2042,7 @@ function elgg_init() { elgg_register_page_handler('ajax', 'elgg_ajax_page_handler'); elgg_register_js('elgg.autocomplete', 'js/lib/autocomplete.js'); + elgg_register_js('jquery.ui.autocomplete.html', 'vendors/jquery/jquery.ui.autocomplete.html.js'); elgg_register_js('elgg.userpicker', 'js/lib/userpicker.js'); elgg_register_js('elgg.friendspicker', 'js/lib/friends_picker.js'); elgg_register_js('jquery.easing', 'vendors/jquery/jquery.easing.1.3.packed.js'); diff --git a/engine/lib/input.php b/engine/lib/input.php index 5c1a6299c..4900817a5 100644 --- a/engine/lib/input.php +++ b/engine/lib/input.php @@ -289,13 +289,35 @@ function input_livesearch_page_handler($page) { if ($entities = get_data($query)) { foreach ($entities as $entity) { + $entity = get_entity($entity->guid); + if (!$entity) { + continue; + } + + if (in_array('groups', $match_on)) { + $value = $entity->guid; + } else { + $value = $entity->username; + } + + $output = elgg_view_list_item($entity, array( + 'hover' => false, + 'class' => 'elgg-autocomplete-item', + )); + + $icon = elgg_view_entity_icon($entity, 'tiny', array( + 'hover' => false, + )); + $result = array( 'type' => 'user', 'name' => $entity->name, 'desc' => $entity->username, - 'icon' => '<img class="elgg-livesearch-icon" src="' . - get_entity($entity->guid)->getIconURL('tiny') . '" />', - 'guid' => $entity->guid + 'guid' => $entity->guid, + 'label' => $output, + 'value' => $value, + 'icon' => $icon, + 'url' => $entity->getURL(), ); $results[$entity->name . rand(1, 100)] = $result; } @@ -316,13 +338,29 @@ function input_livesearch_page_handler($page) { "; if ($entities = get_data($query)) { foreach ($entities as $entity) { + $entity = get_entity($entity->guid); + if (!$entity) { + continue; + } + + $output = elgg_view_list_item($entity, array( + 'hover' => false, + 'class' => 'elgg-autocomplete-item', + )); + + $icon = elgg_view_entity_icon($entity, 'tiny', array( + 'hover' => false, + )); + $result = array( 'type' => 'group', 'name' => $entity->name, 'desc' => strip_tags($entity->description), - 'icon' => '<img class="elgg-livesearch-icon" src="' - . get_entity($entity->guid)->getIconURL('tiny') . '" />', - 'guid' => $entity->guid + 'guid' => $entity->guid, + 'label' => $output, + 'value' => $entity->guid, + 'icon' => $icon, + 'url' => $entity->getURL(), ); $results[$entity->name . rand(1, 100)] = $result; @@ -347,13 +385,29 @@ function input_livesearch_page_handler($page) { if ($entities = get_data($query)) { foreach ($entities as $entity) { + $entity = get_entity($entity->guid); + if (!$entity) { + continue; + } + + $output = elgg_view_list_item($entity, array( + 'hover' => false, + 'class' => 'elgg-autocomplete-item', + )); + + $icon = elgg_view_entity_icon($entity, 'tiny', array( + 'hover' => false, + )); + $result = array( 'type' => 'user', 'name' => $entity->name, 'desc' => $entity->username, - 'icon' => '<img class="elgg-livesearch-icon" src="' - . get_entity($entity->guid)->getIconURL('tiny') . '" />', - 'guid' => $entity->guid + 'guid' => $entity->guid, + 'label' => $output, + 'value' => $entity->username, + 'icon' => $icon, + 'url' => $entity->getURL(), ); $results[$entity->name . rand(1, 100)] = $result; } diff --git a/js/lib/autocomplete.js b/js/lib/autocomplete.js index 917326d4f..46d72d146 100644 --- a/js/lib/autocomplete.js +++ b/js/lib/autocomplete.js @@ -5,35 +5,10 @@ elgg.provide('elgg.autocomplete'); elgg.autocomplete.init = function() { $('.elgg-input-autocomplete').autocomplete({ - source: elgg.autocomplete.url, //gets set by input/autocomplete - minLength: 1, - select: function(event, ui) { - var item = ui.item; - $(this).val(item.name); - - var hidden = $(this).next(); - hidden.val(item.guid); - } + source: elgg.autocomplete.url, //gets set by input/autocomplete view + minLength: 2, + html: "html" }) - - //@todo This seems convoluted - .data("autocomplete")._renderItem = function(ul, item) { - switch (item.type) { - case 'user': - case 'group': - r = item.icon + item.name + ' - ' + item.desc; - break; - - default: - r = item.name + ' - ' + item.desc; - break; - } - - return $("<li/>") - .data("item.autocomplete", item) - .append(r) - .appendTo(ul); - }; }; elgg.register_hook_handler('init', 'system', elgg.autocomplete.init);
\ No newline at end of file diff --git a/js/lib/userpicker.js b/js/lib/userpicker.js index 826bf21a0..ae2add53f 100644 --- a/js/lib/userpicker.js +++ b/js/lib/userpicker.js @@ -1,14 +1,22 @@ elgg.provide('elgg.userpicker'); +/** + * Userpicker initialization + * + * The userpicker is an autocomplete library for selecting multiple users or + * friends. It works in concert with the view input/userpicker. + * + * @return void + */ elgg.userpicker.init = function() { + // binding autocomplete. // doing this as an each so we can pass this to functions. $('.elgg-input-user-picker').each(function() { - - var _this = this; - + $(this).autocomplete({ source: function(request, response) { + var params = elgg.userpicker.getSearchParams(this); elgg.get('livesearch', { @@ -20,64 +28,89 @@ elgg.userpicker.init = function() { }); }, minLength: 2, + html: "html", select: elgg.userpicker.addUser }) - - //@todo This seems convoluted - .data("autocomplete")._renderItem = elgg.userpicker.formatItem; }); -}; -elgg.userpicker.formatItem = function(ul, item) { - switch (item.type) { - case 'user': - case 'group': - r = item.icon + item.name + ' - ' + item.desc; - break; - - default: - r = item.name + ' - ' + item.desc; - break; - } - - return $("<li/>") - .data("item.autocomplete", item) - .append(r) - .appendTo(ul); -}; + $('.elgg-userpicker-remove').live('click', elgg.userpicker.removeUser); +} +/** + * Adds a user to the select user list + * + * elgg.userpicker.userList is defined in the input/userpicker view + * + * @param {Object} event + * @param {Object} ui The object returned by the autocomplete endpoint + * @return void + */ elgg.userpicker.addUser = function(event, ui) { var info = ui.item; - + // do not allow users to be added multiple times if (!(info.guid in elgg.userpicker.userList)) { elgg.userpicker.userList[info.guid] = true; + var users = $(this).siblings('.elgg-user-picker-list'); + var li = '<input type="hidden" name="members[]" value="' + info.guid + '" />'; + li += elgg.userpicker.viewUser(info); + $('<li>').html(li).appendTo(users); + } + + $(this).val(''); + event.preventDefault(); +} + +/** + * Remove a user from the selected user list + * + * @param {Object} event + * @return void + */ +elgg.userpicker.removeUser = function(event) { + var item = $(this).closest('.elgg-user-picker-list > li'); - var picker = $(this).closest('.elgg-user-picker'); - var users = picker.find('.elgg-user-picker-entries'); - var internalName = users.find('[type=hidden]').attr('name'); - - // not sure why formatted isn't. - var formatted = elgg.userpicker.formatItem(data); + var guid = item.find('[name="members[]"]').val(); + delete elgg.userpicker.userList[guid]; - // add guid as hidden input and to list. - var li = formatted + ' <div class="delete-button"><a onclick="elgg.userpicker.removeUser(this, ' + info.guid + ')"><strong>X</strong></a></div>' - + '<input type="hidden" name="' + internalName + '" value="' + info.guid + '" />'; - $('<li>').html(li).appendTo(users); + item.remove(); + event.preventDefault(); +} - $(this).val(''); - } -}; +/** + * Render the list item for insertion into the selected user list + * + * The html in this method has to remain synced with the input/userpicker view + * + * @param {Object} info The object returned by the autocomplete endpoint + * @return string + */ +elgg.userpicker.viewUser = function(info) { + + var deleteLink = "<a href='#' class='elgg-userpicker-remove'>X</a>"; -elgg.userpicker.removeUser = function(link, guid) { - $(link).closest('.elgg-user-picker-entries > li').remove(); -}; + var html = "<div class='elgg-image-block'>"; + html += "<div class='elgg-image'>" + info.icon + "</div>"; + html += "<div class='elgg-image-alt'>" + deleteLink + "</div>"; + html += "<div class='elgg-body'>" + info.name + "</div>"; + html += "</div"; + + return html; +} -elgg.userpicker.getSearchParams = function(e) { - if ($(e).closest('.elgg-user-picker').find('[name=match_on]').attr('checked')) { - return {'match_on[]': 'friends', 'term' : e.term}; +/** + * Get the parameters to use for autocomplete + * + * This grabs the value of the friends checkbox. + * + * @param {Object} obj Object for the autocomplete callback + * @return Object + */ +elgg.userpicker.getSearchParams = function(obj) { + if (obj.element.siblings('[name=match_on]').attr('checked')) { + return {'match_on[]': 'friends', 'term' : obj.term}; } else { - return {'match_on[]': 'users', 'term' : e.term}; + return {'match_on[]': 'users', 'term' : obj.term}; } } diff --git a/mod/groups/views/default/group/default.php b/mod/groups/views/default/group/default.php index fc91f90d0..6eae467c6 100644 --- a/mod/groups/views/default/group/default.php +++ b/mod/groups/views/default/group/default.php @@ -34,5 +34,5 @@ if ($vars['full_view']) { $params = $params + $vars; $list_body = elgg_view('group/elements/summary', $params); - echo elgg_view_image_block($icon, $list_body); + echo elgg_view_image_block($icon, $list_body, $vars); } diff --git a/vendors/jquery/jquery.ui.autocomplete.html.js b/vendors/jquery/jquery.ui.autocomplete.html.js new file mode 100644 index 000000000..a3ed2ee4b --- /dev/null +++ b/vendors/jquery/jquery.ui.autocomplete.html.js @@ -0,0 +1,40 @@ +/* + * jQuery UI Autocomplete HTML Extension + * + * Copyright 2010, Scott González (http://scottgonzalez.com) + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * http://github.com/scottgonzalez/jquery-ui-extensions + */ +(function( $ ) { + +var proto = $.ui.autocomplete.prototype, + initSource = proto._initSource; + +function filter( array, term ) { + var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" ); + return $.grep( array, function(value) { + return matcher.test( $( "<div>" ).html( value.label || value.value || value ).text() ); + }); +} + +$.extend( proto, { + _initSource: function() { + if ( this.options.html && $.isArray(this.options.source) ) { + this.source = function( request, response ) { + response( filter( this.options.source, request.term ) ); + }; + } else { + initSource.call( this ); + } + }, + + _renderItem: function( ul, item) { + return $( "<li></li>" ) + .data( "item.autocomplete", item ) + .append( $( "<a></a>" )[ this.options.html ? "html" : "text" ]( item.label ) ) + .appendTo( ul ); + } +}); + +})( jQuery );
\ No newline at end of file diff --git a/views/default/css/elements/forms.php b/views/default/css/elements/forms.php index 83ec2f602..255d95622 100644 --- a/views/default/css/elements/forms.php +++ b/views/default/css/elements/forms.php @@ -235,19 +235,51 @@ input[type="radio"] { } /* *************************************** - USER PICKER + AUTOCOMPLETE *************************************** */ +<?php //autocomplete will expand to fullscreen without max-width ?> +.ui-autocomplete { + position: absolute; + cursor: default; +} +.elgg-autocomplete-item .elgg-body { + max-width: 600px; +} +.ui-autocomplete { + background-color: white; + border: 1px solid #ccc; + overflow: hidden; + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.ui-autocomplete .ui-menu-item { + padding: 0px 4px; + + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.ui-autocomplete .ui-menu-item:hover { + background-color: #eee; +} +.ui-autocomplete a:hover { + text-decoration: none; + color: #4690D6; +} -.user-picker .user-picker-entry { - clear:both; - height:25px; - padding:5px; - margin-top:5px; - border-bottom:1px solid #cccccc; +/* *************************************** + USER PICKER +*************************************** */ +.elgg-user-picker-list li:first-child { + border-top: 1px dotted #ccc; + margin-top: 5px; } -.user-picker-entry .elgg-button-delete { - margin-right:10px; +.elgg-user-picker-list > li { + border-bottom: 1px dotted #ccc; } + /* *************************************** DATE PICKER **************************************** */ diff --git a/views/default/input/autocomplete.php b/views/default/input/autocomplete.php index 421541e24..e58eb1ae8 100644 --- a/views/default/input/autocomplete.php +++ b/views/default/input/autocomplete.php @@ -8,7 +8,7 @@ * @todo This currently only works for ONE AUTOCOMPLETE TEXT FIELD on a page. * * @uses $vars['value'] Current value for the text input - * @uses $vars['match_on'] Array | str What to match on. all|array(groups|users|friends|subtype) + * @uses $vars['match_on'] Array | str What to match on. all|array(groups|users|friends) * @uses $vars['match_owner'] Bool. Match only entities that are owned by logged in user. * @uses $vars['class'] Additional CSS class */ @@ -26,15 +26,19 @@ $defaults = array( $vars = array_merge($defaults, $vars); -$ac_url_params = http_build_query(array( - 'match_on' => $vars['match_on'], - 'match_owner' => $vars['match_owner'], -)); - -unset($vars['match_on']); -unset($vars['match_owner']); +$params = array(); +if (isset($vars['match_on'])) { + $params['match_on'] = $vars['match_on']; + unset($vars['match_on']); +} +if (isset($vars['match_owner'])) { + $params['match_owner'] = $vars['match_owner']; + unset($vars['match_owner']); +} +$ac_url_params = http_build_query($params); elgg_load_js('elgg.autocomplete'); +elgg_load_js('jquery.ui.autocomplete.html'); ?> diff --git a/views/default/input/userpicker.php b/views/default/input/userpicker.php index b852d24fc..5c4b297b1 100644 --- a/views/default/input/userpicker.php +++ b/views/default/input/userpicker.php @@ -9,14 +9,14 @@ * * The name of the hidden fields is members[] * - * Defaults to lazy load user lists in paginated alphabetical order. User needs + * @warning Only a single input/userpicker is supported per web page. + * + * Defaults to lazy load user lists in alphabetical order. User needs * to type two characters before seeing the user popup list. * - * As users are checked they move down to a "users" box. + * As users are selected they move down to a "users" box. * When this happens, a hidden input is created with the * name of members[] and a value of the GUID. - * - * @warning: this is not stable */ elgg_load_js('elgg.userpicker'); @@ -24,18 +24,18 @@ elgg_load_js('elgg.userpicker'); function user_picker_add_user($user_id) { $user = get_entity($user_id); if (!$user || !($user instanceof ElggUser)) { - return FALSE; + return false; } - $icon = $user->getIconURL('tiny'); - - $code = '<li class="elgg-image-block">'; - $code .= "<div class='elgg-image'><img class=\"livesearch_icon\" src=\"$icon\" /></div>"; - $code .= "<div class='elgg-image-alt'><a onclick='elgg.userpicker.removeUser(this, $user_id)'><strong>X</strong></a></div>"; - $code .= "<div class='elgg-body'>"; - $code .= "$user->name - $user->username"; - $code .= "<input type=\"hidden\" name=\"members[]\" value=\"$user_id\">"; + $icon = elgg_view_entity_icon($user, 'tiny', array('hover' => false)); + + // this html must be synced with the userpicker.js library + $code = '<li><div class="elgg-image-block">'; + $code .= "<div class='elgg-image'>$icon</div>"; + $code .= "<div class='elgg-image-alt'><a href='#' class='elgg-userpicker-remove'>X</a></div>"; + $code .= "<div class='elgg-body'>" . $user->name . "</div>"; $code .= "</div>"; + $code .= "<input type=\"hidden\" name=\"members[]\" value=\"$user_id\">"; $code .= '</li>'; return $code; @@ -62,9 +62,11 @@ foreach ($vars['value'] as $user_id) { ?> <div class="elgg-user-picker"> <input type="text" class="elgg-input-user-picker" size="30"/> - <label><input type="checkbox" name="match_on" value="true" /><?php echo elgg_echo('userpicker:only_friends'); ?></label> - <ul class="elgg-user-picker-entries"><?php echo $user_list; ?></ul> + <input type="checkbox" name="match_on" value="true" /> + <label><?php echo elgg_echo('userpicker:only_friends'); ?></label> + <ul class="elgg-user-picker-list"><?php echo $user_list; ?></ul> </div> <script type="text/javascript"> + // @todo grab the values in the init function rather than using inline JS elgg.userpicker.userList = <?php echo $json_values ?>; </script>
\ No newline at end of file diff --git a/views/default/object/default.php b/views/default/object/default.php index a9c3e15ca..110648304 100644 --- a/views/default/object/default.php +++ b/views/default/object/default.php @@ -46,4 +46,4 @@ $params = array( $params = $params + $vars; $body = elgg_view('object/elements/summary', $params); -echo elgg_view_image_block($icon, $body); +echo elgg_view_image_block($icon, $body, $vars); diff --git a/views/default/user/default.php b/views/default/user/default.php index c0c18f85f..6c84e84ad 100644 --- a/views/default/user/default.php +++ b/views/default/user/default.php @@ -9,7 +9,7 @@ $entity = $vars['entity']; $size = elgg_extract('size', $vars, 'tiny'); -$icon = elgg_view_entity_icon($entity, $size); +$icon = elgg_view_entity_icon($entity, $size, $vars); // Simple XFN $rel = ''; @@ -53,5 +53,5 @@ if (elgg_get_context() == 'gallery') { $list_body = elgg_view('user/elements/summary', $params); - echo elgg_view_image_block($icon, $list_body); + echo elgg_view_image_block($icon, $list_body, $vars); } |