PDA

View Full Version : [3.x] Ext.ux.form.SuperBoxSelect



Pages : [1] 2

galdaka
23 May 2009, 6:34 AM
Danh200 create this awesome extension.


Live example: http://technomedia.co.uk/SuperBoxSelect/examples3.html

Download: http://www.extjs.com/forum/attachment.php?attachmentid=18327&d=1263783379

Thread: http://extjs.com/forum/showthread.php?p=332654#post332654

Greetings,

MD
23 May 2009, 7:14 AM
Truly awesome! Nice work, Danh200!

danh2000
24 May 2009, 2:00 AM
@galdaka Thanks for taking the time to post here, and for your kind words :)

@MD thanks for your feedback.

wojan
24 May 2009, 6:31 AM
Nice extension!

FYI - I had an issue in IE 8 where when you closed one of the selected items, it was hidden but not actually removed from the dom.

danh2000
24 May 2009, 2:45 PM
Nice extension!

FYI - I had an issue in IE 8 where when you closed one of the selected items, it was hidden but not actually removed from the dom.

Thanks for the report.

It's an issue when used with Ext 3 (2.* works OK) - I'll look into the reason and apply a fix.

Thanks again.

crysfel
25 May 2009, 10:21 AM
thanks dude!! :D

i like this extension!! =D>

jay@moduscreate.com
27 May 2009, 9:05 AM
this owns.

danh2000
28 May 2009, 11:23 PM
@crysfel, @jgarcia@tdg-i.com,

Thanks Guys.

randomY
29 May 2009, 10:56 AM
Really nice stuff.

(but the js code needs to be run through jslint)

stever
29 May 2009, 12:16 PM
I think the code find and alter the containing form could be done differently. I was thinking of using the HtmlEditor as an example. It keeps a form field hidden and syncs the content back to the hidden field on keypress and other things that change the content. I think this might work here as well. The getRawValue currently doesn't work, and beforeblur calls it, causing all sorts of issues. Not sure why it doesn't in your examples though...

Anyhow, nice extension! I can help with a sync system if you like...

DigitalSkyline
29 May 2009, 1:59 PM
The best version yet. Great!

danh2000
3 Jun 2009, 12:36 AM
Nice extension!

FYI - I had an issue in IE 8 where when you closed one of the selected items, it was hidden but not actually removed from the dom.


Fixed with the 3.0 RC2 release :)


The best version yet. Great!

Thanks :)

Scorpie
4 Jun 2009, 10:50 PM
Pure ownage.

supercharge2
5 Jun 2009, 6:37 AM
I am trying to use the SuperBoxSelect in a wizard using a card layout. Since going to 3.0rc2 (worked fine in rc1.1), whenever I click away from the box the contents clear. I put the box into various other test scenerios such as a toolbar and the same thing occurs. It seems fine in your example renderd to a div in the page. As one post suggested it may be that the code is using the private doForce() method which has been changed to beforeBlur() in rc2. Any chance you could check on this?

Animal
6 Jun 2009, 9:42 AM
Beautiful work! Should really be on the ExtJs main site as an example. And should be part of the UX download package!

danh2000
8 Jun 2009, 12:56 AM
I am trying to use the SuperBoxSelect in a wizard using a card layout. Since going to 3.0rc2 (worked fine in rc1.1), whenever I click away from the box the contents clear. I put the box into various other test scenerios such as a toolbar and the same thing occurs. It seems fine in your example renderd to a div in the page. As one post suggested it may be that the code is using the private doForce() method which has been changed to beforeBlur() in rc2. Any chance you could check on this?

Do you have an example page that I could take a look at please? Someone else reported an issue with blur that I haven't been able to re-produce. I'd appreciate being able to debug the issue.


Beautiful work! Should really be on the ExtJs main site as an example. And should be part of the UX download package!

Thanks Animal, high praise indeed from you - I've always been impressed with your own work. There are still a few issues and there's some refactoring to do but I'd be happy for the code be used as a demo. I'll try to get a move on with the updates!

I've got another little component I'll release v. soon too!

supercharge2
8 Jun 2009, 6:29 AM
I dropped this into a grid toolbar as one of my tests and it had the problem:


{
xtype: 'superboxselect',
fieldLabel: 'Products',
width: 300,
hideOnSelect: false,
maxHeight: 200,
store: auxillaryListStore,
triggerAction: 'all',
valueField: 'AuxillaryId',
displayField: 'AuxillaryDescription',
hiddenName: 'AuxillaryId',
mode: 'local'
}

Then the grid into a layout and then viewport


var layout = new Ext.Panel({
title: 'Employee Salary by Month',
layout: 'border',
layoutConfig: {
columns: 1
},
width: 600,
height: 600,
items: [chart, grid]
});
new Ext.Viewport({
layout: 'fit',
items: [layout]
});
//layout.render(Ext.getBody());

danh2000
8 Jun 2009, 4:45 PM
I dropped this into a grid toolbar as one of my tests and it had the problem:


{
xtype: 'superboxselect',
fieldLabel: 'Products',
width: 300,
hideOnSelect: false,
maxHeight: 200,
store: auxillaryListStore,
triggerAction: 'all',
valueField: 'AuxillaryId',
displayField: 'AuxillaryDescription',
hiddenName: 'AuxillaryId',
mode: 'local'
}

Then the grid into a layout and then viewport


var layout = new Ext.Panel({
title: 'Employee Salary by Month',
layout: 'border',
layoutConfig: {
columns: 1
},
width: 600,
height: 600,
items: [chart, grid]
});
new Ext.Viewport({
layout: 'fit',
items: [layout]
});
//layout.render(Ext.getBody());

Do you have this code online anywhere so that I can debug?

Thanks

supercharge2
9 Jun 2009, 6:51 AM
You can use one of your own examples. I took from your example page and put a quick test page together. I appreciate you looking into this.



<html>
<head><title>
Test Page
</title>
<link rel="stylesheet" type="text/css" href="ext-3.0-rc2/resources/css/ext-all.css" />
<script type="text/javascript" src="ext-3.0-rc2/adapter/ext/ext-base.js">
</script>
<script type="text/javascript" src="ext-3.0-rc2/ext-all.js">
</script>
<link href="css/superboxselect.css" rel="stylesheet" type="text/css" />
<script src="jscripts/SuperBoxSelect.js" type="text/javascript">
</script>
<script type="text/javascript">
Ext.onReady(function() {
var countryData = [['AU', 'Australia', 'x-flag-au', 'font-style:italic'], ['AT', 'Austria', 'x-flag-at', ''], ['CA', 'Canada', 'x-flag-ca', ''], ['FR', 'France', 'x-flag-fr', ''], ['IT', 'Italy', 'x-flag-it', ''], ['JP', 'Japan', 'x-flag-jp', ''], ['NZ', 'New Zealand', 'x-flag-nz', ''], ['US', 'USA', 'x-flag-us', '']];
var countryStore = new Ext.data.SimpleStore({
fields: ['code', 'name', 'cls', 'style'],
data: countryData,
sortInfo: {
field: 'name',
direction: 'ASC'
}
});
new Ext.Viewport({
layout: 'border',
items: [
new Ext.TabPanel({
region: 'center',
id: 'MainTab',
activeTab: 0,
enableTabScroll: true,
animScroll: true,
layoutOnTabChange: true,
items: [
{
title: 'Home',
id: 'tab-home',
items: [{
allowBlank: false,
id: 'selector3',
xtype: 'superboxselect',
fieldLabel: 'Countries',
resizable: true,
name: 'countries',
store: countryStore,
mode: 'local',
displayField: 'name',
valueField: 'code',
stackItems: true
}]
}]
})
]
});
});
</script>
</head>
<body>
</body>
</html>

supercharge2
9 Jun 2009, 7:38 AM
Bashev posted a workaround for a similar extenstion that solved the issue:



Ext.override(Ext.ux.form.SuperBoxSelect, {
beforeBlur: Ext.emptyFn
})

danh2000
9 Jun 2009, 4:13 PM
@supercharge2,

Thanks - this is the first time I've been able to see the error occur :)

I'll see what's in SVN now - they may have fixed it since RC2.

Thanks again.

Dan

ryzmen
10 Jun 2009, 10:04 AM
Bashev posted a workaround for a similar extenstion that solved the issue:



Ext.override(Ext.ux.form.SuperBoxSelect, {
beforeBlur: Ext.emptyFn
})


This workaround works perfectly ;)

I published example where the issue occured: http://million12.com/temp/superboxselect/application.html (rc2)

galdaka
24 Jun 2009, 8:48 AM
HI,

I attach the cleaned version: http://extjs.com/forum/showthread.php?p=348003#post348003

With a xtheme-gray-extend support. Your example included.

Review your first post danh200 ;)

Greetings,

danh2000
24 Jun 2009, 2:35 PM
HI,

I attach the cleaned version: http://extjs.com/forum/showthread.php?p=348003#post348003

With a xtheme-gray-extend support. Your example included.

Review your first post danh200 ;)

Greetings,

Please DO NOT use or distribute that version - various things have been broken depending on the codebase you use, it seems a couple of things are broken.

Sorry, ignore me - it seems there are a couple of cosmetic problems only and a couple of things which I've fixed in a later unpublished version.

I will definitely be merging the changes and tidying that Marc kindly made, but they need testing and fixing up first and integrating with the changes I have already made.

I'd appreciate it if you would allow me to distribute the changes in a controlled manner.

I should have controlled the versions of the code better - the version in my demo's and the version attached in this forum is not the most recent - I just need to get everything tested and sync'd - I've been really busy, but will do my best to release an update in the next week.


Thanks.

danh2000
27 Jun 2009, 3:22 PM
Dunno whether I should be maintaining 2 posts really, but I've updated this component.

I've updated the main post here (http://extjs.com/forum/showthread.php?p=332654)

Thomas Triplet
13 Jul 2009, 9:58 AM
I've got a problem with setting up default values in the box

Using the examples, I came up with this piece of code. I setup the default value using:
value: 'something'

But it doesn't seems to have any effect :-|


comboSpecies_store = new Ext.data.JsonStore( {
storeId: 'species_store',
autoDestroy: true,
root: 'species',
idProperty: 'code',
url: 'php/jsoner/species.php',
fields: [ 'code', 'name']
});
comboSpecies_store.load();

comboSpecies = new Ext.ux.form.SuperBoxSelect( {
allowBlank: true,
id: 'comboSpecies',
fieldLabel: 'comboSpecies',
name: 'comboSpecies',
resizable: false,
store: comboSpecies_store,
mode: 'local',
displayField: 'code',
stackItems: true,
displayFieldTpl: '{name}',
valueField: 'code',
value: 'ALL',
navigateItemsWithTab: false
});


The problem seems to come from the remote data source, because using a SimpleStore, that works just fine...

EDIT: I solved the problem. Just added:

comboSpecies_store.on('load', function() {comboSpecies.setValue('ALL');});

danh2000
13 Jul 2009, 3:08 PM
The problem seems to come from the remote data source, because using a SimpleStore, that works just fine...

EDIT: I solved the problem...

Hi Thomas,

The component does not currently work fully with a remote store, but I hope to remedy this in the next version.

wki01
24 Jul 2009, 8:19 AM
@galdaka (http://extjs.com/forum/member.php?u=101)


Maybe you can help me.
It seems not work with Domino
He has a strange behavior.

See attached image.

thanks

danh2000
24 Jul 2009, 3:46 PM
@galdaka (http://extjs.com/forum/member.php?u=101)


Maybe you can help me.
It seems not work with Domino
He has a strange behavior.

See attached image.

thanks


By 'strange behaviour' do you mean the close button images won't display or something else?

What about the 'clear' and 'expand' images?

Have you checked the path to the image files?

galdaka
25 Jul 2009, 12:39 AM
@galdaka (http://extjs.com/forum/member.php?u=101)


Maybe you can help me.
It seems not work with Domino
He has a strange behavior.


See attached image.


thanks


You have to adapt the component to Domino. I use a plugin. There is not any bug in original component. You can view in action here: http://www.extjs.com/forum/showthread.php?p=351680#post351680

Contact me if you want profesional support.

Greetings,

danh2000
25 Jul 2009, 12:52 AM
You have to adapt the component to Domino. I use a plugin.
...


I'm glad you know - thanks galdaka.

wki01
27 Jul 2009, 7:15 AM
I do not know why, but if I add this piece of code also works for lotus domino.



afterRender:function(arguments) {
Ext.ux.form.SuperBoxSelect.superclass.afterRender.call(this, arguments);
if (!Ext.isEmpty(this.value)) {
var v = this.value
this.clearValue()
this.setValue(v)
}
},

boonkerz
29 Jul 2009, 12:42 PM
Hello

thx for Component.

But i have Problems with submit Values:


articlegroups 21
articlegroups 26
articlegroups 13
articlegroups 27


Can i rewrite it to articlegroups 21,26,13,27 ?

danh2000
29 Jul 2009, 1:10 PM
Hello

thx for Component.

But i have Problems with submit Values:


articlegroups 21
articlegroups 26
articlegroups 13
articlegroups 27


Can i rewrite it to articlegroups 21,26,13,27 ?

Are you asking how to reformat your data in php?

It's unclear what you have at the moment - what is articlegroups? Is it plain text? Is it an array?

boonkerz
29 Jul 2009, 1:13 PM
Ok

When i submit the form, firebug display this post vars:
articlegroups 21
articlegroups 26
articlegroups 13
articlegroups 27

But this can i not access in php
only:
articlegroups[] 24
articlegroups[] 26

or articlegroups: 24,26 this is what i prefer.

articlegroups is my name of the superboxselect :)

danh2000
29 Jul 2009, 1:24 PM
So you already know that php requires square brackets to access array data. Name the component articlegroups[]
If you want a comma delimited list simply join the array.

boonkerz
29 Jul 2009, 1:43 PM
Hello

Yes i know this with [] but i need to fix this in superboxselect.js to join this :)

I have search in js but i have not found where i can do this join.

sorry for my bad english

danh2000
29 Jul 2009, 1:56 PM
Why do you have to join the data on the client?

If you MUST do this on the client you will need to rewrite the component or intercept the form submission and reformat the data there.

Each selected item is an individual hidden field named as per the component.

As I said though, I think this is the wrong way to go about it, instead just join the data in PHP on the server:


$articleGroups = join(',' $_POST['articlegroups[]']);

boonkerz
29 Jul 2009, 2:16 PM
Hello,

I have set the name to articlegroups[] :D then i can use this with php

this simple note :)

lossendae
26 Aug 2009, 10:26 AM
Hello,

Is it possible to add new value on the fly with this code.

danh2000
26 Aug 2009, 2:26 PM
Hello,

Is it possible to add new value on the fly with this code.

I'm not sure I understand your question - do you mean add values to the component via code rather than user interaction? if this is the case, then Yes absolutely - this is demonstrated on the examples.

danh2000
2 Sep 2009, 8:57 PM
Updated the first post here (http://extjs.com/forum/showthread.php?p=332654) with a new version containing:

A few minor bug fixes and a maintenance release to make compatible with Ext 3.0.1

danh2000
6 Sep 2009, 11:23 PM
UPDATE 07/09/2009

*NEW* Remote compatability - you are no longer limited to a local store!
Multiple bug fixes (many browser version specific).

Updated the First Post Here (http://extjs.com/forum/showthread.php?p=332654) and uploaded new attachment (http://www.extjs.com/forum/attachment.php?attachmentid=16094).
----------------------------------------------------------------------

Please see the new example page (http://www.technomedia.co.uk/SuperBoxSelect/examplesRemote.html) for 2 remote store demo's.

Note. There are still a few small refactorings that I would like to do, but I'm primarily concerned with getting bug reports and some feedback on the remote functionality as I work towards releasing the current features as a stable version 1 release.

Thanks,

Dan

prometheus
7 Sep 2009, 3:40 AM
Hello to developer,
Demo looks very good as previous comments sad many times before, i`m just looking it but have a little problem. If you use button BasicForm.getValues() you could see the resulted parameters which are multiple values for one variable. If I should have an advice, please create another parameter generating solution which generates the parameter list with leading "[]" after all items, because the currently available list in PHP interprets as a single value rather than an array of values.

For example:
Current version returned with: states=AL&states=AR&states=FL
My advice is: states[]=AL&states[]=AR&states[]=FL

Thank you for your time, sorry for my english,

danh2000
7 Sep 2009, 11:33 AM
Prometheus, when developing with PHP, you must append square brackets to the name of form fields being submitted as Arrays - this component is no different:

Name your component states[]

danh2000
21 Sep 2009, 8:56 PM
I've attached a new version to the first post here (http://extjs.com/forum/showthread.php?p=332654).

Summary of changes:

Added 'clear' event.
Added a 'record' param to the addItem and removeItem events.
Added 'supressClearValueRemoveEvents' config.
Added fix to escape internal regexp.
Added new 'supressEvents' param to clearValue method.
Removed docs for removeAllItems method - clearValue should be used instead.
Added addItems method.
Fixed scope issue in getValueEx method.
Fix to provide an originalValue (and fix reset) when remote store used.

danh2000
24 Sep 2009, 12:12 AM
I've updated the first post here (http://extjs.com/forum/showthread.php?p=332654) with a new version containing the following changes:

Fix to work around Ext 3.0.0 bug - store load event not providing params to callback.
Fix to allow store to be provided as an xtype.
Fix to clean up after prevention of 'remove' events during clearValue.

danh2000
26 Sep 2009, 11:18 PM
Update:

Fix to prevent submission of empty value when doing a raw form submit.
Fix for IE8 error during transform.
Inclusion of superboxselect-gray-extend.css from the extended gray theme (https://www.extjs.com/forum/showthread.php?t=65694) (Thanks Galdaka (http://www.extjs.com/forum/member.php?u=101))


Ext 2 Example:

http://technomedia.co.uk/SuperBoxSelect/examples.html

Ext 3 Example:
http://technomedia.co.uk/SuperBoxSelect/examples3.html

Ext 3 Extended Gray Theme Example:
http://www.technomedia.co.uk/SuperBoxSelect/examples3gray.html

Remote Store Example:
http://technomedia.co.uk/SuperBoxSelect/examplesRemote.html

Download
16454

mathec
27 Sep 2009, 8:00 PM
Hi All,

I'm trying to use the SuperBoxSelect as a grid cell editor. Besides the basic data transformations, does anyone know what needs to be done (general approach) for getting the editor to render properly in the cell? For example, currently it will display but selected items aren't visible in the editor and once you've used it once, the dropdown button disappears. It does allow you to select values however. But you just don't see them until you're done editing.

I'm sure a little customization is required of the control but I'm not sure even where to start. Any guidance greatly appreciated!

danh2000
27 Sep 2009, 9:11 PM
Hi All,

I'm trying to use the SuperBoxSelect as a grid cell editor. Besides the basic data transformations, does anyone know what needs to be done (general approach) for getting the editor to render properly in the cell? For example, currently it will display but selected items aren't visible in the editor and once you've used it once, the dropdown button disappears. It does allow you to select values however. But you just don't see them until you're done editing.

I'm sure a little customization is required of the control but I'm not sure even where to start. Any guidance greatly appreciated!

Sorry mathec, it's not something I've done.

I think it would require more than a little customization too - the html structure of the component and it's items would need some re-structuring for this scenario, and the functionality (keyboard handlers etc) is likely to conflict too.

Maybe for a future version.

Gonfi
7 Oct 2009, 9:58 AM
hello,

currently the change event only fires when ADDING items to the selection, not when removing single items or all at once.

i have hacked in a couple lines to get those events too. it's dirty, clicking the x button on the right fires the change for every selected item, plus once. but at least it fires. x-removing single items fires correctly. maybe you want to take it from here ;-)

clearValue : function(supressRemoveEvent){
var hadItems = this.items.getCount() > 0;
var oldVal = (hadItems) ? this.getValue() : "";

Ext.ux.form.SuperBoxSelect.superclass.clearValue.call(this);
this.preventMultipleRemoveEvents = supressRemoveEvent || this.supressClearValueRemoveEvents || false;
this.removeAllItems();
this.fireEvent('clear',this);

if (hadItems) {
this.fireEvent('change',this, "", oldVal);
}

return this;
},

around line 1121:

'remove': function(item){
var valueBefore = this.getValue();

if(this.fireEvent('beforeremoveitem',this,item.value) === false){
return;
}
this.items.removeKey(item.key);
if(this.removeValuesFromStore){
if(this.usedRecords.containsKey(item.value)){
this.store.add(this.usedRecords.get(item.value));
this.usedRecords.removeKey(item.value);
this.sortStore();
if(this.view){
this.view.render();
}
}
}
if(!this.preventMultipleRemoveEvents){
this.fireEvent.defer(250,this,['removeitem',this,item.value, this.findInStore(item.value)]);

this.fireEvent('change',this, this.getValue(), valueBefore);

}
this.preventMultipleRemoveEvents = false;
},


also, around line 1148, this caused a problem in the new ext3 version, and is fine when removed:

destroy: function(){
this.collapse();
//this.autoSize().manageClearBtn().validateValue(); //causes problem
},

it would be nice if there was a rendering that uses standard field height.

thanks for the component,
greetings

danh2000
7 Oct 2009, 7:53 PM
@Gonfi,

Form fields are not supposed to fire the change event until just before blur. If it fires when adding items, I'd say that's the bug, not that it doesn't fire when removing.

I'm very busy at the moment, but will look at the events and aim to get a new release up within a week, however I'll be following suit from other form fields so you won't get the event fired in the places you've added it.

Thanks.

Dan

jackjia
13 Oct 2009, 9:39 PM
Wow, very nice extension. Thanks for sharing.

GraemeBryce
15 Oct 2009, 3:58 AM
This is indeed a great control.

One thing I have spent hours trying to figure out (ashamed to admit it really is hours!) is why when all the values are removed does the field no longer exist in a form post or a request the the basic form getValues.

This means i am unable to clear the value at the server unless I detect the field is acually missing.

I want to just post the field with no value or even '' but no luck.

can anyone suggest a way? or even why it is doing it.

danh2000
19 Oct 2009, 6:31 PM
@jackjia Thanks.

@GraemeBryce Thanks. I've just implemented a forceFormValue option - when set to true, you will get an empty string in the the parent BasicForm getValues method or when you manually submit the raw dom form.

I'll release the next version here in the next 24 hours.

Dan

GraemeBryce
19 Oct 2009, 11:41 PM
Great.

I look forward to seeing how you did it. Many thanks for the continued support of this extension.

danh2000
20 Oct 2009, 10:47 PM
Great.

I look forward to seeing how you did it. Many thanks for the continued support of this extension.

An explanation for you, so you don't have to hunt:

The values come from hidden inputs - 1 for each selected item, they don't come from the input el (where you type for autocomplete). With this in mind....

-I was removing the name attribute from the input el, so that it was not included in the raw form submission. Now, if you use the new forceFormValue config, I manage the name attribute of the input el, so it does get included if no items (zero items) have been selected.

-I have always intercepted the getValues method of the parent BasicForm - I still intercept this method, but I now inject a blank string into the return values if no items (zero items) have been selected.

Regards,

Dan

danh2000
20 Oct 2009, 11:07 PM
I've update the first post in this linked thread (http://www.extjs.com/forum/showthread.php?p=332654#post332654) with a new version containing:

-Fix to prevent the component receiving focus when reset is called.
-Removed unnecessary usage of trim.
-Fixed bug in setValueEx method.
-Added a new forceFormValue config to force form values (submission or BasicForm.getValues) even when no items selected

The amount of functionality X Ext Versions X Browser Combinations are quite an overhead to test, so please go easy on me if I've broken anything and you find errors - I'll do what I can to resolve any issues in a timely manner.

Regards,

Dan

Animal
21 Oct 2009, 10:44 AM
Small bug. When disabling the overall widget, the items are disabled, but their KeyMaps are not disabled, so you can still interact with the widget - it's not fully disabled.

Ext.ux.form.SuperBoxSelectItem needs



setupKeyMap : function(){
this.keyMap = new Ext.KeyMap(this.lnk, [
{
key: [
Ext.EventObject.BACKSPACE,
Ext.EventObject.DELETE,
Ext.EventObject.SPACE
],
fn: this.preDestroy,
scope: this
}, {
key: [
Ext.EventObject.RIGHT,
Ext.EventObject.DOWN
],
fn: function(){
this.moveFocus('right');
},
scope: this
},
{
key: [Ext.EventObject.LEFT,Ext.EventObject.UP],
fn: function(){
this.moveFocus('left');
},
scope: this
},
{
key: [Ext.EventObject.HOME],
fn: function(){
var l = this.owner.items.get(0).el.focus();
if(l){
l.el.focus();
}
},
scope: this
},
{
key: [Ext.EventObject.END],
fn: function(){
this.owner.el.focus();
},
scope: this
},
{
key: Ext.EventObject.ENTER,
fn: function(){
}
}
]);
this.keyMap.stopEvent = true;
},
onEnable: function() {
Ext.ux.form.SuperBoxSelectItem.superclass.onEnable.apply(this, arguments);
this.keyMap.enable();
},
onDisable: function() {
Ext.ux.form.SuperBoxSelectItem.superclass.onDisable.apply(this, arguments);
this.keyMap.disable();
},

danh2000
21 Oct 2009, 7:39 PM
Thanks Animal - fix included for next release :)

Animal
27 Oct 2009, 11:54 AM
Hi Dan,

can I add a small Feature Request that I've just added here at a client's request?

Right now, to terminate one item, and add the typed characters, the ENTER key is explicitly listened for in the onKeyDownHandler method.

I'd like to request that this key be configurable:



/**
* @cfg {Number} itemDelimiterKey The key code which terminates keying in of individual items, and adds the current
* item to the list. Defaults to the ENTER key.
*/
itemDelimiterKey: Ext.EventObject.ENTER,


Then just the changes in red:



onKeyUp : function(e) {
if (this.editable !== false && !e.isSpecialKey() && e.getKey() !== this.itemDelimiterKey && (!e.hasModifier() || e.shiftKey)) {
this.lastKey = e.getKey();
this.dqTask.delay(this.queryDelay);
}
},
onKeyDownHandler : function(e,t) {
var toDestroy,nextFocus,idx;
if ((e.getKey() === e.DELETE) && this.currentFocus){
e.stopEvent();
toDestroy = this.currentFocus;
this.on('expand',function(){this.collapse();},this,{single: true});
idx = this.items.indexOfKey(this.currentFocus.key);

this.clearCurrentFocus();

if(idx < (this.items.getCount() -1)){
nextFocus = this.items.itemAt(idx+1);
}

toDestroy.preDestroy(true);
if(nextFocus){
(function(){
nextFocus.onLnkFocus();
this.currentFocus = nextFocus;
}).defer(200,this);
}

return true;
}

var val = this.el.dom.value, it, ctrl = e.ctrlKey;
if(e.getKey() === this.itemDelimiterKey){


My client wants me to configure it with



itemDelimiterKey: Ext.EventObject.SPACE,

scishop
28 Oct 2009, 11:40 AM
I've placed the SuperBoxSelect in a a grid using a GridEditor. It works except that the entry field does not expand vertically.

Is this a bug, or, more likely, I'm I missing something obvious?

danh2000
29 Oct 2009, 5:14 PM
I've placed the SuperBoxSelect in a a grid using a GridEditor. It works except that the entry field does not expand vertically.

Is this a bug, or, more likely, I'm I missing something obvious?

Sorry, I'm not supporting using it as a grid editor at this stage - it's been requested a couple of times though, so maybe a future release.


Hi Dan,

can I add a small Feature Request that I've just added here at a client's request?
...


Absolutely - I'll add that in - thanks.

radtad
3 Nov 2009, 2:09 PM
Hi Dan,

can I add a small Feature Request that I've just added here at a client's request?



itemDelimiterKey: Ext.EventObject.SPACE,


To expand on this, I think this should be a collection of items. I.e. SPACE, ENTER, semicolon, comma, possibly even an onBlur, etc. That way I can configure it so the end user has multiple ways of having it add and item box.

radtad
3 Nov 2009, 2:17 PM
Possible bug with allowing new data. If I start typing in an empty superbox field and tab out it does not remove the raw text or make it a box. However, if I have at least one box item in the superbox field and start typing and tab out it removes the raw text.

This will automatically wipe the raw text each time no matter what getCount() returns.



applyEmptyText : function(){
this.setRawValue('');
if(this.items.getCount() > 0){
this.el.removeClass(this.emptyClass);
// this.setRawValue(''); the line should go at the top
return this;
}
if(this.rendered && this.emptyText && this.getRawValue().length < 1){
this.setRawValue(this.emptyText);
this.el.addClass(this.emptyClass);
}
return this;
},
It would be awesome if it was an option to make an item box or wipe the raw text onBlur, but this fix works just fine as well.

abraxxa
16 Nov 2009, 11:38 AM
There is a typo in the code:
queryValuesInidicator instead of queryValuesIndicator

Edit: and I've found a bug related to loading elements when loading the form aka valuesQuery:
line 1392 here should be changed from

if(forceAll === true || (q.length >= this.minChars)){
to

if(forceAll === true || (q.length >= this.minChars) || valuesQuery === true){

so that the query is always executed when searching by primary key.

radtad
18 Nov 2009, 6:28 PM
There is a big bug when allowing new data to be added. You can reproduce this on your states Example 4. If you type 'c' and let the menu expand it returns the following:

California
Colorado
Connecticut

This is just what you would expect. Now keep typing California by entering 'ali' etc. Then backspace to just 'c'. You don't get the 3 results you initially received when just typing 'c'. It only returns California still.

danh2000
18 Nov 2009, 6:36 PM
Sorry for my lack of responses - I've just moved house to an island that's only accessible by boat. This has been a massive undertaking (especially with a 7 month old baby).

I'll get round to answering questions and fixing bugs when I catch up with my paid jobs.

Thanks,

Dan

radtad
18 Nov 2009, 7:02 PM
There is a big bug when allowing new data to be added. You can reproduce this on your states Example 4. If you type 'c' and let the menu expand it returns the following:

California
Colorado
Connecticut

This is just what you would expect. Now keep typing California by entering 'ali' etc. Then backspace to just 'c'. You don't get the 3 results you initially received when just typing 'c'. It only returns California still.

This is the code not allowing the query to go through since BACKSPACE is a special key.



onKeyUp : function(e) {
if (this.editable !== false && !e.isSpecialKey() && (!e.hasModifier() || e.shiftKey)) {
this.lastKey = e.getKey();
this.dqTask.delay(this.queryDelay);
}
},
I doubt this is the proper fix, but it works...


if (this.editable !== false && (!e.isSpecialKey() || e.getKey() === e.BACKSPACE) && (!e.hasModifier() || e.shiftKey))
...and no biggie on not responding for a while Dan ;)

syscobra
21 Nov 2009, 1:10 PM
Hello All

First of all Thanks Dan for this very cool extension.

I was using it in a job i was making and my boss wanted to use some normal style like the present in other Ext Widgets, So i modified the css and came out with something more close to the ext widgets style, so i am sharing it now with the community.

Here is an screenshoot:
http://playground.ideashs.com/superboxselect/superboxselect.jpg
Here is the dan Examples using this css:
http://playground.ideashs.com/superboxselect/
And here is the css with the image files required:
http://playground.ideashs.com/superboxselect/superboxselectsmallcss.zip

I hope someone find it useful. :D

Cya around. B)

galdaka
22 Nov 2009, 11:08 AM
Hello All

First of all Thanks Dan for this very cool extension.

I was using it in a job i was making and my boss wanted to use some normal style like the present in other Ext Widgets, So i modified the css and came out with something more close to the ext widgets style, so i am sharing it now with the community.

Here is an screenshoot:
http://playground.ideashs.com/superboxselect/superboxselect.jpg
Here is the dan Examples using this css:
http://playground.ideashs.com/superboxselect/
And here is the css with the image files required:
http://playground.ideashs.com/superboxselect/superboxselectsmallcss.zip

I hope someone find it useful. :D

Cya around. B)

Excellent work!!

I think this changes would be part of original extension. With this style the component fits perfectly to Ext core.

Greetings,

danh2000
22 Nov 2009, 11:52 AM
@syscobra, very nice!

It won't make it into the component as I was specifically asked to create inline icons, but I like how you've adapted it to suit your needs!

abraxxa
22 Nov 2009, 12:49 PM
Is there an option to limit the number of items?
I'd like to replace all comboboxes with this because edit form loading works so flawless with it.

radtad
24 Nov 2009, 1:31 PM
@syscobra really nice! They're much cleaner!

Also, can we add this to the css?



.x-item-disabled .x-superboxselect-input input {background: none !important}
Then you don't get the greyed out input type on disabled. It should follow the standard with all other disabled Ext form components. In addition, I don't know if anyone else agrees, but if the SuperBoxSelect is disabled, it should not show the emptyText if any as well.

syscobra
24 Nov 2009, 4:12 PM
Ok i added it to the zip, so everyone who download it from now will get your fix rad, thanks :D

The empty text stuff i think you can modify it in the line 487 to fit your needs. (you can remove it in there) or you can modify the function in line 946.

lanhun
25 Nov 2009, 4:43 AM
In the EXT 3.0.3 will not work

error:
el.dom Undefined

abraxxa
25 Nov 2009, 4:53 AM
@lanhun: works for me

lanhun
25 Nov 2009, 6:40 AM
@lanhun: works for me

I have a window inside a form, I opened the window to use this extension when there is no problem, but when I close the window when the error arises

lanhun
25 Nov 2009, 7:56 AM
Work for errors in the EXT3.0.3.

el.dom is undefined


autoSize : function(){ if(!this.rendered){
return this;
}
if(!this.metrics){
this.metrics = Ext.util.TextMetrics.createInstance(this.el);
}
var el = this.el,
v = el.dom.value, !!!!!!!!!!!!!!!!

d = document.createElement('div');



var user_attr_multiselect = {
xtype: 'superboxselect',
fieldLabel: 'attr',
resizable: true,
name: 'user_attr[]',
store: new Ext.data.JsonStore({
autoLoad: true,
url: '__APP__/UserAttr/miniList/',
root: 'data',
fields: ['name', 'code']
}),
mode: 'local',
displayField: 'name',
valueField: 'code',
queryDelay: 0,
triggerAction: 'all',
forceSelection: true,
forceFormValue: true,
allowBlank: true
};

abraxxa
25 Nov 2009, 8:37 AM
I found a bug in setValue.
When the value is an empty Array it fails in line 1244:

var values = Ext.isArray(value) ? value : value.split(this.valueDelimiter);To fix it I've added this just before line 1244:

if(!value){
return;
}

lanhun
25 Nov 2009, 8:19 PM
in the 3.0.3, Destruction of the action will cause the error I have described

abraxxa
10 Dec 2009, 4:18 AM
When loading the records from a remote store with a values query the displayed values aren't sorted although they are in the returned json and the store has the sortInfo attribute set.
I couldn't figure out by myself why, help appreciated!

abraxxa
10 Dec 2009, 6:28 AM
This is a patch that prevents a store from loading if a valuesQuery without a value is requested:

change line 1394 to:


if(forceAll === true || (q.length >= this.minChars) || (valuesQuery === true && q )){

raphac
10 Dec 2009, 8:19 AM
in the 3.0.3, Destruction of the action will cause the error I have described

This code work for me:

http://www.extjs.com/forum/showthread.php?p=414817#post414817

abraxxa
17 Dec 2009, 3:41 AM
Will there be a version for 3.1.0?
This one fails at


this.outerWrapEl.setWidth(w - reduce);

danh2000
17 Dec 2009, 4:22 AM
Will there be a version for 3.1.0?
This one fails at


this.outerWrapEl.setWidth(w - reduce);

That's seems a strange place for it to error - it's a simple call to Element.setWidth.

Anyway, now that 3.1 is out, I will release a version that fixes any bugs that I find - I haven't tried the code in 3.1 yet though due to other priorities. I'll take a look when I can.

dusoo
17 Dec 2009, 4:51 AM
Hi danh2000.

Is it possible to use fulltext search
and swap between multyselect and single select mode ?

Thanks
D.

abraxxa
17 Dec 2009, 6:18 AM
Hi danh2000!

Sorry that I wasn't more specific.

this.outerWrapEl returns undef.

@dusoo: I'd need the same thing, an attribute which defines the number of allowed, selected items (1 or many whould be sufficient for me too).

sergey.s
17 Dec 2009, 7:05 AM
As a quick fix for 3.1 check for existence each element @ Ext.ux.form.SuperBoxSelect#onResize

danh2000
17 Dec 2009, 7:21 AM
Hi danh2000.

Is it possible to use fulltext search
and swap between multyselect and single select mode ?

Thanks
D.

I believe this post will do what you want regarding fulltext search - I suggested that he make it a plugin that could be used with ComboBox or any subclass:

http://www.extjs.com/forum/showthread.php?p=340193#post340193

You cannot currently swap between multiselect and single select mode, but you could probably do this yourself by overriding the onTriggerClick template method and using the 'beforeadd' event.

abraxxa
17 Dec 2009, 7:46 AM
@danh2000: will you incorporate my patch from a few days ago?


This is a patch that prevents a store from loading if a valuesQuery without a value is requested:

change line 1394 to:


if(forceAll === true || (q.length >= this.minChars) || (valuesQuery === true && q )){

danh2000
17 Dec 2009, 7:55 AM
@danh2000: will you incorporate my patch from a few days ago?

I've been busy with another priority project, so haven't looked into any of the recent requests (sorry!) or debugged any of the issues raised, but at first glance, I don't see why not.

I can't say exactly when at this stage, but if you add that to an overrides file, I'll announce what's been included in the next build allowing you to remove any overrides that were included/fixed.

Thanks,

Dan

abraxxa
17 Dec 2009, 7:58 AM
I did a search on google but couldn't find out what an override file is.

danh2000
17 Dec 2009, 8:04 AM
I did a search on google but couldn't find out what an override file is.

Rather than patching source files, simply create a file called 'ext-overrides.js' and create overrides for any issues/bugs/requests - document each override and then when you get a new build of Ext or a component, check your overrides file and remove anything that's no longer needed.

Eg:


Ext.override(Ext.ux.form.SuperBoxSelect, {
//override of doQuery for blah blah blh reason...
doQuery : ...
});

It's a neater way of incorporating feature requests, bug fixes etc until the next release.

Update:

PS. Testing q in that way isn't ideal as a record with a value of 0 would fall through. Additionally a bit of background as to what your value is and what problem you experienced is usually more helpful than just a patch as there may be scenarios that you haven't considered, and my source file is usually not in synch with what you are using.

lanhun
17 Dec 2009, 8:27 AM
Writes can search for multiple keywords separated by spaces. Have time to also change the regular expression


doQuery : function(q, forceAll,valuesQuery){
q = Ext.isEmpty(q) ? '' : q;
var qe = {
query: q,
forceAll: forceAll,
combo: this,
cancel:false
};
if(this.fireEvent('beforequery', qe)===false || qe.cancel){
return false;
}
q = qe.query;
forceAll = qe.forceAll;
if(forceAll === true || (q.length >= this.minChars)){
if(this.lastQuery !== q){
this.lastQuery = q;
if(this.mode == 'local'){
this.selectedIndex = -1;
if(forceAll){
this.store.clearFilter();
}else{
//this.store.filter(this.displayField, q);
this.store.filterBy(function(r){
var q_data = q.trim().split(' ');
if(Ext.isArray(q_data) && q_data.length > 1) {
for(var x in r.data){
var flag = true;
q_data.each(function(q){
if(r.data[x].indexOf(q) == -1) {
flag = false;
}
});
return flag;
}
} else {
for(var x in r.data){
if(r.data[x].indexOf(q) != -1) {
return true;
}
}
}
});
}
this.onLoad();
}else{

this.store.baseParams[this.queryParam] = q;
this.store.baseParams[this.queryValuesInidicator] = valuesQuery;
this.store.load({
params: this.getParams(q)
});
this.expand();
}
}else{
this.selectedIndex = -1;
this.onLoad();
}
}
}

danh2000
17 Dec 2009, 8:34 AM
Writes can search for multiple keywords separated by spaces. Have time to also change the regular expression


Sorry, but I didn't follow you - what do you mean by 'writes' and which regular expression are you referring to and what changes?

I can't blindly incorporate code without seeing test cases and/or understanding use cases for new features.

I don't mean to sound rude, but a one liner and a block of code isn't very explanatory.

abraxxa
17 Dec 2009, 8:47 AM
PS. Testing q in that way isn't ideal as a record with a value of 0 would fall through. Additionally a bit of background as to what your value is and what problem you experienced is usually more helpful than just a patch as there may be scenarios that you haven't considered, and my source file is usually not in synch with what you are using.

Thanks for the ext-overrides.js hint!

@valuesquery: I had the problem that an edit form loads its values and each superboxselect triggers a values query although it has no values which slowed down the loading of the form.
Is there a case there a valuesquery without a value makes sense? I can't think of one.

lanhun
17 Dec 2009, 5:33 PM
Sorry, but I didn't follow you - what do you mean by 'writes' and which regular expression are you referring to and what changes?

I can't blindly incorporate code without seeing test cases and/or understanding use cases for new features.

I don't mean to sound rude, but a one liner and a block of code isn't very explanatory.

Sorry,My English is poor. That is I use GOOGLE Translation;

The code can Multi-keyword search with mode: 'local'

I will work hard to learn English.

dusoo
18 Dec 2009, 2:05 AM
@lanhun
Thanks for your code. Search works for me perfectly now.

lanhun
18 Dec 2009, 9:31 AM
Support for multiple keyword search and Highlight multiple keyword.
Thank you for radtad Code.I revised it. [http://www.extjs.com/forum/showthread.php?p=340193#post340193]
Separated by a space.
search eg: 'lanhun card'



Ext.override(Ext.ux.form.SuperBoxSelect, {
caseSensitive: false,
createValueMatcher: function(value) {
return new RegExp('('+value.replace(/\s+/g,'|')+')', this.caseSensitive ? 'g' : 'gi');
},
prepareData : function(data) {
var result = Ext.apply({}, data);
if (Ext.isEmpty(this.getRawValue())) return result;
result[this.displayField] = data[this.displayField].replace(this.createValueMatcher(this.getRawValue()), '<span class="ext-combo-match">$1</span>');
return result;
},
initList : function() {
Ext.ux.form.SuperBoxSelect.superclass.initList.apply(this, arguments);
this.view.prepareData = this.prepareData.createDelegate(this);
},
doQuery : function(q, forceAll,valuesQuery){
q = Ext.isEmpty(q) ? '' : q;
var qe = {
query: q,
forceAll: forceAll,
combo: this,
cancel:false
};
if(this.fireEvent('beforequery', qe)===false || qe.cancel){
return false;
}
q = qe.query;
forceAll = qe.forceAll;
if(forceAll === true || (q.length >= this.minChars)){
if(this.lastQuery !== q){
this.lastQuery = q;
if(this.mode == 'local'){
this.selectedIndex = -1;
if(forceAll){
this.store.clearFilter();
}else{
//this.store.filter(this.displayField, q);
this.multiSelectMode = true;
this.store.filterBy(function(r){
var q_data = q.trim().split(' ');
if(Ext.isArray(q_data) && q_data.length > 1) {
for(var x in r.data){
var flag = true;
q_data.each(function(q){
if(r.data[x].indexOf(q) == -1) {
flag = false;
}
});
return flag;
}
} else {
for(var x in r.data){
if(r.data[x].indexOf(q) != -1) {
return true;
}
}
}
});
}
this.onLoad();
}else{

this.store.baseParams[this.queryParam] = q;
this.store.baseParams[this.queryValuesInidicator] = valuesQuery;
this.store.load({
params: this.getParams(q)
});
this.expand();
}
}else{
this.selectedIndex = -1;
this.onLoad();
}
}
}
});

mystix
18 Dec 2009, 10:39 AM
@lanhun -- friendly tip: highlight your code changes in red so people know what you've changed.

lanhun
18 Dec 2009, 9:42 PM
@lanhun -- friendly tip: highlight your code changes in red so people know what you've changed.

Thank you very much, I changed.

abraxxa
21 Dec 2009, 3:48 AM
A quick'n'dirty change to make it render with 3.1.0 is changing onResize to doResize:


doResize: function(w){
var reduce = Ext.isIE6 ? 4 : Ext.isIE7 ? 1 : Ext.isIE8 ? 1 : 0;

this._width = w;
this.outerWrapEl.setWidth(w - reduce);
if (this.renderFieldBtns) {
reduce += (this.buttonWrap.getWidth() + 20);
this.wrapEl.setWidth(w - reduce);
}
Ext.ux.form.SuperBoxSelect.superclass.doResize.call(this, w);
this.autoSize();
},

I'm not the author of this ux so this won't be the final fix for 3.1.0 but should users like me, who need it working for testing of 3.1.0 compatibility of other components, a head start.

Strati
21 Dec 2009, 5:29 AM
There are some problem if you use numeric values, 'cause in some functions there are function called which only belong to the String object's prototype, like .trim() and .split().

There are some modification recommendations:


setValue : function(value){
if(!this.rendered){
this.preRenderValue = value;
return;
}

var values = Ext.isArray(value) ? value : value.toString().split(this.valueDelimiter);


findInStore : function(val){
var index = this.store.find(this.valueField, val.toString().trim());

this is a very great plugin =D>

mystix
21 Dec 2009, 7:02 AM
@strati: it's safer to do a


String(value)

-- value might be null / undefined.


[edit]
on 2nd thought, this is faster:


(value + '')

Strati
22 Dec 2009, 5:15 AM
@strati: it's safer to do a


String(value)

-- value might be null / undefined.

yeah, it's true

genie_vn
24 Dec 2009, 1:36 AM
@strati: it's safer to do a


String(value)

-- value might be null / undefined.

I'm looking for this, thanks a lot \:D/

danh2000
26 Dec 2009, 6:51 AM
There are some problem if you use numeric values, 'cause in some functions there are function called which only belong to the String object's prototype, like .trim() and .split().

There are some modification recommendations:


setValue : function(value){
if(!this.rendered){
this.preRenderValue = value;
return;
}

var values = Ext.isArray(value) ? value : value.toString().split(this.valueDelimiter);


findInStore : function(val){
var index = this.store.find(this.valueField, val.toString().trim());

this is a very great plugin =D>

Thanks, I had missed the setValue split, but the findInStore method was updated in the last release so you must have an old version.

danh2000
26 Dec 2009, 7:06 AM
New version attached to the first post (http://www.extjs.com/forum/showthread.php?t=69090).

UPDATE 26/10/2009

Fixed queryValuesIndicator typo.
Fix to ensure that blurring component behaves consistently (removing typed chars).
Fix to onResize to ensure all elements have been renderred.
Fix to unset preventMultipleRemoveEvents after all items have been removed.
Fix to normalize string in setValue method.
Fix to add a missing ;
Fixes to reset and destroy processes.
Implemented itemDelimiterKey config.
Implemented beforeselect event.

Let me know if you find any regressions.

Thanks,

Dan

lanhun
26 Dec 2009, 7:10 AM
New version attached to the first post here (http://www.extjs.com/forum/showthread.php?p=422188#post422188). link is misstake.

UPDATE 21/10/2009 date is misstake.

Fixed queryValuesIndicator typo.
Fix to ensure that blurring component behaves consistently (removing typed chars).
Fix to onResize to ensure all elements have been renderred.
Fix to unset preventMultipleRemoveEvents after all items have been removed.
Fix to normalize string in setValue method.
Fix to add a missing ;
Fixes to reset and destroy processes.
Implemented itemDelimiterKey config.
Implemented beforeselect event.

Let me know if you find any regressions.

Thanks,

Dan

some misstake.

danh2000
26 Dec 2009, 7:20 AM
some misstake.

Fixed thanks. Must be too much Christmas cheer :)

danh2000
27 Dec 2009, 1:20 AM
New version attached to the first post in this thread (http://www.extjs.com/forum/showthread.php?t=69090).

UPDATE 27/12/2009

Additional fix to destroy process.
Implemented addNewItem method to fix issue raised in post #242

NOTE Anybody using this component with the allowAddNewData config and a remote store is advised to use the new addNewItem method within their newitem event listeners.

For a demo, see the remote example here:

http://www.technomedia.co.uk/SuperBoxSelect/examplesRemote.html

lanhun
27 Dec 2009, 6:49 PM
i can not use setValue() method.

In this example
http://www.technomedia.co.uk/SuperBoxSelect/examplesRemote.html

danh2000
28 Dec 2009, 6:25 AM
i can not use setValue() method.

In this example
http://www.technomedia.co.uk/SuperBoxSelect/examplesRemote.html

Thanks again! It was a silly typo which I have now fixed.

danh2000
28 Dec 2009, 6:31 AM
New version attached to the first post here (http://www.extjs.com/forum/showthread.php?t=69090).

UPDATE 28/12/2009

Fixed a silly typo which prevented setValue working with a remote store. :">

lanhun
28 Dec 2009, 7:00 AM
Thanks again! It was a silly typo which I have now fixed.

Thank you for giving us such a good extension.You are the best!

lanhun
28 Dec 2009, 8:59 PM
My English is poor.

I'm in a form using this extension.

when i select some items and then close the Form.

Now opens a new form,The selected items here will not arise.


but ver:21/10/2009 is no like this.New forms have all the data.

config: removeValuesFromStore: true,
mode:'local'

danh2000
29 Dec 2009, 1:48 AM
My English is poor.

I'm in a form using this extension.

when i select some items and then close the Form.

Now opens a new form,The selected items here will not arise.


but ver:21/10/2009 is no like this.New forms have all the data.

config: removeValuesFromStore: true,
mode:'local'

Can you please post a test case with working code so that I can debug.

This thread may help : http://www.extjs.com/forum/showthread.php?t=71015

Alternatively, you could point me to an online example with clear instructions to reproduce the problem.

Thanks,

Dan

EDIT:

I think I understand now - because the records are removed from the store they are not replaced when the component is destroyed, so re-using the same store results in those records not being available.

I'll post a fix soon.

danh2000
29 Dec 2009, 3:14 AM
New version attached to the first post here (http://www.extjs.com/forum/showthread.php?t=69090):

UPDATE 29/12/2009

Fix to replace removed store records when component is destroyed.

galdaka
29 Dec 2009, 4:28 AM
Hi danh,

Excellent support!! thanks for all.

One problem.

I´m migrating from 3.0.3 to 3.1.0. When I updated the latest code of your component over this code:




new Ext.ux.form.SuperBoxSelect({
id: 'notasRTEmpleado',
transform: 'notasRTEmpleado',
allowBlank: false,
allowAddNewData: true,
resizable: true,
name: 'notasRTEmpleado',
width: 750,
extraItemCls: 'font-normal',
stackItems: true,
mode: 'local'
})


Don´t work in IE8, OK in FF3X. I have a problem in theses lines (doTransform function):


value = (Ext.isIE && !Ext.isIE8 ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text,
cls = (Ext.isIE && !Ext.isIE8 ? o.getAttributeNode('class').specified : o.hasAttribute('class')) ? o.className : '',
style = (Ext.isIE && !Ext.isIE8 ? o.getAttributeNode('style').specified : o.hasAttribute('style')) ? o.style : '';

If I change theses lines for OLD lines:


value = (Ext.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text,
cls = (Ext.isIE ? o.getAttributeNode('class').specified : o.hasAttribute('class')) ? o.className : '',
style = (Ext.isIE ? o.getAttributeNode('style').specified : o.hasAttribute('style')) ? o.style : '';

Works fine!

P.D: My field notasRTEmpleado is a "select".

Greetings,

danh2000
29 Dec 2009, 4:46 AM
Hi danh,

Excellent support!! thanks for all.

One problem.

I´m migrating from 3.0.3 to 3.1.0. When I updated the latest code of your component over this code

Galdaka,

It's been a long long time since I changed that code - and I've tested in IE8 (at least with basic settings) without any problems.

Are you using compatability mode for some reason, and what doctype do you use - are you in Quirks or Strict mode?

Thanks,

Dan

lanhun
29 Dec 2009, 5:24 AM
@danh2000 Thank you very much! Next time I will give an example.

galdaka
29 Dec 2009, 7:38 AM
Galdaka,

It's been a long long time since I changed that code - and I've tested in IE8 (at least with basic settings) without any problems.

Are you using compatability mode for some reason, and what doctype do you use - are you in Quirks or Strict mode?

Thanks,

Dan

IE8 problem (Version 8.0.6001.18702). No compatibilty mode.

danh2000
29 Dec 2009, 8:23 AM
New version attached to the first post here (http://www.extjs.com/forum/showthread.php?t=69090).

UPDATE 29/12/2009

Fixed IE8 Quirks Mode issue when transforming a HTML Select.
Fixed typo

lanhun
29 Dec 2009, 6:01 PM
@danh2000

when component is destroyed.
error:
this.dom is undefined
error info:
onDestroy() -> killItems() ->resetStore()


sorry,i can not create the example now.

danh2000
30 Dec 2009, 3:11 AM
@danh2000

when component is destroyed.
error:
this.dom is undefined
error info:
onDestroy() -> killItems() ->resetStore()

sorry,i can not create the example now.

I have 2 test cases for this and both are working. I'll need more information and a test case if you are having problems.

Dan

greco
3 Jan 2010, 5:49 AM
Great Job!

I have a question.

I use the SuperBoxSelect.getValueEx() -> var list = [{id:1,name:'x'}, {id:2,name:'y'}]

and then use the SuperBoxSelect.setValueEx(list).

In firebug throws an Exception
"I.style is undefined"
ext-all-debug.js 3920 "out = (v = el.style[prop]) ? v :\n"

This my Code:



this.userFields = ['id', 'name'];
this.userRecord = Ext.data.Record.create(this.userFields);
this.userStore = new Ext.data.JsonStore({
fields: this.userFields
});
this.userList = new Ext.ux.form.SuperBoxSelect({
fieldLabel: 'Users',
resizable: true,
width: 170,
store: this.userStore,
mode: 'local',
displayField: 'name',
valueField: 'id',
forceSelection : true
});

var users = resp.users;
for(var i=0, u; u = users[i++];) {
this.userStore.add(new this.userRecord(u));
}

//##################################

var list = this.userList.getValueEx();

//##################################

this.userList.setValueEx(list);

danh2000
3 Jan 2010, 6:03 AM
...
In firebug throws an Exception
"I.style is undefined"
ext-all-debug.js 3920 "out = (v = el.style[prop]) ? v :\n"
...


I've just tried this scenario and it works without throwing an exception... what version of Ext are you using?

Do you have an online version so that I can debug? or it may help just to see the data you are using in the store.

frederickd
7 Jan 2010, 5:37 PM
This extension is fantastic! It is just what I wanted to eliminate some troublesome checkboxes and be more Web 2.0-ish. Thank you!

I am very close to having my implementation done. My problem is my lack of knowledge of javascript. I've updated my project to ExtJs 3.1.0. The SuperBoxSelect is working perfectly showing my local list of items. What is not happening is the loading of the database field of all the selections. I am only sending to the database the last selection that was made.

Here is what I've got for code snippets. First the definition of the SuperBoxSelect:



var servicesCombo = new Ext.ux.form.SuperBoxSelect({
id: 'services1',
fieldLabel: 'Service(s) Rendered',
emptyText: 'Select service(s) rendered...',
resizable: true,
minChars: 2,
store: [
'AirCondEvap', 'DieselFuelSys', 'GasFuelSys', 'Battery', 'BrakeFlush', 'Driveline',
'Transmission', 'CoolingSys', 'PowerSteer'
],
mode: 'local',
forceSelection: true,
triggerAction: 'all',
name: 'data[Repairorder][services]',
hiddenName: 'data[Repairorder][services]',
selectOnFocus:true
});
Pretty simple stuff. The hiddenName has been how I have loaded other combo boxes. The format of the name is for sending back to CakePHP the database field. [Repairorder] is the model name (database table) and [services] is the field. It is defined as a VARCHAR(150) in a MySQL MyISAM database.

Here is what I have for a handler on the save button:



buttons: [{
text: 'Save', // text for submit button
type: 'submit', // type for button
handler: function() { // when you click the button...
alert(Ext.getCmp('services1').getValue());
add_form.form.submit({
url: 'http://'+host+'/repairorders/add_save',
waitMsg:'Saving Data...',
success: function() {

and so on...

The code in bold was just a debug aid to pop an alert box. The alert box always has all the values that were selected.

Would someone be so kind as to help me put the contents in my database field please? I know this is a newbie type question but I have experimented around and cannot discern the answer myself.

Thank you in advance!

frederickd
7 Jan 2010, 8:16 PM
Did some more scratching around and here is the POST data that is going back to the server:


data[Repairorder][advisor_id]1
data[Repairorder][dealer_id]1
data[Repairorder][distributor_id]1
data[Repairorder][lof]Premium
data[Repairorder][make]Toyota
data[Repairorder][model]4-Runner
data[Repairorder][number]RO20100107b
data[Repairorder][odometer]13456
data[Repairorder][odometer_type]Miles
data[Repairorder][servicedate]2010-01-07
data[Repairorder][services]
data[Repairorder][services]AirCondEvap
data[Repairorder][services]Battery
data[Repairorder][services]BrakeFlush
data[Repairorder][vin]5TDZT34A05S250524
data[Repairorder][year]2008What is interesting is that all three choices show up, but they are not delimited by commas as in the alert box. I can see now why only the last selection is loaded to the database. Does this help? How do I keep the delimited values?

danh2000
8 Jan 2010, 12:39 AM
@frederickd, I think your issue is a PHP one, not a JavaScript one - what gets sent back to the server is an array of selected items. When using PHP, you must append square brackets to the end of the form field name. This is the same as when posting any data to a PHP backend.

Try naming your field data[repairorder][services][]

Note the additional square brackets!

Regards, Dan

frederickd
8 Jan 2010, 10:18 AM
@danh2000

Thanks for the tip. Gotta love square brackets. In this case it didn't work for me. I added the trailing [] to the hiddenName field and the existing value in the field did not display nor would the new values save.

When I manually edit the record using an SQL tool to put comma-delimited values in, the display shows the selections (yeah!) but saves only the last one in the list.

I will see if I can assemble the array on the PHP side. The POST is obviously sending the elements. Hmmm...

danh2000
8 Jan 2010, 1:27 PM
@frederickd, I'm travelling in Europe at the moment and am limited to my iPhone. I'll take another look in a few days when I'm back at home and will see if I can help further - please bear with me.

Thanks,

Dan

abraxxa
11 Jan 2010, 7:36 AM
This is a patch that prevents a store from loading if a valuesQuery without a value is requested:

change line 1394 to:


if(forceAll === true || (q.length >= this.minChars) || (valuesQuery === true && q )){


I stumbled over this bug again in the current version, now it's in line 1423.
Without the patch a valuesquery isn't performed when the value is shorter than the minChars property.

frederickd
12 Jan 2010, 5:44 PM
@danh2000

Thank you for taking a look at this. I hope you had a safe return.

danh2000
12 Jan 2010, 7:18 PM
@danh2000

Thank you for taking a look at this. I hope you had a safe return.

I did thanks.

I've just performed some testing and the values are submitted correctly, you therefore have a problem with your PHP code.

As I said, an array of values is posted to the server (hence the need for square brackets).

If you have setup the component correctly and you can see the array of data posted with firebug, you'll need to debug the issues on your server.

Regards,

Dan

danh2000
12 Jan 2010, 7:36 PM
I stumbled over this bug again in the current version, now it's in line 1423.
Without the patch a valuesquery isn't performed when the value is shorter than the minChars property.

Abraxxa,

Testing q like that isn't advised as a store item with a value of 0 (zero) will fail to query.

I'll consider it a bug if you can you please post a testcase and describe the problems you are having. Show me some code and some data with an explanation - thanks.

frederickd
12 Jan 2010, 7:51 PM
@danh2000

Thank you for taking a look at it. I will try to figure something out. The POST in FireBug from the JavaScript side shows the array elements. Dumping the fields on the PHP side after receiving the data yields only the last element. The previous ones are stripped for some reason.

I was experimenting with the statement v = v.toString(); to remove the array. However, I do not know how to load the variable into the original field before sending it along for the POST.

Do you think that is a viable approach? Thanks!

danh2000
12 Jan 2010, 8:01 PM
@danh2000

Thank you for taking a look at it. I will try to figure something out. The POST in FireBug from the JavaScript side shows the array elements. Dumping the fields on the PHP side after receiving the data yields only the last element. The previous ones are stripped for some reason.

I was experimenting with the statement v = v.toString(); to remove the array. However, I do not know how to load the variable into the original field before sending it along for the POST.

Do you think that is a viable approach? Thanks!

It sounds too much like a workaround without addressing the problem - I'd always try to understand and resolve the real issue.

Firstly, what does a screendump from the Firebug net panel look like showing the post data?

Secondly, what does a dump of the $_POST data look like when it arrives on the server?

frederickd
12 Jan 2010, 8:28 PM
Understood.

Here is the POST from FireBug:

data[Repairorder][advisor_id]1
data[Repairorder][customer_email]fxxx@mac.com
data[Repairorder][dealer_id]1
data[Repairorder][distributor_id]1
data[Repairorder][lof]Premium
data[Repairorder][make]Toyota
data[Repairorder][model]4-Runnerdata
[Repairorder][number]RO20100107b
data[Repairorder][odometer]13456
data[Repairorder][odometer_type]Miles
data[Repairorder][servicedate]2010-01-07
data[Repairorder][services]
data[Repairorder][services]Batterydata
[Repairorder][services]Driveline
data[Repairorder][vin]5TDZT34A05S250524
data[Repairorder][year]2008

Here is the POST data from my server just after receipt and before doing the save to the database:

[POST data] => Array
(
[Repairorder] => Array
(
[distributor_id] => 1
[dealer_id] => 1
[advisor_id] => 1
[number] => RO20100107b
[servicedate] => 2010-01-07
[customer_email] => fxxx@mac.com (frederickd@mac.com)
[vin] => 5TDZT34A05S250524
[year] => 2008
[make] => Toyota
[model] => 4-Runner
[odometer] => 13456
[odometer_type] => Miles
[lof] => Premium
[services] => Driveline
)

)

This is with the configuration option of hiddenName: 'data[Repairorder][services]'. Like this I have noticed that I can start entering characters in a little text box and the list of elements matching appear for selection, which is great. When I have the configuration option of hiddenName: 'data[Repairorder][services][]' that feature no longer works, but the blank array entry from the FireBug POST data is gone.

I do not know why all the array elements would not be passed back to the server. I did notice that on the 2.x extensions forum for SuperBoxSelect someone else had a similar problem and solved it by having the JSON data show [Repairorder][services][]. So maybe it is because the JSON data is coming down as a text field, but trying to go back as an array.

Maybe instead of a "workaround" a configuration option could be added like returnType with a default of 'array' but an option of 'text'.

Just a thought...

danh2000
12 Jan 2010, 8:57 PM
@frederickd

You are NOT sending the data as an array, you are sending it as a string, so PHP only gets the last data that is sent.

Let me try to explain again but with a simpler example - one of my own test cases with US State data.

If I use the following config:


name : 'states[]'

I get this in Firebug:


states[] AL
states[] AK
states[] AZ

And I get an array of data in PHP's states variable.

If I use this config:


hiddenName: 'statesHidden[]'

I get this in Firebug:


statesHidden[] AL
statesHidden[] AK
statesHidden[] AZ

And I get an array of data in PHP's statesHidden variable.

Note the square brackets in both examples - on both occassions, the data arrives on the server as expected.

frederickd
12 Jan 2010, 9:07 PM
Understood. So it must be that the data needs to be defined as an array in the JSON data coming from the server so that it can go back the same way.

Right now the data is coming from the server as a text field and trying to force it to go back as an array won't work.

So I have to figure out how to configure my particular field as an array in the JSON data so that it can go back the same way.

Thanks for your help. I'll see what I can do...

danh2000
12 Jan 2010, 9:13 PM
Understood. So it must be that the data needs to be defined as an array in the JSON data coming from the server so that it can go back the same way.

Right now the data is coming from the server as a text field and trying to force it to go back as an array won't work.

So I have to figure out how to configure my particular field as an array in the JSON data so that it can go back the same way.

My example has nothing to do with data coming from the server.

You could have data come from the server and loaded into the component using a string if you want:

'AK, AL, AZ'

Would load Arkansas, Alabama and Arizona into the component.

Your issue is how you were sending the data to the server. You hadn't used square brackets, which PHP requires to receive the data as an array.

I'm also guessing (by the additional blank entry) that you are using a regular HTML submit button and not using the BasicForm.submit() method, but that's another topic.

frederickd
12 Jan 2010, 9:27 PM
I don't think I am using an HTML submit button, but maybe I am.



buttons: [{
text: 'Save',
type: 'submit',
handler: function() {
edit_form.form.submit({
url: 'http://'+host+'/repairorders/edit_save/'+ edit_select.id,
waitMsg:'Saving Data...',
success: function() {
Ext.MessageBox.show({
title: 'Success Message',
msg: 'Data saved successfully!',
buttons: Ext.MessageBox.OK,
animEl: 'elId',
fn: goBackToGrid,
icon: Ext.MessageBox.INFO
});
},
failure: function() {
Ext.MessageBox.show({
title: 'Failure Message',
msg: 'Save failed! Check error messages.',
buttons: Ext.MessageBox.OK,
animEl: 'elId',
icon: Ext.MessageBox.ERROR
});
}
});
}


I have updated the data in the database to have multiple values, watched it come down from the server as a text field with the multiple values, and defined the hiddenName with [] at the end. The results were not favorable. I will experiment some more. Sorry if I have been frustrating to you. Thank you for your direction.

danh2000
12 Jan 2010, 9:37 PM
@frederickd.

Maybe there's an issue that I need to look at with the blank entry.....

No problem about the questions, it's just a little difficult to follow exactly what you are doing, but the data certainly posts to the server correctly, and can be loaded into the component as an array or a string either by the value config or a call to the setValue method - they both accept an array or delimited string.

Anyway, good luck with your debugging.

abraxxa
14 Jan 2010, 10:35 AM
Abraxxa,

Testing q like that isn't advised as a store item with a value of 0 (zero) will fail to query.

I'll consider it a bug if you can you please post a testcase and describe the problems you are having. Show me some code and some data with an explanation - thanks.

Sorry for the delay, seems I missed the notification email about your post.

Thanks for the hint with 0, you're absolutely right.
I'm using your ux for add and edit forms, the problem occurs in the edit forms.
I don't set any value when generating the form but load the data with a jsonstore.

When the loaded (foreign key) value is shorter than minChars the valuesquery isn't triggered.

I've just found another problem in setValue where null values from the json result in the field getting the string 'null' assigned.
I've fixed it with the following changes. I'm sure this can be written shorter, the else is to clarify the problem.



setValue : function(value){
if(!this.rendered){
this.preRenderValue = value;
return;
}
var values = value;
if(!Ext.isArray(value) && Ext.isDefined(value)){
value = '' + value;
values = value.split(this.valueDelimiter);
}
else {
values = [];
}


Edit: in fact using Ext.isDefined(value) instead of plain value does NOT fix my problem, because Ext.isDefined(null) is true. Using !Ext.isEmpty(value) instead fixes it (note the not in form of an exclamation mark!).

abraxxa
14 Jan 2010, 11:00 AM
I've rewritten the whole part to be more readable:



// make sure values is an array so later code which assumes that
// doesn't fail
var values = [];
if(Ext.isArray(value)){
values = value;
}
else if(!Ext.isEmpty(value)){
value = '' + value;
values = value.split(this.valueDelimiter);
}

danh2000
14 Jan 2010, 11:46 AM
abraxxa,

Thanks for posting again...

This post describes what I meant when I asked for some data and code:

http://www.extjs.com/forum/showthread.php?t=71015

I'm grateful for everyone posting bug reports, but it's sometimes difficult to understand paragraphs of text, and even more difficult and time-consuming attempting to setup test cases that mimic exactly what someone is doing without seeing their code and data. When I have a reproducible test case, I can fix things much quicker.

Update: I have created a test case and re-produced the minChars issue. The fix will be in the next version.

Regarding your second issue, It's could be a good idea to add a check for a null value in the setValue method, but I may be missing something - how does the null scenario occur? Why are you setting the value to null in the first place - what does the data look like?

Thanks,

Dan

danh2000
14 Jan 2010, 9:05 PM
UPDATE 15/01/2010

Fixed regression that made transforming an HTML Select incompatible with Ext 2.x
Fixed issue with setValue being used prior to rendering.
Fix to prevent passing null to setValue from causing problems.
Fixed inconsistency regarding forceFormValue config and form submits.
Fix to prevent component submitting values when disabled.
Fix to ensure that minChars does not affect a values query.

New version attached to the first post here (http://www.extjs.com/forum/showthread.php?t=69090)

danh2000
14 Jan 2010, 10:08 PM
UPDATE 15/01/2010 (part 2)

Fix to disable item listeners when component is disabled.

New version attached to the first post here (http://www.extjs.com/forum/showthread.php?t=69090)

abraxxa
15 Jan 2010, 2:12 AM
abraxxa,

Thanks for posting again...

This post describes what I meant when I asked for some data and code:

http://www.extjs.com/forum/showthread.php?t=71015

I'm grateful for everyone posting bug reports, but it's sometimes difficult to understand paragraphs of text, and even more difficult and time-consuming attempting to setup test cases that mimic exactly what someone is doing without seeing their code and data. When I have a reproducible test case, I can fix things much quicker.

Update: I have created a test case and re-produced the minChars issue. The fix will be in the next version.

Regarding your second issue, It's could be a good idea to add a check for a null value in the setValue method, but I may be missing something - how does the null scenario occur? Why are you setting the value to null in the first place - what does the data look like?

Thanks,

Dan

Hi Dan!

I assumed that bug report policy is only for ExtJS itself, but if you use it too that's fine for me.
As a developer I appreciate good bug reports myself. ;)

Thanks for fixing the minChars issue!

@null value problem: I'm using (and am co-author of) Catalyst::Controller::DBIC::API which returns all object attribute/value pairs of an object, also undefined ones.
My JSON is something like:


{
"success":true,
"data":{
"id":"123",
"fk_foo":null,
"bar":"baz",
}
}

Do you need a more detailed explanation?

danh2000
15 Jan 2010, 3:44 PM
@null value problem: I'm using (and am co-author of) Catalyst::Controller::DBIC::API which returns all object attribute/value pairs of an object, also undefined ones.
My JSON is something like:


{
"success":true,
"data":{
"id":"123",
"fk_foo":null,
"bar":"baz",
}
}

Do you need a more detailed explanation?

So you were loading data into the form, not calling setValue directly with a null value? This is where test cases help with the understanding.

Anyway, did you try with the recent update?

danh2000
15 Jan 2010, 4:14 PM
UPDATE 16/01/2010

Fixed bug when setting disabled in config.
Updated CSS to remove gray background from input when component is disabled.

New version attached to the first post here (http://www.extjs.com/forum/showthread.php?t=69090)

danh2000
15 Jan 2010, 4:36 PM
UPDATE 16/01/2010 (Part 2)

Fix to prevent extra empty hiddenName value from being posted.

@frederickd - I'd forgotten about this one until I saw you on the forum ;)

New version attached to the first post here (http://www.extjs.com/forum/showthread.php?t=69090)

frederickd
15 Jan 2010, 5:25 PM
@danh2000

Thank you for the post and the update! I have just installed and there is a difference for me. Here is my JSON data coming from the server.


{"Repairorder":
{"id":"150",
"advisor_id":"1",
"dealer_id":"1",
"distributor_id":"1",
"number":"RO20100107b",
"servicedate":"2010-01-07",
"vin":"5TDZT34A05S250524",
"year":"2008",
"make":"Toyota",
"model":"4-Runner",
"odometer":"13456",
"odometer_type":"Miles",
"created":"2010-01-07 17:21:28",
"modified":"2010-01-15 17:00:48",
"lof":"Premium",
"lof_sch_60":"2010-03-08",
"lof_act_60":null,
"lof_sch_75":"2010-03-23",
"lof_act_75":null,
"lof_sch_90":"2010-04-07",
"lof_act_90":null,
"services":"Battery, Driveline",
"customer_email":"fxxx@mac.com"},
"Distributor":{"name":"B.C. Distributor #1"},
"Dealer":{"name":"Check UR Emmission - Vancouver"},
"Advisor":{"name":"Brawny Dishtowel"}}Notice that the "services" entry has two values. (I have a valueDelimiter: ", " (comma + space) so have set up the data that way too.) Attached is a screen shot showing that both values display in the combo box, plus a space for the user to type in characters for a new selection instead of using the drop down.

So far so good. My hiddenName configuration is hiddenName: 'data[Repairorder][services]' without an extra "[]" at the end. When I put the "[]" at the end the database values do not get set in the display; the combobox is empty of initial values.

Here is the JSON being sent back to the server after pressing the save button.


data[Repairorder][advisor_id]1
data[Repairorder][customer_email]fxxx@mac.com
data[Repairorder][dealer_id]1
data[Repairorder][distributor_id]1
data[Repairorder][lof]Premium
data[Repairorder][make]Toyota
data[Repairorder][model]4-Runner
data[Repairorder][number]RO20100107b
data[Repairorder][odometer]13456
data[Repairorder][odometer_type]Miles
data[Repairorder][servicedate]2010-01-07
data[Repairorder][services]Battery, Driveline
data[Repairorder][services]Battery
data[Repairorder][services]Driveline
data[Repairorder][vin]5TDZT34A05S250524
data[Repairorder][year]2008
With the previous version the first entry was blank, then the two individual entries for the selections. Now, with the latest version, the first entry has the two selected values, followed by the individual selections.

The same end result happens in that the PHP is updating the database with the last entry sent; in this case 'Driveline'.

So the blank entry is gone, both of the selected values are there instead. I am very confident that if that is all I sent back to the server, the one entry that has both of the selected values, both entries would be updated in the database.

Any other suggestions? I can provide the javascript definition for the combobox if desired. This is extremely close. Thank you!

danh2000
15 Jan 2010, 5:40 PM
@frederickd,

You MUST put the square brackets at the end of the name or hiddenName (if that's what you use) or PHP will not receive all values, it will receive only the last value as a string.

I have MANY working examples where using square brackets at the end of the name/hiddenName post to PHP correctly, so you must have a problem with your PHP code.

It's exactly the same as using regular HTML inputs - PHP MUST have square brackets at the end of the field names to be able to receive an array of data.

What happens when you put saquare brackets at the end, and post some data to your server - do you get an array of values received in PHP?

danh2000
15 Jan 2010, 5:48 PM
So far so good. My hiddenName configuration is hiddenName: 'data[Repairorder][services]' without an extra "[]" at the end. When I put the "[]" at the end the database values do not get set in the display; the combobox is empty of initial values.

You say that you tried WITH square brackets, but your intital values weren't loaded - How do you load your values - are you loading that JSON into the form? Did you also update the JSON to have square brackets at the end of the field name?

frederickd
15 Jan 2010, 7:21 PM
That is one of the parts I do not yet know how to do; update the JSON data so that it has [] coming down to match the [] going back up. With one statement on the PHP side the JSON data is being created from the array of DB records found. The JSON data is not being built by hand. I think underneath it is doing a json_encode on the array of DB data being sent.

With the [] on the 'name' and/or 'hiddenName' configuration elements (I've tried both combinations), what is posted back looks like this:

data[Repairorder][services][]
data[Repairorder][services][]Battery
data[Repairorder][services][]Driveline
data[Repairorder][services][]BrakeFlush

Notice the blank entry is back again for me. I am receiving a syntax error on the PHP side which is preventing the save to the DB from occurring. The PHP is receiving an array that looks identical to what is being sent; element 0 blank, elements 1-3 with values.

I will see what I can do to modify my JSON data coming down to have the [] for the one field in question. I'm sure that is why the initial values are not loading automatically.

When the JSON data looks like a string, yet the box is defined as an array, the initial values do not load.

When the JSON data coming down looks like a string, plus the box is defined as a string, the data values load initially for me.

In either case, returning an array, does not work in my case probably because the initial data looked like a string.

It appears one of two things are needed:

1) I figure out how to change my JSON data to be an array, and define the box as an array, so that the returning JSON data is processed as an array in PHP, or
2) I modify the code to accept a string (which it already does) and send back a string, not sending back an array.

That's why I was asking about the feasibility of adding another configuration option of 'returnDataAs' which would default to 'array' but allow a value of 'text'.

No worries. I will figure it out one way or another. Thank you for your patience. Sometimes using frameworks (whether a PHP framework or a javascript framework) saves time, but there are parts that are hidden making debug analysis difficult.

abraxxa
16 Jan 2010, 12:03 AM
So you were loading data into the form, not calling setValue directly with a null value? This is where test cases help with the understanding.

Anyway, did you try with the recent update?

I tried UPDATE 15/01/2009 (part 2).
'Fix to ensure that minChars does not affect a values query.'seems to work, but the null values in my json still triggered the valueQuery until I added my patch.
Which lines did you change to prevent this?

danh2000
16 Jan 2010, 3:13 PM
@abraxxa, then please provide a test case.

Thanks

danh2000
17 Jan 2010, 6:59 PM
UPDATE 18/01/2010

Fixed regression where disabling the component didn't properly disable the items.

New version attached to the first post here (http://www.extjs.com/forum/showthread.php?t=69090)

Fredric Berling
18 Jan 2010, 5:50 AM
doQuery : function(q, forceAll,valuesQuery, forcedAdd){
q = Ext.isEmpty(q) ? '' : q;
var qe = {
query: q,
forceAll: forceAll,
combo: this,
cancel:false
};
if(this.fireEvent('beforequery', qe)===false || qe.cancel){
return false;
}
q = qe.query;
forceAll = qe.forceAll;
if(forceAll === true || (q.length >= this.minChars)){
if(this.lastQuery !== q || forcedAdd){
this.lastQuery = q;
if(this.mode == 'local'){
this.selectedIndex = -1;
if(forceAll){
this.store.clearFilter();
}else{
this.store.filter(this.displayField, q);
}
this.onLoad();
}else{

this.store.baseParams[this.queryParam] = q;
this.store.baseParams[this.queryValuesIndicator] = valuesQuery;

if (typeof valuesQuery == 'undefined'){
valuesQuery=false
}
this.store.load({
//params: this.getParams(q)
params:{
query:q,
qryvalues:valuesQuery
}
});
if(!forcedAdd){
this.expand();
}
}
}else{
this.selectedIndex = -1;
this.onLoad();
}
}
}
});

When using this wonderful widget in Ext3.1 and using a DirectStore , the qryvalues parameter gets missing. I had this problem in other cases and its because you no longer can change the baseParameters in a beforeQuery event. BaseParams are ment to be the same all along the lifetime of the store. I made a quick fix for this and that works for me. (in red)

I was also wondering if there is anyway to limit the choices . In some cases i really just want the user to be able to select ONE value from the dropdown, and this should overwrite the previously selected value , displaying only one box. In other cases it might be useful to limit the values to a number (say 25) , just to stop some users from selecting any option because the are...well...users... :)

again.. Lovely implementation ...great work.

abraxxa
18 Jan 2010, 6:56 AM
@abraxxa, then please provide a test case.

Thanks

With version UPDATE 18/01/2009 my null value problem seems to be fixed too.
If it reappears I'll post a full test case.
Thanks Dan!

danh2000
19 Jan 2010, 2:01 PM
With version UPDATE 18/01/2009 my null value problem seems to be fixed too.
If it reappears I'll post a full test case.
Thanks Dan!

I haven't touched that code since in the previous releases - it was fixed on 15/01/2009 http://www.extjs.com/forum/showthread.php?p=427545#post427545

danh2000
19 Jan 2010, 2:03 PM
When using this wonderful widget in Ext3.1 and using a DirectStore , the qryvalues parameter gets missing. I had this problem in other cases and its because you no longer can change the baseParameters in a beforeQuery event. BaseParams are ment to be the same all along the lifetime of the store. I made a quick fix for this and that works for me. (in red)


Frederic, Thanks for the report - I don't have a testcase for a direct store so this isn't something I've come accross.

When I have some time, I'll setup a test case and make compatible with Direct.




I was also wondering if there is anyway to limit the choices . In some cases i really just want the user to be able to select ONE value from the dropdown, and this should overwrite the previously selected value , displaying only one box. In other cases it might be useful to limit the values to a number (say 25) , just to stop some users from selecting any option because the are...well...users... :)

again.. Lovely implementation ...great work.

This has been requested previously and I'll consider adding it to a future release.

abraxxa
19 Jan 2010, 3:29 PM
I haven't touched that code since in the previous releases - it was fixed on 15/01/2009 http://www.extjs.com/forum/showthread.php?p=427545#post427545

No idea why it didn't work the first time. :-?

@limit for the number of choices:
Because I use it as complete replacement for all comboboxes, because I need the valuesQuery ability for edit forms, I'd need a possibility to limit the number to one too.
If I can help let me know.

While looking for possible fixes for my problems I've found some code pieces which I'd like to rewrite a bit.
Should I post diffs or the whole patched file?

danh2000
19 Jan 2010, 3:35 PM
While looking for possible fixes for my problems I've found some code pieces which I'd like to rewrite a bit.
Should I post diffs or the whole patched file?

Just post test cases and explanations of why you'd like them re-written or why they are causing problems.

abraxxa
19 Jan 2010, 3:40 PM
No problems, just things like using switch/case instead of repeated if's to make the code nicer.

abraxxa
20 Jan 2010, 10:29 AM
In fact I stumbled over a glitch I've already discovered some time ago.
The order of the valuesQuery reply isn't kept, so sorting on the server is useless at the moment.
It seems the entries are sorted on the client.
You can test this with a valuesQuery that returns a json of:

[["1", "C"],["2", "B"],["3", "A"]]
The entries will be A,B,C instead of C,B,A.

sudhirhv
26 Jan 2010, 6:45 AM
Hi danh2000,

This is a superb extension! Just replaced a few fields with your extension, and I could successfully integrate it with my backend. I am using this as a remote combo box.

Heres all that I have done.

1) Could create remote store fields and load the store
2) Submit values selected to the server
3) Load values into the field for a record retrieved from the backend.

I have one small problem, maybe this is already addressed in the thread, but I could not really find a solution yet... How do I retrieve the value of the posted field values in the backend?? I use a servlet to capture the data, like for a combobox and my field should have also been posted.

I thought we needed the hiddenName parameter, added it and tried accessing it but found it to be null. then tried the name parameter, but that is also null.

Finally I had to pass the values as a string array from the front end via querystringparams.

For this I accessed the field using Ext.getCmp('id').getValue, and I posted the values into the server.. I would like to avoid manually pushing these values like mentioned above, Is this possible?

I am hesitant using getCmp('id') method, becasue I use a tab panel, where users can open multiple records of the same form type and I will have to do ID management of the fields.

Can I avoid this? .

My config is as below.


var recipientsField = new Ext.ux.form.SuperBoxSelect({
allowBlank : false,
id : 'ID_Recipients', // we have to bring a counter
// here..maybe, let me check
// xtype : 'superboxselect',
fieldLabel : 'Recipients',
resizable : true,
minChars : 2,
name : 'EmailIDTX',
anchor : '100%',
store : recipientsStore,
pageSize : 50,
mode : 'remote',
displayField : 'fullname',
displayFieldTpl : '{fullname} ({id})',
valueField : 'id',
queryDelay : 0,
triggerAction : 'all'
//forceFormValue: true
})Once again, thanks for the extension!

Please advise me a better approach if any.

- Sudhir

abraxxa
26 Jan 2010, 7:10 AM
Why would you post the displayField value to the server when you already post the valueField (id)?

sudhirhv
26 Jan 2010, 7:43 AM
Thanks abraxxa for your reply, but I am not posting displayfield, I am just doing a form.submit() and all values are posted into the server, and in the server, I try to access the field values in a servlet using request.getParameter or request.getParameterValues().

Here if I use "EmailIDTX", it returns null and no value is available from the superboxselect field.

How do you access the superbox values in a post? Because I could not access it from the request object in the servlet, I used the querystring to pass the values from the field to the server, which I would like to avoid.

I may be missing something very simple here, but cant get to it.

abraxxa
26 Jan 2010, 8:45 AM
Take a look at what is submitted by using firebug with firefox or on the server with tcpdump or some debug method of your framework/language.

The superbox ux posts the id(s) of the selected store records.

When you say 'querystring' do you mean the url parameters?
A http post submits values in the http body where a http get does it in the url with parameters.
If you submit a form this is usually a http post and the values can be found in the body after decoding and deserialization.
But I don't know what a 'servlet' is and how it does this.

danh2000
26 Jan 2010, 6:03 PM
@sudhirhv,

What @abraxxa said - check in Firebug and see what's being posted. The component certainly works with posting values, so you are likely doing something wrong on the server.

sudhirhv
27 Jan 2010, 4:43 AM
Thanks Abraxxa, danh2000.

It was a mistake from my side, it definitely works..

I was making an AJAX request and posting the data and had assumed that it would come thru..silly from my side..I have changed it to a standard submit now.

thanks again for your help!

ostghost
22 Feb 2010, 1:23 PM
After some articled detail store is loaded i called



Ext.StoreMgr.get('storeTag').on('load', function() {
Ext.each('147,148'.split(','), function(value, key) {
console.log('test');
Ext.getCmp('tag').addItem( {
tag_id : value
});
});
}, this, {
single : true
});
Ext.StoreMgr.get('storeTag').load();


Everythings works fine till i moved Ext.ux.form.SuperBoxSelect 1st tab to 2nd.

ct is null
http://127.0.0.1/domain.tld/public/js/ext/ext-all-debug.js
Line 15666



if(this.el){
15664 this.el = Ext.get(this.el);
15665 if(this.allowDomMove !== false){
15666 ct.dom.insertBefore(this.el.dom, position);
15667 if (div) {
15668 Ext.removeNode(div);
15669 div = null;
15670 }
15671 }
15672 }


Seems that problem is with deferredRender? Tabpanel has deferredRender : false.
Ext verision 3.1.1

talha06
5 Mar 2010, 4:41 AM
Hello everyone,

Thanx a lot all to for your hard works..

I want to select multiple items with SuperBoxSelect, but I can't. I couldn't find the error.. Please can someone help me, thanx..



var myStore = new Ext.data.Store({
data: [
[ 'Ankara' ],
[ 'İstanbul' ],
[ 'İzmir' ],
[ 'Bursa' ],
[ 'Erzurum' ]
//...more rows of data removed for readability...//
],
reader: new Ext.data.ArrayReader({id:'id'}, [
'city'
]
)
});

// form -> item 's inside:
// SuperBoxSelect
allowBlank:false,
msgTarget: 'SuperBoxSelect Example',
id:'mySuperBoxSelect',
xtype:'superboxselect',
fieldLabel: '<b>İller</b>',
resizable: true,
name: 'states2',
anchor:'100%',
store: myStore,
mode: 'local',
displayField: 'city',
navigateItemsWithTab: false

cq.yangyu@gmail.com
5 Mar 2010, 5:39 AM
yeah,super good

lacco
27 Mar 2010, 4:17 PM
Hi, it seems that SuperBoxSelect registers the SuperBoxSelect#onStoreLoad callback on the store, but does not unregister it after desctruction. I propose to use ComboBox#bindStore which is already called after initalize and on destroy:



bindStore: function (store, initial) {
if(this.store && !initial && this.mode === 'remote'){
this.store.un('load', this.onStoreLoad, this);
}
Ext.ux.form.SuperBoxSelect.superclass.bindStore.call(this, store, initial);
if(this.store && this.mode === 'remote'){
this.store.on('load', this.onStoreLoad, this);
}
}


It would be great to include this or a similar fix into the SuperBoxSelect...

wcasado
29 Mar 2010, 8:29 AM
Hi Dan thank you for this great plugging.
I have a issue with a superboxselect which is loading the store from PHP fine, also it's sending the values to the mysql/php server just fine. However when I load the complete record from the server, which already has the values of the superboxselect, after the form has been loaded it tries to reload it's own store with the values comming from the server.
Any configuration solution for that?
Here is a copy of my superboxselect:

xtype: 'superboxselect',
id: 'crnsNCFAffectedCombo',
fieldLabel: 'CF Afectados',
name: 'crnCustomer_affectedNCFs',
store: crnsNCFAffectStore,
valueField: 'ncf',
displayField: 'ncf',
typeAhead: true,
triggerAction: 'all',
mode: 'remote',
triggerAction: 'all',
emptyText: 'Elegir lo(s) o el comprobante(s) fiscal(es) afectado(s)',
forceSelection: true,
selectOnFocus: true,
loadingText: loadingMessage,
valueDelimiter: '|',
queryDelay: 800,
anchor: '99%',
lastQuery: '',
listeners: {
focus: function(){
var customerCodigo = Ext.getCmp('crnsCustomerCodigo').getValue();
if(customerCodigo != '') {
var invType = Ext.getCmp('crnsTypesCombo').getValue();
if (invType != '') {
Ext.apply(this.store.baseParams, {
get_customer_ncfs: customerCodigo,
inv_type: invType
});
this.store.load();
} else {
//The user had not select the affected inv type. Throw a message
this.store.removeAll();
Ext.MessageBox.show({
title: 'Mensaje',
msg: 'Debe elegir el tipo de comprobante afectado!!!',
buttons: Ext.MessageBox.OK,
animEl: 'crnsTypesCombo',
icon: Ext.MessageBox.WARNING
});
}
}
}
}


Thank you for your help!!.

Wilmag

abraxxa
30 Mar 2010, 3:39 AM
When calling isValid in the superboxselect component firebug reports a 'too much recursion' error.
It can be fixed by removing the clearInvalid calls in validateValue.
ExtJS itself doesn't call clearInvalid in validateValue either (for example in Ext.form.NumberField), so I assume this is the correct fix for the problem.
If not please correct me.


validateValue: function(val){
if(this.items.getCount() === 0){
if(this.allowBlank){
/*this.clearInvalid();*/
return true;
}else{
this.markInvalid(this.blankText);
return false;
}
}

/*this.clearInvalid();*/
return true;
},

rdblyth
6 Apr 2010, 1:14 PM
SuperBoxSelect is giving me issues in IE7 after I added a doctype for HTML 4.01 Transitional. If the superboxselect is empty and I peform a search results come back fine and I can select them. If I perform another search after I have one item in my superboxselect the results are not displayed and my search term disappears. If I remove the doctype everything works great. The odd thing is sometime this problem occurs after having one item or two items in the superboxselect.

Firefox works fine either way.

Has anyone seen this problem. Any help would be appreciated.

Thanks
Daniel

rdblyth
8 Apr 2010, 10:32 AM
SuperBoxSelect is giving me issues in IE7 after I added a doctype for HTML 4.01 Transitional. If the superboxselect is empty and I peform a search results come back fine and I can select them. If I perform another search after I have one item in my superboxselect the results are not displayed and my search term disappears. If I remove the doctype everything works great. The odd thing is sometime this problem occurs after having one item or two items in the superboxselect.

Firefox works fine either way.

Has anyone seen this problem. Any help would be appreciated.

Thanks
Daniel

After trying to debug the problem it looks the problem is caused by the SuperBoxSelect losing focus. When the problem occurs if I click outside of the SuperBoxSelect and then back then the problem corrects itself, but will come back after adding a few more items. Also If I use the keyboard and not the mouse to add items I don't get the problem.

jmiguel
12 Apr 2010, 12:14 PM
Very nice component!

I am trying, but when I disable+hide it and then enable+show, the list of the elements changes it's width to a very small one...

To hide/show it I use:

field.hide();
field.show();

This is my implementation:

var sbs = new Ext.ux.form.SuperBoxSelect({
msgTarget: 'under',
allowAddNewData: true,
id: name,
fieldLabel: 'test',
emptyText: '',
resizable: true,
name: 'mytest',
anchor:'90%',
store: mystore,
mode: 'local',
displayField: 'name',
valueField: 'id',
extraItemCls: 'x-tag'
});

Any idea??

Great component!
Regards

abraxxa
12 Apr 2010, 12:26 PM
That happens too when the superboxselect is inside a collapsed or hidden panel after it's expanded/shown.
I'd need a fix for this too.

jarlau
15 Apr 2010, 8:03 PM
Hi Dan,
I ran into 'this.store is null' error in onStoreLoad() when the second form creates(the first form has no problem).
But the problem solved if I changed all 'this.store' to 'store' in the entire function. Is it ok to do such change?

rgds,
Jarlau

jmiguel
16 Apr 2010, 2:42 AM
That happens too when the superboxselect is inside a collapsed or hidden panel after it's expanded/shown.
I'd need a fix for this too.

Solved, I removed the anchor and used a fixed width (loosing some functionality..., but it works)



---anchor:'90%',
+++width: 450,

abraxxa
16 Apr 2010, 2:51 AM
Doesn't fix anything here.

nmohler
20 Apr 2010, 7:10 AM
SuperBoxSelect is giving me issues in IE7 after I added a doctype for HTML 4.01 Transitional. If the superboxselect is empty and I peform a search results come back fine and I can select them. If I perform another search after I have one item in my superboxselect the results are not displayed and my search term disappears. If I remove the doctype everything works great. The odd thing is sometime this problem occurs after having one item or two items in the superboxselect.

Firefox works fine either way.

Has anyone seen this problem. Any help would be appreciated.

Thanks
Daniel

I have seen a similar issue using the same doctype declaration. Removing the doctype eliminates the poor behavior, but introduces even bigger problems in other areas of the app.

I see the behavior in two scenarios:
1) The SuperBoxSelect is populated while the SuperBoxSelect is in a scrollable container and the SuperBoxSelect is not visible.
2) I cause the "blur" event while the SuperBoxSelect is in a scrollable container and the SuperBoxSelect is not visible. For example, scroll the SuperBoxSelect into view, click into the component, scroll so the SuperBoxSelect is not visible, and then click into open space or another component.

In both cases, the SuperBoxSelect jumps to a different area within the scrollable container, typically overlaying another component.

I would appreciate any help as I'm pretty stumped on this one.
Nick

nmohler
22 Apr 2010, 7:00 AM
I have seen a similar issue using the same doctype declaration. Removing the doctype eliminates the poor behavior, but introduces even bigger problems in other areas of the app.

I see the behavior in two scenarios:
1) The SuperBoxSelect is populated while the SuperBoxSelect is in a scrollable container and the SuperBoxSelect is not visible.
2) I cause the "blur" event while the SuperBoxSelect is in a scrollable container and the SuperBoxSelect is not visible. For example, scroll the SuperBoxSelect into view, click into the component, scroll so the SuperBoxSelect is not visible, and then click into open space or another component.

In both cases, the SuperBoxSelect jumps to a different area within the scrollable container, typically overlaying another component.

I would appreciate any help as I'm pretty stumped on this one.
Nick

In digging through this problem, it appears that it occurs when the SuperBoxSelect is within a container using the "Fit" layout. The problem goes away when I remove the "Fit" layout and size the container component using the height and width config options.

I'm still digging to determine why "Fit" creates an issue...

dhiren.lodhia
24 Apr 2010, 11:01 PM
i am using superboxselect in grid inside a row editor in column model.

while editing the row when individual item is removed from superboxselect first time it works fine second time it gives me an error saying

"
Message: 'this.dom' is null or not an object
Line: 5465
Char: 10
Code: 0
URI: http://localhost:8094/GA-Working/ext_js/ext-all-debug.js
"

can any one pls help its critical error in my project.....

Thanx in advance.........

here is some code i have :


var ioKRAColumns = new Ext.grid.ColumnModel({
columns: [
// new Ext.grid.RowNumberer(),
{
dataIndex : 'function_ids',
hidden: true,
hideable:false//User cant make this Column visible
},
{
header : "Function Involved",
width: 350,
height:250,
sortable : true,
dataIndex : 'function_names',
//editor: addKRA_functions_superbox
editor:{
xtype:'superboxselect',
id:'kra-functions-superbox',
//lazyRender: true,//should always be true for editor By enabling lazyRender this prevents the combo box from rendering until requested
resizable: true,
anchor:'100%',
mode: 'local',
forceFit: true,
store:functionComboStore,
displayField: 'combovalue',
displayFieldTpl: '{combovalue}',
valueField: 'comboid',
triggerAction: 'all',
typeAhead: true,
forceSelection :true,
allowBlank : false
}
},{
dataIndex : 'imperative_category_id',
hidden: true,
hideable:false//User cant make this Column visible
},{
header : "Categorization",
width : 200,
sortable : true,
dataIndex : 'imperative_category_name',
editor: new Ext.form.ComboBox({
id:'kra-imp-cat-combo',
emptyText:"Select Category",
store:imperativeCatComboStore,
displayField: 'combovalue',
valueField: 'comboid',
triggerAction: 'all',
forceSelection :true,
allowBlank : KRA_CATEGORY_ALLOW_BLANK
})
},{
dataIndex : 'kra_id',
hidden: true,
hideable:false//User cant make this Column visible
},{
header : "KRA",
width : 150,
sortable : true,
dataIndex : 'kra_name',
editor: new Ext.form.TextArea({
allowBlank : false
})
},{
dataIndex : 'initiative_id',
hidden: true,
hideable:false//User cant make this Column visible
},{
header : "Measure of Success",
width : 150,
sortable : true,
dataIndex : 'measure_of_success',
editor: new Ext.form.TextArea({
allowBlank : false
})
},{
header : "Milestone",
width : 150,
sortable : true,
dataIndex:'milestone',
editor: new Ext.form.TextArea({
allowBlank : true
})
},{
header : "Assigned To",
width : 150,
sortable : true,
//dataIndex:'milestone',
dataIndex : 'assignee_user_names',
editor: new Ext.form.TextArea({
allowBlank : false
})
},{
header : "Remarks",
width : 150,
sortable : true,
dataIndex : 'remarks',//
editor: new Ext.form.TextArea({
///allowBlank : false
})
},{
header : "Actual Users",
width : 350,
sortable : true,
dataIndex : 'actual_user_names',//
hidden:ACTUALUSERVISIBLE,
hideable:false,//User cant make this Column visible
editor: new Ext.ux.form.SuperBoxSelect({
id:'kra-actual-user-names',
resizable: true,
// store: userComboStore,
store:kraAssigneeUserComboStore,
mode: 'local',
displayField: 'combovalue',
displayFieldTpl: '{combovalue}',
valueField: 'comboid',
// queryDelay: 0,
triggerAction: 'all',
allowBlank:KRA_CATEGORY_ALLOW_BLANK
})
},{
header : "Actual User IDs",
dataIndex : 'actual_user_ids',
hidden: true,
hideable:false//User cant make this Column visible
}
]

pym
6 May 2010, 7:03 AM
Anyone else using this with 3.2.1 update having issues? No firebug errors but the combo boxes no longer display select list or values.

abraxxa
6 May 2010, 7:11 AM
Works ok here beside the known problems in areas which are collapsed or hidden on load.
Too bad the ux author doesn't even respond to private messages.

eztam
6 May 2010, 10:44 PM
Hi,
I'm using SuperBoxSelect and I've got a little problem with the (local) filtering.

I've got a displayFieldTpl:


...
displayFieldTpl: "{firstname} {lastname} ({email})",
...

Now I want to filter by this three fields (or).

So I need to have sth. like this:


...
displayField: [firstname, lastname, email],
...

How to do this?


Thanks eztam!

eztam
6 May 2010, 11:27 PM
I solved my problem.

I overwrote the doQuery function and added a class var called filterFields (array of the fields to filter).
If there are items in the filterFields array Im using:


this.store.filterBy(function(record, id) {
var ret = false;
Ext.each(this.filterFields, function(field) {
var regEx = new RegExp(q, "gi");
if (Ext.isDefined(record.data[field]) && record.data[field].match(regEx)) {
ret = true;
}
});
return ret;
}, this);

Instead of:


this.store.filter(this.displayField, q);

slamhan
10 May 2010, 6:21 PM
really nice~

Korbin
16 May 2010, 3:51 PM
Does anyone know how to make the selections appear below the input not above, or outside the field completely? Thanks!

abraxxa
16 May 2010, 11:55 PM
Did you try the stacked option?

Korbin
17 May 2010, 9:41 AM
Did you try the stacked option?
abraxxa: Yes I am using the stacked option already, but the selections appear within the field, and above the input cursor. I was looking to have the selections appear either below the input, or outside the field completely.

Tommi
6 Jun 2010, 10:30 PM
The download link seems to be broken. Anyone have the latest version up for download? Thanks!

mystix
12 Jun 2010, 6:43 AM
The download link seems to be broken. Anyone have the latest version up for download? Thanks!

download's fine. just use a non-IE browser.

Tommi
13 Jun 2010, 9:55 PM
download's fine. just use a non-IE browser.
I'm on Firefox 3. When I click the link, I get an error page stating "Invalid Attachment specified. If you followed a valid link, please notify the administrator". If I try to "Save link as...", the resulting download window refers to "attachment.php".

The link I'm using is the one included in the first post of this thread:
http://extjs.com/forum/attachment.php?attachmentid=13803&d=1243037046

mystix
13 Jun 2010, 10:40 PM
I'm on Firefox 3. When I click the link, I get an error page stating "Invalid Attachment specified. If you followed a valid link, please notify the administrator". If I try to "Save link as...", the resulting download window refers to "attachment.php".

The link I'm using is the one included in the first post of this thread:
http://extjs.com/forum/attachment.php?attachmentid=13803&d=1243037046

don't know where you got that attachment link from, but i just tried again using Chrome and it works fine.
i simply clicked the SuperBoxSelect.zip file from the very 1st post in this thread (found immediately under the green "Download" header):

http://grab.by/4V92

Tommi
13 Jun 2010, 11:19 PM
Unfortunately I cannot even see the green "Download" header at all. Tried with both IE and Firefox, unfortunately don't have Chrome on this computer. For me, the contents of the first post of this thread look like this:


Danh200 create this awesome extension.


Live example: http://technomedia.co.uk/SuperBoxSelect/examples3.html

Download: http://extjs.com/forum/attachment.php?attachmentid=13803&d=1243037046

Thread: http://extjs.com/forum/showthread.php?p=332654#post332654

Greetings,It just looks as if I somehow wasn't allowed to download attachments at all or something.

Condor
13 Jun 2010, 11:22 PM
The green 'Download' text can be found in the thread (3rd link).

I've edited the first post and corrected the download link.

mystix
14 Jun 2010, 4:56 AM
@tommi,

now i know where you got that link from.
i was getting the link to the first post from post #161 of this thread:
http://www.extjs.com/forum/showthread.php?t=69090

which is actually a link to the first post of a thread in the 2.x Ux forum, which then contains the correct download link.

oh well.

Tommi
14 Jun 2010, 9:43 PM
Got it now. Many thanks to both of you for your help!

siberian
17 Jun 2010, 12:10 AM
You just saved me a lot of time. Nice work!

shamansoft
21 Jun 2010, 4:13 AM
hi all!
dan, thanx for extension.
I'm using SuperBoxCombo on ExtJs 3.2.1 and I had a problem. After loading form's values the superbox's trigger wan't work. this is because of onEnable function which calls this.initButtonEvents(), and this functions attaches one more listener to 'click' event, so the drop-down list expands and collapses immediately. I solved this by adding calling this.removeButtonEvents() function in the beginning of the initButtonEvents() function.


initButtonEvents : function() {
this.removeButtonEvents();
this.buttonClear.addClassOnOver('x-superboxselect-btn-over').on('click', function(e) {
e.stopEvent();
if (this.disabled) {
return;
}
this.clearValue();
this.el.focus();
}, this);

this.buttonExpand.addClassOnOver('x-superboxselect-btn-over').on('click', function(e) {
e.stopEvent();
if (this.disabled) {
return;
}
if (this.isExpanded()) {
this.multiSelectMode = false;
} else if (this.pinList) {
this.multiSelectMode = true;
}
this.onTriggerClick();
}, this);
},

If you have better solutions - welcome!

Grolubao
28 Jun 2010, 12:02 AM
Great extension!!

I'm having a bug when adding the items. It seems it doesn't trigger the resize of the parent panel.

Let me explain with some screenshots.

This is the initial case:
21134

Now after I add some items the layout is broken:

21133


If I resize the window then automatically it gets resized properly.

Did any of you experienced such problem?

Grolubao
28 Jun 2010, 1:11 AM
Another problem that I detected is that once an item gets the focus it never loses that css again:

21135

Grolubao
30 Jun 2010, 12:25 AM
Another problem that I detected is that once an item gets the focus it never loses that css again:

21135

Does this css problem happen to anyone else?

polydyne
9 Jul 2010, 11:48 AM
Hello,
We have recently upgraded from Ext 3.0 to 3.2.1 and everything seems to be working fine ofcourse not without a few changes. However with superboxselect user extension we are seeing some weird behavior. It worked fine with previous ext version.

We have 2 superboxselectboxes on a filter window. e.g. filter by part number and filter by commodity.. now when u open the filter window for the first time both the boxes work and you can see the selections and change the selections etc. but then you filter the records and open the filter window again, the first selected superbox does not expand any more. the other superbox works fine.

I am attaching the code here, please take a look and help me out here.. thanks much.

Here is how I am opening the filter window..


handler: function(){
if (!filterWin) {
filterWin = new Ext.Window({
title: 'Awards Filter',
width: 500,
autoHeight: true,
closeAction:'hide',
border: true,
bodyBorder: false,
autoScroll: true,
modal: true,
bodyStyle:'padding:5px 5px 5px 5px',
items: [awardsFilterFormPanel],
layout: 'form'
});
}
filterWin.show();
filterWin.center();
}
Here is my awardsFilterFormPanel..


var awardsFilterFormPanel = new Ext.FormPanel({
id:'awardsFilterForm',
frame:true,
autoHeight: true,
items: [
{
allowBlank: true,
id:'display_parts_combo',
xtype:'combo',
fieldLabel: 'View By',
resizable: true,
name: 'display_parts_combo',
width: 350,
store: displayPartsStore,
mode: 'local',
triggerAction: 'all',
displayField: 'value',
hiddenName:'id',
valueField: 'id',
forceSelection : true,
allowBlank: false,
value:'<%=displayPartsId%>'
},
{
allowBlank: true,
id:'filter_part_number_combo',
xtype:'superboxselect',
fieldLabel: 'Part Numbers',
emptyText: 'Select Part Number',
resizable: true,
name: 'filter_part_number_combo',
width: 350,
store: partsStore,
mode: 'local',
displayField: 'part_name',
displayFieldTpl: '{part_name}',
valueField: 'part_id',
forceSelection: true,
value: '<%=partSelections%>'
},
{
allowBlank: true,
id:'filter_commodity_combo',
xtype:'superboxselect',
fieldLabel: 'Commodities',
emptyText: 'Select Commodity',
resizable: true,
name: 'filter_commodity_combo',
width: 350,
store: filterCommodityComboStore,
mode: 'local',
displayField: 'commodity_code',
displayFieldTpl: '{commodity_code}',
valueField: 'commodity_id',
forceSelection : true,
value: '<%=commSelections%>'
},
{
xtype: 'checkbox',
id: 'chk_sub_commodities',
fieldLabel: 'Include Sub Commodities',
boxLabel: '',
name: 'chk_sub_commodities',
checked: <%=includeSubCommodities.booleanValue()%>
}],
buttons: [
{xtype: 'tbfill'},
{
text: 'Filter',
formBind: true,
handler: function(){
if(true){
awardsFilterFormPanel.getForm().submit({
url: '<%=awardsFilterFormUrl%>',
method: 'POST',
waitMsg: '<%=waitMsgStr%>',
success: function(form, action){
awardsPricingStore.reload();
filterWin.hide();
},
failure: function(form, action){
//TO DO:
//add failure code
}
});
}
}
},
{
text: 'Reset Filter',
formBind: true,
handler: function(){
{
var conn = new Ext.data.Connection();
conn.request({
waitMsg: '<%=waitMsgStr%>',
url: '<%=resetAwardsFilterURL%>',
method: 'POST',
callback: function(opt,success,responseObj){
if (success) {
var responseData = Ext.util.JSON.decode(responseObj.responseText);
if ( responseData.success == true ) {
awardsFilterFormPanel.getForm().reset();
Ext.getCmp('filter_commodity_combo').clearValue();
Ext.getCmp('chk_sub_commodities').setValue('false');
awardsPricingStore.reload();
filterWin.hide();
}
} else {
Ext.Msg.alert('Error', 'Filter could not be reset, Please try again!');
}
}
});
}
}
},
{
text: 'Cancel',
handler: function(){
filterWin.hide();
}
}]
});


The issue may be is something to do with z-index value rendered by ext..

ash11tw
13 Jul 2010, 12:06 PM
Just for someone who need to use it in editorgrid in Ext 3.2.1
I have a simple workaround for making it work. (may have a better way)
First, you need to overwrite assertValue() to a empty function, because assertValue uses getRawValue() to verify the value and it seems like getRawValue() won't work in this component.
Second, You need to remove the css class ".ext-strict .x-small-editor .x-form-text" since it restricts the height of field to 16px.

dorgan
16 Jul 2010, 8:09 AM
Does this css problem happen to anyone else?


Yes I am also experiencing the problem in Firefox 3.6.6

dorgan
16 Jul 2010, 8:41 AM
OK for some reason I am not able to directly attach a JS file to this post but here is the fixed version. Apparently when adding a class for hover you can only add 1. originally that line read:


this.el.addClassOnOver('x-superboxselect-item x-superboxselect-item-hover');
So I changed it to:



this.el.addClassOnOver('x-superboxselect-item-hover');
I just started using this UX today so I figured I would contribute a little.



Ext.namespace('Ext.ux.form');
/**
* <p>SuperBoxSelect is an extension of the ComboBox component that displays selected items as labelled boxes within the form field. As seen on facebook, hotmail and other sites.</p>
* <p>The SuperBoxSelect component was inspired by the BoxSelect component found here: http://efattal.fr/en/extjs/extuxboxselect/</p>
*
* @author <a href="mailto:dan.humphrey@technomedia.co.uk">Dan Humphrey</a>
* @class Ext.ux.form.SuperBoxSelect
* @extends Ext.form.ComboBox
* @constructor
* @component
* @version 1.0
* @license TBA (To be announced)
*
*/
Ext.ux.form.SuperBoxSelect = function(config) {
Ext.ux.form.SuperBoxSelect.superclass.constructor.call(this,config);
this.addEvents(
/**
* Fires before an item is added to the component via user interaction. Return false from the callback function to prevent the item from being added.
* @event beforeadditem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The value of the item to be added
*/
'beforeadditem',

/**
* Fires after a new item is added to the component.
* @event additem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The value of the item which was added
* @param {Record} record The store record which was added
*/
'additem',

/**
* Fires when the allowAddNewData config is set to true, and a user attempts to add an item that is not in the data store.
* @event newitem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The new item's value
*/
'newitem',

/**
* Fires when an item's remove button is clicked. Return false from the callback function to prevent the item from being removed.
* @event beforeremoveitem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The value of the item to be removed
*/
'beforeremoveitem',

/**
* Fires after an item has been removed.
* @event removeitem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The value of the item which was removed
* @param {Record} record The store record which was removed
*/
'removeitem',
/**
* Fires after the component values have been cleared.
* @event clear
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
*/
'clear'
);

};
/**
* @private hide from doc gen
*/
Ext.ux.form.SuperBoxSelect = Ext.extend(Ext.ux.form.SuperBoxSelect,Ext.form.ComboBox,{
/**
* @cfg {Boolean} allowAddNewData When set to true, allows items to be added (via the setValueEx and addItem methods) that do not already exist in the data store. Defaults to false.
*/
allowAddNewData: false,

/**
* @cfg {Boolean} backspaceDeletesLastItem When set to false, the BACKSPACE key will focus the last selected item. When set to true, the last item will be immediately deleted. Defaults to true.
*/
backspaceDeletesLastItem: true,

/**
* @cfg {String} classField The underlying data field that will be used to supply an additional class to each item.
*/
classField: null,

/**
* @cfg {String} clearBtnCls An additional class to add to the in-field clear button.
*/
clearBtnCls: '',

/**
* @cfg {String/XTemplate} displayFieldTpl A template for rendering the displayField in each selected item. Defaults to null.
*/
displayFieldTpl: null,

/**
* @cfg {String} extraItemCls An additional css class to apply to each item.
*/
extraItemCls: '',

/**
* @cfg {String/Object/Function} extraItemStyle Additional css style(s) to apply to each item. Should be a valid argument to Ext.Element.applyStyles.
*/
extraItemStyle: '',

/**
* @cfg {String} expandBtnCls An additional class to add to the in-field expand button.
*/
expandBtnCls: '',

/**
* @cfg {Boolean} fixFocusOnTabSelect When set to true, the component will not lose focus when a list item is selected with the TAB key. Defaults to true.
*/
fixFocusOnTabSelect: true,

/**
* @cfg {Boolean} forceFormValue When set to true, the component will always return a value to the parent form getValues method, and when the parent form is submitted manually. Defaults to false, meaning the component will only be included in the parent form submission (or getValues) if at least 1 item has been selected.
*/
forceFormValue: true,
/**
* @cfg {Number} itemDelimiterKey The key code which terminates keying in of individual items, and adds the current
* item to the list. Defaults to the ENTER key.
*/
itemDelimiterKey: Ext.EventObject.ENTER,
/**
* @cfg {Boolean} navigateItemsWithTab When set to true the tab key will navigate between selected items. Defaults to true.
*/
navigateItemsWithTab: true,

/**
* @cfg {Boolean} pinList When set to true the select list will be pinned to allow for multiple selections. Defaults to true.
*/
pinList: true,

/**
* @cfg {Boolean} preventDuplicates When set to true unique item values will be enforced. Defaults to true.
*/
preventDuplicates: true,

/**
* @cfg {String} queryValuesDelimiter Used to delimit multiple values queried from the server when mode is remote.
*/
queryValuesDelimiter: '|',

/**
* @cfg {String} queryValuesIndicator A request variable that is sent to the server (as true) to indicate that we are querying values rather than display data (as used in autocomplete) when mode is remote.
*/
queryValuesIndicator: 'valuesqry',

/**
* @cfg {Boolean} removeValuesFromStore When set to true, selected records will be removed from the store. Defaults to true.
*/
removeValuesFromStore: true,

/**
* @cfg {String} renderFieldBtns When set to true, will render in-field buttons for clearing the component, and displaying the list for selection. Defaults to true.
*/
renderFieldBtns: true,

/**
* @cfg {Boolean} stackItems When set to true, the items will be stacked 1 per line. Defaults to false which displays the items inline.
*/
stackItems: false,

/**
* @cfg {String} styleField The underlying data field that will be used to supply additional css styles to each item.
*/
styleField : null,

/**
* @cfg {Boolean} supressClearValueRemoveEvents When true, the removeitem event will not be fired for each item when the clearValue method is called, or when the clear button is used. Defaults to false.
*/
supressClearValueRemoveEvents : false,

/**
* @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable automatic validation (defaults to 'blur').
*/
validationEvent : 'blur',

/**
* @cfg {String} valueDelimiter The delimiter to use when joining and splitting value arrays and strings.
*/
valueDelimiter: ',',
initComponent:function() {
Ext.apply(this, {
items : new Ext.util.MixedCollection(false),
usedRecords : new Ext.util.MixedCollection(false),
addedRecords : [],
remoteLookup : [],
hideTrigger : true,
grow : false,
resizable : false,
multiSelectMode : false,
preRenderValue : null
});

if(this.transform){
this.doTransform();
}
if(this.forceFormValue){
this.items.on({
add: this.manageNameAttribute,
remove: this.manageNameAttribute,
clear: this.manageNameAttribute,
scope: this
});
}

Ext.ux.form.SuperBoxSelect.superclass.initComponent.call(this);
if(this.mode === 'remote' && this.store){
this.store.on('load', this.onStoreLoad, this);
}
},
onRender:function(ct, position) {
var h = this.hiddenName;
this.hiddenName = null;
Ext.ux.form.SuperBoxSelect.superclass.onRender.call(this, ct, position);
this.hiddenName = h;
this.manageNameAttribute();

var extraClass = (this.stackItems === true) ? 'x-superboxselect-stacked' : '';
if(this.renderFieldBtns){
extraClass += ' x-superboxselect-display-btns';
}
this.el.removeClass('x-form-text').addClass('x-superboxselect-input-field');

this.wrapEl = this.el.wrap({
tag : 'ul'
});

this.outerWrapEl = this.wrapEl.wrap({
tag : 'div',
cls: 'x-form-text x-superboxselect ' + extraClass
});

this.inputEl = this.el.wrap({
tag : 'li',
cls : 'x-superboxselect-input'
});

if(this.renderFieldBtns){
this.setupFieldButtons().manageClearBtn();
}

this.setupFormInterception();
},
onStoreLoad : function(store, records, options){
//accomodating for bug in Ext 3.0.0 where options.params are empty
var q = options.params[this.queryParam] || store.baseParams[this.queryParam] || "",
isValuesQuery = options.params[this.queryValuesIndicator] || store.baseParams[this.queryValuesIndicator];

if(this.removeValuesFromStore){
this.store.each(function(record) {
if(this.usedRecords.containsKey(record.get(this.valueField))){
this.store.remove(record);
}
}, this);
}
//queried values
if(isValuesQuery){
var params = q.split(this.queryValuesDelimiter);
Ext.each(params,function(p){
this.remoteLookup.remove(p);
var rec = this.findRecord(this.valueField,p);
if(rec){
this.addRecord(rec);
}
},this);

if(this.setOriginal){
this.setOriginal = false;
this.originalValue = this.getValue();
}
}

//queried display (autocomplete) & addItem
if(q !== '' && this.allowAddNewData){
Ext.each(this.remoteLookup,function(r){
if(typeof r == "object" && r[this.displayField] == q){
this.remoteLookup.remove(r);
if(records.length && records[0].get(this.displayField) === q) {
this.addRecord(records[0]);
return;
}
var rec = this.createRecord(r);
this.store.add(rec);
this.addRecord(rec);
this.addedRecords.push(rec); //keep track of records added to store
(function(){
if(this.isExpanded()){
this.collapse();
}
}).defer(10,this);
return;
}
},this);
}

var toAdd = [];
if(q === ''){
Ext.each(this.addedRecords,function(rec){
if(this.preventDuplicates && this.usedRecords.containsKey(rec.get(this.valueField))){
return;
}
toAdd.push(rec);

},this);

}else{
var re = new RegExp(Ext.escapeRe(q) + '.*','i');
Ext.each(this.addedRecords,function(rec){
if(this.preventDuplicates && this.usedRecords.containsKey(rec.get(this.valueField))){
return;
}
if(re.test(rec.get(this.displayField))){
toAdd.push(rec);
}
},this);
}
this.store.add(toAdd);
this.store.sort(this.displayField, 'ASC');

if(this.store.getCount() === 0 && this.isExpanded()){
this.collapse();
}

},
doTransform : function() {
var s = Ext.getDom(this.transform), transformValues = [];
if(!this.store){
this.mode = 'local';
var d = [], opts = s.options;
for(var i = 0, len = opts.length;i < len; i++){
var o = opts[i], oe = Ext.get(o),
value = oe.getAttributeNS(null,'value') || '',
cls = oe.getAttributeNS(null,'className') || '',
style = oe.getAttributeNS(null,'style') || '';
if(o.selected) {
transformValues.push(value);
}
d.push([value, o.text, cls, typeof(style) === "string" ? style : style.cssText]);
}
this.store = new Ext.data.SimpleStore({
'id': 0,
fields: ['value', 'text', 'cls', 'style'],
data : d
});
Ext.apply(this,{
valueField: 'value',
displayField: 'text',
classField: 'cls',
styleField: 'style'
});
}

if(transformValues.length){
this.value = transformValues.join(',');
}
},
setupFieldButtons : function(){
this.buttonWrap = this.outerWrapEl.createChild({
cls: 'x-superboxselect-btns'
});

this.buttonClear = this.buttonWrap.createChild({
tag:'div',
cls: 'x-superboxselect-btn-clear ' + this.clearBtnCls
});

this.buttonExpand = this.buttonWrap.createChild({
tag:'div',
cls: 'x-superboxselect-btn-expand ' + this.expandBtnCls
});

this.initButtonEvents();

return this;
},
initButtonEvents : function() {
this.buttonClear.addClassOnOver('x-superboxselect-btn-over').on('click', function(e) {
e.stopEvent();
if (this.disabled) {
return;
}
this.clearValue();
this.el.focus();
}, this);

this.buttonExpand.addClassOnOver('x-superboxselect-btn-over').on('click', function(e) {
e.stopEvent();
if (this.disabled) {
return;
}
if (this.isExpanded()) {
this.multiSelectMode = false;
} else if (this.pinList) {
this.multiSelectMode = true;
}
this.onTriggerClick();
}, this);
},
removeButtonEvents : function() {
this.buttonClear.removeAllListeners();
this.buttonExpand.removeAllListeners();
return this;
},
clearCurrentFocus : function(){
if(this.currentFocus){
this.currentFocus.onLnkBlur();
this.currentFocus = null;
}
return this;
},
initEvents : function() {
var el = this.el;

el.on({
click : this.onClick,
focus : this.clearCurrentFocus,
blur : this.onBlur,

keydown : this.onKeyDownHandler,
keyup : this.onKeyUpBuffered,

scope : this
});

this.on({
collapse: this.onCollapse,
expand: this.clearCurrentFocus,
scope: this
});

this.wrapEl.on('click', this.onWrapClick, this);
this.outerWrapEl.on('click', this.onWrapClick, this);

this.inputEl.focus = function() {
el.focus();
};

Ext.ux.form.SuperBoxSelect.superclass.initEvents.call(this);

Ext.apply(this.keyNav, {
tab: function(e) {
if (this.fixFocusOnTabSelect && this.isExpanded()) {
e.stopEvent();
el.blur();
this.onViewClick(false);
this.focus(false, 10);
return true;
}

this.onViewClick(false);
if (el.dom.value !== '') {
this.setRawValue('');
}

return true;
},

down: function(e) {
if (!this.isExpanded() && !this.currentFocus) {
this.onTriggerClick();
} else {
this.inKeyMode = true;
this.selectNext();
}
},

enter: function(){}
});
},

onClick: function() {
this.clearCurrentFocus();
this.collapse();
this.autoSize();
},

beforeBlur: Ext.form.ComboBox.superclass.beforeBlur,

onFocus: function() {
this.outerWrapEl.addClass(this.focusClass);

Ext.ux.form.SuperBoxSelect.superclass.onFocus.call(this);
},

onBlur: function() {
this.outerWrapEl.removeClass(this.focusClass);

this.clearCurrentFocus();

if (this.el.dom.value !== '') {
this.applyEmptyText();
this.autoSize();
}

Ext.ux.form.SuperBoxSelect.superclass.onBlur.call(this);
},

onCollapse: function() {
this.view.clearSelections();
this.multiSelectMode = false;
},

onWrapClick: function(e) {
e.stopEvent();
this.collapse();
this.el.focus();
this.clearCurrentFocus();
},
markInvalid : function(msg) {
var elp, t;

if (!this.rendered || this.preventMark ) {
return;
}
this.outerWrapEl.addClass(this.invalidClass);
msg = msg || this.invalidText;

switch (this.msgTarget) {
case 'qtip':
Ext.apply(this.el.dom, {
qtip : msg,
qclass : 'x-form-invalid-tip'
});
Ext.apply(this.wrapEl.dom, {
qtip : msg,
qclass : 'x-form-invalid-tip'
});
if (Ext.QuickTips) { // fix for floating editors interacting with DND
Ext.QuickTips.enable();
}
break;
case 'title':
this.el.dom.title = msg;
this.wrapEl.dom.title = msg;
this.outerWrapEl.dom.title = msg;
break;
case 'under':
if (!this.errorEl) {
elp = this.getErrorCt();
if (!elp) { // field has no container el
this.el.dom.title = msg;
break;
}
this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
this.errorEl.setWidth(elp.getWidth(true) - 20);
}
this.errorEl.update(msg);
Ext.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
break;
case 'side':
if (!this.errorIcon) {
elp = this.getErrorCt();
if (!elp) { // field has no container el
this.el.dom.title = msg;
break;
}
this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
}
this.alignErrorIcon();
Ext.apply(this.errorIcon.dom, {
qtip : msg,
qclass : 'x-form-invalid-tip'
});
this.errorIcon.show();
this.on('resize', this.alignErrorIcon, this);
break;
default:
t = Ext.getDom(this.msgTarget);
t.innerHTML = msg;
t.style.display = this.msgDisplay;
break;
}
this.fireEvent('invalid', this, msg);
},
clearInvalid : function(){
if(!this.rendered || this.preventMark){ // not rendered
return;
}
this.outerWrapEl.removeClass(this.invalidClass);
switch(this.msgTarget){
case 'qtip':
this.el.dom.qtip = '';
this.wrapEl.dom.qtip ='';
break;
case 'title':
this.el.dom.title = '';
this.wrapEl.dom.title = '';
this.outerWrapEl.dom.title = '';
break;
case 'under':
if(this.errorEl){
Ext.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
}
break;
case 'side':
if(this.errorIcon){
this.errorIcon.dom.qtip = '';
this.errorIcon.hide();
this.un('resize', this.alignErrorIcon, this);
}
break;
default:
var t = Ext.getDom(this.msgTarget);
t.innerHTML = '';
t.style.display = 'none';
break;
}
this.fireEvent('valid', this);
},
alignErrorIcon : function(){
if(this.wrap){
this.errorIcon.alignTo(this.wrap, 'tl-tr', [Ext.isIE ? 5 : 2, 3]);
}
},
expand : function(){
if (this.isExpanded() || !this.hasFocus) {
return;
}
this.list.alignTo(this.outerWrapEl, this.listAlign).show();
this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
Ext.getDoc().on({
mousewheel: this.collapseIf,
mousedown: this.collapseIf,
scope: this
});
this.fireEvent('expand', this);
},
restrictHeight : function(){
var inner = this.innerList.dom,
st = inner.scrollTop,
list = this.list;

inner.style.height = '';

var pad = list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight,
h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
space = Math.max(ha, hb, this.minHeight || 0)-list.shadowOffset-pad-5;

h = Math.min(h, space, this.maxHeight);
this.innerList.setHeight(h);

list.beginUpdate();
list.setHeight(h+pad);
list.alignTo(this.outerWrapEl, this.listAlign);
list.endUpdate();

if(this.multiSelectMode){
inner.scrollTop = st;
}
},

validateValue: function(val){
if(this.items.getCount() === 0){
if(this.allowBlank){
this.clearInvalid();
return true;
}else{
this.markInvalid(this.blankText);
return false;
}
}

this.clearInvalid();
return true;
},

manageNameAttribute : function(){
if(this.items.getCount() === 0 && this.forceFormValue){
this.el.dom.setAttribute('name', this.hiddenName || this.name);
}else{
this.el.dom.removeAttribute('name');
}
},
setupFormInterception : function(){
var form;
this.findParentBy(function(p){
if(p.getForm){
form = p.getForm();
}
});
if(form){

var formGet = form.getValues;
form.getValues = function(asString){
this.el.dom.disabled = true;
var oldVal = this.el.dom.value;
this.setRawValue('');
var vals = formGet.call(form);
this.el.dom.disabled = false;
this.setRawValue(oldVal);
if(this.forceFormValue && this.items.getCount() === 0){
vals[this.name] = '';
}
return asString ? Ext.urlEncode(vals) : vals ;
}.createDelegate(this);
}
},
onResize : function(w, h, rw, rh) {
var reduce = Ext.isIE6 ? 4 : Ext.isIE7 ? 1 : Ext.isIE8 ? 1 : 0;
if(this.wrapEl){
this._width = w;
this.outerWrapEl.setWidth(w - reduce);
if (this.renderFieldBtns) {
reduce += (this.buttonWrap.getWidth() + 20);
this.wrapEl.setWidth(w - reduce);
}
}
Ext.ux.form.SuperBoxSelect.superclass.onResize.call(this, w, h, rw, rh);
this.autoSize();
},
onEnable: function(){
Ext.ux.form.SuperBoxSelect.superclass.onEnable.call(this);
this.items.each(function(item){
item.enable();
});
if (this.renderFieldBtns) {
this.initButtonEvents();
}
},
onDisable: function(){
Ext.ux.form.SuperBoxSelect.superclass.onDisable.call(this);
this.items.each(function(item){
item.disable();
});
if(this.renderFieldBtns){
this.removeButtonEvents();
}
},
/**
* Clears all values from the component.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name clearValue
* @param {Boolean} supressRemoveEvent [Optional] When true, the 'removeitem' event will not fire for each item that is removed.
*/
clearValue : function(supressRemoveEvent){
Ext.ux.form.SuperBoxSelect.superclass.clearValue.call(this);
this.preventMultipleRemoveEvents = supressRemoveEvent || this.supressClearValueRemoveEvents || false;
this.removeAllItems();
this.preventMultipleRemoveEvents = false;
this.fireEvent('clear',this);
return this;
},
onKeyUp : function(e) {
if (this.editable !== false && (!e.isSpecialKey() || e.getKey() === e.BACKSPACE) && e.getKey() !== this.itemDelimiterKey && (!e.hasModifier() || e.shiftKey)) {
this.lastKey = e.getKey();
this.dqTask.delay(this.queryDelay);
}
},
onKeyDownHandler : function(e,t) {

var toDestroy,nextFocus,idx;
if ((e.getKey() === e.DELETE || e.getKey() === e.SPACE) && this.currentFocus){
e.stopEvent();
toDestroy = this.currentFocus;
this.on('expand',function(){this.collapse();},this,{single: true});
idx = this.items.indexOfKey(this.currentFocus.key);

this.clearCurrentFocus();

if(idx < (this.items.getCount() -1)){
nextFocus = this.items.itemAt(idx+1);
}

toDestroy.preDestroy(true);
if(nextFocus){
(function(){
nextFocus.onLnkFocus();
this.currentFocus = nextFocus;
}).defer(200,this);
}

return true;
}

var val = this.el.dom.value, it, ctrl = e.ctrlKey;
if(e.getKey() === this.itemDelimiterKey){
e.stopEvent();
if (val !== "") {
if (ctrl || !this.isExpanded()) { //ctrl+enter for new items
this.view.clearSelections();
this.collapse();
this.setRawValue('');
this.fireEvent('newitem', this, val);
}
else {
this.onViewClick();
//removed from 3.0.1
if(this.unsetDelayCheck){
this.delayedCheck = true;
this.unsetDelayCheck.defer(10, this);
}
}
}else{
if(!this.isExpanded()){
return;
}
this.onViewClick();
//removed from 3.0.1
if(this.unsetDelayCheck){
this.delayedCheck = true;
this.unsetDelayCheck.defer(10, this);
}
}
return true;
}

if(val !== '') {
this.autoSize();
return;
}

//select first item
if(e.getKey() === e.HOME){
e.stopEvent();
if(this.items.getCount() > 0){
this.collapse();
it = this.items.get(0);
it.el.focus();

}
return true;
}
//backspace remove
if(e.getKey() === e.BACKSPACE){
e.stopEvent();
if(this.currentFocus) {
toDestroy = this.currentFocus;
this.on('expand',function(){
this.collapse();
},this,{single: true});

idx = this.items.indexOfKey(toDestroy.key);

this.clearCurrentFocus();
if(idx < (this.items.getCount() -1)){
nextFocus = this.items.itemAt(idx+1);
}

toDestroy.preDestroy(true);

if(nextFocus){
(function(){
nextFocus.onLnkFocus();
this.currentFocus = nextFocus;
}).defer(200,this);
}

return;
}else{
it = this.items.get(this.items.getCount() -1);
if(it){
if(this.backspaceDeletesLastItem){
this.on('expand',function(){this.collapse();},this,{single: true});
it.preDestroy(true);
}else{
if(this.navigateItemsWithTab){
it.onElClick();
}else{
this.on('expand',function(){
this.collapse();
this.currentFocus = it;
this.currentFocus.onLnkFocus.defer(20,this.currentFocus);
},this,{single: true});
}
}
}
return true;
}
}

if(!e.isNavKeyPress()){
this.multiSelectMode = false;
this.clearCurrentFocus();
return;
}
//arrow nav
if(e.getKey() === e.LEFT || (e.getKey() === e.UP && !this.isExpanded())){
e.stopEvent();
this.collapse();
//get last item
it = this.items.get(this.items.getCount()-1);
if(this.navigateItemsWithTab){
//focus last el
if(it){
it.focus();
}
}else{
//focus prev item
if(this.currentFocus){
idx = this.items.indexOfKey(this.currentFocus.key);
this.clearCurrentFocus();

if(idx !== 0){
this.currentFocus = this.items.itemAt(idx-1);
this.currentFocus.onLnkFocus();
}
}else{
this.currentFocus = it;
if(it){
it.onLnkFocus();
}
}
}
return true;
}
if(e.getKey() === e.DOWN){
if(this.currentFocus){
this.collapse();
e.stopEvent();
idx = this.items.indexOfKey(this.currentFocus.key);
if(idx == (this.items.getCount() -1)){
this.clearCurrentFocus.defer(10,this);
}else{
this.clearCurrentFocus();
this.currentFocus = this.items.itemAt(idx+1);
if(this.currentFocus){
this.currentFocus.onLnkFocus();
}
}
return true;
}
}
if(e.getKey() === e.RIGHT){
this.collapse();
it = this.items.itemAt(0);
if(this.navigateItemsWithTab){
//focus first el
if(it){
it.focus();
}
}else{
if(this.currentFocus){
idx = this.items.indexOfKey(this.currentFocus.key);
this.clearCurrentFocus();
if(idx < (this.items.getCount() -1)){
this.currentFocus = this.items.itemAt(idx+1);
if(this.currentFocus){
this.currentFocus.onLnkFocus();
}
}
}else{
this.currentFocus = it;
if(it){
it.onLnkFocus();
}
}
}
}
},
onKeyUpBuffered : function(e){
if(!e.isNavKeyPress()){
this.autoSize();
}
},
reset : function(){
this.killItems();
Ext.ux.form.SuperBoxSelect.superclass.reset.call(this);
this.addedRecords = [];
this.autoSize().setRawValue('');
},
applyEmptyText : function(){
this.setRawValue('');
if(this.items.getCount() > 0){
this.el.removeClass(this.emptyClass);
this.setRawValue('');
return this;
}
if(this.rendered && this.emptyText && this.getRawValue().length < 1){
this.setRawValue(this.emptyText);
this.el.addClass(this.emptyClass);
}
return this;
},
/**
* @private
*
* Use clearValue instead
*/
removeAllItems: function(){
this.items.each(function(item){
item.preDestroy(true);
},this);
this.manageClearBtn();
return this;
},
killItems : function(){
this.items.each(function(item){
item.kill();
},this);
this.resetStore();
this.items.clear();
this.manageClearBtn();
return this;
},
resetStore: function(){
this.store.clearFilter();
if(!this.removeValuesFromStore){
return this;
}
this.usedRecords.each(function(rec){
this.store.add(rec);
},this);
this.usedRecords.clear();
this.sortStore();
return this;
},
sortStore: function(){
var ss = this.store.getSortState();
if(ss && ss.field){
this.store.sort(ss.field, ss.direction);
}
return this;
},
getCaption: function(dataObject){
if(typeof this.displayFieldTpl === 'string') {
this.displayFieldTpl = new Ext.XTemplate(this.displayFieldTpl);
}
var caption, recordData = dataObject instanceof Ext.data.Record ? dataObject.data : dataObject;

if(this.displayFieldTpl) {
caption = this.displayFieldTpl.apply(recordData);
} else if(this.displayField) {
caption = recordData[this.displayField];
}

return caption;
},
addRecord : function(record) {
var display = record.data[this.displayField],
caption = this.getCaption(record),
val = record.data[this.valueField],
cls = this.classField ? record.data[this.classField] : '',
style = this.styleField ? record.data[this.styleField] : '';

if (this.removeValuesFromStore) {
this.usedRecords.add(val, record);
this.store.remove(record);
}

this.addItemBox(val, display, caption, cls, style);
this.fireEvent('additem', this, val, record);
},
createRecord : function(recordData){
if(!this.recordConstructor){
var recordFields = [
{name: this.valueField},
{name: this.displayField}
];
if(this.classField){
recordFields.push({name: this.classField});
}
if(this.styleField){
recordFields.push({name: this.styleField});
}
this.recordConstructor = Ext.data.Record.create(recordFields);
}
return new this.recordConstructor(recordData);
},
/**
* Adds an array of items to the SuperBoxSelect component if the {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set to true.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name addItem
* @param {Array} newItemObjects An Array of object literals containing the property names and values for an item. The property names must match those specified in {@link #Ext.ux.form.SuperBoxSelect-displayField}, {@link #Ext.ux.form.SuperBoxSelect-valueField} and {@link #Ext.ux.form.SuperBoxSelect-classField}
*/
addItems : function(newItemObjects){
if (Ext.isArray(newItemObjects)) {
Ext.each(newItemObjects, function(item) {
this.addItem(item);
}, this);
} else {
this.addItem(newItemObjects);
}
},
/**
* Adds a new non-existing item to the SuperBoxSelect component if the {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set to true.
* This method should be used in place of addItem from within the newitem event handler.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name addNewItem
* @param {Object} newItemObject An object literal containing the property names and values for an item. The property names must match those specified in {@link #Ext.ux.form.SuperBoxSelect-displayField}, {@link #Ext.ux.form.SuperBoxSelect-valueField} and {@link #Ext.ux.form.SuperBoxSelect-classField}
*/
addNewItem : function(newItemObject){
this.addItem(newItemObject,true);
},
/**
* Adds an item to the SuperBoxSelect component if the {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set to true.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name addItem
* @param {Object} newItemObject An object literal containing the property names and values for an item. The property names must match those specified in {@link #Ext.ux.form.SuperBoxSelect-displayField}, {@link #Ext.ux.form.SuperBoxSelect-valueField} and {@link #Ext.ux.form.SuperBoxSelect-classField}
*/
addItem : function(newItemObject, /*hidden param*/ forcedAdd){

var val = newItemObject[this.valueField];

if(this.disabled) {
return false;
}
if(this.preventDuplicates && this.hasValue(val)){
return;
}

//use existing record if found
var record = this.findRecord(this.valueField, val);
if (record) {
this.addRecord(record);
return;
} else if (!this.allowAddNewData) { // else it's a new item
return;
}

if(this.mode === 'remote'){
this.remoteLookup.push(newItemObject);
this.doQuery(val,false,false,forcedAdd);
return;
}

var rec = this.createRecord(newItemObject);
this.store.add(rec);
this.addRecord(rec);

return true;
},
addItemBox : function(itemVal,itemDisplay,itemCaption, itemClass, itemStyle) {
var hConfig, parseStyle = function(s){
var ret = '';
if(typeof s == 'function'){
ret = s.call();
}else if(typeof s == 'object'){
for(var p in s){
ret+= p +':'+s[p]+';';
}
}else if(typeof s == 'string'){
ret = s + ';';
}
return ret;
}, itemKey = Ext.id(null,'sbx-item'), box = new Ext.ux.form.SuperBoxSelectItem({
owner: this,
disabled: this.disabled,
renderTo: this.wrapEl,
cls: this.extraItemCls + ' ' + itemClass,
style: parseStyle(this.extraItemStyle) + ' ' + itemStyle,
caption: itemCaption,
display: itemDisplay,
value: itemVal,
key: itemKey,
listeners: {
'remove': function(item){
if(this.fireEvent('beforeremoveitem',this,item.value) === false){
return;
}
this.items.removeKey(item.key);
if(this.removeValuesFromStore){
if(this.usedRecords.containsKey(item.value)){
this.store.add(this.usedRecords.get(item.value));
this.usedRecords.removeKey(item.value);
this.sortStore();
if(this.view){
this.view.render();
}
}
}
if(!this.preventMultipleRemoveEvents){
this.fireEvent.defer(250,this,['removeitem',this,item.value, this.findInStore(item.value)]);
}
},
destroy: function(){
this.collapse();
this.autoSize().manageClearBtn().validateValue();
},
scope: this
}
});
box.render();

hConfig = {
tag :'input',
type :'hidden',
value : itemVal,
name : (this.hiddenName || this.name)
};

if(this.disabled){
Ext.apply(hConfig,{
disabled : 'disabled'
})
}
box.hidden = this.el.insertSibling(hConfig,'before');

this.items.add(itemKey,box);
this.applyEmptyText().autoSize().manageClearBtn().validateValue();
},
manageClearBtn : function() {
if (!this.renderFieldBtns || !this.rendered) {
return this;
}
var cls = 'x-superboxselect-btn-hide';
if (this.items.getCount() === 0) {
this.buttonClear.addClass(cls);
} else {
this.buttonClear.removeClass(cls);
}
return this;
},
findInStore : function(val){
var index = this.store.find(this.valueField, val);
if(index > -1){
return this.store.getAt(index);
}
return false;
},
/**
* Returns a String value containing a concatenated list of item values. The list is concatenated with the {@link #Ext.ux.form.SuperBoxSelect-valueDelimiter}.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name getValue
* @return {String} a String value containing a concatenated list of item values.
*/
getValue : function() {
var ret = [];
this.items.each(function(item){
ret.push(item.value);
});
return ret.join(this.valueDelimiter);
},
/**
* Returns an Array of item objects containing the {@link #Ext.ux.form.SuperBoxSelect-displayField}, {@link #Ext.ux.form.SuperBoxSelect-valueField}, {@link #Ext.ux.form.SuperBoxSelect-classField} and {@link #Ext.ux.form.SuperBoxSelect-styleField} properties.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name getValueEx
* @return {Array} an array of item objects.
*/
getValueEx : function() {
var ret = [];
this.items.each(function(item){
var newItem = {};
newItem[this.valueField] = item.value;
newItem[this.displayField] = item.display;
if(this.classField){
newItem[this.classField] = item.cls || '';
}
if(this.styleField){
newItem[this.styleField] = item.style || '';
}
ret.push(newItem);
},this);
return ret;
},
// private
initValue : function(){

Ext.ux.form.SuperBoxSelect.superclass.initValue.call(this);
if(this.mode === 'remote') {
this.setOriginal = true;
}
},
/**
* Sets the value of the SuperBoxSelect component.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name setValue
* @param {String|Array} value An array of item values, or a String value containing a delimited list of item values. (The list should be delimited with the {@link #Ext.ux.form.SuperBoxSelect-valueDelimiter)
*/
setValue : function(value){
if(!this.rendered){
this.value = value;
return;
}

this.removeAllItems().resetStore();
this.remoteLookup = [];

if(Ext.isEmpty(value)){
return;
}

var values = value;
if(!Ext.isArray(value)){
value = '' + value;
values = value.split(this.valueDelimiter);
}

Ext.each(values,function(val){
var record = this.findRecord(this.valueField, val);
if(record){
this.addRecord(record);
}else if(this.mode === 'remote'){
this.remoteLookup.push(val);
}
},this);

if(this.mode === 'remote'){
var q = this.remoteLookup.join(this.queryValuesDelimiter);
this.doQuery(q,false, true); //3rd param to specify a values query
}

},
/**
* Sets the value of the SuperBoxSelect component, adding new items that don't exist in the data store if the {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set to true.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name setValue
* @param {Array} data An Array of item objects containing the {@link #Ext.ux.form.SuperBoxSelect-displayField}, {@link #Ext.ux.form.SuperBoxSelect-valueField} and {@link #Ext.ux.form.SuperBoxSelect-classField} properties.
*/
setValueEx : function(data){
this.removeAllItems().resetStore();

if(!Ext.isArray(data)){
data = [data];
}
this.remoteLookup = [];

if(this.allowAddNewData && this.mode === 'remote'){ // no need to query
Ext.each(data, function(d){
var r = this.findRecord(this.valueField, d[this.valueField]) || this.createRecord(d);
this.addRecord(r);
},this);
return;
}

Ext.each(data,function(item){
this.addItem(item);
},this);
},
/**
* Returns true if the SuperBoxSelect component has a selected item with a value matching the 'val' parameter.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name hasValue
* @param {Mixed} val The value to test.
* @return {Boolean} true if the component has the selected value, false otherwise.
*/
hasValue: function(val){
var has = false;
this.items.each(function(item){
if(item.value == val){
has = true;
return false;
}
},this);
return has;
},
onSelect : function(record, index) {
if (this.fireEvent('beforeselect', this, record, index) !== false){
var val = record.data[this.valueField];

if(this.preventDuplicates && this.hasValue(val)){
return;
}

this.setRawValue('');
this.lastSelectionText = '';

if(this.fireEvent('beforeadditem',this,val) !== false){
this.addRecord(record);
}
if(this.store.getCount() === 0 || !this.multiSelectMode){
this.collapse();
}else{
this.restrictHeight();
}
}
},
onDestroy : function() {
this.items.purgeListeners();
this.killItems();
if (this.renderFieldBtns) {
Ext.destroy(
this.buttonClear,
this.buttonExpand,
this.buttonWrap
);
}

Ext.destroy(
this.inputEl,
this.wrapEl,
this.outerWrapEl
);

Ext.ux.form.SuperBoxSelect.superclass.onDestroy.call(this);
},
autoSize : function(){
if(!this.rendered){
return this;
}
if(!this.metrics){
this.metrics = Ext.util.TextMetrics.createInstance(this.el);
}
var el = this.el,
v = el.dom.value,
d = document.createElement('div');

if(v === "" && this.emptyText && this.items.getCount() < 1){
v = this.emptyText;
}
d.appendChild(document.createTextNode(v));
v = d.innerHTML;
d = null;
v += "&#160;";
var w = Math.max(this.metrics.getWidth(v) + 24, 24);
if(typeof this._width != 'undefined'){
w = Math.min(this._width, w);
}
this.el.setWidth(w);

if(Ext.isIE){
this.el.dom.style.top='0';
}
return this;
},
doQuery : function(q, forceAll,valuesQuery, forcedAdd){
q = Ext.isEmpty(q) ? '' : q;
var qe = {
query: q,
forceAll: forceAll,
combo: this,
cancel:false
};
if(this.fireEvent('beforequery', qe)===false || qe.cancel){
return false;
}
q = qe.query;
forceAll = qe.forceAll;
if(forceAll === true || (q.length >= this.minChars) || valuesQuery && !Ext.isEmpty(q)){
if(this.lastQuery !== q || forcedAdd){
this.lastQuery = q;
if(this.mode == 'local'){
this.selectedIndex = -1;
if(forceAll){
this.store.clearFilter();
}else{
this.store.filter(this.displayField, q);
}
this.onLoad();
}else{

this.store.baseParams[this.queryParam] = q;
this.store.baseParams[this.queryValuesIndicator] = valuesQuery;
this.store.load({
params: this.getParams(q)
});
if(!forcedAdd){
this.expand();
}
}
}else{
this.selectedIndex = -1;
this.onLoad();
}
}
}
});
Ext.reg('superboxselect', Ext.ux.form.SuperBoxSelect);
/*
* @private
*/
Ext.ux.form.SuperBoxSelectItem = function(config){
Ext.apply(this,config);
Ext.ux.form.SuperBoxSelectItem.superclass.constructor.call(this);
};
/*
* @private
*/
Ext.ux.form.SuperBoxSelectItem = Ext.extend(Ext.ux.form.SuperBoxSelectItem,Ext.Component, {
initComponent : function(){
Ext.ux.form.SuperBoxSelectItem.superclass.initComponent.call(this);
},
onElClick : function(e){
var o = this.owner;
o.clearCurrentFocus().collapse();
if(o.navigateItemsWithTab){
this.focus();
}else{
o.el.dom.focus();
var that = this;
(function(){
this.onLnkFocus();
o.currentFocus = this;
}).defer(10,this);
}
},

onLnkClick : function(e){
if(e) {
e.stopEvent();
}
this.preDestroy();
if(!this.owner.navigateItemsWithTab){
this.owner.el.focus();
}
},
onLnkFocus : function(){
this.el.addClass("x-superboxselect-item-focus");
this.owner.outerWrapEl.addClass("x-form-focus");
},

onLnkBlur : function(){
this.el.removeClass("x-superboxselect-item-focus");
this.owner.outerWrapEl.removeClass("x-form-focus");
},

enableElListeners : function() {
this.el.on('click', this.onElClick, this, {stopEvent:true});
this.el.addClassOnOver('x-superboxselect-item-hover');
},
enableLnkListeners : function() {
this.lnk.on({
click: this.onLnkClick,
focus: this.onLnkFocus,
blur: this.onLnkBlur,
scope: this
});
},

enableAllListeners : function() {
this.enableElListeners();
this.enableLnkListeners();
},
disableAllListeners : function() {
this.el.removeAllListeners();
this.lnk.un('click', this.onLnkClick, this);
this.lnk.un('focus', this.onLnkFocus, this);
this.lnk.un('blur', this.onLnkBlur, this);
},
onRender : function(ct, position){

Ext.ux.form.SuperBoxSelectItem.superclass.onRender.call(this, ct, position);

var el = this.el;
if(el){
el.remove();
}

this.el = el = ct.createChild({ tag: 'li' }, ct.last());
el.addClass('x-superboxselect-item');

var btnEl = this.owner.navigateItemsWithTab ? ( Ext.isSafari ? 'button' : 'a') : 'span';
var itemKey = this.key;

Ext.apply(el, {
focus: function(){
var c = this.down(btnEl +'.x-superboxselect-item-close');
if(c){
c.focus();
}
},
preDestroy: function(){
this.preDestroy();
}.createDelegate(this)
});

this.enableElListeners();

el.update(this.caption);

var cfg = {
tag: btnEl,
'class': 'x-superboxselect-item-close',
tabIndex : this.owner.navigateItemsWithTab ? '0' : '-1'
};
if(btnEl === 'a'){
cfg.href = '#';
}
this.lnk = el.createChild(cfg);


if(!this.disabled) {
this.enableLnkListeners();
}else {
this.disableAllListeners();
}

this.on({
disable: this.disableAllListeners,
enable: this.enableAllListeners,
scope: this
});

this.setupKeyMap();
},
setupKeyMap : function(){
this.keyMap = new Ext.KeyMap(this.lnk, [
{
key: [
Ext.EventObject.BACKSPACE,
Ext.EventObject.DELETE,
Ext.EventObject.SPACE
],
fn: this.preDestroy,
scope: this
}, {
key: [
Ext.EventObject.RIGHT,
Ext.EventObject.DOWN
],
fn: function(){
this.moveFocus('right');
},
scope: this
},
{
key: [Ext.EventObject.LEFT,Ext.EventObject.UP],
fn: function(){
this.moveFocus('left');
},
scope: this
},
{
key: [Ext.EventObject.HOME],
fn: function(){
var l = this.owner.items.get(0).el.focus();
if(l){
l.el.focus();
}
},
scope: this
},
{
key: [Ext.EventObject.END],
fn: function(){
this.owner.el.focus();
},
scope: this
},
{
key: Ext.EventObject.ENTER,
fn: function(){
}
}
]);
this.keyMap.stopEvent = true;
},
moveFocus : function(dir) {
var el = this.el[dir == 'left' ? 'prev' : 'next']() || this.owner.el;

el.focus.defer(100,el);
},

preDestroy : function(supressEffect) {
if(this.fireEvent('remove', this) === false){
return;
}
var actionDestroy = function(){
if(this.owner.navigateItemsWithTab){
this.moveFocus('right');
}
this.hidden.remove();
this.hidden = null;
this.destroy();
};

if(supressEffect){
actionDestroy.call(this);
} else {
this.el.hide({
duration: 0.2,
callback: actionDestroy,
scope: this
});
}
return this;
},
kill : function(){
this.hidden.remove();
this.hidden = null;
this.purgeListeners();
this.destroy();
},
onDisable : function() {
if(this.hidden){
this.hidden.dom.setAttribute('disabled', 'disabled');
}
this.keyMap.disable();
Ext.ux.form.SuperBoxSelectItem.superclass.onDisable.call(this);
},
onEnable : function() {
if(this.hidden){
this.hidden.dom.removeAttribute('disabled');
}
this.keyMap.enable();
Ext.ux.form.SuperBoxSelectItem.superclass.onEnable.call(this);
},
onDestroy : function() {
Ext.destroy(
this.lnk,
this.el
);

Ext.ux.form.SuperBoxSelectItem.superclass.onDestroy.call(this);
}
});

Grolubao
21 Jul 2010, 4:30 AM
Good finding dorgan! I applied it with success! :)

I also decided to extend the SuperBoxSelect. I've added a property called maxHeight that defines the maxHeigh (or height for all that matters) that the component should have. So now one has the option of imposing a height to the component or just let it grow indefinitely. I've also changed the css.

So if one specifies maxHeight: 50, then the component will have the height of 50 and create scrollbars whenever needed, if not then it behaves as the original version.



Ext.namespace('Ext.ux.form');
/**
* <p>SuperBoxSelect is an extension of the ComboBox component that displays selected items as labelled boxes within the form field. As seen on facebook, hotmail and other sites.</p>
* <p>The SuperBoxSelect component was inspired by the BoxSelect component found here: http://efattal.fr/en/extjs/extuxboxselect/</p>
*
* @author <a href="mailto:dan.humphrey@technomedia.co.uk">Dan Humphrey</a>
* @class Ext.ux.form.SuperBoxSelect
* @extends Ext.form.ComboBox
* @constructor
* @component
* @version 1.0b
* @license TBA
*
*/
Ext.ux.form.SuperBoxSelect = function(config) {
Ext.ux.form.SuperBoxSelect.superclass.constructor.call(this,config);
this.addEvents(
/**
* Fires before an item is added to the component via user interaction. Return false from the callback function to prevent the item from being added.
* @event beforeadditem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The value of the item to be added
*/
'beforeadditem',

/**
* Fires after a new item is added to the component.
* @event additem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The value of the item which was added
* @param {Record} record The store record which was added
*/
'additem',

/**
* Fires when the allowAddNewData config is set to true, and a user attempts to add an item that is not in the data store.
* @event newitem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The new item's value
*/
'newitem',

/**
* Fires when an item's remove button is clicked. Return false from the callback function to prevent the item from being removed.
* @event beforeremoveitem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The value of the item to be removed
*/
'beforeremoveitem',

/**
* Fires after an item has been removed.
* @event removeitem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
* @param {Mixed} value The value of the item which was removed
* @param {Record} record The store record which was removed
*/
'removeitem',
/**
* Fires after the component values have been cleared.
* @event clear
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect} this
*/
'clear'
);

};
/**
* @private hide from doc gen
*/
Ext.ux.form.SuperBoxSelect = Ext.extend(Ext.ux.form.SuperBoxSelect,Ext.form.ComboBox,{
maxHeight: null,
/**
* @cfg {Boolean} allowAddNewData When set to true, allows items to be added (via the setValueEx and addItem methods) that do not already exist in the data store. Defaults to false.
*/
allowAddNewData: false,

/**
* @cfg {Boolean} backspaceDeletesLastItem When set to false, the BACKSPACE key will focus the last selected item. When set to true, the last item will be immediately deleted. Defaults to true.
*/
backspaceDeletesLastItem: true,

/**
* @cfg {String} classField The underlying data field that will be used to supply an additional class to each item.
*/
classField: null,

/**
* @cfg {String} clearBtnCls An additional class to add to the in-field clear button.
*/
clearBtnCls: '',

/**
* @cfg {String/XTemplate} displayFieldTpl A template for rendering the displayField in each selected item. Defaults to null.
*/
displayFieldTpl: null,

/**
* @cfg {String} extraItemCls An additional css class to apply to each item.
*/
extraItemCls: '',

/**
* @cfg {String/Object/Function} extraItemStyle Additional css style(s) to apply to each item. Should be a valid argument to Ext.Element.applyStyles.
*/
extraItemStyle: '',

/**
* @cfg {String} expandBtnCls An additional class to add to the in-field expand button.
*/
expandBtnCls: '',

/**
* @cfg {Boolean} fixFocusOnTabSelect When set to true, the component will not lose focus when a list item is selected with the TAB key. Defaults to true.
*/
fixFocusOnTabSelect: true,

/**
* @cfg {Boolean} navigateItemsWithTab When set to true the tab key will navigate between selected items. Defaults to true.
*/
navigateItemsWithTab: true,

/**
* @cfg {Boolean} pinList When set to true the select list will be pinned to allow for multiple selections. Defaults to true.
*/
pinList: true,

/**
* @cfg {Boolean} preventDuplicates When set to true unique item values will be enforced. Defaults to true.
*/
preventDuplicates: true,

/**
* @cfg {String} queryValuesDelimiter Used to delimit multiple values queried from the server when mode is remote.
*/
queryValuesDelimiter: '|',

/**
* @cfg {String} queryValuesIndicator A request variable that is sent to the server (as true) to indicate that we are querying values rather than display data (as used in autocomplete) when mode is remote.
*/
queryValuesInidicator: 'valuesqry',

/**
* @cfg {Boolean} removeValuesFromStore When set to true, selected records will be removed from the store. Defaults to true.
*/
removeValuesFromStore: true,

/**
* @cfg {String} renderFieldBtns When set to true, will render in-field buttons for clearing the component, and displaying the list for selection. Defaults to true.
*/
renderFieldBtns: true,

/**
* @cfg {Boolean} stackItems When set to true, the items will be stacked 1 per line. Defaults to false which displays the items inline.
*/
stackItems: false,

/**
* @cfg {String} styleField The underlying data field that will be used to supply additional css styles to each item.
*/
styleField : null,

/**
* @cfg {Boolean} supressClearValueRemoveEvents When true, the removeitem event will not be fired for each item when the clearValue method is called, or when the clear button is used. Defaults to false.
*/
supressClearValueRemoveEvents : false,

/**
* @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable automatic validation (defaults to 'blur').
*/
validationEvent : 'blur',

/**
* @cfg {String} valueDelimiter The delimiter to use when joining and splitting value arrays and strings.
*/
valueDelimiter: ',',
initComponent:function() {
Ext.apply(this, {
items : new Ext.util.MixedCollection(false),
usedRecords : new Ext.util.MixedCollection(false),
addedRecords : [],
remoteLookup : [],
hideTrigger : true,
grow : false,
resizable : false,
multiSelectMode : false,
preRenderValue : null
});

if(this.transform){
this.doTransform();
}

Ext.ux.form.SuperBoxSelect.superclass.initComponent.call(this);
if(this.mode === 'remote' && this.store){
this.store.on('load', this.onStoreLoad, this);
}
},
onRender:function(ct, position) {
Ext.ux.form.SuperBoxSelect.superclass.onRender.call(this, ct, position);

this.el.dom.removeAttribute('name');

var extraClass = (this.stackItems === true) ? 'x-superboxselect-stacked' : '';
if(this.renderFieldBtns){
extraClass += ' x-superboxselect-display-btns';
}
this.el.removeClass('x-form-text').addClass('x-superboxselect-input-field');

this.wrapEl = this.el.wrap({
tag : 'ul'
});

this.outerWrapEl = this.wrapEl.wrap({
tag : 'div',
cls: 'x-form-text x-superboxselect ' + extraClass,
style: Ext.isEmpty(this.maxHeight) ? 'height: auto;' : 'height: '+this.maxHeight+'px;'
});

this.inputEl = this.el.wrap({
tag : 'li',
cls : 'x-superboxselect-input'
});

if(this.renderFieldBtns){
this.setupFieldButtons().manageClearBtn();
}

this.setupFormInterception();

if(this.preRenderValue){
this.setValue(this.preRenderValue);
this.preRenderValue = null;
}
},
onStoreLoad : function(store, records, options){
//accomodating for bug in Ext 3.0.0 where options.params are empty
var q = options.params[this.queryParam] || store.baseParams[this.queryParam] || "",
isValuesQuery = options.params[this.queryValuesInidicator] || store.baseParams[this.queryValuesInidicator];

if(this.removeValuesFromStore){
this.store.each(function(record) {
if(this.usedRecords.containsKey(record.get(this.valueField))){
this.store.remove(record);
}
}, this);
}
//queried values
if(isValuesQuery){
var params = q.split(this.queryValuesDelimiter);
Ext.each(params,function(p){
this.remoteLookup.remove(p);
var rec = this.findRecord(this.valueField,p);
if(rec){
this.addRecord(rec);
}
},this);

if(this.setOriginal){
this.setOriginal = false;
this.originalValue = this.getValue();
}
}

//queried display (autocomplete) & addItem
if(q !== '' && this.allowAddNewData){
Ext.each(this.remoteLookup,function(r){
if(typeof r == "object" && r[this.displayField] == q){
this.remoteLookup.remove(r);
if(records.length && records[0].get(this.displayField) === q) {
this.addRecord(records[0]);
return;
}
var rec = this.createRecord(r);
this.store.add(rec);
this.addRecord(rec);
this.addedRecords.push(rec); //keep track of records added to store
(function(){
if(this.isExpanded()){
this.collapse();
}
}).defer(10,this);
return;
}
},this);
}

var toAdd = [];
if(q === ''){
Ext.each(this.addedRecords,function(rec){
if(this.preventDuplicates && this.usedRecords.containsKey(rec.get(this.valueField))){
return;
}
toAdd.push(rec);

},this);

}else{
var re = new RegExp(Ext.escapeRe(q) + '.*','i');
Ext.each(this.addedRecords,function(rec){
if(this.preventDuplicates && this.usedRecords.containsKey(rec.get(this.valueField))){
return;
}
if(re.test(rec.get(this.displayField))){
toAdd.push(rec);
}
},this);
}
this.store.add(toAdd);
this.store.sort(this.displayField, 'ASC');

if(this.store.getCount() === 0 && this.isExpanded()){
this.collapse();
}

},
doTransform : function() {
var s = Ext.getDom(this.transform), transformValues = [];
if(!this.store){
this.mode = 'local';
var d = [], opts = s.options;
for(var i = 0, len = opts.length;i < len; i++){
var o = opts[i],
value = (Ext.isIE && !Ext.isIE8 ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text,
cls = (Ext.isIE && !Ext.isIE8 ? o.getAttributeNode('class').specified : o.hasAttribute('class')) ? o.className : '',
style = (Ext.isIE && !Ext.isIE8 ? o.getAttributeNode('style').specified : o.hasAttribute('style')) ? o.style : '';
if(o.selected) {
transformValues.push(value);
}
d.push([value, o.text, cls, style.cssText]);
}
this.store = new Ext.data.SimpleStore({
'id': 0,
fields: ['value', 'text', 'cls', 'style'],
data : d
});
Ext.apply(this,{
valueField: 'value',
displayField: 'text',
classField: 'cls',
styleField: 'style'
});
}

if(transformValues.length){
this.value = transformValues.join(',');
}
},
setupFieldButtons : function(){
this.buttonWrap = this.outerWrapEl.createChild({
cls: 'x-superboxselect-btns'
});

this.buttonClear = this.buttonWrap.createChild({
tag:'div',
cls: 'x-superboxselect-btn-clear ' + this.clearBtnCls
});

this.buttonExpand = this.buttonWrap.createChild({
tag:'div',
cls: 'x-superboxselect-btn-expand ' + this.expandBtnCls
});

this.initButtonEvents();

return this;
},
initButtonEvents : function() {
this.buttonClear.addClassOnOver('x-superboxselect-btn-over').on('click', function(e) {
e.stopEvent();
if (this.disabled) {
return;
}
this.clearValue();
this.el.focus();
}, this);

this.buttonExpand.addClassOnOver('x-superboxselect-btn-over').on('click', function(e) {
e.stopEvent();
if (this.disabled) {
return;
}
if (this.isExpanded()) {
this.multiSelectMode = false;
} else if (this.pinList) {
this.multiSelectMode = true;
}
this.onTriggerClick();
}, this);
},
removeButtonEvents : function() {
this.buttonClear.removeAllListeners();
this.buttonExpand.removeAllListeners();
return this;
},
clearCurrentFocus : function(){
if(this.currentFocus){
this.currentFocus.onLnkBlur();
this.currentFocus = null;
}
return this;
},
initEvents : function() {
var el = this.el;

el.on({
click : this.onClick,
focus : this.clearCurrentFocus,
blur : this.onBlur,

keydown : this.onKeyDownHandler,
keyup : this.onKeyUpBuffered,

scope : this
});

this.on({
collapse: this.onCollapse,
expand: this.clearCurrentFocus,
scope: this
});

this.wrapEl.on('click', this.onWrapClick, this);
this.outerWrapEl.on('click', this.onWrapClick, this);

this.inputEl.focus = function() {
el.focus();
};

Ext.ux.form.SuperBoxSelect.superclass.initEvents.call(this);

Ext.apply(this.keyNav, {
tab: function(e) {
if (this.fixFocusOnTabSelect && this.isExpanded()) {
e.stopEvent();
el.blur();
this.onViewClick(false);
this.focus(false, 10);
return true;
}

this.onViewClick(false);
if (el.dom.value !== '') {
this.setRawValue('');
}

return true;
},

down: function(e) {
if (!this.isExpanded() && !this.currentFocus) {
this.onTriggerClick();
} else {
this.inKeyMode = true;
this.selectNext();
}
},

enter: function(){}
});
},

onClick: function() {
this.clearCurrentFocus();
this.collapse();
this.autoSize();
},

beforeBlur: Ext.form.ComboBox.superclass.beforeBlur,

onFocus: function() {
this.outerWrapEl.addClass(this.focusClass);

Ext.ux.form.SuperBoxSelect.superclass.onFocus.call(this);
},

onBlur: function() {
this.outerWrapEl.removeClass(this.focusClass);

this.clearCurrentFocus();

if (this.el.dom.value !== '') {
this.applyEmptyText();
this.autoSize();
}

Ext.ux.form.SuperBoxSelect.superclass.onBlur.call(this);
},

onCollapse: function() {
this.view.clearSelections();
this.multiSelectMode = false;
},

onWrapClick: function(e) {
e.stopEvent();
this.collapse();
this.el.focus();
this.clearCurrentFocus();
},
markInvalid : function(msg) {
var elp, t;
return;
if (!this.rendered || this.preventMark ) {
return;
}
this.outerWrapEl.addClass(this.invalidClass);
msg = msg || this.invalidText;

switch (this.msgTarget) {
case 'qtip':
Ext.apply(this.el.dom, {
qtip : msg,
qclass : 'x-form-invalid-tip'
});
Ext.apply(this.wrapEl.dom, {
qtip : msg,
qclass : 'x-form-invalid-tip'
});
if (Ext.QuickTips) { // fix for floating editors interacting with DND
Ext.QuickTips.enable();
}
break;
case 'title':
this.el.dom.title = msg;
this.wrapEl.dom.title = msg;
this.outerWrapEl.dom.title = msg;
break;
case 'under':
if (!this.errorEl) {
elp = this.getErrorCt();
if (!elp) { // field has no container el
this.el.dom.title = msg;
break;
}
this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
this.errorEl.setWidth(elp.getWidth(true) - 20);
}
this.errorEl.update(msg);
Ext.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
break;
case 'side':
if (!this.errorIcon) {
elp = this.getErrorCt();
if (!elp) { // field has no container el
this.el.dom.title = msg;
break;
}
this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
}
this.alignErrorIcon();
Ext.apply(this.errorIcon.dom, {
qtip : msg,
qclass : 'x-form-invalid-tip'
});
this.errorIcon.show();
this.on('resize', this.alignErrorIcon, this);
break;
default:
t = Ext.getDom(this.msgTarget);
t.innerHTML = msg;
t.style.display = this.msgDisplay;
break;
}
this.fireEvent('invalid', this, msg);
},
clearInvalid : function(){
if(!this.rendered || this.preventMark){ // not rendered
return;
}
this.outerWrapEl.removeClass(this.invalidClass);
switch(this.msgTarget){
case 'qtip':
this.el.dom.qtip = '';
this.wrapEl.dom.qtip ='';
break;
case 'title':
this.el.dom.title = '';
this.wrapEl.dom.title = '';
this.outerWrapEl.dom.title = '';
break;
case 'under':
if(this.errorEl){
Ext.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
}
break;
case 'side':
if(this.errorIcon){
this.errorIcon.dom.qtip = '';
this.errorIcon.hide();
this.un('resize', this.alignErrorIcon, this);
}
break;
default:
var t = Ext.getDom(this.msgTarget);
t.innerHTML = '';
t.style.display = 'none';
break;
}
this.fireEvent('valid', this);
},
alignErrorIcon : function(){
if(this.wrap){
this.errorIcon.alignTo(this.wrap, 'tl-tr', [Ext.isIE ? 5 : 2, 3]);
}
},
expand : function(){
if (this.isExpanded() || !this.hasFocus) {
return;
}
this.list.alignTo(this.outerWrapEl, this.listAlign).show();
this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
Ext.getDoc().on({
mousewheel: this.collapseIf,
mousedown: this.collapseIf,
scope: this
});
this.fireEvent('expand', this);
},
restrictHeight : function(){
var inner = this.innerList.dom,
st = inner.scrollTop,
list = this.list;

inner.style.height = '';

var pad = list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight,
h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
space = Math.max(ha, hb, this.minHeight || 0)-list.shadowOffset-pad-5;

h = Math.min(h, space, this.maxHeight);
this.innerList.setHeight(h);

list.beginUpdate();
list.setHeight(h+pad);
list.alignTo(this.outerWrapEl, this.listAlign);
list.endUpdate();

if(this.multiSelectMode){
inner.scrollTop = st;
}
},
validateValue: function(val){
if(this.items.getCount() === 0){
if(this.allowBlank){
this.clearInvalid();
return true;
}else{
this.markInvalid(this.blankText);
return false;
}
}else{
this.clearInvalid();
return true;
}
},
setupFormInterception : function(){
var form;
this.findParentBy(function(p){
if(p.getForm){
form = p.getForm();
}
});
if(form){
var formGet = form.getValues;
form.getValues = function(asString){
if(this.items.getCount() > 0){
this.el.dom.disabled = true;
}
var oldVal = this.el.dom.value;
this.setRawValue('');
var vals = formGet.call(form, asString);
this.el.dom.disabled = false;
this.setRawValue(oldVal);
return vals;
}.createDelegate(this);
}
},
onResize : function(w, h, rw, rh) {
var reduce = Ext.isIE6 ? 4 : Ext.isIE7 ? 1 : Ext.isIE8 ? 1 : 0;

this._width = w;
this.outerWrapEl.setWidth(w - reduce);
if (this.renderFieldBtns) {
reduce += (this.buttonWrap.getWidth() + 20);
this.wrapEl.setWidth(w - reduce);
}
Ext.ux.form.SuperBoxSelect.superclass.onResize.call(this, w, h, rw, rh);
this.autoSize();
},
onEnable: function(){
Ext.ux.form.SuperBoxSelect.superclass.onEnable.call(this);
this.items.each(function(item){
item.enable();
});
if (this.renderFieldBtns) {
this.initButtonEvents();
}
},
onDisable: function(){
Ext.ux.form.SuperBoxSelect.superclass.onDisable.call(this);
this.items.each(function(item){
item.disable();
});
if(this.renderFieldBtns){
this.removeButtonEvents();
}
},
/**
* Clears all values from the component.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name clearValue
* @param {Boolean} supressRemoveEvent [Optional] When true, the 'removeitem' event will not fire for each item that is removed.
*/
clearValue : function(supressRemoveEvent){
Ext.ux.form.SuperBoxSelect.superclass.clearValue.call(this);
this.preventMultipleRemoveEvents = supressRemoveEvent || this.supressClearValueRemoveEvents || false;
this.removeAllItems();
this.fireEvent('clear',this);
return this;
},
onKeyUp : function(e) {
if (this.editable !== false && !e.isSpecialKey() && (!e.hasModifier() || e.shiftKey)) {
this.lastKey = e.getKey();
this.dqTask.delay(this.queryDelay);
}
},
onKeyDownHandler : function(e,t) {

var toDestroy,nextFocus,idx;
if ((e.getKey() === e.DELETE || e.getKey() === e.SPACE) && this.currentFocus){
e.stopEvent();
toDestroy = this.currentFocus;
this.on('expand',function(){this.collapse();},this,{single: true});
idx = this.items.indexOfKey(this.currentFocus.key);

this.clearCurrentFocus();

if(idx < (this.items.getCount() -1)){
nextFocus = this.items.itemAt(idx+1);
}

toDestroy.preDestroy(true);
if(nextFocus){
(function(){
nextFocus.onLnkFocus();
this.currentFocus = nextFocus;
}).defer(200,this);
}

return true;
}

var val = this.el.dom.value, it, ctrl = e.ctrlKey;
if(e.getKey() === e.ENTER){
e.stopEvent();
if (val !== "") {
if (ctrl || !this.isExpanded()) { //ctrl+enter for new items
this.view.clearSelections();
this.collapse();
this.setRawValue('');
this.fireEvent('newitem', this, val);
}
else {
this.onViewClick();
//removed from 3.0.1
if(this.unsetDelayCheck){
this.delayedCheck = true;
this.unsetDelayCheck.defer(10, this);
}
}
}else{
if(!this.isExpanded()){
return;
}
this.onViewClick();
//removed from 3.0.1
if(this.unsetDelayCheck){
this.delayedCheck = true;
this.unsetDelayCheck.defer(10, this);
}
}
return true;
}

if(val !== '') {
this.autoSize();
return;
}

//select first item
if(e.getKey() === e.HOME){
e.stopEvent();
if(this.items.getCount() > 0){
this.collapse();
it = this.items.get(0);
it.el.focus();

}
return true;
}
//backspace remove
if(e.getKey() === e.BACKSPACE){
e.stopEvent();
if(this.currentFocus) {
toDestroy = this.currentFocus;
this.on('expand',function(){
this.collapse();
},this,{single: true});

idx = this.items.indexOfKey(toDestroy.key);

this.clearCurrentFocus();
if(idx < (this.items.getCount() -1)){
nextFocus = this.items.itemAt(idx+1);
}

toDestroy.preDestroy(true);

if(nextFocus){
(function(){
nextFocus.onLnkFocus();
this.currentFocus = nextFocus;
}).defer(200,this);
}

return;
}else{
it = this.items.get(this.items.getCount() -1);
if(it){
if(this.backspaceDeletesLastItem){
this.on('expand',function(){this.collapse();},this,{single: true});
it.preDestroy(true);
}else{
if(this.navigateItemsWithTab){
it.onElClick();
}else{
this.on('expand',function(){
this.collapse();
this.currentFocus = it;
this.currentFocus.onLnkFocus.defer(20,this.currentFocus);
},this,{single: true});
}
}
}
return true;
}
}

if(!e.isNavKeyPress()){
this.multiSelectMode = false;
this.clearCurrentFocus();
return;
}
//arrow nav
if(e.getKey() === e.LEFT || (e.getKey() === e.UP && !this.isExpanded())){
e.stopEvent();
this.collapse();
//get last item
it = this.items.get(this.items.getCount()-1);
if(this.navigateItemsWithTab){
//focus last el
if(it){
it.focus();
}
}else{
//focus prev item
if(this.currentFocus){
idx = this.items.indexOfKey(this.currentFocus.key);
this.clearCurrentFocus();

if(idx !== 0){
this.currentFocus = this.items.itemAt(idx-1);
this.currentFocus.onLnkFocus();
}
}else{
this.currentFocus = it;
if(it){
it.onLnkFocus();
}
}
}
return true;
}
if(e.getKey() === e.DOWN){
if(this.currentFocus){
this.collapse();
e.stopEvent();
idx = this.items.indexOfKey(this.currentFocus.key);
if(idx == (this.items.getCount() -1)){
this.clearCurrentFocus.defer(10,this);
}else{
this.clearCurrentFocus();
this.currentFocus = this.items.itemAt(idx+1);
if(this.currentFocus){
this.currentFocus.onLnkFocus();
}
}
return true;
}
}
if(e.getKey() === e.RIGHT){
this.collapse();
it = this.items.itemAt(0);
if(this.navigateItemsWithTab){
//focus first el
if(it){
it.focus();
}
}else{
if(this.currentFocus){
idx = this.items.indexOfKey(this.currentFocus.key);
this.clearCurrentFocus();
if(idx < (this.items.getCount() -1)){
this.currentFocus = this.items.itemAt(idx+1);
if(this.currentFocus){
this.currentFocus.onLnkFocus();
}
}
}else{
this.currentFocus = it;
if(it){
it.onLnkFocus();
}
}
}
}
},
onKeyUpBuffered : function(e){
if(!e.isNavKeyPress()){
this.autoSize();
}
},
reset : function(){
Ext.ux.form.SuperBoxSelect.superclass.reset.call(this);
this.addedRecords = [];
this.autoSize().setRawValue('');
this.el.focus();
},
applyEmptyText : function(){
if(this.items.getCount() > 0){
this.el.removeClass(this.emptyClass);
this.setRawValue('');
return this;
}
if(this.rendered && this.emptyText && this.getRawValue().length < 1){
this.setRawValue(this.emptyText);
this.el.addClass(this.emptyClass);
}
return this;
},
/**
* @private
*
* Use clearValue instead
*/
removeAllItems: function(){
this.items.each(function(item){
item.preDestroy(true);
},this);
this.manageClearBtn();
return this;
},
resetStore: function(){
this.store.clearFilter();
if(!this.removeValuesFromStore){
return this;
}
this.usedRecords.each(function(rec){
this.store.add(rec);
},this);
this.sortStore();
return this;
},
sortStore: function(){
var ss = this.store.getSortState();
if(ss && ss.field){
this.store.sort(ss.field, ss.direction);
}
return this;
},
getCaption: function(dataObject){
if(typeof this.displayFieldTpl === 'string') {
this.displayFieldTpl = new Ext.XTemplate(this.displayFieldTpl);
}
var caption, recordData = dataObject instanceof Ext.data.Record ? dataObject.data : dataObject;

if(this.displayFieldTpl) {
caption = this.displayFieldTpl.apply(recordData);
} else if(this.displayField) {
caption = recordData[this.displayField];
}

return caption;
},
addRecord : function(record) {
var display = record.data[this.displayField],
caption = this.getCaption(record),
val = record.data[this.valueField],
cls = this.classField ? record.data[this.classField] : '',
style = this.styleField ? record.data[this.styleField] : '';

if (this.removeValuesFromStore) {
this.usedRecords.add(val, record);
this.store.remove(record);
}

this.addItemBox(val, display, caption, cls, style);
this.fireEvent('additem', this, val, record);
},
createRecord : function(recordData){
if(!this.recordConstructor){
var recordFields = [
{name: this.valueField},
{name: this.displayField}
];
if(this.classField){
recordFields.push({name: this.classField});
}
if(this.styleField){
recordFields.push({name: this.styleField});
}
this.recordConstructor = Ext.data.Record.create(recordFields);
}
return new this.recordConstructor(recordData);
},
/**
* Adds an array of items to the SuperBoxSelect component if the {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set to true.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name addItem
* @param {Array} newItemObjects An Array of object literals containing the property names and values for an item. The property names must match those specified in {@link #Ext.ux.form.SuperBoxSelect-displayField}, {@link #Ext.ux.form.SuperBoxSelect-valueField} and {@link #Ext.ux.form.SuperBoxSelect-classField}
*/
addItems : function(newItemObjects){
if (Ext.isArray(newItemObjects)) {
Ext.each(newItemObjects, function(item) {
this.addItem(item);
}, this);
} else {
this.addItem(newItemObjects);
}
},
/**
* Adds an item to the SuperBoxSelect component if the {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set to true.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name addItem
* @param {Object} newItemObject An object literal containing the property names and values for an item. The property names must match those specified in {@link #Ext.ux.form.SuperBoxSelect-displayField}, {@link #Ext.ux.form.SuperBoxSelect-valueField} and {@link #Ext.ux.form.SuperBoxSelect-classField}
*/
addItem : function(newItemObject){

var val = newItemObject[this.valueField];

if(this.disabled) {
return false;
}
if(this.preventDuplicates && this.hasValue(val)){
return;
}

//use existing record if found
var record = this.findRecord(this.valueField, val);
if (record) {
this.addRecord(record);
return;
} else if (!this.allowAddNewData) { // else it's a new item
return;
}

if(this.mode === 'remote'){
this.remoteLookup.push(newItemObject);
this.doQuery(val,false,false);
return;
}

var rec = this.createRecord(newItemObject);
this.store.add(rec);
this.addRecord(rec);

return true;
},
addItemBox : function(itemVal,itemDisplay, itemCaption, itemClass, itemStyle) {
var parseStyle = function(s){
var ret = '';
if(typeof s == 'function'){
ret = s.call();
}else if(typeof s == 'object'){
for(var p in s){
ret+= p +':'+s[p]+';';
}
}else if(typeof s == 'string'){
ret = s + ';';
}
return ret;
};
var itemKey = Ext.id(null,'sbx-item');
var box = new Ext.ux.form.SuperBoxSelectItem({
owner: this,
disabled: this.disabled,
renderTo: this.wrapEl,
cls: this.extraItemCls + ' ' + itemClass,
style: parseStyle(this.extraItemStyle) + ' ' + itemStyle,
caption: itemCaption,
display: itemDisplay,
value: itemVal,
key: itemKey,
listeners: {
'remove': function(item){
if(this.fireEvent('beforeremoveitem',this,item.value) === false){
return;
}
this.items.removeKey(item.key);
if(this.removeValuesFromStore){
if(this.usedRecords.containsKey(item.value)){
this.store.add(this.usedRecords.get(item.value));
this.usedRecords.removeKey(item.value);
this.sortStore();
if(this.view){
this.view.render();
}
}
}
if(!this.preventMultipleRemoveEvents){
this.fireEvent.defer(250,this,['removeitem',this,item.value, this.findInStore(item.value)]);
}
this.preventMultipleRemoveEvents = false;
},
destroy: function(){
this.collapse();
this.autoSize().manageClearBtn().validateValue();
},
scope: this
}
});
box.render();

box.hidden = this.el.insertSibling({
tag:'input',
type:'hidden',
value: itemVal,
name: (this.hiddenName || this.name)
},'before');

this.items.add(itemKey,box);
this.applyEmptyText().autoSize().manageClearBtn().validateValue();
},
manageClearBtn : function() {
if (!this.renderFieldBtns || !this.rendered) {
return this;
}
var cls = 'x-superboxselect-btn-hide';
if (this.items.getCount() === 0) {
this.buttonClear.addClass(cls);
} else {
this.buttonClear.removeClass(cls);
}
return this;
},
findInStore : function(val){
var index = this.store.find(this.valueField, val.trim());
if(index > -1){
return this.store.getAt(index);
}
return false;
},
/**
* Returns a String value containing a concatenated list of item values. The list is concatenated with the {@link #Ext.ux.form.SuperBoxSelect-valueDelimiter}.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name getValue
* @return {String} a String value containing a concatenated list of item values.
*/
getValue : function() {
var ret = [];
this.items.each(function(item){
ret.push(item.value);
});
return ret.join(this.valueDelimiter);
},
/**
* Iterates and selects all elements available
* Must take into consideration the ones that are already selected plus the ones from the store.
*/
selectAll: function(){
var ret = this.getValuesAsArray();
Ext.iterate(this.store.data.items,function(item){
ret.push(item.data.name)
});
if(!Ext.isEmpty(ret)){
this.setValue(ret.join(this.valueDelimiter));
}
},
/**
* Returns a String value containing a concatenated list of item values. The list is concatenated with the {@link #Ext.ux.form.SuperBoxSelect-valueDelimiter}.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name getValue
* @return {String} a String value containing a concatenated list of item values.
*/
getValuesAsArray : function() {
var ret = [];
this.items.each(function(item){
ret.push(item.value);
});
return ret;
},
/**
* Returns an Array of item objects containing the {@link #Ext.ux.form.SuperBoxSelect-displayField}, {@link #Ext.ux.form.SuperBoxSelect-valueField} and {@link #Ext.ux.form.SuperBoxSelect-classField} properties.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name getValueEx
* @return {Array} an array of item objects.
*/
getValueEx : function() {
var ret = [];
this.items.each(function(item){
var newItem = {};
newItem[this.valueField] = item.value;
newItem[this.displayField] = item.display;
newItem[this.classField]= item.cls;
ret.push(newItem);
},this);
return ret;
},
// private
initValue : function(){

Ext.ux.form.SuperBoxSelect.superclass.initValue.call(this);
if(this.mode === 'remote') {
this.setOriginal = true;
}
},
/**
* Sets the value of the SuperBoxSelect component.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name setValue
* @param {String|Array} value An array of item values, or a String value containing a delimited list of item values. (The list should be delimited with the {@link #Ext.ux.form.SuperBoxSelect-valueDelimiter)
*/
setValue : function(value){
if(!this.rendered){
this.preRenderValue = value;
return;
}

var values = Ext.isArray(value) ? value : value.split(this.valueDelimiter);
this.removeAllItems().resetStore();

//reset remoteLookup because setValue should overwrite everything
//inc pending data
this.remoteLookup = [];

Ext.each(values,function(val){
var record = this.findRecord(this.valueField, val);
if(record){
this.addRecord(record);
}else if(this.mode === 'remote'){
this.remoteLookup.push(val);
}
},this);

if(this.mode === 'remote'){
var q = this.remoteLookup.join(this.queryValuesDelimiter);
this.doQuery(q,false, true); //3rd param to specify a values query
}

},
/**
* Sets the value of the SuperBoxSelect component, adding new items that don't exist in the data store if the {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set to true.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name setValue
* @param {Array} data An Array of item objects containing the {@link #Ext.ux.form.SuperBoxSelect-displayField}, {@link #Ext.ux.form.SuperBoxSelect-valueField} and {@link #Ext.ux.form.SuperBoxSelect-classField} properties.
*/
setValueEx : function(data){
this.removeAllItems().resetStore();

if(!Ext.isArray(data)){
data = [data];
}
Ext.each(data,function(item){
this.addItem(item);
},this);
},
/**
* Returns true if the SuperBoxSelect component has a selected item with a value matching the 'val' parameter.
* @methodOf Ext.ux.form.SuperBoxSelect
* @name hasValue
* @param {Mixed} val The value to test.
* @return {Boolean} true if the component has the selected value, false otherwise.
*/
hasValue: function(val){
var has = false;
this.items.each(function(item){
if(item.value == val){
has = true;
return false;
}
},this);
return has;
},
onSelect : function(record, index) {
var val = record.data[this.valueField];

if(this.preventDuplicates && this.hasValue(val)){
return;
}

this.setRawValue('');
this.lastSelectionText = '';

if(this.fireEvent('beforeadditem',this,val) !== false){
this.addRecord(record);
}
if(this.store.getCount() === 0 || !this.multiSelectMode){
this.collapse();
}else{
this.restrictHeight();
}
},
onDestroy : function() {
this.items.each(function(item) {
item.preDestroy(true);
}, this);

if (this.renderFieldBtns) {
Ext.destroy(
this.buttonClear,
this.buttonExpand,
this.buttonWrap
);
}

Ext.destroy(
this.inputEl,
this.wrapEl,
this.outerWrapEl
);

Ext.ux.form.SuperBoxSelect.superclass.onDestroy.call(this);
},
autoSize : function(){
if(!this.rendered){
return this;
}
if(!this.metrics){
this.metrics = Ext.util.TextMetrics.createInstance(this.el);
}
var el = this.el,
v = el.dom.value,
d = document.createElement('div');

if(v === "" && this.emptyText && this.items.getCount() < 1){
v = this.emptyText;
}
d.appendChild(document.createTextNode(v));
v = d.innerHTML;
d = null;
v += "&#160;";
var w = Math.max(this.metrics.getWidth(v) + 24, 24);
if(typeof this._width != 'undefined'){
w = Math.min(this._width, w);
}
this.el.setWidth(w);

if(Ext.isIE){
this.el.dom.style.top='0';
}
return this;
},
doQuery : function(q, forceAll,valuesQuery){
q = Ext.isEmpty(q) ? '' : q;
var qe = {
query: q,
forceAll: forceAll,
combo: this,
cancel:false
};
if(this.fireEvent('beforequery', qe)===false || qe.cancel){
return false;
}
q = qe.query;
forceAll = qe.forceAll;
if(forceAll === true || (q.length >= this.minChars)){
if(this.lastQuery !== q){
this.lastQuery = q;
if(this.mode == 'local'){
this.selectedIndex = -1;
if(forceAll){
this.store.clearFilter();
}else{
this.store.filter(this.displayField, q);
}
this.onLoad();
}else{

this.store.baseParams[this.queryParam] = q;
this.store.baseParams[this.queryValuesInidicator] = valuesQuery;
this.store.load({
params: this.getParams(q)
});
this.expand();
}
}else{
this.selectedIndex = -1;
this.onLoad();
}
}
}
});
Ext.reg('superboxselect', Ext.ux.form.SuperBoxSelect);
/*
* @private
*/
Ext.ux.form.SuperBoxSelectItem = function(config){
Ext.apply(this,config);
Ext.ux.form.SuperBoxSelectItem.superclass.constructor.call(this);
};
/*
* @private
*/
Ext.ux.form.SuperBoxSelectItem = Ext.extend(Ext.ux.form.SuperBoxSelectItem,Ext.Component, {
initComponent : function(){
Ext.ux.form.SuperBoxSelectItem.superclass.initComponent.call(this);
},
onElClick : function(e){
var o = this.owner;
o.clearCurrentFocus().collapse();
if(o.navigateItemsWithTab){
this.focus();
}else{
o.el.dom.focus();
var that = this;
(function(){
this.onLnkFocus();
o.currentFocus = this;
}).defer(10,this);
}
},

onLnkClick : function(e){
if(e) {
e.stopEvent();
}
this.preDestroy();
if(!this.owner.navigateItemsWithTab){
this.owner.el.focus();
}
},
onLnkFocus : function(){
this.el.addClass("x-superboxselect-item-focus");
this.owner.outerWrapEl.addClass("x-form-focus");
},

onLnkBlur : function(){
this.el.removeClass("x-superboxselect-item-focus");
this.owner.outerWrapEl.removeClass("x-form-focus");
},

enableElListeners : function() {
this.el.on('click', this.onElClick, this, {stopEvent:true});

this.el.addClassOnOver('x-superboxselect-item-hover');;
},

enableLnkListeners : function() {
this.lnk.on({
click: this.onLnkClick,
focus: this.onLnkFocus,
blur: this.onLnkBlur,
scope: this
});
},

enableAllListeners : function() {
this.enableElListeners();
this.enableLnkListeners();
},
disableAllListeners : function() {
this.el.removeAllListeners();
this.lnk.un('click', this.onLnkClick, this);
this.lnk.un('focus', this.onLnkFocus, this);
this.lnk.un('blur', this.onLnkBlur, this);
},
onRender : function(ct, position){

Ext.ux.form.SuperBoxSelectItem.superclass.onRender.call(this, ct, position);

var el = this.el;
if(el){
el.remove();
}

this.el = el = ct.createChild({ tag: 'li' }, ct.last());
el.addClass('x-superboxselect-item');

var btnEl = this.owner.navigateItemsWithTab ? ( Ext.isSafari ? 'button' : 'a') : 'span';
var itemKey = this.key;

Ext.apply(el, {
focus: function(){
var c = this.down(btnEl +'.x-superboxselect-item-close');
if(c){
c.focus();
}
},
preDestroy: function(){
this.preDestroy();
}.createDelegate(this)
});

this.enableElListeners();

el.update(this.caption);

var cfg = {
tag: btnEl,
'class': 'x-superboxselect-item-close',
tabIndex : this.owner.navigateItemsWithTab ? '0' : '-1'
};
if(btnEl === 'a'){
cfg.href = '#';
}
this.lnk = el.createChild(cfg);


if(!this.disabled) {
this.enableLnkListeners();
}else {
this.disableAllListeners();
}

this.on({
disable: this.disableAllListeners,
enable: this.enableAllListeners,
scope: this
});

this.setupKeyMap();
},
setupKeyMap : function(){
new Ext.KeyMap(this.lnk, [
{
key: [
Ext.EventObject.BACKSPACE,
Ext.EventObject.DELETE,
Ext.EventObject.SPACE
],
fn: this.preDestroy,
scope: this
}, {
key: [
Ext.EventObject.RIGHT,
Ext.EventObject.DOWN
],
fn: function(){
this.moveFocus('right');
},
scope: this
},
{
key: [Ext.EventObject.LEFT,Ext.EventObject.UP],
fn: function(){
this.moveFocus('left');
},
scope: this
},
{
key: [Ext.EventObject.HOME],
fn: function(){
var l = this.owner.items.get(0).el.focus();
if(l){
l.el.focus();
}
},
scope: this
},
{
key: [Ext.EventObject.END],
fn: function(){
this.owner.el.focus();
},
scope: this
},
{
key: Ext.EventObject.ENTER,
fn: function(){
}
}
]).stopEvent = true;
},
moveFocus : function(dir) {
var el = this.el[dir == 'left' ? 'prev' : 'next']() || this.owner.el;

el.focus.defer(100,el);
},

preDestroy : function(supressEffect) {
if(this.fireEvent('remove', this) === false){
return;
}
var actionDestroy = function(){
if(this.owner.navigateItemsWithTab){
this.moveFocus('right');
}
this.hidden.remove();
this.hidden = null;
this.destroy();
};

if(supressEffect){
actionDestroy.call(this);
} else {
this.el.hide({
duration: 0.2,
callback: actionDestroy,
scope: this
});
}
return this;
},
onDestroy : function() {
Ext.destroy(
this.lnk,
this.el
);

Ext.ux.form.SuperBoxSelectItem.superclass.onDestroy.call(this);
}
});




@charset "utf-8";
.x-superboxselect {position:relative; margin: 0px; overflow: auto; padding:2px; display:block; outline: none !important;}
.x-superboxselect input[disabled] {background-color: transparent;};
.x-superboxselect ul {overflow: hidden; cursor: text;}
.x-superboxselect-display-btns {padding-right: 33px !important;}
.x-superboxselect-btns {position: absolute; right: 1px; top: 0; overflow:hidden; padding:2px;}
.x-superboxselect-btns div {float: left; width: 16px; height: 16px; margin-top: 4px;}
.x-superboxselect-btn-clear {background: url(../images/clear.png) no-repeat scroll left 0px;}
.x-superboxselect-btn-expand {background: url(../images/expand.png) no-repeat scroll left 0px;}
.x-superboxselect-btn-over {background-position: left -16px}
.x-superboxselect-btn-hide {display:none;}
.x-superboxselect li {float: left; margin: 1px 1px 2px 1px; padding: 0;line-height: 18px;}
.x-superboxselect-stacked li {float: none !important;}
.x-superboxselect-input input { border: none; outline: none; margin-top: 4px; margin-bottom: 4px;}
body.ext-ie .x-superboxselect-input input {background: none; border: none; margin-top: 3px;}
.x-superboxselect-item {position: relative; -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; o-border-radius: 6px; khtml-border-radius: 6px; border: 1px solid #CAD8F3; background-color: #DEE7F8; padding: 1px 15px 1px 5px !important; }
body.ext-ie7 .x-superboxselect-item {margin: 2px 1px 2px 1px; line-height: 1.2em; padding: 2px 17px 4px 5px !important;}
body.ext-ie6 .x-superboxselect-item {margin: 2px 1px 2px 1px; line-height: 1.2em; padding: 2px 19px 4px 5px !important;}
.x-superboxselect-item-hover {background: #BBCEF1; border: 1px solid #6D95E0;}
.x-superboxselect-item-focus {border-color: #598BEC; background: #598BEC; color: #fff;}
.x-superboxselect-item-close {background: url(../images/close.png) no-repeat scroll left 0px; border: none; cursor: default; font-size: 1px; height: 16px;padding:0; position: absolute; right: 0px; top: 2px; width: 13px;display:block;cursor:pointer;}

.x-superboxselect-item-close:hover, .x-superboxselect-item-close:active { background-position: left -12px;}
.x-superboxselect-item-focus .x-superboxselect-item-close{ background-position: left -24px}
.x-item-disabled .x-superboxselect-item-close{ background-position: left -36px}

Grolubao
5 Aug 2010, 3:48 AM
I'm having the same issues... :(



Hello,
We have recently upgraded from Ext 3.0 to 3.2.1 and everything seems to be working fine ofcourse not without a few changes. However with superboxselect user extension we are seeing some weird behavior. It worked fine with previous ext version.

We have 2 superboxselectboxes on a filter window. e.g. filter by part number and filter by commodity.. now when u open the filter window for the first time both the boxes work and you can see the selections and change the selections etc. but then you filter the records and open the filter window again, the first selected superbox does not expand any more. the other superbox works fine.

I am attaching the code here, please take a look and help me out here.. thanks much.

Here is how I am opening the filter window..


handler: function(){
if (!filterWin) {
filterWin = new Ext.Window({
title: 'Awards Filter',
width: 500,
autoHeight: true,
closeAction:'hide',
border: true,
bodyBorder: false,
autoScroll: true,
modal: true,
bodyStyle:'padding:5px 5px 5px 5px',
items: [awardsFilterFormPanel],
layout: 'form'
});
}
filterWin.show();
filterWin.center();
}
Here is my awardsFilterFormPanel..


var awardsFilterFormPanel = new Ext.FormPanel({
id:'awardsFilterForm',
frame:true,
autoHeight: true,
items: [
{
allowBlank: true,
id:'display_parts_combo',
xtype:'combo',
fieldLabel: 'View By',
resizable: true,
name: 'display_parts_combo',
width: 350,
store: displayPartsStore,
mode: 'local',
triggerAction: 'all',
displayField: 'value',
hiddenName:'id',
valueField: 'id',
forceSelection : true,
allowBlank: false,
value:'<%=displayPartsId%>'
},
{
allowBlank: true,
id:'filter_part_number_combo',
xtype:'superboxselect',
fieldLabel: 'Part Numbers',
emptyText: 'Select Part Number',
resizable: true,
name: 'filter_part_number_combo',
width: 350,
store: partsStore,
mode: 'local',
displayField: 'part_name',
displayFieldTpl: '{part_name}',
valueField: 'part_id',
forceSelection: true,
value: '<%=partSelections%>'
},
{
allowBlank: true,
id:'filter_commodity_combo',
xtype:'superboxselect',
fieldLabel: 'Commodities',
emptyText: 'Select Commodity',
resizable: true,
name: 'filter_commodity_combo',
width: 350,
store: filterCommodityComboStore,
mode: 'local',
displayField: 'commodity_code',
displayFieldTpl: '{commodity_code}',
valueField: 'commodity_id',
forceSelection : true,
value: '<%=commSelections%>'
},
{
xtype: 'checkbox',
id: 'chk_sub_commodities',
fieldLabel: 'Include Sub Commodities',
boxLabel: '',
name: 'chk_sub_commodities',
checked: <%=includeSubCommodities.booleanValue()%>
}],
buttons: [
{xtype: 'tbfill'},
{
text: 'Filter',
formBind: true,
handler: function(){
if(true){
awardsFilterFormPanel.getForm().submit({
url: '<%=awardsFilterFormUrl%>',
method: 'POST',
waitMsg: '<%=waitMsgStr%>',
success: function(form, action){
awardsPricingStore.reload();
filterWin.hide();
},
failure: function(form, action){
//TO DO:
//add failure code
}
});
}
}
},
{
text: 'Reset Filter',
formBind: true,
handler: function(){
{
var conn = new Ext.data.Connection();
conn.request({
waitMsg: '<%=waitMsgStr%>',
url: '<%=resetAwardsFilterURL%>',
method: 'POST',
callback: function(opt,success,responseObj){
if (success) {
var responseData = Ext.util.JSON.decode(responseObj.responseText);
if ( responseData.success == true ) {
awardsFilterFormPanel.getForm().reset();
Ext.getCmp('filter_commodity_combo').clearValue();
Ext.getCmp('chk_sub_commodities').setValue('false');
awardsPricingStore.reload();
filterWin.hide();
}
} else {
Ext.Msg.alert('Error', 'Filter could not be reset, Please try again!');
}
}
});
}
}
},
{
text: 'Cancel',
handler: function(){
filterWin.hide();
}
}]
});


The issue may be is something to do with z-index value rendered by ext..

Grolubao
5 Aug 2010, 3:52 AM
polydyne I found the solution:

http://www.sencha.com/forum/showthread.php?95254-RESOLVED-ComboBox-list-z-index-lower-than-container-problem&p=450841#post450841

sj137
11 Aug 2010, 9:27 AM
hi guys, first of all great great component, if there were awards for best Ext.ux user component then the SBS would be a clear winner in my book!! :)

that said i'm having a small problem with the SBS when added in a toolbar, the problem only occurs in IE6, IE7 and some versions of IE8...

the box area doesn't expand to fit the number of items, instead it always stays a fixed height of 20px....

i've tried everything i can think of to try and solve the issue, but unfortunately css and IE hacks aren't my forte...

has anyone encountered this or know of a solutions

any help is much appreciated!!

SJ

sj137
12 Aug 2010, 2:12 AM
a temporary fix i found, but not really a good one is to make the toolbar a panel

ie




tbar:{

xtype:'panel',
items:[{xtype:'superboxselect',...}]

}

sj137
13 Aug 2010, 5:06 AM
found the solution!

thanks Grolubao!!

have to add the style directly when creating the outerWrap in onRender:
@line 238:


this.outerWrapEl = this.wrapEl.wrap({
tag : 'div',
cls: 'x-form-text x-superboxselect ' + extraClass,
style: 'height: auto !important;'
});

Grolubao
13 Aug 2010, 5:58 AM
I made some more improvements. Namely a SelectAll function which selects all elements, and a fix to the maxHeight that is now called maxCustomHeight



Ext.namespace('Ext.ux.form');
/**
* <p>
* SuperBoxSelect is an extension of the ComboBox component that displays
* selected items as labelled boxes within the form field. As seen on facebook,
* hotmail and other sites.
* </p>
* <p>
* The SuperBoxSelect component was inspired by the BoxSelect component found
* here: http://efattal.fr/en/extjs/extuxboxselect/
* </p>
*
* @author <a href="mailto:dan.humphrey@technomedia.co.uk">Dan Humphrey</a>
* @class Ext.ux.form.SuperBoxSelect
* @extends Ext.form.ComboBox
* @constructor
* @component
* @version 1.0b
* @license TBA
*
*/
Ext.ux.form.SuperBoxSelect = function(config) {
Ext.ux.form.SuperBoxSelect.superclass.constructor.call(this, config);
this.addEvents(
/**
* Fires before an item is added to the component via user
* interaction. Return false from the callback function to prevent
* the item from being added.
*
* @event beforeadditem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect}
* this
* @param {Mixed}
* value The value of the item to be added
*/
'beforeadditem',

/**
* Fires after a new item is added to the component.
*
* @event additem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect}
* this
* @param {Mixed}
* value The value of the item which was added
* @param {Record}
* record The store record which was added
*/
'additem',

/**
* Fires when the allowAddNewData config is set to true, and a user
* attempts to add an item that is not in the data store.
*
* @event newitem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect}
* this
* @param {Mixed}
* value The new item's value
*/
'newitem',

/**
* Fires when an item's remove button is clicked. Return false from
* the callback function to prevent the item from being removed.
*
* @event beforeremoveitem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect}
* this
* @param {Mixed}
* value The value of the item to be removed
*/
'beforeremoveitem',

/**
* Fires after an item has been removed.
*
* @event removeitem
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect}
* this
* @param {Mixed}
* value The value of the item which was removed
* @param {Record}
* record The store record which was removed
*/
'removeitem',
/**
* Fires after the component values have been cleared.
*
* @event clear
* @memberOf Ext.ux.form.SuperBoxSelect
* @param {SuperBoxSelect}
* this
*/
'clear');

};
/**
* @private hide from doc gen
*/
Ext.ux.form.SuperBoxSelect = Ext.extend(Ext.ux.form.SuperBoxSelect,
Ext.form.ComboBox, {
maxCustomHeight : null,
/**
* @cfg {Boolean} allowAddNewData When set to true, allows items to
* be added (via the setValueEx and addItem methods) that do
* not already exist in the data store. Defaults to false.
*/
allowAddNewData : false,

/**
* @cfg {Boolean} backspaceDeletesLastItem When set to false, the
* BACKSPACE key will focus the last selected item. When set to
* true, the last item will be immediately deleted. Defaults to
* true.
*/
backspaceDeletesLastItem : true,

/**
* @cfg {String} classField The underlying data field that will be
* used to supply an additional class to each item.
*/
classField : null,

/**
* @cfg {String} clearBtnCls An additional class to add to the
* in-field clear button.
*/
clearBtnCls : '',

/**
* @cfg {String/XTemplate} displayFieldTpl A template for rendering
* the displayField in each selected item. Defaults to null.
*/
displayFieldTpl : null,

/**
* @cfg {String} extraItemCls An additional css class to apply to
* each item.
*/
extraItemCls : '',

/**
* @cfg {String/Object/Function} extraItemStyle Additional css
* style(s) to apply to each item. Should be a valid argument
* to Ext.Element.applyStyles.
*/
extraItemStyle : '',

/**
* @cfg {String} expandBtnCls An additional class to add to the
* in-field expand button.
*/
expandBtnCls : '',

/**
* @cfg {Boolean} fixFocusOnTabSelect When set to true, the
* component will not lose focus when a list item is selected
* with the TAB key. Defaults to true.
*/
fixFocusOnTabSelect : true,

/**
* @cfg {Boolean} navigateItemsWithTab When set to true the tab key
* will navigate between selected items. Defaults to true.
*/
navigateItemsWithTab : true,

/**
* @cfg {Boolean} pinList When set to true the select list will be
* pinned to allow for multiple selections. Defaults to true.
*/
pinList : true,

/**
* @cfg {Boolean} preventDuplicates When set to true unique item
* values will be enforced. Defaults to true.
*/
preventDuplicates : true,

/**
* @cfg {String} queryValuesDelimiter Used to delimit multiple
* values queried from the server when mode is remote.
*/
queryValuesDelimiter : '|',

/**
* @cfg {String} queryValuesIndicator A request variable that is
* sent to the server (as true) to indicate that we are
* querying values rather than display data (as used in
* autocomplete) when mode is remote.
*/
queryValuesInidicator : 'valuesqry',

/**
* @cfg {Boolean} removeValuesFromStore When set to true, selected
* records will be removed from the store. Defaults to true.
*/
removeValuesFromStore : true,

/**
* @cfg {String} renderFieldBtns When set to true, will render
* in-field buttons for clearing the component, and displaying
* the list for selection. Defaults to true.
*/
renderFieldBtns : true,

/**
* @cfg {Boolean} stackItems When set to true, the items will be
* stacked 1 per line. Defaults to false which displays the
* items inline.
*/
stackItems : false,

/**
* @cfg {String} styleField The underlying data field that will be
* used to supply additional css styles to each item.
*/
styleField : null,

/**
* @cfg {Boolean} supressClearValueRemoveEvents When true, the
* removeitem event will not be fired for each item when the
* clearValue method is called, or when the clear button is
* used. Defaults to false.
*/
supressClearValueRemoveEvents : false,

/**
* @cfg {String/Boolean} validationEvent The event that should
* initiate field validation. Set to false to disable automatic
* validation (defaults to 'blur').
*/
validationEvent : 'blur',

/**
* @cfg {String} valueDelimiter The delimiter to use when joining
* and splitting value arrays and strings.
*/
valueDelimiter : ',',
initComponent : function() {
Ext.apply(this, {
items : new Ext.util.MixedCollection(false),
usedRecords : new Ext.util.MixedCollection(false),
addedRecords : [],
remoteLookup : [],
hideTrigger : true,
grow : false,
resizable : false,
multiSelectMode : false,
preRenderValue : null
});

if (this.transform) {
this.doTransform();
}

Ext.ux.form.SuperBoxSelect.superclass.initComponent.call(this);
if (this.mode === 'remote' && this.store) {
this.store.on('load', this.onStoreLoad, this);
}
},
onRender : function(ct, position) {
Ext.ux.form.SuperBoxSelect.superclass.onRender.call(this, ct,
position);

this.el.dom.removeAttribute('name');

var extraClass = (this.stackItems === true)
? 'x-superboxselect-stacked'
: '';
if (this.renderFieldBtns) {
extraClass += ' x-superboxselect-display-btns';
}
this.el.removeClass('x-form-text')
.addClass('x-superboxselect-input-field');

this.wrapEl = this.el.wrap({
tag : 'ul'
});

this.outerWrapEl = this.wrapEl.wrap({
tag : 'div',
cls : 'x-form-text x-superboxselect ' + extraClass,
style : Ext.isEmpty(this.maxCustomHeight)
? 'height: auto;'
: 'height: ' + this.maxCustomHeight + 'px;'
});

this.inputEl = this.el.wrap({
tag : 'li',
cls : 'x-superboxselect-input'
});

if (this.renderFieldBtns) {
this.setupFieldButtons().manageClearBtn();
}

this.setupFormInterception();

if (this.preRenderValue) {
this.setValue(this.preRenderValue);
this.preRenderValue = null;
}
},
onStoreLoad : function(store, records, options) {
// accomodating for bug in Ext 3.0.0 where
// options.params are empty
var q = options.params[this.queryParam]
|| store.baseParams[this.queryParam] || "", isValuesQuery = options.params[this.queryValuesInidicator]
|| store.baseParams[this.queryValuesInidicator];

if (this.removeValuesFromStore) {
this.store.each(function(record) {
if (this.usedRecords.containsKey(record
.get(this.valueField))) {
this.store.remove(record);
}
}, this);
}
// queried values
if (isValuesQuery) {
var params = q.split(this.queryValuesDelimiter);
Ext.each(params, function(p) {
this.remoteLookup.remove(p);
var rec = this.findRecord(this.valueField, p);
if (rec) {
this.addRecord(rec);
}
}, this);

if (this.setOriginal) {
this.setOriginal = false;
this.originalValue = this.getValue();
}
}

// queried display (autocomplete) & addItem
if (q !== '' && this.allowAddNewData) {
Ext.each(this.remoteLookup, function(r) {
if (typeof r == "object" && r[this.displayField] == q) {
this.remoteLookup.remove(r);
if (records.length
&& records[0].get(this.displayField) === q) {
this.addRecord(records[0]);
return;
}
var rec = this.createRecord(r);
this.store.add(rec);
this.addRecord(rec);
this.addedRecords.push(rec); // keep
// track
// of
// records
// added
// to
// store
(function() {
if (this.isExpanded()) {
this.collapse();
}
}).defer(10, this);
return;
}
}, this);
}

var toAdd = [];
if (q === '') {
Ext.each(this.addedRecords, function(rec) {
if (this.preventDuplicates
&& this.usedRecords.containsKey(rec
.get(this.valueField))) {
return;
}
toAdd.push(rec);

}, this);

} else {
var re = new RegExp(Ext.escapeRe(q) + '.*', 'i');
Ext.each(this.addedRecords, function(rec) {
if (this.preventDuplicates
&& this.usedRecords.containsKey(rec
.get(this.valueField))) {
return;
}
if (re.test(rec.get(this.displayField))) {
toAdd.push(rec);
}
}, this);
}
this.store.add(toAdd);
this.store.sort(this.displayField, 'ASC');

if (this.store.getCount() === 0 && this.isExpanded()) {
this.collapse();
}

},
doTransform : function() {
var s = Ext.getDom(this.transform), transformValues = [];
if (!this.store) {
this.mode = 'local';
var d = [], opts = s.options;
for (var i = 0, len = opts.length; i < len; i++) {
var o = opts[i], value = (Ext.isIE && !Ext.isIE8 ? o
.getAttributeNode('value').specified : o
.hasAttribute('value')) ? o.value : o.text, cls = (Ext.isIE
&& !Ext.isIE8
? o.getAttributeNode('class').specified
: o.hasAttribute('class')) ? o.className : '', style = (Ext.isIE
&& !Ext.isIE8
? o.getAttributeNode('style').specified
: o.hasAttribute('style')) ? o.style : '';
if (o.selected) {
transformValues.push(value);
}
d.push([value, o.text, cls, style.cssText]);
}
this.store = new Ext.data.SimpleStore({
'id' : 0,
fields : ['value', 'text', 'cls', 'style'],
data : d
});
Ext.apply(this, {
valueField : 'value',
displayField : 'text',
classField : 'cls',
styleField : 'style'
});
}

if (transformValues.length) {
this.value = transformValues.join(',');
}
},
setupFieldButtons : function() {
this.buttonWrap = this.outerWrapEl.createChild({
cls : 'x-superboxselect-btns'
});

this.buttonClear = this.buttonWrap.createChild({
tag : 'div',
cls : 'x-superboxselect-btn-clear '
+ this.clearBtnCls
});

this.buttonExpand = this.buttonWrap.createChild({
tag : 'div',
cls : 'x-superboxselect-btn-expand '
+ this.expandBtnCls
});

this.initButtonEvents();

return this;
},
initButtonEvents : function() {
this.buttonClear.addClassOnOver('x-superboxselect-btn-over')
.on('click', function(e) {
e.stopEvent();
if (this.disabled) {
return;
}
this.clearValue();
this.el.focus();
}, this);

this.buttonExpand.addClassOnOver('x-superboxselect-btn-over')
.on('click', function(e) {
e.stopEvent();
if (this.disabled) {
return;
}
if (this.isExpanded()) {
this.multiSelectMode = false;
} else if (this.pinList) {
this.multiSelectMode = true;
}
this.onTriggerClick();
}, this);
},
removeButtonEvents : function() {
this.buttonClear.removeAllListeners();
this.buttonExpand.removeAllListeners();
return this;
},
clearCurrentFocus : function() {
if (this.currentFocus) {
this.currentFocus.onLnkBlur();
this.currentFocus = null;
}
return this;
},
initEvents : function() {
var el = this.el;

el.on({
click : this.onClick,
focus : this.clearCurrentFocus,
blur : this.onBlur,

keydown : this.onKeyDownHandler,
keyup : this.onKeyUpBuffered,

scope : this
});

this.on({
collapse : this.onCollapse,
expand : this.clearCurrentFocus,
scope : this
});

this.wrapEl.on('click', this.onWrapClick, this);
this.outerWrapEl.on('click', this.onWrapClick, this);

this.inputEl.focus = function() {
el.focus();
};

Ext.ux.form.SuperBoxSelect.superclass.initEvents.call(this);

Ext.apply(this.keyNav, {
tab : function(e) {
if (this.fixFocusOnTabSelect
&& this.isExpanded()) {
e.stopEvent();
el.blur();
this.onViewClick(false);
this.focus(false, 10);
return true;
}

this.onViewClick(false);
if (el.dom.value !== '') {
this.setRawValue('');
}

return true;
},

down : function(e) {
if (!this.isExpanded() && !this.currentFocus) {
this.onTriggerClick();
} else {
this.inKeyMode = true;
this.selectNext();
}
},

enter : function() {
}
});
},

onClick : function() {
this.clearCurrentFocus();
this.collapse();
this.autoSize();
},

beforeBlur : Ext.form.ComboBox.superclass.beforeBlur,

onFocus : function() {
this.outerWrapEl.addClass(this.focusClass);

Ext.ux.form.SuperBoxSelect.superclass.onFocus.call(this);
},

onBlur : function() {
this.outerWrapEl.removeClass(this.focusClass);

this.clearCurrentFocus();

if (this.el.dom.value !== '') {
this.applyEmptyText();
this.autoSize();
}

Ext.ux.form.SuperBoxSelect.superclass.onBlur.call(this);
},

onCollapse : function() {
this.view.clearSelections();
this.multiSelectMode = false;
},

onWrapClick : function(e) {
e.stopEvent();
this.collapse();
this.el.focus();
this.clearCurrentFocus();
},
markInvalid : function(msg) {
var elp, t;
return;
if (!this.rendered || this.preventMark) {
return;
}
this.outerWrapEl.addClass(this.invalidClass);
msg = msg || this.invalidText;

switch (this.msgTarget) {
case 'qtip' :
Ext.apply(this.el.dom, {
qtip : msg,
qclass : 'x-form-invalid-tip'
});
Ext.apply(this.wrapEl.dom, {
qtip : msg,
qclass : 'x-form-invalid-tip'
});
if (Ext.QuickTips) { // fix for floating editors
// interacting with DND
Ext.QuickTips.enable();
}
break;
case 'title' :
this.el.dom.title = msg;
this.wrapEl.dom.title = msg;
this.outerWrapEl.dom.title = msg;
break;
case 'under' :
if (!this.errorEl) {
elp = this.getErrorCt();
if (!elp) { // field has no container el
this.el.dom.title = msg;
break;
}
this.errorEl = elp.createChild({
cls : 'x-form-invalid-msg'
});
this.errorEl.setWidth(elp.getWidth(true) - 20);
}
this.errorEl.update(msg);
Ext.form.Field.msgFx[this.msgFx].show(this.errorEl,
this);
break;
case 'side' :
if (!this.errorIcon) {
elp = this.getErrorCt();
if (!elp) { // field has no container el
this.el.dom.title = msg;
break;
}
this.errorIcon = elp.createChild({
cls : 'x-form-invalid-icon'
});
}
this.alignErrorIcon();
Ext.apply(this.errorIcon.dom, {
qtip : msg,
qclass : 'x-form-invalid-tip'
});
this.errorIcon.show();
this.on('resize', this.alignErrorIcon, this);
break;
default :
t = Ext.getDom(this.msgTarget);
t.innerHTML = msg;
t.style.display = this.msgDisplay;
break;
}
this.fireEvent('invalid', this, msg);
},
clearInvalid : function() {
if (!this.rendered || this.preventMark) { // not
// rendered
return;
}
this.outerWrapEl.removeClass(this.invalidClass);
switch (this.msgTarget) {
case 'qtip' :
this.el.dom.qtip = '';
this.wrapEl.dom.qtip = '';
break;
case 'title' :
this.el.dom.title = '';
this.wrapEl.dom.title = '';
this.outerWrapEl.dom.title = '';
break;
case 'under' :
if (this.errorEl) {
Ext.form.Field.msgFx[this.msgFx].hide(this.errorEl,
this);
}
break;
case 'side' :
if (this.errorIcon) {
this.errorIcon.dom.qtip = '';
this.errorIcon.hide();
this.un('resize', this.alignErrorIcon, this);
}
break;
default :
var t = Ext.getDom(this.msgTarget);
t.innerHTML = '';
t.style.display = 'none';
break;
}
this.fireEvent('valid', this);
},
alignErrorIcon : function() {
if (this.wrap) {
this.errorIcon.alignTo(this.wrap, 'tl-tr', [
Ext.isIE ? 5 : 2, 3]);
}
},
expand : function() {
if (this.isExpanded() || !this.hasFocus) {
return;
}
this.list.alignTo(this.outerWrapEl, this.listAlign).show();
this.innerList.setOverflow('auto'); // necessary for FF
// 2.0/Mac
Ext.getDoc().on({
mousewheel : this.collapseIf,
mousedown : this.collapseIf,
scope : this
});
this.fireEvent('expand', this);
},
restrictHeight : function() {
var inner = this.innerList.dom, st = inner.scrollTop, list = this.list;

inner.style.height = '';

var pad = list.getFrameWidth('tb')
+ (this.resizable ? this.handleHeight : 0)
+ this.assetHeight, h = Math.max(inner.clientHeight,
inner.offsetHeight, inner.scrollHeight), ha = this
.getPosition()[1]
- Ext.getBody().getScroll().top, hb = Ext.lib.Dom
.getViewHeight()
- ha - this.getSize().height, space = Math.max(ha, hb,
this.minHeight || 0)
- list.shadowOffset - pad - 5;

h = Math.min(h, space, this.maxHeight);
this.innerList.setHeight(h);

list.beginUpdate();
list.setHeight(h + pad);
list.alignTo(this.outerWrapEl, this.listAlign);
list.endUpdate();

if (this.multiSelectMode) {
inner.scrollTop = st;
}
},
validateValue : function(val) {
if (this.items.getCount() === 0) {
if (this.allowBlank) {
this.clearInvalid();
return true;
} else {
this.markInvalid(this.blankText);
return false;
}
} else {
this.clearInvalid();
return true;
}
},
setupFormInterception : function() {
var form;
this.findParentBy(function(p) {
if (p.getForm) {
form = p.getForm();
}
});
if (form) {
var formGet = form.getValues;
form.getValues = function(asString) {
if (this.items.getCount() > 0) {
this.el.dom.disabled = true;
}
var oldVal = this.el.dom.value;
this.setRawValue('');
var vals = formGet.call(form, asString);
this.el.dom.disabled = false;
this.setRawValue(oldVal);
return vals;
}.createDelegate(this);
}
},
onResize : function(w, h, rw, rh) {
var reduce = Ext.isIE6 ? 4 : Ext.isIE7 ? 1 : Ext.isIE8 ? 1 : 0;

this._width = w;
this.outerWrapEl.setWidth(w - reduce);
if (this.renderFieldBtns) {
reduce += (this.buttonWrap.getWidth() + 20);
this.wrapEl.setWidth(w - reduce);
}
Ext.ux.form.SuperBoxSelect.superclass.onResize.call(this, w, h,
rw, rh);
this.autoSize();
},
onEnable : function() {
Ext.ux.form.SuperBoxSelect.superclass.onEnable.call(this);
this.items.each(function(item) {
item.enable();
});
if (this.renderFieldBtns) {
this.initButtonEvents();
}
},
onDisable : function() {
Ext.ux.form.SuperBoxSelect.superclass.onDisable.call(this);
this.items.each(function(item) {
item.disable();
});
if (this.renderFieldBtns) {
this.removeButtonEvents();
}
},
/**
* Clears all values from the component.
*
* @methodOf Ext.ux.form.SuperBoxSelect
* @name clearValue
* @param {Boolean}
* supressRemoveEvent [Optional] When true, the
* 'removeitem' event will not fire for each item that is
* removed.
*/
clearValue : function(supressRemoveEvent) {
Ext.ux.form.SuperBoxSelect.superclass.clearValue.call(this);
this.preventMultipleRemoveEvents = supressRemoveEvent
|| this.supressClearValueRemoveEvents || false;
this.removeAllItems();
this.fireEvent('clear', this);
return this;
},
onKeyUp : function(e) {
if (this.editable !== false && !e.isSpecialKey()
&& (!e.hasModifier() || e.shiftKey)) {
this.lastKey = e.getKey();
this.dqTask.delay(this.queryDelay);
}
},
onKeyDownHandler : function(e, t) {

var toDestroy, nextFocus, idx;
if ((e.getKey() === e.DELETE || e.getKey() === e.SPACE)
&& this.currentFocus) {
e.stopEvent();
toDestroy = this.currentFocus;
this.on('expand', function() {
this.collapse();
}, this, {
single : true
});
idx = this.items.indexOfKey(this.currentFocus.key);

this.clearCurrentFocus();

if (idx < (this.items.getCount() - 1)) {
nextFocus = this.items.itemAt(idx + 1);
}

toDestroy.preDestroy(true);
if (nextFocus) {
(function() {
nextFocus.onLnkFocus();
this.currentFocus = nextFocus;
}).defer(200, this);
}

return true;
}

var val = this.el.dom.value, it, ctrl = e.ctrlKey;
if (e.getKey() === e.ENTER) {
e.stopEvent();
if (val !== "") {
if (ctrl || !this.isExpanded()) { // ctrl+enter
// for new
// items
this.view.clearSelections();
this.collapse();
this.setRawValue('');
this.fireEvent('newitem', this, val);
} else {
this.onViewClick();
// removed from 3.0.1
if (this.unsetDelayCheck) {
this.delayedCheck = true;
this.unsetDelayCheck.defer(10, this);
}
}
} else {
if (!this.isExpanded()) {
return;
}
this.onViewClick();
// removed from 3.0.1
if (this.unsetDelayCheck) {
this.delayedCheck = true;
this.unsetDelayCheck.defer(10, this);
}
}
return true;
}

if (val !== '') {
this.autoSize();
return;
}

// select first item
if (e.getKey() === e.HOME) {
e.stopEvent();
if (this.items.getCount() > 0) {
this.collapse();
it = this.items.get(0);
it.el.focus();

}
return true;
}
// backspace remove
if (e.getKey() === e.BACKSPACE) {
e.stopEvent();
if (this.currentFocus) {
toDestroy = this.currentFocus;
this.on('expand', function() {
this.collapse();
}, this, {
single : true
});

idx = this.items.indexOfKey(toDestroy.key);

this.clearCurrentFocus();
if (idx < (this.items.getCount() - 1)) {
nextFocus = this.items.itemAt(idx + 1);
}

toDestroy.preDestroy(true);

if (nextFocus) {
(function() {
nextFocus.onLnkFocus();
this.currentFocus = nextFocus;
}).defer(200, this);
}

return;
} else {
it = this.items.get(this.items.getCount() - 1);
if (it) {
if (this.backspaceDeletesLastItem) {
this.on('expand', function() {
this.collapse();
}, this, {
single : true
});
it.preDestroy(true);
} else {
if (this.navigateItemsWithTab) {
it.onElClick();
} else {
this.on('expand', function() {
this.collapse();
this.currentFocus = it;
this.currentFocus.onLnkFocus
.defer(
20,
this.currentFocus);
}, this, {
single : true
});
}
}
}
return true;
}
}

if (!e.isNavKeyPress()) {
this.multiSelectMode = false;
this.clearCurrentFocus();
return;
}
// arrow nav
if (e.getKey() === e.LEFT
|| (e.getKey() === e.UP && !this.isExpanded())) {
e.stopEvent();
this.collapse();
// get last item
it = this.items.get(this.items.getCount() - 1);
if (this.navigateItemsWithTab) {
// focus last el
if (it) {
it.focus();
}
} else {
// focus prev item
if (this.currentFocus) {
idx = this.items.indexOfKey(this.currentFocus.key);
this.clearCurrentFocus();

if (idx !== 0) {
this.currentFocus = this.items.itemAt(idx - 1);
this.currentFocus.onLnkFocus();
}
} else {
this.currentFocus = it;
if (it) {
it.onLnkFocus();
}
}
}
return true;
}
if (e.getKey() === e.DOWN) {
if (this.currentFocus) {
this.collapse();
e.stopEvent();
idx = this.items.indexOfKey(this.currentFocus.key);
if (idx == (this.items.getCount() - 1)) {
this.clearCurrentFocus.defer(10, this);
} else {
this.clearCurrentFocus();
this.currentFocus = this.items.itemAt(idx + 1);
if (this.currentFocus) {
this.currentFocus.onLnkFocus();
}
}
return true;
}
}
if (e.getKey() === e.RIGHT) {
this.collapse();
it = this.items.itemAt(0);
if (this.navigateItemsWithTab) {
// focus first el
if (it) {
it.focus();
}
} else {
if (this.currentFocus) {
idx = this.items.indexOfKey(this.currentFocus.key);
this.clearCurrentFocus();
if (idx < (this.items.getCount() - 1)) {
this.currentFocus = this.items.itemAt(idx + 1);
if (this.currentFocus) {
this.currentFocus.onLnkFocus();
}
}
} else {
this.currentFocus = it;
if (it) {
it.onLnkFocus();
}
}
}
}
},
onKeyUpBuffered : function(e) {
if (!e.isNavKeyPress()) {
this.autoSize();
}
},
reset : function() {
Ext.ux.form.SuperBoxSelect.superclass.reset.call(this);
this.addedRecords = [];
this.autoSize().setRawValue('');
this.el.focus();
},
applyEmptyText : function() {
if (this.items.getCount() > 0) {
this.el.removeClass(this.emptyClass);
this.setRawValue('');
return this;
}
if (this.rendered && this.emptyText
&& this.getRawValue().length < 1) {
this.setRawValue(this.emptyText);
this.el.addClass(this.emptyClass);
}
return this;
},
/**
* @private
*
* Use clearValue instead
*/
removeAllItems : function() {
this.items.each(function(item) {
item.preDestroy(true);
}, this);
this.manageClearBtn();
return this;
},
resetStore : function() {
this.store.clearFilter();
if (!this.removeValuesFromStore) {
return this;
}
this.usedRecords.each(function(rec) {
this.store.add(rec);
}, this);
this.sortStore();
return this;
},
sortStore : function() {
var ss = this.store.getSortState();
if (ss && ss.field) {
this.store.sort(ss.field, ss.direction);
}
return this;
},
getCaption : function(dataObject) {
if (typeof this.displayFieldTpl === 'string') {
this.displayFieldTpl = new Ext.XTemplate(this.displayFieldTpl);
}
var caption, recordData = dataObject instanceof Ext.data.Record
? dataObject.data
: dataObject;

if (this.displayFieldTpl) {
caption = this.displayFieldTpl.apply(recordData);
} else if (this.displayField) {
caption = recordData[this.displayField];
}

return caption;
},
addRecord : function(record) {
var display = record.data[this.displayField], caption = this
.getCaption(record), val = record.data[this.valueField], cls = this.classField
? record.data[this.classField]
: '', style = this.styleField
? record.data[this.styleField]
: '';

if (this.removeValuesFromStore) {
this.usedRecords.add(val, record);
this.store.remove(record);
}

this.addItemBox(val, display, caption, cls, style);
this.fireEvent('additem', this, val, record);
},
createRecord : function(recordData) {
if (!this.recordConstructor) {
var recordFields = [{
name : this.valueField
}, {
name : this.displayField
}];
if (this.classField) {
recordFields.push({
name : this.classField
});
}
if (this.styleField) {
recordFields.push({
name : this.styleField
});
}
this.recordConstructor = Ext.data.Record
.create(recordFields);
}
return new this.recordConstructor(recordData);
},
/**
* Adds an array of items to the SuperBoxSelect component if the
* {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set
* to true.
*
* @methodOf Ext.ux.form.SuperBoxSelect
* @name addItem
* @param {Array}
* newItemObjects An Array of object literals containing
* the property names and values for an item. The
* property names must match those specified in
* {@link #Ext.ux.form.SuperBoxSelect-displayField},
* {@link #Ext.ux.form.SuperBoxSelect-valueField} and
* {@link #Ext.ux.form.SuperBoxSelect-classField}
*/
addItems : function(newItemObjects) {
if (Ext.isArray(newItemObjects)) {
Ext.each(newItemObjects, function(item) {
this.addItem(item);
}, this);
} else {
this.addItem(newItemObjects);
}
},
/**
* Adds an item to the SuperBoxSelect component if the
* {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set
* to true.
*
* @methodOf Ext.ux.form.SuperBoxSelect
* @name addItem
* @param {Object}
* newItemObject An object literal containing the
* property names and values for an item. The property
* names must match those specified in
* {@link #Ext.ux.form.SuperBoxSelect-displayField},
* {@link #Ext.ux.form.SuperBoxSelect-valueField} and
* {@link #Ext.ux.form.SuperBoxSelect-classField}
*/
addItem : function(newItemObject) {

var val = newItemObject[this.valueField];

if (this.disabled) {
return false;
}
if (this.preventDuplicates && this.hasValue(val)) {
return;
}

// use existing record if found
var record = this.findRecord(this.valueField, val);
if (record) {
this.addRecord(record);
return;
} else if (!this.allowAddNewData) { // else it's a new
// item
return;
}

if (this.mode === 'remote') {
this.remoteLookup.push(newItemObject);
this.doQuery(val, false, false);
return;
}

var rec = this.createRecord(newItemObject);
this.store.add(rec);
this.addRecord(rec);

return true;
},
addItemBox : function(itemVal, itemDisplay, itemCaption, itemClass,
itemStyle) {
var parseStyle = function(s) {
var ret = '';
if (typeof s == 'function') {
ret = s.call();
} else if (typeof s == 'object') {
for (var p in s) {
ret += p + ':' + s[p] + ';';
}
} else if (typeof s == 'string') {
ret = s + ';';
}
return ret;
};
var itemKey = Ext.id(null, 'sbx-item');
var box = new Ext.ux.form.SuperBoxSelectItem({
owner : this,
disabled : this.disabled,
renderTo : this.wrapEl,
cls : this.extraItemCls + ' ' + itemClass,
style : parseStyle(this.extraItemStyle) + ' '
+ itemStyle,
caption : itemCaption,
display : itemDisplay,
value : itemVal,
key : itemKey,
listeners : {
'remove' : function(item) {
if (this.fireEvent('beforeremoveitem',
this, item.value) === false) {
return;
}
this.items.removeKey(item.key);
if (this.removeValuesFromStore) {
if (this.usedRecords
.containsKey(item.value)) {
this.store.add(this.usedRecords
.get(item.value));
this.usedRecords
.removeKey(item.value);
this.sortStore();
if (this.view) {
this.view.render();
}
}
}
if (!this.preventMultipleRemoveEvents) {
this.fireEvent
.defer(
250,
this,
[
'removeitem',
this,
item.value,
this
.findInStore(item.value)]);
}
this.preventMultipleRemoveEvents = false;
},
destroy : function() {
this.collapse();
this.autoSize().manageClearBtn()
.validateValue();
},
scope : this
}
});
box.render();

box.hidden = this.el.insertSibling({
tag : 'input',
type : 'hidden',
value : itemVal,
name : (this.hiddenName || this.name)
}, 'before');

this.items.add(itemKey, box);
this.applyEmptyText().autoSize().manageClearBtn()
.validateValue();
},
manageClearBtn : function() {
if (!this.renderFieldBtns || !this.rendered) {
return this;
}
var cls = 'x-superboxselect-btn-hide';
if (this.items.getCount() === 0) {
this.buttonClear.addClass(cls);
} else {
this.buttonClear.removeClass(cls);
}
return this;
},
findInStore : function(val) {
var index = this.store.find(this.valueField, val);
if (index > -1) {
return this.store.getAt(index);
}
return false;
},
/**
* Returns a String value containing a concatenated list of item
* values. The list is concatenated with the
* {@link #Ext.ux.form.SuperBoxSelect-valueDelimiter}.
*
* @methodOf Ext.ux.form.SuperBoxSelect
* @name getValue
* @return {String} a String value containing a concatenated list of
* item values.
*/
getValue : function() {
var ret = [];
this.items.each(function(item) {
ret.push(item.value);
});
return ret.join(this.valueDelimiter);
},
/**
* Iterates and selects all elements available Must take into
* consideration the ones that are already selected plus the ones
* from the store.
*/
selectAll : function() {
var ret = this.getValuesAsArray();
var valueField = this.valueField;
Ext.iterate(this.store.data.items, function(item) {
ret.push(item.get(valueField));
});
if (!Ext.isEmpty(ret)) {
this.setValue(ret.join(this.valueDelimiter));
}
},
selectItems : function(array) {
var ret = [];
Ext.iterate(array, function(item) {
ret.push(item)
});
if (!Ext.isEmpty(ret)) {
this.setValue(ret.join(this.valueDelimiter));
}
},
/**
* Returns a String value containing a concatenated list of item
* values. The list is concatenated with the
* {@link #Ext.ux.form.SuperBoxSelect-valueDelimiter}.
*
* @methodOf Ext.ux.form.SuperBoxSelect
* @name getValue
* @return {String} a String value containing a concatenated list of
* item values.
*/
getValuesAsArray : function() {
var ret = [];
this.items.each(function(item) {
ret.push(item.value);
});
return ret;
},
/**
* Returns an Array of item objects containing the
* {@link #Ext.ux.form.SuperBoxSelect-displayField},
* {@link #Ext.ux.form.SuperBoxSelect-valueField} and
* {@link #Ext.ux.form.SuperBoxSelect-classField} properties.
*
* @methodOf Ext.ux.form.SuperBoxSelect
* @name getValueEx
* @return {Array} an array of item objects.
*/
getValueEx : function() {
var ret = [];
this.items.each(function(item) {
var newItem = {};
newItem[this.valueField] = item.value;
newItem[this.displayField] = item.display;
newItem[this.classField] = item.cls;
ret.push(newItem);
}, this);
return ret;
},
// private
initValue : function() {

Ext.ux.form.SuperBoxSelect.superclass.initValue.call(this);
if (this.mode === 'remote') {
this.setOriginal = true;
}
},
/**
* Sets the value of the SuperBoxSelect component.
*
* @methodOf Ext.ux.form.SuperBoxSelect
* @name setValue
* @param {String|Array}
* value An array of item values, or a String value
* containing a delimited list of item values. (The list
* should be delimited with the
* {@link #Ext.ux.form.SuperBoxSelect-valueDelimiter)
*/
setValue : function(value) {
if (!this.rendered) {
this.preRenderValue = value;
return;
}

var values = Ext.isArray(value) ? value : value
.split(this.valueDelimiter);
this.removeAllItems().resetStore();

// reset remoteLookup because setValue should overwrite
// everything
// inc pending data
this.remoteLookup = [];

Ext.each(values, function(val) {
var record = this.findRecord(this.valueField, val);
if (record) {
this.addRecord(record);
} else if (this.mode === 'remote') {
this.remoteLookup.push(val);
}
}, this);

if (this.mode === 'remote') {
var q = this.remoteLookup.join(this.queryValuesDelimiter);
this.doQuery(q, false, true); // 3rd param to
// specify a values
// query
}

},
/**
* Sets the value of the SuperBoxSelect component, adding new items
* that don't exist in the data store if the
* {@link #Ext.ux.form.SuperBoxSelect-allowAddNewData} config is set
* to true.
*
* @methodOf Ext.ux.form.SuperBoxSelect
* @name setValue
* @param {Array}
* data An Array of item objects containing the
* {@link #Ext.ux.form.SuperBoxSelect-displayField},
* {@link #Ext.ux.form.SuperBoxSelect-valueField} and
* {@link #Ext.ux.form.SuperBoxSelect-classField}
* properties.
*/
setValueEx : function(data) {
this.removeAllItems().resetStore();

if (!Ext.isArray(data)) {
data = [data];
}
Ext.each(data, function(item) {
this.addItem(item);
}, this);
},
/**
* Returns true if the SuperBoxSelect component has a selected item
* with a value matching the 'val' parameter.
*
* @methodOf Ext.ux.form.SuperBoxSelect
* @name hasValue
* @param {Mixed}
* val The value to test.
* @return {Boolean} true if the component has the selected value,
* false otherwise.
*/
hasValue : function(val) {
var has = false;
this.items.each(function(item) {
if (item.value == val) {
has = true;
return false;
}
}, this);
return has;
},
onSelect : function(record, index) {
var val = record.data[this.valueField];

if (this.preventDuplicates && this.hasValue(val)) {
return;
}

this.setRawValue('');
this.lastSelectionText = '';

if (this.fireEvent('beforeadditem', this, val) !== false) {
this.addRecord(record);
}
if (this.store.getCount() === 0 || !this.multiSelectMode) {
this.collapse();
} else {
this.restrictHeight();
}
},
onDestroy : function() {
this.items.each(function(item) {
item.preDestroy(true);
}, this);

if (this.renderFieldBtns) {
Ext.destroy(this.buttonClear, this.buttonExpand,
this.buttonWrap);
}

Ext.destroy(this.inputEl, this.wrapEl, this.outerWrapEl);

Ext.ux.form.SuperBoxSelect.superclass.onDestroy.call(this);
},
autoSize : function() {
if (!this.rendered) {
return this;
}
if (!this.metrics) {
this.metrics = Ext.util.TextMetrics.createInstance(this.el);
}
var el = this.el, v = el.dom.value, d = document
.createElement('div');

if (v === "" && this.emptyText && this.items.getCount() < 1) {
v = this.emptyText;
}
d.appendChild(document.createTextNode(v));
v = d.innerHTML;
d = null;
v += "&#160;";
var w = Math.max(this.metrics.getWidth(v) + 24, 24);
if (typeof this._width != 'undefined') {
w = Math.min(this._width, w);
}
this.el.setWidth(w);

if (Ext.isIE) {
this.el.dom.style.top = '0';
}
return this;
},
doQuery : function(q, forceAll, valuesQuery) {
q = Ext.isEmpty(q) ? '' : q;
var qe = {
query : q,
forceAll : forceAll,
combo : this,
cancel : false
};
if (this.fireEvent('beforequery', qe) === false || qe.cancel) {
return false;
}
q = qe.query;
forceAll = qe.forceAll;
if (forceAll === true || (q.length >= this.minChars)) {
if (this.lastQuery !== q) {
this.lastQuery = q;
if (this.mode == 'local') {
this.selectedIndex = -1;
if (forceAll) {
this.store.clearFilter();
} else {
this.store.filter(this.displayField, q);
}
this.onLoad();
} else {

this.store.baseParams[this.queryParam] = q;
this.store.baseParams[this.queryValuesInidicator] = valuesQuery;
this.store.load({
params : this.getParams(q)
});
this.expand();
}
} else {
this.selectedIndex = -1;
this.onLoad();
}
}
}
});
Ext.reg('superboxselect', Ext.ux.form.SuperBoxSelect);
/*
* @private
*/
Ext.ux.form.SuperBoxSelectItem = function(config) {
Ext.apply(this, config);
Ext.ux.form.SuperBoxSelectItem.superclass.constructor.call(this);
};
/*
* @private
*/
Ext.ux.form.SuperBoxSelectItem = Ext.extend(Ext.ux.form.SuperBoxSelectItem,
Ext.Component, {
initComponent : function() {
Ext.ux.form.SuperBoxSelectItem.superclass.initComponent
.call(this);
},
onElClick : function(e) {
var o = this.owner;
o.clearCurrentFocus().collapse();
if (o.navigateItemsWithTab) {
this.focus();
} else {
o.el.dom.focus();
var that = this;
(function() {
this.onLnkFocus();
o.currentFocus = this;
}).defer(10, this);
}
},

onLnkClick : function(e) {
if (e) {
e.stopEvent();
}
this.preDestroy();
if (!this.owner.navigateItemsWithTab) {
this.owner.el.focus();
}
},
onLnkFocus : function() {
this.el.addClass("x-superboxselect-item-focus");
this.owner.outerWrapEl.addClass("x-form-focus");
},

onLnkBlur : function() {
this.el.removeClass("x-superboxselect-item-focus");
this.owner.outerWrapEl.removeClass("x-form-focus");
},

enableElListeners : function() {
this.el.on('click', this.onElClick, this, {
stopEvent : true
});

this.el.addClassOnOver('x-superboxselect-item-hover');;
},

enableLnkListeners : function() {
this.lnk.on({
click : this.onLnkClick,
focus : this.onLnkFocus,
blur : this.onLnkBlur,
scope : this
});
},

enableAllListeners : function() {
this.enableElListeners();
this.enableLnkListeners();
},
disableAllListeners : function() {
this.el.removeAllListeners();
this.lnk.un('click', this.onLnkClick, this);
this.lnk.un('focus', this.onLnkFocus, this);
this.lnk.un('blur', this.onLnkBlur, this);
},
onRender : function(ct, position) {

Ext.ux.form.SuperBoxSelectItem.superclass.onRender.call(this,
ct, position);

var el = this.el;
if (el) {
el.remove();
}

this.el = el = ct.createChild({
tag : 'li'
}, ct.last());
el.addClass('x-superboxselect-item');

var btnEl = this.owner.navigateItemsWithTab ? (Ext.isSafari
? 'button'
: 'a') : 'span';
var itemKey = this.key;

Ext.apply(el, {
focus : function() {
var c = this.down(btnEl
+ '.x-superboxselect-item-close');
if (c) {
c.focus();
}
},
preDestroy : function() {
this.preDestroy();
}.createDelegate(this)
});

this.enableElListeners();

el.update(this.caption);

var cfg = {
tag : btnEl,
'class' : 'x-superboxselect-item-close',
tabIndex : this.owner.navigateItemsWithTab ? '0' : '-1'
};
if (btnEl === 'a') {
cfg.href = '#';
}
this.lnk = el.createChild(cfg);

if (!this.disabled) {
this.enableLnkListeners();
} else {
this.disableAllListeners();
}

this.on({
disable : this.disableAllListeners,
enable : this.enableAllListeners,
scope : this
});

this.setupKeyMap();
},
setupKeyMap : function() {
new Ext.KeyMap(this.lnk, [{
key : [Ext.EventObject.BACKSPACE,
Ext.EventObject.DELETE,
Ext.EventObject.SPACE],
fn : this.preDestroy,
scope : this
}, {
key : [Ext.EventObject.RIGHT, Ext.EventObject.DOWN],
fn : function() {
this.moveFocus('right');
},
scope : this
}, {
key : [Ext.EventObject.LEFT, Ext.EventObject.UP],
fn : function() {
this.moveFocus('left');
},
scope : this
}, {
key : [Ext.EventObject.HOME],
fn : function() {
var l = this.owner.items.get(0).el.focus();
if (l) {
l.el.focus();
}
},
scope : this
}, {
key : [Ext.EventObject.END],
fn : function() {
this.owner.el.focus();
},
scope : this
}, {
key : Ext.EventObject.ENTER,
fn : function() {
}
}]).stopEvent = true;
},
moveFocus : function(dir) {
var el = this.el[dir == 'left' ? 'prev' : 'next']()
|| this.owner.el;

el.focus.defer(100, el);
},

preDestroy : function(supressEffect) {
if (this.fireEvent('remove', this) === false) {
return;
}
var actionDestroy = function() {
if (this.owner.navigateItemsWithTab) {
this.moveFocus('right');
}
this.hidden.remove();
this.hidden = null;
this.destroy();
};

if (supressEffect) {
actionDestroy.call(this);
} else {
this.el.hide({
duration : 0.2,
callback : actionDestroy,
scope : this
});
}
return this;
},
onDestroy : function() {
Ext.destroy(this.lnk, this.el);

Ext.ux.form.SuperBoxSelectItem.superclass.onDestroy.call(this);
}
});

prakashr
23 Sep 2010, 3:32 AM
Is there a way to pre-select some checkboxes when the control loads?

Scorpie
25 Sep 2010, 3:20 AM
Hi,

Anybody that has an example of how to use this extension in a grideditor? I`ve searched trough all 34 pages but found nothing.........thanks in advance!

danilo.pederiva
26 Sep 2010, 1:55 AM
Hi,

I am trying to use SuperBoxSelect with a remote store. I would like to insert a button which allows the user to select all entries coming from this store. I can do .setValue('1,2,3,...'), but the thing is that I do not know a priori which entries are in the store (or, better, at the moment I know, but those values may change in the future).

Any hints?

Thanks

EDIT: I just saw here in this thread a message by Grolubao with some enhancements to the code, where also a selectAll function was added. It works very nicely, thanks!
http://www.sencha.com/forum/showthread.php?69307-3.x-Ext.ux.form.SuperBoxSelect&p=501005#post501005

sebterzi
7 Oct 2010, 9:03 AM
Hello everyone,

SuperBoxSelect is great but I have a big problem. i use extjs v3

I use SuperBoxSelect with dynamic store in a "Ext.Window" to create and edit user.

In first time my SuperBoxSelect works, i open my window "create user", i choose items in SuperBoxSelect, then i post data in JSON and my window hide, everything is ok.
In second time when i want create a second user, so i open the window for create user and my SuperBoxSelect doesn't show items anymore. It's blocked, no items.

After some research in my code i think the problem is in the function ClientModifyWindow.hide(); because when i remove this function i can create many users, i have items in my SuperBoxSelect but my window is at the forefront and will not close .

Have you any ideas about this problem?

Here's the code synthesized

CODE :




//-- DEBUT RECUPERATION DONNEES MODE PAIEMENT
var recordObj = Ext.data.Record.create([
{name: 'id_secteur'},
{name: 'design_secteur'}
]);

var as_store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({ // on vas utiliser une requette Http
url: 'client/cgrid', //url de l'action qui gère notre grille
method: 'POST' //on va utiliser la méthode poste pour notre requette
}),
baseParams:{gAction: "gGetAS"}, //on défini les paramètre à transmettre au l'action
reader: new Ext.data.JsonReader({
root: 'results',
totalProperty: 'totalcount',
id: 'design_secteur'
},recordObj)
}); // eo my_store
as_store.load();

.............................

as_client_create = new Ext.ux.form.SuperBoxSelect({
name:'as_client_create',
id:'as_client_create',
xtype:'superboxselect',
fieldLabel: 'Activit\351s Sportives',
emptyText: 'Choisir Activit\351s Sportives',
anchor:'95%',
store: as_store,
mode: 'local',
displayField: 'design_secteur',
tabIndex:5,
valueField: 'id_secteur'
});

................................

buttons: [{
text: 'Cr\351er nouveau client',
handler:function()
{
createTheClient();
ClientCreateWindow.hide(); // PB
}

....................................

function resetClientForm(){
//Ext.getCmp('as_client_create').reset();
as_client_create.setValue('');
}

function createTheClient(){
if(isClientFormValid()){
Ext.Ajax.request({
waitMsg: 'Please wait...',
url: 'client/cgrid', //url de l'action qui gère notre grille
params: {
gAction: "gCreateClient",
//FACTURE
//id_clientField
titre_f_client_create: titre_f_client_create.getValue(),
...................
as_client_create: as_client_create.getValue(),
....................

},
success: function(response){
var result=eval(response.responseText);
switch(result){
case 1:
Ext.MessageBox.alert('Creation OK','Le client a bien \351t\351 cr\351\351.');

articlesDataStore.reload();
resetClientForm();
break;
default:
Ext.MessageBox.alert('Attention','Probleme lors de la cr\351tion du client.');
break;
}
},
failure: function(response){
var result=response.responseText;
Ext.MessageBox.alert('Error','Probleme connexion a la BDD');
}
});
} else {
Ext.MessageBox.alert('Attention','Le formulaire n\'est pas valide');


}
} I really need help, I'm lost, Thank you

abraxxa
11 Nov 2010, 8:14 AM
Thanks for improving SuperBoxSelect Grolubao (http://www.sencha.com/forum/member.php?61570-Grolubao)!
Since I've upgraded to your latest version which calls clearInvalid sbs.isValid doesn't work and goes into an infinity recursion when I do console.log(sbs.isValid());
In the previos version the clearInvalid calls where commented out with a FIXME note.

panter4
15 Nov 2010, 2:59 PM
first:
thank you for a great extension!

then i have a tiny demand/idea, or maybe i'm wrong
setting the field as readOnly dosent work
i would like to use the extension for displaying some data, since i't first used to enter the data, it would look nice and consistent do display the data in the same shape


using renderFieldBtns:false removes the main buttons but the X-es on each item stay, so that item can be removed

i would like to propose another config option for removing those, so the extension can also be used for displaying data,
or fix the readOnly property to remove all user input properly

... or (since im new to Ext) maybe i'm completely wrong, and just couldnt find the option, in that case do please enlighten me

boggle
24 Nov 2010, 6:24 AM
I shamefully contribute way too little given the help I get from these extensions and forum so here's a little payback.

I've been using this great extension for a while successfully in an EditorGrid.

Having recently upgraded to 3.3 I found that SuperBoxselect would set the record field value to "".
After a good few hours tracing the problem it looks like it is because the grid editor tries to call this.field.assertValue() on edit complete if this exists. Since superboxSelect inherits form the combo (which has this method) this gets called.
The method is unique to combo (not needed on superbox).

my fix:

Ext.override(Ext.ux.form.SuperBoxSelect, {
assertValue:null
});

(I'd suggest any future release of this include the above property setting)

devtig
30 Nov 2010, 7:55 AM
Use this CSS to have a normal Combox height


.x-superboxselect {position:relative; height: auto !important; margin: 0px; overflow: hidden; padding:2px; display:block; outline: none !important;}
.x-superboxselect input[disabled] {background-color: transparent;}
.x-superboxselect ul {overflow: hidden; cursor: text;}
.x-superboxselect-display-btns {padding-right: 33px !important;}
.x-superboxselect-btns {position: absolute; right: 1px; top: 0; overflow:hidden; padding:2px;}
.x-superboxselect-btns div {float: left; width: 16px; height: 16px; margin-top: 2px;}
.x-superboxselect-btn-clear {background: url(../img/superboxselect/clear.png) no-repeat scroll left 0px;}
.x-superboxselect-btn-expand {background: url(../img/superboxselect/expand.png) no-repeat scroll left 0px;}
.x-superboxselect-btn-over {background-position: left -16px}
.x-superboxselect-btn-hide {display:none;}
.x-superboxselect li {float: left; margin: 1px; padding: 0;line-height: 14px;}
body.ext-ie6 .x-superboxselect li {line-height: 16px;}
.x-superboxselect-stacked li {float: none !important;}
.x-superboxselect-input input { border: none; outline: none; margin-top: 0px; margin-bottom: 1px;}
/* unnecessary (ie7 not tested):
body.ext-ie .x-superboxselect-input input {background: none; border: none; margin-top: 3px;}
.x-superboxselect-item {position: relative; -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; o-border-radius: 6px; khtml-border-radius: 6px; border: 1px solid #CAD8F3; background-color: #DEE7F8; padding: 1px 15px 1px 5px !important; }
body.ext-ie7 .x-superboxselect-item {margin: 2px 1px 2px 1px; line-height: 1.2em; padding: 2px 17px 4px 5px !important;}
body.ext-ie6 .x-superboxselect-item {margin: 2px 1px 2px 1px; line-height: 1.2em; padding: 2px 19px 4px 5px !important;}
*/
.x-superboxselect-item {position: relative; -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; o-border-radius: 6px; khtml-border-radius: 6px; border: 1px solid #CAD8F3; background-color: #DEE7F8; padding: 0px 15px 0px 3px !important; }
.x-superboxselect-item-hover {background: #BBCEF1; border: 1px solid #6D95E0;}
.x-superboxselect-item-focus {border-color: #598BEC; background: #598BEC; color: #fff;}
.x-superboxselect-item-close {background: url(../img/superboxselect/close.png) no-repeat scroll left 0px; border: none; cursor: default; font-size: 1px; height: 16px;padding:0; position: absolute; right: 0px; top: 0px; width: 13px;display:block;cursor:pointer;}
.x-superboxselect-item-close:hover, .x-superboxselect-item-close:active { background-position: left -12px;}
.x-superboxselect-item-focus .x-superboxselect-item-close{ background-position: left -24px}
.x-item-disabled .x-superboxselect-item-close{ background-position: left -36px}

FF, IE8 and IE6 ok. 1px too high in Chrome. IE7 not tested yet. But this is already better then the way to high boxes as before.

abraxxa
13 Dec 2010, 4:57 AM
I've discovered a bug:
The valuesquery isn't executed if the primary key value is shorten than this.minChars.
The fix is below.



diff --git a/root/static/Ext.ux.form.SuperBoxSelect.js b/root/static/Ext.ux.form.SuperBoxSelect.js
index 35f8e4f..529b605 100644
--- a/root/static/Ext.ux.form.SuperBoxSelect.js
+++ b/root/static/Ext.ux.form.SuperBoxSelect.js
@@ -1597,7 +1597,7 @@ Ext.ux.form.SuperBoxSelect = Ext.extend(Ext.ux.form.SuperBoxSelect,
}
q = qe.query;
forceAll = qe.forceAll;
- if (forceAll === true || (q.length >= this.minChars)) {
+ if (forceAll === true || (valuesQuery === true && q.length > 0) || (q.length >= this.minChars)) {
if (this.lastQuery !== q) {
this.lastQuery = q;
if (this.mode == 'local') {


I'd like to setup a github repository for this UX as my main app depends pretty heavy on it.
If you use it too and want to contribute please tell me your opinion!

zozofoz
14 Dec 2010, 5:00 PM
Which version are you using? It looks like it was fixed in the version I have. Mine says version 1.0 at top created 01/17/2010.

forceAll = qe.forceAll;
if(forceAll === true || (q.length >= this.minChars) || valuesQuery && !Ext.isEmpty(q)){
if(this.lastQuery !== q || forcedAdd){

abraxxa
15 Dec 2010, 3:34 AM
I think I've fixed the bug in the original version over a year ago, but the extended version by Grolubao doesn't include it.

Sesshomurai
15 Dec 2010, 9:20 AM
I'm having trouble getting this to work in 'remote' mode. The remote store works, the data loads into the combo, but I want to click on multiple combo results like the local mode. It doesn't work.

Anyone see or fix this?

thanks.

abraxxa
15 Dec 2010, 10:03 AM
Can you please explain the problem in more detail?
I'm using it only in remote mode and everything works as expected.
When you click the trigger all items are displayed, when you click one it is added to the list. You can repeat this multiple times.

devtig
15 Dec 2010, 10:12 AM
First get your functionality to work with a regular Ext.form.ComboBox. If that works and only then, you switch to the superselectbox.

Sesshomurai
3 Jan 2011, 7:12 AM
So in the local mode example, it shows a bunch of items and when I click each item, it is put into the box and removed from the list - and the drop down list remains visible to allow for multiple items to be selected.

In remote mode, it does not behave this way for me. When I select an item from the list, it is put into the box and the list disappears.

Any advice?

thanks.

abraxxa
3 Jan 2011, 7:58 AM
Click the trigger again, does it show the list again?

Sesshomurai
5 Jan 2011, 8:40 AM
Click the trigger again, does it show the list again?

I'll check that when I get back to my computer. If I recall, it does. but what I want is for the list to stay open and items be removed as I click them, like local mode.

abraxxa
5 Jan 2011, 8:54 AM
I've never used local mode so I didn't know it behaves differently.
I'd prefer if the dropdown stays open in remote mode also.

iv_ekker
17 Jan 2011, 5:11 AM
Hello,

I have downloaded sources and i have found
text
License - TBA (To be announced)

Please, could you appoint the license version?

Thank you for the extension.

Kind regards,
Sergei Chentsov.

abraxxa
17 Jan 2011, 6:07 AM
Who should add it? It seems the original author has moved to Austrialia and is out of business: http://technomedia.co.uk/

iv_ekker
17 Jan 2011, 6:19 AM
Who should add it? It seems the original author has moved to Austrialia and is out of business: http://technomedia.co.uk/

Thank you.

I think proprietary rights holder can add this only - i think it is Dan Hamphrey.

danh2000
17 Jan 2011, 3:03 PM
Sergei, If the project is non-commercial, feel free to use SBS without restriction. I do have much newer version with a load of fixes and new features, but I can't handle the support at present so am waiting for Ext 4 before I launch a newer more advanced version with license options.

Regards,

Dan

iv_ekker
17 Jan 2011, 9:52 PM
Hi Dan,

Good to see your message! ;)
Thanks for component, explanation and permission.

I would be happy to use this component.
How can I communicate with you if i would like to use it in my app for commercial case?

Kind regards,
Sergei Chentsov.

abraxxa
18 Jan 2011, 5:38 AM
Good to see you're alive Dan!
Do you have the code on github? This would make contributing much easier!

--miCZar--
20 Jan 2011, 1:00 PM
It looks like there is problem with loosing hover on item with Ext 3.3.0. After loosing hover element still has hover css.

abraxxa
20 Jan 2011, 1:30 PM
That was already fixed, look at one of the previous pages.

--miCZar--
20 Jan 2011, 11:21 PM
Thanks.