2 /*********************************************************************************
3 * SugarCRM Community Edition is a customer relationship management program developed by
4 * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
6 * This program is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Affero General Public License version 3 as published by the
8 * Free Software Foundation with the addition of the following permission added
9 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
18 * You should have received a copy of the GNU Affero General Public License along with
19 * this program; if not, see http://www.gnu.org/licenses or write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26 * The interactive user interfaces in modified source and object code versions
27 * of this program must display Appropriate Legal Notices, as required under
28 * Section 5 of the GNU Affero General Public License version 3.
30 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31 * these Appropriate Legal Notices must retain the display of the "Powered by
32 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33 * technical reasons, the Appropriate Legal Notices must display the words
34 * "Powered by SugarCRM".
35 ********************************************************************************/
39 {if !isset($config.enable_autocomplete) || $config.enable_autocomplete==false}
41 <input type="hidden" id="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}_multiselect"
42 name="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}_multiselect" value="true">
43 {multienum_to_array string={{sugarvar key='value' string=true}} default={{sugarvar key='default' string=true}} assign="values"}
44 <select id="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}"
45 name="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}[]"
46 multiple="true" size='{{$displayParams.size|default:6}}' style="width:150" title='{{$vardef.help}}' tabindex="{{$tabindex}}" {{$displayParams.field}}
47 {{if !empty($displayParams.accesskey)}} accesskey='{{$displayParams.accesskey}}' {{/if}}
48 {{if isset($displayParams.javascript)}}{{$displayParams.javascript}}{{/if}}>
49 {html_options options={{sugarvar key='options' string=true}} selected=$values}
54 {assign var="field_options" value={{sugarvar key='options' string="true"}} }
55 {capture name="field_val"}{{sugarvar key='value'}}{/capture}
56 {assign var="field_val" value=$smarty.capture.field_val}
57 {capture name="ac_key"}{{sugarvar key='name'}}{/capture}
58 {assign var="ac_key" value=$smarty.capture.ac_key}
60 {{if empty($vardef.autocomplete_ajax)}}
61 <input type="hidden" id="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}_multiselect"
62 name="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}_multiselect" value="true">
63 {multienum_to_array string={{sugarvar key='value' string=true}} default={{sugarvar key='default' string=true}} assign="values"}
64 <select style='display:none' id="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}"
65 name="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}[]"
66 multiple="true" size='{{$displayParams.size|default:6}}' style="width:150" title='{{$vardef.help}}' tabindex="{{$tabindex}}" {{$displayParams.field}}
67 {{if !empty($displayParams.accesskey)}} accesskey='{{$displayParams.accesskey}}' {{/if}}
68 {{if isset($displayParams.javascript)}}{{$displayParams.javascript}}{{/if}}>
69 {html_options options={{sugarvar key='options' string=true}} selected=$values}
73 id="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-input"
74 name="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-input"
76 type="text" style="vertical-align: top;">
79 id="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}"
80 name="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}"
81 value="{{sugarvar key='value'}}">
84 id="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-input"
85 name="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-input"
87 type="text" style="vertical-align: top;">
90 <span class="id-ff multiple">
91 <button type="button">
92 <img src="{sugar_getimagepath file="id-ff-down.png"}" id="{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-image">
95 id="btn-clear-{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-input"
97 onclick="SUGAR.clearRelateField(this.form, '{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-input', '{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}};');SUGAR.AutoComplete.{$ac_key}.inputNode.updateHidden()"><img src="{sugar_getimagepath file="id-ff-clear.png"}"></button>
102 SUGAR.AutoComplete.{/literal}{$ac_key}{literal} = [];
105 {{if empty($vardef.autocomplete_ajax)}}
107 YUI().use('datasource', 'datasource-jsonschema', function (Y) {
108 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.ds = new Y.DataSource.Function({
109 source: function (request) {
110 var selectElem = document.getElementById("{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}{literal}");
112 for (i=0;i<selectElem.options.length;i++)
113 if (!(selectElem.options[i].value=='' && selectElem.options[i].innerHTML==''))
114 ret.push({'key':selectElem.options[i].value,'text':selectElem.options[i].innerHTML});
122 // Create a new YUI instance and populate it with the required modules.
123 YUI().use('datasource', 'datasource-jsonschema', function (Y) {
124 // DataSource is available and ready for use.
125 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.ds = new Y.DataSource.Get({
126 source: 'index.php?module=Accounts&action=ajaxautocomplete&to_pdf=1'
128 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.ds.plug(Y.Plugin.DataSourceJSONSchema, {
130 resultListLocator: "option_items",
131 resultFields: ["text", "key"],
140 YUI().use("autocomplete", "autocomplete-filters", "autocomplete-highlighters","node-event-simulate", function (Y) {
143 SUGAR.AutoComplete.{$ac_key}.inputNode = Y.one('#{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-input');
144 SUGAR.AutoComplete.{$ac_key}.inputImage = Y.one('#{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-image');
145 SUGAR.AutoComplete.{$ac_key}.inputHidden = Y.one('#{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}');
147 {{if empty($vardef.autocomplete_ajax)}}
148 SUGAR.AutoComplete.{$ac_key}.minQLen = 0;
149 SUGAR.AutoComplete.{$ac_key}.queryDelay = 0;
150 SUGAR.AutoComplete.{$ac_key}.numOptions = {$field_options|@count};
151 if(SUGAR.AutoComplete.{$ac_key}.numOptions >= 300) {literal}{
153 SUGAR.AutoComplete.{$ac_key}.minQLen = 1;
154 SUGAR.AutoComplete.{$ac_key}.queryDelay = 200;
158 if(SUGAR.AutoComplete.{$ac_key}.numOptions >= 3000) {literal}{
160 SUGAR.AutoComplete.{$ac_key}.minQLen = 1;
161 SUGAR.AutoComplete.{$ac_key}.queryDelay = 500;
166 SUGAR.AutoComplete.{$ac_key}.minQLen = 1;
167 SUGAR.AutoComplete.{$ac_key}.queryDelay = 500;
170 {{if empty($vardef.autocomplete_ajax)}}
172 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.plug(Y.Plugin.AutoComplete, {
173 activateFirstItem: true,
174 allowTrailingDelimiter: true,
176 minQueryLength: SUGAR.AutoComplete.{$ac_key}.minQLen,
177 queryDelay: SUGAR.AutoComplete.{$ac_key}.queryDelay,
181 {{if !empty($vardef.autocomplete_ajax)}}
182 requestTemplate: '&options={{$vardef.autocomplete_options}}&q={literal}{query}{/literal}',
185 source: SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.ds,
187 resultTextLocator: 'text',
188 resultHighlighter: 'phraseMatch',
189 resultFilters: 'phraseMatch',
190 // Chain together a startsWith filter followed by a custom result filter
191 // that only displays tags that haven't already been selected.
192 resultFilters: ['phraseMatch', function (query, results) {
193 // Split the current input value into an array based on comma delimiters.
194 var selected = SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.get('value').split(/\s*,\s*/);
196 // Convert the array into a hash for faster lookups.
197 selected = Y.Array.hash(selected);
199 // Filter out any results that are already selected, then return the
200 // array of filtered results.
201 return Y.Array.filter(results, function (result) {
202 return !selected.hasOwnProperty(result.text);
206 {/literal}{{else}}{literal}
207 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.plug(Y.Plugin.AutoComplete, {
208 activateFirstItem: true,
209 allowTrailingDelimiter: true,
211 minQueryLength: SUGAR.AutoComplete.{$ac_key}.minQLen,
212 queryDelay: SUGAR.AutoComplete.{$ac_key}.queryDelay,
215 requestTemplate: '&options={{$vardef.autocomplete_options}}&q={literal}{query}{/literal}',
217 source: SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.ds,
219 resultTextLocator: 'text',
220 resultHighlighter: 'phraseMatch',
221 resultFilters: 'phraseMatch',
222 // Chain together a startsWith filter followed by a custom result filter
223 // that only displays tags that haven't already been selected.
224 resultFilters: ['phraseMatch', function (query, results) {
225 // Split the current input value into an array based on comma delimiters.
226 var selected = SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.get('value').split(/\s*,\s*/);
228 // Pop the last item off the array, since it represents the current query
229 // and we don't want to filter it out.
232 // Convert the array into a hash for faster lookups.
233 selected = Y.Array.hash(selected);
235 // Filter out any results that are already selected, then return the
236 // array of filtered results.
237 return Y.Array.filter(results, function (result) {
238 return !selected.hasOwnProperty(result.text);
242 {/literal}{{/if}}{literal}
243 if({/literal}SUGAR.AutoComplete.{$ac_key}.minQLen{literal} == 0){
244 // expand the dropdown options upon focus
245 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.on('focus', function () {
246 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.sendRequest('');
250 {{if empty($vardef.autocomplete_ajax)}}
251 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateHidden = function() {
252 var index_array = SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.get('value').split(/\s*,\s*/);
254 var selectElem = document.getElementById("{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}{literal}");
257 for (i=0;i<selectElem.options.length;i++){
258 if (selectElem.options[i].selected)
259 oTable[selectElem.options[i].value] = true;
262 for (i=0;i<selectElem.options.length;i++){
263 selectElem.options[i].selected=false;
268 for (i=0;i<index_array.length;i++){
269 var key = index_array[i];
270 for (c=0;c<selectElem.options.length;c++)
271 if (selectElem.options[c].innerHTML == key){
272 selectElem.options[c].selected=true;
273 nTable[selectElem.options[c].value]=1;
277 //the following two loops check to see if the selected options are different from before.
278 //oTable holds the original select. nTable holds the new one
283 if (!oTable.hasOwnProperty(n))
284 fireEvent = true; //the options are different, fire the event
290 if (!nTable.hasOwnProperty(o))
291 fireEvent=true; //the options are different, fire the event
294 //if the selected options are different from before, fire the 'change' event
296 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputHidden.simulate('change');
300 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateText = function() {
301 //get last option typed into the input widget
302 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.set(SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.get('value'));
303 var index_array = SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.get('value').split(/\s*,\s*/);
304 //create a string ret_string as a comma-delimited list of text from selectElem options.
305 var selectElem = document.getElementById("{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}{literal}");
308 if (selectElem==null || selectElem.options == null)
311 for (i=0;i<selectElem.options.length;i++){
312 if (selectElem.options[i].selected && selectElem.options[i].innerHTML!=''){
313 ret_array.push(selectElem.options[i].innerHTML);
317 //index array - array from input
318 //ret array - array from select
320 var sorted_array = [];
321 var notsorted_array = [];
322 for (i=0;i<index_array.length;i++){
323 for (c=0;c<ret_array.length;c++){
324 if (ret_array[c]==index_array[i]){
325 sorted_array.push(ret_array[c]);
326 ret_array.splice(c,1);
330 ret_string = ret_array.concat(sorted_array).join(', ');
331 if (ret_string.match(/^\s*$/))
336 //update the input widget
337 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.set('value', ret_string);
340 function updateTextOnInterval(){
341 if (document.activeElement != document.getElementById("{/literal}{{if empty($displayParams.idName)}}{{sugarvar key='name'}}{{else}}{{$displayParams.idName}}{{/if}}-input{literal}"))
342 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateText();
343 setTimeout(updateTextOnInterval,100);
346 updateTextOnInterval();
348 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateHidden = function() {
349 var index_array = SUGAR.MultiEnumAutoComplete.getMultiSelectKeysFromValues("{/literal}{{$vardef.autocomplete_options}}{literal}", SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.get('value'));
350 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputHidden.set('value', index_array.join("^,^"));
353 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateText = function() {
354 var index_array = SUGAR.MultiEnumAutoComplete.getMultiSelectValuesFromKeys("{/literal}{{$vardef.autocomplete_options}}{literal}", SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputHidden.get('value'));
355 if(index_array.length < 1){
356 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.set('value', '');
359 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.set('value', index_array.join(", ") + ", ");
362 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateText();
365 {{if empty($vardef.autocomplete_ajax)}}
366 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.on('click', function(e) {
367 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputHidden.simulate('click');
370 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.on('dblclick', function(e) {
371 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputHidden.simulate('dblclick');
374 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.on('focus', function(e) {
375 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputHidden.simulate('focus');
378 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.on('mouseup', function(e) {
379 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputHidden.simulate('mouseup');
382 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.on('mousedown', function(e) {
383 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputHidden.simulate('mousedown');
386 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.on('blur', function(e) {
387 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputHidden.simulate('blur');
391 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.on('blur', function () {
392 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateHidden();
393 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateText();
396 // when they click on the arrow image, toggle the visibility of the options
397 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputImage.on('click', function () {
398 if({/literal}SUGAR.AutoComplete.{$ac_key}.minQLen{literal} == 0){
399 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.sendRequest('');
400 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.show();
403 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.focus();
407 if({/literal}SUGAR.AutoComplete.{$ac_key}.minQLen{literal} == 0){
408 // After a tag is selected, send an empty query to update the list of tags.
409 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.after('select', function () {
410 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.sendRequest('');
411 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.show();
412 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateHidden();
413 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateText();
416 // After a tag is selected, send an empty query to update the list of tags.
417 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.ac.after('select', function () {
418 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateHidden();
419 SUGAR.AutoComplete.{/literal}{$ac_key}{literal}.inputNode.updateText();