﻿/*global jQuery, window, console, document */
/*jslint unparam: true, white: true, browser: true, type: true, unparam: true */
// (c) copyright 2011 carsales.com Ltd

(function ($, csn, undefined) {
	"use strict";
	var prototypeHtml = '<div class="csn-select"><div class="select-box"><a href="javascript:;" class="select-display"><span class="text">&nbsp;</span></a></div>' +
                   '<div class="select-menu" style="display: none"><div class="items-wrapper"><ul></ul></div></div></div>',
        idIncrement = 1,
        isKeyboardControl = false,
        openMenu = null,
        timer,
        selectedChars = "",
        keyCode = {
        	BACKSPACE: 8,
        	COMMA: 188,
        	CONTROL: 17,
        	DELETE: 46,
        	DOWN: 40,
        	END: 35,
        	ENTER: 13,
        	ESCAPE: 27,
        	HOME: 36,
        	INSERT: 45,
        	LEFT: 37,
        	PAGE_DOWN: 34,
        	PAGE_UP: 33,
        	PERIOD: 190,
        	RIGHT: 39,
        	SHIFT: 16,
        	SPACE: 32,
        	TAB: 9,
        	UP: 38
        },

        isMacintoshSafari = navigator.userAgent.match(/Macintosh;/i) && navigator.userAgent.match(/Safari/),

	// utilities
        makeNameFromId = function (id) {
        	return id.replace(/_|-/g, "$");
        },
        makeItemHtml = function (i, opt) {
        	var count = opt.getAttribute("data-count"), className = $.trim((opt.className ? opt.className : "") + (opt.selected ? " selected" : "")), source = opt.getAttribute("data-source");
        	// Using attributes rather than jQuery 'data' to reduce the amount of DOM manipulation.
        	return '<li data-index="' + i + '"' +
                (count ? (' data-count="' + count + '"') : "") +
                (source ? (' data-source="' + source + '"') : "") +
                (className.length ? ' class="' + className + '"' : '') +
                ' data-value="' + opt.value + '"><a href="javascript:;">' + $.trim(opt.text) + '</a></li>';
        },
        parseOptionItems = function (html, options, menu, index) {
        	var opt, i = 0, count = options.length, re = /\W+/g;
        	index = index || 0;
        	while (i < count) {
        		opt = options[i];

        		if (menu._options.addCssClassToItems) {
        			opt.className = opt.text.toLowerCase().replace(re, "-");
        		}

        		html.push(makeItemHtml(index, opt));
        		if (opt.selected) {
        			menu.selectedIndex = index;
        			menu._selectedValue = opt.value;
        		}
        		index += 1;
        		i += 1;
        	};

        	return index;
        },
        parseOptionGroups = function (html, options, menu) {
        	var index = 0;

        	options = $.grep(options, function (o) {
        		return o.nodeType === 1;
        	});

        	$.each(options, function (i, opt) {
        		var tagName = opt.tagName.toUpperCase(), label, items;

        		if (tagName === "OPTGROUP") {
        			items = opt.getElementsByTagName("option"), label;
        			html.push('<li class="optgroup">');
        			label = opt.getAttribute("label");
        			if (label.length) {
        				html.push('<span>' + label + '</span>');
        			}
        			if (items.length) {
        				html.push("<ul>");
        				index = parseOptionItems(html, items, menu, index);
        				html.push("</ul>");
        			}
        			html.push('</li>');
        		} else {
        			html.push(makeItemHtml(index, opt));
        			index++;
        		}
        	});

        	return html.join("");
        },
        setLabel = function (menu, element) {
        	var components = menu.components,
                label, labelText = "", labelWidth, displayWidth;

        	// If you don't want the label as a placeholder, don't specify the options in the logic below
        	if (menu._options.useLabelAsPlaceholder) {
        			label = element.prev("label").remove();
        			labelText = $.trim(label.eq(0).text());
        		} else if (menu._options.label) {
        			labelText = $.trim(menu._options.label);
        	}
        	components.label = label = $('<span class="label">' + labelText + '</span>').prependTo(components.selectDisplay).show();

        	if (menu._options.useEllipsis) {
        		if (components.label.height() <= 0) {
        			// create temporary copy
        			label = label.clone(true).css({ "display": "none", "visibility": "visible" }).appendTo("body");
        			label.css(csn.getTextCss(components.label));
        			// need width before removing temporary copy
        			labelWidth = label.outerWidth(true) + 2;
        			label.remove();
        		} else {
        			// get width of visible label
        			labelWidth = label.outerWidth(true) + 2;
        		}
        		displayWidth = parseInt(components.selectDisplay.css("width")) || components.selectDisplay.width();
        		components.textbox.width(displayWidth - labelWidth);
        	}
        },
        getKeyPressed = function (keyUsed) {
        	if (keyUsed >= 96 && keyUsed <= 106) {
        		// numeric keypad
        		keyUsed -= 48;
        	} else if (keyUsed === 110 || keyUsed === 190) {
        		// "."
        		keyUsed = 46;
        	} else if (keyUsed === 109) {
        		// "-"
        		keyUsed = 45;
        	}
        	return $.trim(String.fromCharCode(keyUsed));
        },
        getIndexMatchedByChars = function (chars, menu) {
        	var sourceItems = menu.components.items, index = menu.selectedIndex,
                findIndex = function (i, max) {
                	while (i < max) {
                		if ($.trim(sourceItems.eq(i).text()).substr(0, chars.length) === chars) {
                			return i;
                		}
                		i++;
                	}
                	return -1;
                };

        	// Same letters again? Probably looking for next in list
        	if ((!isKeyboardControl || chars.length === 1) && $.trim(menu.components.textbox.text()).substr(0, chars.length) === chars) {
        		index += 1;
        	}

        	index = findIndex(index, menu._itemCount);

        	if (index !== -1) {
        		return index;
        	}

        	index = findIndex(0, menu.selectedIndex);
        	return index;
        },

        selectMenuItem = function (menu, index) {
        	var option, text, textWidth, value, direction, components = menu.components;

        	if (!isNaN(index)) {
        		direction = index > menu.selectedIndex ? 1 : (index < menu.selectedIndex ? -1 : 0);
        		menu.selectedIndex = Number(index);
        	}

        	if (menu.selectedIndex > menu._itemCount) {
        		menu.selectedIndex = menu._itemCount;
        	}

        	if (menu.selectedIndex >= 0) {
        		option = components.selectedItem = components.items.eq(menu.selectedIndex).addClass("selected");
        		if (menu.isOpen) {
        			option.children("a").focus();
        		}
        		text = $.trim(option.text());
        		value = option.attr("data-value");

        		components.textbox.text(text);
        		if (menu._options.useEllipsis) {
        			// Switch count around to make ellipsis then switch back
        			text = text.replace(/^([^(]+)(\(\d+\))\s*$/, "$2 $1");
        			components.textbox.text(text);
        			textWidth = parseInt(components.textbox.css("width")) || components.textbox.width();
        			csn.addEllipsis(components.textbox, textWidth);
        			text = components.textbox.text().replace(/^(\(\d+\))([^)]+)$/, "$2 $1");
        			components.textbox.text(text);
        		}
        		menu._selectedValue = value;
        	} else {
        		components.selectedItem = $({});
        		components.textbox.text(menu._options.defaultText);
        		menu._selectedValue = "";
        	}

        	if (components.customValue.length) {
        		components.customValue.val("");
        	}

        	if (menu.isOpen) {
        		scrollMenu(menu, direction);
        	}
        },

        scrollMenu = function (menu, direction) {
        	var itemTop = 0, itemHeight = menu.components.itemHeight, menuHeight = menu.components.itemsWrapper.innerHeight(), distance, isHidden;

        	if (menu.selectedIndex >= 0) {
        		// Safari calculates distance from top of scrollPane differently
        		itemTop = !isMacintoshSafari ? menu.components.selectedItem.position().top : itemHeight * menu.selectedIndex;
        	}

        	// menuTop is always zero, based on the current position of the scrollPane
        	isHidden = (itemTop < 0) || (itemTop >= (menuHeight - itemHeight));
        	switch (direction) {
        		case 1:
        			distance = itemTop - (menuHeight - itemHeight);
        			break;
        		case -1:
        			distance = itemTop - itemHeight;
        			break;
        		default:
        			distance = itemTop - (menuHeight - itemHeight * Math.round(menuHeight / itemHeight / 2));
        			break;
        	}

        	if (isHidden) {
        		menu.components.scrollPane.scrollTop(distance);
        	}
        },
        setKeyboardTimer = function (retainKeyControl, interval) {
        	window.clearTimeout(timer);
        	timer = window.setTimeout(function () {
        		isKeyboardControl = retainKeyControl;
        		selectedChars = "";
        	}, interval);
        },
        checkOpenMenu = function (menu) {
        	if (openMenu !== null && openMenu !== menu) {
        		if (openMenu.isDirty) {
        			openMenu.components.selectDisplay.trigger("change.mps");
        			openMenu.isDirty = false;
        		}
        		openMenu.close();
        	}
        },

	// handlers
        selectHandler = function (event) {
        	var menu = event.data.menu;
        	isKeyboardControl = false;

        	checkOpenMenu(menu);
        	if (!menu.disabled) {
        		event.stopPropagation();

        		if (!menu.isOpen) {
        			menu.open();
        			menu.components.selectDisplay[0].focus();
        		} else {
        			menu.close();
        		}
        	} else {
        		event.preventDefault();
        		return false;
        	}
        },
        menuEventHandler = function (event) {
        	var menu = event.data.menu, components = menu.components, target = $(event.currentTarget), related = $(event.relatedTarget),
                parent = target.parent(), keyUsed = event.which, type = event.type;

        	switch (type) {
        		case "mouseenter":
        			if (!isKeyboardControl) {
        				components.selectedItem.removeClass("selected");
        				if (menu._hoverIndex >= 0) {
        					components.items.eq(menu._hoverIndex).removeClass("hover");
        				}
        				parent.addClass("hover");
        				menu._hoverIndex = parseInt(parent.attr("data-index"), 10);
        			}
        			break;
        		case "mouseleave":
        			if (!isKeyboardControl) {
        				if (related.is("li,a")) {
        					parent.removeClass("hover");
        				}
        				window.clearTimeout(timer);
        			}
        			break;
        		case "mousedown":
        			if (event.which === 1) {
        				menu.moveTo("index", menu._hoverIndex);
        				menu.close();
        				components.selectDisplay.trigger("change.mps");
        				menu.isDirty = false;
        			}
        			break;
        		case "blur":
        			// Need to capture this event to normalise open/close behaviour.
        			if (openMenu === null && menu.isDirty) {
        				components.selectDisplay.trigger("change.mps");
        				menu.isDirty = false;
        			}
        			break;
        		case "keydown":
        			components.items.eq(menu._hoverIndex).removeClass("hover");

        			if (menu.isOpen && target.is(".select-display")) {
        				components.selectedItem.removeClass("selected");
        				menu.selectedIndex = menu._hoverIndex;
        			}
        			isKeyboardControl = true;
        			setKeyboardTimer(false, 300);
        			switch (keyUsed) {
        				case keyCode.ESCAPE:
        					window.clearTimeout(timer);
        					menu.close();
        					return;
        				case keyCode.DOWN:
        				case keyCode.RIGHT:
        					menu.moveTo("down");
        					break;
        				case keyCode.UP:
        				case keyCode.LEFT:
        					if (menu._hoverIndex === 0 && menu._options.allowCustomValue) {
        						components.customValue.focus();
        						components.items.eq(0).removeClass("hover");
        					} else {
        						menu.moveTo("up");
        					}
        					break;
        				case keyCode.PAGE_UP:
        				case keyCode.HOME:
        					menu.moveTo("first");
        					break;
        				case keyCode.PAGE_DOWN:
        				case keyCode.END:
        					menu.moveTo("last");
        					break;
        				case keyCode.ENTER:
        					window.clearTimeout(timer);
        					if (menu.isOpen) {
        						selectMenuItem(menu, menu._hoverIndex);
        						menu.close();
        					}
        					components.selectDisplay.trigger("change.mps");
        					menu.isDirty = false;
        					break;
        				case keyCode.TAB:
        					window.clearTimeout(timer);
        					if (menu.isDirty) {
        						// If item isn't selected (confirmed)
        						if (menu._hoverIndex >= 0) {
        							selectMenuItem(menu, menu._hoverIndex);
        						}
        						components.selectDisplay.trigger("change.mps");
        						menu.isDirty = false;
        					}
        					if (menu.isOpen) {
        						menu.close();
        					}
        					return;
        				default:
        					selectedChars += getKeyPressed(keyUsed);
        					// What if same key pressed e.g. "M" then "M" in rapid succession
        					if (selectedChars.charAt(1) === selectedChars.charAt(0)) {
        						selectedChars = selectedChars.charAt(0);
        						setKeyboardTimer(true, 50);
        					} else {
        						setKeyboardTimer(false, 450);
        					}
        					menu.moveTo("char", selectedChars);
        					break;
        			}
        			menu._hoverIndex = menu.selectedIndex;
        			break;
        		default:
        			break;
        	}
        	event.preventDefault();
        	event.stopPropagation();
        },
        customValueHandler = function (event) {
        	var menu = event.data.menu, field = $(event.target), value = field.val(), keyUsed = event.which, keyPressed;

        	switch (keyUsed) {
        		case keyCode.ENTER:
        		case keyCode.TAB:
        			menu.val(value);
        			menu.close();
        			break;
        		case keyCode.ESCAPE:
        			menu.close();
        			break;
        		case keyCode.DOWN:
        			menuEventHandler(event);
        			break;
        		default:
        			if (!event.metaKey && (keyUsed >= 32 && keyUsed <= 255)) {
        				keyPressed = getKeyPressed(keyUsed);
        				switch (menu._options.customValueType) {
        					case "numeric":
        						// check if using numeric keypad
        						if (!(/[0-9\.\-]/.test(keyPressed))) {
        							event.preventDefault();
        						}
        						break;
        					case "alphanumeric":
        						if (!(/[A-Za-z0-9\.\-]/.test(keyPressed))) {
        							event.preventDefault();
        						}
        						break;
        					case "alpha":
        						if (!(/[A-Za-z]/.test(keyPressed))) {
        							event.preventDefault();
        						}
        						break;
        					default:
        						break;
        				}
        			}
        			break;
        	}
        	event.stopPropagation();
        },

	// Classes
        MenuComponents = function (container) {
        	var selectDisplay = container.find(".select-display"), menu = container.find(".select-menu");
        	this.container = container;
        	this.selectBox = selectDisplay.parent();
        	this.selectBoxWidth = 0;
        	this.selectDisplay = selectDisplay;
        	this.menu = menu.appendTo("body");
        	this.borderMask = null;
        	this.label = null;
        	this.textbox = selectDisplay.children(".text");
        	this.customValue = $({});
        	this.itemsWrapper = menu.children(".items-wrapper").css("overflow", "auto");
        	this.selectedItem = $({});
        	this.list = this.itemsWrapper.children("ul,ol");
        	this.items = null;
        	this.itemHeight = 0;
        	this.itemsWrapperCssHeight = 0;
        	this.scrollPane = null;
        	this.zIndex = undefined;
        },
        MultiPurposeSelect = function (element, options) {
        	var components;

        	this.resetMenu = true;
        	this.selectedIndex = -1;
        	this.disabled = false;
        	this.isOpen = false;
        	this.isDirty = false;
        	this._options = options;
        	this._itemCount = 0;
        	this._selectedValue = "";
        	this._hoverIndex = -1;

        	if (element.is("select,ul,ol")) {
        		components = new MenuComponents($(prototypeHtml).insertAfter(element));
        	} else {
        		components = new MenuComponents(element);
        	}

        	components.container.css({ "position": "relative" }).addClass("closed");
        	components.selectBox.css({ "display": "block", "z-index": "0", "overflow": "hidden", "cursor": "pointer", "position": "absolute" });
        	components.selectDisplay.css("display", "block");
        	components.menu.css({ "position": "absolute", "left": "0" });
        	components.borderMask = $("<hr/>").appendTo(components.menu).css(
                { "position": "absolute",
                	"left": "0",
                	"display": "none",
                	"border-color": components.selectBox.css("background-color"),
                	"border-style": "solid",
                	"border-width": "1px 0 0",
                	"height": "1px",
                	"margin": "0"
                });

        	this.components = components;
        },

	// widget creation
        init = function (element, options) {
        	var menu = new MultiPurposeSelect(element, options), components = menu.components, source = element[0], id = "MultiPurpose_" + (element.attr("id") || idIncrement);

        	if (!components.container.attr("id")) {
        		components.container.attr("id", id);
        	}

        	if (options.disabled || source.disabled) {
        		menu.disable();
        	}

        	if (source.className) {
        		components.container.addClass(source.className);
        	}

        	if (options.menuCssClass) {
        		components.menu.addClass(options.menuCssClass);
        	}

        	setLabel(menu, element);

        	if (element.is("select")) {
        		element.hide();
        		menu.loadItems(element.children());
        	} else if (element.is("ul,ol")) {
        		menu.loadItems(element);
        	} else {
        		menu.loadItems();
        	}

        	// events
        	components.selectDisplay.bind("keydown.mps blur.mps", { menu: menu, components: components }, menuEventHandler);
        	components.selectBox.bind("click.mps", { menu: menu, components: components }, selectHandler);

        	$("html").bind("click.mps", function (ev) {
        		var t = $(ev.target), src = t.parents(".select-menu");
        		if (!src.length && openMenu) {
        			openMenu.close();
        		}
        	});

        	$(window).resize(function () {
        		if (openMenu) {
        			openMenu.close();
        		}
        	});

        	element.data("multiPurposeSelect", menu);
        };

	// Public methods 
	$.extend(MultiPurposeSelect.prototype, {
		open: function () {
			var components = this.components, viewportHeight = $(window).height(), viewportTop = $(document).scrollTop(),
                containerPosition = components.container.offset(), menuTop, menuHeight, displayHeight, listWidth, gutterWidth;

			if (!components.itemsWrapperCssHeight) {
				components.itemsWrapperCssHeight = parseInt(components.itemsWrapper.css("height"), 10);
			}


			if (components.menu.is(":hidden")) {
				checkOpenMenu();
				$.fn.multiPurposeSelect.openMenu = openMenu = this;

				if (isNaN(components.zIndex)) {
					components.zIndex = parseInt(components.container.css("zIndex")) || 100;
					components.container.css("zIndex", components.zIndex);
					components.menu.css("zIndex", (components.zIndex + 100));
					components.borderMask.css("zIndex", (components.zIndex + 102));
				}

				this.isOpen = true;

				displayHeight = components.selectBox.outerHeight();
				components.menu.css({ "top": (containerPosition.top + displayHeight + "px"), "left": (containerPosition.left + "px") }).show();
				components.borderMask.css("top", "0").show();

				components.container.removeClass("closed").addClass("open").css("z-index", (components.zIndex + 1));

				if (!components.selectBoxWidth) {
					components.selectBoxWidth = components.selectBox.innerWidth();
					components.borderMask.width(components.selectBoxWidth);
				}

				if (this.resetMenu) {
					components.menu.width("auto");
					components.itemsWrapper.css({ "width": "auto", "visibility": "hidden", "overflow": "visible" });
					listWidth = components.list.width();

					gutterWidth = parseInt(components.list.css("padding-right"), 10) || 0;
					if (listWidth < components.selectBoxWidth) {
						listWidth = components.selectBoxWidth;
						components.list.width(listWidth - gutterWidth);
					} else {
						listWidth += gutterWidth;
					}
					components.itemsWrapper.css({ "overflow": "auto", "visibility": "visible", "width": (listWidth + "px") });
					components.menu.width(listWidth);

					menuHeight = components.list.innerHeight();
					menuHeight = menuHeight > components.itemsWrapperCssHeight ? components.itemsWrapperCssHeight : menuHeight;
					components.menu.height(menuHeight);

					if (!isMacintoshSafari && $.fn.jScrollPane) {
						components.itemsWrapper.removeData("jsp");
						components.itemsWrapper.jScrollPane({ enableKeyboardNavigation: false, mouseWheelSpeed: 250 });
						components.scrollPane = components.itemsWrapper.data("jsp");
						components.scrollPane.scrollTop = function (value) {
							if (isNaN(value)) {
								return this.getContentPositionY();
							} else {
								this.scrollToY(value);
							}
						};
					} else {
						components.itemsWrapper.css({ "overflow-x": "hidden", "overflow-y": "auto" });
						components.scrollPane = components.itemsWrapper;
					}

					if (!components.itemHeight) {
						components.itemHeight = components.items.eq(0).outerHeight();
					}

					this.resetMenu = false;
				} else {
					menuHeight = components.menu.height();
				}

				if ((containerPosition.top - viewportTop > menuHeight) && ((containerPosition.top - viewportTop + menuHeight + displayHeight) > viewportHeight)) {
					menuHeight += (parseInt(components.menu.css("padding-top")) || 0) + (parseInt(components.menu.css("padding-bottom")) || 0);
					menuTop = containerPosition.top - menuHeight;
					components.menu.css("top", (menuTop + "px")).addClass("inverted");
					components.borderMask.css("top", (menuHeight + "px"));
				}

				scrollMenu(this);
			}
		},
		close: function () {
			var components = this.components;

			if (components.selectDisplay.is(":visible")) {
				components.selectDisplay[0].focus();
			}
			components.menu.hide();
			components.borderMask.hide();
			components.container.removeClass("open inverted").addClass("closed").css("z-index", components.zIndex);
			components.items.eq(this._hoverIndex).removeClass("hover");
			this.isOpen = false;
			this._hoverIndex = -1;
			selectedChars = "";
			$.fn.multiPurposeSelect.openMenu = openMenu = null;
		},
		enable: function () {
			this.components.container.removeClass("disabled");
			this.disabled = false;
		},
		disable: function () {
			this.components.container.addClass("disabled");
			if (this.components.items) {
				selectMenuItem(this, 0);
			}
			this.disabled = true;
		},
		moveTo: function (action, value) {
			var components = this.components, index = this.selectedIndex;

			switch (action) {
				case "up":
					if (index > 0) {
						index -= 1;
					}
					break;
				case "down":
					if (index < this._itemCount - 1) {
						index++;
					}
					break;
				case "first":
					index = 0;
					break;
				case "last":
					index = this._itemCount - 1;
					break;
				case "index":
					index = Number(value) || 0;
					break;
				case "char":
					// If movement results in -1, then keep to the currently selected item
					index = getIndexMatchedByChars(value, this);
					if (index === -1) {
						index = this.selectedIndex;
					}
					break;
				default:
					break;
			}

			if (index !== this.selectedIndex) {
				this.isDirty = true;
				components.selectedItem.removeClass("selected");
				selectMenuItem(this, index);
			}
		},
		val: function (newValue) {
			var components = this.components, index;
			if (newValue === undefined) {
				// Returns the current value
				return this._selectedValue;
			} else {
				components = this.components;
				// TODO: Is this too inefficient? Perhaps we could change the focus so only one method handles value changes
				index = components.items.filter("[data-value='" + newValue + "']").attr("data-index");
				if (!isNaN(index)) {
					this.moveTo("index", index);
					components.customValue.val("");
				} else if (this._options.allowCustomValue) {
					this.reset();
					this._selectedValue = newValue;
					components.customValue.val(newValue);
					components.textbox.text(csn.formatNumber(this._options.customValueFormat, newValue));
				}
				components.selectDisplay.trigger("change.mps");
				this.isDirty = false;
			}
		},
		text: function () {
			return this.components.selectedItem.text();
		},
		isGroupData: function (source) {
			var dataSource = this.components.selectedItem.attr('data-source');
			return dataSource && dataSource !== source;
		},
		loadItems: function (items) {
			var html = [], components = this.components, id = components.container.attr("id");
			this.selectedIndex = 0;

			if (items !== undefined) {

				if (this._options.allowCustomValue) {
					html.push('<div class="custom-value">');
					if (this._options.customValueLabel) {
						html.push('<span class="label">' + this._options.customValueLabel + '</span>');
					}
					html.push('<input type="text" id="' + id + '_CustomValue" class="value"/></div>');
					components.itemsWrapper.before(html.join(""));
					components.customValue = $("#" + id + "_CustomValue").bind("keydown.msp", { menu: this }, customValueHandler);
					html = [];
				}

				if (!items.filter("optgroup").length) {
					parseOptionItems(html, items, this);
					components.list.append(html.join(""));
				} else if (items.eq(0).is("option")) {
					parseOptionGroups(html, items, this);
					components.list.append(html.join(""));
				} else if (items.is("ul,ol")) {
					components.list = items;
				}
			}

			components.items = components.list.find("li:not(.optgroup)");
			this._itemCount = components.items.length;
			components.items.find("a").bind("mousedown.mps keydown.mps mouseenter.mps mouseleave.mps", { menu: this, components: components }, menuEventHandler);
			components.list.addClass("main-list");

			selectMenuItem(this);
		},
		clearItems: function (message) {
			this.components.itemsWrapper.removeClass("jspScrollable").empty();
			this.components.list = $("<ul></ul>").appendTo(this.components.itemsWrapper);
			this.resetMenu = true;
			if (message) {
				this.components.textbox.text(message);
			}
		},
		reset: function () {
			this.components.selectedItem.removeClass("selected");
			this.components.items.find(".hover").removeClass("hover");
			selectMenuItem(this, -1);
		},
		showCount: function () {
			this.components.items.each(function () {
				var item = $(this), count = item.attr("data-count");
				if (count) {
					item.children("a").append(" <span>(" + count + ")</span>");
				}
			});
		},
		bind: function (eventName, eventHandler) {
			this.components.selectDisplay.bind(eventName + ".mps", eventHandler);
		},
		itemCount: function () {
			return this._itemCount;
		}
	});

	$.fn.multiPurposeSelect = function (options) {
		var settings = $.extend({}, $.fn.multiPurposeSelect.defaults, options);
		this.each(function () {
			init($(this), settings);
		});
		return this;
	}

	$.fn.multiPurposeSelect.openMenu = null;

	$.fn.multiPurposeSelect.defaults = {
		useLabelAsPlaceholder: false,
		label: "",
		allowCustomValue: false,
		customValueFormat: "",
		customValueLabel: "",
		customValueType: "",
		defaultText: "",
		menuCssClass: "",
		addCssClassToItems: false,
		disabled: false,
		useEllipsis: false
	};

} (jQuery, window.CSN));
