diff --git a/application/ui.extkeywidget.class.inc.php b/application/ui.extkeywidget.class.inc.php index ad396c032..6a40e2052 100644 --- a/application/ui.extkeywidget.class.inc.php +++ b/application/ui.extkeywidget.class.inc.php @@ -239,13 +239,13 @@ EOF $oPage->add_ready_script("$('.multiselect').multiselect($sJSOptions);"); } $oPage->add_ready_script( -<<iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch); oACWidget_{$this->iId}.emptyHtml = "

$sMessage

"; $('#$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 .= "iId\" value=\"$sDisplayValue\"/>"; + $sHTMLValue .= "iId\" value=\"$sDisplayValue\"/>"; $sHTMLValue .= "
iId}\" onClick=\"oACWidget_{$this->iId}.Search();\">
"; // 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( <<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"), "$1"); - if (item.obsolete == 'yes'){ - val = val + ' old'; - } - return $( "
  • " ) - .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 = "

    $sMessage

    "; + oACWidget_{$this->iId}.AddAutocomplete($iMinChars, $sWizHelperJSON); if ($('#ac_dlg_{$this->iId}').length == 0) { $('body').append('
    '); @@ -334,12 +296,12 @@ JS { $sHTMLValue .= "
    iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\">
    "; $oPage->add_ready_script( -<<iId}').length == 0) { $('body').append('
    '); } -EOF +JS ); } if ($bCreate && $bExtensions) @@ -348,12 +310,12 @@ EOF $sHTMLValue .= "
    iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\">
    "; $oPage->add_ready_script( -<<iId}').length == 0) { $('body').append('
    '); } -EOF +JS ); } $sHTMLValue .= ""; @@ -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) { diff --git a/css/light-grey.scss b/css/light-grey.scss index c8e20e629..2bb5025b1 100644 --- a/css/light-grey.scss +++ b/css/light-grey.scss @@ -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; + } +} diff --git a/js/extkeywidget.js b/js/extkeywidget.js index 9d58634bf..8a258437f 100644 --- a/js/extkeywidget.js +++ b/js/extkeywidget.js @@ -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"), "$1"); + if (item.obsolete == 'yes') + { + val = val+' old'; + } + return $("
  • ") + .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... }; - + } \ No newline at end of file diff --git a/js/forms-json-utils.js b/js/forms-json-utils.js index c1eb96c36..4e5e39bd0 100644 --- a/js/forms-json-utils.js +++ b/js/forms-json-utils.js @@ -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) diff --git a/pages/schema.php b/pages/schema.php index 0254e86af..d608de8d9 100644 --- a/pages/schema.php +++ b/pages/schema.php @@ -247,32 +247,31 @@ function DisplayClassesList($oPage, $sContext) $oPage->add("
    "); $oPage->add("
      \n"); $oPage->add_ready_script( - <<]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); + return $( "
    • " ).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( - <<$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( - << li').show(); + e.parents().show(); +}); +//$('#search-model').trigger("input"); -EOF +JS ); DisplayClassDetails($oPage, $sClass, $sContext); + break; } default: