/***

    Freja 2.0.alpha

    Build $Mon, 12 Jun 2006 02:29:16 UTC$

    Target: minimal

    THIS FILE IS AUTOMATICALLY GENERATED.  If creating patches, please
    diff against the source tree, not this file.

    Copyright (c) 2006 Cédric Savarese <pro@4213miles.com>, Troels Knak-Nielsen <troelskn@gmail.com>
    This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>

***/

if (typeof(dojo) != "undefined") {
	dojo.provide("Freja");
}
if (typeof(Freja) == "undefined") {
	Freja = {};
}
Freja.NAME = "Freja";
Freja.VERSION = "2.0.alpha";
Freja.__repr__ = function () {
	return "[" + this.NAME + " " + this.VERSION + "]";
};
Freja.toString = function () {
	return this.__repr__();
};
/**
  * Single-hierarchy inheritance (class emulation)
  * @see    http://www.itsalleasy.com/2006/02/05/prototype-chain/
  *         http://www.itsalleasy.com/2006/02/24/classjs-third-time-is-the-charm/
  *
  * Extends one prototype by another.
  * The subtype will have two specialpurpose properties:
  *     superconstructor    The parent prototype's constructor
  *     supertype        The parent prototype
  */
Freja.Class = {};
Freja.Class.extend = function(subClass, superconstructor) {
	var inlineSuper = function(){};
	inlineSuper.prototype = superconstructor.prototype;
	subClass.prototype = new inlineSuper();
	subClass.prototype.constructor = subClass;
	subClass.prototype.superconstructor = superconstructor;
	subClass.prototype.supertype = superconstructor.prototype;
};
/**
  * Freja._aux
  * wrapper for external dependencies (frameworks).
  *
  * This is the minimal auxiliary adapter. It contains self-sufficient
  * implementations of all dependencies.
  *
  */
if (typeof(Freja) == "undefined") {
	Freja = {};
}
Freja._aux = {};
/** bind(func, self) : function */
// from http://blog.ianbicking.org/prototype-and-object-prototype.html
Freja._aux.bind = function(func, self) {
	if(typeof (func)=="string"){
		func=self[func];
	}

	var im_func = null;
    if (typeof(func.im_func) == 'function') {
        im_func = func.im_func;
    } else {
        im_func = func;
    }
    func = function () {
        return func.im_func.apply(func.im_self, arguments);
    }
    func.im_func = im_func;
    func.im_self = self;
	return func;

};
/** formContents(elem) : Array */
Freja._aux.formContents = function(elem) {
	if (!elem) v = document;
	var names = [];
	var values = [];
	var inputs = elem.getElementsByTagName("INPUT");
	for (var i = 0; i < inputs.length; ++i) {
		var input = inputs[i];
		if (input.name) {
			if (input.type == "radio" || input.type == "checkbox") {
				if (input.checked) {
					names.push(input.name);
					values.push(input.value);
				} else {
					names.push(input.name);
					values.push("");
				}
			} else {
				names.push(input.name);
				values.push(input.value);
			}
		}
	}
	var textareas = elem.getElementsByTagName("TEXTAREA");
	for (var i = 0; i < textareas.length; ++i) {
		var input = textareas[i];
		if (input.name) {
			names.push(input.name);
			values.push(input.value);
		}
	}
	var selects = elem.getElementsByTagName("SELECT");
	for (var i = 0; i < selects.length; ++i) {
		var input = selects[i];
		if (input.name) {
			if (input.selectedIndex >= 0) {
				var opt = input.options[input.selectedIndex];
				names.push(input.name);
				values.push((opt.value) ? opt.value : "");
			}
		}
	}
	return [names, values];
};
/** getElement(id) : HTMLElement */
Freja._aux.getElement = function(id) {
	if (typeof(id) == "object") {
		return id;
	} else {
		return document.getElementById(id);
	}
};

/** connect(src, signal, fnc) : void */
Freja._aux.connect = function(src, signal, fnc) {

	if(!src) return;
	if (src.addEventListener) {
		var wrapper = function(e) {
			var evt = {
				stop : function() {
					if (e.cancelable) {
						e.preventDefault();
					}
					e.stopPropagation();
				}
			}
			fnc(evt);
		}
		src.addEventListener(signal.replace(/^(on)/, ""), wrapper, false);
	} else if (src.attachEvent) {
		var wrapper = function() {
			var e = window.event;
			var evt = {
				stop : function() {
					e.cancelBubble = true;
					e.returnValue = false;
				}
			}
			fnc(evt);
		}
		src.attachEvent(signal, wrapper);
	}
	if (!src._signals) {
		src._signals = [];
	}
	if (!src._signals[signal]) {
		src._signals[signal] = [];
	}
	src._signals[signal].push(fnc);
};
/** signal(src, signal, ...) : void */
Freja._aux.signal = function(src, signal) {

	try {
		var sigs = src._signals[signal];
		var args = [];
		for (var i=2; i < arguments.length; i++) {
			args.push(arguments[i]);
		}
		for (var i=0; i < sigs.length; i++) {
			try {
				sigs[i].apply(src, args);
			} catch (e) { /* squelch */ }
		}
	} catch (e) { /* squelch */ }
};
/** createDeferred() : Deferred */
Freja._aux.createDeferred = function() {
	return new Freja._aux.Deferred();
};
/** openXMLHttpRequest(method, url, async, user, pass) : XMLHttpRequest */
Freja._aux.openXMLHttpRequest = function(method, url, async, user, pass) {
	var req;
	method = method.toUpperCase();
	try { req = new ActiveXObject("Msxml2.XMLHTTP"); }
	catch (e) { try { req = new ActiveXObject("Microsoft.XMLHTTP"); }
	catch (e) { req = new XMLHttpRequest(); }}
	if (!req) throw new Error("Can't create XMLHttpRequest");
	if (user && pass) {
		req.open(method, url, async, user, pass);
	} else {
		req.open(method, url, async);
	}
	if (method == "POST" || method == "PUT") {
		req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	}
	return req;
};
/** sendXMLHttpRequest(req, sendContent) : Deferred */
Freja._aux.sendXMLHttpRequest = function(req, sendContent) {
	var d = Freja._aux.createDeferred();
	var bComplete = false;
	req.onreadystatechange = function() {
		if (req.readyState == 4 && !bComplete) {
			if (req.status == 0 || req.status == 200 || req.status == 201 || req.status == 304) {
				d.callback(req);
			} else {
				d.errback(req);
			}
			bComplete = true;
		}
	}
	if (!sendContent) sendContent = "";
	req.send(sendContent);
	return d;
};
/** xmlize(anyObject, objectName) : string */
Freja._aux.xmlize = function(anyObject, objectName, indentSpace) {
	var escape = function(sXml) {
		return sXml.replace(/&/g, "&amp;")
			.replace(/</g, "&lt;")
			.replace(/>/g, "&gt;")
			.replace(/"/g, "&quot;")
			.replace(/'/g, "&apos;");
	};
	indentSpace = indentSpace ? indentSpace : '';
	var s = indentSpace  + '<' + objectName + '>';
	var isLeaf = false;
	if(!(anyObject instanceof Object) || anyObject instanceof Number || anyObject instanceof String
	|| anyObject instanceof Boolean || anyObject instanceof Date){
		s += escape(""+anyObject);
		isLeaf = true;
	} else {
		s += "\n";
		var itemKey = '';
		var isArrayItem = anyObject instanceof Array;
		for (var name in anyObject) {
			s += Freja._aux.xmlize(anyObject[name], (isArrayItem ? "array-item key=\""+name+"\"" : name), indentSpace + "   ");
		};
		s += indentSpace;
	};
	return s += (objectName.indexOf(' ') != -1 ? "</array-item>\n":"</" + objectName + ">\n");
};
/** serializeXML(node) : string */
Freja._aux.serializeXML = function(node) {
	if (node.xml) return node.xml;
	return (new XMLSerializer()).serializeToString(node);
};
/** loadXML(string) : XMLDocument */
Freja._aux.loadXML = function(text) {
	if (window.ActiveXObject) {
		var xmlDoc = new ActiveXObject("Msxml2.DOMDocument");
		xmlDoc.loadXML(text);
		xmlDoc.setProperty("SelectionLanguage", "XPath");
		return xmlDoc;
	}
	return (new DOMParser()).parseFromString(text, "text/xml");
};
/** transformXSL(XMLDocument, XSLDocument) : string */
Freja._aux.transformXSL = function(xml, xsl, xslParameters) {
	if (typeof(xml.transformNode) != "undefined") {
		// set the parameters
		for (var paramName in xslParameters) {
			xsl.setProperty ("SelectionNamespaces", "xmlns:xsl='http://www.w3.org/1999/XSL/Transform'");
			var paramNode = xsl.selectSingleNode("//xsl:param[@name='"+ paramName +"']");
			paramNode.appendChild(xsl.createTextNode(xslParameters[paramName]));
			// @TODO: check if we have the 'select' attribute and remove it.
		}
		var result = xml.transformNode(xsl);

		// clean the stylesheet.
		for (var paramName in xslParameters) {
			var paramNode = xsl.selectSingleNode("//xsl:param[@name='"+ paramName +"']");
			while(paramNode.firstChild) {
				paramNode.removeChild(paramNode.firstChild);
			}
		}
		return result;
	};

	var processor = new XSLTProcessor();
	processor.importStylesheet(xsl);
	for (var paramName in xslParameters) {
		processor.setParameter(null, paramName, xslParameters[paramName]);
	}
	return Freja._aux.serializeXML(processor.transformToDocument(xml));

};
/** cloneXMLDocument(document) : XMLDocument */
Freja._aux.cloneXMLDocument = function(xmlDoc) {
	var clone = null;
	try {
		clone = xmlDoc.cloneNode(true);
	} catch(e) { /* squelch */ }

	// Can't clone a DocumentNode in Safari & Opera. Let's try something else.
	// @note Wouldn't it be easier to serialize the document to string and the parse it to a new document ?
	if (!clone) {
		if (document.implementation && document.implementation.createDocument) {
			clone = document.implementation.createDocument("", xmlDoc.documentElement.nodeName, null);
			// importNode is not safe in Safari ! the source document is altered. used cloneNode to fix the prblm
			var data = clone.importNode(xmlDoc.documentElement.cloneNode(true), true);
			try {
				clone.appendChild(data);
			} catch(e) {
				// Opera has already created a documentElement and can't append another root node
				var rootNode = clone.documentElement;
				for (var i = data.childNodes.length; i >= 0; i--) {
					rootNode.insertBefore(data.childNodes[i], rootNode.firstChild);
				}
				// need to copy root node attributes
				for (var i = 0; i < xmlDoc.documentElement.attributes.length; i++) {
					var name  = xmlDoc.documentElement.attributes.item(i).name;
					var value = xmlDoc.documentElement.attributes.item(i).value;
					clone.documentElement.setAttribute(name, value);
				}
			}
		}
	}
	return clone;
};
/** hasSupportForXSLT() : boolean */
Freja._aux.hasSupportForXSLT = function() { return (typeof(XSLTProcessor) != "undefined"); };
/** createQueryEngine() : Freja.QueryEngine */
Freja._aux.createQueryEngine = function() {
	if (window.ActiveXObject || (document.implementation && document.implementation.hasFeature("XPath", "3.0"))) {
		return new Freja.QueryEngine.XPath();
	} else {
		return new Freja.QueryEngine.SimplePath();
	}
};
/** A pale replacement for MochiKit.Async.Deferred */
Freja._aux.Deferred = function() {
	this._good = [];
	this._bad = [];
	this._pending = null;
};
Freja._aux.Deferred.prototype.callback = function() {
	if (!this._good || this._good.length == 0) {
		this._pending = [this.callback, arguments];
		return;
	}
	for (var i=0; this._good && i < this._good.length; i++) {
		this._good[i].apply(window, arguments);
	}
	this._good = [];
};
Freja._aux.Deferred.prototype.errback = function() {
	if (!this._bad || this._bad.length == 0) {
		this._pending = [this.errback, arguments];
		return;
	}
	for (var i=0; this._bad && i < this._bad.length; i++) {
		this._bad[i].apply(window, arguments);
	}
	this._bad = [];
};
Freja._aux.Deferred.prototype.addCallbacks = function(fncOK, fncError) {
	if (fncOK) this._good[this._good.length] = fncOK;
	if (fncError) this._bad[this._bad.length] = fncError;
	if (this._pending) {
		this._pending[0].apply(this, this._pending[1]);
	}
};
Freja._aux.Deferred.prototype.addCallback = function(fncOK) {
	this.addCallbacks(fncOK);
};
Freja._aux.Deferred.prototype.addErrback = function(fncError) {
	this.addCallbacks(null, fncError);
};
if (document.implementation && document.implementation.hasFeature("XPath", "3.0")) {
	XMLDocument.prototype.selectNodes = function(sExpr, contextNode) {
		var nsDoc = this;
		var nsresolver = this.createNSResolver(this.documentElement);

		try {
			var oResult = this.evaluate(sExpr,
				(contextNode ? contextNode : this),
				nsresolver,
				XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
		} catch(e) {
			throw new Error("Can't evaluate expression " + sExpr);
		}
		var nodeList = new Array(oResult.snapshotLength);
		nodeList.item = function(i) {
			return (i < 0 || i >= this.length) ? null : this[i];
		};
		nodeList.expr = sExpr;
		for (var i = 0;i < nodeList.length; i++) {
			nodeList[i] = oResult.snapshotItem(i);
		};
		return nodeList;
	    };
	Element.prototype.selectNodes = function(sExpr) {
		var doc = this.ownerDocument;
		if (doc.selectNodes) {
			return doc.selectNodes(sExpr, this);
		} else {
			throw new Error("Method selectNodes is only supported by XML Elements");
		};
	};
	XMLDocument.prototype.selectSingleNode = function(sExpr, contextNode) {
		var ctx = contextNode ? contextNode : null;
		sExpr = "("+sExpr+")[1]";
		var nodeList = this.selectNodes(sExpr, ctx);
		if (nodeList.length > 0) {
			return nodeList.item(0);
		} else {
			return null;
		}
	};
	Element.prototype.selectSingleNode = function(sExpr) {
		var doc = this.ownerDocument;
		if (doc.selectSingleNode) {
			return doc.selectSingleNode(sExpr, this);
		} else {
			throw new Error("Method selectNodes is only supported by XML Elements");
		}
	};
};

// Adapated From Sarissa
// * @version 0.9.6.1
// * @author: Manos Batsis, mailto: mbatsis at users full stop sourceforge full stop net

Freja._aux.pickRecentProgID = function(idList) {
    var bFound = false;
    for(var i=0; i < idList.length && !bFound; i++){
        try{
            var oDoc = new ActiveXObject(idList[i]);
            return idList[i];
        } catch (objException){ // trap; try next progID
        };
    };
    throw "Could not retrieve a valid progID.";
}

if(typeof XSLTProcessor == 'undefined' && typeof ActiveXObject  != 'undefined') {

    _SARISSA_DOM_PROGID = Freja._aux.pickRecentProgID(["Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"]);
    _SARISSA_XMLHTTP_PROGID = Freja._aux.pickRecentProgID(["Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]);
    _SARISSA_THREADEDDOM_PROGID = Freja._aux.pickRecentProgID(["Msxml2.FreeThreadedDOMDocument.5.0", "MSXML2.FreeThreadedDOMDocument.4.0", "MSXML2.FreeThreadedDOMDocument.3.0"]);
    _SARISSA_XSLTEMPLATE_PROGID = Freja._aux.pickRecentProgID(["Msxml2.XSLTemplate.5.0", "Msxml2.XSLTemplate.4.0", "MSXML2.XSLTemplate.3.0"]);

	XSLTProcessor = function(){
	    this.template = new ActiveXObject(_SARISSA_XSLTEMPLATE_PROGID);
	    this.processor = null;
	};

	XSLTProcessor.prototype.importStylesheet = function(xslDoc){
	    // convert stylesheet to free threaded
	    var converted = new ActiveXObject(_SARISSA_THREADEDDOM_PROGID);
	    converted.loadXML(xslDoc.xml);
	    this.template.stylesheet = converted;
	    this.processor = this.template.createProcessor();
	    // (re)set default param values
	    this.paramsSet = new Array();
	};

	XSLTProcessor.prototype.transformToDocument = function(sourceDoc){
	    this.processor.input = sourceDoc;
	    var outDoc = new ActiveXObject(_SARISSA_DOM_PROGID);
	    this.processor.output = outDoc;
	    this.processor.transform();
	    return outDoc;
	};

	XSLTProcessor.prototype.setParameter = function(nsURI, name, value){
	    /* nsURI is optional but cannot be null */
	    if(nsURI){
	        this.processor.addParameter(name, value, nsURI);
	    }else{
	        this.processor.addParameter(name, value);
	    };
	    /* update updated params for getParameter */
	    if(!this.paramsSet[""+nsURI]){
	        this.paramsSet[""+nsURI] = new Array();
	    };
	    this.paramsSet[""+nsURI][name] = value;
	};

}


/**
  * The baseclass for queryengines
  * @abstract
  */
Freja.QueryEngine = function() {};
Freja.QueryEngine.prototype.getElementById = function(document, id) {
	// getElementById doesn't work on XML document without xml:id
	var allElements = document.getElementsByTagName("*");
	for (var i= 0; i < allElements.length; i++) {
		if (allElements[i].getAttribute("id") == id) {
			return allElements[i];
		}
	}
};
Freja.QueryEngine.prototype.get = function(document, expression) {
	try {
		var node = this._find(document, expression);
	} catch(x) {
		return null;
	}
	if(node) return node.nodeValue;
	
};
Freja.QueryEngine.prototype.set = function(document, expression, value) {
	try {
		var node = this._find(document, expression);
		if(node)
				node.nodeValue = value;				
	} catch(x) {
		// text node not found. Might need to be created.
		// try not to process field names that are not meant to be xpath expressions  
		if(expression.lastIndexOf('/') != -1) {		
			var nodeName = expression.substr(expression.lastIndexOf('/')+1);
			if(nodeName.charAt(0)=='@') {
				// trying to set a non-existing attribute. Let's create it.
				var parentExpression =  expression.substring(0, expression.lastIndexOf('/'));
				var pNode = this._find(document, parentExpression);
				if(pNode) {
					// this._find returns a text node
					pNode = pNode.parentNode;
					pNode.setAttribute(nodeName.substr(1),value);
				}
			}
			// else parent element does not exist.. can't do anything
		}
	}
};
/**
  * XPath query engine.
  */
Freja.QueryEngine.XPath = function() {};
Freja.Class.extend(Freja.QueryEngine.XPath, Freja.QueryEngine);
Freja.QueryEngine.XPath.prototype._find = function(document, expression) {
	var node = document.selectSingleNode(expression);
	if (node && node.nodeType == 2) {
		return node;
	} 
	if (node && node.firstChild /* && node.firstChild.nodeType == 3*/) {
		return node.firstChild;
	} 
	 /*if (node && node.firstChild && node.firstChild.nodeType == 4) {
		return node.firstChild;
	} */
	if (node && node.nodeType==1 && !node.firstChild) {
		// empty element (<tag/>). Let's create and return a blank text node
		return node.appendChild(document.createTextNode(''));			  
	}
	
	throw new Error("Can't evaluate expression " + expression);
	return null;
}; 
/**
  * SimplePath
  */
Freja.QueryEngine.SimplePath = function() {};
Freja.Class.extend(Freja.QueryEngine.SimplePath, Freja.QueryEngine);
Freja.QueryEngine.SimplePath.prototype._find = function(document, expression) {
	if (!expression.match(/^[\d\w\/@\[\]=_\-']*$/)) {
		throw new Error("Can't evaluate expression " + expression);
	}
	var parts = expression.split(/\//);
	var node = document;
	var regAttr = new RegExp("^@([\\d\\w]*)");
	var regOffset = new RegExp("^([@\\d\\w]*)\\[([\\d]*)\\]$");
	var regFilter = new RegExp("^([\\d\\w]+)\\[@([@\\d\\w]+)=['\"]{1}(.*)['\"]{1}\\]$");
	var attr = null;
	var offset = 0;
	for (var i = 0; i < parts.length; ++i) {
		var part = parts[i];
		var filter = regFilter.exec(part);
		if(filter) {
			// filter[1] element name, filter[2] attribute name, filter[3] attribute value
			if(i>0 && parts[i-1]=='') {
				// expression was of type //element[...] 
				var cn = node.getElementsByTagName(filter[1]);
			} else {
				var cn = node.childNodes;
			}
			for(var j=0, l=cn.length; j<l ; j++) {
				if(cn[j].nodeType==1 && cn[j].tagName==filter[1] && cn[j].getAttribute(filter[2])== filter[3]) {
					node = cn[j];
					break;
				}
			}
			if (j==l)
				throw new Error("Can't evaluate expression " + part);
		}
		else {		
			offset = regOffset.exec(part);
			if (offset) {
				part = offset[1];
				offset = offset[2] - 1;
			} else {
				offset = 0;
			}
			if (part != "") {
				attr = regAttr.exec(part);
				if (attr) {
					node = node.getAttributeNode(attr[1]);
				} else {
					node = node.getElementsByTagName(part).item(offset);
				}
			}
		}
	}
	if (node && node.firstChild && node.firstChild.nodeType == 3) {
		return node.firstChild;
	} 
	if (node && node.firstChild && node.firstChild.nodeType == 4) {
		return node.firstChild;
	}
	if (node && node.nodeType==1 && !node.firstChild) {
		// empty element (<tag/>). Let's create and return a blank text node
		return node.appendChild(document.createTextNode(''));			  
	}
	
	if (!node) {		
		throw new Error("Can't evaluate expression " + expression);
	}

	return node;
};
/**
  * Standard model component
  */
Freja.Model = function(url, query) {
	this.url = url;
	this.ready = false;
	this.document = null;
	this._query = query;
};
/**
  * Returns a single value
  */
Freja.Model.prototype.getElementById = function(id) {
	if (this.document) {
		return this._query.getElementById(this.document, id);
	}
	return null;
};
/**
  * Returns a single value
  */
Freja.Model.prototype.get = function(expression) {
	if (this.document) {
		return this._query.get(this.document, expression);
	}
	return null;
};
/**
  * Updates a value
  */
Freja.Model.prototype.set = function(expression, value) {
	if (this.document) {
		return this._query.set(this.document, expression, value);
	}
	return null;
};
/**
  * Updates the model from a view
  */
Freja.Model.prototype.updateFrom = function(view) {
	var values = view.getValues();
	for (var i = 0; i < values[0].length; ++i) {
		this.set(values[0][i], values[1][i]);
	}
};
/**
  * Writes the model back to the remote service
  * @returns MochiKit.Async.Deferred
  */
Freja.Model.prototype.save = function() {
	var url = this.url;
	var match = /^(file:\/\/.*\/)([^\/]*)$/.exec(window.location.href);
	if (match) {
		url = match[1] + url; // local
	}
	// since the serialization may fail, we create a deferred for the
	// purpose, rather than just returning the sendXMLHttpRequest directly.
	var d = Freja._aux.createDeferred();
	var req = Freja.AssetManager.openXMLHttpRequest("POST", url);
	try {
		// for some obscure reason exceptions aren't thrown back if I call the
		// shorthand version of sendXMLHttpRequest in IE6.
		Freja._aux.sendXMLHttpRequest(req, Freja._aux.serializeXML(this.document)).addCallbacks(Freja._aux.bind(d.callback, d), Freja._aux.bind(d.errback, d));
	} catch (ex) {
		d.errback(ex);
	}
	return d;
};
/**
  * Deletes the model from the remote service
  * @returns MochiKit.Async.Deferred
  */
Freja.Model.prototype.remove = function() {
	var url = this.url;
	var match = /^(file:\/\/.*\/)([^\/]*)$/.exec(window.location.href);
	if (match) {
		url = match[1] + url; // local
	}
	var req = Freja.AssetManager.openXMLHttpRequest("DELETE", url);
	return Freja._aux.sendXMLHttpRequest(req);
};
/**
  * @returns MochiKit.Async.Deferred
  */
Freja.Model.prototype.reload = function() {
	this.ready = false;
	var onload = Freja._aux.bind(function(document) {
		this.document = document;
		this.ready = true;
		Freja._aux.signal(this, "onload");
	}, this);
	var d = Freja.AssetManager.loadAsset(this.url, true);
	d.addCallbacks(onload, Freja.AssetManager.onerror);
	return d;
};
/**
  * DataSource provides a gateway-type interface to a REST service.
  */
Freja.Model.DataSource = function(createURL, indexURL) {
	this.createURL = createURL;
	this.indexURL = indexURL;
};
/**
  * Returns a list of primary-keys to records in the datasource
  */
Freja.Model.DataSource.prototype.select = function() {
	return getModel(this.indexURL);
};
/**
  * Creates a new instance of a record
  * @todo errback to the deferred on errors
  */
Freja.Model.DataSource.prototype.create = function(values) {
	var url = this.createURL;
	var match = /^(file:\/\/.*\/)([^\/]*)$/.exec(window.location.href);
	if (match) {
		url = match[1] + url; // local
	}
	var req = Freja.AssetManager.openXMLHttpRequest("PUT", url);
	var payload = {};
	for (var i = 0, len = values[0].length; i < len; ++i) {
		payload[values[0][i]] = values[1][i];
	}
	return Freja._aux.sendXMLHttpRequest(req, Freja._aux.xmlize(payload, 'record'));
};

/**
  * Standard view component
  */
Freja.View = function(url, renderer) {
	this.url = url;
	this.ready = false;
	this.document = null;
	this._renderer = renderer;
	this._destination = null;
	this.behaviors = [];
	this.placeholder = null;
	Freja._aux.connect(this, "onrendercomplete", Freja._aux.bind(this._connectBehavior, this));
};
/**
  * @param    model            Freja.Model
  * @param    placeholder      string    If supplied, this will be used instead of the
  *                                      default placeholder.
  * @returns MochiKit.Async.Deferred
  */
Freja.View.prototype.render = function(model, placeholder, xslParameters) {
	if (typeof(placeholder) == "undefined") placeholder = this.placeholder;
	if (typeof(xslParameters) == "undefined") xslParameters = this.xslParameters;

	var Handler = function(model, view, deferred, xslParameters) {
		this.model = model;
		this.view = view;
		this.deferred = deferred;
		this.xslParameters = xslParameters;
	};
	Handler.prototype.trigger = function() {
		try {
			if (!this.view.ready) {
				Freja._aux.connect(this.view, "onload", Freja._aux.bind(this.trigger, this));
				return;
			}
			if (typeof(this.model) == "object" && this.model instanceof Freja.Model && !this.model.ready) {
				Freja._aux.connect(this.model, "onload", Freja._aux.bind(this.trigger, this));
				return;
			}
			var model;
			if (typeof(this.model) == "undefined") {
				// supply dummy
				model = { document : Freja._aux.loadXML("<?xml version='1.0' ?><dummy/>")};
			} else if (this.model instanceof Freja.Model) {
				model = this.model;
			} else {
				// wrap pojo's in
				model = { document : Freja._aux.loadXML("<?xml version='1.0' ?>\n" + Freja._aux.xmlize(this.model, "item")) };
			}

			var trans = this.view._renderer.transform(model, this.view, this.xslParameters);
			trans.addCallback(Freja._aux.bind(function(html) {
				this._destination.innerHTML = html;
			}, this.view));
			trans.addCallback(Freja._aux.bind(function() {
				Freja._aux.signal(this, "onrendercomplete", this._destination)
			}, this.view));
			trans.addCallback(this.deferred.callback);
			trans.addErrback(this.deferred.errback);
		} catch (ex) {
			this.deferred.errback(ex);
		}
	};

	var d = Freja._aux.createDeferred();
	try {
		this._destination = Freja._aux.getElement(placeholder);
		// @todo    Is this a good idea ?
		// Perhaps we should leave it to the programmer to do this.
		this._destination.innerHTML = Freja.AssetManager.THROBBER_HTML;

		var h = new Handler(model, this, d, xslParameters);
		h.trigger();
	} catch (ex) {
		d.errback(ex);
	}
	return d;
};
/**
  * Decorates the output of the primary renderer, to inject behavior.
  * @note Maybe we could use cssQuery (http://dean.edwards.name/my/cssQuery/)
  *       to identify targets for behavior
  */
Freja.View.prototype._connectBehavior = function(destination) {
	try {
		var connectCallback = function(node, eventType, callback) {

			Freja._aux.connect(node, eventType, Freja._aux.bind(
				function(e) {
					var allow = false;
					try {
						allow = callback(this);
					} catch (ex) {
						throw new Error("An error ocurred in user handler.\n" + ex.message);
					} finally {
						if (!allow) {
							e.stop();
						}
					}
				}, node)
			);
		};
		var applyHandlers = function(node, behaviors) {
			for (var i = 0, c = node.childNodes, l = c.length; i < l; ++i) {
				var child = c[i];
				if (child.nodeType == 1) {
					if(child.className) {
						var classNames = child.className.split(' ');						
						for (var j=0;j<classNames.length;j++) {											
							var handler = behaviors[classNames[j]];
							if (handler) {
								for (var eventType in handler) {
									if (eventType == "init") {
										handler.init(child);
									} else {
										connectCallback(child, eventType, handler[eventType]);
									}
								}
							}
						}
					}
					applyHandlers(child, behaviors);
				}
			}
		};

		// Avoid traversing the DOM tree if there's no handler to process.
		// @note: is there a better way? this.behaviors.length is always 0.
		// @note  This is fine. behaviors is a hashmap, not an array.
		for (var ids in this.behaviors) {
			applyHandlers(destination, this.behaviors);
			break;
		}

	} catch (ex) {
		alert(ex.message);
	}
};
/**
  * Returns the values of a formview
  */
Freja.View.prototype.getValues = function() {
	return Freja._aux.formContents(this._destination);
};
/**
  * Base object for viewrenderers
  */
Freja.View.Renderer = function() {};
/**
  * XSLT based render-engine
  */
Freja.View.Renderer.XSLTransformer = function() {};
Freja.Class.extend(Freja.View.Renderer.XSLTransformer, Freja.View.Renderer);
/**
  * @returns MochiKit.Async.Deferred
  */
Freja.View.Renderer.XSLTransformer.prototype.transform = function(model, view, xslParameters) {
        var d = Freja._aux.createDeferred();
        try {
		var html = Freja._aux.transformXSL(model.document, view.document, xslParameters);
		if (!html) {
			d.errback(new Error("XSL Transformation error."));
		} else {
			// fix empty textareas
			// Can't this be fixed by outputting as html rather than xml ?
			// <xsl:output method="html" />
			// (cedsav) don't remember all the details but method="xml" is the way to go.
			// method="html" would output html not xhtml, plus I think it implies that
			// you want to output a valid html document (with html, head and body tags).
			html = html.replace(/<textarea([^>]*)\/>/gi,"<textarea $1></textarea>");
			d.callback(html);
		}
	} catch (ex) {
		d.errback(ex);
	}
	return d;
};
/**
  * XSLT on a remote service for browser which have no native support.
  * @param    url    URL of the transform service
  */
Freja.View.Renderer.RemoteXSLTransformer = function(url) {
	this.url = url;
};
Freja.Class.extend(Freja.View.Renderer.RemoteXSLTransformer, Freja.View.Renderer);
/**
  * @returns MochiKit.Async.Deferred
  */
Freja.View.Renderer.RemoteXSLTransformer.prototype.transform = function(model, view, xslParameters) {
        var d = Freja._aux.createDeferred();

	// prepare posted data  (no need to send the XSL document, just its url)
	var xslUrl = view.url;
	var postedData = "xslFile=" + encodeURIComponent(xslUrl) + "&xmlData=" + encodeURIComponent(Freja._aux.serializeXML(model.document));
	
	var xslParameterString = '';
	for (var paramname in xslParameters) {
		xslParameterString += encodeURIComponent(paramname + "," + xslParameters[paramname]);
	}
	if(xslParameterString.length > 0) {
		postedData  = postedData + '&xslParam=' + xslParameterString;
	} 
	
	// send request to the server-side XSL transformation service
	var req = Freja.AssetManager.openXMLHttpRequest("POST", Freja.AssetManager.XSLT_SERVICE_URL);
	req.onreadystatechange = function() {
		if (req.readyState == 4) {
			if (req.status == 200) {
				d.callback(req.responseText);
			} else {
				d.errback(req.responseText);
			}
		}
	}
	req.send(postedData);
	return d;
};
/**
  * This is largely s copied with minor stylistic adjustments from Freja 1.1
  * I renamed it from Controller.history to distinguish between window.history
  */
Freja.UndoHistory = function() {
	this.cache = [];
	this.maxLength = 5;
	this._position = 0;
	this._undoSteps = 0;
};
/**
  * Creates a snapshot of the model and stores it.
  */
Freja.UndoHistory.prototype.add = function(model) {
	var historyIndex = this._position % this.maxLength;

	var modelDoc = model.document;
	this.cache[historyIndex] = {};
	this.cache[historyIndex].model = model;
	this.cache[historyIndex].document = Freja._aux.cloneXMLDocument(modelDoc);

	if (!this.cache[historyIndex].document) {
		throw new Error("Couldn't add to history.");
	} else {
		this._position++;
		// clear rest of the history if undo was used.
		var clearHistoryIndex = historyIndex;
		while (this._undoSteps > 0) {
			clearHistoryIndex = (clearHistoryIndex + 1) % this.maxLength;
			this.cache[clearHistoryIndex] = {};
			this._undoSteps--;
		}
		return historyIndex; // what would anybody need this for ?
	}
};
/**
  * Rolls the state back one step.
  */
Freja.UndoHistory.prototype.undo = function(steps) {
	if (this._undoSteps < this.cache.length) {
		this._undoSteps++;
		this._position--;
		if (this._position < 0) {
			this._position = this.maxLength - 1;
		}

		var model = this.cache[this._position].model;
		if (this.cache[this._position].document) {
			model.document = this.cache[this._position].document;
		} else {
			throw new Error("The model's DOMDocument wasn't properly copied into the history");
		}
		if (typeof(steps) != "undefined" && steps > 1) {
			this.undo(steps - 1);
		}
	} else {
		throw new Error("Nothing to undo");
	}
};
/**
  * Reverts the effects of undo.
  */
Freja.UndoHistory.prototype.redo = function() {
	if (this._undoSteps > 0) {
		this._undoSteps--;
		this._position = (this._position + 1) % this.maxLength;

		var model = this.cache[this._position].model;
		model.document = this.cache[this._position].document;
	} else {
		throw new Error("Nothing to redo");
	}
};
/**
  * Removes the last entry in the cache
  */
Freja.UndoHistory.prototype.removeLast = function() {
	this._position--;

	if (this._position < 0) {
		this._position = this.maxLength - 1;
	}
	this.cache[this._position] = {};
	this._undoSteps = 0;
};

/**
  * main repository
  * @static
  */
Freja.AssetManager = {
	models : [],
	views : [],
	_username : null,
	_password : null
};
/**
  * Set to sync to make all requests synchroneous. You shouldn't use
  * this setting for anything but testing/debugging.
  * "async" | "sync"
  */
Freja.AssetManager.HTTP_REQUEST_TYPE = "async";
/**
  * If this is set to null, real PUT and DELETE http-requests will be made,
  * otherwise a header will be set instead, and the request tunneled through
  * POST.
  *
  * Both IE6 and FF1.5 are known to support the required HTTP methods, so
  * if theese are your target platform, you can disable tunneling.
  */
// Freja.AssetManager.HTTP_METHOD_TUNNEL = null;
Freja.AssetManager.HTTP_METHOD_TUNNEL = "Http-Method-Equivalent";
/**
  * Set this url to provide remote xslt-transformation for browsers that
  * doesn't support it natively.
  */
Freja.AssetManager.XSLT_SERVICE_URL = "srvc-xslt.php";
/**
  * HTML displayed while waiting for stuff to happen
  */
Freja.AssetManager.THROBBER_HTML = "<span style='color:white;background:firebrick'>Loading ...</span>";
/**
  * returns an instance of the renderengine to use
  */
Freja.AssetManager.createRenderer = function() {
//	return new Freja.View.Renderer.RemoteXSLTransformer(this.XSLT_SERVICE_URL);
	if (Freja._aux.hasSupportForXSLT()) {
		return new Freja.View.Renderer.XSLTransformer();
	} else {
		return new Freja.View.Renderer.RemoteXSLTransformer(this.XSLT_SERVICE_URL);
	}
};
/**
  * Wipes all caches. This isn't something you will normally use during production,
  * but it's very helpful for debugging/testing
  */
Freja.AssetManager.clearCache = function() {
	this.models = [];
	this.views = [];
};
/**
  * Load a model-component
  * @param    url      string
  */
Freja.AssetManager.getModel = function(url) {
	for (var i=0; i < this.models.length; i++) {
		if (this.models[i].url == url) {
			return this.models[i];
		}
	}
	var m = new Freja.Model(url, Freja._aux.createQueryEngine());
	var onload = Freja._aux.bind(function(document) {
		this.document = document;
		this.ready = true;
		Freja._aux.signal(this, "onload");
	}, m);
	this.loadAsset(url, true).addCallbacks(onload, Freja.AssetManager.onerror);
	this.models.push(m);
	return m;
};
/**
  * Load a view-component
  * @param    url      string
  */
Freja.AssetManager.getView = function(url) {
	for (var i=0; i < this.views.length; i++) {
		if (this.views[i].url == url) {
			return this.views[i];
		}
	}
	var v = new Freja.View(url, this.createRenderer());
	var onload = Freja._aux.bind(function(document) {
		this.document = document;
		this.ready = true;
		Freja._aux.signal(this, "onload");
	}, v);
	this.loadAsset(url, false).addCallbacks(onload, Freja.AssetManager.onerror);
	this.views.push(v);
	return v;
};
/**
  * Creates and opens a http-request, tunneling exotic methods if needed.
  */
Freja.AssetManager.openXMLHttpRequest = function(method, url) {
	var tunnel = null;
	if (Freja.AssetManager.HTTP_METHOD_TUNNEL && method != "GET" && method != "POST") {
		tunnel = method;
		method = "POST";
	}
	var req = Freja._aux.openXMLHttpRequest(method, url, Freja.AssetManager.HTTP_REQUEST_TYPE == "async", Freja.AssetManager._username, Freja.AssetManager._password);
	if (tunnel) {
		req.setRequestHeader(Freja.AssetManager.HTTP_METHOD_TUNNEL, tunnel);
	}
	return req;
};
/**
  * Sets username + password for Http-Authentication
  */
Freja.AssetManager.setCredentials = function(username, password) {
	this._username = username;
	this._password = password;
};
/**
  * @returns MochiKit.Async.Deferred
  */
Freja.AssetManager.loadAsset = function(url, preventCaching) {
	var match = /^(file:\/\/.*\/)([^\/]*)$/.exec(window.location.href);
	if (match) {
		url = match[1] + url; // local
	}
	var d = Freja._aux.createDeferred();
	var handler = function(transport) {
		try {
			if (transport.responseText == "") {
				throw new Error("Empty response.");
			}
			if (transport.responseXML.xml == "") {
				// The server doesn't reply with Content-Type: text/xml
				// this will happen if the file is loaded locally (through file://)
				var document = Freja._aux.loadXML(transport.responseText);
			} else {
				var document = transport.responseXML;
			}
		} catch (ex) {
			d.errback(ex);
		}
		d.callback(document);
	};
	try {
		/* Why using HTTP_METHOD_TUNNEL for a GET? 
		  if (preventCaching && Freja.AssetManager.HTTP_METHOD_TUNNEL) {
			var req = Freja._aux.openXMLHttpRequest("POST", url, Freja.AssetManager.HTTP_REQUEST_TYPE == "async", Freja.AssetManager._username, Freja.AssetManager._password);
			req.setRequestHeader(Freja.AssetManager.HTTP_METHOD_TUNNEL, "GET");
			req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		} else {
		*/
			var req = Freja._aux.openXMLHttpRequest("GET", url, Freja.AssetManager.HTTP_REQUEST_TYPE == "async", Freja.AssetManager._username, Freja.AssetManager._password);
		/*}*/

		// This shouldn't be nescesary, but alas it is - firefox chokes
		// It's probably due to an error in MochiKit, so the problem
		// should be fixed there.
		var comm = Freja._aux.sendXMLHttpRequest(req);
		if (Freja.AssetManager.HTTP_REQUEST_TYPE == "async") {
			comm.addCallbacks(handler, Freja._aux.bind(d.errback, d));
		} else {
			if (req.status == 0 || req.status == 200 || req.status == 304) {
				handler(req);
			} else {
				d.errback(new Error("Request failed:" + req.status));
			}
		}
	} catch (ex) {
		d.errback(ex);
	}
	return d;
};
/**
  * This is a default error-handler. You should provide your own.
  * The handler is called if an asynchronous error happens, since
  * this could not be caught with the usual try ... catch
  *
  * It ought to be replaced completely with Deferred
  */
Freja.AssetManager.onerror = function(ex) {
	if(ex.message) {
		alert("Freja.AssetManager.onerror\n" + ex.message);
	} 
	// @note: on asynchronous calls, ex refers to the xmlhttpobject
	// see Bug #7189 (http://developer.berlios.de/bugs/?func=detailbug&group_id=6277&bug_id=7189)
	else if(ex.status){
		alert('error '+ ex.status + ' ' +  ex.responseText);
	}
};
/**
  * Global exports
  */
window.getModel = Freja._aux.bind("getModel", Freja.AssetManager);
window.getView = Freja._aux.bind("getView", Freja.AssetManager);