function ToolProdCat(root, xml, /*css,*/ tabs) {
    this._ns      = 'toolProdCat';
    this._xmlUrl  = xml;
    this._tabs    = tabs
    this._channel = null;
    if (typeof(root) == 'string') { root = document.getElementById(root); }
    this.rootElem = $ui(root).append($ui('<div id="'+this._ns+'"></div>'));
    this.rootElem.ajaxObject = new ToolProdCatAjaxObject(
        this,
        this.rootElem,
        this._xmlUrl,
        {}
    );
    var contentItems = $ui('#'+this._tabs[1]+'>div');
    $ui('#'+this._tabs[0]+'>li').each(function(i) {
        this.contentElement = contentItems.get(i);
        $ui(this).click(function() {
            ToolProdCat.selectTab(this);
            $ui('a', this).blur();
            return false;
        });
        if (i==tabs[2]) { $ui(this).click(); }
    });
}
ToolProdCat.selectTab = function(tab) {
    var tabs = $ui(tab).parent().children().removeClass('on on-first on-last');
    var cels = $ui(tab.contentElement).parent().children().removeClass('on on-first on-last');
    var classNames = 'on'+(tabs.index(tab)==0 ? ' on-first' : '')+(tabs.index(tab)==tabs.length-1 ? ' on-last' : '');
    $ui(tab).addClass(classNames);
    $ui(tab.contentElement).addClass(classNames);
}
ToolProdCat.numberFormat = function(num, format) {
    num      = ''+parseInt(num*Math.pow(10, format[0]));
    var pre  = num.substr(0, num.length-format[0]);
    var post = format[1] + num.substr(num.length-format[0]);
    while (pre.length > 3) {
        /^(\d+)(\d{3})$/.exec(pre);
        pre  = RegExp.$1;
        post = format[2] + RegExp.$2 + post;
    }
    return pre + post;
}
ToolProdCat.toHtml = function(text) {
    return $ui('<span/>').text(text).html().replace('"', '&quot;');
}

function ToolProdCatAjaxObject(generator, obj, uri, options) {
    this.generator = generator;
    this.object    = obj;
    this.uri       = uri;
    this.options   = options;
    this._ns       = generator._ns;
    this.baseContentUrl = '/sync/mentasyscatalog/getShoppingAds.do_idCategory=${id}';
    this.categories     = [];
    this.initFramework();
    this.load(uri);
}
ToolProdCatAjaxObject.prototype.initFramework = function() {
    var root = $ui(this.object);
    root.html(
          '<div id="'+this._ns+'Nav">'
        + '    <div id="'+this._ns+'NavMainCategories"><ul></ul></div>'
        + '    <div id="'+this._ns+'NavSubCategories"><ul></ul></div>'
        + '</div>'
        + '<div id="'+this._ns+'Content">'
        + '    <div id="'+this._ns+'MainContent">'
        + '    </div>'
        + '    <div id="'+this._ns+'Favorites">'
        + '        <div id="'+this._ns+'FavoritesListContainer">'
        + '            <ol id="'+this._ns+'FavoritesList"></ol>'
        + '        </div>'
        + '    </div>'
        + '    <div id="'+this._ns+'LoadingMessage"></div>'
        + '</div>'
        //+ '<div id="'+this._ns+'Footer"><ul></ul></div>'
    );
    this.mainNavElement    = $ui('#'+this._ns+'NavMainCategories ul', root);
    this.subNavElement     = $ui('#'+this._ns+'NavSubCategories  ul', root);
    this.contentElement    = $ui('#'+this._ns+'Content', root);
    this.itemListContainer = $ui('#'+this._ns+'Favorites', root);
    this.itemViewElement   = $ui('#'+this._ns+'MainContent', root);
    this.itemListElement   = $ui('#'+this._ns+'FavoritesList', root);
    //this.footerElement     = $ui('#'+this._ns+'Footer ul', root);
    this.loadingMessage    = $ui('#'+this._ns+'LoadingMessage', root);
}
ToolProdCatAjaxObject.prototype.load = function(uri) {
    var context = this;
    $ui.ajax({
        type:    'GET',
        url:     uri,
        success: function(xml) { return context.analyseCatalogXml(xml); }
    });
}
ToolProdCatAjaxObject.prototype.analyseCatalogXml = function(xml) {
    var baseUrl;
    if (baseUrl = $ui('catalog', xml).attr('categoryUrl')) {
        this.baseContentUrl = baseUrl;
    } else {
        baseUrl = this.baseContentUrl;
    }
    this.categories = $ui.map($ui('category', xml), function(obj) {
        return new ToolProdCatCategory(obj, baseUrl);
    });
    this.createMainNav(this.categories);
}
ToolProdCatAjaxObject.prototype.analyseCategoryXml = function(xml) {
    this.itemList = $ui.map($ui('top-product-list>product', xml).add($ui('category-content>offer-list>offer', xml)).add($ui('shopping-ads>item', xml)), function(obj) {
        return new ToolProdCatProduct(obj);
    });
    this.createItemList(this.itemList);
}
ToolProdCatAjaxObject.prototype.createNav = function(nav, target) {
    target.empty();
    nav = $ui(nav);
    var active = nav.filter(function() { return this.isActive; }).add(nav.get(0)).get(0);
    var context = this;
    nav.each(function(i) {
        var el = $ui('<li><a href="javascript:void('+this.id+');">'+this.title+'</a></li>');
        if (i==0)            { el.addClass('first'); }
        if (i==nav.length-1) { el.addClass('last'); }
        if (this==active)    { el.addClass('on'); }
        el.data('navobj', this);
        el.data('subnav', this.subnav);
        el.data('dataid', this.id);
        this.element = el;
        target.append(el);
    });
    return active;
}
ToolProdCatAjaxObject.prototype.createMainNav = function(nav) {
    this.activeMainNav = this.createNav(nav, this.mainNavElement).element;
    var context = this;
    $ui('a', this.mainNavElement).click(function() {
        context.activateMainNav($ui(this).parent());
        $ui(this).blur();
    });
    this.createSubNav($ui(this.activeMainNav).data('navobj').subnav);
    return this.activeMainNav;
}
ToolProdCatAjaxObject.prototype.createSubNav = function(nav) {
    this.activeSubNav = this.createNav(nav, this.subNavElement).element;
    var context = this;
    $ui('a', this.subNavElement).click(function() {
        context.activateSubNav($ui(this).parent());
        $ui(this).blur();
    });
    this.importItemList($ui(this.activeSubNav).data('navobj'));
    return this.activeSubNav;
}
ToolProdCatAjaxObject.prototype.createItemList = function(list) {
    var target = this.itemListElement;
    target.get(0).parentNode.parentNode.scrollLeft = 0;
    target.empty();
    if (window.opera) { target.css({display: 'block'}); }// Strange rendering bug in Opera
    var context = this;
    var el = '';
    $ui(list).each(function(i) {
        this.index = i;
        this.list  = list;
        el += '<li><a href="'+this.deeplink+'"><img src="'+this.image.url+'" alt="" width="55" height="55"/><span>'+this.name+'</span></a></li>';
    });
    target.append(el);
    $ui('li', target).each(function(i) {
        $ui(this).data('product', list[i]);
        $ui(this).click(function() {
            context.activateItem($ui(this));
            $ui('a', this).blur();
            return false;
        });
    });
    this.activateItem($ui('li:first', target));
}
ToolProdCatAjaxObject.prototype.showProduct = function(product) {
    var target = this.itemViewElement;
    target.empty();
    var context = this;
    target.html(
          '<div class="'+this._ns+'ProductImage">'
        + '    <em class="previous"><span></span></em>'
        + '    <a href="'+product.deeplink+'"><img src="'+product.image.url+'" alt="'+ToolProdCat.toHtml(product.manufacturer+' '+product.name)+'" width="'+product.image.width+'" height="'+product.image.height+'"/></a>'
        + '    <em class="next"><span></span></em>'
        + '</div>'
        + '<div class="'+this._ns+'ProductTitle">'
        + '    <h3><a href="'+product.deeplink+'">'+product.name+'</a></h3>'
        + '    <span class="'+this._ns+'ProductPrep">ab</span> <span class="'+this._ns+'ProductPrice">'+product.offers.minPrice+'</span>'
        + '</div>'
        + '<div class="'+this._ns+'ProductOffers">'
        + '    <span class="'+this._ns+'ProductOffersAmount"><a href="'+product.deeplink+'">'+product.offers.amount+' Angebot'+(product.offers.amount==1 ? '' : 'e')+'</a></span>'
        + ( product.offers.maxPrice > product.offers.minPrice
            ?   '    <span class="'+this._ns+'ProductPrep">von</span> <span class="'+this._ns+'ProductOffersFrom">'+product.offers.minPrice+'</span>'
              + '    <span class="'+this._ns+'ProductPrep">bis</span> <span class="'+this._ns+'ProductOffersTo">'+product.offers.maxPrice+'</span>'
            :   '    <span class="'+this._ns+'ProductPrep">f&uuml;r</span> <span class="'+this._ns+'ProductOffersFrom">'+product.offers.minPrice+'</span>'
          )
        + '</div>'
        + '<div class="'+this._ns+'ProductDescription">'
        + '    <p>'+product.description+' <a href="'+product.deeplink+'">mehr</a></p>'
        + '    <div class="'+this._ns+'ProductAdditionalLinks">'
        + '        <span class="'+this._ns+'ComparePrices"><a href="'+product.deeplink+'"><span>Preise vergleichen</span></a></span>'
        + '    </div>'
        + '</div>'
    );
    if (product.index==0) {
        $ui('.'+this._ns+'ProductImage .previous span', target).addClass('inactive');
    } else {
        $ui('.'+this._ns+'ProductImage .previous', target).click(function() {
            var item = $ui('#'+context._ns+'FavoritesList li:nth-child('+(1+product.index-1)+')');
            context.activateItem(item);
            if (item.position().left+item.width() > item.offsetParent().width() || item.position().left < 0) {
                context.scrollFavoriteIntoView(item);
            }
        });
    }
    if (product.index==product.list.length-1) {
        $ui('.'+this._ns+'ProductImage .next span', target).addClass('inactive');
    } else {
        $ui('.'+this._ns+'ProductImage .next', target).click(function() {
            var item = $ui('#'+context._ns+'FavoritesList li:nth-child('+(1+product.index+1)+')');
            context.activateItem(item);
            if (item.position().left+item.width() > item.offsetParent().width() || item.position().left < 0) {
                context.scrollFavoriteIntoView(item);
            }
        });
    }
}
ToolProdCatAjaxObject.prototype.scrollFavoriteIntoView = function(item) {
    var targetXPos = parseInt((item.offsetParent().width()-item.width())/2);
    item.offsetParent().scrollLeft(item.offsetParent().scrollLeft() - targetXPos + item.position().left);
}
ToolProdCatAjaxObject.prototype.activateMainNav = function(item) {
    $ui(this.activeMainNav).removeClass('on');
    $ui(item).addClass('on');
    this.activeMainNav = item;
    this.createSubNav(item.data('subnav'));
}
ToolProdCatAjaxObject.prototype.activateSubNav = function(item) {
    $ui(this.activeSubNav).removeClass('on');
    $ui(item).addClass('on');
    this.activeSubNav = item;
    this.importItemList(item.data('navobj'));
}
ToolProdCatAjaxObject.prototype.activateItem = function(item) {
    $ui(this.activeItem).removeClass('on');
    $ui(item).addClass('on');
    this.activeItem = item;
    this.showProduct(item.data('product'));
}
ToolProdCatAjaxObject.prototype.importItemList = function(list) {
    var url = list.categoryUrl.replace('${id}', list.id);
    var context = this;
    $ui.ajax({
        type:    'GET',
        url:     url,
        success: function(xml) {
            context.hideLoading();
            return context.analyseCategoryXml(xml);
        }
    });
    this.showLoading();
}
ToolProdCatAjaxObject.prototype.showLoading = function() {
    if (this.loading) { return; }
    this.loading = true;
    var context = this;
    window.setTimeout(function() { context.renderLoading(); }, 200);
}
ToolProdCatAjaxObject.prototype.renderLoading = function() {
    if (!this.loading) { return; }
    var context = this;
    context.loadingMessage.css({display: 'block'});
    if (window.opera) { context.contentElement.css({marginTop: -1}); }// Strange rendering bug in Opera
}
ToolProdCatAjaxObject.prototype.hideLoading = function() {
    this.loading = false;
    if (window.opera) { this.contentElement.css({marginTop: 0}); }// Strange rendering bug in Opera
    this.loadingMessage.css({display: 'none'});
}

ToolProdCatCategory = function(obj, baseUrl) {
    var context      = this;
    this.object      = $ui(obj);
    this.id          = this.object.attr('id');
    this.title       = this.object.attr('title');
    this.isActive    = !!this.object.attr('active');
    this.categoryUrl = this.object.attr('categoryUrl') || baseUrl;
    this.compParams  = this.object.attr('compParams');
    this.subnav      = $ui.map($ui('subcat', this.object), function(obj) {
        return new ToolProdCatCategory(obj, context.categoryUrl);
    });
}

ToolProdCatProduct = function(obj) {
    var context       = this;
    this.object       = $ui(obj);
    this.type         = this.object.get(0).nodeName;
    switch (this.type) {
        case 'product':
        case 'offer':
            this.name         = $ui('name:first', this.object).text();
            this.manufacturer = $ui('manufacturer:first', this.object).text();
            this.description  = $ui('description:first',  this.object).text();
            this.deeplink     = $ui('deeplink-url:first', this.object).text();
            this.image        = new ToolProdCatImage($ui('image-url:first', this.object));
            this.offers       = new ToolProdCatOffers(this.object, this.type);
            this.tests        = new ToolProdCatTests(this.object, this.type);
        case 'product':
            this.id           = this.object.attr('product-id');
            break;
        case 'offer':
            this.id           = this.object.attr('offer-id');
            break;
    }
}

ToolProdCatImage = function(obj) {
    this.object = $ui(obj);
    this.url    = this.object.text();
    this.width  = this.object.attr('width');
    this.height = this.object.attr('height');
}

ToolProdCatPrice = function(obj) {
    this.object   = $ui(obj);
    this.value    = this.object.text();
    this.currency = this.object.attr('currency');
    this.symbol   = this.symbols[this.currency] || this.symbols['___'];
    this.display  = '${value} ${symbol}';
    this.format   = [2, ',', '.'];
    this.toString = function() {
        return this.display.replace('${value}', ToolProdCat.numberFormat(this.value, this.format)).replace('${symbol}', this.symbol);
    }
}
ToolProdCatPrice.prototype.symbols = {
    'EUR': '\u20AC', // Euro
    'GBP': '\u00A3', // British Pounds
    'YEN': '\u00A5', // Japanese Yen
    'USD': '\u0024', // US-Dollar
    '___': '\u00A4'  // Generic Currency Symbol
}

ToolProdCatOffers = function(obj, type) {
    this.object   = $ui(obj);
    switch (type) {
        case 'product':
            this.amount   = $ui('offer-count', this.object).text();
            this.minPrice = new ToolProdCatPrice($ui('min-price:first', this.object));
            this.maxPrice = new ToolProdCatPrice($ui('max-price:first', this.object));
            break;
        case 'offer':
            this.amount   = 1;
            this.minPrice = new ToolProdCatPrice($ui('price:first', this.object));
            this.maxPrice = this.minPrice;
            break;
    }
}

ToolProdCatTests = function(obj, type) {
    this.object = $ui(obj);
    switch (type) {
        case 'product':
            this.amount = $ui('test-summary:first', this.object).attr('count');
            break;
        case 'offer':
            this.amount = 0;
            break;
    }
}
