1/* 2 * Alternate Select Multiple (crmasmSelect) 1.0.4 beta - jQuery Plugin 3 * http://www.ryancramer.com/projects/crmasmSelect/ 4 * 5 * Copyright (c) 2008 by Ryan Cramer - http://www.ryancramer.com 6 * 7 * Dual licensed under the MIT (MIT-LICENSE.txt) 8 * and GPL (GPL-LICENSE.txt) licenses. 9 * 10 */ 11 12(function($) { 13 14 $.fn.crmasmSelect = function(customOptions) { 15 16 var options = { 17 18 listType: 'ul', // Ordered list 'ol', or unordered list 'ul' 19 sortable: false, // Should the list be sortable? 20 highlight: false, // Use the highlight feature? 21 animate: false, // Animate the the adding/removing of items in the list? 22 addItemTarget: 'bottom', // Where to place new selected items in list: top or bottom 23 hideWhenAdded: false, // Hide the option when added to the list? works only in FF 24 debugMode: false, // Debug mode keeps original select visible 25 26 removeLabel: 'X', // Text used in the "remove" link 27 highlightAddedLabel: 'Added: ', // Text that precedes highlight of added item 28 highlightRemovedLabel: 'Removed: ', // Text that precedes highlight of removed item 29 30 containerClass: 'crmasmContainer', // Class for container that wraps this widget 31 selectClass: 'crmasmSelect', // Class for the newly created <select> 32 optionDisabledClass: 'asmOptionDisabled', // Class for items that are already selected / disabled 33 listClass: 'crmasmList', // Class for the list ($ol) 34 listSortableClass: 'crmasmListSortable', // Another class given to the list when it is sortable 35 listItemClass: 'crmasmListItem', // Class for the <li> list items 36 listItemLabelClass: 'crmasmListItemLabel', // Class for the label text that appears in list items 37 removeClass: 'crmasmListItemRemove', // Class given to the "remove" link 38 highlightClass: 'crm asmHighlight', // Class given to the highlight <span> 39 40 respectParents: false // Force selection of "parent" option items 41 }; 42 43 $.extend(options, customOptions); 44 45 return this.each(function(index) { 46 47 var $original = $(this); // the original select multiple 48 var $container; // a container that is wrapped around our widget 49 var $select; // the new select we have created 50 var $ol; // the list that we are manipulating 51 var buildingSelect = false; // is the new select being constructed right now? 52 var ieClick = false; // in IE, has a click event occurred? ignore if not 53 var ignoreOriginalChangeEvent = false; // originalChangeEvent bypassed when this is true 54 55 function init() { 56 57 // initialize the alternate select multiple 58 59 // this loop ensures uniqueness, in case of existing crmasmSelects placed by ajax (1.0.3) 60 while($("#" + options.containerClass + index).size() > 0) index++; 61 62 $select = $("<select></select>") 63 .addClass(options.selectClass) 64 .attr('id', options.selectClass + index); 65 66 $selectRemoved = $("<select></select>"); 67 68 $ol = $("<" + options.listType + "></" + options.listType + ">") 69 .addClass(options.listClass) 70 .attr('id', options.listClass + index); 71 72 $container = $("<div></div>") 73 .addClass(options.containerClass) 74 .attr('id', options.containerClass + index); 75 76 buildSelect(); 77 78 $select.change(selectChangeEvent) 79 .click(selectClickEvent); 80 81 $original.change(originalChangeEvent) 82 .wrap($container).before($select).before($ol); 83 84 if(options.sortable) makeSortable(); 85 86 if($.browser.msie) $ol.css('display', 'inline-block'); 87 } 88 89 function makeSortable() { 90 91 // make any items in the selected list sortable 92 // requires jQuery UI sortables, draggables, droppables 93 94 $ol.sortable({ 95 items: 'li.' + options.listItemClass, 96 handle: '.' + options.listItemLabelClass, 97 axis: 'y', 98 update: function(e, data) { 99 100 var updatedOptionId; 101 102 $(this).children("li").each(function(n) { 103 104 $option = $('#' + $(this).attr('rel')); 105 106 if($(this).is(".ui-sortable-helper")) { 107 updatedOptionId = $option.attr('id'); 108 return; 109 } 110 111 $original.append($option); 112 }); 113 114 if(updatedOptionId) triggerOriginalChange(updatedOptionId, 'sort'); 115 } 116 117 }).addClass(options.listSortableClass); 118 } 119 120 function selectChangeEvent(e) { 121 122 // an item has been selected on the regular select we created 123 // check to make sure it's not an IE screwup, and add it to the list 124 125 if($.browser.msie && $.browser.version < 7 && !ieClick) return; 126 var id = $(this).children("option:selected").slice(0,1).attr('rel'); 127 addListItem(id); 128 ieClick = false; 129 triggerOriginalChange(id, 'add'); // for use by user-defined callbacks 130 } 131 132 function selectClickEvent() { 133 134 // IE6 lets you scroll around in a select without it being pulled down 135 // making sure a click preceded the change() event reduces the chance 136 // if unintended items being added. there may be a better solution? 137 138 ieClick = true; 139 } 140 141 function originalChangeEvent(e) { 142 143 // select or option change event manually triggered 144 // on the original <select multiple>, so rebuild ours 145 146 if(ignoreOriginalChangeEvent) { 147 ignoreOriginalChangeEvent = false; 148 return; 149 } 150 151 $select.empty(); 152 $ol.empty(); 153 buildSelect(); 154 155 // opera has an issue where it needs a force redraw, otherwise 156 // the items won't appear until something else forces a redraw 157 if($.browser.opera) $ol.hide().fadeIn("fast"); 158 } 159 160 function buildSelect() { 161 162 // build or rebuild the new select that the user 163 // will select items from 164 165 buildingSelect = true; 166 167 // add a first option to be the home option / default selectLabel
168 $select.prepend("<option>" + $original.attr('title') + "</option>"); 169 170 $original.children("option").each(function(n) { 171 172 var $t = $(this); 173 var id; 174 175 if(!$t.attr('id')) $t.attr('id', 'asm' + index + 'option' + n); 176 id = $t.attr('id'); 177 classes = $t.attr('class'); 178 179 if($t.is(":selected")) { 180 addListItem(id); 181 addSelectOption(id, classes, true); 182 } else { 183 addSelectOption(id,classes); 184 } 185 }); 186 187 if(!options.debugMode) $original.hide(); // IE6 requires this on every buildSelect() 188 selectFirstItem(); 189 buildingSelect = false; 190 } 191 192 function addSelectOption(optionId, optionClasses, disabled) { 193 194 // add an <option> to the <select> 195 // used only by buildSelect() 196 197 if(disabled == undefined) var disabled = false; 198 199 var $O = $('#' + optionId); 200 var $option = $("<option>" + $O.text() + "</option>") 201 .val($O.val()) 202 .attr('rel', optionId) 203 .addClass(optionClasses); 204 205 if(disabled) disableSelectOption($option); 206 207 $select.append($option); 208 } 209 210 function selectFirstItem() { 211 212 // select the firm item from the regular select that we created 213 214 $select.children(":eq(0)").attr("selected", true); 215 } 216 217 function disableSelectOption($option) { 218 219 // make an option disabled, indicating that it's already been selected 220 // because safari is the only browser that makes disabled items look 'disabled' 221 // we apply a class that reproduces the disabled look in other browsers 222 223 $option.addClass(options.optionDisabledClass) 224 .attr("selected", false) 225 .attr("disabled", true); 226 227 if(options.hideWhenAdded) $option.hide(); 228 if($.browser.msie) $select.hide().show(); // this forces IE to update display 229 } 230 231 function enableSelectOption($option) { 232 233 // given an already disabled select option, enable it 234 235 $option.removeClass(options.optionDisabledClass) 236 .attr("disabled", false); 237 238 if(options.hideWhenAdded) $option.show(); 239 if($.browser.msie) $select.hide().show(); // this forces IE to update display 240 } 241 242 function addListItem(optionId) { 243 244 // add a new item to the html list 245 246 var $O = $('#' + optionId); 247 248 if(!$O) return; // this is the first item, selectLabel 249 250 var $removeLink = $("<a></a>") 251 .attr("href", "#") 252 .addClass(options.removeClass) 253 .prepend(options.removeLabel) 254 .click(function() { 255 dropListItem($(this).parent('li').attr('rel')); 256 return false; 257 }); 258 259 var $itemLabel = $("<span></span>") 260 .addClass(options.listItemLabelClass) 261 .html($O.html()); 262 263 var $item = $("<li></li>") 264 .attr('rel', optionId) 265 .addClass(options.listItemClass) 266 .addClass($O.attr('class')) 267 .append($itemLabel) 268 .append($removeLink) 269 .hide(); 270 271 if(!buildingSelect) { 272 if($O.is(":selected")) return; // already have it 273 $O.attr('selected', true); 274 } 275 276 if(options.addItemTarget == 'top' && !buildingSelect) { 277 $ol.prepend($item); 278 if(options.sortable) $original.prepend($O); 279 } else { 280 $ol.append($item); 281 if(options.sortable) $original.append($O); 282 } 283 284 addListItemShow($item); 285 286 disableSelectOption($("[rel=" + optionId + "]", $select)); 287 288 if(!buildingSelect) { 289 setHighlight($item, options.highlightAddedLabel); 290 selectFirstItem(); 291 if(options.sortable) $ol.sortable("refresh"); 292 } 293 294 if(options.respectParents) { 295 296 if($O.hasClass('child')) { 297 parentName = $O.attr('class').split('parent-')[1]; 298 parentName = parentName.split(' ')[0]; 299 parentId = $('.option-'+parentName).attr('rel'); 300 addListItem(parentId); 301 }; 302 }; 303 } 304 305 function addListItemShow($item) { 306 307 // reveal the currently hidden item with optional animation 308 // used only by addListItem() 309 310 if(options.animate && !buildingSelect) { 311 $item.animate({ 312 opacity: "show", 313 height: "show" 314 }, 100, "swing", function() { 315 $item.animate({ 316 height: "+=2px" 317 }, 50, "swing", function() { 318 $item.animate({ 319 height: "-=2px" 320 }, 25, "swing"); 321 }); 322 }); 323 } else { 324 $item.show(); 325 } 326 } 327 328 function dropListItem(optionId, highlightItem) { 329 330 // remove an item from the html list 331 332 if(highlightItem == undefined) var highlightItem = true; 333 var $O = $('#' + optionId); 334 335 $O.attr('selected', false); 336 $item = $ol.children("li[rel=" + optionId + "]"); 337 338 dropListItemHide($item); 339 enableSelectOption($("[rel=" + optionId + "]", options.removeWhenAdded ? $selectRemoved : $select)); 340 341 if(highlightItem) setHighlight($item, options.highlightRemovedLabel); 342 343 triggerOriginalChange(optionId, 'drop'); 344 345 } 346 347 function dropListItemHide($item) { 348 349 // remove the currently visible item with optional animation 350 // used only by dropListItem() 351 352 if(options.animate && !buildingSelect) { 353 354 $prevItem = $item.prev("li"); 355 356 $item.animate({ 357 opacity: "hide", 358 height: "hide" 359 }, 100, "linear", function() { 360 $prevItem.animate({ 361 height: "-=2px" 362 }, 50, "swing", function() { 363 $prevItem.animate({ 364 height: "+=2px" 365 }, 100, "swing"); 366 }); 367 $item.remove(); 368 }); 369 370 } else { 371 $item.remove(); 372 } 373 } 374 375 function setHighlight($item, label) { 376 377 // set the contents of the highlight area that appears 378 // directly after the <select> single 379 // fade it in quickly, then fade it out 380 381 if(!options.highlight) return; 382 383 $select.next("#" + options.highlightClass + index).remove(); 384 385 var $highlight = $("<span></span>") 386 .hide() 387 .addClass(options.highlightClass) 388 .attr('id', options.highlightClass + index) 389 .html(label + $item.children("." + options.listItemLabelClass).slice(0,1).text()); 390 391 $select.after($highlight); 392 393 $highlight.fadeIn("fast", function() { 394 setTimeout(function() { $highlight.fadeOut("slow"); }, 50); 395 }); 396 } 397 398 function triggerOriginalChange(optionId, type) { 399 400 // trigger a change event on the original select multiple 401 // so that other scripts can pick them up 402 403 ignoreOriginalChangeEvent = true; 404 $option = $("#" + optionId); 405 406 $original.trigger('change', [{ 407 'option': $option, 408 'value': $option.val(), 409 'id': optionId, 410 'item': $ol.children("[rel=" + optionId + "]"), 411 'type': type 412 }]); 413 } 414 415 init(); 416 }); 417 }; 418 419})(jQuery); 420 421
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.