      1 (function ($, rf) {
      2
      3     rf.ui = rf.ui || {};
      4
      5     rf.ui.Select = function(id, options) {
      6         this.id = id;
      7         this.element = this.attachToDom();
      8         var mergedOptions = $.extend({}, defaultOptions, options);
      9         mergedOptions['attachTo'] = id;
     10         mergedOptions['scrollContainer'] = $(document.getElementById(id + "Items")).parent()[0];
     11         mergedOptions['focusKeeperEnabled'] = false;
     12         $super.constructor.call(this, id, mergedOptions);
     13
     14         this.options = mergedOptions;
     15         this.defaultLabel = mergedOptions.defaultLabel;
     16         var inputLabel = this.__getValue();
     17         this.initialValue = (inputLabel != this.defaultLabel) ? inputLabel : "";
     18         this.selValueInput = $(document.getElementById(id + "selValue"));
     19         this.container = this.selValueInput.parent();
     20         this.clientSelectItems = mergedOptions.clientSelectItems;
     21         this.filterFunction = mergedOptions.filterFunction;
     22
     23
     24         if (mergedOptions.showControl && !mergedOptions.disabled) {
     25             this.container.bind("mousedown", $.proxy(this.__onBtnMouseDown, this))
     26                 .bind("mouseup", $.proxy(this.__onMouseUp, this));
     27         }
     28
     29         this.isFirstAjax = true;
     30         this.previousValue = this.__getValue();
     31         this.selectFirst = mergedOptions.selectFirst;
     32         this.popupList = new rf.ui.PopupList((id + "List"), this, mergedOptions);
     33         this.list = this.popupList.__getList();
     34         this.listElem = $(document.getElementById(id + "List"));
     35
     36         this.listElem.bind("mousedown", $.proxy(this.__onListMouseDown, this));
     37         this.listElem.bind("mouseup", $.proxy(this.__onMouseUp, this));
     38
     39         var listEventHandlers = {};
     40         listEventHandlers["listshow" + this.namespace] = $.proxy(this.__listshowHandler, this);
     41         listEventHandlers["listhide" + this.namespace] = $.proxy(this.__listhideHandler, this);
     42         listEventHandlers["change" + this.namespace] = $.proxy(this.__onInputChangeHandler, this);
     43         rf.Event.bind(this.element, listEventHandlers, this);
     44
     45         this.originalItems = this.list.__getItems();  // initialize here for non-autocomplete use cases
     46         this.enableManualInput = mergedOptions.enableManualInput || mergedOptions.isAutocomplete;
     47
     48         if (this.enableManualInput) {
     49             updateItemsList.call(this, "", this.clientSelectItems);
     50         }
     51         this.changeDelay = mergedOptions.changeDelay;
     52     };
     53
     54     rf.ui.InputBase.extend(rf.ui.Select);
     55     var $super = rf.ui.Select.$super;
     56
     57     var defaultOptions = {
     58         defaultLabel: "",
     59         selectFirst: true,
     60         showControl: true,
     61         enableManualInput: false,
     62         itemCss: "rf-sel-opt",
     63         selectItemCss: "rf-sel-sel",
     64         listCss: "rf-sel-lst-cord",
     65         changeDelay: 8,
     66         disabled: false,
     67         filterFunction : undefined,
     68         isAutocomplete: false,
     69         ajaxMode:true,
     70         lazyClientMode:false,
     71         isCachedAjax:true
     72     };
     73
     74     var REGEXP_TRIM = /^[\n\s]*(.*)[\n\s]*$/;
     75
     76     var updateItemsList = function (value, clientSelectItems) {
     77         if (!clientSelectItems) {
     78             clientSelectItems = [];
     79         }
     80         if (clientSelectItems.length || (!this.options.isAutocomplete && !this.options.isCachedAjax)) {
     81             // do no empty if autocomplete/caching is on
     82             this.clientSelectItems = clientSelectItems;
     83         }
     84
     85         this.originalItems = this.list.__updateItemsList();
     86         this.list.__storeClientSelectItems(clientSelectItems);
     87         if (this.originalItems.length > 0) {
     88             this.cache = new rf.utils.Cache((this.options.ajaxMode ? value : ""), this.originalItems, getData, !this.options.ajaxMode);
     89         }
     90
     91     };
     92
     93     var getData = function (nodeList) {
     94         var data = [];
     95         nodeList.each(function () {
     96             data.push($(this).text().replace(REGEXP_TRIM, "$1"));
     97         });
     98         return data;
     99     }
    100
    101     $.extend(rf.ui.Select.prototype, ( function () {
    102         return{
    103             name : "select",
    104             defaultLabelClass : "rf-sel-dflt-lbl",
    105
    106             __listshowHandler: function(e) {
    107                 if (this.originalItems.length == 0 && this.isFirstAjax) {
    108                     this.callAjax(e);
    109                 }
    110             },
    111
    112             __listhideHandler: function(e) {
    113             },
    114
    115             __onInputChangeHandler: function(e) {
    116                 this.__setValue(this.input.val());
    117             },
    118
    119             __onBtnMouseDown: function(e) {
    120                 if (!this.popupList.isVisible() && !this.options.isAutocomplete) {
    121                     this.__updateItems();
    122                     this.__showPopup();
    123                 } else {
    124                     this.__hidePopup();
    125                 }
    126                 this.isMouseDown = true;
    127             },
    128
    129             __focusHandler: function(e) {
    130                 if (!this.focused) {
    131                     if (this.__getValue() == this.defaultLabel) {
    132                         this.__setValue("");
    133                     }
    134                     this.focusValue = this.selValueInput.val();
    135                     this.focused = true;
    136                     this.invokeEvent.call(this, "focus", document.getElementById(this.id), e);
    137                 }
    138             },
    139
    140             __keydownHandler: function(e) {
    141                 var code;
    142
    143                 if (e.keyCode) {
    144                     code = e.keyCode;
    145                 } else if (e.which) {
    146                     code = e.which;
    147                 }
    148
    149                 var visible = this.popupList.isVisible();
    150
    151                 switch (code) {
    152                     case rf.KEYS.DOWN:
    153                         e.preventDefault();
    154                         if (!visible) {
    155                             this.__updateItems();
    156                             this.__showPopup();
    157                         } else {
    158                             this.list.__selectNext();
    159                         }
    160                         break;
    161
    162                     case rf.KEYS.UP:
    163                         e.preventDefault();
    164                         if (visible) {
    165                             this.list.__selectPrev();
    166                         }
    167                         break;
    168
    169                     case rf.KEYS.RETURN:
    170                         e.preventDefault();
    171                         if (visible) {
    172                             this.list.__selectCurrent();
    173                         }
    174                         return false;
    175                         break;
    176
    177                     case rf.KEYS.TAB:
    178                         break;
    179
    180                     case rf.KEYS.ESC:
    181                         e.preventDefault();
    182                         if (visible) {
    183                             this.__hidePopup();
    184                         }
    185                         break;
    186
    187                     default:
    188                         var _this = this;
    189                         window.clearTimeout(this.changeTimerId);
    190                         this.changeTimerId = window.setTimeout(function() {
    191                             _this.__onChangeValue(e);
    192                         }, this.changeDelay);
    193                         break;
    194                 }
    195             },
    196
    197             __onChangeValue: function(e) {
    198                 var newValue = this.__getValue();
    199                 if (newValue === this.previousValue) {
    200                     return;
    201                 }
    202                 this.previousValue = newValue;
    203                 if (!this.options.isAutocomplete ||
    204                     (this.options.isCachedAjax || !this.options.ajaxMode) && this.cache && this.cache.isCached(newValue)) {
    205                     this.__updateItemsFromCache(newValue, true);
    206                     this.originalItems = this.list.__getItems();
    207
    208                     if (this.list.__getItems().length != 0) {
    209                         this.container.removeClass("rf-sel-fld-err");
    210                     } else {
    211                         this.container.addClass("rf-sel-fld-err");
    212                     }
    213
    214                     if (!this.popupList.isVisible()) {
    215                         this.__showPopup();
    216                     }
    217                 } else {
    218                     if (newValue.length >= this.options.minChars) {
    219                         if ((this.options.ajaxMode || this.options.lazyClientMode)) {
    220                             this.callAjax(e);
    221                         }
    222                     } else {
    223                         if (this.options.ajaxMode) {
    224                             this.clearItems();
    225                             this.__hidePopup();
    226                         }
    227                     }
    228                 }
    229             },
    230
    231             clearItems: function() {
    232                 this.list.removeAllItems();
    233             },
    234
    235             callAjax: function(event) {
    236                 var _this = this;
    237                 var _event = event;
    238                 var ajaxSuccess = function (event) {
    239                     updateItemsList.call(_this, _this.__getValue(), event.componentData && event.componentData[_this.id]);
    240
    241                     if (_this.clientSelectItems.length != 0) {
    242                         _this.__updateItems();
    243                         _this.__showPopup();
    244                     } else {
    245                         _this.__hidePopup();
    246                     }
    247                 };
    248
    249                 var ajaxError = function (event) {
    250                     _this.__hidePopup();
    251                     _this.clearItems();
    252                 };
    253
    254                 this.isFirstAjax = false;
    255                 //caution: JSF submits inputs with empty names causing "WARNING: Parameters: Invalid chunk ignored." in Tomcat log
    256                 var params = {};
    257                 params[this.id + ".ajax"] = "1";
    258                 rf.ajax(this.id, event, {parameters: params, error: ajaxError, complete:ajaxSuccess});
    259
    260             },
    261
    262             __blurHandler: function(e) {
    263                 if (!this.isMouseDown) {
    264                     var that = this;
    265                     this.timeoutId = window.setTimeout(function() {
    266                         if (that.input !== null) {
    267                             that.onblur(e);
    268                         }
    269                     }, 200);
    270                 } else {
    271                     this.__setInputFocus();
    272                     this.isMouseDown = false;
    273                 }
    274             },
    275
    276             __onListMouseDown: function(e) {
    277                 this.isMouseDown = true;
    278             },
    279
    280             __onMouseUp: function(e) {
    281                 this.isMouseDown = false;
    282                 this.__setInputFocus();
    283             },
    284
    285             __updateItems: function() {
    286                 var newValue = this.__getValue();
    287                 newValue = (newValue != this.defaultLabel) ? newValue : "";
    288                 this.__updateItemsFromCache(newValue);
    289
    290                 if (this.selectFirst) {
    291                     this.list.__selectByIndex(0);
    292                 }
    293             },
    294
    295             __updateItemsFromCache: function(value, isAutocomplete) {
    296                 if (this.originalItems.length > 0 && this.enableManualInput || isAutocomplete) {
    297                     var newItems = this.cache.getItems(value, this.filterFunction);
    298                     var items = $(newItems);
    299                     this.list.__unselectPrevious();
    300                     this.list.__setItems(items);
    301                     $(document.getElementById(this.id + "Items")).empty().append(items);
    302                 }
    303             },
    304
    305             __getClientItemFromCache: function(inputLabel) {
    306                 var value;
    307                 var label;
    308                 if (this.enableManualInput) {
    309                     var items = this.cache.getItems(inputLabel, this.filterFunction);
    310                     if (items && items.length > 0) {
    311                         var first = $(items[0]);
    312                         $.each(this.clientSelectItems, function() {
    313                             if (this.id == first.attr("id")) {
    314                                 label = this.label;
    315                                 value = this.value;
    316                                 return false;
    317                             }
    318                         });
    319                     } else {
    320                         label = inputLabel;
    321                         value = "";
    322                     }
    323                 }
    324
    325                 if (label) {
    326                     return {'label': label, 'value': value};
    327                 }
    328             },
    329
    330             __getClientItem: function(inputLabel) {
    331                 var value;
    332                 var label = inputLabel;
    333                 $.each(this.clientSelectItems, function() {
    334                     if (label == this.label) {
    335                         value = this.value;
    336                     }
    337                 });
    338
    339                 if (label && value) {
    340                     return {'label': label, 'value': value};
    341                 }
    342             },
    343
    344             __showPopup: function() {
    345                 if (this.originalItems.length > 0) {
    346                     this.popupList.show();
    347                 }
    348                 this.invokeEvent.call(this, "listshow", document.getElementById(this.id));
    349             },
    350
    351             __hidePopup: function() {
    352                 this.popupList.hide();
    353                 this.invokeEvent.call(this, "listhide", document.getElementById(this.id));
    354             },
    355
    356             showPopup: function() {
    357                 if (!this.popupList.isVisible()) {
    358                     this.__updateItems();
    359                     this.__showPopup();
    360                 }
    361                 this.__setInputFocus();
    362                 if (!this.focused) {
    363                     if (this.__getValue() == this.defaultLabel) {
    364                         this.__setValue("");
    365                     }
    366                     this.focusValue = this.selValueInput.val();
    367                     this.focused = true;
    368                     this.invokeEvent.call(this, "focus", document.getElementById(this.id));
    369                 }
    370             },
    371
    372             hidePopup: function() {
    373                 if (this.popupList.isVisible()) {
    374                     this.__hidePopup();
    375                     var inputLabel = this.__getValue();
    376
    377                     if (!inputLabel || inputLabel == "") {
    378                         this.__setValue(this.defaultLabel);
    379                         this.selValueInput.val("");
    380                     }
    381
    382                     this.focused = false;
    383                     this.invokeEvent.call(this, "blur", document.getElementById(this.id));
    384                     if (this.focusValue != this.selValueInput.val()) {
    385                         this.invokeEvent.call(this, "change", document.getElementById(this.id));
    386                     }
    387                 }
    388             },
    389
    390             processItem: function(item) {
    391                 var key = $(item).attr("id");
    392                 var label;
    393                 $.each(this.clientSelectItems, function() {
    394                     if (this.id == key) {
    395                         label = this.label;
    396                         return false;
    397                     }
    398                 });
    399                 this.__setValue(label);
    400                 this.__hidePopup();
    401                 this.__setInputFocus();
    402                 this.__save();
    403
    404                 this.invokeEvent.call(this, "selectitem", document.getElementById(this.id));
    405             },
    406
    407             __save: function() {
    408                 var value = "";
    409                 var label = "";
    410                 var inputLabel = this.__getValue();
    411                 var clientSelectItem;
    412
    413                 if (inputLabel && inputLabel != "") {
    414                     if (this.enableManualInput) {
    415                         clientSelectItem = this.__getClientItemFromCache(inputLabel);
    416                     } else {
    417                         clientSelectItem = this.__getClientItem(inputLabel);
    418                     }
    419
    420                     if (clientSelectItem) {
    421                         label = clientSelectItem.label;
    422                         value = clientSelectItem.value;
    423                     }
    424                 }
    425
    426                 this.__setValue(label);
    427                 this.selValueInput.val(value);
    428             },
    429
    430             onblur: function(e) {
    431                 this.__hidePopup();
    432                 var inputLabel = this.__getValue();
    433
    434                 if (!inputLabel || inputLabel == "") {
    435                     this.__setValue(this.defaultLabel);
    436                     this.selValueInput.val("");
    437                 }
    438
    439                 this.focused = false;
    440                 this.invokeEvent.call(this, "blur", document.getElementById(this.id), e);
    441                 if (this.focusValue != this.selValueInput.val()) {
    442                     this.invokeEvent.call(this, "change", document.getElementById(this.id), e);
    443                 }
    444             },
    445
    446             getValue: function() {
    447                 return this.selValueInput.val();
    448             },
    449
    450             setValue: function(value) {
    451                 if (value == null || value == '') {
    452                     this.__setValue('');
    453                     this.__save();
    454                     this.__updateItems();
    455                     return;
    456                 }
    457                 var item;
    458                 for (var i = 0; i < this.clientSelectItems.length; i++) {
    459                     item = this.clientSelectItems[i];
    460                     if (item.value == value) {
    461                         this.__setValue(item.label);
    462                         this.__save();
    463                         this.list.__selectByIndex(i);
    464                         return;
    465                     }
    466                 }
    467             },
    468
    469             getLabel: function() {
    470                 return this.__getValue();
    471             },
    472
    473             destroy: function() {
    474                 this.popupList.destroy();
    475                 this.popupList = null;
    476                 $super.destroy.call(this);
    477             }
    478         }
    479     })());
    480
    481     // client-side validation
    482     rf.csv = rf.csv || {};
    483     rf.csv.validateSelectLabelValue = function (input, id, params, msg) {
    484         var value = $(document.getElementById(id + 'selValue')).val();
    485         var label = $(document.getElementById(id + 'Input')).val();
    486
    487         var defaultLabel = RichFaces.component(id).defaultLabel;
    488
    489         if (!value && label && (label != defaultLabel)) {
    490             throw rf.csv.getMessage(null, 'UISELECTONE_INVALID', [id, ""]);
    491         }
    492     };
    493
    494 })(RichFaces.jQuery, window.RichFaces);