aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCash Costello <cash.costello@gmail.com>2011-10-17 19:37:30 -0700
committerCash Costello <cash.costello@gmail.com>2011-10-17 19:37:30 -0700
commit63ebe7b121105beb7e4c8b9b82e3c649f6a9a489 (patch)
tree79f51f8b6e94203297cbbc899f3daa400475de34
parent9a3ec606cfe2cbbe08949a56198bf9d4bb2e1e64 (diff)
parent34c968355231216624c2beae59ea9ad17f7ef2c0 (diff)
downloadelgg-63ebe7b121105beb7e4c8b9b82e3c649f6a9a489.tar.gz
elgg-63ebe7b121105beb7e4c8b9b82e3c649f6a9a489.tar.bz2
Merge pull request #74 from cash/autocomplete
Fixes #2102, #2712, #3450 Finishes autocomplete and userpicker for 1.8.1
-rw-r--r--engine/lib/elgglib.php1
-rw-r--r--engine/lib/input.php72
-rw-r--r--js/lib/autocomplete.js31
-rw-r--r--js/lib/userpicker.js123
-rw-r--r--mod/groups/views/default/group/default.php2
-rw-r--r--vendors/jquery/jquery.ui.autocomplete.html.js40
-rw-r--r--views/default/css/elements/forms.php50
-rw-r--r--views/default/input/autocomplete.php20
-rw-r--r--views/default/input/userpicker.php32
-rw-r--r--views/default/object/default.php2
-rw-r--r--views/default/user/default.php4
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);
}