1/** 2 * jQuery CoreUISelect 3 * Special thanks to Artem Terekhin, Yuriy Khabarov, Alexsey Shein 4 * 5 * @author Gennadiy Ukhanov 6 * @version 0.0.3 7 */ 8(function ($) { 9 10 var defaultOption = { 11 appendToBody : false, 12 jScrollPane : null, 13 onInit : null, 14 onFocus : null, 15 onBlur : null, 16 onOpen : null, 17 onClose : null, 18 onChange : null, 19 onDestroy : null 20 } 21 22 var allSelects = []; 23 24 $.browser.mobile = (/iphone|ipad|ipod|android/i.test(navigator.userAgent.toLowerCase())); 25 $.browser.operamini = Object.prototype.toString.call(window.operamini) === "[object OperaMini]"; 26 27 /** 28 * CoreUISelect - stylized standard select 29 * @constructor 30 */ 31 function CoreUISelect(__elem, __options, __templates) { 32 33 this.domSelect = __elem; 34 this.settings = __options || defaultOption; 35 this.isSelectShow = false; 36 this.isSelectFocus = false; 37 this.isJScrollPane = this.isJScrollPane(); 38 39 // templates 40 this.templates = __templates || 41 { 42 select : { 43 container : '<div class="b-core-ui-select"></div>', 44 value : '<span class="b-core-ui-select__value"></span>', 45 button : '<span class="b-core-ui-select__button"></span>' 46 }, 47 dropdown : { 48 container : '<div class="b-core-ui-select__dropdown"></div>', 49 wrapper : '<div class="b-core-ui-select__dropdown__wrap"></div>', 50 list : '<ul class="b-core-ui-select__dropdown__list"></ul>', 51 optionLabel : '<li class="b-core-ui-select__dropdown__label"></li>', 52 item : '<li class="b-core-ui-select__dropdown__item"></li>' 53 } 54 } 55 56 this.init(this.settings); 57 } 58 59 CoreUISelect.prototype.init = function() { 60 if($.browser.operamini) return this; 61 this.buildUI(); 62 this.hideDomSelect(); 63 if(this.domSelect.is(':disabled')) { 64 this.select.addClass('disabled'); 65 return this; 66 } 67 if(this.isJScrollPane) this.buildJScrollPane(); 68 this.bindUIEvents(); 69 this.settings.onInit && this.settings.onInit.apply(this, [this.domSelect, 'init']); 70 71 } 72 73 CoreUISelect.prototype.buildUI = function() { 74 75 // Build select container 76 this.select = $(this.templates.select.container) 77 .insertBefore(this.domSelect); 78 79 this.selectValue = $(this.templates.select.value) 80 .appendTo(this.select); 81 82 // TODO Add custom states for button 83 this.selectButton = $(this.templates.select.button) 84 .appendTo(this.select); 85 86 // Build dropdown container 87 this.dropdown = $(this.templates.dropdown.container); 88 this.dropdownWrapper = $(this.templates.dropdown.wrapper).appendTo(this.dropdown); 89 90 this.settings.appendToBody ? this.dropdown.appendTo($('body')) : this.dropdown.insertBefore(this.domSelect); 91 92 // Build dropdown 93 this.dropdownList = $(this.templates.dropdown.list).appendTo(this.dropdownWrapper); 94 this.domSelect.find('option').each($.proxy(this, 'addItems')); 95 96 97 // Build dropdown 98 this.dropdownItem = this.dropdown.find('.'+$(this.templates.dropdown.item).attr('class')); 99 100 // Add classes for dropdown 101 this.dropdownItem.filter(':first-child').addClass('first'); 102 this.dropdownItem.filter(':last-child').addClass('last'); 103 104 this.addOptionGroup(); 105 106 // Add placeholder value by selected option 107 this.setSelectValue(this.getSelectedItem().text()); 108 this.updateDropdownPosition(); 109 110 // Set current item form option:selected 111 this.currentItemOfDomSelect = this.currentItemOfDomSelect || this.domSelect.find('option:selected'); 112 113 } 114 115 CoreUISelect.prototype.hideDomSelect = function() { 116 117 this.domSelect.addClass('b-core-ui-select__select_state_hide'); 118 this.domSelect.css({ 119 'top' : this.select.position().top, 120 'left' : this.select.position().left 121 }); 122 } 123 124 CoreUISelect.prototype.showDomSelect = function() { 125 this.domSelect.css({ 126 'top' : 'auto', 127 'left' : 'auto' 128 }) 129 this.domSelect.removeClass('b-core-ui-select__select_state_hide') ; 130 } 131 132 CoreUISelect.prototype.bindUIEvents = function() { 133 // Bind plugin elements 134 this.domSelect.bind('focus', $.proxy(this, 'onFocus')); 135 this.domSelect.bind('blur', $.proxy(this, 'onBlur')); 136 this.domSelect.bind('change', $.proxy(this, 'onChange')); 137 138 if( $.browser.mobile) this.domSelect.bind('change', $.proxy(this, 'changeDropdownData')); 139 this.select.bind('click', $.proxy(this, 'onSelectClick')); 140 this.dropdownItem.bind('click', $.proxy(this, 'onDropdownItemClick')); 141 } 142 143 CoreUISelect.prototype.getCurrentIndexOfItem = function(__item) { 144 return this.domSelect.find('option').index($(this.domSelect).find('option:selected')); 145 } 146 147 CoreUISelect.prototype.scrollToCurrentDropdownItem = function(__item) { 148 if(this.dropdownWrapper.data('jsp')) { 149 this.dropdownWrapper.data('jsp').scrollToElement(__item); 150 return this; 151 } 152 // Alternative scroll to element 153 $(this.dropdownWrapper) 154 .scrollTop($(this.dropdownWrapper) 155 .scrollTop() + __item.position().top - $(this.dropdownWrapper).height()/2 + __item.height()/2); 156 } 157 158 CoreUISelect.prototype.buildJScrollPane = function() { 159 this.dropdownWrapper.wrap($('<div class="j-scroll-pane"></div>')); 160 } 161 162 CoreUISelect.prototype.isJScrollPane = function() { 163 if(this.settings.jScrollPane) { 164 if($.fn.jScrollPane) return true; 165 else throw new Error('jScrollPane no found'); 166 } 167 } 168 169 CoreUISelect.prototype.initJScrollPane = function () { 170 this.dropdownWrapper.jScrollPane(this.settings.jScrollPane); 171 } 172 173 CoreUISelect.prototype.showDropdown = function() { 174 this.domSelect.focus(); 175 this.settings.onOpen && this.settings.onOpen.apply(this, [this.domSelect, 'open']); 176 if($.browser.mobile) return this; 177 if(!this.isSelectShow) { 178 this.isSelectShow = true; 179 this.select.addClass('open'); 180 this.dropdown.addClass('show').removeClass('hide'); 181 if(this.isJScrollPane) this.initJScrollPane(); 182 this.scrollToCurrentDropdownItem(this.dropdownItem.eq(this.getCurrentIndexOfItem(
182))); 183 this.updateDropdownPosition(); 184 } 185 } 186 187 CoreUISelect.prototype.hideDropdown = function() { 188 if(this.isSelectShow) { 189 this.isSelectShow = false; 190 this.select.removeClass('open'); 191 this.dropdown.removeClass('show').addClass('hide'); 192 this.settings.onClose && this.settings.onClose.apply(this, [this.domSelect, 'close']); 193 } 194 if(this.isSelectFocus) this.domSelect.focus(); 195 } 196 197 CoreUISelect.prototype.hideAllDropdown = function() { 198 for(var i in allSelects) { 199 if(allSelects[i].hasOwnProperty(i)) { 200 allSelects.dropdown.isSelectShow = false; 201 allSelects.dropdown.domSelect.blur(); 202 allSelects.dropdown.addClass('hide').removeClass('show'); 203 } 204 } 205 } 206 207 CoreUISelect.prototype.changeDropdownData = function(event) { 208 if((this.isSelectShow || this.isSelectFocus)) { 209 this.currentItemOfDomSelect = this.domSelect.find('option:selected'); 210 this.dropdownItem.removeClass("selected");
211 this.dropdownItem.eq(this.getCurrentIndexOfItem()).addClass("selected"); 212 this.scrollToCurrentDropdownItem(this.dropdownItem.eq(this.getCurrentIndexOfItem())); 213 this.setSelectValue(this.currentItemOfDomSelect.text()); 214 215 } 216 if($.browser.mobile) this.settings.onChange && this.settings.onChange.apply(this, [this.domSelect, 'change']); 217 } 218 219 CoreUISelect.prototype.onDomSelectChange = function(_is) { 220 this.domSelect.bind('change', $.proxy(this, 'onChange')); 221 dispatchEvent(this.domSelect.get(0), 'change'); 222 if(!_is) this.settings.onChange && this.settings.onChange.apply(this, [this.domSelect, 'change']); 223 } 224 225 CoreUISelect.prototype.addListenerByServicesKey = function(event) { 226 if(this.isSelectShow) { 227 switch (event.which) { 228 case 9: // TAB 229 case 13: // ESQ 230 case 27: // ENTER 231 this.hideDropdown(); 232 break; 233 } 234 } 235 } 236 237 CoreUISelect.prototype.onSelectClick = function() { 238 if(!this.isSelectShow) this.showDropdown(); 239 else this.hideDropdown(); 240 return false; 241 } 242 243 CoreUISelect.prototype.onFocus = function () { 244 this.isDocumentMouseDown = false; 245 this.isSelectFocus = true; 246 this.select.addClass('focus'); 247 this.settings.onFocus && this.settings.onFocus.apply(this, [this.domSelect, 'focus']); 248 } 249 250 CoreUISelect.prototype.onBlur = function() { 251 if(!this.isDocumentMouseDown) { 252 this.isSelectFocus = false; 253 this.select.removeClass('focus'); 254 this.settings.onBlur && this.settings.onBlur.apply(this, [this.domSelect, 'blur']); 255 //this.hideDropdown(); 256 } 257 } 258 259 CoreUISelect.prototype.onChange = function () { 260 this.settings.onChange && this.settings.onChange.apply(this, [this.domSelect, 'change']); 261 } 262 263 CoreUISelect.prototype.onDropdownItemClick = function(event) { 264 var item = $(event.currentTarget); 265 if(!(item.hasClass('disabled') || item.hasClass('selected'))) { 266 this.domSelect.unbind('change', $.proxy(this, 'onChange')); 267 var index = this.dropdown.find('.'+$(this.templates.dropdown.item).attr('class')).index(item) 268 this.dropdownItem.removeClass('selected'); 269 this.dropdownItem.eq(index).addClass('selected'); 270 this.domSelect.find('option').removeAttr('selected'); 271 this.domSelect.find('option').eq(index).attr('selected', true); 272 this.setSelectValue(this.getSelectedItem().text()); 273 this.onDomSelectChange(true); 274 275 } 276 this.hideDropdown(); 277 return false; 278 } 279 280 CoreUISelect.prototype.onDocumentMouseDown = function(event) { 281 this.isDocumentMouseDown = true; 282 if($(event.target).closest(this.select).length == 0 && $(event.target).closest(this.dropdown).length== 0) { 283 if($(event.target).closest('option').length==0) { // Hack for Opera 284 this.isDocumentMouseDown = false; 285 this.hideDropdown(); 286 } 287 } 288 return false; 289 } 290 291 CoreUISelect.prototype.updateDropdownPosition = function() { 292 if(this.isSelectShow) { 293 if(this.settings.appendToBody) { 294 this.dropdown.css({ 295 'position' : 'absolute', 296 'top' : this.select.offset().top+this.select.outerHeight(true), 297 'left' : this.select.offset().left, 298 'z-index' : '9999' 299 }); 300 } else { 301 this.dropdown.css({ 302 'position' : 'absolute', 303 'top' : this.select.position().top+this.select.outerHeight(true), 304 'left' : this.select.position().left, 305 'z-index' : '9999' 306 }); 307 } 308 309 var marginDifferenceBySelect = this.select.outerWidth() - this.select.width(); 310 var marginDifferenceByDropdown = this.dropdown.outerWidth() - this.dropdown.width(); 311 312 this.dropdown.width(this.select.outerWidth(true)); 313 314 if(this.dropdown.width() == this.select.outerWidth()) { 315 this.dropdown.width((this.select.width()+marginDifferenceBySelect)-marginDifferenceByDropdown); 316 } 317 318 if(this.isJScrollPane) this.initJScrollPane(); 319 } 320 } 321 322 CoreUISelect.prototype.setSelectValue = function(_text) { 323 this.selectValue.text(_text); 324 } 325 326 CoreUISelect.prototype.isOptionGroup = function() { 327 return this.domSelect.find('optgroup').length>0; 328 } 329 330 CoreUISelect.prototype.addOptionGroup = function() { 331 var optionGroup = this.domSelect.find('optgroup'); 332 for(var i=0; i<optionGroup.length; i++) { 333 var index = this.domSelect.find("option").index($(optionGroup[i]).find('option:first-child')) 334 $(this.templates.dropdown.optionLabel) 335 .text($(optionGroup[i]).attr('label')) 336 .insertBefore(this.dropdownItem.eq(index)); 337 } 338 } 339 340 CoreUISelect.prototype.addItems = function(index, el) { 341 var el = $(el); 342 var item = $(this.templates.dropdown.item).text(el.text()); 343 if(el.attr("disabled")) item.addClass('disabled'); 344 if(el.attr("selected")) { 345 this.domSelect.find('option').removeAttr('selected'); 346 item.addClass('selected'); 347 el.attr('selected', 'selected'); 348 } 349 item.appendTo(this.dropdownList); 350 } 351 352 CoreUISelect.prototype.getSelectedItem = function() { 353 return this.dropdown.find('.selected').eq(0); 354 } 355 356 CoreUISelect.prototype.update = function() { 357 this.destroy(); 358 this.init(); 359 } 360 361 CoreUISelect.prototype.destroy = function() { 362 // Unbind plugin elements 363 this.domSelect.unbind('focus', $.proxy(this, 'onFocus')); 364 this.domSelect.unbind('blur', $.proxy(this, 'onBlur')); 365 this.domSelect.unbind('change', $.proxy(this, 'onChange')); 366 this.select.unbind('click', $.proxy(this, 'onSelectClick')); 367 this.dropdownItem.unbind('click', $.proxy(this, 'onDropdownItemClick')); 368 // Remove select container 369 this.select.remove(); 370 this.dropdown.remove(); 371 this.showDomSelect(); 372 this.settings.onDestroy && this.settings.onDestroy.apply(this, [this.domSelect, 'destroy']); 373 } 374 375 376 $.fn.coreUISelect = function(__options, __templates) { 377 return this.each(function () { 378 var select = $(this).data('coreUISelect'); 379 if(__options == 'destroy' && !select) return; 380 if(select){ 381 __options = (typeof __options == "string" && select[__options]) ? __options : 'update'; 382 select[__options].apply(select); 383 if(__options == 'destroy') { 384 $(this).removeData('coreUISelect'); 385 for(var i=0; i<allSelects.length; i++) { 386 if(allSelects[i] == select) { 387 allSelects.splice(i, 1); 388 break; 389 } 390 } 391 } 392 } else { 393 select = new CoreUISelect($(this), __options, __templates); 394 allSelects.push(select); 395 $(this).data('coreUISelect', select); 396 } 397 398 }); 399 }; 400 401 function dispatchEvent(obj, evt, doc) { 402 var doc = doc || document; 403 if(obj!==undefined || obj!==null) { 404 if (doc.createEvent) { 405 var evObj = doc.createEvent('MouseEvents'); 406 evObj.initEvent(evt, true, false); 407 obj.dispatchEvent(evObj); 408 } else if (doc.createEventObject) { 409 var evObj = doc.createEventObject(); 410 obj.fireEvent('on' + evt, evObj); 411 } 412 } 413 } 414 415 $(document).bind('mousedown', function(event){ 416 for(var i=0; i<allSelects.length; i++){ 417 allSelects[i].onDocumentMouseDown(event); 418 } 419 }); 420 421 $(document).bind('keyup', function(event){ 422 for(var i=0; i<allSelects.length; i++){ 423 if($.browser.safari || $.browser.msie || $.browser.opera) allSelects[i].changeDropdownData(event); // Hack for Safari 424 allSelects[i].addListenerByServicesKey(event); 425 } 426 }); 427 428 $(document).bind($.browser.safari ? 'keydown' : 'keypress', function(event){ 429 for(var i=0; i<allSelects.length; i++){ 430 allSelects[i].changeDropdownData(event); 431 } 432 }); 433 434 $(window).bind('resize', function(event){ 435 for(var i=0; i<allSelects.length; i++){ 436 allSelects[i].updateDropdownPosition(event); 437 } 438 }); 439 440 441 442})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.