
if (!("console" in window) || !("firebug" in console)) {
(function()
{
    window.console = 
    {
        log: function()
        {
            logFormatted(arguments, "");
        },
        
        debug: function()
        {
            logFormatted(arguments, "debug");
        },
        
        info: function()
        {
            logFormatted(arguments, "info");
        },
        
        warn: function()
        {
            logFormatted(arguments, "warning");
        },
        
        error: function()
        {
            logFormatted(arguments, "error");
        },
        
        assert: function(truth, message)
        {
            if (!truth)
            {
                var args = [];
                for (var i = 1; i < arguments.length; ++i)
                    args.push(arguments[i]);
                
                logFormatted(args.length ? args : ["Assertion Failure"], "error");
                throw message ? message : "Assertion Failure";
            }
        },
        
        dir: function(object)
        {
            var html = [];
                        
            var pairs = [];
            for (var name in object)
            {
                try
                {
                    pairs.push([name, object[name]]);
                }
                catch (exc)
                {
                }
            }
            
            pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; });
            
            html.push('<table>');
            for (var i = 0; i < pairs.length; ++i)
            {
                var name = pairs[i][0], value = pairs[i][1];
                
                html.push('<tr>', 
                '<td class="propertyNameCell"><span class="propertyName">',
                    escapeHTML(name), '</span></td>', '<td><span class="propertyValue">');
                appendObject(value, html);
                html.push('</span></td></tr>');
            }
            html.push('</table>');
            
            logRow(html, "dir");
        },
        
        dirxml: function(node)
        {
            var html = [];
            
            appendNode(node, html);
            logRow(html, "dirxml");
        },
        
        group: function()
        {
            logRow(arguments, "group", pushGroup);
        },
        
        groupEnd: function()
        {
            logRow(arguments, "", popGroup);
        },
        
        time: function(name)
        {
            timeMap[name] = (new Date()).getTime();
        },
        
        timeEnd: function(name)
        {
            if (name in timeMap)
            {
                var delta = (new Date()).getTime() - timeMap[name];
                logFormatted([name+ ":", delta+"ms"]);
                delete timeMap[name];
            }
        },
        
        count: function()
        {
            this.warn(["count() not supported."]);
        },
        
        trace: function()
        {
            this.warn(["trace() not supported."]);
        },
        
        profile: function()
        {
            this.warn(["profile() not supported."]);
        },
        
        profileEnd: function()
        {
        },
        
        clear: function()
        {
            consoleBody.innerHTML = "";
        },

        open: function()
        {
            toggleConsole(true);
        },
        
        close: function()
        {
            if (frameVisible)
                toggleConsole();
        }
    };
 
    // ********************************************************************************************
       
    var consoleFrame = null;
    var consoleBody = null;
    var commandLine = null;
    
    var frameVisible = false;
    var messageQueue = [];
    var groupStack = [];
    var timeMap = {};
    
    var clPrefix = ">>> ";
    
    var isFirefox = navigator.userAgent.indexOf("Firefox") != -1;
    var isIE = navigator.userAgent.indexOf("MSIE") != -1;
    var isOpera = navigator.userAgent.indexOf("Opera") != -1;
    var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1;

    // ********************************************************************************************

    function toggleConsole(forceOpen)
    {
        frameVisible = forceOpen || !frameVisible;
        if (consoleFrame)
		{
            consoleFrame.style.visibility = frameVisible ? "visible" : "hidden";
		}
        else
            waitForBody();
    }

    function focusCommandLine()
    {
        toggleConsole(true);
        if (commandLine)
            commandLine.focus();
    }

    function waitForBody()
    {
        if (document.body)
            createFrame();
        else
            setTimeout(waitForBody, 200);
    }    

    function createFrame()
    {
        if (consoleFrame)
            return;
        
        window.onFirebugReady = function(doc)
        {
            window.onFirebugReady = null;

            var toolbar = doc.getElementById("toolbar");
            toolbar.onmousedown = onSplitterMouseDown;

            commandLine = doc.getElementById("commandLine");
            addEvent(commandLine, "keydown", onCommandLineKeyDown);

            addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
            
            consoleBody = doc.getElementById("log");
            layout();
            flush();
        }

        var baseURL = getFirebugURL();

	    var iframe = document.createElement('iframe');
	    iframe.setAttribute('src', 'about:blank');
	    iframe.setAttribute('width', '100%');
        iframe.setAttribute("frameBorder", "0");
	    iframe.setAttribute('height', '200');
	    consoleFrame = document.body.appendChild(document.createElement('div'));
		consoleFrame.appendChild(iframe);
	    window.frames[window.frames.length-1].location.href = baseURL+"/firebug.html";
        consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden");    
        consoleFrame.style.zIndex = "214748364";
        consoleFrame.style.position = "fixed";
        consoleFrame.style.width = "100%";
        consoleFrame.style.left = "0";
        consoleFrame.style.bottom = "0";
        consoleFrame.style.height = "200px";
    }
    
    function getFirebugURL()
    {
        var scripts = document.getElementsByTagName("script");
        for (var i = 0; i < scripts.length; ++i)
        {
            if (scripts[i].src.indexOf("firebug.js") != -1)
            {
                var lastSlash = scripts[i].src.lastIndexOf("/");
                return scripts[i].src.substr(0, lastSlash);
            }
        }
		return 'client/fry/3rdparty/firebug';
    }
    
    function evalCommandLine()
    {
        var text = commandLine.value;
        commandLine.value = "";

        logRow([clPrefix, text], "command");
        
        var value;
        try
        {
            value = eval(text);
        }
        catch (exc)
        {
        }

        console.log(value);
    }
    
    function layout()
    {
        var toolbar = consoleBody.ownerDocument.getElementById("toolbar");
        var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight);
        consoleBody.style.top = toolbar.offsetHeight + "px";
        consoleBody.style.height = height + "px";
        
        commandLine.style.top = (consoleFrame.offsetHeight - commandLine.offsetHeight) + "px";
    }
    
    function logRow(message, className, handler)
    {
        if (consoleBody)
            writeMessage(message, className, handler);
        else
        {
            messageQueue.push([message, className, handler]);
            waitForBody();
        }
    }
    
    function flush()
    {
        var queue = messageQueue;
        messageQueue = [];
        
        for (var i = 0; i < queue.length; ++i)
            writeMessage(queue[i][0], queue[i][1], queue[i][2]);
    }

    function writeMessage(message, className, handler)
    {
        var isScrolledToBottom =
            consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight;

        if (!handler)
            handler = writeRow;
        
        handler(message, className);
        
        if (isScrolledToBottom)
            consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight;
    }
    
    function appendRow(row)
    {
        var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody;
        container.appendChild(row);
    }

    function writeRow(message, className)
    {
        var row = consoleBody.ownerDocument.createElement("div");
        row.className = "logRow" + (className ? " logRow-"+className : "");
        row.innerHTML = message.join("");
        appendRow(row);
    }

    function pushGroup(message, className)
    {
        logFormatted(message, className);

        var groupRow = consoleBody.ownerDocument.createElement("div");
        groupRow.className = "logGroup";
        var groupRowBox = consoleBody.ownerDocument.createElement("div");
        groupRowBox.className = "logGroupBox";
        groupRow.appendChild(groupRowBox);
        appendRow(groupRowBox);
        groupStack.push(groupRowBox);
    }

    function popGroup()
    {
        groupStack.pop();
    }
    
    // ********************************************************************************************

    function logFormatted(objects, className)
    {
        var html = [];

        var format = objects[0];
        var objIndex = 0;

        if (typeof(format) != "string")
        {
            format = "";
            objIndex = -1;
        }

        var parts = parseFormat(format);
        for (var i = 0; i < parts.length; ++i)
        {
            var part = parts[i];
            if (part && typeof(part) == "object")
            {
                var object = objects[++objIndex];
                part.appender(object, html);
            }
            else
                appendText(part, html);
        }

        for (var i = objIndex+1; i < objects.length; ++i)
        {
            appendText(" ", html);
            
            var object = objects[i];
            if (typeof(object) == "string")
                appendText(object, html);
            else
                appendObject(object, html);
        }
        
        logRow(html, className);
    }

    function parseFormat(format)
    {
        var parts = [];

        var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/;    
        var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat};

        for (var m = reg.exec(format); m; m = reg.exec(format))
        {
            var type = m[8] ? m[8] : m[5];
            var appender = type in appenderMap ? appenderMap[type] : appendObject;
            var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);

            parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1));
            parts.push({appender: appender, precision: precision});

            format = format.substr(m.index+m[0].length);
        }

        parts.push(format);

        return parts;
    }

    function escapeHTML(value)
    {
        function replaceChars(ch)
        {
            switch (ch)
            {
                case "<":
                    return "&lt;";
                case ">":
                    return "&gt;";
                case "&":
                    return "&amp;";
                case "'":
                    return "&#39;";
                case '"':
                    return "&quot;";
            }
            return "?";
        };
        return String(value).replace(/[<>&"']/g, replaceChars);
    }

    function objectToString(object)
    {
        try
        {
            return object+"";
        }
        catch (exc)
        {
            return null;
        }
    }

    // ********************************************************************************************

    function appendText(object, html)
    {
        html.push(escapeHTML(objectToString(object)));
    }

    function appendNull(object, html)
    {
        html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>');
    }

    function appendString(object, html)
    {
        html.push('<span class="objectBox-string">&quot;', escapeHTML(objectToString(object)),
            '&quot;</span>');
    }

    function appendInteger(object, html)
    {
        html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
    }

    function appendFloat(object, html)
    {
        html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
    }

    function appendFunction(object, html)
    {
        var reName = /function ?(.*?)\(/;
        var m = reName.exec(objectToString(object));
        var name = m ? m[1] : "function";
        html.push('<span class="objectBox-function">', escapeHTML(name), '()</span>');
    }
    
    function appendObject(object, html)
    {
        try
        {
            if (object == undefined)
                appendNull("undefined", html);
            else if (object == null)
                appendNull("null", html);
            else if (typeof object == "string")
                appendString(object, html);
            else if (typeof object == "number")
                appendInteger(object, html);
            else if (typeof object == "function")
                appendFunction(object, html);
            else if (object.nodeType == 1)
                appendSelector(object, html);
            else if (typeof object == "object")
                appendObjectFormatted(object, html);
            else
                appendText(object, html);
        }
        catch (exc)
        {
        }
    }
        
    function appendObjectFormatted(object, html)
    {
        var text = objectToString(object);
        var reObject = /\[object (.*?)\]/;

        var m = reObject.exec(text);
        html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>')
    }
    
    function appendSelector(object, html)
    {
        html.push('<span class="objectBox-selector">');

        html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>');
        if (object.id)
            html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>');
        if (object.className)
            html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>');

        html.push('</span>');
    }

    function appendNode(node, html)
    {
        if (node.nodeType == 1)
        {
            html.push(
                '<div class="objectBox-element">',
                    '&lt;<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>');

            for (var i = 0; i < node.attributes.length; ++i)
            {
                var attr = node.attributes[i];
                if (!attr.specified)
                    continue;
                
                html.push('&nbsp;<span class="nodeName">', attr.nodeName.toLowerCase(),
                    '</span>=&quot;<span class="nodeValue">', escapeHTML(attr.nodeValue),
                    '</span>&quot;')
            }

            if (node.firstChild)
            {
                html.push('&gt;</div><div class="nodeChildren">');

                for (var child = node.firstChild; child; child = child.nextSibling)
                    appendNode(child, html);
                    
                html.push('</div><div class="objectBox-element">&lt;/<span class="nodeTag">', 
                    node.nodeName.toLowerCase(), '&gt;</span></div>');
            }
            else
                html.push('/&gt;</div>');
        }
        else if (node.nodeType == 3)
        {
            html.push('<div class="nodeText">', escapeHTML(node.nodeValue),
                '</div>');
        }
    }

    // ********************************************************************************************
    
    function addEvent(object, name, handler)
    {
        if (document.all)
            object.attachEvent("on"+name, handler);
        else
            object.addEventListener(name, handler, false);
    }
    
    function removeEvent(object, name, handler)
    {
        if (document.all)
            object.detachEvent("on"+name, handler);
        else
            object.removeEventListener(name, handler, false);
    }
    
    function cancelEvent(event)
    {
        if (document.all)
            event.cancelBubble = true;
        else
            event.stopPropagation();        
    }

    function onError(msg, href, lineNo)
    {
        var html = [];
        
        var lastSlash = href.lastIndexOf("/");
        var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1);
        
        html.push(
            '<span class="errorMessage">', msg, '</span>', 
            '<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>'
        );
        
        logRow(html, "error");
    };

    function onKeyDown(event)
    {
        if (event.keyCode == 125)
            toggleConsole();
        else if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey
                 && (event.metaKey || event.ctrlKey))
            focusCommandLine();
        else
            return;
        
        cancelEvent(event);
    }

    function onSplitterMouseDown(event)
    {
        if (isSafari || isOpera)
            return;
        
        addEvent(document, "mousemove", onSplitterMouseMove);
        addEvent(document, "mouseup", onSplitterMouseUp);

        for (var i = 0; i < frames.length; ++i)
        {
            addEvent(frames[i].document, "mousemove", onSplitterMouseMove);
            addEvent(frames[i].document, "mouseup", onSplitterMouseUp);
        }
    }
    
    function onSplitterMouseMove(event)
    {
        var win = document.all
            ? event.srcElement.ownerDocument.parentWindow
            : event.target.ownerDocument.defaultView;

        var clientY = event.clientY;
        if (win != win.parent)
            clientY += win.frameElement ? win.frameElement.offsetTop : 0;
        
        var height = consoleFrame.offsetTop + consoleFrame.clientHeight;
        var y = height - clientY;
        
        consoleFrame.style.height = y + "px";
        layout();
    }
    
    function onSplitterMouseUp(event)
    {
        removeEvent(document, "mousemove", onSplitterMouseMove);
        removeEvent(document, "mouseup", onSplitterMouseUp);

        for (var i = 0; i < frames.length; ++i)
        {
            removeEvent(frames[i].document, "mousemove", onSplitterMouseMove);
            removeEvent(frames[i].document, "mouseup", onSplitterMouseUp);
        }
    }
    
    function onCommandLineKeyDown(event)
    {
        if (event.keyCode == 13)
            evalCommandLine();
        else if (event.keyCode == 27)
            commandLine.value = "";
    }
    
    window.onerror = onError;
    addEvent(document.documentElement, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
    
    if (document.documentElement.getAttribute("debug") == "true")
        toggleConsole(true);
})();
}
/*
 * 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.
 */

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

// 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('AppleWebKit'),
	isOpera:-1!=navigator.appName.indexOf('pera'),
	isMac:-1!=navigator.appVersion.indexOf('intosh'),
	isGecko:false,
	node:
	{
		getOpacity:function(node)
		{
		    if ( !node || !node.style )
		    {
		        return 1.0;
		    }
			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 ( !node || !node.style )
		    {
		        return;
		    }
		    if ( 0 > opacity )
		    {
		        opacity = 0;
		    }
		    if ( 1 < opacity )
		    {
		        opacity = 1;
		    }
			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, type)
		{
			if ( $notset(evt.target) )
			{
				evt.target = evt.srcElement;
				evt.stopPropagation = function()
				{
					window.event.cancelBubble = true;
				};
			}
			evt.stop = function()
			{
				evt.stopPropagation();
				evt.stopped = true;
			}
			evt.$ = $(evt.target);
			if ( $notset(evt.pageX) )
			{
				evt.pageX = evt.clientX + document.body.scrollLeft;
				evt.pageY = evt.clientY + document.body.scrollTop;
			}
			evt.getOffsetX = function()
			{
				if ( $notset(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 ( $notset(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.ctrlKey||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 )
			{
                node.attachEvent('on'+type, listener);
			}
			else
			{
				node.addEventListener(type, listener, false);
			}
		},
		removeListener:function(node, type, listener)
		{
			if ( node.detachEvent )
			{
                node.detachEvent('on'+type, listener);
                node['on'+type] = null;
			}
			else if ( node.removeEventListener )
			{
				node.removeEventListener(type, listener, false);
			}				
		}		
	},
	behavior:
	{
		disablePageScroll:function()
		{
			if ( $notset($__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
	},
	selection:
	{
		setRange:function(el, selectionStart, selectionEnd)
		{
			if (el.setSelectionRange)
			{
				el.focus();
				el.setSelectionRange(selectionStart, selectionEnd);
			}
			else if (el.createTextRange)
			{
				var range = el.createTextRange();
				range.collapse(true);
				range.moveEnd('character', selectionEnd);
				range.moveStart('character', selectionStart);
				range.select();
			}
		}
	}
}
// some browsers masks its presence having Gecko string somewhere inside its userAgent field...
$__tune.isGecko = !$__tune.isSafari&&!$__tune.isIE&&-1!=navigator.userAgent.indexOf('ecko');
$__tune.isSafari2 = $__tune.isSafari && -1 != navigator.appVersion.indexOf('Kit/4');
$__tune.isSafari3 = $__tune.isSafari && -1 != navigator.appVersion.indexOf('Kit/5');


// Node manipulations

function ACNode(node)
{
	this.$ = node;
	if ( node )
	{
		node.setAttribute('fryis', '1');		
	}
}
// `$$` 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 this.$ && 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, if called with one argument only, previously registered listeners are removed
ACNode.prototype.e = function(t, c, oneUseOnly)
{
	var ser_type_id = 'fryse-'+t;
	if ( !c )
	{
		if ( null != this.$.getAttribute(ser_type_id) )
		{
			var ser_listeners = this.$.getAttribute(ser_type_id).split(',');
	//		console.log('*E* removing listeners for %s, listeners: %s', t, ser_listeners);
			for ( var i=0; i<ser_listeners.length; i++ )
			{
				$__tune.event.removeListener(this.$, t, self[ser_listeners[i]]);
				self[ser_listeners[i]] = null;
			}
			this.$['on'+t] = null;
			this.$.removeAttribute(ser_type_id);
		}
		return this;
	}
	var hash = t+(''+Math.random()).substr(2);
	var ser_listeners = null != this.$.getAttribute(ser_type_id) ? this.$.getAttribute(ser_type_id).split(',') : [];
	ser_listeners.push(hash);
	this.$.setAttribute(ser_type_id, ser_listeners.join(','));
//	console.log('*E* add listener self.%s for %s', hash, t);
	self[hash] = function(evt)
	{
		evt.removeListener = function()
		{
			$__tune.event.removeListener(evt.$.$, t, self[hash]);
//			console.log('*E* remove listener self.%s for %s', hash, t);
			c = null;
			evt = null;
			self[hash] = null;
		}
		evt = evt || self.event;
		if ( null != c )
		{
			c($__tune.event.get(evt));			
		}
		if ( null != evt )
		{
			if ( oneUseOnly )
			{
				evt.removeListener();
				return;
			}
			else if ( evt.stopped )
			{
//				console.log('*E* stop self.%s for %s', hash, t);
				evt = null;				
			}
		}
	};
	this.$.setAttribute('fryhe', 1);
	$__tune.event.addListener(this.$, t, self[hash]);
	return this;
}

function __fry_esupressed(evt)
{
	evt = evt || self.event;
	if ( !evt.stopPropagation )
	{
		evt.cancelBubble = true;
	}
	else
	{
		evt.stopPropagation();
	}
	evt = null;
	return false;
}
// *e*vent *s*upressed - special case when you want to receive an event, do nothing about it and stop it from propagating
ACNode.prototype.es = function(t)
{
	if ( $__tune.isIE && this.$.attachEvent )
	{
        this.$.attachEvent('on'+t, __fry_esupressed);
	}
	else if ( this.$.addEventListener )
	{
		this.$.addEventListener(t, __fry_esupressed, false);
	}
}
// *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.rc();
	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]);
		var num = ls.length;
		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<num; i++ )
			{
				if ( is_final_index )
				{
					lst.push($(ls.item(i)));
				}
				else
				{
					lookup(ls.item(i), qIndex+1);
				}
			}
		}
		ls = null;
	}
	lookup(this.$, 0);
	lookup = null;
	var num = lst.length;
	if ( 1 == num )
	{
		return lst[0];
	}
	else if ( 0 == num )
	{
		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)
{
	n = $(n);
	if ( null != n && n.is() )
	{
		n.rs();
	}
	return this;
}
// *r*emove *c*hildren
ACNode.prototype.rc = function()
{
	if ( !this.$ )
	{
		return this;
	}
	__fry_gcnode(this.$, true);
	return this;
}
// *r*emoves *s*elf - ! does not return self - therefor the call to .rs() must always be the last in a pipe
ACNode.prototype.rs = function()
{
	if ( !this.$ )
	{
		return;
	}
	__fry_gcnode(this.$);
	this.$ = null;
}
// *i*nserts *c*hild node before specific referenced node (rn)
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 specific referenced node (rn)
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()
{
	if ( !this.$ )
	{
		return null;
	}
	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()
{
	if ( !this.$ )
	{
		return null;
	}
	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)
{
    if ( !this.$ )
    {
        return null;
    }
	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)
{
	if ( 'undefined' == typeof id )
	{
		return $(document.body || document.getElementsByTagName('body').item(0));
	}
	if ( 'undefined' == id || null == id )
	{
		return null;
	}
	if ( 'object' != typeof id )
	{
		return new ACNode(document.getElementById(id));
	}
	else
	{
		if ( id instanceof ACNode )
		{
			return id;
		}
		if ( 1 != id.nodeType )
		{
			return null;
		}
		return new ACNode(id);
	}
}

// Language constructs

/*  $class
	======
	Creates new class, multiple class inheritance is allowed.
	Usage:
	
	$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);
	}
*/
var $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');
}


// $new
// ====
// Creates new object.
// Usage: $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;
}

// $delete
// =======
// Safely deletes object (destructors of each base class are called automatically).
// Usage: $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)
	{
	}
}

// $call
// =====
// Calls a method/function of specific object, typically used from within method to call some base class method.
// Usage: $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;
}

// $runafter
// =========
// Runs embedded code after specified interval (in miliseconds, value 1000 means 1 second).
/* Usage:
	$runafter(100, function()
	{
		// your code
	})
*/
var $runafter = function(t, c)
{
	setTimeout(c, t);
}
// $runinterval
// ============
// Runs embedded code from step `from` to the `to` step, each step is delayed for specified interval.
/* Usage:
	$runinterval(1, 10, 100, function(step)
	{
		// your code that will repeat ten times
	})
*/
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);
}
// $dotimes
// ========
// Repeats embedded code n times.
/* Usage:
	$dotimes(20, function(i)
	{
		// your code, i is the counter parameter
	})
*/
var $dotimes = function(n, c)
{
	for ( var i=0; i<n; i++ )
	{
		c(i);
	}
}
// $foreach
// ========
// Iterates through any kind of collection - it can be practically anything you might need, from arrays, serialized XML, DOM nodes, remote results etc.
/* Usage:
	$foreach ( node.g('table/tr'), function(tr, i, control)
	{
		if ( 5 > i )
		{
			control.skip();
			return;
		}
		tr.n(0==i%2 ? 'even' : 'odd');
		if ( 20 < i )
		{
			control.stop();
		}
	})
*/
var $foreach = function(o, c)
{
	if ( !o )
	{
		c = null;
		return;
	}
	if ( 'undefined' == typeof o.length && 'function' != typeof o.__length )
	{
		c = null;
		return;
	}
	var n = 'function' == typeof o.__length ? o.__length() : o.length;
	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;
		}
	}
	// 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;
		}
	}
	control = null;
	c = null;	
}
var $notset = function(value)
{
	return ( 'undefined' == typeof value || 'undefined' == value || null == value );
}
var $isset = function(value)
{
	return ( 'undefined' != typeof value && 'undefined' != value && null != value );
}
var $getdef = function(value, defaultValue)
{
	if ( 'undefined' == typeof value || 'undefined' == value || null == value )
	{
		return defaultValue;
	}
	return value;
}

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];
	}
	if ( 0 == v.length && 0 < options.length )
	{
		return options[0].value;
	}
	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);
}





/* 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.');
		}
		if ( -1 != url.indexOf('?') )
		{
			url = url.embed(pars['a']);
			delete pars['a'];
		}
		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];
			if ( 'object' == typeof value )
			{
				if ( value.join )
				{
					// array
					var n = value.length;
					for ( var i=0; i<n; i++ )
					{
						value[i] = (''+value[i]).replace(/\],\[/g, '`§~§[]§~§`');
					}
					value = '['+value.join('],[')+']';
					name += '(a)';
				}
				else
				{
					// object
					var values = '[';
					for ( var i in value )
					{
						values += i+'='+(''+value[i]).replace(/\],\[/g, '`§~§[]§~§`')+'],[';
					}
					value = '[' == values ? '' : values.substring(0, values.length-2);
					name += '(o)';
				}
			}
			postData += encodeURIComponent(name)+'='+encodeURIComponent(value)+'&';
		}
		obj.send(postData);
		var poll = window.setInterval
		(
			function()
			{
				if ( 4 == obj.readyState )
				{
					window.clearInterval(poll);
					callback(obj);
				}
			},
			150
		);
	},	
	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');
			mmSWFUpload.init
			({
				thirdPartyPath : client.conf.fry.path+'3rdparty',
				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',
				upload_queue_complete_callback : 'fry_remote_upload_onQueueComplete'
			});
			$('SWFUpload').pos(true).x(1).y(1).w(1).h(1);
			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(e)
{
	fry.remote.upload.lastAdapter.onEnd(false, true, e);
}
function fry_remote_upload_onCancel()
{
	fry.remote.upload.lastAdapter.onEnd(true);
}
function fry_remote_upload_onQueueComplete()
{
	fry.remote.upload.lastAdapter.onQueueEnd();
}



$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, result)
{
}
fry.remote.upload.Adapter.prototype.onQueueEnd = function()
{
}
fry.remote.upload.Adapter.prototype.onGetRemoteActionName = function()
{
	return 'upload?'.embed(this.additionalRemoteActionParams ? ',?'.embed(this.additionalRemoteActionParams) : '');
}


/*  ---------------------------------------------------------------- 
	fry.cookie namespace
*/

fry.cookie =
{
	get:function(name)
	{
		//http://www.webreference.com/js/column8/functions.html
		var dc = document.cookie;
		var prefix = name + "=";
		var begin = dc.indexOf("; " + prefix);
		if (begin == -1) 
		{
			begin = dc.indexOf(prefix);
			if (begin != 0) return null;
		} 
		else
		{
			begin += 2;			
		}
		var end = document.cookie.indexOf(";", begin);
		if (end == -1)
		{
			end = dc.length;
		}
		return unescape( dc.substring(begin + prefix.length, end) );
	},
	remove:function(name, path, domain)
	{
		if (getCookie(name)) 
		{
			document.cookie = name + "=" + ((path) ? "; path=" + path : "") + ((domain) ? "; domain=" + domain : "") + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
		}
	},
	set:function(name, value, expires, path, domain, secure)
	{
		var curCookie = name + "=" + escape(value) + ((expires) ? "; expires=" + expires.toGMTString() : "") + ((path) ? "; path=" + path : "") + ((domain) ? "; domain=" + domain : "") + ((secure) ? "; secure" : "");
		document.cookie = curCookie;
	}
}


var $post = fry.remote.post;
var $result = fry.remote.result;
var $rpost = function(params, callbackOk, callbackError, method, url)
{
	method = method || 'POST';
	url = url || client.conf.fry.backendURL;
	$post( function(s) { $result( s, 
		function(r) 
		{
			callbackOk(r);
		},
		function(e) 
		{
			// result Error
			callbackError(e);
		}
		) },
		params, method, url
	);
}
var $upload = function(adapter)
{
	fry.remote.upload.perform(adapter);
}



// Fry Garbage Collector mechanism
var __gc_trash_node = null;
var __gc_running = false;
var __gc_scheduled_timers = [];
var __gc_started_at = 0;

function __fry_gcnode_inner(inode)
{
	if ( null != inode.getAttribute('frydrag') )
	{
		$(inode).removeDrag();
	}
	if ( null != inode.getAttribute('frydnd') )
	{
		$(inode).removeDnD();
	}
	if ( null == inode.getAttribute('fryis') || null == inode.getAttribute('fryhe') )
	{
		inode = null;
		return;
	}
	inode.removeAttribute('fryhe');
	var lst = inode.attributes;
	var num = lst.length;
	for ( var i=0; i<num; i++ )
	{
		var attr_name = lst.item(i).name;
		if ( attr_name && 'fryse' == attr_name.substr(0,5) )
		{
			var type = attr_name.substr(6);
			var listeners = lst.item(i).value.split(',');
			for ( var ii=0; ii<listeners.length; ii++ )
			{
				$__tune.event.removeListener(inode, type, self[listeners[ii]]);
				inode['on'+type] = null;
				self[listeners[ii]] = null;
			}
		}
		attr = null;
	}
	lst = null;
	for ( var i in inode )
	{
		if ( 'on' == i.substr(0,2) && 'function' == typeof inode[i] )
		{
			inode[i] = null;
		}
	}
	inode = null;
}

function __fry_precaunode(node)
{
	var lst = node.getElementsByTagName('*');
	for ( var i=0; i<lst.length; i++ )
	{
		if ( '' != lst.item(i).id )
		{
			lst.item(i).id = '';
		}
	}
	lst = null;
	node.id = '';
	return node;
}

function __fry_gcnode(node, skipSelf)
{
	if ( null == __gc_trash_node )
	{
		// GC not available at the moment
		if ( skipSelf )
		{
			node.innerHTML = '';
		}
		else
		{
			if ( node.parentNode )
			{
				node.parentNode.removeChild(node);
			}
		}
		return;
	}
	if ( skipSelf )
	{
		try
		{
			while ( null != node.firstChild )
			{
				__gc_trash_node.appendChild(__fry_precaunode(node.firstChild));
			}
		}
		catch(e){}
	}
	else
	{
		try
		{
			__gc_trash_node.appendChild(__fry_precaunode(node));
		}
		catch(e){}
	}
//	console.log('GC scheduled.');
	__gc_scheduled_timers[__gc_scheduled_timers.length] = setTimeout('__fry_gc_recycle()', 10000+Math.floor(10000*Math.random()));
}

function __fry_gc_time()
{
	var d = new Date();
	return 60000*d.getMinutes()+1000*d.getSeconds()+d.getMilliseconds();
}

function __fry_gc_recycle()
{
	var n = __gc_scheduled_timers.length;
	for ( var i=0; i<n; i++ )
	{
		clearTimeout(__gc_scheduled_timers[i]);
	}
	__gc_scheduled_timers = [];
	if ( __gc_running )
	{
//		console.log('GC already running.');
		return;
	}
	__gc_running = true;
	__gc_started_at = __fry_gc_time();
//	console.log('GC running for %s nodes. Started at %s', __gc_trash_node.childNodes.length, __gc_started_at);
	while ( null != __gc_trash_node.firstChild )
	{
		var node = __gc_trash_node.firstChild;
		if ( 1 == node.nodeType )
		{
			var lst = node.getElementsByTagName('*');
			var num = lst.length;
			if ( __gc_started_at < __fry_gc_time() - 2000 )
			{
//				console.log('GC did not finish on time. Stopped at %s after %s msecs of running. Number of remaining nodes: %s.', __fry_gc_time(), __fry_gc_time()-__gc_started_at, __gc_trash_node.childNodes.length);
				__gc_running = false;
				__gc_scheduled_timers[__gc_scheduled_timers.length] = setTimeout('__fry_gc_recycle()', 7000+Math.floor(7000*Math.random()));
				return;
			}
			for ( var ii=0; ii<num; ii++ )
			{
				__fry_gcnode_inner(lst.item(ii));
			}
			lst = null;
		}
		__gc_trash_node.removeChild(node);
		node = null;
	}
//	console.log('GC finished');
	__gc_running = false;
}


$__tune.event.addListener(self, 'load', function(evt)
{
	__gc_trash_node = document.getElementsByTagName('body').item(0).appendChild(document.createElement('div'));
	__gc_trash_node.style.display = 'none';		
});
$__tune.event.addListener(self, 'unload', function(evt)
{
	__gc_trash_node = $().$;
	__fry_gc_recycle();
});	fry.__production_mode = true;
	/*
 * AC Fry - JavaScript Framework v1.0
 *
 * Advanced keyboard handling
 *
 * (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.
 */

/*--------*/

fry.keyboard = 
{
	initialized:false,
	last_down_evt: null,
	ignore_further_events: false,
	stopped:true,
	paste: {was:false, none:function(){}},
	down: {none:function(){}},
	press: {none:function(){}},
	shared:{},
	buffer: [],
	listener: null,
	clipboard:{node:null, ie:{node:null}, pastedContent:'', copiedContent:'', content:''},
	CONTROL_CODE:1,
	ALT_KEY:2,
	CTRL_KEY: 4,
	SHIFT_KEY:8,
	META_KEY:16,
	COPY: 128,
	CUT: 256,
	PASTE: 512,
	SIG_CLIPBOARD_GET:1024
}

fry.keyboard.initialize = function()
{
	if (fry.keyboard.initialized)
	{
		fry.keyboard.start();
		return;
	}
	var react_as = 'none';
	
	if ($__tune.isGecko)
	{
		react_as = 'ff_' + ($__tune.isMac ? 'mac' : 'win');
	}
	else if ($__tune.isSafari)
	{
		react_as = 'webkit';
	}
	else if ($__tune.isIE)
	{
		react_as = 'ie';
	}
	else if ($__tune.isOpera)
	{
		react_as = 'opera';
	}
	// following scroll listeners will accomplish movement of helper textareas if page scrolls. Due focusing, page would scroll up when pressing paste combination.
	document.onscroll = function(evt)
	{
		if (fry.keyboard.clipboard.node)
		{
			fry.keyboard.clipboard.node.style.top = ($__tune.isSafari? document.body.scrollTop : document.documentElement.scrollTop) + 'px';
		}
	}
	if ($__tune.isIE)
	{
		document.body.onscroll = function(evt)
		{
			if (fry.keyboard.clipboard.node)
			{
				fry.keyboard.clipboard.node.style.top = document.documentElement.scrollTop + 'px';
			}
			if (fry.keyboard.clipboard.ie.node)
			{
				fry.keyboard.clipboard.ie.node.style.top = document.documentElement.scrollTop + 'px';
			}
		}
	}
	var code = "document.onkeydown = function(evt) {\n";
	code += "if (fry.keyboard.stopped) { return; }\n";
	code += "fry.keyboard.ignore_further_events = false;\n";
	code += "var result = null;\n";
	code += $__tune.isIE ? 'evt = event;\n' : '';
	code += (''+fry.keyboard.paste[react_as]).replace(/function[ ]+\(evt\)/, '').replace(/return /g, 'result=').replace(/^[^{]*{/, '').replace(/\}\s*$/, '').replace('fry.keyboard.shared.copy(evt);', (''+fry.keyboard.shared.copy).replace(/function[ ]+\(evt\)/, '').replace(/return /g, 'result=').replace(/^[^{]*{/, '').replace(/\}\s*$/, ''));
	code += "if (fry.keyboard.ignore_further_events) {	return;	}\n";
	code += "if (result) { fry.keyboard.prepareClipboard();	fry.keyboard.paste.was = true; fry.keyboard.clipboard.node.value = ''; fry.keyboard.clipboard.node.focus();	return true; }\n";
	code += "else { ";
	code += $__tune.isIE ? "if (86 == event.keyCode && event.ctrlKey){ fry.keyboard.clipboard.ie.node.value = ''; fry.keyboard.clipboard.ie.node.focus(); }\n" : "";
	code += "}\n";
	code += (''+fry.keyboard.down[react_as]).replace(/function[ ]+\(evt\)/, '').replace(/^[^{]*{/, '').replace(/\}\s*$/, '') + "\n}"
	code = code.replace(/fry\.keyboard\.([A-Z_]+)/g, function() {return fry.keyboard[arguments[1]];});
    // alert(code);
	eval(code);
	
	code = "document.onkeypress = function(evt)	{\n";
	code += "if (fry.keyboard.stopped || fry.keyboard.ignore_further_events) { return; }\n";
	code += "if (fry.keyboard.paste.was) {\n";
	if (!$__tune.isIE)
	{
	    code += "setTimeout(function() {\n";
		if ($__tune.isSafari)
		{
		    // bug in WebKit - append \n if inserted value ends with \n causing double \n as resulting read value
		    code += "var v = fry.keyboard.clipboard.node.value; if ('\\n\\n' == v.substr(v.length-2,2)) {v = v.substr(0, v.length-1);}fry.keyboard.clipboard.node.value = v;\n";
		}
		code += "fry.keyboard.pushKey(fry.keyboard.clipboard.node.value, fry.keyboard.CONTROL_CODE | fry.keyboard.PASTE); fry.keyboard.clipboard.node.blur(); fry.keyboard.paste.was = false;	}, 20);\n";
	}
	code += "return; }\n";
	if ($__tune.isIE)
	{
    	code += "evt = event;\n"
	}
	code += (''+fry.keyboard.press[react_as]).replace(/function[ ]+\(evt\)/, '').replace(/^[^{]*{/, '').replace(/\}\s*$/, '') + "\n}"
	code = code.replace(/fry\.keyboard\.([A-Z_]+)/g, function() {return fry.keyboard[arguments[1]];});
    // alert(code);
	eval(code);
	
	document.onkeydown2 = function(evt)
	{
		if (fry.keyboard.stopped)
		{
			return;
		}
		fry.keyboard.ignore_further_events = false;
		var result = fry.keyboard.paste[react_as](evt || event);
		if (fry.keyboard.ignore_further_events)
		{
			return;
		}
		if (result)
		{
			fry.keyboard.prepareClipboard();
			fry.keyboard.paste.was = true;
			fry.keyboard.clipboard.node.value = '';
			fry.keyboard.clipboard.node.focus();
			return true;
		}
		else
		{
			if ($__tune.isIE)
			{
				if (86 == event.keyCode && event.ctrlKey)
				{
					fry.keyboard.clipboard.ie.node.value = '';
					fry.keyboard.clipboard.ie.node.focus();
				}
			}			
		}
		return fry.keyboard.down[react_as](evt || event);
	}
	document.onkeypress2 = function(evt)
	{
		if (fry.keyboard.stopped || fry.keyboard.ignore_further_events)
		{
			return;
		}
		if (fry.keyboard.paste.was)
		{
			if (!$__tune.isIE)
			{
				setTimeout(function()
				{
					if ($__tune.isSafari)
					{
						// bug in WebKit - append \n if inserted value ends with \n causing double \n as resulting read value
						var v = fry.keyboard.clipboard.node.value;
						if ('\n\n' == v.substr(v.length-2,2))
						{
							v = v.substr(0, v.length-1);
						}
						fry.keyboard.clipboard.node.value = v;
					}
					fry.keyboard.pushKey(fry.keyboard.clipboard.node.value, fry.keyboard.CONTROL_CODE | fry.keyboard.PASTE);
					fry.keyboard.clipboard.node.blur();
					fry.keyboard.paste.was = false;
				}, 20);
			}
			return;
		}
		return fry.keyboard.press[react_as](evt || event);
	}
	if ($__tune.isIE)
	{
		fry.keyboard.prepareClipboard();
		fry.keyboard.clipboard.ie = {node:$().a($$('textarea')).pos(true).x(-2000).y(document.documentElement.scrollTop).w(20).h(20).e('paste', function(evt)
		{
			if (fry.keyboard.stopped)
			{
				return;
			}
			setTimeout(function(){
				fry.keyboard.paste.was = false;
				fry.keyboard.pushKey(fry.keyboard.clipboard.ie.node.value, fry.keyboard.CONTROL_CODE | fry.keyboard.PASTE);
			}, 10);
		}).$};
	}
	if ($__tune.isGecko && $__tune.isMac)
	{
		document.onkeyup = function(evt)
		{
			if (evt.metaKey && !evt.ctrlKey && !evt.altKey && !evt.shiftKey && 65 <= evt.keyCode && 128 >= evt.keyCode)
			{
				fry.keyboard.pushKey(evt.keyCode + 32, fry.keyboard.META_KEY);
				evt.preventDefault();
				return true;
			}
		}
	}
	fry.keyboard.initialized = true;
	fry.keyboard.start();
}

fry.keyboard.start = function()
{
	fry.keyboard.stopped = false;
}

fry.keyboard.stop = function()
{
	fry.keyboard.stopped = true;
}

fry.keyboard.disableTextfieldsEditation = function()
{
	fry.keyboard.allowTextfieldsEditation(true);
}

fry.keyboard.allowTextfieldsEditation = function(disable)
{
	var lst = document.getElementsByTagName('input');
	var n = lst.length;
	for (var i=0; i<n; i++)
	{
		var item = lst.item(i);
		if ('text' == item.type || 'password' == item.type)
		{
			if (disable)
			{
				item.onfocus = null;
				item.onblur = null;
				item.onkeydown = null;
				item.onkeypress = null;
				item.onkeyup = null;
				continue;
			}
			$(item).e('keydown', function(evt){evt.stopPropagation();}).e('keypress', function(evt){evt.stopPropagation();}).e('keyup', function(evt){evt.stopPropagation();});
			item.onfocus = function(evt)
			{
				fry.keyboard.stop();
			}
			item.onblur = function(evt)
			{
				fry.keyboard.start();
			}
		}
	}
	lst = document.getElementsByTagName('textarea');
	var n = lst.length;
	for (var i=0; i<n; i++)
	{
		var item = lst.item(i);
		if (disable)
		{
			item.onfocus = null;
			item.onblur = null;
			item.onkeydown = null;
			item.onkeypress = null;
			item.onkeyup = null;
			continue;
		}
		$(item).e('keydown', function(evt){evt.stopPropagation();}).e('keypress', function(evt){evt.stopPropagation();});
		item.onfocus = function(evt)
		{
			fry.keyboard.stop();
		}
		item.onblur = function(evt)
		{
			fry.keyboard.start();
		}
	}
}

fry.keyboard.prepareClipboard = function()
{
	if (fry.keyboard.clipboard.node)
	{
		return;
	}
	fry.keyboard.clipboard.node = $().a($$('textarea')).w(20).h(20).pos(true).x(-2000).y($__tune.isSafari? document.body.scrollTop : document.documentElement.scrollTop).$;
}

fry.keyboard.shared.copy = function(evt)
{
	fry.keyboard.prepareClipboard();
	// acquiring clipboard content to be copied into system clipboard
	fry.keyboard.clipboard.copiedContent = '';
	if (fry.keyboard.listener)
	{
		var content = fry.keyboard.listener(0, fry.keyboard.CONTROL_CODE | fry.keyboard.SIG_CLIPBOARD_GET);
		if ('string' == typeof content)
		{
			fry.keyboard.clipboard.copiedContent = content;
		}
	}
	fry.keyboard.clipboard.node.value = fry.keyboard.clipboard.copiedContent;
	fry.keyboard.clipboard.node.select();
	fry.keyboard.clipboard.node.focus();
	setTimeout('fry.keyboard.clipboard.node.blur()', 200);
	fry.keyboard.clipboard.content = fry.keyboard.clipboard.copiedContent;
	fry.keyboard.ignore_further_events = true;
	// sending control code
	fry.keyboard.pushKey(0, fry.keyboard.CONTROL_CODE | (88 == evt.keyCode ? fry.keyboard.CUT : fry.keyboard.COPY));
	return false;
}

fry.keyboard.paste.ff_win = function(evt)
{
	return 86 == evt.keyCode && 0 == evt.charCode && 86 == evt.which && evt.ctrlKey;
}

fry.keyboard.down.ff_win = function(evt)
{
	fry.keyboard.last_down_evt = evt;
	return true;
}

fry.keyboard.press.ff_win = function(evt)
{
	if (null != fry.keyboard.last_down_evt)
	{
		var mask = (evt.altKey ? 2 : 0) + (evt.ctrlKey ? 4 : 0) + (evt.shiftKey ? 8 : 0) + (evt.metaKey ? 16 : 0);
		var code = !evt.keyCode ? evt.charCode : evt.keyCode;
		if (evt.keyCode == evt.charCode && evt.keyCode == fry.keyboard.last_down_evt.charCode)
		{
			code = fry.keyboard.last_down_evt.keyCode;
		}
		if (evt.keyCode == fry.keyboard.last_down_evt.keyCode && (0 == evt.which || 32 > evt.keyCode))
		{
			// control code
			mask++;
		}
		if (!fry.keyboard.pushKey(code, mask))
		{
			return true;
		}
	}
	evt.preventDefault();
	evt.stopPropagation();
	return false;
}

fry.keyboard.paste.ff_mac = function(evt)
{
	fry.keyboard.last_down_evt = null;
	// catching Command+C, Command+X, it's a FF.mac hack
	if (evt.metaKey && ((67 == evt.keyCode && 0 == evt.charCode && 67 == evt.which) || (88 == evt.keyCode && 0 == evt.charCode && 88 == evt.which)))
	{
		return fry.keyboard.shared.copy(evt);
	}
	else
	{
    	return 86 == evt.keyCode && 0 == evt.charCode && 86 == evt.which && evt.metaKey;
	}
}

fry.keyboard.down.ff_mac = function(evt)
{
	return false;
}

fry.keyboard.press.ff_mac = function(evt)
{
	if (null != fry.keyboard.last_down_evt)
	{
		return;
	}
	var mask = (evt.altKey ? 2 : 0) + (evt.ctrlKey ? 4 : 0) + (evt.shiftKey ? 8 : 0) + (evt.metaKey ? 16 : 0);
	if (!evt.charCode || (evt.charCode == evt.keyCode))
	{
		// control code
		fry.keyboard.pushKey(evt.keyCode, 1 + mask);
	}
	else
	{
		if (!fry.keyboard.pushKey(evt.charCode, mask))
		{
			return true;
		}
	}
	evt.preventDefault();
	evt.stopPropagation();
	return false;
}

fry.keyboard.paste.webkit = function(evt)
{
	if ($__tune.isMac)
	{
		return (86 == evt.keyCode && (0 == evt.charCode || 118 == evt.charCode) && evt.metaKey);
	}
	else
	{
		return (86 == evt.keyCode && (0 == evt.charCode || 118 == evt.charCode) && evt.ctrlKey);
	}
}

fry.keyboard.down.webkit = function(evt)
{
	if (0 != evt.keyCode && (48 > evt.keyCode || (111 < evt.keyCode && 128 > evt.keyCode) || 60000 < evt.charCode))
	{
		var mask = (evt.altKey ? 2 : 0) + (evt.ctrlKey ? 4 : 0) + (evt.shiftKey ? 8 : 0) + (evt.metaKey ? 16 : 0);
		if (!evt.charCode || 111 < evt.keyCode || 32 > evt.charCode || 60000 < evt.charCode)
		{
			// control code
			fry.keyboard.pushKey(evt.keyCode, 1 + mask);
		}
		else
		{
			if (!fry.keyboard.pushKey(evt.charCode, mask))
			{
				return true;
			}
		}
		evt.preventDefault();
		evt.stopPropagation();
		fry.keyboard.last_down_evt = null;
		return false;
	}
	fry.keyboard.last_down_evt = evt;
	return true;
}

fry.keyboard.press.webkit = function(evt)
{
	if (null != fry.keyboard.last_down_evt)
	{
		var mask = (evt.altKey ? 2 : 0) + (evt.ctrlKey ? 4 : 0) + (evt.shiftKey ? 8 : 0) + (evt.metaKey ? 16 : 0);
		var code = !evt.keyCode ? evt.charCode : evt.keyCode;
		if (evt.keyCode == evt.charCode && evt.keyCode == fry.keyboard.last_down_evt.charCode && evt.keyCode > 60000)
		{
			code = fry.keyboard.last_down_evt.keyCode;
		}
		if (evt.keyCode == fry.keyboard.last_down_evt.keyCode && 48 > evt.keyCode)
		{
			// control code
			mask++;
		}
		else
		{
			var r_mask = fry.keyboard.SHIFT_KEY + fry.keyboard.META_KEY;
			if (r_mask == (mask & r_mask) && 97 <= code && 122 >= code)
			{
				code -= 32;
			}
		}
		if (!fry.keyboard.pushKey(code, mask))
		{
			return true;
		}
	}
	evt.preventDefault();
	evt.stopPropagation();
	return false;			
}

fry.keyboard.paste.ie = function(evt)
{
	if (evt.ctrlKey && (67 == evt.keyCode || 88 == evt.keyCode))
	{
		// ctrl+c, ctrl+x
		return fry.keyboard.shared.copy(evt);
	}
	else
	{
    	return false;
	}
}

fry.keyboard.down.ie = function(evt)
{
	fry.keyboard.last_down_evt = evt;
	if (48 > evt.keyCode || (111 < evt.keyCode && 128 > evt.keyCode))
	{
		// control code for IE
		var mask = 1 + (evt.altKey ? 2 : 0) + (evt.ctrlKey ? 4 : 0) + (evt.shiftKey ? 8 : 0) + (evt.metaKey ? 16 : 0);
		return !fry.keyboard.pushKey(evt.keyCode, mask)
	}
	else
	{
		var code = evt.keyCode;
		// disabling some other keys (A, F, N, R, S, T)
		if (82 == evt.keyCode || 65 == evt.keyCode || 83 == evt.keyCode || 70 == evt.keyCode || 78 == evt.keyCode || 84 == evt.keyCode)
		{
			if (!evt.shiftKey)
			{
				code += 32;
			}
			var mask = (evt.altKey ? 2 : 0) + (evt.ctrlKey ? 4 : 0) + (evt.shiftKey ? 8 : 0) + (evt.metaKey ? 16 : 0);
			return !fry.keyboard.pushKey(code, mask);
		}
	}
	return true;			
}

fry.keyboard.press.ie = function(evt)
{
	if (null != fry.keyboard.last_down_evt)
	{
		var mask = (evt.altKey ? 2 : 0) + (evt.ctrlKey ? 4 : 0) + (evt.shiftKey ? 8 : 0) + (evt.metaKey ? 16 : 0);
		return !fry.keyboard.pushKey(evt.keyCode, mask);
	}
	return false;			
}


fry.keyboard.paste.opera = function(evt)
{
	return 86 == evt.keyCode && 86 == evt.which && evt.ctrlKey;
}

fry.keyboard.down.opera = function(evt)
{
	fry.keyboard.last_down_evt = evt;
	return false;
}

fry.keyboard.press.opera = function(evt)
{
	var e = fry.keyboard.last_down_evt;
	var mask = (evt.altKey ? 2 : 0) + (evt.ctrlKey ? 4 : 0) + (evt.shiftKey ? 8 : 0) + (evt.metaKey ? 16 : 0);
	var prev_mask = (e.altKey ? 2 : 0) + (e.ctrlKey ? 4 : 0) + (e.shiftKey ? 8 : 0) + (e.metaKey ? 16 : 0);
	if ((evt.keyCode == fry.keyboard.last_down_evt.keyCode || 0 == e.keyCode) && (0 == evt.which || 48 > e.keyCode || 111 < e.keyCode))
	{
		mask++;
	}
	if (!fry.keyboard.pushKey(evt.keyCode, mask))
	{
		return true;
	}
	evt.preventDefault();
	evt.stopPropagation();
	return false;
}

fry.keyboard.addListener = function(listener)
{
	fry.keyboard.listener = listener;
}

fry.keyboard.removeListener = function(listener)
{
    fry.keyboard.listener = null;
}


fry.keyboard.pushKey = function(code, mask)
{
	if (32 == code)
	{
		mask = mask & 65534;
	}
	var was_clipboard_copy = false;
	var was_clipboard_cut = false;
	if ($__tune.isMac)
	{
		was_clipboard_copy = (99 == code) && (fry.keyboard.META_KEY == (mask & fry.keyboard.META_KEY));
		was_clipboard_cut = (120 == code) && (fry.keyboard.META_KEY == (mask & fry.keyboard.META_KEY));
	}
	else
	{
		was_clipboard_copy = (1 == code || 99 == code) && (fry.keyboard.CTRL_KEY == (mask & fry.keyboard.CTRL_KEY));
		was_clipboard_cut = (24 == code || 120 == code) && (fry.keyboard.CTRL_KEY == (mask & fry.keyboard.CTRL_KEY));
	}
	if (was_clipboard_copy || was_clipboard_cut)
	{
		fry.keyboard.prepareClipboard();
		fry.keyboard.clipboard.copiedContent = '';
		var was_custom_content = false;
		if (fry.keyboard.listener)
		{
			var content = fry.keyboard.listener(0, fry.keyboard.CONTROL_CODE | fry.keyboard.SIG_CLIPBOARD_GET);
			if ('string' == typeof content)
			{
				was_custom_content = true;
				fry.keyboard.clipboard.copiedContent = content;
			}
		}
		if (was_custom_content)
		{
			fry.keyboard.clipboard.node.value = fry.keyboard.clipboard.copiedContent;
			fry.keyboard.clipboard.node.select();
			fry.keyboard.clipboard.node.focus();
		}
		fry.keyboard.pushKey(0, fry.keyboard.CONTROL_CODE | (was_clipboard_cut ? fry.keyboard.CUT : fry.keyboard.COPY));
		fry.keyboard.clipboard.content = fry.keyboard.clipboard.copiedContent;
		// returning false will enforce propagation
		return !was_custom_content;
	}
	if (fry.keyboard.PASTE == (mask & fry.keyboard.PASTE))
	{
		fry.keyboard.clipboard.pastedContent = code;
		fry.keyboard.clipboard.content = fry.keyboard.clipboard.pastedContent;
		code = 0;
	}
	// filtering out solo-keys Ctrl, Shift, Alt that are triggered by some browsers.
	if ((17 == code && 5 == (mask & 5)) || (16 == code && 9 == (mask & 9)) || (18 == code && 3 == (mask & 3)))
	{
		return true;
	}
	// filtering out Command+V on FF.mac
	if (118 == code && 16 == mask)
	{
		return true;
	}
	if (fry.keyboard.listener)
	{
        // console.info(code);
	    if (13 != code && 0 != (mask & (fry.keyboard.CONTROL_CODE + fry.keyboard.META_KEY + fry.keyboard.CTRL_KEY)))
	    {
	        // some keystroke occured that we really want to know about the listener result, we have to call it immediatelly
			return fry.keyboard.listener(code, mask);
	    }
	    else
	    {
	        // let's ease the pain of the browser
            setTimeout('fry.keyboard.listener('+code+','+mask+')', 10);
	    }
	}
	else
	{
		fry.keyboard.buffer.unshift([code, mask]);
	}
	return true;
}

fry.keyboard.popKey = function()
{
	return fry.keyboard.buffer.pop();
}

fry.keyboard.getClipboardContent = function()
{
	return fry.keyboard.clipboard.content;
}


/*--------*/
if ( 'undefined' != typeof fry && 'undefined' != typeof fry.script )
{
	fry.script.register();
}/*
 * AC Fry - JavaScript Framework v1.0
 *
 * UI core support extension
 *
 * (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.
 */

/*--------*/

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()
			bool onDragStart(evt) - if true is returned and using cursor node, original node is not hidden
			{x:, y:} | null onDragMove(dragNode, nx, ny, offsetX, offsetY)
			[boolean] onDragStop()	- if true is returned, original node is left intact (it has no coordinates set after drag node)
			var onGetData()
			void onClick(evt)
			node onGetCursorNode(pageX, pageY, offsetX, offsetY)
			[top, right, bottom, left] onGetCursorPadding() - applies for DnD.drag only
			
		DnD.drop:
			node onGetRenderingNode()
			{x:, y:, w:, h:} onGetCoords()
			void onDragEnter(firstEnter, offsetX, offsetY, sourceNode, sourceAdapter, allTargets, targetIndex)
			void onDragLeave(lastLeave)
			void onPutData(data, sourceNode, sourceAdapter, offsetX, offsetY, controlKeyPressed, allTargets, targetIndex)
		
	*/
	/*  ---------------------------------------------------------------- 
		fry.ui.DragAdapter
	*/	
	fry.ui.DragAdapter.prototype.onGetRenderingNode = function()
	{
		return this.node;
	}
	fry.ui.DragAdapter.prototype.onDragStart = function(evt)
	{
	}
	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, sourceNode, sourceAdapter, allTargets, targetIndex)
	{
	}
	fry.ui.DropAdapter.prototype.onDragLeave = function(lastLeave)
	{
	}
	fry.ui.DropAdapter.prototype.onPutData = function(data, sourceNode, sourceAdapter, offsetX, offsetY, controlKeyPressed, allTargets, targetIndex)
	{
	}	
	/*  ---------------------------------------------------------------- 
		Drag
	*/

	fry.ui.drag.start = function(mode, adapter, pageX, pageY)
	{
		$__tune.behavior.clearSelection();
		fry.ui.drag.node = adapter.node;
		fry.ui.drag.adapter = adapter;
		fry.ui.drag.mode = mode;
//		console.log('*DRAG* started.');
		with ( fry.ui.drag.state )
		{
			started = true;
			x = ix = pageX;
			y = iy = pageY;
			mdownMillis = fry.ui.util.getMillis();
		}
		fry.ui.drag.dragNode = null;
	}
	
	fry.ui.drag.move = function(evt)
	{
		$__tune.behavior.clearSelection();
		var state = fry.ui.drag.state;
		var millisOffset = fry.ui.util.getMillis() - state.mdownMillis;
		evt = $__tune.event.get(evt||self.event);
		if ( state.clickInterval > millisOffset && state.ix == evt.pageX && state.iy == evt.pageY )
		{
			// dragging not started yet
			evt.stop();
			evt = null;
//			console.log('*DRAG* not started yet');
			return;
		}
		if ( null == fry.ui.drag.dragNode )
		{
			// initializing cursor/drag node
			var not_hide = fry.ui.drag.adapter.onDragStart(evt);
			if ( fry.ui.drag.adapter.onGetCursorNode )
			{
				fry.ui.drag.dragNode = fry.ui.drag.adapter.onGetCursorNode(evt.pageX, evt.pageY, evt.getOffsetX(), evt.getOffsetY());
			}
			if ( null == fry.ui.drag.dragNode )
			{
				fry.ui.drag.dragNode = fry.ui.drag.node;
			}
			else
			{
				state.usingCursor = true;
				var pos = $(fry.ui.drag.node).abspos();
				fry.ui.drag.dragNode = $().a(fry.ui.drag.dragNode).pos(true).x(pos.x).y(pos.y);
				if ( !not_hide )
				{
					fry.ui.drag.node.v(false);					
				}
			}
			fry.ui.drag.dragNode.z(99999);
		}
		with ( state )
		{
			var ox = evt.pageX - x;
			var oy = evt.pageY - y;
			x = evt.pageX;
			y = evt.pageY;
			var nx = fry.ui.drag.dragNode.x() + ox;
			var ny = fry.ui.drag.dragNode.y() + oy;
			var coords = fry.ui.drag.adapter.onDragMove(fry.ui.drag.node, nx, ny, ox, oy);
			if ( null != coords )
			{
				if ( !isNaN(coords.x) )
				{
					nx = coords.x;
				}
				if ( !isNaN(coords.y) )
				{
					ny = coords.y;
				}
			}
			if ( fry.ui.drag.MODE_VERTICAL != (fry.ui.drag.MODE_VERTICAL & fry.ui.drag.mode) )
			{
				fry.ui.drag.dragNode.x(nx);
			}
			if ( fry.ui.drag.MODE_HORIZONTAL != (fry.ui.drag.MODE_HORIZONTAL & fry.ui.drag.mode) )
			{
				fry.ui.drag.dragNode.y(ny);
			}
		}
		evt.stop();
		evt = null;		
	}	
	fry.ui.drag.stop = function()
	{
//		console.log('*M* up');
		var state = fry.ui.drag.state;
		$__tune.behavior.clearSelection();
		state.started = false;
		var leave_node = fry.ui.drag.adapter.onDragStop();
		if ( state.usingCursor )
		{
			if ( null != fry.ui.drag.dragNode )
			{
				if ( !leave_node )
				{
					fry.ui.drag.node.x(fry.ui.drag.dragNode.x()).y(fry.ui.drag.dragNode.y());							
				}
				fry.ui.drag.dragNode.rs();
			}
			fry.ui.drag.node.v(true);
		}
		state.usingCursor = false;
		var millisOffset = fry.ui.util.getMillis() - state.mdownMillis;
		if ( state.clickInterval > millisOffset )
		{
			if ( fry.ui.drag.adapter.onClick )
			{
				fry.ui.drag.adapter.onClick();
			}
		}
		fry.ui.drag.node = null;
		fry.ui.drag.mode = null;
		fry.ui.drag.dragNode = null;
		fry.ui.drag.adapter = null;
	}
	

	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.');
		}
		// optional methods
		$foreach ( ['onDragStart', 'onDragMove', 'onDragStop', 'onClick', 'onGetCursorNode'], function(method)
		{
			if ( !adapter[method] )
			{
				adapter[method] = function(){return null;};
			}				
		});
		adapter.node = adapter.onGetRenderingNode ? adapter.onGetRenderingNode() : this;
		if ( !adapter.node )
		{
			throw new FryException(102, 'fry.ui: Rendering node not specified.');			
		}
		this.e('mousedown', function(evt)
		{
			fry.ui.drag.start(mode, adapter, evt.pageX, evt.pageY);
			evt.stop();
			evt = null;
		});
		this.sa('frydrag', 1);
		return this;
	}
	ACNode.prototype.removeDrag = function()
	{
		this.e('mousedown');
		this.ra('frydrag');
		return this;
	}

	/*  ---------------------------------------------------------------- 
		DnD (Drag and Drop)
	*/
	
	// Helper method for clearing targets coordinates _during_ drag - there's an internal coordinates cache created right after new drag is started.
	// Sometimes it's necessary to clean it explicitly because position of elements change during drag. There's no need to call it if you 
	// have all drop targets sitting at their position during the drag (mouse) move! Typicall use is the OutlineView widget whose elements (drop targets)
	// may change position during "spring-loading".
	fry.ui.dnd.cleanTargetsCache = function()
	{
		for ( var i in fry.ui.dnd.targets )
		{
			var target = fry.ui.dnd.targets[i];
			if ( null != target.coordsCache )
			{
				target.coordsCache = null;
				if ( null == target.node || !target.node.is() )
				{
					target.adapter = null;
					target.node = null;
					target = null;
					delete fry.ui.dnd.targets[i];
				}				
			}
		}
	}
	
	fry.ui.dnd.start = function(mode, adapter, pageX, pageY, offsetX, offsetY, wasControlKey)
	{
		$__tune.behavior.clearSelection();
		fry.ui.dnd.node = adapter.node;
		fry.ui.dnd.adapter = adapter;
		fry.ui.dnd.mode = mode;		
		fry.ui.dnd.dragNode = null;
		fry.ui.dnd.wasControlKey = wasControlKey;
		
		fry.ui.dnd.state.started = true;
		fry.ui.dnd.state.x = fry.ui.dnd.state.ix = pageX;
		fry.ui.dnd.state.y = fry.ui.dnd.state.iy = pageY;
		fry.ui.dnd.state.offsetX = offsetX;
		fry.ui.dnd.state.offsetY = offsetY;
		fry.ui.dnd.state.mdownMillis = fry.ui.util.getMillis();
		fry.ui.dnd.state.checked = false;
		// cleaning targets caches
		for ( var i in fry.ui.dnd.targets )
		{
			var target = fry.ui.dnd.targets[i];
			target.coordsCache = null;
			target.isActive = false;
		}		
	}
	
	fry.ui.dnd.move = function(evt)
	{
		$__tune.behavior.clearSelection();
		var state = fry.ui.dnd.state;
		evt = $__tune.event.get(evt||self.event);
		if ( !state.started )
		{
			// false alarm, remove listener!
			evt.stop();
			evt = null;
			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
			evt.stop();
			evt = null;
			return;
		}
		if ( null == fry.ui.dnd.dragNode )
		{
			// initializing cursor/drag node
			fry.ui.dnd.adapter.onDragStart();
			if ( fry.ui.dnd.adapter.onGetCursorNode )
			{
				fry.ui.dnd.dragNode = fry.ui.dnd.adapter.onGetCursorNode(evt.pageX, evt.pageY, evt.getOffsetX(), evt.getOffsetY());
			}
			if ( null == fry.ui.dnd.dragNode )
			{
//				console.log('%d - %d', state.ix, state.offsetX);
				fry.ui.dnd.dragNode = $().a($(fry.ui.dnd.adapter.node).dup()).pos(true).x(state.ix-state.offsetX).y(state.iy-state.offsetY);
			}
			else
			{
				fry.ui.dnd.dragNode = $().a(fry.ui.dnd.dragNode);
				var pos = $(fry.ui.dnd.adapter.node).abspos();
//				console.log('%d - %d', evt.pageX, evt.pageY);
				fry.ui.dnd.dragNode.pos(true).x(evt.pageX-state.offsetX).y(evt.pageY-state.offsetY);
			}
			fry.ui.dnd.dragNode.z(99999);
		}
		fry.ui.dnd.pageX = evt.pageX;
		fry.ui.dnd.pageY = evt.pageY;
		fry.ui.dnd.controlKeyPressed = evt.isAnyControlKeyPressed();
		with ( state )
		{
			var ox = evt.pageX - x;
			var oy = evt.pageY - y;
			x = evt.pageX;
			y = evt.pageY;
			var nx = fry.ui.dnd.dragNode.x() + ox;
			var ny = fry.ui.dnd.dragNode.y() + oy;
			var coords = fry.ui.dnd.adapter.onDragMove(fry.ui.dnd.dragNode, nx, ny, ox, oy);
			if ( null != coords )
			{
				if ( coords.x )
				{
					nx = coords.x;
				}
				if ( coords.y )
				{
					ny = coords.y;
				}
			}
			fry.ui.dnd.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;
				fry.ui.dnd.checkTargets(x, y);
			}						
		}
	}
	
	fry.ui.dnd.stop = function()
	{
		$__tune.behavior.clearSelection();
		var state = fry.ui.dnd.state;
		state.started = false;
		fry.ui.dnd.adapter.onDragStop(false);
		var millisOffset = fry.ui.util.getMillis() - state.mdownMillis;
		if ( state.clickInterval > millisOffset )
		{
			if ( fry.ui.dnd.adapter.onClick )
			{
				fry.ui.dnd.adapter.onClick();
			}
			if ( null != fry.ui.dnd.dragNode )
			{
				fry.ui.dnd.dragNode.rs();
				fry.ui.dnd.dragNode = null;				
			}
			fry.ui.dnd.node = null;
			fry.ui.dnd.adapter = null;
			return;
		}
		var active_targets = [];
		// not a click, checking for targets
		if ( fry.ui.dnd.MODE_DRAG_POINTER == (fry.ui.dnd.mode & fry.ui.dnd.MODE_DRAG_POINTER) )
		{
			active_targets = fry.ui.dnd.checkTargets(fry.ui.dnd.pageX, fry.ui.dnd.pageY);
		}
		else
		{
			active_targets = fry.ui.dnd.checkTargets(state.x, state.y);
		}
		for ( var index in active_targets )
		{
			var target = active_targets[index];
			target.t.adapter.onPutData(fry.ui.dnd.adapter.onGetData(), fry.ui.dnd.node, fry.ui.dnd.adapter, state.x-target.t.coordsCache.x, state.y-target.t.coordsCache.y, fry.ui.dnd.controlKeyPressed, active_targets, index);
			target.t.coordsCache = null;
			target.t.isActive = false;
			target.t.adapter.onDragLeave(true);
		}
		if ( null != fry.ui.dnd.dragNode )
		{
			fry.ui.dnd.dragNode.rs();			
		}
		fry.ui.dnd.dragNode = null;
		fry.ui.dnd.node = null;
		fry.ui.dnd.adapter = null;
	}
	
	fry.ui.dnd.checkTargets = function(x, y)
	{
		if ( !fry.ui.dnd.dragNode || !fry.ui.dnd.dragNode.is() )
		{
			return;
		}
		var points = [[x,y], [x+fry.ui.dnd.dragNode.w(),y], [x+fry.ui.dnd.dragNode.w(),y+fry.ui.dnd.dragNode.h()], [x, y+fry.ui.dnd.dragNode.h()]];
		if ( fry.ui.dnd.adapter.onGetCursorPadding )
		{
			var p = fry.ui.dnd.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 = fry.ui.dnd.node.$;
		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 != (fry.ui.dnd.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 )
			{
				fry.ui.dnd.targets[i].adapter = null;
				fry.ui.dnd.targets[i] = null;
				delete fry.ui.dnd.targets[i];
				continue;
			}
			//console.log('%', target.adapter)
			var node = $(target.adapter.onGetRenderingNode()).$;
			// GC check
			if ( !node || null == node.parentNode || null == target.adapter.node || !target.adapter.node.is() )
			{
				fry.ui.dnd.targets[i].adapter = null;
				fry.ui.dnd.targets[i].node = null;
				fry.ui.dnd.targets[i] = null;
				delete fry.ui.dnd.targets[i];
				continue;
			}
			else
			{
				var cn = node;
				while (null != cn.parentNode)
				{
					cn = cn.parentNode;
				}
				if ( document != cn )
				{
					node = null;
					delete node;
					fry.ui.dnd.targets[i].adapter = null;
					fry.ui.dnd.targets[i] = null;
					delete fry.ui.dnd.targets[i];
					cn = null;
					delete cn;
					continue;
				}
				cn = null;
				delete cn;
			}
			if ( node == actual_node && !fry.ui.dnd.wasControlKey )
			{
				// the same node serving as BOTH
				continue;
			}
			var intersected = false;
			if ( fry.ui.dnd.MODE_DRAG_POINTER == (fry.ui.dnd.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;
			}
		}
		for ( var index in active_targets )
		{
			var trg = active_targets[index];
			trg.t.adapter.onDragEnter(trg.firstEnter, trg.offsetX, trg.offsetY, fry.ui.dnd.node, fry.ui.dnd.adapter, active_targets, index);
		}
		return active_targets;		
	}
	
	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.');
		}
		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 ( 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.node = this;
				adapter.onGetCoords = fry.ui.DropAdapter.prototype.onGetCoords;
			}			
			if ( !adapter.onDragEnter )
			{
				adapter.onDragEnter = function(firstEnter, offsetX, offsetY, sourceNode, sourceAdapter, originalMouseDownEvent, allTargets, targetIndex){};
			}
			if ( !adapter.onDragLeave )
			{
				adapter.onDragLeave = function(){};
			}
			if ( !adapter.onGetRenderingNode )
			{
				adapter.node = this;
				adapter.onGetRenderingNode = fry.ui.DragAdapter.prototype.onGetRenderingNode;
			}
			$foreach ( ['onPutData'], function(method)
			{
				if ( !adapter[method] )
				{
					throw new FryException(113, 'fry.ui: DnD `'+method+'` adapter method not specified.');
				}				
			});
			// check for existing target
			for ( var i in fry.ui.dnd.targets )
			{
				var target = fry.ui.dnd.targets[i];
				if ( $(target.adapter.onGetRenderingNode()).$ == this.$ )
				{
					// already existing for the node, removing
					fry.ui.dnd.targets[i] = null;
					delete fry.ui.dnd.targets[i];
				}
			}
			// registering drop target
			fry.ui.dnd.targets.push({adapter:adapter, coordsCache:null, isActive:false});
		}
		if ( fry.ui.dnd.MODE_DRAG == (mode & fry.ui.dnd.MODE_DRAG) )
		{
			// node is draggable
			adapter.node = adapter.onGetRenderingNode ? adapter.onGetRenderingNode() : this;
			this.e('mousedown', function(evt)
			{
//				console.log('%d x %d  offsets: %d x %d', evt.pageX, evt.pageY, evt.getOffsetX(), evt.getOffsetY());
				fry.ui.dnd.start(mode, adapter, evt.pageX, evt.pageY, evt.getOffsetX(), evt.getOffsetY(), evt.isAnyControlKeyPressed());
				evt = null;
			});
		}
		this.sa('frydnd', mode);
		return this;		
	}
	ACNode.prototype.removeDnd = // common typos prevention...
	ACNode.prototype.removeDnD = function()
	{
		if ( null == this.ga('frydnd') )
		{
			return this;
		}
		var mode = parseInt(this.ga('frydnd'));
		if ( fry.ui.dnd.MODE_DRAG == mode & fry.ui.dnd.MODE_DRAG )
		{
			this.e('mousedown');			
		}
		if ( fry.ui.dnd.MODE_DROP == mode & fry.ui.dnd.MODE_DROP )
		{
			for ( var index in fry.ui.dnd.targets )
			{
				var target = fry.ui.dnd.targets[index];
				if ( target.adapter.onGetRenderingNode() == this )
				{
					target.adapter = null;
					fry.ui.dnd.targets[index] = null;
					break;
				}
			}			
		}
		this.ra('frydnd');
		return this;
	}
}


fry_ui_init = function()
{
	fry.ui.info.page.width = document.documentElement.offsetWidth || document.body.offsetWidth;
	fry.ui.info.page.height = self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
	var inf = $__tune.node.getPageScrollPosition();
	fry.ui.info.page.scroll = {left:inf[0], top:inf[1]};
	inf = null;
	$__tune.event.addListener(document.documentElement, 'mousemove', function(evt)
	{
		try
		{
			if ( !fry )
			{
				return;
			}
		}
		catch(e)
		{
			return;
		}
		if ( !fry.ui.drag.state.started )
		{
			if ( fry.ui.dnd.state.started )
			{
				fry.ui.dnd.move(evt);
			}
		}
		else
		{
			fry.ui.drag.move(evt);				
		}
		evt = null;	
	});

	$__tune.event.addListener(document.documentElement, 'mouseup', function(evt)
	{
		try
		{
			if ( !fry )
			{
				return;
			}
		}
		catch(e)
		{
			return;
		}		
		if ( !fry.ui.drag.state.started )
		{
			if ( fry.ui.dnd.state.started )
			{
				fry.ui.dnd.stop();
			}
		}
		else
		{
			fry.ui.drag.stop();
		}
		evt = null;
	});	
}


if ( !fry.__production_mode )
{
	$__tune.event.addListener(self, 'load', function(evt)
	{
		if ( evt.removeListener )
		{
			evt.removeListener();		
		}
		fry_ui_init();
		fry_ui_init = null;
		if ('function' == typeof self['on_fry_load'])
		{
			on_fry_load();
		}
	});	
}



/*--------*/

if ( 'undefined' != typeof fry && 'undefined' != typeof fry.script )
{
	fry.script.register();
}/*
 * AC Fry - JavaScript Framework v1.0
 *
 * XML extension
 *
 * (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.
 */

/*--------*/

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 ( $notset(t) )
			{
				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 ( $notset(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 ( $notset(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.$ )
		{
			if ( 'function' != typeof o.$[i] && 'object' != typeof o.$[i] )
			{
				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;	
}


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


/*--------*/

if ( 'undefined' != typeof fry && 'undefined' != typeof fry.script )
{
	fry.script.register();
}/*
 * AC Fry - JavaScript Framework v1.0
 *
 * Data structures extension
 *
 * (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.
 */

/*--------*/

/*  ---------------------------------------------------------------- 
	ACRelation
*/
$class('ACRelation',
{
	construct:function(id)
	{
		this.id = id || '';
		this.sourceElement = null;
		this.targetElement = null;
		this.sourceElementItemIndex = -1;
		this.targetElementItemIndex = -1;
		this.properties = {};
	},
	destruct:function()
	{
	}
});

ACRelation.prototype.addSourceElement = function( acElem, itemIndex )
{
	itemIndex = $getdef(itemIndex, 0);
	if ( $isset(acElem.properties.items[itemIndex]) )
	{
		this.sourceElement = acElem;
		this.sourceElementItemIndex = itemIndex;
	}
}

ACRelation.prototype.addTargetElement = function( acElem, itemIndex )
{
	itemIndex = $getdef(itemIndex, 0);
	if ( $isset(acElem.properties.items[itemIndex]) )
	{
		this.targetElement = acElem;
		this.targetElementItemIndex = itemIndex;
	}
}

ACRelation.prototype.toString = function( deep, level )
{
    return this.sourceElement+'\n['+this.sourceElementItemIndex+']\n'+this.targetElement+'\n['+this.targetElementItemIndex+']\n';
}


/*  ---------------------------------------------------------------- 
	ACElement
*/
$class('ACElement',
{
	construct:function(id)
	{
		this.STATE_COLLAPSED = 1; 	// element is collapsed
		this.STATE_EXPANDED = 2;  	// element is expanded
		this.STATE_LOADED = 4;		// element has loaded its children
		this.STATE_LOADING = 8;		// element has children that are loading at the moment
		this.STATE_WILL_LOAD = 16;	// element has children to be loaded (it's known that element has children but they are not loaded yet)

		this.id = id || this.genUniqueId();
		this.state = this.STATE_COLLAPSED;
		this.index = 0;
		this.parentElement = null;
		this.rootElement = this;
		this.elements = [];
		this.properties = {};
		this.widgetProperties = {};
		this.isCollection = false;
	},
	destruct:function()
	{
		for ( var i in this.elements )
		{
			$delete(this.elements[i]);
		}
	}
});

ACElement.prototype.genUniqueId = function()
{
	return (''+Math.random()).replace(/\./g, '-');
}

ACElement.prototype.hasChildren = function()
{
	for ( var i in this.elements )
	{
		if ( $isset(this.elements[i]) )
		{
			return true;
		}
	}
	return false;
}

ACElement.prototype.setId = function( id )
{
   this.id = id;
}

ACElement.prototype.setProperties = function( properties )
{
	for ( var i in properties )
	{
		this.properties[i] = properties[i];
	}
}

ACElement.prototype.setState = function( state )
{
	this.state = state;
}

ACElement.prototype.setStateCollapsed = function()
{
	this.state = (this.state & 65533)|this.STATE_COLLAPSED;
}

ACElement.prototype.setStateExpanded = function()
{
	this.state = (this.state & 65534)|this.STATE_EXPANDED;
}

ACElement.prototype.hasState = function( state )
{
	return state == ( this.state & state );
}

ACElement.prototype.getElement = function( el )
{
   if ( 'string' == typeof el )
   {
       return this.getElementById( el, true );
   }
   return el;
}

ACElement.prototype.sort = function( callbackGetSortValue, sortingOrder )
{
	var order = sortingOrder || 0;
	this.elements.sort(function(element1, element2)
	{
		var v1 = (0 == order ? callbackGetSortValue(element1) : callbackGetSortValue(element2));
		var v2 = (0 == order ? callbackGetSortValue(element2) : callbackGetSortValue(element1));
//		alert(v1+' vs '+v2+' : '+(v1<v2));
		return v1==v2 ? 0 : (v1<v2? -1 : 1);		
	});
	/*
	QuickSort(this.elements, function(element1, element2, canBeEqual)
	{
		var v1 = (0 == order ? callbackGetSortValue(element1) : callbackGetSortValue(element2));
		var v2 = (0 == order ? callbackGetSortValue(element2) : callbackGetSortValue(element1));
		return canBeEqual ? v1<=v2 : v1<v2;
	});
	*/
	for ( var i=0; i<this.elements.length; i++ )
	{
		this.elements[i].index = i;
		if ( 0 != this.elements[i].elements.length )
		{
			this.elements[i].sort(callbackGetSortValue, order);
		}
	}
}

ACElement.prototype.getChildrenStamp = function()
{
	var csum = [this.id+'|'+this.state];
	for ( var i=0; i<this.elements.length; i++ )
	{
		csum.push(this.elements[i].id+'|'+this.elements[i].state);
	}
	return csum.join('/');
}

ACElement.prototype.search = function( searchCallback )
{
	var res = searchCallback(this);
	if ( null != res )
	{
		return res;
	}
	for ( var i=0; i<this.elements.length; i++ )
	{
		res = this.elements[i].search(searchCallback);
		if ( null != res )
		{
			return res;
		}
	}
	return null;
}

ACElement.prototype.traverseElement = function( traverseCallback )
{
    if ( traverseCallback(this) )
    {
        return this;
    }
    var n = this.elements.length;
    for ( var i=0; i<n; i++ )
    {
    	var res = this.elements[i].traverseElement( traverseCallback );
    	if ( null != res )
    	{
    		return res;
    	}
    }
    return null;
}

ACElement.prototype.map = function( callback )
{
	if ( false != callback(this) )
	{
	    var n = this.elements.length;
	    for ( var i=0; i<n; i++ )
	    {
			this.elements[i].map(callback);
	    }
	}
}

ACElement.prototype.findChild = function( el )
{
   el = this.getElement( el );
   var n = this.elements.length;
   for ( var i=0; i<n; i++ )
   {
       if ( el == this.elements[i] )
       {
           return el;
       }
   }
   return null;
}

// called for the root element of the hierarchy only
ACElement.prototype.onAfterAppend = function(newEl)
{
}

ACElement.prototype.appendChild = function( newEl )
{
	if ( null != newEl.parentElement )
	{
		if ( this == newEl.parentElement )
		{
			throw new FryException(84, 'fry/data: Unable to append child element. Child already exists.')
		}
		var el = this;
		while ( el )
		{
			if ( el == newEl )
			{
				throw new FryException(85, 'fry/data: Unable to append child element. Impossible nesting (parent cannot become child of its child).')				
			}
			el = el.parentElement;
		}
		var p = newEl.parentElement
		var n = p.elements.length;
        for ( i=newEl.index; i<n-1; i++ )
        {
            p.elements[i] = p.elements[i+1];
            p.elements[i].index--;
        }
		p.elements.length = n-1;
	}
	var ix = this.elements.length;
	this.elements[ix] = newEl;
	newEl.index = ix;
	newEl.parentElement = this;
	newEl.rootElement = this.rootElement;
	this.rootElement.onAfterAppend(newEl);
	return newEl;
}

ACElement.prototype.insertBefore = function( newEl, refEl )
{
	if ( newEl == refEl )
	{
		return newEl;
	}
	if ( 0 == this.elements.length )
	{
		return this.appendChild( newEl );
    }
    else
    {
        if ( null != newEl.parentElement )
        {
        	newEl = newEl.parentElement.removeChild( newEl );
        }
        var n = this.elements.length;
        for ( var i=0; i<n; i++ )
        {
            if ( refEl == this.elements[i] )
            {
                break;
            }
        }
        var ix = n;
        while ( ix > i )
        {
            this.elements[ix] = this.elements[ix-1];
            this.elements[ix].index++;
            ix--;
        }
        this.elements[i] = newEl;
        newEl.parentElement = this;
        newEl.rootElement = this.rootElement;
        newEl.index = i;
        this.elements.length = n+1;
        return newEl;
    }
}
 
ACElement.prototype.insertAfter = function( newEl, refEl )
{
 	if ( null == refEl )
 	{
 		return null;
 	}
    var n = this.elements.length;
    if ( 0 == n )
    {
        return this.appendChild( newEl );
    }
    else
    {
        if ( null != newEl.parentElement )
        {
        	newEl = newEl.parentElement.removeChild( newEl );
        }
        n = this.elements.length;
        for ( var i=0; i<n; i++ )
        {
            if ( refEl == this.elements[i] )
            {
                break;
            }
        }
		i++;
		var ix = n;
		while ( ix > i )
		{
			this.elements[ix] = this.elements[ix-1];
			this.elements[ix].index++;
			ix--;
		}
		this.elements[i] = newEl;
		newEl.parentElement = this;
		newEl.rootElement = this.rootElement;
		newEl.index = i;
		this.elements.length = n+1;
		return newEl;
	}
}

ACElement.prototype.removeChild = function( el )
{
    var n = this.elements.length;
    if ( 'object' == typeof el && this == el.parentElement )
    {
        for ( i=el.index; i<n-1; i++ )
        {
            this.elements[i] = this.elements[i+1];
            this.elements[i].index--;
        }
        el = el.cloneElement( true );
        el.parentElement = null;
        $delete(this.elements[n-1]);
        this.elements.length = n-1;
        return el;
    }
	throw new FryException(89, 'fry/data: Unable to remove child element. Parent does not contain child element.')
}

ACElement.prototype.removeAllChildren = function()
{
    var n = this.elements.length;
    if ( 0 == n )
    {
    	return;
    }
    for ( var i=0; i<n; i++ )
    {
    	if ( this.elements[i] )
    	{
    		$delete(this.elements[i]);
    	}
    }
    this.elements = new Array();
}
 
ACElement.prototype.moveUp = function( el )
{
    var origEl = this.findChild( el );
    if ( null != origEl )
    {
        var ix = origEl.index;
        if ( 0 < ix )
        {
            this.elements[ix].index--;
            this.elements[ix-1].index++;
            var a = this.elements[ix-1];
            this.elements[ix-1] = this.elements[ix];
            this.elements[ix] = a;
        }
    }
}
 
ACElement.prototype.moveDown = function( el )
{
    var origEl = this.findChild( el );
    if ( null != origEl )
    {
        var ix = origEl.index;
        if ( this.elements.length-1 > origEl.index )
        {
            this.elements[ix].index++;
            this.elements[ix+1].index--;
            var a = this.elements[ix+1];
            this.elements[ix+1] = this.elements[ix];
            this.elements[ix] = a;
        }
    }
}

ACElement.prototype.nextSibling = function()
{
	if ( this.parentElement.elements[this.index+1] )
	{
		return this.parentElement.elements[this.index+1];
	}
	return null;
}

ACElement.prototype.previousSibling = function()
{
	if ( this.parentElement.elements[this.index-1] )
	{
		return this.parentElement.elements[this.index-1];
	}
	return null;	
}

ACElement.prototype.getElementById = function( id, deep )
{
	deep = $getdef(deep, true);
    if ( this.id == id )
    {
        return this;
    }
    var n = this.elements.length;
    for ( var i=0; i<n; i++ )
    {
        if ( id == this.elements[i].id )
        {
            return this.elements[i];
        }
        else
        {
            if ( deep )
            {
                var el = this.elements[i].getElementById( id, deep );
                if ( null != el )
                {
                    return el;
                }
            }
        }
    }
    return null;
}
 
ACElement.prototype.cloneElement = function( deep, onCloneCallback, firstCallbackOnly )
{
	onCloneCallback = onCloneCallback || null;
	firstCallbackOnly = firstCallbackOnly || false;
    var o = $new(ACElement, this.genUniqueId());
	if ( 'undefined' != typeof this.onCloneProperties )
	{
		o.onCloneProperties = this.onCloneProperties;
	}
	for ( var i in this )
	{
		if ( 'function' != typeof this[i] && 'index' != i && 'rootElement' != i && 'parentElement' != i && 'widgetProperties' != i && 'elements' != i )
		{
			o[i] = this.onCloneProperties(this[i]);
		}
	}
	o.index = 0;
	o.rootElement = null;
	o.parentElement = null;
	o.widgetProperties = {};
	o.elements = [];
	if ( null != this.rootElement )
	{
	    while ( null != this.rootElement.getElementById(o.id) )
		{
			o.id = o.genUniqueId();
		}		
	}
	if ( null != onCloneCallback )
	{
		onCloneCallback(o, this);
	}
    if ( deep )
    {
        var n = this.elements.length;
        for ( var i=0; i<n; i++ )
        {
            o.appendChild( this.elements[i].cloneElement( true, firstCallbackOnly ? null : onCloneCallback ) );
        }
    }
    return o;
}

ACElement.prototype.onCloneProperties = function(op)
{
	if ( 'object' != typeof op )
	{
		return op;
	}
	var p = {};
	for ( var i in op )
	{
		if ( 'object' == typeof op[i] )
		{
			p[i] = this.onCloneProperties(op[i]);
		}
		else
		{
			p[i] = op[i];
		}
	}
	return p;
}

ACElement.prototype.toString = function( deep, level )
{
	deep = $getdef(deep, true);
	level = $getdef(level, '\t');
    var n = this.elements.length;
    var t = 'ACElement: '+this.index+'['+this.id+'] total:'+n+'\n';
    for ( var i in this.elements )
    {
        t += level+this.elements[i].toString( deep, level+'\t' );
    }
    return t;        
}



/* Miscellanous utilities */

/* Q-Sort */

function QuickSort(vec, comparator, loBound, hiBound )
{
	if ( $notset(comparator) )
	{
		comparator = function(element1, element2, canBeEqual) { return canBeEqual?element1<=element2:element1 < element2; };
	}
	$loBound = $getdef(loBound, 0);
	$hiBound = $getdef(hiBound, vec.length-1);
	var pivot, loSwap, hiSwap, temp;
	// Two items to sort
	if (hiBound - loBound == 1)
	{
		if (!comparator(vec[loBound],vec[hiBound]))
		{
			temp = vec[loBound];
			vec[loBound] = vec[hiBound];
			vec[hiBound] = temp;
		}
		return;
	}
	// Three or more items to sort
	pivot = vec[parseInt((loBound + hiBound) / 2)];
	vec[parseInt((loBound + hiBound) / 2)] = vec[loBound];
	vec[loBound] = pivot;
	loSwap = loBound + 1;
	hiSwap = hiBound;
	do {
		// Find the right loSwap
		while (loSwap <= hiSwap && comparator(vec[loSwap],pivot,true))
		{
			loSwap++;
		}
		// Find the right hiSwap
		while (comparator(pivot, vec[hiSwap]))
		{
			hiSwap--;
		}
		// Swap values if loSwap is less than hiSwap
		if (loSwap < hiSwap)
		{
			temp = vec[loSwap];
			vec[loSwap] = vec[hiSwap];
			vec[hiSwap] = temp;
		}
	} while (loSwap < hiSwap);
	vec[loBound] = vec[hiSwap];
	vec[hiSwap] = pivot;
	// 2 or more items in first section		
	if (loBound < hiSwap - 1)
	{
		QuickSort(vec, comparator, loBound, hiSwap - 1);
	}
	// 2 or more items in second section
	if (hiSwap + 1 < hiBound)
	{
		QuickSort(vec, comparator, hiSwap + 1, hiBound);
	}
}


/*--------*/
if ( 'undefined' != typeof fry && 'undefined' != typeof fry.script )
{
	fry.script.register();
}/*
 * AC Fry - JavaScript Framework v1.0
 *
 * UI Widgets extension
 *
 * (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.
 */

/*--------*/

// declaring default namespace
var ac = 
{
	init:function()
	{
		// registering default click listener for widget focusing 
		$(document.documentElement).e('click', function(evt)
		{
			var el = evt.target;
			var focused = false;
			while ( !focused && el && document.documentElement != el )
			{
				if ( null != el.getAttribute('widget-ident') )
				{
					var ident = el.getAttribute('widget-ident');
					var searchWidget = function(widget)
					{
						if ( widget.ident != ident )
						{
							return false;
						}
						ac.widget.focus(widget);
						focused = true;
						return true;
					}
					ac.widget.rootWidget.listChildWidgets(searchWidget, false, true);
				}
				el = el.parentNode;
			}
			if ( !focused )
			{
				ac.widget.focus(ac.widget.rootWidget);
			}
		});
		if (fry.keyboard)
		{
			fry.keyboard.initialize();
			fry.keyboard.addListener(ac.widget.keyboardListener);
		}
		ac.widget.rootWidget = $new(ac.Widget, $new(ac.WidgetModel), $new(ac.WidgetView), $new(ac.WidgetController), null);
		ac.widget.rootWidget.show($());
		ac.widget.rootWidget.renderingNode.d(false);
	},
	widget:
	{
		// root widget
		rootWidget:null,
		// focus/blur support
		focusedWidget:null,
		focus:function(widget)
		{
			if ( !widget || 'object' != typeof widget || !widget.onFocus)
			{
				return;
			}
			if ( ac.widget.focusedWidget )
			{
				if ( ac.widget.focusedWidget == widget )
				{
					return;
				}
				var w = widget;
				var do_blur = true;
				while ( null != w.parentWidget )
				{
					if ( ac.widget.focusedWidget == w )
					{
						do_blur = false;
						break;
					}
					w = w.parentWidget;
				}
				if ( do_blur )
				{
					ac.widget.focusedWidget.onBlur();					
				}
			}
			ac.widget.focusedWidget = widget;
			widget.onFocus();
		},
		blur:function()
		{
			if ( ac.widget.focusedWidget )
			{
				ac.widget.focusedWidget.onBlur();
			}
			ac.widget.focusedWidget = null;
		},
		isFocused:function(widget)
		{
			return ac.widget.focusedWidget == widget;
		},
		// resizing
		resize:function(widget, width, height)
		{
			widget.onResize(width, height);
			var resizeWidget = function(widget)
			{
				if ( widget.containerNode )
				{
					widget.onResize(widget.containerNode.w(), widget.containerNode.h());
				}
				else
				{
					widget.onResize(width, height);
				}
				return false;
			}
			widget.listChildWidgets(resizeWidget, false, false);			
		},
		// key actions support
		keyActions:[],
		keyActionsMapShown:false,
		keysNodes:[],
		keyboardListener:function(code, mask)
		{
			if (fry.keyboard.CONTROL_CODE == (mask & fry.keyboard.CONTROL_CODE))
			{
				if (fry.keyboard.PASTE == (mask & fry.keyboard.PASTE))
				{
					// pasted text from clipboard received
					if (ac.widget.focusedWidget)
					{
						ac.widget.focusedWidget.onSystemClipboardPaste(fry.keyboard.getClipboardContent());
					}
					return false;
				}
				else if (fry.keyboard.CUT == (mask & fry.keyboard.CUT))
				{
					// cut, let's clear selection if it exists
					if (ac.widget.focusedWidget)
					{
						ac.widget.focusedWidget.onSystemClipboardCut();
					}
					return false;
				}
				else if (fry.keyboard.SIG_CLIPBOARD_GET == (mask & fry.keyboard.SIG_CLIPBOARD_GET))
				{
					// need to return selected content, ask focused widget if it has anything to put into clipboard
					if (ac.widget.focusedWidget)
					{
						return ac.widget.focusedWidget.onSystemClipboardCopy();
					}
					// no, let's just use what u