=encoding ISO8859-1

=head1 NAME

Alien::GvaScript::Autocompleter - autocompletion on form input fields

=head1 SYNOPSIS

In Javascript :

  var autocompleter1 = new GvaScript.Autocompleter(
      "http::/some/url", 
      {minimumChars : 2, 
       strict       : true, 
       onBind       : doSomething,
       onLeave      : doSomethingElse}  );

  var autoCompleter2 = new GvaScript.Autocompleter(
        ["foo", "bar", ...], options);

  var autoCompleter3 = new GvaScript.Autocompleter(
        [{label: "foo", value: "f", otherValue: 123},
         {label: "bar", value: "b", otherValue: 456}, ...], options);

  var autoCompleter4 = new GvaScript.Autocompleter(
        myCompletionFunction, options);


Then, in HTML :

  <input onfocus="autoCompleter1.autocomplete(this)">


=head1 DESCRIPTION

Component designed both as an "autocompleter" (anticipating
further key events by users) and as a replacement for HTML C<SELECT>
form items.

An autocompleter instance encapsulates a datasource (which may be an
inline object, a callback function or an Ajax request), together with
some behavioral options (detailed below).  That autocompleter may then
be I<bound> to one or several input fields in a form (but only one at
a time), and will take care of capturing user input, navigating in the
suggestion list, and filling the field with the chosen value.

An event model is associated with the autocompleter, so that
client code can insert hooks to various steps of
the autocompletion behaviour.

The list of suggestions may contain arbitrary HTML, including rich
formatting options.


=head1 BEHAVIOUR

When the input field gets focus, the autocompleter starts listening
for key events. As soon as C<minimumChars> have been typed, or if the
user presses the C<DOWN> arrow key, the autocompleter gets a list of
suggestions from the datasource, and displays them in a dropdown list.

The dropdown list can be navigated through arrow keys. Selecting a
suggestion in the list is done either by a click, or by pressing the
C<RETURN> key (this is handled by the
L<Alien::GvaScript::ChoiceList|choiceList> component of
GvaScript). Then the value of that suggestion fills the input field
value, dependent fields (if any) are updated, and the C<onLegalValue>
event is triggered.

A number of variations on this behaviour may be controlled by the options
described below.


=head1 CONSTRUCTOR

  var myAutocompleter = new GvaScript.Autocompleter(datasource, options);

=head2 Datasources

=head3 Origin

A datasource may be

=over

=item a plain string

The string is taken as a base URL. Whenever a suggestion list is needed,
the autocompleter will send an Ajax requests to that URL, concatenated with 
the current value in the associated field. So for example if we have

  var ac = new GvaScript.Autocompleter("/myapp/completion?search=");
  ..
  <input name="someInput" onfocus="ac.complete(this)">

and user has typed C<ab> in the input field, then an Ajax request
will be sent to C</myapp/completion?search=ab>.

The server should return a JSON array, in the format explained below.

=item a callback function

That function will be called, with the current value of the field
as single argument.

=item an array

The array is taken as in-memory datasource. The returned suggestion
list is either the complete array (when C<options.ignorePrefix> is true)
or just the list of items that are prefixed by the current value 
of the field. See also C<options.caseSensitive>.

=item an object (JSONP)

Useful when accessing data on a different domain via JSONP services.

  Ex :   { json_url: 'http://search.yahooapis.com/WebSearchService/V1/relatedSuggestion?appid=YahooDemo&query=?1&output=json&callback=?2',
           json_list: 'ResultSet/Result' }

The object should hold details of the JSONP service to be called. 

C<json_url> : url to call with placeholders (?1, ?2) for value to look for and callback method respectively.

C<json_list> : path to the list in the json response

=back

=head3 Format of suggestions returned by datasources

Datasources should return a list of suggestions in the form
of a Javascript array (in case of Ajax requests, the response 
should be a JSON body containing a single array). 

For each suggestion in the array, the autocompleter needs
a I<label> (an HTML fragment to display in suggestion
dropdown lists) and a I<value> (a plain string to put into
the text field when the suggestion is selected). So 
each suggestion may be either

=over

=item a plain string

this string will be used both as label and as value.

=item an inline object

this object is supposed to have a C<label> property and a 
C<value> property. Actually, these are the default names for
the properties; they can be changed in the constructor options.

The C<label> property may contain rich HTML, i.e. including
formatting tags.

=back


=head2 Options

The options to construct an autocompleter object are :

=over

=item minimumChars

How many characters are needed before trying to find suggestions.

=item labelField

Name of the field that contains the HTML to display
(default is C<label>).

=item valueField

Name of the field that contains the value to put in input element
(default is C<value>).

=item autoSuggest

Boolean value; toggles whether suggestions are displayed automatically
when available (true by default). If false, suggestions are only
displayed when the C<ARROW_DOWN> key is pressed.


=item autoSuggestDelay

How many milliseconds to wait after a keypress before displaying 
suggestions. Default is 200.

=item typeAhead

If true (the default), the current suggestion will be automatically 
inserted into the input element.

=item maxHeight

Maximum height for the suggestion DIV (in pixels).
Default is 200.

=item minWidth

Minimum width for the suggestion DIV (in pixels)
Default is 200.

=item offsetX

Offset (in pixels) from the left border of the 
input element to the left border of the 
suggestion DIV.
Default is 0.

=item strict

If this option is true and the user 
leaves the field with an illegal value
(not in the suggestion list), 
the field is marked with a red background.
Default is false.

=item blankOK

If this option is defined and false, 
the field is marked with a red background
when left with an empty value.
Default is true.

=item ignorePrefix

If true, and if the datasource is a Javascript array, then
that whole array is always displayed as suggestions, 
whatever may be typed in the input field (so the field
behaves more or less like a SELECT).
Default is false.

=item caseSensitive

This option only applies if the datasource is a Javascript array
and if C<ignorePrefix> is false.
If true (the default), filtering of the datasource array 
from the current value of the input field
will be case-sensitive.

=item colorIllegal

Which color to put in the background when a "strict" field contains 
an illegal value (default is red).

=item scrollCount

How many items to skip when hitting the 
C<PAGE_UP> or C<PAGE_DOWN> keys. 
Default is 5

=item htmlWrapper

See the C<ChoiceList> documentation.

=item choiceItemTagName

See the C<ChoiceList> documentation.

=item classes

Classes that will be assigned at various stages
to autocompleter DOM elements .
Possible classes are 

=over 


=item loading

Class added to the  input or textarea field while an Ajax
request is pending. Default is C<AC_loading>, a class that displays
an Ajax-loading icon.

=item dropdown

Class for the dropdown div that displays the autocompletion choices.
Default is C<AC_dropdown>.

=item message

Class for displaying warning messages.
Default is C<AC_message>.


=back


=item additional_params

Other parameters to be added in the Ajax query for autocompletion.
Can be either an array or an already encoded string (see C<Ajax.Options> in
C<prototype.js>).
[TODO: should be camelCase to be consistent with other options; 
check dependencies in DMWeb].


=item dependentFields

  var ac =  new GvaScript.Autocompleter(url, {
              dependentFields : {
                foo : "firstname",
                bar : "lastname",
                id  : "id"
              } } );

Inline object that specifies dependencies between the field
controlled by the autocompleter, and other fields in the same form.
When leaving the autocompleted field (C<onBlur>), the dependent fields
will be updated automatically. This only works for autocompleters
in strict mode.

Each key in the inline object specifies the name of a field related
to the autocompleted field. If field names are in dotted notation, 
then the related field is taken as a path relative to the autocompleted
field : so for example if the autocompleted field has name
C<some.very.3.long.2.path>, then the C<foo> entry in
C<dependentFields> will refer to field C<some.very.3.long.2.foo>.


The corresponding value (in our example above: C<firstname>) is
the name of a property to extract from the C<choice> member
that validated the current input. However, the autocompleted field
may also contain an empty value (in which case the related fields
are also emptied), or an illegal value (in which case the related
fields are filled with string C<ILLEGAL_***>, where C<***> is the 
key from the inline object).

If the C<choice> member is not an object, but just a string,
then that string is copied to the dependent field, therefore ignoring
the hash value (C<firstname> in our example).

As a special case, if the hash value is an empty string,
then the dependent field is emptied, ignoring whatever
information may be in the C<choice> element.


The dependent fields structure might also be specified as
an HTML attribute C<ac:dependentFields>, instead of an option 
to the Javascript object :

  <input name="some.very.3.long.2.path"
         onfocus="ac.autocomplete(this)"
         ac:dependentFields="{foo:'firstname',bar:'lastname',id:'id'}" />
  ...
  <input type="hidden"
         name="some.very.3.long.2.id" />



=back

=head1 METHODS

=head2 autocomplete(inputField)

Returns an event handler to be bound to the C<onfocus> event on
an input field, i.e.

  <input name="someInput" onfocus="myAutoCompleter.complete(this)">

The autocompleter will automatically register 
C<onblur>, C<onclick> and C<onkeydown> handlers on the same field, so avoid
setting your own, which may cause unpredictable interactions.
However the autocompleter has its own event model 
to which you can bind your handling code
(see the L<EVENTS> section below).

=head2 detach(inputField)

Permanently detaches the input field from the autocompleter object
(i.e. removes C<onblur> and C<onkeypress> handlers
that were previously set by the first invocation of the 
C<autocomplete> method).

=head2 displayMessage(messageText)

Displays the given message within 
a newly created dropdown DIV under the input field.
Used internally to warn for example about illegal values.


=head1 EVENTS

For a general explanation on registering handlers
for GvaScript events, see the L<event> documentation.
In short, you can register handlers either on the 
HTML input element, as in 

  <input name="someInput" onfocus = "myAutoCompleter.complete(this)"
                          onBind  = "bindHandler(this, event)"
                          onLeave = "leaveHandler">

or on the javascript object, as in 

   myAutocompleter.onBind = function(event) {
        bindHandler(event.target, event)
   };
   myAutocompleter.onLeave = leaveHandler;

Below is the list of events generated by
autocompleter objects.

=head2 onBind

This event is triggered whenever the autocompleter object 
becomes associated with an input field; typically this
occurs when the input field receives focus and then
calls the L</"autocomplete"> method.

=head2 onLeave

This event is triggered whenever the autocompleter object 
cuts the association with an input field; typically this
occurs when the input field loses focus.

If in strict mode, the autocompleter object will also
check if the final value is legal or illegal with respect
to the list of available choices. This may require an
additional Ajax call, so the C<onLeave> event may be
triggered I<before> the C<onLegalValue> or C<onIllegalValue> events.


=head2 onComplete

[OBSOLETE; use C<onLegalValue> or C<onIllegalValue> instead]

This event is triggered whenever the user has chosen
an item in the displayed suggestion list.
The event handler may use C<event.index> to know the index of the
selected choice.


=head2 onLegalValue

This event is triggered when the autocompleter is in strict mode,
the input field has just been left (C<onBlur> event), and the 
autocompleter was able to verify that the current input value
belongs to the list of available choices.

The event contains a C<value> property (current value in the 
input element), and a C<choice> property (member of the 
C<choices> array that matches the current value).
The C<controller> property is null because the event may
occur after the autocompleter object has been detached from the input
field and has been perhaps already bound to another field, so interacting 
with the autocompleter from the event handler would lead to inconsistencies.

=head2 onIllegalValue

This event is triggered when the autocompleter is in strict mode,
the input field has just been left (C<onBlur> event), and the 
autocompleter was not able to verify that the current input value
belongs to the list of available choices.

The event only contains a C<value> property (current value in the 
input element). The C<controller> property is null (same reasons
as C<onLegalValue> above).

return C<true> in onIllegalValue handler to override the illegal behavior; i.e. coloring the 
input in red and invalidating the dependentFields.

=head2 onHighlight

This event is triggered when a choice in the dropdown list
of choices is highlighted.
The event handler may use C<event.index> to know the index of the
highlighted choice.

=head2 onCancel

This event is triggered when the user presses the C<ESCAPE> key.

