+++ /dev/null
-/*
- * Alternate Select Multiple (asmSelect) 1.0.4a beta - jQuery Plugin
- * http://www.ryancramer.com/projects/asmselect/
- *
- * Copyright (c) 2009 by Ryan Cramer - http://www.ryancramer.com
- *
- * Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
- *
- */
-
-(function($) {
-
- $.fn.crmasmSelect = function(customOptions) {
-
- var options = {
-
- listType: 'ul', // Ordered list 'ol', or unordered list 'ul'
- sortable: false, // Should the list be sortable?
- highlight: false, // Use the highlight feature?
- animate: false, // Animate the the adding/removing of items in the list?
- addItemTarget: 'bottom', // Where to place new selected items in list: top or bottom
- hideWhenAdded: false, // Hide the option when added to the list? works only in FF
- debugMode: false, // Debug mode keeps original select visible
-
- removeLabel: 'X', // Text used in the "remove" link
- highlightAddedLabel: 'Added: ', // Text that precedes highlight of added item
- highlightRemovedLabel: 'Removed: ', // Text that precedes highlight of removed item
-
- containerClass: 'crmasmContainer', // Class for container that wraps this widget
- selectClass: 'crmasmSelect', // Class for the newly created <select>
- optionDisabledClass: 'asmOptionDisabled', // Class for items that are already selected / disabled
- listClass: 'crmasmList', // Class for the list ($ol)
- listSortableClass: 'crmasmListSortable', // Another class given to the list when it is sortable
- listItemClass: 'crmasmListItem', // Class for the <li> list items
- listItemLabelClass: 'crmasmListItemLabel', // Class for the label text that appears in list items
- removeClass: 'crmasmListItemRemove', // Class given to the "remove" link
- highlightClass: 'crm asmHighlight', // Class given to the highlight <span>
-
- respectParents: false // Force selection of "parent" option items
- };
-
- $.extend(options, customOptions);
-
- return this.each(function(index) {
-
- var $original = $(this); // the original select multiple
- var $container; // a container that is wrapped around our widget
- var $select; // the new select we have created
- var $ol; // the list that we are manipulating
- var buildingSelect = false; // is the new select being constructed right now?
- var ieClick = false; // in IE, has a click event occurred? ignore if not
- var ignoreOriginalChangeEvent = false; // originalChangeEvent bypassed when this is true
-
- function init() {
-
- // initialize the alternate select multiple
-
- // this loop ensures uniqueness, in case of existing crmasmSelects placed by ajax (1.0.3)
- while($("#" + options.containerClass + index).size() > 0) index++;
-
- $select = $("<select></select>")
- .addClass(options.selectClass)
- .attr('name', options.selectClass + index)
- .attr('id', options.selectClass + index);
-
- $selectRemoved = $("<select></select>");
-
- $ol = $("<" + options.listType + "></" + options.listType + ">")
- .addClass(options.listClass)
- .attr('id', options.listClass + index);
-
- $container = $("<div></div>")
- .addClass(options.containerClass)
- .attr('id', options.containerClass + index);
-
- buildSelect();
-
- $select.change(selectChangeEvent)
- .click(selectClickEvent);
-
- $original.change(originalChangeEvent)
- .wrap($container).before($select).before($ol);
-
- if(options.sortable) makeSortable();
-
- if($.browser.msie && $.browser.version < 8) $ol.css('display', 'inline-block'); // Thanks Matthew Hutton
- }
-
- function makeSortable() {
-
- // make any items in the selected list sortable
- // requires jQuery UI sortables, draggables, droppables
-
- $ol.sortable({
- items: 'li.' + options.listItemClass,
- handle: '.' + options.listItemLabelClass,
- axis: 'y',
- update: function(e, data) {
-
- var updatedOptionId;
-
- $(this).children("li").each(function(n) {
-
- $option = $('#' + $(this).attr('rel'));
-
- if($(this).is(".ui-sortable-helper")) {
- updatedOptionId = $option.attr('id');
- return;
- }
-
- $original.append($option);
- });
-
- if(updatedOptionId) triggerOriginalChange(updatedOptionId, 'sort');
- }
-
- }).addClass(options.listSortableClass);
- }
-
- function selectChangeEvent(e) {
-
- // an item has been selected on the regular select we created
- // check to make sure it's not an IE screwup, and add it to the list
-
- if($.browser.msie && $.browser.version < 7 && !ieClick) return;
- var id = $(this).children("option:selected").slice(0,1).attr('rel');
- addListItem(id);
- ieClick = false;
- triggerOriginalChange(id, 'add'); // for use by user-defined callbacks
- }
-
- function selectClickEvent() {
-
- // IE6 lets you scroll around in a select without it being pulled down
- // making sure a click preceded the change() event reduces the chance
- // if unintended items being added. there may be a better solution?
-
- ieClick = true;
- }
-
- function originalChangeEvent(e) {
-
- // select or option change event manually triggered
- // on the original <select multiple>, so rebuild ours
-
- if(ignoreOriginalChangeEvent) {
- ignoreOriginalChangeEvent = false;
- return;
- }
-
- $select.empty();
- $ol.empty();
- buildSelect();
-
- // opera has an issue where it needs a force redraw, otherwise
- // the items won't appear until something else forces a redraw
- if($.browser.opera) $ol.hide().fadeIn("fast");
- }
-
- function buildSelect() {
-
- // build or rebuild the new select that the user
- // will select items from
-
- buildingSelect = true;
-
- // add a first option to be the home option / default selectLabel
- $select.prepend("<option>" + $original.attr('title') + "</option>");
-
- $original.children("option").each(function(n) {
-
- var $t = $(this);
- var id;
-
- if(!$t.attr('id')) $t.attr('id', 'asm' + index + 'option' + n);
- id = $t.attr('id');
- classes = $t.attr('class');
-
- if($t.is(":selected")) {
- addListItem(id);
- addSelectOption(id, classes, true);
- } else {
- addSelectOption(id,classes);
- }
- });
-
- if(!options.debugMode) $original.hide(); // IE6 requires this on every buildSelect()
- selectFirstItem();
- buildingSelect = false;
- }
-
- function addSelectOption(optionId, optionClasses, disabled) {
-
- // add an <option> to the <select>
- // used only by buildSelect()
-
- if(disabled == undefined) disabled = false;
-
- var $O = $('#' + optionId);
- var $option = $("<option>" + $O.text() + "</option>")
- .val($O.val())
- .attr('rel', optionId)
- .addClass(optionClasses);
-
- if(disabled) disableSelectOption($option);
-
- $select.append($option);
- }
-
- function selectFirstItem() {
-
- // select the firm item from the regular select that we created
-
- $select.children(":eq(0)").prop("selected", true);
- }
-
- function disableSelectOption($option) {
-
- // make an option disabled, indicating that it's already been selected
- // because safari is the only browser that makes disabled items look 'disabled'
- // we apply a class that reproduces the disabled look in other browsers
-
- $option.addClass(options.optionDisabledClass)
- .prop("selected", false)
- .prop("disabled", true);
-
- if(options.hideWhenAdded) $option.hide();
- if($.browser.msie) $select.hide().show(); // this forces IE to update display
- }
-
- function enableSelectOption($option) {
-
- // given an already disabled select option, enable it
-
- $option.removeClass(options.optionDisabledClass)
- .prop("disabled", false);
-
- if(options.hideWhenAdded) $option.show();
- if($.browser.msie) $select.hide().show(); // this forces IE to update display
- }
-
- function addListItem(optionId) {
-
- // add a new item to the html list
-
- var $O = $('#' + optionId);
-
- if(!$O) return; // this is the first item, selectLabel
-
- var $removeLink = $("<a></a>")
- .attr("href", "#")
- .addClass(options.removeClass)
- .prepend(options.removeLabel)
- .click(function() {
- dropListItem($(this).parent('li').attr('rel'));
- return false;
- });
-
- var $itemLabel = $("<span></span>")
- .addClass(options.listItemLabelClass)
- .html($O.html());
-
- var $item = $("<li></li>")
- .attr('rel', optionId)
- .addClass(options.listItemClass)
- .addClass($O.attr('class'))
- .append($itemLabel)
- .append($removeLink)
- .hide();
-
- if(!buildingSelect) {
- if($O.is(":selected")) return; // already have it
- $O.prop('selected', true);
- }
-
- if(options.addItemTarget == 'top' && !buildingSelect) {
- $ol.prepend($item);
- if(options.sortable) $original.prepend($O);
- } else {
- $ol.append($item);
- if(options.sortable) $original.append($O);
- }
-
- addListItemShow($item);
-
- disableSelectOption($("[rel=" + optionId + "]", $select));
-
- if(!buildingSelect) {
- setHighlight($item, options.highlightAddedLabel);
- selectFirstItem();
- if(options.sortable) $ol.sortable("refresh");
- }
-
- if(options.respectParents) {
-
- if($O.hasClass('child')) {
- parentName = $O.attr('class').split('parent-')[1];
- parentName = parentName.split(' ')[0];
- parentId = $('.option-'+parentName).attr('rel');
- addListItem(parentId);
- }
- }
- }
-
- function addListItemShow($item) {
-
- // reveal the currently hidden item with optional animation
- // used only by addListItem()
-
- if(options.animate && !buildingSelect) {
- $item.animate({
- opacity: "show",
- height: "show"
- }, 100, "swing", function() {
- $item.animate({
- height: "+=2px"
- }, 50, "swing", function() {
- $item.animate({
- height: "-=2px"
- }, 25, "swing");
- });
- });
- } else {
- $item.show();
- }
- }
-
- function dropListItem(optionId, highlightItem) {
-
- // remove an item from the html list
-
- if(highlightItem == undefined) highlightItem = true;
- var $O = $('#' + optionId);
-
- $O.prop('selected', false);
- $item = $ol.children("li[rel=" + optionId + "]");
-
- dropListItemHide($item);
- enableSelectOption($("[rel=" + optionId + "]", options.removeWhenAdded ? $selectRemoved : $select));
-
- if(highlightItem) setHighlight($item, options.highlightRemovedLabel);
-
- triggerOriginalChange(optionId, 'drop');
-
- }
-
- function dropListItemHide($item) {
-
- // remove the currently visible item with optional animation
- // used only by dropListItem()
-
- if(options.animate && !buildingSelect) {
-
- $prevItem = $item.prev("li");
-
- $item.animate({
- opacity: "hide",
- height: "hide"
- }, 100, "linear", function() {
- $prevItem.animate({
- height: "-=2px"
- }, 50, "swing", function() {
- $prevItem.animate({
- height: "+=2px"
- }, 100, "swing");
- });
- $item.remove();
- });
-
- } else {
- $item.remove();
- }
- }
-
- function setHighlight($item, label) {
-
- // set the contents of the highlight area that appears
- // directly after the <select> single
- // fade it in quickly, then fade it out
-
- if(!options.highlight) return;
-
- $select.next("#" + options.highlightClass + index).remove();
-
- var $highlight = $("<span></span>")
- .hide()
- .addClass(options.highlightClass)
- .attr('id', options.highlightClass + index)
- .html(label + $item.children("." + options.listItemLabelClass).slice(0,1).text());
-
- $select.after($highlight);
-
- $highlight.fadeIn("fast", function() {
- setTimeout(function() { $highlight.fadeOut("slow"); }, 50);
- });
- }
-
- function triggerOriginalChange(optionId, type) {
-
- // trigger a change event on the original select multiple
- // so that other scripts can pick them up
-
- ignoreOriginalChangeEvent = true;
- $option = $("#" + optionId);
-
- $original.trigger('change', [{
- 'option': $option,
- 'value': $option.val(),
- 'id': optionId,
- 'item': $ol.children("[rel=" + optionId + "]"),
- 'type': type
- }]);
- }
-
- init();
- });
- };
-
-})(jQuery);
-