[gen] Implemented live search.
This commit is contained in:
parent
8591611aac
commit
92d1276ffb
|
@ -750,7 +750,7 @@ class ToolMixin(BaseMixin):
|
||||||
corresponding to the search named p_name, on class p_className.'''
|
corresponding to the search named p_name, on class p_className.'''
|
||||||
initiator = None
|
initiator = None
|
||||||
if name == 'customSearch':
|
if name == 'customSearch':
|
||||||
# It is a custom search whose parameters are in the session.
|
# It is a custom search whose parameters are in the session
|
||||||
fields = self.REQUEST.SESSION['searchCriteria']
|
fields = self.REQUEST.SESSION['searchCriteria']
|
||||||
res = Search('customSearch', **fields)
|
res = Search('customSearch', **fields)
|
||||||
elif ':' in name:
|
elif ':' in name:
|
||||||
|
@ -773,12 +773,24 @@ class ToolMixin(BaseMixin):
|
||||||
else:
|
else:
|
||||||
# It is the search for every instance of p_className
|
# It is the search for every instance of p_className
|
||||||
res = Search('allSearch')
|
res = Search('allSearch')
|
||||||
# Return a UiSearch if required.
|
# Return a UiSearch if required
|
||||||
if ui:
|
if ui:
|
||||||
res = UiSearch(res, className, self)
|
res = UiSearch(res, className, self)
|
||||||
if initiator: res.setInitiator(initiator, initiatorField, mode)
|
if initiator: res.setInitiator(initiator, initiatorField, mode)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def getLiveSearch(self, klass, keywords):
|
||||||
|
'''Gets the Search instance for performing a live search on p_klass.'''
|
||||||
|
if not keywords.endswith('*'): keywords += '*'
|
||||||
|
res = Search('liveSearch', SearchableText=keywords)
|
||||||
|
if hasattr(klass, 'searchAdvanced'):
|
||||||
|
# Take into account default params for the advanced search
|
||||||
|
advanced = klass.searchAdvanced
|
||||||
|
res.fields.update(advanced.fields)
|
||||||
|
res.sortBy = advanced.sortBy
|
||||||
|
res.sortOrder = advanced.sortOrder
|
||||||
|
return res
|
||||||
|
|
||||||
def advancedSearchEnabledFor(self, klass):
|
def advancedSearchEnabledFor(self, klass):
|
||||||
'''Is advanced search visible for p_klass ?'''
|
'''Is advanced search visible for p_klass ?'''
|
||||||
# By default, advanced search is enabled
|
# By default, advanced search is enabled
|
||||||
|
|
|
@ -147,6 +147,10 @@ msgstr ""
|
||||||
msgid "search_results_descr"
|
msgid "search_results_descr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All results"
|
||||||
|
msgid "search_results_all"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "New search"
|
#. Default: "New search"
|
||||||
msgid "search_new"
|
msgid "search_new"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -147,6 +147,10 @@ msgstr ""
|
||||||
msgid "search_results_descr"
|
msgid "search_results_descr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All results"
|
||||||
|
msgid "search_results_all"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "New search"
|
#. Default: "New search"
|
||||||
msgid "search_new"
|
msgid "search_new"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -147,6 +147,10 @@ msgstr "Resultate suchen"
|
||||||
msgid "search_results_descr"
|
msgid "search_results_descr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All results"
|
||||||
|
msgid "search_results_all"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "New search"
|
#. Default: "New search"
|
||||||
msgid "search_new"
|
msgid "search_new"
|
||||||
msgstr "Neues Suchfeld"
|
msgstr "Neues Suchfeld"
|
||||||
|
|
|
@ -148,6 +148,10 @@ msgstr "Search results"
|
||||||
msgid "search_results_descr"
|
msgid "search_results_descr"
|
||||||
msgstr " "
|
msgstr " "
|
||||||
|
|
||||||
|
#. Default: "All results"
|
||||||
|
msgid "search_results_all"
|
||||||
|
msgstr "All results"
|
||||||
|
|
||||||
#. Default: "New search"
|
#. Default: "New search"
|
||||||
msgid "search_new"
|
msgid "search_new"
|
||||||
msgstr "New search"
|
msgstr "New search"
|
||||||
|
|
|
@ -147,6 +147,10 @@ msgstr "Resultados"
|
||||||
msgid "search_results_descr"
|
msgid "search_results_descr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All results"
|
||||||
|
msgid "search_results_all"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "New search"
|
#. Default: "New search"
|
||||||
msgid "search_new"
|
msgid "search_new"
|
||||||
msgstr "Nueva búsqueda"
|
msgstr "Nueva búsqueda"
|
||||||
|
|
|
@ -148,6 +148,10 @@ msgstr "Résultats"
|
||||||
msgid "search_results_descr"
|
msgid "search_results_descr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All results"
|
||||||
|
msgid "search_results_all"
|
||||||
|
msgstr "Tous les résultats"
|
||||||
|
|
||||||
#. Default: "New search"
|
#. Default: "New search"
|
||||||
msgid "search_new"
|
msgid "search_new"
|
||||||
msgstr "Nouvelle recherche"
|
msgstr "Nouvelle recherche"
|
||||||
|
|
|
@ -147,6 +147,10 @@ msgstr "Risultati"
|
||||||
msgid "search_results_descr"
|
msgid "search_results_descr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All results"
|
||||||
|
msgid "search_results_all"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "New search"
|
#. Default: "New search"
|
||||||
msgid "search_new"
|
msgid "search_new"
|
||||||
msgstr "Nuova ricerca"
|
msgstr "Nuova ricerca"
|
||||||
|
|
|
@ -147,6 +147,10 @@ msgstr "Resultaten"
|
||||||
msgid "search_results_descr"
|
msgid "search_results_descr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All results"
|
||||||
|
msgid "search_results_all"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "New search"
|
#. Default: "New search"
|
||||||
msgid "search_new"
|
msgid "search_new"
|
||||||
msgstr "Nieuwe opzoeking"
|
msgstr "Nieuwe opzoeking"
|
||||||
|
|
|
@ -104,6 +104,9 @@ td.search { padding-top: 8px }
|
||||||
border: 1px solid #cccccc; background-color: white;
|
border: 1px solid #cccccc; background-color: white;
|
||||||
padding: 3px 4px 3px; font-size: 8pt; font-weight: normal;
|
padding: 3px 4px 3px; font-size: 8pt; font-weight: normal;
|
||||||
text-align: left; z-index: 2; line-height: normal }
|
text-align: left; z-index: 2; line-height: normal }
|
||||||
|
.liveSearch { top: 19px; padding: 0; width: 250px }
|
||||||
|
.lsSelected { background-color: #d9d7d9 }
|
||||||
|
.lsNoResult { text-align: center; padding: 3px 3px; font-style: italic }
|
||||||
.dropdownMenu { cursor: pointer; font-size: 93%; position: relative }
|
.dropdownMenu { cursor: pointer; font-size: 93%; position: relative }
|
||||||
.dropdown a:hover { text-decoration: underline }
|
.dropdown a:hover { text-decoration: underline }
|
||||||
.addForm { display: inline }
|
.addForm { display: inline }
|
||||||
|
|
|
@ -1125,3 +1125,96 @@ function reindexObject(indexName){
|
||||||
f.indexName.value = indexName;
|
f.indexName.value = indexName;
|
||||||
f.submit();
|
f.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Live-search-related functions (LS)
|
||||||
|
var lsTimeout;
|
||||||
|
function detectEventType(event) {
|
||||||
|
/* After p_event occurred on a live search input field, must we trigger a
|
||||||
|
search (a new char has been added), move up/down within the search
|
||||||
|
results (key up/down has been pressed) or hide the dropdown (escape)? */
|
||||||
|
if (event.type == 'focus') return 'search'
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case 38: return 'up';
|
||||||
|
case 40: return 'down';
|
||||||
|
case 27: return 'hide'; // escape
|
||||||
|
case 13: return 'go'; // cr
|
||||||
|
case 37: break; // left
|
||||||
|
case 39: break; // right
|
||||||
|
default: return 'search';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Function that selects the search result within the dropdown, after the user
|
||||||
|
has pressed the 'up' od 'down' key (p_direction). */
|
||||||
|
function selectLSResult(dropdown, direction){
|
||||||
|
var results = dropdown.children[0].getElementsByTagName('div');
|
||||||
|
if (results.length == 0) return;
|
||||||
|
var j; // The index of the new element to select
|
||||||
|
for (var i=0, len=results.length; i<len; i++) {
|
||||||
|
if (results[i].className == 'lsSelected') {
|
||||||
|
if (direction == 'up') {
|
||||||
|
if (i > 0) j = i-1;
|
||||||
|
else j = len-1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (i < (len-1)) j = i+1;
|
||||||
|
else j = 0;
|
||||||
|
}
|
||||||
|
results[i].className = '';
|
||||||
|
results[j].className = 'lsSelected';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isNaN(j)) results[0].className = 'lsSelected';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function that allows to go to a selected search result
|
||||||
|
function gotoLSLink(dropdown) {
|
||||||
|
var results = dropdown.children[0].getElementsByTagName('div');
|
||||||
|
for (var i=0, len=results.length; i<len; i++) {
|
||||||
|
if (results[i].className == 'lsSelected') {
|
||||||
|
var a = results[i].children[0];
|
||||||
|
if (a.href) window.location = a.href;
|
||||||
|
else eval(a.onclick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideLSDropdown(dropdown, timeout) {
|
||||||
|
if (dropdown.style.display == 'none') return;
|
||||||
|
if (!timeout) { dropdown.style.display = 'none'; return; }
|
||||||
|
lsTimeout = setTimeout(function(){
|
||||||
|
dropdown.style.display = 'none';}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function that manages an p_event that occurred on a live search input field
|
||||||
|
function onLiveSearchEvent(event, klass, action, toolUrl) {
|
||||||
|
var dropdown = document.getElementById(klass + '_LSDropdown');
|
||||||
|
if (lsTimeout) clearTimeout(lsTimeout);
|
||||||
|
// Hide the dropdown if action is forced to 'hide'
|
||||||
|
if (action == 'hide') { hideLSDropdown(dropdown, true); return; }
|
||||||
|
// Detect if the dropdown must be shown or hidden
|
||||||
|
var input = document.getElementById(klass + '_LSinput');
|
||||||
|
if (input.value.length > 2) {
|
||||||
|
var eventType = detectEventType(event);
|
||||||
|
if (!eventType) return;
|
||||||
|
if (eventType == 'hide') { hideLSDropdown(dropdown, false); return;}
|
||||||
|
if (eventType == 'go') { gotoLSLink(dropdown); return; }
|
||||||
|
if (eventType == 'search') {
|
||||||
|
// Trigger an Ajax search and refresh the dropdown content
|
||||||
|
var formElems = document.getElementById(klass + '_LSForm').elements;
|
||||||
|
var params = {};
|
||||||
|
for (var i=0, len=formElems.length; i<len; i++) {
|
||||||
|
var param = formElems.item(i);
|
||||||
|
var paramName = formElems.item(i).name;
|
||||||
|
if (param.name == 'action') continue;
|
||||||
|
params[param.name] = param.value;
|
||||||
|
}
|
||||||
|
lsTimeout = setTimeout(function() {
|
||||||
|
askAjaxChunk(klass+ '_LSResults', 'GET', toolUrl, 'pxLiveSearchResults',
|
||||||
|
params);
|
||||||
|
dropdown.style.display = 'block';}, 400);
|
||||||
|
}
|
||||||
|
else { selectLSResult(dropdown, eventType);} // Move up/down in results
|
||||||
|
}
|
||||||
|
else { hideLSDropdown(dropdown, true); }
|
||||||
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ class ToolWrapper(AbstractWrapper):
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Global elements included in every page.
|
# Global elements included in every page.
|
||||||
pxPagePrologue = Px('''
|
pxPagePrologue = Px('''
|
||||||
<!-- Include type-specific CSS and JS. -->
|
<!-- Include type-specific CSS and JS -->
|
||||||
<x if="cssJs">
|
<x if="cssJs">
|
||||||
<link for="cssFile in cssJs['css']" rel="stylesheet" type="text/css"
|
<link for="cssFile in cssJs['css']" rel="stylesheet" type="text/css"
|
||||||
href=":url(cssFile)"/>
|
href=":url(cssFile)"/>
|
||||||
|
@ -154,6 +154,54 @@ class ToolWrapper(AbstractWrapper):
|
||||||
(q(zobj.absolute_url()), q(layoutType), info[0], info[1])
|
(q(zobj.absolute_url()), q(layoutType), info[0], info[1])
|
||||||
</script>''')
|
</script>''')
|
||||||
|
|
||||||
|
pxLiveSearchResults = Px('''
|
||||||
|
<x var="className=req['className'];
|
||||||
|
klass=ztool.getAppyClass(className);
|
||||||
|
search=ztool.getLiveSearch(klass, req['w_SearchableText']);
|
||||||
|
zobjects=ztool.executeQuery(className, search=search, \
|
||||||
|
maxResults=10).objects">
|
||||||
|
<p if="not zobjects" class="lsNoResult">:_('query_no_result')</p>
|
||||||
|
<div for="ztied in zobjects" style="padding: 3px 5px">
|
||||||
|
<a href=":ztied.absolute_url()"
|
||||||
|
var="content=ztool.truncateValue(ztied.Title(), width=80)"
|
||||||
|
title=":ztied.Title()">:content</a>
|
||||||
|
</div>
|
||||||
|
<!-- Go to the page showing all results -->
|
||||||
|
<div if="zobjects" align=":dright" style="padding: 3px">
|
||||||
|
<a class="clickable" style="font-size: 95%; font-style: italic"
|
||||||
|
onclick=":'document.forms[%s].submit()' % \
|
||||||
|
q('%s_LSForm' % className)">:_('search_results_all') + '...'</a>
|
||||||
|
</div>
|
||||||
|
</x>''')
|
||||||
|
|
||||||
|
pxLiveSearch = Px('''
|
||||||
|
<form var="formId='%s_LSForm' % className"
|
||||||
|
id=":formId" name=":formId" action=":'%s/do' % toolUrl">
|
||||||
|
<input type="hidden" name="action" value="SearchObjects"/>
|
||||||
|
<input type="hidden" name="className" value=":className"/>
|
||||||
|
<table cellpadding="0" cellspacing="0"
|
||||||
|
var="searchLabel=_('search_button')">
|
||||||
|
<tr valign="bottom">
|
||||||
|
<td style="position: relative">
|
||||||
|
<input type="text" size="14" name="w_SearchableText" autocomplete="off"
|
||||||
|
id=":'%s_LSinput' % className" class="inputSearch"
|
||||||
|
title=":searchLabel"
|
||||||
|
var="jsCall='onLiveSearchEvent(event, %s, %s, %s)' % \
|
||||||
|
(q(className), q('auto'), q(toolUrl))"
|
||||||
|
onkeyup=":jsCall" onfocus=":jsCall"
|
||||||
|
onblur=":'onLiveSearchEvent(event, %s, %s)' % \
|
||||||
|
(q(className), q('hide'))"/>
|
||||||
|
<!-- Dropdown containing live search results -->
|
||||||
|
<div id=":'%s_LSDropdown' % className" class="dropdown liveSearch">
|
||||||
|
<div id=":'%s_LSResults' % className"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="image" class="clickable" src=":url('search')"
|
||||||
|
title=":searchLabel"/></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>''')
|
||||||
|
|
||||||
pxPortlet = Px('''
|
pxPortlet = Px('''
|
||||||
<x var="toolUrl=tool.url;
|
<x var="toolUrl=tool.url;
|
||||||
queryUrl='%s/query' % toolUrl;
|
queryUrl='%s/query' % toolUrl;
|
||||||
|
@ -201,19 +249,7 @@ class ToolWrapper(AbstractWrapper):
|
||||||
<!-- Searches -->
|
<!-- Searches -->
|
||||||
<x if="ztool.advancedSearchEnabledFor(rootClass)">
|
<x if="ztool.advancedSearchEnabledFor(rootClass)">
|
||||||
<!-- Live search -->
|
<!-- Live search -->
|
||||||
<form action=":'%s/do' % toolUrl">
|
<x>:tool.pxLiveSearch</x>
|
||||||
<input type="hidden" name="action" value="SearchObjects"/>
|
|
||||||
<input type="hidden" name="className" value=":className"/>
|
|
||||||
<table cellpadding="0" cellspacing="0">
|
|
||||||
<tr valign="bottom">
|
|
||||||
<td><input type="text" size="14" name="w_SearchableText"
|
|
||||||
class="inputSearch"/></td>
|
|
||||||
<td>
|
|
||||||
<input type="image" class="clickable" src=":url('search')"
|
|
||||||
title=":_('search_button')"/></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- Advanced search -->
|
<!-- Advanced search -->
|
||||||
<div var="highlighted=(currentClass == className) and \
|
<div var="highlighted=(currentClass == className) and \
|
||||||
|
|
Loading…
Reference in a new issue