1/*! 2 * jQuery Selectbox plugin 0.2 3 * 4 * Copyright 2011-2012, Dimitar Ivanov (http://www.bulgaria-web-developers.com/projects/javascript/selectbox/) 5 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. 6 * 7 * Date: Tue Jul 17 19:58:36 2012 +0300 8 */ 9(function ($, undefined) { 10 var PROP_NAME = 'selectbox', 11 FALSE = false, 12 TRUE = true; 13 /** 14 * Selectbox manager. 15 * Use the singleton instance of this class, $.selectbox, to interact with the select box. 16 * Settings for (groups of) select boxes are maintained in an instance object, 17 * allowing multiple different settings on the same page 18 */ 19 function Selectbox() { 20 this._state = []; 21 this._defaults = { // Global defaults for all the select box instances 22 classHolder: "sbHolder", 23 classHolderDisabled: "sbHolderDisabled", 24 classSelector: "sbSelector", 25 classOptions: "sbOptions", 26 classGroup: "sbGroup", 27 classSub: "sbSub", 28 classDisabled: "sbDisabled", 29 classToggleOpen: "sbToggleOpen", 30 classToggle: "sbToggle", 31 classFocus: "sbFocus", 32 speed: 200, 33 effect: "slide", // "slide" or "fade" 34 onChange: null, //Define a callback function when the selectbox is changed 35 onOpen: null, //Define a callback function when the selectbox is open 36 onClose: null //Define a callback function when the selectbox is closed 37 }; 38 } 39 40 $.extend(Selectbox.prototype, { 41 /** 42 * Is the first field in a jQuery collection open as a selectbox 43 * 44 * @param {Object} target 45 * @return {Boolean} 46 */ 47 _isOpenSelectbox: function (target) { 48 if (!target) { 49 return FALSE; 50 } 51 var inst = this._getInst(target); 52 return inst.isOpen; 53 }, 54 /** 55 * Is the first field in a jQuery collection disabled as a selectbox 56 * 57 * @param {HTMLElement} target 58 * @return {Boolean} 59 */ 60 _isDisabledSelectbox: function (target) { 61 if (!target) { 62 return FALSE; 63 } 64 var inst = this._getInst(target); 65 return inst.isDisabled; 66 }, 67 /** 68 * Attach the select box to a jQuery selection. 69 * 70 * @param {HTMLElement} target 71 * @param {Object} settings 72 */ 73 _attachSelectbox: function (target, settings) { 74 if (this._getInst(target)) { 75 return FALSE; 76 } 77 var $target = $(target), 78 self = this, 79 inst = self._newInst($target), 80 sbHolder, sbSelector, sbToggle, sbOptions, 81 s = FALSE, optGroup = $target.find("optgroup"), opts = $target.find("option"), olen = opts.length; 82 83 $target.attr("sb", inst.uid); 84 85 $.extend(inst.settings, self._defaults, settings); 86 self._state[inst.uid] = FALSE; 87 $target.hide(); 88 89 function closeOthers() { 90 var key, sel, 91 uid = this.attr("id").split("_")[1]; 92 for (key in self._state) { 93 if (key !== uid) { 94 if (self._state.hasOwnProperty(key)) { 95 sel = $("select[sb='" + key + "']")[0]; 96 if (sel) { 97 self._closeSelectbox(sel); 98 } 99 } 100 } 101 } 102 } 103 104 sbHolder = $("<div>", { 105 "id": "sbHolder_" + inst.uid, 106 "class": inst.settings.classHolder, 107 "tabindex": $target.attr("tabindex") 108 }); 109 110 sbSelector = $("<a>", { 111 "id": "sbSelector_" + inst.uid, 112 "href": "#", 113 "class": inst.settings.classSelector, 114 "click": function (e) { 115 e.preventDefault(); 116 closeOthers.apply($(this), []); 117 var uid = $(this).attr("id").split("_")[1]; 118 if (self._state[uid]) { 119 self._closeSelectbox(target); 120 } else { 121 self._openSelectbox(target); 122 } 123 } 124 }); 125 126 sbToggle = $("<a>", { 127 "id": "sbToggle_" + inst.uid, 128 "href": "#", 129 "class": inst.settings.classToggle, 130 "click": function (e) { 131 e.preventDefault(); 132 closeOthers.apply($(this), []); 133 var uid = $(this).attr("id").split("_")[1]; 134 if (self._state[uid]) { 135 self._closeSelectbox(target); 136 } else { 137 self._openSelectbox(target); 138 } 139 } 140 }); 141 sbToggle.appendTo(sbHolder); 142 143 sbOptions = $("<ul>", { 144 "id": "sbOptions_" + inst.uid, 145 "class": inst.settings.classOptions, 146 "css": { 147 "display": "none" 148 } 149 }); 150 151 $target.children().each(function(i) { 152 var that = $(this), li, config = {}; 153 if (that.is("option")) { 154 getOptions(that); 155 } else if (that.is("optgroup")) { 156 li = $("<li>"); 157 $("<span>", { 158 "text": that.attr("label") 159 }
159).addClass(inst.settings.classGroup).appendTo(li); 160 li.appendTo(sbOptions); 161 if (that.is(":disabled")) { 162 config.disabled = true; 163 } 164 config.sub = true; 165 getOptions(that.find("option"), config); 166 } 167 }); 168 169 function getOptions () { 170 var sub = arguments[1] && arguments[1].sub ? true : false, 171 disabled = arguments[1] && arguments[1].disabled ? true : false; 172 arguments[0].each(function (i) { 173 var that = $(this), 174 li = $("<li>", { 175 "class": that.attr("class") 176 }), 177 child; 178 if (that.is(":selected")) { 179 sbSelector.text(that.text()); 180 s = TRUE; 181 } 182 if (i === olen - 1) { 183 li.addClass("last"); 184 } 185 if (!that.is(":disabled") && !disabled) { 186 child = $("<a>", { 187 "href": "#" + that.val(), 188 "rel": that.val(), 189 "class": that.val() 190 }).text(that.text()).bind("click.sb", function (e) { 191 if (e && e.preventDefault) { 192 e.preventDefault(); 193 } 194 var t = sbToggle, 195 $this = $(this), 196 uid = t.attr("id").split("_")[1]; 197 self._changeSelectbox(target, $this.attr("rel"), $this.text()); 198 self._closeSelectbox(target); 199 }).bind("mouseover.sb", function () { 200 var $this = $(this); 201 $this.parent().siblings().find("a").removeClass(inst.settings.classFocus); 202 $this.addClass(inst.settings.classFocus); 203 }).bind("mouseout.sb", function () { 204 $(this).removeClass(inst.settings.classFocus); 205 }); 206 if (sub) { 207 child.addClass(inst.settings.classSub); 208 } 209 if (that.is(":selected")) { 210 child.addClass(inst.settings.classFocus); 211 } 212 child.appendTo(li); 213 } else { 214 child = $("<span>", { 215 "text": that.text() 216 }).addClass(inst.settings.classDisabled); 217 if (sub) { 218 child.addClass(inst.settings.classSub); 219 } 220 child.appendTo(li); 221 } 222 li.appendTo(sbOptions); 223 }); 224 } 225 226 if (!s) { 227 sbSelector.text(opts.first().text()); 228 } 229 230 $.data(target, PROP_NAME, inst); 231 232 sbHolder.data("uid", inst.uid).bind("keydown.sb", function (e) { 233 var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0, 234 $this = $(this), 235 uid = $this.data("uid"), 236 inst = $this.siblings("select[sb='"+uid+"']").data(PROP_NAME), 237 trgt = $this.siblings(["select[sb='", uid, "']"].join("")).get(0), 238 $f = $this.find("ul").find("a." + inst.settings.classFocus); 239 switch (key) { 240 case 37: //Arrow Left 241 case 38: //Arrow Up 242 if ($f.length > 0) { 243 var $next; 244 $("a", $this).removeClass(inst.settings.classFocus); 245 $next = $f.parent().prevAll("li:has(a)").eq(0).find("a"); 246 if ($next.length > 0) { 247 $next.addClass(inst.settings.classFocus).focus(); 248 $("#sbSelector_" + uid).text($next.text()); 249 } 250 } 251 break; 252 case 39: //Arrow Right 253 case 40: //Arrow Down 254 var $next; 255 $("a", $this).removeClass(inst.settings.classFocus); 256 if ($f.length > 0) { 257 $next = $f.parent().nextAll("li:has(a)").eq(0).find("a"); 258 } else { 259 $next = $this.find("ul").find("a").eq(0); 260 } 261 if ($next.length > 0) { 262 $next.addClass(inst.settings.classFocus).focus(); 263 $("#sbSelector_" + uid).text($next.text()); 264 } 265 break; 266 case 13: //Enter 267 if ($f.length > 0) { 268 self._changeSelectbox(trgt, $f.attr("rel"), $f.text()); 269 } 270 self._closeSelectbox(trgt); 271 break; 272 case 9: //Tab 273 if (trgt) { 274 var inst = self._getInst(trgt); 275 if (inst/* && inst.isOpen*/) { 276 if ($f.length > 0) { 277 self._changeSelectbox(trgt, $f.attr("rel"), $f.text()); 278 } 279 self._closeSelectbox(trgt); 280 } 281 } 282 var i = parseInt($this.attr("tabindex"), 10); 283 if (!e.shiftKey) { 284 i++; 285 } else { 286 i--; 287 } 288 $("*[tabindex='" + i + "']").focus(); 289 break; 290 case 27: //Escape 291 self._closeSelectbox(trgt); 292 break; 293 } 294 e.stopPropagation(); 295 return false;
296 }).delegate("a", "mouseover", function (e) { 297 $(this).addClass(inst.settings.classFocus); 298 }).delegate("a", "mouseout", function (e) { 299 $(this).removeClass(inst.settings.classFocus); 300 }); 301 302 sbSelector.appendTo(sbHolder); 303 sbOptions.appendTo(sbHolder); 304 sbHolder.insertAfter($target); 305 306 $("html").live('mousedown', function(e) { 307 e.stopPropagation(); 308 $("select").selectbox('close'); 309 }); 310 $([".", inst.settings.classHolder, ", .", inst.settings.classSelector].join("")).mousedown(function(e) { 311 e.stopPropagation(); 312 }); 313 }, 314 /** 315 * Remove the selectbox functionality completely. This will return the element back to its pre-init state. 316 * 317 * @param {HTMLElement} target 318 */ 319 _detachSelectbox: function (target) { 320 var inst = this._getInst(target); 321 if (!inst) { 322 return FALSE; 323 } 324 $("#sbHolder_" + inst.uid).remove(); 325 $.data(target, PROP_NAME, null); 326 $(target).show(); 327 }, 328 /** 329 * Change selected attribute of the selectbox. 330 * 331 * @param {HTMLElement} target 332 * @param {String} value 333 * @param {String} text 334 */ 335 _changeSelectbox: function (target, value, text) { 336 var onChange, 337 inst = this._getInst(target); 338 if (inst) { 339 onChange = this._get(inst, 'onChange'); 340 $("#sbSelector_" + inst.uid).text(text); 341 } 342 value = value.replace(/\'/g, "\\'"); 343 $(target).find("option[value='" + value + "']").attr("selected", TRUE); 344 if (inst && onChange) { 345 onChange.apply((inst.input ? inst.input[0] : null), [value, inst]); 346 } else if (inst && inst.input) { 347 inst.input.trigger('change'); 348 } 349 }, 350 /** 351 * Enable the selectbox. 352 * 353 * @param {HTMLElement} target 354 */ 355 _enableSelectbox: function (target) { 356 var inst = this._getInst(target); 357 if (!inst || !inst.isDisabled) { 358 return FALSE; 359 } 360 $("#sbHolder_" + inst.uid).removeClass(inst.settings.classHolderDisabled); 361 inst.isDisabled = FALSE; 362 $.data(target, PROP_NAME, inst); 363 }, 364 /** 365 * Disable the selectbox. 366 * 367 * @param {HTMLElement} target 368 */ 369 _disableSelectbox: function (target) { 370 var inst = this._getInst(target); 371 if (!inst || inst.isDisabled) { 372 return FALSE; 373 } 374 $("#sbHolder_" + inst.uid).addClass(inst.settings.classHolderDisabled); 375 inst.isDisabled = TRUE; 376 $.data(target, PROP_NAME, inst); 377 }, 378 /** 379 * Get or set any selectbox option. If no value is specified, will act as a getter. 380 * 381 * @param {HTMLElement} target 382 * @param {String} name 383 * @param {Object} value 384 */ 385 _optionSelectbox: function (target, name, value) { 386 var inst = this._getInst(target); 387 if (!inst) { 388 return FALSE; 389 } 390 //TODO check name 391 inst[name] = value; 392 $.data(target, PROP_NAME, inst); 393 }, 394 /** 395 * Call up attached selectbox 396 * 397 * @param {HTMLElement} target 398 */ 399 _openSelectbox: function (target) { 400 var inst = this._getInst(target); 401 //if (!inst || this._state[inst.uid] || inst.isDisabled) { 402 if (!inst || inst.isOpen || inst.isDisabled) { 403 return; 404 } 405 var el = $("#sbOptions_" + inst.uid), 406 viewportHeight = parseInt($(window).height(), 10), 407 offset = $("#sbHolder_" + inst.uid).offset(), 408 scrollTop = $(window).scrollTop(), 409 height = el.prev().height(), 410 diff = viewportHeight - (offset.top - scrollTop) - height / 2, 411 onOpen = this._get(inst, 'onOpen'); 412 el.css({ 413 "top": height + "px", 414 "maxHeight": (diff - height) + "px" 415 }); 416 inst.settings.effect === "fade" ? el.fadeIn(inst.settings.speed) : el.slideDown(inst.settings.speed); 417 $("#sbToggle_" + inst.uid).addClass(inst.settings.classToggleOpen); 418 this._state[inst.uid] = TRUE; 419 inst.isOpen = TRUE; 420 if (onOpen) { 421 onOpen.apply((inst.input ? inst.input[0] : null), [inst]); 422 } 423 $.data(target, PROP_NAME, inst); 424 }, 425 /** 426 * Close opened selectbox 427 * 428 * @param {HTMLElement} target 429 */ 430 _closeSelectbox: function (target) { 431 var inst = this._getInst(target); 432 //if (!inst || !this._state[inst.uid]) { 433 if (!inst || !inst.isOpen) { 434 return; 435 } 436 var onClose = this._get(inst, 'onClose'); 437 inst.settings.effect === "fade" ? $("#sbOptions_" + inst.uid).fadeOut(inst.settings.speed) : $("#sbOptions_" + inst.uid).slideUp(inst.settings.speed); 438 $("#sbToggle_" + inst.uid).removeClass(inst.settings.classToggleOpen); 439 this._state[inst.uid] = FALSE; 440 inst.isOpen = FALSE; 441 if (onClose) { 442 onClose.apply((inst.input ? inst.input[0] : null), [inst]); 443 } 444 $.data(target, PROP_NAME, inst); 445 }, 446 /** 447 * Create a new instance object 448 * 449 * @param {HTMLElement} target 450 * @return {Object} 451 */ 452 _newInst: function(target) { 453 var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); 454 return { 455 id: id, 456 input: target, 457 uid: Math.floor(Math.random() * 99999999), 458 isOpen: FALSE, 459 isDisabled: FALSE, 460 settings: {} 461 }; 462 }, 463 /** 464 * Retrieve the instance data for the target control. 465 * 466 * @param {HTMLElement} target 467 * @return {Object} - the associated instance data 468 * @throws error if a jQuery problem getting data 469 */ 470 _getInst: function(target) { 471 try { 472 return $.data(target, PROP_NAME); 473 } 474 catch (err) { 475 throw 'Missing instance data for this selectbox'; 476 } 477 }, 478 /** 479 * Get a setting value, defaulting if necessary 480 * 481 * @param {Object} inst 482 * @param {String} name 483 * @return {Mixed} 484 */ 485 _get: function(inst, name) { 486 return inst.settings[name] !== undefined ? inst.settings[name] : this._defaults[name]; 487 } 488 }); 489 490 /** 491 * Invoke the selectbox functionality. 492 * 493 * @param {Object|String} options 494 * @return {Object} 495 */ 496 $.fn.selectbox = function (options) { 497 498 var otherArgs = Array.prototype.slice.call(arguments, 1); 499 if (typeof options == 'string' && options == 'isDisabled') { 500 return $.selectbox['_' + options + 'Selectbox'].apply($.selectbox, [this[0]].concat(otherArgs)); 501 } 502 503 if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') { 504 return $.selectbox['_' + options + 'Selectbox'].apply($.selectbox, [this[0]].concat(otherArgs)); 505 } 506 507 return this.each(function() { 508 typeof options == 'string' ? 509 $.selectbox['_' + options + 'Selectbox'].apply($.selectbox, [this].concat(otherArgs)) : 510 $.selectbox._attachSelectbox(this, options); 511 }); 512 }; 513 514 $.selectbox = new Selectbox(); // singleton instance 515 $.selectbox.version = "0.2"; 516})(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.