/*meta*/

/*include*/templates/*end*/
/*include*/../js/i18n/*end*/

var oxdtd = "http://localhost/html/dtd/ox.dtd";
var ox, js;

/* 
 * deps = { name: { includes: [name], prefix: Number } }
 * 
 */

var lastprefix = 0;

function current(name) {
    var inc = deps[name] && deps[name].includes;
    if (!inc) return false;
    if (inc === true) return true;
    var xml = new File(format("html/%s.xml", name)).lastModified();
    var html = new File(format("%s/%s.xml", args.dest, name)).lastModified();
    var js = new File(format("%s/%s.js", args.dest, name)).lastModified();
    Check: {
        if (xml > html || xml > js) break Check;
        for (var i = 0; i < inc.length; i++) if (!current(inc[i])) break Check;
        lastprefix = Math.max(lastprefix, deps[name].prefix);
        newdeps[name] = deps[name];
        deps[name] = { includes: true, prefix: deps[name].prefix };
        return true;
    }
    deps[name].includes = false;
    return false;
}

/**
 * @return [xml, js] if the page was rebuilt, false if it was up to date.
 */
function processXML(name, level) {
    level = level || "";
    if (current(name)) {
        if (!level) return false;
        var xml = loadXML(format("%s/%s.xml", args.dest, name));
        var js = eval("(" + read(format("%s/%s.js", args.dest, name)) + ")");
        return [xml, js];
    }
    var file = loadXML(format("html/%s.xml", name));
    ox = ox || file.namespace("ox");
    var xml = <parent>{ file }</parent>;
    xml.addNamespace(ox);
    var js = { images: {}, hide: {}, movable: [], size: [] };
    
    newdeps[name] = { includes: [], prefix: ++lastprefix };
    deps[name] = { includes: true, prefix: lastprefix };
    
    var id = 0;
    for each (var e in xml..*) {
        if (e.nodeKind() == "element") {
            e.@ox::id = String(e.@id) || format("d%de%d", lastprefix, ++id);
        }
    }
    
    for each (var button in xml..ox::button) processButton(button);

    var list = xml..ox::notag;
    while (list.length()) {
        for each (var notag in list) replaceXML(notag, notag.*);
        list = xml..ox::notag;
    }
    
    js.help = collectIDs2(xml..*.(@ox::help.length()), "help");
    js.min = collectIDs2(xml..*.(@ox::min.length()), "min");
    js.max = collectIDs2(xml..*.(@ox::max.length()), "max");
    
    for each (var version in xml..ox::version) {
        replaceXML(version, new XML(args.version));
    }
    for each (var pversion in xml..ox::pversion) {
        replaceXML(pversion, new XML(args.pversion));
    }
    
    for each (var img in xml..img.(String(@src))) {
        js.images[img.@ox::id] = String(img.@src);
        processImg(img);
    }
    
    for each (var iframe in xml..ox::iframe) {
        js.hide[iframe.@ox::id] = true;
        processIFrame(iframe);
    }
    
    for each (var movable in xml..ox::popupwindow) {
        js.movable.push(movable.@ox::id + "-h");
        js.images[movable.@ox::id + "-h-i"] = "themes/default/img/x.png";
        processMovable(movable);
    }

    for each (var timestamp in xml..ox::timestamp) processTimestamp(timestamp);
    
    var list = xml..ox::border;
    while (list.length()) {
        for each (var border in list) processBorder(border);
        list = xml..ox::border;
    }
    
    var incs = {};
    processAlign2(xml, js.size, incs);

    for each (var attr in xml..*.(@ox::align.length())) {
        processID(attr, "align");
    }

    js.split = collectIDs2(xml..*.(@ox::split.length()), "split");

    for each (var node in xml..*.(@ox::padding.length())) {
        node.@style = format("padding:%s;", node.@ox::padding) + node.@style;
    }
    for each (var node in xml..*.(@ox::border.length())) {
        node.@style = format("border-width:%s;", node.@ox::border) + node.@style;
    }
    for each (node in xml..*.(@ox::margin.length())) {
        node.@style = format("margin:%s;", node.@ox::margin) + node.@style;
    }
    
    js.i18n = collectIDs2(xml..*.(@ox::i18n.length()) , "i18n");

    for each (var include in xml..ox::include) {
        var n = String(include.@src).replace(/\.xml$/, "");
        newdeps[name].includes.push(n);
        var [xml2, js2] = processXML(n, level + "  ");
        replaceXML(include, xml2.*);
        copyJS(js.help, js2.help);
        copyJS(js.min, js2.min);
        copyJS(js.max, js2.max);
        copyJS(js.images, js2.images);
        copyJS(js.hide, js2.hide);
        js.movable = js.movable.concat(js2.movable);
        var placeholder = incs[include.@ox::id];
        for (var i = 0; i < js2.size.length; i++) placeholder.push(js2.size[i]);
        copyJS(js.split, js2.split);
        copyJS(js.i18n, js2.i18n);
    }
    
    delete xml..@ox::*;
    var w = new Writer(format("%s/%s.xml", args.dest, name));
    w.write(xml.toXMLString());
    w.close();
    w = new Writer(format("%s/%s.js", args.dest, name));
    writeJSON(w, js);
    w.close();
    return [xml, js];
}

function processHTML(name, varname, path) {
    var xml, js, page = processXML(name);
    var base = args["final"] + "/" + name.replace(/^(.*\/)*/, "");
    if (!page) {
        var srcXMLName = format("%s/%s.xml", args.dest, name);
        var srcJSName = format("%s/%s.js", args.dest, name);
        var srcXML = new File(srcXMLName).lastModified();
        var srcJS = new File(srcJSName).lastModified();
        var destXML = new File(base + ".html").lastModified();
        var destJS = new File(base + ".js").lastModified();
        if (destXML >= srcXML && destJS >= srcJS) return;
        xml = loadXML(srcXMLName);
        js = eval("(" + read(srcJSName) + ")");
    } else {
        [xml, js] = page;
    }
    
    if (path) {
        for each (var img in xml..img.(String(@src))) {
            img.@src = path + String(img.@src);
        }
        for (var i in js.images) js.images[i] = path + js.images[i];
    }
    
    var xmlns = new Namespace("http://www.w3.org/2000/xmlns/");
    delete xml..@xmlns::*;
    var w = new Writer(base + ".html");
    w.writeln(/*src*/<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<!--/Creative Commons License-->
<!--
    <rdf:RDF>
        <Work rdf:about="">
            <license rdf:resource="http://creativecommons.org/licenses/by-nc-sa/2.5/" />
        </Work>
        <License rdf:about="http://creativecommons.org/licenses/by-nc-sa/2.5/">
            <permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
            <permits rdf:resource="http://web.resource.org/cc/Distribution"/>
            <requires rdf:resource="http://web.resource.org/cc/Notice"/>
            <requires rdf:resource="http://web.resource.org/cc/Attribution"/>
            <prohibits rdf:resource="http://web.resource.org/cc/CommercialUse"/>
            <permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
            <requires rdf:resource="http://web.resource.org/cc/ShareAlike"/>
        </License>
    </rdf:RDF>
-->/*end*/);
    for each (var child in xml.*) toHTML(child).writeHTML(w);
    w.close();

    var w = new Writer(base + ".js");
    w.writeln(format("var %s = ", varname || "init"));
    writeJSON(w, js);
    w.writeln(";");
    w.close();
}

function writeJSON(w, x) {
    switch (typeof x) {
        case "object":
            if (x == null) {
                w.write("null");
            } else if (x.writeJSON) {
                x.writeJSON(w);
            } else if (x instanceof Array) {
                w.write("[");
                for (var i = 0; i < x.length; i++) {
                    if (i) w.write(",");
                    writeJSON(w, x[i]);
                }
                w.write("]");
            } else {
                w.write("{");
                var comma = false;
                for (var i in x) {
                    if (comma) w.write(",");
                    comma = true;
                    w.write(escapeString(i));
                    w.write(":");
                    writeJSON(w, x[i]);
                }
                w.write("}");
            }
            break;
        case "string":
            w.write(escapeString(x));
            break;
        case "undefined":
        case "boolean":
        case "number":
            w.write(String(x));
            break;
        default:
            throw format(_("Invalid JSON value: %s"), x);
    }
}

function processAlign(xml) {
    if (xml.@ox::align.length()) {
        js.writeln(format('{ id: "%s",', xml.@ox::id));
        js.writeln(format('align: "%s",', xml.@ox::align));
        if (xml.@ox::size.length())
            js.writeln(format('size: "%s",', xml.@ox::size));
        if (xml.@ox::padding.length())
            js.writeln(format('padding: "%s",', xml.@ox::padding));
        if (xml.@ox::border.length())
            js.writeln(format('border: "%s",', xml.@ox::border));
        if (xml.@ox::margin.length())
            js.writeln(format('margin: "%s",', xml.@ox::margin));
        if (xml.@ox::resize.length())
            js.writeln(format('resize: "%s",', xml.@ox::resize));
        js.writeln('children: [');
        for each (var child in xml.*) processAlign(child);
        js.writeln("] },");
    } else {
        for each (var child in xml.*) processAlign(child);
    }
}

function processAlign2(xml, result, incs) {
    if (xml.@ox::align.length()) {
        var size = { id: String(xml.@ox::id), align: String(xml.@ox::align) };
        if (xml.@ox::size.length()) size.size = String(xml.@ox::size);
        if (xml.@ox::padding.length()) size.padding = String(xml.@ox::padding);
        if (xml.@ox::border.length()) size.border = String(xml.@ox::border);
        if (xml.@ox::margin.length()) size.margin = String(xml.@ox::margin);
        if (xml.@ox::resize.length()) size.resize = String(xml.@ox::resize);
        size.children = [];
        for each (var child in xml.*) processAlign2(child, size.children, incs);
        result.push(size);
    } else if (xml.name() == QName(ox.uri, "include")) {
        var placeholder = [];
        placeholder.writeJSON = function(w) {
            for (var i = 0; i < this.length; i++) {
                if (i) w.write(",");
                writeJSON(w, this[i]);
            }
        };
        result.push(placeholder);
        incs[xml.@ox::id] = placeholder;
    } else {
        for each (var child in xml.*) processAlign2(child, result, incs);
    }
}

function collectIDs(list, name) {
    var a = [];
    for each (var attr in list) {
        a.push(format('"%s": "%s"', attr.@ox::id, attr.@ox::[name]));
        processID(attr, name);
    }
    return a.join(",\n");
}

function collectIDs2(list, name) {
    var ids = {};
    for each (var attr in list) {
        ids[attr.@ox::id] = String(attr.@ox::[name]);
        processID(attr, name);
    }
    return ids;
}

function copyJS(dest, src) {
    for (var i in src) {
        if (i in dest) {
            throw format(_("Duplicate ID %s: %s -> %s"), i, src[i], dest[i]);
        }
        dest[i] = src[i];
    }
}

function processBorder(xml) {
    var dir = Number(xml.@dir);
    delete xml.@dir;
    var type = String(xml.@type);
    delete xml.@type;
    var cls = xml["@class"];
    delete xml["@class"];
    var style = String(xml.@style);
    var aligned = Boolean(xml.@ox::align.length());
    // bitmask:     128    64    32    16    8    4    2    1
    var suffixes = ["br", "bl", "tr", "tl", "b", "r", "l", "t"];
    var div = xml.*;
    var first = true;
    var paddings = {
        topheader: "8px 4px 0px 4px",
        topheaderactive: "8px 0px 0px 0px",
        cpheader: "0px 4px 0px 4px",
        cpbody: "0px 4px 0px 4px",
        cpbottom: "0px 4px 8px 4px",
        calbody: "0px 4px 0px 0px"
    };
    // dir=2 fix
    if ((type == "cpheader" || type == "cpbody") && dir == 2) {
        paddings[type] = "0px 0px 0px 4px";
    }
    for (var i = 0; i < suffixes.length; i++) {
        if (dir & 0x80) {
            dir = dir * 2 & 0xff;
            var old = aligned && first && dir && !xml.*.@ox::align.length()
                    ? <div style={ format("margin:%s;", paddings[type]) }>
                          { div }
                      </div>
                    : div;
            div = <div { dir ? "" : mkAttrs(xml.@*) } xmlns:ox={ ox }
                       class={ format("%s-%s %s", type, suffixes[i], cls) }>
                      { old }
                  </div>;
            if (dir && aligned) div.@style +=
                "position:absolute;left:0;top:0;width:100%;height:100%;";
            if (first) {
                if (!aligned) {
                    div.@style += format("padding:%s;", paddings[type]);
                }
                div.@style += style;
            }
            first = false;
        } else dir *= 2;
    }
    div["@class"] += format(" %s-color", type);
    // is aligned?
    if (aligned) {
        // set padding
        div.@ox::padding = paddings[type];
    }
    replaceXML(xml, div);
}

function processTimestamp(xml) {
    replaceXML(xml, new XML(formatDateTime("yyyy-MM-dd HH:mm:ss", new Date())));
}

function processImg(xml) {
    xml.@id = xml.@ox::id;
    xml.@src = "themes/default/img/dummy.gif";
}

function processIFrame(xml) {
    replaceXML(xml, <>
        <iframe { mkAttrs(xml.@*) } xmlns:ox={ ox } ox:align="stretch">
        </iframe>
        <div xmlns:ox={ ox } ox:id={ xml.@ox::id  + "-hide" }
             id={ xml.@ox::id  + "-hide" }
             style="overflow:hidden;display:none;position:absolute;left:0;top:0;width:100%;height:100%">
            <img src="themes/default/img/dummy.gif"
                 style="width: 100%; height: 100%"/>
        </div>
    </>);
}

function processID(xml, name) {
    xml.@id = xml.@ox::id;
    delete xml["@ox::" + name];
}

function processButton(xml) {
    function outer(attr) {
        return attr.name() in { style: 1, id: 1, onclick: 1, onkeydown: 1 };
    }
    function inner(attr) {
        return !(attr.localName() in { style: 1, id: 1, onclick: 1, onkeydown: 1,
                                  "class": 1 });
    }
    
    replaceXML(xml,
        <div { mkAttrs(filterXML(xml.@*, outer)) }
             class={ "fakeButton background-color-default " + xml["@class"] }
             tabindex="0" xmlns:ox={ ox }>
            <div class="fakeButtonBottom">
                <div class="fakeButtonLeft">
                    <div class="fakeButtonRight">
                        <div class="fakeButtonTopLeft">
                            <div class="fakeButtonTopRight">
                                <div class="fakeButtonBottomLeft">
                                    <div { mkAttrs(filterXML(xml.@*, inner)) }
                                         class="fakeButtonBottomRight"
                                         ox:id={ xml.@ox::id + "-button" }>
                                        { xml.* }
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>);
}

function filterXML(xml, predicate) {
    var result = <></>;
    for each (var node in xml) if (predicate(node)) result += node;
    return result;
}

function mkAttrs(attrs) {
    var result = [];
    for each (var a in attrs) {
        var s = format('%s="%s"', a.localName(), escapeHTML(a.toString()));
        if (a.name().uri == oxdtd) s = "ox:" + s;
        result.push(s);
    }
    return result.join(" ");
}

function processMovable(xml) {
    function attrs(a) { return a.name() != "caption" && a.name() != "class"; }
    replaceXML(xml,
        <div { mkAttrs(filterXML(xml.@*, attrs)) } xmlns:ox={ ox }
             class={ "popup-window popupBackground " + xml["@class"] }> 
        
        <div class="popup_border_lo">
        	<div class="popupHeaderTableLeft1 popup_master_background"></div>
        	<div class="popupHeaderTableLeft2"></div>
        	<div class="popupHeaderTableLeft3 popup_master_background"><img src="themes/default/img/dummy.gif"
                height="1px" width="1px"/></div>
        </div>
        <div class="popup_border_oo">
        
	        <div class="popup-header popupHeaderBackground"
	            id={ xml.@ox::id + "-h" }>
	           <img ox:id={ xml.@ox::id + "-h-i" }
	                ox:i18n="alt,title"
	                style="margin-top:2px; float: right; cursor: pointer;"
	                src="themes/default/img/x.png" alt="Close"
	                title="Close"/>
	           <span ox:id={ xml.@ox::id + "-h-c" } ox:i18n="">
	               { xml.@caption }
	           </span>
	       </div>
        
        </div>
        <div class="popup_border_ro">
	    	<div class="popupHeaderTableRight1 popup_master_background"></div>
	    	<div class="popupHeaderTableRight2"></div>
	    	<div class="popupHeaderTableRight3 popup_master_background"><img src="themes/default/img/dummy.gif"
	            height="1px" width="1px"/></div>
        </div>

        <div class="popup_border_ll">&#x00A0;</div>
        <div class="popup_center_area">
        	{ xml.* }
        </div>
        <div class="popup_border_rr">&#x00A0;</div>

        <div class="popup_master_background  popup_border_lu"></div>
        <div class="popup_border_uu"></div>
        <div class="popup_master_background  popup_border_ru"></div>
            
        </div>);
}

function replaceXML(xml, replacement) {
    xml.parent().replace(xml.childIndex(), replacement);
}

function processIncludes(filename) {
    var xml = loadXML(filename);
    var ox = xml.namespace("ox");
    for each (var include in xml..ox::include) {
        replaceXML(include, processIncludes("html/" + include.@src));
    }
    return xml;
}

function getDir(filename) {
    return filename.replace(/\/[^\/]*$/, "/");
}

function loadXML(file) {
    return eval(read(file).replace(/<\?xml[^?]*\?>|<!--([^-]|-[^-])*-->|xmlns="[^"]*"/g, ""));
}

function toHTML(xml) {
    switch (xml.nodeKind()) {
        case "element":
            var children = [];
            for each (var child in xml.*) children.push(toHTML(child));
            var attrs = {};
            for each (var attr in xml.@*) attrs[attr.name()] = attr.toString();
            return newnode(xml.name().toString(), 0, attrs, children);
        case "text":
            return newtext(xml.toString());
        default:
            throw "Invalid XML: " + xml.toXMLString();
    }
}

var varnames = ["init", "preload", "init"];
var paths = [args.build + "/", args.build + "/", ""];
var pages = {
    "mainpage": 1, "index": 0, "ox": 0, "mail/mail_core": 1,
    "calendar/calendar_core": 1, "contacts/contacts_core": 1,
    "infostore/infostore_core": 1, "tasks/tasks_core": 1,
    "tasks/newTask": 2, "calendar/newAppointment": 2,
    "contacts/newContact": 2, "contacts/newDistributionList": 2,
    "mail/newMail": 2, "mail/detailMail": 2,
    "infostore/newInfoItemHidden": 2, "popup_trigger_cb": 2,
    "infostore/newInfoItemMain": 2, "messaging/messaging": 1
};

var depsFile = new File(args.dest + "/deps.js");
if (depsFile.exists()) {
    deps = eval("(" + (read(depsFile) || "null") + ")");
    depsFile["delete"]();
} else {
    deps = {};
}
var newdeps = {};
for (var i in pages) processHTML(i, varnames[pages[i]], paths[pages[i]]);
var w = new Writer(args.dest + "/deps.js");
writeJSON(w, newdeps);
w.close();

/*end*/