text: // MooTools: the javascript framework.
  // Load this file's selection again by visiting: http://mootools.net/more/bf67c9e4cfa2cecce65f7e0bbe56af33 
  // Or build this file again with packager using: packager build More/Array.Extras More/Number.Format More/Object.Extras More/String.Extras
  /*
  ---
  
  script: More.js
  
  name: More
  
  description: MooTools More
  
  license: MIT-style license
  
  authors:
    - Guillermo Rauch
    - Thomas Aylott
    - Scott Kyle
    - Arian Stolwijk
    - Tim Wienk
    - Christoph Pojer
    - Aaron Newton
    - Jacob Thornton
  
  requires:
    - Core/MooTools
  
  provides: [MooTools.More]
  
  ...
  */
  
  MooTools.More = {
  	'version': '1.3.2.1',
  	'build': 'e586bcd2496e9b22acfde32e12f84d49ce09e59d'
  };
  
  
  /*
  ---
  
  script: Array.Extras.js
  
  name: Array.Extras
  
  description: Extends the Array native object to include useful methods to work with arrays.
  
  license: MIT-style license
  
  authors:
    - Christoph Pojer
    - Sebastian Markbåge
  
  requires:
    - Core/Array
    - MooTools.More
  
  provides: [Array.Extras]
  
  ...
  */
  
  (function(nil){
  
  Array.implement({
  
  	min: function(){
  		return Math.min.apply(null, this);
  	},
  
  	max: function(){
  		return Math.max.apply(null, this);
  	},
  
  	average: function(){
  		return this.length ? this.sum() / this.length : 0;
  	},
  
  	sum: function(){
  		var result = 0, l = this.length;
  		if (l){
  			while (l--) result += this[l];
  		}
  		return result;
  	},
  
  	unique: function(){
  		return [].combine(this);
  	},
  
  	shuffle: function(){
  		for (var i = this.length; i && --i;){
  			var temp = this[i], r = Math.floor(Math.random() * ( i + 1 ));
  			this[i] = this[r];
  			this[r] = temp;
  		}
  		return this;
  	},
  
  	reduce: function(fn, value){
  		for (var i = 0, l = this.length; i < l; i++){
  			if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this);
  		}
  		return value;
  	},
  
  	reduceRight: function(fn, value){
  		var i = this.length;
  		while (i--){
  			if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this);
  		}
  		return value;
  	}
  
  });
  
  })();
  
  
  /*
  ---
  
  script: Object.Extras.js
  
  name: Object.Extras
  
  description: Extra Object generics, like getFromPath which allows a path notation to child elements.
  
  license: MIT-style license
  
  authors:
    - Aaron Newton
  
  requires:
    - Core/Object
    - /MooTools.More
  
  provides: [Object.Extras]
  
  ...
  */
  
  (function(){
  
  var defined = function(value){
  	return value != null;
  };
  
  var hasOwnProperty = Object.prototype.hasOwnProperty;
  
  Object.extend({
  
  	getFromPath: function(source, parts){
  		if (typeof parts == 'string') parts = parts.split('.');
  		for (var i = 0, l = parts.length; i < l; i++){
  			if (hasOwnProperty.call(source, parts[i])) source = source[parts[i]];
  			else return null;
  		}
  		return source;
  	},
  
  	cleanValues: function(object, method){
  		method = method || defined;
  		for (var key in object) if (!method(object[key])){
  			delete object[key];
  		}
  		return object;
  	},
  
  	erase: function(object, key){
  		if (hasOwnProperty.call(object, key)) delete object[key];
  		return object;
  	},
  
  	run: function(object){
  		var args = Array.slice(arguments, 1);
  		for (var key in object) if (object[key].apply){
  			object[key].apply(object, args);
  		}
  		return object;
  	}
  
  });
  
  })();
  
  
  /*
  ---
  
  script: Locale.js
  
  name: Locale
  
  description: Provides methods for localization.
  
  license: MIT-style license
  
  authors:
    - Aaron Newton
    - Arian Stolwijk
  
  requires:
    - Core/Events
    - /Object.Extras
    - /MooTools.More
  
  provides: [Locale, Lang]
  
  ...
  */
  
  (function(){
  
  var current = null,
  	locales = {},
  	inherits = {};
  
  var getSet = function(set){
  	if (instanceOf(set, Locale.Set)) return set;
  	else return locales[set];
  };
  
  var Locale = this.Locale = {
  
  	define: function(locale, set, key, value){
  		var name;
  		if (instanceOf(locale, Locale.Set)){
  			name = locale.name;
  			if (name) locales[name] = locale;
  		} else {
  			name = locale;
  			if (!locales[name]) locales[name] = new Locale.Set(name);
  			locale = locales[name];
  		}
  
  		if (set) locale.define(set, key, value);
  
  		/*<1.2compat>*/
  		if (set == 'cascade') return Locale.inherit(name, key);
  		/*</1.2compat>*/
  
  		if (!current) current = locale;
  
  		return locale;
  	},
  
  	use: function(locale){
  		locale = getSet(locale);
  
  		if (locale){
  			current = locale;
  
  			this.fireEvent('change', locale);
  
  			/*<1.2compat>*/
  			this.fireEvent('langChange', locale.name);
  			/*</1.2compat>*/
  		}
  
  		return this;
  	},
  
  	getCurrent: function(){
  		return current;
  	},
  
  	get: function(key, args){
  		return (current) ? current.get(key, args) : '';
  	},
  
  	inherit: function(locale, inherits, set){
  		locale = getSet(locale);
  
  		if (locale) locale.inherit(inherits, set);
  		return this;
  	},
  
  	list: function(){
  		return Object.keys(locales);
  	}
  
  };
  
  Object.append(Locale, new Events);
  
  Locale.Set = new Class({
  
  	sets: {},
  
  	inherits: {
  		locales: [],
  		sets: {}
  	},
  
  	initialize: function(name){
  		this.name = name || '';
  	},
  
  	define: function(set, key, value){
  		var defineData = this.sets[set];
  		if (!defineData) defineData = {};
  
  		if (key){
  			if (typeOf(key) == 'object') defineData = Object.merge(defineData, key);
  			else defineData[key] = value;
  		}
  		this.sets[set] = defineData;
  
  		return this;
  	},
  
  	get: function(key, args, _base){
  		var value = Object.getFromPath(this.sets, key);
  		if (value != null){
  			var type = typeOf(value);
  			if (type == 'function') value = value.apply(null, Array.from(args));
  			else if (type == 'object') value = Object.clone(value);
  			return value;
  		}
  
  		// get value of inherited locales
  		var index = key.indexOf('.'),
  			set = index < 0 ? key : key.substr(0, index),
  			names = (this.inherits.sets[set] || []).combine(this.inherits.locales).include('en-US');
  		if (!_base) _base = [];
  
  		for (var i = 0, l = names.length; i < l; i++){
  			if (_base.contains(names[i])) continue;
  			_base.include(names[i]);
  
  			var locale = locales[names[i]];
  			if (!locale) continue;
  
  			value = locale.get(key, args, _base);
  			if (value != null) return value;
  		}
  
  		return '';
  	},
  
  	inherit: function(names, set){
  		names = Array.from(names);
  
  		if (set && !this.inherits.sets[set]) this.inherits.sets[set] = [];
  
  		var l = names.length;
  		while (l--) (set ? this.inherits.sets[set] : this.inherits.locales).unshift(names[l]);
  
  		return this;
  	}
  
  });
  
  /*<1.2compat>*/
  var lang = MooTools.lang = {};
  
  Object.append(lang, Locale, {
  	setLanguage: Locale.use,
  	getCurrentLanguage: function(){
  		var current = Locale.getCurrent();
  		return (current) ? current.name : null;
  	},
  	set: function(){
  		Locale.define.apply(this, arguments);
  		return this;
  	},
  	get: function(set, key, args){
  		if (key) set += '.' + key;
  		return Locale.get(set, args);
  	}
  });
  /*</1.2compat>*/
  
  })();
  
  
  /*
  ---
  
  name: Locale.en-US.Number
  
  description: Number messages for US English.
  
  license: MIT-style license
  
  authors:
    - Arian Stolwijk
  
  requires:
    - /Locale
  
  provides: [Locale.en-US.Number]
  
  ...
  */
  
  Locale.define('en-US', 'Number', {
  
  	decimal: '.',
  	group: ',',
  
  /* 	Commented properties are the defaults for Number.format
  	decimals: 0,
  	precision: 0,
  	scientific: null,
  
  	prefix: null,
  	suffic: null,
  
  	// Negative/Currency/percentage will mixin Number
  	negative: {
  		prefix: '-'
  	},*/
  
  	currency: {
  //		decimals: 2,
  		prefix: '$ '
  	}/*,
  
  	percentage: {
  		decimals: 2,
  		suffix: '%'
  	}*/
  
  });
  
  
  
  
  /*
  ---
  name: Number.Format
  description: Extends the Number Type object to include a number formatting method.
  license: MIT-style license
  authors: [Arian Stolwijk]
  requires: [Core/Number, Locale.en-US.Number]
  # Number.Extras is for compatibility
  provides: [Number.Format, Number.Extras]
  ...
  */
  
  
  Number.implement({
  
  	format: function(options){
  		// Thanks dojo and YUI for some inspiration
  		var value = this;
  		options = options ? Object.clone(options) : {};
  		var getOption = function(key){
  			if (options[key] != null) return options[key];
  			return Locale.get('Number.' + key);
  		};
  
  		var negative = value < 0,
  			decimal = getOption('decimal'),
  			precision = getOption('precision'),
  			group = getOption('group'),
  			decimals = getOption('decimals');
  
  		if (negative){
  			var negativeLocale = getOption('negative') || {};
  			if (negativeLocale.prefix == null && negativeLocale.suffix == null) negativeLocale.prefix = '-';
  			['prefix', 'suffix'].each(function(key){
  				if (negativeLocale[key]) options[key] = getOption(key) + negativeLocale[key];
  			});
  
  			value = -value;
  		}
  
  		var prefix = getOption('prefix'),
  			suffix = getOption('suffix');
  
  		if (decimals !== '' && decimals >= 0 && decimals <= 20) value = value.toFixed(decimals);
  		if (precision >= 1 && precision <= 21) value = (+value).toPrecision(precision);
  
  		value += '';
  		var index;
  		if (getOption('scientific') === false && value.indexOf('e') > -1){
  			var match = value.split('e'),
  				zeros = +match[1];
  			value = match[0].replace('.', '');
  
  			if (zeros < 0){
  				zeros = -zeros - 1;
  				index = match[0].indexOf('.');
  				if (index > -1) zeros -= index - 1;
  				while (zeros--) value = '0' + value;
  				value = '0.' + value;
  			} else {
  				index = match[0].lastIndexOf('.');
  				if (index > -1) zeros -= match[0].length - index - 1;
  				while (zeros--) value += '0';
  			}
  		}
  
  		if (decimal != '.') value = value.replace('.', decimal);
  
  		if (group){
  			index = value.lastIndexOf(decimal);
  			index = (index > -1) ? index : value.length;
  			var newOutput = value.substring(index),
  				i = index;
  
  			while (i--){
  				if ((index - i - 1) % 3 == 0 && i != (index - 1)) newOutput = group + newOutput;
  				newOutput = value.charAt(i) + newOutput;
  			}
  
  			value = newOutput;
  		}
  
  		if (prefix) value = prefix + value;
  		if (suffix) value += suffix;
  
  		return value;
  	},
  
  	formatCurrency: function(){
  		var locale = Locale.get('Number.currency') || {};
  		if (locale.scientific == null) locale.scientific = false;
  		if (locale.decimals == null) locale.decimals = 2;
  
  		return this.format(locale);
  	},
  
  	formatPercentage: function(){
  		var locale = Locale.get('Number.percentage') || {};
  		if (locale.suffix == null) locale.suffix = '%';
  		if (locale.decimals == null) locale.decimals = 2;
  
  		return this.format(locale);
  	}
  
  });
  
  
  /*
  ---
  
  script: String.Extras.js
  
  name: String.Extras
  
  description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).
  
  license: MIT-style license
  
  authors:
    - Aaron Newton
    - Guillermo Rauch
    - Christopher Pitt
  
  requires:
    - Core/String
    - Core/Array
    - MooTools.More
  
  provides: [String.Extras]
  
  ...
  */
  
  (function(){
  
  var special = {
  	'a': /[àáâãäåăą]/g,
  	'A': /[ÀÁÂÃÄÅĂĄ]/g,
  	'c': /[ćčç]/g,
  	'C': /[ĆČÇ]/g,
  	'd': /[ďđ]/g,
  	'D': /[ĎÐ]/g,
  	'e': /[èéêëěę]/g,
  	'E': /[ÈÉÊËĚĘ]/g,
  	'g': /[ğ]/g,
  	'G': /[Ğ]/g,
  	'i': /[ìíîï]/g,
  	'I': /[ÌÍÎÏ]/g,
  	'l': /[ĺľł]/g,
  	'L': /[ĹĽŁ]/g,
  	'n': /[ñňń]/g,
  	'N': /[ÑŇŃ]/g,
  	'o': /[òóôõöøő]/g,
  	'O': /[ÒÓÔÕÖØ]/g,
  	'r': /[řŕ]/g,
  	'R': /[ŘŔ]/g,
  	's': /[ššş]/g,
  	'S': /[ŠŞŚ]/g,
  	't': /[ťţ]/g,
  	'T': /[ŤŢ]/g,
  	'ue': /[ü]/g,
  	'UE': /[Ü]/g,
  	'u': /[ùúûůµ]/g,
  	'U': /[ÙÚÛŮ]/g,
  	'y': /[ÿý]/g,
  	'Y': /[ŸÝ]/g,
  	'z': /[žźż]/g,
  	'Z': /[ŽŹŻ]/g,
  	'th': /[þ]/g,
  	'TH': /[Þ]/g,
  	'dh': /[ð]/g,
  	'DH': /[Ð]/g,
  	'ss': /[ß]/g,
  	'oe': /[œ]/g,
  	'OE': /[Œ]/g,
  	'ae': /[æ]/g,
  	'AE': /[Æ]/g
  },
  
  tidy = {
  	' ': /[\xa0\u2002\u2003\u2009]/g,
  	'*': /[\xb7]/g,
  	'\'': /[\u2018\u2019]/g,
  	'"': /[\u201c\u201d]/g,
  	'...': /[\u2026]/g,
  	'-': /[\u2013]/g,
  //	'--': /[\u2014]/g,
  	'&raquo;': /[\uFFFD]/g
  };
  
  var walk = function(string, replacements){
  	var result = string, key;
  	for (key in replacements) result = result.replace(replacements[key], key);
  	return result;
  };
  
  var getRegexForTag = function(tag, contents){
  	tag = tag || '';
  	var regstr = contents ? "<" + tag + "(?!\\w)[^>]*>([\\s\\S]*?)<\/" + tag + "(?!\\w)>" : "<\/?" + tag + "([^>]+)?>",
  		reg = new RegExp(regstr, "gi");
  	return reg;
  };
  
  String.implement({
  
  	standardize: function(){
  		return walk(this, special);
  	},
  
  	repeat: function(times){
  		return new Array(times + 1).join(this);
  	},
  
  	pad: function(length, str, direction){
  		if (this.length >= length) return this;
  
  		var pad = (str == null ? ' ' : '' + str)
  			.repeat(length - this.length)
  			.substr(0, length - this.length);
  
  		if (!direction || direction == 'right') return this + pad;
  		if (direction == 'left') return pad + this;
  
  		return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil());
  	},
  
  	getTags: function(tag, contents){
  		return this.match(getRegexForTag(tag, contents)) || [];
  	},
  
  	stripTags: function(tag, contents){
  		return this.replace(getRegexForTag(tag, contents), '');
  	},
  
  	tidy: function(){
  		return walk(this, tidy);
  	},
  
  	truncate: function(max, trail, atChar){
  		var string = this;
  		if (trail == null && arguments.length == 1) trail = '…';
  		if (string.length > max){
  			string = string.substring(0, max);
  			if (atChar){
  				var index = string.lastIndexOf(atChar);
  				if (index != -1) string = string.substr(0, index);
  			}
  			if (trail) string += trail;
  		}
  		return string;
  	}
  
  });
  
  })();
  
  