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 child; 176 if (that.is(":selected")) { 177 sbSelector.text(that.text()); 178 s = TRUE; 179 } 180 if (i === olen - 1) { 181 li.addClass("last"); 182 } 183 if (!that.is(":disabled") && !disabled) { 184 child = $("<a>", { 185 "href": "#" + that.val(), 186 "rel": that.val() 187 }).text(that.text()).bind("click.sb", function (e) { 188 if (e && e.preventDefault) { 189 e.preventDefault(); 190 } 191 var t = sbToggle, 192 $this = $(this), 193 uid = t.attr("id").split("_")[1]; 194 self._changeSelectbox(target, $this.attr("rel"), $this.text()); 195 self._closeSelectbox(target); 196 }).bind("mouseover.sb", function () { 197 var $this = $(this); 198 $this.parent().siblings().find("a").removeClass(inst.settings.classFocus); 199 $this.addClass(inst.settings.classFocus); 200 }).bind("mouseout.sb", function () { 201 $(this).removeClass(inst.settings.classFocus); 202 }); 203 if (sub) { 204 child.addClass(inst.settings.classSub); 205 } 206 if (that.is(":selected")) { 207 child.addClass(inst.settings.classFocus); 208 } 209 child.appendTo(li); 210 } else { 211 child = $("<span>", { 212 "text": that.text() 213 }).addClass(inst.settings.classDisabled); 214 if (sub) { 215 child.addClass(inst.settings.classSub); 216 } 217 child.appendTo(li); 218 } 219 li.appendTo(sbOptions); 220 }); 221 } 222 223 if (!s) { 224 sbSelector.text(opts.first().text()); 225 } 226 227 $.data(target, PROP_NAME, inst); 228 229 sbHolder.data("uid", inst.uid).bind("keydown.sb", function (e) { 230 var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0, 231 $this = $(this), 232 uid = $this.data("uid"), 233 inst = $this.siblings("select[sb='"+uid+"']").data(PROP_NAME), 234 trgt = $this.siblings(["select[sb='", uid, "']"].join("")).get(0), 235 $f = $this.find("ul").find("a." + inst.settings.classFocus); 236 switch (key) { 237 case 37: //Arrow Left 238 case 38: //Arrow Up 239 if ($f.length > 0) { 240 var $next; 241 $("a", $this).removeClass(inst.settings.classFocus); 242 $next = $f.parent().prevAll("li:has(a)").eq(0).find("a"); 243 if ($next.length > 0) { 244 $next.addClass(inst.settings.classFocus).focus(); 245 $("#sbSelector_" + uid).text($next.text()); 246 } 247 } 248 break; 249 case 39: //Arrow Right 250 case 40: //Arrow Down 251 var $next; 252 $("a", $this).removeClass(inst.settings.classFocus); 253 if ($f.length > 0) { 254 $next = $f.parent().nextAll("li:has(a)").eq(0).find("a"); 255 } else { 256 $next = $this.find("ul").find("a").eq(0); 257 } 258 if ($next.length > 0) { 259 $next.addClass(inst.settings.classFocus).focus(); 260 $("#sbSelector_" + uid).text($next.text()); 261 } 262 break; 263 case 13: //Enter 264 if ($f.length > 0) { 265 self._changeSelectbox(trgt, $f.attr("rel"), $f.text()); 266 } 267 self._closeSelectbox(trgt); 268 break; 269 case 9: //Tab 270 if (trgt) { 271 var inst = self._getInst(trgt); 272 if (inst/* && inst.isOpen*/) { 273 if ($f.length > 0) { 274 self._changeSelectbox(trgt, $f.attr("rel"), $f.text()); 275 } 276 self._closeSelectbox(trgt); 277 } 278 } 279 var i = parseInt($this.attr("tabindex"), 10); 280 if (!e.shiftKey) { 281 i++; 282 } else { 283 i--; 284 } 285 $("*[tabindex='" + i + "']").focus(); 286 break; 287 case 27: //Escape 288 self._closeSelectbox(trgt); 289 break; 290 } 291 e.stopPropagation(); 292 return false; 293 }).on("mouseover", "a", function (e) { 294 $(this).addClass(inst.settings.classFocus); 295 }).on("mouseout", "a", function (e) { 296 $(this).removeClass(inst.settings.classFocus); 297 }); 298 299 sbSelector.appendTo(sbHolder); 300 sbOptions.appendTo(sbHolder); 301 sbHolder.insertAfter($target); 302 303 $("html").on("mousedown", "a", function(e) { 304 e.stopPropagation(); 305 $("select").selectbox('close'); 306 }); 307 308 $([".", inst.settings.classHolder, ", .", inst.settings.classSelector].join("")).mousedown(function(e) { 309 e.stopPropagation(); 310 }); 311 }, 312 /** 313 * Remove the selectbox functionality completely. This will return the element back to its pre-init state. 314 * 315 * @param {HTMLElement} target 316 */ 317 _detachSelectbox: function (target) { 318 var inst = this._getInst(target); 319 if (!inst) { 320 return FALSE; 321 } 322 $("#sbHolder_" + inst.uid).remove(); 323 $.data(target, PROP_NAME, null); 324 $(target).show(); 325 }, 326 /** 327 * Change selected attribute of the selectbox. 328 * 329 * @param {HTMLElement} target 330 * @param {String} value 331 * @param {String} text 332 */ 333 _changeSelectbox: function (target, value, text) { 334 var onChange, 335 inst = this._getInst(target); 336 if (inst) { 337 onChange = this._get(inst, 'onChange'); 338 $("#sbSelector_" + inst.uid).text(text); 339 } 340 value = value.replace(/\'/g, "\\'"); 341 $(target).find("option[value='" + value + "']").attr("selected", TRUE); 342 if (inst && onChange) { 343 onChange.apply((inst.input ? inst.input[0] : null), [value, inst]); 344 } else if (inst && inst.input) { 345 inst.input.trigger('change'); 346 } 347 }, 348 /** 349 * Enable the selectbox. 350 * 351 * @param {HTMLElement} target 352 */ 353 _enableSelectbox: function (target) { 354 var inst = this._getInst(target); 355 if (!inst || !inst.isDisabled) { 356 return FALSE; 357 } 358 $("#sbHolder_" + inst.uid).removeClass(inst.settings.classHolderDisabled); 359 inst.isDisabled = FALSE; 360 $.data(target, PROP_NAME, inst); 361 }, 362 /** 363 * Disable the selectbox. 364 * 365 * @param {HTMLElement} target 366 */ 367 _disableSelectbox: function (target) { 368 var inst = this._getInst(target); 369 if (!inst || inst.isDisabled) { 370 return FALSE; 371 } 372 $("#sbHolder_" + inst.uid).addClass(inst.settings.classHolderDisabled); 373 inst.isDisabled = TRUE; 374 $.data(target, PROP_NAME, inst); 375 }, 376 /** 377 * Get or set any selectbox option. If no value is specified, will act as a getter. 378 * 379 * @param {HTMLElement} target 380 * @param {String} name 381 * @param {Object} value 382 */ 383 _optionSelectbox: function (target, name, value) { 384 var inst = this._getInst(target); 385 if (!inst) { 386 return FALSE; 387 } 388 //TODO check name 389 inst[name] = value; 390 $.data(target, PROP_NAME, inst); 391 }, 392 /** 393 * Call up attached selectbox 394 * 395 * @param {HTMLElement} target 396 */ 397 _openSelectbox: function (target) { 398 var inst = this._getInst(target); 399 //if (!inst || this._state[inst.uid] || inst.isDisabled) { 400 if (!inst || inst.isOpen || inst.isDisabled) { 401 return; 402 } 403 var el = $("#sbOptions_" + inst.uid),
404 viewportHeight = parseInt($(window).height(), 10), 405 offset = $("#sbHolder_" + inst.uid).offset(), 406 scrollTop = $(window).scrollTop(), 407 height = el.prev().height(), 408 diff = viewportHeight - (offset.top - scrollTop) - height / 2, 409 onOpen = this._get(inst, 'onOpen'); 410 el.css({ 411 "top": height + "px", 412 "maxHeight": (diff - height) + "px" 413 }); 414 inst.settings.effect === "fade" ? el.fadeIn(inst.settings.speed) : el.slideDown(inst.settings.speed); 415 $("#sbToggle_" + inst.uid).addClass(inst.settings.classToggleOpen); 416 this._state[inst.uid] = TRUE; 417 inst.isOpen = TRUE; 418 if (onOpen) { 419 onOpen.apply((inst.input ? inst.input[0] : null), [inst]); 420 } 421 $.data(target, PROP_NAME, inst); 422 }, 423 /** 424 * Close opened selectbox 425 * 426 * @param {HTMLElement} target 427 */ 428 _closeSelectbox: function (target) { 429 var inst = this._getInst(target); 430 //if (!inst || !this._state[inst.uid]) { 431 if (!inst || !inst.isOpen) { 432 return; 433 } 434 var onClose = this._get(inst, 'onClose'); 435 inst.settings.effect === "fade" ? $("#sbOptions_" + inst.uid).fadeOut(inst.settings.speed) : $("#sbOptions_" + inst.uid).slideUp(inst.settings.speed); 436 $("#sbToggle_" + inst.uid).removeClass(inst.settings.classToggleOpen); 437 this._state[inst.uid] = FALSE; 438 inst.isOpen = FALSE; 439 if (onClose) { 440 onClose.apply((inst.input ? inst.input[0] : null), [inst]); 441 } 442 $.data(target, PROP_NAME, inst); 443 }, 444 /** 445 * Create a new instance object 446 * 447 * @param {HTMLElement} target 448 * @return {Object} 449 */ 450 _newInst: function(target) { 451 var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); 452 return { 453 id: id, 454 input: target, 455 uid: Math.floor(Math.random() * 99999999), 456 isOpen: FALSE, 457 isDisabled: FALSE, 458 settings: {} 459 }; 460 }, 461 /** 462 * Retrieve the instance data for the target control. 463 * 464 * @param {HTMLElement} target 465 * @return {Object} - the associated instance data 466 * @throws error if a jQuery problem getting data 467 */ 468 _getInst: function(target) { 469 try { 470 return $.data(target, PROP_NAME); 471 } 472 catch (err) { 473 throw 'Missing instance data for this selectbox'; 474 } 475 }, 476 /** 477 * Get a setting value, defaulting if necessary 478 * 479 * @param {Object} inst 480 * @param {String} name 481 * @return {Mixed} 482 */ 483 _get: function(inst, name) { 484 return inst.settings[name] !== undefined ? inst.settings[name] : this._defaults[name]; 485 } 486 }); 487 488 /** 489 * Invoke the selectbox functionality. 490 * 491 * @param {Object|String} options 492 * @return {Object} 493 */ 494 $.fn.selectbox = function (options) { 495 496 var otherArgs = Array.prototype.slice.call(arguments, 1); 497 if (typeof options == 'string' && options == 'isDisabled') { 498 return $.selectbox['_' + options + 'Selectbox'].apply($.selectbox, [this[0]].concat(otherArgs)); 499 } 500 501 if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') { 502 return $.selectbox['_' + options + 'Selectbox'].apply($.selectbox, [this[0]].concat(otherArgs)); 503 } 504 505 return this.each(function() { 506 typeof options == 'string' ? 507 $.selectbox['_' + options + 'Selectbox'].apply($.selectbox, [this].concat(otherArgs)) : 508 $.selectbox._attachSelectbox(this, options); 509 }); 510 }; 511 512 $.selectbox = new Selectbox(); // singleton instance 513 $.selectbox.version = "0.2"; 514})(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.