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);