// --- Automatically generated, do not edit --- 
// ------ compact distro: ac.fry ------

/*
 * AC Fry - JavaScript Framework v1.0
 * (c)2006 Petr Krontorad, April-Child.com
 * Portions of code based on WHOA Bender Framework, (c)2002-2005 Petr Krontorad, WHOA Group.
 * http://www.april-child.com. All rights reserved.
 * See the license/license.txt for additional details regarding the license.
 * Special thanks to Matt Groening and David X. Cohen for all the robots.
 */


// String prototype enhancements
String.prototype.camelize = function()
{
	return this.replace( /(-.)/g, function(){ return arguments[0].substr(1).toUpperCase();} );
}

String.prototype.decamelize = function()
{
	return this.replace( /([A-Z])/g, function(){ return '-'+arguments[0].toLowerCase();} );
}

String.prototype.trim = function()
{
    return this.replace(/(^\s*)|(\s*$)/g, '' );
}

String.prototype.stripLines = function()
{
	return this.replace( /\n/g, '' );
}

String.prototype.stripMarkup = function()
{
	return this.replace( /<(.|\n)+?>/g, '' );
}

String.prototype.replaceMarkup = function( charRep )
{
	return this.replace( /(<(.|\n)+?>)/g, function()
	{
		var t = '';
		for ( var i=0; i<arguments[0].length; i++ )
		{
			t += charRep;
		}
		return t;
	} );
}

String.prototype.encodeMarkup = function()
{
	return this.replace( /&/g, '&amp;' ).replace( />/g, '&gt;' ).replace( /</g, '&lt;' );
}

String.prototype.decodeMarkup = function()
{
	return this.replace( /&lt;/g, '<' ).replace( /&gt;/g, '>' ).replace( /&amp;/g, '&' );
}

String.prototype.surround = function(t, side)
{
	side = side || 3;
	return (1==1&side?t:'')+this+(2==2&side?t:'');
}

String.prototype.surroundTag = function(t)
{
	return '<'+t+'>'+this+'</'+t+'>';
}

// example of use: var pattern = '? is ?; alert(pattern.embed('Decin', 'sunny')); pattern = '@city is @weather'; alert(pattern.embed({weather:'cloudy', city:'Decin'}))
String.prototype.embed = function()
{
	var t = this;
	if ( 1 == arguments.length && 'object' == typeof arguments[0] )
	{
		// named placeholders
		for ( var i in arguments[0] )
		{
			eval('var re=/@'+i+'/g;');
			t = t.replace(re, arguments[0][i]);
		}
	}
	else
	{
		// anonymous placeholders `?`
		for ( var i=0; i<arguments.length; i++ )
		{
			var ix = t.indexOf('?');
			if ( -1 != ix )
			{
				t = t.substr(0,ix)+arguments[i]+t.substr(ix+1);
				continue;
			}
			break;
		}
	}
	return t;
}

// Tuning helpers - dealing with user-agent differences
var $__tune = 
{
	__prop:{},
	isIE:('function' == typeof window.ActiveXObject),
	isSafari:-1!=navigator.appVersion.indexOf('afari'),
	isGecko:false,
	node:
	{
		getOpacity:function(node)
		{
			if ( $__tune.isIE )
			{
				var f = node.style.filter;
				if ( !f || -1 == f.indexOf('alpha') )
				{
					return 1.0;
				}
				return 1.0;
			}
			return parseFloat(node.style[$__tune.isGecko?'MozOpacity':'opacity'] || 1.0);
		},
		setOpacity:function(node, opacity)
		{
			if ( $__tune.isIE )
			{
				node.style.filter = 'alpha(opacity='+(100*opacity)+')';
			}
			else
			{
				node.style.opacity = opacity;
				node.style.MozOpacity = opacity;
			}
		},
		getPageScrollPosition:function()
		{
			var d = document.documentElement;
			if ( d && d.scrollTop) 
			{
				return [d.scrollLeft, d.scrollTop];
			} 
			else if (document.body) 
			{
				return [document.body.scrollLeft, document.body.scrollTop];
			}
			else
			{
				return [0, 0];
			}
		}
	},
	event:
	{
		get:function(evt)
		{
			if ( 'undefined' == typeof evt.target )
			{
				evt.target = evt.srcElement;
				evt.stopPropagation = function()
				{
					window.event.cancelBubble = true;
				};
			}
			if ( 'undefined' == typeof evt.pageX )
			{
				evt.pageX = evt.clientX + document.body.scrollLeft;
				evt.pageY = evt.clientY + document.body.scrollTop;
			}
			evt.getOffsetX = function()
			{
				if ( 'undefined' == typeof evt.offsetX )
				{
					var pos = evt.$.abspos();
					evt.offsetX = evt.pageX - pos.x;
					evt.offsetY = evt.pageY - pos.y;
				}
				return evt.offsetX;
			}
			evt.getOffsetY = function()
			{
				if ( 'undefined' == typeof evt.offsetY )
				{
					var pos = evt.$.abspos();
					evt.offsetX = evt.pageX - pos.x;
					evt.offsetY = evt.pageY - pos.y;
				}
				return evt.offsetY;				
			}
			evt.isAnyControlKeyPressed = function()
			{
				return evt.metaKey||evt.controlKey||evt.altKey||evt.shiftKey;
			}
			evt.KEY_ESCAPE = 27;
			evt.KEY_ENTER = 13;
			evt.KEY_ARR_RIGHT = 39;
			evt.KEY_ARR_LEFT = 37;
			evt.KEY_ARR_UP = 38;
			evt.KEY_ARR_DOWN = 40;
			return evt;
		},
		addListener:function(node, type, listener)
		{
			if ( $__tune.isIE )
			{
                node.attachEvent('on'+type, listener);				
			}
			else
			{
				node.addEventListener(type, listener, false);
			}
		},
		removeListener:function(node, type, listener)
		{
			if ( $__tune.isIE )
			{
                node.detachEvent('on'+type, listener);
			}
			else
			{
				node.removeEventListener(type, listener, false);
			}
		}		
	},
	behavior:
	{
		disablePageScroll:function()
		{
			if ( 'undefined' == typeof $__tune.__prop.page_scroll )
			{
				$__tune.__prop.page_scroll = [$().s().overflow, $().ga('scroll')];
			}
			$().s('overflow:hidden').sa('scroll', 'no');
		},
		enablePageScroll:function()
		{
			$().s('overflow:auto').sa('scroll', 'yes');
		},
		disableCombos:function()
		{
			$().g('select', function(node)
			{
				node.sa('__dis_combo', node.s().visibility);
				$(node).v(false);
			});
		},
		enableCombos:function()
		{
			$().g('select', function(node)
			{
				node.s({visibility:node.ga('__dis_combo') || 'visible'});
			});
		},
		clearSelection:function()
		{
			try
			{
				if ( window.getSelection )
				{
					if ( $__tune.isSafari )
					{
						window.getSelection().collapse();
					}
					else
					{
						window.getSelection().removeAllRanges();
					}
				}
				else
				{
					if ( document.selection )
					{
						if ( document.selection.empty )
						{
							document.selection.empty();
						}
						else
						{
							if ( document.selection.clear )
							{
								document.selection.clear();
							}
						}
					}
				}
			}
			catch (e) {}
		},
		makeBodyUnscrollable:function()
		{
			$().s('position:fixed').w(fry.ui.info.page.width);
		}
	},
	ui:
	{
		scrollbarWidth:-1!=navigator.appVersion.indexOf('intosh')?15:17
	}
}
// some browsers masks its presence having Gecko string somewhere inside its userAgent field...
$__tune.isGecko = !$__tune.isSafari&&!$__tune.isIE&&-1!=navigator.userAgent.indexOf('ecko');

// Node manipulations

function ACNode(node)
{
	this.$ = node;
}
// `$$` creates new node
ACNode.prototype.$$ = function(tagName)
{
	return $$(tagName);
}
// *is* - tells whether node is a part of the active DOM tree (that is displayed on page). Node may exist only in memory before appending or after removing when references, in which case node.is() will return false.
ACNode.prototype.is = function()
{
	return null != this.$.parentNode;
}
// *i*d
ACNode.prototype.i = function(id)
{
	if ( 'undefined' == typeof id )
	{
		return this.$.id||'';
	}
	this.$.id = id;
	return this;
}
// class *n*ame
ACNode.prototype.n = function(n)
{
	if ( 'undefined' == typeof n)
	{
		return this.$.className||'';
	}
	this.$.className = n;
	return this;
}
// *e*vent listener
ACNode.prototype.e = function(t, c)
{
	var node = this.$;
	var listener = function(evt)
	{
		evt = $__tune.event.get(evt||self.event);
		evt.stop = function()
		{
			evt.stopPropagation();
		}
		evt.removeListener = function()
		{
			$__tune.event.removeListener(node, t, listener);
		}
		evt.$ = $(evt.target);
		c(evt);
	};
	$__tune.event.addListener(node, t, listener);
	return this;
}
// *x* coordinate
ACNode.prototype.x = function(x)
{
	if ( 'undefined' == typeof x )
	{
		return parseInt(this.$.style.left||0);
	}
	this.$.style.left = x+'px';
	return this;
}
// *y* coordinate
ACNode.prototype.y = function(y)
{
	if ( 'undefined' == typeof y )
	{
		return parseInt(this.$.style.top||0);
	}
	this.$.style.top = y+'px';
	return this;
}
// *abs*olute page *pos*ition coordinates (you can optionally specify node to which the position is calculated), returns {x:, y:} coordinates
ACNode.prototype.abspos = function(n)
{
	if ( document.getBoxObjectFor )
	{
		var p = document.getBoxObjectFor(this.$);
		return {x:p.x, y:p.y};
	}
	if ( this.$.getBoundingClientRect )
	{
		var p = this.$.getBoundingClientRect();
		return {x:p.left+(document.documentElement.scrollLeft || document.body.scrollLeft), y:p.top+(document.documentElement.scrollTop || document.body.scrollTop)};
	}
	var p = {x:0, y:0};
	var n2 = this.$;
	while ( document.body != n2 && document != n2 && n != n2 )
	{
		p.x += n2.offsetLeft - n2.scrollLeft;
		p.y += n2.offsetTop - n2.scrollTop;
		if ( n2.offsetParent )
		{
			n2 = n2.offsetParent;
		}
		else
		{
			n2 = n2.parentNode;
		}
	}
	return p;
}
// *pos*ition, if true - absolute, false - relative
ACNode.prototype.pos = function(p)
{
	if ( 'undefined' == typeof p )
	{
		return 'absolute' == this.$.style.position;
	}
	this.$.style.position = p ? 'absolute' : 'relative';
	return this;
}
// *z*-index coordinate
ACNode.prototype.z = function(z)
{
	if ( 'undefined' == typeof z )
	{
		return parseInt(this.$.style.zIndex||0);
	}
	this.$.style.zIndex = z;
	return this;
}
// *w*idth
ACNode.prototype.w = function(w)
{
	if ( 'undefined' == typeof w )
	{
		return parseInt(this.$.style.width||this.$.offsetWidth);
	}
	this.$.style.width = w+'px';
	return this;
}
// *h*eight
ACNode.prototype.h = function(h)
{
	if ( 'undefined' == typeof h )
	{
		return parseInt(this.$.style.height||this.$.offsetHeight);
	}
	this.$.style.height = h+'px';
	if ( $__tune.isIE && 8 > h )
	{
		this.$.style.fontSize = '1px';
	}
	return this;
}
// *s*tyle information - argument can be either "{color:'red', backgroundColor:'blue'}" or "'color:red;background-color:blue'"
ACNode.prototype.s = function(s)
{
	if ( 'undefined' == typeof s )
	{
		return this.$.style;
	}
	if ( 'object' == typeof s )
	{
		for ( var n in s )
		{
			this.$.style[n] = s[n];
		}
	}
	else if ( 'string' == typeof s )
	{
		if ( '' != s )
		{
			var styles = s.split(';');
			for ( var i=0; i<styles.length; i++ )
			{
				var style = styles[i].split(':');
				if ( 2 == style.length )
				{
					this.$.style[style[0].trim().camelize()] = style[1].trim();
				}
			}
		}
	}
	return this;
}
// *o*pacity
ACNode.prototype.o = function(o)
{
	if ( 'undefined' == typeof o )
	{
		return $__tune.node.getOpacity(this.$);
	}
	$__tune.node.setOpacity(this.$, o);
	return this;
}
// *d*isplay
ACNode.prototype.d = function(d)
{
	if ( 'undefined' == typeof d )
	{
		return 'none' != this.$.style.display;
	}
	this.$.style.display = d ? 'block' : 'none';
	return this;						
}
// *v*isibility
ACNode.prototype.v = function(v)
{
	if ( 'undefined' == typeof v )
	{
		return 'hidden' != this.$.style.visibility;
	}
	this.$.style.visibility = v ? 'visible' : 'hidden';
	return this;			
}
// H*T*ML source (equivalent to infamous innerHTML, remember innerHTML is not considered *evil* here - see the KISS principle, plus it's actually faster than DOM)
ACNode.prototype.t = function(t)
{
	if ( 'undefined' == typeof t )
	{
		return this.$.innerHTML;
	}
	this.$.innerHTML = t;
	return this;
}
// *p*arent node
ACNode.prototype.p = function(p)
{
	if ( 'undefined' == typeof p )
	{
		return $(this.$.parentNode);
	}
	return $(p).a(this);
}
// *g*et child node(s), the format of a query might be either `[['table',0],['tr',2],['td',4]]`, `['table',['tr',2],['td',4]]`, 'table:0/tr:2/td:4' or 'table/tr:2/td:4'. you can use `*` in path for any node
ACNode.prototype.g = function(q)
{
	var lst = [];
	if ( 'string' == typeof q )
	{
		var qt = q.split('/');
		q = [];
		for ( var i=0; i<qt.length; i++ )
		{
			var qtt = qt[i].split(':');
			q[q.length] = qtt;
		}
	}
	var lookup = function(node, qIndex)
	{
		if ( !node )
		{
			return;
		}
		var qq = q[qIndex];
		var is_final_index = q.length-1 == qIndex;
		var ls = node.getElementsByTagName(qq[0]);
		if ( 2 == qq.length )
		{
			// specific node required
			if ( is_final_index )
			{
				// store results
				lst.push($(ls.item(parseInt(qq[1]))));
			}
			else
			{
				lookup(ls.item(parseInt(qq[1])), qIndex+1);
			}
		}
		else
		{
			// all nodes required
			for ( var i=0; i<ls.length; i++ )
			{
				if ( is_final_index )
				{
					lst.push($(ls.item(i)));
				}
				else
				{
					lookup(ls.item(i), qIndex+1);
				}
			}
		}
	}
	
	lookup(this.$, 0);
	if ( 1 == lst.length )
	{
		return lst[0];
	}
	else if ( 0 == lst.length )
	{
		lst = null;
	}
	return lst;
}
// *g*et *p*arent node at some path - allows for returning grand-grand-grand...parent node. imagine node tree: `div>div>table>tbody>tr>td>` and node at `td`
// to return second div you would call `gp('tr/tbody/table/div')` or `tr/table/div:1`, first div could be acquired using `table/div:2` etc. you can use `*` for any node.
ACNode.prototype.gp = function(q)
{
	if ( 'string' == typeof q )
	{
		q = q.split('/');
	}
	var fq = [];
	for ( var i=0; i<q.length; i++ )
	{
		if ( -1 != q[i].indexOf(':') )
		{
			q[i] = q[i].split(':');
			for ( var ii=0; ii<q[i][1]; ii++ )
			{
				fq.push(q[i][0]);
			}
		}
		else
		{
			fq.push(q[i]);
		}
	}
	var c = 0;
	var p = this.$;
	while ( p && c < fq.length)
	{
		p = p.parentNode;
		if ( '*' == fq[c] || fq[c] == p.tagName.toLowerCase() )
		{
			c++;
		}
	}
	return $(p);
}
// *a*ppends child node
ACNode.prototype.a = function(n)
{
	if ( 'undefined' != typeof n['$'] )
	{
		n = n.$;
	}
	return $(this.$.appendChild(n));
}
// *a*ppend H*T*ML code - adds innerHTML to existing node code
ACNode.prototype.at = function(t)
{
	var ht = this.$.innerHTML;
	this.$.innerHTML = ht + t;
	return this;
}
// *r*emoves child node
ACNode.prototype.r = function(n)
{
	if ( 'undefined' != typeof n['$'] )
	{
		n = n.$;
	}
	return $(this.$.removeChild(n));
}
// *r*emoves *s*elf node
ACNode.prototype.rs = function()
{
	this.$ = this.$.parentNode.removeChild(this.$);
	return this;
}
// *i*nserts *c*hild node before specified node
ACNode.prototype.ib = function(n, rn)
{
	if ( 'undefined' != typeof n['$'] )
	{
		n = n.$;
	}
	if ( 'undefined' != typeof rn['$'] )
	{
		rn = rn.$;
	}
	return $(this.$.insertBefore(n,rn));
}
// *i*nserts *c*hild node after specified node
ACNode.prototype.ia = function(n, rn)
{
	if ( 'undefined' != typeof n['$'] )
	{
		n = n.$;
	}
	if ( null == $(rn).ns() )
	{
		return $(this.$.appendChild(n));
	}
	else
	{
		return $(this.$.insertBefore(n,($(rn).ns()).$));
	}
}
// *f*irst *c*hild of the node - always returns first $-ed node (ignoring text, comment etc. nodes)
ACNode.prototype.fc = function()
{
	var n = this.$.firstChild;
	while ( null != n && 1 != n.nodeType )
	{
		n = n.nextSibling;
	}
	return null != n ? $(n) : null;
}
// *l*ast *c*hild of the node - always returns last $-ed node (ignoring text, comment etc. nodes)
ACNode.prototype.lc = function()
{
	var n = this.$.lastChild;
	while ( null != n && 1 != n.nodeType )
	{
		n = n.previousSibling;
	}
	return null != n ? $(n) : null;			
}
// *n*ext *s*ibling of the node - always returns first $-ed node (ignoring text, comment etc. nodes)
ACNode.prototype.ns = function()
{
	var n = this.$.nextSibling;
	while ( null != n && 1 != n.nodeType )
	{
		n = n.nextSibling;
	}
	return null != n ? $(n) : null;
}
// *p*revious *s*ibling of the node - always returns last $-ed node (ignoring text, comment etc. nodes)
ACNode.prototype.ps = function()
{
	var n = this.$.previousSibling;
	while ( null != n && 1 != n.nodeType )
	{
		n = n.previousSibling;
	}
	return null != n ? $(n) : null;			
}
// *g*et *a*ttribute
ACNode.prototype.ga = function(n)
{
	return this.$.getAttribute(n);
}
// *s*et *a*ttribute
ACNode.prototype.sa = function(n, v)
{
	this.$.setAttribute(n, v);
	return this;
}
// *r*emove *a*ttribute
ACNode.prototype.ra = function(n)
{
	this.$.removeAttribute(n);
	return this;
}
// *dup*licate node
ACNode.prototype.dup = function()
{
	return $(this.$.cloneNode(true));
}

// `$_` converts any value into string - useful for numeric values before calling for String enhanced methods.
var $_ = function(t)
{
	return ''+t;
}

// `$$` creates new node with specified tag name, returns $-ed node
var $$ = function(n)
{
	return $(document.createElement(n||'div'));
}

// returns $-ed node for existing node, argument can be either ID string or node itself (standard or $-ed). If argument is omitted, returns the body node
var $ = function(id)
{
	var node = id || document.body || document.getElementsByTagName('body').item(0);
	if ( 'string' == typeof node )
	{
		node = document.getElementById(id);
	}
	else
	{
		if ( 'undefined' != typeof node['$'] )
		{
			return node;
		}
		if ( 1 != node.nodeType )
		{
			return null;
		}
	}
	if ( null == node )
	{
		return null;
	}
	return new ACNode(node);
}

var $_supressed_event = function(evt)
{
	evt.stop();
}

// Language constructs

/* creates new class, multiple class inheritance is allowed.
	$class('AClass',
	{
		construct:function(a)
		{
			this.a = a || '';
		},
		destruct:function()
		{
			$delete(this.a);
		}
	});
	AClass.prototype.hello = function(msg)
	{
		alert(msg + this.a);
	}
	$class('BClass < AClass',
	{
		construct:function(a, b)
		{
			this.b = b || '';
		}
	});
	$class('CClass');
	$class('DClass < BClass, CClass');
	DClass.prototype.hello = function(msg, msg2)
	{
		$call(this, 'AClass.hello', msg);
		alert(msg2 + this.b);
	}
*/
$class = function(className, methods)
{
	if ( 'string' != typeof className )
	{
		throw new FryException(29, 'Class inheritance error. Undefined class name.');
	}
	var n = className.split('<');
	className = n[0].replace(/ /g, '');
	var bases = [];
	if ( 1 == n.length )
	{
		// no inheritance, will inherit from `Object`
		bases[0] = 'Object';
	}
	else
	{
		// defined inheritance, might be multiple eg. `ClassA < ClassB, ClassC`
		bases = n[1].split(',');
	}
	var getSource = function(s)
	{
		s = ''+s;
		return s.substring(s.indexOf('{')+1, s.lastIndexOf('}'));
	}
	var getParams = function(s, p)
	{
		p = p || {};
		s = ''+s;
		s = s.substring(s.indexOf('(')+1, s.indexOf(')')).split(',');
		for ( var i in s )
		{
			var n = s[i].replace(/ /g, '');
			if ( !p[n] && '' != n )
			{
				p[n] = true;
			}
		}
		return p;
	}
	var preprocessSource = function(s, cn)
	{
		// parsing source code and replacing calls to base constructor or methods
		eval('var re = /'+cn+'\.([^\\(]*)\\(([\\)]*)/g;');		
		s = s.replace(re, function()
		{
			return 'this.__'+cn+'_'+arguments[1]+'.call(this'+(''==arguments[2].replace(/ /g, '')?',':'')+arguments[2];
		});
		eval('re = /'+cn+'[^\\(]*\\(([\\)]*)/g;');
		s = s.replace(re, function()
		{
			return 'this.__'+cn+'_construct.call(this'+(''==arguments[1].replace(/ /g, '')?',':'')+arguments[1];
		});
		return s;
	}	
	methods = methods || {};
	var c_code = '';
	var d_code = '';
	var params = {};
	for ( var i in bases )
	{
		bases[i] = bases[i].replace(/ /g, '');
		if ( 'Object' == bases[i] )
		{
			continue;
		}
		eval('var b_code='+bases[i]+';');
		params = getParams(b_code, params);
		b_code = getSource(b_code);
		c_code += b_code+';';
		eval('var d_code='+bases[i]+'.prototype.destruct||"{}";');
		d_code = getSource(d_code);
		d_code += d_code+';';
	}
	params = getParams(methods.construct||'', params);
	var p = [];
	for ( var i in params)
	{
		p.push(i);
	}
	if ( methods.construct )
	{
		// own constructor defined
		oc_code = getSource(methods.construct);
		for ( var i in bases )
		{
			if ( 'Object' != bases[i] )
			{
				oc_code = preprocessSource(oc_code, bases[i]);				
			}
		}
		c_code += oc_code;
	}	
	d_code += methods.destruct ? getSource(methods.destruct) : '';
	try
	{
		eval('var newClass=function('+p.join(',')+'){'+c_code+'};');		
	}
	catch (e)
	{
		throw new FryException(30, 'Class inheritance error. Class `?`, constructor: `?`, error message: `?`.'.embed(className, c_code, e));
	}
	newClass.prototype = new Object();
	for ( var i in bases )
	{
		if ( 'Object' == bases[i] )
		{
			continue;
		}
		// creating links to base class methods
		var p_base = bases[i].replace(/\./g, '_');
		eval('for(var m in '+bases[i]+'.prototype){newClass.prototype[m]='+bases[i]+'.prototype[m]; if ("__" !=m.substr(0,2)) {newClass.prototype["__'+p_base+'_"+m]='+bases[i]+'.prototype[m];}}');
	}
	// creating class metadata for reflection
	newClass.prototype.__class_name = className;
	newClass.prototype.__base_class_names = bases;
	eval('newClass.prototype.construct=function('+p.join(',')+'){'+c_code+'};')
	eval('newClass.prototype.destruct=function(){'+d_code+'};')
	eval(className+'=newClass');
}


// creates new object.  $new(ClassName, [arguments])
var $new = function()
{
	if ( !arguments[0] )
	{
		throw new FryException(31, 'Object instantiation error. Invalid class provided `?`.'.embed(arguments[0]));
	}
	var arg_list = [];
	for ( var i=1; i<arguments.length; i++ )
	{
		arg_list.push('arguments['+i+']');
	}
	try
	{
		eval('var obj = new arguments[0]('+arg_list.join(',')+');');
	}
	catch(e)
	{
		throw new FryException(32, 'Object instantiation error. Class: `?`, num arguments: `?`, error message: `?`.'.embed(arguments[0].prototype.__class_name, arg_list.length, e));
	}
	return obj;
}

// deletes object (destructor of base class are called automatically). $delete(object)
var $delete = function(object)
{
	if ( 'object' != typeof object )
	{
		return;
	}
	try
	{
		if ( 'string' == typeof object.__base_class_names )
		{
			var bases = object.__base_class_names.split(',');
			for ( var i in bases )
			{
				if ( 'Object' != bases[i] )
				{
					$call(object, bases[i]+'.destruct()');				
				}
			}
		}
		if ( object.destruct )
		{
			object.destruct();
		}
		delete object;
	}
	catch(e)
	{
	}
}

// calls a method/function with defined object, typically used from within method to call some parent class method. $call(this, 'AClass.aMethod', [arguments])
var $call = function()
{
	caller = arguments[0];
	var arg_list = [];
	for ( var i=2; i<arguments.length; i++ )
	{
		arg_list.push('arguments['+i+']');
	}
	try
	{
		eval('var r = caller.__'+arguments[1].replace(/\./g, '_')+'.call(caller'+(0!=arg_list.length?',':'')+arg_list.join(',')+');');
	}
	catch (e)
	{
		throw new FryException(32, 'Function call error. Function `?`, num arguments: ?, error: `?`.'.embed(arguments[1], arguments.length-2, e));
	}
	return r;
}

var $runafter = function(t, c)
{
	setTimeout(c, t);
}
var $runinterval = function(from, to, interval, c)
{
	var i = from;
	var control = 
	{
		from:from,
		to:to,
		stopped:false,
		stop:function()
		{
			this.stopped = true;
		}
	}
	var t = self.setInterval(function()
	{
		if ( i > to && to>=from )
		{
			self.clearInterval(t);
		}
		else
		{
			c(i, control);
			if ( control.stopped )
			{
				self.clearInterval(t);
			}
		}
		i++;
	}, interval);
}
var $dotimes = function(n, c)
{
	for ( var i=0; i<n; i++ )
	{
		c(i);
	}
}
var $foreach = function(o, c)
{
	if ( !o )
	{
		return;
	}
	var control = 
	{
		stopped:false,
		stop:function()
		{
			this.stopped = true;
		},
		skipped:false,
		skip:function()
		{
			this.skipped = true;
		},
		removed:false,
		remove:function(stopAfterwards)
		{
			this.removed = true;
			this.stopped = true == stopAfterwards;
		}
	}
	if ( 'undefined' == typeof o.length && 'function' != typeof o.__length )
	{
		c(o, 0, control);
		return;
	}
	var n = 'function' == typeof o.__length ? o.__length() : o.length;
	// cannot just extend Array.prototype for `item()` method due bug in IE6 iteration mechanism. Some day (>2010 :) this might get fixed and will become obsolete
	for ( var i=0; i<n; i++ )
	{
		var item = null;
		if ( 'function' == typeof o.item )
		{
			item = o.item(i);
		}
		else if ( 'function' == typeof o.__item )
		{
			item = o.__item(i);
		}
		else
		{
			if ( 'undefined' == typeof o[i] )
			{
				continue;
			}
			item = o[i];
		}
		c(item, 'function' == typeof o.__key ? o.__key(i) : i, control);
		if ( control.removed )
		{
			control.removed = false;
			if ( 'undefined' != typeof o[i] )
			{
				delete o[i];
			}
			else
			{
				if ( 'function' == typeof o.removeItem )
				{
					o.removeItem(i);
				}
				else if ( 'function' == typeof o.__remove )
				{
					o.remove(i);
				}
			}
		}
		if ( control.stopped )
		{
			break;
		}
		if ( control.skipped )
		{
			control.skipped = false;
			continue;
		}
	}	
}


var $combofill = function(n, c)
{
	var i = -1;
	n = $(n);
	while (-1<++i)
	{
		var v = c(i);
		if ( 'object' != typeof v )
		{
			break;
		}
		var option = n.a($$('option')).t(v[1]);
		option.$.value = v[0];
		if ( v[2] )
		{
			option.sa('selected', 'selected');
		}
	}
	return n;
}
var $comboget = function(n)
{
	var v = [];
	var options = $(n).$.options;
	for ( var i=0; i<options.length; i++ )
	{
		if ( '' != options[i].selected )
		{
			v[v.length] = options[i].value;
		}
	}
	if ( 1 == v.length )
	{
		return v[0];
	}
	return v;
}
var $comboset = function(n, v)
{
	var options = $(n).$.options;
	try
	{
		for ( var i=0; i<options.length; i++ )
		{
			if ( options[i].value == v )
			{
				options[i].selected = 'selected';
			}
		}
	}
	catch(e)
	{
	}
	return $(n);
}


/* Reserving global `fry` object */
var fry = 
{
	version:1.0
};


/* Generic exception object */
function FryException(code, message)
{
	this.code = code;
	this.message = message;
}
FryException.prototype.toString = function()
{
	return 'Fry Exception: code[?] message[?]'.embed(this.code, this.message);
}

/* Remote call support (ajax) */
fry.remote =
{
	support:
	{
		getRequestObject: function()
		{
			var obj = null;
			try
			{
				if ( $__tune.isIE )
				{
					$foreach ( ['MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP','Microsoft.XMLHTTP'], function(progid, index, control)
					{
						try
						{
							obj = new ActiveXObject(progid);
							control.stop();
						}
						catch(e){}
					});
				}
				else
				{
					obj = new XMLHttpRequest();
				}
			}
			catch(e){}
			return obj;
		}
	},
	/* Loosely based (especially status handling code) on YUI Library */
	post:function(callback, pars, httpMethod, url)
	{
		url = url || client.conf.fry.backendURL;
		if ( !url )
		{
			throw new FryException(1, 'Undefined backend URL specified in client.conf. Use client.conf.fry.backendURL=\'{YOUR_BACKEND_SCRIPT_URL}\'; to set it.');
		}
		var obj = fry.remote.support.getRequestObject();
		if ( !obj )
		{
			throw new FryException(2, 'Unable to acquire HTTP request object. Check to see if your browser is among supported browsers.');
		}
		obj.open(httpMethod||'POST', url, true);
		obj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		var postData = '';
		for ( var name in pars )
		{
			var value = pars[name];
			postData += encodeURIComponent(name)+'='+encodeURIComponent(value)+'&';				
		}
		obj.send(postData);
		var poll = window.setInterval
		(
			function()
			{
				if ( 4 == obj.readyState )
				{
					window.clearInterval(poll);
					callback( obj );
				}
			},
			200
		);
	},	
	result:function(s, callbackOk, callbackError)
	{
		var httpStatus;
		var responseObject;	
		try
		{
			httpStatus = s.status;
		}
		catch(e)
		{
			httpStatus = 13030;
		}
		if ( 200 == httpStatus )
		{
			// parsing response
			var r = null;
			var headers = s.getAllResponseHeaders();
			var contentType = (-1 != headers.indexOf('/xml') && s.responseXML) ? 'text/xml' : 'text/html';
			if ( 'text/xml' == contentType )
			{
				// text/xml
				try
				{
					r = $xmlserialize(s.responseText);
				}
				catch(e)
				{
					callbackError('Error while serializing remote-side response. Probably corrupted data. Error: `?`. Sent data `?`.'.embed(e.message, s.responseText));
				}
				try
				{
					callbackOk(r);					
				}
				catch(e)
				{
					throw new FryException(45, 'fry/remote: Error while executing callback after successful remote call. Error: `?`.'.embed(e.message));
				}
			}
			else
			{
				// text/html
				var code = s.responseText.substring(0,3);
				if ( '#S#' == code )
				{
					r = s.responseText.substr(3);
					callbackOk( r );
				}
				else
				{
					if ( '' == s.responseText )
					{
						callbackError('No data returned from remote side.');
					}
					else
					{
						callbackError('Invalid data returned from remote side: `?`.'.embed(s.responseText.substr('#E#'==code?3:0)));
					}					
				}
			}
		}
		else
		{
			switch (httpStatus)
			{
				// The following case labels are wininet.dll error codes that may be encountered.
				// Server timeout
				case 12002:
				// 12029 to 12031 correspond to dropped connections.
				case 12029:
				case 12030:
				case 12031:
				// Connection closed by server.
				case 12152:
				// See above comments for variable status.
				case 13030:
				default:
				{
					if ( callbackError )
					{
						callbackError('Connection ended up with status: '+httpStatus);
					}
				};break;
			}
		}
		delete s;
	},
	upload:
	{
		lastAdapter:null,
		// using SWFUpload component
		perform:function(adapter)
		{
			var url = client.conf.fry.backendURL;
			if ( !url )
			{
				throw new FryException(19, 'Undefined backend URL specified in client.conf. Use client.conf.fry.backendURL=\'{YOUR_BACKEND_SCRIPT_URL}\'; to set it.');
			}
			
			fry.remote.upload.lastAdapter = adapter;
			if ( null != $('SWFUpload') )
			{
				$('SWFUpload').rs();
			}
			$().a($$()).i('SWFUpload').w(500).s('margin 0 auto').t('<form action="" onsubmit="return false;"><input type="file" name="file" /><input type="submit" value="" /></form>');
			mmSWFUpload.init
			({
				upload_backend : url+'?a='+adapter.onGetRemoteActionName(),
				target : 'SWFUpload',
				allowed_filesize : adapter.allowedFileSizeInKBytes,
				allowed_filetypes : adapter.allowedFileTypes,
				upload_start_callback : 'fry_remote_upload_onStart',
				upload_progress_callback : 'fry_remote_upload_onProgress',
				upload_complete_callback : 'fry_remote_upload_onComplete',
				upload_error_callback : 'fry_remote_upload_onError',
				upload_cancel_callback : 'fry_remote_upload_onCancel'
			});
			mmSWFUpload.callSWF();
		}
	}
};

function fry_remote_upload_onStart(f)
{
	fry.remote.upload.lastAdapter.onStart(f);
}
function fry_remote_upload_onProgress(f, b)
{
	fry.remote.upload.lastAdapter.onProgress(f, b);
}
function fry_remote_upload_onComplete(f)
{
	fry.remote.upload.lastAdapter.onEnd(false, false, f);
}
function fry_remote_upload_onError(f)
{
	fry.remote.upload.lastAdapter.onEnd(false, true, f);
}
function fry_remote_upload_onCancel()
{
	fry.remote.upload.lastAdapter.onEnd(true);
}


$class('fry.remote.upload.Adapter',
{
	construct:function(allowedFileSizeInKBytes, allowedFileTypes, additionalRemoteActionParams)
	{
		this.allowedFileSizeInKBytes = allowedFileSizeInKBytes || 2000;
		this.allowedFileTypes = allowedFileTypes || '*';
		this.additionalRemoteActionParams = additionalRemoteActionParams || '';
	}
});
fry.remote.upload.Adapter.prototype.onStart = function(fileObj)
{
}
fry.remote.upload.Adapter.prototype.onProgress = function(fileObj, bytesLoaded)
{
}
fry.remote.upload.Adapter.prototype.onEnd = function(wasCanceled, wasError, fileObj)
{
}
fry.remote.upload.Adapter.prototype.onGetRemoteActionName = function()
{
	return 'upload?'.embed(this.additionalRemoteActionParams ? ',?'.embed(this.additionalRemoteActionParams) : '');
}

/*  ---------------------------------------------------------------- 
	fry.debug namespace
*/
fry.debug = 
{
	__initialized:false,
	init:function()
	{
		if ( $('debug') )
		{
			with ( $('debug') )
			{
				z(9999).t('<strong><img src="/mm/i/theme/apple/arrow-right.gif" width="12" height="11" border="1" />AC Fry Debug Console &nbsp; &nbsp; <a href="javascript:fry.debug.clear()">Clear</a></strong><div class="messages" style="display:none"></div>');
				g('img:0').e('click', function(evt)
				{
					var is_collapsed = -1 != evt.$.ga('src').indexOf('down');
					h(is_collapsed?20:450);
					g('div:0').d(!is_collapsed);
					evt.$.sa('src', '/mm/i/theme/ac/arrow-?.gif'.embed(is_collapsed?'right':'down'));
				});
			}
		}
		fry.debug.__initialized = true;
	},
	
	clear:function()
	{
		$('debug').g('div:0').t('');
	},
	
	write:function(msg)
	{
		if ( !fry.debug.__initialized )
		{
			fry.debug.init();
		}
		if ( $('debug') )
		{
			with ( $('debug').g('div:0') )
			{
				a($$('hr'));
				a($$('span')).t(msg);
			}
		}
	}
}

$post = fry.remote.post;
$result = fry.remote.result;
$rpost = function(params, callbackOk, callbackError)
{
	$post( function(s) { $result( s, 
		function(r) 
		{
			callbackOk(r);
		},
		function(e) 
		{
			// result Error
			callbackError(e);
		}
		) },
		params
	);
}
$upload = function(adapter)
{
	fry.remote.upload.perform(adapter);
}

// ------ compact distro: ac.fry.xml ------



fry.xml =
{
	support:
	{
		obj:{init:false, domDoc:null, xslTempl:null, domParser:null, xmlSerial:null},
		init:function()
		{
			with ( fry.xml.support )
			{
				if ( obj.init )
				{
					return;
				}
				try
				{
					if ( $__tune.isIE )
					{
						$foreach ( ['Msxml2.FreeThreadedDOMDocument.4.0', 'Msxml2.FreeThreadedDOMDocument', 'Msxml.FreeThreadedDOMDocument'], function(progid, i, control)
						{
							try
							{
								var o = new ActiveXObject(progid);
								obj.domDoc = function()
								{
									return new ActiveXObject(progid);
								}
								delete o;
								control.stop();
							}
							catch(ee){}
						});
						$foreach ( ['MSXML2.XSLTemplate.4.0', 'MSXML2.XSLTemplate'], function(progid, i, control)
						{
							try
							{
								var o = new ActiveXObject(progid);
								obj.xslTempl = function()
								{
									return new ActiveXObject(progid);
								}
								delete o;
								control.stop();
							}
							catch(ee){}
						});
					}
					else
					{
						if ( DOMParser )
						{
							obj.domParser = new DOMParser();
						}
						if ( XMLSerializer )
						{
							obj.xmlSerial = new XMLSerializer();
						}
						obj.domDoc = function()
						{
							return document.implementation.createDocument('','',null);
						}
					}
				}
				catch (e)
				{
					throw new FryException(300, 'fry.xml: Uncapable user agent `?`.'.embed(e));
				}
				obj.init = true;
			}
		}
	},
	util:
	{
		removeAllChildren:function(node)
		{
			while ( 0 != node.childNodes.length )
			{
				node.removeChild(node.firstChild);
			}				
		},
		nodeText:function(node, t)
		{
			if ( 'undefined' == typeof t )
			{
				var t = '';
				$foreach (node.childNodes, function(node)
				{
					if ( 3 == node.nodeType || 4 == node.nodeType )
					{
						t += node.data;
					}							
				});
				return t;
			}
			fry.xml.util.removeAllChildren(node);
			node.appendChild(node.ownerDocument.createCDATASection(t));
		},
		innerXML:function(node, xmls)
		{
			if ( node instanceof fry.xml.Document )
			{
				node = node.document;
			}
			if ( 'undefined' == typeof xmls )
			{
				if ( $__tune.isIE )
				{
					return node.xml;
				}
				else
				{
					return fry.xml.support.obj.xmlSerial.serializeToString(node);
				}
			}
			try
			{
				var doc = null;
				if ( $__tune.isIE )
				{
					doc = fry.xml.support.obj.domDoc();
					doc.loadXML(xmls);
				}
				else
				{
					doc = fry.xml.support.obj.domParser.parseFromString(xmls, 'text/xml');
				}
				var clone = function(toNode, fromNode)
				{
					$foreach ( fromNode.childNodes, function(node)
					{
						switch ( node.nodeType )
						{
							case 1:
							{
								// element node
								var nn = toNode.appendChild(toNode.ownerDocument.createElement(node.tagName));
								for ( var i=0; i<node.attributes.length; i++ )
								{
									nn.setAttribute( node.attributes.item(i).name, node.attributes.item(i).value );
								}
								clone(nn, node);
							};break;
							case 3:
							{
								// text node
								toNode.appendChild(toNode.ownerDocument.createTextNode(node.data));
							};break;
							case 4:
							{
								// CDATA node
								toNode.appendChild(toNode.ownerDocument.createCDATASection(node.data));
							};break;
						}
					});
				}
				fry.xml.util.removeAllChildren(node);
				if ( 9 == node.nodeType )
				{
					var dd = doc.documentElement;
					var nn = node.appendChild(node.createElement(dd.tagName));
					for ( var i=0; i<dd.attributes.length; i++ )
					{
						nn.setAttribute( dd.attributes.item(i).name, dd.attributes.item(i).value );
					}
					doc = dd;
					node = node.documentElement;
				}
				clone(node, doc);
				delete doc;
			}
			catch (e)
			{
				alert(e.message);
				throw new FryException(301, 'fry.xml: Error when setting innerXML snippet `?`[max 200 chars included] to node. ?'.embed(xmls.substr(0,200), e));
			}
		},
		encodeCDATA:function(data)
		{
		    return data.replace( /<\!\[CDATA\[/g, '```~~~```!```[```CDATA```[').replace( /\]\]>/g, ']```]```~~~' );
		},
		decodeCDATA:function(data)
		{
		    return data.replace( /```~~~```\!```\[```CDATA```\[/g, '<![CDATA[').replace( /\]```\]```~~~/g, ']]>' );
		}
	}
}

fry.xml.support.init();

$class('fry.xml.Document',
{
	construct:function(xmls)
	{
		if ( 'object' == typeof xmls )
		{
			xmls = this.getSourceFromObject(xmls);
		}
		this.document = fry.xml.support.obj.domDoc();
		if ( xmls )
		{
			this.innerXML(xmls);
		}
		else
		{
			this.document.appendChild(this.createElement('root'));
		}
	},
	destruct:function()
	{
		delete this.document;
	}
});

fry.xml.Document.prototype.innerXML = function(xmls)
{
	if ( 'undefined' == typeof xmls )
	{
		return fry.xml.util.innerXML(this);
	}
	fry.xml.util.innerXML(this, xmls);	
}

fry.xml.Document.prototype.getSourceFromObject = function(o, propName)
{
	propName = propName || 'object';
	var xmls = '<'+propName;
	if ( o.$ )
	{
		for ( var i in o.$ )
		{
			xmls += ' ?="?"'.embed(i, o.$[i].replace(/"/g, '&quot;'));
		}
	}
	xmls += '>';
	if ( 'object' != typeof o )
	{
		xmls += '<![CDATA[?]]>'.embed(o);
	}
	else
	{
		for ( var i in o )
		{
			if ( 'function' == typeof o[i] || '$' == i )
			{
				continue;
			}
			if ( 'object' == typeof o[i] )
			{
				if ( o[i].push )
				{
					// is an array
					var item_name = o[i].__name || i;
					if ( i != item_name )
					{
						xmls += '<'+i+'>';
					}
					for ( var ii=0; ii<o[i].length; ii++ )
					{
						xmls += this.getSourceFromObject(o[i][ii], item_name);
					}
					if ( i != item_name )
					{
						xmls += '</'+i+'>';
					}
					continue;
				}
			}
			if ( '_' == i )
			{
				if ( '' != o[i] )
				{
					xmls += '<![CDATA['+o[i]+']]>';				
				}
			}
			else
			{
				xmls += this.getSourceFromObject(o[i], i);			
			}
		}		
	}
	return xmls+'</?>'.embed(propName);
}

// returns xml document serialized into object, the opposite can be done by passing document to the constructor
fry.xml.Document.prototype.serialize = function(node, o)
{
	o = o || {_:''};
	node = node || this.root();
	var num_elements = 0;
	var map = {};
	for ( var i=0; i<node.childNodes.length; i++ )
	{
		var child_node = node.childNodes.item(i);
		if ( 1 == child_node.nodeType )
		{
			var prop_name = child_node.tagName;
			var o_child = this.serialize(child_node);
			if ( o[prop_name] )
			{
				// already exists, will become an array
				if ( !o[prop_name].push )
				{
					// not an array yet
					o[prop_name] = [o[prop_name], o_child];
				}
				else
				{
					o[prop_name].push(o_child);
				}
			}
			else
			{
				o[prop_name] = o_child;
				map[num_elements] = prop_name;
				num_elements++;
			}
		}
		else if ( 3 == child_node.nodeType || 4 == child_node.nodeType )
		{
			o._ += child_node.data;
		}
	}
	if ( !o.$ )
	{
		if ( 0 == node.attributes.length && 0 == num_elements )
		{
			o = o._;
		}
		else
		{			
			o.$ = {};
			for ( i=0; i<node.attributes.length; i++ )
			{
				var attr = node.attributes.item(i);
				o.$[attr.name] = attr.value;
			}
			if ( 1 == num_elements && o[map[0]] instanceof Array )
			{
				// plain array
				o = o[map[0]];
				o.__name = map[0];
			}
			else
			{
				// adding iterator
				o.__length = function()
				{
					return num_elements;
				}
				o.__item = function(index)
				{
					return o[map[index]];
				}
				o.__key = function(index)
				{
					return map[index];
				}
				o.__remove = function(index)
				{
					delete o[map[index]];
					delete map[index];
					num_elements--;
				}
			}
		}
	}
	return o;
}

fry.xml.Document.prototype.createElement = function(tagName)
{
	return this.document.createElement(tagName);
}

fry.xml.Document.prototype.createTextNode = function(text)
{
	return this.document.createTextNode(text);
}

fry.xml.Document.prototype.createCDATASection = function(data)
{
	return this.document.createCDATASection(data);
}

fry.xml.Document.prototype.root = function()
{
	if ( this.document )
	{
		return this.document.documentElement;
	}
	return null;
}

fry.xml.Document.prototype.g = function(q)
{
	var lst = [];
	if ( 'string' == typeof q )
	{
		var qt = q.split('/');
		q = [];
		for ( var i=0; i<qt.length; i++ )
		{
			var qtt = qt[i].split(':');
			q[q.length] = qtt;
		}
	}
	var lookup = function(node, qIndex)
	{
		var qq = q[qIndex];
		var is_final_index = q.length-1 == qIndex;
		var ls = node.getElementsByTagName(qq[0]);
		if ( 2 == qq.length )
		{
			// specific node required
			if ( is_final_index )
			{
				// store results
				lst.push(ls.item(parseInt(qq[1])));
			}
			else
			{
				lookup(ls.item(parseInt(qq[1])), qIndex+1);
			}
		}
		else
		{
			// all nodes required
			for ( var i=0; i<ls.length; i++ )
			{
				if ( is_final_index )
				{
					lst.push(ls.item(i));
				}
				else
				{
					lookup(ls.item(i), qIndex+1);
				}
			}
		}
	}
	lookup(this.document, 0);
	if ( 0 == lst.length )
	{
		return null;
	}
	if ( 1 == lst.length )
	{
		return lst[0];
	}
	return lst;	
}


$xmlcreate = function(xmls)
{
	return $new(fry.xml.Document, xmls);
}
$xmlinner = fry.xml.util.innerXML;
$xmltext = fry.xml.util.nodeText;
$xmlserialize = function(xd)
{
	if ( 'object' == typeof xd && xd.serialize )
	{
		return xd.serialize();
	}
	else
	{
		xd = $xmlcreate(xd);
		var o = xd.serialize();
		$delete(xd);
		return o;
	}
}
$xmldeserialize = function(o)
{
	return $new(fry.xml.Document, o);
}



// ------ compact distro: ac.fry.ui ------



if ( 'undefined' != typeof ACNode )
{
	/*  ---------------------------------------------------------------- 
		fry.ui namespace
	*/
	fry.ui = 
	{
		info:
		{
			// returns page scroll position
			page:
			{
				scroll:
				{
					left:0,
					top:0
				},
				width:0,
				height:0
			},
			isIE:$__tune.isIE,
			isSafari:$__tune.isSafari,
			isGecko:$__tune.isGecko
		},	
		util:
		{
			getMillis:function()
			{
				var d = new Date();
				return 60000*d.getMinutes()+1000*d.getSeconds()+d.getMilliseconds();
			}
		},
		format:
		{
			formatDateTime:function( timestamp, langCode )
			{
				var ret = '--';
				try
				{
					var s = parseInt( timestamp );
					var d = new Date( s*1000 );
					var m = d.getMinutes();
					var time_p = ' '+d.getHours()+':'+(10>m?'0':'')+m;
					switch ( langCode )
					{
						case 'cs':
						{
							ret = d.getDate()+'.'+(d.getMonth()+1)+' '+d.getFullYear()+time_p
						}; break;
						default:
						{
							ret = (d.getMonth()+1)+'/'+d.getDate()+'/'+d.getFullYear()+time_p
						}
					}
				}
				catch (e)
				{
				}
				return ret;
			}			
		},
		drag:
		{
			MODE_STANDARD:0,
			MODE_HORIZONTAL:1,
			MODE_VERTICAL:2,
			
			state:{started:false, x:0, y:0, ix:0, iy:0, clickInterval:300, mdownMillis:0, usingCursor:false}
		},
		dnd:
		{
			MODE_DRAG:1,
			MODE_DROP:2,
			MODE_BOTH:3,
			MODE_DRAG_POINTER:17,	// pointer mode - will select target under mouse cursor, not using cursor node vs target node intersection
			MODE_BOTH_POINTER:19,

			state:{started:false, x:0, y:0, ix:0, iy:0, offsetX:0, offsetY:0, lastCheck:0, lastCheckInterval:100, mdownMillis:0, clickInterval:300},
			targets: []
		}
	}
	
	
	$class('fry.ui.DragAdapter',
	{
		construct:function(node)
		{
			this.node = 'undefined' == typeof node ? null : $(node);
		}
	});
	
	$class('fry.ui.DropAdapter',
	{
		construct:function(node)
		{
			this.node = 'undefined' == typeof node ? null : $(node);
		}
	});

	/* Extending ACNode capabilities to support drag and DnD (drag and drop) */
	/* Adapter API:
		drag/DnD.drag:
			node onGetRenderingNode()
			void onDragStart()
			{x:, y:} | null onDragMove(dragNode, nx, ny, offsetX, offsetY)
			[boolean] onDragStop(originalMouseDownEvent)	- if true is returned, original node is left intact (it has no coordinates set after drag node)
			var onGetData()
			void onClick(evt)
			node onGetCursorNode()
			[top, right, bottom, left] onGetCursorPadding() - applies for DnD.drag only
			
		DnD.drop:
			node onGetRenderingNode()
			{x:, y:, w:, h:} onGetCoords()
			void onDragEnter(firstEnter, offsetX, offsetY, source, originalMouseDownEvent, allTargets, targetIndex)
			void onDragLeave(lastLeave)
			void onPutData(data, source, offsetX, offsetY, originalMouseDownEvent, allTargets, targetIndex)
		
	*/
	/*  ---------------------------------------------------------------- 
		fry.ui.DragAdapter
	*/	
	fry.ui.DragAdapter.prototype.onGetRenderingNode = function()
	{
		return this.node;
	}
	fry.ui.DragAdapter.prototype.onDragStart = function()
	{
	}
	fry.ui.DragAdapter.prototype.onDragMove = function(dragNode, nx, ny, offsetX, offsetY)
	{
		return null;
	}
	fry.ui.DragAdapter.prototype.onDragStop = function()
	{
	}
	fry.ui.DragAdapter.prototype.onGetData = function()
	{
		return null;
	}
	fry.ui.DragAdapter.prototype.onClick = function(evt)
	{
	}
	fry.ui.DragAdapter.prototype.onGetCursorNode = function()
	{
		return null;
	}
	fry.ui.DragAdapter.prototype.onGetCursorPadding = function()
	{
		return [0,0,0,0];
	}
	/*  ---------------------------------------------------------------- 
		fry.ui.DropAdapter
	*/		
	fry.ui.DropAdapter.prototype.onGetRenderingNode = function()
	{
		return this.node;
	}
	fry.ui.DropAdapter.prototype.onGetCoords = function()
	{
		var pos = this.node.abspos();
		return {x:pos.x, y:pos.y, w:this.node.w(), h:this.node.h()};
	}
	fry.ui.DropAdapter.prototype.onDragEnter = function(firstEnter, offsetX, offsetY, source, originalMouseDownEvent, allTargets, targetIndex)
	{
	}
	fry.ui.DropAdapter.prototype.onDragLeave = function(lastLeave)
	{
	}
	fry.ui.DropAdapter.prototype.onPutData = function(data, source, offsetX, offsetY, originalMouseDownEvent, allTargets, targetIndex)
	{
	}	
	/*  ---------------------------------------------------------------- 
		Drag
	*/		
	ACNode.prototype.addDrag = function(mode, adapter)
	{
		if ( 2 > arguments.length )
		{
			throw new FryException(100, 'fry.ui: Drag adapter not specified.');
		}
		if ( 2 < arguments.length )
		{
			adapter = 
			{
				onGetRenderingNode:arguments[1],
				onDragStart:arguments[2],
				onDragMove:arguments[3],
				onDragStop:arguments[4],
				onClick:arguments[5],
				onGetCursorNode:arguments[6]
			};
		}
		// checking adapter
		if ( 'object' != typeof adapter )
		{
			throw new FryException(101, 'fry.ui: Drag adapter not an object.');
		}
		if ( !adapter.onGetRenderingNode )
		{
			adapter.onGetRenderingNode = function()
			{
				return caller;
			}
		}
		// optional methods
		$foreach ( ['onDragStart', 'onDragMove', 'onDragStop', 'onClick', 'onGetCursorNode'], function(method)
		{
			if ( !adapter[method] )
			{
				adapter[method] = function(){return null;};
			}				
		});
		var dragNode = null;
		var caller = this;
		this.e('mousedown', function(evt)
		{
			caller.dragMouseDownEvent = evt;
			evt.stop();
			$__tune.behavior.clearSelection();
			if ( fry.ui.drag.state.started )
			{
				return;
			}
			var node = adapter.onGetRenderingNode();
			if ( !node )
			{
				throw new FryException(102, 'fry.ui: Rendering node not specified.');
			}
			var pos = $(node).abspos();
			dragNode = null;
			with ( fry.ui.drag.state )
			{
				started = true;
				x = ix = evt.pageX;
				y = iy = evt.pageY;
				mdownMillis = fry.ui.util.getMillis();
			}
			var lastMoveEvent = null;
			$(document.documentElement).e('mouseup', function(evt)
			{
				evt.stop();
				evt.removeListener();
				var state = fry.ui.drag.state;
				if ( !state.started )
				{
					return;
				}
				if ( null != lastMoveEvent )
				{
					lastMoveEvent.removeListener();
				}
				$__tune.behavior.clearSelection();
				state.started = false;
				var leave_node = adapter.onDragStop(caller.dragMouseDownEvent);
				if ( state.usingCursor )
				{
					if ( null != dragNode )
					{
						if ( !leave_node )
						{
							node.x(dragNode.x()).y(dragNode.y());							
						}
						dragNode.rs();
					}
					node.v(true);
				}
				state.usingCursor = false;
				var millisOffset = fry.ui.util.getMillis() - state.mdownMillis;
				if ( state.clickInterval > millisOffset )
				{
					if ( adapter.onClick )
					{
						adapter.onClick(evt);
					}
				}
			});
			$(document.documentElement).e('mousemove', function(evt)
			{
				lastMoveEvent = evt;
				evt.stop();
				$__tune.behavior.clearSelection();
				var state = fry.ui.drag.state;
				if ( !state.started )
				{
					// false alarm, remove listener!
					evt.removeListener();
					return;
				}
				var millisOffset = fry.ui.util.getMillis() - state.mdownMillis;
				if ( state.clickInterval > millisOffset && state.ix == evt.pageX && state.iy == evt.pageY )
				{
					// dragging not started yet
					return;
				}
				if ( null == dragNode )
				{
					// initializing cursor/drag node
					adapter.onDragStart();
					var node = adapter.onGetRenderingNode();
					if ( adapter.onGetCursorNode )
					{
						dragNode = adapter.onGetCursorNode();
					}
					if ( null == dragNode )
					{
						dragNode = $(node);
					}
					else
					{
						state.usingCursor = true;
						var pos = $(node).abspos();
						dragNode = $().a(dragNode).pos(true).x(pos.x).y(pos.y);
						node.v(false);
					}
					dragNode.z(99999);
				}
				with ( state )
				{
					var ox = evt.pageX - x;
					var oy = evt.pageY - y;
					x = evt.pageX;
					y = evt.pageY;
					var nx = $(dragNode).x() + ox;
					var ny = $(dragNode).y() + oy;
					var coords = adapter.onDragMove(node, nx, ny, ox, oy);
					if ( null != coords )
					{
						if ( coords.x )
						{
							nx = coords.x;
						}
						if ( coords.y )
						{
							ny = coords.y;
						}
					}
					if ( fry.ui.drag.MODE_VERTICAL != (fry.ui.drag.MODE_VERTICAL & mode) )
					{
						dragNode.x(nx);
					}
					if ( fry.ui.drag.MODE_HORIZONTAL != (fry.ui.drag.MODE_HORIZONTAL & mode) )
					{
						dragNode.y(ny);
					}
				}
			});
		});
		return this;
	}
	ACNode.prototype.removeDrag = function()
	{
		if ( 'undefined' != typeof this.dragMouseDownEvent )
		{
			this.dragMouseDownEvent.removeListener();
		}
		return this;
	}

	/*  ---------------------------------------------------------------- 
		DnD (Drag and Drop)
	*/		
	ACNode.prototype.addDnd = // common typos prevention...
	ACNode.prototype.addDnD = function(mode, adapter)
	{
		if ( 2 > arguments.length )
		{
			throw new FryException(110, 'fry.ui: DnD adapter not specified.');
		}
		this.dndMode = mode;
		var caller = this;
		if ( 2 < arguments.length )
		{
			var arg_offset = 1;
			adapter = {};
			if ( fry.ui.dnd.MODE_DRAG == (mode & fry.ui.dnd.MODE_DRAG) )
			{
				adapter = 
				{
					onGetRenderingNode:arguments[1],
					onDragStart:arguments[2],
					onDragMove:arguments[3],
					onDragStop:arguments[4],
					onGetData:arguments[5],
					onClick:arguments[6],
					onGetCursorNode:arguments[7],
					onGetCursorPadding:arguments[8]
				};
				arg_offset = 9;
			}
			if ( fry.ui.dnd.MODE_DROP == (mode & fry.ui.dnd.MODE_DROP) )
			{
				if ( 1 == arg_offset )
				{
					adapter.onGetRenderingNode = arguments[arg_offset++];
				}
				adapter.onGetCoords = arguments[arg_offset++];
				adapter.onDragEnter = arguments[arg_offset++];
				adapter.onDragLeave = arguments[arg_offset++];
				adapter.onPutData = arguments[arg_offset++];
			}
		}
		// checking adapter
		if ( !adapter.onGetRenderingNode )
		{
			adapter.onGetRenderingNode = function()
			{
				return caller;
			}
		}
		if ( fry.ui.dnd.MODE_DRAG == (mode & fry.ui.dnd.MODE_DRAG) )
		{
			if ( 'object' != typeof adapter )
			{
				throw new FryException(111, 'fry.ui: DnD adapter not an object.');
			}
			// required methods
			$foreach ( ['onGetData'], function(method)
			{
				if ( !adapter[method] )
				{
					throw new FryException(112, 'fry.ui: DnD `'+method+'` adapter method not specified.');
				}				
			});
			// optional methods
			$foreach ( ['onDragStart', 'onDragMove', 'onDragStop', 'onGetCursorNode'], function(method)
			{
				if ( !adapter[method] )
				{
					adapter[method] = function(){return null;};
				}				
			});
			if  ( !adapter.onGetCursorPadding )
			{
				adapter.onGetCursorPadding = function(){return [0,0,0,0];};
			}
		}
		// checking adapter for drop
		if ( fry.ui.dnd.MODE_DROP == (mode & fry.ui.dnd.MODE_DROP) )
		{
			if ( !adapter.onGetCoords )
			{
				adapter.onGetCoords = function()
				{
					var pos = caller.abspos();
					return {x:pos.x, y:pos.y, w:caller.w(), h:caller.h()};
				}
			}			
			if ( !adapter.onDragEnter )
			{
				adapter.onDragEnter = function(firstEnter, offsetX, offsetY, source, originalMouseDownEvent, allTargets, targetIndex){};
			}
			if ( !adapter.onDragLeave )
			{
				adapter.onDragLeave = function(){};
			}
			$foreach ( ['onPutData'], function(method)
			{
				if ( !adapter[method] )
				{
					throw new FryException(113, 'fry.ui: DnD `'+method+'` adapter method not specified.');
				}				
			});
			// check for existing target
			$foreach ( fry.ui.dnd.targets, function(target, i, control)
			{
				if ( $(target.adapter.onGetRenderingNode()).$ == caller.$ )
				{
					// already existing for the node, removing
					control.remove();
				}
			})
			// registering drop target
			fry.ui.dnd.targets.push({adapter:adapter, coordsCache:null, isActive:false});
		}
		caller.adapter = adapter;
		var dragNode = null;
		if ( fry.ui.dnd.MODE_DRAG == (mode & fry.ui.dnd.MODE_DRAG) )
		{
			// helper function for checking up area intersections, returns first active target index (matching intersection), returns -1 if not found
			var checkTargets = function(x, y)
			{
				if ( !dragNode )
				{
					return;
				}
				var points = [[x,y], [x+dragNode.w(),y], [x+dragNode.w(),y+dragNode.h()], [x, y+dragNode.h()]];
				if ( adapter.onGetCursorPadding )
				{
					var p = adapter.onGetCursorPadding();
					points[0][0] += p[3];
					points[0][1] += p[0];
					points[1][0] -= p[1];
					points[1][1] += p[0];
					points[2][0] -= p[1];
					points[2][1] -= p[2];
					points[3][0] += p[3];
					points[3][1] -= p[2];
				}
				var active_targets = [];
				var actual_node = $(adapter.onGetRenderingNode()).$;
				for ( var i in fry.ui.dnd.targets )
				{
					var target = fry.ui.dnd.targets[i];
					if ( null == target.coordsCache )
					{
						var tc = target.adapter.onGetCoords();
						if ( fry.ui.dnd.MODE_DRAG_POINTER != (mode & fry.ui.dnd.MODE_DRAG_POINTER) )
						{
							tc.x += fry.ui.dnd.state.offsetX;
							tc.y += fry.ui.dnd.state.offsetY;							
						}
						target.coordsCache = tc;
					}
					var coords = target.coordsCache;
					if ( !coords )
					{
						delete fry.ui.dnd.targets[i];
						continue;
					}
					var node = $(target.adapter.onGetRenderingNode()).$;
					// GC check
					if ( !node || null == node.parentNode )
					{
						delete fry.ui.dnd.targets[i];
						continue;
					}
					else
					{
						var cn = node;
						while (null != cn.parentNode)
						{
							cn = cn.parentNode;
						}
						if ( document != cn )
						{
							delete node;
							delete fry.ui.dnd.targets[i];
							continue;
						}
					}
					if ( node == actual_node && !caller.dndCntKey )
					{
						// the same node serving as BOTH
						continue;
					}
					var intersected = false;
					if ( fry.ui.dnd.MODE_DRAG_POINTER == (mode & fry.ui.dnd.MODE_DRAG_POINTER) )
					{
						intersected = coords.x<x && coords.y<y && coords.x+coords.w>x && coords.y+coords.h>y;
					}
					else
					{
						// intersection drag mode
						for ( var ii=0; ii<4 && !intersected; ii++ )
						{
							intersected = coords.x<points[ii][0] && coords.y<points[ii][1] && coords.x+coords.w>points[ii][0] && coords.y+coords.h>points[ii][1];
						}							
					}
					if ( intersected )
					{
						active_targets.push({t:target, firstEnter:!target.isActive, offsetX:x-coords.x, offsetY:y-coords.y});
						target.isActive = true;
					}
					else
					{
						if ( target.isActive )
						{
							target.adapter.onDragLeave(false);
						}
						target.isActive = false;
					}
				}
				$foreach ( active_targets, function(trg, index)
				{
					trg.t.adapter.onDragEnter(trg.firstEnter, trg.offsetX, trg.offsetY, caller, caller.dndMouseDownEvent, active_targets, index);
				});
				return active_targets;
			};
			// node is draggable
			this.e('mousedown', function(evt)
			{
				caller.dndMouseDownEvent = evt;
				caller.dndCntKey = evt.isAnyControlKeyPressed();
				evt.stop();
				var state = fry.ui.dnd.state;
				$__tune.behavior.clearSelection();
				if ( state.started )
				{
					return;
				}
				var node = adapter.onGetRenderingNode();
				if ( !node )
				{
					throw new FryException(114, 'fry.ui: DnD rendering node not specified.');
				}
				var pos = $(node).abspos();
				dragNode = null;
				with ( fry.ui.dnd.state )
				{
					started = true;
					x = ix = evt.pageX;
					y = iy = evt.pageY;
					offsetX = evt.getOffsetX();
					offsetY = evt.getOffsetY();
					mdownMillis = fry.ui.util.getMillis();
				}
				// cleaning targets caches
				state.checked = false;
				for ( var i in fry.ui.dnd.targets )
				{
					var target = fry.ui.dnd.targets[i];
					target.coordsCache = null;
					target.isActive = false;
				}

				var lastMoveEvent = null;
				$(document.documentElement).e('mouseup', function(evt)
				{
					evt.stop();
					evt.removeListener();
					var state = fry.ui.dnd.state;
					if ( !state.started )
					{
						return;
					}
					if ( null != lastMoveEvent )
					{
						lastMoveEvent.removeListener();
						if ( dragNode )
						{
							dragNode.rs();							
						}
					}
					$__tune.behavior.clearSelection();
					state.started = false;
					adapter.onDragStop(false);
					if ( state.usingCursor )
					{
						$(dragNode).rs();
					}
					var millisOffset = fry.ui.util.getMillis() - state.mdownMillis;
					if ( state.clickInterval > millisOffset )
					{
						if ( adapter.onClick )
						{
							adapter.onClick();
						}
						return;
					}
					var active_targets = [];
					// not a click, checking for targets
					if ( fry.ui.dnd.MODE_DRAG_POINTER == (mode & fry.ui.dnd.MODE_DRAG_POINTER) )
					{
						active_targets = checkTargets(evt.pageX, evt.pageY);
					}
					else
					{
						active_targets = checkTargets(state.x, state.y);
					}
					$foreach ( active_targets, function(target, index)
					{
						target.t.adapter.onPutData(adapter.onGetData(), caller, state.x-target.t.coordsCache.x, state.y-target.t.coordsCache.y, caller.dndMouseDownEvent, active_targets, index);
						target.t.coordsCache = null;
						target.t.isActive = false;
						target.t.adapter.onDragLeave(true);
					});
				});				
				
				$(document.documentElement).e('mousemove', function(evt)
				{
					lastMoveEvent = evt;
					evt.stop();
					$__tune.behavior.clearSelection();
					var state = fry.ui.dnd.state;
					if ( !state.started )
					{
						// false alarm, remove listener!
						evt.removeListener();
						return;
					}
					var millisOffset = fry.ui.util.getMillis() - state.mdownMillis;
					if ( state.clickInterval > millisOffset && state.ix == evt.clientX && state.iy == evt.clientY )
					{
						// dragging not started yet
						return;
					}
					if ( null == dragNode )
					{
						// initializing cursor/drag node
						adapter.onDragStart();
						var node = adapter.onGetRenderingNode();
						
						if ( adapter.onGetCursorNode )
						{
							dragNode = adapter.onGetCursorNode();
						}
						if ( null == dragNode )
						{
							dragNode = $().a($(node).dup());
							dragNode.pos(true).x(state.ix-state.offsetX).y(state.iy-state.offsetY);
						}
						else
						{
							dragNode = $().a(dragNode);
							var pos = $(node).abspos();
							dragNode.pos(true).x(evt.pageX-state.offsetX).y(evt.pageY-state.offsetY);
						}
						dragNode.z(99999);
					}
					with ( state )
					{
						var ox = evt.pageX - x;
						var oy = evt.pageY - y;
						x = evt.pageX;
						y = evt.pageY;
						var nx = dragNode.x() + ox;
						var ny = dragNode.y() + oy;
						var coords = adapter.onDragMove(dragNode, nx, ny, ox, oy);
						if ( null != coords )
						{
							if ( coords.x )
							{
								nx = coords.x;
							}
							if ( coords.y )
							{
								ny = coords.y;
							}
						}
						dragNode.x(nx).y(ny);
						// checking for targets
						var millis = fry.ui.util.getMillis();
						if ( lastCheckInterval < millis - lastCheck || 0 > millis - lastCheck )
						{
							// time out, let's check targets
							lastCheck = millis;
							if ( fry.ui.dnd.MODE_DRAG_POINTER == (mode & fry.ui.dnd.MODE_DRAG_POINTER) )
							{
								checkTargets(x, y);
							}
							else
							{
								checkTargets(x, y);								
							}
						}						
					}
				});				
			});
		}
		return this;		
	}
	ACNode.prototype.removeDnd = // common typos prevention...
	ACNode.prototype.removeDnD = function()
	{
		if ( 'undefined' != typeof this.dndMouseDownEvent )
		{
			this.dndMouseDownEvent.removeListener();
		}
		if ( fry.ui.dnd.MODE_DROP == (fry.ui.dnd.MODE_DROP & this.dndMode) )
		{
			// removing target
			var caller = this;
			$foreach ( fry.ui.dnd.targets, function(target, index, control)
			{
				if ( target == caller )
				{
					control.remove(true);
				}
			});
		}
		return this;
	}
}

fry.ui.init = function()
{
	with ( fry.ui.info )
	{
		page.width = document.documentElement.offsetWidth || document.body.offsetWidth;
		page.height = self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
		var inf = $__tune.node.getPageScrollPosition();
		page.scroll = {left:inf[0], top:inf[1]};
	}	
}

$__tune.event.addListener(self, 'load', function(evt)
{
	if ( evt.removeListener )
	{
		evt.removeListener();		
	}
	fry.ui.init();
});



// ------ compact distro: ac.fry.ui.support ------



if ( 'undefined' == typeof fry.ui.theme )
{
	fry.ui.theme = {name:'apple'};
}

// declaring default `fry.ui.snippet` namespace
fry.ui.snippet = 
{
	RoundBox:function(width, radius, cssClassName, onContentCallback, surfaceBackgroundColor)
	{
		surfaceBackgroundColor = surfaceBackgroundColor || '#fff';
		var node = $$().n(cssClassName).w(width);
		node.a($$()).n('up-left').s('background-color:?'.embed(surfaceBackgroundColor)).w(width).a($$()).w(width-radius).n('up').a($$()).n('up-right').s('background-color:?'.embed(surfaceBackgroundColor)).w(radius);
		var c_node = node.a($$()).w(width).a($$()).n('inner');
		node.a($$()).n('down-left').s('background-color:?'.embed(surfaceBackgroundColor)).w(width).a($$()).w(width-radius).n('down').a($$()).n('down-right').s('background-color:?'.embed(surfaceBackgroundColor)).w(radius);
		if ( onContentCallback )
		{
			onContentCallback(c_node);
		}
		return node;
	},
	ShadowedBox:function(width, height, cssClassName, onContentCallback, offsetFromTop)
	{
		offsetFromTop = offsetFromTop || 0;
		var node = $$().n(cssClassName).w(width);
		if ( null != height )
		{
			node.h(height);
		}
		if ( onContentCallback )
		{
			onContentCallback(node);
		}
		fry.ui.snippet.ApplyShadowedBox(node, cssClassName, offsetFromTop);
		return node;
	},
	ApplyShadowedBox:function(node, cssClassName, offsetFromTop)
	{
		cssClassName = cssClassName || 'shadow-box';
		offsetFromTop = offsetFromTop || 0;
		// adding placeholder
		var placeholder = null;
		if ( 0 != node.$.childNodes.length )
		{
			placeholder = node.ib($$(), node.fc());
		}
		else
		{
			placeholder = node.a($$());
		}
		var h = node.h();
		var w = node.w();
		// attaching rup
		node.ib($$('img'), placeholder).pos(true).x(w).y(offsetFromTop).w(13).h(20).sa('src', '/mm/i/theme/?/?-rup.png'.embed(fry.ui.theme.name, cssClassName));
//		node.ib($$('img'), placeholder).pos(true).x(w).y(offsetFromTop).w(13).h(20).sa('src', '/mm/i/theme/?/?-rup.png'.embed(fry.ui.theme.name, cssClassName)).s({filter:"progid:DXImageTransform.Microsoft.AlphaImageLoader (src='/mm/i/theme/?/?-rup.png', sizingMethod='scale')".embed(fry.ui.theme.name, cssClassName)});
		// attaching right
		node.ib($$(), placeholder).pos(true).x(w).y(20+offsetFromTop).w(13).h(h-20-offsetFromTop).s('background-image:url(/mm/i/theme/?/?-right.png);background-repeat:repeat-y'.embed(fry.ui.theme.name, cssClassName));
//		node.ib($$(), placeholder).pos(true).x(w).y(20+offsetFromTop).w(13).h(h-20-offsetFromTop).s({filter:"progid:DXImageTransform.Microsoft.AlphaImageLoader (src='/mm/i/theme/?/?-right.png', sizingMethod='scale')".embed(fry.ui.theme.name, cssClassName)}).t('<img style="display:none" />');
		// attaching rdown
		node.ib($$('img'), placeholder).pos(true).x(w-18).y(h).w(31).h(16).sa('src', '/mm/i/theme/?/?-rdown.png'.embed(fry.ui.theme.name, cssClassName));
		// attaching down
		node.ib($$(), placeholder).pos(true).x(18).y(h).w(w-36).h(16).s('background-image:url(/mm/i/theme/?/?-down.png);background-repeat:repeat-x'.embed(fry.ui.theme.name, cssClassName));
		// attaching ldown
		node.ib($$('img'), placeholder).pos(true).x(-13).y(h).w(31).h(16).sa('src', '/mm/i/theme/?/?-ldown.png'.embed(fry.ui.theme.name, cssClassName));
		// attaching left
		node.ib($$(), placeholder).pos(true).x(-13).y(20+offsetFromTop).w(13).h(h-20-offsetFromTop).s('background-image:url(/mm/i/theme/?/?-left.png);background-repeat:repeat-y'.embed(fry.ui.theme.name, cssClassName));
		// attaching lup
		node.ib($$('img'), placeholder).pos(true).x(-13).y(offsetFromTop).w(13).h(20).sa('src', '/mm/i/theme/?/?-lup.png'.embed(fry.ui.theme.name, cssClassName));
//		alert(node.$.innerHTML);
		placeholder.rs();
		delete placeholder;
	},
	RemoveShadowedBox:function(node)
	{
		$dotimes(7, function()
		{
			node.fc().rs();
		});
	},	
	ResizeShadowedBox:function(node, width, height)
	{
		var offsetFromTop = node.fc().y();
		node.w(width).h(height);
		node = node.fc()
		// rup
		node.x(width);
		node = node.ns();
		// right
		node.h(height-20-offsetFromTop);
		node.x(width);
		node = node.ns();
		// rdown
		node.x(width-18).y(height);
		node = node.ns();
		// down
		node.y(height).w(width-36);
		node = node.ns();
		// ldown
		node.y(height);
		node = node.ns();
		// left
		node.y(20+offsetFromTop).h(height-20-offsetFromTop);
	}
}

fry.ui.interval = 
{
	PI:3.14159265358979,
	hPI:1.57079632679,
	
	Linear:function(limit)
	{
		var i = 1;
		this.next = function()
		{
			if ( i > limit )
			{
				return null;
			}
			return i++;	
		}
	},
	SlowDown:function(limit)
	{
		var dx = fry.ui.interval.hPI / limit;
		var i = 0;
		this.next = function()
		{
			if ( i == limit )
			{
				return null;
			}
			return Math.sin(i++*dx)*limit;
		}
	},
	FastSlowDown:function(limit)
	{
		var i = 0;
		this.next = function()
		{
			if ( i == limit )
			{
				return null;
			}
			return 1+limit-limit/(1+i++);
		}
	},
	SlowUp:function(limit)
	{
		var dx = fry.ui.interval.hPI / limit;
		var i = limit;
		this.next = function()
		{
			if ( i == -1 )
			{
				return null;
			}
			return limit - Math.sin(i--*dx)*limit;
		}
	}
}

fry.ui.anim = 
{
	ByFunction:function(interval, waitInterval, func)
	{
		var d = interval.next();
		if ( null != d )
		{
			var pos = func(d);
			if ( 'undefined' != typeof pos.x )
			{
				this.x(pos.x);
			}
			if ( 'undefined' != typeof pos.y )
			{
				this.y(pos.y);
			}
			var caller = this;
			$runafter(waitInterval, function()
			{
				caller.animByFunction(interval, waitInterval, func);
			});
		}
		return this;
	}
}

for ( var i in fry.ui.anim )
{
	ACNode.prototype['anim'+i] = fry.ui.anim[i];
}


fry.ui.draw = 
{
	Begin:function()
	{
		this.__drawBufferNode = document.createElement('div');
		this.__drawBufferNode.style.position = 'absolute';
		this.__drawBufferNode.style.top = this.__drawBufferNode.style.left = '0';
		this.__drawBufferStack = [];
		return this;
	},
	End:function(discard)
	{
		if ( 'object' != typeof this.__drawBufferNode )
		{
			throw new FryException(409, 'fry/ui/support: Cannot end drawings before starting it. Call the drawBegin() method before drawEnd().')
		}
		if ( discard )
		{
			delete this.__drawBufferNode;
			delete this.__drawBufferStack;
		}
		else
		{
			this.$.appendChild(this.__drawBufferNode);
			if ( 0 < this.__drawBufferStack.length )
			{
				var ht = this.$.innerHTML;
				this.$.innerHTML = ht + this.__drawBufferStack.join('');
			}
			this.drawBegin();
		}
		return this;
	},
	__write:function(node)
	{
		if ( 'object' != typeof node )
		{
			throw new FryException(238, 'fry/ui/support: Invalid object passed as parameter for __write method. Check your drawing primitive code.');
		}
		var is_arr = node instanceof Array;
		if ( 'object' != typeof this.__drawBufferNode )
		{
			// direct draw
			if ( is_arr )
			{
				var ht = this.$.innerHTML;
				this.$.innerHTML = ht + node.join('');
			}
			else
			{
				this.$.appendChild(node);
			}
		}
		else
		{
			// buffered draw
			if ( is_arr )
			{
				this.__drawBufferStack = this.__drawBufferStack.concat(node);
			}
			else
			{
				this.__drawBufferNode.appendChild(node);
			}
		}
	},
	HorizontalLine:function(x, y, w, color, thickness)
	{
		color = color || '#000';
		thickness = thickness || 1;
		this.draw__write( $$().pos(true).s('background:'+color).x(x).y(y).w(w).h(thickness).$ );
		return this;
	},
	VerticalLine:function(x, y, h, color, thickness)
	{
		color = color || '#000';
		thickness = thickness || 1;
		this.draw__write( $$().pos(true).s('background:'+color).x(x).y(y).w(thickness).h(h).$ );
		return this;
	},
	Line:function(x1, y1, x2, y2, color, thickness)
	{
		x1 = Math.floor(x1);
		y1 = Math.ceil(y1);
		x2 = Math.floor(x2);
		y2 = Math.ceil(y2);
		color = color || '#000';
		thickness = thickness || 1;
		var dx = Math.abs(x2-x1);
		var dy = Math.abs(y2-y1);
		var is_h = dx > dy;
		var d = is_h ? dx/dy : dy/dx;
		var n = is_h ? dy : dx;
		var sign = 1;
		var swap = function()
		{
			var ty = y2;
			y2 = y1;
			y1 = ty;
			var tx = x2;
			x2 = x1;
			x1 = tx;			
		}
		if ( is_h )
		{
			if ( x1 > x2 )
			{
				swap();
			}
			if ( y1 > y2 )
			{
				sign = -1;				
			}
		}
		else
		{
			if ( y1 > y2 )
			{
				swap();
			}
			if ( x1 > x2 )
			{
				sign = -1;				
			}
		}
		if ( 0 == n )
		{
			if ( is_h )
			{
				return this.drawHorizontalLine(x1, y1, dx, color, thickness);
			}
			else
			{
				return this.drawVerticalLine(x1, y1, dy, color, thickness);
			}
		}
		var mt = thickness/2;
		var stack = [];
		var dd = Math.ceil(d);
		for ( var i=0; i<n; i++ )
		{
			if ( $__tune.isSafari )
			{
				// Safari has much faster DOM than string manipulation
				var node = document.createElement('div');
				node.style.position = 'absolute';
				node.style.background = color;
				if ( is_h )
				{
					node.style.left = Math.floor(x1+d*i)+'px';
					node.style.top = Math.floor(y1+sign*i-mt)+'px';
					node.style.width = dd+'px';
					node.style.height = thickness+'px';
				}
				else
				{
					node.style.left = Math.floor(x1+sign*i-mt)+'px';
					node.style.top = Math.floor(y1+d*i)+'px';
					node.style.height = dd+'px';
					node.style.width = thickness+'px';
				}
				this.draw__write(node);
			}
			else
			{
				var t = '<div style="position:absolute;background:'+color;
				if ( is_h )
				{
					t += ';left:'+Math.floor(x1+d*i)+'px;';
					t += 'top:'+Math.floor(y1+sign*i-mt)+'px;';
					t += 'width:'+dd+'px;';
					t += 'height:'+thickness+'px';
				}
				else
				{
					t += ';left:'+Math.floor(x1+sign*i-mt)+'px;';
					t += 'top:'+Math.floor(y1+d*i)+'px;';
					t += 'height:'+dd+'px;';
					t += 'width:'+thickness+'px';
				}
				t += ';font:1px arial"></div>';
				stack.push(t);
				// a magic number, it helps Opera and Firefox not to have too big array stack, so we flush it occasionally
				if ( 600 == stack.length )
				{
					this.draw__write(stack);
					stack = [];					
				}
			}
		}
		if ( !$__tune.isSafari )
		{
			this.draw__write(stack);
		}
		return this;
	},
	Polygon:function(points, color, thickness, isOpened)
	{
		var n = points.length;
		if ( 3 > n )
		{
			throw new FryException(989, 'fry/ui/support: Polygon must have at least 3 points.');
		}
		n--;
		mt = 2 < thickness ? thickness/2 : 0;
		mt = 0;
		for ( var i=0; i<n; i++ )
		{
			this.drawLine(points[i][0]+mt, points[i][1]+mt, points[i+1][0], points[i+1][1], color, thickness);
		}
		if ( !isOpened )
		{
			this.drawLine(points[0][0]+mt, points[0][1]+mt, points[n][0], points[n][1], color, thickness);
		}
		return this;
	},
	Circle:function(x, y, radius, outlineColor, fillColor, thickness)
	{
		var qpoints = [];
		var rr = radius*radius;
		for ( var i=0; i<radius; i++ )
		{
			qpoints.push([x+i, y+Math.floor(Math.sqrt(rr-i*i))]);
		}
		qpoints.push([x+radius, y]);
		this.drawPolygon(qpoints, outlineColor, thickness, true);
		$foreach ( qpoints, function(point)
		{
			point[0] = x - (point[0]-x);
		});
		this.drawPolygon(qpoints, outlineColor, thickness, true);
		$foreach ( qpoints, function(point)
		{
			point[1] = y - (point[1]-y);
		});
		this.drawPolygon(qpoints, outlineColor, thickness, true);
		$foreach ( qpoints, function(point)
		{
			point[0] = x + (x-point[0]);
		});
		this.drawPolygon(qpoints, outlineColor, thickness, true);
		delete qpoints;
		return this;
	}
}

for ( var i in fry.ui.draw )
{
	ACNode.prototype['draw'+i] = fry.ui.draw[i];
}

fry.ui.effect = 
{
	Stop:function(node)
	{
		$foreach ((node.ga('fry-effects') || '').split(','), function(effect)
		{
			var m = fry.ui.effect['__stop'+effect];
			if ( m )
			{
				m(node);
			}
		});
	},
	__reg:function(node, effectName)
	{
		node.sa('fry-effects', (node.ga('fry-effects')||'')+',?'.embed(effectName));
	},
	__unreg:function(node, effectName)
	{
		var re = new RegExp();
		re.pattern = '/,?/g'.embed(effectName);
		node.sa('fry-effects', (node.ga('fry-effects')||'').replace(re.pattern, ''));
	},
	Pulsing:function(node, minOpacity, maxOpacity)
	{
		minOpacity = minOpacity || 0.0;
		maxOpacity = maxOpacity || 1.0;
		node.sa('eff-pulsing', node.o());
		node.sa('eff-pulsing-c', 2*Math.random());
		$runinterval(1, 0, 100, function(i, control)
		{
			if ( null != node.ga('eff-pulsing-stop') )
			{
				node.ra('eff-pulsing-stop');
				node.o(node.ga('eff-pulsing'));
				node.ra('eff-pulsing');
				control.stop();
			}
			else
			{
				var c = parseFloat(node.ga('eff-pulsing-c'));
				var o = Math.abs(Math.cos(c));
				if ( o < minOpacity )
				{
					o = minOpacity;
				}
				else if ( o > maxOpacity )
				{
					o = maxOpacity;
				}
				node.o(o);
				node.sa('eff-pulsing-c', c+0.18);
				control.to = i+1;
			}
		});
		fry.ui.effect.__reg(node, 'Pulsing');
	},
	__stopPulsing:function(node)
	{
		if ( node.ga('eff-pulsing') )
		{
			node.sa('eff-pulsing-stop', '1');
		}
		fry.ui.effect.__unreg(node, 'Pulsing');
	},
	FadeOut:function(node, speed, callback)
	{
		speed = speed || 5;
		if ( 0 >= speed )
		{
			return;
		}
		node.sa('eff-fadeout', node.o());
		var offset = node.o()/speed;
		node.sa('eff-offset', offset);
		$runinterval(1, speed, 50, function(i, control)
		{
			if ( speed == i || null != node.ga('eff-fadeout-stop') )
			{
				node.o(0);
				control.stop();
				if ( callback )
				{
					callback(node);					
				}
			}
			node.o(node.o()-parseFloat(node.ga('eff-offset')));
		});
		fry.ui.effect.__reg(node, 'FadeOut');
	},
	__stopFadeOut:function(node)
	{
		if ( node.ga('eff-fadeout') )
		{
			node.sa('eff-fadeout-stop', '1');
		}
		fry.ui.effect.__unreg(node, 'FadeOut');
	}
}


