mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-22 01:58:47 +02:00
N°2284 - Replace JQuery Autocompleter plugin by JQuery UI Autocomplete widget
N°2390 - Auto-complete - Relevant results in first
This commit is contained in:
@@ -239,13 +239,13 @@ EOF
|
||||
$oPage->add_ready_script("$('.multiselect').multiselect($sJSOptions);");
|
||||
}
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
<<<JS
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
|
||||
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
|
||||
|
||||
EOF
|
||||
JS
|
||||
);
|
||||
} // Switch
|
||||
}
|
||||
@@ -269,10 +269,10 @@ EOF
|
||||
{
|
||||
$sDisplayValue = $this->GetObjectName($value);
|
||||
}
|
||||
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
|
||||
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 2; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
|
||||
|
||||
// the input for the auto-complete
|
||||
$sHTMLValue .= "<input id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
|
||||
$sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\"><i class=\"fas fa-search\"></i></div></span>";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
@@ -282,47 +282,9 @@ EOF
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$('#label_$this->iId').autocomplete({
|
||||
source: function( request, response ) {
|
||||
$.post( {
|
||||
url: GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
dataType: "json",
|
||||
data: {
|
||||
q:request.term,
|
||||
operation:'ac_extkey',
|
||||
sTargetClass:'{$this->sTargetClass}',
|
||||
sFilter:'$sFilter',
|
||||
bSearchMode:$JSSearchMode,
|
||||
sOutputFormat:'json',
|
||||
json: function() { return $sWizHelperJSON; }
|
||||
},
|
||||
success: function( data ) {
|
||||
response( data );
|
||||
}
|
||||
} );
|
||||
},
|
||||
autoFocus: true,
|
||||
minLength:{$iMinChars},
|
||||
select: function( event, ui ) {
|
||||
$('#$this->iId').val( ui.item.value );
|
||||
$('#label_$this->iId').val( ui.item.label );
|
||||
$('#$this->iId').trigger('validate');
|
||||
$('#$this->iId').trigger('extkeychange');
|
||||
$('#$this->iId').trigger('change');
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.autocomplete( "instance" )._renderItem = function( ul, item ) {
|
||||
var term = this.term.replace("/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi", "\\$1");
|
||||
var val = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
|
||||
if (item.obsolete == 'yes'){
|
||||
val = val + ' <b>old</b>';
|
||||
}
|
||||
return $( "<li>" )
|
||||
.append( val )
|
||||
.appendTo( ul );
|
||||
};
|
||||
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
oACWidget_{$this->iId}.AddAutocomplete($iMinChars, $sWizHelperJSON);
|
||||
if ($('#ac_dlg_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ac_dlg_{$this->iId}"></div>');
|
||||
@@ -334,12 +296,12 @@ JS
|
||||
{
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"><i class=\"fas fa-sitemap\"></i></div></span>";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
<<<JS
|
||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ac_tree_{$this->iId}"></div>');
|
||||
}
|
||||
EOF
|
||||
JS
|
||||
);
|
||||
}
|
||||
if ($bCreate && $bExtensions)
|
||||
@@ -348,12 +310,12 @@ EOF
|
||||
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"><i class=\"fas fa-plus\"></i></div></span>";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
<<<JS
|
||||
if ($('#ajax_{$this->iId}').length == 0)
|
||||
{
|
||||
$('body').append('<div id="ajax_{$this->iId}"></div>');
|
||||
}
|
||||
EOF
|
||||
JS
|
||||
);
|
||||
}
|
||||
$sHTMLValue .= "</div>";
|
||||
@@ -476,27 +438,17 @@ EOF
|
||||
$iMax = 150;
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$oValuesSet->SetSort(false);
|
||||
$aOrder = array('friendlyname'=>true);
|
||||
$oValuesSet->SetOrderBy($aOrder);
|
||||
$oValuesSet->SetSort(true);
|
||||
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
|
||||
$aValues = array();
|
||||
foreach($aValuesContains as $sKey => $sFriendlyName)
|
||||
asort($aValuesContains);
|
||||
$aValues = $aValuesContains;
|
||||
if (sizeof($aValues) < $iMax)
|
||||
{
|
||||
if (!isset($aValues[$sKey]))
|
||||
{
|
||||
$aValues[$sKey] = $sFriendlyName;
|
||||
}
|
||||
}
|
||||
if (sizeof($aValuesContains) < $iMax)
|
||||
{
|
||||
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains,
|
||||
'contains');
|
||||
//asort($aValuesContains);
|
||||
$iSize=sizeof($aValuesContains);
|
||||
foreach($aValuesContains as $sKey => $sFriendlyName)
|
||||
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
|
||||
asort($aValuesContains);
|
||||
$iSize = sizeof($aValuesContains);
|
||||
foreach ($aValuesContains as $sKey => $sFriendlyName)
|
||||
{
|
||||
if (!isset($aValues[$sKey]))
|
||||
{
|
||||
@@ -508,6 +460,11 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (!in_array($sContains, $aValues))
|
||||
{
|
||||
$aValuesEquals = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals');
|
||||
$aValues = array_merge($aValuesEquals, $aValues);
|
||||
}
|
||||
|
||||
switch($sOutputFormat)
|
||||
{
|
||||
|
||||
@@ -3907,10 +3907,14 @@ input:checked + .slider:before {
|
||||
/*for autocomplete*/
|
||||
.ui-autocomplete {
|
||||
padding: 0px;
|
||||
border: 1px solid black;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
z-index: 99999;
|
||||
/*for scrollbar*/
|
||||
max-height: 180px;
|
||||
overflow-y: auto;
|
||||
/* prevent horizontal scrollbar */
|
||||
overflow-x: hidden;
|
||||
|
||||
ul {
|
||||
width: 100%;
|
||||
@@ -3952,3 +3956,11 @@ input:checked + .slider:before {
|
||||
}
|
||||
}
|
||||
}
|
||||
.field_autocomplete
|
||||
{
|
||||
background: #fff url($approot-relative + "images/ac-background.gif?v=" + $version) no-repeat right;
|
||||
border: 1px solid black;
|
||||
&:focus{
|
||||
border: 2px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,99 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
|
||||
// make sure that the form is clean
|
||||
$('#'+this.id+'_btnRemove').prop('disabled',true);
|
||||
$('#'+this.id+'_linksToRemove').val('');
|
||||
}
|
||||
this.AddAutocomplete = function(iMinChars, sWizHelperJSON)
|
||||
{
|
||||
var hasFocus = 0;
|
||||
var cache = {};
|
||||
$('#label_'+me.id).autocomplete({
|
||||
source: function (request, response) {
|
||||
term = request.term.toLowerCase().latinise().replace(/[\u0300-\u036f]/g, "");
|
||||
|
||||
if (term in cache)
|
||||
{
|
||||
response(cache[term]);
|
||||
return;
|
||||
}
|
||||
if (term.indexOf(this.previous) >= 0 && cache[this.previous] != null && cache[this.previous].length < 120)
|
||||
{
|
||||
//we have already all the possibility in cache
|
||||
var data = [];
|
||||
$.each(cache[this.previous], function (key, value) {
|
||||
if (value.label.toLowerCase().latinise().replace(/[\u0300-\u036f]/g, "").indexOf(term) >= 0)
|
||||
{
|
||||
data.push(value);
|
||||
}
|
||||
});
|
||||
cache[term] = data;
|
||||
response(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
$.post({
|
||||
url: GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
|
||||
dataType: "json",
|
||||
data: {
|
||||
q: request.term,
|
||||
operation: 'ac_extkey',
|
||||
sTargetClass: me.sTargetClass,
|
||||
sFilter: me.sFilter,
|
||||
bSearchMode: me.bSearchMode,
|
||||
sOutputFormat: 'json',
|
||||
json: function () {
|
||||
return sWizHelperJSON;
|
||||
}
|
||||
},
|
||||
success: function (data) {
|
||||
cache[term] = data;
|
||||
response(data);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
autoFocus: true,
|
||||
minLength: iMinChars,
|
||||
focus: function (event, ui) {
|
||||
// $('#label_$this->iId').val( ui.item.label );
|
||||
return false;
|
||||
},
|
||||
select: function (event, ui) {
|
||||
$('#'+me.id).val(ui.item.value);
|
||||
$('#label_'+me.id).val(ui.item.label);
|
||||
$('#'+me.id).trigger('validate');
|
||||
$('#'+me.id).trigger('extkeychange');
|
||||
$('#'+me.id).trigger('change');
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.autocomplete("instance")._renderItem = function (ul, item) {
|
||||
var term = this.term.replace("/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi", "\\$1");
|
||||
var val = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)("+term+")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
|
||||
if (item.obsolete == 'yes')
|
||||
{
|
||||
val = val+' <b>old</b>';
|
||||
}
|
||||
return $("<li>")
|
||||
.append(val)
|
||||
.appendTo(ul);
|
||||
};
|
||||
|
||||
$('#label_'+me.id).focus(function(){
|
||||
// track whether the field has focus, we shouldn't process any
|
||||
// results if the field no longer has focus
|
||||
hasFocus++;
|
||||
}).blur(function() {
|
||||
hasFocus = 0;
|
||||
}).click(
|
||||
function() {
|
||||
if(hasFocus++>1)
|
||||
{
|
||||
$('#label_'+me.id).autocomplete( "search");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
this.StopPendingRequest = function()
|
||||
{
|
||||
if (me.ajax_request)
|
||||
@@ -600,5 +691,5 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
|
||||
|
||||
return false; // Do NOT submit the form in case we are called by OnSubmit...
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -543,6 +543,7 @@ function UpdateDuration(iId)
|
||||
}
|
||||
|
||||
// Called when filling an autocomplete field
|
||||
//deprecated in 2.8
|
||||
function OnAutoComplete(id, event, data, formatted)
|
||||
{
|
||||
if (data)
|
||||
|
||||
114
pages/schema.php
114
pages/schema.php
@@ -247,32 +247,31 @@ function DisplayClassesList($oPage, $sContext)
|
||||
$oPage->add("<div id=\"delDataModelSearch\"> <i class=\"fas fa-times-circle\"></i></div>");
|
||||
$oPage->add("<ul id=\"ClassesList\" class=\"treeview fileview\">\n");
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$("#search-model").result(function(e,f,g,h){
|
||||
//$(this).trigger(jQuery.Event('input'));
|
||||
<<<JS
|
||||
function getListClass (request, response,aListe) {
|
||||
var results = $.ui.autocomplete.filter(aListe, request.term);
|
||||
var top_suggestions = $.grep(results, function (n,i) {
|
||||
return (n.label.substr(0, request.term.length).toLowerCase() == request.term.toLowerCase());
|
||||
});
|
||||
response($.merge(top_suggestions,results));
|
||||
}
|
||||
$("#search-model").autocomplete({
|
||||
source: function (request, response) {
|
||||
getListClass (request, response,autocompleteClassLabelAndCode);
|
||||
},
|
||||
select: function( event, ui ) {
|
||||
var preUrl = "?operation=details_class&class=";
|
||||
var sufUrl = "&c[menu]=DataModelMenu";
|
||||
var code = '';
|
||||
switch($("#displaySelector").val()){
|
||||
case 'labelandcode':
|
||||
var id = autocompleteClassLabelAndCode.indexOf(g);
|
||||
if(id != undefined)
|
||||
code = autocompleteClassCode[id];
|
||||
break;
|
||||
case 'label':
|
||||
var id = autocompleteClassLabel.indexOf(g);
|
||||
if(id != undefined)
|
||||
code = autocompleteClassCode[id];
|
||||
break;
|
||||
case 'code':
|
||||
var id = autocompleteClassCode.indexOf(g);
|
||||
if(id != undefined)
|
||||
code = autocompleteClassCode[id];
|
||||
break;
|
||||
}
|
||||
if(code != '')
|
||||
window.location = preUrl + code + sufUrl;
|
||||
});
|
||||
window.location = preUrl + ui.item.value + sufUrl;
|
||||
},
|
||||
focus: true
|
||||
})
|
||||
.autocomplete( "instance" )._renderItem = function( ul, item ) {
|
||||
var term = this.term.replace("/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi", "\\$1");
|
||||
var val = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
|
||||
return $( "<li>" ).append( val ).appendTo( ul );
|
||||
};
|
||||
|
||||
$("#search-model").on('input', function() {
|
||||
var search_result = [];
|
||||
$("#ClassesList").find("li").each(function(){
|
||||
@@ -293,11 +292,14 @@ function DisplayClassesList($oPage, $sContext)
|
||||
$("#search-model").val("");
|
||||
$("#search-model").trigger('input');
|
||||
});
|
||||
EOF
|
||||
JS
|
||||
|
||||
);
|
||||
// Get all the "root" classes for display
|
||||
$aRootClasses = array();
|
||||
$aClassLabelAndCodeAsJSON = [];
|
||||
$aClassLabelAsJSON = array();
|
||||
$aClassCodeAsJSON = array();
|
||||
foreach (MetaModel::GetClasses() as $sClassName)
|
||||
{
|
||||
if (MetaModel::IsRootClass($sClassName))
|
||||
@@ -312,20 +314,19 @@ EOF
|
||||
|
||||
//Fetch classes names for autocomplete purpose
|
||||
// - Encode as JSON to escape quotes and other characters
|
||||
$sClassLabelAndCodeAsJSON = json_encode("$sLabelClassName ($sClassName)");
|
||||
$sClassLabelAsJSON = json_encode($sLabelClassName);
|
||||
$sClassCodeAsJSON = json_encode($sClassName);
|
||||
// - Push to autocomplete
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
autocompleteClassLabelAndCode.push($sClassLabelAndCodeAsJSON);
|
||||
autocompleteClassLabel.push($sClassLabelAsJSON);
|
||||
autocompleteClassCode.push($sClassCodeAsJSON);
|
||||
EOF
|
||||
);
|
||||
array_push ($aClassLabelAndCodeAsJSON, ["value"=>$sClassName,"label"=>"$sLabelClassName ($sClassName)"]);
|
||||
array_push ($aClassLabelAsJSON, ["value"=>$sClassName,"label"=>"$sLabelClassName"]);
|
||||
array_push ($aClassCodeAsJSON, ["value"=>$sClassName,"label"=>"$sClassName"]);
|
||||
}
|
||||
usort($aClassLabelAndCodeAsJSON, "Label_sort");
|
||||
// - Push to autocomplete
|
||||
$oPage->add_script("autocompleteClassLabelAndCode=".json_encode($aClassLabelAndCodeAsJSON)."; console.warn(autocompleteClassLabelAndCode);");
|
||||
$oPage->add_script("autocompleteClassLabel=".json_encode($aClassLabelAsJSON).";");
|
||||
$oPage->add_script("autocompleteClassCode=".json_encode($aClassCodeAsJSON).";");
|
||||
|
||||
// Sort them alphabetically on their display name
|
||||
asort($aRootClasses);
|
||||
asort($aClassLabelAndCodeAsJSON);
|
||||
//usort($aRootClasses,"Label_sort");
|
||||
foreach ($aRootClasses as $sClassName => $sDisplayName)
|
||||
{
|
||||
if (MetaModel::IsRootClass($sClassName))
|
||||
@@ -343,6 +344,9 @@ EOF
|
||||
$oPage->add_ready_script('$("#ClassesList").treeview();');
|
||||
}
|
||||
|
||||
function Label_sort($building_a, $building_b) {
|
||||
return strnatcmp ($building_a["label"], $building_b["label"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for the list of classes related to the given class in a graphical way
|
||||
@@ -1055,19 +1059,28 @@ function DisplayGranularityDisplayer($oPage)
|
||||
$('.attrCode').show();
|
||||
$('.attrLabel').show();
|
||||
$('.parenthesis').show();
|
||||
$("#search-model").autocomplete(autocompleteClassLabelAndCode, {scroll:true, matchContains:true});
|
||||
$("#search-model").autocomplete({
|
||||
source: function (request, response) {
|
||||
getListClass (request, response,autocompleteClassLabelAndCode);
|
||||
}});
|
||||
break;
|
||||
case 'label':
|
||||
$('.attrCode').hide();
|
||||
$('.attrLabel').show();
|
||||
$('.parenthesis').hide();
|
||||
$("#search-model").autocomplete(autocompleteClassLabel, {scroll:true, matchContains:true});
|
||||
$("#search-model").autocomplete({
|
||||
source: function (request, response) {
|
||||
getListClass (request, response,autocompleteClassLabel);
|
||||
}});
|
||||
break;
|
||||
case 'code':
|
||||
$('.attrCode').show();
|
||||
$('.attrLabel').hide();
|
||||
$('.parenthesis').hide();
|
||||
$("#search-model").autocomplete(autocompleteClassCode, {scroll:true, matchContains:true});
|
||||
$("#search-model").autocomplete({
|
||||
source: function (request, response) {
|
||||
getListClass (request, response,autocompleteClassCode);
|
||||
}});
|
||||
break;
|
||||
}
|
||||
SetUserPreference("datamodel_viewer_display_granularity", $('#displaySelector').val(), true);
|
||||
@@ -1144,13 +1157,28 @@ switch ($operation)
|
||||
if ($sClass != '')
|
||||
{
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
<<<JS
|
||||
$('#search-model').val('$sClass');
|
||||
$('#search-model').trigger("input");
|
||||
var search_result = [];
|
||||
$("#ClassesList").find("li").each(function(){
|
||||
if( ! ~$(this).children("a").text().toLowerCase().indexOf('$sClass'.toLowerCase())){
|
||||
$(this).hide();
|
||||
}
|
||||
else{
|
||||
search_result.push($(this));
|
||||
}
|
||||
});
|
||||
search_result.forEach(function(e){
|
||||
e.show();
|
||||
e.find('ul > li').show();
|
||||
e.parents().show();
|
||||
});
|
||||
//$('#search-model').trigger("input");
|
||||
|
||||
EOF
|
||||
JS
|
||||
);
|
||||
DisplayClassDetails($oPage, $sClass, $sContext);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user