1//Title: Custom DropDown plugin by PC 2//Documentation: http://designwithpc.com/Plugins/ddslick 3//Author: PC 4//Website: http://designwithpc.com 5//Twitter: http://twitter.com/chaudharyp 6 7(function ($) { 8 9 $.fn.ddslick = function (method) { 10 if (methods[method]) { 11 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 12 } else if (typeof method === 'object' || !method) { 13 return methods.init.apply(this, arguments); 14 } else { 15 $.error('Method ' + method + ' does not exists.'); 16 } 17 }; 18 19 var methods = {}, 20 21 //Set defauls for the control 22 defaults = { 23 data: [], 24 keepJSONItemsOnTop: false, 25 width: 260, 26 unfoldWidth:null, 27 textAlign:null, 28 height: null, 29 fixedHeight:true, 30 background: "#eee", 31 selectText: "", 32 defaultSelectedIndex: null, 33 truncateTitle: false, 34 truncateDescription: true, 35 imagePosition: "left", 36 roundImage:false, 37 showSelectedHTML: true, 38 clickOffToClose: true, 39 fireOnSelectedOnInit : true, 40 fireOnSelectedOnClick : true, 41 fireOnSelectedOnSelectMethod : true, 42 keyboardSearch:false, 43 keyboardTimeout:1000, 44 keyboardAutoSelect:false, 45 onSelected: function () { } 46 }, 47 48 ddSelectHtml = '<div class="dd-select"><input class="dd-selected-value" type="hidden" /><a class="dd-selected"></a><span class="dd-pointer dd-pointer-down"></span></div>', 49 ddOptionsHtml = '<ul class="dd-options"></ul>', 50 51 //CSS for ddSlick 52 ddslickCSS = '<style id="css-ddslick" type="text/css">' + 53 '.dd-select{ border-radius:2px; border:solid 1px #ccc; position:relative; cursor:pointer;}' + 54 '.dd-desc { color:#aaa; display:block; overflow: hidden; font-weight:normal; line-height: 1.4em; }' + 55 '.dd-selected{ overflow:hidden; display:block; padding:10px; padding-right:20px; font-weight:bold;}' + 56 '.dd-pointer{ width:0; height:0; position:absolute; right:10px; top:50%; margin-top:-3px;}' + 57 '.dd-pointer-down{ border:solid 5px transparent; border-top:solid 5px #000; }' + 58 '.dd-pointer-up{border:solid 5px transparent !important; border-bottom:solid 5px #000 !important; margin-top:-8px;}' + 59 '.dd-options{ border:solid 1px #ccc; border-top:none; list-style:none; box-shadow:0px 1px 5px #ddd; display:none; position:absolute; z-index:2000; margin:0; padding:0;background:#fff; overflow:auto;}' + 60 '.dd-option{ padding:10px; display:block; border-bottom:solid 1px #ddd; overflow:hidden; text-decoration:none; color:#333; cursor:pointer;-webkit-transition: all 0.25s ease-in-out; -moz-transition: all 0.25s ease-in-out;-o-transition: all 0.25s ease-in-out;-ms-transition: all 0.25s ease-in-out; }' + 61 '.dd-option-text { cursor:pointer; }' + 62 '.dd-options > li:last-child > .dd-option{ border-bottom:none;}' + 63 '.dd-option:hover{ background:#f3f3f3; color:#000;}' + 64 '.dd-selected-title-truncated { text-overflow: ellipsis; white-space:nowrap; display:inherit; overflow:hidden; }' + 65 '.dd-selected-description-truncated { text-overflow: ellipsis; white-space:nowrap; }' + 66 '.dd-option-selected { background:#f6f6f6; }' + 67 '.dd-option-image, .dd-selected-image { vertical-align:middle; float:left; margin-right:5px; max-width:64px; max-height:40px;}' + 68 '.dd-image-round { border-radius: 40px; -webkit-border-radius: 40px; -moz-border-radius: 40px; }' + 69 '.dd-image-right { float:right; margin-right:15px; margin-left:5px;}' + 70 '.dd-container { position:relative; }â ' + 71 '.dd-selected-text { font-weight:bold }â ' + 72 '</style>'; 73 74 //CSS styles are only added once. 75 if ($('#css-ddslick').length <= 0) { 76 $(ddslickCSS).appendTo('head'); 77 } 78 79 //Public methods 80 methods.init = function (options) { 81 //Preserve the original defaults by passing an empty object as the target 82 var options = $.extend({}, defaults, options); 83 84 //Apply on all selected elements 85 return this.each(function () { 86 var obj = $(this), 87 data = obj.data('ddslick'); 88 //If the plugin has not been initialized yet 89 if (!data) { 90 91 var ddSelect = [], ddJson = options.data; 92 93 //Get data from HTML select options 94 obj.find('option').each(function () { 95 var $this = $(this), thisData = $this.data(); 96 ddSelect.push({ 97 text: $.trim($this.text()), 98 value: $this.val(), 99 selected: $this.is(':selected'), 100 description: thisData.description, 101 imageSrc: thisData.imagesrc, //keep it lowercase for HTML5 data-attributes 102 customValue: thisData.customvalue //keep it lowercase for HTML5 data-attributes 103 }); 104 }); 105 106 //Update Plugin data merging both HTML select data and JSON data for the dropdown 107 if (options.keepJSONItemsOnTop) 108 $.merge(options.data, ddSelect); 109 else options.data = $.merge(ddSelect, options.data); 110 111 //Replace HTML select with empty placeholder, keep the original 112 // (tabindex is added to allow div focusing for keyboard searching)
113 var original = obj, placeholder = $('<div id="' + obj.attr('id') + '" '+ (options.keyboardSearch ? 'tabindex=0' : '') +'></div>'); 114 obj.replaceWith(placeholder); 115 obj = placeholder; 116 117 //Add classes and append ddSelectHtml & ddOptionsHtml to the container 118 obj.addClass('dd-container').append(ddSelectHtml).append(ddOptionsHtml); 119 120 //Get newly created ddOptions and ddSelect to manipulate 121 var ddSelect = obj.find('.dd-select'), 122 ddOptions = obj.find('.dd-options'); 123 124 //Set widths 125 ddOptions.css({ width: options.unfoldWidth === null ? options.width : options.unfoldWidth }); 126 ddSelect.css({ width: options.width, background: options.background }); 127 obj.css({ width: options.width }); 128 129 // Set text align into dropdown 130 if (options.textAlign != null) { 131 ddOptions.css({ "text-align": options.textAlign }); 132 ddSelect.css({ "text-align": options.textAlign }); 133 } 134 135 //Set height 136 if (options.height != null) { 137 if (options.fixedHeight == true) 138 ddOptions.css({ height: options.height, overflow: 'auto' }); 139 else 140 ddOptions.css({ 'max-height': options.height, overflow: 'auto' }); 141 } 142 143 //Add ddOptions to the container. Replace with template engine later. 144 $.each(options.data, function (index, item) { 145 if (item.selected) options.defaultSelectedIndex = index; 146 ddOptions.append('<li>' + 147 '<a class="dd-option">' + 148 (item.value ? ' <input class="dd-option-value" type="hidden" value="' + item.value + '" />' : '') + 149 (item.customValue ? ' <input class="dd-option-customvalue" type="hidden" value="' + item.customValue + '" />' : '') + 150 (item.imageSrc ? ' <img class="dd-option-image' + (options.imagePosition == "right" ? ' dd-image-right' : '') + (options.roundImage ? ' dd-image-round' : '') + '" src="' + item.imageSrc + '" />' : '') + 151 (item.text ? ' <label class="dd-option-text">' + item.text + '</label>' : '') + 152 (item.description ? ' <small class="dd-option-description dd-desc">' + item.description + '</small>' : '') + 153 '</a>' + 154 '</li>'); 155 }); 156 157 //Save plugin data. 158 var pluginData = { 159 settings: options, 160 original: original, 161 selectedIndex: -1, 162 selectedItem: null, 163 selectedData: null 164 } 165 obj.data('ddslick', pluginData); 166 167 //Check if needs to show the select text, otherwise show selected or default selection 168 if (options.selectText.length > 0 && options.defaultSelectedIndex == null) { 169 obj.find('.dd-selected').html(options.selectText); 170 } 171 else { 172 var index = (options.defaultSelectedIndex != null && options.defaultSelectedIndex >= 0 && options.defaultSelectedIndex < options.data.length) 173 ? options.defaultSelectedIndex 174 : 0; 175 selectIndex(obj, index, options.fireOnSelectedOnInit, "init"); 176 } 177 178 //EVENTS 179 //Displaying options 180 obj.find('.dd-select').on('click.ddslick', function () { 181 open(obj); 182 }); 183 184 //Selecting an option 185 obj.find('.dd-option').on('click.ddslick', function () { 186 selectIndex(obj, $(this).closest('li').index(), options.fireOnSelectedOnClick, "click"); 187 }); 188
189 //Click anywhere to close 190 if (options.clickOffToClose) { 191 ddOptions.addClass('dd-click-off-close'); 192 obj.on('click.ddslick', function (e) { e.stopPropagation(); }); 193 $('body').on('click', function () { 194 $('.dd-click-off-close').slideUp(50).siblings('.dd-select').find('.dd-pointer').removeClass('dd-pointer-up'); 195 }); 196 } 197 198 var searchedString = ""; 199 var lastDateKeyDown = new Date().getTime(); 200 var currentDisplayIndex = undefined; 201 202 // if keyboard searching is allowed => bind the keydown event to catch it 203 if (pluginData.settings.keyboardSearch) { 204 205 obj.keydown(function(event) { 206 207 // on ne gère pas la touche "Tab" 208 if (event.keyCode !== 9) { 209 var $this = $(this), 210 pluginData = $this.data('ddslick'), 211 wasOpen = $this.find('.dd-options').is(':visible'); 212 213 // open dropdown only if keyboardAutoSelect is false 214 if (!wasOpen && !pluginData.settings.keyboardAutoSelect) 215 open($this); 216 217 // set value to currentdisplayindex regarding dropdown mode 218 if (currentDisplayIndex === undefined || pluginData.settings.keyboardAutoSelect) 219 currentDisplayIndex = pluginData.selectedIndex; 220 221 // enter OR escape touch is pressed (13 : enter, 27 : esc) 222 if (event.keyCode === 13 || event.keyCode === 27) { 223 224 // on enter select curent display value ! 225 if (event.keyCode === 13 && currentDisplayIndex !== undefined) { 226 selectIndex($this, currentDisplayIndex, true, "keyboard", false); 227 } 228 else { 229 // on escape, highlight the old selected value 230 var selectedOption = $this.find('.dd-option').eq(pluginData.selectedIndex); 231 232 //Highlight selected option 233 $this.find('.dd-option').removeClass('dd-option-selected'); 234 selectedOption.addClass('dd-option-selected'); 235 } 236 237 // reset currentdisplayindex and close the dropdown 238 currentDisplayIndex = pluginData.selectedIndex; 239 close($this); 240 } 241 // if arrow pressed (38 : up, 40 : down) 242 else if (event.keyCode === 38 || event.keyCode === 40) { 243 244 // arrow up : check if we're again in table limits 245 if (event.keyCode === 38 && currentDisplayIndex >= 1) { 246 247 currentDisplayIndex = parseInt(currentDisplayIndex) - 1; 248 249 if (pluginData.settings.keyboardAutoSelect) 250 selectIndex($this, currentDisplayIndex, true, "keyboard", true); 251 } 252 253 // arrow down : check if we're again in table limits 254 if (event.keyCode === 40 && currentDisplayIndex < pluginData.settings.data.length - 1) { 255 256 currentDisplayIndex = parseInt(currentDisplayIndex) + 1; 257 258 if (pluginData.settings.keyboardAutoSelect) 259 selectIndex($this, currentDisplayIndex, true, "keyboard", true); 260 } 261 262 // if we don't have autoselect, highlight currentdisplayindex and autoscroll to display it 263 if (!pluginData.settings.keyboardAutoSelect) { 264 var ddOptions = $this.find('.dd-options'), 265 selectedOption = $this.find('.dd-option').eq(currentDisplayIndex), 266 selectedLiItem = selectedOption.closest('li'); 267 268 //Highlight selected option 269 $this.find('.dd-option').removeClass('dd-option-selected'); 270 selectedOption.addClass('dd-option-selected'); 271 272 // auto scroll to the selected item (place it on top of open ddOptions) 273 if (selectedLiItem !== undefined && selectedLiItem.offset() !== undefined && ddOptions !== undefined) { 274 ddOptions.scrollTop(selectedLiItem.offset().top + ddOptions.scrollTop() - ddOptions.offset().top); 275 } 276 } 277 } 278 // else it's another key so we search it ! 279 else { 280 currentDisplayIndex = pluginData.selectedIndex; 281 //Check if plugin is initialized 282 if (pluginData && pluginData.settings && pluginData.settings.data) { 283 if (pluginData.settings.data.length > 0) { 284 285 // get the current time 286 var newDateKeyDown = new Date().getTime(); 287 288 // check if difference between last input time and current time allow 289 // to continue the same searched string or starts a new one 290 if (newDateKeyDown - lastDateKeyDown > pluginData.settings.keyboardTimeout) { 291 searchedString = ""; 292 } 293 294 // adding pressed key to searched string 295 searchedString += String.fromCharCode(event.keyCode); 296 297 // memorize current time as last input time 298 lastDateKeyDown = newDateKeyDown; 299 300 // in the entire list, check text and description to find a match that starts with the searched string 301 // PS : compare in lowercase to be case insensitive 302 for (var i = 0, max = pluginData.settings.data.length; i < max; i++) { 303 304 if ((pluginData.settings.data[i].text !== undefined && pluginData.settings.data[i].text.toLowerCase().indexOf(searchedString.toLowerCase()) === 0) 305 || (pluginData.settings.data[i].description !== undefined && pluginData.settings.data[i].description.toLowerCase().indexOf(searchedString.toLowerCase()) === 0)) { 306 307 // if autoselect, then select the item ! 308 if (pluginData.settings.keyboardAutoSelect) 309 selectIndex($this, i, true, "keyboard", true); 310 else { 311 // else just highlight it and autoscroll to it 312 currentDisplayIndex = i; 313 314 var ddOptions = $this.find('.dd-options'), 315 selectedOption = $this.find('.dd-option').eq(i), 316 selectedLiItem = selectedOption.closest('li'); 317 318 //Highlight selected option 319 $this.find('.dd-option').removeClass('dd-option-selected'); 320 selectedOption.addClass('dd-option-selected'); 321 322 // auto scroll to the selected item (place it on top of open ddOptions) 323 if (selectedLiItem !== undefined && selectedLiItem.offset() !== undefined && ddOptions !== undefined) { 324 ddOptions.scrollTop(selectedLiItem.offset().top + ddOptions.scrollTop() - ddOptions.offset().top); 325 } 326 } 327 // then we break the for to increase performance 328 break; 329 } 330 } 331 } 332 } 333 } 334 335 // stop event propagation 336 event.preventDefault(); 337 event.stopPropagation(); 338 } 339 }); 340 } 341 } 342 }); 343 }; 344 345 //Public method to select an option by its index 346 methods.select = function (options) { 347 return this.each(function () { 348 if (options.index) 349 { 350 var $this = $(this), 351 pluginData = $this.data('ddslick'); 352 353 if (pluginData) { 354 var fireEvent = pluginData.settings.fireOnSelectedOnSelectMethod; 355 if (options.fireEvent !== undefined) 356 { 357 fireEvent = options.fireEvent; 358 } 359 selectIndex($(this), options.index, fireEvent, "select"); 360 } 361 } 362 }); 363 } 364 365 //Public method to open drop down 366 methods.open = function () { 367 return this.each(function () { 368 var $this = $(this), 369 pluginData = $this.data('ddslick'); 370 371 //Check if plugin is initialized 372 if (pluginData) { 373 open($this); 374 } 375 }); 376 }; 377 378 //Public method to close drop down 379 methods.close = function () { 380 return this.each(function () { 381 var $this = $(this), 382 pluginData = $this.data('ddslick'); 383 384 //Check if plugin is initialized 385 if (pluginData) { 386 close($this); 387 } 388 }); 389 }; 390 391 //Public method to destroy. Unbind all events and restore the original Html select/options 392 methods.destroy = function () { 393 return this.each(function () { 394 var $this = $(this), 395 pluginData = $this.data('ddslick'); 396 397 //Check if already destroyed 398 if (pluginData) { 399 var originalElement = pluginData.original; 400 $this.removeData('ddslick').unbind('.ddslick').replaceWith(originalElement); 401 } 402 }); 403 } 404 405 //Public method to set dropdown value if it exists in data 406 methods.setValue = function (options) { 407 return this.each(function () { 408 409 var $this = $(this), 410 pluginData = $this.data('ddslick'); 411 412 if (pluginData) { 413 for (var i = 0, max = pluginData.settings.data.length - 1 ; i <= max; i++) { 414 if (pluginData.settings.data[i].value == options.value) { 415 var fireEvent = pluginData.settings.fireOnSelectedOnSelectMethod; 416 if (options.fireEvent !== undefined) 417 { 418 fireEvent = options.fireEvent; 419 } 420 selectIndex($(this), i, fireEvent, "select"); 421 break; 422 } 423 } 424 } 425 }); 426 } 427
428 //Private: Select index 429 function selectIndex(obj, index, needToFireOnSelectedEvent, eventDescription, closeOptions) { 430 431 //Get plugin data 432 var pluginData = obj.data('ddslick'); 433 434 //Get required elements 435 var ddSelected = obj.find('.dd-selected'), 436 ddSelectedValue = ddSelected.siblings('.dd-selected-value'), 437 ddOptions = obj.find('.dd-options'), 438 ddPointer = ddSelected.siblings('.dd-pointer'), 439 selectedOption = obj.find('.dd-option').eq(index), 440 selectedLiItem = selectedOption.closest('li'), 441 settings = pluginData.settings, 442 selectedData = pluginData.settings.data[index]; 443 444 //Highlight selected option 445 obj.find('.dd-option').removeClass('dd-option-selected'); 446 selectedOption.addClass('dd-option-selected'); 447 448 //Update or Set plugin data with new selection 449 pluginData.selectedIndex = index; 450 pluginData.selectedItem = selectedLiItem; 451 pluginData.selectedData = selectedData; 452 453 // auto scroll to the selected item (place it on top of open ddOptions) 454 if (selectedLiItem !== undefined && selectedLiItem.offset() !== undefined && ddOptions !== undefined) { 455 ddOptions.scrollTop(selectedLiItem.offset().top + ddOptions.scrollTop() - ddOptions.offset().top); 456 } 457 458 //If set to display to full html, add html 459 if (settings.showSelectedHTML) { 460 ddSelected.html( 461 (selectedData.imageSrc ? '<img class="dd-selected-image' + (settings.imagePosition == "right" ? ' dd-image-right' : '') + (settings.roundImage ? ' dd-image-round' : '') + '" src="' + selectedData.imageSrc + '" />' : '') + 462 (selectedData.text ? '<label class="dd-selected-text' + (settings.truncateTitle ? ' dd-selected-title-truncated' : '') + '">' + selectedData.text + '</label>' : '') + 463 (selectedData.description ? '<small class="dd-selected-description dd-desc' + (settings.truncateDescription ? ' dd-selected-description-truncated' : '') + '" >' + selectedData.description + '</small>' : '') 464 ); 465 466 } 467 //Else only display text as selection 468 else ddSelected.html(selectedData.text); 469 470 //Updating selected option value 471 ddSelectedValue.val(selectedData.value); 472 473 //BONUS! Update the original element attribute with the new selection 474 pluginData.original.val(selectedData.value); 475 obj.data('ddslick', pluginData); 476 477 //Close options on selection 478 if (closeOptions === undefined || closeOptions) { 479 close(obj); 480 } 481 482 //Adjust appearence for selected option 483 adjustSelectedHeight(obj); 484 485 if (needToFireOnSelectedEvent) 486 { 487 //Callback function on selection 488 if (typeof settings.onSelected == 'function') { 489 settings.onSelected.call(this, pluginData, eventDescription); 490 } 491 } 492 } 493 494 //Private: Open the drop down options 495 function open(obj) { 496 var $this = obj.find('.dd-select'), 497 ddOptions = $this.siblings('.dd-options'), 498 ddPointer = $this.find('.dd-pointer'), 499 wasOpen = ddOptions.is(':visible'), 500 selectedItem = obj.find('.dd-option-selected').closest('li'); 501 502 //Close all open options (multiple plugins) on the page 503 $('.dd-click-off-close').not(ddOptions).slideUp(50); 504 $('.dd-pointer').removeClass('dd-pointer-up'); 505 506 if (wasOpen) { 507 ddOptions.slideUp('fast'); 508 ddPointer.removeClass('dd-pointer-up'); 509 } 510 else { 511 ddOptions.slideDown('fast'); 512 ddPointer.addClass('dd-pointer-up'); 513 } 514 515 // auto scroll to the selected item (place it on top of open ddOptions) 516 if (selectedItem !== undefined && selectedItem.offset() !== undefined && ddOptions !== undefined) { 517 ddOptions.scrollTop(selectedItem.offset().top + ddOptions.scrollTop() - ddOptions.offset().top); 518 } 519 520 //Fix text height (i.e. display title in center), if there is no description 521 adjustOptionsHeight(obj); 522 } 523
524 //Private: Close the drop down options 525 function close(obj) { 526 //Close drop down and adjust pointer direction 527 obj.find('.dd-options').slideUp(50); 528 obj.find('.dd-pointer').removeClass('dd-pointer-up').removeClass('dd-pointer-up'); 529 } 530 531 //Private: Adjust appearence for selected option (move title to middle), when no desripction 532 function adjustSelectedHeight(obj) { 533 534 //Get height of dd-selected 535 var lSHeight = obj.find('.dd-select').css('height'); 536 537 //Check if there is selected description 538 var descriptionSelected = obj.find('.dd-selected-description'); 539 var imgSelected = obj.find('.dd-selected-image'); 540 if (descriptionSelected.length <= 0 && imgSelected.length > 0) { 541 obj.find('.dd-selected-text').css('lineHeight', lSHeight); 542 } 543 } 544 545 //Private: Adjust appearence for drop down options (move title to middle), when no desripction 546 function adjustOptionsHeight(obj) { 547 obj.find('.dd-option').each(function () { 548 var $this = $(this); 549 var lOHeight = $this.css('height'); 550 var descriptionOption = $this.find('.dd-option-description'); 551 var imgOption = obj.find('.dd-option-image'); 552 if (descriptionOption.length <= 0 && imgOption.length > 0) { 553 $this.find('.dd-option-text').css('lineHeight', lOHeight); 554 } 555 }); 556 } 557 558})(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.