text: /**
   * jquery.slider - Dependencies JS Bundle:
   * 
   * jshashtable
   * jquery.numberformatter
   * tmpl
   * jquery.dependClass
   * draggable
   * 
   **/
   
   
  /**
   * Copyright 2010 Tim Down.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /**
   * jshashtable
   *
   * jshashtable is a JavaScript implementation of a hash table. It creates a single constructor function called Hashtable
   * in the global scope.
   *
   * Author: Tim Down <tim@timdown.co.uk>
   * Version: 2.1
   * Build date: 21 March 2010
   * Website: http://www.timdown.co.uk/jshashtable
   */
  
  var Hashtable = (function() {
  	var FUNCTION = "function";
  
  	var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ?
  		function(arr, idx) {
  			arr.splice(idx, 1);
  		} :
  
  		function(arr, idx) {
  			var itemsAfterDeleted, i, len;
  			if (idx === arr.length - 1) {
  				arr.length = idx;
  			} else {
  				itemsAfterDeleted = arr.slice(idx + 1);
  				arr.length = idx;
  				for (i = 0, len = itemsAfterDeleted.length; i < len; ++i) {
  					arr[idx + i] = itemsAfterDeleted[i];
  				}
  			}
  		};
  
  	function hashObject(obj) {
  		var hashCode;
  		if (typeof obj == "string") {
  			return obj;
  		} else if (typeof obj.hashCode == FUNCTION) {
  			// Check the hashCode method really has returned a string
  			hashCode = obj.hashCode();
  			return (typeof hashCode == "string") ? hashCode : hashObject(hashCode);
  		} else if (typeof obj.toString == FUNCTION) {
  			return obj.toString();
  		} else {
  			try {
  				return String(obj);
  			} catch (ex) {
  				// For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when
  				// passed to String()
  				return Object.prototype.toString.call(obj);
  			}
  		}
  	}
  
  	function equals_fixedValueHasEquals(fixedValue, variableValue) {
  		return fixedValue.equals(variableValue);
  	}
  
  	function equals_fixedValueNoEquals(fixedValue, variableValue) {
  		return (typeof variableValue.equals == FUNCTION) ?
  			   variableValue.equals(fixedValue) : (fixedValue === variableValue);
  	}
  
  	function createKeyValCheck(kvStr) {
  		return function(kv) {
  			if (kv === null) {
  				throw new Error("null is not a valid " + kvStr);
  			} else if (typeof kv == "undefined") {
  				throw new Error(kvStr + " must not be undefined");
  			}
  		};
  	}
  
  	var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value");
  
  	/*----------------------------------------------------------------------------------------------------------------*/
  
  	function Bucket(hash, firstKey, firstValue, equalityFunction) {
          this[0] = hash;
  		this.entries = [];
  		this.addEntry(firstKey, firstValue);
  
  		if (equalityFunction !== null) {
  			this.getEqualityFunction = function() {
  				return equalityFunction;
  			};
  		}
  	}
  
  	var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2;
  
  	function createBucketSearcher(mode) {
  		return function(key) {
  			var i = this.entries.length, entry, equals = this.getEqualityFunction(key);
  			while (i--) {
  				entry = this.entries[i];
  				if ( equals(key, entry[0]) ) {
  					switch (mode) {
  						case EXISTENCE:
  							return true;
  						case ENTRY:
  							return entry;
  						case ENTRY_INDEX_AND_VALUE:
  							return [ i, entry[1] ];
  					}
  				}
  			}
  			return false;
  		};
  	}
  
  	function createBucketLister(entryProperty) {
  		return function(aggregatedArr) {
  			var startIndex = aggregatedArr.length;
  			for (var i = 0, len = this.entries.length; i < len; ++i) {
  				aggregatedArr[startIndex + i] = this.entries[i][entryProperty];
  			}
  		};
  	}
  
  	Bucket.prototype = {
  		getEqualityFunction: function(searchValue) {
  			return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals;
  		},
  
  		getEntryForKey: createBucketSearcher(ENTRY),
  
  		getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE),
  
  		removeEntryForKey: function(key) {
  			var result = this.getEntryAndIndexForKey(key);
  			if (result) {
  				arrayRemoveAt(this.entries, result[0]);
  				return result[1];
  			}
  			return null;
  		},
  
  		addEntry: function(key, value) {
  			this.entries[this.entries.length] = [key, value];
  		},
  
  		keys: createBucketLister(0),
  
  		values: createBucketLister(1),
  
  		getEntries: function(entries) {
  			var startIndex = entries.length;
  			for (var i = 0, len = this.entries.length; i < len; ++i) {
  				// Clone the entry stored in the bucket before adding to array
  				entries[startIndex + i] = this.entries[i].slice(0);
  			}
  		},
  
  		containsKey: createBucketSearcher(EXISTENCE),
  
  		containsValue: function(value) {
  			var i = this.entries.length;
  			while (i--) {
  				if ( value === this.entries[i][1] ) {
  					return true;
  				}
  			}
  			return false;
  		}
  	};
  
  	/*----------------------------------------------------------------------------------------------------------------*/
  
  	// Supporting functions for searching hashtable buckets
  
  	function searchBuckets(buckets, hash) {
  		var i = buckets.length, bucket;
  		while (i--) {
  			bucket = buckets[i];
  			if (hash === bucket[0]) {
  				return i;
  			}
  		}
  		return null;
  	}
  
  	function getBucketForHash(bucketsByHash, hash) {
  		var bucket = bucketsByHash[hash];
  
  		// Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype
  		return ( bucket && (bucket instanceof Bucket) ) ? bucket : null;
  	}
  
  	/*----------------------------------------------------------------------------------------------------------------*/
  
  	function Hashtable(hashingFunctionParam, equalityFunctionParam) {
  		var that = this;
  		var buckets = [];
  		var bucketsByHash = {};
  
  		var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject;
  		var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null;
  
  		this.put = function(key, value) {
  			checkKey(key);
  			checkValue(value);
  			var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null;
  
  			// Check if a bucket exists for the bucket key
  			bucket = getBucketForHash(bucketsByHash, hash);
  			if (bucket) {
  				// Check this bucket to see if it already contains this key
  				bucketEntry = bucket.getEntryForKey(key);
  				if (bucketEntry) {
  					// This bucket entry is the current mapping of key to value, so replace old value and we're done.
  					oldValue = bucketEntry[1];
  					bucketEntry[1] = value;
  				} else {
  					// The bucket does not contain an entry for this key, so add one
  					bucket.addEntry(key, value);
  				}
  			} else {
  				// No bucket exists for the key, so create one and put our key/value mapping in
  				bucket = new Bucket(hash, key, value, equalityFunction);
  				buckets[buckets.length] = bucket;
  				bucketsByHash[hash] = bucket;
  			}
  			return oldValue;
  		};
  
  		this.get = function(key) {
  			checkKey(key);
  
  			var hash = hashingFunction(key);
  
  			// Check if a bucket exists for the bucket key
  			var bucket = getBucketForHash(bucketsByHash, hash);
  			if (bucket) {
  				// Check this bucket to see if it contains this key
  				var bucketEntry = bucket.getEntryForKey(key);
  				if (bucketEntry) {
  					// This bucket entry is the current mapping of key to value, so return the value.
  					return bucketEntry[1];
  				}
  			}
  			return null;
  		};
  
  		this.containsKey = function(key) {
  			checkKey(key);
  			var bucketKey = hashingFunction(key);
  
  			// Check if a bucket exists for the bucket key
  			var bucket = getBucketForHash(bucketsByHash, bucketKey);
  
  			return bucket ? bucket.containsKey(key) : false;
  		};
  
  		this.containsValue = function(value) {
  			checkValue(value);
  			var i = buckets.length;
  			while (i--) {
  				if (buckets[i].containsValue(value)) {
  					return true;
  				}
  			}
  			return false;
  		};
  
  		this.clear = function() {
  			buckets.length = 0;
  			bucketsByHash = {};
  		};
  
  		this.isEmpty = function() {
  			return !buckets.length;
  		};
  
  		var createBucketAggregator = function(bucketFuncName) {
  			return function() {
  				var aggregated = [], i = buckets.length;
  				while (i--) {
  					buckets[i][bucketFuncName](aggregated);
  				}
  				return aggregated;
  			};
  		};
  
  		this.keys = createBucketAggregator("keys");
  		this.values = createBucketAggregator("values");
  		this.entries = createBucketAggregator("getEntries");
  
  		this.remove = function(key) {
  			checkKey(key);
  
  			var hash = hashingFunction(key), bucketIndex, oldValue = null;
  
  			// Check if a bucket exists for the bucket key
  			var bucket = getBucketForHash(bucketsByHash, hash);
  
  			if (bucket) {
  				// Remove entry from this bucket for this key
  				oldValue = bucket.removeEntryForKey(key);
  				if (oldValue !== null) {
  					// Entry was removed, so check if bucket is empty
  					if (!bucket.entries.length) {
  						// Bucket is empty, so remove it from the bucket collections
  						bucketIndex = searchBuckets(buckets, hash);
  						arrayRemoveAt(buckets, bucketIndex);
  						delete bucketsByHash[hash];
  					}
  				}
  			}
  			return oldValue;
  		};
  
  		this.size = function() {
  			var total = 0, i = buckets.length;
  			while (i--) {
  				total += buckets[i].entries.length;
  			}
  			return total;
  		};
  
  		this.each = function(callback) {
  			var entries = that.entries(), i = entries.length, entry;
  			while (i--) {
  				entry = entries[i];
  				callback(entry[0], entry[1]);
  			}
  		};
  
  		this.putAll = function(hashtable, conflictCallback) {
  			var entries = hashtable.entries();
  			var entry, key, value, thisValue, i = entries.length;
  			var hasConflictCallback = (typeof conflictCallback == FUNCTION);
  			while (i--) {
  				entry = entries[i];
  				key = entry[0];
  				value = entry[1];
  
  				// Check for a conflict. The default behaviour is to overwrite the value for an existing key
  				if ( hasConflictCallback && (thisValue = that.get(key)) ) {
  					value = conflictCallback(key, thisValue, value);
  				}
  				that.put(key, value);
  			}
  		};
  
  		this.clone = function() {
  			var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam);
  			clone.putAll(that);
  			return clone;
  		};
  	}
  
  	return Hashtable;
  })();
  
  /**
   * jquery.numberformatter - Formatting/Parsing Numbers in jQuery
   * 
   * Written by
   * Michael Abernethy (mike@abernethysoft.com),
   * Andrew Parry (aparry0@gmail.com)
   *
   * Dual licensed under the MIT (MIT-LICENSE.txt)
   * and GPL (GPL-LICENSE.txt) licenses.
   *
   * @author Michael Abernethy, Andrew Parry
   * @version 1.2.3-SNAPSHOT ($Id$)
   * 
   * Dependencies
   * 
   * jQuery (http://jquery.com)
   * jshashtable (http://www.timdown.co.uk/jshashtable)
   * 
   * Notes & Thanks
   * 
   * many thanks to advweb.nanasi.jp for his bug fixes
   * jsHashtable is now used also, so thanks to the author for that excellent little class.
   *
   * This plugin can be used to format numbers as text and parse text as Numbers
   * Because we live in an international world, we cannot assume that everyone
   * uses "," to divide thousands, and "." as a decimal point.
   *
   * As of 1.2 the way this plugin works has changed slightly, parsing text to a number
   * has 1 set of functions, formatting a number to text has it's own. Before things
   * were a little confusing, so I wanted to separate the 2 out more.
   *
   *
   * jQuery extension functions:
   *
   * formatNumber(options, writeBack, giveReturnValue) - Reads the value from the subject, parses to
   * a Javascript Number object, then formats back to text using the passed options and write back to
   * the subject.
   * 
   * parseNumber(options) - Parses the value in the subject to a Number object using the passed options
   * to decipher the actual number from the text, then writes the value as text back to the subject.
   * 
   * 
   * Generic functions:
   * 
   * formatNumber(numberString, options) - Takes a plain number as a string (e.g. '1002.0123') and returns
   * a string of the given format options.
   * 
   * parseNumber(numberString, options) - Takes a number as text that is formatted the same as the given
   * options then and returns it as a plain Number object.
   * 
   * To achieve the old way of combining parsing and formatting to keep say a input field always formatted
   * to a given format after it has lost focus you'd simply use a combination of the functions.
   * 
   * e.g.
   * $("#salary").blur(function(){
   * 		$(this).parseNumber({format:"#,###.00", locale:"us"});
   * 		$(this).formatNumber({format:"#,###.00", locale:"us"});
   * });
   *
   * The syntax for the formatting is:
   * 0 = Digit
   * # = Digit, zero shows as absent
   * . = Decimal separator
   * - = Negative sign
   * , = Grouping Separator
   * % = Percent (multiplies the number by 100)
   * 
   * For example, a format of "#,###.00" and text of 4500.20 will
   * display as "4.500,20" with a locale of "de", and "4,500.20" with a locale of "us"
   *
   *
   * As of now, the only acceptable locales are 
   * Arab Emirates -> "ae"
   * Australia -> "au"
   * Austria -> "at"
   * Brazil -> "br"
   * Canada -> "ca"
   * China -> "cn"
   * Czech -> "cz"
   * Denmark -> "dk"
   * Egypt -> "eg"
   * Finland -> "fi"
   * France  -> "fr"
   * Germany -> "de"
   * Greece -> "gr"
   * Great Britain -> "gb"
   * Hong Kong -> "hk"
   * India -> "in"
   * Israel -> "il"
   * Japan -> "jp"
   * Russia -> "ru"
   * South Korea -> "kr"
   * Spain -> "es"
   * Sweden -> "se"
   * Switzerland -> "ch"
   * Taiwan -> "tw"
   * Thailand -> "th"
   * United States -> "us"
   * Vietnam -> "vn"
   **/
  
  (function(jQuery) {
  
  	var nfLocales = new Hashtable();
  	
  	var nfLocalesLikeUS = [ 'ae','au','ca','cn','eg','gb','hk','il','in','jp','sk','th','tw','us' ];
  	var nfLocalesLikeDE = [ 'at','br','de','dk','es','gr','it','nl','pt','tr','vn' ];
  	var nfLocalesLikeFR = [ 'cz','fi','fr','ru','se','pl' ];
  	var nfLocalesLikeCH = [ 'ch' ];
  	
  	var nfLocaleFormatting = [ [".", ","], [",", "."], [",", " "], [".", "'"] ]; 
  	var nfAllLocales = [ nfLocalesLikeUS, nfLocalesLikeDE, nfLocalesLikeFR, nfLocalesLikeCH ]
  
  	function FormatData(dec, group, neg) {
  		this.dec = dec;
  		this.group = group;
  		this.neg = neg;
  	};
  
  	function init() {
  		// write the arrays into the hashtable
  		for (var localeGroupIdx = 0; localeGroupIdx < nfAllLocales.length; localeGroupIdx++) {
  			localeGroup = nfAllLocales[localeGroupIdx];
  			for (var i = 0; i < localeGroup.length; i++) {
  				nfLocales.put(localeGroup[i], localeGroupIdx);
  			}
  		}
  	};
  
  	function formatCodes(locale, isFullLocale) {
  		if (nfLocales.size() == 0)
  			init();
  
           // default values
           var dec = ".";
           var group = ",";
           var neg = "-";
           
           if (isFullLocale == false) {
  	         // Extract and convert to lower-case any language code from a real 'locale' formatted string, if not use as-is
  	         // (To prevent locale format like : "fr_FR", "en_US", "de_DE", "fr_FR", "en-US", "de-DE")
  	         if (locale.indexOf('_') != -1)
  				locale = locale.split('_')[1].toLowerCase();
  			 else if (locale.indexOf('-') != -1)
  				locale = locale.split('-')[1].toLowerCase();
  		}
  
  		 // hashtable lookup to match locale with codes
  		 var codesIndex = nfLocales.get(locale);
  		 if (codesIndex) {
  		 	var codes = nfLocaleFormatting[codesIndex];
  			if (codes) {
  				dec = codes[0];
  				group = codes[1];
  			}
  		 }
  		 return new FormatData(dec, group, neg);
      };
  	
  	
  	/*	Formatting Methods	*/
  	
  	
  	/**
  	 * Formats anything containing a number in standard js number notation.
  	 * 
  	 * @param {Object}	options			The formatting options to use
  	 * @param {Boolean}	writeBack		(true) If the output value should be written back to the subject
  	 * @param {Boolean} giveReturnValue	(true) If the function should return the output string
  	 */
  	jQuery.fn.formatNumber = function(options, writeBack, giveReturnValue) {
  	
  		return this.each(function() {
  			// enforce defaults
  			if (writeBack == null)
  				writeBack = true;
  			if (giveReturnValue == null)
  				giveReturnValue = true;
  			
  			// get text
  			var text;
  			if (jQuery(this).is(":input"))
  				text = new String(jQuery(this).val());
  			else
  				text = new String(jQuery(this).text());
  
  			// format
  			var returnString = jQuery.formatNumber(text, options);
  		
  			// set formatted string back, only if a success
  //			if (returnString) {
  				if (writeBack) {
  					if (jQuery(this).is(":input"))
  						jQuery(this).val(returnString);
  					else
  						jQuery(this).text(returnString);
  				}
  				if (giveReturnValue)
  					return returnString;
  //			}
  //			return '';
  		});
  	};
  	
  	/**
  	 * First parses a string and reformats it with the given options.
  	 * 
  	 * @param {Object} numberString
  	 * @param {Object} options
  	 */
  	jQuery.formatNumber = function(numberString, options){
  		var options = jQuery.extend({}, jQuery.fn.formatNumber.defaults, options);
  		var formatData = formatCodes(options.locale.toLowerCase(), options.isFullLocale);
  		
  		var dec = formatData.dec;
  		var group = formatData.group;
  		var neg = formatData.neg;
  		
  		var validFormat = "0#-,.";
  		
  		// strip all the invalid characters at the beginning and the end
  		// of the format, and we'll stick them back on at the end
  		// make a special case for the negative sign "-" though, so 
  		// we can have formats like -$23.32
  		var prefix = "";
  		var negativeInFront = false;
  		for (var i = 0; i < options.format.length; i++) {
  			if (validFormat.indexOf(options.format.charAt(i)) == -1) 
  				prefix = prefix + options.format.charAt(i);
  			else 
  				if (i == 0 && options.format.charAt(i) == '-') {
  					negativeInFront = true;
  					continue;
  				}
  				else 
  					break;
  		}
  		var suffix = "";
  		for (var i = options.format.length - 1; i >= 0; i--) {
  			if (validFormat.indexOf(options.format.charAt(i)) == -1) 
  				suffix = options.format.charAt(i) + suffix;
  			else 
  				break;
  		}
  		
  		options.format = options.format.substring(prefix.length);
  		options.format = options.format.substring(0, options.format.length - suffix.length);
  		
  		// now we need to convert it into a number
  		//while (numberString.indexOf(group) > -1) 
  		//	numberString = numberString.replace(group, '');
  		//var number = new Number(numberString.replace(dec, ".").replace(neg, "-"));
  		var number = new Number(numberString);
  		
  		return jQuery._formatNumber(number, options, suffix, prefix, negativeInFront);
  	};
  	
  	/**
  	 * Formats a Number object into a string, using the given formatting options
  	 * 
  	 * @param {Object} numberString
  	 * @param {Object} options
  	 */
  	jQuery._formatNumber = function(number, options, suffix, prefix, negativeInFront) {
  		var options = jQuery.extend({}, jQuery.fn.formatNumber.defaults, options);
  		var formatData = formatCodes(options.locale.toLowerCase(), options.isFullLocale);
  		
  		var dec = formatData.dec;
  		var group = formatData.group;
  		var neg = formatData.neg;
  		
  		var forcedToZero = false;
  		if (isNaN(number)) {
  			if (options.nanForceZero == true) {
  				number = 0;
  				forcedToZero = true;
  			} else 
  				return null;
  		}
  
  		// special case for percentages
          if (suffix == "%")
          	number = number * 100;
  
  		var returnString = "";
  		if (options.format.indexOf(".") > -1) {
  			var decimalPortion = dec;
  			var decimalFormat = options.format.substring(options.format.lastIndexOf(".") + 1);
  			
  			// round or truncate number as needed
  			if (options.round == true)
  				number = new Number(number.toFixed(decimalFormat.length));
  			else {
  				var numStr = number.toString();
  				numStr = numStr.substring(0, numStr.lastIndexOf('.') + decimalFormat.length + 1);
  				number = new Number(numStr);
  			}
  			
  			var decimalValue = number % 1;
  			var decimalString = new String(decimalValue.toFixed(decimalFormat.length));
  			decimalString = decimalString.substring(decimalString.lastIndexOf(".") + 1);
  			
  			for (var i = 0; i < decimalFormat.length; i++) {
  				if (decimalFormat.charAt(i) == '#' && decimalString.charAt(i) != '0') {
                  	decimalPortion += decimalString.charAt(i);
  					continue;
  				} else if (decimalFormat.charAt(i) == '#' && decimalString.charAt(i) == '0') {
  					var notParsed = decimalString.substring(i);
  					if (notParsed.match('[1-9]')) {
  						decimalPortion += decimalString.charAt(i);
  						continue;
  					} else
  						break;
  				} else if (decimalFormat.charAt(i) == "0")
  					decimalPortion += decimalString.charAt(i);
  			}
  			returnString += decimalPortion
           } else
  			number = Math.round(number);
  
  		var ones = Math.floor(number);
  		if (number < 0)
  			ones = Math.ceil(number);
  
  		var onesFormat = "";
  		if (options.format.indexOf(".") == -1)
  			onesFormat = options.format;
  		else
  			onesFormat = options.format.substring(0, options.format.indexOf("."));
  
  		var onePortion = "";
  		if (!(ones == 0 && onesFormat.substr(onesFormat.length - 1) == '#') || forcedToZero) {
  			// find how many digits are in the group
  			var oneText = new String(Math.abs(ones));
  			var groupLength = 9999;
  			if (onesFormat.lastIndexOf(",") != -1)
  				groupLength = onesFormat.length - onesFormat.lastIndexOf(",") - 1;
  			var groupCount = 0;
  			for (var i = oneText.length - 1; i > -1; i--) {
  				onePortion = oneText.charAt(i) + onePortion;
  				groupCount++;
  				if (groupCount == groupLength && i != 0) {
  					onePortion = group + onePortion;
  					groupCount = 0;
  				}
  			}
  			
  			// account for any pre-data padding
  			if (onesFormat.length > onePortion.length) {
  				var padStart = onesFormat.indexOf('0');
  				if (padStart != -1) {
  					var padLen = onesFormat.length - padStart;
  					
  					// pad to left with 0's or group char
  					var pos = onesFormat.length - onePortion.length - 1;
  					while (onePortion.length < padLen) {
  						var padChar = onesFormat.charAt(pos);
  						// replace with real group char if needed
  						if (padChar == ',')
  							padChar = group;
  						onePortion = padChar + onePortion;
  						pos--;
  					}
  				}
  			}
  		}
  		
  		if (!onePortion && onesFormat.indexOf('0', onesFormat.length - 1) !== -1)
     			onePortion = '0';
  
  		returnString = onePortion + returnString;
  
  		// handle special case where negative is in front of the invalid characters
  		if (number < 0 && negativeInFront && prefix.length > 0)
  			prefix = neg + prefix;
  		else if (number < 0)
  			returnString = neg + returnString;
  		
  		if (!options.decimalSeparatorAlwaysShown) {
  			if (returnString.lastIndexOf(dec) == returnString.length - 1) {
  				returnString = returnString.substring(0, returnString.length - 1);
  			}
  		}
  		returnString = prefix + returnString + suffix;
  		return returnString;
  	};
  
  
  	/*	Parsing Methods	*/
  
  
  	/**
  	 * Parses a number of given format from the element and returns a Number object.
  	 * @param {Object} options
  	 */
  	jQuery.fn.parseNumber = function(options, writeBack, giveReturnValue) {
  		// enforce defaults
  		if (writeBack == null)
  			writeBack = true;
  		if (giveReturnValue == null)
  			giveReturnValue = true;
  		
  		// get text
  		var text;
  		if (jQuery(this).is(":input"))
  			text = new String(jQuery(this).val());
  		else
  			text = new String(jQuery(this).text());
  	
  		// parse text
  		var number = jQuery.parseNumber(text, options);
  		
  		if (number) {
  			if (writeBack) {
  				if (jQuery(this).is(":input"))
  					jQuery(this).val(number.toString());
  				else
  					jQuery(this).text(number.toString());
  			}
  			if (giveReturnValue)
  				return number;
  		}
  	};
  	
  	/**
  	 * Parses a string of given format into a Number object.
  	 * 
  	 * @param {Object} string
  	 * @param {Object} options
  	 */
  	jQuery.parseNumber = function(numberString, options) {
  		var options = jQuery.extend({}, jQuery.fn.parseNumber.defaults, options);
  		var formatData = formatCodes(options.locale.toLowerCase(), options.isFullLocale);
  
  		var dec = formatData.dec;
  		var group = formatData.group;
  		var neg = formatData.neg;
  
  		var valid = "1234567890.-";
  		
  		// now we need to convert it into a number
  		while (numberString.indexOf(group)>-1)
  			numberString = numberString.replace(group,'');
  		numberString = numberString.replace(dec,".").replace(neg,"-");
  		var validText = "";
  		var hasPercent = false;
  		if (numberString.charAt(numberString.length - 1) == "%" || options.isPercentage == true)
  			hasPercent = true;
  		for (var i=0; i<numberString.length; i++) {
  			if (valid.indexOf(numberString.charAt(i))>-1)
  				validText = validText + numberString.charAt(i);
  		}
  		var number = new Number(validText);
  		if (hasPercent) {
  			number = number / 100;
  			var decimalPos = validText.indexOf('.');
  			if (decimalPos != -1) {
  				var decimalPoints = validText.length - decimalPos - 1;
  				number = number.toFixed(decimalPoints + 2);
  			} else {
  				number = number.toFixed(validText.length - 1);
  			}
  		}
  
  		return number;
  	};
  
  	jQuery.fn.parseNumber.defaults = {
  		locale: "us",
  		decimalSeparatorAlwaysShown: false,
  		isPercentage: false,
  		isFullLocale: false
  	};
  
  	jQuery.fn.formatNumber.defaults = {
  		format: "#,###.00",
  		locale: "us",
  		decimalSeparatorAlwaysShown: false,
  		nanForceZero: true,
  		round: true,
  		isFullLocale: false
  	};
  	
  	Number.prototype.toFixed = function(precision) {
      	return jQuery._roundNumber(this, precision);
  	};
  	
  	jQuery._roundNumber = function(number, decimalPlaces) {
  		var power = Math.pow(10, decimalPlaces || 0);
      	var value = String(Math.round(number * power) / power);
      	
      	// ensure the decimal places are there
      	if (decimalPlaces > 0) {
      		var dp = value.indexOf(".");
      		if (dp == -1) {
      			value += '.';
      			dp = 0;
      		} else {
      			dp = value.length - (dp + 1);
      		}
      		
      		while (dp < decimalPlaces) {
      			value += '0';
      			dp++;
      		}
      	}
      	return value;
  	};
  
   })(jQuery);
   
  // Simple JavaScript Templating
  // John Resig - http://ejohn.org/ - MIT Licensed
  (function($) {
    var cache = {};
    
    this.tmpl = function tmpl(str, data){
      // Figure out if we're getting a template, or if we need to
      // load the template - and be sure to cache the result.
      var fn = !/\W/.test(str) ?
        cache[str] = cache[str] ||
          tmpl(document.getElementById(str).innerHTML) :
        
        // Generate a reusable function that will serve as a template
        // generator (and which will be cached).
        new Function("obj",
          "var p=[],print=function(){p.push.apply(p,arguments);};" +
          
          // Introduce the data as local variables using with(){}
          "with(obj){p.push('" +
          
          // Convert the template into pure JavaScript
          str
            .replace(/[\r\t\n]/g, " ")
            .split("<%").join("\t")
            .replace(/((^|%>)[^\t]*)'/g, "$1\r")
            .replace(/\t=(.*?)%>/g, "',$1,'")
            .split("\t").join("');")
            .split("%>").join("p.push('")
            .split("\r").join("\\'")
        + "');}return p.join('');");
      
      // Provide some basic currying to the user
      return data ? fn( data ) : fn;
    };
  })(jQuery);
  
  /**
   * jquery.dependClass - Attach class based on first class in list of current element
   * 
   * Written by
   * Egor Khmelev (hmelyoff@gmail.com)
   *
   * Licensed under the MIT (MIT-LICENSE.txt).
   *
   * @author Egor Khmelev
   * @version 0.1.0-BETA ($Id$)
   * 
   **/
  
  (function($) {
  	$.baseClass = function(obj){
  	  obj = $(obj);
  	  return obj.get(0).className.match(/([^ ]+)/)[1];
  	};
  	
  	$.fn.addDependClass = function(className, delimiter){
  		var options = {
  		  delimiter: delimiter ? delimiter : '-'
  		}
  		return this.each(function(){
  		  var baseClass = $.baseClass(this);
  		  if(baseClass)
      		$(this).addClass(baseClass + options.delimiter + className);
  		});
  	};
  
  	$.fn.removeDependClass = function(className, delimiter){
  		var options = {
  		  delimiter: delimiter ? delimiter : '-'
  		}
  		return this.each(function(){
  		  var baseClass = $.baseClass(this);
  		  if(baseClass)
      		$(this).removeClass(baseClass + options.delimiter + className);
  		});
  	};
  
  	$.fn.toggleDependClass = function(className, delimiter){
  		var options = {
  		  delimiter: delimiter ? delimiter : '-'
  		}
  		return this.each(function(){
  		  var baseClass = $.baseClass(this);
  		  if(baseClass)
  		    if($(this).is("." + baseClass + options.delimiter + className))
      		  $(this).removeClass(baseClass + options.delimiter + className);
      		else
      		  $(this).addClass(baseClass + options.delimiter + className);
  		});
  	};
  
  })(jQuery);
  
  /**
   * draggable - Class allows to make any element draggable
   * 
   * Written by
   * Egor Khmelev (hmelyoff@gmail.com)
   *
   * Licensed under the MIT (MIT-LICENSE.txt).
   *
   * @author Egor Khmelev
   * @version 0.1.0-BETA ($Id$)
   * 
   **/
  
  (function($){
  
    function Draggable(){
    	this._init.apply( this, arguments );
    };
  
  	Draggable.prototype.oninit = function(){
  	  
  	};
  	
  	Draggable.prototype.events = function(){
  	  
  	};
  	
  	Draggable.prototype.onmousedown = function(){
  		this.ptr.css({ position: "absolute" });
  	};
  	
  	Draggable.prototype.onmousemove = function( evt, x, y ){
  		this.ptr.css({ left: x, top: y });
  	};
  	
  	Draggable.prototype.onmouseup = function(){
  	  
  	};
  
  	Draggable.prototype.isDefault = {
  		drag: false,
  		clicked: false,
  		toclick: true,
  		mouseup: false
  	};
  
  	Draggable.prototype._init = function(){
  		if( arguments.length > 0 ){
  			this.ptr = $(arguments[0]);
  			this.outer = $(".draggable-outer");
  
  			this.is = {};
  			$.extend( this.is, this.isDefault );
  
  			var _offset = this.ptr.offset();
  			this.d = {
  				left: _offset.left,
  				top: _offset.top,
  				width: this.ptr.width(),
  				height: this.ptr.height()
  			};
  
  			this.oninit.apply( this, arguments );
  
  			this._events();
  		}
  	};
  	
  	Draggable.prototype._getPageCoords = function( event ){
  	  if( event.targetTouches && event.targetTouches[0] ){
  	    return { x: event.targetTouches[0].pageX, y: event.targetTouches[0].pageY };
  	  } else
  	    return { x: event.pageX, y: event.pageY };
  	};
  	
  	Draggable.prototype._bindEvent = function( ptr, eventType, handler ){
  	  var self = this;
  
  	  if( this.supportTouches_ )
        ptr.get(0).addEventListener( this.events_[ eventType ], handler, false );
  	  
  	  else
  	    ptr.bind( this.events_[ eventType ], handler );
  	};
  	
  	Draggable.prototype._events = function(){
  		var self = this;
  
      this.supportTouches_ = ( $.support.transition && navigator.userAgent.indexOf("Mobile") != -1 );
      this.events_ = {
        "click": this.supportTouches_ ? "touchstart" : "click",
        "down": this.supportTouches_ ? "touchstart" : "mousedown",
        "move": this.supportTouches_ ? "touchmove" : "mousemove",
        "up"  : this.supportTouches_ ? "touchend" : "mouseup"
      };
  
      this._bindEvent( $( document ), "move", function( event ){
  			if( self.is.drag ){
          event.stopPropagation();
          event.preventDefault();
  				self._mousemove( event );
  			}
  		});
      this._bindEvent( $( document ), "down", function( event ){
  			if( self.is.drag ){
          event.stopPropagation();
          event.preventDefault();
  			}
  		});
      this._bindEvent( $( document ), "up", function( event ){
  			self._mouseup( event );
  		});
  		
      this._bindEvent( this.ptr, "down", function( event ){
  			self._mousedown( event );
  			return false;
  		});
      this._bindEvent( this.ptr, "up", function( event ){
  			self._mouseup( event );
  		});
  		
  		this.ptr.find("a")
  			.click(function(){
  				self.is.clicked = true;
  
  				if( !self.is.toclick ){
  					self.is.toclick = true;
  					return false;
  				}
  			})
  			.mousedown(function( event ){
  				self._mousedown( event );
  				return false;
  			});
  
  		this.events();
  	};
  	
  	Draggable.prototype._mousedown = function( evt ){
  		this.is.drag = true;
  		this.is.clicked = false;
  		this.is.mouseup = false;
  
  		var _offset = this.ptr.offset();
  		var coords = this._getPageCoords( evt );
  		this.cx = coords.x - _offset.left;
  		this.cy = coords.y - _offset.top;
  
  		$.extend(this.d, {
  			left: _offset.left,
  			top: _offset.top,
  			width: this.ptr.width(),
  			height: this.ptr.height()
  		});
  
  		if( this.outer && this.outer.get(0) ){
  			this.outer.css({ height: Math.max(this.outer.height(), $(document.body).height()), overflow: "hidden" });
  		}
  
  		this.onmousedown( evt );
  	};
  	
  	Draggable.prototype._mousemove = function( evt ){
  		this.is.toclick = false;
  		var coords = this._getPageCoords( evt );
  		this.onmousemove( evt, coords.x - this.cx, coords.y - this.cy );
  	};
  	
  	Draggable.prototype._mouseup = function( evt ){
  		var oThis = this;
  
  		if( this.is.drag ){
  			this.is.drag = false;
  
  			if( this.outer && this.outer.get(0) ){
  
  				if( $.browser.mozilla ){
  					this.outer.css({ overflow: "hidden" });
  				} else {
  					this.outer.css({ overflow: "visible" });
  				}
  
          if( $.browser.msie && $.browser.version == '6.0' ){
           this.outer.css({ height: "100%" });
          } else {
           this.outer.css({ height: "auto" });
          }  
  			}
  
  			this.onmouseup( evt );
  		}
  	};
  	
  	window.Draggable = Draggable;
  
  })(jQuery);
  