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 }).delegate("a", "mouseover", function (e) { 294 $(this).addClass(inst.settings.classFocus); 295 }).delegate("a", "mouseout", 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").live('mousedown', function(e) { 304 e.stopPropagation(); 305 $("select").selectbox('close'); 306 }); 307 $([".", inst.settings.classHolder, ", .", inst.settings.classSelector].join("")).mousedown(function(e) { 308 e.stopPropagation(); 309 }); 310 }, 311 /** 312 * Remove the selectbox functionality completely. This will return the element back to its pre-init state. 313 * 314 * @param {HTMLElement} target 315 */ 316 _detachSelectbox: function (target) { 317 var inst = this._getInst(target); 318 if (!inst) { 319 return FALSE; 320 } 321 $("#sbHolder_" + inst.uid).remove(); 322 $.data(target, PROP_NAME, null); 323 $(target).show(); 324 }, 325 /** 326 * Change selected attribute of the selectbox. 327 * 328 * @param {HTMLElement} target 329 * @param {String} value 330 * @param {String} text 331 */ 332 _changeSelectbox: function (target, value, text) { 333 var onChange, 334 inst = this._getInst(target); 335 if (inst) { 336 onChange = this._get(inst, 'onChange'); 337 $("#sbSelector_" + inst.uid).text(text); 338 } 339 value = value.replace(/\'/g, "\\'"); 340 $(target).find("option[value='" + value + "']").attr("selected", TRUE); 341 if (inst && onChange) { 342 onChange.apply((inst.input ? inst.input[0] : null), [value, inst]); 343 } else if (inst && inst.input) { 344 inst.input.trigger('change'); 345 } 346 }, 347 /** 348 * Enable the selectbox. 349 * 350 * @param {HTMLElement} target 351 */ 352 _enableSelectbox: function (target) { 353 var inst = this._getInst(target); 354 if (!inst || !inst.isDisabled) { 355 return FALSE; 356 } 357 $("#sbHolder_" + inst.uid).removeClass(inst.settings.classHolderDisabled); 358 inst.isDisabled = FALSE; 359 $.data(target, PROP_NAME, inst); 360 }, 361 /** 362 * Disable the selectbox. 363 * 364 * @param {HTMLElement} target 365 */ 366 _disableSelectbox: function (target) { 367 var inst = this._getInst(target); 368 if (!inst || inst.isDisabled) { 369 return FALSE; 370 } 371 $("#sbHolder_" + inst.uid).addClass(inst.settings.classHolderDisabled); 372 inst.isDisabled = TRUE; 373 $.data(target, PROP_NAME, inst); 374 }, 375 /** 376 * Get or set any selectbox option. If no value is specified, will act as a getter. 377 * 378 * @param {HTMLElement} target 379 * @param {String} name 380 * @param {Object} value 381 */ 382 _optionSelectbox: function (target, name, value) { 383 var inst = this._getInst(target); 384 if (!inst) { 385 return FALSE; 386 } 387 //TODO check name 388 inst[name] = value; 389 $.data(target, PROP_NAME, inst); 390 }, 391 /** 392 * Call up attached selectbox 393 * 394 * @param {HTMLElement} target 395 */ 396 _openSelectbox: function (target) { 397 var inst = this._getInst(target); 398 //if (!inst || this._state[inst.uid] || inst.isDisabled) { 399 if (!inst || inst.isOpen || inst.isDisabled) { 400 return; 401 } 402 var el = $("#sbOptions_" + inst.uid),
403 viewportHeight = parseInt($(window).height(), 10), 404 offset = $("#sbHolder_" + inst.uid).offset(), 405 scrollTop = $(window).scrollTop(), 406 height = el.prev().height(), 407 diff = viewportHeight - (offset.top - scrollTop) - height / 2, 408 onOpen = this._get(inst, 'onOpen'); 409 el.css({ 410 "top": height + "px", 411 "maxHeight": (diff - height) + "px" 412 }); 413 inst.settings.effect === "fade" ? el.fadeIn(inst.settings.speed) : el.slideDown(inst.settings.speed); 414 $("#sbToggle_" + inst.uid).addClass(inst.settings.classToggleOpen); 415 this._state[inst.uid] = TRUE; 416 inst.isOpen = TRUE; 417 if (onOpen) { 418 onOpen.apply((inst.input ? inst.input[0] : null), [inst]); 419 } 420 $.data(target, PROP_NAME, inst); 421 }, 422 /** 423 * Close opened selectbox 424 * 425 * @param {HTMLElement} target 426 */ 427 _closeSelectbox: function (target) { 428 var inst = this._getInst(target); 429 //if (!inst || !this._state[inst.uid]) { 430 if (!inst || !inst.isOpen) { 431 return; 432 } 433 var onClose = this._get(inst, 'onClose'); 434 inst.settings.effect === "fade" ? $("#sbOptions_" + inst.uid).fadeOut(inst.settings.speed) : $("#sbOptions_" + inst.uid).slideUp(inst.settings.speed); 435 $("#sbToggle_" + inst.uid).removeClass(inst.settings.classToggleOpen); 436 this._state[inst.uid] = FALSE; 437 inst.isOpen = FALSE; 438 if (onClose) { 439 onClose.apply((inst.input ? inst.input[0] : null), [inst]); 440 } 441 $.data(target, PROP_NAME, inst); 442 }, 443 /** 444 * Create a new instance object 445 * 446 * @param {HTMLElement} target 447 * @return {Object} 448 */ 449 _newInst: function(target) { 450 var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); 451 return { 452 id: id, 453 input: target, 454 uid: Math.floor(Math.random() * 99999999), 455 isOpen: FALSE, 456 isDisabled: FALSE, 457 settings: {} 458 }; 459 }, 460 /** 461 * Retrieve the instance data for the target control. 462 * 463 * @param {HTMLElement} target 464 * @return {Object} - the associated instance data 465 * @throws error if a jQuery problem getting data 466 */ 467 _getInst: function(target) { 468 try { 469 return $.data(target, PROP_NAME); 470 } 471 catch (err) { 472 throw 'Missing instance data for this selectbox'; 473 } 474 }, 475 /** 476 * Get a setting value, defaulting if necessary 477 * 478 * @param {Object} inst 479 * @param {String} name 480 * @return {Mixed} 481 */ 482 _get: function(inst, name) { 483 return inst.settings[name] !== undefined ? inst.settings[name] : this._defaults[name]; 484 } 485 }); 486 487 /** 488 * Invoke the selectbox functionality. 489 * 490 * @param {Object|String} options 491 * @return {Object} 492 */ 493 $.fn.selectbox = function (options) { 494 495 var otherArgs = Array.prototype.slice.call(arguments, 1); 496 if (typeof options == 'string' && options == 'isDisabled') { 497 return $.selectbox['_' + options + 'Selectbox'].apply($.selectbox, [this[0]].concat(otherArgs)); 498 } 499 500 if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') { 501 return $.selectbox['_' + options + 'Selectbox'].apply($.selectbox, [this[0]].concat(otherArgs)); 502 } 503 504 return this.each(function() { 505 typeof options == 'string' ? 506 $.selectbox['_' + options + 'Selectbox'].apply($.selectbox, [this].concat(otherArgs)) : 507 $.selectbox._attachSelectbox(this, options); 508 }); 509 }; 510 511 $.selectbox = new Selectbox(); // singleton instance 512 $.selectbox.version = "0.2"; 513})(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.