//*** Javascript overrides
Ext.applyIf(Date, {
	getLongMonthName : function(month) {
		var months = [
			'January', 
			'Febraury', 
			'March', 
			'April', 
			'May', 
			'June', 
			'July', 
			'August', 
			'September', 
			'October', 
			'November', 
			'December'
		];
		return months[month];
	}
});
Ext.applyIf(Math, {
    /**
     * extend Math class with a randRange method
     * @return {Number} A random number greater than or equal to min and less than or equal to max.
     */
    randRange : function(min, max) {
      	return Math.max(Math.min(Math.round(Math.random() * max), max), min);
    }
});
//*** Ext.js overrides
Ext.override(Ext.Element, {
	getHtml : function() {
		return this.dom.innerHTML;
	},
	getTag : function() {
		return this.dom.tagName.lower();
	},
	removeStyles : function() {
		this.dom.removeAttribute('style');
	},
	setValue : function(v) {
		this.dom.value = v;
	}
});
Ext.override(Ext.data.Store, {
    /**
     * Sort by multiple fields in the specified order.
     * @param {Array} An Array of field sort specifications, or, if ascending
     * sort is required on all columns, an Array of field names. A field specification
     * looks like:<pre><code>
{
    field: 'orderNumber',
    direction: 'ASC'
}
</code><pre>
     */
    sortByFields: function(fields) {
        
//      Collect sort type functions,
//      Convert string field names to field+direction spec objects.
        var st = [];
        for (var i = 0; i < fields.length; i++) {
            if (typeof fields[i] == 'string') {
                fields[i] = {
                    field: fields[i],
                    direction: 'ASC'
                };
            }
            st.push(this.fields.get(fields[i].field).sortType);
        }

        var fn = function(r1, r2) {
            var result;
            for (var i = 0; !result && i < fields.length; i++) {
                var v1 = st[i](r1.data[fields[i].field]);
                var v2 = st[i](r2.data[fields[i].field]);
                result = (v1 > v2) ? 1 : ((v1 < v2) ? -1 : 0);
                if (fields[i].direction == 'DESC') result = -result;
            }
            return result;
        };
        this.data.sort('ASC', fn);
        if(this.snapshot && this.snapshot != this.data){
            this.snapshot.sort('ASC', fn);
        }
        this.fireEvent("datachanged", this);
    }
});



//*** required
function $(e)
{	
	return (Ext.get(e)) ? Ext.get(e) : Ext.get($Q('*[name='+e+']')[0]);
	//return Ext.get(e);
}
function $$(e, f)
{
	return (f) ? Ext.select(e, f) : Ext.select(e);
}
function $Q(e)
{
	return Ext.DomQuery.select(e);
}
function $F(c, element, where)
{
	var form = undefined;
	if (Ext.type(c) == 'object' || (Ext.type(c) == 'object' && (element || (element && where)))) {
		form = IP.createForm(c, element, where);
	} else {
		form = $(c) ? $(c) : $($Q('form[name='+c+']')[0]);
	}
	return form;
}



//*** base class
var IP = {
	Ajax : {
		//*** properties
		config : {
			url : '',
			scope : undefined,
			params : {},
			method : 'POST',
			success : undefined,
			failure : undefined,
			disableCaching : true,
			headers : undefined
		},
		//*** methods
		add: function(n, v) {
			this.setParameter(n, v);
		},
		addListener : function(ev, handler) {
			if ((ev == 'success' || ev == 'failure') && Ext.type(handler) == 'function') this.config[ev] = handler;
		},
		clear : function() {
			this.config.params = {};
		},
		go: function(c) {
			this.request(c);
		},
		on : function(ev, handler) {
			this.addListener(ev, handler);
		},
		request: function(c) {
			if (Ext.type(c) == 'object') Ext.apply(this.config, c, this.config);
			Ext.Ajax.request(this.config);
		},
		setDisableCaching : function(v) {
			this.config.disableCaching = v;
		},
		setHeaders : function(v) {
			this.config.headers = v;
		},
		setMethod : function(v) {
			this.config.method = v;
		},
		setParameter : function(n, v) {
			this.config.params[n] = v;
		},
		setScope : function(v) {
			this.config.scope = v;
		},
		setUrl : function(v) {
			this.config.url = v;
		}
	},
	Button : {
		//*** methods
		disable: function(e){
			$$(e).each(function(o){
				if (o.is('input[type=button]') || o.is('input[type=submit]')) o.dom.disabled = true;
			});
		},
		enable: function(e){
			$$(e).each(function(o){
				if (o.is('input[type=button]') || o.is('input[type=submit]')) o.dom.disabled = false;
			});
		},
		toggle: function(e){
			$$(e).each(function(o){
				if (o.is('input[type=button]') || o.is('input[type=submit]')) o.dom.disabled = !o.dom.disabled;
			});
		}
	},
	Client : {},
	components : {},
	createForm : function(c, element, where) {
		if (Ext.type(c) == 'object') {
			var f = $F(c.name);
			if (!f) {
				var e = '<form name="'+c.name+'"></form>';
				if (element && where) {
					f = $(element).insertHtml(where, e, true);
				} else if (element && !where) {
					f = $(element).insertHtml('afterEnd', e, true);
				} else {
					f = Ext.getBody().insertHtml('afterBegin', e, true);
				}
			}
			if (f) {
				f.dom.action = c.action;
				f.dom.method = c.method;
				f.dom.onsubmit = function() {
					return c.onsubmit;
				};
			}
			return f;
		}
	},
	Cookies : {
		//*** public properties
		provider : undefined,
		//*** public events
		clear : function(key) {
			this.provider.clear(key);
		},
		get : function(key) {
			if (Ext.isEmpty(this.provider)) this.setProvider();
			return this.provider.get(key);
		},
		getCookie : function(key) {
			this.get(key);			
		},
		set : function(key, value) {
			if (Ext.isEmpty(this.provider)) this.setProvider();
			this.provider.set(key, value);
		},
		setCookie : function(key, value) {
			this.set(key, value);			
		},
		setProvider : function(config) {
			if (Ext.isEmpty(this.provider)) {
				this.provider = new Ext.state.CookieProvider(config);
			}	
		}
	},
	Date : {
		format : function(d,f) {
			var r = 'UNKNOWN';
			if (!d.empty()) {
				var date = new Date(d);
				r = date.format(f);
			}
			return r;
		}
	},
	dd : {
		Drag : function(e) {
			this.el = $(e);
			this.constraint = {
				x : {
					lo : undefined,
					hi : undefined
				},
				y : {
					lo : undefined,
					hi : undefined
				}
			};
			this.gap = [0, 0];
			this.isDragging = false;
			this.init();
		}
	},
	Debug : {
		traceCount : 0,
		enabled : false,
		trace : function(s, top) {
			if (this.enabled) {
				var el = Ext.isEmpty($('x-trace')) ? Ext.getBody().insertHtml('beforeEnd', '<div id="x-trace"></div>', true) : $('x-trace');
				var where = top ? 'afterBegin' : 'beforeEnd';
				el.insertHtml(where, '<pre style="margin:0;padding:0;"><code>' + String.leftPad((this.traceCount++).toString(), 6, '') + '    ' + s + '</code></pre>');
			}
		}
	},
	fireEvent : function(e) {
		var args = [];
		for (var i = 0; i < arguments.length; i++) {
			args[i] = arguments[i];
		}
		IP.EventDispatcher.fire.apply(IP.EventDispatcher, args);
	},
	Image : function() {
		//*** properties
		this.config = {
			callback : undefined,
			url : undefined
		};
		this.image = undefined;
	},
	onEvent : function(e, f, s, m) {
		IP.EventDispatcher.addListener(e, f, s, m);
	},
	page : {
		Buttons : function() {
			//*** properties
			this.checkboxes = undefined;
			this.form = undefined;
		},
		Checkboxes : function() {
			//*** properties
			this.form = undefined;
			this.toggleBox = undefined;
		},
		Edit : function() {
			//*** properties
			this.form = undefined;
			this.fp = undefined;
			this.processing = undefined;
		},
		Form : function(c, element, where) {
			//*** properties
			this.inputs = [];
			this.form = undefined;
			this.toggle = undefined;
			//*** constructor
			var config = undefined;
			if (Ext.type(c) == 'object') {
				config = {
					action : '',
					key : undefined,
					method : 'post',
					name : 'x-form',
					onsubmit : false,
					toggle : ''
				};
				Ext.apply(config, c, config);
			} else {
				config = c;
			}
			this.form = $F(config, element, where);
			if (this.form) this.dom = this.form.dom;
			if (this.form) this.init(config);
		},
		List : function() {
			//*** properties
			this.form = undefined;
		}
	},
	Print : {
		add : function(html, returnEl) {
			return this.addToBody(html, returnEl);
		},
		addToBody : function(html, returnEl) {
			var el = this.getBody().insertHtml('beforeEnd', html, true);
			if (Ext.isEmpty(returnEl)) returnEl = false;
			return returnEl ? el : this.getShell();
		},
		clear : function() {
			this.clearBody();
		},
		clearBody : function() {
			this.getBody().update('');
		},
		getTitle : function() {
			return this.getShell().down('div');
		},
		getBody : function() {
			return this.getShell().down('div').next('div');
		},
		getShell : function() {
			return Ext.isEmpty($('x-print')) ? Ext.getBody().insertHtml('beforeEnd', '<div id="x-print"><div class="printTitle"></div><div class="printBody"></div></div>', true) : $('x-print');
		},
		setBody : function(value) {
			this.getBody().update(value);
		},
		setTitle : function(value) {
			this.getTitle().update(value);
		},
		style : function() {
			Ext.each($Q(this.getBody().getTag() + '[id=' + this.getBody().id + '] *[style]'), function(e) {
				$(e).setStyle({
					'color' : '#000000'
				});
			}, this);
		}
	},
	SwfManager : {},
	trace : function(s, top) {
		IP.Debug.trace(s, top);
	},
	Util : {
		replace : function(haystack, needle, replacement) {
			var re = new RegExp(needle, 'g');
			return haystack.replace(re, replacement);
		}
	},
	web : {
		init : function() {
		},
		page : {
			init : function() {
			},
			CategorizedList : function(el, c) { 
				//*** private properties
				this.config = {
					dataFields : [],
					dataRecord : '',
					dateField : '',
					detailTitle : '',
					hiliteColor : '#FFFFFF',
					listConfig : {
						width : '100px', 
						height : '100px',
						top : '0',
						left : '0',
						padding : '0',
						fontColor : '#000000',
						fontSize : '12px',
						fontWeight : 'normal',
						scrollbarHandleColor : '#FFFFFF',
						scrollbarTrackColor : '#000000'
					},
					navConfig : {
						width : '100px', 
						height : '100px',
						top : '0',
						left : '0',
						padding : '0',
						fontColor : '#000000',
						fontSize : '12px',
						fontWeight : 'normal',
						border : '1px solid #000000',
						scrollbarHandleColor : '#FFFFFF',
						scrollbarTrackColor : '#000000',
						levels : [
							{
								field : '',
								print : 'All Items'
							},
							{
								field : 'year',
								print : 'All Items'
							}
						
						]
					},
					noDataMsg : 'No data available.',
					root : '',
					sortByFields : [],
					url : ''
				};
				if (Ext.type(c) == 'object') Ext.apply(this.config, c, this.config);
				this.detail = undefined;
				this.ds = undefined;
				this.list = undefined;
				this.nav = undefined;
				this.parent = $(el);
				this.sortFilter = [];
			},
			ScrollBox : function(e, c, a) {
				this.source = $(e);
				this.config = {
					width : '100px',
					height : '100px',
					left : '0',
					top : '0',
					margin : '0',
					padding : '0',
					fontSize : '12px',
					color : '#000000',
					bgColor : '#000000',
					bgAlpha : '0',
					borderWidth : '0',
					borderColor : '#000000',
					borderAlpha : '0',
					scrollbarWidth : '10px',
					scrollbarMargin : '10px',
					scrollbarHandleColor : '#FFFFFF',
					scrollbarHandleAlpha : '1',
					scrollbarTrackColor : '#000000',
					scrollbarTrackAlpha : '.1'
				}
				if (Ext.type(c) == 'object') Ext.apply(this.config, c, this.config);
				this.anim = a;
				this.scrollbox = undefined;
				this.background = undefined;
				this.border = undefined;
				this.focus = undefined;
				this.mask = undefined;
				this.handle = undefined;
				this.content = undefined;
				this.handleT = undefined;
				this.handleH = undefined;
				this.track = undefined;
				this.trackT = undefined;
				this.trackH = undefined;
				this.contentH = undefined;
				this.scrollH = undefined;
				this.map = undefined;
				this.drag = undefined;
				this.scrollable = false;
				this.init();
			}
		}
	}
};
Ext.override(IP.web.page.CategorizedList, {
	//*** private methods
	buildDetail : function(r) {
		IP.Print.clear();
		IP.fireEvent('categorylist-detail');
		this.removeDetail();		
		this.clearList();
		if (Ext.isEmpty(this.detail)) {
			this.detail = this.parent.insertHtml('beforeEnd', '<div></div>', true);
			this.detail.setStyle({
				'position' : 'absolute',
				'width' : this.config.listConfig.width,
				'height' : this.config.listConfig.height,
				'left' : this.config.listConfig.left,
				'top' : this.config.listConfig.top,
				'padding' : this.config.listConfig.padding,
				'font-size' : this.config.listConfig.fontSize,
				'font-weight' : 'bold',
				'color' : this.config.listConfig.fontColor
			});
		}
		this.detail.load({
			url : this.config.root + r.get('url'),
			callback : function(el, success, response) {
				if (!success || response.responseText.indexOf('n : \'unavailable\'') != -1) this.detail.update('<p style="font-size:18px;color:' + this.config.hiliteColor + ';">Oops!</p><p>You have requested a page that might have been removed or is temporarily unavailable.</p>');
					
					this.detail.dom.innerHTML = IP.Util.replace(unescape(this.detail.dom.innerHTML).htmlDecode().remove(window.location), '<!--root-->', this.config.root);
					IP.Print.add('<div style="padding:0 0 15px 0;font-size:28px;font-weight:bold;">' + this.detailTitle + '</div>');
					IP.Print.add(this.detail.dom.innerHTML);
					IP.Print.style();
					var box = new IP.web.page.ScrollBox(this.detail, {
						width : this.config.listConfig.width,
						height : this.config.listConfig.height,
						left : this.config.listConfig.left,
						top : this.config.listConfig.top,
						padding : this.config.listConfig.padding,
						fontSize : this.config.listConfig.fontSize,
						color : this.config.listConfig.fontColor,
						scrollbarHandleColor : this.config.listConfig.scrollbarHandleColor,
						scrollbarTrackColor : this.config.listConfig.scrollbarTrackColor
					});
					this.onBeforeDetailRender(box.content, r);
					box.refresh();
					box.show({
						duration : .5
					});
			},
			method : 'GET',
			scope : this,
			text : this.config.loadMsg,
			nocache : false
		});
	},
	buildList : function() {
		IP.Print.clear();
		IP.fireEvent('categorylist-list');
		this.removeDetail();
		this.clearList();
		this.showList();
		this.ds.clearFilter();
		this.filter();
		var box = new IP.web.page.ScrollBox(this.list, {
			width : (this.list.getWidth() - this.list.getPadding('r')).toString() + 'px',
			height : this.config.listConfig.height,
			left : this.config.listConfig.left,
			top : this.config.listConfig.top,
			padding : '0 ' + this.list.getPadding('r').toString() + 'px 0 0',
			fontSize : this.config.listConfig.fontSize,
			color : this.config.listConfig.fontColor,
			scrollbarHandleColor : this.config.listConfig.scrollbarHandleColor,
			scrollbarTrackColor : this.config.listConfig.scrollbarTrackColor
		});
		if (this.ds.getCount() == 0) {
			var el = box.content.insertHtml('beforeEnd', String.format('<div><div style="font-size:14px;">{0}</div></div>', this.config.noDataMsg), true);
			el.setStyle({
				'padding' : '8px 25px'
			});
			IP.Print.add(String.format('<div style="font-size:18px;">{0}</div>', this.config.noDataMsg));
		} else {
			this.ds.each(function(r){
				var layer = new Ext.Layer({
					constrain : false, 
					shadow : false,
					shim : false
				});
				this.createListItem(r, box, layer);
			}, this);
		}
		box.refresh();
		box.onScroll = IP.Delegate.create(this, this.onScroll_private);
		box.show({
			duration : .5
		});
		IP.Print.style();
	},
	buildListItem : function(r, el, layer) {
		var bg = el.down('div');
		var text = bg.next('div');
		text.setStyle({
			'padding' : '8px 25px',
			'font-size' : '12px'
		});
		bg.setStyle({
			'width' : text.getWidth().toString() + 'px',
			'height' : text.getHeight().toString() + 'px',
			'position' : 'absolute',
			'background-color' : this.config.hiliteColor
		});
		bg.setOpacity(.1);
		bg.hide();
		el.setStyle({
			'width' : '100%',
			'cursor' : 'pointer'
		});
		layer.setStyle({
			'padding' : '15px',
			'background-color' : this.config.hiliteColor,
			'border' : '5px solid #FFFFFF',
			'width' : '450px',
			'color' : '#FFFFFF',
			'font-family' : 'arial',
			'font-size' : '12px',
			'line-height' : '1.5em'
		});
		IP.onEvent('categorylist-detail', function() {
			delay.cancel();
			bg.stopFx();
			bg.hide();
			layer.hide();
		}, this);
		IP.onEvent('categorylist-scroll', function() {
			delay.cancel();
			layer.hide();
		}, this);
		IP.onEvent('categorylist-hidelayer', function() {
			delay.cancel();
			layer.hide();
		}, this);
		var delay = new Ext.util.DelayedTask();
		el.unselectable();
		el.on({
			'click' : {
				fn : function(e) {
					this.onListItemClick(r);
				},
				scope : this
			},
			'mouseover' : {
				fn : function(e) {
					e.preventDefault();
					bg.stopFx();
					bg.show();
					if (!layer.isVisible()) {
						delay.delay(750, function() {
							layer.setXY([e.getXY()[0]+layer.getBorderWidth('l')-(layer.getWidth()/2), e.getXY()[1]+layer.getBorderWidth('t')-layer.getHeight()-10]);
							layer.show({duration:1});
						}, this);
					}
				},
				scope : this
			},
			'mousemove' : {
				fn : function(e) {
					e.preventDefault();
					if (layer.isVisible()) {
						layer.setXY([e.getXY()[0]+layer.getBorderWidth('l')-(layer.getWidth()/2), e.getXY()[1]+layer.getBorderWidth('t')-layer.getHeight()-10]);
					}
				},
				scope : this
			},
			'mouseout' : {
				fn : function(e, t) {
					if (!e.within(el, true)) {
						e.preventDefault();	
						bg.stopFx();
						bg.hide();
						delay.cancel();
						layer.hide();
					}
				},
				scope : this
			}
		});
	},
	buildNav : function() {
		var box = new IP.web.page.ScrollBox(this.nav, {
			width : this.config.navConfig.width,
			height : this.config.navConfig.height,
			left : this.config.navConfig.left,
			top : this.config.navConfig.top,
			padding : this.config.navConfig.padding,
			fontSize : this.config.navConfig.fontSize,
			color : this.config.navConfig.fontColor,
			scrollbarHandleColor : this.config.navConfig.scrollbarHandleColor,
			scrollbarTrackColor : this.config.navConfig.scrollbarTrackColor
		});
		box.mask.setStyle({
			'border-right' : this.config.navConfig.border
		});
		this.buildNavLevels(box);
		box.refresh();
		box.show({
			duration : .5
		});
	},
	buildNavItem : function(parent, title, printTitle, sort, isBold, indent) {
		if (Ext.isEmpty(isBold)) isBold = false;
		if (Ext.isEmpty(indent)) indent = false;
		var el = parent.insertHtml('beforeEnd', String.format('<div><div></div><div>{0}</div></div>', title), true);
		if (this.selectedId == undefined) this.selectedId = el.id;
		var bg = el.down('div');
		var text = bg.next('div');
		text.setStyle({
			'padding' : (indent ? '4px 10px 4px 20px' : '4px 10px 4px 10px'),
			'font-size' : this.config.navConfig.fontSize,
			'text-align' : 'left',
			'color' : this.config.navConfig.fontColor,
			'line-height' : 'normal',
			'background-color' : 'transparent'
		});
		if (isBold) {
			text.setStyle({
				'font-weight' : 'bold'
			});
		}
		bg.setStyle({
			'width' : text.getWidth().toString() + 'px',
			'height' : (text.getHeight()+1).toString() + 'px',
			'position' : 'absolute',
			'background-color' : this.config.hiliteColor
		});
		bg.hide();
		el.setStyle({
			'cursor' : 'pointer'
		});
		IP.onEvent('categorylist-list', function() {
			bg.setOpacity(.1);
			bg.hide();
			if (el.id == this.selectedId) {
				IP.Print.add(String.format('<div style="padding:0 0 15px 0;font-size:28px;font-weight:bold;">{0}</div>', printTitle));
				text.setStyle({
					'background-color' : this.config.hiliteColor,
					'color' : '#ffffff'
				});
			} else {
				text.setStyle({
					'background-color' : 'transparent',
					'color' : this.config.navConfig.fontColor
				});
			}
			
		}, this);
		IP.onEvent('categorylist-detail', function() {
			if (el.id == this.selectedId) {
				bg.show();
				bg.setOpacity(.1);
			} else {
				bg.setOpacity(.1);
				bg.hide();
			}
			text.setStyle({
				'background-color' : 'transparent',
				'color' : this.config.navConfig.fontColor
			});
		}, this);
		el.unselectable();
		el.on({
			'click' : {
				fn: function(e) {
					if (bg.isVisible()) {
						e.preventDefault();
						this.selectedId = el.id;
						this.sortFilter = sort;
						this.buildList();
					}
				},
				scope : this
			},
			'mouseover' : {
				fn: function(e) {
					e.preventDefault();
					if (bg.isVisible()) bg.setOpacity(.1);
					if (el.id != this.selectedId) bg.show();
				},
				scope : this
			},
			'mouseout' : {
				fn: function(e) {
					e.preventDefault();
					if (el.id == this.selectedId) {
						bg.setOpacity(.1);

					} else {
						bg.setOpacity(.1);
						bg.hide();
					}
				},
				scope : this
			}
		});
	},
	buildNavLevels : function(box) {
		this.buildNavItem(box.content, 'View All', 'All News', [], true);
	},
	clearList : function() {
		this.list.update('');
	},
	createListItem : function(r, box) {
	},
	createStore : function() {
		var proxy = new Ext.data.HttpProxy({
			url : this.config.url
		});
		var reader = new Ext.data.XmlReader({
			record : this.config.dataRecord
		}, this.config.dataFields);
		this.ds = new Ext.data.Store({
			proxy : proxy,
			reader : reader
		});
		this.ds.on('load', this.onload_private, this);
	},
	filter : function(filter) {
		if (Ext.type(filter) != 'array') filter = this.sortFilter;
		this.ds.filterBy(function(r) {
			var flag = true;
			Ext.each(filter, function(item) {
				if (flag) flag = r.get(item.field) == item.value;
			}, this);
			return flag;
		}, this);	
	},
	getList : function() {
		return this.list;
	},
	getNav : function() {
		return this.nav;
	},
	init : function() {
		if (Ext.isEmpty(this.nav)) this.nav = this.parent.insertHtml('beforeEnd', '<div></div>', true);
		if (Ext.isEmpty(this.list)) this.list = this.parent.insertHtml('beforeEnd', '<div></div>', true);
		this.nav.setStyle({
			'position' : 'absolute',
			'width' : this.config.listConfig.width,
			'height' : this.config.listConfig.height,
			'left' : this.config.listConfig.left,
			'top' : this.config.listConfig.top,
			'padding' : this.config.listConfig.padding,
			'font-size' : this.config.listConfig.fontSize,
			'font-weight' : this.config.listConfig.fontWeight,
			'color' : this.config.listConfig.fontColor
		});
		this.list.setStyle({
			'position' : 'absolute',
			'width' : this.config.listConfig.width,
			'height' : this.config.listConfig.height,
			'left' : this.config.listConfig.left,
			'top' : this.config.listConfig.top,
			'padding' : this.config.listConfig.padding,
			'font-size' : this.config.listConfig.fontSize,
			'font-weight' : this.config.listConfig.fontWeight,
			'color' : this.config.listConfig.fontColor
		});		
		this.prepareFields();
		this.sort();
		this.buildNav();
		this.buildList();		
	},
	prepareFields : function() {
		if (!Ext.isEmpty(this.config.dateField)) {
			this.ds.each(function(r) {
				var date = r.get(this.config.dateField);
				if (!Ext.isEmpty(date)) {
					r.set('year', Ext.util.Format.date(date, 'Y'));
					r.set('month', Ext.util.Format.date(date, 'm'));
					r.set('day', Ext.util.Format.date(date, 'd'));
					r.set(this.config.dateField, Ext.util.Format.date(date, 'F j, Y'));
				}
			}, this);
		}
	},
	removeDetail : function() {
		if (!Ext.isEmpty(this.detail)) {
			this.detail.remove();
			this.detail = undefined;
		}
	},
	removeList : function() {
		if (!Ext.isEmpty(this.list)) {
			this.list.remove();
			this.list = undefined;
		}
	},
	showList : function() {
		this.removeDetail();
		this.list.show();
	},
	sort : function(fields) {
		var sort = !Ext.isEmpty(fields) ? fields : this.config.sortByFields;
		this.ds.sortByFields(sort);
	},
	//*** public methods
	load : function() {
		this.createStore();
		this.ds.load();
	},
	loadDetail : function(url) {
		this.buildDetail(url);
	},
	setDataFields : function(fields) {
		if (Ext.type(fields) == 'array') {
			this.config.dataFields = fields;
		}
	},
	setDateField : function(date) {
		if (Ext.type(date) == 'string') {
			this.config.dateField = date;
		}
	},
	setListConfig : function(config) {
		if (Ext.type(config) == 'object') {
			this.config.listConfig = config;
		}
	},
	setNavConfig : function(config) {
		if (Ext.type(config) == 'object') {
			this.config.navConfig = config;
		}
	},
	setRepeatingRecord : function(record) {
		if (Ext.type(record) == 'string') {
			this.config.dataRecord = record;
		}
	},
	setSortByFields : function(fields) {
		if (Ext.type(fields) == 'array') this.config.sortByFields = fields;
	},
	setSortFilter : function(filter) {
		if (Ext.type(filter) == 'array') this.sortFilter = filter;
	},
	setUrl : function(url) {
		this.config.url = url;
	},
	//*** private events
	onload_private : function() {
		this.onLoad();
		this.init();
	},
	onScroll_private : function() {
		IP.fireEvent('categorylist-scroll');
	},
	//*** public events
	onBeforeDetailRender : function() {
	},
	onDetailLoad : function() {
	},
	onListItemClick : function() {
	},
	onLoad : function() {
	}
});
IP.Delegate = {
	create : function(scope, fn, argz) {
		return fn.createDelegate(scope, argz);
	}
};
IP.EventDispatcher = {
	ec : new Ext.util.MixedCollection(),
	addEvent : function(e) {
		if (e) {
			if (!this.hasEvent(e)) this.ec.add(e,[]);
		}
	},
	addListener : function(e,f,s,m) {
		if (e && f)
		{
			if (!this.hasEvent(e)) this.ec.add(e,[]);
			if (m || (!m && !this.hasListener(f,s))) {
				this.ec.get(e).push({
					fn : f,
					scope : s,
					delegate : ((s) ? IP.Delegate.create(s,f) : undefined)
				});
			}
		}
	},
	fire : function(e) {
		var args = [];
		for (var i = 1; i < arguments.length; i++) {
			args[i-1] = arguments[i];
		}
		if (e) {
			if (this.hasEvent(e)) {
				Ext.each(this.ec.get(e), function(o){						
					if (o) (o.delegate) ? o.delegate.apply(this, args) : o.fn.apply(this, args);
				}, this);
			}
		}
	},
	hasEvent : function(e) {
		return (e) ? this.ec.containsKey(e) : false;
	},
	hasListener : function(f,s) {	
		var has = false;
		if (f) {
			this.ec.each(function(a){
				Ext.each(a, function(o) {
					if ((s && o.fn == f && o.scope == s) || (!s && o.fn == f && !o.scope)) has = true;
				});					
			});
		}
		return has;
	},
	on : function(e,f,s,m) {
		this.addListener(e,f,s,m);
	},
	removeEvent : function(e) {
		if (e) this.ec.removeKey(e);
	},
	removeListener : function(f,s) {
		if (f) {
			this.ec.each(function(a){
				Ext.each(a, function(o) {
					if ((s && o.fn == f && o.scope == s) || (!s && o.fn == f && !o.scope)) a.remove(o);
				});					
			});
		}
	},
	un : function(f,s) {
		this.removeListener(f,s);
	}
};
Ext.apply(IP.web.page.ScrollBox.prototype, {
	//*** methods
	init : function() {
		this.source.hide();
		var sourceContent = this.source.dom.innerHTML;
		var template = {
			id : Ext.id(null, 'ip.web.page.scrollbox.'),
			tag : 'div',
			cls : 'ip-web-page-scrollbox',
			children : [
				{
					id : Ext.id(null, 'ip.web.page.scrollbox.'),
					tag : 'div',
					cls : 'ip-web-page-scrollbox-background'
				},
				{
					id : Ext.id(null, 'ip.web.page.scrollbox.'),
					tag : 'div',
					cls : 'ip-web-page-scrollbox-border'
				},
				{
					id : Ext.id(null, 'ip.web.page.scrollbox.'),
					tag : 'div',
					cls : 'ip-web-page-scrollbox-mask',
					children : [
						{
							id : Ext.id(null, 'ip.web.page.scrollbox.'),
							tag : 'div',
							cls : 'ip-web-page-scrollbox-focus'
						},
						{
							id : Ext.id(null, 'ip.web.page.scrollbox.'),
							tag : 'div',
							cls : 'ip-web-page-scrollbox-content'
						}
					]
				},
				{
					id : Ext.id(null, 'ip.web.page.scrollbox.'),
					tag : 'div',
					cls : 'ip-web-page-scrollbox-track'
				},
				{
					id : Ext.id(null, 'ip.web.page.scrollbox.'),
					tag : 'div',
					cls : 'ip-web-page-scrollbox-handle'
				}
			]
		};
		this.scrollbox = $(this.source.replaceWith(template));
		this.background = $($Q('div[id=' + this.scrollbox.id + '] div[class=ip-web-page-scrollbox-background]')[0]);
		this.border = $($Q('div[id=' + this.scrollbox.id + '] div[class=ip-web-page-scrollbox-border]')[0]);
		this.mask = $($Q('div[id=' + this.scrollbox.id + '] div[class=ip-web-page-scrollbox-mask]')[0]);
		this.content = $($Q('div[id=' + this.scrollbox.id + '] div[class=ip-web-page-scrollbox-content]')[0]);
		this.focus = $($Q('div[id=' + this.scrollbox.id + '] div[class=ip-web-page-scrollbox-focus]')[0]);
		this.handle = $($Q('div[id=' + this.scrollbox.id + '] div[class=ip-web-page-scrollbox-handle]')[0]);
		this.track = $($Q('div[id=' + this.scrollbox.id + '] div[class=ip-web-page-scrollbox-track]')[0]);
		this.content.update(sourceContent);
		this.scrollbox.setStyle({
			'position' : 'absolute',
			'width' : this.config.width,
			'height' : this.config.height,
			'left' : this.config.left,
			'top' : this.config.top,
			'margin' : this.config.margin,
			'padding' : this.config.padding
		});
		this.background.setStyle({
			'position' : 'absolute',
			'background-color' : this.config.bgColor
		});
		this.border.setStyle({
			'position' : 'absolute',
			'border' : 'solid ' + this.config.borderWidth,
			'border-color' : this.config.borderColor
		});
		this.mask.setStyle({
			'position' : 'absolute',
			'overflow' : 'hidden'
		});
		this.content.setStyle({
			'margin' : '0',
			'padding' : '0',
			'color' : this.config.color,
			'font-size' : this.config.fontSize,
			'z-index' : '20'
		});
		this.focus.setStyle({
			'position' : 'absolute',
			'width' : '10px',
			'height' : this.content.getHeight() + 'px',
			'left' : '0',
			'top' : '0',
			'z-index' : '10'
		});
		this.handle.setStyle({
			'position' : 'absolute',
			'background-color' : this.config.scrollbarHandleColor,
			'cursor' : 'pointer',
			'z-index' : '40'
		});
		this.track.setStyle({
			'position' : 'absolute',
			'background-color' : this.config.scrollbarTrackColor,
			'width' : this.config.scrollbarWidth,
			'margin' : this.config.scrollbarMargin,
			'cursor' : 'pointer',
			'z-index' : '30'
		});
		
		this.background.setXY([this.scrollbox.getX(), this.scrollbox.getY()]);
		this.background.setSize(this.scrollbox.getWidth(), this.scrollbox.getHeight());
		this.background.setOpacity(this.config.bgAlpha);

		if (Ext.isGecko) this.border.setXY([this.scrollbox.getX() + this.border.getBorderWidth('l'), this.scrollbox.getY() + this.border.getBorderWidth('t')]);
		if (!Ext.isGecko) this.border.setXY([this.scrollbox.getX(), this.scrollbox.getY()]);
		this.border.setSize(this.scrollbox.getWidth(), this.scrollbox.getHeight());
		this.border.setOpacity(this.config.borderAlpha);

		this.drag = new IP.dd.Drag(this.handle);
		Ext.EventManager.on(window, 'resize', function() {
			this.drag.setXConstraint(0, 0);
			this.drag.setYConstraint(Math.abs(this.handle.getY() - (this.track.getY() + 1)), Math.abs((this.handle.getY() + this.handleH) - (this.track.getY() + 1 + this.trackH)));
		}, this);
		Ext.EventManager.onTextResize(function(){
			this.setScroll();
		}, this);
		this.setScroll();
		this.content.hide();
		this.track.hide();
		this.handle.hide();
	},
	setScroll : function() {
		this.mask.removeAllListeners();
		this.track.removeAllListeners();
		if (!Ext.isEmpty(this.map)) Ext.destroy(this.map);
		this.track.setStyle({
			'margin' : this.config.scrollbarMargin
		});
		if (this.content.getComputedHeight() > this.background.getHeight() - this.scrollbox.getPadding('tb'))
		{

			this.mask.setXY([(this.scrollbox.getX() + this.scrollbox.getPadding('l')), this.background.getY() + this.scrollbox.getPadding('t')]);
			this.mask.setSize(this.background.getWidth() - this.scrollbox.getPadding('lr') - this.track.getWidth() - this.track.getMargins('l'), this.background.getHeight() - this.scrollbox.getPadding('tb'));
			this.track.setStyle({
				'margin' : '0'
			});

			this.track.setXY([this.background.getX() + this.background.getWidth() - this.scrollbox.getPadding('r') - this.track.getWidth(), this.background.getY() + this.scrollbox.getPadding('t')]);
			this.track.setSize(this.track.getWidth(), this.background.getHeight() - this.scrollbox.getPadding('tb'));
			//this.track.setOpacity(this.config.scrollbarTrackAlpha);

			this.handle.setXY([this.track.getX() + 1, this.track.getY() + 1]);
			this.handle.setSize(this.track.getWidth() - 2, this.track.getHeight() - 2);
			//this.handle.setOpacity(this.config.scrollbarHandleAlpha);

			if (this.mask.getY() != this.content.getY()) this.content.setY(this.mask.getY());

			this.scrollH = this.mask.getComputedHeight();
			this.contentH = this.content.getComputedHeight();
			this.trackH = this.track.getHeight() - 2;
			this.handle.setHeight(Math.max(this.scrollH / this.contentH * this.trackH, this.handle.getWidth()));
			this.handleT = this.handle.getTop();
			this.handleH = this.handle.getComputedHeight();
			this.track.on({
				'click' : function() {
					this.handle.setY(Math.max(this.handleT, Math.min(Ext.EventObject.getXY()[1]-(this.handleH/2), this.handleT+(this.trackH-this.handleH))));
					this.onDrag();
				},
				scope : this
			});
			
			this.focus.show();
			this.focus.setSize(0, this.contentH);
			this.focus.dom.setAttribute('tabindex', 100);  //*** FF hack for key events
			
			this.mask.on('mousewheel', function(e){
				var delta = e.getWheelDelta()*5;
				this.updatePos(delta);
			}, this);
			this.mask.on('mouseover', function(e){
				e.preventDefault();
				this.focus.focus();
			}, this);
			this.mask.on('mouseout', function(e){
				e.preventDefault();
				this.focus.blur();
			}, this);
			
		this.map = new Ext.KeyMap(this.mask, [
			{
				key : Ext.EventObject.UP,
				fn : function(e) {	
					var delta = 5;
					this.updatePos(delta);
				},
				scope : this
			},
			{
				key : Ext.EventObject.PAGEUP,
				fn : function(e) {	
					var delta = this.handleH;
					this.updatePos(delta);
				},
				scope : this
			},
			{
				key : Ext.EventObject.DOWN,
				fn : function(e) {	
					var delta = -5;
					this.updatePos(delta);
				},
				scope : this
			},
			{
				key : Ext.EventObject.PAGEDOWN,
				fn : function(e) {	
					var delta = -this.handleH;
					this.updatePos(delta);
				},
				scope : this
			},
			{
				key : Ext.EventObject.HOME,
				fn : function(e) {	
					var delta = this.scrollH;
					this.updatePos(delta);
				},
				scope : this
			},
			{
				key : Ext.EventObject.END,
				fn : function(e) {	
					var delta = -this.scrollH;
					this.updatePos(delta);
				},
				scope : this
			}
		]);
		this.map.stopEvent = true;
			this.map.enable();

			this.drag.setXConstraint(0, 0);
			this.drag.setYConstraint(0, this.trackH - this.handleH);
			this.drag.onDrag = IP.Delegate.create(this, this.onDrag);
			this.scrollable = true;
			
		} else {
			this.mask.setXY([(this.scrollbox.getX() + this.scrollbox.getPadding('l')), this.background.getY() + this.scrollbox.getPadding('t')]);
			this.mask.setSize(this.background.getWidth() - this.scrollbox.getPadding('lr'), this.background.getHeight() - this.scrollbox.getPadding('tb'));
			this.content.setXY([this.mask.getX(), this.mask.getY()]);
			//this.mask.removeAllListeners();
			this.focus.hide();
			//this.map.disable();
			this.scrollable = false;
		}
	},
	refresh : function() {
		this.setScroll();
	},
	show : function(config) {
		var c = this.scrollable ? {} : {callback:this.onShow};
		Ext.apply(c, config, c);
		var contentY = this.content.getY();
		this.content.setY(contentY + (this.mask.getHeight()/(this.mask.getHeight()/375*15)));
		this.content.syncFx().fadeIn(config).setY(contentY, c);
		if (this.scrollable) {
			c = {endOpacity:this.config.scrollbarTrackAlpha};
			Ext.apply(c, config, c);
			this.track.fadeIn(c);
			c = {callback:this.onShow, endOpacity:this.config.scrollbarHandleAlpha};
			Ext.apply(c, config, c);
			this.handle.fadeIn(c);
		}
	},
	updatePos : function(delta) {
		this.handle.setY(Math.max(this.handleT, Math.min(this.handle.getY()-(delta), this.handleT+(this.trackH-this.handleH))));
		this.onDrag();
	},
	//*** events
	onDrag : function() {
		var handleY = this.handle.getY();
		var p = (handleY - this.handleT) / (this.trackH - this.handleH);
		var n = (this.contentH - this.scrollH) * p;
		this.mask.scrollTo('top', n);
		this.onScroll();
	},
	onScroll : function() {
	},
	onShow : function() {
	}
});
Ext.apply(IP.dd.Drag.prototype, {
	//*** properties
	//*** methods
	clearXConstraint : function() {
		this.constraint.x.lo = undefined;
		this.constraint.x.hi = undefined;
	},
	clearYConstraint : function() {
		this.constraint.y.lo = undefined;
		this.constraint.y.hi = undefined;
	},
	init : function() {
		this.el.on('mousedown', this.onMouseDown, this, {preventDefault : true});
		Ext.getBody().on('mousemove', this.onMouseMove, this);
		Ext.getBody().on('mouseup', this.onMouseUp, this);
	},
	remove : function() {
		this.el.un('mousedown', this.onMouseDown);
		Ext.getBody().un('mousemove', this.onMouseMove);
		Ext.getBody().un('mousemove', this.onMouseMove);
		this.constraint = {
			x : {
				lo : undefined,
				hi : undefined
			},
			y : {
				lo : undefined,
				hi : undefined
			}
		};
		this.gap = [0, 0];
		this.isDragging = false;
	},
	setXConstraint : function(v1, v2) {
		if (Ext.type(v1) == 'number') this.constraint.x.lo = this.el.getXY()[0] - v1;
		if (Ext.type(v2) == 'number') this.constraint.x.hi = this.el.getXY()[0] + v2;
	},
	setYConstraint : function(v1, v2) {
		if (Ext.type(v1) == 'number') this.constraint.y.lo = this.el.getXY()[1] - v1;
		if (Ext.type(v2) == 'number') this.constraint.y.hi = this.el.getXY()[1] + v2;
	},
	//*** private events
	onMouseDown : function(e) {
		this.isDragging = true;
		this.gap = [e.getXY()[0]-this.el.getXY()[0], e.getXY()[1]-this.el.getXY()[1]];
		Ext.getBody().setStyle({
			'cursor' : 'pointer'
		});
	},
	onMouseMove : function(e) {
		if (this.isDragging) {
			e.preventDefault();
			var x = e.getXY()[0]-this.gap[0];
			var y = e.getXY()[1]-this.gap[1];
			if (this.constraint.x.lo != undefined && this.constraint.x.hi != undefined) {
				x = Math.min(Math.max(x, this.constraint.x.lo), this.constraint.x.hi);
			}
			if (this.constraint.y.lo != undefined && this.constraint.y.hi != undefined) {
				y = Math.min(Math.max(y, this.constraint.y.lo), this.constraint.y.hi);
			}
			this.el.setXY([x, y]);
			this.onDrag();
		}
	},
	onMouseUp : function(e) {
		if (this.isDragging) e.preventDefault();
		this.isDragging = false;
		Ext.getBody().setStyle({
			'cursor' : 'default'
		});
	},
	//*** public events
	onDrag : function() {
	}
});

Ext.apply(IP.Image.prototype, {
	//*** methods
	callback : function(handler) {
		if (Ext.type(handler) == 'function') this.config.callback = handler;
	},
	handler : function(handler) {
		this.callback(handler);
	},
	load: function(c) {
		if (Ext.type(c) == 'object') Ext.apply(this.config, c, this.config);
		if (this.config.url) {
			this.image = Ext.getBody().insertHtml('afterBegin', '<img />', true).hide();
			this.image.on('load', function() {
				this.config.callback();
				this.reset();
			}, this);
			this.image.dom.src = this.config.url;
		}
	},
	onload : function(handler) {
		this.callback(handler);
	},
	reset : function() {
		this.config = {
			callback : undefined,
			url : undefined
		};
		if (this.image) this.image.remove(); 
	},
	setUrl : function(v) {
		this.config.url = v;
	}
});

Ext.apply(IP.page.Buttons.prototype, {
	//*** methods
	attach : function(f, cb) {
		if (f) this.form = f;
		if (cb) this.checkboxes = cb;
		if (this.form) {
			IP.onEvent('status', this.status, this);
			IP.onEvent('remove', this.remove, this);
		}
	},
	status: function() {
		if (this.form && this.checkboxes) {
			if (this.checkboxes.verify()) {
				this.createInputs();
				this.form.setValue('fa','status');
				this.form.setValue('sl',this.checkboxes.toString());
				this.form.submit();
			} else {
				alert('Please select one or more items.');
			}
		}
	},
	remove: function() {
		if (this.form && this.checkboxes) {
			if (this.checkboxes.verify()) {
				if (confirm('Permanently delete item(s)?')) {
					this.createInputs();
					this.form.setValue('fa','delete');
					this.form.setValue('sl',this.checkboxes.toString());
					this.form.submit();
				}
			} else {
				alert('Please select one or more items.');
			}
		}
	},
	createInputs : function() {
		if (this.form) {
			if (!this.form.hasInput('fa', true)) {
				this.form.createInput({
					name : 'fa',
					type : 'hidden'
				}, 'fk');
			}
			if (!this.form.hasInput('sl', true)) {
				this.form.createInput({
					name : 'sl',
					type : 'hidden'
				}, 'fa');
			}
		}
	}
});
Ext.apply(IP.page.Checkboxes.prototype, {
	//*** methods
	attach: function(f, t) {
		if (f) this.form = f;
		if (this.form) {
			if (t) this.toggleBox = $($Q('input[name='+t+']')[0]);
			if (this.toggleBox) {
				this.toggleBox.on('click', this.toggle, this);
				$$('form[name='+this.form.dom.name+'] input[type=checkbox]').each(function(e) {
					if (e.dom != this.toggleBox.dom) e.on('click', this.update, this);
				}, this);
			}
		}
	},
	update : function() {
		if (this.form && this.toggleBox) {
			var c = 0, l = 0;
			$$('form[name='+this.form.dom.name+'] input[type=checkbox]').each(function(e) {
				if (e.dom != this.toggleBox.dom) {
					l++;
					if (e.dom.checked || e.dom.disabled) c++;
				}

			}, this);
			this.toggleBox.dom.checked = (c == l);
		}
	},
	toggle: function() {
		if (this.form && this.toggleBox) {
			var c = (this.toggleBox.dom.checked) ? 1 : 0;
			$$('form[name='+this.form.dom.name+'] input[type=checkbox]').each(function(e) {
				if (e.dom != this.toggleBox.dom) {
					if (!e.dom.disabled) e.dom.checked = c;
				}
			}, this);
		}
	},
	toString: function() {
		if (this.form && this.toggleBox) {
			var d = '|', s = '';
			$$('form[name='+this.form.dom.name+'] input[type=checkbox]').each(function(e) {
				if (e.dom != this.toggleBox.dom) {
					if (e.dom.checked && !e.dom.disabled) s += e.dom.value + '|';
				}
			}, this);
			return s.substr(0,s.length-d.length);
		}
	},
	verify: function() {
		var r = false;
		if (this.form && this.toggleBox) {
			$$('form[name='+this.form.dom.name+'] input[type=checkbox]').each(function(e) {
				if (e.dom != this.toggleBox.dom) {
					if (e.dom.checked) r = true;
				}
			}, this);
		}
		return r;
	}
});
Ext.apply(IP.page.Edit.prototype, {
	//*** methods
	close : function() {
		if (this.form) {
			if (!this.processing) {
				this.processing = true;
				if (!this.form.hasInput('fa', true)) {
					this.form.createInput({
						name : 'fa',
						type : 'hidden'
					});
				}
				this.form.setValue('fa', 'close');
				this.form.submit();
			} else {
				this.form.alert('Still processing changes...','orange');
			}
		}
	},
	createInput : function(c, element, where) {
		if (this.form) this.form.createInput(c, element, where);
	},
	init : function(c) {
		if (Ext.type(c) == 'object') {
			var config = {
				dm : undefined,
				sp : undefined,
				pp : undefined,
				pt : undefined
			};
			Ext.apply(config, c, config);
			if (config.pp && config.pt) {
				this.fp = new IP.page.Form({
					name : 'x-fp',
					action : config.pp,
					method : 'post'
				}, 'body', 'afterBegin');
				if (!this.fp.hasInput('t', true)) {
					this.fp.createInput({
						name : 't',
						type : 'hidden',
						value : config.pt
					}, 'x-fp', 'beforeEnd');
				}
				if (!this.fp.hasInput('c', true)) {
					this.fp.createInput({
						name : 'c',
						type : 'hidden'
					}, 't');
				}
				if (!this.fp.hasInput('n', true)) {
					this.fp.createInput({
						name : 'n',
						type : 'hidden'
					}, 'c');
				}
			}
		}
		$('body').insertHtml('beforeEnd', '<div id="x-date"></div>');
		if (config.dm) {
			$('x-date').update('Last Modified: ' + IP.Date.format(config.dm, 'n/j/Y @ g:i A'));
		}
		if (config.sp) {
			Ext.getBody().insertHtml('beforeEnd', '<div id="x-sustain"></div>', true).hide();
			var sustain = new Ext.Updater('x-sustain');
			sustain.startAutoRefresh(60, config.sp);
		}
		this.form.setAttributes();
		this.form.setLabels();
		IP.onEvent('close', this.close, this);
		IP.onEvent('preview', this.preview, this);
		IP.onEvent('save', this.save, this);
	},
	preview : function() {
		if (this.fp) {
			this.form.clean();
			var c = '';
			Ext.each(this.form.getInputs(), function(i) {
				if (i.type != 'hidden') {
					if (!this.form.getValue(i.name).empty() && !i.encrypt && !i.preview.footer) {
						if (i.preview.section) c += '<div>';
						c += '<b>' + i.label + ':</b> ';
						if (i.preview.newline) c += '<br />';
						c += this.form.getValue(i.name, true);
						if (i.preview.section) c += '</div>';
					}
				}
			}, this);
			var n = (!$('x-date').dom.innerHTML.empty()) ? '<div class="dm">' + $('x-date').dom.innerHTML + '</div>' : '';
			Ext.each(this.form.getInputs(), function(i) {
				if (i.type != 'hidden') {
					if (!this.form.getValue(i.name).empty() && !i.encrypt && i.preview.footer) {
						if (i.preview.section) n += '<div>';
						n += i.label + ': ';
						if (i.preview.newline) n += '<br />';
						if (i.preview.value) {
							n += (this.form.getValue(i.name).toBoolean()) ? i.preview.value.on : i.preview.value.off;
						} else {
							n += this.form.getValue(i.name, true);
						}
						if (i.preview.section) n += '</div>';
					}
				}
			}, this);
			this.fp.setValue('c', encodeURIComponent(c));
			this.fp.setValue('n', encodeURIComponent(n));
			this.fp.dom.target = 'new';
			this.fp.submit();
		}
	},
	save : function(c) {
		if (this.form) {
			if (!this.processing) {
				this.form.clean();
				var ok = true;
				Ext.each(this.form.getInputs(), function(i) {
					if (i.type != 'hidden' && !i.validation.disabled) {
						if (!this.form.validate(this.form.getValue(i.name), i.validation.type, i.validation.accept, i.validation.reject)) {
							this.form.alert(i.validation.text, 'red');
							ok = false;
						}
						if (i.maxlength) {
							if (!this.form.validate(this.form.getValue(i.name), 'limit', i.maxlength)) {
								this.form.alert(i.label + ' is too long. The number characters cannot exceed ' + i.maxlength + ' character'.plural(i.maxlength) + '.', 'red');
								ok = false;
							}
						}
					}
					return ok;
				}, this);
				if (ok) {
					this.processing = true;
					IP.Ajax.on('failure', function(r) {
						this.processing = false;
						if (!r.responseText.empty()) {
							this.form.alert('An error was encountered while saving changes. Close edit session to continue.', 'red');
						}
						IP.Ajax.clear();
					});
					IP.Ajax.on('success', function(r) {
						this.processing = false;
						if (r.responseText.isJSON()) {
							var o = Ext.decode(r.responseText);
							if (o.Status.down() == 'ok') {
								this.form.setValue(this.form.getUpdatable('input'), o.ItemId);
								if (c) {
									this.close();
								} else {
									$('x-date').update('Last Modified: ' + IP.Date.format(o.DateModified, 'n/j/Y @ g:i A'));
									this.form.alert('Changes saved successfully.', 'green', 3, true);
								}
							} else {
								this.form.alert('An error was encountered while saving changes. Close edit session to continue.', 'red');
							}
						} else {
							this.form.alert('An error was encountered while saving changes. Close edit session to continue.', 'red');
						}
						IP.Ajax.clear();
					});
					if (!this.form.hasInput('fa', true)) {
						this.form.createInput({
							name : 'fa',
							type : 'hidden'
						});
					}
					this.form.setValue('fa', 'set');
					IP.Ajax.setScope(this);
					IP.Ajax.setUrl(this.form.dom.action);
					Ext.each(this.form.getInputs(), function(i) {
						if (i.submit) {
							 if ((i.encrypt && !this.form.getValue(i.name).empty()) || (i.encryptBlank && this.form.getValue(i.name).empty())) {
								IP.Ajax.add(this.form.crypt(Ext.id()), this.form.crypt(this.form.getValue(i.name)));
								this.form.setValue(i.name, '');
							} else {
								IP.Ajax.add(i.name, this.form.getValue(i.name));
							}
						}
					}, this);
					IP.Ajax.go();
				}
			} else {
				this.form.alert('Still processing changes...', 'orange');
			}
		}
	},
	setForm : function (c) {
		this.form = new IP.page.Form(c);
	},
	setInput : function(c) {
		if (this.form) this.form.setInput(c);
	}
});
Ext.apply(IP.page.Form.prototype, {
	//*** methods
	addInput : function(c, element, where) {
		if (Ext.type(c) == 'object') {
			var config = {};
			var defaults = {
				cls : undefined,
				disabled : false,
				encrypt : undefined,
				encryptBlank : false,
				label : '',
				maxlength : undefined,
				name : '',
				preview : {
					footer : false,
					newline : false,
					section : false,
					value : undefined
				},
				submit : true,
				style : undefined,
				updatable : false,
				tabindex : undefined,
				type : 'text',
				validation : {
					accept : undefined,
					disabled : false,
					reject : undefined,
					text : '',
					type : ''
				},
				value : undefined,
				wrap : false
			};
			Ext.apply(config, c, defaults);
			if (this.form && !this.hasInput(config.name)) this.inputs.push(config);
			if (this.form || (this.form && (element || (element && where)))) {
				if (!this.hasInput(config.name, true)) {
					var e = '<input type="'+config.type+'" name="'+config.name+'" name="'+config.name+'" value="'+config.value+'" />';
					if (element && where) {
						$(element).insertHtml(where, e);
					} else if (element && !where) {
						$(element).insertHtml('afterEnd', e);
					} else {
						this.form.insertHtml('afterBegin', e);
					}
				}
				if (config.wrap) {
					var e = this.getInput(config.name).wrap();
					e.setStyle({
						'position' : 'relative'
					});
				}
			}
		}
	},
	alert : function(e,m,c,p,f) {
		if ($('x-alert')) $('x-alert').stopFx().remove();
		var anchor = Ext.isEmpty(e) ? Ext.getBody() : $(e);
		var alert = anchor.insertHtml('afterBegin','<div id="x-alert"><div>'+m+'</div></div>',true).setStyle({'background-color':c, 'width' : (anchor.getWidth() - anchor.getBorderWidth('lr')).toString() + 'px'});
		if (Ext.isIE) alert.position('absolute', 999999, anchor.getX() + anchor.getBorderWidth('l'), anchor.getY() + anchor.getBorderWidth('t'));
		if (!Ext.isIE) alert.position('absolute', 999999, anchor.getX(), anchor.getY());
		alert.fadeIn()
		if (p) alert.pause(p)
		if (f) alert.fadeOut();
	},
	crypt : function(p, salt) {
		if (!Ext.isEmpty(Ext.ux.Crypto.SHA1)) {
			return salt ? Ext.ux.Crypto.SHA1.hash(Ext.ux.Crypto.SHA1.hash(p)+salt) : Ext.ux.Crypto.SHA1.hash(p);
		}
	},
	clean : function() {
		$$('form[name='+this.dom.name+'] input[type=text]').each(function(e) {
			e.dom.value = e.getValue().stripScripts().stripTags().normalize();
		});
		$$('form[name='+this.dom.name+'] textarea').each(function(e) {
			e.dom.value = e.getValue().stripScripts().stripTags().trim();
		});
	},
	clearAlert :function() {
		if ($('x-alert')) $('x-alert').stopFx().remove();
	},
	createInput : function(c, element, where) {
		this.addInput(c, element, where);
	},
	disable : function(n) {
		Ext.each(this.getInput(n), function(e) {
			e.dom.disabled = true;
		});
	},
	dom : undefined,
	get : function(dom) {
		 return dom ? this.dom : this.form;
	},
	getInput : function(n) {
		var input = undefined;		
		$$('form[name='+this.dom.name+'] *[name='+n+']').each(function(e) {
			if (e.dom.tagName.down() == 'input' && e.dom.type.down() == 'radio') {
				if (!Ext.isArray(input)) input = [];
				if (Ext.isArray(input)) input.push($(e.dom));
			} else {
				input = $(e.dom);
			}
		}, this);	
		return input;
	},
	getInputs : function() {
		return this.inputs;
	},
	getLabel : function(n) {
		var label = undefined;		
		$$('form[name='+this.dom.name+'] label[name='+n+'_label]').each(function(e) {
			label = e.dom;
		}, this);	
		return $(label);
	},
	getUpdatable : function() {
		var input = '';
		Ext.each(this.getInputs(), function(i) {
			if (i.updatable) input = i.name;
		}, this);
		return input;
	},
	getValue : function(n, txt) {
		var value = '';
		Ext.each(this.getInput(n), function(e) {
			if (e.dom.type.down() != 'radio' || (e.dom.type.down() == 'radio' && e.dom.checked)) value = e.getValue();
			if (txt && e.dom.tagName.down() == 'select') value = e.dom.options[e.dom.selectedIndex].text;
		}, this);
		return value;
	},
	hasInput : function(i, dom) {
		if (dom) {
			return this.getInput(i) != undefined;
		} else {
			return (i) ? this.inputs.indexOf(i) != -1 : false;
		}
	},
	hiliteLabel : function(n, c, b) {
		var label = this.getLabel(n);
		if (Ext.isEmpty(b)) b = 'transparent';
		label.down('span').setStyle({
			'background-color' : b,
			'color' : c,
			'font-weight' : 'bold'
		});
	},
	init : function(c) {
		if (this.form && Ext.type(c) == 'object') {
			this.toggle = c.toggle;
			if (c.key) {
				if (!this.hasInput('fk', true)) {
					this.createInput({
						name : 'fk',
						type : 'hidden',
						value : c.key
					});
				}
			}
		}
	},
	limit : function(n, l) {
		if (this.getValue(n).length > l) this.setValue(n, this.getValue(e).substr(0, l));
	},
	removeInput : function(i) {
		if (i) this.inputs.remove(i);
	},
	resetLabels : function() {
		$$('form[name='+this.dom.name+'] label').each(function(e) {
			e.down('span').removeStyles();
		}, this);			
	},
	setAttribute : function(n, a){
		Ext.each(this.getInput(n), function(e) {
			if (a != undefined) e.set(a);
		});
	},
	setAttributes : function() {
		this.setClasses();
		this.setDisabled();
		this.setStyles();
		this.setValues();
		Ext.each(this.inputs, function(i) {
			var ml = Ext.num(i.maxlength, 0);
			if (ml > 0 && i.type == 'text') {
				this.setAttribute(i.name, {'maxlength' : i.maxlength});
			} else if (ml > 0 && i.type == 'textarea') {
				this.getInput(i.name).on({
					'keyup' : {
						fn : function(e, t) {
							e.preventDefault();
							var val = $(t).getValue();
							var scroll = $(t).getScroll();
							if (val.length > ml) $(t).dom.value = Ext.util.Format.substr(val, 0, ml);
							$(t).scrollTo('top', scroll.top, false);
						},
						scope : this
					}
				});
			}
			if (!Ext.isEmpty(i.tabindex)) {
				this.setAttribute(i.name, {'tabindex' : i.tabindex});
			}
		}, this);
	},
	setChecked : function(n, v) {
		Ext.each(this.getInput(n), function(e) {
			if (e.dom.value == v.toBoolean(true)) e.dom.checked = true;
		});
	},
	setClass : function(n, c) {
		Ext.each(this.getInput(n), function(e) {
			if (c != undefined) e.addClass(c);
		});
	},
	setClasses : function() {
		Ext.each(this.inputs, function(i) {
			this.setClass(i.name, i.cls);
		}, this);
	},
	setDisabled : function() {
		Ext.each(this.inputs, function(i) {
			if (i.disabled) this.disable(i.name);
		}, this);
	},
	setInput : function(c) {
		this.addInput(c);
	},
	setLabel : function(n, v) {
		Ext.each(this.getInput(n), function(e) {
			//if (v != undefined) e.insertHtml('beforeBegin', '<label name="' + e.dom.name + '_label">'+v+'</label>');
			var labelName = e.dom.name;
			if (e.parent().dom.tagName.down() == 'div') e = e.parent();
			if (v != undefined) e.wrap({
				tag : 'label', 
				name : labelName + '_label',
				html : '<span>' + v + '</span>'
			});
			return false;
		});
	},
	setLabels : function() {
		Ext.each(this.inputs, function(i) {
			if (!i.hidden && i.type != 'hidden') this.setLabel(i.name, i.label);
		}, this);
	},
	setStyle : function(n, s) {
		Ext.each(this.getInput(n), function(e) {
			if (s != undefined) e.setStyle(s);
		});
	},
	setStyles : function(n, s) {
		Ext.each(this.inputs, function(i) {
			this.setStyle(i.name, i.style);
		}, this);
	},
	setValue : function(n, v) {
		Ext.each(this.getInput(n), function(e) {
			if (v != undefined) {
				if (e.dom.type.down() == 'radio' && e.dom.value == v.toBoolean(true)) {
					e.dom.checked = true;
				} else if (e.dom.type.down() != 'radio') {
					e.dom.value = v;
				}
			}
		});
	},
	setValues : function() {
		Ext.each(this.inputs, function(i) {
			this.setValue(i.name, i.value);
		}, this);
	},
	submit : function() {
		if (this.dom) this.dom.submit();
	},
	validate : function(v, t, a, r) {
		var pass = true;
		if(!t) t = '';
		switch(t)
		{
			case 'select':
				if (a) pass = v == a;
				if (pass && r) pass = v != r;
				break;
			case 'limit':
				pass = a ? v.length <= a : false;
				break;
			case 'email':
				pass = Ext.form.VTypes.email(v);
				break;
			case 'number':
				pass = !v.toString().empty() && Ext.isEmpty(v.stripNumeric());
				break;
			default:
				pass = !v.toString().empty();
				break;
		}
		return pass;
	}
});
Ext.apply(IP.page.List.prototype, {
	//*** methods
	createInput : function(c, element, where) {
		if (this.form != undefined) this.form.createInput(c, element, where);
	},
	adjustRank : function(i, a) {
		if (this.form != undefined) {
			if (!this.form.hasInput('fa', true)) {
				this.form.createInput({
					name : 'fa',
					type : 'hidden'
				});
			}
			if (!this.form.hasInput('id', true)) {
				this.form.createInput({
					name : 'id',
					type : 'hidden'
				});
			}
			if (!this.form.hasInput('ra', true)) {
				this.form.createInput({
					name : 'ra',
					type : 'hidden'
				});
			}
			this.form.setValue('fa', 'rank');
			this.form.setValue('id', i);
			this.form.setValue('ra', a);
			this.form.submit();
		}
	},
	closeItem : function(v) {
		if (this.form != undefined && v != undefined) {
			if (!this.form.hasInput('fa', true)) {
				this.form.createInput({
					name : 'fa',
					type : 'hidden'
				});
			}
			if (!this.form.hasInput('id', true)) {
				this.form.createInput({
					name : 'id',
					type : 'hidden'
				});
			}
			this.form.setValue('fa', 'close_override');
			this.form.setValue('id', v);
			this.form.submit();
		}
	},
	init : function() {
		$$('div[id*=open]').each(function(e){
			var close = e.insertHtml('beforeEnd', '<a href="#" onclick="IP.fireEvent(\'closeItem\', \''+e.dom.id.substr(5)+'\')" class="x-close">Close</a>', true);
			close.position('absolute', 999998);
			close.alignTo(e,'tr-tr',[0,0]);
		});
	},
	setForm : function (c) {
		this.form = new IP.page.Form(c);
		var cb = new IP.page.Checkboxes();
		cb.attach(this.form, this.form.toggle);
		var bn = new IP.page.Buttons();
		bn.attach(this.form, cb);
		IP.onEvent('closeItem', this.closeItem, this);
	},
	setInput : function(c) {
		if (this.form != undefined) this.form.setInput(c);
	}
});
Ext.onReady(function(){	
	TextMetrics = Ext.util.TextMetrics.createInstance(Ext.getBody());
});
var TextMetrics;
Ext.apply(String.prototype,{
	camelize: function() {
		var parts = this.split('-'), len = parts.length;
		if (len == 1) return parts[0];

		var camelized = this.charAt(0) == '-'
		  ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
		  : parts[0];

		for (var i = 1; i < len; i++)
		  camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

		return camelized;
	},
	empty: function(){
	  return Ext.isEmpty(this.trim());
	},
	getWidth: function(){
	  return TextMetrics.getWidth(this);
	},
	getHeight : function() {
		return TextMetrics.getHeight(this);
	},
	capitalize: function(){
	  return Ext.util.Format.capitalize(this);
	},
	ellipsis: function(l){
	  return Ext.util.Format.ellipsis(this,l);
	},
	stripScripts: function(){
	  return Ext.util.Format.stripScripts(this);
	},
	stripTags: function(){
	  return Ext.util.Format.stripTags(this);
	},
	startsWith: function(s,i){
		i=(i)?'i':'';
		var re=new RegExp('^'+s,i);
		return this.match(re);
	},
	endsWith: function(s,i){
		i=(i)?'gi':'g';
		var re=new RegExp(s+'$',i);
		return this.match(re);
	},
	htmlDecode: function(){
	  return Ext.util.Format.htmlDecode(this);
	},
	htmlEncode: function(){
	  return Ext.util.Format.htmlEncode(this);
	},
	contains: function(s){
	  return this.has(s);
	},
	includes: function(s){
	  return this.has(s);
	},
	has: function(s){
	  return this.indexOf(s) != -1;
	},
	remove: function(s) {
		var re = new RegExp(s, 'g');
		return this.replace(re, '');
	},
	stripNonAlpha: function() {
		return this.replace(/[^A-Za-z ]+/g, '');
	},
	stripNonAlphaNumeric: function() {
		return this.replace(/[^A-Za-z0-9 ]+/g, '');
	},
	stripNonNumeric: function() {
		return this.replace(/[^0-9-.]/g, '');
	},
	stripNumeric: function() {
		return this.replace(/[0-9]/g, '');
	},
	strip: function() {
		return this.replace(/\s/g,'');
	},
	normalize: function(){ 
		return this.trim().replace(/\s+/g,' ');
	},
	plural: function(v, e){ 
		var end = (e) ? e : 's';
		return Ext.num(v, 0) > 1 ? this + end : this;
	},
	up: function(){ 
		return this.toUpperCase();
	},
	upper: function(){ 
		return this.toUpperCase();
	},
	down: function(){ 
		return this.toLowerCase();
	},
	lower: function(){ 
		return this.toLowerCase();
	},
	toBoolean: function(asNumber){ 
		var s = this.down();
		var r = (s == 'true' || s == '1' || s == 'yes' || s == 'y' || s == 'on' || s == 't') ;
		if (asNumber) r = r ? 1 : 0;
		return r;
	}, 
	isJSON: function(){ 
		try {
			Ext.decode(this);
			return true;
		} catch(e) {
			return false;
		}
	}
});







