﻿/*
* jQuery MultiSelect UI Widget 1.10
* Copyright (c) 2011 Eric Hynds
*
* http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
*
* Depends:
*   - jQuery 1.4.2+
*   - jQuery UI 1.8 widget factory
*
* Optional:
*   - jQuery UI effects
*   - jQuery UI position utility
*
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.php
*   http://www.gnu.org/licenses/gpl.html
*
*/
(function (d) { var i = 0; d.widget("ech.multiselect", { options: { header: !0, height: 175, minWidth: 225, classes: "", checkAllText: "Check all", uncheckAllText: "Uncheck all", noneSelectedText: "Select options", selectedText: "# selected", selectedList: 0, show: "", hide: "", autoOpen: !1, multiple: !0, position: {} }, _create: function () { var a = this.element.hide(), b = this.options; this.speed = d.fx.speeds._default; this._isOpen = !1; a = (this.button = d('<button type="button"><span class="ui-icon ui-icon-triangle-2-n-s"></span></button>')).addClass("ui-multiselect ui-widget ui-state-default ui-corner-all").addClass(b.classes).attr({ title: a.attr("title"), "aria-haspopup": !0, tabIndex: a.attr("tabIndex") }).insertAfter(a); (this.buttonlabel = d("<span />")).html(b.noneSelectedText).appendTo(a); var a = (this.menu = d("<div />")).addClass("ui-multiselect-menu ui-widget ui-widget-content ui-corner-all").addClass(b.classes).insertAfter(a), c = (this.header = d("<div />")).addClass("ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix").appendTo(a); (this.headerLinkContainer = d("<ul />")).addClass("ui-helper-reset").html(function () { return b.header === !0 ? '<li><a class="ui-multiselect-all" href="#"><span class="ui-icon ui-icon-check"></span><span>' + b.checkAllText + '</span></a></li><li><a class="ui-multiselect-none" href="#"><span class="ui-icon ui-icon-closethick"></span><span>' + b.uncheckAllText + "</span></a></li>" : typeof b.header === "string" ? "<li>" + b.header + "</li>" : "" }).append('<li class="ui-multiselect-close"><a href="#" class="ui-multiselect-close"><span class="ui-icon ui-icon-circle-close"></span></a></li>').appendTo(c); (this.checkboxContainer = d("<ul />")).addClass("ui-multiselect-checkboxes ui-helper-reset").appendTo(a); this._bindEvents(); this.refresh(!0); b.multiple || a.addClass("ui-multiselect-single") }, _init: function () { this.options.header === !1 && this.header.hide(); this.options.multiple || this.headerLinkContainer.find(".ui-multiselect-all, .ui-multiselect-none").hide(); this.options.autoOpen && this.open(); this.element.is(":disabled") && this.disable() }, refresh: function (a) { var b = this.options, c = this.menu, e = this.checkboxContainer, h = [], f = [], g = this.element.attr("id") || i++; this.element.find("option").each(function (a) { d(this); var e = this.parentNode, c = this.innerHTML, i = this.value, a = this.id || "ui-multiselect-" + g + "-option-" + a, j = this.disabled, l = this.selected, k = ["ui-corner-all"]; e.tagName.toLowerCase() === "optgroup" && (e = e.getAttribute("label"), d.inArray(e, h) === -1 && (f.push('<li class="ui-multiselect-optgroup-label"><a href="#">' + e + "</a></li>"), h.push(e))); j && k.push("ui-state-disabled"); l && !b.multiple && k.push("ui-state-active"); f.push('<li class="' + (j ? "ui-multiselect-disabled" : "") + '">'); f.push('<label for="' + a + '" class="' + k.join(" ") + '">'); f.push('<input id="' + a + '" name="multiselect_' + g + '" type="' + (b.multiple ? "checkbox" : "radio") + '" value="' + i + '" title="' + c + '"'); l && (f.push(' checked="checked"'), f.push(' aria-selected="true"')); j && (f.push(' disabled="disabled"'), f.push(' aria-disabled="true"')); f.push(" /><span>" + c + "</span></label></li>") }); e.html(f.join("")); this.labels = c.find("label"); this._setButtonWidth(); this._setMenuWidth(); this.button[0].defaultValue = this.update(); a || this._trigger("refresh") }, update: function () { var a = this.options, b = this.labels.find("input"), c = b.filter(":checked"), e = c.length, a = e === 0 ? a.noneSelectedText : d.isFunction(a.selectedText) ? a.selectedText.call(this, e, b.length, c.get()) : /\d/.test(a.selectedList) && a.selectedList > 0 && e <= a.selectedList ? c.map(function () { return this.title }).get().join(", ") : a.selectedText.replace("#", e).replace("#", b.length); this.buttonlabel.html(a); return a }, _bindEvents: function () { function a() { b[b._isOpen ? "close" : "open"](); return !1 } var b = this, c = this.button; c.find("span").bind("click.multiselect", a); c.bind({ click: a, keypress: function (a) { switch (a.which) { case 27: case 38: case 37: b.close(); break; case 39: case 40: b.open() } }, mouseenter: function () { c.hasClass("ui-state-disabled") || d(this).addClass("ui-state-hover") }, mouseleave: function () { d(this).removeClass("ui-state-hover") }, focus: function () { c.hasClass("ui-state-disabled") || d(this).addClass("ui-state-focus") }, blur: function () { d(this).removeClass("ui-state-focus") } }); this.header.delegate("a", "click.multiselect", function (a) { if (d(this).hasClass("ui-multiselect-close")) b.close(); else b[d(this).hasClass("ui-multiselect-all") ? "checkAll" : "uncheckAll"](); a.preventDefault() }); this.menu.delegate("li.ui-multiselect-optgroup-label a", "click.multiselect", function (a) { a.preventDefault(); var c = d(this), f = c.parent().nextUntil("li.ui-multiselect-optgroup-label").find("input:visible:not(:disabled)"), g = f.get(), c = c.parent().text(); b._trigger("beforeoptgrouptoggle", a, { inputs: g, label: c }) !== !1 && (b._toggleChecked(f.filter(":checked").length !== f.length, f), b._trigger("optgrouptoggle", a, { inputs: g, label: c, checked: g[0].checked })) }).delegate("label", "mouseenter.multiselect", function () { d(this).hasClass("ui-state-disabled") || (b.labels.removeClass("ui-state-hover"), d(this).addClass("ui-state-hover").find("input").focus()) }).delegate("label", "keydown.multiselect", function (a) { a.preventDefault(); switch (a.which) { case 9: case 27: b.close(); break; case 38: case 40: case 37: case 39: b._traverse(a.which, this); break; case 13: d(this).find("input")[0].click() } }).delegate('input[type="checkbox"], input[type="radio"]', "click.multiselect", function (a) { var c = d(this), f = this.value, g = this.checked, i = b.element.find("option"); this.disabled || b._trigger("click", a, { value: f, text: this.title, checked: g }) === !1 ? a.preventDefault() : (c.attr("aria-selected", g), i.each(function () { if (this.value === f) this.selected = g; else if (!b.options.multiple) this.selected = !1 }), b.options.multiple || (b.labels.removeClass("ui-state-active"), c.closest("label").toggleClass("ui-state-active", g), b.close()), setTimeout(d.proxy(b.update, b), 10)) }); d(document).bind("mousedown.multiselect", function (a) { b._isOpen && !d.contains(b.menu[0], a.target) && !d.contains(b.button[0], a.target) && a.target !== b.button[0] && b.close() }); d(this.element[0].form).bind("reset.multiselect", function () { setTimeout(function () { b.update() }, 10) }) }, _setButtonWidth: function () { var a = this.element.outerWidth(), b = this.options; if (/\d/.test(b.minWidth) && a < b.minWidth) a = b.minWidth; this.button.width(a) }, _setMenuWidth: function () { var a = this.menu, b = this.button.outerWidth() - parseInt(a.css("padding-left"), 10) - parseInt(a.css("padding-right"), 10) - parseInt(a.css("border-right-width"), 10) - parseInt(a.css("border-left-width"), 10); a.width(b || this.button.outerWidth()) }, _traverse: function (a, b) { var c = d(b), e = a === 38 || a === 37, c = c.parent()[e ? "prevAll" : "nextAll"]("li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)")[e ? "last" : "first"](); c.length ? c.find("label").trigger("mouseover") : (c = this.menu.find("ul:last"), this.menu.find("label")[e ? "last" : "first"]().trigger("mouseover"), c.scrollTop(e ? c.height() : 0)) }, _toggleCheckbox: function (a, b) { return function () { !this.disabled && (this[a] = b); b ? this.setAttribute("aria-selected", !0) : this.removeAttribute("aria-selected") } }, _toggleChecked: function (a, b) { var c = b && b.length ? b : this.labels.find("input"), e = this; c.each(this._toggleCheckbox("checked", a)); this.update(); var h = c.map(function () { return this.value }).get(); this.element.find("option").each(function () { !this.disabled && d.inArray(this.value, h) > -1 && e._toggleCheckbox("selected", a).call(this) }) }, _toggleDisabled: function (a) { this.button.attr({ disabled: a, "aria-disabled": a })[a ? "addClass" : "removeClass"]("ui-state-disabled"); this.menu.find("input").attr({ disabled: a, "aria-disabled": a }).parent()[a ? "addClass" : "removeClass"]("ui-state-disabled"); this.element.attr({ disabled: a, "aria-disabled": a }) }, open: function () { var a = this.button, b = this.menu, c = this.speed, e = this.options; if (!(this._trigger("beforeopen") === !1 || a.hasClass("ui-state-disabled") || this._isOpen)) { var h = b.find("ul:last"), f = e.show, g = a.position(); d.isArray(e.show) && (f = e.show[0], c = e.show[1] || this.speed); h.scrollTop(0).height(e.height); d.ui.position && !d.isEmptyObject(e.position) ? (e.position.of = e.position.of || a, b.show().position(e.position).hide().show(f, c)) : b.css({ top: g.top + a.outerHeight(), left: g.left }).show(f, c); this.labels.eq(0).trigger("mouseover").trigger("mouseenter").find("input").trigger("focus"); a.addClass("ui-state-active"); this._isOpen = !0; this._trigger("open") } }, close: function () { if (this._trigger("beforeclose") !== !1) { var a = this.options, b = a.hide, c = this.speed; d.isArray(a.hide) && (b = a.hide[0], c = a.hide[1] || this.speed); this.menu.hide(b, c); this.button.removeClass("ui-state-active").trigger("blur").trigger("mouseleave"); this._isOpen = !1; this._trigger("close") } }, enable: function () { this._toggleDisabled(!1) }, disable: function () { this._toggleDisabled(!0) }, checkAll: function () { this._toggleChecked(!0); this._trigger("checkAll") }, uncheckAll: function () { this._toggleChecked(!1); this._trigger("uncheckAll") }, getChecked: function () { return this.menu.find("input").filter(":checked") }, destroy: function () { d.Widget.prototype.destroy.call(this); this.button.remove(); this.menu.remove(); this.element.show(); return this }, isOpen: function () { return this._isOpen }, widget: function () { return this.menu }, _setOption: function (a, b) { var c = this.menu; switch (a) { case "header": c.find("div.ui-multiselect-header")[b ? "show" : "hide"](); break; case "checkAllText": c.find("a.ui-multiselect-all span").eq(-1).text(b); break; case "uncheckAllText": c.find("a.ui-multiselect-none span").eq(-1).text(b); break; case "height": c.find("ul:last").height(parseInt(b, 10)); break; case "minWidth": this.options[a] = parseInt(b, 10); this._setButtonWidth(); this._setMenuWidth(); break; case "selectedText": case "selectedList": case "noneSelectedText": this.options[a] = b; this.update(); break; case "classes": c.add(this.button).removeClass(this.options.classes).addClass(b) } d.Widget.prototype._setOption.apply(this, arguments) } }) })(jQuery);
/*
* jQuery MultiSelect UI Widget Filtering Plugin 1.3pre
* Copyright (c) 2011 Eric Hynds
*
* http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
*
* Depends:
*   - jQuery UI MultiSelect widget
*
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.php
*   http://www.gnu.org/licenses/gpl.html
*
*/
(function ($) {
	var rEscape = /[\-\[\]{}()*+?.,\\^$|#\s]/g;

	$.widget("ech.multiselectfilter", {

		options: {
			label: "Filter:",
			width: null, /* override default width set in css file (px). null will inherit */
			placeholder: "Enter keywords"
		},

		_create: function () {
			var self = this,
				opts = this.options,
				instance = (this.instance = $(this.element).data("multiselect")),

			// store header; add filter class so the close/check all/uncheck all links can be positioned correctly
				header = (this.header = instance.menu.find(".ui-multiselect-header").addClass("ui-multiselect-hasfilter")),

			// wrapper elem
				wrapper = (this.wrapper = $('<div class="ui-multiselect-filter">' + (opts.label.length ? opts.label : '') + '<input placeholder="' + opts.placeholder + '" type="search"' + (/\d/.test(opts.width) ? 'style="width:' + opts.width + 'px"' : '') + ' /></div>').prependTo(this.header));

			// reference to the actual inputs
			this.inputs = instance.menu.find('input[type="checkbox"], input[type="radio"]');

			// build the input box
			this.input = wrapper
			.find("input")
			.bind({
				keydown: function (e) {
					// prevent the enter key from submitting the form / closing the widget
					if (e.which === 13) {
						e.preventDefault();
					}
				},
				keyup: $.proxy(self._handler, self),
				click: $.proxy(self._handler, self)
			});

			// cache input values for searching
			this.updateCache();

			// rewrite internal _toggleChecked fn so that when checkAll/uncheckAll is fired,
			// only the currently filtered elements are checked
			instance._toggleChecked = function (flag, group) {
				var $inputs = (group && group.length) ?
						group :
						this.labels.find('input'),

					_self = this,

				// do not include hidden elems if the menu isn't open.
					selector = self.instance._isOpen ?
						":disabled, :hidden" :
						":disabled";

				$inputs = $inputs.not(selector).each(this._toggleCheckbox('checked', flag));

				// update text
				this.update();

				// figure out which option tags need to be selected
				var values = $inputs.map(function () {
					return this.value;
				}).get();

				// select option tags
				this.element
					.find('option')
					.filter(function () {
						if (!this.disabled && $.inArray(this.value, values) > -1) {
							_self._toggleCheckbox('selected', flag).call(this);
						}
					});
			};

			// rebuild cache when multiselect is updated
			$(document).bind("multiselectrefresh", function () {
				self.updateCache();
				self._handler();
			});
		},

		// thx for the logic here ben alman
		_handler: function (e) {
			var term = $.trim(this.input[0].value.toLowerCase()),

			// speed up lookups
				rows = this.rows, inputs = this.inputs, cache = this.cache;

			if (!term) {
				rows.show();
			} else {
				rows.hide();

				var regex = new RegExp(term.replace(rEscape, "\\$&"), 'gi');

				this._trigger("filter", e, $.map(cache, function (v, i) {
					if (v.search(regex) !== -1) {
						rows.eq(i).show();
						return inputs.get(i);
					}

					return null;
				}));
			}

			// show/hide optgroups
			this.instance.menu.find(".ui-multiselect-optgroup-label").each(function () {
				var $this = $(this);
				$this[$this.nextUntil('.ui-multiselect-optgroup-label').filter(':visible').length ? 'show' : 'hide']();
			});
		},

		updateCache: function () {
			// each list item
			this.rows = this.instance.menu.find(".ui-multiselect-checkboxes li:not(.ui-multiselect-optgroup-label)");

			// cache
			this.cache = this.element.children().map(function () {
				var self = $(this);

				// account for optgroups
				if (this.tagName.toLowerCase() === "optgroup") {
					self = self.children();
				}

				return self.map(function () {
					return this.innerHTML.toLowerCase();
				}).get();
			}).get();
		},

		widget: function () {
			return this.wrapper;
		},

		destroy: function () {
			$.Widget.prototype.destroy.call(this);
			this.input.val('').trigger("keyup");
			this.wrapper.remove();
		}
	});
})(jQuery);
/*
* jQuery sprintf - perl based functionallity for sprintf and friends.
*
* Copyright © 2008 Carl Fürstenberg
*
* Released under GPL, BSD, or MIT license.
* ---------------------------------------------------------------------------
*  GPL:
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (c) The Regents of the University of California.
* All rights reserved.
*
* ---------------------------------------------------------------------------
*  BSD:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
°* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* 
* ---------------------------------------------------------------------------
*  MIT:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* 
* ---------------------------------------------------------------------------
*
*  Version: 0.0.6
*/

/** 
* Inserts the arguments into the format and returns the formated string
*
* @example alert($.vsprintf( "%s %s", [ "Hello", "world" ] ));
*
* @param String format the format to use
* @param Array args the arguments to insert into the format
* @return String the formated string
*/
jQuery.vsprintf = function jQuery_vsprintf(format, args) {
	if (format == null) {
		throw "Not enough arguments for vsprintf";
	}
	if (args == null) {
		args = [];
	}

	function _sprintf_format(type, value, flags) {

		// Similar to how perl printf works
		if (value == undefined) {
			if (type == 's') {
				return '';
			} else {
				return '0';
			}
		}

		var result;
		var prefix = '';
		var fill = '';
		var fillchar = ' ';
		if (flags['short'] || flags['long'] || flags['long_long']) {
			/* This is pretty ugly, but as JS ignores bit lengths except 
			* somewhat when working with bit operators. 
			* So we fake a bit :) */
			switch (type) {
				case 'e':
				case 'f':
				case 'G':
				case 'E':
				case 'G':
				case 'd': /* signed */
					if (flags['short']) {
						if (value >= 32767) {
							value = 32767;
						} else if (value <= -32767 - 1) {
							value = -32767 - 1;
						}
					} else if (flags['long']) {
						if (value >= 2147483647) {
							value = 2147483647;
						} else if (value <= -2147483647 - 1) {
							value = -2147483647 - 1;
						}
					} else /*if ( flags['long_long'] )*/{
						if (value >= 9223372036854775807) {
							value = 9223372036854775807;
						} else if (value <= -9223372036854775807 - 1) {
							value = -9223372036854775807 - 1;
						}
					}
					break;
				case 'X':
				case 'B':
				case 'u':
				case 'o':
				case 'x':
				case 'b': /* unsigned */
					if (value < 0) {
						/* Pretty ugly, but one only solution */
						value = Math.abs(value) - 1;
					}
					if (flags['short']) {
						if (value >= 65535) {
							value = 65535;
						}
					} else if (flags['long']) {
						if (value >= 4294967295) {
							value = 4294967295;
						}

					} else /*if ( flags['long_long'] )*/{
						if (value >= 18446744073709551615) {
							value = 18446744073709551615;
						}

					}
					break;
			}
		}
		switch (type) {
			case 'c':
				result = String.fromCharCode(parseInt(value));
				break;
			case 's':
				result = value.toString();
				break;
			case 'd':
				result = (new Number(parseInt(value))).toString();
				break;
			case 'u':
				result = (new Number(parseInt(value))).toString();
				break;
			case 'o':
				result = (new Number(parseInt(value))).toString(8);
				break;
			case 'x':
				result = (new Number(parseInt(value))).toString(16);
				break;
			case 'B':
			case 'b':
				result = (new Number(parseInt(value))).toString(2);
				break;
			case 'e':
				var digits = flags['precision'] ? flags['precision'] : 6;
				result = (new Number(value)).toExponential(digits).toString();
				break;
			case 'f':
				var digits = flags['precision'] ? flags['precision'] : 6;
				result = (new Number(value)).toFixed(digits).toString();
				break;
			case 'g':
				var digits = flags['precision'] ? flags['precision'] : 6;
				result = (new Number(value)).toPrecision(digits).toString();
				break;
			case 'X':
				result = (new Number(parseInt(value))).toString(16).toUpperCase();
				break;
			case 'E':
				var digits = flags['precision'] ? flags['precision'] : 6;
				result = (new Number(value)).toExponential(digits).toString().toUpperCase();
				break;
			case 'G':
				var digits = flags['precision'] ? flags['precision'] : 6;
				result = (new Number(value)).toPrecision(digits).toString().toUpperCase();
				break;
		}

		if (flags['+'] && parseFloat(value) > 0 && ['d', 'e', 'f', 'g', 'E', 'G'].indexOf(type) != -1) {
			prefix = '+';
		}

		if (flags[' '] && parseFloat(value) > 0 && ['d', 'e', 'f', 'g', 'E', 'G'].indexOf(type) != -1) {
			prefix = ' ';
		}

		if (flags['#'] && parseInt(value) != 0) {
			switch (type) {
				case 'o':
					prefix = '0';
					break;
				case 'x':
				case 'X':
					prefix = '0x';
					break;
				case 'b':
					prefix = '0b';
					break;
				case 'B':
					prefix = '0B';
					break;
			}
		}

		if (flags['0'] && !flags['-']) {
			fillchar = '0';
		}

		if (flags['width'] && flags['width'] > (result.length + prefix.length)) {
			var tofill = flags['width'] - result.length - prefix.length;
			for (var i = 0; i < tofill; ++i) {
				fill += fillchar;
			}
		}

		if (flags['-'] && !flags['0']) {
			result += fill;
		} else {
			result = fill + result;
		}

		return prefix + result;
	};

	var result = "";

	var index = 0;
	var current_index = 0;
	var flags = {
		'long': false,
		'short': false,
		'long_long': false
	};
	var in_operator = false;
	var relative = false;
	var precision = false;
	var fixed = false;
	var vector = false;
	var bitwidth = false;
	var vector_delimiter = '.';

	for (var i = 0; i < format.length; ++i) {
		var current_char = format.charAt(i);
		if (in_operator) {
			// backward compat
			switch (current_char) {
				case 'i':
					current_char = 'd';
					break;
				case 'D':
					flags['long'] = true;
					current_char = 'd';
					break;
				case 'U':
					flags['long'] = true;
					current_char = 'u';
					break;
				case 'O':
					flags['long'] = true;
					current_char = 'o';
					break;
				case 'F':
					current_char = 'f';
					break;
			}
			switch (current_char) {
				case 'c':
				case 's':
				case 'd':
				case 'u':
				case 'o':
				case 'x':
				case 'e':
				case 'f':
				case 'g':
				case 'X':
				case 'E':
				case 'G':
				case 'b':
				case 'B':
					var value = args[current_index];
					if (vector) {
						var fixed_value = value;
						if (value instanceof Array) {
							// if the value is an array, assume to work on it directly
							fixed_value = value;
						} else if (typeof (value) == 'string' || value instanceof String) {
							// normal behavour, assume string is a bitmap
							fixed_value = value.split('').map(function (value) { return value.charCodeAt(); });
						} else if ((typeof (value) == 'number' || value instanceof Number) && flags['bitwidth']) {
							// if we defined a width, assume we want to vectorize the bits directly
							fixed_value = [];
							do {
								fixed_value.unshift(value & ~(~0 << flags['bitwidth']));
							} while (value >>>= flags['bitwidth']);
						} else {
							fixed_value = value.toString().split('').map(function (value) { return value.charCodeAt(); });

						}
						result += fixed_value.map(function (value) {
							return _sprintf_format(current_char, value, flags);
						}).join(vector_delimiter);
					} else {
						result += _sprintf_format(current_char, value, flags);
					}
					if (!fixed) {
						++index;
					}
					current_index = index;
					flags = {};
					relative = false;
					in_operator = false;
					precision = false;
					fixed = false;
					vector = false;
					bitwidth = false;
					vector_delimiter = '.';
					break;
				case 'v':
					vector = true;
					break;
				case ' ':
				case '0':
				case '-':
				case '+':
				case '#':
					flags[current_char] = true;
					break;
				case '*':
					relative = true;
					break;
				case '.':
					precision = true;
					break;
				case '@':
					bitwidth = true;
					break;
				case 'l':
					if (flags['long']) {
						flags['long_long'] = true;
						flags['long'] = false;
					} else {
						flags['long'] = true;
						flags['long_long'] = false;
					}
					flags['short'] = false;
					break;
				case 'q':
				case 'L':
					flags['long_long'] = true;
					flags['long'] = false;
					flags['short'] = false;
					break;
				case 'h':
					flags['short'] = true;
					flags['long'] = false;
					flags['long_long'] = false;
					break;
			}
			if (/\d/.test(current_char)) {
				var num = parseInt(format.substr(i));
				var len = num.toString().length;
				i += len - 1;
				var next = format.charAt(i + 1);
				if (next == '$') {
					if (num < 0 || num > args.length) {
						throw "out of bound";
					}
					if (relative) {
						if (precision) {
							flags['precision'] = args[num - 1];
							precision = false;
						} else if (format.charAt(i + 2) == 'v') {
							vector_delimiter = args[num - 1];
						} else {
							flags['width'] = args[num - 1];
						}
						relative = false;
					} else {
						fixed = true;
						current_index = num - 1;
					}
					++i;
				} else if (precision) {
					flags['precision'] = num;
					precision = false;
				} else if (bitwidth) {
					flags['bitwidth'] = num;
					bitwidth = false;
				} else {
					flags['width'] = num;
				}
			} else if (relative && !/\d/.test(format.charAt(i + 1))) {
				if (precision) {
					flags['precision'] = args[current_index];
					precision = false;
				} else if (format.charAt(i + 1) == 'v') {
					vector_delimiter = args[current_index];
				} else {
					flags['width'] = args[current_index];
				}
				++index;
				if (!fixed) {
					current_index++;
				}
				relative = false;
			}
		} else {
			if (current_char == '%') {
				// If the next character is an %, then we have an escaped %, 
				// we'll take this as an exception to the normal lookup, as
				// we don't want/need to process this.
				if (format.charAt(i + 1) == '%') {
					result += '%';
					++i;
					continue;
				}
				in_operator = true;
				continue;
			} else {
				result += current_char;
				continue;
			}
		}
	}
	return result;
};

/** 
* Inserts the arguments§ into the format and returns the formated string
*
* @example alert($.sprintf( "%s %s", "Hello", "world" ));
*
* @param String format the format to use
* @param Object args... the arguments to insert into the format
* @return String the formated string
*/

jQuery.sprintf = function jQuery_sprintf() {
	if (arguments.length == 0) {
		throw "Not enough arguments for sprintf";
	}

	var args = Array.prototype.slice.call(arguments);
	var format = args.shift();

	return jQuery.vsprintf(format, args);
};

/** 
* Inserts the arguments into the format and appends the formated string
* to the objects in question.
*
* @example $('p').printf( "%d <strong>%s</strong>", 2, "world" );
*
* @before <p>Hello</p>
*
* @after <p>Hello2 <strong>world</strong></p>
*
* @param String format the format to use
* @param Object args... the arguments to insert into the format
*/

jQuery.fn.printf = function jQuery_fn_printf() {
	if (arguments.length == 0) {
		throw "Not enough arguments for sprintf";
	}
	var args = Array.prototype.slice.call(arguments);
	var format = args.shift();

	return this.append(jQuery.vsprintf(format, args));
};

/** 
* Inserts the arguments into the format and appends the formated string
* to the objects in question.
*
* @example $('p').vprintf( "%d <strong>%s</strong>", [ 2, "world" ] );
*
* @before <p>Hello</p>
*
* @after <p>Hello2 <strong>world</strong></p>
*
* @param String format the format to use
* @param Array args the arguments to insert into the format
*/
jQuery.fn.vprintf = function jQuery_fn_vprintf(format, args) {
	if (arguments.length == 0) {
		throw "Not enough arguments for sprintf";
	}

	return this.append(jQuery.vsprintf(format, args));
};

/** 
* Formats the objects html in question and replaces the content 
* with the formated content
*
* @example $('p').vformat( [ "Hello", "world" ] );
*
* @before <p>%s %s</p>
*
* @after <p>Hello world</p>
*
* @param Array args the arguments to insert into the format
*/

jQuery.fn.vformat = function jQuery_fn_vformat(args) {
	if (arguments.length == 0) {
		throw "Not enough arguments for sprintf";
	}
	return this.each(function () {
		self = jQuery(this);
		self.html(jQuery.vsprintf(self.html(), args))
	}
	);
}

/** 
* Formats the objects html in question and replaces the content 
* with the formated content
*
* @example $('p').format( "Hello", "world" );
*
* @before <p>%s %s</p>
*
* @after <p>Hello world</p>
*
* @param Object args... the arguments to insert into the format
*/
jQuery.fn.format = function jQuery_fn_format() {
	if (arguments.length == 0) {
		throw "Not enough arguments for sprintf";
	}
	var args = Array.prototype.slice.call(arguments);
	return this.each(function () {
		self = jQuery(this);
		self.html(jQuery.vsprintf(self.html(), args))
	}
	);
}

/** 
* Inserts the arguments into the format and prints formated string 
* to console or dump
*
* @example $.printf( "%s %s", "Hello", "world" );
*
* @param String format the format to use
* @param Object args... the arguments to insert into the format
*/
jQuery.printf = function jQuery_printf() {
	if (arguments.length == 0) {
		throw "Not enough arguments for sprintf";
	}
	var args = Array.prototype.slice.call(arguments);
	var format = args.shift();
	var ret = jQuery.vsprintf(format, args);

	if (window.console) {
		window.console.info(ret);
	} else {
		window.dump(ret);
	}
};
/** 
* Inserts the arguments into the format and prints formated string 
* to console or dump
*
* @example $.vprintf( "%s %s", [ "Hello", "world" ] );
*
* @param String format the format to use
* @param Array args the arguments to insert into the format
*/
jQuery.vprintf = function jQuery_vprintf(format, args) {
	if (arguments.length == 0) {
		throw "Not enough arguments for sprintf";
	}
	var ret = jQuery.vsprintf(format, args);

	if (window.console) {
		window.console.info(ret);
	} else {
		window.dump(ret);
	}

};

